pi-subagents 0.63.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -1
- package/README.md +2 -2
- package/agents/reviewer.md +1 -1
- package/agents/scout.md +1 -1
- package/docs/agents.md +18 -16
- package/docs/configuration.md +7 -15
- package/docs/extension-api.md +15 -6
- package/docs/missions.md +2 -0
- package/docs/observability.md +10 -12
- package/docs/tool-reference.md +7 -6
- package/docs/watchdog.md +92 -114
- package/docs/workflows.md +5 -3
- package/package.json +3 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +6 -5
- package/src/agents/agent-management.ts +47 -15
- package/src/api/capability-ceiling.ts +0 -1
- package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/src/api/preflight.ts +2 -3
- package/src/extension/doctor.ts +2 -10
- package/src/extension/fanout-child.ts +9 -11
- package/src/extension/index.ts +27 -5
- package/src/extension/public-execution.ts +14 -0
- package/src/extension/rpc.ts +3 -2
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +9 -8
- package/src/intercom/native-supervisor-channel.ts +138 -60
- package/src/intercom/supervisor-ui.ts +243 -0
- package/src/runs/background/async-execution.ts +46 -24
- package/src/runs/background/async-job-tracker.ts +11 -0
- package/src/runs/background/async-resume.ts +11 -2
- package/src/runs/background/control-channel.ts +2 -204
- package/src/runs/background/notify.ts +41 -2
- package/src/runs/background/process-terminal.ts +1 -1
- package/src/runs/background/run-child-session.ts +613 -0
- package/src/runs/background/run-status.ts +0 -1
- package/src/runs/background/runner-aliases.ts +125 -0
- package/src/runs/background/runner-child-sessions.ts +31 -0
- package/src/runs/background/scheduled-runs.ts +18 -4
- package/src/runs/background/subagent-runner.ts +203 -898
- package/src/runs/foreground/async-steering-action.ts +1 -17
- package/src/runs/foreground/execution.ts +196 -378
- package/src/runs/foreground/foreground-control.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +121 -58
- package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
- package/src/runs/shared/abort-recovery.ts +3 -3
- package/src/runs/shared/acceptance.ts +10 -0
- package/src/runs/shared/async-status-projection.ts +11 -43
- package/src/runs/shared/capability-ceiling.ts +1 -2
- package/src/runs/shared/child-hooks.ts +25 -0
- package/src/runs/shared/child-identity.ts +13 -2
- package/src/runs/shared/child-launch.ts +314 -0
- package/src/runs/shared/child-lifecycle.ts +25 -0
- package/src/runs/shared/child-runtime-config.ts +126 -0
- package/src/runs/shared/child-session.ts +342 -0
- package/src/runs/shared/child-tool-plan.ts +530 -0
- package/src/runs/shared/claude-code-adapter.ts +5 -1
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/external-cli-preflight.ts +16 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/src/runs/shared/model-exclusions.ts +82 -14
- package/src/runs/shared/model-fallback.ts +47 -4
- package/src/runs/shared/nested-events.ts +29 -45
- package/src/runs/shared/nested-path.ts +0 -14
- package/src/runs/shared/orca-progress-tabs.ts +12 -7
- package/src/runs/shared/parallel-utils.ts +0 -2
- package/src/runs/shared/permissions.ts +0 -13
- package/src/runs/shared/process-signal.ts +4 -1
- package/src/runs/shared/run-fanout-budget.ts +0 -13
- package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
- package/src/runs/shared/structured-output.ts +17 -4
- package/src/runs/shared/subagent-control.ts +6 -2
- package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
- package/src/runs/shared/tool-availability.ts +18 -62
- package/src/runs/shared/tool-budget.ts +0 -14
- package/src/runs/shared/worktree-cleanup-plan.ts +25 -6
- package/src/runs/shared/worktree.ts +117 -30
- package/src/shared/child-session-name.ts +1 -1
- package/src/shared/jsonl-writer.ts +11 -0
- package/src/shared/thinking-ceiling.ts +0 -6
- package/src/shared/types.ts +64 -30
- package/src/shared/utils.ts +3 -4
- package/src/slash/slash-commands.ts +0 -6
- package/src/tui/fleet.ts +0 -1
- package/src/tui/render.ts +238 -31
- package/src/watchdog/child-status.ts +54 -34
- package/src/watchdog/diff-tool.ts +77 -0
- package/src/watchdog/emission-guard.ts +5 -3
- package/src/watchdog/guidance.ts +20 -0
- package/src/watchdog/register-child.ts +28 -25
- package/src/watchdog/register-main.ts +10 -9
- package/src/watchdog/render.ts +4 -5
- package/src/watchdog/review.ts +15 -4
- package/src/watchdog/rules.ts +70 -0
- package/src/watchdog/runtime.ts +75 -92
- package/src/watchdog/scope.ts +0 -11
- package/src/watchdog/settings.ts +48 -104
- package/src/watchdog/types.ts +18 -32
- package/src/watchdog/warning-format.ts +0 -1
- package/src/workflows/chat-progress.ts +3 -2
- package/src/workflows/scripted-workflow.ts +48 -1
- package/src/workflows/workflow-checklist.ts +10 -12
- package/src/workflows/workflow-preflight.ts +28 -1
- package/src/runs/shared/child-protocol.ts +0 -415
- package/src/runs/shared/pi-args.ts +0 -1062
- package/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/src/shared/post-exit-stdio-guard.ts +0 -85
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
3
2
|
import * as fs from "node:fs";
|
|
4
3
|
import * as path from "node:path";
|
|
5
4
|
import { pathToFileURL } from "node:url";
|
|
@@ -11,9 +10,9 @@ import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilie
|
|
|
11
10
|
import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
|
|
12
11
|
import { updateActiveRunIndex } from "./active-run-index.ts";
|
|
13
12
|
import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
14
|
-
import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest,
|
|
13
|
+
import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest, watchAsyncControlInbox, type SteerRequest, type StopRequest } from "./control-channel.ts";
|
|
15
14
|
import { appendJsonl as appendRawJsonl, formatOutputArtifactContent, getArtifactPaths, writeArtifact, writeMetadata } from "../../shared/artifacts.ts";
|
|
16
|
-
import { PI_CODING_AGENT_PACKAGE,
|
|
15
|
+
import { PI_CODING_AGENT_PACKAGE, resolveInstalledPiPackageRoot } from "../shared/pi-spawn.ts";
|
|
17
16
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
18
17
|
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, injectSingleOutputInstruction, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
19
18
|
import {
|
|
@@ -32,7 +31,6 @@ import {
|
|
|
32
31
|
type RuntimeAcknowledgedChildExtensionsV1,
|
|
33
32
|
type ModelAttempt,
|
|
34
33
|
type PiWriterProcessInstanceExitV1,
|
|
35
|
-
type ProcessTreeTerminalV1,
|
|
36
34
|
type NestedRouteInfo,
|
|
37
35
|
type NestedRunSummary,
|
|
38
36
|
type ResolvedControlConfig,
|
|
@@ -52,7 +50,6 @@ import {
|
|
|
52
50
|
type MaxOutputConfig,
|
|
53
51
|
SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
54
52
|
truncateOutput,
|
|
55
|
-
getSubagentDepthEnv,
|
|
56
53
|
} from "../../shared/types.ts";
|
|
57
54
|
import {
|
|
58
55
|
DEFAULT_CONTROL_CONFIG,
|
|
@@ -75,31 +72,25 @@ import {
|
|
|
75
72
|
DEFAULT_GLOBAL_CONCURRENCY_LIMIT,
|
|
76
73
|
Semaphore,
|
|
77
74
|
} from "../shared/parallel-utils.ts";
|
|
78
|
-
import { applyThinkingSuffix,
|
|
75
|
+
import { applyThinkingSuffix, deriveForkPromptCacheKey, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/child-tool-plan.ts";
|
|
76
|
+
import { buildInProcessChildLaunch, type InheritedChildRuntime } from "../shared/child-launch.ts";
|
|
77
|
+
import type { ChildSessionFactory } from "../shared/child-session.ts";
|
|
78
|
+
import { runChildSession, type ChildEvent, type RunChildSessionResult, type StepSteerHandler } from "./run-child-session.ts";
|
|
79
|
+
import { loadRunnerChildSessionFactory } from "./runner-child-sessions.ts";
|
|
80
|
+
import { SUBAGENT_CHILD_ENV } from "../shared/child-runtime-config.ts";
|
|
79
81
|
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
80
82
|
import { alignForkedSessionCwd } from "../../shared/fork-session-cwd.ts";
|
|
81
|
-
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
82
83
|
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
83
|
-
import { clearStructuredOutputCaptures, createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
84
|
-
import { formatMidToolExitError,
|
|
85
|
-
import {
|
|
84
|
+
import { clearStructuredOutputCaptures, createStructuredOutputFileCapture, createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
85
|
+
import { formatMidToolExitError, isOrdinaryToolForMidToolExit, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
86
|
+
import { formatChildToolDiagnostic } from "../shared/tool-availability.ts";
|
|
86
87
|
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
87
88
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.ts";
|
|
88
89
|
import { claimRunFanoutBatch, getRunFanoutBudgetSnapshot } from "../shared/run-fanout-budget.ts";
|
|
89
90
|
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.ts";
|
|
90
91
|
import { formatModelAttemptNote, formatSubagentModelVerificationError, isContextOverflow, isRetryableModelFailureAttempt, recordRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
91
|
-
import {
|
|
92
|
-
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
93
|
-
formatSubagentExtensionConflictError,
|
|
94
|
-
formatSubagentStartupRetryExhaustedError,
|
|
95
|
-
formatSubagentStartupRetryNote,
|
|
96
|
-
isRetryableSubagentStartupFailure,
|
|
97
|
-
waitForSubagentStartupRetry,
|
|
98
|
-
} from "../shared/subagent-startup-retry.ts";
|
|
99
92
|
import { markProcessTerminalCandidateLeaseRelease, writeProcessTerminalCandidate, type ProcessTerminalCandidate } from "./process-terminal.ts";
|
|
100
|
-
import { createOwnedProcessTreeController, type OwnedProcessTreeController } from "./owned-process-tree.ts";
|
|
101
93
|
import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, updateSteeringTarget } from "./steering.ts";
|
|
102
|
-
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
103
94
|
import { PROMPT_REDACTED, detectSubagentError, extractTextFromContent, extractToolArgsPreview, formatEmptyTerminalAssistantResponseError, getFinalOutput, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.ts";
|
|
104
95
|
import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
105
96
|
import { planCompletionEvidence, projectSettlementDiagnostic } from "../shared/completion-evidence.ts";
|
|
@@ -128,7 +119,7 @@ import {
|
|
|
128
119
|
type WorktreeSetup,
|
|
129
120
|
} from "../shared/worktree.ts";
|
|
130
121
|
import { findModelInfo, resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
131
|
-
import { assertThinkingWithinCeiling
|
|
122
|
+
import { assertThinkingWithinCeiling } from "../../shared/thinking-ceiling.ts";
|
|
132
123
|
import { launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
133
124
|
import { writeInitialProgressFile } from "../../shared/settings.ts";
|
|
134
125
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
@@ -143,7 +134,6 @@ import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey }
|
|
|
143
134
|
import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.ts";
|
|
144
135
|
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
|
|
145
136
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
146
|
-
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, PI_AGGREGATE_EVENT_PROJECTOR, projectChildLifecycle, type ChildLifecycleAction, type ChildLifecycleState, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
147
137
|
import { acquireSessionLease, type SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
148
138
|
import { buildExternalCliPrompt, runExternalCli } from "../shared/external-cli-runner.ts";
|
|
149
139
|
import { resolveClaudeCodeLaunch } from "../shared/claude-code-adapter.ts";
|
|
@@ -152,19 +142,22 @@ import { resolveCursorAgentLaunch } from "../shared/cursor-agent-adapter.ts";
|
|
|
152
142
|
import { resolveExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
|
|
153
143
|
import { runExternalJob } from "../shared/external-job-runner.ts";
|
|
154
144
|
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
155
|
-
import {
|
|
145
|
+
import type { ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
156
146
|
import {
|
|
157
|
-
CHILD_WATCHDOG_CONFIG_ENV,
|
|
158
147
|
acceptChildWatchdogEvent,
|
|
159
|
-
|
|
160
|
-
decodeChildWatchdogConfig,
|
|
148
|
+
applyChildWatchdogMessage,
|
|
161
149
|
isChildWatchdogStatusEvent,
|
|
162
150
|
resolveChildWatchdogConfig,
|
|
163
|
-
type
|
|
151
|
+
type ChildWatchdogStatusEvent,
|
|
164
152
|
} from "../../watchdog/child-status.ts";
|
|
165
153
|
|
|
166
154
|
const INTERCOM_DETACH_RECEIPT = "Detached for intercom coordination before task completion.";
|
|
167
155
|
|
|
156
|
+
// This process hosts child sessions. An ambient copy of pi-subagents loaded
|
|
157
|
+
// into one of them must register nothing; the variable marks the process as a
|
|
158
|
+
// child host.
|
|
159
|
+
process.env[SUBAGENT_CHILD_ENV] = "1";
|
|
160
|
+
|
|
168
161
|
interface SubagentRunConfig {
|
|
169
162
|
id: string;
|
|
170
163
|
steps: RunnerStep[];
|
|
@@ -182,10 +175,14 @@ interface SubagentRunConfig {
|
|
|
182
175
|
sessionId?: string | null;
|
|
183
176
|
completionOwnerId?: string;
|
|
184
177
|
piPackageRoot?: string;
|
|
185
|
-
|
|
178
|
+
/** Test seam: module the runner imports its `ChildSessionFactory` from. */
|
|
179
|
+
childSessionFactoryModule?: string;
|
|
180
|
+
/** The launching executor's own child runtime when it was itself an in-process child. */
|
|
181
|
+
inheritedChildRuntime?: InheritedChildRuntime;
|
|
186
182
|
worktreeSetupHook?: string;
|
|
187
183
|
worktreeSetupHookTimeoutMs?: number;
|
|
188
184
|
worktreeBaseDir?: string;
|
|
185
|
+
baseRef?: string;
|
|
189
186
|
worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
|
|
190
187
|
worktreeBranchPrefix?: string;
|
|
191
188
|
controlConfig?: ResolvedControlConfig;
|
|
@@ -231,7 +228,6 @@ interface StepResult {
|
|
|
231
228
|
output: string;
|
|
232
229
|
outputState?: SubagentOutputState;
|
|
233
230
|
error?: string;
|
|
234
|
-
protocolError?: ProtocolOutputLimit;
|
|
235
231
|
success?: boolean;
|
|
236
232
|
exitCode: number | null;
|
|
237
233
|
usage?: Usage;
|
|
@@ -248,6 +244,7 @@ interface StepResult {
|
|
|
248
244
|
sessionFile?: string;
|
|
249
245
|
intercomTarget?: string;
|
|
250
246
|
model?: string;
|
|
247
|
+
thinking?: string;
|
|
251
248
|
attemptedModels?: string[];
|
|
252
249
|
modelAttempts?: ModelAttempt[];
|
|
253
250
|
/** True when the dispatch failed because the input exceeded the model's context window. */
|
|
@@ -269,8 +266,6 @@ interface StepResult {
|
|
|
269
266
|
structuredOutputSchemaPath?: string;
|
|
270
267
|
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
271
268
|
watchdog?: import("../../shared/types.ts").ChildWatchdogProgress;
|
|
272
|
-
writerProcesses?: PiWriterProcessInstanceExitV1[];
|
|
273
|
-
writerAttemptCount?: number;
|
|
274
269
|
runner?: ExternalCliRunnerStatus | ExternalJobRunnerStatus;
|
|
275
270
|
externalProcess?: ExternalProcessStatus;
|
|
276
271
|
externalJob?: ExternalJobStatus;
|
|
@@ -371,10 +366,6 @@ function appendDiagnosticJsonl(filePath: string, line: string, droppedEventType?
|
|
|
371
366
|
state.diagnosticsTruncated = true;
|
|
372
367
|
}
|
|
373
368
|
|
|
374
|
-
function shouldPersistChildEvent(event: Record<string, unknown>): boolean {
|
|
375
|
-
return event.type !== "message_update";
|
|
376
|
-
}
|
|
377
|
-
|
|
378
369
|
function isBlockingSupervisorTool(toolName: string | undefined, args: unknown): boolean {
|
|
379
370
|
if (!args || typeof args !== "object" || Array.isArray(args)) return false;
|
|
380
371
|
if (toolName === "contact_supervisor") {
|
|
@@ -457,15 +448,6 @@ function appendRecentStepOutput(step: RunnerStatusStep, lines: string[]): void {
|
|
|
457
448
|
}
|
|
458
449
|
}
|
|
459
450
|
|
|
460
|
-
function assistantStartsToolCall(message: Message): boolean {
|
|
461
|
-
return Array.isArray(message.content)
|
|
462
|
-
&& message.content.some((part) => (part as { type?: string }).type === "toolCall");
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function isTerminalAssistantStop(message: Message): boolean {
|
|
466
|
-
return (message as { stopReason?: string }).stopReason === "stop" && !assistantStartsToolCall(message);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
451
|
type UndefinedOmitted<T extends object> = {
|
|
470
452
|
[K in keyof T]: Exclude<T[K], undefined>;
|
|
471
453
|
};
|
|
@@ -508,73 +490,6 @@ function resetStepLiveDetail(step: RunnerStatusStep): void {
|
|
|
508
490
|
step.recentOutput = [];
|
|
509
491
|
}
|
|
510
492
|
|
|
511
|
-
interface ChildEventContext {
|
|
512
|
-
eventsPath: string;
|
|
513
|
-
runId: string;
|
|
514
|
-
stepIndex: number;
|
|
515
|
-
agent: string;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
interface ChildUsage {
|
|
519
|
-
input?: number;
|
|
520
|
-
inputTokens?: number;
|
|
521
|
-
output?: number;
|
|
522
|
-
outputTokens?: number;
|
|
523
|
-
cacheRead?: number;
|
|
524
|
-
cacheReadTokens?: number;
|
|
525
|
-
cacheWrite?: number;
|
|
526
|
-
cost?: { total?: number };
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
type ChildMessage = Message & {
|
|
530
|
-
model?: string;
|
|
531
|
-
errorMessage?: string;
|
|
532
|
-
usage?: ChildUsage;
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
interface ChildEvent {
|
|
536
|
-
type?: string;
|
|
537
|
-
message?: ChildMessage;
|
|
538
|
-
toolName?: string;
|
|
539
|
-
args?: Record<string, unknown>;
|
|
540
|
-
willRetry?: unknown;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
interface RunPiStreamingResult {
|
|
544
|
-
stderr: string;
|
|
545
|
-
exitCode: number | null;
|
|
546
|
-
messages: Message[];
|
|
547
|
-
usage: Usage;
|
|
548
|
-
toolCount: number;
|
|
549
|
-
durationMs: number;
|
|
550
|
-
model?: string;
|
|
551
|
-
error?: string;
|
|
552
|
-
protocolError?: ProtocolOutputLimit;
|
|
553
|
-
finalOutput: string;
|
|
554
|
-
outputState: SubagentOutputState;
|
|
555
|
-
interrupted?: boolean;
|
|
556
|
-
timedOut?: boolean;
|
|
557
|
-
stopped?: boolean;
|
|
558
|
-
toolBudget?: ToolBudgetState;
|
|
559
|
-
toolBudgetBlocked?: boolean;
|
|
560
|
-
observedMutationAttempt?: boolean;
|
|
561
|
-
structuredOutputToolInvoked?: boolean;
|
|
562
|
-
structuredOutputMessageStartIndex?: number;
|
|
563
|
-
structuredOutput?: unknown;
|
|
564
|
-
watchdog?: ChildWatchdogStateSnapshot;
|
|
565
|
-
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
566
|
-
processInstanceId: string;
|
|
567
|
-
processCloseObservedAt?: number;
|
|
568
|
-
processSignal?: string | null;
|
|
569
|
-
processTree: ProcessTreeTerminalV1;
|
|
570
|
-
currentTool?: string;
|
|
571
|
-
currentToolArgs?: string;
|
|
572
|
-
currentPath?: string;
|
|
573
|
-
afterCompactionSettlement?: boolean;
|
|
574
|
-
abortRecoveryDiagnostic?: string;
|
|
575
|
-
effects?: import("../../shared/types.ts").EffectsProjection;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
493
|
const MAX_CHILD_FAILURE_DIAGNOSTIC_CHARS = 8_192;
|
|
579
494
|
|
|
580
495
|
function formatRequiredOutputError(requiredOutput: {
|
|
@@ -609,565 +524,6 @@ function formatChildFailureDiagnostic(input: {
|
|
|
609
524
|
return `${baseError.slice(0, Math.max(0, errorLimit))}${context ? `\n${context}` : ""}`;
|
|
610
525
|
}
|
|
611
526
|
|
|
612
|
-
function runPiStreaming(
|
|
613
|
-
args: string[],
|
|
614
|
-
cwd: string,
|
|
615
|
-
outputFile: string,
|
|
616
|
-
env?: Record<string, string | undefined>,
|
|
617
|
-
piPackageRoot?: string,
|
|
618
|
-
piArgv1?: string,
|
|
619
|
-
maxSubagentDepth?: number,
|
|
620
|
-
childEventContext?: ChildEventContext,
|
|
621
|
-
registerInterrupt?: (interrupt: (() => void) | undefined) => void,
|
|
622
|
-
onChildEvent?: (event: ChildEvent) => void,
|
|
623
|
-
transcriptWriter?: ChildTranscriptWriter,
|
|
624
|
-
registerTimeout?: (interrupt: (() => void) | undefined) => void,
|
|
625
|
-
timeoutMessage?: string,
|
|
626
|
-
registerStop?: (stop: (() => void) | undefined) => void,
|
|
627
|
-
stopMessage?: string,
|
|
628
|
-
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void,
|
|
629
|
-
toolTimeoutMs?: number,
|
|
630
|
-
runDeadlineAt?: number,
|
|
631
|
-
orcaProgressTab?: OrcaProgressTab,
|
|
632
|
-
expectedModelForVerification?: string,
|
|
633
|
-
modelVerificationRegistry?: Array<{ provider: string; id: string; fullId: string }>,
|
|
634
|
-
mutationTools?: readonly string[],
|
|
635
|
-
): Promise<RunPiStreamingResult> {
|
|
636
|
-
return new Promise((resolve) => {
|
|
637
|
-
const startedAt = Date.now();
|
|
638
|
-
const processInstanceId = randomUUID();
|
|
639
|
-
onWriterProcess?.({ state: "spawning" });
|
|
640
|
-
const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
|
|
641
|
-
const spawnEnv = { ...process.env, ...(env ?? {}), ...getSubagentDepthEnv(maxSubagentDepth) };
|
|
642
|
-
const spawnSpec = getPiSpawnCommand(args, {
|
|
643
|
-
...(piPackageRoot ? { piPackageRoot } : {}),
|
|
644
|
-
...(piArgv1 ? { argv1: piArgv1 } : {}),
|
|
645
|
-
});
|
|
646
|
-
const child = spawn(spawnSpec.command, spawnSpec.args, {
|
|
647
|
-
cwd,
|
|
648
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
649
|
-
env: spawnEnv,
|
|
650
|
-
windowsHide: true,
|
|
651
|
-
detached: process.platform !== "win32",
|
|
652
|
-
});
|
|
653
|
-
let processTreeController: OwnedProcessTreeController | undefined;
|
|
654
|
-
const stderrTail = createBoundedByteTail();
|
|
655
|
-
const rawStdoutTail = createBoundedByteTail();
|
|
656
|
-
const messages: Message[] = [];
|
|
657
|
-
const usage = emptyUsage();
|
|
658
|
-
let model: string | undefined;
|
|
659
|
-
let writerRegistrationError: string | undefined;
|
|
660
|
-
if (typeof child.pid === "number") {
|
|
661
|
-
processTreeController = createOwnedProcessTreeController(child.pid);
|
|
662
|
-
try {
|
|
663
|
-
onWriterProcess?.({ state: "running", pid: child.pid });
|
|
664
|
-
} catch (writerError) {
|
|
665
|
-
writerRegistrationError = `Failed to record revived Pi writer ownership: ${writerError instanceof Error ? writerError.message : String(writerError)}`;
|
|
666
|
-
trySignalChild(child, "SIGKILL");
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
let error: string | undefined = writerRegistrationError;
|
|
670
|
-
let assistantError: string | undefined;
|
|
671
|
-
let interrupted = false;
|
|
672
|
-
let timedOut = false;
|
|
673
|
-
let stopped = false;
|
|
674
|
-
let observedMutationAttempt = false;
|
|
675
|
-
let structuredOutputToolInvoked = false;
|
|
676
|
-
let structuredOutputMessageStartIndex: number | undefined;
|
|
677
|
-
let currentTool: string | undefined;
|
|
678
|
-
let currentToolArgs: string | undefined;
|
|
679
|
-
let currentPath: string | undefined;
|
|
680
|
-
let toolCount = 0;
|
|
681
|
-
type ActiveToolCall = { key: string; tool: string; args?: string; path?: string };
|
|
682
|
-
let activeToolSequence = 0;
|
|
683
|
-
const activeToolCalls = new Map<string, ActiveToolCall>();
|
|
684
|
-
const activeToolKeysByName = new Map<string, string[]>();
|
|
685
|
-
const refreshCurrentTool = (): void => {
|
|
686
|
-
const active = [...activeToolCalls.values()].at(-1);
|
|
687
|
-
currentTool = active?.tool;
|
|
688
|
-
currentToolArgs = active?.args;
|
|
689
|
-
currentPath = active?.path;
|
|
690
|
-
};
|
|
691
|
-
const recordActiveToolCall = (event: { toolCallId?: unknown; toolName: string; args?: Record<string, unknown> }): void => {
|
|
692
|
-
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
693
|
-
const active = omitUndefinedProperties({
|
|
694
|
-
key,
|
|
695
|
-
tool: event.toolName,
|
|
696
|
-
args: extractToolArgsPreview(event.args ?? {}),
|
|
697
|
-
path: resolveCurrentPath(event.toolName, event.args),
|
|
698
|
-
});
|
|
699
|
-
activeToolCalls.set(key, active);
|
|
700
|
-
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
701
|
-
keys.push(key);
|
|
702
|
-
activeToolKeysByName.set(active.tool, keys);
|
|
703
|
-
refreshCurrentTool();
|
|
704
|
-
};
|
|
705
|
-
const removeActiveToolCall = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
706
|
-
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
707
|
-
? `id:${event.toolCallId}`
|
|
708
|
-
: typeof event.toolName === "string"
|
|
709
|
-
? activeToolKeysByName.get(event.toolName)?.[0]
|
|
710
|
-
: activeToolCalls.size === 1
|
|
711
|
-
? [...activeToolCalls.keys()][0]
|
|
712
|
-
: undefined;
|
|
713
|
-
if (!key) return;
|
|
714
|
-
const active = activeToolCalls.get(key);
|
|
715
|
-
if (!active) return;
|
|
716
|
-
activeToolCalls.delete(key);
|
|
717
|
-
const keys = activeToolKeysByName.get(active.tool)?.filter((candidate) => candidate !== key) ?? [];
|
|
718
|
-
if (keys.length > 0) activeToolKeysByName.set(active.tool, keys);
|
|
719
|
-
else activeToolKeysByName.delete(active.tool);
|
|
720
|
-
refreshCurrentTool();
|
|
721
|
-
};
|
|
722
|
-
const childWatchdogConfig = decodeChildWatchdogConfig(env?.[CHILD_WATCHDOG_CONFIG_ENV]);
|
|
723
|
-
let childWatchdogState: ChildWatchdogStateSnapshot | undefined;
|
|
724
|
-
const childLifecycleState: ChildLifecycleState = { compactionRetryActive: false };
|
|
725
|
-
let applyChildLifecycle = (_action: ChildLifecycleAction): void => {};
|
|
726
|
-
const updateChildWatchdogState = (snapshot: ChildWatchdogStateSnapshot): void => {
|
|
727
|
-
childWatchdogState = snapshot;
|
|
728
|
-
};
|
|
729
|
-
|
|
730
|
-
const writeOutputLine = (line: string) => {
|
|
731
|
-
if (!line.trim()) return;
|
|
732
|
-
outputStream.write(`${line}\n`);
|
|
733
|
-
orcaProgressTab?.append(`${line}\n`);
|
|
734
|
-
};
|
|
735
|
-
|
|
736
|
-
const writeOutputText = (text: string) => {
|
|
737
|
-
for (const line of text.split("\n")) {
|
|
738
|
-
writeOutputLine(line);
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
const appendChildEvent = (event: Record<string, unknown>) => {
|
|
743
|
-
if (!childEventContext) return;
|
|
744
|
-
if (!shouldPersistChildEvent(event)) return;
|
|
745
|
-
appendDiagnosticJsonl(childEventContext.eventsPath, JSON.stringify({
|
|
746
|
-
...event,
|
|
747
|
-
subagentSource: "child",
|
|
748
|
-
subagentRunId: childEventContext.runId,
|
|
749
|
-
subagentStepIndex: childEventContext.stepIndex,
|
|
750
|
-
subagentAgent: childEventContext.agent,
|
|
751
|
-
observedAt: Date.now(),
|
|
752
|
-
}), typeof event.type === "string" ? event.type : undefined);
|
|
753
|
-
};
|
|
754
|
-
|
|
755
|
-
const appendChildLine = (type: "subagent.child.stdout" | "subagent.child.stderr", line: string) => {
|
|
756
|
-
appendChildEvent({ type, line });
|
|
757
|
-
if (type === "subagent.child.stdout") transcriptWriter?.writeStdoutLine(line);
|
|
758
|
-
else transcriptWriter?.writeStderrLine(line);
|
|
759
|
-
};
|
|
760
|
-
|
|
761
|
-
const processStdoutLine = (line: string) => {
|
|
762
|
-
if (!line.trim()) return;
|
|
763
|
-
let event: ChildEvent;
|
|
764
|
-
try {
|
|
765
|
-
event = JSON.parse(line) as ChildEvent;
|
|
766
|
-
} catch {
|
|
767
|
-
rawStdoutTail.push(`${line}\n`);
|
|
768
|
-
writeOutputLine(line);
|
|
769
|
-
appendChildLine("subagent.child.stdout", line);
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
appendChildEvent(event as unknown as Record<string, unknown>);
|
|
774
|
-
transcriptWriter?.writeChildEvent(event);
|
|
775
|
-
if (event.type === "compaction_start") compactionStartedReceived = true;
|
|
776
|
-
if (event.type === "compaction_end" && event.willRetry === true) {
|
|
777
|
-
compactionStartedReceived = false;
|
|
778
|
-
afterCompactionSettlement = false;
|
|
779
|
-
}
|
|
780
|
-
if (event.type === "agent_start" || event.type === "auto_retry_start") {
|
|
781
|
-
compactionStartedReceived = false;
|
|
782
|
-
afterCompactionSettlement = false;
|
|
783
|
-
}
|
|
784
|
-
const lifecycleAction = projectChildLifecycle(event, false, childLifecycleState);
|
|
785
|
-
if (event.type === "agent_settled" && lifecycleAction === "start-drain") {
|
|
786
|
-
agentSettledReceived = true;
|
|
787
|
-
afterCompactionSettlement = compactionStartedReceived;
|
|
788
|
-
}
|
|
789
|
-
applyChildLifecycle(lifecycleAction);
|
|
790
|
-
|
|
791
|
-
if (isChildWatchdogStatusEvent(event)) {
|
|
792
|
-
if (!childWatchdogConfig) return;
|
|
793
|
-
const next = acceptChildWatchdogEvent({
|
|
794
|
-
current: childWatchdogState,
|
|
795
|
-
event,
|
|
796
|
-
...(childEventContext ? {
|
|
797
|
-
runId: childEventContext.runId,
|
|
798
|
-
agent: childEventContext.agent,
|
|
799
|
-
childIndex: childEventContext.stepIndex,
|
|
800
|
-
} : {}),
|
|
801
|
-
});
|
|
802
|
-
if (!next) return;
|
|
803
|
-
updateChildWatchdogState(next);
|
|
804
|
-
onChildEvent?.(event);
|
|
805
|
-
if (childWatchdogIsActive(next)) {
|
|
806
|
-
if (finalDrainTimer) {
|
|
807
|
-
clearTimeout(finalDrainTimer);
|
|
808
|
-
finalDrainTimer = undefined;
|
|
809
|
-
}
|
|
810
|
-
if (finalHardKillTimer) {
|
|
811
|
-
clearTimeout(finalHardKillTimer);
|
|
812
|
-
finalHardKillTimer = undefined;
|
|
813
|
-
}
|
|
814
|
-
armWatchdogTail();
|
|
815
|
-
} else {
|
|
816
|
-
clearWatchdogTailTimer();
|
|
817
|
-
if (cleanTerminalAssistantStopReceived || agentSettledReceived) startFinalDrain();
|
|
818
|
-
}
|
|
819
|
-
return;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
onChildEvent?.(event);
|
|
823
|
-
|
|
824
|
-
if (event.type === "tool_execution_end") {
|
|
825
|
-
clearActiveToolTimeout(event);
|
|
826
|
-
removeActiveToolCall(event);
|
|
827
|
-
return;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
if (event.type === "tool_execution_start" && event.toolName) {
|
|
831
|
-
toolCount += 1;
|
|
832
|
-
armToolTimeout({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName });
|
|
833
|
-
recordActiveToolCall({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName, args: event.args });
|
|
834
|
-
if (event.toolName === "structured_output") {
|
|
835
|
-
structuredOutputToolInvoked = true;
|
|
836
|
-
structuredOutputMessageStartIndex = messages.length;
|
|
837
|
-
}
|
|
838
|
-
observedMutationAttempt = observedMutationAttempt || isMutatingTool(event.toolName, event.args, mutationTools);
|
|
839
|
-
const toolArgs = extractToolArgsPreview(event.args ?? {});
|
|
840
|
-
writeOutputLine(toolArgs ? `${event.toolName}: ${toolArgs}` : event.toolName);
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
if ((event.type === "message_end" || event.type === "tool_result_end") && event.message) {
|
|
845
|
-
if (event.type === "tool_result_end") {
|
|
846
|
-
clearActiveToolTimeout(event);
|
|
847
|
-
removeActiveToolCall({
|
|
848
|
-
toolCallId: (event.message as { toolCallId?: unknown }).toolCallId ?? (event as { toolCallId?: unknown }).toolCallId,
|
|
849
|
-
toolName: (event.message as { toolName?: unknown }).toolName ?? event.toolName,
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
messages.push(event.message);
|
|
853
|
-
const text = extractTextFromContent(event.message.content);
|
|
854
|
-
if (text) writeOutputText(text);
|
|
855
|
-
|
|
856
|
-
if (event.type !== "message_end" || event.message.role !== "assistant") return;
|
|
857
|
-
const hasToolCall = assistantStartsToolCall(event.message);
|
|
858
|
-
if (event.message.model) {
|
|
859
|
-
model = event.message.model;
|
|
860
|
-
if (expectedModelForVerification && !hasToolCall) {
|
|
861
|
-
const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, event.message.model, modelVerificationRegistry);
|
|
862
|
-
if (modelVerificationError && !error) error = modelVerificationError;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
if (event.message.errorMessage) assistantError = event.message.errorMessage;
|
|
866
|
-
const eventUsage = event.message.usage;
|
|
867
|
-
if (eventUsage) {
|
|
868
|
-
usage.turns++;
|
|
869
|
-
usage.input += eventUsage.input ?? eventUsage.inputTokens ?? 0;
|
|
870
|
-
usage.output += eventUsage.output ?? eventUsage.outputTokens ?? 0;
|
|
871
|
-
usage.cacheRead += eventUsage.cacheRead ?? 0;
|
|
872
|
-
usage.cacheWrite += eventUsage.cacheWrite ?? 0;
|
|
873
|
-
usage.cost += eventUsage.cost?.total ?? 0;
|
|
874
|
-
}
|
|
875
|
-
if (isTerminalAssistantStop(event.message)) {
|
|
876
|
-
if (!event.message.errorMessage && extractTextFromContent(event.message.content).trim()) assistantError = undefined;
|
|
877
|
-
cleanTerminalAssistantStopReceived ||= !event.message.errorMessage;
|
|
878
|
-
clearAllToolTimeouts();
|
|
879
|
-
activeToolCalls.clear();
|
|
880
|
-
activeToolKeysByName.clear();
|
|
881
|
-
refreshCurrentTool();
|
|
882
|
-
applyChildLifecycle(projectChildLifecycle(event, true, childLifecycleState));
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
|
|
887
|
-
// Guard both cases that can leave the parent waiting on `close` forever:
|
|
888
|
-
// a lingering stdio holder after `exit`, or a child that never exits.
|
|
889
|
-
const FINAL_STOP_GRACE_MS = 1000;
|
|
890
|
-
const HARD_KILL_MS = 3000;
|
|
891
|
-
let childExited = false;
|
|
892
|
-
let forcedTerminationSignal = false;
|
|
893
|
-
let cleanTerminalAssistantStopReceived = false;
|
|
894
|
-
let agentSettledReceived = false;
|
|
895
|
-
let compactionStartedReceived = false;
|
|
896
|
-
let afterCompactionSettlement = false;
|
|
897
|
-
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
898
|
-
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
899
|
-
let watchdogTailTimer: NodeJS.Timeout | undefined;
|
|
900
|
-
let protocolHardKillTimer: NodeJS.Timeout | undefined;
|
|
901
|
-
let protocolError: ProtocolOutputLimit | undefined;
|
|
902
|
-
let settled = false;
|
|
903
|
-
applyChildLifecycle = (action: ChildLifecycleAction): void => {
|
|
904
|
-
if (action === "cancel-drain") {
|
|
905
|
-
if (finalDrainTimer) {
|
|
906
|
-
clearTimeout(finalDrainTimer);
|
|
907
|
-
finalDrainTimer = undefined;
|
|
908
|
-
}
|
|
909
|
-
if (finalHardKillTimer) {
|
|
910
|
-
clearTimeout(finalHardKillTimer);
|
|
911
|
-
finalHardKillTimer = undefined;
|
|
912
|
-
}
|
|
913
|
-
clearWatchdogTailTimer();
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
if (action === "start-drain") startFinalDrain();
|
|
917
|
-
};
|
|
918
|
-
const failProtocol = (limit: ProtocolOutputLimit): void => {
|
|
919
|
-
if (protocolError) return;
|
|
920
|
-
protocolError = limit;
|
|
921
|
-
error = formatProtocolOutputLimit(limit);
|
|
922
|
-
if (!childExited) {
|
|
923
|
-
trySignalChild(child, "SIGTERM");
|
|
924
|
-
protocolHardKillTimer = setTimeout(() => {
|
|
925
|
-
if (!settled) trySignalChild(child, "SIGKILL");
|
|
926
|
-
}, 3000);
|
|
927
|
-
protocolHardKillTimer.unref?.();
|
|
928
|
-
}
|
|
929
|
-
};
|
|
930
|
-
const stdoutReader = createBoundedLineReader({
|
|
931
|
-
oversizedLineProjector: PI_AGGREGATE_EVENT_PROJECTOR,
|
|
932
|
-
onLine: processStdoutLine,
|
|
933
|
-
onLimit: failProtocol,
|
|
934
|
-
});
|
|
935
|
-
const stderrReader = createBoundedLineReader({
|
|
936
|
-
stream: "stderr",
|
|
937
|
-
maxPendingLineBytes: MAX_CHILD_STDERR_BYTES,
|
|
938
|
-
onLine: (line) => appendChildLine("subagent.child.stderr", line),
|
|
939
|
-
onLimit: (limit) => appendChildLine("subagent.child.stderr", formatProtocolOutputLimit(limit)),
|
|
940
|
-
});
|
|
941
|
-
const clearStdioGuard = attachPostExitStdioGuard(child, { idleMs: 2000, hardMs: 8000 });
|
|
942
|
-
child.stdout.on("data", (chunk: Buffer) => stdoutReader.push(chunk));
|
|
943
|
-
child.stderr.on("data", (chunk: Buffer) => {
|
|
944
|
-
stderrTail.push(chunk);
|
|
945
|
-
stderrReader.push(chunk);
|
|
946
|
-
outputStream.write(chunk);
|
|
947
|
-
orcaProgressTab?.append(chunk.toString("utf-8"));
|
|
948
|
-
});
|
|
949
|
-
registerInterrupt?.(() => {
|
|
950
|
-
if (settled || timedOut || stopped) return;
|
|
951
|
-
interrupted = true;
|
|
952
|
-
if (!error) error = "Interrupted. Waiting for explicit next action.";
|
|
953
|
-
trySignalChild(child, "SIGINT");
|
|
954
|
-
setTimeout(() => {
|
|
955
|
-
if (!settled && !timedOut && !stopped) trySignalChild(child, "SIGTERM");
|
|
956
|
-
}, 1000).unref?.();
|
|
957
|
-
});
|
|
958
|
-
const terminateForTimeout = (message: string): void => {
|
|
959
|
-
if (settled || timedOut || stopped) return;
|
|
960
|
-
timedOut = true;
|
|
961
|
-
// runPiStreaming's terminal result derives the timeout error from this
|
|
962
|
-
// message, so retain the tool-specific reason through finalization.
|
|
963
|
-
timeoutMessage = message;
|
|
964
|
-
interrupted = false;
|
|
965
|
-
error = message;
|
|
966
|
-
if (processTreeController) void processTreeController.terminate();
|
|
967
|
-
else trySignalChild(child, "SIGTERM");
|
|
968
|
-
};
|
|
969
|
-
let toolTimeoutSequence = 0;
|
|
970
|
-
const activeToolTimeouts = new Map<string, { toolName: string; timer: ReturnType<typeof setTimeout> }>();
|
|
971
|
-
const activeToolTimeoutKeysByName = new Map<string, string[]>();
|
|
972
|
-
const removeToolTimeoutKey = (key: string): void => {
|
|
973
|
-
const active = activeToolTimeouts.get(key);
|
|
974
|
-
if (!active) return;
|
|
975
|
-
clearTimeout(active.timer);
|
|
976
|
-
activeToolTimeouts.delete(key);
|
|
977
|
-
const keys = activeToolTimeoutKeysByName.get(active.toolName)?.filter((candidate) => candidate !== key) ?? [];
|
|
978
|
-
if (keys.length > 0) activeToolTimeoutKeysByName.set(active.toolName, keys);
|
|
979
|
-
else activeToolTimeoutKeysByName.delete(active.toolName);
|
|
980
|
-
};
|
|
981
|
-
const clearActiveToolTimeout = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
982
|
-
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
983
|
-
? `id:${event.toolCallId}`
|
|
984
|
-
: typeof event.toolName === "string"
|
|
985
|
-
? activeToolTimeoutKeysByName.get(event.toolName)?.[0]
|
|
986
|
-
: activeToolTimeouts.size === 1
|
|
987
|
-
? [...activeToolTimeouts.keys()][0]
|
|
988
|
-
: undefined;
|
|
989
|
-
if (key) removeToolTimeoutKey(key);
|
|
990
|
-
};
|
|
991
|
-
const clearAllToolTimeouts = (): void => {
|
|
992
|
-
for (const key of [...activeToolTimeouts.keys()]) removeToolTimeoutKey(key);
|
|
993
|
-
};
|
|
994
|
-
const armToolTimeout = (event: { toolCallId?: unknown; toolName: string }): void => {
|
|
995
|
-
const timeoutForTool = effectiveToolTimeoutMs(event.toolName, toolTimeoutMs);
|
|
996
|
-
if (timeoutForTool === undefined) return;
|
|
997
|
-
const runRemaining = runDeadlineAt === undefined ? undefined : Math.max(0, runDeadlineAt - Date.now());
|
|
998
|
-
if (runRemaining !== undefined && timeoutForTool >= runRemaining) return;
|
|
999
|
-
const key = toolTimeoutCallKey(event, ++toolTimeoutSequence);
|
|
1000
|
-
const toolName = event.toolName;
|
|
1001
|
-
const timer = setTimeout(() => {
|
|
1002
|
-
removeToolTimeoutKey(key);
|
|
1003
|
-
terminateForTimeout(formatToolTimeoutMessage(toolName, timeoutForTool));
|
|
1004
|
-
}, timeoutForTool);
|
|
1005
|
-
timer.unref?.();
|
|
1006
|
-
activeToolTimeouts.set(key, { toolName, timer });
|
|
1007
|
-
const keys = activeToolTimeoutKeysByName.get(toolName) ?? [];
|
|
1008
|
-
keys.push(key);
|
|
1009
|
-
activeToolTimeoutKeysByName.set(toolName, keys);
|
|
1010
|
-
};
|
|
1011
|
-
registerTimeout?.(() => terminateForTimeout(timeoutMessage ?? "Subagent timed out."));
|
|
1012
|
-
registerStop?.(() => {
|
|
1013
|
-
if (settled || timedOut || stopped) return;
|
|
1014
|
-
stopped = true;
|
|
1015
|
-
interrupted = false;
|
|
1016
|
-
error = stopMessage ?? "Subagent stopped by user.";
|
|
1017
|
-
if (processTreeController) void processTreeController.terminate();
|
|
1018
|
-
else trySignalChild(child, "SIGTERM");
|
|
1019
|
-
});
|
|
1020
|
-
const clearDrainTimers = () => {
|
|
1021
|
-
clearAllToolTimeouts();
|
|
1022
|
-
if (finalDrainTimer) {
|
|
1023
|
-
clearTimeout(finalDrainTimer);
|
|
1024
|
-
finalDrainTimer = undefined;
|
|
1025
|
-
}
|
|
1026
|
-
if (finalHardKillTimer) {
|
|
1027
|
-
clearTimeout(finalHardKillTimer);
|
|
1028
|
-
finalHardKillTimer = undefined;
|
|
1029
|
-
}
|
|
1030
|
-
clearWatchdogTailTimer();
|
|
1031
|
-
if (protocolHardKillTimer) {
|
|
1032
|
-
clearTimeout(protocolHardKillTimer);
|
|
1033
|
-
protocolHardKillTimer = undefined;
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
function startFinalDrain(): void {
|
|
1037
|
-
if (childWatchdogIsActive(childWatchdogState)) {
|
|
1038
|
-
armWatchdogTail();
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (childExited || finalDrainTimer || settled) return;
|
|
1042
|
-
finalDrainTimer = setTimeout(() => {
|
|
1043
|
-
if (settled) return;
|
|
1044
|
-
const termSent = trySignalChild(child, "SIGTERM");
|
|
1045
|
-
if (!termSent) return;
|
|
1046
|
-
forcedTerminationSignal = true;
|
|
1047
|
-
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived && !error && !assistantError) {
|
|
1048
|
-
error = `Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its terminal event. Forcing termination.`;
|
|
1049
|
-
}
|
|
1050
|
-
finalHardKillTimer = setTimeout(() => {
|
|
1051
|
-
if (settled) return;
|
|
1052
|
-
forcedTerminationSignal = trySignalChild(child, "SIGKILL") || forcedTerminationSignal;
|
|
1053
|
-
}, HARD_KILL_MS);
|
|
1054
|
-
finalHardKillTimer.unref?.();
|
|
1055
|
-
}, FINAL_STOP_GRACE_MS);
|
|
1056
|
-
finalDrainTimer.unref?.();
|
|
1057
|
-
}
|
|
1058
|
-
function clearWatchdogTailTimer(): void {
|
|
1059
|
-
if (watchdogTailTimer) {
|
|
1060
|
-
clearTimeout(watchdogTailTimer);
|
|
1061
|
-
watchdogTailTimer = undefined;
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
function armWatchdogTail(): void {
|
|
1065
|
-
if ((!cleanTerminalAssistantStopReceived && !agentSettledReceived) || watchdogTailTimer || settled) return;
|
|
1066
|
-
watchdogTailTimer = setTimeout(() => {
|
|
1067
|
-
watchdogTailTimer = undefined;
|
|
1068
|
-
updateChildWatchdogState({
|
|
1069
|
-
phase: "stale",
|
|
1070
|
-
seq: (childWatchdogState?.seq ?? 0) + 1,
|
|
1071
|
-
lastUpdate: Date.now(),
|
|
1072
|
-
followUpPending: false,
|
|
1073
|
-
reason: "child watchdog tail timeout",
|
|
1074
|
-
timedOut: true,
|
|
1075
|
-
});
|
|
1076
|
-
startFinalDrain();
|
|
1077
|
-
}, childWatchdogConfig?.watchdogTailTimeoutMs ?? 120_000);
|
|
1078
|
-
watchdogTailTimer.unref?.();
|
|
1079
|
-
}
|
|
1080
|
-
child.on("exit", () => {
|
|
1081
|
-
childExited = true;
|
|
1082
|
-
clearDrainTimers();
|
|
1083
|
-
});
|
|
1084
|
-
child.on("close", async (exitCode, signal) => {
|
|
1085
|
-
settled = true;
|
|
1086
|
-
const processCloseObservedAt = Date.now();
|
|
1087
|
-
const processTree = processTreeController
|
|
1088
|
-
? await processTreeController.finishAfterWriterClose()
|
|
1089
|
-
: { state: "unknown" as const, reason: "verification-failed" as const, diagnostic: "Writer PID was unavailable." };
|
|
1090
|
-
try {
|
|
1091
|
-
onWriterProcess?.({ state: "none" });
|
|
1092
|
-
} catch {
|
|
1093
|
-
// The runner still owns and releases the lease during finalization.
|
|
1094
|
-
}
|
|
1095
|
-
registerInterrupt?.(undefined);
|
|
1096
|
-
registerTimeout?.(undefined);
|
|
1097
|
-
registerStop?.(undefined);
|
|
1098
|
-
clearDrainTimers();
|
|
1099
|
-
clearStdioGuard();
|
|
1100
|
-
stdoutReader.end();
|
|
1101
|
-
stderrReader.end();
|
|
1102
|
-
outputStream.end();
|
|
1103
|
-
const stderr = stderrTail.text();
|
|
1104
|
-
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
1105
|
-
const finalError = error ?? assistantError;
|
|
1106
|
-
const forcedDrainAfterFinalSuccess = Boolean(forcedTerminationSignal || signal) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !finalError;
|
|
1107
|
-
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(messages);
|
|
1108
|
-
const forcedDrainError = forcedDrainAfterEmptyTerminal && stderr.trim()
|
|
1109
|
-
? stderr.trim()
|
|
1110
|
-
: undefined;
|
|
1111
|
-
const signalError = isUnexplainedProcessSignal({
|
|
1112
|
-
processSignal: signal,
|
|
1113
|
-
interrupted,
|
|
1114
|
-
timedOut,
|
|
1115
|
-
stopped,
|
|
1116
|
-
forcedDrainAfterFinalSuccess: forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal,
|
|
1117
|
-
}) ? formatProcessSignalError(signal!) : undefined;
|
|
1118
|
-
resolve(omitUndefinedProperties({
|
|
1119
|
-
stderr,
|
|
1120
|
-
exitCode: timedOut || stopped ? 1 : interrupted || (forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal) ? 0 : forcedTerminationSignal || signal ? (exitCode ?? 1) : exitCode,
|
|
1121
|
-
messages,
|
|
1122
|
-
usage,
|
|
1123
|
-
toolCount,
|
|
1124
|
-
durationMs: Date.now() - startedAt,
|
|
1125
|
-
model,
|
|
1126
|
-
error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : interrupted || (forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal) ? undefined : finalError ?? forcedDrainError ?? signalError,
|
|
1127
|
-
protocolError,
|
|
1128
|
-
finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput,
|
|
1129
|
-
outputState: finalOutput.trim() ? "present" : "absent",
|
|
1130
|
-
interrupted,
|
|
1131
|
-
timedOut,
|
|
1132
|
-
stopped,
|
|
1133
|
-
observedMutationAttempt,
|
|
1134
|
-
structuredOutputToolInvoked,
|
|
1135
|
-
structuredOutputMessageStartIndex,
|
|
1136
|
-
watchdog: childWatchdogState,
|
|
1137
|
-
processInstanceId,
|
|
1138
|
-
processCloseObservedAt,
|
|
1139
|
-
processSignal: signal,
|
|
1140
|
-
processTree,
|
|
1141
|
-
currentTool,
|
|
1142
|
-
currentToolArgs,
|
|
1143
|
-
currentPath,
|
|
1144
|
-
afterCompactionSettlement: afterCompactionSettlement || undefined,
|
|
1145
|
-
}));
|
|
1146
|
-
});
|
|
1147
|
-
|
|
1148
|
-
child.on("error", (spawnError) => {
|
|
1149
|
-
settled = true;
|
|
1150
|
-
try {
|
|
1151
|
-
onWriterProcess?.({ state: "none" });
|
|
1152
|
-
} catch {
|
|
1153
|
-
// The runner still owns and releases the lease during finalization.
|
|
1154
|
-
}
|
|
1155
|
-
registerInterrupt?.(undefined);
|
|
1156
|
-
registerTimeout?.(undefined);
|
|
1157
|
-
registerStop?.(undefined);
|
|
1158
|
-
clearDrainTimers();
|
|
1159
|
-
clearStdioGuard();
|
|
1160
|
-
stdoutReader.end();
|
|
1161
|
-
stderrReader.end();
|
|
1162
|
-
outputStream.end();
|
|
1163
|
-
const stderr = stderrTail.text();
|
|
1164
|
-
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
1165
|
-
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
1166
|
-
resolve(omitUndefinedProperties({ stderr, exitCode: 1, messages, usage, toolCount, durationMs: Date.now() - startedAt, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : error ?? assistantError ?? spawnErrorMessage, protocolError, finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput, outputState: finalOutput.trim() ? "present" : "absent", timedOut, stopped, observedMutationAttempt, structuredOutputToolInvoked, structuredOutputMessageStartIndex, watchdog: childWatchdogState, processInstanceId, processTree: { state: "unknown", reason: "verification-failed", diagnostic: spawnErrorMessage } }));
|
|
1167
|
-
});
|
|
1168
|
-
});
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
527
|
function resolvePiPackageRootFallback(): string {
|
|
1172
528
|
const root = resolveInstalledPiPackageRoot();
|
|
1173
529
|
if (root) return root;
|
|
@@ -1289,15 +645,17 @@ interface SingleStepContext {
|
|
|
1289
645
|
flatIndex: number;
|
|
1290
646
|
flatStepCount: number;
|
|
1291
647
|
outputFile: string;
|
|
1292
|
-
steerInboxDir?: string;
|
|
1293
|
-
steerCapabilityPath?: string;
|
|
1294
|
-
steerAckDir?: string;
|
|
1295
648
|
transcriptPath?: string;
|
|
1296
649
|
piPackageRoot?: string;
|
|
1297
|
-
|
|
650
|
+
/** Factory the runner creates this step's child session through. */
|
|
651
|
+
childSessions: ChildSessionFactory;
|
|
652
|
+
/** The launching executor's own child runtime; nested route, depth, and ceilings come from here. */
|
|
653
|
+
inheritedChildRuntime?: InheritedChildRuntime;
|
|
1298
654
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void;
|
|
1299
655
|
registerTimeout?: (interrupt: (() => void) | undefined) => void;
|
|
1300
656
|
registerStop?: (stop: (() => void) | undefined) => void;
|
|
657
|
+
/** Receives the live child's steer handler while its session runs. */
|
|
658
|
+
registerSteer?: (steer: StepSteerHandler | undefined) => void;
|
|
1301
659
|
timeoutSignal?: AbortSignal;
|
|
1302
660
|
stopSignal?: AbortSignal;
|
|
1303
661
|
timeoutMessage?: string;
|
|
@@ -1313,7 +671,6 @@ interface SingleStepContext {
|
|
|
1313
671
|
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
1314
672
|
onAttemptStart?: (attempt: { model?: string; thinking?: string; contextLimit?: number }) => void;
|
|
1315
673
|
onChildEvent?: (event: ChildEvent) => void;
|
|
1316
|
-
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void;
|
|
1317
674
|
onExternalProcess?: (process: ExternalProcessStatus) => void;
|
|
1318
675
|
onExternalJob?: (status: ExternalJobStatus) => void;
|
|
1319
676
|
skipAcceptance?: () => boolean;
|
|
@@ -1419,13 +776,11 @@ async function runSingleStepInner(
|
|
|
1419
776
|
model: step.model,
|
|
1420
777
|
modelCandidates: step.modelCandidates,
|
|
1421
778
|
mcpDirectTools: step.mcpDirectTools,
|
|
1422
|
-
mcpConfig: step.mcpConfig,
|
|
1423
|
-
runtimeServerNames: step.runtimeServerNames,
|
|
1424
779
|
cwd: step.cwd ?? ctx.cwd,
|
|
1425
780
|
requireReadTool: Boolean(step.skills?.length),
|
|
1426
781
|
structuredOutput: Boolean(effectiveStructuredOutput),
|
|
1427
782
|
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1428
|
-
inheritedCapabilityCeiling:
|
|
783
|
+
inheritedCapabilityCeiling: ctx.inheritedChildRuntime?.capabilityCeiling,
|
|
1429
784
|
permissionRules: step.permissionRules,
|
|
1430
785
|
}));
|
|
1431
786
|
const contractTools = resolvedTaskToolPlan.explicitToolAllowlist ? resolvedTaskToolPlan.effectiveToolAllowlist : undefined;
|
|
@@ -1645,12 +1000,10 @@ async function runSingleStepInner(
|
|
|
1645
1000
|
let capabilityAudit: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit | undefined;
|
|
1646
1001
|
let launchResolvedExtensions = step.launchResolvedExtensions;
|
|
1647
1002
|
const modelAttempts: ModelAttempt[] = [];
|
|
1648
|
-
const writerProcesses: PiWriterProcessInstanceExitV1[] = [];
|
|
1649
|
-
let writerAttemptCount = 0;
|
|
1650
1003
|
const attemptNotes: string[] = [];
|
|
1651
1004
|
let finalRequiredOutputMissing: boolean | undefined;
|
|
1652
1005
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
1653
|
-
let finalResult:
|
|
1006
|
+
let finalResult: RunChildSessionResult | undefined;
|
|
1654
1007
|
let finalOutputSnapshot: SingleOutputSnapshot | undefined;
|
|
1655
1008
|
let structuredAcceptanceReport: unknown;
|
|
1656
1009
|
let structuredAcceptanceReportError: string | undefined;
|
|
@@ -1662,10 +1015,6 @@ async function runSingleStepInner(
|
|
|
1662
1015
|
let finalMutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
1663
1016
|
|
|
1664
1017
|
let modelIndex = 0;
|
|
1665
|
-
let startupAttemptIndex = 0;
|
|
1666
|
-
// Escalated to "file" after an unexplained zero-activity startup failure so
|
|
1667
|
-
// retries keep the task text out of argv (endpoint pre-exec scans may deny it).
|
|
1668
|
-
let taskDeliveryOverride: SubagentTaskDelivery | undefined;
|
|
1669
1018
|
let contextOverflow = false;
|
|
1670
1019
|
let launchWarningsEmitted = false;
|
|
1671
1020
|
let abortRecoveryAttempted = false;
|
|
@@ -1677,7 +1026,7 @@ async function runSingleStepInner(
|
|
|
1677
1026
|
const candidate = candidates[modelIndex];
|
|
1678
1027
|
const expectedModelForVerification = candidate && !(step.skipPrimaryModelVerification && modelIndex === 0) ? candidate : undefined;
|
|
1679
1028
|
try {
|
|
1680
|
-
assertThinkingWithinCeiling({ model: candidate, configThinking: step.thinking, ceiling: step.thinkingCeiling
|
|
1029
|
+
assertThinkingWithinCeiling({ model: candidate, configThinking: step.thinking, ceiling: step.thinkingCeiling, agent: step.agent, runId: ctx.id });
|
|
1681
1030
|
} catch (error) {
|
|
1682
1031
|
const message = error instanceof Error ? error.message : String(error);
|
|
1683
1032
|
return omitUndefinedProperties({ agent: step.agent, output: message, error: message, exitCode: 1, context: step.context, thinkingCeiling: step.thinkingCeiling });
|
|
@@ -1704,12 +1053,10 @@ async function runSingleStepInner(
|
|
|
1704
1053
|
childIndex: ctx.flatIndex,
|
|
1705
1054
|
})
|
|
1706
1055
|
: undefined;
|
|
1707
|
-
|
|
1056
|
+
let watchdogSink: ((event: ChildWatchdogStatusEvent) => void) | undefined;
|
|
1057
|
+
const launch = buildInProcessChildLaunch(omitUndefinedProperties({
|
|
1708
1058
|
parentSessionId: step.parentSessionId,
|
|
1709
1059
|
forkCacheKey: step.context === "fork" ? deriveForkPromptCacheKey(step.parentSessionId) : undefined,
|
|
1710
|
-
baseArgs: ["--mode", "json", "-p"],
|
|
1711
|
-
task: attemptTask,
|
|
1712
|
-
taskDelivery: taskDeliveryOverride,
|
|
1713
1060
|
sessionEnabled,
|
|
1714
1061
|
sessionDir,
|
|
1715
1062
|
sessionFile: step.sessionFile,
|
|
@@ -1728,28 +1075,20 @@ async function runSingleStepInner(
|
|
|
1728
1075
|
systemPrompt: step.systemPrompt ?? "",
|
|
1729
1076
|
systemPromptMode: step.systemPromptMode,
|
|
1730
1077
|
mcpDirectTools: step.mcpDirectTools,
|
|
1731
|
-
|
|
1732
|
-
runtimeServerNames: step.runtimeServerNames,
|
|
1078
|
+
extensionBindings,
|
|
1733
1079
|
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1734
1080
|
cwd: step.cwd ?? ctx.cwd,
|
|
1735
|
-
promptFileStem: step.agent,
|
|
1736
1081
|
intercomSessionName: ctx.childIntercomTarget,
|
|
1737
1082
|
sessionName: childSessionName,
|
|
1738
1083
|
orchestratorIntercomTarget: ctx.orchestratorIntercomTarget,
|
|
1739
1084
|
runId: ctx.id,
|
|
1740
1085
|
childAgentName: step.agent,
|
|
1741
1086
|
childIndex: ctx.flatIndex,
|
|
1742
|
-
|
|
1743
|
-
parentControlInbox: ctx.nestedRoute?.controlInbox,
|
|
1744
|
-
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
1745
|
-
parentCapabilityToken: ctx.nestedRoute?.capabilityToken,
|
|
1087
|
+
nestedRoute: ctx.nestedRoute,
|
|
1746
1088
|
runFanoutBudget: ctx.runFanoutBudget ? {
|
|
1747
1089
|
...ctx.runFanoutBudget,
|
|
1748
1090
|
...(step.runFanoutPath ? { parentPath: `${ctx.runFanoutBudget.parentPath ? `${ctx.runFanoutBudget.parentPath}/` : ""}${step.runFanoutPath}` } : {}),
|
|
1749
1091
|
} : undefined,
|
|
1750
|
-
steerInboxDir: ctx.steerInboxDir,
|
|
1751
|
-
steerCapabilityPath: ctx.steerCapabilityPath,
|
|
1752
|
-
steerAckDir: ctx.steerAckDir,
|
|
1753
1092
|
structuredOutput: effectiveStructuredOutput,
|
|
1754
1093
|
toolBudget: step.toolBudget,
|
|
1755
1094
|
permissionRules: step.permissionRules,
|
|
@@ -1757,11 +1096,19 @@ async function runSingleStepInner(
|
|
|
1757
1096
|
? path.join(ctx.artifactsDir, "permission-audit", `${ctx.id}-${ctx.flatIndex}.jsonl`)
|
|
1758
1097
|
: undefined,
|
|
1759
1098
|
childWatchdog,
|
|
1099
|
+
watchdogStatus: (event) => watchdogSink?.(event),
|
|
1760
1100
|
waitToolEnabled: step.waitToolEnabled,
|
|
1761
1101
|
waitToolDefaultTimeoutMs: step.waitToolDefaultTimeoutMs,
|
|
1762
1102
|
thinkingCeiling: step.thinkingCeiling,
|
|
1763
|
-
|
|
1103
|
+
maxSubagentDepth: step.maxSubagentDepth,
|
|
1104
|
+
inherited: ctx.inheritedChildRuntime,
|
|
1105
|
+
host: "runner",
|
|
1764
1106
|
}));
|
|
1107
|
+
if (effectiveStructuredOutput && launch.config.structuredOutput) {
|
|
1108
|
+
// The runner reads the value back from the runtime's files after the run.
|
|
1109
|
+
launch.config.structuredOutput.capture = createStructuredOutputFileCapture(effectiveStructuredOutput);
|
|
1110
|
+
}
|
|
1111
|
+
const { warnings, capabilityAudit: attemptCapabilityAudit } = launch;
|
|
1765
1112
|
if (!launchWarningsEmitted && warnings.length > 0) {
|
|
1766
1113
|
for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
|
|
1767
1114
|
launchWarningsEmitted = true;
|
|
@@ -1777,13 +1124,11 @@ async function runSingleStepInner(
|
|
|
1777
1124
|
model: step.model,
|
|
1778
1125
|
modelCandidates: step.modelCandidates,
|
|
1779
1126
|
mcpDirectTools: step.mcpDirectTools,
|
|
1780
|
-
mcpConfig: step.mcpConfig,
|
|
1781
|
-
runtimeServerNames: step.runtimeServerNames,
|
|
1782
1127
|
cwd: step.cwd ?? ctx.cwd,
|
|
1783
1128
|
requireReadTool: Boolean(step.skills?.length),
|
|
1784
1129
|
structuredOutput: Boolean(effectiveStructuredOutput),
|
|
1785
1130
|
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1786
|
-
inheritedCapabilityCeiling:
|
|
1131
|
+
inheritedCapabilityCeiling: ctx.inheritedChildRuntime?.capabilityCeiling,
|
|
1787
1132
|
permissionRules: step.permissionRules,
|
|
1788
1133
|
}));
|
|
1789
1134
|
launchResolvedExtensions = projectLaunchResolvedChildExtensions(toolPlan);
|
|
@@ -1812,59 +1157,49 @@ async function runSingleStepInner(
|
|
|
1812
1157
|
}));
|
|
1813
1158
|
}
|
|
1814
1159
|
capabilityAudit = attemptCapabilityAudit;
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
ctx.
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
step.
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1160
|
+
// Each attempt rewrites the step output log; synchronous appends keep a
|
|
1161
|
+
// retried attempt from interleaving with the previous attempt's flush.
|
|
1162
|
+
fs.writeFileSync(ctx.outputFile, "", "utf-8");
|
|
1163
|
+
const run = await runChildSession(omitUndefinedProperties({
|
|
1164
|
+
factory: ctx.childSessions,
|
|
1165
|
+
launch,
|
|
1166
|
+
prompt: `Task: ${attemptTask}`,
|
|
1167
|
+
childWatchdog,
|
|
1168
|
+
childEventContext: { runId: ctx.id, stepIndex: ctx.flatIndex, agent: step.agent },
|
|
1169
|
+
appendChildEvent: (event) => appendDiagnosticJsonl(eventsPath, JSON.stringify(event), typeof event.type === "string" ? event.type : undefined),
|
|
1170
|
+
writeOutputLine: (line) => {
|
|
1171
|
+
try {
|
|
1172
|
+
fs.appendFileSync(ctx.outputFile, `${line}\n`, "utf-8");
|
|
1173
|
+
} catch {
|
|
1174
|
+
// The output log is observability only.
|
|
1175
|
+
}
|
|
1176
|
+
ctx.orcaProgressTab?.append(`${line}\n`);
|
|
1177
|
+
},
|
|
1178
|
+
registerInterrupt: ctx.registerInterrupt,
|
|
1179
|
+
registerTimeout: ctx.registerTimeout,
|
|
1180
|
+
registerStop: ctx.registerStop,
|
|
1181
|
+
registerSteer: ctx.registerSteer,
|
|
1182
|
+
registerWatchdogStatus: (sink) => { watchdogSink = sink; },
|
|
1183
|
+
timeoutMessage: ctx.timeoutMessage,
|
|
1184
|
+
stopMessage: ctx.stopMessage,
|
|
1185
|
+
onChildEvent: ctx.onChildEvent,
|
|
1827
1186
|
transcriptWriter,
|
|
1828
|
-
ctx.
|
|
1829
|
-
ctx.
|
|
1830
|
-
ctx.registerStop,
|
|
1831
|
-
ctx.stopMessage,
|
|
1832
|
-
ctx.onWriterProcess,
|
|
1833
|
-
ctx.toolTimeoutMs,
|
|
1834
|
-
ctx.deadlineAt,
|
|
1835
|
-
ctx.orcaProgressTab,
|
|
1187
|
+
toolTimeoutMs: ctx.toolTimeoutMs,
|
|
1188
|
+
runDeadlineAt: ctx.deadlineAt,
|
|
1836
1189
|
expectedModelForVerification,
|
|
1837
|
-
step.modelVerificationRegistry,
|
|
1838
|
-
step.mutationTools,
|
|
1839
|
-
);
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
kind: "pi-writer",
|
|
1844
|
-
attempt: writerAttemptCount - 1,
|
|
1845
|
-
closeObservedAt: run.processCloseObservedAt,
|
|
1846
|
-
exitCode: run.exitCode,
|
|
1847
|
-
signal: run.processSignal ?? null,
|
|
1848
|
-
processTree: run.processTree,
|
|
1849
|
-
});
|
|
1850
|
-
}
|
|
1851
|
-
const toolAvailabilityError = run.exitCode === 0 && !run.error
|
|
1852
|
-
? readChildToolDiagnosticError(toolDiagnosticPath)
|
|
1853
|
-
: undefined;
|
|
1854
|
-
const runtimeAcknowledgedExtensions = readRuntimeAcknowledgedExtensions(runtimeAcknowledgedExtensionsPath);
|
|
1855
|
-
cleanupTempDir(tempDir);
|
|
1190
|
+
modelVerificationRegistry: step.modelVerificationRegistry,
|
|
1191
|
+
mutationTools: step.mutationTools,
|
|
1192
|
+
}));
|
|
1193
|
+
const toolDiagnostic = run.exitCode === 0 && !run.error ? launch.capture.toolDiagnostic() : undefined;
|
|
1194
|
+
const toolAvailabilityError = toolDiagnostic ? formatChildToolDiagnostic(toolDiagnostic) : undefined;
|
|
1195
|
+
const runtimeAcknowledgedExtensions = launch.capture.runtimeAcknowledgedExtensions();
|
|
1856
1196
|
const midToolExitError = run.currentTool
|
|
1857
1197
|
&& isOrdinaryToolForMidToolExit(run.currentTool)
|
|
1858
1198
|
&& !run.interrupted
|
|
1859
1199
|
&& !run.timedOut
|
|
1860
1200
|
&& !run.stopped
|
|
1861
|
-
&& !run.protocolError
|
|
1862
1201
|
&& !toolAvailabilityError
|
|
1863
|
-
? formatMidToolExitError({
|
|
1864
|
-
toolName: run.currentTool,
|
|
1865
|
-
exitCode: run.exitCode,
|
|
1866
|
-
processSignal: run.processSignal,
|
|
1867
|
-
})
|
|
1202
|
+
? formatMidToolExitError({ toolName: run.currentTool })
|
|
1868
1203
|
: undefined;
|
|
1869
1204
|
|
|
1870
1205
|
let structuredOutput: unknown;
|
|
@@ -1951,19 +1286,10 @@ async function runSingleStepInner(
|
|
|
1951
1286
|
: run.error && run.exitCode === 0
|
|
1952
1287
|
? 1
|
|
1953
1288
|
: run.exitCode;
|
|
1954
|
-
const signalError = run.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
1955
|
-
processSignal: run.processSignal,
|
|
1956
|
-
interrupted: run.interrupted,
|
|
1957
|
-
timedOut: run.timedOut,
|
|
1958
|
-
stopped: run.stopped,
|
|
1959
|
-
})) ? formatProcessSignalError(run.processSignal!) : undefined;
|
|
1960
1289
|
const underlyingError = toolAvailabilityError
|
|
1961
1290
|
?? midToolExitError
|
|
1962
1291
|
?? structuredError
|
|
1963
1292
|
?? run.error
|
|
1964
|
-
?? signalError
|
|
1965
|
-
?? (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1966
|
-
?? ((emptyOutputError || missingRequiredOutputError) && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1967
1293
|
?? emptyOutputError
|
|
1968
1294
|
?? (missingRequiredOutputAfterMutation ? missingRequiredOutputError : undefined)
|
|
1969
1295
|
?? (hiddenError?.hasError
|
|
@@ -1971,13 +1297,7 @@ async function runSingleStepInner(
|
|
|
1971
1297
|
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1972
1298
|
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1973
1299
|
: undefined);
|
|
1974
|
-
const error =
|
|
1975
|
-
underlyingError ?? missingRequiredOutputError ?? completionEvidence.legacyFailureError,
|
|
1976
|
-
{
|
|
1977
|
-
agent: step.agent,
|
|
1978
|
-
ambientExtensionsEnabled: launchResolvedExtensions?.disableAmbientExtensions === false,
|
|
1979
|
-
},
|
|
1980
|
-
);
|
|
1300
|
+
const error = underlyingError ?? missingRequiredOutputError ?? completionEvidence.legacyFailureError;
|
|
1981
1301
|
const attempt: ModelAttempt = omitUndefinedProperties({
|
|
1982
1302
|
model: candidate ?? run.model ?? step.model ?? "default",
|
|
1983
1303
|
success: effectiveExitCode === 0 && !error,
|
|
@@ -1986,7 +1306,7 @@ async function runSingleStepInner(
|
|
|
1986
1306
|
usage: run.usage,
|
|
1987
1307
|
});
|
|
1988
1308
|
modelAttempts.push(attempt);
|
|
1989
|
-
if (!recoveringAbort && candidate
|
|
1309
|
+
if (!recoveringAbort && candidate) attemptedModels.push(candidate);
|
|
1990
1310
|
completionGuardTriggeredFinal = completionEvidence.guardTriggered && !underlyingError && !missingRequiredOutputError;
|
|
1991
1311
|
finalOutputSnapshot = outputSnapshot;
|
|
1992
1312
|
if (step.toolBudget) {
|
|
@@ -2003,11 +1323,10 @@ async function runSingleStepInner(
|
|
|
2003
1323
|
afterCompactionSettlement: run.afterCompactionSettlement === true,
|
|
2004
1324
|
});
|
|
2005
1325
|
const fileMutationEffect = completionEvidence.fileMutation ?? (missingRequiredOutputAfterMutation ? { status: "observed" as const, expected: completionEvidence.mutationExpected, attempted: true, evidence: mutationEvidence } : undefined);
|
|
2006
|
-
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, runtimeAcknowledgedExtensions, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect || settlementDiagnostic ? { effects: { ...(fileMutationEffect ? { fileMutation: fileMutationEffect } : {}), ...(settlementDiagnostic ? { settlementDiagnostic } : {}) } } : {}) } as
|
|
1326
|
+
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, runtimeAcknowledgedExtensions, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect || settlementDiagnostic ? { effects: { ...(fileMutationEffect ? { fileMutation: fileMutationEffect } : {}), ...(settlementDiagnostic ? { settlementDiagnostic } : {}) } } : {}) } as RunChildSessionResult;
|
|
2007
1327
|
const abortRecovery = !attempt.success ? planAbortRecovery({
|
|
2008
1328
|
messages: run.messages,
|
|
2009
1329
|
error,
|
|
2010
|
-
processSignal: run.processSignal,
|
|
2011
1330
|
sessionAvailable: Boolean(step.sessionFile && fs.existsSync(step.sessionFile)),
|
|
2012
1331
|
alreadyResumed: abortRecoveryAttempted,
|
|
2013
1332
|
stopped: run.stopped || ctx.stopSignal?.aborted || ctx.skipAcceptance?.(),
|
|
@@ -2038,50 +1357,6 @@ async function runSingleStepInner(
|
|
|
2038
1357
|
}
|
|
2039
1358
|
if (completionEvidence.guardTriggered) break modelAttemptsLoop;
|
|
2040
1359
|
|
|
2041
|
-
const startupFailure = isRetryableSubagentStartupFailure(omitUndefinedProperties({
|
|
2042
|
-
exitCode: effectiveExitCode,
|
|
2043
|
-
error,
|
|
2044
|
-
finalOutput: run.finalOutput,
|
|
2045
|
-
messageCount: run.messages.length,
|
|
2046
|
-
toolCount: run.toolCount,
|
|
2047
|
-
usage: run.usage,
|
|
2048
|
-
durationMs: run.durationMs,
|
|
2049
|
-
protocolError: run.protocolError,
|
|
2050
|
-
processSignal: run.processSignal,
|
|
2051
|
-
observedMutationAttempt: run.observedMutationAttempt,
|
|
2052
|
-
interrupted: run.interrupted,
|
|
2053
|
-
timedOut: run.timedOut,
|
|
2054
|
-
stopped: run.stopped,
|
|
2055
|
-
}));
|
|
2056
|
-
const retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];
|
|
2057
|
-
if (startupFailure && retryDelayMs !== undefined) {
|
|
2058
|
-
const retryNote = formatSubagentStartupRetryNote({
|
|
2059
|
-
model: attempt.model,
|
|
2060
|
-
attempt: startupAttemptIndex + 1,
|
|
2061
|
-
maxAttempts: SUBAGENT_STARTUP_RETRY_DELAYS_MS.length + 1,
|
|
2062
|
-
delayMs: retryDelayMs,
|
|
2063
|
-
});
|
|
2064
|
-
const shouldRetry = await waitForSubagentStartupRetry(retryDelayMs, [ctx.timeoutSignal, ctx.stopSignal]);
|
|
2065
|
-
if (!shouldRetry || ctx.skipAcceptance?.()) break modelAttemptsLoop;
|
|
2066
|
-
if (!taskDeliveryOverride && run.processSignal === "SIGKILL") {
|
|
2067
|
-
taskDeliveryOverride = "file";
|
|
2068
|
-
attemptNotes.push("[startup-retry] retrying with file task delivery to keep the task text out of the child process argv.");
|
|
2069
|
-
}
|
|
2070
|
-
attempt.error = retryNote;
|
|
2071
|
-
attemptNotes.push(retryNote);
|
|
2072
|
-
startupAttemptIndex += 1;
|
|
2073
|
-
continue;
|
|
2074
|
-
}
|
|
2075
|
-
if (startupFailure) {
|
|
2076
|
-
const startupError = formatSubagentStartupRetryExhaustedError({
|
|
2077
|
-
model: attempt.model,
|
|
2078
|
-
attempts: startupAttemptIndex + 1,
|
|
2079
|
-
});
|
|
2080
|
-
attempt.error = startupError;
|
|
2081
|
-
finalResult.error = startupError;
|
|
2082
|
-
finalResult.finalOutput = startupError;
|
|
2083
|
-
break modelAttemptsLoop;
|
|
2084
|
-
}
|
|
2085
1360
|
const retryableModelFailure = isRetryableModelFailureAttempt({ error, messages: run.messages, toolCount: run.toolCount });
|
|
2086
1361
|
if (retryableModelFailure) recordRetryableModelFailure(candidate ?? run.model ?? step.model, error);
|
|
2087
1362
|
if (isContextOverflow(error)) {
|
|
@@ -2092,7 +1367,6 @@ async function runSingleStepInner(
|
|
|
2092
1367
|
if (!retryableModelFailure || modelIndex === candidates.length - 1) break modelAttemptsLoop;
|
|
2093
1368
|
attemptNotes.push(formatModelAttemptNote(attempt, candidates[modelIndex + 1]));
|
|
2094
1369
|
modelIndex += 1;
|
|
2095
|
-
startupAttemptIndex = 0;
|
|
2096
1370
|
}
|
|
2097
1371
|
|
|
2098
1372
|
const rawOutput = finalResult?.finalOutput ?? "";
|
|
@@ -2120,7 +1394,7 @@ async function runSingleStepInner(
|
|
|
2120
1394
|
? extractChildWrittenOutput(finalResult?.messages, step.outputPath, step.cwd ?? ctx.cwd)
|
|
2121
1395
|
: undefined;
|
|
2122
1396
|
const outputState: SubagentOutputState = finalResult?.outputState === "present"
|
|
2123
|
-
|| (finalResult as (
|
|
1397
|
+
|| (finalResult as (RunChildSessionResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput !== undefined
|
|
2124
1398
|
|| Boolean(childWrittenOutput?.trim())
|
|
2125
1399
|
? "present"
|
|
2126
1400
|
: resolvedOutput.savedPath
|
|
@@ -2167,6 +1441,7 @@ async function runSingleStepInner(
|
|
|
2167
1441
|
reportOptional: isAgentContractV1(step.agentContract),
|
|
2168
1442
|
artifactsDir: ctx.artifactsDir,
|
|
2169
1443
|
runId: ctx.id,
|
|
1444
|
+
watchdog: finalResult?.watchdog,
|
|
2170
1445
|
}))
|
|
2171
1446
|
: undefined;
|
|
2172
1447
|
const stoppedAfterAcceptance = finalResult?.stopped === true || ctx.stopSignal?.aborted === true;
|
|
@@ -2221,7 +1496,7 @@ async function runSingleStepInner(
|
|
|
2221
1496
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
2222
1497
|
launchContractDigest: actualLaunchContractDigest,
|
|
2223
1498
|
launchResolvedExtensions,
|
|
2224
|
-
...((finalResult as (
|
|
1499
|
+
...((finalResult as (RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1 }) | undefined)?.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: (finalResult as RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1 }).runtimeAcknowledgedExtensions } : {}),
|
|
2225
1500
|
...(transcriptWriter ? { transcriptPath: artifactPaths.transcriptPath } : {}),
|
|
2226
1501
|
transcriptError: transcriptWriter?.getError(),
|
|
2227
1502
|
skills: step.skills,
|
|
@@ -2240,10 +1515,10 @@ async function runSingleStepInner(
|
|
|
2240
1515
|
outputState,
|
|
2241
1516
|
exitCode: effectiveFinalExitCode,
|
|
2242
1517
|
error: effectiveFinalError,
|
|
2243
|
-
protocolError: finalResult?.protocolError,
|
|
2244
1518
|
sessionFile: step.sessionFile,
|
|
2245
1519
|
intercomTarget: ctx.childIntercomTarget,
|
|
2246
1520
|
model: finalResult?.model,
|
|
1521
|
+
thinking: resolveEffectiveThinking(finalResult?.model, step.thinking),
|
|
2247
1522
|
attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
|
|
2248
1523
|
modelAttempts,
|
|
2249
1524
|
contextOverflow: contextOverflow || undefined,
|
|
@@ -2257,22 +1532,19 @@ async function runSingleStepInner(
|
|
|
2257
1532
|
interrupted: timedOutAfterAcceptance || stoppedAfterAcceptance ? false : finalResult?.interrupted,
|
|
2258
1533
|
timedOut: timedOutAfterAcceptance ? true : finalResult?.timedOut,
|
|
2259
1534
|
stopped: stoppedAfterAcceptance ? true : finalResult?.stopped,
|
|
2260
|
-
processSignal: finalResult?.processSignal,
|
|
2261
1535
|
timeoutRecovery,
|
|
2262
1536
|
toolBudget,
|
|
2263
1537
|
toolBudgetBlocked: toolBudgetBlocked || undefined,
|
|
2264
1538
|
completionGuardTriggered: completionGuardTriggeredFinal,
|
|
2265
|
-
...((finalResult as (
|
|
2266
|
-
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : (finalResult as (
|
|
1539
|
+
...((finalResult as (RunChildSessionResult & { effects?: import("../../shared/types.ts").EffectsProjection }) | undefined)?.effects ? { effects: (finalResult as RunChildSessionResult & { effects?: import("../../shared/types.ts").EffectsProjection }).effects } : {}),
|
|
1540
|
+
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : (finalResult as (RunChildSessionResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput,
|
|
2267
1541
|
structuredOutputPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.outputPath,
|
|
2268
1542
|
structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.schemaPath,
|
|
2269
1543
|
acceptance: effectiveAcceptance,
|
|
2270
1544
|
watchdog: finalResult?.watchdog,
|
|
2271
1545
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
2272
1546
|
launchResolvedExtensions,
|
|
2273
|
-
...((finalResult as (
|
|
2274
|
-
writerProcesses,
|
|
2275
|
-
writerAttemptCount,
|
|
1547
|
+
...((finalResult as (RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1 }) | undefined)?.runtimeAcknowledgedExtensions ? { runtimeAcknowledgedExtensions: (finalResult as RunChildSessionResult & { runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1 }).runtimeAcknowledgedExtensions } : {}),
|
|
2276
1548
|
});
|
|
2277
1549
|
return isAgentContractV1(step.agentContract) ? attachContractProjections(result as unknown as import("../../shared/types.ts").SingleResult) as unknown as typeof result : result;
|
|
2278
1550
|
}
|
|
@@ -2566,7 +1838,7 @@ async function runSingleStepWithTimeout(
|
|
|
2566
1838
|
|
|
2567
1839
|
async function runSubagent(
|
|
2568
1840
|
config: SubagentRunConfig,
|
|
2569
|
-
|
|
1841
|
+
childSessions: ChildSessionFactory,
|
|
2570
1842
|
): Promise<void> {
|
|
2571
1843
|
const { id, steps, resultPath, cwd, placeholder, taskIndex, totalTasks, maxOutput, artifactsDir, artifactConfig } =
|
|
2572
1844
|
config;
|
|
@@ -2586,9 +1858,11 @@ async function runSubagent(
|
|
|
2586
1858
|
const activeChildInterrupts = new Map<number, () => void>();
|
|
2587
1859
|
const activeChildTimeouts = new Map<number, () => void>();
|
|
2588
1860
|
const activeChildStops = new Map<number, () => void>();
|
|
1861
|
+
const activeChildSteers = new Map<number, StepSteerHandler>();
|
|
1862
|
+
/** Steers routed to a running step before its session was created. */
|
|
1863
|
+
const queuedStepSteers = new Map<number, SteerRequest[]>();
|
|
2589
1864
|
const childStopRequests = new Map<number, { childId: string; requestedAt: number }>();
|
|
2590
1865
|
const pendingStepSteers: SteerRequest[] = [];
|
|
2591
|
-
const steeringCapabilities = new Map<number, SteerCapability>();
|
|
2592
1866
|
let interrupted = false;
|
|
2593
1867
|
let currentActivityState: ActivityState | undefined;
|
|
2594
1868
|
let activityTimer: NodeJS.Timeout | undefined;
|
|
@@ -2905,6 +2179,7 @@ async function runSubagent(
|
|
|
2905
2179
|
success: statusResultSuccess(state, step),
|
|
2906
2180
|
sessionFile: step.sessionFile,
|
|
2907
2181
|
model: step.model,
|
|
2182
|
+
thinking: step.thinking,
|
|
2908
2183
|
attemptedModels: step.attemptedModels,
|
|
2909
2184
|
modelAttempts: step.modelAttempts,
|
|
2910
2185
|
usage: usageFromAttempts(step.modelAttempts),
|
|
@@ -3045,6 +2320,16 @@ async function runSubagent(
|
|
|
3045
2320
|
activeChildStops.set(flatIndex, stop);
|
|
3046
2321
|
if (stopped || childStopRequests.has(flatIndex)) stop();
|
|
3047
2322
|
};
|
|
2323
|
+
const registerStepSteer = (flatIndex: number, steer: StepSteerHandler | undefined): void => {
|
|
2324
|
+
if (!steer) {
|
|
2325
|
+
activeChildSteers.delete(flatIndex);
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
activeChildSteers.set(flatIndex, steer);
|
|
2329
|
+
const queued = queuedStepSteers.get(flatIndex);
|
|
2330
|
+
queuedStepSteers.delete(flatIndex);
|
|
2331
|
+
for (const request of queued ?? []) steerLiveChild(flatIndex, request);
|
|
2332
|
+
};
|
|
3048
2333
|
const interruptActiveChildren = (): void => {
|
|
3049
2334
|
for (const interrupt of [...activeChildInterrupts.values()]) interrupt();
|
|
3050
2335
|
};
|
|
@@ -3497,22 +2782,15 @@ async function runSubagent(
|
|
|
3497
2782
|
if (!step) return { index, state: "failed" as const, reason: "child index out of range" };
|
|
3498
2783
|
if (step.status === "pending") return { index, state: "scheduled" as const };
|
|
3499
2784
|
if (step.status !== "running") return { index, state: "failed" as const, reason: `child is ${step.status}` };
|
|
3500
|
-
if (steeringCapabilities.get(index)?.supported === false) return { index, state: "failed" as const, reason: "child Pi session does not support steering" };
|
|
3501
2785
|
return { index, state: "routed" as const };
|
|
3502
2786
|
});
|
|
3503
2787
|
recordSteeringLifecycle(request, targetStates);
|
|
3504
2788
|
emitSteeringEvent("subagent.steer.requested", request, undefined, { targets: targetStates });
|
|
3505
2789
|
for (const target of targetStates) {
|
|
3506
2790
|
if (target.state === "routed") {
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
emitSteeringEvent("subagent.steer.routed", request, target.index);
|
|
3511
|
-
} catch (error) {
|
|
3512
|
-
markSteeringAttention(target.index);
|
|
3513
|
-
updateSteeringLifecycleTarget(request.id, target.index, "failed", now, { reason: error instanceof Error ? error.message : String(error) });
|
|
3514
|
-
emitSteeringEvent("subagent.steer.failed", request, target.index, { reason: error instanceof Error ? error.message : String(error) });
|
|
3515
|
-
}
|
|
2791
|
+
updateSteeringLifecycleTarget(request.id, target.index, "routed", now);
|
|
2792
|
+
emitSteeringEvent("subagent.steer.routed", request, target.index);
|
|
2793
|
+
steerLiveChild(target.index, request);
|
|
3516
2794
|
} else if (target.state === "failed") {
|
|
3517
2795
|
markSteeringAttention(target.index);
|
|
3518
2796
|
emitSteeringEvent("subagent.steer.failed", request, target.index, { reason: target.reason });
|
|
@@ -3524,27 +2802,42 @@ async function runSubagent(
|
|
|
3524
2802
|
statusPayload.lastUpdate = now;
|
|
3525
2803
|
writeStatusPayload();
|
|
3526
2804
|
};
|
|
3527
|
-
|
|
2805
|
+
/** Record the outcome of handing a routed steer to the live child session. */
|
|
2806
|
+
const applySteerDelivery = (requestId: string, index: number, delivery: { state: "delivered" | "queued" | "failed"; message: string }): void => {
|
|
3528
2807
|
const lifecycle = steeringStatus(statusPayload);
|
|
3529
|
-
const request = lifecycle.recent.find((candidate) => candidate.id ===
|
|
3530
|
-
if (!request || !request.targets.some((target) => target.index ===
|
|
3531
|
-
const late = fs.existsSync(steeringMarkerPath(
|
|
2808
|
+
const request = lifecycle.recent.find((candidate) => candidate.id === requestId);
|
|
2809
|
+
if (!request || !request.targets.some((target) => target.index === index)) return;
|
|
2810
|
+
const late = fs.existsSync(steeringMarkerPath(requestId));
|
|
3532
2811
|
const now = Date.now();
|
|
3533
|
-
if (
|
|
3534
|
-
updateSteeringLifecycleTarget(
|
|
3535
|
-
emitSteeringEvent("subagent.steer.delivered", { type: "steer", id:
|
|
3536
|
-
} else if (
|
|
3537
|
-
updateSteeringLifecycleTarget(
|
|
3538
|
-
emitSteeringEvent("subagent.steer.queued", { type: "steer", id:
|
|
2812
|
+
if (delivery.state === "delivered") {
|
|
2813
|
+
updateSteeringLifecycleTarget(requestId, index, late ? "late" : "delivered", now, omitUndefinedProperties({ reason: late ? "acknowledged after recovery commit" : undefined }));
|
|
2814
|
+
emitSteeringEvent("subagent.steer.delivered", { type: "steer", id: requestId, ts: now, message: delivery.message }, index, { late, deliveryStatus: "delivered", message: delivery.message });
|
|
2815
|
+
} else if (delivery.state === "queued") {
|
|
2816
|
+
updateSteeringLifecycleTarget(requestId, index, "queued", now);
|
|
2817
|
+
emitSteeringEvent("subagent.steer.queued", { type: "steer", id: requestId, ts: now, message: delivery.message }, index, { deliveryStatus: "queued", message: delivery.message });
|
|
3539
2818
|
} else {
|
|
3540
|
-
markSteeringAttention(
|
|
3541
|
-
updateSteeringLifecycleTarget(
|
|
3542
|
-
emitSteeringEvent("subagent.steer.failed", { type: "steer", id:
|
|
2819
|
+
markSteeringAttention(index);
|
|
2820
|
+
updateSteeringLifecycleTarget(requestId, index, "failed", now, { reason: delivery.message });
|
|
2821
|
+
emitSteeringEvent("subagent.steer.failed", { type: "steer", id: requestId, ts: now, message: delivery.message }, index, { reason: delivery.message });
|
|
3543
2822
|
}
|
|
3544
|
-
emitTerminalSteeringNotice(
|
|
2823
|
+
emitTerminalSteeringNotice(requestId, `Steering failed for run ${id}: ${delivery.message}`);
|
|
3545
2824
|
statusPayload.lastUpdate = now;
|
|
3546
2825
|
writeStatusPayload();
|
|
3547
2826
|
};
|
|
2827
|
+
/** Hand a routed steer to the step's live session, or hold it until the session exists. */
|
|
2828
|
+
const steerLiveChild = (index: number, request: SteerRequest): void => {
|
|
2829
|
+
const steer = activeChildSteers.get(index);
|
|
2830
|
+
if (!steer) {
|
|
2831
|
+
const queued = queuedStepSteers.get(index) ?? [];
|
|
2832
|
+
queued.push(request);
|
|
2833
|
+
queuedStepSteers.set(index, queued);
|
|
2834
|
+
return;
|
|
2835
|
+
}
|
|
2836
|
+
void steer(request).then(
|
|
2837
|
+
(delivery) => applySteerDelivery(request.id, index, delivery),
|
|
2838
|
+
(error) => applySteerDelivery(request.id, index, { state: "failed", message: error instanceof Error ? error.message : String(error) }),
|
|
2839
|
+
);
|
|
2840
|
+
};
|
|
3548
2841
|
const flushPendingStepSteers = (flatIndex: number): void => {
|
|
3549
2842
|
const remaining: SteerRequest[] = [];
|
|
3550
2843
|
for (const request of pendingStepSteers.splice(0)) {
|
|
@@ -3584,6 +2877,15 @@ async function runSubagent(
|
|
|
3584
2877
|
writeStatusPayload(false);
|
|
3585
2878
|
return;
|
|
3586
2879
|
}
|
|
2880
|
+
if (event.type === "message_end") {
|
|
2881
|
+
const next = applyChildWatchdogMessage(step.watchdog, event.message, now);
|
|
2882
|
+
if (next) step.watchdog = next;
|
|
2883
|
+
if (next && (event.message as { role?: unknown } | undefined)?.role === "custom") {
|
|
2884
|
+
statusPayload.lastUpdate = now;
|
|
2885
|
+
writeStatusPayload(false);
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
3587
2889
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
3588
2890
|
const mutates = isMutatingTool(event.toolName, event.args, flatSteps[flatIndex]?.mutationTools);
|
|
3589
2891
|
const currentPath = resolveCurrentPath(event.toolName, event.args);
|
|
@@ -3619,6 +2921,7 @@ async function runSubagent(
|
|
|
3619
2921
|
tokens: step.tokens?.total,
|
|
3620
2922
|
toolCount: step.toolCount,
|
|
3621
2923
|
currentTool: step.currentTool,
|
|
2924
|
+
toolCallId: event.toolCallId,
|
|
3622
2925
|
currentToolDurationMs: 0,
|
|
3623
2926
|
currentPath: step.currentPath,
|
|
3624
2927
|
})));
|
|
@@ -3728,6 +3031,7 @@ async function runSubagent(
|
|
|
3728
3031
|
config: controlConfig,
|
|
3729
3032
|
startedAt: step.startedAt ?? overallStartTime,
|
|
3730
3033
|
lastActivityAt,
|
|
3034
|
+
turnCount: step.turnCount,
|
|
3731
3035
|
currentTool: step.currentTool,
|
|
3732
3036
|
thinking: step.thinking,
|
|
3733
3037
|
now,
|
|
@@ -3892,23 +3196,6 @@ async function runSubagent(
|
|
|
3892
3196
|
pendingStepSteers.push(request);
|
|
3893
3197
|
}
|
|
3894
3198
|
},
|
|
3895
|
-
onSteerCapability: (capability) => {
|
|
3896
|
-
steeringCapabilities.set(capability.index, capability);
|
|
3897
|
-
if (!capability.supported) {
|
|
3898
|
-
const now = Date.now();
|
|
3899
|
-
const lifecycle = steeringStatus(statusPayload);
|
|
3900
|
-
for (const request of lifecycle.recent) {
|
|
3901
|
-
if (!request.targets.some((target) => target.index === capability.index && (target.state === "routed" || target.state === "scheduled"))) continue;
|
|
3902
|
-
markSteeringAttention(capability.index);
|
|
3903
|
-
updateSteeringLifecycleTarget(request.id, capability.index, "failed", now, { reason: "child Pi session does not support steering" });
|
|
3904
|
-
emitSteeringEvent("subagent.steer.failed", { type: "steer", id: request.id, ts: request.requestedAt, message: "child Pi session does not support steering" }, capability.index, { reason: "child Pi session does not support steering" });
|
|
3905
|
-
emitTerminalSteeringNotice(request.id, `Steering failed for run ${id}: child ${capability.index} does not support steering.`);
|
|
3906
|
-
}
|
|
3907
|
-
statusPayload.lastUpdate = now;
|
|
3908
|
-
writeStatusPayload();
|
|
3909
|
-
}
|
|
3910
|
-
},
|
|
3911
|
-
onSteerAck: consumeSteerAck,
|
|
3912
3199
|
});
|
|
3913
3200
|
if (config.deadlineAt !== undefined) {
|
|
3914
3201
|
const remainingMs = Math.max(0, config.deadlineAt - Date.now());
|
|
@@ -3965,7 +3252,7 @@ async function runSubagent(
|
|
|
3965
3252
|
: [undefined]
|
|
3966
3253
|
: model ? [model] : [undefined];
|
|
3967
3254
|
for (const candidate of candidates) {
|
|
3968
|
-
assertThinkingWithinCeiling({ model: candidate, configThinking, ceiling: step.parallel.thinkingCeiling
|
|
3255
|
+
assertThinkingWithinCeiling({ model: candidate, configThinking, ceiling: step.parallel.thinkingCeiling, agent: step.parallel.agent, runId: id });
|
|
3969
3256
|
}
|
|
3970
3257
|
}
|
|
3971
3258
|
if (materialized.collectedOnEmpty) await validateDynamicCollection(step.collect.outputSchema, materialized.collectedOnEmpty);
|
|
@@ -4235,11 +3522,9 @@ async function runSubagent(
|
|
|
4235
3522
|
artifactsDir, artifactConfig, id,
|
|
4236
3523
|
flatIndex: fi, flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
4237
3524
|
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
4238
|
-
steerInboxDir: stepSteerInboxDir(asyncDir, fi),
|
|
4239
|
-
steerCapabilityPath: steerCapabilityPath(asyncDir, fi),
|
|
4240
|
-
steerAckDir: steerAcksDir(asyncDir, fi),
|
|
4241
3525
|
piPackageRoot: config.piPackageRoot,
|
|
4242
|
-
|
|
3526
|
+
childSessions,
|
|
3527
|
+
inheritedChildRuntime: config.inheritedChildRuntime,
|
|
4243
3528
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
4244
3529
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
4245
3530
|
nestedRoute: config.nestedRoute,
|
|
@@ -4248,6 +3533,7 @@ async function runSubagent(
|
|
|
4248
3533
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4249
3534
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4250
3535
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
3536
|
+
registerSteer: (steer) => registerStepSteer(fi, steer),
|
|
4251
3537
|
timeoutSignal: timeoutAbortController.signal,
|
|
4252
3538
|
stopSignal: stopAbortController.signal,
|
|
4253
3539
|
trackedMutationEvidenceForCompletionGuard: false,
|
|
@@ -4256,7 +3542,6 @@ async function runSubagent(
|
|
|
4256
3542
|
toolTimeoutMs: task.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4257
3543
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4258
3544
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
4259
|
-
onWriterProcess,
|
|
4260
3545
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4261
3546
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4262
3547
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
@@ -4337,7 +3622,6 @@ async function runSubagent(
|
|
|
4337
3622
|
output: pr.output,
|
|
4338
3623
|
outputState: pr.outputState,
|
|
4339
3624
|
error: pr.error,
|
|
4340
|
-
protocolError: pr.protocolError,
|
|
4341
3625
|
success: pr.stopped !== true && pr.interrupted !== true && pr.exitCode === 0,
|
|
4342
3626
|
exitCode: pr.interrupted === true ? 0 : pr.exitCode,
|
|
4343
3627
|
skipped: pr.skipped,
|
|
@@ -4349,6 +3633,7 @@ async function runSubagent(
|
|
|
4349
3633
|
sessionFile: pr.sessionFile,
|
|
4350
3634
|
intercomTarget: pr.intercomTarget,
|
|
4351
3635
|
model: pr.model,
|
|
3636
|
+
thinking: pr.thinking,
|
|
4352
3637
|
attemptedModels: pr.attemptedModels,
|
|
4353
3638
|
modelAttempts: pr.modelAttempts,
|
|
4354
3639
|
contextOverflow: pr.contextOverflow,
|
|
@@ -4496,6 +3781,7 @@ async function runSubagent(
|
|
|
4496
3781
|
labels: group.parallel.map((task) => task.lane?.key ?? config.workflowKey ?? task.outputName ?? task.label),
|
|
4497
3782
|
tasks: group.parallel.map((task) => task.task),
|
|
4498
3783
|
provider: config.worktreeProvider,
|
|
3784
|
+
baseRef: config.baseRef,
|
|
4499
3785
|
branchPrefix: config.worktreeBranchPrefix,
|
|
4500
3786
|
setupHook: config.worktreeSetupHook
|
|
4501
3787
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
@@ -4633,11 +3919,9 @@ async function runSubagent(
|
|
|
4633
3919
|
artifactsDir, artifactConfig, id,
|
|
4634
3920
|
flatIndex: fi, flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
4635
3921
|
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
4636
|
-
steerInboxDir: stepSteerInboxDir(asyncDir, fi),
|
|
4637
|
-
steerCapabilityPath: steerCapabilityPath(asyncDir, fi),
|
|
4638
|
-
steerAckDir: steerAcksDir(asyncDir, fi),
|
|
4639
3922
|
piPackageRoot: config.piPackageRoot,
|
|
4640
|
-
|
|
3923
|
+
childSessions,
|
|
3924
|
+
inheritedChildRuntime: config.inheritedChildRuntime,
|
|
4641
3925
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
4642
3926
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
4643
3927
|
nestedRoute: config.nestedRoute,
|
|
@@ -4646,6 +3930,7 @@ async function runSubagent(
|
|
|
4646
3930
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4647
3931
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4648
3932
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
3933
|
+
registerSteer: (steer) => registerStepSteer(fi, steer),
|
|
4649
3934
|
timeoutSignal: timeoutAbortController.signal,
|
|
4650
3935
|
stopSignal: stopAbortController.signal,
|
|
4651
3936
|
trackedMutationEvidenceForCompletionGuard: Boolean(worktreeSetup),
|
|
@@ -4654,7 +3939,6 @@ async function runSubagent(
|
|
|
4654
3939
|
toolTimeoutMs: taskForRun.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4655
3940
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4656
3941
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
4657
|
-
onWriterProcess,
|
|
4658
3942
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4659
3943
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4660
3944
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
@@ -4782,7 +4066,6 @@ async function runSubagent(
|
|
|
4782
4066
|
output: pr.output,
|
|
4783
4067
|
outputState: pr.outputState,
|
|
4784
4068
|
error: pr.error,
|
|
4785
|
-
protocolError: pr.protocolError,
|
|
4786
4069
|
success: pr.stopped !== true && pr.interrupted !== true && pr.exitCode === 0,
|
|
4787
4070
|
exitCode: pr.interrupted === true ? 0 : pr.exitCode,
|
|
4788
4071
|
skipped: pr.skipped,
|
|
@@ -4794,6 +4077,7 @@ async function runSubagent(
|
|
|
4794
4077
|
sessionFile: pr.sessionFile,
|
|
4795
4078
|
intercomTarget: pr.intercomTarget,
|
|
4796
4079
|
model: pr.model,
|
|
4080
|
+
thinking: pr.thinking,
|
|
4797
4081
|
attemptedModels: pr.attemptedModels,
|
|
4798
4082
|
modelAttempts: pr.modelAttempts,
|
|
4799
4083
|
contextOverflow: pr.contextOverflow,
|
|
@@ -4932,6 +4216,7 @@ async function runSubagent(
|
|
|
4932
4216
|
labels: [seqStep.lane?.key ?? config.workflowKey ?? seqStep.outputName ?? seqStep.label],
|
|
4933
4217
|
tasks: [seqStep.task],
|
|
4934
4218
|
provider: config.worktreeProvider,
|
|
4219
|
+
baseRef: config.baseRef,
|
|
4935
4220
|
branchPrefix: config.worktreeBranchPrefix,
|
|
4936
4221
|
setupHook: config.worktreeSetupHook
|
|
4937
4222
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
@@ -4999,11 +4284,9 @@ async function runSubagent(
|
|
|
4999
4284
|
artifactsDir, artifactConfig, id,
|
|
5000
4285
|
flatIndex, flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
5001
4286
|
outputFile: path.join(asyncDir, `output-${flatIndex}.log`),
|
|
5002
|
-
steerInboxDir: stepSteerInboxDir(asyncDir, flatIndex),
|
|
5003
|
-
steerCapabilityPath: steerCapabilityPath(asyncDir, flatIndex),
|
|
5004
|
-
steerAckDir: steerAcksDir(asyncDir, flatIndex),
|
|
5005
4287
|
piPackageRoot: config.piPackageRoot,
|
|
5006
|
-
|
|
4288
|
+
childSessions,
|
|
4289
|
+
inheritedChildRuntime: config.inheritedChildRuntime,
|
|
5007
4290
|
childIntercomTarget: config.childIntercomTargets?.[flatIndex],
|
|
5008
4291
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
5009
4292
|
nestedRoute: config.nestedRoute,
|
|
@@ -5012,6 +4295,7 @@ async function runSubagent(
|
|
|
5012
4295
|
registerInterrupt: (interrupt) => registerStepInterrupt(flatIndex, interrupt),
|
|
5013
4296
|
registerTimeout: (interrupt) => registerStepTimeout(flatIndex, interrupt),
|
|
5014
4297
|
registerStop: (stop) => registerStepStop(flatIndex, stop),
|
|
4298
|
+
registerSteer: (steer) => registerStepSteer(flatIndex, steer),
|
|
5015
4299
|
timeoutSignal: timeoutAbortController.signal,
|
|
5016
4300
|
stopSignal: stopAbortController.signal,
|
|
5017
4301
|
timeoutMessage,
|
|
@@ -5019,7 +4303,6 @@ async function runSubagent(
|
|
|
5019
4303
|
toolTimeoutMs: seqStep.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
5020
4304
|
onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
5021
4305
|
onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
|
|
5022
|
-
onWriterProcess,
|
|
5023
4306
|
onExternalProcess: (process) => updateExternalProcess(flatIndex, process),
|
|
5024
4307
|
onExternalJob: (externalJob) => updateExternalJob(flatIndex, externalJob),
|
|
5025
4308
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(flatIndex),
|
|
@@ -5047,12 +4330,12 @@ async function runSubagent(
|
|
|
5047
4330
|
output: stopped || childStopped ? stopMessage : timedOut ? singleResult.output || (timeoutMessage ?? "Subagent timed out.") : singleResult.output,
|
|
5048
4331
|
outputState: singleResult.outputState,
|
|
5049
4332
|
error: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error,
|
|
5050
|
-
protocolError: singleResult.protocolError,
|
|
5051
4333
|
success: !stopped && !childStopped && !timedOut && singleResult.interrupted !== true && singleResult.exitCode === 0,
|
|
5052
4334
|
exitCode: stopped || childStopped ? 1 : timedOut ? 1 : singleResult.interrupted === true ? 0 : singleResult.exitCode,
|
|
5053
4335
|
sessionFile: singleResult.sessionFile,
|
|
5054
4336
|
intercomTarget: singleResult.intercomTarget,
|
|
5055
4337
|
model: singleResult.model,
|
|
4338
|
+
thinking: singleResult.thinking,
|
|
5056
4339
|
attemptedModels: singleResult.attemptedModels,
|
|
5057
4340
|
modelAttempts: singleResult.modelAttempts,
|
|
5058
4341
|
contextOverflow: singleResult.contextOverflow,
|
|
@@ -5314,7 +4597,7 @@ async function runSubagent(
|
|
|
5314
4597
|
clearTimeout(timeoutTimer);
|
|
5315
4598
|
timeoutTimer = undefined;
|
|
5316
4599
|
}
|
|
5317
|
-
if (!timedOut && !stopped && !interrupted && config.timeoutMs !== undefined && results.some((result) => result.timedOut === true && result.error
|
|
4600
|
+
if (!timedOut && !stopped && !interrupted && config.timeoutMs !== undefined && timeoutMessage !== undefined && results.some((result) => result.timedOut === true && result.error?.startsWith(timeoutMessage))) {
|
|
5318
4601
|
timedOut = true;
|
|
5319
4602
|
}
|
|
5320
4603
|
const signalTerminated = !stopped && !timedOut && !interrupted && results.some((result) => result.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
@@ -5404,7 +4687,6 @@ async function runSubagent(
|
|
|
5404
4687
|
output: r.output,
|
|
5405
4688
|
outputState: r.outputState,
|
|
5406
4689
|
error: r.error,
|
|
5407
|
-
protocolError: r.protocolError,
|
|
5408
4690
|
success: r.success,
|
|
5409
4691
|
skipped: r.skipped || undefined,
|
|
5410
4692
|
interrupted: r.interrupted || undefined,
|
|
@@ -5416,6 +4698,7 @@ async function runSubagent(
|
|
|
5416
4698
|
sessionFile: r.sessionFile,
|
|
5417
4699
|
intercomTarget: r.intercomTarget,
|
|
5418
4700
|
model: r.model,
|
|
4701
|
+
thinking: r.thinking,
|
|
5419
4702
|
attemptedModels: r.attemptedModels,
|
|
5420
4703
|
modelAttempts: r.modelAttempts,
|
|
5421
4704
|
contextOverflow: r.contextOverflow,
|
|
@@ -5485,7 +4768,7 @@ async function runSubagent(
|
|
|
5485
4768
|
statusPayload.lastUpdate = Date.now();
|
|
5486
4769
|
}
|
|
5487
4770
|
writeStatusPayload();
|
|
5488
|
-
orcaProgressTab?.finish(statusPayload.state === "complete" ? "completed" : statusPayload.state === "stopped" ? "stopped" : "failed", effectiveSessionFile);
|
|
4771
|
+
await orcaProgressTab?.finish(statusPayload.state === "complete" ? "completed" : statusPayload.state === "stopped" ? "stopped" : "failed", effectiveSessionFile);
|
|
5489
4772
|
appendJsonl(
|
|
5490
4773
|
eventsPath,
|
|
5491
4774
|
JSON.stringify({
|
|
@@ -5520,14 +4803,21 @@ async function runSubagent(
|
|
|
5520
4803
|
}), (filePath, content) => runPersistence.write(filePath, { content }, (_path, payload) => {
|
|
5521
4804
|
fs.writeFileSync(_path, (payload as { content: string }).content, "utf-8");
|
|
5522
4805
|
}));
|
|
5523
|
-
|
|
5524
|
-
|
|
4806
|
+
// The run is committed: release the child sessions first, then wait for the
|
|
4807
|
+
// storage-capacity retries that may still hold the final status or result.
|
|
4808
|
+
await childSessions.dispose().catch((error: unknown) => console.error("Failed to dispose runner child sessions:", error));
|
|
4809
|
+
while (runPersistence.pendingCount() + indexPersistence.pendingCount() > 0) {
|
|
4810
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
4811
|
+
}
|
|
4812
|
+
runPersistence.dispose();
|
|
4813
|
+
indexPersistence.dispose();
|
|
5525
4814
|
if (config.runnerProcessInstanceId) {
|
|
4815
|
+
// Children run inside this process, so no step has writer processes to prove terminal.
|
|
5526
4816
|
const writers: Record<string, PiWriterProcessInstanceExitV1[]> = {};
|
|
5527
4817
|
const expectedWriters: Record<string, number> = {};
|
|
5528
|
-
for (const
|
|
5529
|
-
writers[String(index)] =
|
|
5530
|
-
expectedWriters[String(index)] =
|
|
4818
|
+
for (const index of results.keys()) {
|
|
4819
|
+
writers[String(index)] = [];
|
|
4820
|
+
expectedWriters[String(index)] = 0;
|
|
5531
4821
|
}
|
|
5532
4822
|
const candidate: ProcessTerminalCandidate = {
|
|
5533
4823
|
version: 1,
|
|
@@ -5609,7 +4899,16 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
5609
4899
|
}
|
|
5610
4900
|
}
|
|
5611
4901
|
}
|
|
5612
|
-
|
|
4902
|
+
const childSessions = await loadRunnerChildSessionFactory(config);
|
|
4903
|
+
try {
|
|
4904
|
+
await runSubagent(config, childSessions);
|
|
4905
|
+
} finally {
|
|
4906
|
+
try {
|
|
4907
|
+
await childSessions.dispose();
|
|
4908
|
+
} catch (error) {
|
|
4909
|
+
console.error("Failed to dispose runner child sessions:", error);
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
5613
4912
|
} catch (error) {
|
|
5614
4913
|
if (!startupCommitted) {
|
|
5615
4914
|
try {
|
|
@@ -5638,10 +4937,16 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
5638
4937
|
}
|
|
5639
4938
|
|
|
5640
4939
|
function startConfiguredSubagent(config: SubagentRunConfig): void {
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
4940
|
+
// Child sessions and the extensions loaded into them may leave handles
|
|
4941
|
+
// behind even after shutdown; the run is fully persisted by now, so exit
|
|
4942
|
+
// explicitly instead of waiting for the event loop to drain.
|
|
4943
|
+
runConfiguredSubagent(config).then(
|
|
4944
|
+
() => process.exit(0),
|
|
4945
|
+
(runErr) => {
|
|
4946
|
+
console.error("Subagent runner error:", runErr);
|
|
4947
|
+
process.exit(1);
|
|
4948
|
+
},
|
|
4949
|
+
);
|
|
5645
4950
|
}
|
|
5646
4951
|
|
|
5647
4952
|
const configArg = process.argv[2];
|