66 * while the exported JSONL contract remains snake_case and versioned.
77 */
88import type { Argv } from 'yargs' ;
9+ import yargs from 'yargs' ;
10+ import { hideBin } from 'yargs/helpers' ;
911import { z } from 'zod' ;
1012import { Agent } from '../agent/Agent.js' ;
1113import { drainLoop } from '../agent/loop/index.js' ;
1214import type { LoopEvent } from '../agent/loop/types.js' ;
15+ import { SessionRuntime } from '../agent/runtime/SessionRuntime.js' ;
1316import type { ChatContext } from '../agent/types.js' ;
17+ import { globalOptions } from '../cli/config.js' ;
18+ import {
19+ loadConfiguration ,
20+ validateOutput ,
21+ validatePermissions ,
22+ } from '../cli/middleware.js' ;
1423import { PermissionMode } from '../config/types.js' ;
1524import type { Message } from '../services/ChatServiceInterface.js' ;
1625import type { TodoItem } from '../tools/builtin/todo/types.js' ;
17- import { getCwd } from '../utils/cwd.js' ;
1826import type {
1927 ConfirmationDetails ,
2028 ConfirmationResponse ,
2129} from '../tools/types/ExecutionTypes.js' ;
2230import {
23- initializeCliPlugins ,
24- normalizeCliInput ,
25- readCliInput ,
26- } from './shared/commandInput .js' ;
31+ formatToolCallSummary ,
32+ formatToolDisplay ,
33+ } from '../ui/utils/toolFormatters.js' ;
34+ import { getCwd } from '../utils/cwd .js' ;
2735import {
36+ createHeadlessJsonlEvent ,
2837 type HeadlessJsonlEventPayload ,
2938 type HeadlessJsonlEventType ,
30- createHeadlessJsonlEvent ,
3139} from './headlessEvents.js' ;
3240import {
33- formatToolCallSummary ,
34- formatToolDisplay ,
35- } from '../ui/utils/toolFormatters.js' ;
41+ initializeCliPlugins ,
42+ normalizeCliInput ,
43+ readCliInput ,
44+ } from './shared/commandInput.js' ;
45+ import { resolveNonInteractiveSession } from './shared/sessionContext.js' ;
3646
3747/** Minimal writable stream contract used by headless output sinks. */
3848interface WritableLike {
@@ -67,6 +77,10 @@ export const HeadlessOptionsSchema = z.object({
6777 mcpConfig : z . array ( z . string ( ) ) . optional ( ) ,
6878 strictMcpConfig : z . boolean ( ) . optional ( ) ,
6979 sessionId : z . string ( ) . optional ( ) ,
80+ allowedTools : z . array ( z . string ( ) ) . optional ( ) ,
81+ disallowedTools : z . array ( z . string ( ) ) . optional ( ) ,
82+ continue : z . boolean ( ) . optional ( ) ,
83+ resume : z . union ( [ z . string ( ) , z . boolean ( ) ] ) . optional ( ) ,
7084 outputFormat : HeadlessOutputFormatSchema . optional ( ) ,
7185} ) ;
7286
@@ -93,6 +107,14 @@ export interface HeadlessOptions {
93107 strictMcpConfig ?: boolean ;
94108 /** Session identifier used in the chat context. */
95109 sessionId ?: string ;
110+ /** Tool whitelist for this run. */
111+ allowedTools ?: string [ ] ;
112+ /** Tool blacklist for this run. */
113+ disallowedTools ?: string [ ] ;
114+ /** Continue the most recent conversation. */
115+ continue ?: boolean ;
116+ /** Resume a specific conversation. */
117+ resume ?: string | boolean ;
96118 /** Terminal output format. */
97119 outputFormat ?: string ;
98120}
@@ -565,6 +587,7 @@ export async function runHeadless(
565587 let eventWriter = createEventWriter ( io , outputFormat ) ;
566588 const streamState = new HeadlessStreamState ( ) ;
567589 const phaseState : HeadlessPhaseState = { targetLocked : false } ;
590+ let runtime : SessionRuntime | undefined ;
568591
569592 try {
570593 const validatedOptions = validateHeadlessOptions ( options ) ;
@@ -585,22 +608,38 @@ export async function runHeadless(
585608 const permissionMode =
586609 ( validatedOptions . permissionMode as PermissionMode | undefined ) ??
587610 PermissionMode . YOLO ;
588- const contextMessages : Message [ ] = [ ] ;
611+ const { sessionId, messages } = await resolveNonInteractiveSession ( {
612+ sessionId : validatedOptions . sessionId ,
613+ continue : validatedOptions . continue ,
614+ resume : validatedOptions . resume ,
615+ fallbackSessionPrefix : 'headless' ,
616+ } ) ;
617+ const contextMessages : Message [ ] = [ ...messages ] ;
589618 const chatContext : ChatContext = {
590619 messages : contextMessages ,
591620 userId : 'cli-user' ,
592- sessionId : validatedOptions . sessionId ?? `headless- ${ Date . now ( ) } ` ,
621+ sessionId,
593622 workspaceRoot : getCwd ( ) ,
594623 permissionMode,
595624 confirmationHandler : createConfirmationHandler ( ) ,
596625 } ;
597626
598- const agent = await Agent . create ( {
627+ runtime = await SessionRuntime . create ( {
628+ sessionId,
629+ modelId : validatedOptions . model ,
630+ mcpConfig : validatedOptions . mcpConfig ,
631+ strictMcpConfig : validatedOptions . strictMcpConfig ,
632+ } ) ;
633+
634+ const agent = await Agent . createWithRuntime ( runtime , {
635+ sessionId,
599636 systemPrompt : validatedOptions . systemPrompt ,
600637 appendSystemPrompt : validatedOptions . appendSystemPrompt ,
601638 maxTurns : validatedOptions . maxTurns ,
602639 modelId : validatedOptions . model ,
603640 permissionMode,
641+ toolWhitelist : validatedOptions . allowedTools ,
642+ toolBlacklist : validatedOptions . disallowedTools ,
604643 mcpConfig : validatedOptions . mcpConfig ,
605644 strictMcpConfig : validatedOptions . strictMcpConfig ,
606645 } ) ;
@@ -765,6 +804,8 @@ export async function runHeadless(
765804 }
766805 eventWriter . error ( `Error: ${ extractHeadlessErrorMessage ( error ) } ` ) ;
767806 return 1 ;
807+ } finally {
808+ await runtime ?. dispose ( ) ;
768809 }
769810}
770811
@@ -775,19 +816,19 @@ export async function handleHeadlessMode(): Promise<boolean> {
775816 return false ;
776817 }
777818
778- const yargs = ( await import ( 'yargs' ) ) . default ;
779- const { hideBin } = await import ( 'yargs/helpers' ) ;
780- const { globalOptions } = await import ( '../cli/config.js' ) ;
781819 const {
782- loadConfiguration,
783- validateOutput,
784- validatePermissions,
785- } = await import ( '../cli/middleware.js' ) ;
820+ headless : _h ,
821+ 'output-format' : _of ,
822+ 'system-prompt' : _sp ,
823+ 'append-system-prompt' : _asp ,
824+ 'max-turns' : _mt ,
825+ ...cliOptions
826+ } = globalOptions ;
786827
787828 const cli = yargs ( hideBin ( process . argv ) )
788829 . scriptName ( 'blade' )
789830 . strict ( false )
790- . options ( globalOptions )
831+ . options ( cliOptions )
791832 . middleware ( [ validatePermissions , loadConfiguration , validateOutput ] ) ;
792833
793834 headlessCommand ( cli ) ;
0 commit comments