pi-subagents 0.58.0 → 0.60.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 +102 -0
- package/docs/agents.md +5 -3
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +1 -1
- package/docs/models.md +24 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +93 -5
- package/docs/workflows.md +94 -3
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +87 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +29 -233
- package/skills/pi-subagents/references/execution-controls.md +52 -7
- package/skills/pi-subagents/references/management-authoring-rpc.md +3 -4
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +54 -26
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +159 -42
- package/src/agents/agent-serializer.ts +4 -2
- package/src/agents/agents.ts +67 -26
- package/src/agents/runtime-agent-registry.ts +9 -16
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +6 -9
- package/src/api/shared-types.ts +2 -0
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +51 -23
- package/src/extension/public-execution.ts +16 -2
- package/src/extension/schemas.ts +36 -10
- package/src/extension/tool-description.ts +24 -5
- package/src/intercom/result-intercom.ts +2 -0
- package/src/profiles/profiles.ts +5 -6
- package/src/runs/background/active-async-capacity.ts +2 -2
- package/src/runs/background/async-execution.ts +52 -37
- package/src/runs/background/async-job-tracker.ts +17 -12
- package/src/runs/background/async-resume.ts +23 -25
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +110 -8
- package/src/runs/background/chain-append.ts +6 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/fleet-view.ts +21 -11
- package/src/runs/background/notify.ts +158 -6
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +2 -0
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/retained-children.ts +1 -1
- package/src/runs/background/run-status.ts +30 -9
- package/src/runs/background/scheduled-runs.ts +86 -7
- package/src/runs/background/stale-run-reconciler.ts +10 -4
- package/src/runs/background/steering.ts +4 -14
- package/src/runs/background/subagent-runner.ts +351 -358
- package/src/runs/background/subagent-wait.ts +68 -12
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +25 -1
- package/src/runs/background/wait-config.ts +23 -9
- package/src/runs/background/wait-tool.ts +9 -2
- package/src/runs/foreground/async-steering-action.ts +2 -2
- package/src/runs/foreground/execution.ts +125 -122
- package/src/runs/foreground/foreground-control.ts +3 -0
- package/src/runs/foreground/foreground-history.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +576 -240
- package/src/runs/foreground/workflow-detach-reconcile.ts +99 -200
- package/src/runs/shared/abort-recovery.ts +119 -0
- package/src/runs/shared/async-status-projection.ts +597 -0
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/child-identity.ts +19 -4
- package/src/runs/shared/child-launch-plan.ts +151 -0
- package/src/runs/shared/completion-evidence.ts +89 -0
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/mcp-config-sources.ts +42 -6
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +93 -143
- package/src/runs/shared/mcp-direct-tool-grant.ts +194 -0
- package/src/runs/shared/model-fallback.ts +9 -2
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/nested-events.ts +6 -2
- package/src/runs/shared/nested-render.ts +7 -3
- package/src/runs/shared/parallel-handoff.ts +419 -7
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +67 -3
- package/src/runs/shared/single-output.ts +72 -22
- package/src/runs/shared/subagent-prompt-runtime.ts +33 -6
- package/src/runs/shared/workflow-graph.ts +15 -0
- package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/src/runs/shared/worktree.ts +18 -0
- package/src/shared/child-session-name.ts +46 -0
- package/src/shared/extension-context.ts +24 -0
- package/src/shared/formatters.ts +5 -2
- package/src/shared/launch-contract.ts +1 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +232 -30
- package/src/shared/utils.ts +35 -55
- package/src/slash/delegation-adapters.ts +1 -8
- package/src/slash/delegation-request.ts +0 -4
- package/src/slash/slash-bridge.ts +1 -2
- package/src/slash/slash-commands.ts +369 -90
- package/src/slash/slash-live-state.ts +22 -11
- package/src/tui/fleet-status.ts +134 -75
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +895 -121
- package/src/watchdog/change-signature.ts +40 -1
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +235 -0
- package/src/workflows/scripted-workflow.ts +503 -38
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +43 -4
- package/src/workflows/workflow-settlement.ts +246 -0
- package/src/runs/shared/turn-budget.ts +0 -98
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
findLatestSessionFile,
|
|
47
47
|
detectSubagentError,
|
|
48
48
|
hasEmptyTerminalAssistantResponse,
|
|
49
|
+
formatEmptyTerminalAssistantResponseError,
|
|
49
50
|
extractToolArgsPreview,
|
|
50
51
|
extractTextFromContent,
|
|
51
52
|
boundStreamedRecentTools,
|
|
@@ -55,7 +56,9 @@ import {
|
|
|
55
56
|
import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
56
57
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
57
58
|
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, resolveToolTimeoutMs, toolTimeoutCallKey, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
58
|
-
import { evaluateCompletionMutationGuard, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
59
|
+
import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
60
|
+
import { planCompletionEvidence } from "../shared/completion-evidence.ts";
|
|
61
|
+
import { planAbortRecovery } from "../shared/abort-recovery.ts";
|
|
59
62
|
import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.ts";
|
|
60
63
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
61
64
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
@@ -63,7 +66,8 @@ import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
|
63
66
|
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
64
67
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
65
68
|
import { resolvePermissionRules } from "../shared/permissions.ts";
|
|
66
|
-
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
69
|
+
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, deriveForkPromptCacheKey, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
70
|
+
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
67
71
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
68
72
|
import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.ts";
|
|
69
73
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
@@ -72,7 +76,7 @@ import { MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructu
|
|
|
72
76
|
import { formatMidToolExitError, formatProcessSignalError, isOrdinaryToolForMidToolExit, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
73
77
|
import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
|
|
74
78
|
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
75
|
-
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
79
|
+
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, hasSingleOutputChangedSinceSnapshot, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
76
80
|
import {
|
|
77
81
|
buildModelCandidates,
|
|
78
82
|
formatSubagentModelVerificationError,
|
|
@@ -103,7 +107,6 @@ import {
|
|
|
103
107
|
import { acceptanceFailureMessage, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport, validateAcceptanceInput } from "../shared/acceptance.ts";
|
|
104
108
|
import { PROMPT_REDACTED } from "../../shared/utils.ts";
|
|
105
109
|
import { attachContractProjections, isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
106
|
-
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
107
110
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
108
111
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
109
112
|
import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
@@ -294,6 +297,9 @@ function snapshotStreamResult(result: SingleResult, progress: AgentProgress): Si
|
|
|
294
297
|
return snapshot;
|
|
295
298
|
}
|
|
296
299
|
|
|
300
|
+
const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
|
|
301
|
+
type AbortRecoverySingleResult = SingleResult & { [AFTER_COMPACTION_SETTLEMENT]?: true };
|
|
302
|
+
|
|
297
303
|
async function runSingleAttempt(
|
|
298
304
|
runtimeCwd: string,
|
|
299
305
|
agent: AgentConfig,
|
|
@@ -323,6 +329,10 @@ async function runSingleAttempt(
|
|
|
323
329
|
assertThinkingWithinCeiling({ model: modelArg, configThinking: effectiveThinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
324
330
|
const expectedModelForVerification = shared.verifyModel ? modelArg : undefined;
|
|
325
331
|
const resolvedThinking = resolveEffectiveThinking(modelArg, effectiveThinking);
|
|
332
|
+
// Display name for the child session: applied inside the child via
|
|
333
|
+
// PI_SUBAGENT_SESSION_NAME and echoed back on the result payload so hosts
|
|
334
|
+
// can label this run without reading the child's session file.
|
|
335
|
+
const childSessionName = deriveChildSessionName({ agent: agent.name, task: shared.originalTask ?? task });
|
|
326
336
|
const watchdogConfig = resolveWatchdogConfig(options.cwd ?? runtimeCwd);
|
|
327
337
|
const childWatchdog = watchdogConfig.ok
|
|
328
338
|
? resolveChildWatchdogConfig({
|
|
@@ -351,13 +361,15 @@ async function runSingleAttempt(
|
|
|
351
361
|
inheritSkills: agent.inheritSkills,
|
|
352
362
|
requireReadTool: Boolean(shared.resolvedSkillNames?.length),
|
|
353
363
|
tools: agent.tools,
|
|
364
|
+
allowNestedSubagents: agent.allowNestedSubagents,
|
|
354
365
|
extensions: agent.extensions,
|
|
355
366
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
356
|
-
systemPrompt:
|
|
367
|
+
systemPrompt: shared.systemPrompt,
|
|
357
368
|
mcpDirectTools: agent.mcpDirectTools,
|
|
358
369
|
cwd: options.cwd ?? runtimeCwd,
|
|
359
370
|
promptFileStem: agent.name,
|
|
360
371
|
intercomSessionName: options.intercomSessionName,
|
|
372
|
+
sessionName: childSessionName,
|
|
361
373
|
orchestratorIntercomTarget: options.orchestratorIntercomTarget,
|
|
362
374
|
runId: options.runId,
|
|
363
375
|
childAgentName: agent.name,
|
|
@@ -368,6 +380,7 @@ async function runSingleAttempt(
|
|
|
368
380
|
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
369
381
|
runFanoutBudget: options.runFanoutBudget,
|
|
370
382
|
parentSessionId: options.parentSessionId,
|
|
383
|
+
forkCacheKey: options.context === "fork" ? deriveForkPromptCacheKey(options.parentSessionId) : undefined,
|
|
371
384
|
steerInboxDir: options.steerInboxDir,
|
|
372
385
|
steerCapabilityPath: options.steerCapabilityPath,
|
|
373
386
|
steerAckDir: options.steerAckDir,
|
|
@@ -380,6 +393,7 @@ async function runSingleAttempt(
|
|
|
380
393
|
permissionAuditPath,
|
|
381
394
|
childWatchdog,
|
|
382
395
|
waitToolEnabled: options.waitToolEnabled,
|
|
396
|
+
waitToolDefaultTimeoutMs: options.waitToolDefaultTimeoutMs,
|
|
383
397
|
capabilityCeiling: options.capabilityCeiling,
|
|
384
398
|
thinkingCeiling: options.thinkingCeiling,
|
|
385
399
|
extensionBindings: options.extensionBindings,
|
|
@@ -390,9 +404,10 @@ async function runSingleAttempt(
|
|
|
390
404
|
shared.launchWarnings.emitted = true;
|
|
391
405
|
}
|
|
392
406
|
|
|
393
|
-
const effectiveSystemPrompt =
|
|
407
|
+
const effectiveSystemPrompt = shared.systemPrompt;
|
|
394
408
|
const toolPlan = resolvePiLaunchToolPlan({
|
|
395
409
|
tools: agent.tools,
|
|
410
|
+
allowNestedSubagents: agent.allowNestedSubagents,
|
|
396
411
|
extensions: agent.extensions,
|
|
397
412
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
398
413
|
mcpDirectTools: agent.mcpDirectTools,
|
|
@@ -425,6 +440,7 @@ async function runSingleAttempt(
|
|
|
425
440
|
index: options.index ?? 0,
|
|
426
441
|
agent: agent.name,
|
|
427
442
|
task,
|
|
443
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
428
444
|
messages: [],
|
|
429
445
|
finalOutput: "",
|
|
430
446
|
exitCode: 1,
|
|
@@ -465,6 +481,7 @@ async function runSingleAttempt(
|
|
|
465
481
|
index: options.index ?? 0,
|
|
466
482
|
agent: agent.name,
|
|
467
483
|
task: shared.originalTask ?? task,
|
|
484
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
468
485
|
...(options.agentContract ? { agentContract: options.agentContract } : {}),
|
|
469
486
|
launchContractDigest,
|
|
470
487
|
launchResolvedExtensions,
|
|
@@ -478,7 +495,6 @@ async function runSingleAttempt(
|
|
|
478
495
|
transcriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,
|
|
479
496
|
skills: shared.resolvedSkillNames,
|
|
480
497
|
skillsWarning: shared.skillsWarning,
|
|
481
|
-
...(options.turnBudget ? { turnBudget: initialTurnBudgetState(options.turnBudget) } : {}),
|
|
482
498
|
...(options.toolBudget ? { toolBudget: initialToolBudgetState(options.toolBudget) } : {}),
|
|
483
499
|
...(options.capabilityCeiling ? { capabilityCeiling: options.capabilityCeiling } : {}),
|
|
484
500
|
...(capabilityAudit ? { capabilityAudit } : {}),
|
|
@@ -507,6 +523,7 @@ async function runSingleAttempt(
|
|
|
507
523
|
const progress: AgentProgress = {
|
|
508
524
|
index: options.index ?? 0,
|
|
509
525
|
agent: agent.name,
|
|
526
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
510
527
|
status: "running",
|
|
511
528
|
task,
|
|
512
529
|
skills: shared.resolvedSkillNames,
|
|
@@ -566,6 +583,7 @@ async function runSingleAttempt(
|
|
|
566
583
|
return result;
|
|
567
584
|
}
|
|
568
585
|
}
|
|
586
|
+
let afterCompactionSettlement = false;
|
|
569
587
|
const exitCode = await new Promise<number>((resolve) => {
|
|
570
588
|
const proc = spawn(spawnSpec.command, spawnSpec.args, {
|
|
571
589
|
cwd: options.cwd ?? runtimeCwd,
|
|
@@ -585,20 +603,7 @@ async function runSingleAttempt(
|
|
|
585
603
|
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
586
604
|
let timeoutTerminationTimer: NodeJS.Timeout | undefined;
|
|
587
605
|
let timeoutHardKillTimer: NodeJS.Timeout | undefined;
|
|
588
|
-
let turnBudgetSoftReached = false;
|
|
589
|
-
let turnBudgetTerminationTimer: NodeJS.Timeout | undefined;
|
|
590
|
-
let turnBudgetHardKillTimer: NodeJS.Timeout | undefined;
|
|
591
606
|
let protocolHardKillTimer: NodeJS.Timeout | undefined;
|
|
592
|
-
const clearTurnBudgetTimers = () => {
|
|
593
|
-
if (turnBudgetTerminationTimer) {
|
|
594
|
-
clearTimeout(turnBudgetTerminationTimer);
|
|
595
|
-
turnBudgetTerminationTimer = undefined;
|
|
596
|
-
}
|
|
597
|
-
if (turnBudgetHardKillTimer) {
|
|
598
|
-
clearTimeout(turnBudgetHardKillTimer);
|
|
599
|
-
turnBudgetHardKillTimer = undefined;
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
607
|
const clearTimeoutTimers = () => {
|
|
603
608
|
if (timeoutTimer) {
|
|
604
609
|
clearTimeout(timeoutTimer);
|
|
@@ -661,6 +666,7 @@ async function runSingleAttempt(
|
|
|
661
666
|
let forcedTerminationSignal = false;
|
|
662
667
|
let cleanTerminalAssistantStopReceived = false;
|
|
663
668
|
let agentSettledReceived = false;
|
|
669
|
+
let compactionStartedReceived = false;
|
|
664
670
|
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
665
671
|
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
666
672
|
let watchdogTailTimer: NodeJS.Timeout | undefined;
|
|
@@ -759,7 +765,6 @@ async function runSingleAttempt(
|
|
|
759
765
|
clearStdioGuard();
|
|
760
766
|
clearTimeoutTimers();
|
|
761
767
|
clearAllToolTimeouts();
|
|
762
|
-
clearTurnBudgetTimers();
|
|
763
768
|
if (protocolHardKillTimer) {
|
|
764
769
|
clearTimeout(protocolHardKillTimer);
|
|
765
770
|
protocolHardKillTimer = undefined;
|
|
@@ -897,57 +902,6 @@ async function runSingleAttempt(
|
|
|
897
902
|
}));
|
|
898
903
|
return true;
|
|
899
904
|
};
|
|
900
|
-
const requestTurnBudgetAbort = (turnCount: number) => {
|
|
901
|
-
const budget = options.turnBudget;
|
|
902
|
-
if (!budget || result.timedOut || result.turnBudgetExceeded || interruptedByControl || processClosed || lifecycleFinished) return;
|
|
903
|
-
const message = turnBudgetExceededMessage(budget, turnCount);
|
|
904
|
-
result.turnBudgetExceeded = true;
|
|
905
|
-
result.wrapUpRequested = true;
|
|
906
|
-
result.turnBudget = turnBudgetState(budget, turnCount, true);
|
|
907
|
-
result.error = message;
|
|
908
|
-
result.finalOutput = message;
|
|
909
|
-
progress.status = "failed";
|
|
910
|
-
progress.error = message;
|
|
911
|
-
progress.durationMs = Date.now() - startTime;
|
|
912
|
-
fireUpdate();
|
|
913
|
-
trySignalChild(proc, "SIGINT");
|
|
914
|
-
turnBudgetTerminationTimer = setTimeout(() => {
|
|
915
|
-
if (processClosed || lifecycleFinished || result.timedOut) return;
|
|
916
|
-
trySignalChild(proc, "SIGTERM");
|
|
917
|
-
}, 1000);
|
|
918
|
-
turnBudgetTerminationTimer.unref?.();
|
|
919
|
-
turnBudgetHardKillTimer = setTimeout(() => {
|
|
920
|
-
if (processClosed || lifecycleFinished || result.timedOut) return;
|
|
921
|
-
trySignalChild(proc, "SIGKILL");
|
|
922
|
-
}, 4000);
|
|
923
|
-
turnBudgetHardKillTimer.unref?.();
|
|
924
|
-
};
|
|
925
|
-
|
|
926
|
-
const updateTurnBudget = (turnCount: number, terminalAssistantStop: boolean, toolWorkActiveOrStarting: boolean) => {
|
|
927
|
-
const budget = options.turnBudget;
|
|
928
|
-
if (!budget || result.timedOut || result.turnBudgetExceeded) return;
|
|
929
|
-
if (turnCount < budget.maxTurns) {
|
|
930
|
-
result.turnBudget = { ...budget, outcome: "within-budget", turnCount };
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
if (!turnBudgetSoftReached) {
|
|
934
|
-
turnBudgetSoftReached = true;
|
|
935
|
-
result.wrapUpRequested = true;
|
|
936
|
-
appendRecentOutput(progress, [turnBudgetSoftNote(budget, turnCount)]);
|
|
937
|
-
}
|
|
938
|
-
const decision = turnBudgetDecision(budget, turnCount, terminalAssistantStop, toolWorkActiveOrStarting, options.enforceHardTurnLimit);
|
|
939
|
-
if (decision === "defer") {
|
|
940
|
-
result.turnBudget = turnBudgetDeferredState(
|
|
941
|
-
budget,
|
|
942
|
-
turnCount,
|
|
943
|
-
result.turnBudget?.terminationDeferredAtTurn,
|
|
944
|
-
);
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
result.turnBudget = turnBudgetState(budget, turnCount, false);
|
|
948
|
-
if (decision === "abort") requestTurnBudgetAbort(turnCount);
|
|
949
|
-
};
|
|
950
|
-
|
|
951
905
|
const updateActivityState = (now: number): boolean => {
|
|
952
906
|
if (!controlConfig.enabled) return false;
|
|
953
907
|
const idleState = deriveActivityState({
|
|
@@ -1001,7 +955,7 @@ async function runSingleAttempt(
|
|
|
1001
955
|
const fireUpdate = () => {
|
|
1002
956
|
if (!options.onUpdate || processClosed) return;
|
|
1003
957
|
progress.durationMs = Date.now() - startTime;
|
|
1004
|
-
const output =
|
|
958
|
+
const output = result.timedOut && result.finalOutput ? result.finalOutput : getFinalOutput(result.messages ?? []);
|
|
1005
959
|
emitUpdateSnapshot(output || "(running...)");
|
|
1006
960
|
};
|
|
1007
961
|
|
|
@@ -1021,8 +975,20 @@ async function runSingleAttempt(
|
|
|
1021
975
|
}
|
|
1022
976
|
shared.transcriptWriter?.writeChildEvent(evt);
|
|
1023
977
|
shared.orcaProgressTab?.event(evt);
|
|
978
|
+
if (evt.type === "compaction_start") compactionStartedReceived = true;
|
|
979
|
+
if (evt.type === "compaction_end" && evt.willRetry === true) {
|
|
980
|
+
compactionStartedReceived = false;
|
|
981
|
+
afterCompactionSettlement = false;
|
|
982
|
+
}
|
|
983
|
+
if (evt.type === "agent_start" || evt.type === "auto_retry_start") {
|
|
984
|
+
compactionStartedReceived = false;
|
|
985
|
+
afterCompactionSettlement = false;
|
|
986
|
+
}
|
|
1024
987
|
const lifecycleAction = projectChildLifecycle(evt, false, childLifecycleState);
|
|
1025
|
-
if (evt.type === "agent_settled" && lifecycleAction === "start-drain")
|
|
988
|
+
if (evt.type === "agent_settled" && lifecycleAction === "start-drain") {
|
|
989
|
+
agentSettledReceived = true;
|
|
990
|
+
afterCompactionSettlement = compactionStartedReceived;
|
|
991
|
+
}
|
|
1026
992
|
applyChildLifecycle(lifecycleAction);
|
|
1027
993
|
|
|
1028
994
|
if (isChildWatchdogStatusEvent(evt)) {
|
|
@@ -1094,16 +1060,11 @@ async function runSingleAttempt(
|
|
|
1094
1060
|
if (evt.message.role === "assistant") {
|
|
1095
1061
|
result.usage.turns++;
|
|
1096
1062
|
progress.turnCount = result.usage.turns;
|
|
1097
|
-
const stopReason = (evt.message as { stopReason?: string }).stopReason;
|
|
1098
1063
|
const toolCalls = Array.isArray(evt.message.content)
|
|
1099
1064
|
? evt.message.content.filter((part) => (part as { type?: string }).type === "toolCall")
|
|
1100
1065
|
: [];
|
|
1101
1066
|
const hasToolCall = toolCalls.length > 0;
|
|
1102
|
-
const terminalAssistantStop = stopReason === "stop" && !hasToolCall;
|
|
1103
|
-
const terminalStructuredOutputCall = Boolean(options.structuredOutput)
|
|
1104
|
-
&& toolCalls.length === 1
|
|
1105
|
-
&& (toolCalls[0] as { name?: string }).name === "structured_output";
|
|
1106
|
-
updateTurnBudget(result.usage.turns, terminalAssistantStop || terminalStructuredOutputCall, hasToolCall || Boolean(progress.currentTool));
|
|
1067
|
+
const terminalAssistantStop = (evt.message as { stopReason?: string }).stopReason === "stop" && !hasToolCall;
|
|
1107
1068
|
const u = evt.message.usage;
|
|
1108
1069
|
if (u) {
|
|
1109
1070
|
const window = (u.input || 0) + (u.cacheRead || 0);
|
|
@@ -1363,14 +1324,17 @@ async function runSingleAttempt(
|
|
|
1363
1324
|
const rawStdout = rawStdoutTail.text();
|
|
1364
1325
|
let closeError = result.error ?? toolDiagnosticError ?? assistantError;
|
|
1365
1326
|
const forcedDrainAfterFinalSuccess = Boolean(forcedTerminationSignal || signal) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !closeError;
|
|
1327
|
+
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(result.messages ?? []);
|
|
1366
1328
|
if (signal) result.processSignal = signal;
|
|
1329
|
+
if (!closeError && forcedDrainAfterEmptyTerminal && stderr.trim()) {
|
|
1330
|
+
closeError = stderr.trim();
|
|
1331
|
+
}
|
|
1367
1332
|
if (!closeError && isUnexplainedProcessSignal({
|
|
1368
1333
|
processSignal: signal,
|
|
1369
1334
|
interrupted: result.interrupted,
|
|
1370
1335
|
timedOut: result.timedOut,
|
|
1371
1336
|
stopped: result.stopped,
|
|
1372
|
-
|
|
1373
|
-
forcedDrainAfterFinalSuccess,
|
|
1337
|
+
forcedDrainAfterFinalSuccess: forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal,
|
|
1374
1338
|
})) {
|
|
1375
1339
|
closeError = formatProcessSignalError(signal!);
|
|
1376
1340
|
}
|
|
@@ -1380,7 +1344,7 @@ async function runSingleAttempt(
|
|
|
1380
1344
|
if (code !== 0 && stderr.trim() && !closeError && !forcedDrainAfterFinalSuccess) {
|
|
1381
1345
|
closeError = stderr.trim();
|
|
1382
1346
|
}
|
|
1383
|
-
const finalCode = forcedDrainAfterFinalSuccess ? 0 : forcedTerminationSignal || signal ? (code ?? 1) : (code ?? 0);
|
|
1347
|
+
const finalCode = forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal ? 0 : forcedTerminationSignal || signal ? (code ?? 1) : (code ?? 0);
|
|
1384
1348
|
if (!result.error && closeError) result.error = closeError;
|
|
1385
1349
|
finish(finalCode);
|
|
1386
1350
|
});
|
|
@@ -1452,6 +1416,9 @@ async function runSingleAttempt(
|
|
|
1452
1416
|
}
|
|
1453
1417
|
});
|
|
1454
1418
|
result.exitCode = exitCode;
|
|
1419
|
+
if (afterCompactionSettlement) {
|
|
1420
|
+
(result as AbortRecoverySingleResult)[AFTER_COMPACTION_SETTLEMENT] = true;
|
|
1421
|
+
}
|
|
1455
1422
|
if (interruptedByControl) {
|
|
1456
1423
|
result.exitCode = 0;
|
|
1457
1424
|
result.interrupted = true;
|
|
@@ -1472,7 +1439,6 @@ async function runSingleAttempt(
|
|
|
1472
1439
|
&& !abortedBySignal
|
|
1473
1440
|
&& !result.timedOut
|
|
1474
1441
|
&& !result.stopped
|
|
1475
|
-
&& !result.turnBudgetExceeded
|
|
1476
1442
|
&& !result.protocolError
|
|
1477
1443
|
&& !toolAvailabilityError) {
|
|
1478
1444
|
const processExitCode = result.exitCode;
|
|
@@ -1525,9 +1491,12 @@ async function runSingleAttempt(
|
|
|
1525
1491
|
: messages;
|
|
1526
1492
|
const errInfo = detectSubagentError(errorMessages);
|
|
1527
1493
|
const missingOutput = !finalText?.trim() && !validatedStructuredOutput;
|
|
1528
|
-
|
|
1494
|
+
const terminalEmptyAfterUsefulWork = !validatedStructuredOutput
|
|
1495
|
+
&& hasEmptyTerminalAssistantResponse(messages)
|
|
1496
|
+
&& (progress.toolCount > 0 || Boolean(finalText?.trim()));
|
|
1497
|
+
if ((missingOutput || terminalEmptyAfterUsefulWork) && (!errInfo.hasError || hasEmptyTerminalAssistantResponse(messages))) {
|
|
1529
1498
|
result.exitCode = 1;
|
|
1530
|
-
result.error =
|
|
1499
|
+
result.error = formatEmptyTerminalAssistantResponseError(messages);
|
|
1531
1500
|
} else if (errInfo.hasError) {
|
|
1532
1501
|
result.exitCode = errInfo.exitCode ?? 1;
|
|
1533
1502
|
result.error = errInfo.details
|
|
@@ -1546,6 +1515,7 @@ async function runSingleAttempt(
|
|
|
1546
1515
|
}
|
|
1547
1516
|
|
|
1548
1517
|
result.progressSummary = {
|
|
1518
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
1549
1519
|
toolCount: progress.toolCount,
|
|
1550
1520
|
tokens: progress.tokens,
|
|
1551
1521
|
durationMs: progress.durationMs,
|
|
@@ -1558,9 +1528,17 @@ async function runSingleAttempt(
|
|
|
1558
1528
|
result.outputState = fullOutput.trim() || result.structuredOutput !== undefined ? "present" : "absent";
|
|
1559
1529
|
if (result.timedOut) {
|
|
1560
1530
|
const timeoutMessage = formatTimeoutMessage(options.timeoutMs ?? 0);
|
|
1531
|
+
let requiredOutputMissing: boolean | undefined;
|
|
1532
|
+
if (options.outputMode === "file-only" && options.outputPath) {
|
|
1533
|
+
const outputChanged = hasSingleOutputChangedSinceSnapshot(options.outputPath, shared.outputSnapshot);
|
|
1534
|
+
requiredOutputMissing = outputChanged === undefined ? undefined : !outputChanged;
|
|
1535
|
+
} else if (options.structuredOutput) {
|
|
1536
|
+
requiredOutputMissing = !existsSync(options.structuredOutput.outputPath);
|
|
1537
|
+
}
|
|
1561
1538
|
result.timeoutRecovery = buildTimeoutRecoverySummary({
|
|
1562
1539
|
termination: "timed-out",
|
|
1563
1540
|
evidence: mutationEvidence,
|
|
1541
|
+
requiredOutputMissing,
|
|
1564
1542
|
currentTool: progress.currentTool,
|
|
1565
1543
|
currentToolArgs: progress.currentToolArgs,
|
|
1566
1544
|
currentPath: progress.currentPath,
|
|
@@ -1571,14 +1549,6 @@ async function runSingleAttempt(
|
|
|
1571
1549
|
fullOutput = fullOutput.trim()
|
|
1572
1550
|
? `${timeoutMessage}\n\n${result.timeoutRecovery.message}\n\nPartial output before timeout:\n${fullOutput}`
|
|
1573
1551
|
: `${timeoutMessage}\n\n${result.timeoutRecovery.message}`;
|
|
1574
|
-
} else if (result.turnBudgetExceeded && result.turnBudget) {
|
|
1575
|
-
fullOutput = formatTurnBudgetOutput(turnBudgetExceededMessage(result.turnBudget, result.turnBudget.turnCount), fullOutput);
|
|
1576
|
-
} else if (result.turnBudget?.outcome === "termination-deferred") {
|
|
1577
|
-
const note = turnBudgetDeferredNote(result.turnBudget, result.turnBudget.terminationDeferredAtTurn ?? result.turnBudget.turnCount);
|
|
1578
|
-
fullOutput = fullOutput.trim() ? `${note}\n\n${fullOutput}` : note;
|
|
1579
|
-
} else if (result.wrapUpRequested && result.turnBudget?.outcome === "wrap-up-requested") {
|
|
1580
|
-
const note = turnBudgetSoftNote(result.turnBudget, result.turnBudget.wrapUpRequestedAtTurn ?? result.turnBudget.turnCount);
|
|
1581
|
-
fullOutput = fullOutput.trim() ? `${note}\n\n${fullOutput}` : note;
|
|
1582
1552
|
}
|
|
1583
1553
|
const completionGuardEnabled = isAgentContractV1(options.agentContract) ? agent.completionGuard === true : agent.completionGuard !== false;
|
|
1584
1554
|
const completionGuard = ((result.exitCode === 0 && !result.error) || toolAvailabilityError) && completionGuardEnabled
|
|
@@ -1595,7 +1565,6 @@ async function runSingleAttempt(
|
|
|
1595
1565
|
: undefined;
|
|
1596
1566
|
const mutationAttemptObserved = observedMutationAttempt || mutationEvidence.attemptedMutation;
|
|
1597
1567
|
let completionGuardTriggered = completionGuard?.triggered === true && !mutationAttemptObserved;
|
|
1598
|
-
const completionGuardBlocked = completionGuard?.blocked === true;
|
|
1599
1568
|
// The classifier is deliberately narrow, so a read-only review task can
|
|
1600
1569
|
// still be misread as implementation. Arbitrate BEFORE any failure side
|
|
1601
1570
|
// effect is published (effects, exit code, progress, notifications,
|
|
@@ -1612,31 +1581,26 @@ async function runSingleAttempt(
|
|
|
1612
1581
|
completionGuardTriggered = arbitration.triggered;
|
|
1613
1582
|
arbiterRescued = arbitration.rescued;
|
|
1614
1583
|
}
|
|
1615
|
-
|
|
1584
|
+
const completionEvidence = planCompletionEvidence({
|
|
1585
|
+
guard: completionGuard,
|
|
1586
|
+
guardTriggered: completionGuardTriggered,
|
|
1587
|
+
completionGuardEnabled,
|
|
1588
|
+
mutationCapable: hasMutationToolCapability(contractTools, toolPlan.effectiveMcpTools),
|
|
1589
|
+
implementationMutationExpected: expectsImplementationMutation(agent.name, shared.originalTask ?? task),
|
|
1590
|
+
mutationAttemptObserved,
|
|
1591
|
+
mutationEvidence,
|
|
1592
|
+
arbiterRescued,
|
|
1593
|
+
agentContractV1: isAgentContractV1(options.agentContract),
|
|
1594
|
+
});
|
|
1595
|
+
if (completionEvidence.fileMutation) {
|
|
1616
1596
|
result.effects = {
|
|
1617
1597
|
...(result.effects ?? {}),
|
|
1618
|
-
fileMutation:
|
|
1619
|
-
status: completionGuardBlocked
|
|
1620
|
-
? "blocked"
|
|
1621
|
-
: completionGuard.expectedMutation
|
|
1622
|
-
? completionGuardTriggered
|
|
1623
|
-
? "missing"
|
|
1624
|
-
: arbiterRescued
|
|
1625
|
-
? "not-applicable"
|
|
1626
|
-
: "observed"
|
|
1627
|
-
: "not-applicable",
|
|
1628
|
-
expected: completionGuard.expectedMutation,
|
|
1629
|
-
attempted: completionGuardBlocked ? false : completionGuard.attemptedMutation || mutationAttemptObserved,
|
|
1630
|
-
evidence: mutationEvidence,
|
|
1631
|
-
...(completionGuardBlocked && completionGuard.message ? { message: completionGuard.message } : {}),
|
|
1632
|
-
...(completionGuardTriggered ? { message: "Subagent completed without making edits for an implementation task." } : {}),
|
|
1633
|
-
...(arbiterRescued ? { resolvedBy: "llm-intent-arbiter" } : {}),
|
|
1634
|
-
},
|
|
1598
|
+
fileMutation: completionEvidence.fileMutation,
|
|
1635
1599
|
};
|
|
1636
1600
|
}
|
|
1637
|
-
if (
|
|
1601
|
+
if (completionEvidence.legacyFailureError) {
|
|
1638
1602
|
result.exitCode = 1;
|
|
1639
|
-
result.error =
|
|
1603
|
+
result.error = completionEvidence.legacyFailureError;
|
|
1640
1604
|
progress.status = "failed";
|
|
1641
1605
|
progress.error = result.error;
|
|
1642
1606
|
emitControlEvent(buildControlEvent({
|
|
@@ -1651,10 +1615,14 @@ async function runSingleAttempt(
|
|
|
1651
1615
|
}));
|
|
1652
1616
|
}
|
|
1653
1617
|
if (options.outputPath && result.exitCode === 0) {
|
|
1654
|
-
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot);
|
|
1618
|
+
const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot, options.outputClaimPath);
|
|
1655
1619
|
fullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);
|
|
1656
1620
|
result.savedOutputPath = resolvedOutput.savedPath;
|
|
1657
1621
|
result.outputSaveError = resolvedOutput.saveError;
|
|
1622
|
+
if (resolvedOutput.fatalError) {
|
|
1623
|
+
result.exitCode = 1;
|
|
1624
|
+
result.error = result.error ? `${result.error}\n${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
1625
|
+
}
|
|
1658
1626
|
if (resolvedOutput.savedPath) {
|
|
1659
1627
|
result.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);
|
|
1660
1628
|
if (result.outputState === "absent") result.outputState = "unknown";
|
|
@@ -1711,6 +1679,7 @@ async function runSyncCompletionInner(
|
|
|
1711
1679
|
...options,
|
|
1712
1680
|
capabilityCeiling: intersectSubagentCapabilityCeilings(options.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(options.parentSessionId), decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV])),
|
|
1713
1681
|
};
|
|
1682
|
+
const childSessionName = deriveChildSessionName({ agent: agentName, task });
|
|
1714
1683
|
const agent = agents.find((a) => a.name === agentName);
|
|
1715
1684
|
if (!agent) {
|
|
1716
1685
|
const diagnosticContext = options.unknownAgentDiagnosticContext
|
|
@@ -1953,12 +1922,16 @@ async function runSyncCompletionInner(
|
|
|
1953
1922
|
// Escalated to "file" after an unexplained zero-activity startup failure so
|
|
1954
1923
|
// retries keep the task text out of argv (endpoint pre-exec scans may deny it).
|
|
1955
1924
|
let taskDeliveryOverride: SubagentTaskDelivery | undefined;
|
|
1925
|
+
let abortRecoveryAttempted = false;
|
|
1926
|
+
let nextAttemptTask = taskWithAcceptance;
|
|
1956
1927
|
modelAttemptsLoop: for (let modelIndex = 0; modelIndex < modelsToTry.length; modelIndex++) {
|
|
1957
1928
|
const candidate = modelsToTry[modelIndex];
|
|
1958
1929
|
for (let startupAttemptIndex = 0; ; startupAttemptIndex++) {
|
|
1930
|
+
const recoveringAbort = abortRecoveryAttempted;
|
|
1931
|
+
const attemptTask = nextAttemptTask;
|
|
1959
1932
|
const verifyModel = Boolean(candidate) && !(options.modelOverrideFromParent && modelIndex === 0);
|
|
1960
1933
|
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
1961
|
-
const result = await runSingleAttempt(runtimeCwd, agent,
|
|
1934
|
+
const result = await runSingleAttempt(runtimeCwd, agent, attemptTask, candidate, attemptOptions, {
|
|
1962
1935
|
sessionEnabled,
|
|
1963
1936
|
systemPrompt,
|
|
1964
1937
|
resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
|
|
@@ -1978,7 +1951,7 @@ async function runSyncCompletionInner(
|
|
|
1978
1951
|
verifyModel,
|
|
1979
1952
|
});
|
|
1980
1953
|
lastResult = result;
|
|
1981
|
-
if (startupAttemptIndex === 0) {
|
|
1954
|
+
if (!recoveringAbort && startupAttemptIndex === 0) {
|
|
1982
1955
|
if (result.model) attemptedModels.push(result.model);
|
|
1983
1956
|
else if (candidate) attemptedModels.push(candidate);
|
|
1984
1957
|
}
|
|
@@ -1994,11 +1967,42 @@ async function runSyncCompletionInner(
|
|
|
1994
1967
|
usage: { ...result.usage },
|
|
1995
1968
|
};
|
|
1996
1969
|
modelAttempts.push(attempt);
|
|
1970
|
+
if (!attemptSucceeded) {
|
|
1971
|
+
const afterCompactionSettlement = (result as AbortRecoverySingleResult)[AFTER_COMPACTION_SETTLEMENT];
|
|
1972
|
+
const abortRecovery = planAbortRecovery({
|
|
1973
|
+
messages: result.messages ?? [],
|
|
1974
|
+
error: result.error,
|
|
1975
|
+
processSignal: result.processSignal,
|
|
1976
|
+
sessionAvailable: Boolean(options.sessionFile && existsSync(options.sessionFile)),
|
|
1977
|
+
alreadyResumed: abortRecoveryAttempted,
|
|
1978
|
+
stopped: result.stopped || result.detached || options.signal?.aborted,
|
|
1979
|
+
interrupted: result.interrupted || intercomDetached || options.interruptSignal?.aborted,
|
|
1980
|
+
timedOut: result.timedOut,
|
|
1981
|
+
toolBudgetExhausted: result.toolBudgetBlocked,
|
|
1982
|
+
usageBudgetExhausted: false,
|
|
1983
|
+
structuredOutputFailed: result.structuredOutputFailed,
|
|
1984
|
+
acceptanceFailed: false,
|
|
1985
|
+
currentTool: result.progress?.currentTool,
|
|
1986
|
+
afterCompactionSettlement,
|
|
1987
|
+
});
|
|
1988
|
+
if (abortRecovery.action === "resume") {
|
|
1989
|
+
abortRecoveryAttempted = true;
|
|
1990
|
+
nextAttemptTask = abortRecovery.prompt;
|
|
1991
|
+
attemptNotes.push("[abort-recovery] provider/transport abort after useful progress; resuming the retained child session once.");
|
|
1992
|
+
continue;
|
|
1993
|
+
}
|
|
1994
|
+
if (abortRecovery.diagnostic) {
|
|
1995
|
+
result.error = result.error ? `${result.error}\n${abortRecovery.diagnostic}` : abortRecovery.diagnostic;
|
|
1996
|
+
attempt.error = result.error;
|
|
1997
|
+
break modelAttemptsLoop;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
if (recoveringAbort && !attemptSucceeded) break modelAttemptsLoop;
|
|
1997
2001
|
if (options.workflowChildPermitLaunch && !attemptSucceeded) break modelAttemptsLoop;
|
|
1998
2002
|
// Preserve the legacy intercom handoff contract: once this logical run has
|
|
1999
2003
|
// been handed to a supervisor, terminating that attempt must not launch a
|
|
2000
2004
|
// startup retry or model fallback. Explicit user detach retains fallback.
|
|
2001
|
-
if (intercomDetached || result.timedOut
|
|
2005
|
+
if (intercomDetached || result.timedOut) break modelAttemptsLoop;
|
|
2002
2006
|
if (attemptSucceeded) break modelAttemptsLoop;
|
|
2003
2007
|
|
|
2004
2008
|
const startupFailure = isRetryableSubagentStartupFailure({
|
|
@@ -2015,7 +2019,6 @@ async function runSyncCompletionInner(
|
|
|
2015
2019
|
interrupted: result.interrupted,
|
|
2016
2020
|
timedOut: result.timedOut,
|
|
2017
2021
|
stopped: result.stopped,
|
|
2018
|
-
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
2019
2022
|
});
|
|
2020
2023
|
const retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];
|
|
2021
2024
|
if (startupFailure && retryDelayMs !== undefined) {
|
|
@@ -2089,11 +2092,13 @@ async function runSyncCompletionInner(
|
|
|
2089
2092
|
usage: emptyUsage(),
|
|
2090
2093
|
error: "Subagent did not produce a result.",
|
|
2091
2094
|
} satisfies SingleResult, options.context);
|
|
2095
|
+
result.task = task;
|
|
2092
2096
|
|
|
2093
2097
|
result.usage = aggregateUsage;
|
|
2094
2098
|
result.attemptedModels = attemptedModels.length > 0 ? attemptedModels : undefined;
|
|
2095
2099
|
result.modelAttempts = modelAttempts.length > 0 ? modelAttempts : undefined;
|
|
2096
2100
|
result.progressSummary = {
|
|
2101
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
2097
2102
|
toolCount: totalToolCount,
|
|
2098
2103
|
tokens: aggregateUsage.input + aggregateUsage.output,
|
|
2099
2104
|
durationMs: totalDurationMs,
|
|
@@ -2147,8 +2152,6 @@ async function runSyncCompletionInner(
|
|
|
2147
2152
|
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "stopped", message: "Acceptance was not evaluated because the subagent was stopped." });
|
|
2148
2153
|
} else if (result.timedOut) {
|
|
2149
2154
|
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "timeout", message: "Acceptance was not evaluated because the subagent timed out." });
|
|
2150
|
-
} else if (result.turnBudgetExceeded) {
|
|
2151
|
-
result.acceptance = buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "turn-budget", message: "Acceptance was not evaluated because the subagent exceeded its turn budget." });
|
|
2152
2155
|
} else {
|
|
2153
2156
|
result.acceptance = await evaluateAcceptance({
|
|
2154
2157
|
acceptance: effectiveAcceptance,
|
|
@@ -18,6 +18,7 @@ interface BeginForegroundChildInput {
|
|
|
18
18
|
|
|
19
19
|
function copyProgress(target: ForegroundChildControl, progress: AgentProgress | undefined): void {
|
|
20
20
|
if (!progress) return;
|
|
21
|
+
target.sessionName = progress.sessionName;
|
|
21
22
|
target.currentActivityState = progress.activityState;
|
|
22
23
|
target.lastActivityAt = progress.lastActivityAt;
|
|
23
24
|
target.currentTool = progress.currentTool;
|
|
@@ -36,6 +37,7 @@ function copyProgress(target: ForegroundChildControl, progress: AgentProgress |
|
|
|
36
37
|
|
|
37
38
|
function syncCurrentChild(control: ForegroundRunControl, child: ForegroundChildControl): void {
|
|
38
39
|
control.currentAgent = child.agent;
|
|
40
|
+
control.sessionName = child.sessionName;
|
|
39
41
|
control.currentIndex = child.index;
|
|
40
42
|
control.description = child.description;
|
|
41
43
|
control.currentActivityState = child.currentActivityState;
|
|
@@ -59,6 +61,7 @@ function syncCurrentChild(control: ForegroundRunControl, child: ForegroundChildC
|
|
|
59
61
|
|
|
60
62
|
function clearCurrentChild(control: ForegroundRunControl): void {
|
|
61
63
|
control.currentAgent = undefined;
|
|
64
|
+
control.sessionName = undefined;
|
|
62
65
|
control.currentIndex = undefined;
|
|
63
66
|
control.currentActivityState = undefined;
|
|
64
67
|
control.lastActivityAt = undefined;
|
|
@@ -29,6 +29,7 @@ function compactChild(child: ForegroundResumeChild): ForegroundResumeChild {
|
|
|
29
29
|
return {
|
|
30
30
|
agent: child.agent,
|
|
31
31
|
index: child.index,
|
|
32
|
+
...(child.sessionName ? { sessionName: child.sessionName } : {}),
|
|
32
33
|
...(child.context ? { context: child.context } : {}),
|
|
33
34
|
...(child.sessionFile ? { sessionFile: child.sessionFile } : {}),
|
|
34
35
|
...(child.model ? { model: child.model } : {}),
|