pi-subagents 0.58.0 → 0.59.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 +65 -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 +89 -4
- package/docs/workflows.md +93 -2
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +1 -1
- package/skills/pi-subagents/SKILL.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +5 -1
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +24 -3
- package/src/agents/agent-management.ts +5 -17
- 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/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +51 -23
- package/src/extension/public-execution.ts +15 -2
- package/src/extension/schemas.ts +35 -10
- package/src/extension/tool-description.ts +18 -3
- 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 +50 -35
- package/src/runs/background/async-job-tracker.ts +14 -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 +66 -7
- package/src/runs/background/chain-append.ts +6 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- 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 +18 -8
- 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 +346 -357
- package/src/runs/background/subagent-wait.ts +58 -10
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +22 -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 +114 -120
- 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 +478 -239
- 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 +463 -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 +197 -0
- package/src/runs/shared/model-fallback.ts +9 -2
- 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 +20 -2
- package/src/runs/shared/single-output.ts +27 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -4
- 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 +198 -29
- 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 +125 -74
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render.ts +316 -27
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +230 -0
- package/src/workflows/scripted-workflow.ts +452 -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
|
@@ -36,25 +36,21 @@ import {
|
|
|
36
36
|
type NestedRouteInfo,
|
|
37
37
|
type NestedRunSummary,
|
|
38
38
|
type ResolvedControlConfig,
|
|
39
|
-
type ResolvedTurnBudget,
|
|
40
39
|
type ResolvedToolBudget,
|
|
41
40
|
type RunFanoutBudgetDescriptor,
|
|
42
41
|
type SubagentRunMode,
|
|
43
42
|
type SubagentOutputState,
|
|
44
43
|
type UsageBudgetConfig,
|
|
45
44
|
type ToolBudgetState,
|
|
46
|
-
type TurnBudgetState,
|
|
47
45
|
type Usage,
|
|
48
46
|
type WorkflowGraphSnapshot,
|
|
49
|
-
type SteeringStatus,
|
|
50
47
|
type SteeringTargetState,
|
|
51
48
|
type SteeringTargetStatus,
|
|
52
49
|
type SubagentChildStatusEvent,
|
|
53
|
-
type
|
|
50
|
+
type WorkflowLaneMetadata,
|
|
54
51
|
DEFAULT_MAX_OUTPUT,
|
|
55
52
|
type MaxOutputConfig,
|
|
56
53
|
SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
57
|
-
POLL_INTERVAL_MS,
|
|
58
54
|
truncateOutput,
|
|
59
55
|
getSubagentDepthEnv,
|
|
60
56
|
} from "../../shared/types.ts";
|
|
@@ -80,6 +76,7 @@ import {
|
|
|
80
76
|
Semaphore,
|
|
81
77
|
} from "../shared/parallel-utils.ts";
|
|
82
78
|
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
79
|
+
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
83
80
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
84
81
|
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
85
82
|
import { createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
@@ -102,8 +99,10 @@ import { markProcessTerminalCandidateLeaseRelease, writeProcessTerminalCandidate
|
|
|
102
99
|
import { createOwnedProcessTreeController, type OwnedProcessTreeController } from "./owned-process-tree.ts";
|
|
103
100
|
import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, updateSteeringTarget } from "./steering.ts";
|
|
104
101
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
105
|
-
import { PROMPT_REDACTED, detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.ts";
|
|
102
|
+
import { PROMPT_REDACTED, detectSubagentError, extractTextFromContent, extractToolArgsPreview, formatEmptyTerminalAssistantResponseError, getFinalOutput, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.ts";
|
|
106
103
|
import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
104
|
+
import { planCompletionEvidence, projectSettlementDiagnostic } from "../shared/completion-evidence.ts";
|
|
105
|
+
import { planAbortRecovery } from "../shared/abort-recovery.ts";
|
|
107
106
|
import {
|
|
108
107
|
createMutatingFailureState,
|
|
109
108
|
didMutatingToolFail,
|
|
@@ -137,7 +136,6 @@ import { waitForImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
|
137
136
|
import { normalizeExtensionBindings } from "../shared/extension-bindings.ts";
|
|
138
137
|
import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests, statusStepDescription } from "./chain-append.ts";
|
|
139
138
|
import { asyncStatusChildIdentity } from "../shared/child-identity.ts";
|
|
140
|
-
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
141
139
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
142
140
|
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey } from "../shared/tool-timeout.ts";
|
|
143
141
|
import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.ts";
|
|
@@ -199,7 +197,6 @@ interface SubagentRunConfig {
|
|
|
199
197
|
deadlineAt?: number;
|
|
200
198
|
/** Resolved configured hard per-tool-call timeout (ms); fast tools still have a default when undefined. */
|
|
201
199
|
toolTimeoutMs?: number;
|
|
202
|
-
turnBudget?: ResolvedTurnBudget;
|
|
203
200
|
toolBudget?: ResolvedToolBudget;
|
|
204
201
|
usageBudget?: UsageBudgetConfig;
|
|
205
202
|
revivalLease?: SessionLeaseRequest;
|
|
@@ -215,10 +212,13 @@ interface SubagentRunConfig {
|
|
|
215
212
|
launchBarrierToken?: string;
|
|
216
213
|
parentWorkflowRunId?: string;
|
|
217
214
|
workflowKey?: string;
|
|
215
|
+
lane?: WorkflowLaneMetadata;
|
|
218
216
|
}
|
|
219
217
|
|
|
220
218
|
interface StepResult {
|
|
221
219
|
agent: string;
|
|
220
|
+
/** Human-readable display name for the child session, when derived at launch. */
|
|
221
|
+
sessionName?: string;
|
|
222
222
|
context?: "fresh" | "fork";
|
|
223
223
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
224
224
|
capabilityAudit?: import("../shared/capability-ceiling.ts").SubagentCapabilityAudit;
|
|
@@ -239,9 +239,6 @@ interface StepResult {
|
|
|
239
239
|
stopped?: boolean;
|
|
240
240
|
processSignal?: string | null;
|
|
241
241
|
timeoutRecovery?: import("../../shared/types.ts").TimeoutRecoverySummary;
|
|
242
|
-
turnBudget?: TurnBudgetState;
|
|
243
|
-
turnBudgetExceeded?: boolean;
|
|
244
|
-
wrapUpRequested?: boolean;
|
|
245
242
|
toolBudget?: ToolBudgetState;
|
|
246
243
|
toolBudgetBlocked?: boolean;
|
|
247
244
|
sessionFile?: string;
|
|
@@ -429,6 +426,23 @@ function costSummaryFromAttempts(attempts: ModelAttempt[] | undefined): CostSumm
|
|
|
429
426
|
: undefined;
|
|
430
427
|
}
|
|
431
428
|
|
|
429
|
+
function usageFromAttempts(attempts: ModelAttempt[] | undefined): Usage | undefined {
|
|
430
|
+
if (!attempts || attempts.length === 0) return undefined;
|
|
431
|
+
const usage = emptyUsage();
|
|
432
|
+
for (const attempt of attempts) {
|
|
433
|
+
if (!attempt.usage) continue;
|
|
434
|
+
usage.input += attempt.usage.input;
|
|
435
|
+
usage.output += attempt.usage.output;
|
|
436
|
+
usage.cacheRead += attempt.usage.cacheRead;
|
|
437
|
+
usage.cacheWrite += attempt.usage.cacheWrite;
|
|
438
|
+
usage.cost += attempt.usage.cost;
|
|
439
|
+
usage.turns += attempt.usage.turns;
|
|
440
|
+
}
|
|
441
|
+
return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0
|
|
442
|
+
? usage
|
|
443
|
+
: undefined;
|
|
444
|
+
}
|
|
445
|
+
|
|
432
446
|
function appendRecentStepOutput(step: RunnerStatusStep, lines: string[]): void {
|
|
433
447
|
const nonEmpty = lines.filter((line) => line.trim());
|
|
434
448
|
if (nonEmpty.length === 0) return;
|
|
@@ -537,9 +551,6 @@ interface RunPiStreamingResult {
|
|
|
537
551
|
interrupted?: boolean;
|
|
538
552
|
timedOut?: boolean;
|
|
539
553
|
stopped?: boolean;
|
|
540
|
-
turnBudget?: TurnBudgetState;
|
|
541
|
-
turnBudgetExceeded?: boolean;
|
|
542
|
-
wrapUpRequested?: boolean;
|
|
543
554
|
toolBudget?: ToolBudgetState;
|
|
544
555
|
toolBudgetBlocked?: boolean;
|
|
545
556
|
observedMutationAttempt?: boolean;
|
|
@@ -556,21 +567,36 @@ interface RunPiStreamingResult {
|
|
|
556
567
|
currentToolArgs?: string;
|
|
557
568
|
currentPath?: string;
|
|
558
569
|
afterCompactionSettlement?: boolean;
|
|
570
|
+
abortRecoveryDiagnostic?: string;
|
|
559
571
|
effects?: import("../../shared/types.ts").EffectsProjection;
|
|
560
572
|
}
|
|
561
573
|
|
|
562
574
|
const MAX_CHILD_FAILURE_DIAGNOSTIC_CHARS = 8_192;
|
|
563
575
|
|
|
576
|
+
function formatRequiredOutputError(requiredOutput: {
|
|
577
|
+
kind: "file-only" | "structured";
|
|
578
|
+
path: string;
|
|
579
|
+
missing: boolean;
|
|
580
|
+
} | undefined): string | undefined {
|
|
581
|
+
if (!requiredOutput?.missing) return undefined;
|
|
582
|
+
return `Required ${requiredOutput.kind} output was not produced: ${requiredOutput.path.slice(0, 2_048)}`;
|
|
583
|
+
}
|
|
584
|
+
|
|
564
585
|
function formatChildFailureDiagnostic(input: {
|
|
565
586
|
error: string | undefined;
|
|
566
587
|
afterCompactionSettlement?: boolean;
|
|
567
|
-
|
|
568
|
-
|
|
588
|
+
abortRecoveryDiagnostic?: string;
|
|
589
|
+
requiredOutput?: {
|
|
590
|
+
kind: "file-only" | "structured";
|
|
591
|
+
path: string;
|
|
592
|
+
missing: boolean;
|
|
593
|
+
};
|
|
569
594
|
}): string | undefined {
|
|
595
|
+
const missingOutput = formatRequiredOutputError(input.requiredOutput);
|
|
570
596
|
const notes = [
|
|
597
|
+
input.abortRecoveryDiagnostic,
|
|
571
598
|
input.afterCompactionSettlement ? "Child failure followed session compaction and agent settlement." : undefined,
|
|
572
|
-
input.
|
|
573
|
-
input.missingStructuredOutput ? `Required structured output was not produced: ${input.missingStructuredOutput.slice(0, 2_048)}` : undefined,
|
|
599
|
+
missingOutput && input.error !== missingOutput ? missingOutput : undefined,
|
|
574
600
|
].filter((note): note is string => Boolean(note));
|
|
575
601
|
if (notes.length === 0) return input.error;
|
|
576
602
|
const context = notes.join("\n");
|
|
@@ -595,7 +621,6 @@ function runPiStreaming(
|
|
|
595
621
|
timeoutMessage?: string,
|
|
596
622
|
registerStop?: (stop: (() => void) | undefined) => void,
|
|
597
623
|
stopMessage?: string,
|
|
598
|
-
registerTurnBudgetAbort?: (abort: ((message: string, state?: TurnBudgetState) => void) | undefined) => void,
|
|
599
624
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void,
|
|
600
625
|
toolTimeoutMs?: number,
|
|
601
626
|
runDeadlineAt?: number,
|
|
@@ -642,9 +667,6 @@ function runPiStreaming(
|
|
|
642
667
|
let interrupted = false;
|
|
643
668
|
let timedOut = false;
|
|
644
669
|
let stopped = false;
|
|
645
|
-
let turnBudgetExceeded = false;
|
|
646
|
-
let turnBudgetMessage: string | undefined;
|
|
647
|
-
let turnBudget: TurnBudgetState | undefined;
|
|
648
670
|
let observedMutationAttempt = false;
|
|
649
671
|
let structuredOutputToolInvoked = false;
|
|
650
672
|
let structuredOutputMessageStartIndex: number | undefined;
|
|
@@ -747,8 +769,19 @@ function runPiStreaming(
|
|
|
747
769
|
appendChildEvent(event as unknown as Record<string, unknown>);
|
|
748
770
|
transcriptWriter?.writeChildEvent(event);
|
|
749
771
|
if (event.type === "compaction_start") compactionStartedReceived = true;
|
|
772
|
+
if (event.type === "compaction_end" && event.willRetry === true) {
|
|
773
|
+
compactionStartedReceived = false;
|
|
774
|
+
afterCompactionSettlement = false;
|
|
775
|
+
}
|
|
776
|
+
if (event.type === "agent_start" || event.type === "auto_retry_start") {
|
|
777
|
+
compactionStartedReceived = false;
|
|
778
|
+
afterCompactionSettlement = false;
|
|
779
|
+
}
|
|
750
780
|
const lifecycleAction = projectChildLifecycle(event, false, childLifecycleState);
|
|
751
|
-
if (event.type === "agent_settled" && lifecycleAction === "start-drain")
|
|
781
|
+
if (event.type === "agent_settled" && lifecycleAction === "start-drain") {
|
|
782
|
+
agentSettledReceived = true;
|
|
783
|
+
afterCompactionSettlement = compactionStartedReceived;
|
|
784
|
+
}
|
|
752
785
|
applyChildLifecycle(lifecycleAction);
|
|
753
786
|
|
|
754
787
|
if (isChildWatchdogStatusEvent(event)) {
|
|
@@ -856,11 +889,10 @@ function runPiStreaming(
|
|
|
856
889
|
let cleanTerminalAssistantStopReceived = false;
|
|
857
890
|
let agentSettledReceived = false;
|
|
858
891
|
let compactionStartedReceived = false;
|
|
892
|
+
let afterCompactionSettlement = false;
|
|
859
893
|
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
860
894
|
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
861
895
|
let watchdogTailTimer: NodeJS.Timeout | undefined;
|
|
862
|
-
let turnBudgetTerminationTimer: NodeJS.Timeout | undefined;
|
|
863
|
-
let turnBudgetHardKillTimer: NodeJS.Timeout | undefined;
|
|
864
896
|
let protocolHardKillTimer: NodeJS.Timeout | undefined;
|
|
865
897
|
let protocolError: ProtocolOutputLimit | undefined;
|
|
866
898
|
let settled = false;
|
|
@@ -981,23 +1013,6 @@ function runPiStreaming(
|
|
|
981
1013
|
if (processTreeController) void processTreeController.terminate();
|
|
982
1014
|
else trySignalChild(child, "SIGTERM");
|
|
983
1015
|
});
|
|
984
|
-
registerTurnBudgetAbort?.((message, state) => {
|
|
985
|
-
if (settled || timedOut || stopped || turnBudgetExceeded) return;
|
|
986
|
-
turnBudgetExceeded = true;
|
|
987
|
-
turnBudgetMessage = message;
|
|
988
|
-
turnBudget = state;
|
|
989
|
-
interrupted = false;
|
|
990
|
-
error = message;
|
|
991
|
-
trySignalChild(child, "SIGINT");
|
|
992
|
-
turnBudgetTerminationTimer = setTimeout(() => {
|
|
993
|
-
if (!settled && !timedOut && !stopped) trySignalChild(child, "SIGTERM");
|
|
994
|
-
}, 1000);
|
|
995
|
-
turnBudgetTerminationTimer.unref?.();
|
|
996
|
-
turnBudgetHardKillTimer = setTimeout(() => {
|
|
997
|
-
if (!settled && !timedOut && !stopped) trySignalChild(child, "SIGKILL");
|
|
998
|
-
}, 4000);
|
|
999
|
-
turnBudgetHardKillTimer.unref?.();
|
|
1000
|
-
});
|
|
1001
1016
|
const clearDrainTimers = () => {
|
|
1002
1017
|
clearAllToolTimeouts();
|
|
1003
1018
|
if (finalDrainTimer) {
|
|
@@ -1009,14 +1024,6 @@ function runPiStreaming(
|
|
|
1009
1024
|
finalHardKillTimer = undefined;
|
|
1010
1025
|
}
|
|
1011
1026
|
clearWatchdogTailTimer();
|
|
1012
|
-
if (turnBudgetTerminationTimer) {
|
|
1013
|
-
clearTimeout(turnBudgetTerminationTimer);
|
|
1014
|
-
turnBudgetTerminationTimer = undefined;
|
|
1015
|
-
}
|
|
1016
|
-
if (turnBudgetHardKillTimer) {
|
|
1017
|
-
clearTimeout(turnBudgetHardKillTimer);
|
|
1018
|
-
turnBudgetHardKillTimer = undefined;
|
|
1019
|
-
}
|
|
1020
1027
|
if (protocolHardKillTimer) {
|
|
1021
1028
|
clearTimeout(protocolHardKillTimer);
|
|
1022
1029
|
protocolHardKillTimer = undefined;
|
|
@@ -1084,7 +1091,6 @@ function runPiStreaming(
|
|
|
1084
1091
|
registerInterrupt?.(undefined);
|
|
1085
1092
|
registerTimeout?.(undefined);
|
|
1086
1093
|
registerStop?.(undefined);
|
|
1087
|
-
registerTurnBudgetAbort?.(undefined);
|
|
1088
1094
|
clearDrainTimers();
|
|
1089
1095
|
clearStdioGuard();
|
|
1090
1096
|
stdoutReader.end();
|
|
@@ -1094,32 +1100,32 @@ function runPiStreaming(
|
|
|
1094
1100
|
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
1095
1101
|
const finalError = error ?? assistantError;
|
|
1096
1102
|
const forcedDrainAfterFinalSuccess = Boolean(forcedTerminationSignal || signal) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !finalError;
|
|
1103
|
+
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(messages);
|
|
1104
|
+
const forcedDrainError = forcedDrainAfterEmptyTerminal && stderr.trim()
|
|
1105
|
+
? stderr.trim()
|
|
1106
|
+
: undefined;
|
|
1097
1107
|
const signalError = isUnexplainedProcessSignal({
|
|
1098
1108
|
processSignal: signal,
|
|
1099
1109
|
interrupted,
|
|
1100
1110
|
timedOut,
|
|
1101
1111
|
stopped,
|
|
1102
|
-
|
|
1103
|
-
forcedDrainAfterFinalSuccess,
|
|
1112
|
+
forcedDrainAfterFinalSuccess: forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal,
|
|
1104
1113
|
}) ? formatProcessSignalError(signal!) : undefined;
|
|
1105
1114
|
resolve(omitUndefinedProperties({
|
|
1106
1115
|
stderr,
|
|
1107
|
-
exitCode: timedOut || stopped ? 1 :
|
|
1116
|
+
exitCode: timedOut || stopped ? 1 : interrupted || (forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal) ? 0 : forcedTerminationSignal || signal ? (exitCode ?? 1) : exitCode,
|
|
1108
1117
|
messages,
|
|
1109
1118
|
usage,
|
|
1110
1119
|
toolCount,
|
|
1111
1120
|
durationMs: Date.now() - startedAt,
|
|
1112
1121
|
model,
|
|
1113
|
-
error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") :
|
|
1122
|
+
error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : interrupted || (forcedDrainAfterFinalSuccess && !forcedDrainAfterEmptyTerminal) ? undefined : finalError ?? forcedDrainError ?? signalError,
|
|
1114
1123
|
protocolError,
|
|
1115
1124
|
finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput,
|
|
1116
1125
|
outputState: finalOutput.trim() ? "present" : "absent",
|
|
1117
1126
|
interrupted,
|
|
1118
1127
|
timedOut,
|
|
1119
1128
|
stopped,
|
|
1120
|
-
turnBudget,
|
|
1121
|
-
turnBudgetExceeded,
|
|
1122
|
-
wrapUpRequested: turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined,
|
|
1123
1129
|
observedMutationAttempt,
|
|
1124
1130
|
structuredOutputToolInvoked,
|
|
1125
1131
|
structuredOutputMessageStartIndex,
|
|
@@ -1131,7 +1137,7 @@ function runPiStreaming(
|
|
|
1131
1137
|
currentTool,
|
|
1132
1138
|
currentToolArgs,
|
|
1133
1139
|
currentPath,
|
|
1134
|
-
afterCompactionSettlement:
|
|
1140
|
+
afterCompactionSettlement: afterCompactionSettlement || undefined,
|
|
1135
1141
|
}));
|
|
1136
1142
|
});
|
|
1137
1143
|
|
|
@@ -1145,7 +1151,6 @@ function runPiStreaming(
|
|
|
1145
1151
|
registerInterrupt?.(undefined);
|
|
1146
1152
|
registerTimeout?.(undefined);
|
|
1147
1153
|
registerStop?.(undefined);
|
|
1148
|
-
registerTurnBudgetAbort?.(undefined);
|
|
1149
1154
|
clearDrainTimers();
|
|
1150
1155
|
clearStdioGuard();
|
|
1151
1156
|
stdoutReader.end();
|
|
@@ -1154,7 +1159,7 @@ function runPiStreaming(
|
|
|
1154
1159
|
const stderr = stderrTail.text();
|
|
1155
1160
|
const finalOutput = getFinalOutput(messages) || rawStdoutTail.text().trim();
|
|
1156
1161
|
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
1157
|
-
resolve(omitUndefinedProperties({ stderr, exitCode: 1, messages, usage, toolCount, durationMs: Date.now() - startedAt, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") :
|
|
1162
|
+
resolve(omitUndefinedProperties({ stderr, exitCode: 1, messages, usage, toolCount, durationMs: Date.now() - startedAt, model, error: stopped ? (stopMessage ?? "Subagent stopped by user.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : error ?? assistantError ?? spawnErrorMessage, protocolError, finalOutput: (timedOut || stopped) && !finalOutput.trim() ? (stopped ? stopMessage ?? "Subagent stopped by user." : timeoutMessage ?? "Subagent timed out.") : finalOutput, outputState: finalOutput.trim() ? "present" : "absent", timedOut, stopped, observedMutationAttempt, structuredOutputToolInvoked, structuredOutputMessageStartIndex, watchdog: childWatchdogState, processInstanceId, processTree: { state: "unknown", reason: "verification-failed", diagnostic: spawnErrorMessage } }));
|
|
1158
1163
|
});
|
|
1159
1164
|
});
|
|
1160
1165
|
}
|
|
@@ -1289,7 +1294,6 @@ interface SingleStepContext {
|
|
|
1289
1294
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void;
|
|
1290
1295
|
registerTimeout?: (interrupt: (() => void) | undefined) => void;
|
|
1291
1296
|
registerStop?: (stop: (() => void) | undefined) => void;
|
|
1292
|
-
registerTurnBudgetAbort?: (abort: ((message: string, state?: TurnBudgetState) => void) | undefined) => void;
|
|
1293
1297
|
timeoutSignal?: AbortSignal;
|
|
1294
1298
|
stopSignal?: AbortSignal;
|
|
1295
1299
|
timeoutMessage?: string;
|
|
@@ -1298,7 +1302,6 @@ interface SingleStepContext {
|
|
|
1298
1302
|
toolTimeoutMs?: number;
|
|
1299
1303
|
/** Effective step deadline (Date.now() + effective timeout) when a run budget exists. */
|
|
1300
1304
|
deadlineAt?: number;
|
|
1301
|
-
turnBudget?: ResolvedTurnBudget;
|
|
1302
1305
|
childIntercomTarget?: string;
|
|
1303
1306
|
orchestratorIntercomTarget?: string;
|
|
1304
1307
|
nestedRoute?: NestedRouteInfo;
|
|
@@ -1310,6 +1313,7 @@ interface SingleStepContext {
|
|
|
1310
1313
|
onExternalProcess?: (process: ExternalProcessStatus) => void;
|
|
1311
1314
|
onExternalJob?: (status: ExternalJobStatus) => void;
|
|
1312
1315
|
skipAcceptance?: () => boolean;
|
|
1316
|
+
usageBudgetExhausted?: () => boolean;
|
|
1313
1317
|
/** False when sibling work in the same Git worktree could have caused the tracked diff. */
|
|
1314
1318
|
trackedMutationEvidenceForCompletionGuard?: boolean;
|
|
1315
1319
|
orcaProgressTab?: OrcaProgressTab;
|
|
@@ -1378,10 +1382,13 @@ async function runSingleStepInner(
|
|
|
1378
1382
|
modelAttempts: imported.modelAttempts,
|
|
1379
1383
|
contextOverflow: imported.contextOverflow,
|
|
1380
1384
|
totalCost: imported.totalCost,
|
|
1385
|
+
usage: imported.usage,
|
|
1381
1386
|
structuredOutput: timedOut || stopped ? undefined : imported.structuredOutput,
|
|
1382
1387
|
structuredOutputPath: timedOut || stopped ? undefined : imported.structuredOutputPath,
|
|
1383
1388
|
structuredOutputSchemaPath: timedOut || stopped ? undefined : imported.structuredOutputSchemaPath,
|
|
1384
1389
|
acceptance: timedOut || stopped ? undefined : imported.acceptance,
|
|
1390
|
+
execution: timedOut || stopped ? undefined : imported.execution,
|
|
1391
|
+
effects: timedOut || stopped ? undefined : imported.effects,
|
|
1385
1392
|
});
|
|
1386
1393
|
} finally {
|
|
1387
1394
|
ctx.registerTimeout?.(undefined);
|
|
@@ -1400,6 +1407,7 @@ async function runSingleStepInner(
|
|
|
1400
1407
|
if (!step.runner) {
|
|
1401
1408
|
resolvedTaskToolPlan = resolvePiLaunchToolPlan(omitUndefinedProperties({
|
|
1402
1409
|
tools: step.tools,
|
|
1410
|
+
allowNestedSubagents: step.allowNestedSubagents,
|
|
1403
1411
|
extensions: step.extensions,
|
|
1404
1412
|
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1405
1413
|
fast: step.fast,
|
|
@@ -1438,6 +1446,9 @@ async function runSingleStepInner(
|
|
|
1438
1446
|
});
|
|
1439
1447
|
}
|
|
1440
1448
|
}
|
|
1449
|
+
// Derive from the pre-acceptance task so internal acceptance/recovery
|
|
1450
|
+
// instructions never leak into the display name.
|
|
1451
|
+
const childSessionName = step.sessionName ?? deriveChildSessionName({ agent: step.agent, task, label: step.label });
|
|
1441
1452
|
if (step.effectiveAcceptance) {
|
|
1442
1453
|
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional: isAgentContractV1(step.agentContract), structuredOutput: Boolean(step.structuredOutput?.acceptanceReportPath) });
|
|
1443
1454
|
if (acceptancePrompt) task = `${task}\n${acceptancePrompt}`;
|
|
@@ -1501,14 +1512,18 @@ async function runSingleStepInner(
|
|
|
1501
1512
|
}));
|
|
1502
1513
|
try { fs.writeFileSync(ctx.outputFile, external.output, "utf-8"); } catch { /* Observability output is best-effort. */ }
|
|
1503
1514
|
const resolvedOutput = step.outputPath && external.exitCode === 0
|
|
1504
|
-
? resolveSingleOutput(step.outputPath, external.output, outputSnapshot)
|
|
1515
|
+
? resolveSingleOutput(step.outputPath, external.output, outputSnapshot, step.outputClaimPath)
|
|
1505
1516
|
: { fullOutput: external.output };
|
|
1506
1517
|
const outputReference = resolvedOutput.savedPath ? formatSavedOutputReference(resolvedOutput.savedPath, resolvedOutput.fullOutput) : undefined;
|
|
1518
|
+
const exitCode = resolvedOutput.fatalError ? 1 : external.exitCode;
|
|
1519
|
+
const error = resolvedOutput.fatalError && resolvedOutput.saveError
|
|
1520
|
+
? external.error ? `${external.error}\n${resolvedOutput.saveError}` : resolvedOutput.saveError
|
|
1521
|
+
: external.error;
|
|
1507
1522
|
const finalizedOutput = finalizeSingleOutput(omitUndefinedProperties({
|
|
1508
1523
|
fullOutput: resolvedOutput.fullOutput,
|
|
1509
1524
|
outputPath: step.outputPath,
|
|
1510
1525
|
outputMode: step.outputMode,
|
|
1511
|
-
exitCode:
|
|
1526
|
+
exitCode: exitCode ?? 1,
|
|
1512
1527
|
savedPath: resolvedOutput.savedPath,
|
|
1513
1528
|
outputReference,
|
|
1514
1529
|
saveError: resolvedOutput.saveError,
|
|
@@ -1523,16 +1538,17 @@ async function runSingleStepInner(
|
|
|
1523
1538
|
: {};
|
|
1524
1539
|
return omitUndefinedProperties({
|
|
1525
1540
|
agent: step.agent,
|
|
1541
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
1526
1542
|
context: step.context,
|
|
1527
1543
|
output: finalizedOutput.displayOutput,
|
|
1528
1544
|
outputState: external.output.trim() ? "present" : "absent",
|
|
1529
|
-
exitCode
|
|
1530
|
-
error
|
|
1545
|
+
exitCode,
|
|
1546
|
+
error,
|
|
1531
1547
|
timedOut: external.timedOut,
|
|
1532
1548
|
stopped: external.stopped,
|
|
1533
1549
|
processSignal: external.processSignal,
|
|
1534
1550
|
artifactPaths,
|
|
1535
|
-
outputSaveError: artifactErrors.outputSaveError,
|
|
1551
|
+
outputSaveError: [resolvedOutput.saveError, artifactErrors.outputSaveError].filter(Boolean).join("\n") || undefined,
|
|
1536
1552
|
metadataSaveError: artifactErrors.metadataSaveError,
|
|
1537
1553
|
runner,
|
|
1538
1554
|
externalProcess: external.externalProcess,
|
|
@@ -1566,14 +1582,18 @@ async function runSingleStepInner(
|
|
|
1566
1582
|
}));
|
|
1567
1583
|
try { fs.writeFileSync(ctx.outputFile, external.output, "utf-8"); } catch { /* Observability output is best-effort. */ }
|
|
1568
1584
|
const resolvedOutput = step.outputPath && external.exitCode === 0
|
|
1569
|
-
? resolveSingleOutput(step.outputPath, external.output, outputSnapshot)
|
|
1585
|
+
? resolveSingleOutput(step.outputPath, external.output, outputSnapshot, step.outputClaimPath)
|
|
1570
1586
|
: { fullOutput: external.output };
|
|
1571
1587
|
const outputReference = resolvedOutput.savedPath ? formatSavedOutputReference(resolvedOutput.savedPath, resolvedOutput.fullOutput) : undefined;
|
|
1588
|
+
const exitCode = resolvedOutput.fatalError ? 1 : external.exitCode;
|
|
1589
|
+
const error = resolvedOutput.fatalError && resolvedOutput.saveError
|
|
1590
|
+
? external.error ? `${external.error}\n${resolvedOutput.saveError}` : resolvedOutput.saveError
|
|
1591
|
+
: external.error;
|
|
1572
1592
|
const finalizedOutput = finalizeSingleOutput(omitUndefinedProperties({
|
|
1573
1593
|
fullOutput: resolvedOutput.fullOutput,
|
|
1574
1594
|
outputPath: step.outputPath,
|
|
1575
1595
|
outputMode: step.outputMode,
|
|
1576
|
-
exitCode:
|
|
1596
|
+
exitCode: exitCode ?? 1,
|
|
1577
1597
|
savedPath: resolvedOutput.savedPath,
|
|
1578
1598
|
outputReference,
|
|
1579
1599
|
saveError: resolvedOutput.saveError,
|
|
@@ -1588,15 +1608,16 @@ async function runSingleStepInner(
|
|
|
1588
1608
|
: {};
|
|
1589
1609
|
return omitUndefinedProperties({
|
|
1590
1610
|
agent: step.agent,
|
|
1611
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
1591
1612
|
context: step.context,
|
|
1592
1613
|
output: finalizedOutput.displayOutput,
|
|
1593
1614
|
outputState: external.output.trim() ? "present" : "absent",
|
|
1594
|
-
exitCode
|
|
1595
|
-
error
|
|
1615
|
+
exitCode,
|
|
1616
|
+
error,
|
|
1596
1617
|
timedOut: external.timedOut,
|
|
1597
1618
|
stopped: external.stopped,
|
|
1598
1619
|
artifactPaths,
|
|
1599
|
-
outputSaveError: artifactErrors.outputSaveError,
|
|
1620
|
+
outputSaveError: [resolvedOutput.saveError, artifactErrors.outputSaveError].filter(Boolean).join("\n") || undefined,
|
|
1600
1621
|
metadataSaveError: artifactErrors.metadataSaveError,
|
|
1601
1622
|
runner,
|
|
1602
1623
|
externalJob: external.externalJob,
|
|
@@ -1625,7 +1646,6 @@ async function runSingleStepInner(
|
|
|
1625
1646
|
let structuredAcceptanceReport: unknown;
|
|
1626
1647
|
let structuredAcceptanceReportError: string | undefined;
|
|
1627
1648
|
let completionGuardTriggeredFinal = false;
|
|
1628
|
-
let turnBudget = ctx.turnBudget ? initialTurnBudgetState(ctx.turnBudget) : undefined;
|
|
1629
1649
|
let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
|
|
1630
1650
|
let toolBudgetBlocked = false;
|
|
1631
1651
|
let actualLaunchContractDigest = step.launchContractDigest;
|
|
@@ -1639,8 +1659,12 @@ async function runSingleStepInner(
|
|
|
1639
1659
|
let taskDeliveryOverride: SubagentTaskDelivery | undefined;
|
|
1640
1660
|
let contextOverflow = false;
|
|
1641
1661
|
let launchWarningsEmitted = false;
|
|
1662
|
+
let abortRecoveryAttempted = false;
|
|
1663
|
+
let nextAttemptTask = task;
|
|
1642
1664
|
modelAttemptsLoop: while (modelIndex < candidates.length) {
|
|
1643
1665
|
if (ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break;
|
|
1666
|
+
const recoveringAbort = abortRecoveryAttempted;
|
|
1667
|
+
const attemptTask = nextAttemptTask;
|
|
1644
1668
|
const candidate = candidates[modelIndex];
|
|
1645
1669
|
const expectedModelForVerification = candidate && !(step.skipPrimaryModelVerification && modelIndex === 0) ? candidate : undefined;
|
|
1646
1670
|
try {
|
|
@@ -1675,7 +1699,7 @@ async function runSingleStepInner(
|
|
|
1675
1699
|
const { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: attemptCapabilityAudit, warnings } = buildPiArgs(omitUndefinedProperties({
|
|
1676
1700
|
parentSessionId: step.parentSessionId,
|
|
1677
1701
|
baseArgs: ["--mode", "json", "-p"],
|
|
1678
|
-
task,
|
|
1702
|
+
task: attemptTask,
|
|
1679
1703
|
taskDelivery: taskDeliveryOverride,
|
|
1680
1704
|
sessionEnabled,
|
|
1681
1705
|
sessionDir,
|
|
@@ -1686,11 +1710,12 @@ async function runSingleStepInner(
|
|
|
1686
1710
|
inheritSkills: step.inheritSkills,
|
|
1687
1711
|
requireReadTool: Boolean(step.skills?.length),
|
|
1688
1712
|
tools: step.tools,
|
|
1713
|
+
allowNestedSubagents: step.allowNestedSubagents,
|
|
1689
1714
|
extensions: step.extensions,
|
|
1690
1715
|
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1691
1716
|
fast: step.fast,
|
|
1692
1717
|
modelCandidates: step.modelCandidates,
|
|
1693
|
-
systemPrompt:
|
|
1718
|
+
systemPrompt: step.systemPrompt ?? "",
|
|
1694
1719
|
systemPromptMode: step.systemPromptMode,
|
|
1695
1720
|
mcpDirectTools: step.mcpDirectTools,
|
|
1696
1721
|
mcpConfig: step.mcpConfig,
|
|
@@ -1699,6 +1724,7 @@ async function runSingleStepInner(
|
|
|
1699
1724
|
cwd: step.cwd ?? ctx.cwd,
|
|
1700
1725
|
promptFileStem: step.agent,
|
|
1701
1726
|
intercomSessionName: ctx.childIntercomTarget,
|
|
1727
|
+
sessionName: childSessionName,
|
|
1702
1728
|
orchestratorIntercomTarget: ctx.orchestratorIntercomTarget,
|
|
1703
1729
|
runId: ctx.id,
|
|
1704
1730
|
childAgentName: step.agent,
|
|
@@ -1722,6 +1748,7 @@ async function runSingleStepInner(
|
|
|
1722
1748
|
: undefined,
|
|
1723
1749
|
childWatchdog,
|
|
1724
1750
|
waitToolEnabled: step.waitToolEnabled,
|
|
1751
|
+
waitToolDefaultTimeoutMs: step.waitToolDefaultTimeoutMs,
|
|
1725
1752
|
thinkingCeiling: step.thinkingCeiling,
|
|
1726
1753
|
extensionBindings,
|
|
1727
1754
|
}));
|
|
@@ -1732,6 +1759,7 @@ async function runSingleStepInner(
|
|
|
1732
1759
|
if (step.definitionDigest) {
|
|
1733
1760
|
const toolPlan = resolvedTaskToolPlan ?? resolvePiLaunchToolPlan(omitUndefinedProperties({
|
|
1734
1761
|
tools: step.tools,
|
|
1762
|
+
allowNestedSubagents: step.allowNestedSubagents,
|
|
1735
1763
|
extensions: step.extensions,
|
|
1736
1764
|
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1737
1765
|
fast: step.fast,
|
|
@@ -1756,7 +1784,7 @@ async function runSingleStepInner(
|
|
|
1756
1784
|
...(step.fast !== undefined ? { fast: step.fast } : {}),
|
|
1757
1785
|
...(resolveEffectiveThinking(candidate, step.thinking) ? { thinking: resolveEffectiveThinking(candidate, step.thinking) } : {}),
|
|
1758
1786
|
...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
|
|
1759
|
-
systemPrompt:
|
|
1787
|
+
systemPrompt: step.systemPrompt ?? "",
|
|
1760
1788
|
systemPromptMode: step.systemPromptMode,
|
|
1761
1789
|
inheritProjectContext: step.inheritProjectContext,
|
|
1762
1790
|
inheritGlobalContext: step.inheritGlobalContext,
|
|
@@ -1789,7 +1817,6 @@ async function runSingleStepInner(
|
|
|
1789
1817
|
ctx.timeoutMessage,
|
|
1790
1818
|
ctx.registerStop,
|
|
1791
1819
|
ctx.stopMessage,
|
|
1792
|
-
ctx.registerTurnBudgetAbort,
|
|
1793
1820
|
ctx.onWriterProcess,
|
|
1794
1821
|
ctx.toolTimeoutMs,
|
|
1795
1822
|
ctx.deadlineAt,
|
|
@@ -1809,25 +1836,6 @@ async function runSingleStepInner(
|
|
|
1809
1836
|
processTree: run.processTree,
|
|
1810
1837
|
});
|
|
1811
1838
|
}
|
|
1812
|
-
if (run.turnBudget) turnBudget = run.turnBudget;
|
|
1813
|
-
else if (ctx.turnBudget) {
|
|
1814
|
-
const assistantMessages = run.messages.filter((message) => message.role === "assistant");
|
|
1815
|
-
const turnCount = assistantMessages.length;
|
|
1816
|
-
const lastAssistantMessage = assistantMessages.at(-1);
|
|
1817
|
-
if (turnCount > 0 && turnCount < ctx.turnBudget.maxTurns) {
|
|
1818
|
-
turnBudget = { ...ctx.turnBudget, outcome: "within-budget", turnCount };
|
|
1819
|
-
} else if (turnCount >= ctx.turnBudget.maxTurns) {
|
|
1820
|
-
const decision = turnBudgetDecision(
|
|
1821
|
-
ctx.turnBudget,
|
|
1822
|
-
turnCount,
|
|
1823
|
-
lastAssistantMessage ? isTerminalAssistantStop(lastAssistantMessage) : false,
|
|
1824
|
-
lastAssistantMessage ? assistantStartsToolCall(lastAssistantMessage) : false,
|
|
1825
|
-
);
|
|
1826
|
-
turnBudget = decision === "defer"
|
|
1827
|
-
? turnBudgetDeferredState(ctx.turnBudget, turnCount)
|
|
1828
|
-
: turnBudgetState(ctx.turnBudget, turnCount, decision === "abort");
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
1839
|
const toolAvailabilityError = run.exitCode === 0 && !run.error
|
|
1832
1840
|
? readChildToolDiagnosticError(toolDiagnosticPath)
|
|
1833
1841
|
: undefined;
|
|
@@ -1838,7 +1846,6 @@ async function runSingleStepInner(
|
|
|
1838
1846
|
&& !run.interrupted
|
|
1839
1847
|
&& !run.timedOut
|
|
1840
1848
|
&& !run.stopped
|
|
1841
|
-
&& !run.turnBudgetExceeded
|
|
1842
1849
|
&& !run.protocolError
|
|
1843
1850
|
&& !toolAvailabilityError
|
|
1844
1851
|
? formatMidToolExitError({
|
|
@@ -1876,14 +1883,17 @@ async function runSingleStepInner(
|
|
|
1876
1883
|
const hiddenError = run.exitCode === 0 && !run.error && !toolAvailabilityError && !structuredError && !midToolExitError
|
|
1877
1884
|
? detectSubagentError(errorMessages)
|
|
1878
1885
|
: null;
|
|
1886
|
+
const terminalEmptyAfterUsefulWork = !validatedStructuredOutput
|
|
1887
|
+
&& hasEmptyTerminalAssistantResponse(run.messages)
|
|
1888
|
+
&& (run.toolCount > 0 || Boolean(run.finalOutput.trim()));
|
|
1879
1889
|
const emptyOutputError = run.exitCode === 0
|
|
1880
1890
|
&& !run.error
|
|
1881
1891
|
&& !toolAvailabilityError
|
|
1882
1892
|
&& !structuredError
|
|
1883
|
-
&& !run.finalOutput.trim()
|
|
1884
1893
|
&& !validatedStructuredOutput
|
|
1894
|
+
&& (!run.finalOutput.trim() || terminalEmptyAfterUsefulWork)
|
|
1885
1895
|
&& (!hiddenError?.hasError || hasEmptyTerminalAssistantResponse(run.messages))
|
|
1886
|
-
?
|
|
1896
|
+
? formatEmptyTerminalAssistantResponseError(run.messages)
|
|
1887
1897
|
: undefined;
|
|
1888
1898
|
const completionGuardEnabled = isAgentContractV1(step.agentContract) ? step.completionGuard === true : step.completionGuard !== false;
|
|
1889
1899
|
const completionToolPlan = resolvedTaskToolPlan;
|
|
@@ -1904,25 +1914,24 @@ async function runSingleStepInner(
|
|
|
1904
1914
|
}))
|
|
1905
1915
|
: undefined;
|
|
1906
1916
|
const mutationAttemptObserved = run.observedMutationAttempt === true || completionMutationEvidence?.attemptedMutation === true;
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
: undefined;
|
|
1922
|
-
const completionGuardError = completionGuardTriggered && !isAgentContractV1(step.agentContract)
|
|
1923
|
-
? "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes."
|
|
1917
|
+
const completionEvidence = planCompletionEvidence({
|
|
1918
|
+
guard: completionGuard,
|
|
1919
|
+
completionGuardEnabled,
|
|
1920
|
+
mutationCapable: hasMutationToolCapability(completionTools, completionToolPlan?.effectiveMcpTools ?? step.mcpDirectTools),
|
|
1921
|
+
implementationMutationExpected: expectsImplementationMutation(step.agent, taskForCompletionGuard),
|
|
1922
|
+
mutationAttemptObserved,
|
|
1923
|
+
mutationEvidence: completionMutationEvidence,
|
|
1924
|
+
agentContractV1: isAgentContractV1(step.agentContract),
|
|
1925
|
+
});
|
|
1926
|
+
const finalOutputHasPersistableFileContent = run.exitCode === 0 && !run.error && !emptyOutputError && Boolean(stripAcceptanceReport(run.finalOutput).trim());
|
|
1927
|
+
const requiredOutput = step.outputMode === "file-only" && step.outputPath
|
|
1928
|
+
? { kind: "file-only" as const, path: step.outputPath, missing: !fs.existsSync(step.outputPath) && !finalOutputHasPersistableFileContent }
|
|
1929
|
+
: effectiveStructuredOutput
|
|
1930
|
+
? { kind: "structured" as const, path: effectiveStructuredOutput.outputPath, missing: !fs.existsSync(effectiveStructuredOutput.outputPath) }
|
|
1924
1931
|
: undefined;
|
|
1925
|
-
const
|
|
1932
|
+
const missingRequiredOutputError = formatRequiredOutputError(requiredOutput);
|
|
1933
|
+
const missingRequiredOutputAfterMutation = Boolean(missingRequiredOutputError) && (mutationAttemptObserved || Boolean(mutationEvidence.changedFiles.length));
|
|
1934
|
+
const effectiveExitCode = toolAvailabilityError || completionEvidence.legacyFailureError || midToolExitError || structuredError || emptyOutputError || missingRequiredOutputError
|
|
1926
1935
|
? 1
|
|
1927
1936
|
: hiddenError?.hasError
|
|
1928
1937
|
? (hiddenError.exitCode ?? 1)
|
|
@@ -1934,19 +1943,23 @@ async function runSingleStepInner(
|
|
|
1934
1943
|
interrupted: run.interrupted,
|
|
1935
1944
|
timedOut: run.timedOut,
|
|
1936
1945
|
stopped: run.stopped,
|
|
1937
|
-
turnBudgetExceeded: run.turnBudgetExceeded,
|
|
1938
1946
|
})) ? formatProcessSignalError(run.processSignal!) : undefined;
|
|
1947
|
+
const underlyingError = toolAvailabilityError
|
|
1948
|
+
?? midToolExitError
|
|
1949
|
+
?? structuredError
|
|
1950
|
+
?? run.error
|
|
1951
|
+
?? signalError
|
|
1952
|
+
?? (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1953
|
+
?? ((emptyOutputError || missingRequiredOutputError) && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1954
|
+
?? emptyOutputError
|
|
1955
|
+
?? (missingRequiredOutputAfterMutation ? missingRequiredOutputError : undefined)
|
|
1956
|
+
?? (hiddenError?.hasError
|
|
1957
|
+
? hiddenError.details
|
|
1958
|
+
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1959
|
+
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1960
|
+
: undefined);
|
|
1939
1961
|
const error = formatSubagentExtensionConflictError(
|
|
1940
|
-
|
|
1941
|
-
?? completionGuardError
|
|
1942
|
-
?? midToolExitError
|
|
1943
|
-
?? structuredError
|
|
1944
|
-
?? emptyOutputError
|
|
1945
|
-
?? (hiddenError?.hasError
|
|
1946
|
-
? hiddenError.details
|
|
1947
|
-
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1948
|
-
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1949
|
-
: run.error || signalError || (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)),
|
|
1962
|
+
underlyingError ?? missingRequiredOutputError ?? completionEvidence.legacyFailureError,
|
|
1950
1963
|
{
|
|
1951
1964
|
agent: step.agent,
|
|
1952
1965
|
ambientExtensionsEnabled: launchResolvedExtensions?.disableAmbientExtensions === false,
|
|
@@ -1960,8 +1973,8 @@ async function runSingleStepInner(
|
|
|
1960
1973
|
usage: run.usage,
|
|
1961
1974
|
});
|
|
1962
1975
|
modelAttempts.push(attempt);
|
|
1963
|
-
if (candidate && startupAttemptIndex === 0) attemptedModels.push(candidate);
|
|
1964
|
-
completionGuardTriggeredFinal =
|
|
1976
|
+
if (!recoveringAbort && candidate && startupAttemptIndex === 0) attemptedModels.push(candidate);
|
|
1977
|
+
completionGuardTriggeredFinal = completionEvidence.guardTriggered && !underlyingError && !missingRequiredOutputError;
|
|
1965
1978
|
finalOutputSnapshot = outputSnapshot;
|
|
1966
1979
|
if (step.toolBudget) {
|
|
1967
1980
|
const toolMessages = run.messages.filter((message) => message.role === "toolResult");
|
|
@@ -1969,27 +1982,48 @@ async function runSingleStepInner(
|
|
|
1969
1982
|
toolBudgetBlocked = Boolean(blockedMessage);
|
|
1970
1983
|
toolBudget = toolBudgetState(step.toolBudget, toolMessages.length, blockedMessage ? (blockedMessage as { toolName?: string }).toolName : undefined);
|
|
1971
1984
|
}
|
|
1972
|
-
const
|
|
1973
|
-
|
|
1974
|
-
:
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
mutation: {
|
|
1981
|
-
expected: mutationExpected,
|
|
1982
|
-
attempted: completionGuard?.attemptedMutation === true || mutationAttemptObserved,
|
|
1983
|
-
observed: mutationEvidence.attemptedMutation,
|
|
1984
|
-
},
|
|
1985
|
-
...(requiredOutput ? { requiredOutput } : {}),
|
|
1986
|
-
afterCompactionSettlement: run.afterCompactionSettlement === true,
|
|
1987
|
-
}
|
|
1988
|
-
: undefined;
|
|
1985
|
+
const settlementDiagnostic = projectSettlementDiagnostic(completionEvidence, {
|
|
1986
|
+
terminalFailed: effectiveExitCode !== 0,
|
|
1987
|
+
finalTextPresent: Boolean(stripAcceptanceReport(run.finalOutput).trim()),
|
|
1988
|
+
mutationObserved: mutationEvidence.attemptedMutation,
|
|
1989
|
+
requiredOutput,
|
|
1990
|
+
afterCompactionSettlement: run.afterCompactionSettlement === true,
|
|
1991
|
+
});
|
|
1992
|
+
const fileMutationEffect = completionEvidence.fileMutation ?? (missingRequiredOutputAfterMutation ? { status: "observed" as const, expected: completionEvidence.mutationExpected, attempted: true, evidence: mutationEvidence } : undefined);
|
|
1989
1993
|
finalResult = { ...run, exitCode: effectiveExitCode, model: candidate ?? run.model, error, structuredOutput, runtimeAcknowledgedExtensions, ...(step.agentContract ? { agentContract: step.agentContract } : {}), ...(fileMutationEffect || settlementDiagnostic ? { effects: { ...(fileMutationEffect ? { fileMutation: fileMutationEffect } : {}), ...(settlementDiagnostic ? { settlementDiagnostic } : {}) } } : {}) } as RunPiStreamingResult;
|
|
1990
|
-
|
|
1994
|
+
const abortRecovery = !attempt.success ? planAbortRecovery({
|
|
1995
|
+
messages: run.messages,
|
|
1996
|
+
error,
|
|
1997
|
+
processSignal: run.processSignal,
|
|
1998
|
+
sessionAvailable: Boolean(step.sessionFile && fs.existsSync(step.sessionFile)),
|
|
1999
|
+
alreadyResumed: abortRecoveryAttempted,
|
|
2000
|
+
stopped: run.stopped || ctx.stopSignal?.aborted || ctx.skipAcceptance?.(),
|
|
2001
|
+
interrupted: run.interrupted,
|
|
2002
|
+
timedOut: run.timedOut || ctx.timeoutSignal?.aborted,
|
|
2003
|
+
toolBudgetExhausted: run.toolBudgetBlocked || toolBudgetBlocked,
|
|
2004
|
+
usageBudgetExhausted: ctx.usageBudgetExhausted?.(),
|
|
2005
|
+
structuredOutputFailed: Boolean(structuredError),
|
|
2006
|
+
acceptanceFailed: false,
|
|
2007
|
+
currentTool: run.currentTool,
|
|
2008
|
+
afterCompactionSettlement: run.afterCompactionSettlement,
|
|
2009
|
+
}) : undefined;
|
|
2010
|
+
if (abortRecovery?.action === "settle" && abortRecovery.diagnostic) {
|
|
2011
|
+
attempt.error = attempt.error
|
|
2012
|
+
? `${abortRecovery.diagnostic}\n${attempt.error.slice(0, 8_000)}`
|
|
2013
|
+
: abortRecovery.diagnostic;
|
|
2014
|
+
if (finalResult) finalResult.abortRecoveryDiagnostic = abortRecovery.diagnostic;
|
|
2015
|
+
}
|
|
1991
2016
|
if (run.stopped || run.timedOut || ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break modelAttemptsLoop;
|
|
1992
|
-
if (
|
|
2017
|
+
if (abortRecovery?.action === "settle" && abortRecovery.diagnostic) break modelAttemptsLoop;
|
|
2018
|
+
if (attempt.success) break modelAttemptsLoop;
|
|
2019
|
+
if (recoveringAbort) break modelAttemptsLoop;
|
|
2020
|
+
if (abortRecovery?.action === "resume") {
|
|
2021
|
+
abortRecoveryAttempted = true;
|
|
2022
|
+
nextAttemptTask = abortRecovery.prompt;
|
|
2023
|
+
attemptNotes.push("[abort-recovery] provider/transport abort after useful progress; resuming the retained child session once.");
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
if (completionEvidence.guardTriggered) break modelAttemptsLoop;
|
|
1993
2027
|
|
|
1994
2028
|
const startupFailure = isRetryableSubagentStartupFailure(omitUndefinedProperties({
|
|
1995
2029
|
exitCode: effectiveExitCode,
|
|
@@ -2005,7 +2039,6 @@ async function runSingleStepInner(
|
|
|
2005
2039
|
interrupted: run.interrupted,
|
|
2006
2040
|
timedOut: run.timedOut,
|
|
2007
2041
|
stopped: run.stopped,
|
|
2008
|
-
turnBudgetExceeded: run.turnBudgetExceeded,
|
|
2009
2042
|
}));
|
|
2010
2043
|
const retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];
|
|
2011
2044
|
if (startupFailure && retryDelayMs !== undefined) {
|
|
@@ -2052,8 +2085,14 @@ async function runSingleStepInner(
|
|
|
2052
2085
|
const rawOutput = finalResult?.finalOutput ?? "";
|
|
2053
2086
|
const outputForPersistence = stripAcceptanceReport(rawOutput);
|
|
2054
2087
|
const resolvedOutput = step.outputPath && finalResult?.exitCode === 0
|
|
2055
|
-
? resolveSingleOutput(step.outputPath, outputForPersistence, finalOutputSnapshot)
|
|
2088
|
+
? resolveSingleOutput(step.outputPath, outputForPersistence, finalOutputSnapshot, step.outputClaimPath)
|
|
2056
2089
|
: { fullOutput: outputForPersistence };
|
|
2090
|
+
if (resolvedOutput.fatalError) {
|
|
2091
|
+
if (finalResult) {
|
|
2092
|
+
finalResult.exitCode = 1;
|
|
2093
|
+
finalResult.error = finalResult.error ? `${finalResult.error}\n${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2057
2096
|
const output = stripAcceptanceReport(resolvedOutput.fullOutput);
|
|
2058
2097
|
const outputReference = resolvedOutput.savedPath ? formatSavedOutputReference(resolvedOutput.savedPath, output) : undefined;
|
|
2059
2098
|
let outputForSummary = output;
|
|
@@ -2062,14 +2101,6 @@ async function runSingleStepInner(
|
|
|
2062
2101
|
}
|
|
2063
2102
|
if (finalResult?.stopped && !outputForSummary.trim()) {
|
|
2064
2103
|
outputForSummary = ctx.stopMessage ?? "Subagent stopped by user.";
|
|
2065
|
-
} else if (!finalResult?.timedOut && !finalResult?.stopped && finalResult?.turnBudgetExceeded && turnBudget) {
|
|
2066
|
-
outputForSummary = formatTurnBudgetOutput(turnBudgetExceededMessage(turnBudget, turnBudget.turnCount), outputForSummary);
|
|
2067
|
-
} else if (!finalResult?.timedOut && !finalResult?.stopped && turnBudget?.outcome === "termination-deferred") {
|
|
2068
|
-
const note = turnBudgetDeferredNote(turnBudget, turnBudget.terminationDeferredAtTurn ?? turnBudget.turnCount);
|
|
2069
|
-
outputForSummary = outputForSummary.trim() ? `${note}\n\n${outputForSummary}` : note;
|
|
2070
|
-
} else if (!finalResult?.timedOut && !finalResult?.stopped && turnBudget?.outcome === "wrap-up-requested") {
|
|
2071
|
-
const note = turnBudgetSoftNote(turnBudget, turnBudget.wrapUpRequestedAtTurn ?? turnBudget.turnCount);
|
|
2072
|
-
outputForSummary = outputForSummary.trim() ? `${note}\n\n${outputForSummary}` : note;
|
|
2073
2104
|
}
|
|
2074
2105
|
const outputForAcceptance = rawOutput;
|
|
2075
2106
|
const childWrittenOutput = step.outputPath
|
|
@@ -2107,7 +2138,7 @@ async function runSingleStepInner(
|
|
|
2107
2138
|
saveError: resolvedOutput.saveError,
|
|
2108
2139
|
}));
|
|
2109
2140
|
outputForSummary = finalizedOutput.displayOutput;
|
|
2110
|
-
const acceptance = step.effectiveAcceptance && !finalResult?.stopped && !
|
|
2141
|
+
const acceptance = step.effectiveAcceptance && !finalResult?.stopped && !ctx.timeoutSignal?.aborted && !ctx.stopSignal?.aborted && !ctx.skipAcceptance?.()
|
|
2111
2142
|
? await evaluateAcceptance(omitUndefinedProperties({
|
|
2112
2143
|
acceptance: step.effectiveAcceptance,
|
|
2113
2144
|
output: outputForAcceptance,
|
|
@@ -2126,39 +2157,31 @@ async function runSingleStepInner(
|
|
|
2126
2157
|
: undefined;
|
|
2127
2158
|
const stoppedAfterAcceptance = finalResult?.stopped === true || ctx.stopSignal?.aborted === true;
|
|
2128
2159
|
const timedOutAfterAcceptance = !stoppedAfterAcceptance && (finalResult?.timedOut === true || ctx.timeoutSignal?.aborted === true);
|
|
2129
|
-
const turnBudgetExceeded = finalResult?.turnBudgetExceeded === true;
|
|
2130
2160
|
const effectiveAcceptance = step.effectiveAcceptance
|
|
2131
2161
|
? stoppedAfterAcceptance
|
|
2132
2162
|
? buildSkippedAcceptanceLedger(step.effectiveAcceptance, { id: "stopped", message: "Acceptance was not evaluated because the subagent was stopped." })
|
|
2133
2163
|
: timedOutAfterAcceptance
|
|
2134
2164
|
? buildSkippedAcceptanceLedger(step.effectiveAcceptance, { id: "timeout", message: "Acceptance was not evaluated because the subagent timed out." })
|
|
2135
|
-
:
|
|
2136
|
-
? buildSkippedAcceptanceLedger(step.effectiveAcceptance, { id: "turn-budget", message: "Acceptance was not evaluated because the subagent exceeded its turn budget." })
|
|
2137
|
-
: acceptance
|
|
2165
|
+
: acceptance
|
|
2138
2166
|
: undefined;
|
|
2139
2167
|
const acceptanceFailure = effectiveAcceptance ? acceptanceFailureMessage(effectiveAcceptance) : undefined;
|
|
2140
|
-
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !
|
|
2141
|
-
const effectiveFinalExitCode = timedOutAfterAcceptance || stoppedAfterAcceptance
|
|
2168
|
+
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !isAgentContractV1(step.agentContract);
|
|
2169
|
+
const effectiveFinalExitCode = timedOutAfterAcceptance || stoppedAfterAcceptance ? 1 : acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
2142
2170
|
const intercomDetachReceipt = finalResult?.finalOutput === INTERCOM_DETACH_RECEIPT;
|
|
2143
2171
|
const baseFinalError = stoppedAfterAcceptance
|
|
2144
2172
|
? ctx.stopMessage ?? "Subagent stopped by user."
|
|
2145
2173
|
: timedOutAfterAcceptance
|
|
2146
2174
|
? finalResult?.error ?? ctx.timeoutMessage ?? "Subagent timed out."
|
|
2147
|
-
:
|
|
2148
|
-
? finalResult?.error ?? (turnBudget ? turnBudgetExceededMessage(turnBudget, turnBudget.turnCount) : "Subagent exceeded turn budget.")
|
|
2149
|
-
: acceptanceCanFailRun
|
|
2175
|
+
: acceptanceCanFailRun
|
|
2150
2176
|
? (finalResult?.error ? `${finalResult.error}\n${acceptanceFailure}` : acceptanceFailure)
|
|
2151
2177
|
: finalResult?.error ?? (intercomDetachReceipt ? INTERCOM_DETACH_RECEIPT : undefined);
|
|
2152
2178
|
const effectiveFinalError = formatChildFailureDiagnostic({
|
|
2153
2179
|
error: baseFinalError,
|
|
2154
2180
|
afterCompactionSettlement: effectiveFinalExitCode !== 0 ? finalResult?.afterCompactionSettlement : undefined,
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
: undefined,
|
|
2158
|
-
missingStructuredOutput: effectiveFinalExitCode !== 0 && finalResult?.effects?.settlementDiagnostic?.requiredOutput?.kind === "structured" && finalResult.effects.settlementDiagnostic.requiredOutput.missing
|
|
2159
|
-
? finalResult.effects.settlementDiagnostic.requiredOutput.path
|
|
2160
|
-
: undefined,
|
|
2181
|
+
abortRecoveryDiagnostic: effectiveFinalExitCode !== 0 ? finalResult?.abortRecoveryDiagnostic : undefined,
|
|
2182
|
+
requiredOutput: effectiveFinalExitCode !== 0 ? finalResult?.effects?.settlementDiagnostic?.requiredOutput : undefined,
|
|
2161
2183
|
});
|
|
2184
|
+
const usage = usageFromAttempts(modelAttempts);
|
|
2162
2185
|
|
|
2163
2186
|
const artifactErrors = artifactPaths && ctx.artifactConfig?.enabled !== false
|
|
2164
2187
|
? persistStepArtifacts({
|
|
@@ -2178,6 +2201,7 @@ async function runSingleStepInner(
|
|
|
2178
2201
|
model: finalResult?.model,
|
|
2179
2202
|
attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
|
|
2180
2203
|
modelAttempts,
|
|
2204
|
+
usage,
|
|
2181
2205
|
error: effectiveFinalError,
|
|
2182
2206
|
acceptance: effectiveAcceptance,
|
|
2183
2207
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
@@ -2194,6 +2218,7 @@ async function runSingleStepInner(
|
|
|
2194
2218
|
|
|
2195
2219
|
const result: StepResult & { completionGuardTriggered?: boolean } = omitUndefinedProperties({
|
|
2196
2220
|
agent: step.agent,
|
|
2221
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
2197
2222
|
context: step.context,
|
|
2198
2223
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
2199
2224
|
launchContractDigest: actualLaunchContractDigest,
|
|
@@ -2209,26 +2234,24 @@ async function runSingleStepInner(
|
|
|
2209
2234
|
modelAttempts,
|
|
2210
2235
|
contextOverflow: contextOverflow || undefined,
|
|
2211
2236
|
totalCost: costSummaryFromAttempts(modelAttempts),
|
|
2237
|
+
usage,
|
|
2212
2238
|
artifactPaths,
|
|
2213
|
-
outputSaveError: artifactErrors.outputSaveError,
|
|
2239
|
+
outputSaveError: [resolvedOutput.saveError, artifactErrors.outputSaveError].filter(Boolean).join("\n") || undefined,
|
|
2214
2240
|
metadataSaveError: artifactErrors.metadataSaveError,
|
|
2215
2241
|
transcriptPath: transcriptWriter ? artifactPaths?.transcriptPath : undefined,
|
|
2216
2242
|
transcriptError: transcriptWriter?.getError(),
|
|
2217
|
-
interrupted: timedOutAfterAcceptance || stoppedAfterAcceptance
|
|
2243
|
+
interrupted: timedOutAfterAcceptance || stoppedAfterAcceptance ? false : finalResult?.interrupted,
|
|
2218
2244
|
timedOut: timedOutAfterAcceptance ? true : finalResult?.timedOut,
|
|
2219
2245
|
stopped: stoppedAfterAcceptance ? true : finalResult?.stopped,
|
|
2220
2246
|
processSignal: finalResult?.processSignal,
|
|
2221
2247
|
timeoutRecovery,
|
|
2222
|
-
turnBudget,
|
|
2223
|
-
turnBudgetExceeded: turnBudgetExceeded || undefined,
|
|
2224
|
-
wrapUpRequested: finalResult?.wrapUpRequested || turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined,
|
|
2225
2248
|
toolBudget,
|
|
2226
2249
|
toolBudgetBlocked: toolBudgetBlocked || undefined,
|
|
2227
2250
|
completionGuardTriggered: completionGuardTriggeredFinal,
|
|
2228
2251
|
...((finalResult as (RunPiStreamingResult & { effects?: import("../../shared/types.ts").EffectsProjection }) | undefined)?.effects ? { effects: (finalResult as RunPiStreamingResult & { effects?: import("../../shared/types.ts").EffectsProjection }).effects } : {}),
|
|
2229
|
-
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance
|
|
2230
|
-
structuredOutputPath: timedOutAfterAcceptance || stoppedAfterAcceptance
|
|
2231
|
-
structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance
|
|
2252
|
+
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : (finalResult as (RunPiStreamingResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput,
|
|
2253
|
+
structuredOutputPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.outputPath,
|
|
2254
|
+
structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.schemaPath,
|
|
2232
2255
|
acceptance: effectiveAcceptance,
|
|
2233
2256
|
watchdog: finalResult?.watchdog,
|
|
2234
2257
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
@@ -2302,6 +2325,11 @@ function requiredStatusStep(statusPayload: RunnerStatusPayload, index: number):
|
|
|
2302
2325
|
return step;
|
|
2303
2326
|
}
|
|
2304
2327
|
|
|
2328
|
+
function setStatusWorktreeReference(statusStep: RunnerStatusStep, worktree: WorktreeSetup["worktrees"][number]): void {
|
|
2329
|
+
statusStep.worktreePath = worktree.path;
|
|
2330
|
+
statusStep.branch = worktree.branch;
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2305
2333
|
function markParallelGroupSetupFailure(input: {
|
|
2306
2334
|
statusPayload: RunnerStatusPayload;
|
|
2307
2335
|
results: StepResult[];
|
|
@@ -2430,6 +2458,25 @@ function resolveAsyncStepTranscriptPath(input: {
|
|
|
2430
2458
|
|
|
2431
2459
|
type SingleStepResult = Awaited<ReturnType<typeof runSingleStep>>;
|
|
2432
2460
|
|
|
2461
|
+
function missingRequiredOutputAfterUsefulMutation(result: SingleStepResult): boolean {
|
|
2462
|
+
const effects = result.effects;
|
|
2463
|
+
return effects?.settlementDiagnostic?.requiredOutput?.missing === true
|
|
2464
|
+
&& (effects.settlementDiagnostic.mutation.attempted || effects.fileMutation?.attempted === true || Boolean(effects.fileMutation?.evidence?.changedFiles.length));
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
function partialExecutionWithUsefulMutation(result: SingleStepResult): boolean {
|
|
2468
|
+
const fileMutation = result.effects?.fileMutation;
|
|
2469
|
+
return result.execution?.status === "partial" && (fileMutation?.attempted === true || Boolean(fileMutation?.evidence?.changedFiles.length));
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
function partialEvidenceResult(result: SingleStepResult): boolean {
|
|
2473
|
+
return missingRequiredOutputAfterUsefulMutation(result) || partialExecutionWithUsefulMutation(result);
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
function concreteFailureResult(result: SingleStepResult): boolean {
|
|
2477
|
+
return result.success === false && !partialEvidenceResult(result);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2433
2480
|
function combinedAbortSignal(signals: Array<AbortSignal | undefined>): AbortSignal | undefined {
|
|
2434
2481
|
const activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));
|
|
2435
2482
|
if (activeSignals.length === 0) return undefined;
|
|
@@ -2501,6 +2548,8 @@ async function runSubagent(
|
|
|
2501
2548
|
const overallStartTime = Date.now();
|
|
2502
2549
|
const shareEnabled = config.share === true;
|
|
2503
2550
|
const asyncDir = config.asyncDir;
|
|
2551
|
+
const handoffWorkflowKey = config.workflowKey;
|
|
2552
|
+
const handoffChildRunId = handoffWorkflowKey ? id : undefined;
|
|
2504
2553
|
const statusPath = path.join(asyncDir, "status.json");
|
|
2505
2554
|
const eventsPath = path.join(asyncDir, "events.jsonl");
|
|
2506
2555
|
const logPath = path.join(asyncDir, `subagent-log-${id}.md`);
|
|
@@ -2509,7 +2558,6 @@ async function runSubagent(
|
|
|
2509
2558
|
const activeChildTimeouts = new Map<number, () => void>();
|
|
2510
2559
|
const activeChildStops = new Map<number, () => void>();
|
|
2511
2560
|
const childStopRequests = new Map<number, { childId: string; requestedAt: number }>();
|
|
2512
|
-
const activeChildTurnBudgetAborts = new Map<number, (message: string, state?: TurnBudgetState) => void>();
|
|
2513
2561
|
const pendingStepSteers: SteerRequest[] = [];
|
|
2514
2562
|
const steeringCapabilities = new Map<number, SteerCapability>();
|
|
2515
2563
|
let interrupted = false;
|
|
@@ -2518,7 +2566,6 @@ async function runSubagent(
|
|
|
2518
2566
|
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
2519
2567
|
let timedOut = false;
|
|
2520
2568
|
let stopped = false;
|
|
2521
|
-
let turnBudgetExceeded = false;
|
|
2522
2569
|
let usageBudgetExceeded = false;
|
|
2523
2570
|
const timeoutMessage = config.timeoutMs !== undefined ? `Subagent timed out after ${config.timeoutMs}ms.` : undefined;
|
|
2524
2571
|
const stopMessage = "Subagent stopped by user.";
|
|
@@ -2539,8 +2586,11 @@ async function runSubagent(
|
|
|
2539
2586
|
for (const task of step.parallel) {
|
|
2540
2587
|
const taskFlatIndex = flatStepCount;
|
|
2541
2588
|
const transcriptPath = resolveAsyncStepTranscriptPath(omitUndefinedProperties({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: taskFlatIndex, flatStepCount: initialFlatStepCount }));
|
|
2589
|
+
const taskSessionName = task.sessionName ?? deriveChildSessionName({ agent: task.agent, task: task.task, label: task.label });
|
|
2542
2590
|
initialStatusSteps.push(omitUndefinedProperties({
|
|
2543
2591
|
agent: task.agent,
|
|
2592
|
+
...(task.lane ? { lane: task.lane } : config.lane ? { lane: config.lane } : {}),
|
|
2593
|
+
...(taskSessionName ? { sessionName: taskSessionName } : {}),
|
|
2544
2594
|
...(externalRunnerStatus(task.runner) ? { runner: externalRunnerStatus(task.runner) } : {}),
|
|
2545
2595
|
...(statusStepDescription(task.task) ? { description: statusStepDescription(task.task) } : {}),
|
|
2546
2596
|
...(task.context ? { context: task.context } : {}),
|
|
@@ -2590,8 +2640,11 @@ async function runSubagent(
|
|
|
2590
2640
|
} else {
|
|
2591
2641
|
const stepFlatIndex = flatStepCount;
|
|
2592
2642
|
const transcriptPath = resolveAsyncStepTranscriptPath(omitUndefinedProperties({ artifactsDir, artifactConfig, runId: id, agent: step.agent, flatIndex: stepFlatIndex, flatStepCount: initialFlatStepCount }));
|
|
2643
|
+
const stepSessionName = step.sessionName ?? deriveChildSessionName({ agent: step.agent, task: step.task, label: step.label });
|
|
2593
2644
|
initialStatusSteps.push(omitUndefinedProperties({
|
|
2594
2645
|
agent: step.agent,
|
|
2646
|
+
...(step.lane ? { lane: step.lane } : config.lane ? { lane: config.lane } : {}),
|
|
2647
|
+
...(stepSessionName ? { sessionName: stepSessionName } : {}),
|
|
2595
2648
|
...(externalRunnerStatus(step.runner) ? { runner: externalRunnerStatus(step.runner) } : {}),
|
|
2596
2649
|
...(statusStepDescription(step.task) ? { description: statusStepDescription(step.task) } : {}),
|
|
2597
2650
|
...(step.context ? { context: step.context } : {}),
|
|
@@ -2654,7 +2707,6 @@ async function runSubagent(
|
|
|
2654
2707
|
lastUpdate: overallStartTime,
|
|
2655
2708
|
...(config.timeoutMs !== undefined ? { timeoutMs: config.timeoutMs } : {}),
|
|
2656
2709
|
...(config.deadlineAt !== undefined ? { deadlineAt: config.deadlineAt } : {}),
|
|
2657
|
-
...(config.turnBudget ? { turnBudget: initialTurnBudgetState(config.turnBudget) } : {}),
|
|
2658
2710
|
...(config.toolBudget ? { toolBudget: initialToolBudgetState(config.toolBudget) } : {}),
|
|
2659
2711
|
...(config.usageBudget ? { usageBudget: usageBudgetState(config.usageBudget, undefined) } : {}),
|
|
2660
2712
|
pid: process.pid,
|
|
@@ -2669,6 +2721,7 @@ async function runSubagent(
|
|
|
2669
2721
|
...(config.runFanoutBudget ? { runFanoutBudget: getRunFanoutBudgetSnapshot(config.runFanoutBudget) } : {}),
|
|
2670
2722
|
...(config.parentWorkflowRunId ? { parentWorkflowRunId: config.parentWorkflowRunId } : {}),
|
|
2671
2723
|
...(config.workflowKey ? { workflowKey: config.workflowKey } : {}),
|
|
2724
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
2672
2725
|
...(config.runnerProcessInstanceId ? { processTerminal: { version: 1 as const, state: "pending" as const, runId: id, runnerProcessInstanceId: config.runnerProcessInstanceId } } : {}),
|
|
2673
2726
|
steps: initialStatusSteps,
|
|
2674
2727
|
artifactsDir,
|
|
@@ -2786,6 +2839,7 @@ async function runSubagent(
|
|
|
2786
2839
|
const statusResultSummary = (state: AsyncStatus["state"]): string => {
|
|
2787
2840
|
if (statusPayload.error) return statusPayload.error;
|
|
2788
2841
|
if (state === "paused") return "Paused after interrupt. Waiting for explicit next action.";
|
|
2842
|
+
if (state === "partial") return "Subagent needs attention after partial work.";
|
|
2789
2843
|
if (state === "stopped") return stopMessage;
|
|
2790
2844
|
if (state === "rejected") return "Subagent rejected.";
|
|
2791
2845
|
return state === "complete" ? "Subagent completed." : "Subagent failed.";
|
|
@@ -2794,12 +2848,13 @@ async function runSubagent(
|
|
|
2794
2848
|
if (step.status === "complete" || step.status === "completed") return true;
|
|
2795
2849
|
if (step.status === "failed" || step.status === "stopped" || step.status === "rejected") return false;
|
|
2796
2850
|
if (state === "complete") return true;
|
|
2797
|
-
if (state === "failed" || state === "stopped" || state === "rejected") return false;
|
|
2851
|
+
if (state === "failed" || state === "partial" || state === "stopped" || state === "rejected") return false;
|
|
2798
2852
|
return undefined;
|
|
2799
2853
|
};
|
|
2800
2854
|
const writeRecoverableStatusResult = (): void => {
|
|
2801
2855
|
const state = statusResultState();
|
|
2802
2856
|
if (!state || finalResultCommitted || !config.sessionId) return;
|
|
2857
|
+
if ((state as string) === "complete") return;
|
|
2803
2858
|
const now = statusPayload.endedAt ?? statusPayload.lastUpdate ?? Date.now();
|
|
2804
2859
|
const summary = statusResultSummary(state);
|
|
2805
2860
|
runPersistence.write(resultPath, omitUndefinedProperties({
|
|
@@ -2811,10 +2866,11 @@ async function runSubagent(
|
|
|
2811
2866
|
success: state === "complete",
|
|
2812
2867
|
state,
|
|
2813
2868
|
summary,
|
|
2814
|
-
error: state === "failed" || state === "stopped" || state === "rejected" ? summary : undefined,
|
|
2869
|
+
error: state === "failed" || state === "partial" || state === "stopped" || state === "rejected" ? summary : undefined,
|
|
2815
2870
|
stopped: state === "stopped" ? true : undefined,
|
|
2816
2871
|
results: statusPayload.steps.map((step) => omitUndefinedProperties({
|
|
2817
2872
|
agent: step.agent,
|
|
2873
|
+
...(step.sessionName ? { sessionName: step.sessionName } : {}),
|
|
2818
2874
|
output: step.status === "complete" || step.status === "completed" ? "" : step.error ?? summary,
|
|
2819
2875
|
error: step.error,
|
|
2820
2876
|
success: statusResultSuccess(state, step),
|
|
@@ -2822,6 +2878,7 @@ async function runSubagent(
|
|
|
2822
2878
|
model: step.model,
|
|
2823
2879
|
attemptedModels: step.attemptedModels,
|
|
2824
2880
|
modelAttempts: step.modelAttempts,
|
|
2881
|
+
usage: usageFromAttempts(step.modelAttempts),
|
|
2825
2882
|
contextOverflow: step.contextOverflow,
|
|
2826
2883
|
})),
|
|
2827
2884
|
exitCode: state === "complete" || state === "paused" ? 0 : 1,
|
|
@@ -2916,7 +2973,7 @@ async function runSubagent(
|
|
|
2916
2973
|
};
|
|
2917
2974
|
const childStopResult = (index: number, agent: string, context?: "fresh" | "fork"): SingleStepResult => {
|
|
2918
2975
|
markChildStopped(index);
|
|
2919
|
-
return stoppedStepResult(agent, context);
|
|
2976
|
+
return stoppedStepResult(agent, context, requiredStatusStep(statusPayload, index).sessionName);
|
|
2920
2977
|
};
|
|
2921
2978
|
const stopChildStep = (request: StopRequest): void => {
|
|
2922
2979
|
if (request.targetIndex === undefined) {
|
|
@@ -2959,13 +3016,6 @@ async function runSubagent(
|
|
|
2959
3016
|
activeChildStops.set(flatIndex, stop);
|
|
2960
3017
|
if (stopped || childStopRequests.has(flatIndex)) stop();
|
|
2961
3018
|
};
|
|
2962
|
-
const registerStepTurnBudgetAbort = (flatIndex: number, abort: ((message: string, state?: TurnBudgetState) => void) | undefined): void => {
|
|
2963
|
-
if (!abort) {
|
|
2964
|
-
activeChildTurnBudgetAborts.delete(flatIndex);
|
|
2965
|
-
return;
|
|
2966
|
-
}
|
|
2967
|
-
activeChildTurnBudgetAborts.set(flatIndex, abort);
|
|
2968
|
-
};
|
|
2969
3019
|
const interruptActiveChildren = (): void => {
|
|
2970
3020
|
for (const interrupt of [...activeChildInterrupts.values()]) interrupt();
|
|
2971
3021
|
};
|
|
@@ -3075,23 +3125,26 @@ async function runSubagent(
|
|
|
3075
3125
|
}
|
|
3076
3126
|
}
|
|
3077
3127
|
};
|
|
3078
|
-
const pausedStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => omitUndefinedProperties({
|
|
3128
|
+
const pausedStepResult = (agent: string, context?: "fresh" | "fork", sessionName?: string): SingleStepResult => omitUndefinedProperties({
|
|
3079
3129
|
agent,
|
|
3130
|
+
sessionName,
|
|
3080
3131
|
context,
|
|
3081
3132
|
output: "Paused after interrupt. Waiting for explicit next action.",
|
|
3082
3133
|
exitCode: 0,
|
|
3083
3134
|
interrupted: true,
|
|
3084
3135
|
});
|
|
3085
|
-
const timedOutStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => omitUndefinedProperties({
|
|
3136
|
+
const timedOutStepResult = (agent: string, context?: "fresh" | "fork", sessionName?: string): SingleStepResult => omitUndefinedProperties({
|
|
3086
3137
|
agent,
|
|
3138
|
+
sessionName,
|
|
3087
3139
|
context,
|
|
3088
3140
|
output: timeoutMessage ?? "Subagent timed out.",
|
|
3089
3141
|
error: timeoutMessage ?? "Subagent timed out.",
|
|
3090
3142
|
exitCode: 1,
|
|
3091
3143
|
timedOut: true,
|
|
3092
3144
|
});
|
|
3093
|
-
const stoppedStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => omitUndefinedProperties({
|
|
3145
|
+
const stoppedStepResult = (agent: string, context?: "fresh" | "fork", sessionName?: string): SingleStepResult => omitUndefinedProperties({
|
|
3094
3146
|
agent,
|
|
3147
|
+
sessionName,
|
|
3095
3148
|
context,
|
|
3096
3149
|
output: stopMessage,
|
|
3097
3150
|
error: stopMessage,
|
|
@@ -3481,66 +3534,6 @@ async function runSubagent(
|
|
|
3481
3534
|
statusPayload.lastUpdate = now;
|
|
3482
3535
|
writeStatusPayload();
|
|
3483
3536
|
};
|
|
3484
|
-
const updateStepTurnBudget = (
|
|
3485
|
-
flatIndex: number,
|
|
3486
|
-
turnCount: number,
|
|
3487
|
-
now: number,
|
|
3488
|
-
terminalAssistantStop: boolean,
|
|
3489
|
-
toolWorkActiveOrStarting: boolean,
|
|
3490
|
-
): void => {
|
|
3491
|
-
const budget = config.turnBudget;
|
|
3492
|
-
const step = statusPayload.steps[flatIndex];
|
|
3493
|
-
if (!budget || !step || timedOut || stopped || turnBudgetExceeded || step.turnBudgetExceeded) return;
|
|
3494
|
-
if (turnCount < budget.maxTurns) {
|
|
3495
|
-
const state: TurnBudgetState = { ...budget, outcome: "within-budget", turnCount };
|
|
3496
|
-
step.turnBudget = state;
|
|
3497
|
-
statusPayload.turnBudget = state;
|
|
3498
|
-
return;
|
|
3499
|
-
}
|
|
3500
|
-
if (!step.wrapUpRequested) {
|
|
3501
|
-
step.wrapUpRequested = true;
|
|
3502
|
-
statusPayload.wrapUpRequested = true;
|
|
3503
|
-
appendRecentStepOutput(step, [turnBudgetSoftNote(budget, turnCount)]);
|
|
3504
|
-
}
|
|
3505
|
-
const decision = turnBudgetDecision(budget, turnCount, terminalAssistantStop, toolWorkActiveOrStarting);
|
|
3506
|
-
if (decision === "defer") {
|
|
3507
|
-
const firstDeferredTurn = step.turnBudget?.terminationDeferredAtTurn;
|
|
3508
|
-
const deferredState = turnBudgetDeferredState(budget, turnCount, firstDeferredTurn);
|
|
3509
|
-
step.turnBudget = deferredState;
|
|
3510
|
-
statusPayload.turnBudget = deferredState;
|
|
3511
|
-
if (firstDeferredTurn === undefined) {
|
|
3512
|
-
appendJsonl(eventsPath, JSON.stringify({
|
|
3513
|
-
type: "subagent.step.turn_budget_deferred",
|
|
3514
|
-
ts: now,
|
|
3515
|
-
runId: id,
|
|
3516
|
-
stepIndex: flatIndex,
|
|
3517
|
-
agent: step.agent,
|
|
3518
|
-
turnCount,
|
|
3519
|
-
maxTurns: budget.maxTurns,
|
|
3520
|
-
graceTurns: budget.graceTurns,
|
|
3521
|
-
}));
|
|
3522
|
-
}
|
|
3523
|
-
return;
|
|
3524
|
-
}
|
|
3525
|
-
const state = turnBudgetState(budget, turnCount, false);
|
|
3526
|
-
step.turnBudget = state;
|
|
3527
|
-
statusPayload.turnBudget = state;
|
|
3528
|
-
if (decision !== "abort") return;
|
|
3529
|
-
const exceededState = turnBudgetState(budget, turnCount, true);
|
|
3530
|
-
const message = turnBudgetExceededMessage(budget, turnCount);
|
|
3531
|
-
step.turnBudget = exceededState;
|
|
3532
|
-
step.turnBudgetExceeded = true;
|
|
3533
|
-
step.wrapUpRequested = true;
|
|
3534
|
-
step.error = message;
|
|
3535
|
-
turnBudgetExceeded = true;
|
|
3536
|
-
statusPayload.turnBudget = exceededState;
|
|
3537
|
-
statusPayload.turnBudgetExceeded = true;
|
|
3538
|
-
statusPayload.wrapUpRequested = true;
|
|
3539
|
-
statusPayload.error = message;
|
|
3540
|
-
statusPayload.lastUpdate = now;
|
|
3541
|
-
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.turn_budget_exceeded", ts: now, runId: id, stepIndex: flatIndex, agent: step.agent, turnCount, maxTurns: budget.maxTurns, graceTurns: budget.graceTurns, message }));
|
|
3542
|
-
activeChildTurnBudgetAborts.get(flatIndex)?.(message, exceededState);
|
|
3543
|
-
};
|
|
3544
3537
|
const updateStepFromChildEvent = (flatIndex: number, event: ChildEvent): void => {
|
|
3545
3538
|
const step = statusPayload.steps[flatIndex];
|
|
3546
3539
|
if (!step) return;
|
|
@@ -3681,13 +3674,6 @@ async function runSubagent(
|
|
|
3681
3674
|
refreshUsageBudget();
|
|
3682
3675
|
}
|
|
3683
3676
|
statusPayload.turnCount = Math.max(statusPayload.turnCount ?? 0, step.turnCount);
|
|
3684
|
-
updateStepTurnBudget(
|
|
3685
|
-
flatIndex,
|
|
3686
|
-
step.turnCount,
|
|
3687
|
-
now,
|
|
3688
|
-
isTerminalAssistantStop(event.message),
|
|
3689
|
-
assistantStartsToolCall(event.message) || Boolean(step.currentTool),
|
|
3690
|
-
);
|
|
3691
3677
|
}
|
|
3692
3678
|
syncTopLevelCurrentTool();
|
|
3693
3679
|
step.lastActivityAt = now;
|
|
@@ -3916,7 +3902,7 @@ async function runSubagent(
|
|
|
3916
3902
|
let flatIndex = 0;
|
|
3917
3903
|
let stepCursor = 0;
|
|
3918
3904
|
while (true) {
|
|
3919
|
-
if (interrupted || timedOut || stopped
|
|
3905
|
+
if (interrupted || timedOut || stopped) break;
|
|
3920
3906
|
consumePendingAppendRequests();
|
|
3921
3907
|
if (stepCursor >= steps.length) break;
|
|
3922
3908
|
refreshUsageBudget();
|
|
@@ -4061,10 +4047,12 @@ async function runSubagent(
|
|
|
4061
4047
|
: step.parallel.outputPath;
|
|
4062
4048
|
const taskText = task.task ?? step.parallel.task;
|
|
4063
4049
|
const materializedTask = step.parallel.namespaceOutputPath ? injectSingleOutputInstruction(taskText, outputPath, step.parallel) : taskText;
|
|
4050
|
+
const sessionName = deriveChildSessionName({ agent: step.parallel.agent, task: taskText, label: task.label ?? step.parallel.label });
|
|
4064
4051
|
return omitUndefinedProperties({
|
|
4065
4052
|
...step.parallel,
|
|
4066
4053
|
runFanoutPath: `chain[${stepIndex}].expand[${itemIndex}]`,
|
|
4067
4054
|
task: materializedTask,
|
|
4055
|
+
...(sessionName ? { sessionName } : {}),
|
|
4068
4056
|
effectiveAcceptance: resolveEffectiveAcceptance(omitUndefinedProperties({
|
|
4069
4057
|
explicit: step.parallel.acceptanceInput,
|
|
4070
4058
|
agentName: step.parallel.agent,
|
|
@@ -4095,6 +4083,7 @@ async function runSubagent(
|
|
|
4095
4083
|
const transcriptPath = resolveAsyncStepTranscriptPath(omitUndefinedProperties({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: groupStartFlatIndex + itemIndex, flatStepCount: dynamicFlatStepCount }));
|
|
4096
4084
|
return omitUndefinedProperties({
|
|
4097
4085
|
agent: task.agent,
|
|
4086
|
+
...(task.sessionName ? { sessionName: task.sessionName } : {}),
|
|
4098
4087
|
...(statusStepDescription(task.task) ? { description: statusStepDescription(task.task) } : {}),
|
|
4099
4088
|
...(task.context ? { context: task.context } : {}),
|
|
4100
4089
|
...(task.phase ?? step.phase ? { phase: task.phase ?? step.phase } : {}),
|
|
@@ -4178,12 +4167,12 @@ async function runSubagent(
|
|
|
4178
4167
|
usageBudgetExceeded = true;
|
|
4179
4168
|
writeStatusPayload();
|
|
4180
4169
|
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.failed", ts: skippedAt, runId: id, stepIndex: fi, agent: task.agent, exitCode: 1, durationMs: 0 }));
|
|
4181
|
-
return omitUndefinedProperties({ agent: task.agent, context: task.context, output: message, error: message, exitCode: 1 as number | null, skipped: true });
|
|
4170
|
+
return omitUndefinedProperties({ agent: task.agent, ...(task.sessionName ? { sessionName: task.sessionName } : {}), context: task.context, output: message, error: message, exitCode: 1 as number | null, skipped: true });
|
|
4182
4171
|
}
|
|
4183
|
-
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
4184
|
-
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
4172
|
+
if (timedOut) return timedOutStepResult(task.agent, task.context, task.sessionName);
|
|
4173
|
+
if (stopped) return stoppedStepResult(task.agent, task.context, task.sessionName);
|
|
4185
4174
|
if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
|
|
4186
|
-
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
4175
|
+
if (interrupted) return pausedStepResult(task.agent, task.context, task.sessionName);
|
|
4187
4176
|
if (aborted && failFast) {
|
|
4188
4177
|
const skippedAt = Date.now();
|
|
4189
4178
|
requiredStatusStep(statusPayload, fi).status = "failed";
|
|
@@ -4194,7 +4183,7 @@ async function runSubagent(
|
|
|
4194
4183
|
requiredStatusStep(statusPayload, fi).exitCode = -1;
|
|
4195
4184
|
statusPayload.lastUpdate = skippedAt;
|
|
4196
4185
|
writeStatusPayload();
|
|
4197
|
-
return omitUndefinedProperties({ agent: task.agent, context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true });
|
|
4186
|
+
return omitUndefinedProperties({ agent: task.agent, ...(task.sessionName ? { sessionName: task.sessionName } : {}), context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true });
|
|
4198
4187
|
}
|
|
4199
4188
|
const taskStartTime = Date.now();
|
|
4200
4189
|
statusPayload.currentStep = fi;
|
|
@@ -4230,13 +4219,11 @@ async function runSubagent(
|
|
|
4230
4219
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4231
4220
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4232
4221
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
4233
|
-
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
4234
4222
|
timeoutSignal: timeoutAbortController.signal,
|
|
4235
4223
|
stopSignal: stopAbortController.signal,
|
|
4236
4224
|
trackedMutationEvidenceForCompletionGuard: false,
|
|
4237
4225
|
timeoutMessage,
|
|
4238
4226
|
stopMessage,
|
|
4239
|
-
turnBudget: config.turnBudget,
|
|
4240
4227
|
toolTimeoutMs: task.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4241
4228
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4242
4229
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
@@ -4244,6 +4231,7 @@ async function runSubagent(
|
|
|
4244
4231
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4245
4232
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4246
4233
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4234
|
+
usageBudgetExhausted: () => refreshUsageBudget()?.exhausted === true,
|
|
4247
4235
|
orcaProgressTab,
|
|
4248
4236
|
}), config.deadlineAt);
|
|
4249
4237
|
const taskEndTime = Date.now();
|
|
@@ -4255,16 +4243,11 @@ async function runSubagent(
|
|
|
4255
4243
|
requiredStatusStep(statusPayload, fi).exitCode = stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
4256
4244
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "timedOut", timedOut || singleResult.timedOut ? true : undefined);
|
|
4257
4245
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "stopped", stopped || childStopped ? true : undefined);
|
|
4258
|
-
setOptionalProperty(requiredStatusStep(statusPayload, fi), "turnBudget", singleResult.turnBudget);
|
|
4259
|
-
setOptionalProperty(requiredStatusStep(statusPayload, fi), "turnBudgetExceeded", singleResult.turnBudgetExceeded);
|
|
4260
|
-
setOptionalProperty(requiredStatusStep(statusPayload, fi), "wrapUpRequested", singleResult.wrapUpRequested);
|
|
4261
4246
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudget", singleResult.toolBudget);
|
|
4262
4247
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudgetBlocked", singleResult.toolBudgetBlocked);
|
|
4263
4248
|
if (singleResult.toolBudget) statusPayload.toolBudget = singleResult.toolBudget;
|
|
4264
4249
|
if (singleResult.toolBudgetBlocked) statusPayload.toolBudgetBlocked = true;
|
|
4265
|
-
|
|
4266
|
-
if (singleResult.turnBudgetExceeded) statusPayload.turnBudgetExceeded = true;
|
|
4267
|
-
if (singleResult.wrapUpRequested) statusPayload.wrapUpRequested = true;
|
|
4250
|
+
setOptionalProperty(requiredStatusStep(statusPayload, fi), "sessionName", singleResult.sessionName);
|
|
4268
4251
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "model", singleResult.model);
|
|
4269
4252
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, fi).thinking));
|
|
4270
4253
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "attemptedModels", singleResult.attemptedModels);
|
|
@@ -4316,6 +4299,7 @@ async function runSubagent(
|
|
|
4316
4299
|
for (const pr of parallelResults) {
|
|
4317
4300
|
results.push(omitUndefinedProperties({
|
|
4318
4301
|
agent: pr.agent,
|
|
4302
|
+
...(pr.sessionName ? { sessionName: pr.sessionName } : {}),
|
|
4319
4303
|
context: pr.context,
|
|
4320
4304
|
agentContract: pr.agentContract,
|
|
4321
4305
|
launchContractDigest: pr.launchContractDigest,
|
|
@@ -4331,9 +4315,6 @@ async function runSubagent(
|
|
|
4331
4315
|
interrupted: pr.interrupted,
|
|
4332
4316
|
timedOut: pr.timedOut,
|
|
4333
4317
|
stopped: pr.stopped,
|
|
4334
|
-
turnBudget: pr.turnBudget,
|
|
4335
|
-
turnBudgetExceeded: pr.turnBudgetExceeded,
|
|
4336
|
-
wrapUpRequested: pr.wrapUpRequested,
|
|
4337
4318
|
toolBudget: pr.toolBudget,
|
|
4338
4319
|
toolBudgetBlocked: pr.toolBudgetBlocked,
|
|
4339
4320
|
sessionFile: pr.sessionFile,
|
|
@@ -4343,6 +4324,7 @@ async function runSubagent(
|
|
|
4343
4324
|
modelAttempts: pr.modelAttempts,
|
|
4344
4325
|
contextOverflow: pr.contextOverflow,
|
|
4345
4326
|
totalCost: pr.totalCost,
|
|
4327
|
+
usage: pr.usage,
|
|
4346
4328
|
artifactPaths: pr.artifactPaths,
|
|
4347
4329
|
transcriptPath: pr.transcriptPath,
|
|
4348
4330
|
transcriptError: pr.transcriptError,
|
|
@@ -4486,6 +4468,23 @@ async function runSubagent(
|
|
|
4486
4468
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
4487
4469
|
: undefined,
|
|
4488
4470
|
baseDir: config.worktreeBaseDir,
|
|
4471
|
+
beforeCreate: (plannedSetup) => {
|
|
4472
|
+
for (const worktree of plannedSetup.worktrees) setStatusWorktreeReference(requiredStatusStep(statusPayload, groupStartFlatIndex + worktree.index), worktree);
|
|
4473
|
+
const pendingHandoff = writePendingParallelHandoff({
|
|
4474
|
+
manifestPath: parallelHandoffPath(asyncDir),
|
|
4475
|
+
runId: id,
|
|
4476
|
+
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" : "chain",
|
|
4477
|
+
source: "async",
|
|
4478
|
+
cwd,
|
|
4479
|
+
stepIndex,
|
|
4480
|
+
flatStartIndex: groupStartFlatIndex,
|
|
4481
|
+
setup: plannedSetup,
|
|
4482
|
+
laneBindings: handoffWorkflowKey || config.lane ? [{ index: groupStartFlatIndex, taskIndex: 0, ...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}), ...(handoffChildRunId ? { runId: handoffChildRunId } : {}), ...(config.lane ? { lane: config.lane } : {}) }] : undefined,
|
|
4483
|
+
});
|
|
4484
|
+
statusPayload.parallelHandoff = pendingHandoff;
|
|
4485
|
+
statusPayload.lastUpdate = Date.now();
|
|
4486
|
+
writeStatusPayload();
|
|
4487
|
+
},
|
|
4489
4488
|
}));
|
|
4490
4489
|
} catch (error) {
|
|
4491
4490
|
const setupError = error instanceof Error ? error.message : String(error);
|
|
@@ -4510,18 +4509,6 @@ async function runSubagent(
|
|
|
4510
4509
|
}
|
|
4511
4510
|
|
|
4512
4511
|
try {
|
|
4513
|
-
if (worktreeSetup) {
|
|
4514
|
-
writePendingParallelHandoff({
|
|
4515
|
-
manifestPath: parallelHandoffPath(asyncDir),
|
|
4516
|
-
runId: id,
|
|
4517
|
-
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" : "chain",
|
|
4518
|
-
source: "async",
|
|
4519
|
-
cwd,
|
|
4520
|
-
stepIndex,
|
|
4521
|
-
flatStartIndex: groupStartFlatIndex,
|
|
4522
|
-
setup: worktreeSetup,
|
|
4523
|
-
});
|
|
4524
|
-
}
|
|
4525
4512
|
if (group.worktree) ensureParallelProgressFile(cwd, group);
|
|
4526
4513
|
const groupStartTime = Date.now();
|
|
4527
4514
|
markParallelGroupRunning({
|
|
@@ -4558,12 +4545,12 @@ async function runSubagent(
|
|
|
4558
4545
|
appendJsonl(eventsPath, JSON.stringify({
|
|
4559
4546
|
type: "subagent.step.failed", ts: skippedAt, runId: id, stepIndex: fi, agent: task.agent, exitCode: 1, durationMs: 0,
|
|
4560
4547
|
}));
|
|
4561
|
-
return omitUndefinedProperties({ agent: task.agent, context: task.context, output: message, error: message, exitCode: 1 as number | null, skipped: true });
|
|
4548
|
+
return omitUndefinedProperties({ agent: task.agent, ...(task.sessionName ? { sessionName: task.sessionName } : {}), context: task.context, output: message, error: message, exitCode: 1 as number | null, skipped: true });
|
|
4562
4549
|
}
|
|
4563
|
-
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
4564
|
-
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
4550
|
+
if (timedOut) return timedOutStepResult(task.agent, task.context, task.sessionName);
|
|
4551
|
+
if (stopped) return stoppedStepResult(task.agent, task.context, task.sessionName);
|
|
4565
4552
|
if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
|
|
4566
|
-
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
4553
|
+
if (interrupted) return pausedStepResult(task.agent, task.context, task.sessionName);
|
|
4567
4554
|
if (aborted && failFast) {
|
|
4568
4555
|
const skippedAt = Date.now();
|
|
4569
4556
|
requiredStatusStep(statusPayload, fi).status = "failed";
|
|
@@ -4578,7 +4565,7 @@ async function runSubagent(
|
|
|
4578
4565
|
appendJsonl(eventsPath, JSON.stringify({
|
|
4579
4566
|
type: "subagent.step.failed", ts: skippedAt, runId: id, stepIndex: fi, agent: task.agent, exitCode: -1, durationMs: 0,
|
|
4580
4567
|
}));
|
|
4581
|
-
return omitUndefinedProperties({ agent: task.agent, context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true });
|
|
4568
|
+
return omitUndefinedProperties({ agent: task.agent, ...(task.sessionName ? { sessionName: task.sessionName } : {}), context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true });
|
|
4582
4569
|
}
|
|
4583
4570
|
|
|
4584
4571
|
const taskStartTime = Date.now();
|
|
@@ -4626,13 +4613,11 @@ async function runSubagent(
|
|
|
4626
4613
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4627
4614
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4628
4615
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
4629
|
-
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
4630
4616
|
timeoutSignal: timeoutAbortController.signal,
|
|
4631
4617
|
stopSignal: stopAbortController.signal,
|
|
4632
4618
|
trackedMutationEvidenceForCompletionGuard: Boolean(worktreeSetup),
|
|
4633
4619
|
timeoutMessage,
|
|
4634
4620
|
stopMessage,
|
|
4635
|
-
turnBudget: config.turnBudget,
|
|
4636
4621
|
toolTimeoutMs: taskForRun.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4637
4622
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4638
4623
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
@@ -4640,6 +4625,7 @@ async function runSubagent(
|
|
|
4640
4625
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4641
4626
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4642
4627
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4628
|
+
usageBudgetExhausted: () => refreshUsageBudget()?.exhausted === true,
|
|
4643
4629
|
orcaProgressTab,
|
|
4644
4630
|
}), config.deadlineAt);
|
|
4645
4631
|
if (task.sessionFile) {
|
|
@@ -4657,16 +4643,11 @@ async function runSubagent(
|
|
|
4657
4643
|
requiredStatusStep(statusPayload, fi).exitCode = stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
4658
4644
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "timedOut", timedOut || singleResult.timedOut ? true : undefined);
|
|
4659
4645
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "stopped", stopped || childStopped ? true : undefined);
|
|
4660
|
-
setOptionalProperty(requiredStatusStep(statusPayload, fi), "turnBudget", singleResult.turnBudget);
|
|
4661
|
-
setOptionalProperty(requiredStatusStep(statusPayload, fi), "turnBudgetExceeded", singleResult.turnBudgetExceeded);
|
|
4662
|
-
setOptionalProperty(requiredStatusStep(statusPayload, fi), "wrapUpRequested", singleResult.wrapUpRequested);
|
|
4663
4646
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudget", singleResult.toolBudget);
|
|
4664
4647
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudgetBlocked", singleResult.toolBudgetBlocked);
|
|
4665
4648
|
if (singleResult.toolBudget) statusPayload.toolBudget = singleResult.toolBudget;
|
|
4666
4649
|
if (singleResult.toolBudgetBlocked) statusPayload.toolBudgetBlocked = true;
|
|
4667
|
-
|
|
4668
|
-
if (singleResult.turnBudgetExceeded) statusPayload.turnBudgetExceeded = true;
|
|
4669
|
-
if (singleResult.wrapUpRequested) statusPayload.wrapUpRequested = true;
|
|
4650
|
+
setOptionalProperty(requiredStatusStep(statusPayload, fi), "sessionName", singleResult.sessionName);
|
|
4670
4651
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "model", singleResult.model);
|
|
4671
4652
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, fi).thinking));
|
|
4672
4653
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "attemptedModels", singleResult.attemptedModels);
|
|
@@ -4775,9 +4756,6 @@ async function runSubagent(
|
|
|
4775
4756
|
interrupted: pr.interrupted,
|
|
4776
4757
|
timedOut: pr.timedOut,
|
|
4777
4758
|
stopped: pr.stopped,
|
|
4778
|
-
turnBudget: pr.turnBudget,
|
|
4779
|
-
turnBudgetExceeded: pr.turnBudgetExceeded,
|
|
4780
|
-
wrapUpRequested: pr.wrapUpRequested,
|
|
4781
4759
|
toolBudget: pr.toolBudget,
|
|
4782
4760
|
toolBudgetBlocked: pr.toolBudgetBlocked,
|
|
4783
4761
|
sessionFile: pr.sessionFile,
|
|
@@ -4787,6 +4765,7 @@ async function runSubagent(
|
|
|
4787
4765
|
modelAttempts: pr.modelAttempts,
|
|
4788
4766
|
contextOverflow: pr.contextOverflow,
|
|
4789
4767
|
totalCost: pr.totalCost,
|
|
4768
|
+
usage: pr.usage,
|
|
4790
4769
|
artifactPaths: pr.artifactPaths,
|
|
4791
4770
|
transcriptPath: pr.transcriptPath,
|
|
4792
4771
|
transcriptError: pr.transcriptError,
|
|
@@ -4840,12 +4819,14 @@ async function runSubagent(
|
|
|
4840
4819
|
diffs: captured.diffs,
|
|
4841
4820
|
results: parallelResults.map((result) => ({
|
|
4842
4821
|
agent: result.agent,
|
|
4822
|
+
...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}),
|
|
4823
|
+
...(handoffChildRunId ? { runId: handoffChildRunId } : {}),
|
|
4824
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
4843
4825
|
status: result.stopped || (result.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
4844
4826
|
processSignal: result.processSignal,
|
|
4845
4827
|
interrupted: result.interrupted,
|
|
4846
4828
|
timedOut: result.timedOut,
|
|
4847
4829
|
stopped: result.stopped,
|
|
4848
|
-
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
4849
4830
|
}))) ? "stopped" as const : result.interrupted ? "paused" as const : result.exitCode === 0 ? "completed" as const : "failed" as const,
|
|
4850
4831
|
summary: result.output || result.error || "(no output)",
|
|
4851
4832
|
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
@@ -4891,12 +4872,12 @@ async function runSubagent(
|
|
|
4891
4872
|
} else {
|
|
4892
4873
|
const seqStep = step as SubagentStep;
|
|
4893
4874
|
if (timedOut) {
|
|
4894
|
-
results.push(timedOutStepResult(seqStep.agent, seqStep.context));
|
|
4875
|
+
results.push(timedOutStepResult(seqStep.agent, seqStep.context, seqStep.sessionName));
|
|
4895
4876
|
flatIndex++;
|
|
4896
4877
|
continue;
|
|
4897
4878
|
}
|
|
4898
4879
|
if (stopped) {
|
|
4899
|
-
results.push(stoppedStepResult(seqStep.agent, seqStep.context));
|
|
4880
|
+
results.push(stoppedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName));
|
|
4900
4881
|
flatIndex++;
|
|
4901
4882
|
continue;
|
|
4902
4883
|
}
|
|
@@ -4906,7 +4887,7 @@ async function runSubagent(
|
|
|
4906
4887
|
continue;
|
|
4907
4888
|
}
|
|
4908
4889
|
if (interrupted) {
|
|
4909
|
-
results.push(pausedStepResult(seqStep.agent, seqStep.context));
|
|
4890
|
+
results.push(pausedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName));
|
|
4910
4891
|
flatIndex++;
|
|
4911
4892
|
continue;
|
|
4912
4893
|
}
|
|
@@ -4919,17 +4900,27 @@ async function runSubagent(
|
|
|
4919
4900
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
4920
4901
|
: undefined,
|
|
4921
4902
|
baseDir: config.worktreeBaseDir,
|
|
4903
|
+
beforeCreate: (plannedSetup) => {
|
|
4904
|
+
const worktree = plannedSetup.worktrees[0];
|
|
4905
|
+
if (worktree) {
|
|
4906
|
+
setStatusWorktreeReference(requiredStatusStep(statusPayload, flatIndex), worktree);
|
|
4907
|
+
}
|
|
4908
|
+
const pendingHandoff = writePendingParallelHandoff({
|
|
4909
|
+
manifestPath: parallelHandoffPath(asyncDir),
|
|
4910
|
+
runId: id,
|
|
4911
|
+
mode: "single",
|
|
4912
|
+
source: "async",
|
|
4913
|
+
cwd,
|
|
4914
|
+
stepIndex,
|
|
4915
|
+
flatStartIndex: flatIndex,
|
|
4916
|
+
setup: plannedSetup,
|
|
4917
|
+
laneBindings: handoffWorkflowKey || config.lane ? [{ index: flatIndex, taskIndex: 0, ...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}), ...(handoffChildRunId ? { runId: handoffChildRunId } : {}), ...(config.lane ? { lane: config.lane } : {}) }] : undefined,
|
|
4918
|
+
});
|
|
4919
|
+
statusPayload.parallelHandoff = pendingHandoff;
|
|
4920
|
+
statusPayload.lastUpdate = Date.now();
|
|
4921
|
+
writeStatusPayload();
|
|
4922
|
+
},
|
|
4922
4923
|
}));
|
|
4923
|
-
writePendingParallelHandoff({
|
|
4924
|
-
manifestPath: parallelHandoffPath(asyncDir),
|
|
4925
|
-
runId: id,
|
|
4926
|
-
mode: "single",
|
|
4927
|
-
source: "async",
|
|
4928
|
-
cwd,
|
|
4929
|
-
stepIndex,
|
|
4930
|
-
flatStartIndex: flatIndex,
|
|
4931
|
-
setup: singleWorktreeSetup,
|
|
4932
|
-
});
|
|
4933
4924
|
} catch (error) {
|
|
4934
4925
|
if (singleWorktreeSetup) cleanupWorktrees(singleWorktreeSetup);
|
|
4935
4926
|
throw error;
|
|
@@ -4982,12 +4973,10 @@ async function runSubagent(
|
|
|
4982
4973
|
registerInterrupt: (interrupt) => registerStepInterrupt(flatIndex, interrupt),
|
|
4983
4974
|
registerTimeout: (interrupt) => registerStepTimeout(flatIndex, interrupt),
|
|
4984
4975
|
registerStop: (stop) => registerStepStop(flatIndex, stop),
|
|
4985
|
-
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(flatIndex, abort),
|
|
4986
4976
|
timeoutSignal: timeoutAbortController.signal,
|
|
4987
4977
|
stopSignal: stopAbortController.signal,
|
|
4988
4978
|
timeoutMessage,
|
|
4989
4979
|
stopMessage,
|
|
4990
|
-
turnBudget: config.turnBudget,
|
|
4991
4980
|
toolTimeoutMs: seqStep.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4992
4981
|
onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4993
4982
|
onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
|
|
@@ -4995,6 +4984,7 @@ async function runSubagent(
|
|
|
4995
4984
|
onExternalProcess: (process) => updateExternalProcess(flatIndex, process),
|
|
4996
4985
|
onExternalJob: (externalJob) => updateExternalJob(flatIndex, externalJob),
|
|
4997
4986
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(flatIndex),
|
|
4987
|
+
usageBudgetExhausted: () => refreshUsageBudget()?.exhausted === true,
|
|
4998
4988
|
orcaProgressTab,
|
|
4999
4989
|
}), config.deadlineAt);
|
|
5000
4990
|
} catch (error) {
|
|
@@ -5009,6 +4999,7 @@ async function runSubagent(
|
|
|
5009
4999
|
const childStopped = singleResult.stopped === true;
|
|
5010
5000
|
results.push(omitUndefinedProperties({
|
|
5011
5001
|
agent: singleResult.agent,
|
|
5002
|
+
...(singleResult.sessionName ? { sessionName: singleResult.sessionName } : {}),
|
|
5012
5003
|
context: singleResult.context,
|
|
5013
5004
|
agentContract: singleResult.agentContract,
|
|
5014
5005
|
launchContractDigest: singleResult.launchContractDigest,
|
|
@@ -5027,6 +5018,7 @@ async function runSubagent(
|
|
|
5027
5018
|
modelAttempts: singleResult.modelAttempts,
|
|
5028
5019
|
contextOverflow: singleResult.contextOverflow,
|
|
5029
5020
|
totalCost: singleResult.totalCost,
|
|
5021
|
+
usage: singleResult.usage,
|
|
5030
5022
|
artifactPaths: singleResult.artifactPaths,
|
|
5031
5023
|
transcriptPath: singleResult.transcriptPath,
|
|
5032
5024
|
transcriptError: singleResult.transcriptError,
|
|
@@ -5044,9 +5036,6 @@ async function runSubagent(
|
|
|
5044
5036
|
interrupted: singleResult.interrupted,
|
|
5045
5037
|
timedOut: timedOut || singleResult.timedOut ? true : undefined,
|
|
5046
5038
|
stopped: stopped || childStopped ? true : undefined,
|
|
5047
|
-
turnBudget: singleResult.turnBudget,
|
|
5048
|
-
turnBudgetExceeded: singleResult.turnBudgetExceeded,
|
|
5049
|
-
wrapUpRequested: singleResult.wrapUpRequested,
|
|
5050
5039
|
toolBudget: singleResult.toolBudget,
|
|
5051
5040
|
toolBudgetBlocked: singleResult.toolBudgetBlocked,
|
|
5052
5041
|
runner: singleResult.runner,
|
|
@@ -5101,16 +5090,11 @@ async function runSubagent(
|
|
|
5101
5090
|
requiredStatusStep(statusPayload, flatIndex).exitCode = stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
5102
5091
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "timedOut", timedOut || singleResult.timedOut ? true : undefined);
|
|
5103
5092
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "stopped", stopped || childStopped ? true : undefined);
|
|
5104
|
-
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "turnBudget", singleResult.turnBudget);
|
|
5105
|
-
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "turnBudgetExceeded", singleResult.turnBudgetExceeded);
|
|
5106
|
-
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "wrapUpRequested", singleResult.wrapUpRequested);
|
|
5107
5093
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "toolBudget", singleResult.toolBudget);
|
|
5108
5094
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "toolBudgetBlocked", singleResult.toolBudgetBlocked);
|
|
5109
5095
|
if (singleResult.toolBudget) statusPayload.toolBudget = singleResult.toolBudget;
|
|
5110
5096
|
if (singleResult.toolBudgetBlocked) statusPayload.toolBudgetBlocked = true;
|
|
5111
|
-
|
|
5112
|
-
if (singleResult.turnBudgetExceeded) statusPayload.turnBudgetExceeded = true;
|
|
5113
|
-
if (singleResult.wrapUpRequested) statusPayload.wrapUpRequested = true;
|
|
5097
|
+
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "sessionName", singleResult.sessionName);
|
|
5114
5098
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "model", singleResult.model);
|
|
5115
5099
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, flatIndex).thinking));
|
|
5116
5100
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "attemptedModels", singleResult.attemptedModels);
|
|
@@ -5171,6 +5155,9 @@ async function runSubagent(
|
|
|
5171
5155
|
diffs,
|
|
5172
5156
|
results: [{
|
|
5173
5157
|
agent: singleResult.agent,
|
|
5158
|
+
...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}),
|
|
5159
|
+
...(handoffChildRunId ? { runId: handoffChildRunId } : {}),
|
|
5160
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
5174
5161
|
status: singleResult.stopped ? "stopped" as const : singleResult.interrupted ? "paused" as const : singleResult.exitCode === 0 ? "completed" as const : "failed" as const,
|
|
5175
5162
|
summary: singleResult.output || singleResult.error || "(no output)",
|
|
5176
5163
|
...(singleResult.artifactPaths?.outputPath ? { outputPath: singleResult.artifactPaths.outputPath } : {}),
|
|
@@ -5291,14 +5278,14 @@ async function runSubagent(
|
|
|
5291
5278
|
if (!timedOut && !stopped && !interrupted && config.timeoutMs !== undefined && results.some((result) => result.timedOut === true && result.error === timeoutMessage)) {
|
|
5292
5279
|
timedOut = true;
|
|
5293
5280
|
}
|
|
5294
|
-
const signalTerminated = !stopped && !timedOut && !
|
|
5281
|
+
const signalTerminated = !stopped && !timedOut && !interrupted && results.some((result) => result.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
5295
5282
|
processSignal: result.processSignal,
|
|
5296
5283
|
interrupted: result.interrupted,
|
|
5297
5284
|
timedOut: result.timedOut,
|
|
5298
5285
|
stopped: result.stopped,
|
|
5299
|
-
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
5300
5286
|
})));
|
|
5301
|
-
|
|
5287
|
+
const partialWithEvidence = !stopped && !signalTerminated && !timedOut && !usageBudgetExceeded && !interrupted && results.some(partialEvidenceResult) && !results.some(concreteFailureResult);
|
|
5288
|
+
statusPayload.state = stopped || signalTerminated ? "stopped" : timedOut || usageBudgetExceeded ? "failed" : interrupted ? "paused" : results.every((r) => r.success) ? "complete" : partialWithEvidence ? "partial" : "failed";
|
|
5302
5289
|
closeSteerInbox(asyncDir, statusPayload.state, (filePath, payload) => runPersistence.write(filePath, payload));
|
|
5303
5290
|
disposeControlInbox();
|
|
5304
5291
|
for (const request of consumeSteerRequests(asyncDir)) deliverSteerRequest(request);
|
|
@@ -5328,13 +5315,17 @@ async function runSubagent(
|
|
|
5328
5315
|
statusPayload.timedOut = true;
|
|
5329
5316
|
statusPayload.error = timeoutMessage ?? "Subagent timed out.";
|
|
5330
5317
|
}
|
|
5331
|
-
if (turnBudgetExceeded && !statusPayload.error) {
|
|
5332
|
-
const budget = statusPayload.turnBudget;
|
|
5333
|
-
statusPayload.error = budget ? turnBudgetExceededMessage(budget, budget.turnCount) : "Subagent exceeded turn budget.";
|
|
5334
|
-
}
|
|
5335
5318
|
if (usageBudgetExceeded && statusPayload.usageBudget && !statusPayload.error) {
|
|
5336
5319
|
statusPayload.error = usageBudgetExceededMessage(statusPayload.usageBudget);
|
|
5337
5320
|
}
|
|
5321
|
+
if (partialWithEvidence) {
|
|
5322
|
+
const partialResult = results.find(partialEvidenceResult);
|
|
5323
|
+
statusPayload.activityState = "needs_attention";
|
|
5324
|
+
statusPayload.error = partialResult?.error ?? statusPayload.error;
|
|
5325
|
+
for (const step of statusPayload.steps) {
|
|
5326
|
+
if (step.status === "failed") step.activityState = "needs_attention";
|
|
5327
|
+
}
|
|
5328
|
+
}
|
|
5338
5329
|
statusPayload.endedAt = runEndedAt;
|
|
5339
5330
|
statusPayload.lastUpdate = runEndedAt;
|
|
5340
5331
|
setOptionalProperty(statusPayload, "sessionFile", effectiveSessionFile);
|
|
@@ -5344,8 +5335,10 @@ async function runSubagent(
|
|
|
5344
5335
|
setOptionalProperty(statusPayload, "shareUrl", shareUrl);
|
|
5345
5336
|
setOptionalProperty(statusPayload, "gistUrl", gistUrl);
|
|
5346
5337
|
setOptionalProperty(statusPayload, "shareError", shareError);
|
|
5347
|
-
if (statusPayload.state === "failed" && !statusPayload.error) {
|
|
5348
|
-
const
|
|
5338
|
+
if ((statusPayload.state === "failed" || statusPayload.state === "partial") && !statusPayload.error) {
|
|
5339
|
+
const concreteFailure = results.find(concreteFailureResult);
|
|
5340
|
+
const failedStep = concreteFailure ? undefined : statusPayload.steps.find((s) => s.status === "failed");
|
|
5341
|
+
if (concreteFailure?.error) statusPayload.error = concreteFailure.error;
|
|
5349
5342
|
if (failedStep?.agent) {
|
|
5350
5343
|
statusPayload.error = `Step failed: ${failedStep.agent}`;
|
|
5351
5344
|
}
|
|
@@ -5357,20 +5350,18 @@ async function runSubagent(
|
|
|
5357
5350
|
id,
|
|
5358
5351
|
agent: agentName,
|
|
5359
5352
|
mode: resultMode,
|
|
5360
|
-
success:
|
|
5361
|
-
state:
|
|
5362
|
-
summary: stopped ? stopMessage : signalTerminated ? (statusPayload.error ?? "Subagent process terminated by signal.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") :
|
|
5353
|
+
success: statusPayload.state === "complete",
|
|
5354
|
+
state: statusPayload.state,
|
|
5355
|
+
summary: stopped ? stopMessage : signalTerminated ? (statusPayload.error ?? "Subagent process terminated by signal.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") : usageBudgetExceeded ? (statusPayload.error ?? "Usage budget exhausted.") : interrupted ? "Paused after interrupt. Waiting for explicit next action." : statusPayload.state === "partial" ? (statusPayload.error ?? summary) : summary,
|
|
5363
5356
|
...(config.timeoutMs !== undefined ? { timeoutMs: config.timeoutMs } : {}),
|
|
5364
5357
|
...(config.deadlineAt !== undefined ? { deadlineAt: config.deadlineAt } : {}),
|
|
5365
|
-
...(statusPayload.turnBudget ? { turnBudget: statusPayload.turnBudget } : {}),
|
|
5366
|
-
...(statusPayload.turnBudgetExceeded ? { turnBudgetExceeded: true } : {}),
|
|
5367
|
-
...(statusPayload.wrapUpRequested ? { wrapUpRequested: true } : {}),
|
|
5368
5358
|
...(statusPayload.toolBudget ? { toolBudget: statusPayload.toolBudget } : {}),
|
|
5369
5359
|
...(statusPayload.toolBudgetBlocked ? { toolBudgetBlocked: true } : {}),
|
|
5370
5360
|
...(statusPayload.usageBudget ? { usageBudget: statusPayload.usageBudget } : {}),
|
|
5371
|
-
...(stopped ? { stopped: true, error: stopMessage } : timedOut ? { timedOut: true, error: timeoutMessage ?? "Subagent timed out." } :
|
|
5361
|
+
...(stopped ? { stopped: true, error: stopMessage } : timedOut ? { timedOut: true, error: timeoutMessage ?? "Subagent timed out." } : usageBudgetExceeded ? { error: statusPayload.error ?? "Usage budget exhausted." } : {}),
|
|
5372
5362
|
results: results.map((r) => omitUndefinedProperties({
|
|
5373
5363
|
agent: r.agent,
|
|
5364
|
+
...(r.sessionName ? { sessionName: r.sessionName } : {}),
|
|
5374
5365
|
context: r.context,
|
|
5375
5366
|
output: r.output,
|
|
5376
5367
|
outputState: r.outputState,
|
|
@@ -5382,9 +5373,6 @@ async function runSubagent(
|
|
|
5382
5373
|
timedOut: r.timedOut || undefined,
|
|
5383
5374
|
stopped: r.stopped || undefined,
|
|
5384
5375
|
processSignal: r.processSignal || undefined,
|
|
5385
|
-
turnBudget: r.turnBudget,
|
|
5386
|
-
turnBudgetExceeded: r.turnBudgetExceeded || undefined,
|
|
5387
|
-
wrapUpRequested: r.wrapUpRequested || undefined,
|
|
5388
5376
|
toolBudget: r.toolBudget,
|
|
5389
5377
|
toolBudgetBlocked: r.toolBudgetBlocked || undefined,
|
|
5390
5378
|
sessionFile: r.sessionFile,
|
|
@@ -5394,6 +5382,7 @@ async function runSubagent(
|
|
|
5394
5382
|
modelAttempts: r.modelAttempts,
|
|
5395
5383
|
contextOverflow: r.contextOverflow,
|
|
5396
5384
|
totalCost: r.totalCost,
|
|
5385
|
+
usage: r.usage,
|
|
5397
5386
|
artifactPaths: r.artifactPaths,
|
|
5398
5387
|
outputSaveError: r.outputSaveError,
|
|
5399
5388
|
metadataSaveError: r.metadataSaveError,
|
|
@@ -5426,7 +5415,7 @@ async function runSubagent(
|
|
|
5426
5415
|
capabilityAudit: statusPayload.capabilityAudit,
|
|
5427
5416
|
...(config.parentWorkflowRunId ? { parentWorkflowRunId: config.parentWorkflowRunId } : {}),
|
|
5428
5417
|
...(config.workflowKey ? { workflowKey: config.workflowKey } : {}),
|
|
5429
|
-
exitCode:
|
|
5418
|
+
exitCode: statusPayload.state === "complete" || statusPayload.state === "paused" ? 0 : 1,
|
|
5430
5419
|
timestamp: runEndedAt,
|
|
5431
5420
|
durationMs: runEndedAt - overallStartTime,
|
|
5432
5421
|
totalTokens: statusPayload.totalTokens,
|