pi-subagents 0.64.0 → 0.65.1
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 +65 -0
- package/README.md +2 -2
- package/agents/reviewer.md +1 -1
- package/agents/scout.md +1 -1
- package/docs/agents.md +19 -17
- package/docs/configuration.md +23 -15
- package/docs/extension-api.md +18 -6
- package/docs/missions.md +2 -0
- package/docs/observability.md +10 -12
- package/docs/tool-reference.md +15 -8
- package/docs/watchdog.md +1 -1
- package/docs/workflows.md +13 -3
- package/package.json +5 -4
- package/runner-server-preload.mjs +13 -0
- package/skills/pi-subagents/SKILL.md +2 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +7 -3
- package/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
- 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 +7 -4
- package/src/extension/config.ts +4 -2
- package/src/extension/doctor.ts +2 -10
- package/src/extension/fanout-child.ts +9 -11
- package/src/extension/index.ts +58 -7
- package/src/extension/public-execution.ts +14 -0
- package/src/extension/rpc.ts +3 -2
- package/src/extension/schemas.ts +3 -2
- package/src/extension/tool-description.ts +13 -8
- package/src/integrations/pi-web-session-liveness.ts +73 -0
- package/src/intercom/native-supervisor-channel.ts +159 -95
- package/src/intercom/supervisor-ui.ts +244 -0
- package/src/missions/workflow-state.ts +37 -16
- package/src/runs/background/async-execution.ts +51 -25
- package/src/runs/background/async-job-tracker.ts +11 -0
- package/src/runs/background/async-resume.ts +14 -3
- package/src/runs/background/async-retention.ts +9 -0
- package/src/runs/background/control-channel.ts +2 -204
- package/src/runs/background/notify.ts +2 -0
- package/src/runs/background/process-terminal.ts +1 -1
- package/src/runs/background/retained-nested-route-tracker.ts +96 -0
- package/src/runs/background/run-child-session.ts +614 -0
- package/src/runs/background/run-status.ts +0 -1
- package/src/runs/background/runner-aliases.ts +152 -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 -901
- package/src/runs/foreground/async-steering-action.ts +1 -17
- package/src/runs/foreground/execution.ts +204 -378
- package/src/runs/foreground/foreground-control.ts +4 -0
- package/src/runs/foreground/foreground-history.ts +3 -1
- package/src/runs/foreground/prompt-audit.ts +9 -5
- package/src/runs/foreground/subagent-executor.ts +161 -91
- 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 +14 -1
- 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 +337 -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/llm-intent-arbiter.ts +20 -11
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/src/runs/shared/model-exclusions.ts +84 -15
- package/src/runs/shared/model-fallback.ts +78 -6
- package/src/runs/shared/nested-events.ts +32 -48
- 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 +1 -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 +2 -0
- 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 +31 -9
- package/src/runs/shared/worktree.ts +192 -40
- package/src/shared/child-session-name.ts +1 -1
- package/src/shared/jsonl-writer.ts +11 -0
- package/src/shared/model-response-aliases.ts +13 -0
- package/src/shared/thinking-ceiling.ts +0 -6
- package/src/shared/types.ts +63 -28
- package/src/shared/utils.ts +6 -4
- package/src/shared/watch-strategy.ts +2 -0
- package/src/slash/slash-commands.ts +0 -6
- package/src/tui/fleet-status.ts +1 -1
- package/src/tui/fleet.ts +0 -1
- package/src/tui/render.ts +254 -41
- package/src/watchdog/child-status.ts +0 -1
- package/src/watchdog/register-child.ts +12 -12
- package/src/workflows/scripted-workflow.ts +80 -7
- package/src/workflows/workflow-checklist.ts +4 -3
- package/src/runs/shared/child-protocol.ts +0 -415
- package/src/runs/shared/pi-args.ts +0 -1059
- package/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/src/shared/post-exit-stdio-guard.ts +0 -85
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Core execution logic for running subagents
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import { existsSync } from "node:fs";
|
|
5
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
7
6
|
import * as path from "node:path";
|
|
8
7
|
import type { Message } from "@earendil-works/pi-ai";
|
|
9
8
|
import { discoverAgents, formatUnknownAgentError, unknownAgentDiagnosticContext, type AgentConfig } from "../../agents/agents.ts";
|
|
@@ -31,7 +30,6 @@ import {
|
|
|
31
30
|
type AcceptanceLedger,
|
|
32
31
|
type ResolvedAcceptanceConfig,
|
|
33
32
|
truncateOutput,
|
|
34
|
-
getSubagentDepthEnv,
|
|
35
33
|
} from "../../shared/types.ts";
|
|
36
34
|
import {
|
|
37
35
|
DEFAULT_CONTROL_CONFIG,
|
|
@@ -61,21 +59,18 @@ import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMuta
|
|
|
61
59
|
import { planCompletionEvidence } from "../shared/completion-evidence.ts";
|
|
62
60
|
import { planAbortRecovery } from "../shared/abort-recovery.ts";
|
|
63
61
|
import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.ts";
|
|
64
|
-
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
65
62
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
66
63
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
67
64
|
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
68
|
-
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
69
65
|
import { resolvePermissionRules } from "../shared/permissions.ts";
|
|
70
|
-
import { applyThinkingSuffix,
|
|
66
|
+
import { applyThinkingSuffix, deriveForkPromptCacheKey } from "../shared/child-tool-plan.ts";
|
|
71
67
|
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
72
|
-
import {
|
|
73
|
-
import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.ts";
|
|
68
|
+
import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
74
69
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
75
|
-
import { assertThinkingWithinCeiling,
|
|
76
|
-
import {
|
|
77
|
-
import { formatMidToolExitError,
|
|
78
|
-
import {
|
|
70
|
+
import { assertThinkingWithinCeiling, intersectThinkingCeilings } from "../../shared/thinking-ceiling.ts";
|
|
71
|
+
import { MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR, MISSING_STRUCTURED_OUTPUT_CALL_ERROR } from "../shared/structured-output.ts";
|
|
72
|
+
import { formatMidToolExitError, isOrdinaryToolForMidToolExit } from "../shared/process-signal.ts";
|
|
73
|
+
import { formatChildToolDiagnostic } from "../shared/tool-availability.ts";
|
|
79
74
|
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
80
75
|
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, hasSingleOutputChangedSinceSnapshot, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
81
76
|
import {
|
|
@@ -86,14 +81,6 @@ import {
|
|
|
86
81
|
isRetryableModelFailureAttempt,
|
|
87
82
|
recordRetryableModelFailure,
|
|
88
83
|
} from "../shared/model-fallback.ts";
|
|
89
|
-
import {
|
|
90
|
-
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
91
|
-
formatSubagentExtensionConflictError,
|
|
92
|
-
formatSubagentStartupRetryExhaustedError,
|
|
93
|
-
formatSubagentStartupRetryNote,
|
|
94
|
-
isRetryableSubagentStartupFailure,
|
|
95
|
-
waitForSubagentStartupRetry,
|
|
96
|
-
} from "../shared/subagent-startup-retry.ts";
|
|
97
84
|
import {
|
|
98
85
|
createMutatingFailureState,
|
|
99
86
|
didMutatingToolFail,
|
|
@@ -112,7 +99,7 @@ import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.t
|
|
|
112
99
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
113
100
|
import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
114
101
|
import { consumeWorkflowChildPermit } from "../../shared/workflow-child-permit.ts";
|
|
115
|
-
import {
|
|
102
|
+
import { projectChildLifecycle, type ChildLifecycleAction, type ChildLifecycleState } from "../shared/child-lifecycle.ts";
|
|
116
103
|
import {
|
|
117
104
|
acceptChildWatchdogEvent,
|
|
118
105
|
applyChildWatchdogMessage,
|
|
@@ -120,7 +107,10 @@ import {
|
|
|
120
107
|
isChildWatchdogStatusEvent,
|
|
121
108
|
resolveChildWatchdogConfig,
|
|
122
109
|
type ChildWatchdogStateSnapshot,
|
|
110
|
+
type ChildWatchdogStatusEvent,
|
|
123
111
|
} from "../../watchdog/child-status.ts";
|
|
112
|
+
import { buildInProcessChildLaunch } from "../shared/child-launch.ts";
|
|
113
|
+
import { childSessionFactory, projectChildSessionEventForJson, type ChildSession, type ChildSessionEvent } from "../shared/child-session.ts";
|
|
124
114
|
|
|
125
115
|
const artifactOutputByResult = new WeakMap<SingleResult, string>();
|
|
126
116
|
const acceptanceOutputByResult = new WeakMap<SingleResult, string>();
|
|
@@ -288,8 +278,8 @@ function snapshotResult(result: SingleResult, progress: AgentProgress): SingleRe
|
|
|
288
278
|
/**
|
|
289
279
|
* Streaming variant of snapshotResult for `onUpdate` progress events: it drops the
|
|
290
280
|
* unbounded `messages` transcript in favour of compact tool-call summaries so a
|
|
291
|
-
* single `tool_execution_update`
|
|
292
|
-
*
|
|
281
|
+
* single `tool_execution_update` event stays small; the parent records every
|
|
282
|
+
* one in its transcript and `events.jsonl`. Non-streaming consumers such as
|
|
293
283
|
* foreground detach receipts and terminal consumers use snapshotResult directly.
|
|
294
284
|
*/
|
|
295
285
|
function snapshotStreamResult(result: SingleResult, progress: AgentProgress): SingleResult {
|
|
@@ -351,6 +341,9 @@ function structuredDelegationProgressChanged(
|
|
|
351
341
|
const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
|
|
352
342
|
type AbortRecoverySingleResult = SingleResult & { [AFTER_COMPACTION_SETTLEMENT]?: true };
|
|
353
343
|
|
|
344
|
+
const STOPPED_BEFORE_COMPLETION_ERROR = "Subagent stopped before completion.";
|
|
345
|
+
|
|
346
|
+
|
|
354
347
|
async function runSingleAttempt(
|
|
355
348
|
runtimeCwd: string,
|
|
356
349
|
agent: AgentConfig,
|
|
@@ -369,7 +362,6 @@ async function runSingleAttempt(
|
|
|
369
362
|
attemptNotes: string[];
|
|
370
363
|
outputSnapshot?: SingleOutputSnapshot;
|
|
371
364
|
originalTask?: string;
|
|
372
|
-
taskDelivery?: SubagentTaskDelivery;
|
|
373
365
|
orcaProgressTab?: OrcaProgressTab;
|
|
374
366
|
launchWarnings: { emitted: boolean };
|
|
375
367
|
verifyModel: boolean;
|
|
@@ -380,9 +372,9 @@ async function runSingleAttempt(
|
|
|
380
372
|
assertThinkingWithinCeiling({ model: modelArg, configThinking: effectiveThinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
381
373
|
const expectedModelForVerification = shared.verifyModel ? modelArg : undefined;
|
|
382
374
|
const resolvedThinking = resolveEffectiveThinking(modelArg, effectiveThinking);
|
|
383
|
-
// Display name for the child session: applied inside the child
|
|
384
|
-
//
|
|
385
|
-
//
|
|
375
|
+
// Display name for the child session: applied inside the child through its
|
|
376
|
+
// runtime config and echoed back on the result payload so hosts can label
|
|
377
|
+
// this run without reading the child's session file.
|
|
386
378
|
const childSessionName = deriveChildSessionName({ agent: agent.name, task: shared.originalTask ?? task });
|
|
387
379
|
const watchdogConfig = resolveWatchdogConfig(options.cwd ?? runtimeCwd);
|
|
388
380
|
const childWatchdog = watchdogConfig.ok
|
|
@@ -397,15 +389,12 @@ async function runSingleAttempt(
|
|
|
397
389
|
const permissionAuditPath = permissionRules && options.artifactsDir
|
|
398
390
|
? path.join(options.artifactsDir, "permission-audit", `${options.runId}-${options.index ?? 0}.jsonl`)
|
|
399
391
|
: undefined;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
task,
|
|
403
|
-
taskDelivery: shared.taskDelivery,
|
|
392
|
+
let onWatchdogStatus: ((event: ChildWatchdogStatusEvent) => void) | undefined;
|
|
393
|
+
const launch = buildInProcessChildLaunch({
|
|
404
394
|
sessionEnabled: shared.sessionEnabled,
|
|
405
395
|
sessionDir: options.sessionDir,
|
|
406
396
|
sessionFile: options.sessionFile,
|
|
407
397
|
model: modelArg,
|
|
408
|
-
thinking: effectiveThinking,
|
|
409
398
|
systemPromptMode: agent.systemPromptMode,
|
|
410
399
|
inheritProjectContext: agent.inheritProjectContext,
|
|
411
400
|
inheritGlobalContext: agent.inheritGlobalContext,
|
|
@@ -419,63 +408,40 @@ async function runSingleAttempt(
|
|
|
419
408
|
systemPrompt: shared.systemPrompt,
|
|
420
409
|
mcpDirectTools: agent.mcpDirectTools,
|
|
421
410
|
cwd: options.cwd ?? runtimeCwd,
|
|
422
|
-
promptFileStem: agent.name,
|
|
423
411
|
intercomSessionName: options.intercomSessionName,
|
|
424
412
|
sessionName: childSessionName,
|
|
425
413
|
orchestratorIntercomTarget: options.orchestratorIntercomTarget,
|
|
426
414
|
runId: options.runId,
|
|
427
415
|
childAgentName: agent.name,
|
|
428
416
|
childIndex: options.index ?? 0,
|
|
429
|
-
|
|
430
|
-
parentControlInbox: options.nestedRoute?.controlInbox,
|
|
431
|
-
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
432
|
-
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
417
|
+
nestedRoute: options.nestedRoute,
|
|
433
418
|
runFanoutBudget: options.runFanoutBudget,
|
|
434
419
|
parentSessionId: options.parentSessionId,
|
|
435
420
|
forkCacheKey: options.context === "fork" ? deriveForkPromptCacheKey(options.parentSessionId) : undefined,
|
|
436
|
-
steerInboxDir: options.steerInboxDir,
|
|
437
|
-
steerCapabilityPath: options.steerCapabilityPath,
|
|
438
|
-
steerAckDir: options.steerAckDir,
|
|
439
421
|
structuredOutput: options.structuredOutput,
|
|
440
422
|
fast: options.fast ?? agent.fast,
|
|
441
423
|
modelCandidates: shared.modelCandidates,
|
|
442
424
|
toolBudget: options.toolBudget,
|
|
443
|
-
allowZeroToolBudget: options.allowZeroToolBudget,
|
|
444
425
|
permissionRules,
|
|
445
426
|
permissionAuditPath,
|
|
446
427
|
childWatchdog,
|
|
428
|
+
watchdogStatus: (event) => onWatchdogStatus?.(event),
|
|
447
429
|
waitToolEnabled: options.waitToolEnabled,
|
|
448
430
|
waitToolDefaultTimeoutMs: options.waitToolDefaultTimeoutMs,
|
|
449
431
|
capabilityCeiling: options.capabilityCeiling,
|
|
450
432
|
thinkingCeiling: options.thinkingCeiling,
|
|
451
|
-
|
|
433
|
+
maxSubagentDepth: options.maxSubagentDepth,
|
|
452
434
|
runtimeSnapshotHost: options.runtimeSnapshotHost,
|
|
435
|
+
inherited: options.childRuntime,
|
|
436
|
+
host: "parent",
|
|
453
437
|
});
|
|
438
|
+
const { toolPlan, capabilityAudit, warnings, launchResolvedExtensions, capture } = launch;
|
|
454
439
|
if (!shared.launchWarnings.emitted && warnings.length > 0) {
|
|
455
440
|
for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
|
|
456
441
|
shared.launchWarnings.emitted = true;
|
|
457
442
|
}
|
|
458
443
|
|
|
459
444
|
const effectiveSystemPrompt = shared.systemPrompt;
|
|
460
|
-
const toolPlan = resolvePiLaunchToolPlan({
|
|
461
|
-
tools: agent.tools,
|
|
462
|
-
allowNestedSubagents: agent.allowNestedSubagents,
|
|
463
|
-
extensions: agent.extensions,
|
|
464
|
-
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
465
|
-
mcpDirectTools: agent.mcpDirectTools,
|
|
466
|
-
cwd: options.cwd ?? runtimeCwd,
|
|
467
|
-
requireReadTool: Boolean(shared.resolvedSkillNames?.length),
|
|
468
|
-
structuredOutput: Boolean(options.structuredOutput),
|
|
469
|
-
excludeTools: agent.excludeTools,
|
|
470
|
-
fast: options.fast ?? agent.fast,
|
|
471
|
-
model: modelArg,
|
|
472
|
-
modelCandidates: shared.modelCandidates,
|
|
473
|
-
capabilityCeiling: options.capabilityCeiling,
|
|
474
|
-
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
475
|
-
agentName: agent.name,
|
|
476
|
-
permissionRules,
|
|
477
|
-
runtimeSnapshotHost: options.runtimeSnapshotHost,
|
|
478
|
-
});
|
|
479
445
|
const contractTools = toolPlan.explicitToolAllowlist ? toolPlan.effectiveToolAllowlist : undefined;
|
|
480
446
|
const contractError = validateImplementationToolContract({
|
|
481
447
|
agent: agent.name,
|
|
@@ -488,7 +454,6 @@ async function runSingleAttempt(
|
|
|
488
454
|
completionGuard: agent.completionGuard,
|
|
489
455
|
});
|
|
490
456
|
if (contractError) {
|
|
491
|
-
cleanupTempDir(tempDir);
|
|
492
457
|
return {
|
|
493
458
|
index: options.index ?? 0,
|
|
494
459
|
agent: agent.name,
|
|
@@ -507,7 +472,6 @@ async function runSingleAttempt(
|
|
|
507
472
|
...(toolPlan.capabilityAudit ? { capabilityAudit: toolPlan.capabilityAudit } : {}),
|
|
508
473
|
};
|
|
509
474
|
}
|
|
510
|
-
const launchResolvedExtensions = projectLaunchResolvedChildExtensions(toolPlan);
|
|
511
475
|
const launchContractDigest = launchBindingDigest({
|
|
512
476
|
definitionDigest: agentDefinitionDigest(agent),
|
|
513
477
|
task: shared.originalTask ?? task,
|
|
@@ -554,17 +518,6 @@ async function runSingleAttempt(
|
|
|
554
518
|
...(capabilityAudit ? { capabilityAudit } : {}),
|
|
555
519
|
}, options.context);
|
|
556
520
|
const startTime = Date.now();
|
|
557
|
-
if (options.structuredOutput) {
|
|
558
|
-
const cleanupError = clearStructuredOutputCaptures(options.structuredOutput);
|
|
559
|
-
if (cleanupError) {
|
|
560
|
-
cleanupTempDir(tempDir);
|
|
561
|
-
result.exitCode = 1;
|
|
562
|
-
result.error = cleanupError;
|
|
563
|
-
result.finalOutput = cleanupError;
|
|
564
|
-
result.progressSummary = { toolCount: 0, tokens: 0, durationMs: Date.now() - startTime };
|
|
565
|
-
return result;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
521
|
const controlConfig = options.controlConfig ?? DEFAULT_CONTROL_CONFIG;
|
|
569
522
|
let interruptedByControl = false;
|
|
570
523
|
const allControlEvents: ControlEvent[] = [];
|
|
@@ -599,7 +552,6 @@ async function runSingleAttempt(
|
|
|
599
552
|
result.progress = progress;
|
|
600
553
|
const attemptTimeout = resolveAttemptTimeout(options);
|
|
601
554
|
if (attemptTimeout?.remainingMs === 0) {
|
|
602
|
-
cleanupTempDir(tempDir);
|
|
603
555
|
result.exitCode = 1;
|
|
604
556
|
result.timedOut = true;
|
|
605
557
|
result.error = attemptTimeout.message;
|
|
@@ -613,7 +565,6 @@ async function runSingleAttempt(
|
|
|
613
565
|
};
|
|
614
566
|
return result;
|
|
615
567
|
}
|
|
616
|
-
const spawnEnv = { ...process.env, ...sharedEnv, ...getSubagentDepthEnv(options.maxSubagentDepth) };
|
|
617
568
|
const mutationSnapshot = snapshotTrackedMutations(options.cwd ?? runtimeCwd);
|
|
618
569
|
let observedMutationAttempt = false;
|
|
619
570
|
let structuredOutputToolInvoked = false;
|
|
@@ -621,7 +572,6 @@ async function runSingleAttempt(
|
|
|
621
572
|
let toolAvailabilityError: string | undefined;
|
|
622
573
|
let abortedBySignal = options.signal?.aborted === true;
|
|
623
574
|
|
|
624
|
-
const spawnSpec = getPiSpawnCommand(args);
|
|
625
575
|
if (options.workflowChildPermitLaunch) {
|
|
626
576
|
const permitError = consumeWorkflowChildPermit(options.workflowChildPermitLaunch.permit, {
|
|
627
577
|
workflowRunId: options.workflowChildPermitLaunch.workflowRunId,
|
|
@@ -632,7 +582,6 @@ async function runSingleAttempt(
|
|
|
632
582
|
runner: "pi",
|
|
633
583
|
});
|
|
634
584
|
if (permitError) {
|
|
635
|
-
cleanupTempDir(tempDir);
|
|
636
585
|
result.exitCode = 1;
|
|
637
586
|
result.error = permitError;
|
|
638
587
|
result.finalOutput = permitError;
|
|
@@ -641,16 +590,13 @@ async function runSingleAttempt(
|
|
|
641
590
|
return result;
|
|
642
591
|
}
|
|
643
592
|
}
|
|
593
|
+
const childSessions = options.childSessionFactory ?? childSessionFactory();
|
|
644
594
|
let afterCompactionSettlement = false;
|
|
645
595
|
const exitCode = await new Promise<number>((resolve) => {
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
windowsHide: true,
|
|
651
|
-
});
|
|
652
|
-
const jsonlWriter = createJsonlWriter(shared.jsonlPath, proc.stdout);
|
|
653
|
-
let processClosed = false;
|
|
596
|
+
const jsonlWriter = createJsonlWriter(shared.jsonlPath, { pause() {}, resume() {} });
|
|
597
|
+
let session: ChildSession | undefined;
|
|
598
|
+
let unsubscribe: (() => void) | undefined;
|
|
599
|
+
let sessionSettled = false;
|
|
654
600
|
let lifecycleFinished = false;
|
|
655
601
|
let detached = false;
|
|
656
602
|
let intercomStarted = false;
|
|
@@ -659,26 +605,26 @@ async function runSingleAttempt(
|
|
|
659
605
|
let removeInterruptListener: (() => void) | undefined;
|
|
660
606
|
let activityTimer: NodeJS.Timeout | undefined;
|
|
661
607
|
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
662
|
-
let
|
|
663
|
-
let timeoutHardKillTimer: NodeJS.Timeout | undefined;
|
|
664
|
-
let protocolHardKillTimer: NodeJS.Timeout | undefined;
|
|
608
|
+
let timeoutHardFinishTimer: NodeJS.Timeout | undefined;
|
|
665
609
|
const clearTimeoutTimers = () => {
|
|
666
610
|
if (timeoutTimer) {
|
|
667
611
|
clearTimeout(timeoutTimer);
|
|
668
612
|
timeoutTimer = undefined;
|
|
669
613
|
}
|
|
670
|
-
if (
|
|
671
|
-
clearTimeout(
|
|
672
|
-
|
|
673
|
-
}
|
|
674
|
-
if (timeoutHardKillTimer) {
|
|
675
|
-
clearTimeout(timeoutHardKillTimer);
|
|
676
|
-
timeoutHardKillTimer = undefined;
|
|
614
|
+
if (timeoutHardFinishTimer) {
|
|
615
|
+
clearTimeout(timeoutHardFinishTimer);
|
|
616
|
+
timeoutHardFinishTimer = undefined;
|
|
677
617
|
}
|
|
678
618
|
};
|
|
619
|
+
const abortChild = (): void => {
|
|
620
|
+
if (!session || sessionSettled || lifecycleFinished) return;
|
|
621
|
+
void session.abort().catch(() => {
|
|
622
|
+
// The session settles through its prompt promise; abort failures are not separately actionable.
|
|
623
|
+
});
|
|
624
|
+
};
|
|
679
625
|
|
|
680
626
|
const detachForeground = (reason: string): boolean => {
|
|
681
|
-
if (detached ||
|
|
627
|
+
if (detached || sessionSettled || lifecycleFinished || options.signal?.aborted) return false;
|
|
682
628
|
const receiptProgress = snapshotProgress(progress);
|
|
683
629
|
receiptProgress.status = "detached";
|
|
684
630
|
receiptProgress.durationMs = Date.now() - startTime;
|
|
@@ -713,20 +659,21 @@ async function runSingleAttempt(
|
|
|
713
659
|
}
|
|
714
660
|
if (!accepted) return false;
|
|
715
661
|
detached = true;
|
|
662
|
+
if (session) session.detached = true;
|
|
716
663
|
return true;
|
|
717
664
|
};
|
|
718
665
|
|
|
719
|
-
// If the child emits a terminal assistant stop but never
|
|
720
|
-
//
|
|
666
|
+
// If the child emits a terminal assistant stop but its run never settles
|
|
667
|
+
// (a hook is stuck), abort it after a short grace period and then finish
|
|
668
|
+
// without it.
|
|
721
669
|
const FINAL_STOP_GRACE_MS = 1000;
|
|
722
|
-
const
|
|
723
|
-
let
|
|
724
|
-
let forcedTerminationSignal = false;
|
|
670
|
+
const HARD_FINISH_MS = 3000;
|
|
671
|
+
let forcedTermination = false;
|
|
725
672
|
let cleanTerminalAssistantStopReceived = false;
|
|
726
673
|
let agentSettledReceived = false;
|
|
727
674
|
let compactionStartedReceived = false;
|
|
728
675
|
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
729
|
-
let
|
|
676
|
+
let finalHardFinishTimer: NodeJS.Timeout | undefined;
|
|
730
677
|
let watchdogTailTimer: NodeJS.Timeout | undefined;
|
|
731
678
|
let childWatchdogState: ChildWatchdogStateSnapshot | undefined;
|
|
732
679
|
const updateChildWatchdogState = (snapshot: ChildWatchdogStateSnapshot): void => {
|
|
@@ -745,9 +692,9 @@ async function runSingleAttempt(
|
|
|
745
692
|
clearTimeout(finalDrainTimer);
|
|
746
693
|
finalDrainTimer = undefined;
|
|
747
694
|
}
|
|
748
|
-
if (
|
|
749
|
-
clearTimeout(
|
|
750
|
-
|
|
695
|
+
if (finalHardFinishTimer) {
|
|
696
|
+
clearTimeout(finalHardFinishTimer);
|
|
697
|
+
finalHardFinishTimer = undefined;
|
|
751
698
|
}
|
|
752
699
|
};
|
|
753
700
|
const startFinalDrain = () => {
|
|
@@ -755,25 +702,24 @@ async function runSingleAttempt(
|
|
|
755
702
|
armWatchdogTail();
|
|
756
703
|
return;
|
|
757
704
|
}
|
|
758
|
-
if (
|
|
705
|
+
if (sessionSettled || finalDrainTimer || lifecycleFinished) return;
|
|
759
706
|
finalDrainTimer = setTimeout(() => {
|
|
760
|
-
if (lifecycleFinished ||
|
|
761
|
-
|
|
762
|
-
if (!termSent) return;
|
|
763
|
-
forcedTerminationSignal = true;
|
|
707
|
+
if (lifecycleFinished || sessionSettled) return;
|
|
708
|
+
forcedTermination = true;
|
|
764
709
|
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived && !assistantError) {
|
|
765
|
-
result.error = result.error ?? `Subagent
|
|
710
|
+
result.error = result.error ?? `Subagent session did not settle within ${FINAL_STOP_GRACE_MS}ms after its terminal event. Aborting it.`;
|
|
766
711
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
712
|
+
abortChild();
|
|
713
|
+
finalHardFinishTimer = setTimeout(() => {
|
|
714
|
+
if (lifecycleFinished || sessionSettled) return;
|
|
715
|
+
settle(undefined, true);
|
|
716
|
+
}, HARD_FINISH_MS);
|
|
717
|
+
finalHardFinishTimer.unref?.();
|
|
772
718
|
}, FINAL_STOP_GRACE_MS);
|
|
773
719
|
finalDrainTimer.unref?.();
|
|
774
720
|
};
|
|
775
721
|
function armWatchdogTail(): void {
|
|
776
|
-
if ((!cleanTerminalAssistantStopReceived && !agentSettledReceived) || watchdogTailTimer || lifecycleFinished ||
|
|
722
|
+
if ((!cleanTerminalAssistantStopReceived && !agentSettledReceived) || watchdogTailTimer || lifecycleFinished || sessionSettled) return;
|
|
777
723
|
watchdogTailTimer = setTimeout(() => {
|
|
778
724
|
watchdogTailTimer = undefined;
|
|
779
725
|
updateChildWatchdogState({
|
|
@@ -799,7 +745,7 @@ async function runSingleAttempt(
|
|
|
799
745
|
const childLifecycleState: ChildLifecycleState = { compactionRetryActive: false };
|
|
800
746
|
|
|
801
747
|
const unsubscribeIntercomDetach = options.intercomEvents?.on?.(INTERCOM_DETACH_REQUEST_EVENT, (payload) => {
|
|
802
|
-
if (!options.allowIntercomDetach ||
|
|
748
|
+
if (!options.allowIntercomDetach || sessionSettled) return;
|
|
803
749
|
if (!payload || typeof payload !== "object") return;
|
|
804
750
|
const event = payload as { requestId?: unknown; runId?: unknown; agent?: unknown; childIndex?: unknown };
|
|
805
751
|
const requestId = event.requestId;
|
|
@@ -819,13 +765,8 @@ async function runSingleAttempt(
|
|
|
819
765
|
lifecycleFinished = true;
|
|
820
766
|
clearFinalDrainTimers();
|
|
821
767
|
clearWatchdogTailTimer();
|
|
822
|
-
clearStdioGuard();
|
|
823
768
|
clearTimeoutTimers();
|
|
824
769
|
clearAllToolTimeouts();
|
|
825
|
-
if (protocolHardKillTimer) {
|
|
826
|
-
clearTimeout(protocolHardKillTimer);
|
|
827
|
-
protocolHardKillTimer = undefined;
|
|
828
|
-
}
|
|
829
770
|
if (activityTimer) {
|
|
830
771
|
clearInterval(activityTimer);
|
|
831
772
|
activityTimer = undefined;
|
|
@@ -833,7 +774,13 @@ async function runSingleAttempt(
|
|
|
833
774
|
unsubscribeIntercomDetach?.();
|
|
834
775
|
removeAbortListener?.();
|
|
835
776
|
removeInterruptListener?.();
|
|
836
|
-
|
|
777
|
+
unsubscribe?.();
|
|
778
|
+
onWatchdogStatus = undefined;
|
|
779
|
+
void jsonlWriter.close().catch(() => {
|
|
780
|
+
// JSONL artifact flush is best effort.
|
|
781
|
+
});
|
|
782
|
+
// Report the run only after the child's extensions have shut down.
|
|
783
|
+
void Promise.resolve().then(() => session?.dispose()).catch(() => undefined).then(() => resolve(code));
|
|
837
784
|
};
|
|
838
785
|
|
|
839
786
|
const drainPendingControlEvents = (): ControlEvent[] | undefined => {
|
|
@@ -996,7 +943,7 @@ async function runSingleAttempt(
|
|
|
996
943
|
|
|
997
944
|
let lastStructuredDelegationProgressState: StructuredDelegationProgressState | undefined;
|
|
998
945
|
const emitUpdateSnapshot = (text: string) => {
|
|
999
|
-
if (!options.onUpdate ||
|
|
946
|
+
if (!options.onUpdate || sessionSettled) return;
|
|
1000
947
|
const progressSnapshot = snapshotProgress(progress);
|
|
1001
948
|
const resultSnapshot = snapshotStreamResult(result, progressSnapshot);
|
|
1002
949
|
const controlEvents = drainPendingControlEvents();
|
|
@@ -1012,7 +959,7 @@ async function runSingleAttempt(
|
|
|
1012
959
|
};
|
|
1013
960
|
|
|
1014
961
|
const fireUpdate = () => {
|
|
1015
|
-
if (!options.onUpdate ||
|
|
962
|
+
if (!options.onUpdate || sessionSettled) return;
|
|
1016
963
|
progress.durationMs = Date.now() - startTime;
|
|
1017
964
|
if (options.suppressUnchangedDelegationUpdates) {
|
|
1018
965
|
if (lastStructuredDelegationProgressState && !structuredDelegationProgressChanged(lastStructuredDelegationProgressState, progress, result)) return;
|
|
@@ -1022,20 +969,9 @@ async function runSingleAttempt(
|
|
|
1022
969
|
emitUpdateSnapshot(output || "(running...)");
|
|
1023
970
|
};
|
|
1024
971
|
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
jsonlWriter.writeLine(line);
|
|
1029
|
-
let evt: { type?: string; message?: Message; toolName?: string; toolCallId?: string; args?: unknown; willRetry?: unknown };
|
|
1030
|
-
try {
|
|
1031
|
-
evt = JSON.parse(line) as { type?: string; message?: Message; toolName?: string; toolCallId?: string; args?: unknown; willRetry?: unknown };
|
|
1032
|
-
} catch {
|
|
1033
|
-
rawStdoutTail.push(`${line}\n`);
|
|
1034
|
-
shared.transcriptWriter?.writeStdoutLine(line);
|
|
1035
|
-
shared.orcaProgressTab?.append(`${line}\n`);
|
|
1036
|
-
// Non-JSON stdout lines are expected; only structured events are parsed.
|
|
1037
|
-
return;
|
|
1038
|
-
}
|
|
972
|
+
const processEvent = (evt: ChildSessionEvent & { message?: Message; toolName?: string; toolCallId?: string; args?: unknown; willRetry?: unknown }) => {
|
|
973
|
+
if (lifecycleFinished) return;
|
|
974
|
+
jsonlWriter.writeLine(JSON.stringify(projectChildSessionEventForJson(evt)));
|
|
1039
975
|
shared.transcriptWriter?.writeChildEvent(evt);
|
|
1040
976
|
shared.orcaProgressTab?.event(evt);
|
|
1041
977
|
if (evt.type === "compaction_start") compactionStartedReceived = true;
|
|
@@ -1047,6 +983,20 @@ async function runSingleAttempt(
|
|
|
1047
983
|
compactionStartedReceived = false;
|
|
1048
984
|
afterCompactionSettlement = false;
|
|
1049
985
|
}
|
|
986
|
+
if (evt.type === "agent_start") {
|
|
987
|
+
const diagnostic = capture.toolDiagnostic();
|
|
988
|
+
if (diagnostic) {
|
|
989
|
+
const message = formatChildToolDiagnostic(diagnostic, { host: "parent" });
|
|
990
|
+
toolAvailabilityError = message;
|
|
991
|
+
result.error = message;
|
|
992
|
+
result.finalOutput = message;
|
|
993
|
+
progress.status = "failed";
|
|
994
|
+
progress.error = message;
|
|
995
|
+
fireUpdate();
|
|
996
|
+
abortChild();
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1050
1000
|
const lifecycleAction = projectChildLifecycle(evt, false, childLifecycleState);
|
|
1051
1001
|
if (evt.type === "agent_settled" && lifecycleAction === "start-drain") {
|
|
1052
1002
|
agentSettledReceived = true;
|
|
@@ -1150,14 +1100,14 @@ async function runSingleAttempt(
|
|
|
1150
1100
|
progress.model = evt.message.model;
|
|
1151
1101
|
if (!result.model) result.model = evt.message.model;
|
|
1152
1102
|
if (expectedModelForVerification && !hasToolCall) {
|
|
1153
|
-
const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, evt.message.model, options.availableModels);
|
|
1103
|
+
const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, evt.message.model, options.availableModels, options.modelResponseAliases);
|
|
1154
1104
|
if (modelVerificationError && !result.error) result.error = modelVerificationError;
|
|
1155
1105
|
}
|
|
1156
1106
|
}
|
|
1157
1107
|
if (evt.message.errorMessage) assistantError = evt.message.errorMessage;
|
|
1158
1108
|
const assistantText = extractTextFromContent(evt.message.content);
|
|
1159
1109
|
appendRecentOutput(progress, assistantText.split("\n").slice(-10));
|
|
1160
|
-
// Final assistant message: start the
|
|
1110
|
+
// Final assistant message: start the settle drain window.
|
|
1161
1111
|
if (terminalAssistantStop) {
|
|
1162
1112
|
if (!evt.message.errorMessage && assistantText.trim()) assistantError = undefined;
|
|
1163
1113
|
cleanTerminalAssistantStopReceived ||= !evt.message.errorMessage;
|
|
@@ -1219,11 +1169,12 @@ async function runSingleAttempt(
|
|
|
1219
1169
|
fireUpdate();
|
|
1220
1170
|
}
|
|
1221
1171
|
};
|
|
1172
|
+
onWatchdogStatus = (event) => processEvent(event as unknown as Parameters<typeof processEvent>[0]);
|
|
1222
1173
|
|
|
1223
1174
|
fireUpdate();
|
|
1224
1175
|
if (controlConfig.enabled || options.onUpdate) {
|
|
1225
1176
|
activityTimer = setInterval(() => {
|
|
1226
|
-
if (
|
|
1177
|
+
if (sessionSettled || lifecycleFinished) {
|
|
1227
1178
|
return;
|
|
1228
1179
|
}
|
|
1229
1180
|
updateActivityState(Date.now());
|
|
@@ -1234,7 +1185,7 @@ async function runSingleAttempt(
|
|
|
1234
1185
|
|
|
1235
1186
|
if (attemptTimeout) {
|
|
1236
1187
|
timeoutTimer = setTimeout(() => {
|
|
1237
|
-
if (
|
|
1188
|
+
if (sessionSettled || lifecycleFinished || interruptedByControl) return;
|
|
1238
1189
|
result.timedOut = true;
|
|
1239
1190
|
clearAllToolTimeouts();
|
|
1240
1191
|
result.error = attemptTimeout.message;
|
|
@@ -1243,17 +1194,12 @@ async function runSingleAttempt(
|
|
|
1243
1194
|
progress.error = attemptTimeout.message;
|
|
1244
1195
|
progress.durationMs = Date.now() - startTime;
|
|
1245
1196
|
fireUpdate();
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
if (
|
|
1249
|
-
|
|
1250
|
-
}, 1000);
|
|
1251
|
-
timeoutTerminationTimer.unref?.();
|
|
1252
|
-
timeoutHardKillTimer = setTimeout(() => {
|
|
1253
|
-
if (processClosed || lifecycleFinished) return;
|
|
1254
|
-
trySignalChild(proc, "SIGKILL");
|
|
1197
|
+
abortChild();
|
|
1198
|
+
timeoutHardFinishTimer = setTimeout(() => {
|
|
1199
|
+
if (sessionSettled || lifecycleFinished) return;
|
|
1200
|
+
settle(undefined, true);
|
|
1255
1201
|
}, 4000);
|
|
1256
|
-
|
|
1202
|
+
timeoutHardFinishTimer.unref?.();
|
|
1257
1203
|
}, attemptTimeout.remainingMs);
|
|
1258
1204
|
timeoutTimer.unref?.();
|
|
1259
1205
|
}
|
|
@@ -1261,8 +1207,7 @@ async function runSingleAttempt(
|
|
|
1261
1207
|
let toolTimeoutSequence = 0;
|
|
1262
1208
|
const activeToolTimeouts = new Map<string, { toolName: string; timer: ReturnType<typeof setTimeout> }>();
|
|
1263
1209
|
const activeToolTimeoutKeysByName = new Map<string, string[]>();
|
|
1264
|
-
let
|
|
1265
|
-
let toolTimeoutHardKillTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1210
|
+
let toolTimeoutHardFinishTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1266
1211
|
const removeToolTimeoutKey = (key: string): void => {
|
|
1267
1212
|
const active = activeToolTimeouts.get(key);
|
|
1268
1213
|
if (!active) return;
|
|
@@ -1284,17 +1229,13 @@ async function runSingleAttempt(
|
|
|
1284
1229
|
};
|
|
1285
1230
|
const clearAllToolTimeouts = (): void => {
|
|
1286
1231
|
for (const key of [...activeToolTimeouts.keys()]) removeToolTimeoutKey(key);
|
|
1287
|
-
if (
|
|
1288
|
-
clearTimeout(
|
|
1289
|
-
|
|
1290
|
-
}
|
|
1291
|
-
if (toolTimeoutHardKillTimer) {
|
|
1292
|
-
clearTimeout(toolTimeoutHardKillTimer);
|
|
1293
|
-
toolTimeoutHardKillTimer = undefined;
|
|
1232
|
+
if (toolTimeoutHardFinishTimer) {
|
|
1233
|
+
clearTimeout(toolTimeoutHardFinishTimer);
|
|
1234
|
+
toolTimeoutHardFinishTimer = undefined;
|
|
1294
1235
|
}
|
|
1295
1236
|
};
|
|
1296
1237
|
const terminateForToolTimeout = (message: string): void => {
|
|
1297
|
-
if (
|
|
1238
|
+
if (sessionSettled || lifecycleFinished || interruptedByControl) return;
|
|
1298
1239
|
result.timedOut = true;
|
|
1299
1240
|
result.error = message;
|
|
1300
1241
|
result.finalOutput = message;
|
|
@@ -1302,17 +1243,12 @@ async function runSingleAttempt(
|
|
|
1302
1243
|
progress.error = message;
|
|
1303
1244
|
progress.durationMs = Date.now() - startTime;
|
|
1304
1245
|
fireUpdate();
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
if (
|
|
1308
|
-
|
|
1309
|
-
}, 1000);
|
|
1310
|
-
toolTimeoutTerminationTimer.unref?.();
|
|
1311
|
-
toolTimeoutHardKillTimer = setTimeout(() => {
|
|
1312
|
-
if (processClosed || lifecycleFinished) return;
|
|
1313
|
-
trySignalChild(proc, "SIGKILL");
|
|
1246
|
+
abortChild();
|
|
1247
|
+
toolTimeoutHardFinishTimer = setTimeout(() => {
|
|
1248
|
+
if (sessionSettled || lifecycleFinished) return;
|
|
1249
|
+
settle(undefined, true);
|
|
1314
1250
|
}, 4000);
|
|
1315
|
-
|
|
1251
|
+
toolTimeoutHardFinishTimer.unref?.();
|
|
1316
1252
|
};
|
|
1317
1253
|
const armToolTimeout = (event: { toolCallId?: unknown; toolName: string }): void => {
|
|
1318
1254
|
const timeoutForTool = effectiveToolTimeoutMs(event.toolName, options.toolTimeoutMs);
|
|
@@ -1333,111 +1269,42 @@ async function runSingleAttempt(
|
|
|
1333
1269
|
activeToolTimeoutKeysByName.set(toolName, keys);
|
|
1334
1270
|
};
|
|
1335
1271
|
|
|
1336
|
-
|
|
1337
|
-
const
|
|
1338
|
-
if (
|
|
1339
|
-
|
|
1340
|
-
result.error = formatProtocolOutputLimit(limit);
|
|
1341
|
-
progress.status = "failed";
|
|
1342
|
-
progress.error = result.error;
|
|
1343
|
-
progress.durationMs = Date.now() - startTime;
|
|
1344
|
-
fireUpdate();
|
|
1345
|
-
if (!childExited) {
|
|
1346
|
-
trySignalChild(proc, "SIGTERM");
|
|
1347
|
-
protocolHardKillTimer = setTimeout(() => {
|
|
1348
|
-
if (!childExited) trySignalChild(proc, "SIGKILL");
|
|
1349
|
-
}, 3000);
|
|
1350
|
-
protocolHardKillTimer.unref?.();
|
|
1351
|
-
}
|
|
1352
|
-
};
|
|
1353
|
-
const stdoutReader = createBoundedLineReader({
|
|
1354
|
-
oversizedLineProjector: PI_AGGREGATE_EVENT_PROJECTOR,
|
|
1355
|
-
onLine: processLine,
|
|
1356
|
-
onLimit: failProtocol,
|
|
1357
|
-
});
|
|
1358
|
-
const stderrReader = createBoundedLineReader({
|
|
1359
|
-
stream: "stderr",
|
|
1360
|
-
maxPendingLineBytes: MAX_CHILD_STDERR_BYTES,
|
|
1361
|
-
onLine: (line) => shared.transcriptWriter?.writeStderrLine(line),
|
|
1362
|
-
onLimit: (limit) => shared.transcriptWriter?.writeStderrLine(formatProtocolOutputLimit(limit)),
|
|
1363
|
-
});
|
|
1364
|
-
|
|
1365
|
-
const clearStdioGuard = attachPostExitStdioGuard(proc, { idleMs: 2000, hardMs: 8000 });
|
|
1366
|
-
proc.stdout.on("data", (chunk: Buffer) => stdoutReader.push(chunk));
|
|
1367
|
-
proc.stderr.on("data", (chunk: Buffer) => {
|
|
1368
|
-
stderrTail.push(chunk);
|
|
1369
|
-
stderrReader.push(chunk);
|
|
1370
|
-
shared.orcaProgressTab?.append(chunk.toString("utf-8"));
|
|
1371
|
-
});
|
|
1372
|
-
proc.on("exit", () => {
|
|
1373
|
-
childExited = true;
|
|
1374
|
-
clearFinalDrainTimers();
|
|
1375
|
-
});
|
|
1376
|
-
proc.on("close", (code, signal) => {
|
|
1377
|
-
if (lifecycleFinished) return;
|
|
1378
|
-
processClosed = true;
|
|
1272
|
+
/** The child run ended (or was forced to end); fold in the captures and finish. */
|
|
1273
|
+
const settle = (promptError: unknown, forced = false): void => {
|
|
1274
|
+
if (lifecycleFinished || sessionSettled) return;
|
|
1275
|
+
sessionSettled = true;
|
|
1379
1276
|
clearFinalDrainTimers();
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
// JSONL artifact flush is best effort.
|
|
1383
|
-
});
|
|
1384
|
-
const toolDiagnosticError = readChildToolDiagnosticError(toolDiagnosticPath);
|
|
1277
|
+
const diagnostic = capture.toolDiagnostic();
|
|
1278
|
+
const toolDiagnosticError = diagnostic ? formatChildToolDiagnostic(diagnostic, { host: "parent" }) : undefined;
|
|
1385
1279
|
toolAvailabilityError = toolDiagnosticError;
|
|
1386
|
-
result.runtimeAcknowledgedExtensions =
|
|
1387
|
-
cleanupTempDir(tempDir);
|
|
1388
|
-
stdoutReader.end();
|
|
1389
|
-
stderrReader.end();
|
|
1390
|
-
const stderr = stderrTail.text();
|
|
1391
|
-
const rawStdout = rawStdoutTail.text();
|
|
1280
|
+
result.runtimeAcknowledgedExtensions = capture.runtimeAcknowledgedExtensions();
|
|
1392
1281
|
let closeError = result.error ?? toolDiagnosticError ?? assistantError;
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
if (signal) result.processSignal = signal;
|
|
1396
|
-
if (!closeError && forcedDrainAfterEmptyTerminal && stderr.trim()) {
|
|
1397
|
-
closeError = stderr.trim();
|
|
1398
|
-
}
|
|
1399
|
-
if (!closeError && isUnexplainedProcessSignal({
|
|
1400
|
-
processSignal: signal,
|
|
1401
|
-
interrupted: result.interrupted,
|
|
1402
|
-
timedOut: result.timedOut,
|
|
1403
|
-
stopped: result.stopped,
|
|
1404
|
-
forcedDrainAfterFinalSuccess: forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal,
|
|
1405
|
-
})) {
|
|
1406
|
-
closeError = formatProcessSignalError(signal!);
|
|
1282
|
+
if (!closeError && promptError !== undefined) {
|
|
1283
|
+
closeError = promptError instanceof Error ? promptError.message : String(promptError);
|
|
1407
1284
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1285
|
+
const forcedDrainAfterFinalSuccess = (forced || forcedTermination) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !closeError;
|
|
1286
|
+
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(result.messages ?? []);
|
|
1287
|
+
if (!closeError && (abortedBySignal || session?.shutDown) && !result.interrupted && !result.timedOut) {
|
|
1288
|
+
closeError = session?.shutDown ? "Subagent stopped because the parent session shut down." : STOPPED_BEFORE_COMPLETION_ERROR;
|
|
1410
1289
|
}
|
|
1411
|
-
if (
|
|
1412
|
-
closeError =
|
|
1290
|
+
if (!closeError && forced && !forcedDrainAfterFinalSuccess) {
|
|
1291
|
+
closeError = "Subagent session did not settle after it was aborted.";
|
|
1413
1292
|
}
|
|
1414
|
-
const finalCode = forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal ? 0 :
|
|
1293
|
+
const finalCode = forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal ? 0 : closeError || promptError !== undefined ? 1 : 0;
|
|
1415
1294
|
if (!result.error && closeError) result.error = closeError;
|
|
1416
1295
|
finish(finalCode);
|
|
1417
|
-
}
|
|
1418
|
-
proc.on("error", (error) => {
|
|
1419
|
-
if (lifecycleFinished) return;
|
|
1420
|
-
processClosed = true;
|
|
1421
|
-
clearFinalDrainTimers();
|
|
1422
|
-
clearStdioGuard();
|
|
1423
|
-
void jsonlWriter.close().catch(() => {
|
|
1424
|
-
// JSONL artifact flush is best effort.
|
|
1425
|
-
});
|
|
1426
|
-
cleanupTempDir(tempDir);
|
|
1427
|
-
stdoutReader.end();
|
|
1428
|
-
stderrReader.end();
|
|
1429
|
-
if (!result.error) {
|
|
1430
|
-
result.error = error instanceof Error ? error.message : String(error);
|
|
1431
|
-
}
|
|
1432
|
-
finish(1);
|
|
1433
|
-
});
|
|
1296
|
+
};
|
|
1434
1297
|
|
|
1435
1298
|
if (options.signal) {
|
|
1436
1299
|
const kill = () => {
|
|
1437
|
-
if (
|
|
1300
|
+
if (sessionSettled || lifecycleFinished) return;
|
|
1438
1301
|
abortedBySignal = true;
|
|
1439
|
-
|
|
1440
|
-
setTimeout(() =>
|
|
1302
|
+
abortChild();
|
|
1303
|
+
const hardTimer = setTimeout(() => {
|
|
1304
|
+
if (sessionSettled || lifecycleFinished) return;
|
|
1305
|
+
settle(undefined, true);
|
|
1306
|
+
}, 3000);
|
|
1307
|
+
hardTimer.unref?.();
|
|
1441
1308
|
};
|
|
1442
1309
|
if (options.signal.aborted) kill();
|
|
1443
1310
|
else {
|
|
@@ -1448,7 +1315,7 @@ async function runSingleAttempt(
|
|
|
1448
1315
|
|
|
1449
1316
|
if (options.interruptSignal) {
|
|
1450
1317
|
const interrupt = () => {
|
|
1451
|
-
if (
|
|
1318
|
+
if (sessionSettled || lifecycleFinished) return;
|
|
1452
1319
|
if (result.timedOut) return;
|
|
1453
1320
|
interruptedByControl = true;
|
|
1454
1321
|
clearTimeoutTimers();
|
|
@@ -1459,11 +1326,12 @@ async function runSingleAttempt(
|
|
|
1459
1326
|
result.finalOutput = "Interrupted. Waiting for explicit next action.";
|
|
1460
1327
|
progress.activityState = undefined;
|
|
1461
1328
|
fireUpdate();
|
|
1462
|
-
|
|
1463
|
-
setTimeout(() => {
|
|
1464
|
-
if (
|
|
1465
|
-
|
|
1466
|
-
},
|
|
1329
|
+
abortChild();
|
|
1330
|
+
const hardTimer = setTimeout(() => {
|
|
1331
|
+
if (sessionSettled || lifecycleFinished) return;
|
|
1332
|
+
settle(undefined, true);
|
|
1333
|
+
}, 3000);
|
|
1334
|
+
hardTimer.unref?.();
|
|
1467
1335
|
};
|
|
1468
1336
|
if (options.interruptSignal.aborted) interrupt();
|
|
1469
1337
|
else {
|
|
@@ -1478,9 +1346,35 @@ async function runSingleAttempt(
|
|
|
1478
1346
|
options.onDetachReady?.((reason = "user request") => detachForeground(reason));
|
|
1479
1347
|
} catch (error) {
|
|
1480
1348
|
// A consumer callback is advisory. Keep the child attached and observable
|
|
1481
|
-
// rather than rejecting this attempt and orphaning its live
|
|
1349
|
+
// rather than rejecting this attempt and orphaning its live session.
|
|
1482
1350
|
appendRecentOutput(progress, [`Foreground detach callback failed: ${error instanceof Error ? error.message : String(error)}`]);
|
|
1483
1351
|
}
|
|
1352
|
+
|
|
1353
|
+
void (async () => {
|
|
1354
|
+
try {
|
|
1355
|
+
const created = await childSessions.create({
|
|
1356
|
+
...launch.session,
|
|
1357
|
+
onExtensionError: (error) => {
|
|
1358
|
+
shared.transcriptWriter?.writeStderrLine(`Extension error (${error.extensionPath}, ${error.event}): ${error.error instanceof Error ? error.error.message : String(error.error)}`);
|
|
1359
|
+
},
|
|
1360
|
+
});
|
|
1361
|
+
if (lifecycleFinished) {
|
|
1362
|
+
void created.dispose();
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
session = created;
|
|
1366
|
+
created.detached = detached;
|
|
1367
|
+
unsubscribe = created.subscribe((event) => processEvent(event as Parameters<typeof processEvent>[0]));
|
|
1368
|
+
if (abortedBySignal || interruptedByControl || result.timedOut) {
|
|
1369
|
+
abortChild();
|
|
1370
|
+
}
|
|
1371
|
+
options.onChildSession?.({ steer: (text) => created.steer(text), followUp: (text) => created.followUp(text) });
|
|
1372
|
+
await created.prompt(`Task: ${task}`);
|
|
1373
|
+
settle(undefined);
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
settle(error ?? new Error("Child session failed."));
|
|
1376
|
+
}
|
|
1377
|
+
})();
|
|
1484
1378
|
});
|
|
1485
1379
|
result.exitCode = exitCode;
|
|
1486
1380
|
if (afterCompactionSettlement) {
|
|
@@ -1506,20 +1400,10 @@ async function runSingleAttempt(
|
|
|
1506
1400
|
&& !abortedBySignal
|
|
1507
1401
|
&& !result.timedOut
|
|
1508
1402
|
&& !result.stopped
|
|
1509
|
-
&& !result.protocolError
|
|
1510
1403
|
&& !toolAvailabilityError) {
|
|
1511
|
-
const processExitCode = result.exitCode;
|
|
1512
1404
|
result.exitCode = 1;
|
|
1513
|
-
result.error = formatMidToolExitError({
|
|
1514
|
-
toolName: progress.currentTool,
|
|
1515
|
-
exitCode: processExitCode,
|
|
1516
|
-
processSignal: result.processSignal,
|
|
1517
|
-
});
|
|
1405
|
+
result.error = formatMidToolExitError({ toolName: progress.currentTool });
|
|
1518
1406
|
}
|
|
1519
|
-
result.error = formatSubagentExtensionConflictError(result.error, {
|
|
1520
|
-
agent: agent.name,
|
|
1521
|
-
ambientExtensionsEnabled: !launchResolvedExtensions.disableAmbientExtensions,
|
|
1522
|
-
});
|
|
1523
1407
|
if (result.error && result.exitCode === 0) {
|
|
1524
1408
|
result.exitCode = 1;
|
|
1525
1409
|
}
|
|
@@ -1527,27 +1411,23 @@ async function runSingleAttempt(
|
|
|
1527
1411
|
if (options.structuredOutput && result.exitCode === 0 && !result.error) {
|
|
1528
1412
|
result.structuredOutputSchemaPath = options.structuredOutput.schemaPath;
|
|
1529
1413
|
result.structuredOutputPath = options.structuredOutput.outputPath;
|
|
1530
|
-
|
|
1414
|
+
const structured = capture.structuredOutput();
|
|
1415
|
+
if (!structuredOutputToolInvoked || !structured.called) {
|
|
1531
1416
|
result.exitCode = 1;
|
|
1532
1417
|
result.error = MISSING_STRUCTURED_OUTPUT_CALL_ERROR;
|
|
1533
1418
|
result.structuredOutputFailed = true;
|
|
1534
1419
|
} else {
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
const acceptanceReport = readStructuredOutputAcceptanceReport(options.structuredOutput);
|
|
1547
|
-
(result as SingleResult & { structuredAcceptanceReport?: unknown; structuredAcceptanceReportError?: string }).structuredAcceptanceReport = acceptanceReport.value;
|
|
1548
|
-
(result as SingleResult & { structuredAcceptanceReport?: unknown; structuredAcceptanceReportError?: string }).structuredAcceptanceReportError = acceptanceReport.error;
|
|
1549
|
-
validatedStructuredOutput = true;
|
|
1550
|
-
}
|
|
1420
|
+
result.structuredOutput = structured.value;
|
|
1421
|
+
const acceptanceMode = options.structuredOutput.acceptanceReportPath
|
|
1422
|
+
? options.structuredOutput.acceptanceReportRequired ? "required" : "optional"
|
|
1423
|
+
: undefined;
|
|
1424
|
+
const acceptanceReportError = acceptanceMode === "required" && !structured.acceptanceReportProvided
|
|
1425
|
+
? MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR
|
|
1426
|
+
: undefined;
|
|
1427
|
+
(result as SingleResult & { structuredAcceptanceReport?: unknown; structuredAcceptanceReportError?: string }).structuredAcceptanceReport = acceptanceMode ? structured.acceptanceReport : undefined;
|
|
1428
|
+
(result as SingleResult & { structuredAcceptanceReport?: unknown; structuredAcceptanceReportError?: string }).structuredAcceptanceReportError = acceptanceReportError;
|
|
1429
|
+
writeStructuredOutputArtifacts(options.structuredOutput, structured.value, acceptanceMode ? structured.acceptanceReport : undefined);
|
|
1430
|
+
validatedStructuredOutput = true;
|
|
1551
1431
|
}
|
|
1552
1432
|
}
|
|
1553
1433
|
if (result.exitCode === 0 && !result.error) {
|
|
@@ -1600,7 +1480,7 @@ async function runSingleAttempt(
|
|
|
1600
1480
|
const outputChanged = hasSingleOutputChangedSinceSnapshot(options.outputPath, shared.outputSnapshot);
|
|
1601
1481
|
requiredOutputMissing = outputChanged === undefined ? undefined : !outputChanged;
|
|
1602
1482
|
} else if (options.structuredOutput) {
|
|
1603
|
-
requiredOutputMissing = !
|
|
1483
|
+
requiredOutputMissing = !capture.structuredOutput().called;
|
|
1604
1484
|
}
|
|
1605
1485
|
result.timeoutRecovery = buildTimeoutRecoverySummary({
|
|
1606
1486
|
termination: "timed-out",
|
|
@@ -1719,6 +1599,20 @@ async function runSingleAttempt(
|
|
|
1719
1599
|
return result;
|
|
1720
1600
|
}
|
|
1721
1601
|
|
|
1602
|
+
/** Keep the structured output artifact files other consumers read by path. */
|
|
1603
|
+
function writeStructuredOutputArtifacts(runtime: NonNullable<RunSyncOptions["structuredOutput"]>, value: unknown, acceptanceReport: unknown): void {
|
|
1604
|
+
try {
|
|
1605
|
+
mkdirSync(path.dirname(runtime.outputPath), { recursive: true });
|
|
1606
|
+
writeFileSync(runtime.outputPath, JSON.stringify(value), { mode: 0o600 });
|
|
1607
|
+
if (runtime.acceptanceReportPath) {
|
|
1608
|
+
if (acceptanceReport !== undefined) writeFileSync(runtime.acceptanceReportPath, JSON.stringify(acceptanceReport), { mode: 0o600 });
|
|
1609
|
+
else rmSync(runtime.acceptanceReportPath, { force: true });
|
|
1610
|
+
}
|
|
1611
|
+
} catch {
|
|
1612
|
+
// The captured value is already on the result; the file is observability only.
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1722
1616
|
/**
|
|
1723
1617
|
* Run a subagent synchronously (blocking until complete)
|
|
1724
1618
|
*/
|
|
@@ -1744,7 +1638,7 @@ async function runSyncCompletionInner(
|
|
|
1744
1638
|
}
|
|
1745
1639
|
options = {
|
|
1746
1640
|
...options,
|
|
1747
|
-
capabilityCeiling: intersectSubagentCapabilityCeilings(options.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(options.parentSessionId),
|
|
1641
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(options.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(options.parentSessionId), options.childRuntime?.capabilityCeiling),
|
|
1748
1642
|
};
|
|
1749
1643
|
const childSessionName = deriveChildSessionName({ agent: agentName, task });
|
|
1750
1644
|
const agent = agents.find((a) => a.name === agentName);
|
|
@@ -1766,7 +1660,7 @@ async function runSyncCompletionInner(
|
|
|
1766
1660
|
thinkingCeiling: intersectThinkingCeilings(
|
|
1767
1661
|
options.thinkingCeiling,
|
|
1768
1662
|
agent.maxThinking,
|
|
1769
|
-
|
|
1663
|
+
options.childRuntime?.thinkingCeiling,
|
|
1770
1664
|
),
|
|
1771
1665
|
};
|
|
1772
1666
|
try {
|
|
@@ -1990,14 +1884,12 @@ async function runSyncCompletionInner(
|
|
|
1990
1884
|
};
|
|
1991
1885
|
let lastResult: SingleResult | undefined;
|
|
1992
1886
|
const modelsToTry = candidates.length > 0 ? candidates : [undefined];
|
|
1993
|
-
// Escalated to "file" after an unexplained zero-activity startup failure so
|
|
1994
|
-
// retries keep the task text out of argv (endpoint pre-exec scans may deny it).
|
|
1995
|
-
let taskDeliveryOverride: SubagentTaskDelivery | undefined;
|
|
1996
1887
|
let abortRecoveryAttempted = false;
|
|
1997
1888
|
let nextAttemptTask = taskWithAcceptance;
|
|
1998
1889
|
modelAttemptsLoop: for (let modelIndex = 0; modelIndex < modelsToTry.length; modelIndex++) {
|
|
1999
1890
|
const candidate = modelsToTry[modelIndex];
|
|
2000
|
-
|
|
1891
|
+
// The inner loop re-runs the same candidate at most once, for abort recovery.
|
|
1892
|
+
for (;;) {
|
|
2001
1893
|
const recoveringAbort = abortRecoveryAttempted;
|
|
2002
1894
|
const attemptTask = nextAttemptTask;
|
|
2003
1895
|
const verifyModel = Boolean(candidate) && !(options.modelOverrideFromParent && modelIndex === 0);
|
|
@@ -2016,13 +1908,12 @@ async function runSyncCompletionInner(
|
|
|
2016
1908
|
.filter((modelCandidate): modelCandidate is string => Boolean(modelCandidate)),
|
|
2017
1909
|
outputSnapshot,
|
|
2018
1910
|
originalTask: task,
|
|
2019
|
-
taskDelivery: taskDeliveryOverride,
|
|
2020
1911
|
orcaProgressTab,
|
|
2021
1912
|
launchWarnings,
|
|
2022
1913
|
verifyModel,
|
|
2023
1914
|
});
|
|
2024
1915
|
lastResult = result;
|
|
2025
|
-
if (!recoveringAbort
|
|
1916
|
+
if (!recoveringAbort) {
|
|
2026
1917
|
if (result.model) attemptedModels.push(result.model);
|
|
2027
1918
|
else if (candidate) attemptedModels.push(candidate);
|
|
2028
1919
|
}
|
|
@@ -2072,75 +1963,10 @@ async function runSyncCompletionInner(
|
|
|
2072
1963
|
if (options.workflowChildPermitLaunch && !attemptSucceeded) break modelAttemptsLoop;
|
|
2073
1964
|
// Preserve the legacy intercom handoff contract: once this logical run has
|
|
2074
1965
|
// been handed to a supervisor, terminating that attempt must not launch a
|
|
2075
|
-
//
|
|
1966
|
+
// model fallback. Explicit user detach retains fallback.
|
|
2076
1967
|
if (intercomDetached || result.timedOut) break modelAttemptsLoop;
|
|
2077
1968
|
if (attemptSucceeded) break modelAttemptsLoop;
|
|
2078
1969
|
|
|
2079
|
-
const startupFailure = isRetryableSubagentStartupFailure({
|
|
2080
|
-
exitCode: result.exitCode,
|
|
2081
|
-
error: result.error,
|
|
2082
|
-
finalOutput: result.finalOutput,
|
|
2083
|
-
messageCount: result.messages?.length ?? 0,
|
|
2084
|
-
toolCount: result.progressSummary?.toolCount ?? 0,
|
|
2085
|
-
usage: result.usage,
|
|
2086
|
-
durationMs: result.progressSummary?.durationMs ?? Number.POSITIVE_INFINITY,
|
|
2087
|
-
protocolError: result.protocolError,
|
|
2088
|
-
processSignal: result.processSignal,
|
|
2089
|
-
detached: result.detached,
|
|
2090
|
-
interrupted: result.interrupted,
|
|
2091
|
-
timedOut: result.timedOut,
|
|
2092
|
-
stopped: result.stopped,
|
|
2093
|
-
});
|
|
2094
|
-
const retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];
|
|
2095
|
-
if (startupFailure && retryDelayMs !== undefined) {
|
|
2096
|
-
const retryNote = formatSubagentStartupRetryNote({
|
|
2097
|
-
model: attempt.model,
|
|
2098
|
-
attempt: startupAttemptIndex + 1,
|
|
2099
|
-
maxAttempts: SUBAGENT_STARTUP_RETRY_DELAYS_MS.length + 1,
|
|
2100
|
-
delayMs: retryDelayMs,
|
|
2101
|
-
});
|
|
2102
|
-
const shouldRetry = await waitForSubagentStartupRetry(retryDelayMs, [options.signal, options.interruptSignal]);
|
|
2103
|
-
if (!shouldRetry) {
|
|
2104
|
-
if (options.interruptSignal?.aborted) {
|
|
2105
|
-
result.exitCode = 0;
|
|
2106
|
-
result.interrupted = true;
|
|
2107
|
-
result.error = undefined;
|
|
2108
|
-
result.finalOutput = "Interrupted. Waiting for explicit next action.";
|
|
2109
|
-
if (result.progress) {
|
|
2110
|
-
result.progress.status = "running";
|
|
2111
|
-
result.progress.error = undefined;
|
|
2112
|
-
}
|
|
2113
|
-
} else {
|
|
2114
|
-
const cancellationError = "Subagent startup retry cancelled before relaunch.";
|
|
2115
|
-
result.error = cancellationError;
|
|
2116
|
-
result.finalOutput = cancellationError;
|
|
2117
|
-
attempt.error = cancellationError;
|
|
2118
|
-
if (result.progress) result.progress.error = cancellationError;
|
|
2119
|
-
}
|
|
2120
|
-
break modelAttemptsLoop;
|
|
2121
|
-
}
|
|
2122
|
-
if (!taskDeliveryOverride && result.processSignal === "SIGKILL") {
|
|
2123
|
-
taskDeliveryOverride = "file";
|
|
2124
|
-
attemptNotes.push("[startup-retry] retrying with file task delivery to keep the task text out of the child process argv.");
|
|
2125
|
-
}
|
|
2126
|
-
attempt.error = retryNote;
|
|
2127
|
-
attemptNotes.push(retryNote);
|
|
2128
|
-
continue;
|
|
2129
|
-
}
|
|
2130
|
-
if (startupFailure) {
|
|
2131
|
-
const startupError = formatSubagentStartupRetryExhaustedError({
|
|
2132
|
-
model: attempt.model,
|
|
2133
|
-
attempts: startupAttemptIndex + 1,
|
|
2134
|
-
});
|
|
2135
|
-
result.error = startupError;
|
|
2136
|
-
result.finalOutput = startupError;
|
|
2137
|
-
if (result.progress) {
|
|
2138
|
-
result.progress.error = startupError;
|
|
2139
|
-
result.progress.status = "failed";
|
|
2140
|
-
}
|
|
2141
|
-
attempt.error = startupError;
|
|
2142
|
-
break modelAttemptsLoop;
|
|
2143
|
-
}
|
|
2144
1970
|
const retryableModelFailure = isRetryableModelFailureAttempt({ error: result.error, messages: result.messages, toolCount: result.progressSummary?.toolCount });
|
|
2145
1971
|
if (retryableModelFailure) recordRetryableModelFailure(result.model ?? candidate, result.error);
|
|
2146
1972
|
if (isContextOverflow(result.error)) {
|