{"version":3,"file":"async-execution.d.ts","sourceRoot":"","sources":["../../../../src/runs/background/async-execution.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Async execution logic for subagent tool\n */\n\nimport { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ExtensionAPI } from \"@lpb-work/pi-coding-agent\";\nimport { buildAgentMemoryInjection } from \"../../agents/agent-memory.ts\";\nimport { appendAgentRefinementOverlay } from \"../../agents/agent-refinements.ts\";\nimport type { AgentConfig } from \"../../agents/agents.ts\";\nimport { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from \"../../agents/skills.ts\";\nimport { writePrivateAtomicJson } from \"../../shared/atomic-json.ts\";\nimport { agentDefinitionDigest, launchBindingDigest } from \"../../shared/launch-contract.ts\";\nimport { resolveEffectiveThinking } from \"../../shared/model-info.ts\";\nimport {\n\tbuildChainInstructions,\n\ttype ChainStep,\n\tisCheckpointStep,\n\tisDynamicParallelStep,\n\tisParallelStep,\n\ttype ResolvedStepBehavior,\n\tresolveStepBehavior,\n\ttype SequentialStep,\n\ttype StepOverrides,\n\tsuppressProgressForReadOnlyTask,\n\twriteInitialProgressFile,\n} from \"../../shared/settings.ts\";\nimport {\n\ttype AcceptanceInput,\n\ttype AgentContract,\n\ttype ArtifactConfig,\n\ttype Details,\n\tDIRS,\n\tgetAsyncConfigPath,\n\ttype JsonSchemaObject,\n\ttype MaxOutputConfig,\n\ttype NestedRouteInfo,\n\ttype ResolvedControlConfig,\n\ttype ResolvedToolBudget,\n\ttype ResolvedTurnBudget,\n\tresolveChildMaxSubagentDepth,\n\ttype SteeringRecoveryDescriptor,\n\tSUBAGENT_ASYNC_STARTED_EVENT,\n\tSUBAGENT_LIFECYCLE_ARTIFACT_VERSION,\n\tSUBAGENT_PROCESS_TERMINAL_EVENT,\n\ttype SubagentRunMode,\n\tTEMP_ROOT_DIR,\n\ttype ToolBudgetConfig,\n\ttype UsageBudgetConfig,\n} from \"../../shared/types.ts\";\nimport { PI_CODING_AGENT_PACKAGE_ROOT_ENV, resolveChildCwd } from \"../../shared/utils.ts\";\nimport {\n\tresolveEffectiveAcceptance,\n\tvalidateAcceptanceInput,\n\tvalidateExecutionAcceptance,\n} from \"../shared/acceptance.ts\";\nimport {\n\tassertAgentAllowedByCapabilityCeiling,\n\tdecodeSubagentCapabilityCeiling,\n\tintersectSubagentCapabilityCeilings,\n\ttype ResolvedSubagentCapabilityCeiling,\n\tresolveCurrentSubagentCapabilityCeiling,\n\tSUBAGENT_CAPABILITY_CEILING_ENV,\n} from \"../shared/capability-ceiling.ts\";\nimport { ChainOutputValidationError, validateChainOutputBindings } from \"../shared/chain-outputs.ts\";\nimport type { ContextMode } from \"../shared/context-mode.ts\";\nimport {\n\ttype AvailableModelInfo,\n\tbuildModelCandidates,\n\ttype ParentModel,\n\tresolveEffectiveSubagentModel,\n\tresolveSubagentModelOverride,\n} from \"../shared/model-fallback.ts\";\nimport type { ModelScopeConfig } from \"../shared/model-scope.ts\";\nimport {\n\tnestedResultsPath,\n\tnestedSummaryFromAsyncStatus,\n\tresolveInheritedNestedRouteFromEnv,\n\tresolveNestedParentAddressFromEnv,\n\twriteNestedEvent,\n} from \"../shared/nested-events.ts\";\nimport type { RunnerStep } from \"../shared/parallel-utils.ts\";\nimport { type PermissionConfig, resolvePermissionRules } from \"../shared/permissions.ts\";\nimport {\n\tapplyThinkingSuffix,\n\tprojectLaunchResolvedChildExtensions,\n\tresolvePiLaunchToolPlan,\n} from \"../shared/pi-args.ts\";\nimport { resolvePiPackageRoot } from \"../shared/pi-spawn.ts\";\nimport type { SessionLeaseRequest } from \"../shared/session-lease.ts\";\nimport {\n\tinjectOutputPathSystemPrompt,\n\tinjectSingleOutputInstruction,\n\tnormalizeSingleOutputOverride,\n\tresolveSingleOutputPath,\n\tvalidateFileOnlyOutputMode,\n} from \"../shared/single-output.ts\";\nimport { createStructuredOutputRuntime } from \"../shared/structured-output.ts\";\nimport { validateToolBudgetConfig } from \"../shared/tool-budget.ts\";\nimport { appendTurnBudgetSystemPrompt, initialTurnBudgetState } from \"../shared/turn-budget.ts\";\nimport { usageBudgetState } from \"../shared/usage-budget.ts\";\nimport { buildWorkflowGraphSnapshot } from \"../shared/workflow-graph.ts\";\nimport { resolveExpectedWorktreeAgentCwd } from \"../shared/worktree.ts\";\nimport type { ImportedAsyncRoot } from \"./chain-root-attachment.ts\";\nimport { finalizeProcessTerminal, readProcessTerminal } from \"./process-terminal.ts\";\n\nconst require = createRequire(import.meta.url);\nconst piPackageRoot = resolvePiPackageRoot();\n\nfunction resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefined {\n\tif (!fs.existsSync(packageJsonPath)) return undefined;\n\tconst packageRoot = path.dirname(packageJsonPath);\n\tconst pkg = JSON.parse(fs.readFileSync(packageJsonPath, \"utf-8\")) as {\n\t\tbin?: string | Record<string, string>;\n\t};\n\tconst binField = pkg.bin;\n\tconst binPath = typeof binField === \"string\" ? binField : (binField?.jiti ?? Object.values(binField ?? {})[0]);\n\tconst candidates = [binPath, \"lib/jiti-cli.mjs\"].filter((candidate): candidate is string => Boolean(candidate));\n\tfor (const candidate of candidates) {\n\t\tconst cliPath = path.resolve(packageRoot, candidate);\n\t\tif (fs.existsSync(cliPath)) return cliPath;\n\t}\n\treturn undefined;\n}\n\nfunction resolveJitiCliPath(): string | undefined {\n\tconst candidates: Array<() => string | undefined> = [\n\t\t() => require.resolve(\"jiti/package.json\"),\n\t\t() =>\n\t\t\tpiPackageRoot\n\t\t\t\t? createRequire(path.join(piPackageRoot, \"package.json\")).resolve(\"jiti/package.json\")\n\t\t\t\t: undefined,\n\t\t() => {\n\t\t\tif (!process.argv[1]) return undefined;\n\t\t\tconst piEntry = fs.realpathSync(process.argv[1]);\n\t\t\treturn createRequire(piEntry).resolve(\"jiti/package.json\");\n\t\t},\n\t\t() => (piPackageRoot ? path.join(piPackageRoot, \"node_modules\", \"jiti\", \"package.json\") : undefined),\n\t];\n\tfor (const candidate of candidates) {\n\t\ttry {\n\t\t\tconst packageJsonPath = candidate();\n\t\t\tif (!packageJsonPath) continue;\n\t\t\tconst cliPath = resolveJitiCliFromPackageJson(packageJsonPath);\n\t\t\tif (cliPath) return cliPath;\n\t\t} catch {\n\t\t\t// Candidate not available in this install, continue probing.\n\t\t}\n\t}\n\treturn undefined;\n}\n\nconst jitiCliPath = resolveJitiCliPath();\n\ninterface AsyncExecutionContext {\n\tpi: ExtensionAPI;\n\tcwd: string;\n\tcurrentSessionId: string;\n\t/** Parent session id used by permission-system ask forwarding. */\n\tparentSessionId?: string;\n\tpermissions?: PermissionConfig;\n\tcurrentModelProvider?: string;\n\tcurrentModel?: ParentModel;\n\t/** Optional model-scope enforcement resolved from subagent settings. */\n\tmodelScope?: ModelScopeConfig;\n\t/** Whether the parent session has an interactive UI. */\n\tinteractive?: boolean;\n}\n\ninterface AsyncChainParams {\n\tchain: ChainStep[];\n\ttask?: string;\n\t/** Raw caller-facing goal used only by the started event. */\n\tgoal?: string;\n\tattachRoot?: ImportedAsyncRoot & { agent: string; outputName?: string; label?: string };\n\tresultMode?: Exclude<SubagentRunMode, \"single\">;\n\tagents: AgentConfig[];\n\tctx: AsyncExecutionContext;\n\tavailableModels?: AvailableModelInfo[];\n\tcwd?: string;\n\tmaxOutput?: MaxOutputConfig;\n\tartifactsDir?: string;\n\tartifactConfig: ArtifactConfig;\n\tshareEnabled: boolean;\n\tsessionRoot?: string;\n\tagentContract?: AgentContract;\n\tchainSkills?: string[];\n\tsessionFilesByFlatIndex?: (string | undefined)[];\n\tthinkingOverridesByFlatIndex?: (AgentConfig[\"thinking\"] | undefined)[];\n\tcontextForAgent?: (agentName: string) => ContextMode;\n\tprogressDir?: string;\n\tdynamicFanoutMaxItems?: number;\n\tmaxSubagentDepth: number;\n\twaitToolEnabled?: boolean;\n\tworktreeSetupHook?: string;\n\tworktreeSetupHookTimeoutMs?: number;\n\tworktreeBaseDir?: string;\n\tcontrolConfig?: ResolvedControlConfig;\n\tcontrolIntercomTarget?: string;\n\tchildIntercomTarget?: (agent: string, index: number) => string | undefined;\n\tnestedRoute?: NestedRouteInfo;\n\tacceptance?: AcceptanceInput;\n\ttimeoutMs?: number;\n\tturnBudget?: ResolvedTurnBudget;\n\ttoolBudget?: ResolvedToolBudget;\n\tusageBudget?: UsageBudgetConfig;\n\tconfigToolBudget?: ResolvedToolBudget;\n\t/** Global cap on simultaneously-running subagent tasks within the async run. */\n\tglobalConcurrencyLimit?: number;\n\tcapabilityCeiling?: ResolvedSubagentCapabilityCeiling;\n\tparentWorkflowRunId?: string;\n\tworkflowKey?: string;\n}\n\ninterface AsyncSingleParams {\n\tagent: string;\n\ttask?: string;\n\t/** Raw caller-facing goal used only by the started event. */\n\tgoal?: string;\n\tagentConfig: AgentConfig;\n\tctx: AsyncExecutionContext;\n\tcwd?: string;\n\tmaxOutput?: MaxOutputConfig;\n\tartifactsDir?: string;\n\tartifactConfig: ArtifactConfig;\n\tshareEnabled: boolean;\n\tsessionRoot?: string;\n\tsessionDir?: string;\n\tsessionFile?: string;\n\trevivalLease?: SessionLeaseRequest;\n\tcontext?: ContextMode;\n\tskills?: string[];\n\toutput?: string | boolean;\n\toutputMode?: \"inline\" | \"file-only\";\n\toutputBaseDir?: string;\n\tagentContract?: AgentContract;\n\tstructuredOutputSchema?: JsonSchemaObject;\n\tmodelOverride?: string;\n\tthinkingOverride?: AgentConfig[\"thinking\"];\n\tavailableModels?: AvailableModelInfo[];\n\tmaxSubagentDepth: number;\n\twaitToolEnabled?: boolean;\n\tworktreeSetupHook?: string;\n\tworktreeSetupHookTimeoutMs?: number;\n\tworktreeBaseDir?: string;\n\tcontrolConfig?: ResolvedControlConfig;\n\tcontrolIntercomTarget?: string;\n\tchildIntercomTarget?: (agent: string, index: number) => string | undefined;\n\tnestedRoute?: NestedRouteInfo;\n\tacceptance?: AcceptanceInput;\n\ttimeoutMs?: number;\n\tabsoluteDeadlineAt?: number;\n\tturnBudget?: { maxTurns: number; graceTurns?: number };\n\ttoolBudget?: ResolvedToolBudget | ToolBudgetConfig;\n\tusageBudget?: UsageBudgetConfig;\n\tconfigToolBudget?: ResolvedToolBudget;\n\tallowZeroToolBudget?: boolean;\n\tcapabilityCeiling?: ResolvedSubagentCapabilityCeiling;\n\tparentWorkflowRunId?: string;\n\tworkflowKey?: string;\n}\n\ninterface AsyncExecutionResult {\n\tcontent: Array<{ type: \"text\"; text: string }>;\n\tdetails: Details;\n\tisError?: boolean;\n}\n\nexport interface AsyncRunnerStepBuildParams {\n\tchain: ChainStep[];\n\ttask?: string;\n\tattachRoot?: ImportedAsyncRoot & { agent: string; outputName?: string; label?: string };\n\tresultMode?: SubagentRunMode;\n\tagents: AgentConfig[];\n\tctx: AsyncExecutionContext;\n\tavailableModels?: AvailableModelInfo[];\n\tcwd?: string;\n\tchainSkills?: string[];\n\tsessionFilesByFlatIndex?: (string | undefined)[];\n\tthinkingOverridesByFlatIndex?: (AgentConfig[\"thinking\"] | undefined)[];\n\tcontextForAgent?: (agentName: string) => ContextMode;\n\tprogressDir?: string;\n\tagentContract?: AgentContract;\n\tdynamicFanoutMaxItems?: number;\n\tmaxSubagentDepth: number;\n\twaitToolEnabled?: boolean;\n\tworktreeBaseDir?: string;\n\tasyncDir: string;\n\toutputBaseDir?: string;\n\tvalidateOutputBindings?: boolean;\n\ttoolBudget?: ResolvedToolBudget;\n\tconfigToolBudget?: ResolvedToolBudget;\n\tcapabilityCeiling?: ResolvedSubagentCapabilityCeiling;\n}\n\nexport type AsyncRunnerStepBuildResult =\n\t| {\n\t\t\tsteps: RunnerStep[];\n\t\t\trunnerCwd: string;\n\t\t\tworkflowGraph: ReturnType<typeof buildWorkflowGraphSnapshot>;\n\t\t\teventChain: ChainStep[];\n\t\t\toriginalTask?: string;\n\t  }\n\t| { error: string };\n\nexport function formatAsyncStartedMessage(headline: string, interactive: boolean): string {\n\tconst guidance = interactive\n\t\t? [\n\t\t\t\t\"The async run is detached and running in the background.\",\n\t\t\t\t\"You are in an interactive session. By default, return control to the user now; Pi will wake you on completion when the run finishes or needs attention. Do NOT call subagent_wait() merely to wait, and do not run sleep/polling loops to wait for it.\",\n\t\t\t\t\"Override that default and call subagent_wait() before ending the turn only when the current request is run-to-completion — for example, the user asked you to report results back here before continuing, or a skill must finish in one turn. In that case, call subagent_wait() to block until the run completes so its results are delivered in this turn instead of deferred.\",\n\t\t\t\t'Otherwise, continue any independent work or return control to the user. Use subagent({ action: \"status\", id: \"...\" }) for a one-shot status/result or to inspect a blocked/stale run, never as a wait loop.',\n\t\t\t]\n\t\t: [\n\t\t\t\t\"The async run is detached. Do not run sleep timers or polling loops just to wait for it.\",\n\t\t\t\t\"This is a non-interactive run: Pi auto-drains current-session background work at agent_end so detached children are not abandoned; call subagent_wait() when this turn must receive the run's results before it ends, otherwise let the headless auto-drain finish the work.\",\n\t\t\t\t'Use subagent({ action: \"status\", id: \"...\" }) when you need a one-shot status/result or to inspect a blocked/stale run. To block until completion, use subagent_wait() — do not poll in a loop.',\n\t\t\t];\n\treturn [headline, \"\", ...guidance].join(\"\\n\");\n}\n\n/**\n * Check if jiti is available for async execution\n */\nexport function isAsyncAvailable(): boolean {\n\treturn jitiCliPath !== undefined;\n}\n\n/**\n * Resolve the detached runner next to the source or compiled executor.\n */\nexport function resolveAsyncRunnerPath(modulePath = fileURLToPath(import.meta.url)): string {\n\tconst moduleExtension = path.extname(modulePath);\n\tif (moduleExtension !== \".ts\" && moduleExtension !== \".js\") {\n\t\tthrow new Error(`Unsupported async execution module extension: ${moduleExtension || \"<none>\"}`);\n\t}\n\treturn path.join(path.dirname(modulePath), `subagent-runner${moduleExtension}`);\n}\n\nfunction isNodeExecutableName(execPath: string): boolean {\n\tconst basename = path.basename(execPath).toLowerCase();\n\treturn basename === \"node\" || basename === \"node.exe\" || basename === \"nodejs\" || basename === \"nodejs.exe\";\n}\n\nfunction canUseCurrentNodeExecutable(execPath: string): boolean {\n\ttry {\n\t\tfs.accessSync(execPath, process.platform === \"win32\" ? fs.constants.F_OK : fs.constants.X_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction resolveAsyncRunnerNodeCommand(): string {\n\tif (isNodeExecutableName(process.execPath) && canUseCurrentNodeExecutable(process.execPath)) {\n\t\treturn process.execPath;\n\t}\n\treturn process.platform === \"win32\" ? \"node.exe\" : \"node\";\n}\n\nexport function resolveAsyncRunnerLogPaths(cfg: object): { stdoutPath: string; stderrPath: string } | undefined {\n\tconst asyncDir =\n\t\ttypeof (cfg as { asyncDir?: unknown }).asyncDir === \"string\" ? (cfg as { asyncDir: string }).asyncDir : undefined;\n\tif (!asyncDir) return undefined;\n\treturn {\n\t\tstdoutPath: path.join(asyncDir, \"runner.stdout.log\"),\n\t\tstderrPath: path.join(asyncDir, \"runner.stderr.log\"),\n\t};\n}\n\nfunction closeFd(fd: number | undefined): void {\n\tif (fd === undefined) return;\n\ttry {\n\t\tfs.closeSync(fd);\n\t} catch {\n\t\t// Best-effort cleanup; child process already owns its duplicated stdio fd.\n\t}\n}\n\n/**\n * Spawn the async runner process\n */\nconst RUNNER_STARTUP_TIMEOUT_MS = 10_000;\nconst RUNNER_STARTUP_WAIT_BUFFER = typeof SharedArrayBuffer !== \"undefined\" ? new SharedArrayBuffer(4) : undefined;\nconst RUNNER_STARTUP_WAIT_VIEW = RUNNER_STARTUP_WAIT_BUFFER ? new Int32Array(RUNNER_STARTUP_WAIT_BUFFER) : undefined;\n\ntype RunnerStartupState = \"ready\" | \"acknowledged\";\n\ntype RunnerStartupWaitResult = { ok: true; token: string } | { ok: false; error: string };\n\nfunction waitForStartupInterval(delayMs = 20): void {\n\tif (RUNNER_STARTUP_WAIT_VIEW) {\n\t\tAtomics.wait(RUNNER_STARTUP_WAIT_VIEW, 0, 0, delayMs);\n\t\treturn;\n\t}\n\tconst waitUntil = Date.now() + delayMs;\n\twhile (Date.now() < waitUntil) {\n\t\t// Startup handshakes are synchronous so resume rejects before reporting a run as started.\n\t}\n}\n\nfunction readRunnerStartup(\n\tstartupPath: string,\n\texpectedState: RunnerStartupState,\n\texpectedToken?: string,\n): RunnerStartupWaitResult | undefined {\n\tif (!fs.existsSync(startupPath)) return undefined;\n\ttry {\n\t\tconst payload = JSON.parse(fs.readFileSync(startupPath, \"utf-8\")) as {\n\t\t\tstate?: unknown;\n\t\t\ttoken?: unknown;\n\t\t\terror?: unknown;\n\t\t};\n\t\tif (payload.state === \"error\" && typeof payload.error === \"string\") return { ok: false, error: payload.error };\n\t\tif (payload.state !== expectedState) return undefined;\n\t\tif (typeof payload.token !== \"string\" || (expectedToken !== undefined && payload.token !== expectedToken)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `Async runner wrote an invalid ${expectedState} startup handshake: ${startupPath}`,\n\t\t\t};\n\t\t}\n\t\treturn { ok: true, token: payload.token };\n\t} catch (error) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `Failed to read async runner startup handshake '${startupPath}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t};\n\t}\n}\n\nfunction waitForRunnerStartup(\n\tstartupPath: string,\n\texpectedState: RunnerStartupState,\n\ttimeoutMs: number,\n\texpectedToken?: string,\n): RunnerStartupWaitResult {\n\tconst deadline = Date.now() + timeoutMs;\n\tfor (;;) {\n\t\tconst result = readRunnerStartup(startupPath, expectedState, expectedToken);\n\t\tif (result) return result;\n\t\tif (Date.now() >= deadline) break;\n\t\twaitForStartupInterval(Math.min(20, Math.max(1, deadline - Date.now())));\n\t}\n\tconst finalResult = readRunnerStartup(startupPath, expectedState, expectedToken);\n\tif (finalResult) return finalResult;\n\treturn {\n\t\tok: false,\n\t\terror: `Timed out after ${timeoutMs}ms waiting for the async runner startup state '${expectedState}'.`,\n\t};\n}\n\nfunction writeRunnerStartupControl(filePath: string, payload: { action: \"ack\" | \"proceed\"; token: string }): void {\n\t// Delegate to the shared atomic JSON writer (temp file + rename, retrying\n\t// transient Windows EPERM/EBUSY/EACCES locks and cleaning up the temp file\n\t// on failure), so the startup handshake gets the same locking resilience as\n\t// every other async control/result file. This is exercised by\n\t// test/unit/atomic-json.test.ts.\n\twritePrivateAtomicJson(filePath, payload);\n}\n\nfunction runnerIsAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\nfunction terminateRunnerBeforeProceed(pid: number): void {\n\tfor (const signal of [\"SIGTERM\", \"SIGKILL\"] as const) {\n\t\tif (!runnerIsAlive(pid)) return;\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t} catch {\n\t\t\tif (!runnerIsAlive(pid)) return;\n\t\t}\n\t\tconst deadline = Date.now() + 1000;\n\t\twhile (runnerIsAlive(pid) && Date.now() < deadline) waitForStartupInterval();\n\t}\n}\n\nfunction spawnRunner(\n\tcfg: object,\n\tsuffix: string,\n\tcwd: string,\n\tonProcessTerminal?: (proof: unknown) => void,\n): { pid?: number; error?: string } {\n\tif (!jitiCliPath) {\n\t\treturn {\n\t\t\terror: \"upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst cwdStats = fs.statSync(cwd);\n\t\tif (!cwdStats.isDirectory()) {\n\t\t\treturn { error: `cwd is not a directory: ${cwd}` };\n\t\t}\n\t} catch {\n\t\treturn { error: `cwd does not exist: ${cwd}` };\n\t}\n\n\tfs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });\n\tconst cfgPath = getAsyncConfigPath(suffix);\n\tconst runnerProcessInstanceId = randomUUID();\n\tconst launchConfig = { ...cfg, runnerProcessInstanceId };\n\tfs.writeFileSync(cfgPath, JSON.stringify(launchConfig));\n\tconst runner = resolveAsyncRunnerPath();\n\tconst nodeCommand = resolveAsyncRunnerNodeCommand();\n\tconst launchForStartup = launchConfig as typeof launchConfig & { asyncDir?: unknown; revivalLease?: unknown };\n\tconst launchAsyncDir = typeof launchForStartup.asyncDir === \"string\" ? launchForStartup.asyncDir : undefined;\n\tconst startupPath =\n\t\ttypeof launchForStartup.revivalLease === \"object\" && launchAsyncDir\n\t\t\t? path.join(launchAsyncDir, \"runner-startup.json\")\n\t\t\t: undefined;\n\tconst startupAckPath = startupPath ? path.join(path.dirname(startupPath), \"runner-startup-ack.json\") : undefined;\n\tconst startupProceedPath = startupPath\n\t\t? path.join(path.dirname(startupPath), \"runner-startup-proceed.json\")\n\t\t: undefined;\n\tif (startupPath) fs.rmSync(startupPath, { force: true });\n\tif (startupAckPath) fs.rmSync(startupAckPath, { force: true });\n\tif (startupProceedPath) fs.rmSync(startupProceedPath, { force: true });\n\n\tconst logPaths = resolveAsyncRunnerLogPaths(launchConfig);\n\tlet stdoutFd: number | undefined;\n\tlet stderrFd: number | undefined;\n\ttry {\n\t\tif (logPaths) {\n\t\t\tfs.mkdirSync(path.dirname(logPaths.stdoutPath), { recursive: true });\n\t\t\tstdoutFd = fs.openSync(logPaths.stdoutPath, \"a\");\n\t\t\tstderrFd = fs.openSync(logPaths.stderrPath, \"a\");\n\t\t}\n\t\tconst proc = spawn(nodeCommand, [jitiCliPath, runner, cfgPath], {\n\t\t\tcwd,\n\t\t\tdetached: true,\n\t\t\tstdio: [\"ignore\", stdoutFd ?? \"ignore\", stderrFd ?? \"ignore\"],\n\t\t\twindowsHide: true,\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...(piPackageRoot ? { [PI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}),\n\t\t\t},\n\t\t});\n\t\tcloseFd(stdoutFd);\n\t\tcloseFd(stderrFd);\n\t\tproc.on(\"error\", (error) => {\n\t\t\tconsole.error(`[pi-subagents] async spawn failed: ${error.message}`);\n\t\t});\n\t\tproc.once(\"close\", (exitCode, signal) => {\n\t\t\tconst launch = launchConfig as {\n\t\t\t\tasyncDir?: unknown;\n\t\t\t\tid?: unknown;\n\t\t\t\tnestedRoute?: NestedRouteInfo;\n\t\t\t\tnestedSelf?: {\n\t\t\t\t\tparentRunId: string;\n\t\t\t\t\tparentStepIndex?: number;\n\t\t\t\t\tdepth: number;\n\t\t\t\t\tpath?: Array<{ runId: string; stepIndex?: number; agent?: string }>;\n\t\t\t\t};\n\t\t\t};\n\t\t\tconst asyncDir = launch.asyncDir;\n\t\t\tconst runId = launch.id;\n\t\t\tif (typeof asyncDir !== \"string\" || typeof runId !== \"string\") return;\n\t\t\tfinalizeProcessTerminal(asyncDir, runId, {\n\t\t\t\tprocessInstanceId: runnerProcessInstanceId,\n\t\t\t\tcloseObservedAt: Date.now(),\n\t\t\t\texitCode,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tconst persisted = readProcessTerminal(asyncDir, { runId, runnerProcessInstanceId });\n\t\t\tif (!persisted) return;\n\t\t\tif (launch.nestedRoute && launch.nestedSelf) {\n\t\t\t\ttry {\n\t\t\t\t\tlet status: import(\"../../shared/types.ts\").AsyncStatus;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstatus = JSON.parse(\n\t\t\t\t\t\t\tfs.readFileSync(path.join(asyncDir, \"status.json\"), \"utf-8\"),\n\t\t\t\t\t\t) as import(\"../../shared/types.ts\").AsyncStatus;\n\t\t\t\t\t\tstatus.processTerminal = persisted;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tstatus = {\n\t\t\t\t\t\t\trunId,\n\t\t\t\t\t\t\tmode: \"single\",\n\t\t\t\t\t\t\tstate: persisted.state === \"observed\" ? \"complete\" : \"failed\",\n\t\t\t\t\t\t\tstartedAt: persisted.observedAt ?? Date.now(),\n\t\t\t\t\t\t\tlastUpdate: Date.now(),\n\t\t\t\t\t\t\tprocessTerminal: persisted,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\twriteNestedEvent(launch.nestedRoute, {\n\t\t\t\t\t\ttype: \"subagent.nested.completed\",\n\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\tparentRunId: launch.nestedSelf.parentRunId,\n\t\t\t\t\t\tparentStepIndex: launch.nestedSelf.parentStepIndex,\n\t\t\t\t\t\tchild: nestedSummaryFromAsyncStatus(status, asyncDir, {\n\t\t\t\t\t\t\tid: runId,\n\t\t\t\t\t\t\tparentRunId: launch.nestedSelf.parentRunId,\n\t\t\t\t\t\t\tparentStepIndex: launch.nestedSelf.parentStepIndex,\n\t\t\t\t\t\t\tdepth: launch.nestedSelf.depth,\n\t\t\t\t\t\t\tpath: launch.nestedSelf.path,\n\t\t\t\t\t\t\tmode: status.mode,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Failed to emit final nested process-terminal status:\", error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tonProcessTerminal?.(persisted);\n\t\t});\n\t\tif (typeof proc.pid !== \"number\") {\n\t\t\treturn { error: `async runner did not produce a pid for cwd: ${cwd}` };\n\t\t}\n\t\tproc.unref();\n\t\tif (startupPath && startupAckPath && startupProceedPath) {\n\t\t\tconst ready = waitForRunnerStartup(startupPath, \"ready\", RUNNER_STARTUP_TIMEOUT_MS);\n\t\t\tif (ready.ok === false) {\n\t\t\t\tterminateRunnerBeforeProceed(proc.pid);\n\t\t\t\treturn { error: ready.error };\n\t\t\t}\n\t\t\ttry {\n\t\t\t\twriteRunnerStartupControl(startupAckPath, { action: \"ack\", token: ready.token });\n\t\t\t} catch (error) {\n\t\t\t\tterminateRunnerBeforeProceed(proc.pid);\n\t\t\t\treturn {\n\t\t\t\t\terror: `Failed to acknowledge async runner startup: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst acknowledged = waitForRunnerStartup(startupPath, \"acknowledged\", RUNNER_STARTUP_TIMEOUT_MS, ready.token);\n\t\t\tif (acknowledged.ok === false) {\n\t\t\t\tterminateRunnerBeforeProceed(proc.pid);\n\t\t\t\treturn { error: acknowledged.error };\n\t\t\t}\n\t\t\ttry {\n\t\t\t\twriteRunnerStartupControl(startupProceedPath, { action: \"proceed\", token: ready.token });\n\t\t\t} catch (error) {\n\t\t\t\tterminateRunnerBeforeProceed(proc.pid);\n\t\t\t\treturn {\n\t\t\t\t\terror: `Failed to authorize async runner startup: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tfs.rmSync(startupPath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// Proceed is the commit point; handshake cleanup cannot turn a running revival into a start error.\n\t\t\t}\n\t\t}\n\t\treturn { pid: proc.pid };\n\t} catch (error) {\n\t\tcloseFd(stdoutFd);\n\t\tcloseFd(stderrFd);\n\t\treturn { error: error instanceof Error ? error.message : String(error) };\n\t}\n}\n\nfunction formatAsyncStartError(mode: SubagentRunMode, message: string): AsyncExecutionResult {\n\treturn {\n\t\tcontent: [{ type: \"text\", text: message }],\n\t\tisError: true,\n\t\tdetails: { mode, results: [] },\n\t};\n}\n\nconst UNAVAILABLE_SUBAGENT_SKILL_ERROR = \"Skills not found: pi-subagents\";\n\nclass UnavailableSubagentSkillError extends Error {}\nclass AsyncStartValidationError extends Error {}\n\nexport function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildParams): AsyncRunnerStepBuildResult {\n\tconst {\n\t\tchain,\n\t\tagents,\n\t\tctx,\n\t\tcwd,\n\t\tsessionFilesByFlatIndex,\n\t\tthinkingOverridesByFlatIndex,\n\t\tmaxSubagentDepth,\n\t\tworktreeBaseDir,\n\t\tasyncDir,\n\t} = params;\n\tconst outputBaseDir = params.outputBaseDir;\n\tconst resultMode = params.resultMode ?? \"chain\";\n\tconst chainSkills = params.chainSkills ?? [];\n\tconst availableModels = params.availableModels;\n\tconst runnerCwd = resolveChildCwd(ctx.cwd, cwd);\n\tconst progressDir = params.progressDir ?? runnerCwd;\n\tconst graphChain: ChainStep[] = params.attachRoot\n\t\t? [\n\t\t\t\t{\n\t\t\t\t\tagent: params.attachRoot.agent,\n\t\t\t\t\ttask: `Attach async root ${params.attachRoot.runId}`,\n\t\t\t\t\tlabel: params.attachRoot.label ?? `Attached root ${params.attachRoot.runId}`,\n\t\t\t\t\t...(params.attachRoot.outputName ? { as: params.attachRoot.outputName } : {}),\n\t\t\t\t},\n\t\t\t\t...chain,\n\t\t\t]\n\t\t: chain;\n\tconst firstStep = chain[0];\n\tconst originalTask =\n\t\tparams.task ??\n\t\t(firstStep\n\t\t\t? isCheckpointStep(firstStep)\n\t\t\t\t? undefined\n\t\t\t\t: isParallelStep(firstStep)\n\t\t\t\t\t? firstStep.parallel[0]?.task\n\t\t\t\t\t: isDynamicParallelStep(firstStep)\n\t\t\t\t\t\t? firstStep.parallel.task\n\t\t\t\t\t\t: (firstStep as SequentialStep).task\n\t\t\t: undefined);\n\ttry {\n\t\tif (params.validateOutputBindings !== false) {\n\t\t\tvalidateChainOutputBindings(chain, { maxItems: params.dynamicFanoutMaxItems });\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof ChainOutputValidationError) return { error: error.message };\n\t\tthrow error;\n\t}\n\tconst workflowGraph = buildWorkflowGraphSnapshot({ runId: id, mode: resultMode, steps: graphChain });\n\n\tfor (const s of chain) {\n\t\tconst stepAgents = isCheckpointStep(s)\n\t\t\t? []\n\t\t\t: isParallelStep(s)\n\t\t\t\t? s.parallel.map((t) => t.agent)\n\t\t\t\t: isDynamicParallelStep(s)\n\t\t\t\t\t? [s.parallel.agent]\n\t\t\t\t\t: [(s as SequentialStep).agent];\n\t\tfor (const agentName of stepAgents) {\n\t\t\tif (!agents.find((x) => x.name === agentName)) {\n\t\t\t\treturn { error: `Unknown agent: ${agentName}` };\n\t\t\t}\n\t\t}\n\t}\n\n\tlet progressInstructionCreated = false;\n\tconst buildStepOverrides = (s: SequentialStep): StepOverrides => {\n\t\tconst stepSkillInput = normalizeSkillInput(s.skill);\n\t\treturn {\n\t\t\t...(s.output !== undefined ? { output: s.output } : {}),\n\t\t\t...(s.outputMode !== undefined ? { outputMode: s.outputMode } : {}),\n\t\t\t...(s.reads !== undefined ? { reads: s.reads } : {}),\n\t\t\t...(s.progress !== undefined ? { progress: s.progress } : {}),\n\t\t\t...(stepSkillInput !== undefined ? { skills: stepSkillInput } : {}),\n\t\t\t...(s.model !== undefined ? { model: s.model } : {}),\n\t\t};\n\t};\n\tconst buildSeqStep = (\n\t\ts: SequentialStep,\n\t\tsessionFile?: string,\n\t\tbehaviorCwd?: string,\n\t\tprogressPrecreated = false,\n\t\tresolvedBehavior?: ResolvedStepBehavior,\n\t\tflatIndex?: number,\n\t\tparallelOutputNamespace?: { stepIndex: number; taskIndex?: number },\n\t) => {\n\t\tconst a = agents.find((x) => x.name === s.agent)!;\n\t\tconst externalRunner = a.runner?.type === \"external-cli\";\n\t\tif (externalRunner) {\n\t\t\tconst unsupported: string[] = [];\n\t\t\tif (s.model !== undefined) unsupported.push(\"model override\");\n\t\t\tif (s.outputSchema !== undefined) unsupported.push(\"structured output\");\n\t\t\tif (s.acceptance !== undefined || params.agentContract !== undefined || s.agentContract !== undefined)\n\t\t\t\tunsupported.push(\"acceptance/agent contract\");\n\t\t\tif (\n\t\t\t\ts.toolBudget !== undefined ||\n\t\t\t\tparams.toolBudget !== undefined ||\n\t\t\t\ta.toolBudget !== undefined ||\n\t\t\t\tparams.configToolBudget !== undefined\n\t\t\t)\n\t\t\t\tunsupported.push(\"tool budget\");\n\t\t\tif (params.contextForAgent?.(s.agent) === \"fork\") unsupported.push(\"fork context\");\n\t\t\tif (unsupported.length > 0)\n\t\t\t\tthrow new AsyncStartValidationError(\n\t\t\t\t\t`Agent '${a.name}' uses runner.type='external-cli' and does not support: ${unsupported.join(\", \")}.`,\n\t\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tassertAgentAllowedByCapabilityCeiling(\n\t\t\t\ta.name,\n\t\t\t\tintersectSubagentCapabilityCeilings(\n\t\t\t\t\tparams.capabilityCeiling,\n\t\t\t\t\tdecodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),\n\t\t\t\t),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new AsyncStartValidationError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t\tconst toolBudgetInput = s.toolBudget ?? params.toolBudget ?? a.toolBudget ?? params.configToolBudget;\n\t\tconst resolvedToolBudget = validateToolBudgetConfig(\n\t\t\ttoolBudgetInput,\n\t\t\ts.toolBudget ? \"toolBudget\" : a.toolBudget ? \"agent.toolBudget\" : \"config.toolBudget\",\n\t\t);\n\t\tif (resolvedToolBudget.error) throw new AsyncStartValidationError(resolvedToolBudget.error);\n\t\tconst stepCwd = resolveChildCwd(runnerCwd, s.cwd);\n\t\tconst instructionCwd = behaviorCwd ?? stepCwd;\n\t\tlet behavior = suppressProgressForReadOnlyTask(\n\t\t\tresolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills),\n\t\t\ts.task,\n\t\t\toriginalTask,\n\t\t);\n\t\tconst inheritedRelativeParallelOutput =\n\t\t\tparallelOutputNamespace &&\n\t\t\ts.output === undefined &&\n\t\t\ttypeof behavior.output === \"string\" &&\n\t\t\t!path.isAbsolute(behavior.output);\n\t\tif (inheritedRelativeParallelOutput && parallelOutputNamespace.taskIndex !== undefined) {\n\t\t\tbehavior = {\n\t\t\t\t...behavior,\n\t\t\t\toutput: path.join(\n\t\t\t\t\t`parallel-${parallelOutputNamespace.stepIndex}`,\n\t\t\t\t\t`${parallelOutputNamespace.taskIndex}-${s.agent}`,\n\t\t\t\t\tbehavior.output as string,\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\t\tconst namespaceOutputPath = Boolean(\n\t\t\tinheritedRelativeParallelOutput && parallelOutputNamespace.taskIndex === undefined,\n\t\t);\n\t\tconst skillNames = behavior.skills === false ? [] : behavior.skills;\n\t\tconst { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(\n\t\t\tskillNames,\n\t\t\tstepCwd,\n\t\t\tctx.cwd,\n\t\t\ta.skillPath,\n\t\t\ta.filePath ? path.dirname(a.filePath) : stepCwd,\n\t\t);\n\t\tif (missingSkills.includes(\"pi-subagents\"))\n\t\t\tthrow new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);\n\n\t\tlet systemPrompt = a.systemPrompt?.trim() ?? \"\";\n\t\tif (resolvedSkills.length > 0) {\n\t\t\tconst injection = buildSkillInjection(resolvedSkills);\n\t\t\tsystemPrompt = systemPrompt ? `${systemPrompt}\\n\\n${injection}` : injection;\n\t\t}\n\t\tconst memoryInjection = buildAgentMemoryInjection(a, stepCwd);\n\t\tif (memoryInjection) {\n\t\t\tsystemPrompt = systemPrompt ? `${systemPrompt}\\n\\n${memoryInjection}` : memoryInjection;\n\t\t}\n\t\tsystemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: stepCwd, agentName: a.name });\n\n\t\tconst readInstructions = buildChainInstructions(\n\t\t\t{ ...behavior, output: false, progress: false },\n\t\t\tinstructionCwd,\n\t\t\tfalse,\n\t\t);\n\t\tconst isFirstProgressAgent = behavior.progress && !progressPrecreated && !progressInstructionCreated;\n\t\tif (behavior.progress) progressInstructionCreated = true;\n\t\tconst progressInstructions = buildChainInstructions(\n\t\t\t{ ...behavior, output: false, reads: false },\n\t\t\tprogressDir,\n\t\t\tisFirstProgressAgent,\n\t\t);\n\t\tconst outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd, outputBaseDir);\n\t\tif (!namespaceOutputPath) systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, a);\n\t\tconst validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);\n\t\tif (validationError) throw new AsyncStartValidationError(validationError);\n\t\tlet taskTemplate = s.task ?? \"{previous}\";\n\t\ttaskTemplate = taskTemplate.replace(/\\{task\\}/g, originalTask ?? \"\");\n\t\ttaskTemplate = taskTemplate.replace(/\\{chain_dir\\}/g, runnerCwd);\n\t\tconst taskText = `${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`;\n\t\tconst task = namespaceOutputPath ? taskText : injectSingleOutputInstruction(taskText, outputPath, a);\n\n\t\tconst primaryModel = externalRunner\n\t\t\t? undefined\n\t\t\t: resolveEffectiveSubagentModel(\n\t\t\t\t\ts.model,\n\t\t\t\t\ta.model,\n\t\t\t\t\tctx.currentModel,\n\t\t\t\t\tavailableModels,\n\t\t\t\t\tctx.currentModelProvider,\n\t\t\t\t\t{ scope: ctx.modelScope },\n\t\t\t\t);\n\t\tconst thinkingOverride = flatIndex === undefined ? undefined : thinkingOverridesByFlatIndex?.[flatIndex];\n\t\tconst effectiveThinking = externalRunner ? undefined : (thinkingOverride ?? a.thinking);\n\t\tconst model = externalRunner\n\t\t\t? undefined\n\t\t\t: applyThinkingSuffix(primaryModel, effectiveThinking, thinkingOverride !== undefined);\n\t\tconst agentContract = s.agentContract ?? params.agentContract;\n\t\tconst toolPlan = resolvePiLaunchToolPlan({\n\t\t\ttools: a.tools,\n\t\t\textensions: a.extensions,\n\t\t\tsubagentOnlyExtensions: a.subagentOnlyExtensions,\n\t\t\tmcpDirectTools: a.mcpDirectTools,\n\t\t\tcwd: stepCwd,\n\t\t\trequireReadTool: Boolean(resolvedSkills.length),\n\t\t\tstructuredOutput: Boolean(s.outputSchema),\n\t\t\tcapabilityCeiling: params.capabilityCeiling,\n\t\t\tinheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),\n\t\t\tagentName: a.name,\n\t\t});\n\t\tconst launchResolvedExtensions = externalRunner ? undefined : projectLaunchResolvedChildExtensions(toolPlan);\n\t\tconst permissionRules = resolvePermissionRules(ctx.permissions, a.permissions);\n\t\tif (externalRunner && permissionRules) {\n\t\t\tthrow new AsyncStartValidationError(\n\t\t\t\t`Agent '${a.name}' uses runner.type='external-cli', which cannot enforce native Pi child permission rules.`,\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tparentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,\n\t\t\tpermissionRules,\n\t\t\t...(params.capabilityCeiling ? { capabilityCeiling: params.capabilityCeiling } : {}),\n\t\t\tagent: s.agent,\n\t\t\ttask,\n\t\t\t...(a.runner ? { runner: a.runner } : {}),\n\t\t\t...(params.contextForAgent ? { context: params.contextForAgent(s.agent) } : {}),\n\t\t\t...(agentContract ? { agentContract } : {}),\n\t\t\tphase: s.phase,\n\t\t\tlabel: s.label,\n\t\t\toutputName: s.as,\n\t\t\tstructured: Boolean(s.outputSchema),\n\t\t\tcwd: stepCwd,\n\t\t\tmodel,\n\t\t\tthinking: resolveEffectiveThinking(model, effectiveThinking),\n\t\t\tlaunchResolvedExtensions,\n\t\t\tmodelCandidates: externalRunner\n\t\t\t\t? undefined\n\t\t\t\t: buildModelCandidates(primaryModel, a.fallbackModels, availableModels, ctx.currentModelProvider, {\n\t\t\t\t\t\tscope: ctx.modelScope,\n\t\t\t\t\t}).map((candidate) => applyThinkingSuffix(candidate, effectiveThinking, thinkingOverride !== undefined)),\n\t\t\ttools: a.tools,\n\t\t\textensions: a.extensions,\n\t\t\tsubagentOnlyExtensions: a.subagentOnlyExtensions,\n\t\t\tmcpDirectTools: a.mcpDirectTools,\n\t\t\tcompletionGuard: a.completionGuard,\n\t\t\tsystemPrompt,\n\t\t\tsystemPromptMode: a.systemPromptMode,\n\t\t\tinheritProjectContext: a.inheritProjectContext,\n\t\t\tinheritSkills: a.inheritSkills,\n\t\t\tskills: resolvedSkills.map((r) => r.name),\n\t\t\toutputPath,\n\t\t\t...(namespaceOutputPath ? { namespaceOutputPath: true } : {}),\n\t\t\toutputMode: behavior.outputMode,\n\t\t\tsessionFile,\n\t\t\tmaxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),\n\t\t\twaitToolEnabled: params.waitToolEnabled,\n\t\t\teffectiveAcceptance: resolveEffectiveAcceptance({\n\t\t\t\texplicit: s.acceptance,\n\t\t\t\tagentName: s.agent,\n\t\t\t\tacceptanceRole: a.acceptanceRole,\n\t\t\t\ttask,\n\t\t\t\tmode: resultMode,\n\t\t\t\tasync: true,\n\t\t\t\tdynamic: false,\n\t\t\t\tagentContract,\n\t\t\t}),\n\t\t\tacceptanceInput: s.acceptance,\n\t\t\tacceptanceRole: a.acceptanceRole,\n\t\t\t...(s.gateOn ? { gateOn: s.gateOn } : {}),\n\t\t\t...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),\n\t\t\t...(s.outputSchema\n\t\t\t\t? {\n\t\t\t\t\t\tstructuredOutput: createStructuredOutputRuntime(\n\t\t\t\t\t\t\ts.outputSchema,\n\t\t\t\t\t\t\tpath.join(asyncDir, \"structured-output\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),\n\t\t};\n\t};\n\n\tlet flatStepIndex = 0;\n\tconst nextFlatStep = (): { index: number; sessionFile?: string; thinkingOverride?: AgentConfig[\"thinking\"] } => {\n\t\tconst index = flatStepIndex;\n\t\tconst sessionFile = sessionFilesByFlatIndex?.[flatStepIndex];\n\t\tconst thinkingOverride = thinkingOverridesByFlatIndex?.[flatStepIndex];\n\t\tflatStepIndex++;\n\t\treturn {\n\t\t\tindex,\n\t\t\t...(sessionFile ? { sessionFile } : {}),\n\t\t\t...(thinkingOverride ? { thinkingOverride } : {}),\n\t\t};\n\t};\n\n\ttry {\n\t\tconst builtSteps = chain.map((s, stepIndex) => {\n\t\t\tif (isCheckpointStep(s)) {\n\t\t\t\treturn {\n\t\t\t\t\tcheckpoint: s.checkpoint,\n\t\t\t\t\t...(s.message ? { message: s.message } : {}),\n\t\t\t\t\tphase: s.phase,\n\t\t\t\t\tlabel: s.label,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (isParallelStep(s)) {\n\t\t\t\tconst parallelBehaviors = s.parallel.map((task) => {\n\t\t\t\t\tconst agent = agents.find((candidate) => candidate.name === task.agent)!;\n\t\t\t\t\treturn suppressProgressForReadOnlyTask(\n\t\t\t\t\t\tresolveStepBehavior(agent, buildStepOverrides(task), chainSkills),\n\t\t\t\t\t\ttask.task,\n\t\t\t\t\t\toriginalTask,\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\tconst progressPrecreated = parallelBehaviors.some((behavior) => behavior.progress);\n\t\t\t\tif (progressPrecreated) {\n\t\t\t\t\tif (!s.worktree || params.progressDir) writeInitialProgressFile(progressDir);\n\t\t\t\t\tprogressInstructionCreated = true;\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tparallel: s.parallel.map((t, taskIndex) => {\n\t\t\t\t\t\tlet behaviorCwd: string | undefined;\n\t\t\t\t\t\tif (s.worktree) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tbehaviorCwd = resolveExpectedWorktreeAgentCwd(\n\t\t\t\t\t\t\t\t\trunnerCwd,\n\t\t\t\t\t\t\t\t\t`${id}-s${stepIndex}`,\n\t\t\t\t\t\t\t\t\ttaskIndex,\n\t\t\t\t\t\t\t\t\tworktreeBaseDir,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tbehaviorCwd = undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst staticStep = nextFlatStep();\n\t\t\t\t\t\treturn buildSeqStep(\n\t\t\t\t\t\t\t{ ...t, agentContract: t.agentContract ?? s.agentContract, gateOn: t.gateOn ?? s.gateOn },\n\t\t\t\t\t\t\tstaticStep.sessionFile,\n\t\t\t\t\t\t\tbehaviorCwd,\n\t\t\t\t\t\t\tprogressPrecreated,\n\t\t\t\t\t\t\tparallelBehaviors[taskIndex],\n\t\t\t\t\t\t\tstaticStep.index,\n\t\t\t\t\t\t\t{ stepIndex, taskIndex },\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t\tconcurrency: s.concurrency,\n\t\t\t\t\tfailFast: s.failFast,\n\t\t\t\t\tworktree: s.worktree,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (isDynamicParallelStep(s)) {\n\t\t\t\tconst agent = agents.find((candidate) => candidate.name === s.parallel.agent)!;\n\t\t\t\tconst behavior = suppressProgressForReadOnlyTask(\n\t\t\t\t\tresolveStepBehavior(agent, buildStepOverrides(s.parallel), chainSkills),\n\t\t\t\t\ts.parallel.task,\n\t\t\t\t\toriginalTask,\n\t\t\t\t);\n\t\t\t\tconst progressPrecreated = behavior.progress;\n\t\t\t\tif (progressPrecreated) {\n\t\t\t\t\twriteInitialProgressFile(progressDir);\n\t\t\t\t\tprogressInstructionCreated = true;\n\t\t\t\t}\n\t\t\t\tconst maxItems = s.expand.maxItems ?? params.dynamicFanoutMaxItems ?? 0;\n\t\t\t\tconst dynamicFlatSteps = Array.from({ length: maxItems }, () => nextFlatStep());\n\t\t\t\tconst parallel = buildSeqStep(\n\t\t\t\t\t{\n\t\t\t\t\t\t...(s.parallel as SequentialStep),\n\t\t\t\t\t\tagentContract: s.parallel.agentContract ?? s.agentContract,\n\t\t\t\t\t\tgateOn: s.parallel.gateOn ?? s.gateOn,\n\t\t\t\t\t},\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tprogressPrecreated,\n\t\t\t\t\tbehavior,\n\t\t\t\t\tundefined,\n\t\t\t\t\t{ stepIndex },\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\texpand: s.expand,\n\t\t\t\t\tparallel,\n\t\t\t\t\tcollect: s.collect,\n\t\t\t\t\tconcurrency: s.concurrency,\n\t\t\t\t\tfailFast: s.failFast,\n\t\t\t\t\tphase: s.phase,\n\t\t\t\t\tlabel: s.label,\n\t\t\t\t\tsessionFiles: dynamicFlatSteps.map((step) => step.sessionFile),\n\t\t\t\t\tthinkingOverrides: dynamicFlatSteps.map((step) => step.thinkingOverride),\n\t\t\t\t\teffectiveAcceptance: resolveEffectiveAcceptance({\n\t\t\t\t\t\texplicit: s.acceptance,\n\t\t\t\t\t\tagentName: s.parallel.agent,\n\t\t\t\t\t\tacceptanceRole: agent.acceptanceRole,\n\t\t\t\t\t\ttask: parallel.task,\n\t\t\t\t\t\tmode: resultMode,\n\t\t\t\t\t\tasync: true,\n\t\t\t\t\t\tdynamicGroup: true,\n\t\t\t\t\t\tagentContract: s.agentContract ?? params.agentContract,\n\t\t\t\t\t}),\n\t\t\t\t\tacceptanceInput: s.acceptance,\n\t\t\t\t\tacceptanceRole: agent.acceptanceRole,\n\t\t\t\t\t...((s.agentContract ?? params.agentContract)\n\t\t\t\t\t\t? { agentContract: s.agentContract ?? params.agentContract }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(s.gateOn ? { gateOn: s.gateOn } : {}),\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst staticStep = nextFlatStep();\n\t\t\treturn buildSeqStep(\n\t\t\t\ts as SequentialStep,\n\t\t\t\tstaticStep.sessionFile,\n\t\t\t\tundefined,\n\t\t\t\tfalse,\n\t\t\t\tundefined,\n\t\t\t\tstaticStep.index,\n\t\t\t);\n\t\t});\n\t\tconst steps = params.attachRoot\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tagent: params.attachRoot.agent,\n\t\t\t\t\t\ttask: \"\",\n\t\t\t\t\t\tlabel: params.attachRoot.label ?? `Attached root ${params.attachRoot.runId}`,\n\t\t\t\t\t\toutputName: params.attachRoot.outputName,\n\t\t\t\t\t\timportAsyncRoot: {\n\t\t\t\t\t\t\trunId: params.attachRoot.runId,\n\t\t\t\t\t\t\tasyncDir: params.attachRoot.asyncDir,\n\t\t\t\t\t\t\tresultPath: params.attachRoot.resultPath,\n\t\t\t\t\t\t\tindex: params.attachRoot.index,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tinheritProjectContext: false,\n\t\t\t\t\t\tinheritSkills: false,\n\t\t\t\t\t},\n\t\t\t\t\t...builtSteps,\n\t\t\t\t]\n\t\t\t: builtSteps;\n\t\tfor (const step of steps) {\n\t\t\tif (!(\"parallel\" in step) || !Array.isArray(step.parallel)) continue;\n\t\t\tconst seen = new Map<string, { index: number; agent: string }>();\n\t\t\tfor (let index = 0; index < step.parallel.length; index++) {\n\t\t\t\tconst task = step.parallel[index]!;\n\t\t\t\tif (!task.outputPath) continue;\n\t\t\t\tconst previous = seen.get(task.outputPath);\n\t\t\t\tif (previous) {\n\t\t\t\t\tthrow new AsyncStartValidationError(\n\t\t\t\t\t\t`Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${task.outputPath}. Use distinct output paths.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tseen.set(task.outputPath, { index, agent: task.agent });\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tsteps: steps as RunnerStep[],\n\t\t\trunnerCwd,\n\t\t\tworkflowGraph,\n\t\t\teventChain: graphChain,\n\t\t\t...(originalTask !== undefined ? { originalTask } : {}),\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof UnavailableSubagentSkillError || error instanceof AsyncStartValidationError)\n\t\t\treturn { error: error.message };\n\t\tthrow error;\n\t}\n}\n\n/**\n * Execute a chain asynchronously\n */\nexport function executeAsyncChain(id: string, params: AsyncChainParams): AsyncExecutionResult {\n\tconst {\n\t\tchain,\n\t\tagents,\n\t\tctx,\n\t\tcwd,\n\t\tmaxOutput,\n\t\tartifactsDir,\n\t\tartifactConfig,\n\t\tshareEnabled,\n\t\tsessionRoot,\n\t\tsessionFilesByFlatIndex,\n\t\tthinkingOverridesByFlatIndex,\n\t\tmaxSubagentDepth,\n\t\tworktreeSetupHook,\n\t\tworktreeSetupHookTimeoutMs,\n\t\tworktreeBaseDir,\n\t\tcontrolConfig,\n\t\tcontrolIntercomTarget,\n\t\tchildIntercomTarget,\n\t\tnestedRoute,\n\t} = params;\n\tconst resultMode = params.resultMode ?? \"chain\";\n\tconst acceptanceErrors = validateExecutionAcceptance({\n\t\tchain: chain.map((step) => {\n\t\t\tif (isCheckpointStep(step)) return {};\n\t\t\tif (isParallelStep(step)) return { parallel: step.parallel };\n\t\t\tif (isDynamicParallelStep(step)) return { acceptance: step.acceptance, parallel: step.parallel };\n\t\t\treturn { acceptance: step.acceptance };\n\t\t}),\n\t});\n\tif (acceptanceErrors.length > 0) return formatAsyncStartError(resultMode, acceptanceErrors.join(\" \"));\n\tconst capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);\n\tconst inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();\n\tconst nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;\n\tconst asyncDir = inheritedNestedRoute\n\t\t? path.join(TEMP_ROOT_DIR, \"nested-subagent-runs\", inheritedNestedRoute.rootRunId, id)\n\t\t: path.join(DIRS.async, id);\n\ttry {\n\t\tfs.mkdirSync(asyncDir, { recursive: true });\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Failed to create async run directory '${asyncDir}': ${message}` }],\n\t\t\tisError: true,\n\t\t\tdetails: { mode: resultMode, results: [] },\n\t\t};\n\t}\n\n\tconst built = buildAsyncRunnerSteps(id, {\n\t\tchain,\n\t\ttask: params.task,\n\t\tattachRoot: params.attachRoot,\n\t\tresultMode,\n\t\tagents,\n\t\tctx,\n\t\tavailableModels: params.availableModels,\n\t\tcwd,\n\t\tchainSkills: params.chainSkills,\n\t\tsessionFilesByFlatIndex,\n\t\tthinkingOverridesByFlatIndex,\n\t\tcontextForAgent: params.contextForAgent,\n\t\tprogressDir:\n\t\t\tparams.progressDir ??\n\t\t\t(artifactsDir\n\t\t\t\t? path.join(artifactsDir, \"progress\", id)\n\t\t\t\t: resultMode === \"parallel\"\n\t\t\t\t\t? path.join(asyncDir, \"progress\")\n\t\t\t\t\t: undefined),\n\t\tagentContract: params.agentContract,\n\t\toutputBaseDir: artifactsDir ? path.join(artifactsDir, \"outputs\", id) : undefined,\n\t\tdynamicFanoutMaxItems: params.dynamicFanoutMaxItems,\n\t\tmaxSubagentDepth,\n\t\twaitToolEnabled: params.waitToolEnabled,\n\t\tworktreeBaseDir,\n\t\tasyncDir,\n\t\ttoolBudget: params.toolBudget,\n\t\tconfigToolBudget: params.configToolBudget,\n\t\tcapabilityCeiling,\n\t});\n\tif (\"error\" in built) {\n\t\ttry {\n\t\t\tfs.rmSync(asyncDir, { recursive: true, force: true });\n\t\t} catch {\n\t\t\t// Best-effort cleanup for validation failures before the runner is spawned.\n\t\t}\n\t\treturn formatAsyncStartError(resultMode, built.error);\n\t}\n\tconst { steps, runnerCwd, workflowGraph, eventChain } = built;\n\tconst deadlineAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;\n\tconst initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;\n\tconst initialUsageBudget = usageBudgetState(params.usageBudget, undefined);\n\tlet childTargetIndex = 0;\n\tconst childIntercomTargets = childIntercomTarget\n\t\t? steps.flatMap((step) => {\n\t\t\t\tif (!(\"parallel\" in step) && \"importAsyncRoot\" in step && step.importAsyncRoot) {\n\t\t\t\t\tchildTargetIndex++;\n\t\t\t\t\treturn [undefined];\n\t\t\t\t}\n\t\t\t\tif (\"parallel\" in step) {\n\t\t\t\t\tif (!Array.isArray(step.parallel)) {\n\t\t\t\t\t\tchildTargetIndex++;\n\t\t\t\t\t\treturn [undefined];\n\t\t\t\t\t}\n\t\t\t\t\treturn step.parallel.map((task) => childIntercomTarget(task.agent, childTargetIndex++));\n\t\t\t\t}\n\t\t\t\treturn \"agent\" in step ? [childIntercomTarget(step.agent, childTargetIndex++)] : [undefined];\n\t\t\t})\n\t\t: undefined;\n\n\tlet spawnResult: { pid?: number; error?: string } = {};\n\ttry {\n\t\tspawnResult = spawnRunner(\n\t\t\t{\n\t\t\t\tid,\n\t\t\t\tsteps,\n\t\t\t\tresultPath: inheritedNestedRoute\n\t\t\t\t\t? nestedResultsPath(inheritedNestedRoute.rootRunId, id)\n\t\t\t\t\t: path.join(DIRS.results, `${id}.json`),\n\t\t\t\tcwd: runnerCwd,\n\t\t\t\tplaceholder: \"{previous}\",\n\t\t\t\tmaxOutput,\n\t\t\t\tartifactsDir: artifactConfig.enabled ? artifactsDir : undefined,\n\t\t\t\tartifactConfig,\n\t\t\t\tshare: shareEnabled,\n\t\t\t\tsessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,\n\t\t\t\tasyncDir,\n\t\t\t\tsessionId: ctx.currentSessionId,\n\t\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t\tpiPackageRoot,\n\t\t\t\tpiArgv1: process.argv[1],\n\t\t\t\tworktreeSetupHook,\n\t\t\t\tworktreeSetupHookTimeoutMs,\n\t\t\t\tworktreeBaseDir,\n\t\t\t\tcontrolConfig,\n\t\t\t\tturnBudget: params.turnBudget,\n\t\t\t\ttoolBudget: params.toolBudget,\n\t\t\t\tusageBudget: params.usageBudget,\n\t\t\t\tcontrolIntercomTarget,\n\t\t\t\tchildIntercomTargets,\n\t\t\t\tresultMode,\n\t\t\t\tdynamicFanoutMaxItems: params.dynamicFanoutMaxItems,\n\t\t\t\ttimeoutMs: params.timeoutMs,\n\t\t\t\tdeadlineAt,\n\t\t\t\tglobalConcurrencyLimit: params.globalConcurrencyLimit,\n\t\t\t\tworkflowGraph,\n\t\t\t\t...(params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}),\n\t\t\t\t...(params.workflowKey ? { workflowKey: params.workflowKey } : {}),\n\t\t\t\tnestedRoute: nestedRoute ?? inheritedNestedRoute,\n\t\t\t\tnestedSelf:\n\t\t\t\t\tinheritedNestedRoute && nestedAddress\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tparentRunId: nestedAddress.parentRunId,\n\t\t\t\t\t\t\t\tparentStepIndex: nestedAddress.parentStepIndex,\n\t\t\t\t\t\t\t\tdepth: nestedAddress.depth,\n\t\t\t\t\t\t\t\tpath: nestedAddress.path,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t},\n\t\t\tid,\n\t\t\trunnerCwd,\n\t\t\t(proof) => ctx.pi.events.emit(SUBAGENT_PROCESS_TERMINAL_EVENT, proof),\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${message}`);\n\t}\n\n\tif (spawnResult.error) {\n\t\treturn formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${spawnResult.error}`);\n\t}\n\n\tif (spawnResult.pid) {\n\t\tconst eventFirstStep = eventChain[0];\n\t\tif (!eventFirstStep) {\n\t\t\treturn formatAsyncStartError(\n\t\t\t\tresultMode,\n\t\t\t\t`Failed to start async ${resultMode} '${id}': event chain has no steps`,\n\t\t\t);\n\t\t}\n\t\tconst firstAgents = isParallelStep(eventFirstStep)\n\t\t\t? eventFirstStep.parallel.map((t) => t.agent)\n\t\t\t: isDynamicParallelStep(eventFirstStep)\n\t\t\t\t? [eventFirstStep.parallel.agent]\n\t\t\t\t: [(eventFirstStep as SequentialStep).agent];\n\t\tconst firstTask = isParallelStep(eventFirstStep)\n\t\t\t? eventFirstStep.parallel[0]?.task\n\t\t\t: isDynamicParallelStep(eventFirstStep)\n\t\t\t\t? eventFirstStep.parallel.task\n\t\t\t\t: (eventFirstStep as SequentialStep).task;\n\t\tconst workflowGoal = params.goal ?? (params.task?.trim() || firstTask);\n\t\tconst parallelGroups: Array<{ start: number; count: number; stepIndex: number }> = [];\n\t\tconst flatAgents: string[] = [];\n\t\tlet flatStepStart = 0;\n\t\tfor (let stepIndex = 0; stepIndex < eventChain.length; stepIndex++) {\n\t\t\tconst step = eventChain[stepIndex]!;\n\t\t\tif (isParallelStep(step)) {\n\t\t\t\tparallelGroups.push({ start: flatStepStart, count: step.parallel.length, stepIndex });\n\t\t\t\tflatAgents.push(...step.parallel.map((task) => task.agent));\n\t\t\t\tflatStepStart += step.parallel.length;\n\t\t\t} else if (isDynamicParallelStep(step)) {\n\t\t\t\tparallelGroups.push({ start: flatStepStart, count: 1, stepIndex });\n\t\t\t\tflatAgents.push(step.parallel.agent);\n\t\t\t\tflatStepStart++;\n\t\t\t} else {\n\t\t\t\tflatAgents.push((step as SequentialStep).agent);\n\t\t\t\tflatStepStart++;\n\t\t\t}\n\t\t}\n\t\tif (inheritedNestedRoute && nestedAddress) {\n\t\t\tconst now = Date.now();\n\t\t\ttry {\n\t\t\t\twriteNestedEvent(inheritedNestedRoute, {\n\t\t\t\t\ttype: \"subagent.nested.started\",\n\t\t\t\t\tts: now,\n\t\t\t\t\tparentRunId: nestedAddress.parentRunId,\n\t\t\t\t\tparentStepIndex: nestedAddress.parentStepIndex,\n\t\t\t\t\tchild: {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tparentRunId: nestedAddress.parentRunId,\n\t\t\t\t\t\tparentStepIndex: nestedAddress.parentStepIndex,\n\t\t\t\t\t\tdepth: nestedAddress.depth,\n\t\t\t\t\t\tpath: nestedAddress.path,\n\t\t\t\t\t\tasyncDir,\n\t\t\t\t\t\tpid: spawnResult.pid,\n\t\t\t\t\t\townerIntercomTarget: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,\n\t\t\t\t\t\tleafIntercomTarget: childIntercomTargets?.[0],\n\t\t\t\t\t\tintercomTarget: childIntercomTargets?.[0],\n\t\t\t\t\t\townerState: \"live\",\n\t\t\t\t\t\tmode: resultMode,\n\t\t\t\t\t\tstate: \"running\",\n\t\t\t\t\t\tagent: firstAgents[0],\n\t\t\t\t\t\tagents: flatAgents,\n\t\t\t\t\t\tchainStepCount: eventChain.length,\n\t\t\t\t\t\tparallelGroups,\n\t\t\t\t\t\t...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),\n\t\t\t\t\t\t...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),\n\t\t\t\t\t\tstartedAt: now,\n\t\t\t\t\t\tlastUpdate: now,\n\t\t\t\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to emit nested async start event:\", error);\n\t\t\t}\n\t\t}\n\t\tctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {\n\t\t\tlifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,\n\t\t\tid,\n\t\t\tpid: spawnResult.pid,\n\t\t\tsessionId: ctx.currentSessionId,\n\t\t\tmode: resultMode,\n\t\t\tagent: firstAgents[0],\n\t\t\tagents: flatAgents,\n\t\t\ttask: firstTask?.slice(0, 50),\n\t\t\tgoal: workflowGoal?.slice(0, 120),\n\t\t\tchain: eventChain.map((s) =>\n\t\t\t\tisParallelStep(s)\n\t\t\t\t\t? `[${s.parallel.map((t) => t.agent).join(\"+\")}]`\n\t\t\t\t\t: isDynamicParallelStep(s)\n\t\t\t\t\t\t? `expand:${s.parallel.agent}`\n\t\t\t\t\t\t: (s as SequentialStep).agent,\n\t\t\t),\n\t\t\tchainStepCount: eventChain.length,\n\t\t\tparallelGroups,\n\t\t\tworkflowGraph,\n\t\t\tcwd: runnerCwd,\n\t\t\tasyncDir,\n\t\t\t...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),\n\t\t\t...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),\n\t\t\t...(initialUsageBudget ? { usageBudget: initialUsageBudget } : {}),\n\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t...(params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}),\n\t\t\t...(params.workflowKey ? { workflowKey: params.workflowKey } : {}),\n\t\t\tnestedRoute,\n\t\t});\n\t}\n\n\tconst chainDesc = chain\n\t\t.map((s) =>\n\t\t\tisParallelStep(s)\n\t\t\t\t? `[${s.parallel.map((t) => t.agent).join(\"+\")}]`\n\t\t\t\t: isDynamicParallelStep(s)\n\t\t\t\t\t? `expand:${s.parallel.agent}`\n\t\t\t\t\t: (s as SequentialStep).agent,\n\t\t)\n\t\t.join(\" -> \");\n\n\treturn {\n\t\tcontent: [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`, ctx.interactive === true),\n\t\t\t},\n\t\t],\n\t\tdetails: {\n\t\t\tmode: resultMode,\n\t\t\trunId: id,\n\t\t\tresults: [],\n\t\t\tasyncId: id,\n\t\t\tasyncDir,\n\t\t\tworkflowGraph,\n\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t...(params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}),\n\t\t\t...(params.workflowKey ? { workflowKey: params.workflowKey } : {}),\n\t\t\t...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),\n\t\t\t...(params.turnBudget ? { turnBudget: params.turnBudget } : {}),\n\t\t\t...(params.toolBudget ? { toolBudget: params.toolBudget } : {}),\n\t\t\t...(initialUsageBudget ? { usageBudget: initialUsageBudget } : {}),\n\t\t},\n\t};\n}\n\n/**\n * Execute a single agent asynchronously\n */\nexport function executeAsyncSingle(id: string, params: AsyncSingleParams): AsyncExecutionResult {\n\tconst {\n\t\tagent,\n\t\tagentConfig,\n\t\tctx,\n\t\tcwd,\n\t\tmaxOutput,\n\t\tartifactsDir,\n\t\tartifactConfig,\n\t\tshareEnabled,\n\t\tsessionRoot,\n\t\tsessionFile,\n\t\tmaxSubagentDepth,\n\t\tworktreeSetupHook,\n\t\tworktreeSetupHookTimeoutMs,\n\t\tworktreeBaseDir,\n\t\tcontrolConfig,\n\t\tcontrolIntercomTarget,\n\t\tchildIntercomTarget,\n\t\tnestedRoute,\n\t} = params;\n\tconst task = params.task ?? \"\";\n\tconst acceptanceErrors = validateAcceptanceInput(params.acceptance);\n\tif (acceptanceErrors.length > 0) return formatAsyncStartError(\"single\", acceptanceErrors.join(\" \"));\n\tconst externalRunner = agentConfig.runner?.type === \"external-cli\";\n\tconst permissionRules = resolvePermissionRules(ctx.permissions, agentConfig.permissions);\n\tif (externalRunner) {\n\t\tconst unsupported: string[] = [];\n\t\tif (params.modelOverride !== undefined) unsupported.push(\"model override\");\n\t\tif (params.thinkingOverride !== undefined) unsupported.push(\"thinking override\");\n\t\tif (params.structuredOutputSchema !== undefined) unsupported.push(\"structured output\");\n\t\tif (params.acceptance !== undefined || params.agentContract !== undefined)\n\t\t\tunsupported.push(\"acceptance/agent contract\");\n\t\tif (\n\t\t\tparams.toolBudget !== undefined ||\n\t\t\tagentConfig.toolBudget !== undefined ||\n\t\t\tparams.configToolBudget !== undefined\n\t\t)\n\t\t\tunsupported.push(\"tool budget\");\n\t\tif (params.context === \"fork\") unsupported.push(\"fork context\");\n\t\tif ((params.skills?.length ?? 0) > 0) unsupported.push(\"skills\");\n\t\tif (permissionRules) unsupported.push(\"native Pi child permissions\");\n\t\tif (unsupported.length > 0)\n\t\t\treturn formatAsyncStartError(\n\t\t\t\t\"single\",\n\t\t\t\t`Agent '${agentConfig.name}' uses runner.type='external-cli' and does not support: ${unsupported.join(\", \")}.`,\n\t\t\t);\n\t}\n\tconst capabilityCeiling = intersectSubagentCapabilityCeilings(\n\t\tparams.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId),\n\t\tdecodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),\n\t);\n\ttry {\n\t\tassertAgentAllowedByCapabilityCeiling(agentConfig.name, capabilityCeiling);\n\t} catch (error) {\n\t\treturn formatAsyncStartError(\"single\", error instanceof Error ? error.message : String(error));\n\t}\n\tconst runnerCwd = resolveChildCwd(ctx.cwd, cwd);\n\tconst skillNames = params.skills ?? agentConfig.skills ?? [];\n\tconst availableModels = params.availableModels;\n\tconst { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(\n\t\tskillNames,\n\t\trunnerCwd,\n\t\tctx.cwd,\n\t\tagentConfig.skillPath,\n\t\tagentConfig.filePath ? path.dirname(agentConfig.filePath) : runnerCwd,\n\t);\n\tif (missingSkills.includes(\"pi-subagents\")) return formatAsyncStartError(\"single\", UNAVAILABLE_SUBAGENT_SKILL_ERROR);\n\tlet systemPrompt = agentConfig.systemPrompt?.trim() ?? \"\";\n\tif (resolvedSkills.length > 0) {\n\t\tconst injection = buildSkillInjection(resolvedSkills);\n\t\tsystemPrompt = systemPrompt ? `${systemPrompt}\\n\\n${injection}` : injection;\n\t}\n\tconst memoryInjection = buildAgentMemoryInjection(agentConfig, runnerCwd);\n\tif (memoryInjection) {\n\t\tsystemPrompt = systemPrompt ? `${systemPrompt}\\n\\n${memoryInjection}` : memoryInjection;\n\t}\n\tsystemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: runnerCwd, agentName: agentConfig.name });\n\n\tconst inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();\n\tconst nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;\n\tconst asyncDir = inheritedNestedRoute\n\t\t? path.join(TEMP_ROOT_DIR, \"nested-subagent-runs\", inheritedNestedRoute.rootRunId, id)\n\t\t: path.join(DIRS.async, id);\n\ttry {\n\t\tfs.mkdirSync(asyncDir, { recursive: true });\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `Failed to create async run directory '${asyncDir}': ${message}` }],\n\t\t\tisError: true,\n\t\t\tdetails: { mode: \"single\" as const, results: [] },\n\t\t};\n\t}\n\n\tconst effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);\n\tconst outputPath = resolveSingleOutputPath(\n\t\teffectiveOutput,\n\t\tctx.cwd,\n\t\trunnerCwd,\n\t\tparams.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, \"outputs\", id) : undefined),\n\t);\n\tsystemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, agentConfig);\n\tconst outputMode = params.outputMode ?? \"inline\";\n\tconst validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);\n\tif (validationError) return formatAsyncStartError(\"single\", validationError);\n\tconst taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath, agentConfig);\n\tconst primaryModel = externalRunner\n\t\t? undefined\n\t\t: resolveSubagentModelOverride(\n\t\t\t\tparams.modelOverride ?? agentConfig.model,\n\t\t\t\tctx.currentModel,\n\t\t\t\tavailableModels,\n\t\t\t\tctx.currentModelProvider,\n\t\t\t);\n\tconst effectiveThinking = externalRunner ? undefined : (params.thinkingOverride ?? agentConfig.thinking);\n\tconst model = externalRunner\n\t\t? undefined\n\t\t: applyThinkingSuffix(primaryModel, effectiveThinking, params.thinkingOverride !== undefined);\n\tconst toolBudgetInput = params.toolBudget ?? agentConfig.toolBudget ?? params.configToolBudget;\n\tconst resolvedToolBudget = validateToolBudgetConfig(\n\t\ttoolBudgetInput,\n\t\tparams.toolBudget ? \"toolBudget\" : agentConfig.toolBudget ? \"agent.toolBudget\" : \"config.toolBudget\",\n\t);\n\tif (resolvedToolBudget.error) return formatAsyncStartError(\"single\", resolvedToolBudget.error);\n\tconst deadlineAt =\n\t\tparams.absoluteDeadlineAt ?? (params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined);\n\tconst timeoutMs =\n\t\tparams.absoluteDeadlineAt !== undefined && deadlineAt !== undefined ? deadlineAt - Date.now() : params.timeoutMs;\n\tif (timeoutMs !== undefined && timeoutMs <= 0)\n\t\treturn formatAsyncStartError(\n\t\t\t\"single\",\n\t\t\t\"The source run's absolute deadline expired before recovery could launch.\",\n\t\t);\n\tconst initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;\n\tconst initialUsageBudget = usageBudgetState(params.usageBudget, undefined);\n\tconst resolvedSessionDir = params.sessionDir ?? (sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined);\n\tconst structuredOutput = params.structuredOutputSchema\n\t\t? createStructuredOutputRuntime(params.structuredOutputSchema, path.join(asyncDir, \"structured-output\"))\n\t\t: undefined;\n\tconst modelCandidates = buildModelCandidates(\n\t\tprimaryModel,\n\t\tagentConfig.fallbackModels,\n\t\tavailableModels,\n\t\tctx.currentModelProvider,\n\t\t{ scope: ctx.modelScope },\n\t).flatMap((candidate) => {\n\t\tconst resolved = applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined);\n\t\treturn resolved ? [resolved] : [];\n\t});\n\tconst effectiveSystemPrompt = appendTurnBudgetSystemPrompt(systemPrompt, params.turnBudget);\n\tconst toolPlan = resolvePiLaunchToolPlan({\n\t\ttools: agentConfig.tools,\n\t\textensions: agentConfig.extensions,\n\t\tsubagentOnlyExtensions: agentConfig.subagentOnlyExtensions,\n\t\tmcpDirectTools: agentConfig.mcpDirectTools,\n\t\tcwd: runnerCwd,\n\t\trequireReadTool: Boolean(resolvedSkills.length),\n\t\tstructuredOutput: Boolean(params.structuredOutputSchema),\n\t\tcapabilityCeiling,\n\t\tinheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),\n\t\tagentName: agentConfig.name,\n\t});\n\tconst launchResolvedExtensions = externalRunner ? undefined : projectLaunchResolvedChildExtensions(toolPlan);\n\tconst launchContractDigest = launchBindingDigest({\n\t\tdefinitionDigest: agentDefinitionDigest(agentConfig),\n\t\ttask,\n\t\t...(model ? { model } : {}),\n\t\tmodelCandidates,\n\t\t...(resolveEffectiveThinking(model, effectiveThinking)\n\t\t\t? { thinking: resolveEffectiveThinking(model, effectiveThinking) }\n\t\t\t: {}),\n\t\tsystemPrompt: effectiveSystemPrompt,\n\t\tsystemPromptMode: agentConfig.systemPromptMode,\n\t\tinheritProjectContext: agentConfig.inheritProjectContext,\n\t\tinheritSkills: agentConfig.inheritSkills,\n\t\tskills: resolvedSkills.map((skill) => skill.name),\n\t\ttools: toolPlan.effectiveToolAllowlist,\n\t\textensions: toolPlan.extensionArgs,\n\t\tmcpDirectTools: toolPlan.effectiveMcpTools,\n\t\t...(outputPath ? { outputPath } : {}),\n\t\toutputMode,\n\t\t...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),\n\t});\n\tconst resolvedAcceptance = resolveEffectiveAcceptance({\n\t\texplicit: params.acceptance,\n\t\tagentName: agent,\n\t\tacceptanceRole: agentConfig.acceptanceRole,\n\t\ttask,\n\t\tmode: \"single\",\n\t\tasync: true,\n\t\tagentContract: params.agentContract,\n\t});\n\tconst recoveryDescriptor: SteeringRecoveryDescriptor = {\n\t\tversion: 1,\n\t\tlaunchContractDigest,\n\t\tsourceRunId: id,\n\t\t...(params.agentContract ? { agentContract: params.agentContract } : {}),\n\t\tagent,\n\t\tlaunchResolvedExtensions,\n\t\t...(sessionFile ? { sessionFile } : {}),\n\t\tcwd: runnerCwd,\n\t\t...(model ? { model } : {}),\n\t\t...(agentConfig.fallbackModels ? { fallbackModels: [...agentConfig.fallbackModels] } : {}),\n\t\t...(effectiveThinking ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),\n\t\t...(agentConfig.tools ? { tools: [...agentConfig.tools] } : {}),\n\t\t...(agentConfig.extensions ? { extensions: [...agentConfig.extensions] } : {}),\n\t\t...(agentConfig.subagentOnlyExtensions\n\t\t\t? { subagentOnlyExtensions: [...agentConfig.subagentOnlyExtensions] }\n\t\t\t: {}),\n\t\t...(agentConfig.mcpDirectTools ? { mcpDirectTools: [...agentConfig.mcpDirectTools] } : {}),\n\t\t...(agentConfig.systemPrompt ? { systemPrompt: agentConfig.systemPrompt } : {}),\n\t\tsystemPromptMode: agentConfig.systemPromptMode,\n\t\tinheritProjectContext: agentConfig.inheritProjectContext,\n\t\tinheritSkills: agentConfig.inheritSkills,\n\t\t...(resolvedSkills.length ? { skills: resolvedSkills.map((skill) => skill.name) } : {}),\n\t\t...(agentConfig.skillPath ? { skillPath: [...agentConfig.skillPath] } : {}),\n\t\t...(agentConfig.filePath ? { agentFilePath: agentConfig.filePath } : {}),\n\t\t...(agentConfig.completionGuard !== undefined ? { completionGuard: agentConfig.completionGuard } : {}),\n\t\t...(agentConfig.memory ? { memory: { ...agentConfig.memory } } : {}),\n\t\t...(outputPath ? { outputPath } : {}),\n\t\toutputMode,\n\t\t...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),\n\t\t...(params.acceptance !== undefined ? { acceptance: params.acceptance } : {}),\n\t\t...(controlConfig ? { controlConfig } : {}),\n\t\t...(deadlineAt !== undefined ? { absoluteDeadlineAt: deadlineAt } : {}),\n\t\t...(initialTurnBudget ? { initialTurnBudget } : {}),\n\t\t...(resolvedToolBudget.budget ? { initialToolBudget: resolvedToolBudget.budget } : {}),\n\t\tmaxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),\n\t\t...(maxOutput ? { maxOutput } : {}),\n\t\tshare: shareEnabled,\n\t\t...(resolvedSessionDir ? { sessionDir: resolvedSessionDir } : {}),\n\t\t...(artifactsDir ? { artifactsDir } : {}),\n\t\tartifactConfig,\n\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t};\n\tif (!externalRunner) {\n\t\ttry {\n\t\t\twritePrivateAtomicJson(path.join(asyncDir, \"recovery-descriptor.json\"), recoveryDescriptor);\n\t\t} catch (error) {\n\t\t\treturn formatAsyncStartError(\n\t\t\t\t\"single\",\n\t\t\t\t`Failed to persist async recovery descriptor for '${id}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\tlet spawnResult: { pid?: number; error?: string } = {};\n\ttry {\n\t\tspawnResult = spawnRunner(\n\t\t\t{\n\t\t\t\tid,\n\t\t\t\tsteps: [\n\t\t\t\t\t{\n\t\t\t\t\t\tparentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,\n\t\t\t\t\t\tpermissionRules,\n\t\t\t\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t\t\t\tagent,\n\t\t\t\t\t\ttask: taskWithOutputInstruction,\n\t\t\t\t\t\t...(agentConfig.runner ? { runner: agentConfig.runner } : {}),\n\t\t\t\t\t\t...(params.context ? { context: params.context } : {}),\n\t\t\t\t\t\tcwd: runnerCwd,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tthinking: resolveEffectiveThinking(model, effectiveThinking),\n\t\t\t\t\t\tmodelCandidates,\n\t\t\t\t\t\ttools: agentConfig.tools,\n\t\t\t\t\t\textensions: agentConfig.extensions,\n\t\t\t\t\t\tsubagentOnlyExtensions: agentConfig.subagentOnlyExtensions,\n\t\t\t\t\t\tmcpDirectTools: agentConfig.mcpDirectTools,\n\t\t\t\t\t\tcompletionGuard: agentConfig.completionGuard,\n\t\t\t\t\t\tsystemPrompt,\n\t\t\t\t\t\tsystemPromptMode: agentConfig.systemPromptMode,\n\t\t\t\t\t\tinheritProjectContext: agentConfig.inheritProjectContext,\n\t\t\t\t\t\tinheritSkills: agentConfig.inheritSkills,\n\t\t\t\t\t\tskills: resolvedSkills.map((r) => r.name),\n\t\t\t\t\t\toutputPath,\n\t\t\t\t\t\toutputMode,\n\t\t\t\t\t\t...(!externalRunner && sessionFile ? { sessionFile } : {}),\n\t\t\t\t\t\tmaxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),\n\t\t\t\t\t\twaitToolEnabled: params.waitToolEnabled,\n\t\t\t\t\t\t...(params.agentContract ? { agentContract: params.agentContract } : {}),\n\t\t\t\t\t\tdefinitionDigest: agentDefinitionDigest(agentConfig),\n\t\t\t\t\t\tlaunchBindingTask: task,\n\t\t\t\t\t\tlaunchContractDigest,\n\t\t\t\t\t\tlaunchResolvedExtensions,\n\t\t\t\t\t\teffectiveAcceptance: resolvedAcceptance,\n\t\t\t\t\t\t...(structuredOutput ? { structuredOutput } : {}),\n\t\t\t\t\t\t...(params.structuredOutputSchema ? { structuredOutputSchema: params.structuredOutputSchema } : {}),\n\t\t\t\t\t\t...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tresultPath: inheritedNestedRoute\n\t\t\t\t\t? nestedResultsPath(inheritedNestedRoute.rootRunId, id)\n\t\t\t\t\t: path.join(DIRS.results, `${id}.json`),\n\t\t\t\tcwd: runnerCwd,\n\t\t\t\tplaceholder: \"{previous}\",\n\t\t\t\tmaxOutput,\n\t\t\t\tartifactsDir: artifactConfig.enabled ? artifactsDir : undefined,\n\t\t\t\tartifactConfig,\n\t\t\t\tshare: shareEnabled,\n\t\t\t\tsessionDir: resolvedSessionDir,\n\t\t\t\tasyncDir,\n\t\t\t\tsessionId: ctx.currentSessionId,\n\t\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t\tpiPackageRoot,\n\t\t\t\tpiArgv1: process.argv[1],\n\t\t\t\tworktreeSetupHook,\n\t\t\t\tworktreeSetupHookTimeoutMs,\n\t\t\t\tworktreeBaseDir,\n\t\t\t\tcontrolConfig,\n\t\t\t\ttimeoutMs,\n\t\t\t\tdeadlineAt,\n\t\t\t\tturnBudget: params.turnBudget,\n\t\t\t\ttoolBudget: params.toolBudget,\n\t\t\t\tusageBudget: params.usageBudget,\n\t\t\t\tcontrolIntercomTarget,\n\t\t\t\tchildIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,\n\t\t\t\tresultMode: \"single\",\n\t\t\t\tlaunchContractDigest,\n\t\t\t\tlaunchResolvedExtensions,\n\t\t\t\t...(params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}),\n\t\t\t\t...(params.workflowKey ? { workflowKey: params.workflowKey } : {}),\n\t\t\t\t...(params.revivalLease ? { revivalLease: params.revivalLease } : {}),\n\t\t\t\tnestedRoute: nestedRoute ?? inheritedNestedRoute,\n\t\t\t\tnestedSelf:\n\t\t\t\t\tinheritedNestedRoute && nestedAddress\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tparentRunId: nestedAddress.parentRunId,\n\t\t\t\t\t\t\t\tparentStepIndex: nestedAddress.parentStepIndex,\n\t\t\t\t\t\t\t\tdepth: nestedAddress.depth,\n\t\t\t\t\t\t\t\tpath: nestedAddress.path,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t},\n\t\t\tid,\n\t\t\trunnerCwd,\n\t\t\t(proof) => ctx.pi.events.emit(SUBAGENT_PROCESS_TERMINAL_EVENT, proof),\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn formatAsyncStartError(\"single\", `Failed to start async run '${id}': ${message}`);\n\t}\n\n\tif (spawnResult.error) {\n\t\treturn formatAsyncStartError(\"single\", `Failed to start async run '${id}': ${spawnResult.error}`);\n\t}\n\n\tif (spawnResult.pid) {\n\t\tif (inheritedNestedRoute && nestedAddress) {\n\t\t\tconst now = Date.now();\n\t\t\ttry {\n\t\t\t\twriteNestedEvent(inheritedNestedRoute, {\n\t\t\t\t\ttype: \"subagent.nested.started\",\n\t\t\t\t\tts: now,\n\t\t\t\t\tparentRunId: nestedAddress.parentRunId,\n\t\t\t\t\tparentStepIndex: nestedAddress.parentStepIndex,\n\t\t\t\t\tchild: {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tparentRunId: nestedAddress.parentRunId,\n\t\t\t\t\t\tparentStepIndex: nestedAddress.parentStepIndex,\n\t\t\t\t\t\tdepth: nestedAddress.depth,\n\t\t\t\t\t\tpath: nestedAddress.path,\n\t\t\t\t\t\tasyncDir,\n\t\t\t\t\t\tpid: spawnResult.pid,\n\t\t\t\t\t\townerIntercomTarget: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,\n\t\t\t\t\t\tleafIntercomTarget: childIntercomTarget?.(agent, 0),\n\t\t\t\t\t\tintercomTarget: childIntercomTarget?.(agent, 0),\n\t\t\t\t\t\townerState: \"live\",\n\t\t\t\t\t\tmode: \"single\",\n\t\t\t\t\t\tstate: \"running\",\n\t\t\t\t\t\tagent,\n\t\t\t\t\t\tagents: [agent],\n\t\t\t\t\t\tchainStepCount: 1,\n\t\t\t\t\t\t...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),\n\t\t\t\t\t\t...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),\n\t\t\t\t\t\tstartedAt: now,\n\t\t\t\t\t\tlastUpdate: now,\n\t\t\t\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to emit nested async start event:\", error);\n\t\t\t}\n\t\t}\n\t\tctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {\n\t\t\tlifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,\n\t\t\tid,\n\t\t\tpid: spawnResult.pid,\n\t\t\tsessionId: ctx.currentSessionId,\n\t\t\tmode: \"single\",\n\t\t\tagent,\n\t\t\ttask: task?.slice(0, 50),\n\t\t\tgoal: (params.goal ?? task).slice(0, 120),\n\t\t\tcwd: runnerCwd,\n\t\t\tasyncDir,\n\t\t\tlaunchContractDigest,\n\t\t\tlaunchResolvedExtensions,\n\t\t\t...(params.parentWorkflowRunId ? { parentWorkflowRunId: params.parentWorkflowRunId } : {}),\n\t\t\t...(params.workflowKey ? { workflowKey: params.workflowKey } : {}),\n\t\t\t...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),\n\t\t\t...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),\n\t\t\t...(initialUsageBudget ? { usageBudget: initialUsageBudget } : {}),\n\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\tnestedRoute,\n\t\t});\n\t}\n\n\treturn {\n\t\tcontent: [{ type: \"text\", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`, ctx.interactive === true) }],\n\t\tdetails: {\n\t\t\tmode: \"single\",\n\t\t\trunId: id,\n\t\t\tresults: [],\n\t\t\tasyncId: id,\n\t\t\tasyncDir,\n\t\t\tlaunchContractDigest,\n\t\t\tlaunchResolvedExtensions,\n\t\t\t...(capabilityCeiling ? { capabilityCeiling } : {}),\n\t\t\t...(params.context ? { context: params.context } : {}),\n\t\t\t...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),\n\t\t\t...(params.turnBudget ? { turnBudget: params.turnBudget } : {}),\n\t\t\t...(params.toolBudget ? { toolBudget: resolvedToolBudget.budget ?? params.toolBudget } : {}),\n\t\t\t...(initialUsageBudget ? { usageBudget: initialUsageBudget } : {}),\n\t\t} as Details,\n\t};\n}\n"]}