{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../../src/runs/foreground/execution.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAc1D,OAAO,EAWN,KAAK,cAAc,EACnB,KAAK,YAAY,EAGjB,MAAM,uBAAuB,CAAC;AAq4D/B;;;GAGG;AACH,wBAAsB,OAAO,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,YAAY,CAAC,CAiIvB","sourcesContent":["/**\n * Core execution logic for running subagents\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { Message } from \"@lpb-work/pi-ai\";\nimport { buildAgentMemoryInjection } from \"../../agents/agent-memory.ts\";\nimport { appendAgentRefinementOverlay } from \"../../agents/agent-refinements.ts\";\nimport type { AgentConfig } from \"../../agents/agents.ts\";\nimport { buildSkillInjection, resolveSkillsWithFallback } from \"../../agents/skills.ts\";\nimport {\n\tensureArtifactsDir,\n\tformatOutputArtifactContent,\n\tgetArtifactPaths,\n\twriteArtifact,\n\twriteMetadata,\n} from \"../../shared/artifacts.ts\";\nimport { type ChildTranscriptWriter, createChildTranscriptWriter } from \"../../shared/child-transcript.ts\";\nimport { createJsonlWriter } from \"../../shared/jsonl-writer.ts\";\nimport { agentDefinitionDigest, launchBindingDigest } from \"../../shared/launch-contract.ts\";\nimport { resolveEffectiveThinking } from \"../../shared/model-info.ts\";\nimport { attachPostExitStdioGuard, trySignalChild } from \"../../shared/post-exit-stdio-guard.ts\";\nimport {\n\ttype AcceptanceLedger,\n\ttype AgentProgress,\n\ttype ArtifactPaths,\n\ttype ControlEvent,\n\tDEFAULT_MAX_OUTPUT,\n\tgetSubagentDepthEnv,\n\tINTERCOM_DETACH_REQUEST_EVENT,\n\tINTERCOM_DETACH_RESPONSE_EVENT,\n\ttype ModelAttempt,\n\ttype ResolvedAcceptanceConfig,\n\ttype RunSyncOptions,\n\ttype SingleResult,\n\ttruncateOutput,\n\ttype Usage,\n} from \"../../shared/types.ts\";\nimport {\n\tboundStreamedRecentOutput,\n\tboundStreamedRecentTools,\n\tboundStreamedToolCalls,\n\tdetectSubagentError,\n\textractTextFromContent,\n\textractToolArgsPreview,\n\tfindLatestSessionFile,\n\tgetFinalOutput,\n\thasEmptyTerminalAssistantResponse,\n} from \"../../shared/utils.ts\";\nimport {\n\tacceptChildWatchdogEvent,\n\ttype ChildWatchdogStateSnapshot,\n\tchildWatchdogIsActive,\n\tisChildWatchdogStatusEvent,\n\tresolveChildWatchdogConfig,\n} from \"../../watchdog/child-status.ts\";\nimport { resolveWatchdogConfig } from \"../../watchdog/settings.ts\";\nimport {\n\tacceptanceFailureMessage,\n\tbuildSkippedAcceptanceLedger,\n\tevaluateAcceptance,\n\tformatAcceptancePrompt,\n\tresolveEffectiveAcceptance,\n\tstripAcceptanceReport,\n\tvalidateAcceptanceInput,\n} from \"../shared/acceptance.ts\";\nimport { attachContractProjections, isAgentContractV1 } from \"../shared/agent-contract.ts\";\nimport {\n\tassertAgentAllowedByCapabilityCeiling,\n\tdecodeSubagentCapabilityCeiling,\n\tintersectSubagentCapabilityCeilings,\n\tresolveCurrentSubagentCapabilityCeiling,\n\tSUBAGENT_CAPABILITY_CEILING_ENV,\n} from \"../shared/capability-ceiling.ts\";\nimport {\n\ttype ChildLifecycleAction,\n\tcreateBoundedByteTail,\n\tcreateBoundedLineReader,\n\tformatProtocolOutputLimit,\n\tMAX_CHILD_STDERR_BYTES,\n\tPI_AGGREGATE_EVENT_PROJECTOR,\n\ttype ProtocolOutputLimit,\n\tprojectChildLifecycle,\n} from \"../shared/child-protocol.ts\";\nimport { evaluateCompletionMutationGuard } from \"../shared/completion-guard.ts\";\nimport {\n\tcreateMutatingFailureState,\n\tdidMutatingToolFail,\n\tisMutatingTool,\n\tnextLongRunningTrigger,\n\trecordMutatingFailure,\n\tresetMutatingFailureState,\n\tresolveCurrentPath,\n\tshouldEscalateMutatingFailures,\n\tsummarizeRecentMutatingFailures,\n} from \"../shared/long-running-guard.ts\";\nimport { buildModelCandidates, formatModelAttemptNote, isRetryableModelFailure } from \"../shared/model-fallback.ts\";\nimport { resolvePermissionRules } from \"../shared/permissions.ts\";\nimport {\n\tapplyThinkingSuffix,\n\tbuildPiArgs,\n\tcleanupTempDir,\n\tprojectLaunchResolvedChildExtensions,\n\tresolvePiLaunchToolPlan,\n} from \"../shared/pi-args.ts\";\nimport { getPiSpawnCommand } from \"../shared/pi-spawn.ts\";\nimport { formatProcessSignalError, isUnexplainedProcessSignal } from \"../shared/process-signal.ts\";\nimport { readRuntimeAcknowledgedExtensions } from \"../shared/runtime-acknowledged-extensions.ts\";\nimport {\n\tcaptureSingleOutputSnapshot,\n\textractChildWrittenOutput,\n\tformatSavedOutputReference,\n\tinjectOutputPathSystemPrompt,\n\tresolveSingleOutput,\n\ttype SingleOutputSnapshot,\n\tvalidateFileOnlyOutputMode,\n} from \"../shared/single-output.ts\";\nimport { MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput } from \"../shared/structured-output.ts\";\nimport {\n\tbuildControlEvent,\n\tclaimControlNotification,\n\tDEFAULT_CONTROL_CONFIG,\n\tderiveActivityState,\n\tshouldNotifyControlEvent,\n} from \"../shared/subagent-control.ts\";\nimport {\n\tformatSubagentStartupRetryExhaustedError,\n\tformatSubagentStartupRetryNote,\n\tisRetryableSubagentStartupFailure,\n\tSUBAGENT_STARTUP_RETRY_DELAYS_MS,\n\twaitForSubagentStartupRetry,\n} from \"../shared/subagent-startup-retry.ts\";\nimport { readChildToolDiagnosticError } from \"../shared/tool-availability.ts\";\nimport { initialToolBudgetState, toolBudgetState } from \"../shared/tool-budget.ts\";\nimport {\n\tappendTurnBudgetSystemPrompt,\n\tformatTurnBudgetOutput,\n\tinitialTurnBudgetState,\n\tturnBudgetDecision,\n\tturnBudgetDeferredNote,\n\tturnBudgetDeferredState,\n\tturnBudgetExceededMessage,\n\tturnBudgetSoftNote,\n\tturnBudgetState,\n} from \"../shared/turn-budget.ts\";\n\nconst artifactOutputByResult = new WeakMap<SingleResult, string>();\nconst acceptanceOutputByResult = new WeakMap<SingleResult, string>();\n\nfunction emptyUsage(): Usage {\n\treturn { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };\n}\n\nfunction withRunContext<T extends SingleResult>(result: T, context: RunSyncOptions[\"context\"]): T {\n\tif (!context) return result;\n\tresult.context = context;\n\treturn result;\n}\n\nfunction sumUsage(target: Usage, source: Usage): void {\n\ttarget.input += source.input;\n\ttarget.output += source.output;\n\ttarget.cacheRead += source.cacheRead;\n\ttarget.cacheWrite += source.cacheWrite;\n\ttarget.cost += source.cost;\n\ttarget.turns += source.turns;\n}\n\nfunction persistSingleResultMetadata(input: {\n\tmetadataPath?: string;\n\tenabled: boolean;\n\trunId?: string;\n\tagent: string;\n\ttask: string;\n\tresult: SingleResult;\n}): void {\n\tif (!input.enabled || !input.metadataPath) return;\n\tconst target = input.result;\n\twriteMetadata(input.metadataPath, {\n\t\trunId: input.runId,\n\t\tagent: input.agent,\n\t\ttask: input.task,\n\t\texitCode: target.exitCode,\n\t\tprocessSignal: target.processSignal,\n\t\tusage: target.usage,\n\t\tmodel: target.model,\n\t\tattemptedModels: target.attemptedModels,\n\t\tmodelAttempts: target.modelAttempts,\n\t\tdurationMs: target.progressSummary?.durationMs,\n\t\ttoolCount: target.progressSummary?.toolCount,\n\t\terror: target.error,\n\t\tagentContract: target.agentContract,\n\t\tlaunchContractDigest: target.launchContractDigest,\n\t\tlaunchResolvedExtensions: target.launchResolvedExtensions,\n\t\truntimeAcknowledgedExtensions: target.runtimeAcknowledgedExtensions,\n\t\texecution: target.execution,\n\t\tacceptance: target.acceptance,\n\t\tcapabilityCeiling: target.capabilityCeiling,\n\t\tcapabilityAudit: target.capabilityAudit,\n\t\treview: target.review,\n\t\teffects: target.effects,\n\t\ttranscriptPath: target.transcriptPath,\n\t\ttranscriptError: target.transcriptError,\n\t\tskills: target.skills,\n\t\tskillsWarning: target.skillsWarning,\n\t\ttimestamp: Date.now(),\n\t});\n}\n\nfunction formatTimeoutMessage(timeoutMs: number): string {\n\treturn `Subagent timed out after ${timeoutMs}ms.`;\n}\n\nfunction resolveAttemptTimeout(\n\toptions: RunSyncOptions,\n): { timeoutMs: number; remainingMs: number; message: string } | undefined {\n\tif (options.timeoutMs === undefined) return undefined;\n\tconst deadlineAt = options.deadlineAt ?? Date.now() + options.timeoutMs;\n\treturn {\n\t\ttimeoutMs: options.timeoutMs,\n\t\tremainingMs: Math.max(0, deadlineAt - Date.now()),\n\t\tmessage: formatTimeoutMessage(options.timeoutMs),\n\t};\n}\n\nfunction buildPendingAcceptanceLedger(acceptance: ResolvedAcceptanceConfig): AcceptanceLedger {\n\treturn {\n\t\tstatus: \"pending\",\n\t\tevidenceStatus: \"pending\",\n\t\texplicit: acceptance.explicit,\n\t\teffectiveAcceptance: acceptance,\n\t\tinferredReason: acceptance.inferredReason,\n\t\tcriteria: acceptance.criteria,\n\t\truntimeChecks: [],\n\t\tverifyRuns: [],\n\t};\n}\n\nfunction buildInterruptedAcceptanceLedger(acceptance: ResolvedAcceptanceConfig): AcceptanceLedger {\n\tconst pending = buildPendingAcceptanceLedger(acceptance);\n\tif (acceptance.level === \"none\") {\n\t\tpending.status = \"not-required\";\n\t\tpending.evidenceStatus = \"not-required\";\n\t} else {\n\t\tpending.runtimeChecks = [\n\t\t\t{\n\t\t\t\tid: \"interrupted\",\n\t\t\t\tstatus: \"not-applicable\",\n\t\t\t\tmessage: \"Acceptance was not evaluated because the subagent was interrupted.\",\n\t\t\t},\n\t\t];\n\t}\n\treturn pending;\n}\n\nfunction appendRecentOutput(progress: AgentProgress, lines: string[]): void {\n\tif (lines.length === 0) return;\n\tprogress.recentOutput.push(...lines.filter((line) => line.trim()));\n\tif (progress.recentOutput.length > 50) {\n\t\tprogress.recentOutput.splice(0, progress.recentOutput.length - 50);\n\t}\n}\n\nfunction stripAcceptanceReportsFromMessages(messages: Message[] | undefined): void {\n\tfor (const message of messages ?? []) {\n\t\tif (message.role !== \"assistant\" || !Array.isArray(message.content)) continue;\n\t\tfor (const part of message.content) {\n\t\t\tif (part.type === \"text\" && \"text\" in part && typeof part.text === \"string\") {\n\t\t\t\tpart.text = stripAcceptanceReport(part.text);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction snapshotProgress(progress: AgentProgress): AgentProgress {\n\treturn {\n\t\t...progress,\n\t\tskills: progress.skills ? [...progress.skills] : undefined,\n\t\trecentTools: boundStreamedRecentTools(progress.recentTools),\n\t\trecentOutput: boundStreamedRecentOutput(progress.recentOutput),\n\t};\n}\n\nfunction snapshotResult(result: SingleResult, progress: AgentProgress): SingleResult {\n\treturn {\n\t\t...result,\n\t\tmessages:\n\t\t\tresult.outputMode === \"file-only\" && result.savedOutputPath\n\t\t\t\t? undefined\n\t\t\t\t: result.messages\n\t\t\t\t\t? [...result.messages]\n\t\t\t\t\t: undefined,\n\t\tusage: { ...result.usage },\n\t\tskills: result.skills ? [...result.skills] : undefined,\n\t\tattemptedModels: result.attemptedModels ? [...result.attemptedModels] : undefined,\n\t\tmodelAttempts: result.modelAttempts\n\t\t\t? result.modelAttempts.map((attempt) => ({\n\t\t\t\t\t...attempt,\n\t\t\t\t\tusage: attempt.usage ? { ...attempt.usage } : undefined,\n\t\t\t\t}))\n\t\t\t: undefined,\n\t\tcontrolEvents: result.controlEvents ? result.controlEvents.map((event) => ({ ...event })) : undefined,\n\t\tprogress,\n\t\tprogressSummary: result.progressSummary ? { ...result.progressSummary } : undefined,\n\t\tartifactPaths: result.artifactPaths ? { ...result.artifactPaths } : undefined,\n\t\ttruncation: result.truncation ? { ...result.truncation } : undefined,\n\t\toutputReference: result.outputReference ? { ...result.outputReference } : undefined,\n\t};\n}\n\n/**\n * Streaming variant of snapshotResult for `onUpdate` progress events: it drops the\n * unbounded `messages` transcript in favour of compact tool-call summaries so a\n * single `tool_execution_update` line stays well under the child-stdout protocol\n * cap (`MAX_CHILD_PENDING_LINE_BYTES`). Non-streaming consumers such as\n * foreground detach receipts and terminal consumers use snapshotResult directly.\n */\nfunction snapshotStreamResult(result: SingleResult, progress: AgentProgress): SingleResult {\n\tconst snapshot = snapshotResult(result, progress);\n\tsnapshot.messages = undefined;\n\tsnapshot.toolCalls = boundStreamedToolCalls(result);\n\treturn snapshot;\n}\n\nasync function runSingleAttempt(\n\truntimeCwd: string,\n\tagent: AgentConfig,\n\ttask: string,\n\tmodel: string | undefined,\n\toptions: RunSyncOptions,\n\tshared: {\n\t\tsessionEnabled: boolean;\n\t\tsystemPrompt: string;\n\t\tresolvedSkillNames?: string[];\n\t\tmodelCandidates?: string[];\n\t\tskillsWarning?: string;\n\t\tjsonlPath?: string;\n\t\tartifactPaths?: ArtifactPaths;\n\t\ttranscriptWriter?: ChildTranscriptWriter;\n\t\tattemptNotes: string[];\n\t\toutputSnapshot?: SingleOutputSnapshot;\n\t\toriginalTask?: string;\n\t},\n): Promise<SingleResult> {\n\tconst effectiveThinking = options.thinkingOverride ?? agent.thinking;\n\tconst modelArg = applyThinkingSuffix(model, effectiveThinking, options.thinkingOverride !== undefined);\n\tconst resolvedThinking = resolveEffectiveThinking(modelArg, effectiveThinking);\n\tconst watchdogConfig = resolveWatchdogConfig(options.cwd ?? runtimeCwd);\n\tconst childWatchdog = watchdogConfig.ok\n\t\t? resolveChildWatchdogConfig({\n\t\t\t\tconfig: watchdogConfig.config,\n\t\t\t\tagent: agent.name,\n\t\t\t\trunId: options.runId,\n\t\t\t\tchildIndex: options.index ?? 0,\n\t\t\t})\n\t\t: undefined;\n\tconst permissionRules = resolvePermissionRules(options.permissions, agent.permissions);\n\tconst permissionAuditPath =\n\t\tpermissionRules && options.artifactsDir\n\t\t\t? path.join(options.artifactsDir, \"permission-audit\", `${options.runId}-${options.index ?? 0}.jsonl`)\n\t\t\t: undefined;\n\tconst {\n\t\targs,\n\t\tenv: sharedEnv,\n\t\ttempDir,\n\t\ttoolDiagnosticPath,\n\t\truntimeAcknowledgedExtensionsPath,\n\t\tcapabilityAudit,\n\t} = buildPiArgs({\n\t\tbaseArgs: [\"--mode\", \"json\", \"-p\"],\n\t\ttask,\n\t\tsessionEnabled: shared.sessionEnabled,\n\t\tsessionDir: options.sessionDir,\n\t\tsessionFile: options.sessionFile,\n\t\tmodel: modelArg,\n\t\tthinking: effectiveThinking,\n\t\tsystemPromptMode: agent.systemPromptMode,\n\t\tinheritProjectContext: agent.inheritProjectContext,\n\t\tinheritSkills: agent.inheritSkills,\n\t\trequireReadTool: Boolean(shared.resolvedSkillNames?.length),\n\t\ttools: agent.tools,\n\t\textensions: agent.extensions,\n\t\tsubagentOnlyExtensions: agent.subagentOnlyExtensions,\n\t\tsystemPrompt: appendTurnBudgetSystemPrompt(shared.systemPrompt, options.turnBudget),\n\t\tmcpDirectTools: agent.mcpDirectTools,\n\t\tcwd: options.cwd ?? runtimeCwd,\n\t\tpromptFileStem: agent.name,\n\t\tintercomSessionName: options.intercomSessionName,\n\t\torchestratorIntercomTarget: options.orchestratorIntercomTarget,\n\t\trunId: options.runId,\n\t\tchildAgentName: agent.name,\n\t\tchildIndex: options.index ?? 0,\n\t\tparentEventSink: options.nestedRoute?.eventSink,\n\t\tparentControlInbox: options.nestedRoute?.controlInbox,\n\t\tparentRootRunId: options.nestedRoute?.rootRunId,\n\t\tparentCapabilityToken: options.nestedRoute?.capabilityToken,\n\t\tparentSessionId: options.parentSessionId,\n\t\tstructuredOutput: options.structuredOutput,\n\t\ttoolBudget: options.toolBudget,\n\t\tallowZeroToolBudget: options.allowZeroToolBudget,\n\t\tpermissionRules,\n\t\tpermissionAuditPath,\n\t\tchildWatchdog,\n\t\twaitToolEnabled: options.waitToolEnabled,\n\t\tcapabilityCeiling: options.capabilityCeiling,\n\t});\n\n\tconst effectiveSystemPrompt = appendTurnBudgetSystemPrompt(shared.systemPrompt, options.turnBudget);\n\tconst toolPlan = resolvePiLaunchToolPlan({\n\t\ttools: agent.tools,\n\t\textensions: agent.extensions,\n\t\tsubagentOnlyExtensions: agent.subagentOnlyExtensions,\n\t\tmcpDirectTools: agent.mcpDirectTools,\n\t\tcwd: options.cwd ?? runtimeCwd,\n\t\trequireReadTool: Boolean(shared.resolvedSkillNames?.length),\n\t\tstructuredOutput: Boolean(options.structuredOutput),\n\t\tcapabilityCeiling: options.capabilityCeiling,\n\t\tinheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),\n\t\tagentName: agent.name,\n\t});\n\tconst launchResolvedExtensions = projectLaunchResolvedChildExtensions(toolPlan);\n\tconst launchContractDigest = launchBindingDigest({\n\t\tdefinitionDigest: agentDefinitionDigest(agent),\n\t\ttask: shared.originalTask ?? task,\n\t\t...(modelArg ? { model: modelArg } : {}),\n\t\tmodelCandidates: shared.modelCandidates,\n\t\t...(resolvedThinking ? { thinking: resolvedThinking } : {}),\n\t\tsystemPrompt: effectiveSystemPrompt,\n\t\tsystemPromptMode: agent.systemPromptMode,\n\t\tinheritProjectContext: agent.inheritProjectContext,\n\t\tinheritSkills: agent.inheritSkills,\n\t\tskills: shared.resolvedSkillNames ?? [],\n\t\ttools: toolPlan.effectiveToolAllowlist,\n\t\textensions: toolPlan.extensionArgs,\n\t\tmcpDirectTools: toolPlan.effectiveMcpTools,\n\t\t...(options.outputPath ? { outputPath: options.outputPath } : {}),\n\t\toutputMode: options.outputMode ?? \"inline\",\n\t\t...(options.structuredOutput ? { structuredOutputSchema: options.structuredOutput.schema } : {}),\n\t});\n\tconst result: SingleResult = withRunContext(\n\t\t{\n\t\t\tindex: options.index ?? 0,\n\t\t\tagent: agent.name,\n\t\t\ttask: shared.originalTask ?? task,\n\t\t\t...(options.agentContract ? { agentContract: options.agentContract } : {}),\n\t\t\tlaunchContractDigest,\n\t\t\tlaunchResolvedExtensions,\n\t\t\texitCode: 0,\n\t\t\toutputState: \"absent\",\n\t\t\tmessages: [],\n\t\t\tusage: emptyUsage(),\n\t\t\tmodel: modelArg,\n\t\t\t...(resolvedThinking ? { thinking: resolvedThinking } : {}),\n\t\t\tartifactPaths: shared.artifactPaths,\n\t\t\ttranscriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,\n\t\t\tskills: shared.resolvedSkillNames,\n\t\t\tskillsWarning: shared.skillsWarning,\n\t\t\t...(options.turnBudget ? { turnBudget: initialTurnBudgetState(options.turnBudget) } : {}),\n\t\t\t...(options.toolBudget ? { toolBudget: initialToolBudgetState(options.toolBudget) } : {}),\n\t\t\t...(options.capabilityCeiling ? { capabilityCeiling: options.capabilityCeiling } : {}),\n\t\t\t...(capabilityAudit ? { capabilityAudit } : {}),\n\t\t},\n\t\toptions.context,\n\t);\n\tconst startTime = Date.now();\n\tif (options.structuredOutput) {\n\t\ttry {\n\t\t\tif (existsSync(options.structuredOutput.outputPath)) unlinkSync(options.structuredOutput.outputPath);\n\t\t} catch {\n\t\t\t// Missing/stale structured-output files are handled after the child exits.\n\t\t}\n\t}\n\tconst controlConfig = options.controlConfig ?? DEFAULT_CONTROL_CONFIG;\n\tlet interruptedByControl = false;\n\tconst allControlEvents: ControlEvent[] = [];\n\tlet pendingControlEvents: ControlEvent[] = [];\n\tconst emittedControlEventKeys = new Set<string>();\n\tconst emitControlEvent = (event: ControlEvent) => {\n\t\tif (!shouldNotifyControlEvent(controlConfig, event)) return;\n\t\tif (!claimControlNotification(controlConfig, event, emittedControlEventKeys)) return;\n\t\tallControlEvents.push(event);\n\t\tpendingControlEvents.push(event);\n\t\toptions.onControlEvent?.(event);\n\t};\n\n\tconst progress: AgentProgress = {\n\t\tindex: options.index ?? 0,\n\t\tagent: agent.name,\n\t\tstatus: \"running\",\n\t\ttask,\n\t\tskills: shared.resolvedSkillNames,\n\t\trecentTools: [],\n\t\trecentOutput: [...shared.attemptNotes],\n\t\ttoolCount: 0,\n\t\ttokens: 0,\n\t\t...(modelArg ? { model: modelArg } : {}),\n\t\t...(resolvedThinking ? { thinking: resolvedThinking } : {}),\n\t\tinputTokens: 0,\n\t\toutputTokens: 0,\n\t\tdurationMs: 0,\n\t\tlastActivityAt: startTime,\n\t};\n\tresult.progress = progress;\n\tconst attemptTimeout = resolveAttemptTimeout(options);\n\tif (attemptTimeout?.remainingMs === 0) {\n\t\tcleanupTempDir(tempDir);\n\t\tresult.exitCode = 1;\n\t\tresult.timedOut = true;\n\t\tresult.error = attemptTimeout.message;\n\t\tresult.finalOutput = attemptTimeout.message;\n\t\tprogress.status = \"failed\";\n\t\tprogress.error = attemptTimeout.message;\n\t\tresult.progressSummary = {\n\t\t\ttoolCount: progress.toolCount,\n\t\t\ttokens: progress.tokens,\n\t\t\tdurationMs: progress.durationMs,\n\t\t};\n\t\treturn result;\n\t}\n\tconst spawnEnv = { ...process.env, ...sharedEnv, ...getSubagentDepthEnv(options.maxSubagentDepth) };\n\tlet observedMutationAttempt = false;\n\tlet structuredOutputToolInvoked = false;\n\n\tconst exitCode = await new Promise<number>((resolve) => {\n\t\tconst spawnSpec = getPiSpawnCommand(args);\n\t\tconst proc = spawn(spawnSpec.command, spawnSpec.args, {\n\t\t\tcwd: options.cwd ?? runtimeCwd,\n\t\t\tenv: spawnEnv,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t});\n\t\tconst jsonlWriter = createJsonlWriter(shared.jsonlPath, proc.stdout);\n\t\tlet processClosed = false;\n\t\tlet lifecycleFinished = false;\n\t\tlet detached = false;\n\t\tlet intercomStarted = false;\n\t\tlet assistantError: string | undefined;\n\t\tlet removeAbortListener: (() => void) | undefined;\n\t\tlet removeInterruptListener: (() => void) | undefined;\n\t\tlet activityTimer: NodeJS.Timeout | undefined;\n\t\tlet timeoutTimer: NodeJS.Timeout | undefined;\n\t\tlet timeoutTerminationTimer: NodeJS.Timeout | undefined;\n\t\tlet timeoutHardKillTimer: NodeJS.Timeout | undefined;\n\t\tlet turnBudgetSoftReached = false;\n\t\tlet turnBudgetTerminationTimer: NodeJS.Timeout | undefined;\n\t\tlet turnBudgetHardKillTimer: NodeJS.Timeout | undefined;\n\t\tlet protocolHardKillTimer: NodeJS.Timeout | undefined;\n\t\tconst clearTurnBudgetTimers = () => {\n\t\t\tif (turnBudgetTerminationTimer) {\n\t\t\t\tclearTimeout(turnBudgetTerminationTimer);\n\t\t\t\tturnBudgetTerminationTimer = undefined;\n\t\t\t}\n\t\t\tif (turnBudgetHardKillTimer) {\n\t\t\t\tclearTimeout(turnBudgetHardKillTimer);\n\t\t\t\tturnBudgetHardKillTimer = undefined;\n\t\t\t}\n\t\t};\n\t\tconst clearTimeoutTimers = () => {\n\t\t\tif (timeoutTimer) {\n\t\t\t\tclearTimeout(timeoutTimer);\n\t\t\t\ttimeoutTimer = undefined;\n\t\t\t}\n\t\t\tif (timeoutTerminationTimer) {\n\t\t\t\tclearTimeout(timeoutTerminationTimer);\n\t\t\t\ttimeoutTerminationTimer = undefined;\n\t\t\t}\n\t\t\tif (timeoutHardKillTimer) {\n\t\t\t\tclearTimeout(timeoutHardKillTimer);\n\t\t\t\ttimeoutHardKillTimer = undefined;\n\t\t\t}\n\t\t};\n\n\t\tconst detachForeground = (reason: string): boolean => {\n\t\t\tif (detached || processClosed || lifecycleFinished || options.signal?.aborted) return false;\n\t\t\tconst receiptProgress = snapshotProgress(progress);\n\t\t\treceiptProgress.status = \"detached\";\n\t\t\treceiptProgress.durationMs = Date.now() - startTime;\n\t\t\tconst receipt = snapshotResult(result, receiptProgress);\n\t\t\treceipt.exitCode = -2;\n\t\t\treceipt.detached = true;\n\t\t\treceipt.detachedReason = reason;\n\t\t\treceipt.finalOutput =\n\t\t\t\treason === \"intercom coordination\"\n\t\t\t\t\t? \"Detached for intercom coordination before task completion.\"\n\t\t\t\t\t: reason === \"user request\"\n\t\t\t\t\t\t? \"Detached at user request before task completion.\"\n\t\t\t\t\t\t: `Detached for ${reason} before task completion.`;\n\t\t\treceipt.outputMode = options.outputMode ?? \"inline\";\n\t\t\tif (options.outputPath) {\n\t\t\t\treceipt.outputSaveError =\n\t\t\t\t\treason === \"user request\"\n\t\t\t\t\t\t? \"Output file was not finalized because the subagent detached at user request.\"\n\t\t\t\t\t\t: `Output file was not finalized because the subagent detached for ${reason}.`;\n\t\t\t}\n\t\t\treceipt.progressSummary = {\n\t\t\t\ttoolCount: receiptProgress.toolCount,\n\t\t\t\ttokens: receiptProgress.tokens,\n\t\t\t\tdurationMs: receiptProgress.durationMs,\n\t\t\t};\n\t\t\t// The attempt is not detached until the outer coordinator has accepted and\n\t\t\t// synchronously published the receipt. A persistence/callback failure must\n\t\t\t// leave this attempt attached and abortable rather than orphaning it.\n\t\t\tlet accepted = false;\n\t\t\ttry {\n\t\t\t\taccepted = options.onDetachReceipt?.(receipt) === true;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!accepted) return false;\n\t\t\tdetached = true;\n\t\t\treturn true;\n\t\t};\n\n\t\t// If the child emits a terminal assistant stop but never exits,\n\t\t// give it a short grace period to flush naturally, then clean it up.\n\t\tconst FINAL_STOP_GRACE_MS = 1000;\n\t\tconst HARD_KILL_MS = 3000;\n\t\tlet childExited = false;\n\t\tlet forcedTerminationSignal = false;\n\t\tlet cleanTerminalAssistantStopReceived = false;\n\t\tlet agentSettledReceived = false;\n\t\tlet finalDrainTimer: NodeJS.Timeout | undefined;\n\t\tlet finalHardKillTimer: NodeJS.Timeout | undefined;\n\t\tlet watchdogTailTimer: NodeJS.Timeout | undefined;\n\t\tlet childWatchdogState: ChildWatchdogStateSnapshot | undefined;\n\t\tconst updateChildWatchdogState = (snapshot: ChildWatchdogStateSnapshot): void => {\n\t\t\tchildWatchdogState = snapshot;\n\t\t\tresult.watchdog = snapshot;\n\t\t\tprogress.watchdog = snapshot;\n\t\t};\n\t\tconst clearWatchdogTailTimer = () => {\n\t\t\tif (watchdogTailTimer) {\n\t\t\t\tclearTimeout(watchdogTailTimer);\n\t\t\t\twatchdogTailTimer = undefined;\n\t\t\t}\n\t\t};\n\t\tconst clearFinalDrainTimers = () => {\n\t\t\tif (finalDrainTimer) {\n\t\t\t\tclearTimeout(finalDrainTimer);\n\t\t\t\tfinalDrainTimer = undefined;\n\t\t\t}\n\t\t\tif (finalHardKillTimer) {\n\t\t\t\tclearTimeout(finalHardKillTimer);\n\t\t\t\tfinalHardKillTimer = undefined;\n\t\t\t}\n\t\t};\n\t\tconst startFinalDrain = () => {\n\t\t\tif (childWatchdogIsActive(childWatchdogState)) {\n\t\t\t\tarmWatchdogTail();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (childExited || finalDrainTimer || lifecycleFinished || processClosed) return;\n\t\t\tfinalDrainTimer = setTimeout(() => {\n\t\t\t\tif (lifecycleFinished || processClosed) return;\n\t\t\t\tconst termSent = trySignalChild(proc, \"SIGTERM\");\n\t\t\t\tif (!termSent) return;\n\t\t\t\tforcedTerminationSignal = true;\n\t\t\t\tif (!cleanTerminalAssistantStopReceived && !agentSettledReceived && !assistantError) {\n\t\t\t\t\tresult.error =\n\t\t\t\t\t\tresult.error ??\n\t\t\t\t\t\t`Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its terminal event. Forcing termination.`;\n\t\t\t\t}\n\t\t\t\tfinalHardKillTimer = setTimeout(() => {\n\t\t\t\t\tif (lifecycleFinished || processClosed) return;\n\t\t\t\t\tforcedTerminationSignal = trySignalChild(proc, \"SIGKILL\") || forcedTerminationSignal;\n\t\t\t\t}, HARD_KILL_MS);\n\t\t\t\tfinalHardKillTimer.unref?.();\n\t\t\t}, FINAL_STOP_GRACE_MS);\n\t\t\tfinalDrainTimer.unref?.();\n\t\t};\n\t\tfunction armWatchdogTail(): void {\n\t\t\tif (\n\t\t\t\t(!cleanTerminalAssistantStopReceived && !agentSettledReceived) ||\n\t\t\t\twatchdogTailTimer ||\n\t\t\t\tlifecycleFinished ||\n\t\t\t\tprocessClosed\n\t\t\t)\n\t\t\t\treturn;\n\t\t\twatchdogTailTimer = setTimeout(() => {\n\t\t\t\twatchdogTailTimer = undefined;\n\t\t\t\tupdateChildWatchdogState({\n\t\t\t\t\tphase: \"stale\",\n\t\t\t\t\tseq: (childWatchdogState?.seq ?? 0) + 1,\n\t\t\t\t\tlastUpdate: Date.now(),\n\t\t\t\t\tfollowUpPending: false,\n\t\t\t\t\treason: \"child watchdog tail timeout\",\n\t\t\t\t\ttimedOut: true,\n\t\t\t\t});\n\t\t\t\tstartFinalDrain();\n\t\t\t\tfireUpdate();\n\t\t\t}, childWatchdog?.watchdogTailTimeoutMs ?? 120_000);\n\t\t\twatchdogTailTimer.unref?.();\n\t\t}\n\t\tconst applyChildLifecycle = (action: ChildLifecycleAction): void => {\n\t\t\tif (action === \"cancel-drain\") {\n\t\t\t\tclearFinalDrainTimers();\n\t\t\t\tclearWatchdogTailTimer();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (action === \"start-drain\") startFinalDrain();\n\t\t};\n\n\t\tconst unsubscribeIntercomDetach = options.intercomEvents?.on?.(INTERCOM_DETACH_REQUEST_EVENT, (payload) => {\n\t\t\tif (!options.allowIntercomDetach || processClosed) return;\n\t\t\tif (!payload || typeof payload !== \"object\") return;\n\t\t\tconst event = payload as { requestId?: unknown; runId?: unknown; agent?: unknown; childIndex?: unknown };\n\t\t\tconst requestId = event.requestId;\n\t\t\tif (typeof requestId !== \"string\" || requestId.length === 0) return;\n\t\t\tconst hasRoute = event.runId !== undefined || event.agent !== undefined || event.childIndex !== undefined;\n\t\t\tif (hasRoute) {\n\t\t\t\tif (typeof event.runId === \"string\" && event.runId !== options.runId) return;\n\t\t\t\tif (typeof event.agent === \"string\" && event.agent !== agent.name) return;\n\t\t\t\tif (typeof event.childIndex === \"number\" && event.childIndex !== (options.index ?? 0)) return;\n\t\t\t} else if (!intercomStarted) return;\n\t\t\tconst accepted = detachForeground(\"intercom coordination\");\n\t\t\toptions.intercomEvents?.emit(INTERCOM_DETACH_RESPONSE_EVENT, {\n\t\t\t\trequestId,\n\t\t\t\taccepted,\n\t\t\t\trunId: options.runId,\n\t\t\t\tagent: agent.name,\n\t\t\t\tchildIndex: options.index ?? 0,\n\t\t\t});\n\t\t});\n\n\t\tconst finish = (code: number) => {\n\t\t\tif (lifecycleFinished) return;\n\t\t\tlifecycleFinished = true;\n\t\t\tclearFinalDrainTimers();\n\t\t\tclearWatchdogTailTimer();\n\t\t\tclearStdioGuard();\n\t\t\tclearTimeoutTimers();\n\t\t\tclearTurnBudgetTimers();\n\t\t\tif (protocolHardKillTimer) {\n\t\t\t\tclearTimeout(protocolHardKillTimer);\n\t\t\t\tprotocolHardKillTimer = undefined;\n\t\t\t}\n\t\t\tif (activityTimer) {\n\t\t\t\tclearInterval(activityTimer);\n\t\t\t\tactivityTimer = undefined;\n\t\t\t}\n\t\t\tunsubscribeIntercomDetach?.();\n\t\t\tremoveAbortListener?.();\n\t\t\tremoveInterruptListener?.();\n\t\t\tresolve(code);\n\t\t};\n\n\t\tconst drainPendingControlEvents = (): ControlEvent[] | undefined => {\n\t\t\tif (pendingControlEvents.length === 0) return undefined;\n\t\t\tconst events = pendingControlEvents;\n\t\t\tpendingControlEvents = [];\n\t\t\treturn events;\n\t\t};\n\n\t\tlet activeLongRunningNotified = false;\n\t\tlet pendingToolResult: { tool: string; path?: string; mutates: boolean; startedAt?: number } | undefined;\n\t\tconst mutatingFailures = createMutatingFailureState();\n\t\tconst mutatingFailureWindowMs = 5 * 60_000;\n\t\tconst currentToolDurationMs = (now: number) =>\n\t\t\tprogress.currentToolStartedAt ? Math.max(0, now - progress.currentToolStartedAt) : undefined;\n\t\tconst emitNeedsAttention = (\n\t\t\tnow: number,\n\t\t\tinput: {\n\t\t\t\tmessage?: string;\n\t\t\t\treason?: ControlEvent[\"reason\"];\n\t\t\t\trecentFailureSummary?: string;\n\t\t\t\tcurrentTool?: string;\n\t\t\t\tcurrentPath?: string;\n\t\t\t\tcurrentToolDurationMs?: number;\n\t\t\t} = {},\n\t\t): boolean => {\n\t\t\tif (!controlConfig.enabled) return false;\n\t\t\tconst previous = progress.activityState;\n\t\t\tprogress.activityState = \"needs_attention\";\n\t\t\tconst event = buildControlEvent({\n\t\t\t\ttype: \"needs_attention\",\n\t\t\t\tfrom: previous,\n\t\t\t\tto: \"needs_attention\",\n\t\t\t\trunId: options.runId,\n\t\t\t\tagent: agent.name,\n\t\t\t\tindex: options.index,\n\t\t\t\tts: now,\n\t\t\t\tlastActivityAt: progress.lastActivityAt,\n\t\t\t\tmessage: input.message,\n\t\t\t\treason: input.reason ?? \"idle\",\n\t\t\t\tturns: result.usage.turns,\n\t\t\t\ttokens: progress.tokens,\n\t\t\t\ttoolCount: progress.toolCount,\n\t\t\t\tcurrentTool: input.currentTool ?? progress.currentTool,\n\t\t\t\tcurrentToolDurationMs: input.currentToolDurationMs ?? currentToolDurationMs(now),\n\t\t\t\tcurrentPath: input.currentPath ?? progress.currentPath,\n\t\t\t\trecentFailureSummary: input.recentFailureSummary,\n\t\t\t});\n\t\t\temitControlEvent(event);\n\t\t\treturn previous !== \"needs_attention\";\n\t\t};\n\t\tconst emitActiveLongRunning = (now: number, reason: ControlEvent[\"reason\"]): boolean => {\n\t\t\tif (!controlConfig.enabled || activeLongRunningNotified || progress.activityState === \"needs_attention\")\n\t\t\t\treturn false;\n\t\t\tactiveLongRunningNotified = true;\n\t\t\tconst previous = progress.activityState;\n\t\t\tprogress.activityState = \"active_long_running\";\n\t\t\temitControlEvent(\n\t\t\t\tbuildControlEvent({\n\t\t\t\t\ttype: \"active_long_running\",\n\t\t\t\t\tfrom: previous,\n\t\t\t\t\tto: \"active_long_running\",\n\t\t\t\t\trunId: options.runId,\n\t\t\t\t\tagent: agent.name,\n\t\t\t\t\tindex: options.index,\n\t\t\t\t\tts: now,\n\t\t\t\t\tmessage: `${agent.name} is still active but long-running`,\n\t\t\t\t\treason,\n\t\t\t\t\tturns: result.usage.turns,\n\t\t\t\t\ttokens: progress.tokens,\n\t\t\t\t\ttoolCount: progress.toolCount,\n\t\t\t\t\tcurrentTool: progress.currentTool,\n\t\t\t\t\tcurrentToolDurationMs: currentToolDurationMs(now),\n\t\t\t\t\tcurrentPath: progress.currentPath,\n\t\t\t\t\telapsedMs: now - startTime,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn true;\n\t\t};\n\t\tconst requestTurnBudgetAbort = (turnCount: number) => {\n\t\t\tconst budget = options.turnBudget;\n\t\t\tif (\n\t\t\t\t!budget ||\n\t\t\t\tresult.timedOut ||\n\t\t\t\tresult.turnBudgetExceeded ||\n\t\t\t\tinterruptedByControl ||\n\t\t\t\tprocessClosed ||\n\t\t\t\tlifecycleFinished\n\t\t\t)\n\t\t\t\treturn;\n\t\t\tconst message = turnBudgetExceededMessage(budget, turnCount);\n\t\t\tresult.turnBudgetExceeded = true;\n\t\t\tresult.wrapUpRequested = true;\n\t\t\tresult.turnBudget = turnBudgetState(budget, turnCount, true);\n\t\t\tresult.error = message;\n\t\t\tresult.finalOutput = message;\n\t\t\tprogress.status = \"failed\";\n\t\t\tprogress.error = message;\n\t\t\tprogress.durationMs = Date.now() - startTime;\n\t\t\tfireUpdate();\n\t\t\ttrySignalChild(proc, \"SIGINT\");\n\t\t\tturnBudgetTerminationTimer = setTimeout(() => {\n\t\t\t\tif (processClosed || lifecycleFinished || result.timedOut) return;\n\t\t\t\ttrySignalChild(proc, \"SIGTERM\");\n\t\t\t}, 1000);\n\t\t\tturnBudgetTerminationTimer.unref?.();\n\t\t\tturnBudgetHardKillTimer = setTimeout(() => {\n\t\t\t\tif (processClosed || lifecycleFinished || result.timedOut) return;\n\t\t\t\ttrySignalChild(proc, \"SIGKILL\");\n\t\t\t}, 4000);\n\t\t\tturnBudgetHardKillTimer.unref?.();\n\t\t};\n\n\t\tconst updateTurnBudget = (\n\t\t\tturnCount: number,\n\t\t\tterminalAssistantStop: boolean,\n\t\t\ttoolWorkActiveOrStarting: boolean,\n\t\t) => {\n\t\t\tconst budget = options.turnBudget;\n\t\t\tif (!budget || result.timedOut || result.turnBudgetExceeded) return;\n\t\t\tif (turnCount < budget.maxTurns) {\n\t\t\t\tresult.turnBudget = { ...budget, outcome: \"within-budget\", turnCount };\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!turnBudgetSoftReached) {\n\t\t\t\tturnBudgetSoftReached = true;\n\t\t\t\tresult.wrapUpRequested = true;\n\t\t\t\tappendRecentOutput(progress, [turnBudgetSoftNote(budget, turnCount)]);\n\t\t\t}\n\t\t\tconst decision = turnBudgetDecision(\n\t\t\t\tbudget,\n\t\t\t\tturnCount,\n\t\t\t\tterminalAssistantStop,\n\t\t\t\ttoolWorkActiveOrStarting,\n\t\t\t\toptions.enforceHardTurnLimit,\n\t\t\t);\n\t\t\tif (decision === \"defer\") {\n\t\t\t\tresult.turnBudget = turnBudgetDeferredState(\n\t\t\t\t\tbudget,\n\t\t\t\t\tturnCount,\n\t\t\t\t\tresult.turnBudget?.terminationDeferredAtTurn,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresult.turnBudget = turnBudgetState(budget, turnCount, false);\n\t\t\tif (decision === \"abort\") requestTurnBudgetAbort(turnCount);\n\t\t};\n\n\t\tconst updateActivityState = (now: number): boolean => {\n\t\t\tif (!controlConfig.enabled) return false;\n\t\t\tconst idleState = deriveActivityState({\n\t\t\t\tconfig: controlConfig,\n\t\t\t\tstartedAt: startTime,\n\t\t\t\tlastActivityAt: progress.lastActivityAt,\n\t\t\t\tcurrentTool: progress.currentTool,\n\t\t\t\tnow,\n\t\t\t});\n\t\t\tif (idleState === \"needs_attention\") {\n\t\t\t\treturn progress.activityState === \"needs_attention\" ? false : emitNeedsAttention(now);\n\t\t\t}\n\t\t\tconst activeReason = nextLongRunningTrigger(controlConfig, {\n\t\t\t\tstartedAt: startTime,\n\t\t\t\tnow,\n\t\t\t\tturns: result.usage.turns,\n\t\t\t\ttokens: progress.tokens,\n\t\t\t});\n\t\t\treturn activeReason ? emitActiveLongRunning(now, activeReason) : false;\n\t\t};\n\n\t\tconst emitUpdateSnapshot = (text: string) => {\n\t\t\tif (!options.onUpdate || processClosed) return;\n\t\t\tconst progressSnapshot = snapshotProgress(progress);\n\t\t\tconst resultSnapshot = snapshotStreamResult(result, progressSnapshot);\n\t\t\tconst controlEvents = drainPendingControlEvents();\n\t\t\toptions.onUpdate({\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: {\n\t\t\t\t\tmode: \"single\",\n\t\t\t\t\tresults: [resultSnapshot],\n\t\t\t\t\tprogress: [progressSnapshot],\n\t\t\t\t\tcontrolEvents,\n\t\t\t\t},\n\t\t\t});\n\t\t};\n\n\t\tconst fireUpdate = () => {\n\t\t\tif (!options.onUpdate || processClosed) return;\n\t\t\tprogress.durationMs = Date.now() - startTime;\n\t\t\tconst output =\n\t\t\t\t(result.timedOut || result.turnBudgetExceeded) && result.finalOutput\n\t\t\t\t\t? result.finalOutput\n\t\t\t\t\t: getFinalOutput(result.messages ?? []);\n\t\t\temitUpdateSnapshot(output || \"(running...)\");\n\t\t};\n\n\t\tconst rawStdoutTail = createBoundedByteTail();\n\t\tconst processLine = (line: string) => {\n\t\t\tif (!line.trim()) return;\n\t\t\tjsonlWriter.writeLine(line);\n\t\t\tlet evt: { type?: string; message?: Message; toolName?: string; args?: unknown; willRetry?: unknown };\n\t\t\ttry {\n\t\t\t\tevt = JSON.parse(line) as {\n\t\t\t\t\ttype?: string;\n\t\t\t\t\tmessage?: Message;\n\t\t\t\t\ttoolName?: string;\n\t\t\t\t\targs?: unknown;\n\t\t\t\t\twillRetry?: unknown;\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\trawStdoutTail.push(`${line}\\n`);\n\t\t\t\tshared.transcriptWriter?.writeStdoutLine(line);\n\t\t\t\t// Non-JSON stdout lines are expected; only structured events are parsed.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tshared.transcriptWriter?.writeChildEvent(evt);\n\t\t\tif (evt.type === \"agent_settled\") agentSettledReceived = true;\n\t\t\tapplyChildLifecycle(projectChildLifecycle(evt));\n\n\t\t\tif (isChildWatchdogStatusEvent(evt)) {\n\t\t\t\tif (!childWatchdog) return;\n\t\t\t\tconst next = acceptChildWatchdogEvent({\n\t\t\t\t\tcurrent: childWatchdogState,\n\t\t\t\t\tevent: evt,\n\t\t\t\t\trunId: options.runId,\n\t\t\t\t\tagent: agent.name,\n\t\t\t\t\tchildIndex: options.index ?? 0,\n\t\t\t\t});\n\t\t\t\tif (!next) return;\n\t\t\t\tupdateChildWatchdogState(next);\n\t\t\t\tif (childWatchdogIsActive(next)) {\n\t\t\t\t\tclearFinalDrainTimers();\n\t\t\t\t\tarmWatchdogTail();\n\t\t\t\t} else {\n\t\t\t\t\tclearWatchdogTailTimer();\n\t\t\t\t\tif (cleanTerminalAssistantStopReceived || agentSettledReceived) startFinalDrain();\n\t\t\t\t}\n\t\t\t\tfireUpdate();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst now = Date.now();\n\t\t\tprogress.durationMs = now - startTime;\n\t\t\tprogress.lastActivityAt = now;\n\t\t\tupdateActivityState(now);\n\n\t\t\tif (evt.type === \"tool_execution_start\") {\n\t\t\t\tconst toolArgs =\n\t\t\t\t\tevt.args && typeof evt.args === \"object\" && !Array.isArray(evt.args)\n\t\t\t\t\t\t? (evt.args as Record<string, unknown>)\n\t\t\t\t\t\t: {};\n\t\t\t\tif (options.structuredOutput && evt.toolName === \"structured_output\") structuredOutputToolInvoked = true;\n\t\t\t\tif (options.allowIntercomDetach && (evt.toolName === \"intercom\" || evt.toolName === \"contact_supervisor\")) {\n\t\t\t\t\tintercomStarted = true;\n\t\t\t\t}\n\t\t\t\tprogress.toolCount++;\n\t\t\t\tif (options.toolBudget) {\n\t\t\t\t\tresult.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount);\n\t\t\t\t}\n\t\t\t\tprogress.currentTool = evt.toolName;\n\t\t\t\tprogress.currentToolArgs = extractToolArgsPreview(toolArgs);\n\t\t\t\tprogress.currentToolStartedAt = now;\n\t\t\t\tprogress.currentPath = resolveCurrentPath(evt.toolName, toolArgs);\n\t\t\t\tconst mutates = isMutatingTool(evt.toolName, toolArgs);\n\t\t\t\tobservedMutationAttempt = observedMutationAttempt || mutates;\n\t\t\t\tpendingToolResult = { tool: evt.toolName ?? \"tool\", path: progress.currentPath, mutates, startedAt: now };\n\t\t\t\tfireUpdate();\n\t\t\t}\n\n\t\t\tif (evt.type === \"tool_execution_end\") {\n\t\t\t\tif (progress.currentTool) {\n\t\t\t\t\tprogress.recentTools.push({\n\t\t\t\t\t\ttool: progress.currentTool,\n\t\t\t\t\t\targs: progress.currentToolArgs || \"\",\n\t\t\t\t\t\tendMs: now,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tprogress.currentTool = undefined;\n\t\t\t\tprogress.currentToolArgs = undefined;\n\t\t\t\tprogress.currentToolStartedAt = undefined;\n\t\t\t\tprogress.currentPath = undefined;\n\t\t\t\tfireUpdate();\n\t\t\t}\n\n\t\t\tif (evt.type === \"message_end\" && evt.message) {\n\t\t\t\tresult.messages!.push(evt.message);\n\t\t\t\tif (evt.message.role === \"assistant\") {\n\t\t\t\t\tresult.usage.turns++;\n\t\t\t\t\tprogress.turnCount = result.usage.turns;\n\t\t\t\t\tconst stopReason = (evt.message as { stopReason?: string }).stopReason;\n\t\t\t\t\tconst toolCalls = Array.isArray(evt.message.content)\n\t\t\t\t\t\t? evt.message.content.filter((part) => (part as { type?: string }).type === \"toolCall\")\n\t\t\t\t\t\t: [];\n\t\t\t\t\tconst hasToolCall = toolCalls.length > 0;\n\t\t\t\t\tconst terminalAssistantStop = stopReason === \"stop\" && !hasToolCall;\n\t\t\t\t\tconst terminalStructuredOutputCall =\n\t\t\t\t\t\tBoolean(options.structuredOutput) &&\n\t\t\t\t\t\ttoolCalls.length === 1 &&\n\t\t\t\t\t\t(toolCalls[0] as { name?: string }).name === \"structured_output\";\n\t\t\t\t\tupdateTurnBudget(\n\t\t\t\t\t\tresult.usage.turns,\n\t\t\t\t\t\tterminalAssistantStop || terminalStructuredOutputCall,\n\t\t\t\t\t\thasToolCall || Boolean(progress.currentTool),\n\t\t\t\t\t);\n\t\t\t\t\tconst u = evt.message.usage;\n\t\t\t\t\tif (u) {\n\t\t\t\t\t\tresult.usage.input += u.input || 0;\n\t\t\t\t\t\tresult.usage.output += u.output || 0;\n\t\t\t\t\t\tresult.usage.cacheRead += u.cacheRead || 0;\n\t\t\t\t\t\tresult.usage.cacheWrite += u.cacheWrite || 0;\n\t\t\t\t\t\tresult.usage.cost += u.cost?.total || 0;\n\t\t\t\t\t\tprogress.tokens = result.usage.input + result.usage.output;\n\t\t\t\t\t\tprogress.inputTokens = result.usage.input;\n\t\t\t\t\t\tprogress.outputTokens = result.usage.output;\n\t\t\t\t\t}\n\t\t\t\t\tif (evt.message.model) {\n\t\t\t\t\t\tprogress.model = evt.message.model;\n\t\t\t\t\t\tif (!result.model) result.model = evt.message.model;\n\t\t\t\t\t}\n\t\t\t\t\tif (evt.message.errorMessage) assistantError = evt.message.errorMessage;\n\t\t\t\t\tconst assistantText = extractTextFromContent(evt.message.content);\n\t\t\t\t\tappendRecentOutput(progress, assistantText.split(\"\\n\").slice(-10));\n\t\t\t\t\t// Final assistant message: start the exit drain window.\n\t\t\t\t\tif (terminalAssistantStop) {\n\t\t\t\t\t\tif (!evt.message.errorMessage && assistantText.trim()) assistantError = undefined;\n\t\t\t\t\t\tcleanTerminalAssistantStopReceived ||= !evt.message.errorMessage;\n\t\t\t\t\t\tapplyChildLifecycle(projectChildLifecycle(evt, true));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tupdateActivityState(now);\n\t\t\t\tfireUpdate();\n\t\t\t}\n\n\t\t\tif (evt.type === \"tool_result_end\" && evt.message) {\n\t\t\t\tresult.messages!.push(evt.message);\n\t\t\t\tconst resultText = extractTextFromContent(evt.message.content);\n\t\t\t\tif (options.toolBudget && pendingToolResult && resultText.includes(\"Tool budget hard limit reached\")) {\n\t\t\t\t\tresult.toolBudgetBlocked = true;\n\t\t\t\t\tresult.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount, pendingToolResult.tool);\n\t\t\t\t}\n\t\t\t\tappendRecentOutput(progress, resultText.split(\"\\n\").slice(-10));\n\t\t\t\tconst toolSnapshot = pendingToolResult;\n\t\t\t\tpendingToolResult = undefined;\n\t\t\t\tif (toolSnapshot?.mutates && didMutatingToolFail(resultText)) {\n\t\t\t\t\trecordMutatingFailure(\n\t\t\t\t\t\tmutatingFailures,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttool: toolSnapshot.tool,\n\t\t\t\t\t\t\tpath: toolSnapshot.path,\n\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\tresultText\n\t\t\t\t\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t\t\t\t\t.find((line) => line.trim())\n\t\t\t\t\t\t\t\t\t?.trim()\n\t\t\t\t\t\t\t\t\t.slice(0, 180) ?? \"mutating tool failed\",\n\t\t\t\t\t\t\tts: now,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmutatingFailureWindowMs,\n\t\t\t\t\t);\n\t\t\t\t\tif (shouldEscalateMutatingFailures(mutatingFailures, controlConfig.failedToolAttemptsBeforeAttention)) {\n\t\t\t\t\t\temitNeedsAttention(now, {\n\t\t\t\t\t\t\tmessage: `${agent.name} needs attention after repeated mutating tool failures`,\n\t\t\t\t\t\t\treason: \"tool_failures\",\n\t\t\t\t\t\t\tcurrentTool: toolSnapshot.tool,\n\t\t\t\t\t\t\tcurrentPath: toolSnapshot.path,\n\t\t\t\t\t\t\tcurrentToolDurationMs: toolSnapshot.startedAt\n\t\t\t\t\t\t\t\t? Math.max(0, now - toolSnapshot.startedAt)\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\trecentFailureSummary: summarizeRecentMutatingFailures(mutatingFailures),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else if (toolSnapshot?.mutates) {\n\t\t\t\t\tresetMutatingFailureState(mutatingFailures);\n\t\t\t\t}\n\t\t\t\tfireUpdate();\n\t\t\t}\n\t\t};\n\n\t\tfireUpdate();\n\t\tif (controlConfig.enabled || options.onUpdate) {\n\t\t\tactivityTimer = setInterval(() => {\n\t\t\t\tif (processClosed || lifecycleFinished) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateActivityState(Date.now());\n\t\t\t\tfireUpdate();\n\t\t\t}, 1000);\n\t\t\tactivityTimer.unref?.();\n\t\t}\n\n\t\tif (attemptTimeout) {\n\t\t\ttimeoutTimer = setTimeout(() => {\n\t\t\t\tif (processClosed || lifecycleFinished || interruptedByControl) return;\n\t\t\t\tresult.timedOut = true;\n\t\t\t\tresult.error = attemptTimeout.message;\n\t\t\t\tresult.finalOutput = attemptTimeout.message;\n\t\t\t\tprogress.status = \"failed\";\n\t\t\t\tprogress.error = attemptTimeout.message;\n\t\t\t\tprogress.durationMs = Date.now() - startTime;\n\t\t\t\tfireUpdate();\n\t\t\t\ttrySignalChild(proc, \"SIGINT\");\n\t\t\t\ttimeoutTerminationTimer = setTimeout(() => {\n\t\t\t\t\tif (processClosed || lifecycleFinished) return;\n\t\t\t\t\ttrySignalChild(proc, \"SIGTERM\");\n\t\t\t\t}, 1000);\n\t\t\t\ttimeoutTerminationTimer.unref?.();\n\t\t\t\ttimeoutHardKillTimer = setTimeout(() => {\n\t\t\t\t\tif (processClosed || lifecycleFinished) return;\n\t\t\t\t\ttrySignalChild(proc, \"SIGKILL\");\n\t\t\t\t}, 4000);\n\t\t\t\ttimeoutHardKillTimer.unref?.();\n\t\t\t}, attemptTimeout.remainingMs);\n\t\t\ttimeoutTimer.unref?.();\n\t\t}\n\n\t\tconst stderrTail = createBoundedByteTail();\n\t\tconst failProtocol = (limit: ProtocolOutputLimit): void => {\n\t\t\tif (result.protocolError) return;\n\t\t\tresult.protocolError = limit;\n\t\t\tresult.error = formatProtocolOutputLimit(limit);\n\t\t\tprogress.status = \"failed\";\n\t\t\tprogress.error = result.error;\n\t\t\tprogress.durationMs = Date.now() - startTime;\n\t\t\tfireUpdate();\n\t\t\tif (!childExited) {\n\t\t\t\ttrySignalChild(proc, \"SIGTERM\");\n\t\t\t\tprotocolHardKillTimer = setTimeout(() => {\n\t\t\t\t\tif (!childExited) trySignalChild(proc, \"SIGKILL\");\n\t\t\t\t}, 3000);\n\t\t\t\tprotocolHardKillTimer.unref?.();\n\t\t\t}\n\t\t};\n\t\tconst stdoutReader = createBoundedLineReader({\n\t\t\toversizedLineProjector: PI_AGGREGATE_EVENT_PROJECTOR,\n\t\t\tonLine: processLine,\n\t\t\tonLimit: failProtocol,\n\t\t});\n\t\tconst stderrReader = createBoundedLineReader({\n\t\t\tstream: \"stderr\",\n\t\t\tmaxPendingLineBytes: MAX_CHILD_STDERR_BYTES,\n\t\t\tonLine: (line) => shared.transcriptWriter?.writeStderrLine(line),\n\t\t\tonLimit: (limit) => shared.transcriptWriter?.writeStderrLine(formatProtocolOutputLimit(limit)),\n\t\t});\n\n\t\tconst clearStdioGuard = attachPostExitStdioGuard(proc, { idleMs: 2000, hardMs: 8000 });\n\t\tproc.stdout.on(\"data\", (chunk: Buffer) => stdoutReader.push(chunk));\n\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\tstderrTail.push(chunk);\n\t\t\tstderrReader.push(chunk);\n\t\t});\n\t\tproc.on(\"exit\", () => {\n\t\t\tchildExited = true;\n\t\t\tclearFinalDrainTimers();\n\t\t});\n\t\tproc.on(\"close\", (code, signal) => {\n\t\t\tif (lifecycleFinished) return;\n\t\t\tprocessClosed = true;\n\t\t\tclearFinalDrainTimers();\n\t\t\tclearStdioGuard();\n\t\t\tvoid jsonlWriter.close().catch(() => {\n\t\t\t\t// JSONL artifact flush is best effort.\n\t\t\t});\n\t\t\tconst toolDiagnosticError = readChildToolDiagnosticError(toolDiagnosticPath);\n\t\t\tresult.runtimeAcknowledgedExtensions = readRuntimeAcknowledgedExtensions(runtimeAcknowledgedExtensionsPath);\n\t\t\tcleanupTempDir(tempDir);\n\t\t\tstdoutReader.end();\n\t\t\tstderrReader.end();\n\t\t\tconst stderr = stderrTail.text();\n\t\t\tconst rawStdout = rawStdoutTail.text();\n\t\t\tlet closeError = result.error ?? toolDiagnosticError ?? assistantError;\n\t\t\tconst forcedDrainAfterFinalSuccess =\n\t\t\t\tBoolean(forcedTerminationSignal || signal) &&\n\t\t\t\t(cleanTerminalAssistantStopReceived || agentSettledReceived) &&\n\t\t\t\t!closeError;\n\t\t\tif (signal) result.processSignal = signal;\n\t\t\tif (\n\t\t\t\t!closeError &&\n\t\t\t\tisUnexplainedProcessSignal({\n\t\t\t\t\tprocessSignal: signal,\n\t\t\t\t\tinterrupted: result.interrupted,\n\t\t\t\t\ttimedOut: result.timedOut,\n\t\t\t\t\tstopped: result.stopped,\n\t\t\t\t\tturnBudgetExceeded: result.turnBudgetExceeded,\n\t\t\t\t\tforcedDrainAfterFinalSuccess,\n\t\t\t\t})\n\t\t\t) {\n\t\t\t\tcloseError = formatProcessSignalError(signal!);\n\t\t\t}\n\t\t\tif (code !== 0 && rawStdout.trim() && !closeError && !forcedDrainAfterFinalSuccess) {\n\t\t\t\tcloseError = rawStdout.trim();\n\t\t\t}\n\t\t\tif (code !== 0 && stderr.trim() && !closeError && !forcedDrainAfterFinalSuccess) {\n\t\t\t\tcloseError = stderr.trim();\n\t\t\t}\n\t\t\tconst finalCode = forcedDrainAfterFinalSuccess\n\t\t\t\t? 0\n\t\t\t\t: forcedTerminationSignal || signal\n\t\t\t\t\t? (code ?? 1)\n\t\t\t\t\t: (code ?? 0);\n\t\t\tif (!result.error && closeError) result.error = closeError;\n\t\t\tfinish(finalCode);\n\t\t});\n\t\tproc.on(\"error\", (error) => {\n\t\t\tif (lifecycleFinished) return;\n\t\t\tprocessClosed = true;\n\t\t\tclearFinalDrainTimers();\n\t\t\tclearStdioGuard();\n\t\t\tvoid jsonlWriter.close().catch(() => {\n\t\t\t\t// JSONL artifact flush is best effort.\n\t\t\t});\n\t\t\tcleanupTempDir(tempDir);\n\t\t\tstdoutReader.end();\n\t\t\tstderrReader.end();\n\t\t\tif (!result.error) {\n\t\t\t\tresult.error = error instanceof Error ? error.message : String(error);\n\t\t\t}\n\t\t\tfinish(1);\n\t\t});\n\n\t\tif (options.signal) {\n\t\t\tconst kill = () => {\n\t\t\t\tif (processClosed || lifecycleFinished) return;\n\t\t\t\tproc.kill(\"SIGTERM\");\n\t\t\t\tsetTimeout(() => !proc.killed && proc.kill(\"SIGKILL\"), 3000);\n\t\t\t};\n\t\t\tif (options.signal.aborted) kill();\n\t\t\telse {\n\t\t\t\toptions.signal.addEventListener(\"abort\", kill, { once: true });\n\t\t\t\tremoveAbortListener = () => options.signal?.removeEventListener(\"abort\", kill);\n\t\t\t}\n\t\t}\n\n\t\tif (options.interruptSignal) {\n\t\t\tconst interrupt = () => {\n\t\t\t\tif (processClosed || lifecycleFinished) return;\n\t\t\t\tif (result.timedOut) return;\n\t\t\t\tinterruptedByControl = true;\n\t\t\t\tclearTimeoutTimers();\n\t\t\t\tprogress.status = \"running\";\n\t\t\t\tprogress.durationMs = Date.now() - startTime;\n\t\t\t\tresult.interrupted = true;\n\t\t\t\tresult.finalOutput = \"Interrupted. Waiting for explicit next action.\";\n\t\t\t\tprogress.activityState = undefined;\n\t\t\t\tfireUpdate();\n\t\t\t\ttrySignalChild(proc, \"SIGINT\");\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (lifecycleFinished || processClosed) return;\n\t\t\t\t\ttrySignalChild(proc, \"SIGTERM\");\n\t\t\t\t}, 1000).unref?.();\n\t\t\t};\n\t\t\tif (options.interruptSignal.aborted) interrupt();\n\t\t\telse {\n\t\t\t\toptions.interruptSignal.addEventListener(\"abort\", interrupt, { once: true });\n\t\t\t\tremoveInterruptListener = () => options.interruptSignal?.removeEventListener(\"abort\", interrupt);\n\t\t\t}\n\t\t}\n\n\t\t// Publish only after every callback and cleanup guard captured by detach or\n\t\t// later lifecycle events has initialized. Consumers may invoke synchronously.\n\t\ttry {\n\t\t\toptions.onDetachReady?.((reason = \"user request\") => detachForeground(reason));\n\t\t} catch (error) {\n\t\t\t// A consumer callback is advisory. Keep the child attached and observable\n\t\t\t// rather than rejecting this attempt and orphaning its live process.\n\t\t\tappendRecentOutput(progress, [\n\t\t\t\t`Foreground detach callback failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t]);\n\t\t}\n\t});\n\tresult.exitCode = exitCode;\n\tif (interruptedByControl) {\n\t\tresult.exitCode = 0;\n\t\tresult.interrupted = true;\n\t\tresult.error = undefined;\n\t\tresult.finalOutput = result.finalOutput || \"Interrupted. Waiting for explicit next action.\";\n\t\tresult.controlEvents = allControlEvents.length ? allControlEvents : undefined;\n\t\tprogress.activityState = undefined;\n\t\tprogress.durationMs = Date.now() - startTime;\n\t\tresult.progressSummary = {\n\t\t\ttoolCount: progress.toolCount,\n\t\t\ttokens: progress.tokens,\n\t\t\tdurationMs: progress.durationMs,\n\t\t};\n\t\treturn result;\n\t}\n\tif (result.error && result.exitCode === 0) {\n\t\tresult.exitCode = 1;\n\t}\n\tif (result.exitCode === 0 && !result.error) {\n\t\tconst messages = result.messages ?? [];\n\t\tconst finalText = getFinalOutput(messages);\n\t\tconst missingStructuredOutput = options.structuredOutput\n\t\t\t? !existsSync(options.structuredOutput.outputPath)\n\t\t\t: false;\n\t\tconst errInfo = detectSubagentError(messages);\n\t\tconst missingOutput = !finalText?.trim() && (!options.structuredOutput || missingStructuredOutput);\n\t\tif (missingOutput && (!errInfo.hasError || hasEmptyTerminalAssistantResponse(messages))) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.error = \"Subagent produced no output (possible model cold-start or empty response).\";\n\t\t} else if (errInfo.hasError) {\n\t\t\tresult.exitCode = errInfo.exitCode ?? 1;\n\t\t\tresult.error = errInfo.details\n\t\t\t\t? `${errInfo.errorType} failed (exit ${errInfo.exitCode}): ${errInfo.details}`\n\t\t\t\t: `${errInfo.errorType} failed with exit code ${errInfo.exitCode}`;\n\t\t}\n\t}\n\tif (options.structuredOutput && result.exitCode === 0 && !result.error) {\n\t\tresult.structuredOutputSchemaPath = options.structuredOutput.schemaPath;\n\t\tresult.structuredOutputPath = options.structuredOutput.outputPath;\n\t\tif (!structuredOutputToolInvoked) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.error = MISSING_STRUCTURED_OUTPUT_CALL_ERROR;\n\t\t\tresult.structuredOutputFailed = true;\n\t\t} else {\n\t\t\tconst structured = await readStructuredOutput({\n\t\t\t\tschema: options.structuredOutput.schema,\n\t\t\t\tschemaPath: options.structuredOutput.schemaPath,\n\t\t\t\toutputPath: options.structuredOutput.outputPath,\n\t\t\t});\n\t\t\tif (structured.error) {\n\t\t\t\tresult.exitCode = 1;\n\t\t\t\tresult.error = structured.error;\n\t\t\t\tresult.structuredOutputFailed = true;\n\t\t\t} else {\n\t\t\t\tresult.structuredOutput = structured.value;\n\t\t\t}\n\t\t}\n\t}\n\n\tprogress.status = result.exitCode === 0 ? \"completed\" : \"failed\";\n\tprogress.durationMs = Date.now() - startTime;\n\tif (result.error) {\n\t\tprogress.error = result.error;\n\t\tif (progress.currentTool) {\n\t\t\tprogress.failedTool = progress.currentTool;\n\t\t}\n\t}\n\n\tresult.progressSummary = {\n\t\ttoolCount: progress.toolCount,\n\t\ttokens: progress.tokens,\n\t\tdurationMs: progress.durationMs,\n\t};\n\n\tconst acceptanceOutput = getFinalOutput(result.messages ?? []);\n\tlet fullOutput = stripAcceptanceReport(acceptanceOutput);\n\tif (!fullOutput.trim() && result.structuredOutput !== undefined)\n\t\tfullOutput = JSON.stringify(result.structuredOutput, null, 2);\n\tresult.outputState = fullOutput.trim() || result.structuredOutput !== undefined ? \"present\" : \"absent\";\n\tif (result.timedOut) {\n\t\tconst timeoutMessage = formatTimeoutMessage(options.timeoutMs ?? 0);\n\t\tfullOutput = fullOutput.trim()\n\t\t\t? `${timeoutMessage}\\n\\nPartial output before timeout:\\n${fullOutput}`\n\t\t\t: timeoutMessage;\n\t} else if (result.turnBudgetExceeded && result.turnBudget) {\n\t\tfullOutput = formatTurnBudgetOutput(\n\t\t\tturnBudgetExceededMessage(result.turnBudget, result.turnBudget.turnCount),\n\t\t\tfullOutput,\n\t\t);\n\t} else if (result.turnBudget?.outcome === \"termination-deferred\") {\n\t\tconst note = turnBudgetDeferredNote(\n\t\t\tresult.turnBudget,\n\t\t\tresult.turnBudget.terminationDeferredAtTurn ?? result.turnBudget.turnCount,\n\t\t);\n\t\tfullOutput = fullOutput.trim() ? `${note}\\n\\n${fullOutput}` : note;\n\t} else if (result.wrapUpRequested && result.turnBudget?.outcome === \"wrap-up-requested\") {\n\t\tconst note = turnBudgetSoftNote(\n\t\t\tresult.turnBudget,\n\t\t\tresult.turnBudget.wrapUpRequestedAtTurn ?? result.turnBudget.turnCount,\n\t\t);\n\t\tfullOutput = fullOutput.trim() ? `${note}\\n\\n${fullOutput}` : note;\n\t}\n\tconst completionGuardEnabled = isAgentContractV1(options.agentContract)\n\t\t? agent.completionGuard === true\n\t\t: agent.completionGuard !== false;\n\tconst completionGuard =\n\t\tresult.exitCode === 0 && !result.error && completionGuardEnabled\n\t\t\t? evaluateCompletionMutationGuard({\n\t\t\t\t\tagent: agent.name,\n\t\t\t\t\ttask: shared.originalTask ?? task,\n\t\t\t\t\tmessages: result.messages ?? [],\n\t\t\t\t\ttools: agent.tools,\n\t\t\t\t\tmcpDirectTools: agent.mcpDirectTools,\n\t\t\t\t})\n\t\t\t: undefined;\n\tconst completionGuardTriggered = completionGuard?.triggered === true && !observedMutationAttempt;\n\tif (completionGuard) {\n\t\tresult.effects = {\n\t\t\t...(result.effects ?? {}),\n\t\t\tfileMutation: {\n\t\t\t\tstatus: completionGuard.expectedMutation\n\t\t\t\t\t? completionGuardTriggered\n\t\t\t\t\t\t? \"missing\"\n\t\t\t\t\t\t: \"observed\"\n\t\t\t\t\t: \"not-applicable\",\n\t\t\t\texpected: completionGuard.expectedMutation,\n\t\t\t\tattempted: completionGuard.attemptedMutation || observedMutationAttempt,\n\t\t\t\t...(completionGuardTriggered\n\t\t\t\t\t? { message: \"Subagent completed without making edits for an implementation task.\" }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t};\n\t}\n\tif (completionGuardTriggered && !isAgentContractV1(options.agentContract)) {\n\t\tresult.exitCode = 1;\n\t\tresult.error =\n\t\t\t\"Subagent completed without making edits for an implementation task.\\nIt appears to have returned planning or scratchpad output instead of applying changes.\";\n\t\tprogress.status = \"failed\";\n\t\tprogress.error = result.error;\n\t\temitControlEvent(\n\t\t\tbuildControlEvent({\n\t\t\t\tfrom: progress.activityState,\n\t\t\t\tto: \"needs_attention\",\n\t\t\t\trunId: options.runId ?? agent.name,\n\t\t\t\tagent: agent.name,\n\t\t\t\tindex: options.index,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: `${agent.name} completed without making edits for an implementation task`,\n\t\t\t\treason: \"completion_guard\",\n\t\t\t}),\n\t\t);\n\t}\n\tif (options.outputPath && result.exitCode === 0) {\n\t\tconst resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot);\n\t\tfullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);\n\t\tresult.savedOutputPath = resolvedOutput.savedPath;\n\t\tresult.outputSaveError = resolvedOutput.saveError;\n\t\tif (resolvedOutput.savedPath) {\n\t\t\tresult.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);\n\t\t\tif (result.outputState === \"absent\") result.outputState = \"unknown\";\n\t\t}\n\t}\n\tartifactOutputByResult.set(result, fullOutput);\n\tacceptanceOutputByResult.set(result, acceptanceOutput);\n\tresult.outputMode = options.outputMode ?? \"inline\";\n\tresult.finalOutput =\n\t\toptions.outputMode === \"file-only\" && result.savedOutputPath && result.outputReference\n\t\t\t? result.outputReference.message\n\t\t\t: fullOutput;\n\tresult.controlEvents = allControlEvents.length ? allControlEvents : undefined;\n\tif (options.onUpdate) {\n\t\tconst finalText = result.finalOutput || result.error || \"(no output)\";\n\t\tconst progressSnapshot = snapshotProgress(progress);\n\t\tconst resultSnapshot = snapshotStreamResult(result, progressSnapshot);\n\t\toptions.onUpdate({\n\t\t\tcontent: [{ type: \"text\", text: finalText }],\n\t\t\tdetails: {\n\t\t\t\tmode: \"single\",\n\t\t\t\tresults: [resultSnapshot],\n\t\t\t\tprogress: [progressSnapshot],\n\t\t\t\tcontrolEvents: allControlEvents.length ? allControlEvents : undefined,\n\t\t\t},\n\t\t});\n\t}\n\treturn result;\n}\n\n/**\n * Run a subagent synchronously (blocking until complete)\n */\nasync function runSyncCompletion(\n\truntimeCwd: string,\n\tagents: AgentConfig[],\n\tagentName: string,\n\ttask: string,\n\toptions: RunSyncOptions,\n): Promise<SingleResult> {\n\toptions = {\n\t\t...options,\n\t\tcapabilityCeiling: intersectSubagentCapabilityCeilings(\n\t\t\toptions.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(options.parentSessionId),\n\t\t\tdecodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),\n\t\t),\n\t};\n\tconst agent = agents.find((a) => a.name === agentName);\n\tif (!agent) {\n\t\treturn withRunContext(\n\t\t\t{\n\t\t\t\tindex: options.index ?? 0,\n\t\t\t\tagent: agentName,\n\t\t\t\ttask,\n\t\t\t\texitCode: 1,\n\t\t\t\tmessages: [],\n\t\t\t\tusage: emptyUsage(),\n\t\t\t\terror: `Unknown agent: ${agentName}`,\n\t\t\t},\n\t\t\toptions.context,\n\t\t);\n\t}\n\ttry {\n\t\tassertAgentAllowedByCapabilityCeiling(agent.name, options.capabilityCeiling);\n\t} catch (error) {\n\t\treturn withRunContext(\n\t\t\t{\n\t\t\t\tindex: options.index ?? 0,\n\t\t\t\tagent: agent.name,\n\t\t\t\ttask,\n\t\t\t\texitCode: 1,\n\t\t\t\tmessages: [],\n\t\t\t\tusage: emptyUsage(),\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t...(options.capabilityCeiling ? { capabilityCeiling: options.capabilityCeiling } : {}),\n\t\t\t},\n\t\t\toptions.context,\n\t\t);\n\t}\n\tconst acceptanceErrors = validateAcceptanceInput(options.acceptance);\n\tif (acceptanceErrors.length > 0) {\n\t\treturn withRunContext(\n\t\t\t{\n\t\t\t\tindex: options.index ?? 0,\n\t\t\t\tagent: agentName,\n\t\t\t\ttask,\n\t\t\t\texitCode: 1,\n\t\t\t\tmessages: [],\n\t\t\t\tusage: emptyUsage(),\n\t\t\t\terror: acceptanceErrors.join(\" \"),\n\t\t\t},\n\t\t\toptions.context,\n\t\t);\n\t}\n\tconst outputModeValidationError = validateFileOnlyOutputMode(\n\t\toptions.outputMode,\n\t\toptions.outputPath,\n\t\t`Single run (${agentName})`,\n\t);\n\tif (outputModeValidationError) {\n\t\treturn withRunContext(\n\t\t\t{\n\t\t\t\tindex: options.index ?? 0,\n\t\t\t\tagent: agentName,\n\t\t\t\ttask,\n\t\t\t\texitCode: 1,\n\t\t\t\tmessages: [],\n\t\t\t\tusage: emptyUsage(),\n\t\t\t\toutputMode: options.outputMode,\n\t\t\t\terror: outputModeValidationError,\n\t\t\t},\n\t\t\toptions.context,\n\t\t);\n\t}\n\n\tconst shareEnabled = options.share === true;\n\tconst effectiveAcceptance = resolveEffectiveAcceptance({\n\t\texplicit: options.acceptance,\n\t\tagentName,\n\t\tacceptanceRole: agent.acceptanceRole,\n\t\ttask,\n\t\tmode: options.acceptanceContext?.mode ?? \"single\",\n\t\tasync: options.acceptanceContext?.async,\n\t\tdynamic: options.acceptanceContext?.dynamic,\n\t\tdynamicGroup: options.acceptanceContext?.dynamicGroup,\n\t\tagentContract: options.agentContract,\n\t});\n\tconst acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance, {\n\t\treportOptional: isAgentContractV1(options.agentContract),\n\t});\n\tconst taskWithAcceptance = acceptancePrompt ? `${task}\\n${acceptancePrompt}` : task;\n\tconst sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;\n\tconst skillNames = options.skills ?? agent.skills ?? [];\n\tconst skillCwd = options.cwd ?? runtimeCwd;\n\tconst { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(\n\t\tskillNames,\n\t\tskillCwd,\n\t\truntimeCwd,\n\t\tagent.skillPath,\n\t\tagent.filePath ? path.dirname(agent.filePath) : skillCwd,\n\t);\n\tif (skillNames.some((skill) => skill.trim() === \"pi-subagents\") && missingSkills.includes(\"pi-subagents\")) {\n\t\treturn withRunContext(\n\t\t\t{\n\t\t\t\tindex: options.index ?? 0,\n\t\t\t\tagent: agentName,\n\t\t\t\ttask,\n\t\t\t\texitCode: 1,\n\t\t\t\tmessages: [],\n\t\t\t\tusage: emptyUsage(),\n\t\t\t\terror: \"Skills not found: pi-subagents\",\n\t\t\t},\n\t\t\toptions.context,\n\t\t);\n\t}\n\tlet systemPrompt = agent.systemPrompt?.trim() || \"\";\n\tif (resolvedSkills.length > 0) {\n\t\tconst skillInjection = buildSkillInjection(resolvedSkills);\n\t\tsystemPrompt = systemPrompt ? `${systemPrompt}\\n\\n${skillInjection}` : skillInjection;\n\t}\n\tconst memoryInjection = buildAgentMemoryInjection(agent, skillCwd);\n\tif (memoryInjection) {\n\t\tsystemPrompt = systemPrompt ? `${systemPrompt}\\n\\n${memoryInjection}` : memoryInjection;\n\t}\n\tsystemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: skillCwd, agentName });\n\tsystemPrompt = injectOutputPathSystemPrompt(systemPrompt, options.outputPath, agent);\n\n\tconst candidates = buildModelCandidates(\n\t\toptions.modelOverride ?? agent.model,\n\t\tagent.fallbackModels,\n\t\toptions.availableModels,\n\t\toptions.preferredModelProvider,\n\t\t{ scope: options.modelScope },\n\t);\n\tconst attemptedModels: string[] = [];\n\tconst modelAttempts: ModelAttempt[] = [];\n\tconst aggregateUsage = emptyUsage();\n\tconst attemptNotes: string[] = [];\n\tlet totalToolCount = 0;\n\tlet totalDurationMs = 0;\n\n\tlet artifactPathsResult: ArtifactPaths | undefined;\n\tlet jsonlPath: string | undefined;\n\tlet transcriptWriter: ChildTranscriptWriter | undefined;\n\tif (options.artifactsDir && options.artifactConfig?.enabled !== false) {\n\t\tartifactPathsResult = getArtifactPaths(options.artifactsDir, options.runId, agentName, options.index);\n\t\tensureArtifactsDir(options.artifactsDir);\n\t\tif (options.artifactConfig?.includeInput !== false) {\n\t\t\twriteArtifact(artifactPathsResult.inputPath, `# Task for ${agentName}\\n\\n${taskWithAcceptance}`);\n\t\t}\n\t\tif (options.artifactConfig?.includeJsonl !== false) {\n\t\t\tjsonlPath = artifactPathsResult.jsonlPath;\n\t\t}\n\t\tif (options.artifactConfig?.includeTranscript !== false) {\n\t\t\ttranscriptWriter = createChildTranscriptWriter({\n\t\t\t\ttranscriptPath: artifactPathsResult.transcriptPath,\n\t\t\t\tsource: \"foreground\",\n\t\t\t\trunId: options.runId,\n\t\t\t\tagent: agentName,\n\t\t\t\tchildIndex: options.index,\n\t\t\t\tcwd: options.cwd ?? runtimeCwd,\n\t\t\t});\n\t\t\ttranscriptWriter.writeInitialUserMessage(taskWithAcceptance);\n\t\t}\n\t}\n\n\tconst persistResultMetadata = (target: SingleResult): void => {\n\t\tpersistSingleResultMetadata({\n\t\t\tmetadataPath: artifactPathsResult?.metadataPath,\n\t\t\tenabled: options.artifactConfig?.enabled !== false && options.artifactConfig?.includeMetadata !== false,\n\t\t\trunId: options.runId,\n\t\t\tagent: agentName,\n\t\t\ttask,\n\t\t\tresult: target,\n\t\t});\n\t};\n\n\tlet intercomDetached = false;\n\tlet detachedReason: string | undefined;\n\tconst attemptOptions: RunSyncOptions = {\n\t\t...options,\n\t\tonDetachReceipt: (receipt) => {\n\t\t\treceipt.acceptance = buildPendingAcceptanceLedger(effectiveAcceptance);\n\t\t\ttry {\n\t\t\t\tpersistResultMetadata(receipt);\n\t\t\t} catch (error) {\n\t\t\t\treceipt.metadataSaveError = error instanceof Error ? error.message : String(error);\n\t\t\t}\n\t\t\tconst accepted = options.onDetachReceipt?.(receipt) === true;\n\t\t\tif (accepted) {\n\t\t\t\tdetachedReason = receipt.detachedReason;\n\t\t\t\tif (receipt.detachedReason === \"intercom coordination\") intercomDetached = true;\n\t\t\t}\n\t\t\treturn accepted;\n\t\t},\n\t};\n\tlet lastResult: SingleResult | undefined;\n\tconst modelsToTry = candidates.length > 0 ? candidates : [undefined];\n\tmodelAttemptsLoop: for (let modelIndex = 0; modelIndex < modelsToTry.length; modelIndex++) {\n\t\tconst candidate = modelsToTry[modelIndex];\n\t\tfor (let startupAttemptIndex = 0; ; startupAttemptIndex++) {\n\t\t\tconst outputSnapshot = captureSingleOutputSnapshot(options.outputPath);\n\t\t\tconst result = await runSingleAttempt(runtimeCwd, agent, taskWithAcceptance, candidate, attemptOptions, {\n\t\t\t\tsessionEnabled,\n\t\t\t\tsystemPrompt,\n\t\t\t\tresolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,\n\t\t\t\tskillsWarning: missingSkills.length > 0 ? `Skills not found: ${missingSkills.join(\", \")}` : undefined,\n\t\t\t\tjsonlPath,\n\t\t\t\tartifactPaths: artifactPathsResult,\n\t\t\t\ttranscriptWriter,\n\t\t\t\tattemptNotes,\n\t\t\t\tmodelCandidates: candidates\n\t\t\t\t\t.map((modelCandidate) =>\n\t\t\t\t\t\tapplyThinkingSuffix(\n\t\t\t\t\t\t\tmodelCandidate,\n\t\t\t\t\t\t\toptions.thinkingOverride ?? agent.thinking,\n\t\t\t\t\t\t\toptions.thinkingOverride !== undefined,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\t.filter((modelCandidate): modelCandidate is string => Boolean(modelCandidate)),\n\t\t\t\toutputSnapshot,\n\t\t\t\toriginalTask: task,\n\t\t\t});\n\t\t\tlastResult = result;\n\t\t\tif (startupAttemptIndex === 0) {\n\t\t\t\tif (result.model) attemptedModels.push(result.model);\n\t\t\t\telse if (candidate) attemptedModels.push(candidate);\n\t\t\t}\n\t\t\tsumUsage(aggregateUsage, result.usage);\n\t\t\ttotalToolCount += result.progressSummary?.toolCount ?? 0;\n\t\t\ttotalDurationMs += result.progressSummary?.durationMs ?? 0;\n\t\t\tconst attemptSucceeded = result.exitCode === 0 && !result.error;\n\t\t\tconst attempt: ModelAttempt = {\n\t\t\t\tmodel: result.model ?? candidate ?? agent.model ?? \"default\",\n\t\t\t\tsuccess: attemptSucceeded,\n\t\t\t\texitCode: result.exitCode,\n\t\t\t\terror: result.error,\n\t\t\t\tusage: { ...result.usage },\n\t\t\t};\n\t\t\tmodelAttempts.push(attempt);\n\t\t\t// Preserve the legacy intercom handoff contract: once this logical run has\n\t\t\t// been handed to a supervisor, terminating that attempt must not launch a\n\t\t\t// startup retry or model fallback. Explicit user detach retains fallback.\n\t\t\tif (intercomDetached || result.timedOut || result.turnBudgetExceeded) break modelAttemptsLoop;\n\t\t\tif (attemptSucceeded) break modelAttemptsLoop;\n\n\t\t\tconst startupFailure = isRetryableSubagentStartupFailure({\n\t\t\t\texitCode: result.exitCode,\n\t\t\t\terror: result.error,\n\t\t\t\tfinalOutput: result.finalOutput,\n\t\t\t\tmessageCount: result.messages?.length ?? 0,\n\t\t\t\ttoolCount: result.progressSummary?.toolCount ?? 0,\n\t\t\t\tusage: result.usage,\n\t\t\t\tdurationMs: result.progressSummary?.durationMs ?? Number.POSITIVE_INFINITY,\n\t\t\t\tprotocolError: result.protocolError,\n\t\t\t\tprocessSignal: result.processSignal,\n\t\t\t\tdetached: result.detached,\n\t\t\t\tinterrupted: result.interrupted,\n\t\t\t\ttimedOut: result.timedOut,\n\t\t\t\tstopped: result.stopped,\n\t\t\t\tturnBudgetExceeded: result.turnBudgetExceeded,\n\t\t\t});\n\t\t\tconst retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];\n\t\t\tif (startupFailure && retryDelayMs !== undefined) {\n\t\t\t\tconst retryNote = formatSubagentStartupRetryNote({\n\t\t\t\t\tmodel: attempt.model,\n\t\t\t\t\tattempt: startupAttemptIndex + 1,\n\t\t\t\t\tmaxAttempts: SUBAGENT_STARTUP_RETRY_DELAYS_MS.length + 1,\n\t\t\t\t\tdelayMs: retryDelayMs,\n\t\t\t\t});\n\t\t\t\tconst shouldRetry = await waitForSubagentStartupRetry(retryDelayMs, [\n\t\t\t\t\toptions.signal,\n\t\t\t\t\toptions.interruptSignal,\n\t\t\t\t]);\n\t\t\t\tif (!shouldRetry) {\n\t\t\t\t\tif (options.interruptSignal?.aborted) {\n\t\t\t\t\t\tresult.exitCode = 0;\n\t\t\t\t\t\tresult.interrupted = true;\n\t\t\t\t\t\tresult.error = undefined;\n\t\t\t\t\t\tresult.finalOutput = \"Interrupted. Waiting for explicit next action.\";\n\t\t\t\t\t\tif (result.progress) {\n\t\t\t\t\t\t\tresult.progress.status = \"running\";\n\t\t\t\t\t\t\tresult.progress.error = undefined;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst cancellationError = \"Subagent startup retry cancelled before relaunch.\";\n\t\t\t\t\t\tresult.error = cancellationError;\n\t\t\t\t\t\tresult.finalOutput = cancellationError;\n\t\t\t\t\t\tattempt.error = cancellationError;\n\t\t\t\t\t\tif (result.progress) result.progress.error = cancellationError;\n\t\t\t\t\t}\n\t\t\t\t\tbreak modelAttemptsLoop;\n\t\t\t\t}\n\t\t\t\tattempt.error = retryNote;\n\t\t\t\tattemptNotes.push(retryNote);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (startupFailure) {\n\t\t\t\tconst startupError = formatSubagentStartupRetryExhaustedError({\n\t\t\t\t\tmodel: attempt.model,\n\t\t\t\t\tattempts: startupAttemptIndex + 1,\n\t\t\t\t});\n\t\t\t\tresult.error = startupError;\n\t\t\t\tresult.finalOutput = startupError;\n\t\t\t\tif (result.progress) {\n\t\t\t\t\tresult.progress.error = startupError;\n\t\t\t\t\tresult.progress.status = \"failed\";\n\t\t\t\t}\n\t\t\t\tattempt.error = startupError;\n\t\t\t\tbreak modelAttemptsLoop;\n\t\t\t}\n\t\t\tif (!isRetryableModelFailure(result.error) || modelIndex === modelsToTry.length - 1) break modelAttemptsLoop;\n\t\t\tattemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[modelIndex + 1]));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst result = withRunContext(\n\t\tlastResult ??\n\t\t\t({\n\t\t\t\tindex: options.index ?? 0,\n\t\t\t\tagent: agentName,\n\t\t\t\ttask,\n\t\t\t\texitCode: 1,\n\t\t\t\tmessages: [],\n\t\t\t\tusage: emptyUsage(),\n\t\t\t\terror: \"Subagent did not produce a result.\",\n\t\t\t} satisfies SingleResult),\n\t\toptions.context,\n\t);\n\n\tresult.usage = aggregateUsage;\n\tresult.attemptedModels = attemptedModels.length > 0 ? attemptedModels : undefined;\n\tresult.modelAttempts = modelAttempts.length > 0 ? modelAttempts : undefined;\n\tresult.progressSummary = {\n\t\ttoolCount: totalToolCount,\n\t\ttokens: aggregateUsage.input + aggregateUsage.output,\n\t\tdurationMs: totalDurationMs,\n\t};\n\tif (transcriptWriter) result.transcriptPath = artifactPathsResult?.transcriptPath;\n\tif (transcriptWriter?.getError()) result.transcriptError = transcriptWriter.getError();\n\n\ttry {\n\t\tif (artifactPathsResult && options.artifactConfig?.enabled !== false) {\n\t\t\tresult.artifactPaths = artifactPathsResult;\n\t\t\tif (options.artifactConfig?.includeOutput !== false) {\n\t\t\t\twriteArtifact(\n\t\t\t\t\tartifactPathsResult.outputPath,\n\t\t\t\t\tformatOutputArtifactContent({\n\t\t\t\t\t\toutput: artifactOutputByResult.get(result) ?? result.finalOutput ?? \"\",\n\t\t\t\t\t\terror: result.error,\n\t\t\t\t\t\ttranscriptPath: result.transcriptPath,\n\t\t\t\t\t\tmetadataPath:\n\t\t\t\t\t\t\toptions.artifactConfig?.includeMetadata === false ? undefined : artifactPathsResult.metadataPath,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (options.maxOutput) {\n\t\t\t\tconst config = { ...DEFAULT_MAX_OUTPUT, ...options.maxOutput };\n\t\t\t\tconst truncationResult = truncateOutput(result.finalOutput ?? \"\", config, artifactPathsResult.outputPath);\n\t\t\t\tif (truncationResult.truncated) result.truncation = truncationResult;\n\t\t\t}\n\t\t} else if (options.maxOutput) {\n\t\t\tconst config = { ...DEFAULT_MAX_OUTPUT, ...options.maxOutput };\n\t\t\tconst truncationResult = truncateOutput(result.finalOutput ?? \"\", config);\n\t\t\tif (truncationResult.truncated) result.truncation = truncationResult;\n\t\t}\n\t} catch (error) {\n\t\tconst message = `Artifact output post-processing failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\tresult.outputSaveError = result.outputSaveError ? `${result.outputSaveError}\\n${message}` : message;\n\t}\n\n\tif (options.sessionFile && (existsSync(options.sessionFile) || result.messages?.length)) {\n\t\tresult.sessionFile = options.sessionFile;\n\t} else if (shareEnabled && options.sessionDir) {\n\t\tconst sessionFile = findLatestSessionFile(options.sessionDir);\n\t\tif (sessionFile) result.sessionFile = sessionFile;\n\t}\n\n\tconst childWrittenOutput = options.outputPath\n\t\t? extractChildWrittenOutput(result.messages, options.outputPath, options.cwd ?? runtimeCwd)\n\t\t: undefined;\n\ttry {\n\t\tif (result.interrupted && detachedReason === \"user request\") {\n\t\t\t// Only an accepted user-detach receipt needs a non-rejecting terminal\n\t\t\t// ledger. Attached and background execution retain their baseline\n\t\t\t// acceptance behavior.\n\t\t\tresult.acceptance = buildInterruptedAcceptanceLedger(effectiveAcceptance);\n\t\t} else if (result.stopped) {\n\t\t\tresult.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, {\n\t\t\t\tid: \"stopped\",\n\t\t\t\tmessage: \"Acceptance was not evaluated because the subagent was stopped.\",\n\t\t\t});\n\t\t} else if (result.timedOut) {\n\t\t\tresult.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, {\n\t\t\t\tid: \"timeout\",\n\t\t\t\tmessage: \"Acceptance was not evaluated because the subagent timed out.\",\n\t\t\t});\n\t\t} else if (result.turnBudgetExceeded) {\n\t\t\tresult.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, {\n\t\t\t\tid: \"turn-budget\",\n\t\t\t\tmessage: \"Acceptance was not evaluated because the subagent exceeded its turn budget.\",\n\t\t\t});\n\t\t} else {\n\t\t\tresult.acceptance = await evaluateAcceptance({\n\t\t\t\tacceptance: effectiveAcceptance,\n\t\t\t\toutput: acceptanceOutputByResult.get(result) ?? result.finalOutput ?? \"\",\n\t\t\t\tfileOutput:\n\t\t\t\t\tchildWrittenOutput !== undefined && options.outputPath\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tcontent: childWrittenOutput,\n\t\t\t\t\t\t\t\tpath: options.outputPath,\n\t\t\t\t\t\t\t\tauthoritative: options.outputMode === \"file-only\",\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\tcwd: options.cwd ?? runtimeCwd,\n\t\t\t\treportOptional: isAgentContractV1(options.agentContract),\n\t\t\t\tartifactsDir: options.artifactsDir,\n\t\t\t\trunId: options.runId,\n\t\t\t});\n\t\t}\n\t} catch (error) {\n\t\tconst message = `Acceptance evaluation failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\tresult.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: \"acceptance-evaluation\", message });\n\t}\n\tconst acceptanceFailure = acceptanceFailureMessage(result.acceptance);\n\tstripAcceptanceReportsFromMessages(result.messages);\n\tif (\n\t\tacceptanceFailure &&\n\t\tresult.acceptance.explicit &&\n\t\tresult.exitCode === 0 &&\n\t\t!result.interrupted &&\n\t\t!result.timedOut &&\n\t\t!isAgentContractV1(options.agentContract)\n\t) {\n\t\tresult.exitCode = 1;\n\t\tresult.error = result.error ? `${result.error}\\n${acceptanceFailure}` : acceptanceFailure;\n\t\tif (result.progress) {\n\t\t\tresult.progress.status = \"failed\";\n\t\t\tresult.progress.error = result.error;\n\t\t}\n\t}\n\tif (isAgentContractV1(options.agentContract)) attachContractProjections(result);\n\ttry {\n\t\tpersistResultMetadata(result);\n\t} catch (error) {\n\t\tresult.metadataSaveError = error instanceof Error ? error.message : String(error);\n\t}\n\n\treturn result;\n}\n\n/**\n * Runs the authoritative completion pipeline independently from the foreground\n * receipt. Detachment is same-runtime only: it does not adopt or daemonize work.\n */\nexport async function runSync(\n\truntimeCwd: string,\n\tagents: AgentConfig[],\n\tagentName: string,\n\ttask: string,\n\toptions: RunSyncOptions,\n): Promise<SingleResult> {\n\t// Capture the strict contract before consumer-owned objects can be mutated\n\t// after a detached receipt is published.\n\tconst strictContract = isAgentContractV1(options.agentContract);\n\tlet detachedReason: string | undefined;\n\tlet publishedReceipt: SingleResult | undefined;\n\tlet activeDetachAttempt: ((reason?: string) => boolean) | undefined;\n\tlet detachReadyPublished = false;\n\tlet resolveReceipt!: (result: SingleResult) => void;\n\tconst receipt = new Promise<SingleResult>((resolve) => {\n\t\tresolveReceipt = resolve;\n\t});\n\tconst originSignal = options.signal;\n\tconst originController = new AbortController();\n\tconst forwardOriginAbort = () => {\n\t\tif (!detachedReason) originController.abort(originSignal?.reason);\n\t};\n\tif (originSignal?.aborted) forwardOriginAbort();\n\telse originSignal?.addEventListener(\"abort\", forwardOriginAbort, { once: true });\n\n\tconst completion = runSyncCompletion(runtimeCwd, agents, agentName, task, {\n\t\t...options,\n\t\tsignal: originController.signal,\n\t\tonDetachedExit: undefined,\n\t\tonDetachReceipt: (detachedReceipt) => {\n\t\t\tif (detachedReason || publishedReceipt) return false;\n\t\t\t// Keep the authoritative result, fallback snapshot, and caller receipt\n\t\t\t// separately owned while the completion pipeline remains live.\n\t\t\tpublishedReceipt = structuredClone(detachedReceipt);\n\t\t\tconst callerReceipt = structuredClone(detachedReceipt);\n\t\t\t// A strict contract was already validated before detach; normalize private\n\t\t\t// and caller snapshots to the only safely known version.\n\t\t\tpublishedReceipt.agentContract = strictContract ? { version: 1 } : undefined;\n\t\t\tcallerReceipt.agentContract = strictContract ? { version: 1 } : undefined;\n\t\t\tdetachedReason = detachedReceipt.detachedReason ?? \"user request\";\n\t\t\tresolveReceipt(callerReceipt);\n\t\t\treturn true;\n\t\t},\n\t\tonDetachReady: (detachAttempt) => {\n\t\t\tactiveDetachAttempt = detachAttempt;\n\t\t\tif (detachReadyPublished) return;\n\t\t\tdetachReadyPublished = true;\n\t\t\toptions.onDetachReady?.((reason = \"user request\") => {\n\t\t\t\tif (detachedReason || originController.signal.aborted) return false;\n\t\t\t\treturn activeDetachAttempt?.(reason) ?? false;\n\t\t\t});\n\t\t},\n\t});\n\n\tconst authoritativeCompletion = completion.catch((error: unknown) => {\n\t\tif (!publishedReceipt || !detachedReason) throw error;\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst failureMessage = `Detached completion pipeline failed after receipt: ${message}`;\n\t\tconst failedProgress: AgentProgress = publishedReceipt.progress\n\t\t\t? { ...publishedReceipt.progress, status: \"failed\", error: failureMessage }\n\t\t\t: {\n\t\t\t\t\tindex: options.index ?? 0,\n\t\t\t\t\tagent: publishedReceipt.agent,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\ttask: publishedReceipt.task,\n\t\t\t\t\trecentTools: [],\n\t\t\t\t\trecentOutput: [],\n\t\t\t\t\ttoolCount: publishedReceipt.progressSummary?.toolCount ?? 0,\n\t\t\t\t\ttokens: publishedReceipt.progressSummary?.tokens ?? 0,\n\t\t\t\t\tdurationMs: publishedReceipt.progressSummary?.durationMs ?? 0,\n\t\t\t\t\terror: failureMessage,\n\t\t\t\t};\n\t\tconst failedResult: SingleResult = {\n\t\t\t...publishedReceipt,\n\t\t\tdetached: undefined,\n\t\t\tdetachedReason,\n\t\t\texitCode: 1,\n\t\t\terror: failureMessage,\n\t\t\tfinalOutput: failureMessage,\n\t\t\tprogress: failedProgress,\n\t\t\tprogressSummary: {\n\t\t\t\ttoolCount: failedProgress.toolCount,\n\t\t\t\ttokens: failedProgress.tokens,\n\t\t\t\tdurationMs: failedProgress.durationMs,\n\t\t\t},\n\t\t\tacceptance: publishedReceipt.acceptance\n\t\t\t\t? buildSkippedAcceptanceLedger(publishedReceipt.acceptance.effectiveAcceptance, {\n\t\t\t\t\t\tid: \"completion-pipeline\",\n\t\t\t\t\t\tmessage: failureMessage,\n\t\t\t\t\t})\n\t\t\t\t: undefined,\n\t\t};\n\t\tif (strictContract) attachContractProjections(failedResult);\n\t\ttry {\n\t\t\t// Replace the provisional detach receipt metadata with the authoritative\n\t\t\t// terminal failure. Persistence remains best-effort and cannot orphan work.\n\t\t\tpersistSingleResultMetadata({\n\t\t\t\tmetadataPath: failedResult.artifactPaths?.metadataPath,\n\t\t\t\tenabled: options.artifactConfig?.enabled !== false && options.artifactConfig?.includeMetadata !== false,\n\t\t\t\trunId: options.runId,\n\t\t\t\tagent: failedResult.agent,\n\t\t\t\ttask: failedResult.task,\n\t\t\t\tresult: failedResult,\n\t\t\t});\n\t\t} catch (metadataError) {\n\t\t\tfailedResult.metadataSaveError =\n\t\t\t\tmetadataError instanceof Error ? metadataError.message : String(metadataError);\n\t\t}\n\t\treturn failedResult;\n\t});\n\n\tlet terminalCallbackInvoked = false;\n\tvoid authoritativeCompletion\n\t\t.then((terminalResult) => {\n\t\t\tif (!detachedReason || terminalCallbackInvoked) return;\n\t\t\tterminalCallbackInvoked = true;\n\t\t\tterminalResult.detached = undefined;\n\t\t\tterminalResult.detachedReason = detachedReason;\n\t\t\ttry {\n\t\t\t\toptions.onDetachedExit?.(terminalResult);\n\t\t\t} catch {\n\t\t\t\t// The authoritative result has settled. Consumer callback failures are\n\t\t\t\t// contained here; each consumer owns cleanup through its own finally block.\n\t\t\t}\n\t\t})\n\t\t.catch(() => {\n\t\t\t// Attached completion rejection remains observable through the returned\n\t\t\t// promise without becoming an unhandled side-channel rejection.\n\t\t})\n\t\t.finally(() => {\n\t\t\toriginSignal?.removeEventListener(\"abort\", forwardOriginAbort);\n\t\t});\n\n\treturn Promise.race([authoritativeCompletion, receipt]);\n}\n"]}