@robota-sdk/agent-subagent-runner 3.0.0-beta.79 → 3.0.0-beta.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -14
- package/dist/node/index.cjs +2 -1
- package/dist/node/index.d.cts +436 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +357 -17
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +2 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +31 -18
- package/dist/node/child-process-subagent-ipc-BKEo2kRL.js +0 -2
- package/dist/node/child-process-subagent-ipc-BKEo2kRL.js.map +0 -1
- package/dist/node/child-process-subagent-ipc-C4zByGSA.cjs +0 -1
- package/dist/node/child-process-subagent-worker.cjs +0 -1
- package/dist/node/child-process-subagent-worker.d.ts +0 -1
- package/dist/node/child-process-subagent-worker.js +0 -2
- package/dist/node/child-process-subagent-worker.js.map +0 -1
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["SPAWN_DETACHED"],"sources":["../../src/child-process-subagent-transport.ts","../../src/child-process-subagent-runner-result.ts","../../src/child-process-subagent-runner.ts","../../src/worker-path-resolver.ts"],"sourcesContent":["import {\n BackgroundTaskError,\n type ISubagentJobStart,\n type TBackgroundTaskRunnerEvent,\n} from '@robota-sdk/agent-executor';\nimport { killProcessTree } from '@robota-sdk/agent-process';\n\n/** POSIX children are forked detached so a process-group kill reaps grandchildren (CORE-023). */\nconst SPAWN_DETACHED = process.platform !== 'win32';\n\n/** Resolve when the child exits or after `ms` — lets the graceful IPC cancel land before signalling. */\nfunction waitForExitOrTimeout(child: ChildProcess, ms: number): Promise<void> {\n return new Promise<void>((resolve) => {\n if (child.exitCode !== null || child.signalCode !== null) {\n resolve();\n return;\n }\n const timer = setTimeout(() => {\n child.removeListener('exit', onExit);\n resolve();\n }, ms);\n timer.unref?.();\n const onExit = (): void => {\n clearTimeout(timer);\n resolve();\n };\n child.once('exit', onExit);\n });\n}\n\nimport type {\n ISubagentWorkerResultMessage,\n TSubagentWorkerChildMessage,\n TSubagentWorkerParentMessage,\n} from './child-process-subagent-ipc.js';\nimport type { TToolArgs } from '@robota-sdk/agent-core';\nimport type { ChildProcess } from 'node:child_process';\n\nexport interface IChildProcessRuntime {\n job: ISubagentJobStart;\n child: ChildProcess;\n killGraceMs: number;\n}\n\nexport function handleWorkerMessage(\n message: TSubagentWorkerChildMessage,\n startWorker: () => void,\n resolveOnce: (result: ISubagentWorkerResultMessage) => void,\n rejectOnce: (error: Error) => void,\n emit?: (event: TBackgroundTaskRunnerEvent) => void,\n): void {\n switch (message.type) {\n case 'ready':\n startWorker();\n break;\n case 'result':\n resolveOnce(message);\n break;\n case 'error':\n rejectOnce(new BackgroundTaskError('runner', message.message));\n break;\n case 'cancelled':\n rejectOnce(new BackgroundTaskError('runner', message.reason ?? 'Subagent worker cancelled'));\n break;\n case 'text_delta':\n emit?.({ type: 'background_task_text_delta', delta: message.delta });\n break;\n case 'tool_start':\n emit?.({\n type: 'background_task_tool_start',\n toolName: message.toolName,\n firstArg: extractFirstArg(message.toolArgs),\n });\n break;\n case 'tool_end':\n emit?.({\n type: 'background_task_tool_end',\n toolName: message.toolName,\n success: message.success,\n });\n break;\n default:\n rejectOnce(new BackgroundTaskError('runner', 'Unhandled subagent worker message'));\n }\n}\n\nfunction extractFirstArg(toolArgs?: TToolArgs): string | undefined {\n if (!toolArgs) return undefined;\n const firstValue = Object.values(toolArgs)[0];\n if (firstValue === undefined) return undefined;\n return typeof firstValue === 'object' ? JSON.stringify(firstValue) : String(firstValue);\n}\n\nexport function sendWorkerMessage(\n child: ChildProcess,\n message: TSubagentWorkerParentMessage,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!child.connected) {\n reject(new BackgroundTaskError('crash', 'Subagent worker IPC channel is closed'));\n return;\n }\n child.send(message, (error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n}\n\nexport async function cancelChildProcess(\n runtime: IChildProcessRuntime,\n reason?: string,\n): Promise<void> {\n // CORE-023: graceful IPC cancel first (preKill), then SIGTERM→grace→SIGKILL over the process\n // group — the previous path signalled SIGTERM only and never escalated, so a worker ignoring\n // SIGTERM survived forever.\n await killProcessTree(runtime.child, {\n graceMs: runtime.killGraceMs,\n processGroup: SPAWN_DETACHED,\n preKill: async () => {\n if (!runtime.child.connected) return;\n await sendWorkerMessage(runtime.child, { type: 'cancel', reason }).catch(() => undefined);\n // Give the worker the grace window to shut down cleanly on the IPC cancel before signalling.\n await waitForExitOrTimeout(runtime.child, runtime.killGraceMs);\n },\n });\n}\n","import {\n BackgroundTaskError,\n type ISubagentJobResult,\n type ISubagentJobStart,\n} from '@robota-sdk/agent-executor';\n\nimport {\n isSubagentWorkerChildMessage,\n type ISubagentWorkerResultMessage,\n type ISubagentWorkerStartPayload,\n type TSubagentWorkerWireValue,\n} from './child-process-subagent-ipc.js';\nimport {\n cancelChildProcess,\n handleWorkerMessage,\n sendWorkerMessage,\n type IChildProcessRuntime,\n} from './child-process-subagent-transport.js';\n\nexport interface ICancellationResult {\n promise: Promise<ISubagentJobResult>;\n reject(reason?: string): void;\n}\n\nexport interface IChildProcessSubagentResultOptions {\n runtime: IChildProcessRuntime;\n payload: ISubagentWorkerStartPayload;\n resolveTranscriptPath: (job: ISubagentJobStart) => string | undefined;\n}\n\nexport function createChildProcessSubagentResult(\n options: IChildProcessSubagentResultOptions,\n): Promise<ISubagentJobResult> {\n return new Promise<ISubagentJobResult>((resolve, reject) => {\n new ChildProcessSubagentResultController(options, resolve, reject).start();\n });\n}\n\nclass ChildProcessSubagentResultController {\n private settled = false;\n private started = false;\n private readonly timeoutTimer?: ReturnType<typeof setTimeout>;\n\n constructor(\n private readonly options: IChildProcessSubagentResultOptions,\n private readonly resolve: (result: ISubagentJobResult) => void,\n private readonly reject: (error: Error) => void,\n ) {\n this.timeoutTimer = createTimeoutTimer(this.options.runtime, (error) => this.rejectOnce(error));\n }\n\n start(): void {\n const { child } = this.options.runtime;\n child.on('message', this.onMessage);\n child.on('error', this.onError);\n child.on('exit', this.onExit);\n child.once('spawn', () => {\n setImmediate(this.startWorker);\n });\n }\n\n private readonly startWorker = (): void => {\n if (this.started) return;\n this.started = true;\n const { child } = this.options.runtime;\n void sendWorkerMessage(child, { type: 'start', payload: this.options.payload }).catch(\n (error) => {\n this.rejectOnce(error instanceof Error ? error : new Error(String(error)));\n },\n );\n };\n\n private readonly onMessage = (message: TSubagentWorkerWireValue): void => {\n if (!isSubagentWorkerChildMessage(message)) {\n this.rejectOnce(\n new BackgroundTaskError('runner', 'Received malformed subagent worker message'),\n );\n return;\n }\n const { job } = this.options.runtime;\n handleWorkerMessage(message, this.startWorker, this.resolveOnce, this.rejectOnce, job.emit);\n };\n\n private readonly onError = (error: Error): void => {\n this.rejectOnce(new BackgroundTaskError('crash', error.message));\n };\n\n private readonly onExit = (code: number | null, signal: NodeJS.Signals | null): void => {\n if (this.settled) return;\n this.rejectOnce(new BackgroundTaskError('crash', formatEarlyExitMessage(code, signal)));\n };\n\n private readonly resolveOnce = (result: ISubagentWorkerResultMessage): void => {\n if (this.settled) return;\n this.settled = true;\n this.clearTimers();\n this.cleanup();\n const { runtime, resolveTranscriptPath } = this.options;\n this.resolve(toSubagentResult(runtime.job, result, resolveTranscriptPath));\n };\n\n private readonly rejectOnce = (error: Error): void => {\n if (this.settled) return;\n this.settled = true;\n this.clearTimers();\n this.cleanup();\n this.reject(error);\n };\n\n private clearTimers(): void {\n if (this.timeoutTimer) clearTimeout(this.timeoutTimer);\n }\n\n private cleanup(): void {\n const { child } = this.options.runtime;\n child.off('message', this.onMessage);\n child.off('error', this.onError);\n child.off('exit', this.onExit);\n }\n}\n\nexport function createCancellationResult(jobId: string): ICancellationResult {\n let settled = false;\n let rejectFn: (error: Error) => void = () => {};\n const promise = new Promise<ISubagentJobResult>((_resolve, reject) => {\n rejectFn = reject;\n });\n return {\n promise,\n reject(reason?: string): void {\n if (settled) return;\n settled = true;\n rejectFn(new BackgroundTaskError('runner', reason ?? `Subagent job cancelled: ${jobId}`));\n },\n };\n}\n\nfunction createTimeoutTimer(\n runtime: IChildProcessRuntime,\n rejectOnce: (error: Error) => void,\n): ReturnType<typeof setTimeout> | undefined {\n if (!runtime.job.request.timeoutMs) return undefined;\n return setTimeout(() => {\n void cancelChildProcess(runtime, 'Subagent worker timed out');\n rejectOnce(new BackgroundTaskError('timeout', 'Subagent worker timed out'));\n }, runtime.job.request.timeoutMs);\n}\n\nfunction toSubagentResult(\n job: ISubagentJobStart,\n result: ISubagentWorkerResultMessage,\n resolveTranscriptPath: (job: ISubagentJobStart) => string | undefined,\n): ISubagentJobResult {\n const transcriptPath = resolveTranscriptPath(job);\n return {\n jobId: job.jobId,\n output: result.output,\n ...(transcriptPath ? { metadata: { transcriptPath, logPath: transcriptPath } } : {}),\n // ANALYTICS-001 (Phase 2): carry the subagent's forwarded token usage so the background-task\n // tracker can attribute it to this agent as a source in the parent log.\n ...(result.usage ? { usage: result.usage } : {}),\n };\n}\n\nfunction formatEarlyExitMessage(code: number | null, signal: NodeJS.Signals | null): string {\n const detail =\n signal !== null ? `signal ${signal}` : `exit code ${code === null ? 'unknown' : code}`;\n return `Subagent worker exited before result: ${detail}`;\n}\n","import { fork } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n BackgroundTaskError,\n createBackgroundTaskLogPage,\n createGitWorktreeIsolationAdapter,\n createWorktreeSubagentRunner,\n type ISubagentJobHandle,\n type ISubagentJobStart,\n type ISubagentRunner,\n type ISubagentWorktreeAdapter,\n} from '@robota-sdk/agent-executor';\nimport { getBuiltInAgent } from '@robota-sdk/agent-framework';\nimport { DEFAULT_KILL_GRACE_MS } from '@robota-sdk/agent-process';\n\nimport {\n createCancellationResult,\n createChildProcessSubagentResult,\n} from './child-process-subagent-runner-result.js';\nimport {\n cancelChildProcess,\n sendWorkerMessage,\n type IChildProcessRuntime,\n} from './child-process-subagent-transport.js';\nimport { getDefaultSubagentWorkerPath } from './worker-path-resolver.js';\n\nimport type { ISubagentWorkerStartPayload } from './child-process-subagent-ipc.js';\nimport type { IProviderDefinitionConfig } from '@robota-sdk/agent-core';\nimport type {\n IAgentDefinition,\n IInProcessSubagentRunnerDeps,\n TSubagentRunnerFactory,\n} from '@robota-sdk/agent-framework';\nimport type {\n IBackgroundTaskLogCursor,\n IBackgroundTaskLogPage,\n ISerializableProviderProfile,\n} from '@robota-sdk/agent-interface-transport';\n\n/** POSIX children are forked detached so a process-group kill reaps grandchildren (CORE-023). */\nconst SPAWN_DETACHED = process.platform !== 'win32';\n\nexport interface IChildProcessSubagentRunnerOptions {\n workerPath: string;\n providerConfig?: IProviderDefinitionConfig;\n execArgv?: string[];\n killGraceMs?: number;\n env?: NodeJS.ProcessEnv;\n worktreeIsolation?: boolean;\n worktreeAdapter?: ISubagentWorktreeAdapter;\n logsDir?: string;\n}\n\nexport function createChildProcessSubagentRunnerFactory(\n options: IChildProcessSubagentRunnerOptions,\n): TSubagentRunnerFactory {\n return (deps) => {\n const runner = new ChildProcessSubagentRunner(deps, options);\n if (options.worktreeIsolation === false) return runner;\n return createWorktreeSubagentRunner({\n runner,\n worktreeAdapter: options.worktreeAdapter ?? createGitWorktreeIsolationAdapter(),\n hooks: deps.config.hooks,\n hookTypeExecutors: deps.hookTypeExecutors,\n });\n };\n}\n\nexport class ChildProcessSubagentRunner implements ISubagentRunner {\n private readonly workerPath: string;\n private readonly execArgv?: string[];\n private readonly killGraceMs: number;\n private readonly providerConfig?: IProviderDefinitionConfig;\n private readonly env?: NodeJS.ProcessEnv;\n private readonly logsDir?: string;\n\n constructor(\n private readonly deps: IInProcessSubagentRunnerDeps,\n options: IChildProcessSubagentRunnerOptions,\n ) {\n this.workerPath = options.workerPath;\n this.execArgv = options.execArgv;\n this.killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS;\n this.providerConfig = options.providerConfig;\n this.env = options.env;\n this.logsDir = options.logsDir;\n }\n\n start(job: ISubagentJobStart): ISubagentJobHandle {\n const child = fork(this.workerPath, [], {\n cwd: job.request.cwd,\n env: { ...process.env, ...(this.env ?? {}) },\n execArgv: this.execArgv ?? resolveExecArgv(this.workerPath),\n stdio: ['ignore', 'ignore', 'ignore', 'ipc'],\n detached: SPAWN_DETACHED,\n });\n const runtime: IChildProcessRuntime = {\n job,\n child,\n killGraceMs: this.killGraceMs,\n };\n const payload = this.createStartPayload(job);\n const workerResult = createChildProcessSubagentResult({\n runtime,\n payload,\n resolveTranscriptPath: (request) => this.resolveTranscriptPath(request),\n });\n const cancellation = createCancellationResult(job.jobId);\n void workerResult.catch(() => undefined);\n const result = Promise.race([workerResult, cancellation.promise]);\n // CORE-023: cancel() now awaits the SIGTERM→grace→SIGKILL escalation, so it settles later\n // than the synchronous cancellation.reject(). Guard `result` so its rejection is never\n // \"unhandled\" during that window; real consumers still await it and receive the rejection.\n void result.catch(() => undefined);\n const transcriptPath = this.resolveTranscriptPath(job);\n\n return {\n jobId: job.jobId,\n ...(child.pid !== undefined && { pid: child.pid }),\n ...(transcriptPath !== undefined && { transcriptPath, logPath: transcriptPath }),\n result,\n cancel: async (reason?: string) => {\n cancellation.reject(reason);\n await cancelChildProcess(runtime, reason);\n },\n send: async (prompt: string) => {\n await sendWorkerMessage(child, { type: 'send', prompt });\n },\n ...(transcriptPath !== undefined && {\n readLog: async (cursor?: IBackgroundTaskLogCursor) =>\n readTranscriptLog(job.jobId, transcriptPath, cursor),\n }),\n };\n }\n\n private createStartPayload(job: ISubagentJobStart): ISubagentWorkerStartPayload {\n const definition = resolveAgentDefinition(job.request.type, this.deps.customAgentRegistry);\n return {\n jobId: job.jobId,\n request: job.request,\n agentDefinition: applyRequestOverrides(definition, job),\n parentConfig: this.deps.config,\n parentContext: this.deps.context,\n providerProfile: createProviderProfile(this.providerConfig, this.deps, job),\n permissionMode: this.deps.permissionMode,\n ...(this.logsDir ? { logsDir: this.logsDir } : {}),\n };\n }\n\n private resolveTranscriptPath(job: ISubagentJobStart): string | undefined {\n if (!this.logsDir) return undefined;\n return join(this.logsDir, job.request.parentSessionId, 'subagents', `${job.jobId}.jsonl`);\n }\n}\n\nfunction resolveAgentDefinition(\n agentType: string,\n customRegistry?: (name: string) => IAgentDefinition | undefined,\n): IAgentDefinition {\n const definition = customRegistry?.(agentType) ?? getBuiltInAgent(agentType);\n if (!definition) {\n throw new BackgroundTaskError('validation', `Unknown agent type: ${agentType}`);\n }\n return definition;\n}\n\nfunction applyRequestOverrides(\n definition: IAgentDefinition,\n job: ISubagentJobStart,\n): IAgentDefinition {\n return {\n ...definition,\n ...(job.request.model ? { model: job.request.model } : {}),\n ...(job.request.allowedTools ? { tools: job.request.allowedTools } : {}),\n ...(job.request.disallowedTools ? { disallowedTools: job.request.disallowedTools } : {}),\n };\n}\n\nfunction createProviderProfile(\n providerConfig: IProviderDefinitionConfig | undefined,\n deps: IInProcessSubagentRunnerDeps,\n job: ISubagentJobStart,\n): ISerializableProviderProfile {\n const provider = providerConfig ?? deps.config.provider;\n return {\n profileName: deps.config.currentProvider,\n type: provider.name,\n model: job.request.model ?? provider.model,\n apiKey: provider.apiKey,\n baseURL: provider.baseURL,\n timeout: provider.timeout,\n options: provider.options,\n };\n}\n\nfunction resolveExecArgv(workerPath: string): string[] {\n if (!workerPath.endsWith('.ts')) {\n return process.execArgv;\n }\n if (process.execArgv.some((arg) => arg.includes('tsx'))) {\n return process.execArgv;\n }\n return [...process.execArgv, '--import', 'tsx'];\n}\n\nfunction readTranscriptLog(\n jobId: string,\n transcriptPath: string,\n cursor?: IBackgroundTaskLogCursor,\n): IBackgroundTaskLogPage {\n if (!existsSync(transcriptPath)) {\n return {\n taskId: jobId,\n cursor,\n lines: [],\n };\n }\n const lines = readFileSync(transcriptPath, 'utf8').split(/\\r?\\n/).filter(Boolean);\n return createBackgroundTaskLogPage(jobId, lines, cursor);\n}\n","import { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport function getDefaultSubagentWorkerPath(): string {\n return join(dirname(fileURLToPath(import.meta.url)), 'child-process-subagent-worker.js');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["isRecord","SPAWN_DETACHED"],"sources":["../../src/parent-config-projection.ts","../../src/parent-context-projection.ts","../../src/subagent-worker-start-dto.ts","../../src/child-process-subagent-projection.ts","../../src/child-process-subagent-ipc.ts","../../src/child-process-subagent-transport.ts","../../src/child-process-subagent-runner-result.ts","../../src/worker-entry.ts","../../src/child-process-subagent-runner.ts","../../src/child-process-subagent-resume.ts","../../src/worker-composition.ts","../../src/child-process-subagent-worker.ts"],"sourcesContent":["/**\n * ARCH-044 (issue #2047) — what the parent's config contributes to the child's wire payload.\n *\n * Its own module because \"which config members cross a process boundary\" is a different question\n * from \"how a child process is run\", and the runner answered both until this file existed.\n */\n\nimport type { ISubagentWorkerParentConfig } from './child-process-subagent-ipc.js';\nimport type { IInProcessSubagentRunnerDeps } from '@robota-sdk/agent-framework';\n\n/**\n * ARCH-044 (issue #2047): the config members the CHILD reads, and no others.\n *\n * `parentConfig` was the parent's whole `IResolvedConfig`, so the payload's shape was derived from a\n * runtime type and grew whenever that type grew. It carried the resolved `provider.apiKey` — two\n * lines above `createProviderProfile`, which SEC-009 hardened precisely to keep that secret off the\n * wire — and an `env` map, and **nothing in the child read either**. Measured: the child touches\n * `provider.model`, `permissions`, `defaultTrustLevel` and `hooks`, and no spread, `Object.keys` or\n * whole-object pass reaches the rest.\n *\n * Declared as an explicit shape rather than an `Omit` of the runtime type, so a new field on\n * `IResolvedConfig` does NOT reach the child by default — which is the whole of ARCH-044. It is\n * built key by key for the same reason: structural typing would accept the whole config where this\n * type is expected, so the type documents the intent and this function is what enforces it.\n */\nexport function projectParentConfig(\n config: IInProcessSubagentRunnerDeps['config'],\n): ISubagentWorkerParentConfig {\n return {\n provider: { model: config.provider.model },\n permissions: config.permissions,\n defaultTrustLevel: config.defaultTrustLevel,\n ...(config.hooks === undefined ? {} : { hooks: config.hooks }),\n };\n}\n","/**\n * Issue #2317 — what the parent's loaded context contributes to the child's wire payload.\n *\n * Beside `parent-config-projection.ts` for the same reason it exists: \"which context members cross\n * a process boundary\" is a different question from \"how a child process is run\".\n */\n\nimport type {\n IInProcessSubagentRunnerDeps,\n ISubagentParentContext,\n} from '@robota-sdk/agent-framework';\n\n/**\n * Issue #2317: the context members the CHILD reads, and no others.\n *\n * `parentContext` was the parent's whole `ILoadedContext`. Measured: the child reads `agentsMd` and\n * `projectNotesMd` (create-subagent-session.ts) and nothing else — no spread, `Object.keys` or\n * whole-object pass reaches the rest. The rest includes `agentsFileEntries` and\n * `projectNotesFileEntries`, each entry carrying the full `content` of a file the parent loaded, so\n * every AGENTS.md and CLAUDE.md was structurally cloned into every child process and read by nothing.\n *\n * Built key by key: structural typing would accept the whole context where the narrow type is\n * expected, so the type documents the intent and this function is what enforces it.\n */\nexport function projectParentContext(\n context: IInProcessSubagentRunnerDeps['context'],\n): ISubagentParentContext {\n return {\n agentsMd: context.agentsMd,\n projectNotesMd: context.projectNotesMd,\n };\n}\n","/**\n * ARCH-044 (issue #2047): the JSON-safe DTOs the child-process start payload carries for the agent\n * definition and the parent's loaded context — OWNED by this process boundary, not indexed out of\n * the in-process runtime types.\n *\n * `IAgentDefinition` and `ILoadedContext` are runtime models; reusing them as the wire shape coupled\n * process-protocol evolution to in-process model evolution and defined no IPC semantics for what a\n * field may hold. Here every field is declared, every field is projected by the encoder and decoded\n * by the decoder, and the serialization mode is plain JSON: strings, finite numbers, string arrays and\n * arrays of flat records. No `Date`, no `undefined` on the wire (an absent optional is simply absent),\n * no tagged representation because nothing here needs one.\n *\n * Field-coverage guard: `AGENT_DEFINITION_DTO_FIELDS` and `PARENT_CONTEXT_DTO_FIELDS` are typed as\n * `Record<keyof Dto, …>`, so adding a DTO field without naming it there — and therefore without the\n * encoder/decoder that the tables drive — is a compile error, not a silent gap.\n */\n\nimport { isModelEffort } from '@robota-sdk/agent-core';\n\nimport type {\n IAgentDefinition,\n IInProcessSubagentRunnerDeps,\n ISubagentParentContext,\n} from '@robota-sdk/agent-framework';\n\n/**\n * The parent's loaded-context RUNTIME model (`ILoadedContext`, which the framework barrel does not\n * export). Named here for the encoder/restore signatures only — the wire DTO below never references it.\n */\ntype TParentContextModel = IInProcessSubagentRunnerDeps['context'];\n\nexport interface ISubagentWorkerAgentDefinitionDto {\n readonly name: string;\n readonly description: string;\n readonly systemPrompt: string;\n readonly model?: string;\n readonly effort?: IAgentDefinition['effort'];\n readonly role?: string;\n readonly maxTurns?: number;\n readonly tools?: readonly string[];\n readonly disallowedTools?: readonly string[];\n}\n\nexport interface ISubagentWorkerContextFileEntryDto {\n readonly filePath: string;\n readonly content: string;\n readonly contentHash: string;\n}\n\nexport interface ISubagentWorkerParentContextDto {\n readonly agentsMd: string;\n readonly projectNotesMd: string;\n readonly memoryMd?: string;\n readonly taskContext?: string;\n readonly compactInstructions?: string;\n readonly agentsFileEntries?: readonly ISubagentWorkerContextFileEntryDto[];\n readonly projectNotesFileEntries?: readonly ISubagentWorkerContextFileEntryDto[];\n}\n\ntype TScalarKind = 'string' | 'number' | 'effort' | 'string[]' | 'file-entry[]';\ninterface IFieldRule {\n readonly kind: TScalarKind;\n readonly required: boolean;\n}\n\nexport const AGENT_DEFINITION_DTO_FIELDS: Record<\n keyof ISubagentWorkerAgentDefinitionDto,\n IFieldRule\n> = {\n name: { kind: 'string', required: true },\n description: { kind: 'string', required: true },\n systemPrompt: { kind: 'string', required: true },\n model: { kind: 'string', required: false },\n effort: { kind: 'effort', required: false },\n role: { kind: 'string', required: false },\n maxTurns: { kind: 'number', required: false },\n tools: { kind: 'string[]', required: false },\n disallowedTools: { kind: 'string[]', required: false },\n};\n\nexport const PARENT_CONTEXT_DTO_FIELDS: Record<keyof ISubagentWorkerParentContextDto, IFieldRule> =\n {\n agentsMd: { kind: 'string', required: true },\n projectNotesMd: { kind: 'string', required: true },\n memoryMd: { kind: 'string', required: false },\n taskContext: { kind: 'string', required: false },\n compactInstructions: { kind: 'string', required: false },\n agentsFileEntries: { kind: 'file-entry[]', required: false },\n projectNotesFileEntries: { kind: 'file-entry[]', required: false },\n };\n\nexport type TDtoDecodeResult<TDto> =\n { readonly ok: true; readonly value: TDto } | { readonly ok: false; readonly reason: string };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === 'string');\n}\n\nfunction isFileEntry(value: unknown): value is ISubagentWorkerContextFileEntryDto {\n return (\n isRecord(value) &&\n typeof value['filePath'] === 'string' &&\n typeof value['content'] === 'string' &&\n typeof value['contentHash'] === 'string'\n );\n}\n\nfunction valueMatches(kind: TScalarKind, value: unknown): boolean {\n switch (kind) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n return typeof value === 'number' && Number.isFinite(value);\n case 'effort':\n return typeof value === 'string' && isModelEffort(value);\n case 'string[]':\n return isStringArray(value);\n case 'file-entry[]':\n return Array.isArray(value) && value.every(isFileEntry);\n }\n}\n\n/** Project exactly the declared fields (undefined optionals dropped), driven by the field table. */\nfunction project<TDto extends object>(\n source: Record<string, unknown>,\n fields: Record<keyof TDto, IFieldRule>,\n): TDto {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(fields)) {\n const value = source[key];\n if (value !== undefined) out[key] = value;\n }\n return out as TDto;\n}\n\n/** Decode exactly the declared fields; arrays where a record is required, and stray types, fail. */\nfunction decode<TDto extends object>(\n label: string,\n value: unknown,\n fields: Record<keyof TDto, IFieldRule>,\n): TDtoDecodeResult<TDto> {\n if (!isRecord(value)) return { ok: false, reason: `${label}: expected an object` };\n for (const [key, rule] of Object.entries(fields) as [string, IFieldRule][]) {\n const field = value[key];\n if (field === undefined) {\n if (rule.required) return { ok: false, reason: `${label}.${key}: required` };\n continue;\n }\n if (!valueMatches(rule.kind, field)) {\n return { ok: false, reason: `${label}.${key}: expected ${rule.kind}` };\n }\n }\n return { ok: true, value: project<TDto>(value, fields) };\n}\n\nexport function encodeAgentDefinition(\n definition: IAgentDefinition,\n): ISubagentWorkerAgentDefinitionDto {\n return project<ISubagentWorkerAgentDefinitionDto>(\n definition as unknown as Record<string, unknown>,\n AGENT_DEFINITION_DTO_FIELDS,\n );\n}\n\nexport function decodeAgentDefinitionDto(\n value: unknown,\n): TDtoDecodeResult<ISubagentWorkerAgentDefinitionDto> {\n return decode<ISubagentWorkerAgentDefinitionDto>(\n 'agentDefinition',\n value,\n AGENT_DEFINITION_DTO_FIELDS,\n );\n}\n\n/** Explicit restore in the worker: the DTO's fields are the runtime model's, copied, not aliased. */\nexport function restoreAgentDefinition(dto: ISubagentWorkerAgentDefinitionDto): IAgentDefinition {\n const definition: IAgentDefinition = {\n name: dto.name,\n description: dto.description,\n systemPrompt: dto.systemPrompt,\n };\n if (dto.model !== undefined) definition.model = dto.model;\n if (dto.effort !== undefined) definition.effort = dto.effort;\n if (dto.role !== undefined) definition.role = dto.role;\n if (dto.maxTurns !== undefined) definition.maxTurns = dto.maxTurns;\n if (dto.tools !== undefined) definition.tools = [...dto.tools];\n if (dto.disallowedTools !== undefined) definition.disallowedTools = [...dto.disallowedTools];\n return definition;\n}\n\n/** Accepts the issue #2317 projection (or anything wider, structurally); only declared fields cross. */\nexport function encodeParentContext(\n context: ISubagentParentContext,\n): ISubagentWorkerParentContextDto {\n return project<ISubagentWorkerParentContextDto>(\n context as unknown as Record<string, unknown>,\n PARENT_CONTEXT_DTO_FIELDS,\n );\n}\n\nexport function decodeParentContextDto(\n value: unknown,\n): TDtoDecodeResult<ISubagentWorkerParentContextDto> {\n return decode<ISubagentWorkerParentContextDto>('parentContext', value, PARENT_CONTEXT_DTO_FIELDS);\n}\n\nexport function restoreParentContext(dto: ISubagentWorkerParentContextDto): TParentContextModel {\n const context: TParentContextModel = {\n agentsMd: dto.agentsMd,\n projectNotesMd: dto.projectNotesMd,\n };\n if (dto.memoryMd !== undefined) context.memoryMd = dto.memoryMd;\n if (dto.taskContext !== undefined) context.taskContext = dto.taskContext;\n if (dto.compactInstructions !== undefined) context.compactInstructions = dto.compactInstructions;\n if (dto.agentsFileEntries !== undefined) {\n context.agentsFileEntries = dto.agentsFileEntries.map((entry) => ({ ...entry }));\n }\n if (dto.projectNotesFileEntries !== undefined) {\n context.projectNotesFileEntries = dto.projectNotesFileEntries.map((entry) => ({ ...entry }));\n }\n return context;\n}\n","/**\n * What the PARENT projects onto a child-process subagent's start payload.\n *\n * A child rebuilds its own tool surface in another process, so anything the parent's SESSION decided\n * — which assembly tiers it carried, which sandbox it holds — has to travel as data. This module is\n * the producer half of that contract; `worker-composition.ts` is the consumer half.\n *\n * It exists as its own file because review of ARCH-033/ARCH-034 found both fields declared on the\n * wire type and read by the worker while NOTHING wrote them. Keeping the producer beside the\n * consumer's vocabulary, rather than inside a runner that is mostly about process lifecycle, is what\n * makes \"who writes this field\" answerable by looking.\n *\n * CLI-1994 moved the whole payload builder here from the runner's private method, for the same\n * reason: the ARCH-044 boundary — a fork job's conversation never crosses this wire — is a property\n * of THIS projection, and a test can only pin it against the code that actually produces the payload.\n */\n\nimport { findProviderDefinition } from '@robota-sdk/agent-core';\nimport {\n BackgroundTaskError,\n connectionEnvironmentNames,\n findConnectionEnvironmentDivergence,\n sealConnectionEnvironment,\n} from '@robota-sdk/agent-executor';\n\nimport { projectParentConfig } from './parent-config-projection.js';\nimport { projectParentContext } from './parent-context-projection.js';\nimport { encodeAgentDefinition, encodeParentContext } from './subagent-worker-start-dto.js';\n\nimport type { ISubagentWorkerStartPayload } from './child-process-subagent-ipc.js';\nimport type { ISandboxProjection } from './worker-composition.js';\nimport type { IProviderDefinition, IProviderDefinitionConfig } from '@robota-sdk/agent-core';\nimport type { IConnectionEnvironmentCheck, ISubagentJobStart } from '@robota-sdk/agent-executor';\nimport type { IAgentDefinition, IInProcessSubagentRunnerDeps } from '@robota-sdk/agent-framework';\nimport type { ISerializableProviderProfile } from '@robota-sdk/agent-interface-execution';\n\n/** The parent-decided fields of a start payload, ready to spread onto it. */\nexport interface IProjectedParentState {\n sessionTiers?: IInProcessSubagentRunnerDeps['sessionTiers'];\n sandboxProjection?: ISandboxProjection;\n}\n\n/**\n * ARCH-034: the parent's assembly tiers, verbatim.\n *\n * `{ includeGoalTool: false }` and \"the parent said nothing\" are DIFFERENT states and are kept\n * apart: folding them together would make the tier unreadable in exactly the case a product turns it\n * off deliberately.\n */\nfunction projectSessionTiers(\n deps: Pick<IInProcessSubagentRunnerDeps, 'sessionTiers'>,\n): Pick<IProjectedParentState, 'sessionTiers'> {\n return deps.sessionTiers === undefined ? {} : { sessionTiers: deps.sessionTiers };\n}\n\n/**\n * ARCH-033: `(type, snapshotId)` for the parent's sandbox, or nothing.\n *\n * BOTH halves are required to produce either. A snapshot with no registered type is a reference\n * nothing on the worker side opens; a type with no snapshot rebuilds an EMPTY sandbox, which is a\n * child that looks sandboxed while sharing none of the parent's state. Silence here is the honest\n * answer for a half-configured composition — `assertChildProcessSubagentsCanReproduce` is what\n * REFUSES it, at the composition root, where it can name the missing piece.\n *\n * A `snapshot()` that throws propagates: the alternative is a child that starts with no sandbox\n * after its parent was asked for one, which is the silent half-capability this exists to prevent.\n */\nasync function projectSandbox(\n deps: Pick<IInProcessSubagentRunnerDeps, 'sandboxClient' | 'sandboxType'>,\n): Promise<Pick<IProjectedParentState, 'sandboxProjection'>> {\n const { sandboxClient, sandboxType } = deps;\n if (sandboxClient?.snapshot === undefined || sandboxType === undefined) return {};\n return { sandboxProjection: { type: sandboxType, snapshotId: await sandboxClient.snapshot() } };\n}\n\n/** The runner-owned inputs the payload carries beside the parent's deps. */\nexport interface IStartPayloadOptions {\n readonly providerConfig?: IProviderDefinitionConfig;\n /** The parent's provider registry: its defaults and the environment each provider reads. */\n readonly providerDefinitions: readonly IProviderDefinition[];\n readonly logsDir?: string;\n /** The connection the runner already checked; projected here when absent. */\n readonly connection?: IProjectedConnection;\n}\n\n/** The provider connection a child is given, and the check it repeats before using it. */\nexport interface IProjectedConnection {\n readonly providerProfile: ISerializableProviderProfile;\n readonly connectionCheck: IConnectionEnvironmentCheck;\n}\n\n/**\n * The parent's provider connection, checked against the environment the child will run in.\n *\n * Throws, naming the variable and never its value, when the child's environment would decide the\n * destination or the credential differently from the parent's — before anything is spawned or sent,\n * so the credential never reaches a process that would use it elsewhere.\n */\nexport function projectProviderConnection(\n job: ISubagentJobStart,\n deps: IInProcessSubagentRunnerDeps,\n options: Pick<IStartPayloadOptions, 'providerConfig' | 'providerDefinitions'>,\n parentEnv: NodeJS.ProcessEnv,\n childEnv: NodeJS.ProcessEnv,\n): IProjectedConnection {\n const definitions = options.providerDefinitions;\n const type = (options.providerConfig ?? deps.config.provider).name;\n // Fail closed: without the provider's definition the parent can neither complete the connection\n // nor know which environment its client reads, and the child builds exactly what it is given.\n if (findProviderDefinition(definitions, type) === undefined) {\n throw new BackgroundTaskError(\n 'validation',\n `No provider definition for \"${type}\" was given to the subagent runner, so the connection a ` +\n 'child would make cannot be checked; the subagent was not started.',\n );\n }\n const providerProfile = createProviderProfile(options.providerConfig, deps, job, definitions);\n const names = connectionEnvironmentNames(providerProfile, definitions);\n const diverging = findConnectionEnvironmentDivergence(names, parentEnv, childEnv);\n if (diverging !== undefined) {\n throw new BackgroundTaskError(\n 'validation',\n `The subagent's environment sets ${diverging} differently from this session, which would ` +\n 'change where its provider connects or which credential it sends; the subagent was not started.',\n );\n }\n return { providerProfile, connectionCheck: sealConnectionEnvironment(names, childEnv) };\n}\n\n/**\n * The payload the child is started with — ASYNC, because projecting the parent's sandbox means\n * asking it for a snapshot and `snapshot()` returns a promise.\n *\n * NOT an `async` function, deliberately. `resolveAgentDefinition` throws for an unknown agent type,\n * and `start()` has always surfaced that SYNCHRONOUSLY — an `async` body would turn it into a\n * rejected result promise, which is a contract change no caller asked for and which the ARCH-036\n * cases caught immediately. Everything that can be known now is computed now; only the sandbox half\n * waits.\n *\n * CLI-1994 / ARCH-044: `request` crosses VERBATIM — for a fork job that means `resumeSessionId` and\n * nothing more. The conversation the id names stays in the session store on the far side; nothing\n * here reads it, so nothing here can put it on the wire. `subagent-worker-start-dto.test.ts` TC-06\n * pins the key set.\n */\nexport function projectStartPayload(\n job: ISubagentJobStart,\n deps: IInProcessSubagentRunnerDeps,\n options: IStartPayloadOptions,\n): Promise<ISubagentWorkerStartPayload> {\n const definition = resolveAgentDefinition(\n job.request.agentType,\n deps.customAgentRegistry,\n deps.builtInAgents,\n deps.agentDefinitions,\n );\n const base: ISubagentWorkerStartPayload = {\n taskId: job.taskId,\n request: job.request,\n ...(job.worktree ? { worktree: job.worktree } : {}),\n agentDefinition: encodeAgentDefinition(applyRequestOverrides(definition, job)),\n // Issue #3081: the rules the parent's gate enforces now, not its settings file's.\n parentConfig: projectParentConfig(\n deps.getParentPermissionRules === undefined\n ? deps.config\n : { ...deps.config, permissions: deps.getParentPermissionRules() },\n ),\n // Issue #2317 narrows to the two members the child reads; ARCH-044 (issue #2047) encodes them.\n parentContext: encodeParentContext(projectParentContext(deps.context)),\n ...(options.connection ??\n projectProviderConnection(job, deps, options, process.env, process.env)),\n permissionMode: deps.permissionMode,\n ...projectSessionTiers(deps),\n ...(options.logsDir ? { logsDir: options.logsDir } : {}),\n };\n return projectSandbox(deps).then((sandbox) => ({ ...base, ...sandbox }));\n}\n\n/**\n * ARCH-036: `builtInAgents` is threaded through because NEUT-003 made an injected set REPLACE the\n * module built-ins — an empty array removes them entirely — and the in-process sibling already\n * honours it (`agent-framework/src/subagents/in-process-subagent-runner.ts`). Reading only\n * `customAgentRegistry` here meant the composition root's choice reached one runner and not the\n * other, so selecting a runner for isolation silently also selected a capability.\n */\nfunction resolveAgentDefinition(\n agentType: string,\n customRegistry?: (name: string) => IAgentDefinition | undefined,\n builtInAgents?: readonly IAgentDefinition[],\n agentDefinitions?: readonly IAgentDefinition[],\n): IAgentDefinition {\n const definition =\n customRegistry?.(agentType) ??\n builtInAgents?.find((agent) => agent.name === agentType) ??\n agentDefinitions?.find((agent) => agent.name === agentType);\n if (!definition) {\n throw new BackgroundTaskError('validation', `Unknown agent type: ${agentType}`);\n }\n return definition;\n}\n\nfunction applyRequestOverrides(\n definition: IAgentDefinition,\n job: ISubagentJobStart,\n): IAgentDefinition {\n return {\n ...definition,\n ...(job.request.model ? { model: job.request.model } : {}),\n ...(job.request.effort !== undefined ? { effort: job.request.effort } : {}),\n ...(job.request.allowedTools ? { tools: job.request.allowedTools } : {}),\n ...(job.request.disallowedTools ? { disallowedTools: job.request.disallowedTools } : {}),\n };\n}\n\nconst ENV_REFERENCE_PREFIX = '$ENV:';\n\n/**\n * The credential the child resolves: the profile's own, or else its definition's default — sent as\n * the variable it names, so the child reads the same variable and the check compares it.\n */\nfunction projectCredential(\n provider: IProviderDefinitionConfig,\n defaultApiKey: string | undefined,\n): Pick<ISerializableProviderProfile, 'apiKey' | 'apiKeyEnv'> {\n if (provider.apiKeyEnv !== undefined) return { apiKeyEnv: provider.apiKeyEnv };\n if (provider.apiKey !== undefined) return { apiKey: provider.apiKey };\n if (defaultApiKey === undefined) return {};\n return defaultApiKey.startsWith(ENV_REFERENCE_PREFIX)\n ? { apiKeyEnv: defaultApiKey.slice(ENV_REFERENCE_PREFIX.length) }\n : { apiKey: defaultApiKey };\n}\n\nfunction createProviderProfile(\n providerConfig: IProviderDefinitionConfig | undefined,\n deps: IInProcessSubagentRunnerDeps,\n job: ISubagentJobStart,\n providerDefinitions: readonly IProviderDefinition[],\n): ISerializableProviderProfile {\n const provider = providerConfig ?? deps.config.provider;\n // The EFFECTIVE connection: the child builds it exactly, never filling a gap from its own\n // registry's defaults, so the parent's defaults are applied here.\n const defaults = findProviderDefinition(providerDefinitions, provider.name)?.defaults ?? {};\n const baseURL = provider.baseURL ?? defaults.baseURL;\n const options = provider.options ?? defaults.options;\n // SEC-009: carry the REFERENCE, not the secret. Config loading resolves a `$ENV:` value into the\n // credential itself, so copying `apiKey` here put plaintext into a structured-clone IPC message —\n // a second copy of the secret, in a second process, reachable by anything observing the channel.\n // The child already inherits this process's environment (`env:` at the spawn in the runner), and\n // `resolveProfileApiKey` already reads `apiKeyEnv`, so the reference resolves on the far side with\n // no new plumbing. When no reference was recorded the config genuinely holds a literal and the\n // literal is all there is to send.\n // allow-fallback: a profile storing a plaintext credential has no reference to carry; the\n // org policy `requireApiKeyFromEnv` is the documented way to forbid that storage form.\n const credential = projectCredential(provider, defaults.apiKey);\n return {\n // Named only when it names THIS connection. A runner-supplied config may come from a different\n // profile (`--provider`) than the settings' current one.\n ...(providerConfig === undefined && deps.config.currentProvider !== undefined\n ? { profileName: deps.config.currentProvider }\n : {}),\n type: provider.name,\n model: job.request.model ?? provider.model,\n ...credential,\n ...(baseURL !== undefined ? { baseURL } : {}),\n ...(provider.timeout !== undefined ? { timeout: provider.timeout } : {}),\n ...(options !== undefined ? { options } : {}),\n };\n}\n","import {\n decodeAgentDefinitionDto,\n decodeParentContextDto,\n type ISubagentWorkerAgentDefinitionDto,\n type ISubagentWorkerParentContextDto,\n} from './subagent-worker-start-dto.js';\n\nimport type { ISandboxProjection } from './worker-composition.js';\nimport type { ISessionUsageTotals, TPermissionMode, TToolArgs } from '@robota-sdk/agent-core';\nimport type { IConnectionEnvironmentCheck } from '@robota-sdk/agent-executor';\nimport type { IResolvedConfig } from '@robota-sdk/agent-framework';\nimport type {\n ISerializableProviderProfile,\n ISubagentSpawnRequest,\n} from '@robota-sdk/agent-interface-execution';\n\nexport type TSubagentWorkerWireValue = string | number | boolean | null | undefined | object;\n\ntype TSubagentWorkerWireRecord = Record<string, TSubagentWorkerWireValue>;\n\n/** ARCH-044: the four config members the child reads. See `projectParentConfig`. */\nexport interface ISubagentWorkerParentConfig {\n readonly provider: { readonly model: string };\n readonly permissions: IResolvedConfig['permissions'];\n readonly defaultTrustLevel: IResolvedConfig['defaultTrustLevel'];\n readonly hooks?: IResolvedConfig['hooks'];\n}\n\nexport interface ISubagentWorkerStartPayload {\n taskId: string;\n request: ISubagentSpawnRequest;\n /**\n * ARCH-031: the worktree the parent's runner prepared, carried across the fork so the child can\n * answer `subagentExecutionRoot` the same way the parent would. Runner-produced, so it rides beside\n * the request rather than on it.\n *\n * `branch` crosses the fork too, even though nothing reads it here yet: dropping it at the IPC\n * boundary would make the child's view of its own isolated run poorer than the parent's, for no\n * reason other than the absence of a present-day consumer.\n */\n worktree?: { readonly path: string; readonly branch?: string };\n /** ARCH-044 (issue #2047): a JSON-safe DTO owned here, projected from `IAgentDefinition` by the parent. */\n agentDefinition: ISubagentWorkerAgentDefinitionDto;\n /**\n * ARCH-044 (issue #2047): the config members the child reads, declared here rather than indexed\n * out of the runtime type.\n *\n * It was `IInProcessSubagentRunnerDeps['config']`, so the wire shape was the in-process shape and\n * grew with it — which put the parent's resolved `provider.apiKey` and its `env` map into a second\n * process where nothing read either. Declaring the members means a new field on `IResolvedConfig`\n * does not reach the child by default; `projectParentConfig` is what enforces it at runtime,\n * because structural typing would accept the whole config here.\n */\n parentConfig: ISubagentWorkerParentConfig;\n /**\n * ARCH-044 (issue #2047): a JSON-safe DTO owned here, decoded totally on the child side. The parent\n * fills it from `projectParentContext` (issue #2317): the two context members the child reads —\n * `agentsMd` and `projectNotesMd` — and never the parent's whole `ILoadedContext`, whose file\n * entries carry the full text of every AGENTS.md and CLAUDE.md the parent loaded.\n */\n parentContext: ISubagentWorkerParentContextDto;\n providerProfile: ISerializableProviderProfile;\n /**\n * The destination-deciding environment the parent checked before spawning, sealed so the child can\n * repeat the check before it builds a provider. Values never travel; only a keyed digest does.\n */\n connectionCheck: IConnectionEnvironmentCheck;\n /**\n * ARCH-033: how the child rebuilds the parent's sandbox, as `(type, snapshotId)`.\n *\n * The live client cannot cross a process boundary — it is an open session against a remote machine.\n * This pair can: the type selects a factory the composition root registered, and the snapshot is a\n * provider-owned reference the parent's `snapshot()` returned. Both halves are required, because a\n * snapshot with no registry is a reference nothing opens and a registry with no snapshot rebuilds\n * an EMPTY sandbox — a child that looks sandboxed while sharing none of the parent's state.\n *\n * Absent ⇒ the parent holds no sandbox, which is every product that has not registered one.\n */\n sandboxProjection?: ISandboxProjection;\n /**\n * ARCH-034: which session-assembly tiers the parent's surface carried.\n *\n * A property of the parent's SESSION rather than of the child's root, so it rides on the payload\n * beside the request instead of being derived at the child. Absent ⇒ the parent had none.\n */\n sessionTiers?: { readonly includeGoalTool?: boolean };\n permissionMode?: TPermissionMode;\n logsDir?: string;\n}\n\nexport interface ISubagentWorkerStartMessage {\n type: 'start';\n payload: ISubagentWorkerStartPayload;\n}\n\nexport interface ISubagentWorkerSendMessage {\n type: 'send';\n prompt: string;\n}\n\nexport interface ISubagentWorkerCancelMessage {\n type: 'cancel';\n reason?: string;\n}\n\nexport type TSubagentWorkerParentMessage =\n ISubagentWorkerStartMessage | ISubagentWorkerSendMessage | ISubagentWorkerCancelMessage;\n\nexport interface ISubagentWorkerReadyMessage {\n type: 'ready';\n /**\n * ARCH-021: the tool names the child actually composed, so \"the child has the product's surface\"\n * is VERIFIED per run rather than assumed by construction. Names only — the tools themselves are\n * code and do not cross this boundary; that is the whole point of the composition port.\n *\n * Enumerated at the worker's own cwd before any job arrives, which is sound because a pack's tool\n * NAMES do not depend on the root (the root binds the path guard, not the name set).\n */\n composedToolNames?: readonly string[];\n}\n\nexport interface ISubagentWorkerTextDeltaMessage {\n type: 'text_delta';\n delta: string;\n}\n\nexport interface ISubagentWorkerToolStartMessage {\n type: 'tool_start';\n toolName: string;\n toolArgs?: TToolArgs;\n}\n\nexport interface ISubagentWorkerToolEndMessage {\n type: 'tool_end';\n toolName: string;\n success: boolean;\n}\n\nexport interface ISubagentWorkerResultMessage {\n type: 'result';\n output: string;\n /** ANALYTICS-001 (Phase 2): total token usage of the subagent run, forwarded to the parent. */\n usage?: ISessionUsageTotals;\n}\n\nexport interface ISubagentWorkerErrorMessage {\n type: 'error';\n message: string;\n}\n\nexport interface ISubagentWorkerCancelledMessage {\n type: 'cancelled';\n reason?: string;\n}\n\nexport type TSubagentWorkerChildMessage =\n | ISubagentWorkerReadyMessage\n | ISubagentWorkerTextDeltaMessage\n | ISubagentWorkerToolStartMessage\n | ISubagentWorkerToolEndMessage\n | ISubagentWorkerResultMessage\n | ISubagentWorkerErrorMessage\n | ISubagentWorkerCancelledMessage;\n\nfunction isRecord(value: TSubagentWorkerWireValue): value is TSubagentWorkerWireRecord {\n return typeof value === 'object' && value !== null;\n}\n\n/** A sealed connection check: names, a nonce and a digest, all strings. */\nfunction isConnectionCheck(value: TSubagentWorkerWireValue): boolean {\n if (!isRecord(value)) return false;\n if (!hasString(value, 'nonce') || !hasString(value, 'digest')) return false;\n const names = value.names;\n return Array.isArray(names) && names.every((name) => typeof name === 'string');\n}\n\n/**\n * ARCH-031: `key` is `string`, so a renamed contract field compiles clean here and then rejects every\n * payload at runtime — which is exactly what a `type` → `agentType` rename would have done, silently.\n * The typed overloads below make the next rename a compile error instead.\n */\nfunction hasString(value: TSubagentWorkerWireRecord, key: string): boolean {\n return typeof value[key] === 'string';\n}\n\n/** Assert a key that must exist on the spawn request, so a contract rename is compiler-found. */\nfunction hasRequestString(\n value: TSubagentWorkerWireRecord,\n key: keyof ISubagentSpawnRequest & string,\n): boolean {\n return hasString(value, key);\n}\n\n/** Assert a key that must exist on the worker start payload, for the same reason. */\nfunction hasPayloadString(\n value: TSubagentWorkerWireRecord,\n key: keyof ISubagentWorkerStartPayload & string,\n): boolean {\n return hasString(value, key);\n}\n\n/** An OPTIONAL request key: absent is valid, present must be a string — typed against the request. */\nfunction hasOptionalRequestString(\n value: TSubagentWorkerWireRecord,\n key: keyof ISubagentSpawnRequest & string,\n): boolean {\n return value[key] === undefined || typeof value[key] === 'string';\n}\n\n/**\n * CORE-024 (RUNTIME-47): validate the optional `usage` payload on a `result` message so a\n * malformed object cannot be spread verbatim into the parent's token/cost accounting. Absent is\n * valid (usage is optional); present must be an `ISessionUsageTotals` with three numeric fields.\n */\nfunction hasValidOptionalUsage(value: TSubagentWorkerWireRecord): boolean {\n if (value.usage === undefined) return true;\n const usage = value.usage;\n if (!isRecord(usage)) return false;\n return (\n typeof usage.promptTokens === 'number' &&\n typeof usage.completionTokens === 'number' &&\n typeof usage.totalTokens === 'number'\n );\n}\n\n/**\n * ARCH-021: validate the optional parity declaration for the same reason CORE-024 (RUNTIME-47) added\n * `hasValidOptionalUsage` beside it — the guard asserts a typed shape, so an unvalidated optional\n * field hands a consumer a `readonly string[]` type over whatever the wire carried. Absent is valid;\n * present must be an array of strings.\n */\nfunction hasValidOptionalComposedToolNames(value: TSubagentWorkerWireRecord): boolean {\n if (value.composedToolNames === undefined) return true;\n const names = value.composedToolNames;\n if (!Array.isArray(names)) return false;\n return names.every((name) => typeof name === 'string');\n}\n\nfunction isStartPayload(value: TSubagentWorkerWireValue): value is ISubagentWorkerStartPayload {\n if (!isRecord(value)) return false;\n if (!hasPayloadString(value, 'taskId')) return false;\n if (!isRecord(value.request)) return false;\n if (!hasRequestString(value.request, 'agentType')) return false;\n if (!hasRequestString(value.request, 'prompt')) return false;\n // ARCH-031: required at the spawn boundary, so the guard asserts it too. Without this a payload\n // missing the policy passes, and the worker's conditional spread then silently omits it — which is\n // how CORE-025 lost this exact field once already.\n if (!hasRequestString(value.request, 'permissionPolicy')) return false;\n // ARCH-010/ARCH-031: `cwd` is the fallback carrier of the execution root — with no `worktree` on\n // the payload, `subagentExecutionRoot` returns it verbatim. A payload without it gives the child's\n // tools `undefined` as their containment root, which is the breach this rule exists to prevent.\n if (!hasRequestString(value.request, 'cwd')) return false;\n // CLI-1994: a fork job names the record it resumes — an id, never the conversation. A non-string\n // here is a payload that put something else where the id goes, and the worker must not guess.\n if (!hasOptionalRequestString(value.request, 'resumeSessionId')) return false;\n // …and `worktree.path` is the HIGHER-precedence carrier — `worktree?.path ?? request.cwd` — so\n // validating `cwd` alone leaves the winning branch unchecked. Before ARCH-031 the runner rewrote\n // `request.cwd` to the worktree, so one check covered both; now it does not.\n if (value.worktree !== undefined) {\n if (!isRecord(value.worktree)) return false;\n if (!hasString(value.worktree, 'path')) return false;\n }\n // ARCH-044 (issue #2047): both DTOs are decoded totally — every declared field, arrays rejected\n // where a record is required — instead of being accepted as any `object`.\n if (!decodeAgentDefinitionDto(value.agentDefinition).ok) return false;\n if (!isRecord(value.parentConfig)) return false;\n if (!decodeParentContextDto(value.parentContext).ok) return false;\n if (!isRecord(value.providerProfile)) return false;\n if (!hasString(value.providerProfile, 'type')) return false;\n if (!hasString(value.providerProfile, 'model')) return false;\n return isConnectionCheck(value.connectionCheck);\n}\n\nexport function isSubagentWorkerParentMessage(\n value: TSubagentWorkerWireValue,\n): value is TSubagentWorkerParentMessage {\n if (!isRecord(value) || !hasString(value, 'type')) return false;\n switch (value.type) {\n case 'start':\n return isStartPayload(value.payload);\n case 'send':\n return hasString(value, 'prompt');\n case 'cancel':\n return value.reason === undefined || typeof value.reason === 'string';\n default:\n return false;\n }\n}\n\nexport function isSubagentWorkerChildMessage(\n value: TSubagentWorkerWireValue,\n): value is TSubagentWorkerChildMessage {\n if (!isRecord(value) || !hasString(value, 'type')) return false;\n switch (value.type) {\n case 'ready':\n return hasValidOptionalComposedToolNames(value);\n case 'text_delta':\n return hasString(value, 'delta');\n case 'tool_start':\n return hasString(value, 'toolName');\n case 'tool_end':\n return hasString(value, 'toolName') && typeof value.success === 'boolean';\n case 'result':\n return hasString(value, 'output') && hasValidOptionalUsage(value);\n case 'error':\n return hasString(value, 'message');\n case 'cancelled':\n return value.reason === undefined || typeof value.reason === 'string';\n default:\n return false;\n }\n}\n","import { createBoundedOutput } from '@robota-sdk/agent-core';\nimport {\n BackgroundTaskError,\n type ISubagentJobStart,\n type TBackgroundTaskRunnerEvent,\n} from '@robota-sdk/agent-executor';\nimport { killProcessTree } from '@robota-sdk/agent-process';\n\n/** POSIX children are forked detached so a process-group kill reaps grandchildren (CORE-023). */\nconst SPAWN_DETACHED = process.platform !== 'win32';\n\n/** Resolve when the child exits or after `ms` — lets the graceful IPC cancel land before signalling. */\nfunction waitForExitOrTimeout(child: ChildProcess, ms: number): Promise<void> {\n return new Promise<void>((resolve) => {\n if (child.exitCode !== null || child.signalCode !== null) {\n resolve();\n return;\n }\n const timer = setTimeout(() => {\n child.removeListener('exit', onExit);\n resolve();\n }, ms);\n timer.unref?.();\n const onExit = (): void => {\n clearTimeout(timer);\n resolve();\n };\n child.once('exit', onExit);\n });\n}\n\nimport type {\n ISubagentWorkerResultMessage,\n TSubagentWorkerChildMessage,\n TSubagentWorkerParentMessage,\n} from './child-process-subagent-ipc.js';\nimport type { IBoundedOutput, TToolArgs } from '@robota-sdk/agent-core';\nimport type { ChildProcess } from 'node:child_process';\n\nexport interface IChildProcessRuntime {\n job: ISubagentJobStart;\n child: ChildProcess;\n killGraceMs: number;\n}\n\n/**\n * DIST-006: the tail of the child's stderr, kept so a death before the first IPC message can say\n * WHY. Bounded — a runaway child must not be able to grow the parent's memory through this.\n */\nconst STDERR_TAIL_LIMIT = 4096;\n// ARCH-056: the shared bounded-output contract in tail mode, not a locally rediscovered slice.\nconst stderrTails = new WeakMap<ChildProcess, IBoundedOutput>();\n\n/** Attach a bounded stderr reader. Without it a failed start reports only an exit code. */\nexport function captureChildStderr(child: ChildProcess): void {\n const stream = child.stderr;\n if (!stream) return;\n const tail = createBoundedOutput({\n maxBytes: STDERR_TAIL_LIMIT,\n retain: 'tail',\n truncationMarker: () => '',\n });\n stderrTails.set(child, tail);\n // A stream error here is not the subagent's result; without a listener it would reach the\n // parent's `uncaughtException` handler.\n stream.on('error', () => {});\n stream.on('data', (chunk: Buffer) => tail.append(chunk));\n}\n\n/** What the child wrote to stderr, trimmed; empty when it wrote nothing. */\nexport function readChildStderrTail(child: ChildProcess): string {\n return (stderrTails.get(child)?.toString() ?? '').trim();\n}\n\nexport function handleWorkerMessage(\n message: TSubagentWorkerChildMessage,\n startWorker: () => void,\n resolveOnce: (result: ISubagentWorkerResultMessage) => void,\n rejectOnce: (error: Error) => void,\n emit?: (event: TBackgroundTaskRunnerEvent) => void,\n): void {\n switch (message.type) {\n case 'ready':\n startWorker();\n break;\n case 'result':\n resolveOnce(message);\n break;\n case 'error':\n rejectOnce(new BackgroundTaskError('runner', message.message));\n break;\n case 'cancelled':\n rejectOnce(new BackgroundTaskError('runner', message.reason ?? 'Subagent worker cancelled'));\n break;\n case 'text_delta':\n emit?.({ type: 'background_task_text_delta', delta: message.delta });\n break;\n case 'tool_start':\n emit?.({\n type: 'background_task_tool_start',\n toolName: message.toolName,\n firstArg: extractFirstArg(message.toolArgs),\n });\n break;\n case 'tool_end':\n emit?.({\n type: 'background_task_tool_end',\n toolName: message.toolName,\n success: message.success,\n });\n break;\n default:\n rejectOnce(new BackgroundTaskError('runner', 'Unhandled subagent worker message'));\n }\n}\n\nfunction extractFirstArg(toolArgs?: TToolArgs): string | undefined {\n if (!toolArgs) return undefined;\n const firstValue = Object.values(toolArgs)[0];\n if (firstValue === undefined) return undefined;\n return typeof firstValue === 'object' ? JSON.stringify(firstValue) : String(firstValue);\n}\n\nexport function sendWorkerMessage(\n child: ChildProcess,\n message: TSubagentWorkerParentMessage,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!child.connected) {\n reject(new BackgroundTaskError('crash', 'Subagent worker IPC channel is closed'));\n return;\n }\n child.send(message, (error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n}\n\nexport async function cancelChildProcess(\n runtime: IChildProcessRuntime,\n reason?: string,\n): Promise<void> {\n // CORE-023: graceful IPC cancel first (preKill), then SIGTERM→grace→SIGKILL over the process\n // group — the previous path signalled SIGTERM only and never escalated, so a worker ignoring\n // SIGTERM survived forever.\n await killProcessTree(runtime.child, {\n graceMs: runtime.killGraceMs,\n processGroup: SPAWN_DETACHED,\n preKill: async () => {\n if (!runtime.child.connected) return;\n await sendWorkerMessage(runtime.child, { type: 'cancel', reason }).catch(() => undefined);\n // Give the worker the grace window to shut down cleanly on the IPC cancel before signalling.\n await waitForExitOrTimeout(runtime.child, runtime.killGraceMs);\n },\n });\n}\n","import { BackgroundTaskError, type ISubagentJobStart } from '@robota-sdk/agent-executor';\n\nimport {\n isSubagentWorkerChildMessage,\n type ISubagentWorkerResultMessage,\n type ISubagentWorkerStartPayload,\n type TSubagentWorkerWireValue,\n} from './child-process-subagent-ipc.js';\nimport {\n cancelChildProcess,\n handleWorkerMessage,\n readChildStderrTail,\n sendWorkerMessage,\n type IChildProcessRuntime,\n} from './child-process-subagent-transport.js';\n\nimport type { ISubagentJobResult } from '@robota-sdk/agent-interface-execution';\n\n/**\n * DIST-006: how long a spawned worker may take to say anything at all. Generous — it covers process\n * start plus module load of a bundled CLI — but finite, because the alternative is a silent hang.\n */\nconst DEFAULT_HANDSHAKE_BUDGET_MS = 30_000;\n\nexport interface ICancellationResult {\n promise: Promise<ISubagentJobResult>;\n reject(reason?: string): void;\n}\n\nexport interface IChildProcessSubagentResultOptions {\n runtime: IChildProcessRuntime;\n /** How long the worker may take to say anything. Injectable so a test can reach this branch. */\n handshakeBudgetMs?: number;\n /**\n * ARCH-033: a PROMISE, because part of the payload cannot be known synchronously — projecting the\n * parent's sandbox means asking it for a snapshot, and `snapshot()` is async. `start()` must stay\n * synchronous (it returns a handle the caller cancels), so the await happens HERE, between the\n * child saying `ready` and the parent sending `start`, which is the one point where waiting costs\n * nothing that was not already being waited for.\n */\n payload: Promise<ISubagentWorkerStartPayload>;\n resolveTranscriptPath: (job: ISubagentJobStart) => string | undefined;\n}\n\nexport function createChildProcessSubagentResult(\n options: IChildProcessSubagentResultOptions,\n): Promise<ISubagentJobResult> {\n return new Promise<ISubagentJobResult>((resolve, reject) => {\n new ChildProcessSubagentResultController(options, resolve, reject).start();\n });\n}\n\nclass ChildProcessSubagentResultController {\n private settled = false;\n private started = false;\n private ready = false;\n private readonly timeoutTimer?: ReturnType<typeof setTimeout>;\n private readonly handshakeTimer: ReturnType<typeof setTimeout>;\n private readonly handshakeBudgetMs: number;\n /** The start payload, or `undefined` when building it failed and the job was already rejected. */\n private readonly payload: Promise<ISubagentWorkerStartPayload | undefined>;\n\n constructor(\n private readonly options: IChildProcessSubagentResultOptions,\n private readonly resolve: (result: ISubagentJobResult) => void,\n private readonly reject: (error: Error) => void,\n ) {\n // `?? DEFAULT` would turn `0` — the plausible spelling of \"no deadline\" — into a timer that\n // rejects every job on the next tick. A non-positive budget is not a way to opt out.\n const budget = options.handshakeBudgetMs;\n this.handshakeBudgetMs =\n budget !== undefined && budget > 0 ? budget : DEFAULT_HANDSHAKE_BUDGET_MS;\n // ARCH-033: the payload's failure handler is attached HERE, not in `startWorker`, which may be\n // several ticks away — the child has to say `ready` first. A payload that cannot be BUILT (a\n // sandbox whose `snapshot()` throws) fails the job either way, but without this the rejection\n // surfaces as an unhandled one before anything consumed it. `undefined` means \"already\n // reported\": there is nothing left to send, and sending a partial payload would start a child\n // that looks sandboxed while sharing none of the parent's state.\n this.payload = options.payload.catch((error) => {\n this.rejectOnce(error instanceof Error ? error : new Error(String(error)));\n return undefined;\n });\n this.timeoutTimer = createTimeoutTimer(this.options.runtime, (error) => this.rejectOnce(error));\n // DIST-006: a worker that never answers must not hang the parent forever. The old seam failed\n // LOUDLY when the entry was wrong (`Cannot find module`, then exit); this one re-executes the\n // host artifact, so a caller who wires `workerEntry` to something that is not a robota entry\n // gets a second copy of their app with an IPC channel and no `ready` — and `request.timeoutMs`\n // is optional, so without this the wait is unbounded. Occurrence #3 self-reports either way.\n this.handshakeTimer = setTimeout(() => {\n if (this.ready || this.settled) return;\n void cancelChildProcess(this.options.runtime, 'Subagent worker never signalled ready');\n this.rejectOnce(\n new BackgroundTaskError(\n 'runner',\n `Subagent worker never signalled ready within ${this.handshakeBudgetMs}ms. ` +\n 'Its entry must dispatch worker mode before starting the host application.',\n ),\n );\n }, this.handshakeBudgetMs);\n this.handshakeTimer.unref?.();\n }\n\n start(): void {\n const { child } = this.options.runtime;\n child.on('message', this.onMessage);\n child.on('error', this.onError);\n child.on('exit', this.onExit);\n child.once('spawn', () => {\n setImmediate(this.startWorker);\n });\n }\n\n private readonly startWorker = (): void => {\n if (this.started) return;\n this.started = true;\n const { child } = this.options.runtime;\n void this.payload\n .then((payload) =>\n payload === undefined ? undefined : sendWorkerMessage(child, { type: 'start', payload }),\n )\n .catch((error) => {\n this.rejectOnce(error instanceof Error ? error : new Error(String(error)));\n });\n };\n\n private readonly onMessage = (message: TSubagentWorkerWireValue): void => {\n if (!isSubagentWorkerChildMessage(message)) {\n this.rejectOnce(\n new BackgroundTaskError('runner', 'Received malformed subagent worker message'),\n );\n return;\n }\n // Any well-formed child message proves the entry reached worker mode.\n this.ready = true;\n clearTimeout(this.handshakeTimer);\n const { job } = this.options.runtime;\n handleWorkerMessage(message, this.startWorker, this.resolveOnce, this.rejectOnce, job.emit);\n };\n\n private readonly onError = (error: Error): void => {\n this.rejectOnce(new BackgroundTaskError('crash', error.message));\n };\n\n private readonly onExit = (code: number | null, signal: NodeJS.Signals | null): void => {\n if (this.settled) return;\n // DIST-006: read the tail here, at `'exit'`. Review proposed deferring to `'close'` on the\n // theory that the pipe has not drained yet; measured, that is not the mechanism. When a child\n // calls `process.exit()` its pending pipe writes are TRUNCATED, so the last line is never\n // written at all — lost 10/10 at `'close'` just as at `'exit'`. When a child ends naturally the\n // tail is already complete — present 40/40 at `'exit'` across 1k–400k lines. Waiting buys\n // nothing in either case, and a wait whose stated reason is false is worse than no wait.\n this.rejectOnce(\n new BackgroundTaskError(\n 'crash',\n formatEarlyExitMessage(code, signal, readChildStderrTail(this.options.runtime.child)),\n ),\n );\n };\n\n private readonly resolveOnce = (result: ISubagentWorkerResultMessage): void => {\n if (this.settled) return;\n this.settled = true;\n this.clearTimers();\n this.cleanup();\n const { runtime, resolveTranscriptPath } = this.options;\n this.resolve(toSubagentResult(runtime.job, result, resolveTranscriptPath));\n };\n\n private readonly rejectOnce = (error: Error): void => {\n if (this.settled) return;\n this.settled = true;\n this.clearTimers();\n this.cleanup();\n this.reject(error);\n };\n\n private clearTimers(): void {\n if (this.timeoutTimer) clearTimeout(this.timeoutTimer);\n clearTimeout(this.handshakeTimer);\n }\n\n private cleanup(): void {\n const { child } = this.options.runtime;\n child.off('message', this.onMessage);\n child.off('error', this.onError);\n child.off('exit', this.onExit);\n }\n}\n\nexport function createCancellationResult(taskId: string): ICancellationResult {\n let settled = false;\n let rejectFn: (error: Error) => void = () => {};\n const promise = new Promise<ISubagentJobResult>((_resolve, reject) => {\n rejectFn = reject;\n });\n return {\n promise,\n reject(reason?: string): void {\n if (settled) return;\n settled = true;\n rejectFn(new BackgroundTaskError('runner', reason ?? `Subagent job cancelled: ${taskId}`));\n },\n };\n}\n\nfunction createTimeoutTimer(\n runtime: IChildProcessRuntime,\n rejectOnce: (error: Error) => void,\n): ReturnType<typeof setTimeout> | undefined {\n if (!runtime.job.request.timeoutMs) return undefined;\n return setTimeout(() => {\n void cancelChildProcess(runtime, 'Subagent worker timed out');\n rejectOnce(new BackgroundTaskError('timeout', 'Subagent worker timed out'));\n }, runtime.job.request.timeoutMs);\n}\n\nfunction toSubagentResult(\n job: ISubagentJobStart,\n result: ISubagentWorkerResultMessage,\n resolveTranscriptPath: (job: ISubagentJobStart) => string | undefined,\n): ISubagentJobResult {\n const transcriptPath = resolveTranscriptPath(job);\n return {\n taskId: job.taskId,\n output: result.output,\n ...(transcriptPath ? { metadata: { transcriptPath, logPath: transcriptPath } } : {}),\n // ANALYTICS-001 (Phase 2): carry the subagent's forwarded token usage so the background-task\n // tracker can attribute it to this agent as a source in the parent log.\n ...(result.usage ? { usage: result.usage } : {}),\n };\n}\n\nfunction formatEarlyExitMessage(\n code: number | null,\n signal: NodeJS.Signals | null,\n stderrTail: string,\n): string {\n const detail =\n signal !== null ? `signal ${signal}` : `exit code ${code === null ? 'unknown' : code}`;\n // DIST-006: the exit code alone said nothing, so the previous occurrence of this defect had to be\n // diagnosed by hand. The child's own words are what make the next one self-reporting.\n const cause = stderrTail.length > 0 ? `: ${stderrTail}` : '';\n return `Subagent worker exited before result: ${detail}${cause}`;\n}\n","/**\n * DIST-006: how a subagent worker process is STARTED, stated by the composition root.\n *\n * The seam this replaces asked a library \"where is my worker file on disk?\" — a question it cannot\n * answer, because the answer is a property of the packaging step, not of the library. It was wrong\n * twice for the same reason: once when the worker had no bundle entry at all, and again when a\n * downstream bundler inlined this package into another artifact and moved the resolver's notion of\n * \"next to me\" one package along.\n *\n * The only party that knows how a process is packaged is that process. So the composition root\n * states how to start a copy of itself, and this package owns nothing but the IPC contract.\n */\n\n/**\n * The argv flag that puts a composition root's own entry into subagent-worker mode.\n *\n * Deliberately not a plausible user flag: it is part of an internal process contract, and a user\n * who types it gets a loud refusal rather than a half-started worker.\n */\nexport const SUBAGENT_WORKER_MODE_FLAG = '--__robota-subagent-worker';\n\n/**\n * How to spawn a copy of the running artifact in subagent-worker mode.\n *\n * `execPath` + `args` is the whole contract, and it is satisfiable by every artifact shape:\n * - a bundled Node build names the file it is currently executing;\n * - a `tsx` source run names the same thing and adds `--import tsx` to `execArgv`;\n * - a single-file compiled binary names NOTHING — `process.execPath` is the binary, and\n * re-executing it re-enters its embedded entry.\n */\nexport interface ISubagentWorkerEntry {\n /** The executable to run. `process.execPath` for every artifact this repository ships. */\n readonly execPath: string;\n /** Arguments before the worker-mode flag — the entry module, or nothing when it is embedded. */\n readonly args: readonly string[];\n /** Extra runtime flags, e.g. `--import tsx` when the entry is TypeScript source. */\n readonly execArgv?: readonly string[];\n}\n\n/** True when this process was started as a subagent worker. */\nexport function isSubagentWorkerModeArgv(argv: readonly string[]): boolean {\n return argv.includes(SUBAGENT_WORKER_MODE_FLAG);\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n createBackgroundTaskLogPage,\n createWorktreeSubagentRunner,\n subagentExecutionRoot,\n type ISubagentJobHandle,\n type ISubagentJobStart,\n type ISubagentRunner,\n type ISubagentWorktreeAdapter,\n} from '@robota-sdk/agent-executor';\nimport { DEFAULT_KILL_GRACE_MS } from '@robota-sdk/agent-process';\n\nimport {\n projectProviderConnection,\n projectStartPayload,\n type IProjectedConnection,\n} from './child-process-subagent-projection.js';\nimport {\n createCancellationResult,\n createChildProcessSubagentResult,\n} from './child-process-subagent-runner-result.js';\nimport {\n cancelChildProcess,\n captureChildStderr,\n sendWorkerMessage,\n type IChildProcessRuntime,\n} from './child-process-subagent-transport.js';\nimport { SUBAGENT_WORKER_MODE_FLAG, type ISubagentWorkerEntry } from './worker-entry.js';\n\nimport type { ISubagentWorkerStartPayload } from './child-process-subagent-ipc.js';\nimport type { IProviderDefinition, IProviderDefinitionConfig } from '@robota-sdk/agent-core';\nimport type {\n IInProcessSubagentRunnerDeps,\n TSubagentRunnerFactory,\n} from '@robota-sdk/agent-framework';\nimport type {\n IBackgroundTaskLogCursor,\n IBackgroundTaskLogPage,\n} from '@robota-sdk/agent-interface-execution';\n\n/** POSIX children are forked detached so a process-group kill reaps grandchildren (CORE-023). */\nconst SPAWN_DETACHED = process.platform !== 'win32';\n\nexport interface IChildProcessSubagentRunnerOptions {\n /**\n * DIST-006: how to start a copy of the running artifact in subagent-worker mode, stated by the\n * composition root. It replaced `workerPath`, which asked this package to locate a file whose\n * location is a property of the packaging step — a question no library can answer, and one that\n * was answered wrongly twice.\n */\n workerEntry: ISubagentWorkerEntry;\n providerConfig?: IProviderDefinitionConfig;\n /**\n * The parent's provider registry. Its defaults complete the connection the child is given, and\n * each definition names the environment its client reads. Required: a job whose provider has no\n * definition here is refused, because its connection cannot be checked.\n */\n providerDefinitions: readonly IProviderDefinition[];\n killGraceMs?: number;\n /**\n * How long a spawned worker may take to signal `ready` before the runner gives up. Injectable so\n * the branch is reachable in a test; without that it is a fix that ships untested.\n */\n handshakeBudgetMs?: number;\n env?: NodeJS.ProcessEnv;\n worktreeIsolation?: boolean;\n worktreeAdapter: ISubagentWorktreeAdapter;\n logsDir?: string;\n}\n\nexport function createChildProcessSubagentRunnerFactory(\n options: IChildProcessSubagentRunnerOptions,\n): TSubagentRunnerFactory {\n return (deps) => {\n const runner = new ChildProcessSubagentRunner(deps, options);\n if (options.worktreeIsolation === false) return runner;\n return createWorktreeSubagentRunner({\n runner,\n worktreeAdapter: options.worktreeAdapter,\n hooks: deps.config.hooks,\n hookTypeExecutors: deps.hookTypeExecutors,\n });\n };\n}\n\nexport class ChildProcessSubagentRunner implements ISubagentRunner {\n private readonly workerEntry: ISubagentWorkerEntry;\n private readonly killGraceMs: number;\n private readonly handshakeBudgetMs?: number;\n private readonly providerConfig?: IProviderDefinitionConfig;\n private readonly providerDefinitions: readonly IProviderDefinition[];\n private readonly env?: NodeJS.ProcessEnv;\n private readonly logsDir?: string;\n\n constructor(\n private readonly deps: IInProcessSubagentRunnerDeps,\n options: IChildProcessSubagentRunnerOptions,\n ) {\n this.workerEntry = options.workerEntry;\n this.killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS;\n this.handshakeBudgetMs = options.handshakeBudgetMs;\n this.providerConfig = options.providerConfig;\n this.providerDefinitions = options.providerDefinitions;\n this.env = options.env;\n this.logsDir = options.logsDir;\n }\n\n start(job: ISubagentJobStart): ISubagentJobHandle {\n // DIST-006: `spawn` rather than `fork` — `fork` is `spawn(process.execPath, [module, …])` with\n // an ipc stdio, and the module is exactly the thing that cannot be named for every artifact.\n // Stating execPath and args outright is the same mechanism without the assumption.\n const entry = this.workerEntry;\n const env = { ...process.env, ...(this.env ?? {}) };\n // Checked BEFORE spawning: a child whose environment would point the provider elsewhere never\n // starts, so the parent's credential is never handed to it.\n const connection = projectProviderConnection(\n job,\n this.deps,\n {\n ...(this.providerConfig !== undefined ? { providerConfig: this.providerConfig } : {}),\n providerDefinitions: this.providerDefinitions,\n },\n process.env,\n env,\n );\n const child = spawn(\n entry.execPath,\n [...(entry.execArgv ?? []), ...entry.args, SUBAGENT_WORKER_MODE_FLAG],\n {\n // ARCH-010/ARCH-031: the forked process's OS working directory answers the same question as\n // the session's execution root, so it reads the same rule. Reading `request.cwd` directly was\n // only ever correct while the worktree runner rewrote that field — this is the second carrier\n // that removal would have left disagreeing with the first.\n cwd: subagentExecutionRoot(job),\n env,\n // DIST-006: stderr was `'ignore'`, so a child that died before its first IPC message\n // reported only `exit code 1`. That is why this defect's second occurrence had to be\n // diagnosed by hand — the cause was written to a stream nothing was reading.\n stdio: ['ignore', 'ignore', 'pipe', 'ipc'],\n detached: SPAWN_DETACHED,\n },\n );\n captureChildStderr(child);\n const runtime: IChildProcessRuntime = {\n job,\n child,\n killGraceMs: this.killGraceMs,\n };\n const payload = this.createStartPayload(job, connection);\n const workerResult = createChildProcessSubagentResult({\n runtime,\n payload,\n ...(this.handshakeBudgetMs !== undefined\n ? { handshakeBudgetMs: this.handshakeBudgetMs }\n : {}),\n resolveTranscriptPath: (request) => this.resolveTranscriptPath(request),\n });\n const cancellation = createCancellationResult(job.taskId);\n void workerResult.catch(() => undefined);\n const result = Promise.race([workerResult, cancellation.promise]);\n // CORE-023: cancel() now awaits the SIGTERM→grace→SIGKILL escalation, so it settles later\n // than the synchronous cancellation.reject(). Guard `result` so its rejection is never\n // \"unhandled\" during that window; real consumers still await it and receive the rejection.\n void result.catch(() => undefined);\n const transcriptPath = this.resolveTranscriptPath(job);\n\n return {\n taskId: job.taskId,\n ...(child.pid !== undefined && { pid: child.pid }),\n ...(transcriptPath !== undefined && { transcriptPath, logPath: transcriptPath }),\n result,\n cancel: async (reason?: string) => {\n cancellation.reject(reason);\n await cancelChildProcess(runtime, reason);\n },\n send: async (prompt: string) => {\n await sendWorkerMessage(child, { type: 'send', prompt });\n },\n ...(transcriptPath !== undefined && {\n readLog: async (cursor?: IBackgroundTaskLogCursor) =>\n readTranscriptLog(job.taskId, transcriptPath, cursor),\n }),\n };\n }\n\n /**\n * The payload the child is started with. The builder lives in\n * `child-process-subagent-projection.ts` (CLI-1994 moved it there so the ARCH-044 key-set test\n * pins the code that produces it); review of ARCH-033/ARCH-034 is the reason it is a named\n * producer at all — both fields were declared on the wire type, read by the worker, and set by\n * nothing, because this was the only production site that constructs a payload and no test\n * reached it.\n */\n private createStartPayload(\n job: ISubagentJobStart,\n connection: IProjectedConnection,\n ): Promise<ISubagentWorkerStartPayload> {\n return projectStartPayload(job, this.deps, {\n connection,\n providerDefinitions: this.providerDefinitions,\n ...(this.logsDir !== undefined ? { logsDir: this.logsDir } : {}),\n });\n }\n\n private resolveTranscriptPath(job: ISubagentJobStart): string | undefined {\n if (!this.logsDir) return undefined;\n return join(this.logsDir, job.request.parentSessionId, 'subagents', `${job.taskId}.jsonl`);\n }\n}\n\nfunction readTranscriptLog(\n taskId: string,\n transcriptPath: string,\n cursor?: IBackgroundTaskLogCursor,\n): IBackgroundTaskLogPage {\n if (!existsSync(transcriptPath)) {\n return {\n taskId,\n cursor,\n lines: [],\n };\n }\n const lines = readFileSync(transcriptPath, 'utf8').split(/\\r?\\n/).filter(Boolean);\n return createBackgroundTaskLogPage(taskId, lines, cursor);\n}\n","/**\n * CLI-1994: how a fork job's conversation reaches the CHILD process.\n *\n * Only the id crossed the wire (ARCH-044) — the copy `/fork` wrote stays in the session store on the\n * far side, and this is where the id becomes the conversation again. Beside the worker rather than\n * inside it for the same reason the start-payload projection sits beside the wire vocabulary: \"how a\n * fork job resumes its record\" is a different question from \"how a worker process runs a job\", and\n * the worker is already at the anti-monolith limit.\n *\n * The store is opened for the PARENT's cwd (`request.cwd`), not the execution root: a\n * worktree-isolated child runs in a directory that holds no session records of its own.\n */\n\nimport { restoreSessionRecordIntoSession } from '@robota-sdk/agent-framework';\n\nimport type { ISubagentWorkerStartPayload } from './child-process-subagent-ipc.js';\nimport type { ISubagentWorkerComposition, TResumeSessionStore } from './worker-composition.js';\nimport type { createSubagentSession } from '@robota-sdk/agent-framework';\n\n/**\n * Restore the record a job names into the freshly built child, before its first turn.\n *\n * A job that names no record returns immediately — the ordinary subagent, unchanged. A composition\n * that opens no store FAILS the job, naming the seam to register: starting the child empty would be\n * a fork that silently forgot its parent, and a caller has no way to detect that afterwards.\n */\nexport function resumeRequestedRecord(\n payload: ISubagentWorkerStartPayload,\n childSession: ReturnType<typeof createSubagentSession>,\n resumeSessionStore: TResumeSessionStore | undefined,\n): void {\n const resumeSessionId = payload.request.resumeSessionId;\n if (resumeSessionId === undefined) return;\n if (resumeSessionStore === undefined) {\n throw new Error(\n `subagent worker: job ${payload.taskId} asks to resume session ${resumeSessionId}, but this ` +\n 'composition opens no session store. Register ISubagentWorkerComposition.openSessionStore ' +\n 'at the composition root — the same place providerDefinitions is registered.',\n );\n }\n restoreSessionRecordIntoSession(resumeSessionStore, resumeSessionId, childSession);\n}\n\n/** Open the store once so the resumed child can both restore and persist its copied record. */\nexport function openResumeSessionStore(\n payload: ISubagentWorkerStartPayload,\n composition: ISubagentWorkerComposition,\n): TResumeSessionStore | undefined {\n const resumeSessionId = payload.request.resumeSessionId;\n if (resumeSessionId === undefined) return undefined;\n if (composition.openSessionStore === undefined) {\n throw new Error(\n `subagent worker: job ${payload.taskId} asks to resume session ${resumeSessionId}, but this ` +\n 'composition opens no session store. Register ISubagentWorkerComposition.openSessionStore ' +\n 'at the composition root — the same place providerDefinitions is registered.',\n );\n }\n return composition.openSessionStore({ cwd: payload.request.cwd });\n}\n","import type {\n IHookTypeExecutor,\n IProviderDefinition,\n IToolWithEventService,\n} from '@robota-sdk/agent-core';\nimport type { restoreSessionRecordIntoSession } from '@robota-sdk/agent-framework';\n\n/**\n * The session record store a fork job's `resumeSessionId` names a record in, typed FROM the one\n * function that reads it (`restoreSessionRecordIntoSession`, agent-framework) rather than from the\n * interface package that declares the port — this package does not depend on that package, and a\n * type derived from the consumer cannot drift from what the consumer accepts.\n */\nexport type TResumeSessionStore = Parameters<typeof restoreSessionRecordIntoSession>[0];\n\n/**\n * ARCH-021: what the product composes, stated by the composition root.\n *\n * This is the sibling of {@link ISubagentWorkerEntry} one level up. That seam answers \"how is this\n * artifact started\"; this one answers \"what does this product compose\" — and the same rule decides\n * both: **the only party that knows is the product itself.**\n *\n * The seam this replaces had a neutral package importing `createDefaultTools()` and\n * `createDefaultProviderDefinitions()` and building the child's surface from them, while the\n * composition root had already handed the runner the fully composed surface. So a product's custom\n * providers and pack-owned tools reached an in-process subagent and not a child-process one, and\n * ARCH-006's invariant — every tool robota runs comes from a pack — was false in the child.\n *\n * **Why a recipe rather than the instances.** A composition cannot be projected across a process\n * boundary, because it is code: `createProvider` is a function and a tool carries `execute`. The two\n * structurally sound answers are to proxy the instances or to stop expressing the contract as\n * instances. Proxying loses on containment — a proxied tool executes in the PARENT, bound to the\n * parent's checkout, while a worktree-isolated child's execution root is a different directory. So\n * the recipe crosses and the child builds an equivalent surface at its own root, which is what every\n * comparable product does.\n */\nexport interface ISubagentWorkerComposition {\n /** Product-selected hook executors for the child session. */\n createHookTypeExecutors?: () => IHookTypeExecutor[];\n /**\n * The product's tool surface for THIS subagent's execution root.\n *\n * `cwd` is a required argument for the same reason `ICreateDefaultToolsOptions.cwd` is (ARCH-010):\n * a tool set built without its root carries a disarmed path guard, and the measured consequence\n * was a subagent `Read` returning `/etc/hostname`. Passing the root through the call rather than\n * capturing it in the factory is what stops a child from inheriting the parent's.\n */\n createTools(context: {\n readonly cwd: string;\n /**\n * ARCH-034: the tiers session assembly adds ON TOP of the product's tool set.\n *\n * The two runners of `ISubagentRunner` were handing a subagent different surfaces, and the\n * difference was silent because both paths succeed. In-process passes the parent's fully\n * ASSEMBLED tools; this path rebuilds the product's set at the child's root. For a product whose\n * packs own the tool surface those agree — but what session assembly adds AFTER the packs did\n * not cross: the goal tool (`includeGoalTool`) and edit-checkpoint wrapping.\n *\n * Choosing a runner is an isolation and packaging decision. It is not supposed to be a capability\n * decision, so the composition root states which of those tiers the child should also receive and\n * the recipe carries the answer rather than the parent's live wrappers.\n */\n readonly sessionTiers?: {\n /** Whether the parent's session included the goal-status tool. */\n readonly includeGoalTool?: boolean;\n };\n /**\n * ARCH-033: the sandbox the child restored, when the parent projected one.\n *\n * Threaded rather than captured, for the same reason `cwd` is: a tool surface built without the\n * sandbox it is supposed to act in would run on the HOST while the parent runs sandboxed, which\n * is the divergence the composition root's refusal exists to prevent. Absent ⇒ no sandbox, and\n * the child's tools act on its own confined root.\n */\n readonly sandboxClient?: TProjectedSandboxClient;\n }): IToolWithEventService[];\n\n /**\n * The product's provider registry. Carried as definitions rather than a constructed provider\n * because `createProvider` is code — the child builds its own provider from the serialized profile\n * against THIS registry, so a custom provider type resolves instead of throwing `Unknown provider`.\n */\n readonly providerDefinitions: readonly IProviderDefinition[];\n\n /**\n * How the child rebuilds a SANDBOX that the parent is running in (ARCH-033).\n *\n * The same shape as `providerDefinitions`, and for the same reason. A live `ISandboxClient` is an\n * open session against a remote machine; it cannot cross a process boundary. What CAN cross is the\n * pair (which client type, which snapshot) — `ISandboxClient.snapshot()` returns a\n * provider-owned reference and `restore(id)` hydrates a fresh client from it, and a reference is\n * just a string.\n *\n * So the composition root registers the constructor by type name, exactly as it registers provider\n * definitions, and the recipe carries `{ type, snapshotId }`. The child looks the type up here and\n * restores. Neither half works alone: a snapshot with no registry is a reference nothing can open,\n * and a registry with no snapshot rebuilds an EMPTY sandbox, which is worse than refusing because\n * the child would look sandboxed while sharing none of the parent's state.\n *\n * Absent ⇒ the product composes no sandbox, and `assertChildProcessSubagentsCanReproduce` in the\n * composition root refuses to start a sandboxed parent that cannot project. That refusal remains\n * the correct behaviour for a product that has not registered a factory; this seam is what lets one\n * stop refusing.\n */\n readonly sandboxFactories?: Readonly<Record<string, TSandboxClientFactory>>;\n\n /**\n * CLI-1994: how the child opens the session store a fork job's record was written to.\n *\n * The same shape as `providerDefinitions` and `sandboxFactories`, and for the same reason: where a\n * product keeps its session records is the composition root's knowledge, and it cannot be\n * projected onto the wire without also projecting the records — which is exactly what ARCH-044\n * keeps off it. So the parent sends the id, and the child asks the composition to open the store\n * FOR THE PARENT'S cwd (`request.cwd`, not the execution root — a worktree-isolated child runs in\n * a directory that has no session records of its own).\n *\n * Absent ⇒ the product composes no store, and a job that names a record to resume fails, stated\n * as such, rather than starting with an empty conversation that looks like a fork.\n */\n readonly openSessionStore?: (context: { readonly cwd: string }) => TResumeSessionStore;\n}\n\n/**\n * Rebuilds a sandbox client of ONE type from a snapshot reference the parent produced.\n *\n * Deliberately not `() => ISandboxClient`: a factory that cannot receive the reference can only make\n * an empty sandbox, which is the failure mode this seam exists to avoid.\n */\nexport type TSandboxClientFactory = (snapshotId: string) => Promise<TProjectedSandboxClient>;\n\n/**\n * What the factory hands back, expressed STRUCTURALLY rather than as `ISandboxClient`.\n *\n * This package is the neutral runner: it depends on `agent-core`, `agent-executor`,\n * `agent-framework`, `agent-interface-execution` and `agent-process` — deliberately not on\n * `agent-tools`, where `ISandboxClient` lives. Importing that type to describe a value this package\n * only ever passes through would add a dependency edge for a pass-through, which is the shape\n * ARCH-021 removed from here on the provider axis.\n *\n * So the seam names the minimum it needs to be honest about — the object is opaque to the runner and\n * meaningful only to the composition root that registered the factory and the tools that receive it.\n */\nexport type TProjectedSandboxClient = object;\n\n/**\n * The serializable half — what the parent puts in the recipe.\n *\n * Both fields are required. `type` selects the factory; `snapshotId` is what the parent's\n * `snapshot()` returned. Carrying one without the other is the empty-sandbox failure above.\n */\nexport interface ISandboxProjection {\n readonly type: string;\n readonly snapshotId: string;\n}\n\n/**\n * Resolve a projection against the composition's registry, or explain precisely why it cannot be.\n *\n * Returns the client rather than throwing on absence, because the CALLER decides what an\n * unprojectable sandbox means: the composition root refuses to start, while a child that reaches\n * this with no projection simply has no sandbox and runs host tools at its own confined root.\n */\nexport async function restoreProjectedSandbox(\n projection: ISandboxProjection | undefined,\n factories: Readonly<Record<string, TSandboxClientFactory>> | undefined,\n): Promise<TProjectedSandboxClient | undefined> {\n if (projection === undefined) return undefined;\n const factory = factories?.[projection.type];\n if (factory === undefined) {\n // Fail loudly rather than silently running unsandboxed. A child that was TOLD to be sandboxed and\n // quietly was not is ARCH-010's shape — the measured breach there was a subagent reading outside\n // its root — so an unregistered type must stop the job, not degrade it.\n throw new Error(\n `subagent worker: sandbox type \"${projection.type}\" is not registered in the worker composition. ` +\n `The parent is sandboxed and passed a snapshot reference, but this child cannot construct that ` +\n `client type. Register it in ISubagentWorkerComposition.sandboxFactories at the composition ` +\n `root — the same place providerDefinitions is registered, and for the same reason.`,\n );\n }\n return factory(projection.snapshotId);\n}\n","import { sumHistoryUsage } from '@robota-sdk/agent-core';\nimport {\n createProviderFromExactProfile,\n subagentExecutionRoot,\n verifyConnectionEnvironment,\n} from '@robota-sdk/agent-executor';\nimport { createSubagentLogger, createSubagentSession } from '@robota-sdk/agent-framework';\n\nimport {\n isSubagentWorkerParentMessage,\n type ISubagentWorkerStartPayload,\n type TSubagentWorkerChildMessage,\n type TSubagentWorkerWireValue,\n} from './child-process-subagent-ipc.js';\nimport { openResumeSessionStore, resumeRequestedRecord } from './child-process-subagent-resume.js';\nimport { restoreAgentDefinition, restoreParentContext } from './subagent-worker-start-dto.js';\nimport { restoreProjectedSandbox } from './worker-composition.js';\n\nimport type { ISubagentWorkerComposition } from './worker-composition.js';\nimport type { ITerminalOutput } from '@robota-sdk/agent-core';\n\nconst CANCEL_EXIT_CODE = 130;\n/** DIST-006: worker mode reached without an IPC channel — a misuse, not a run that failed. */\nconst WORKER_MISUSE_EXIT_CODE = 2;\n/** Force-exit fallback if the IPC flush callback never fires (broken channel). */\nconst FLUSH_EXIT_FALLBACK_MS = 2000;\n\nconst NOOP_TERMINAL: ITerminalOutput = {\n write: (): void => {},\n writeLine: (): void => {},\n writeMarkdown: (): void => {},\n writeError: (): void => {},\n prompt: (): Promise<string> => Promise.resolve(''),\n select: (): Promise<number> => Promise.resolve(0),\n spinner: () => ({ stop: (): void => {}, update: (): void => {} }),\n};\n\ntype TSubagentSessionToolEvent = Parameters<\n NonNullable<Parameters<typeof createSubagentSession>[0]['onToolExecution']>\n>[0];\n\nlet session: ReturnType<typeof createSubagentSession> | null = null;\nlet cancelled = false;\nlet running: Promise<void> = Promise.resolve();\n\nfunction sendChildMessage(message: TSubagentWorkerChildMessage): void {\n if (process.send) {\n process.send(message);\n }\n}\n\n/**\n * CORE-024 (RUNTIME-20): send the terminal message and exit ONLY after the IPC write has drained.\n * `process.send` is asynchronous; exiting from a `finally` before the write flushes made the\n * parent's `onExit` fire before the `result` arrived — a successful run was misreported as a crash\n * and its `usage` payload was lost. Exit from the flush callback; a fallback timer guards a broken\n * channel so the worker never hangs.\n */\nfunction sendTerminalMessageAndExit(message: TSubagentWorkerChildMessage, exitCode: number): void {\n let exited = false;\n const exitOnce = (): void => {\n if (exited) return;\n exited = true;\n process.exit(exitCode);\n };\n if (process.send) {\n const fallback = setTimeout(exitOnce, FLUSH_EXIT_FALLBACK_MS);\n fallback.unref?.();\n process.send(message, undefined, undefined, () => {\n clearTimeout(fallback);\n exitOnce();\n });\n } else {\n exitOnce();\n }\n}\n\n/** Best-effort total token usage of the finished subagent session; never throws. */\nfunction readSessionUsage(\n finishedSession: ReturnType<typeof createSubagentSession>,\n): ReturnType<typeof sumHistoryUsage> {\n try {\n return sumHistoryUsage(finishedSession.getFullHistory());\n } catch {\n // allow-fallback: usage capture is auxiliary — history read failure must not fail the subagent run\n return undefined;\n }\n}\n\nasync function runInitialPrompt(\n payload: ISubagentWorkerStartPayload,\n composition: ISubagentWorkerComposition,\n): Promise<void> {\n try {\n // The parent compared this environment before spawning; repeat it before anything is built, so\n // a change between spawn and start cannot send the parent's credential elsewhere.\n if (!verifyConnectionEnvironment(payload.connectionCheck, process.env)) {\n throw new Error(\n 'The provider connection environment changed after the parent checked it; the subagent was not started.',\n );\n }\n // ARCH-021: the PRODUCT's registry, not an imported six-vendor default. A custom provider type\n // used to throw `Unknown provider` here while the parent ran on it perfectly well.\n const restoredSandbox = await restoreProjectedSandbox(\n payload.sandboxProjection,\n composition.sandboxFactories,\n );\n // Exactly as the parent resolved it: no defaults from this process's own registry.\n const provider = createProviderFromExactProfile(\n payload.providerProfile,\n payload.request.model,\n composition.providerDefinitions,\n );\n const sessionLogger = payload.logsDir\n ? createSubagentLogger(payload.request.parentSessionId, payload.taskId, payload.logsDir)\n : undefined;\n const resumeSessionStore = openResumeSessionStore(payload, composition);\n session = createSubagentSession({\n // ARCH-044 (issue #2047): explicit restore from the wire DTOs into the runtime models.\n agentDefinition: restoreAgentDefinition(payload.agentDefinition),\n parentConfig: payload.parentConfig,\n parentContext: restoreParentContext(payload.parentContext),\n // ARCH-010: the spawn request already carries the root; this call simply never passed it, so\n // every tool the child built was unconfined. Same reader as the session root below — the tools\n // and the session being told DIFFERENT roots is the same class of defect as neither being told\n // one.\n // ARCH-021: the product's tool surface, built at THIS child's execution root. Previously\n // `createDefaultTools(...)`, which meant dropping a pack did not drop its tools from a\n // child-process subagent — ARCH-006's invariant was true in the parent and false here.\n // ARCH-033: restore the parent's sandbox before building tools, so the child acts where the\n // parent acts. An unregistered type throws here rather than degrading to host tools — a child\n // that was told it is sandboxed and quietly is not is ARCH-010's shape.\n // ARCH-034: the session tiers cross too, so the runner choice stays a packaging decision\n // rather than a capability one. `includeGoalTool` rides on the payload because it is a property\n // of the PARENT'S session, not of this child's root.\n parentTools: composition.createTools({\n cwd: subagentExecutionRoot(payload),\n ...(restoredSandbox !== undefined ? { sandboxClient: restoredSandbox } : {}),\n ...(payload.sessionTiers !== undefined ? { sessionTiers: payload.sessionTiers } : {}),\n }),\n cwd: subagentExecutionRoot(payload),\n provider,\n terminal: NOOP_TERMINAL,\n sessionId: payload.request.resumeSessionId ?? payload.taskId,\n ...(resumeSessionStore !== undefined ? { sessionStore: resumeSessionStore } : {}),\n ...(sessionLogger ? { sessionLogger } : {}),\n permissionMode: payload.permissionMode,\n // CORE-025: enforce the task's permission policy in the child-process subagent too.\n ...(payload.request.permissionPolicy !== undefined\n ? { permissionPolicy: payload.request.permissionPolicy }\n : {}),\n ...(payload.request.allowedTools !== undefined\n ? { taskAllowedTools: payload.request.allowedTools }\n : {}),\n ...(payload.request.disallowedTools !== undefined\n ? { taskDisallowedTools: payload.request.disallowedTools }\n : {}),\n hooks: payload.parentConfig.hooks,\n ...(composition.createHookTypeExecutors !== undefined\n ? { hookTypeExecutors: composition.createHookTypeExecutors() }\n : {}),\n onTextDelta: (delta) => sendChildMessage({ type: 'text_delta', delta }),\n onToolExecution: forwardToolExecution,\n });\n resumeRequestedRecord(payload, session, resumeSessionStore);\n const output = await session.run(payload.request.prompt);\n if (cancelled) {\n sendTerminalMessageAndExit(\n { type: 'cancelled', reason: 'Subagent worker cancelled' },\n CANCEL_EXIT_CODE,\n );\n return;\n }\n // ANALYTICS-001 (Phase 2): forward the subagent's total token usage so the parent log can\n // attribute it to this agent as a source. Best-effort — usage capture must never fail the run.\n const usage = readSessionUsage(session);\n // CORE-024 (RUNTIME-20): exit only after this result (with usage) has flushed over IPC, so the\n // parent settles on the result instead of racing a crash-projection from an early exit.\n sendTerminalMessageAndExit({ type: 'result', output, ...(usage ? { usage } : {}) }, 0);\n } catch (error) {\n // allow-fallback: child process must report errors to parent via IPC, not crash silently; exit follows the IPC flush (CORE-024 RUNTIME-20)\n if (cancelled) {\n sendTerminalMessageAndExit(\n { type: 'cancelled', reason: 'Subagent worker cancelled' },\n CANCEL_EXIT_CODE,\n );\n return;\n }\n const message = error instanceof Error ? error.message : String(error);\n sendTerminalMessageAndExit({ type: 'error', message }, 0);\n }\n}\n\nfunction forwardToolExecution(event: TSubagentSessionToolEvent): void {\n if (event.type === 'start') {\n sendChildMessage({ type: 'tool_start', toolName: event.toolName, toolArgs: event.toolArgs });\n return;\n }\n sendChildMessage({ type: 'tool_end', toolName: event.toolName, success: event.success ?? true });\n}\n\nfunction runFollowUp(prompt: string): void {\n if (session === null) {\n sendChildMessage({ type: 'error', message: 'Subagent worker has not started' });\n return;\n }\n running = running.then(async () => {\n try {\n // allow-fallback: child process must report errors to parent via IPC, not crash silently\n await session?.run(prompt);\n } catch (error) {\n // allow-fallback: child process must report errors to parent via IPC, not crash silently\n const message = error instanceof Error ? error.message : String(error);\n sendChildMessage({ type: 'error', message });\n }\n });\n}\n\nasync function cancelWorker(reason?: string): Promise<void> {\n cancelled = true;\n session?.abort();\n sendChildMessage({ type: 'cancelled', reason });\n await session?.shutdown({ reason: 'other' }).catch(() => undefined); // allow-fallback: shutdown during cancel — process will exit regardless\n setTimeout(() => process.exit(CANCEL_EXIT_CODE), 0);\n}\n\n/**\n * The names the composition yields at this process's own cwd. Failure to enumerate must not stop the\n * worker — the declaration is verification, not the run itself — but it must not be silent either.\n */\nfunction composedToolNames(composition: ISubagentWorkerComposition): readonly string[] | undefined {\n try {\n return composition.createTools({ cwd: process.cwd() }).map((tool) => tool.getName());\n } catch (error) {\n // allow-fallback: a parity declaration must never take the subagent down with it. But it must\n // not lie either — `[]` would read as \"this product composed no tools\", which is the one\n // confusion a parity channel cannot afford. `undefined` says \"could not enumerate\".\n process.stderr.write(\n `robota: could not enumerate the composed tool surface: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n return undefined;\n }\n}\n\n/**\n * DIST-006: worker mode is ENTERED, not implied by loading this module.\n *\n * These handlers used to run as module top-level side effects, which is what forced the worker to\n * be a separate file that something had to locate on disk. As a function, the composition root's\n * own entry can become the worker — so there is no second artifact and no path to get wrong.\n *\n * ARCH-021: `composition` is REQUIRED, deliberately. An optional parameter falling back to imported\n * defaults would reinstate the exact defect this seam removes — and at this line conventions have a\n * measured failure rate of 100% (ARCH-010 and ARCH-006 are both findings here).\n */\nexport function runSubagentWorkerMain(composition: ISubagentWorkerComposition): void {\n if (process.send === undefined) {\n // \"Silence is not success\": a worker without an IPC channel can never report anything, so it\n // must fail where someone can see it rather than sit there looking started.\n process.stderr.write(\n 'robota: subagent worker mode requires an IPC channel; it is started by the agent runtime, not by hand.\\n',\n );\n process.exit(WORKER_MISUSE_EXIT_CODE);\n }\n\n process.on('message', (message: TSubagentWorkerWireValue) => {\n if (!isSubagentWorkerParentMessage(message)) {\n sendChildMessage({ type: 'error', message: 'Malformed subagent worker parent message' });\n return;\n }\n\n switch (message.type) {\n case 'start':\n running = running.then(() => runInitialPrompt(message.payload, composition));\n break;\n case 'send':\n runFollowUp(message.prompt);\n break;\n case 'cancel':\n void cancelWorker(message.reason);\n break;\n default:\n sendChildMessage({ type: 'error', message: 'Unhandled subagent worker parent message' });\n }\n });\n\n process.on('disconnect', () => {\n cancelled = true;\n session?.abort();\n void session?.shutdown({ reason: 'other' }).catch(() => undefined); // allow-fallback: cleanup on disconnect — process will exit regardless\n });\n\n // ARCH-021: declare what this child composed. This is a VERIFICATION channel — the built-binary\n // test and the port-level test read it; no production consumer does, and saying otherwise would\n // stop the next reader asking whether this public wire field has one. It turns \"equivalent by\n // construction\" into \"verified per run\" at a seam where two findings have already landed.\n const names = composedToolNames(composition);\n sendChildMessage({\n type: 'ready',\n ...(names ? { composedToolNames: names } : {}),\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robota-sdk/agent-subagent-runner",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.81",
|
|
4
4
|
"description": "Child-process subagent runner for Robota SDK — optional package for running subagents in isolated child processes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/node/index.js",
|
|
7
7
|
"types": "dist/node/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"types": "./dist/node/index.d.ts",
|
|
11
10
|
"source": "./src/index.ts",
|
|
12
11
|
"node": {
|
|
13
|
-
"import":
|
|
14
|
-
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/node/index.d.ts",
|
|
14
|
+
"default": "./dist/node/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/node/index.d.cts",
|
|
18
|
+
"default": "./dist/node/index.cjs"
|
|
19
|
+
}
|
|
15
20
|
},
|
|
16
21
|
"default": {
|
|
17
|
-
"import":
|
|
18
|
-
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/node/index.d.ts",
|
|
24
|
+
"default": "./dist/node/index.js"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./dist/node/index.d.cts",
|
|
28
|
+
"default": "./dist/node/index.cjs"
|
|
29
|
+
}
|
|
19
30
|
}
|
|
20
31
|
}
|
|
21
32
|
},
|
|
@@ -31,31 +42,33 @@
|
|
|
31
42
|
"dist"
|
|
32
43
|
],
|
|
33
44
|
"dependencies": {
|
|
34
|
-
"@robota-sdk/agent-core": "3.0.0-beta.
|
|
35
|
-
"@robota-sdk/agent-executor": "3.0.0-beta.
|
|
36
|
-
"@robota-sdk/agent-
|
|
37
|
-
"@robota-sdk/agent-
|
|
38
|
-
"@robota-sdk/agent-process": "3.0.0-beta.
|
|
39
|
-
"@robota-sdk/agent-provider": "3.0.0-beta.79"
|
|
45
|
+
"@robota-sdk/agent-core": "3.0.0-beta.81",
|
|
46
|
+
"@robota-sdk/agent-executor": "3.0.0-beta.81",
|
|
47
|
+
"@robota-sdk/agent-interface-execution": "3.0.0-beta.81",
|
|
48
|
+
"@robota-sdk/agent-framework": "3.0.0-beta.81",
|
|
49
|
+
"@robota-sdk/agent-process": "3.0.0-beta.81"
|
|
40
50
|
},
|
|
41
51
|
"devDependencies": {
|
|
42
52
|
"@types/node": "^22.19.21",
|
|
43
53
|
"rimraf": "^5.0.10",
|
|
44
|
-
"tsdown": "^0.22.
|
|
45
|
-
"typescript": "^
|
|
54
|
+
"tsdown": "^0.22.14",
|
|
55
|
+
"typescript": "^6.0.3",
|
|
46
56
|
"vitest": "^3.2.6"
|
|
47
57
|
},
|
|
48
58
|
"license": "AGPL-3.0-only OR LicenseRef-Commercial",
|
|
49
59
|
"publishConfig": {
|
|
50
60
|
"access": "public"
|
|
51
61
|
},
|
|
62
|
+
"volta": {
|
|
63
|
+
"extends": "../../package.json"
|
|
64
|
+
},
|
|
52
65
|
"scripts": {
|
|
53
|
-
"build": "tsdown",
|
|
54
|
-
"build:js": "
|
|
55
|
-
"build:types": "
|
|
66
|
+
"build": "rimraf dist && tsdown",
|
|
67
|
+
"build:js": "pnpm run build",
|
|
68
|
+
"build:types": "pnpm run build",
|
|
56
69
|
"test": "vitest run --passWithNoTests",
|
|
57
70
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
58
|
-
"typecheck": "
|
|
71
|
+
"typecheck": "tsgo --noEmit",
|
|
59
72
|
"lint": "eslint src/ --ext .ts",
|
|
60
73
|
"clean": "rimraf dist"
|
|
61
74
|
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
function e(e){return typeof e==`object`&&!!e}function t(e,t){return typeof e[t]==`string`}function n(t){if(t.usage===void 0)return!0;let n=t.usage;return e(n)?typeof n.promptTokens==`number`&&typeof n.completionTokens==`number`&&typeof n.totalTokens==`number`:!1}function r(n){return!e(n)||!t(n,`jobId`)||!e(n.request)||!t(n.request,`type`)||!t(n.request,`prompt`)||!e(n.agentDefinition)||!t(n.agentDefinition,`name`)||!t(n.agentDefinition,`systemPrompt`)||!e(n.parentConfig)||!e(n.parentContext)||!e(n.providerProfile)||!t(n.providerProfile,`type`)?!1:t(n.providerProfile,`model`)}function i(n){if(!e(n)||!t(n,`type`))return!1;switch(n.type){case`start`:return r(n.payload);case`send`:return t(n,`prompt`);case`cancel`:return n.reason===void 0||typeof n.reason==`string`;default:return!1}}function a(r){if(!e(r)||!t(r,`type`))return!1;switch(r.type){case`ready`:return!0;case`text_delta`:return t(r,`delta`);case`tool_start`:return t(r,`toolName`);case`tool_end`:return t(r,`toolName`)&&typeof r.success==`boolean`;case`result`:return t(r,`output`)&&n(r);case`error`:return t(r,`message`);case`cancelled`:return r.reason===void 0||typeof r.reason==`string`;default:return!1}}export{i as n,a as t};
|
|
2
|
-
//# sourceMappingURL=child-process-subagent-ipc-BKEo2kRL.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"child-process-subagent-ipc-BKEo2kRL.js","names":[],"sources":["../../src/child-process-subagent-ipc.ts"],"sourcesContent":["import type { ISessionUsageTotals, TPermissionMode, TToolArgs } from '@robota-sdk/agent-core';\nimport type { ISubagentSpawnRequest } from '@robota-sdk/agent-executor';\nimport type { IAgentDefinition, IInProcessSubagentRunnerDeps } from '@robota-sdk/agent-framework';\nimport type { ISerializableProviderProfile } from '@robota-sdk/agent-interface-transport';\n\nexport type TSubagentWorkerWireValue = string | number | boolean | null | undefined | object;\n\ntype TSubagentWorkerWireRecord = Record<string, TSubagentWorkerWireValue>;\n\nexport interface ISubagentWorkerStartPayload {\n jobId: string;\n request: ISubagentSpawnRequest;\n agentDefinition: IAgentDefinition;\n parentConfig: IInProcessSubagentRunnerDeps['config'];\n parentContext: IInProcessSubagentRunnerDeps['context'];\n providerProfile: ISerializableProviderProfile;\n permissionMode?: TPermissionMode;\n logsDir?: string;\n}\n\nexport interface ISubagentWorkerStartMessage {\n type: 'start';\n payload: ISubagentWorkerStartPayload;\n}\n\nexport interface ISubagentWorkerSendMessage {\n type: 'send';\n prompt: string;\n}\n\nexport interface ISubagentWorkerCancelMessage {\n type: 'cancel';\n reason?: string;\n}\n\nexport type TSubagentWorkerParentMessage =\n | ISubagentWorkerStartMessage\n | ISubagentWorkerSendMessage\n | ISubagentWorkerCancelMessage;\n\nexport interface ISubagentWorkerReadyMessage {\n type: 'ready';\n}\n\nexport interface ISubagentWorkerTextDeltaMessage {\n type: 'text_delta';\n delta: string;\n}\n\nexport interface ISubagentWorkerToolStartMessage {\n type: 'tool_start';\n toolName: string;\n toolArgs?: TToolArgs;\n}\n\nexport interface ISubagentWorkerToolEndMessage {\n type: 'tool_end';\n toolName: string;\n success: boolean;\n}\n\nexport interface ISubagentWorkerResultMessage {\n type: 'result';\n output: string;\n /** ANALYTICS-001 (Phase 2): total token usage of the subagent run, forwarded to the parent. */\n usage?: ISessionUsageTotals;\n}\n\nexport interface ISubagentWorkerErrorMessage {\n type: 'error';\n message: string;\n}\n\nexport interface ISubagentWorkerCancelledMessage {\n type: 'cancelled';\n reason?: string;\n}\n\nexport type TSubagentWorkerChildMessage =\n | ISubagentWorkerReadyMessage\n | ISubagentWorkerTextDeltaMessage\n | ISubagentWorkerToolStartMessage\n | ISubagentWorkerToolEndMessage\n | ISubagentWorkerResultMessage\n | ISubagentWorkerErrorMessage\n | ISubagentWorkerCancelledMessage;\n\nfunction isRecord(value: TSubagentWorkerWireValue): value is TSubagentWorkerWireRecord {\n return typeof value === 'object' && value !== null;\n}\n\nfunction hasString(value: TSubagentWorkerWireRecord, key: string): boolean {\n return typeof value[key] === 'string';\n}\n\n/**\n * CORE-024 (RUNTIME-47): validate the optional `usage` payload on a `result` message so a\n * malformed object cannot be spread verbatim into the parent's token/cost accounting. Absent is\n * valid (usage is optional); present must be an `ISessionUsageTotals` with three numeric fields.\n */\nfunction hasValidOptionalUsage(value: TSubagentWorkerWireRecord): boolean {\n if (value.usage === undefined) return true;\n const usage = value.usage;\n if (!isRecord(usage)) return false;\n return (\n typeof usage.promptTokens === 'number' &&\n typeof usage.completionTokens === 'number' &&\n typeof usage.totalTokens === 'number'\n );\n}\n\nfunction isStartPayload(value: TSubagentWorkerWireValue): value is ISubagentWorkerStartPayload {\n if (!isRecord(value)) return false;\n if (!hasString(value, 'jobId')) return false;\n if (!isRecord(value.request)) return false;\n if (!hasString(value.request, 'type')) return false;\n if (!hasString(value.request, 'prompt')) return false;\n if (!isRecord(value.agentDefinition)) return false;\n if (!hasString(value.agentDefinition, 'name')) return false;\n if (!hasString(value.agentDefinition, 'systemPrompt')) return false;\n if (!isRecord(value.parentConfig)) return false;\n if (!isRecord(value.parentContext)) return false;\n if (!isRecord(value.providerProfile)) return false;\n if (!hasString(value.providerProfile, 'type')) return false;\n return hasString(value.providerProfile, 'model');\n}\n\nexport function isSubagentWorkerParentMessage(\n value: TSubagentWorkerWireValue,\n): value is TSubagentWorkerParentMessage {\n if (!isRecord(value) || !hasString(value, 'type')) return false;\n switch (value.type) {\n case 'start':\n return isStartPayload(value.payload);\n case 'send':\n return hasString(value, 'prompt');\n case 'cancel':\n return value.reason === undefined || typeof value.reason === 'string';\n default:\n return false;\n }\n}\n\nexport function isSubagentWorkerChildMessage(\n value: TSubagentWorkerWireValue,\n): value is TSubagentWorkerChildMessage {\n if (!isRecord(value) || !hasString(value, 'type')) return false;\n switch (value.type) {\n case 'ready':\n return true;\n case 'text_delta':\n return hasString(value, 'delta');\n case 'tool_start':\n return hasString(value, 'toolName');\n case 'tool_end':\n return hasString(value, 'toolName') && typeof value.success === 'boolean';\n case 'result':\n return hasString(value, 'output') && hasValidOptionalUsage(value);\n case 'error':\n return hasString(value, 'message');\n case 'cancelled':\n return value.reason === undefined || typeof value.reason === 'string';\n default:\n return false;\n }\n}\n"],"mappings":"AAuFA,SAAS,EAAS,EAAqE,CACrF,OAAO,OAAO,GAAU,YAAY,CACtC,CAEA,SAAS,EAAU,EAAkC,EAAsB,CACzE,OAAO,OAAO,EAAM,IAAS,QAC/B,CAOA,SAAS,EAAsB,EAA2C,CACxE,GAAI,EAAM,QAAU,IAAA,GAAW,MAAO,GACtC,IAAM,EAAQ,EAAM,MAEpB,OADK,EAAS,CAAK,EAEjB,OAAO,EAAM,cAAiB,UAC9B,OAAO,EAAM,kBAAqB,UAClC,OAAO,EAAM,aAAgB,SAJF,EAM/B,CAEA,SAAS,EAAe,EAAuE,CAa7F,MAZI,CAAC,EAAS,CAAK,GACf,CAAC,EAAU,EAAO,OAAO,GACzB,CAAC,EAAS,EAAM,OAAO,GACvB,CAAC,EAAU,EAAM,QAAS,MAAM,GAChC,CAAC,EAAU,EAAM,QAAS,QAAQ,GAClC,CAAC,EAAS,EAAM,eAAe,GAC/B,CAAC,EAAU,EAAM,gBAAiB,MAAM,GACxC,CAAC,EAAU,EAAM,gBAAiB,cAAc,GAChD,CAAC,EAAS,EAAM,YAAY,GAC5B,CAAC,EAAS,EAAM,aAAa,GAC7B,CAAC,EAAS,EAAM,eAAe,GAC/B,CAAC,EAAU,EAAM,gBAAiB,MAAM,EAAU,GAC/C,EAAU,EAAM,gBAAiB,OAAO,CACjD,CAEA,SAAgB,EACd,EACuC,CACvC,GAAI,CAAC,EAAS,CAAK,GAAK,CAAC,EAAU,EAAO,MAAM,EAAG,MAAO,GAC1D,OAAQ,EAAM,KAAd,CACE,IAAK,QACH,OAAO,EAAe,EAAM,OAAO,EACrC,IAAK,OACH,OAAO,EAAU,EAAO,QAAQ,EAClC,IAAK,SACH,OAAO,EAAM,SAAW,IAAA,IAAa,OAAO,EAAM,QAAW,SAC/D,QACE,MAAO,EACX,CACF,CAEA,SAAgB,EACd,EACsC,CACtC,GAAI,CAAC,EAAS,CAAK,GAAK,CAAC,EAAU,EAAO,MAAM,EAAG,MAAO,GAC1D,OAAQ,EAAM,KAAd,CACE,IAAK,QACH,MAAO,GACT,IAAK,aACH,OAAO,EAAU,EAAO,OAAO,EACjC,IAAK,aACH,OAAO,EAAU,EAAO,UAAU,EACpC,IAAK,WACH,OAAO,EAAU,EAAO,UAAU,GAAK,OAAO,EAAM,SAAY,UAClE,IAAK,SACH,OAAO,EAAU,EAAO,QAAQ,GAAK,EAAsB,CAAK,EAClE,IAAK,QACH,OAAO,EAAU,EAAO,SAAS,EACnC,IAAK,YACH,OAAO,EAAM,SAAW,IAAA,IAAa,OAAO,EAAM,QAAW,SAC/D,QACE,MAAO,EACX,CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function e(e){return typeof e==`object`&&!!e}function t(e,t){return typeof e[t]==`string`}function n(t){if(t.usage===void 0)return!0;let n=t.usage;return e(n)?typeof n.promptTokens==`number`&&typeof n.completionTokens==`number`&&typeof n.totalTokens==`number`:!1}function r(n){return!e(n)||!t(n,`jobId`)||!e(n.request)||!t(n.request,`type`)||!t(n.request,`prompt`)||!e(n.agentDefinition)||!t(n.agentDefinition,`name`)||!t(n.agentDefinition,`systemPrompt`)||!e(n.parentConfig)||!e(n.parentContext)||!e(n.providerProfile)||!t(n.providerProfile,`type`)?!1:t(n.providerProfile,`model`)}function i(n){if(!e(n)||!t(n,`type`))return!1;switch(n.type){case`start`:return r(n.payload);case`send`:return t(n,`prompt`);case`cancel`:return n.reason===void 0||typeof n.reason==`string`;default:return!1}}function a(r){if(!e(r)||!t(r,`type`))return!1;switch(r.type){case`ready`:return!0;case`text_delta`:return t(r,`delta`);case`tool_start`:return t(r,`toolName`);case`tool_end`:return t(r,`toolName`)&&typeof r.success==`boolean`;case`result`:return t(r,`output`)&&n(r);case`error`:return t(r,`message`);case`cancelled`:return r.reason===void 0||typeof r.reason==`string`;default:return!1}}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return a}});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=require("./child-process-subagent-ipc-C4zByGSA.cjs");let t=require("@robota-sdk/agent-executor"),n=require("@robota-sdk/agent-framework"),r=require("@robota-sdk/agent-core"),i=require("@robota-sdk/agent-provider");const a={write:()=>{},writeLine:()=>{},writeMarkdown:()=>{},writeError:()=>{},prompt:()=>Promise.resolve(``),select:()=>Promise.resolve(0),spinner:()=>({stop:()=>{},update:()=>{}})};let o=null,s=!1,c=Promise.resolve();function l(e){process.send&&process.send(e)}function u(e,t){let n=!1,r=()=>{n||(n=!0,process.exit(t))};if(process.send){let t=setTimeout(r,2e3);t.unref?.(),process.send(e,void 0,void 0,()=>{clearTimeout(t),r()})}else r()}function d(e){try{return(0,r.sumHistoryUsage)(e.getFullHistory())}catch{return}}async function f(e){try{let r=(0,t.createProviderFromProfile)(e.providerProfile,e.request.model,(0,i.createDefaultProviderDefinitions)()),c=e.logsDir?(0,n.createSubagentLogger)(e.request.parentSessionId,e.jobId,e.logsDir):void 0;o=(0,n.createSubagentSession)({agentDefinition:e.agentDefinition,parentConfig:e.parentConfig,parentContext:e.parentContext,parentTools:(0,n.createDefaultTools)(),provider:r,terminal:a,sessionId:e.jobId,...c?{sessionLogger:c}:{},permissionMode:e.permissionMode,hooks:e.parentConfig.hooks,onTextDelta:e=>l({type:`text_delta`,delta:e}),onToolExecution:p});let f=await o.run(e.request.prompt);if(s){u({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}let m=d(o);u({type:`result`,output:f,...m?{usage:m}:{}},0)}catch(e){if(s){u({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}u({type:`error`,message:e instanceof Error?e.message:String(e)},0)}}function p(e){if(e.type===`start`){l({type:`tool_start`,toolName:e.toolName,toolArgs:e.toolArgs});return}l({type:`tool_end`,toolName:e.toolName,success:e.success??!0})}function m(e){if(o===null){l({type:`error`,message:`Subagent worker has not started`});return}c=c.then(async()=>{try{await o?.run(e)}catch(e){l({type:`error`,message:e instanceof Error?e.message:String(e)})}})}async function h(e){s=!0,o?.abort(),l({type:`cancelled`,reason:e}),await o?.shutdown({reason:`other`}).catch(()=>void 0),setTimeout(()=>process.exit(130),0)}process.on(`message`,t=>{if(!e.n(t)){l({type:`error`,message:`Malformed subagent worker parent message`});return}switch(t.type){case`start`:c=c.then(()=>f(t.payload));break;case`send`:m(t.prompt);break;case`cancel`:h(t.reason);break;default:l({type:`error`,message:`Unhandled subagent worker parent message`})}}),process.on(`disconnect`,()=>{s=!0,o?.abort(),o?.shutdown({reason:`other`}).catch(()=>void 0)}),l({type:`ready`});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{n as e}from"./child-process-subagent-ipc-BKEo2kRL.js";import{createProviderFromProfile as t}from"@robota-sdk/agent-executor";import{createDefaultTools as n,createSubagentLogger as r,createSubagentSession as i}from"@robota-sdk/agent-framework";import{sumHistoryUsage as a}from"@robota-sdk/agent-core";import{createDefaultProviderDefinitions as o}from"@robota-sdk/agent-provider";const s={write:()=>{},writeLine:()=>{},writeMarkdown:()=>{},writeError:()=>{},prompt:()=>Promise.resolve(``),select:()=>Promise.resolve(0),spinner:()=>({stop:()=>{},update:()=>{}})};let c=null,l=!1,u=Promise.resolve();function d(e){process.send&&process.send(e)}function f(e,t){let n=!1,r=()=>{n||(n=!0,process.exit(t))};if(process.send){let t=setTimeout(r,2e3);t.unref?.(),process.send(e,void 0,void 0,()=>{clearTimeout(t),r()})}else r()}function p(e){try{return a(e.getFullHistory())}catch{return}}async function m(e){try{let a=t(e.providerProfile,e.request.model,o()),u=e.logsDir?r(e.request.parentSessionId,e.jobId,e.logsDir):void 0;c=i({agentDefinition:e.agentDefinition,parentConfig:e.parentConfig,parentContext:e.parentContext,parentTools:n(),provider:a,terminal:s,sessionId:e.jobId,...u?{sessionLogger:u}:{},permissionMode:e.permissionMode,hooks:e.parentConfig.hooks,onTextDelta:e=>d({type:`text_delta`,delta:e}),onToolExecution:h});let m=await c.run(e.request.prompt);if(l){f({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}let g=p(c);f({type:`result`,output:m,...g?{usage:g}:{}},0)}catch(e){if(l){f({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}f({type:`error`,message:e instanceof Error?e.message:String(e)},0)}}function h(e){if(e.type===`start`){d({type:`tool_start`,toolName:e.toolName,toolArgs:e.toolArgs});return}d({type:`tool_end`,toolName:e.toolName,success:e.success??!0})}function g(e){if(c===null){d({type:`error`,message:`Subagent worker has not started`});return}u=u.then(async()=>{try{await c?.run(e)}catch(e){d({type:`error`,message:e instanceof Error?e.message:String(e)})}})}async function _(e){l=!0,c?.abort(),d({type:`cancelled`,reason:e}),await c?.shutdown({reason:`other`}).catch(()=>void 0),setTimeout(()=>process.exit(130),0)}process.on(`message`,t=>{if(!e(t)){d({type:`error`,message:`Malformed subagent worker parent message`});return}switch(t.type){case`start`:u=u.then(()=>m(t.payload));break;case`send`:g(t.prompt);break;case`cancel`:_(t.reason);break;default:d({type:`error`,message:`Unhandled subagent worker parent message`})}}),process.on(`disconnect`,()=>{l=!0,c?.abort(),c?.shutdown({reason:`other`}).catch(()=>void 0)}),d({type:`ready`});export{};
|
|
2
|
-
//# sourceMappingURL=child-process-subagent-worker.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"child-process-subagent-worker.js","names":[],"sources":["../../src/child-process-subagent-worker.ts"],"sourcesContent":["import { sumHistoryUsage } from '@robota-sdk/agent-core';\nimport { createProviderFromProfile } from '@robota-sdk/agent-executor';\nimport {\n createDefaultTools,\n createSubagentLogger,\n createSubagentSession,\n} from '@robota-sdk/agent-framework';\nimport { createDefaultProviderDefinitions } from '@robota-sdk/agent-provider';\n\nimport {\n isSubagentWorkerParentMessage,\n type ISubagentWorkerStartPayload,\n type TSubagentWorkerChildMessage,\n type TSubagentWorkerWireValue,\n} from './child-process-subagent-ipc.js';\n\nimport type { ITerminalOutput } from '@robota-sdk/agent-core';\n\nconst CANCEL_EXIT_CODE = 130;\n/** Force-exit fallback if the IPC flush callback never fires (broken channel). */\nconst FLUSH_EXIT_FALLBACK_MS = 2000;\n\nconst NOOP_TERMINAL: ITerminalOutput = {\n write: (): void => {},\n writeLine: (): void => {},\n writeMarkdown: (): void => {},\n writeError: (): void => {},\n prompt: (): Promise<string> => Promise.resolve(''),\n select: (): Promise<number> => Promise.resolve(0),\n spinner: () => ({ stop: (): void => {}, update: (): void => {} }),\n};\n\ntype TSubagentSessionToolEvent = Parameters<\n NonNullable<Parameters<typeof createSubagentSession>[0]['onToolExecution']>\n>[0];\n\nlet session: ReturnType<typeof createSubagentSession> | null = null;\nlet cancelled = false;\nlet running: Promise<void> = Promise.resolve();\n\nfunction sendChildMessage(message: TSubagentWorkerChildMessage): void {\n if (process.send) {\n process.send(message);\n }\n}\n\n/**\n * CORE-024 (RUNTIME-20): send the terminal message and exit ONLY after the IPC write has drained.\n * `process.send` is asynchronous; exiting from a `finally` before the write flushes made the\n * parent's `onExit` fire before the `result` arrived — a successful run was misreported as a crash\n * and its `usage` payload was lost. Exit from the flush callback; a fallback timer guards a broken\n * channel so the worker never hangs.\n */\nfunction sendTerminalMessageAndExit(message: TSubagentWorkerChildMessage, exitCode: number): void {\n let exited = false;\n const exitOnce = (): void => {\n if (exited) return;\n exited = true;\n process.exit(exitCode);\n };\n if (process.send) {\n const fallback = setTimeout(exitOnce, FLUSH_EXIT_FALLBACK_MS);\n fallback.unref?.();\n process.send(message, undefined, undefined, () => {\n clearTimeout(fallback);\n exitOnce();\n });\n } else {\n exitOnce();\n }\n}\n\n/** Best-effort total token usage of the finished subagent session; never throws. */\nfunction readSessionUsage(\n finishedSession: ReturnType<typeof createSubagentSession>,\n): ReturnType<typeof sumHistoryUsage> {\n try {\n return sumHistoryUsage(finishedSession.getFullHistory());\n } catch {\n // allow-fallback: usage capture is auxiliary — history read failure must not fail the subagent run\n return undefined;\n }\n}\n\nasync function runInitialPrompt(payload: ISubagentWorkerStartPayload): Promise<void> {\n try {\n const provider = createProviderFromProfile(\n payload.providerProfile,\n payload.request.model,\n createDefaultProviderDefinitions(),\n );\n const sessionLogger = payload.logsDir\n ? createSubagentLogger(payload.request.parentSessionId, payload.jobId, payload.logsDir)\n : undefined;\n session = createSubagentSession({\n agentDefinition: payload.agentDefinition,\n parentConfig: payload.parentConfig,\n parentContext: payload.parentContext,\n parentTools: createDefaultTools(),\n provider,\n terminal: NOOP_TERMINAL,\n sessionId: payload.jobId,\n ...(sessionLogger ? { sessionLogger } : {}),\n permissionMode: payload.permissionMode,\n hooks: payload.parentConfig.hooks,\n onTextDelta: (delta) => sendChildMessage({ type: 'text_delta', delta }),\n onToolExecution: forwardToolExecution,\n });\n const output = await session.run(payload.request.prompt);\n if (cancelled) {\n sendTerminalMessageAndExit(\n { type: 'cancelled', reason: 'Subagent worker cancelled' },\n CANCEL_EXIT_CODE,\n );\n return;\n }\n // ANALYTICS-001 (Phase 2): forward the subagent's total token usage so the parent log can\n // attribute it to this agent as a source. Best-effort — usage capture must never fail the run.\n const usage = readSessionUsage(session);\n // CORE-024 (RUNTIME-20): exit only after this result (with usage) has flushed over IPC, so the\n // parent settles on the result instead of racing a crash-projection from an early exit.\n sendTerminalMessageAndExit({ type: 'result', output, ...(usage ? { usage } : {}) }, 0);\n } catch (error) {\n // allow-fallback: child process must report errors to parent via IPC, not crash silently; exit follows the IPC flush (CORE-024 RUNTIME-20)\n if (cancelled) {\n sendTerminalMessageAndExit(\n { type: 'cancelled', reason: 'Subagent worker cancelled' },\n CANCEL_EXIT_CODE,\n );\n return;\n }\n const message = error instanceof Error ? error.message : String(error);\n sendTerminalMessageAndExit({ type: 'error', message }, 0);\n }\n}\n\nfunction forwardToolExecution(event: TSubagentSessionToolEvent): void {\n if (event.type === 'start') {\n sendChildMessage({ type: 'tool_start', toolName: event.toolName, toolArgs: event.toolArgs });\n return;\n }\n sendChildMessage({ type: 'tool_end', toolName: event.toolName, success: event.success ?? true });\n}\n\nfunction runFollowUp(prompt: string): void {\n if (session === null) {\n sendChildMessage({ type: 'error', message: 'Subagent worker has not started' });\n return;\n }\n running = running.then(async () => {\n try {\n // allow-fallback: child process must report errors to parent via IPC, not crash silently\n await session?.run(prompt);\n } catch (error) {\n // allow-fallback: child process must report errors to parent via IPC, not crash silently\n const message = error instanceof Error ? error.message : String(error);\n sendChildMessage({ type: 'error', message });\n }\n });\n}\n\nasync function cancelWorker(reason?: string): Promise<void> {\n cancelled = true;\n session?.abort();\n sendChildMessage({ type: 'cancelled', reason });\n await session?.shutdown({ reason: 'other' }).catch(() => undefined); // allow-fallback: shutdown during cancel — process will exit regardless\n setTimeout(() => process.exit(CANCEL_EXIT_CODE), 0);\n}\n\nprocess.on('message', (message: TSubagentWorkerWireValue) => {\n if (!isSubagentWorkerParentMessage(message)) {\n sendChildMessage({ type: 'error', message: 'Malformed subagent worker parent message' });\n return;\n }\n\n switch (message.type) {\n case 'start':\n running = running.then(() => runInitialPrompt(message.payload));\n break;\n case 'send':\n runFollowUp(message.prompt);\n break;\n case 'cancel':\n void cancelWorker(message.reason);\n break;\n default:\n sendChildMessage({ type: 'error', message: 'Unhandled subagent worker parent message' });\n }\n});\n\nprocess.on('disconnect', () => {\n cancelled = true;\n session?.abort();\n void session?.shutdown({ reason: 'other' }).catch(() => undefined); // allow-fallback: cleanup on disconnect — process will exit regardless\n});\n\nsendChildMessage({ type: 'ready' });\n"],"mappings":"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"}
|