pi-subagents 0.58.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +102 -0
- package/docs/agents.md +5 -3
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +1 -1
- package/docs/models.md +24 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +93 -5
- package/docs/workflows.md +94 -3
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +87 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +29 -233
- package/skills/pi-subagents/references/execution-controls.md +52 -7
- package/skills/pi-subagents/references/management-authoring-rpc.md +3 -4
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +54 -26
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +159 -42
- package/src/agents/agent-serializer.ts +4 -2
- package/src/agents/agents.ts +67 -26
- package/src/agents/runtime-agent-registry.ts +9 -16
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +6 -9
- package/src/api/shared-types.ts +2 -0
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +51 -23
- package/src/extension/public-execution.ts +16 -2
- package/src/extension/schemas.ts +36 -10
- package/src/extension/tool-description.ts +24 -5
- package/src/intercom/result-intercom.ts +2 -0
- package/src/profiles/profiles.ts +5 -6
- package/src/runs/background/active-async-capacity.ts +2 -2
- package/src/runs/background/async-execution.ts +52 -37
- package/src/runs/background/async-job-tracker.ts +17 -12
- package/src/runs/background/async-resume.ts +23 -25
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +110 -8
- package/src/runs/background/chain-append.ts +6 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/fleet-view.ts +21 -11
- package/src/runs/background/notify.ts +158 -6
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +2 -0
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/retained-children.ts +1 -1
- package/src/runs/background/run-status.ts +30 -9
- package/src/runs/background/scheduled-runs.ts +86 -7
- package/src/runs/background/stale-run-reconciler.ts +10 -4
- package/src/runs/background/steering.ts +4 -14
- package/src/runs/background/subagent-runner.ts +351 -358
- package/src/runs/background/subagent-wait.ts +68 -12
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +25 -1
- package/src/runs/background/wait-config.ts +23 -9
- package/src/runs/background/wait-tool.ts +9 -2
- package/src/runs/foreground/async-steering-action.ts +2 -2
- package/src/runs/foreground/execution.ts +125 -122
- package/src/runs/foreground/foreground-control.ts +3 -0
- package/src/runs/foreground/foreground-history.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +576 -240
- package/src/runs/foreground/workflow-detach-reconcile.ts +99 -200
- package/src/runs/shared/abort-recovery.ts +119 -0
- package/src/runs/shared/async-status-projection.ts +597 -0
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/child-identity.ts +19 -4
- package/src/runs/shared/child-launch-plan.ts +151 -0
- package/src/runs/shared/completion-evidence.ts +89 -0
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/mcp-config-sources.ts +42 -6
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +93 -143
- package/src/runs/shared/mcp-direct-tool-grant.ts +194 -0
- package/src/runs/shared/model-fallback.ts +9 -2
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/nested-events.ts +6 -2
- package/src/runs/shared/nested-render.ts +7 -3
- package/src/runs/shared/parallel-handoff.ts +419 -7
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +67 -3
- package/src/runs/shared/single-output.ts +72 -22
- package/src/runs/shared/subagent-prompt-runtime.ts +33 -6
- package/src/runs/shared/workflow-graph.ts +15 -0
- package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/src/runs/shared/worktree.ts +18 -0
- package/src/shared/child-session-name.ts +46 -0
- package/src/shared/extension-context.ts +24 -0
- package/src/shared/formatters.ts +5 -2
- package/src/shared/launch-contract.ts +1 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +232 -30
- package/src/shared/utils.ts +35 -55
- package/src/slash/delegation-adapters.ts +1 -8
- package/src/slash/delegation-request.ts +0 -4
- package/src/slash/slash-bridge.ts +1 -2
- package/src/slash/slash-commands.ts +369 -90
- package/src/slash/slash-live-state.ts +22 -11
- package/src/tui/fleet-status.ts +134 -75
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +895 -121
- package/src/watchdog/change-signature.ts +40 -1
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +235 -0
- package/src/workflows/scripted-workflow.ts +503 -38
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +43 -4
- package/src/workflows/workflow-settlement.ts +246 -0
- package/src/runs/shared/turn-budget.ts +0 -98
|
@@ -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";
|
|
@@ -79,7 +75,8 @@ import {
|
|
|
79
75
|
DEFAULT_GLOBAL_CONCURRENCY_LIMIT,
|
|
80
76
|
Semaphore,
|
|
81
77
|
} from "../shared/parallel-utils.ts";
|
|
82
|
-
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
78
|
+
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, deriveForkPromptCacheKey, 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,
|
|
@@ -1619,13 +1640,13 @@ async function runSingleStepInner(
|
|
|
1619
1640
|
const writerProcesses: PiWriterProcessInstanceExitV1[] = [];
|
|
1620
1641
|
let writerAttemptCount = 0;
|
|
1621
1642
|
const attemptNotes: string[] = [];
|
|
1643
|
+
let finalRequiredOutputMissing: boolean | undefined;
|
|
1622
1644
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
1623
1645
|
let finalResult: RunPiStreamingResult | undefined;
|
|
1624
1646
|
let finalOutputSnapshot: SingleOutputSnapshot | undefined;
|
|
1625
1647
|
let structuredAcceptanceReport: unknown;
|
|
1626
1648
|
let structuredAcceptanceReportError: string | undefined;
|
|
1627
1649
|
let completionGuardTriggeredFinal = false;
|
|
1628
|
-
let turnBudget = ctx.turnBudget ? initialTurnBudgetState(ctx.turnBudget) : undefined;
|
|
1629
1650
|
let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
|
|
1630
1651
|
let toolBudgetBlocked = false;
|
|
1631
1652
|
let actualLaunchContractDigest = step.launchContractDigest;
|
|
@@ -1639,8 +1660,12 @@ async function runSingleStepInner(
|
|
|
1639
1660
|
let taskDeliveryOverride: SubagentTaskDelivery | undefined;
|
|
1640
1661
|
let contextOverflow = false;
|
|
1641
1662
|
let launchWarningsEmitted = false;
|
|
1663
|
+
let abortRecoveryAttempted = false;
|
|
1664
|
+
let nextAttemptTask = task;
|
|
1642
1665
|
modelAttemptsLoop: while (modelIndex < candidates.length) {
|
|
1643
1666
|
if (ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break;
|
|
1667
|
+
const recoveringAbort = abortRecoveryAttempted;
|
|
1668
|
+
const attemptTask = nextAttemptTask;
|
|
1644
1669
|
const candidate = candidates[modelIndex];
|
|
1645
1670
|
const expectedModelForVerification = candidate && !(step.skipPrimaryModelVerification && modelIndex === 0) ? candidate : undefined;
|
|
1646
1671
|
try {
|
|
@@ -1674,8 +1699,9 @@ async function runSingleStepInner(
|
|
|
1674
1699
|
: undefined;
|
|
1675
1700
|
const { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: attemptCapabilityAudit, warnings } = buildPiArgs(omitUndefinedProperties({
|
|
1676
1701
|
parentSessionId: step.parentSessionId,
|
|
1702
|
+
forkCacheKey: step.context === "fork" ? deriveForkPromptCacheKey(step.parentSessionId) : undefined,
|
|
1677
1703
|
baseArgs: ["--mode", "json", "-p"],
|
|
1678
|
-
task,
|
|
1704
|
+
task: attemptTask,
|
|
1679
1705
|
taskDelivery: taskDeliveryOverride,
|
|
1680
1706
|
sessionEnabled,
|
|
1681
1707
|
sessionDir,
|
|
@@ -1686,11 +1712,12 @@ async function runSingleStepInner(
|
|
|
1686
1712
|
inheritSkills: step.inheritSkills,
|
|
1687
1713
|
requireReadTool: Boolean(step.skills?.length),
|
|
1688
1714
|
tools: step.tools,
|
|
1715
|
+
allowNestedSubagents: step.allowNestedSubagents,
|
|
1689
1716
|
extensions: step.extensions,
|
|
1690
1717
|
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1691
1718
|
fast: step.fast,
|
|
1692
1719
|
modelCandidates: step.modelCandidates,
|
|
1693
|
-
systemPrompt:
|
|
1720
|
+
systemPrompt: step.systemPrompt ?? "",
|
|
1694
1721
|
systemPromptMode: step.systemPromptMode,
|
|
1695
1722
|
mcpDirectTools: step.mcpDirectTools,
|
|
1696
1723
|
mcpConfig: step.mcpConfig,
|
|
@@ -1699,6 +1726,7 @@ async function runSingleStepInner(
|
|
|
1699
1726
|
cwd: step.cwd ?? ctx.cwd,
|
|
1700
1727
|
promptFileStem: step.agent,
|
|
1701
1728
|
intercomSessionName: ctx.childIntercomTarget,
|
|
1729
|
+
sessionName: childSessionName,
|
|
1702
1730
|
orchestratorIntercomTarget: ctx.orchestratorIntercomTarget,
|
|
1703
1731
|
runId: ctx.id,
|
|
1704
1732
|
childAgentName: step.agent,
|
|
@@ -1722,6 +1750,7 @@ async function runSingleStepInner(
|
|
|
1722
1750
|
: undefined,
|
|
1723
1751
|
childWatchdog,
|
|
1724
1752
|
waitToolEnabled: step.waitToolEnabled,
|
|
1753
|
+
waitToolDefaultTimeoutMs: step.waitToolDefaultTimeoutMs,
|
|
1725
1754
|
thinkingCeiling: step.thinkingCeiling,
|
|
1726
1755
|
extensionBindings,
|
|
1727
1756
|
}));
|
|
@@ -1732,6 +1761,7 @@ async function runSingleStepInner(
|
|
|
1732
1761
|
if (step.definitionDigest) {
|
|
1733
1762
|
const toolPlan = resolvedTaskToolPlan ?? resolvePiLaunchToolPlan(omitUndefinedProperties({
|
|
1734
1763
|
tools: step.tools,
|
|
1764
|
+
allowNestedSubagents: step.allowNestedSubagents,
|
|
1735
1765
|
extensions: step.extensions,
|
|
1736
1766
|
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1737
1767
|
fast: step.fast,
|
|
@@ -1756,7 +1786,7 @@ async function runSingleStepInner(
|
|
|
1756
1786
|
...(step.fast !== undefined ? { fast: step.fast } : {}),
|
|
1757
1787
|
...(resolveEffectiveThinking(candidate, step.thinking) ? { thinking: resolveEffectiveThinking(candidate, step.thinking) } : {}),
|
|
1758
1788
|
...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
|
|
1759
|
-
systemPrompt:
|
|
1789
|
+
systemPrompt: step.systemPrompt ?? "",
|
|
1760
1790
|
systemPromptMode: step.systemPromptMode,
|
|
1761
1791
|
inheritProjectContext: step.inheritProjectContext,
|
|
1762
1792
|
inheritGlobalContext: step.inheritGlobalContext,
|
|
@@ -1789,7 +1819,6 @@ async function runSingleStepInner(
|
|
|
1789
1819
|
ctx.timeoutMessage,
|
|
1790
1820
|
ctx.registerStop,
|
|
1791
1821
|
ctx.stopMessage,
|
|
1792
|
-
ctx.registerTurnBudgetAbort,
|
|
1793
1822
|
ctx.onWriterProcess,
|
|
1794
1823
|
ctx.toolTimeoutMs,
|
|
1795
1824
|
ctx.deadlineAt,
|
|
@@ -1809,25 +1838,6 @@ async function runSingleStepInner(
|
|
|
1809
1838
|
processTree: run.processTree,
|
|
1810
1839
|
});
|
|
1811
1840
|
}
|
|
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
1841
|
const toolAvailabilityError = run.exitCode === 0 && !run.error
|
|
1832
1842
|
? readChildToolDiagnosticError(toolDiagnosticPath)
|
|
1833
1843
|
: undefined;
|
|
@@ -1838,7 +1848,6 @@ async function runSingleStepInner(
|
|
|
1838
1848
|
&& !run.interrupted
|
|
1839
1849
|
&& !run.timedOut
|
|
1840
1850
|
&& !run.stopped
|
|
1841
|
-
&& !run.turnBudgetExceeded
|
|
1842
1851
|
&& !run.protocolError
|
|
1843
1852
|
&& !toolAvailabilityError
|
|
1844
1853
|
? formatMidToolExitError({
|
|
@@ -1876,14 +1885,17 @@ async function runSingleStepInner(
|
|
|
1876
1885
|
const hiddenError = run.exitCode === 0 && !run.error && !toolAvailabilityError && !structuredError && !midToolExitError
|
|
1877
1886
|
? detectSubagentError(errorMessages)
|
|
1878
1887
|
: null;
|
|
1888
|
+
const terminalEmptyAfterUsefulWork = !validatedStructuredOutput
|
|
1889
|
+
&& hasEmptyTerminalAssistantResponse(run.messages)
|
|
1890
|
+
&& (run.toolCount > 0 || Boolean(run.finalOutput.trim()));
|
|
1879
1891
|
const emptyOutputError = run.exitCode === 0
|
|
1880
1892
|
&& !run.error
|
|
1881
1893
|
&& !toolAvailabilityError
|
|
1882
1894
|
&& !structuredError
|
|
1883
|
-
&& !run.finalOutput.trim()
|
|
1884
1895
|
&& !validatedStructuredOutput
|
|
1896
|
+
&& (!run.finalOutput.trim() || terminalEmptyAfterUsefulWork)
|
|
1885
1897
|
&& (!hiddenError?.hasError || hasEmptyTerminalAssistantResponse(run.messages))
|
|
1886
|
-
?
|
|
1898
|
+
? formatEmptyTerminalAssistantResponseError(run.messages)
|
|
1887
1899
|
: undefined;
|
|
1888
1900
|
const completionGuardEnabled = isAgentContractV1(step.agentContract) ? step.completionGuard === true : step.completionGuard !== false;
|
|
1889
1901
|
const completionToolPlan = resolvedTaskToolPlan;
|
|
@@ -1904,25 +1916,25 @@ async function runSingleStepInner(
|
|
|
1904
1916
|
}))
|
|
1905
1917
|
: undefined;
|
|
1906
1918
|
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."
|
|
1919
|
+
const completionEvidence = planCompletionEvidence({
|
|
1920
|
+
guard: completionGuard,
|
|
1921
|
+
completionGuardEnabled,
|
|
1922
|
+
mutationCapable: hasMutationToolCapability(completionTools, completionToolPlan?.effectiveMcpTools ?? step.mcpDirectTools),
|
|
1923
|
+
implementationMutationExpected: expectsImplementationMutation(step.agent, taskForCompletionGuard),
|
|
1924
|
+
mutationAttemptObserved,
|
|
1925
|
+
mutationEvidence: completionMutationEvidence,
|
|
1926
|
+
agentContractV1: isAgentContractV1(step.agentContract),
|
|
1927
|
+
});
|
|
1928
|
+
const finalOutputHasPersistableFileContent = run.exitCode === 0 && !run.error && !emptyOutputError && Boolean(stripAcceptanceReport(run.finalOutput).trim());
|
|
1929
|
+
const requiredOutput = step.outputMode === "file-only" && step.outputPath
|
|
1930
|
+
? { kind: "file-only" as const, path: step.outputPath, missing: !fs.existsSync(step.outputPath) && !finalOutputHasPersistableFileContent }
|
|
1931
|
+
: effectiveStructuredOutput
|
|
1932
|
+
? { kind: "structured" as const, path: effectiveStructuredOutput.outputPath, missing: !fs.existsSync(effectiveStructuredOutput.outputPath) }
|
|
1924
1933
|
: undefined;
|
|
1925
|
-
|
|
1934
|
+
finalRequiredOutputMissing = requiredOutput?.missing;
|
|
1935
|
+
const missingRequiredOutputError = formatRequiredOutputError(requiredOutput);
|
|
1936
|
+
const missingRequiredOutputAfterMutation = Boolean(missingRequiredOutputError) && (mutationAttemptObserved || Boolean(mutationEvidence.changedFiles.length));
|
|
1937
|
+
const effectiveExitCode = toolAvailabilityError || completionEvidence.legacyFailureError || midToolExitError || structuredError || emptyOutputError || missingRequiredOutputError
|
|
1926
1938
|
? 1
|
|
1927
1939
|
: hiddenError?.hasError
|
|
1928
1940
|
? (hiddenError.exitCode ?? 1)
|
|
@@ -1934,19 +1946,23 @@ async function runSingleStepInner(
|
|
|
1934
1946
|
interrupted: run.interrupted,
|
|
1935
1947
|
timedOut: run.timedOut,
|
|
1936
1948
|
stopped: run.stopped,
|
|
1937
|
-
turnBudgetExceeded: run.turnBudgetExceeded,
|
|
1938
1949
|
})) ? formatProcessSignalError(run.processSignal!) : undefined;
|
|
1950
|
+
const underlyingError = toolAvailabilityError
|
|
1951
|
+
?? midToolExitError
|
|
1952
|
+
?? structuredError
|
|
1953
|
+
?? run.error
|
|
1954
|
+
?? signalError
|
|
1955
|
+
?? (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1956
|
+
?? ((emptyOutputError || missingRequiredOutputError) && run.stderr.trim() ? run.stderr.trim() : undefined)
|
|
1957
|
+
?? emptyOutputError
|
|
1958
|
+
?? (missingRequiredOutputAfterMutation ? missingRequiredOutputError : undefined)
|
|
1959
|
+
?? (hiddenError?.hasError
|
|
1960
|
+
? hiddenError.details
|
|
1961
|
+
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1962
|
+
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1963
|
+
: undefined);
|
|
1939
1964
|
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)),
|
|
1965
|
+
underlyingError ?? missingRequiredOutputError ?? completionEvidence.legacyFailureError,
|
|
1950
1966
|
{
|
|
1951
1967
|
agent: step.agent,
|
|
1952
1968
|
ambientExtensionsEnabled: launchResolvedExtensions?.disableAmbientExtensions === false,
|
|
@@ -1960,8 +1976,8 @@ async function runSingleStepInner(
|
|
|
1960
1976
|
usage: run.usage,
|
|
1961
1977
|
});
|
|
1962
1978
|
modelAttempts.push(attempt);
|
|
1963
|
-
if (candidate && startupAttemptIndex === 0) attemptedModels.push(candidate);
|
|
1964
|
-
completionGuardTriggeredFinal =
|
|
1979
|
+
if (!recoveringAbort && candidate && startupAttemptIndex === 0) attemptedModels.push(candidate);
|
|
1980
|
+
completionGuardTriggeredFinal = completionEvidence.guardTriggered && !underlyingError && !missingRequiredOutputError;
|
|
1965
1981
|
finalOutputSnapshot = outputSnapshot;
|
|
1966
1982
|
if (step.toolBudget) {
|
|
1967
1983
|
const toolMessages = run.messages.filter((message) => message.role === "toolResult");
|
|
@@ -1969,27 +1985,48 @@ async function runSingleStepInner(
|
|
|
1969
1985
|
toolBudgetBlocked = Boolean(blockedMessage);
|
|
1970
1986
|
toolBudget = toolBudgetState(step.toolBudget, toolMessages.length, blockedMessage ? (blockedMessage as { toolName?: string }).toolName : undefined);
|
|
1971
1987
|
}
|
|
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;
|
|
1988
|
+
const settlementDiagnostic = projectSettlementDiagnostic(completionEvidence, {
|
|
1989
|
+
terminalFailed: effectiveExitCode !== 0,
|
|
1990
|
+
finalTextPresent: Boolean(stripAcceptanceReport(run.finalOutput).trim()),
|
|
1991
|
+
mutationObserved: mutationEvidence.attemptedMutation,
|
|
1992
|
+
requiredOutput,
|
|
1993
|
+
afterCompactionSettlement: run.afterCompactionSettlement === true,
|
|
1994
|
+
});
|
|
1995
|
+
const fileMutationEffect = completionEvidence.fileMutation ?? (missingRequiredOutputAfterMutation ? { status: "observed" as const, expected: completionEvidence.mutationExpected, attempted: true, evidence: mutationEvidence } : undefined);
|
|
1989
1996
|
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
|
-
|
|
1997
|
+
const abortRecovery = !attempt.success ? planAbortRecovery({
|
|
1998
|
+
messages: run.messages,
|
|
1999
|
+
error,
|
|
2000
|
+
processSignal: run.processSignal,
|
|
2001
|
+
sessionAvailable: Boolean(step.sessionFile && fs.existsSync(step.sessionFile)),
|
|
2002
|
+
alreadyResumed: abortRecoveryAttempted,
|
|
2003
|
+
stopped: run.stopped || ctx.stopSignal?.aborted || ctx.skipAcceptance?.(),
|
|
2004
|
+
interrupted: run.interrupted,
|
|
2005
|
+
timedOut: run.timedOut || ctx.timeoutSignal?.aborted,
|
|
2006
|
+
toolBudgetExhausted: run.toolBudgetBlocked || toolBudgetBlocked,
|
|
2007
|
+
usageBudgetExhausted: ctx.usageBudgetExhausted?.(),
|
|
2008
|
+
structuredOutputFailed: Boolean(structuredError),
|
|
2009
|
+
acceptanceFailed: false,
|
|
2010
|
+
currentTool: run.currentTool,
|
|
2011
|
+
afterCompactionSettlement: run.afterCompactionSettlement,
|
|
2012
|
+
}) : undefined;
|
|
2013
|
+
if (abortRecovery?.action === "settle" && abortRecovery.diagnostic) {
|
|
2014
|
+
attempt.error = attempt.error
|
|
2015
|
+
? `${abortRecovery.diagnostic}\n${attempt.error.slice(0, 8_000)}`
|
|
2016
|
+
: abortRecovery.diagnostic;
|
|
2017
|
+
if (finalResult) finalResult.abortRecoveryDiagnostic = abortRecovery.diagnostic;
|
|
2018
|
+
}
|
|
1991
2019
|
if (run.stopped || run.timedOut || ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break modelAttemptsLoop;
|
|
1992
|
-
if (
|
|
2020
|
+
if (abortRecovery?.action === "settle" && abortRecovery.diagnostic) break modelAttemptsLoop;
|
|
2021
|
+
if (attempt.success) break modelAttemptsLoop;
|
|
2022
|
+
if (recoveringAbort) break modelAttemptsLoop;
|
|
2023
|
+
if (abortRecovery?.action === "resume") {
|
|
2024
|
+
abortRecoveryAttempted = true;
|
|
2025
|
+
nextAttemptTask = abortRecovery.prompt;
|
|
2026
|
+
attemptNotes.push("[abort-recovery] provider/transport abort after useful progress; resuming the retained child session once.");
|
|
2027
|
+
continue;
|
|
2028
|
+
}
|
|
2029
|
+
if (completionEvidence.guardTriggered) break modelAttemptsLoop;
|
|
1993
2030
|
|
|
1994
2031
|
const startupFailure = isRetryableSubagentStartupFailure(omitUndefinedProperties({
|
|
1995
2032
|
exitCode: effectiveExitCode,
|
|
@@ -2005,7 +2042,6 @@ async function runSingleStepInner(
|
|
|
2005
2042
|
interrupted: run.interrupted,
|
|
2006
2043
|
timedOut: run.timedOut,
|
|
2007
2044
|
stopped: run.stopped,
|
|
2008
|
-
turnBudgetExceeded: run.turnBudgetExceeded,
|
|
2009
2045
|
}));
|
|
2010
2046
|
const retryDelayMs = SUBAGENT_STARTUP_RETRY_DELAYS_MS[startupAttemptIndex];
|
|
2011
2047
|
if (startupFailure && retryDelayMs !== undefined) {
|
|
@@ -2052,8 +2088,14 @@ async function runSingleStepInner(
|
|
|
2052
2088
|
const rawOutput = finalResult?.finalOutput ?? "";
|
|
2053
2089
|
const outputForPersistence = stripAcceptanceReport(rawOutput);
|
|
2054
2090
|
const resolvedOutput = step.outputPath && finalResult?.exitCode === 0
|
|
2055
|
-
? resolveSingleOutput(step.outputPath, outputForPersistence, finalOutputSnapshot)
|
|
2091
|
+
? resolveSingleOutput(step.outputPath, outputForPersistence, finalOutputSnapshot, step.outputClaimPath)
|
|
2056
2092
|
: { fullOutput: outputForPersistence };
|
|
2093
|
+
if (resolvedOutput.fatalError) {
|
|
2094
|
+
if (finalResult) {
|
|
2095
|
+
finalResult.exitCode = 1;
|
|
2096
|
+
finalResult.error = finalResult.error ? `${finalResult.error}\n${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2057
2099
|
const output = stripAcceptanceReport(resolvedOutput.fullOutput);
|
|
2058
2100
|
const outputReference = resolvedOutput.savedPath ? formatSavedOutputReference(resolvedOutput.savedPath, output) : undefined;
|
|
2059
2101
|
let outputForSummary = output;
|
|
@@ -2062,14 +2104,6 @@ async function runSingleStepInner(
|
|
|
2062
2104
|
}
|
|
2063
2105
|
if (finalResult?.stopped && !outputForSummary.trim()) {
|
|
2064
2106
|
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
2107
|
}
|
|
2074
2108
|
const outputForAcceptance = rawOutput;
|
|
2075
2109
|
const childWrittenOutput = step.outputPath
|
|
@@ -2086,6 +2120,7 @@ async function runSingleStepInner(
|
|
|
2086
2120
|
? buildTimeoutRecoverySummary({
|
|
2087
2121
|
termination: "timed-out",
|
|
2088
2122
|
evidence: finalMutationEvidence,
|
|
2123
|
+
requiredOutputMissing: finalRequiredOutputMissing,
|
|
2089
2124
|
currentTool: finalResult?.currentTool,
|
|
2090
2125
|
currentToolArgs: finalResult?.currentToolArgs,
|
|
2091
2126
|
currentPath: finalResult?.currentPath,
|
|
@@ -2107,7 +2142,7 @@ async function runSingleStepInner(
|
|
|
2107
2142
|
saveError: resolvedOutput.saveError,
|
|
2108
2143
|
}));
|
|
2109
2144
|
outputForSummary = finalizedOutput.displayOutput;
|
|
2110
|
-
const acceptance = step.effectiveAcceptance && !finalResult?.stopped && !
|
|
2145
|
+
const acceptance = step.effectiveAcceptance && !finalResult?.stopped && !ctx.timeoutSignal?.aborted && !ctx.stopSignal?.aborted && !ctx.skipAcceptance?.()
|
|
2111
2146
|
? await evaluateAcceptance(omitUndefinedProperties({
|
|
2112
2147
|
acceptance: step.effectiveAcceptance,
|
|
2113
2148
|
output: outputForAcceptance,
|
|
@@ -2126,39 +2161,31 @@ async function runSingleStepInner(
|
|
|
2126
2161
|
: undefined;
|
|
2127
2162
|
const stoppedAfterAcceptance = finalResult?.stopped === true || ctx.stopSignal?.aborted === true;
|
|
2128
2163
|
const timedOutAfterAcceptance = !stoppedAfterAcceptance && (finalResult?.timedOut === true || ctx.timeoutSignal?.aborted === true);
|
|
2129
|
-
const turnBudgetExceeded = finalResult?.turnBudgetExceeded === true;
|
|
2130
2164
|
const effectiveAcceptance = step.effectiveAcceptance
|
|
2131
2165
|
? stoppedAfterAcceptance
|
|
2132
2166
|
? buildSkippedAcceptanceLedger(step.effectiveAcceptance, { id: "stopped", message: "Acceptance was not evaluated because the subagent was stopped." })
|
|
2133
2167
|
: timedOutAfterAcceptance
|
|
2134
2168
|
? 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
|
|
2169
|
+
: acceptance
|
|
2138
2170
|
: undefined;
|
|
2139
2171
|
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
|
|
2172
|
+
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !isAgentContractV1(step.agentContract);
|
|
2173
|
+
const effectiveFinalExitCode = timedOutAfterAcceptance || stoppedAfterAcceptance ? 1 : acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
2142
2174
|
const intercomDetachReceipt = finalResult?.finalOutput === INTERCOM_DETACH_RECEIPT;
|
|
2143
2175
|
const baseFinalError = stoppedAfterAcceptance
|
|
2144
2176
|
? ctx.stopMessage ?? "Subagent stopped by user."
|
|
2145
2177
|
: timedOutAfterAcceptance
|
|
2146
2178
|
? finalResult?.error ?? ctx.timeoutMessage ?? "Subagent timed out."
|
|
2147
|
-
:
|
|
2148
|
-
? finalResult?.error ?? (turnBudget ? turnBudgetExceededMessage(turnBudget, turnBudget.turnCount) : "Subagent exceeded turn budget.")
|
|
2149
|
-
: acceptanceCanFailRun
|
|
2179
|
+
: acceptanceCanFailRun
|
|
2150
2180
|
? (finalResult?.error ? `${finalResult.error}\n${acceptanceFailure}` : acceptanceFailure)
|
|
2151
2181
|
: finalResult?.error ?? (intercomDetachReceipt ? INTERCOM_DETACH_RECEIPT : undefined);
|
|
2152
2182
|
const effectiveFinalError = formatChildFailureDiagnostic({
|
|
2153
2183
|
error: baseFinalError,
|
|
2154
2184
|
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,
|
|
2185
|
+
abortRecoveryDiagnostic: effectiveFinalExitCode !== 0 ? finalResult?.abortRecoveryDiagnostic : undefined,
|
|
2186
|
+
requiredOutput: effectiveFinalExitCode !== 0 ? finalResult?.effects?.settlementDiagnostic?.requiredOutput : undefined,
|
|
2161
2187
|
});
|
|
2188
|
+
const usage = usageFromAttempts(modelAttempts);
|
|
2162
2189
|
|
|
2163
2190
|
const artifactErrors = artifactPaths && ctx.artifactConfig?.enabled !== false
|
|
2164
2191
|
? persistStepArtifacts({
|
|
@@ -2178,6 +2205,7 @@ async function runSingleStepInner(
|
|
|
2178
2205
|
model: finalResult?.model,
|
|
2179
2206
|
attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
|
|
2180
2207
|
modelAttempts,
|
|
2208
|
+
usage,
|
|
2181
2209
|
error: effectiveFinalError,
|
|
2182
2210
|
acceptance: effectiveAcceptance,
|
|
2183
2211
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
@@ -2194,6 +2222,7 @@ async function runSingleStepInner(
|
|
|
2194
2222
|
|
|
2195
2223
|
const result: StepResult & { completionGuardTriggered?: boolean } = omitUndefinedProperties({
|
|
2196
2224
|
agent: step.agent,
|
|
2225
|
+
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
2197
2226
|
context: step.context,
|
|
2198
2227
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
2199
2228
|
launchContractDigest: actualLaunchContractDigest,
|
|
@@ -2209,26 +2238,24 @@ async function runSingleStepInner(
|
|
|
2209
2238
|
modelAttempts,
|
|
2210
2239
|
contextOverflow: contextOverflow || undefined,
|
|
2211
2240
|
totalCost: costSummaryFromAttempts(modelAttempts),
|
|
2241
|
+
usage,
|
|
2212
2242
|
artifactPaths,
|
|
2213
|
-
outputSaveError: artifactErrors.outputSaveError,
|
|
2243
|
+
outputSaveError: [resolvedOutput.saveError, artifactErrors.outputSaveError].filter(Boolean).join("\n") || undefined,
|
|
2214
2244
|
metadataSaveError: artifactErrors.metadataSaveError,
|
|
2215
2245
|
transcriptPath: transcriptWriter ? artifactPaths?.transcriptPath : undefined,
|
|
2216
2246
|
transcriptError: transcriptWriter?.getError(),
|
|
2217
|
-
interrupted: timedOutAfterAcceptance || stoppedAfterAcceptance
|
|
2247
|
+
interrupted: timedOutAfterAcceptance || stoppedAfterAcceptance ? false : finalResult?.interrupted,
|
|
2218
2248
|
timedOut: timedOutAfterAcceptance ? true : finalResult?.timedOut,
|
|
2219
2249
|
stopped: stoppedAfterAcceptance ? true : finalResult?.stopped,
|
|
2220
2250
|
processSignal: finalResult?.processSignal,
|
|
2221
2251
|
timeoutRecovery,
|
|
2222
|
-
turnBudget,
|
|
2223
|
-
turnBudgetExceeded: turnBudgetExceeded || undefined,
|
|
2224
|
-
wrapUpRequested: finalResult?.wrapUpRequested || turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined,
|
|
2225
2252
|
toolBudget,
|
|
2226
2253
|
toolBudgetBlocked: toolBudgetBlocked || undefined,
|
|
2227
2254
|
completionGuardTriggered: completionGuardTriggeredFinal,
|
|
2228
2255
|
...((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
|
|
2256
|
+
structuredOutput: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : (finalResult as (RunPiStreamingResult & { structuredOutput?: unknown }) | undefined)?.structuredOutput,
|
|
2257
|
+
structuredOutputPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.outputPath,
|
|
2258
|
+
structuredOutputSchemaPath: timedOutAfterAcceptance || stoppedAfterAcceptance ? undefined : effectiveStructuredOutput?.schemaPath,
|
|
2232
2259
|
acceptance: effectiveAcceptance,
|
|
2233
2260
|
watchdog: finalResult?.watchdog,
|
|
2234
2261
|
...(capabilityAudit ? { capabilityCeiling: capabilityAudit.ceiling, capabilityAudit } : {}),
|
|
@@ -2302,6 +2329,11 @@ function requiredStatusStep(statusPayload: RunnerStatusPayload, index: number):
|
|
|
2302
2329
|
return step;
|
|
2303
2330
|
}
|
|
2304
2331
|
|
|
2332
|
+
function setStatusWorktreeReference(statusStep: RunnerStatusStep, worktree: WorktreeSetup["worktrees"][number]): void {
|
|
2333
|
+
statusStep.worktreePath = worktree.path;
|
|
2334
|
+
statusStep.branch = worktree.branch;
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2305
2337
|
function markParallelGroupSetupFailure(input: {
|
|
2306
2338
|
statusPayload: RunnerStatusPayload;
|
|
2307
2339
|
results: StepResult[];
|
|
@@ -2430,6 +2462,25 @@ function resolveAsyncStepTranscriptPath(input: {
|
|
|
2430
2462
|
|
|
2431
2463
|
type SingleStepResult = Awaited<ReturnType<typeof runSingleStep>>;
|
|
2432
2464
|
|
|
2465
|
+
function missingRequiredOutputAfterUsefulMutation(result: SingleStepResult): boolean {
|
|
2466
|
+
const effects = result.effects;
|
|
2467
|
+
return effects?.settlementDiagnostic?.requiredOutput?.missing === true
|
|
2468
|
+
&& (effects.settlementDiagnostic.mutation.attempted || effects.fileMutation?.attempted === true || Boolean(effects.fileMutation?.evidence?.changedFiles.length));
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
function partialExecutionWithUsefulMutation(result: SingleStepResult): boolean {
|
|
2472
|
+
const fileMutation = result.effects?.fileMutation;
|
|
2473
|
+
return result.execution?.status === "partial" && (fileMutation?.attempted === true || Boolean(fileMutation?.evidence?.changedFiles.length));
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
function partialEvidenceResult(result: SingleStepResult): boolean {
|
|
2477
|
+
return missingRequiredOutputAfterUsefulMutation(result) || partialExecutionWithUsefulMutation(result);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
function concreteFailureResult(result: SingleStepResult): boolean {
|
|
2481
|
+
return result.success === false && !partialEvidenceResult(result);
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2433
2484
|
function combinedAbortSignal(signals: Array<AbortSignal | undefined>): AbortSignal | undefined {
|
|
2434
2485
|
const activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));
|
|
2435
2486
|
if (activeSignals.length === 0) return undefined;
|
|
@@ -2501,6 +2552,8 @@ async function runSubagent(
|
|
|
2501
2552
|
const overallStartTime = Date.now();
|
|
2502
2553
|
const shareEnabled = config.share === true;
|
|
2503
2554
|
const asyncDir = config.asyncDir;
|
|
2555
|
+
const handoffWorkflowKey = config.workflowKey;
|
|
2556
|
+
const handoffChildRunId = handoffWorkflowKey ? id : undefined;
|
|
2504
2557
|
const statusPath = path.join(asyncDir, "status.json");
|
|
2505
2558
|
const eventsPath = path.join(asyncDir, "events.jsonl");
|
|
2506
2559
|
const logPath = path.join(asyncDir, `subagent-log-${id}.md`);
|
|
@@ -2509,7 +2562,6 @@ async function runSubagent(
|
|
|
2509
2562
|
const activeChildTimeouts = new Map<number, () => void>();
|
|
2510
2563
|
const activeChildStops = new Map<number, () => void>();
|
|
2511
2564
|
const childStopRequests = new Map<number, { childId: string; requestedAt: number }>();
|
|
2512
|
-
const activeChildTurnBudgetAborts = new Map<number, (message: string, state?: TurnBudgetState) => void>();
|
|
2513
2565
|
const pendingStepSteers: SteerRequest[] = [];
|
|
2514
2566
|
const steeringCapabilities = new Map<number, SteerCapability>();
|
|
2515
2567
|
let interrupted = false;
|
|
@@ -2518,7 +2570,6 @@ async function runSubagent(
|
|
|
2518
2570
|
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
2519
2571
|
let timedOut = false;
|
|
2520
2572
|
let stopped = false;
|
|
2521
|
-
let turnBudgetExceeded = false;
|
|
2522
2573
|
let usageBudgetExceeded = false;
|
|
2523
2574
|
const timeoutMessage = config.timeoutMs !== undefined ? `Subagent timed out after ${config.timeoutMs}ms.` : undefined;
|
|
2524
2575
|
const stopMessage = "Subagent stopped by user.";
|
|
@@ -2539,8 +2590,11 @@ async function runSubagent(
|
|
|
2539
2590
|
for (const task of step.parallel) {
|
|
2540
2591
|
const taskFlatIndex = flatStepCount;
|
|
2541
2592
|
const transcriptPath = resolveAsyncStepTranscriptPath(omitUndefinedProperties({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: taskFlatIndex, flatStepCount: initialFlatStepCount }));
|
|
2593
|
+
const taskSessionName = task.sessionName ?? deriveChildSessionName({ agent: task.agent, task: task.task, label: task.label });
|
|
2542
2594
|
initialStatusSteps.push(omitUndefinedProperties({
|
|
2543
2595
|
agent: task.agent,
|
|
2596
|
+
...(task.lane ? { lane: task.lane } : config.lane ? { lane: config.lane } : {}),
|
|
2597
|
+
...(taskSessionName ? { sessionName: taskSessionName } : {}),
|
|
2544
2598
|
...(externalRunnerStatus(task.runner) ? { runner: externalRunnerStatus(task.runner) } : {}),
|
|
2545
2599
|
...(statusStepDescription(task.task) ? { description: statusStepDescription(task.task) } : {}),
|
|
2546
2600
|
...(task.context ? { context: task.context } : {}),
|
|
@@ -2590,8 +2644,11 @@ async function runSubagent(
|
|
|
2590
2644
|
} else {
|
|
2591
2645
|
const stepFlatIndex = flatStepCount;
|
|
2592
2646
|
const transcriptPath = resolveAsyncStepTranscriptPath(omitUndefinedProperties({ artifactsDir, artifactConfig, runId: id, agent: step.agent, flatIndex: stepFlatIndex, flatStepCount: initialFlatStepCount }));
|
|
2647
|
+
const stepSessionName = step.sessionName ?? deriveChildSessionName({ agent: step.agent, task: step.task, label: step.label });
|
|
2593
2648
|
initialStatusSteps.push(omitUndefinedProperties({
|
|
2594
2649
|
agent: step.agent,
|
|
2650
|
+
...(step.lane ? { lane: step.lane } : config.lane ? { lane: config.lane } : {}),
|
|
2651
|
+
...(stepSessionName ? { sessionName: stepSessionName } : {}),
|
|
2595
2652
|
...(externalRunnerStatus(step.runner) ? { runner: externalRunnerStatus(step.runner) } : {}),
|
|
2596
2653
|
...(statusStepDescription(step.task) ? { description: statusStepDescription(step.task) } : {}),
|
|
2597
2654
|
...(step.context ? { context: step.context } : {}),
|
|
@@ -2654,7 +2711,6 @@ async function runSubagent(
|
|
|
2654
2711
|
lastUpdate: overallStartTime,
|
|
2655
2712
|
...(config.timeoutMs !== undefined ? { timeoutMs: config.timeoutMs } : {}),
|
|
2656
2713
|
...(config.deadlineAt !== undefined ? { deadlineAt: config.deadlineAt } : {}),
|
|
2657
|
-
...(config.turnBudget ? { turnBudget: initialTurnBudgetState(config.turnBudget) } : {}),
|
|
2658
2714
|
...(config.toolBudget ? { toolBudget: initialToolBudgetState(config.toolBudget) } : {}),
|
|
2659
2715
|
...(config.usageBudget ? { usageBudget: usageBudgetState(config.usageBudget, undefined) } : {}),
|
|
2660
2716
|
pid: process.pid,
|
|
@@ -2669,6 +2725,7 @@ async function runSubagent(
|
|
|
2669
2725
|
...(config.runFanoutBudget ? { runFanoutBudget: getRunFanoutBudgetSnapshot(config.runFanoutBudget) } : {}),
|
|
2670
2726
|
...(config.parentWorkflowRunId ? { parentWorkflowRunId: config.parentWorkflowRunId } : {}),
|
|
2671
2727
|
...(config.workflowKey ? { workflowKey: config.workflowKey } : {}),
|
|
2728
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
2672
2729
|
...(config.runnerProcessInstanceId ? { processTerminal: { version: 1 as const, state: "pending" as const, runId: id, runnerProcessInstanceId: config.runnerProcessInstanceId } } : {}),
|
|
2673
2730
|
steps: initialStatusSteps,
|
|
2674
2731
|
artifactsDir,
|
|
@@ -2786,6 +2843,7 @@ async function runSubagent(
|
|
|
2786
2843
|
const statusResultSummary = (state: AsyncStatus["state"]): string => {
|
|
2787
2844
|
if (statusPayload.error) return statusPayload.error;
|
|
2788
2845
|
if (state === "paused") return "Paused after interrupt. Waiting for explicit next action.";
|
|
2846
|
+
if (state === "partial") return "Subagent needs attention after partial work.";
|
|
2789
2847
|
if (state === "stopped") return stopMessage;
|
|
2790
2848
|
if (state === "rejected") return "Subagent rejected.";
|
|
2791
2849
|
return state === "complete" ? "Subagent completed." : "Subagent failed.";
|
|
@@ -2794,12 +2852,13 @@ async function runSubagent(
|
|
|
2794
2852
|
if (step.status === "complete" || step.status === "completed") return true;
|
|
2795
2853
|
if (step.status === "failed" || step.status === "stopped" || step.status === "rejected") return false;
|
|
2796
2854
|
if (state === "complete") return true;
|
|
2797
|
-
if (state === "failed" || state === "stopped" || state === "rejected") return false;
|
|
2855
|
+
if (state === "failed" || state === "partial" || state === "stopped" || state === "rejected") return false;
|
|
2798
2856
|
return undefined;
|
|
2799
2857
|
};
|
|
2800
2858
|
const writeRecoverableStatusResult = (): void => {
|
|
2801
2859
|
const state = statusResultState();
|
|
2802
2860
|
if (!state || finalResultCommitted || !config.sessionId) return;
|
|
2861
|
+
if ((state as string) === "complete") return;
|
|
2803
2862
|
const now = statusPayload.endedAt ?? statusPayload.lastUpdate ?? Date.now();
|
|
2804
2863
|
const summary = statusResultSummary(state);
|
|
2805
2864
|
runPersistence.write(resultPath, omitUndefinedProperties({
|
|
@@ -2811,10 +2870,11 @@ async function runSubagent(
|
|
|
2811
2870
|
success: state === "complete",
|
|
2812
2871
|
state,
|
|
2813
2872
|
summary,
|
|
2814
|
-
error: state === "failed" || state === "stopped" || state === "rejected" ? summary : undefined,
|
|
2873
|
+
error: state === "failed" || state === "partial" || state === "stopped" || state === "rejected" ? summary : undefined,
|
|
2815
2874
|
stopped: state === "stopped" ? true : undefined,
|
|
2816
2875
|
results: statusPayload.steps.map((step) => omitUndefinedProperties({
|
|
2817
2876
|
agent: step.agent,
|
|
2877
|
+
...(step.sessionName ? { sessionName: step.sessionName } : {}),
|
|
2818
2878
|
output: step.status === "complete" || step.status === "completed" ? "" : step.error ?? summary,
|
|
2819
2879
|
error: step.error,
|
|
2820
2880
|
success: statusResultSuccess(state, step),
|
|
@@ -2822,6 +2882,7 @@ async function runSubagent(
|
|
|
2822
2882
|
model: step.model,
|
|
2823
2883
|
attemptedModels: step.attemptedModels,
|
|
2824
2884
|
modelAttempts: step.modelAttempts,
|
|
2885
|
+
usage: usageFromAttempts(step.modelAttempts),
|
|
2825
2886
|
contextOverflow: step.contextOverflow,
|
|
2826
2887
|
})),
|
|
2827
2888
|
exitCode: state === "complete" || state === "paused" ? 0 : 1,
|
|
@@ -2916,7 +2977,7 @@ async function runSubagent(
|
|
|
2916
2977
|
};
|
|
2917
2978
|
const childStopResult = (index: number, agent: string, context?: "fresh" | "fork"): SingleStepResult => {
|
|
2918
2979
|
markChildStopped(index);
|
|
2919
|
-
return stoppedStepResult(agent, context);
|
|
2980
|
+
return stoppedStepResult(agent, context, requiredStatusStep(statusPayload, index).sessionName);
|
|
2920
2981
|
};
|
|
2921
2982
|
const stopChildStep = (request: StopRequest): void => {
|
|
2922
2983
|
if (request.targetIndex === undefined) {
|
|
@@ -2959,13 +3020,6 @@ async function runSubagent(
|
|
|
2959
3020
|
activeChildStops.set(flatIndex, stop);
|
|
2960
3021
|
if (stopped || childStopRequests.has(flatIndex)) stop();
|
|
2961
3022
|
};
|
|
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
3023
|
const interruptActiveChildren = (): void => {
|
|
2970
3024
|
for (const interrupt of [...activeChildInterrupts.values()]) interrupt();
|
|
2971
3025
|
};
|
|
@@ -3075,23 +3129,26 @@ async function runSubagent(
|
|
|
3075
3129
|
}
|
|
3076
3130
|
}
|
|
3077
3131
|
};
|
|
3078
|
-
const pausedStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => omitUndefinedProperties({
|
|
3132
|
+
const pausedStepResult = (agent: string, context?: "fresh" | "fork", sessionName?: string): SingleStepResult => omitUndefinedProperties({
|
|
3079
3133
|
agent,
|
|
3134
|
+
sessionName,
|
|
3080
3135
|
context,
|
|
3081
3136
|
output: "Paused after interrupt. Waiting for explicit next action.",
|
|
3082
3137
|
exitCode: 0,
|
|
3083
3138
|
interrupted: true,
|
|
3084
3139
|
});
|
|
3085
|
-
const timedOutStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => omitUndefinedProperties({
|
|
3140
|
+
const timedOutStepResult = (agent: string, context?: "fresh" | "fork", sessionName?: string): SingleStepResult => omitUndefinedProperties({
|
|
3086
3141
|
agent,
|
|
3142
|
+
sessionName,
|
|
3087
3143
|
context,
|
|
3088
3144
|
output: timeoutMessage ?? "Subagent timed out.",
|
|
3089
3145
|
error: timeoutMessage ?? "Subagent timed out.",
|
|
3090
3146
|
exitCode: 1,
|
|
3091
3147
|
timedOut: true,
|
|
3092
3148
|
});
|
|
3093
|
-
const stoppedStepResult = (agent: string, context?: "fresh" | "fork"): SingleStepResult => omitUndefinedProperties({
|
|
3149
|
+
const stoppedStepResult = (agent: string, context?: "fresh" | "fork", sessionName?: string): SingleStepResult => omitUndefinedProperties({
|
|
3094
3150
|
agent,
|
|
3151
|
+
sessionName,
|
|
3095
3152
|
context,
|
|
3096
3153
|
output: stopMessage,
|
|
3097
3154
|
error: stopMessage,
|
|
@@ -3481,66 +3538,6 @@ async function runSubagent(
|
|
|
3481
3538
|
statusPayload.lastUpdate = now;
|
|
3482
3539
|
writeStatusPayload();
|
|
3483
3540
|
};
|
|
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
3541
|
const updateStepFromChildEvent = (flatIndex: number, event: ChildEvent): void => {
|
|
3545
3542
|
const step = statusPayload.steps[flatIndex];
|
|
3546
3543
|
if (!step) return;
|
|
@@ -3681,13 +3678,6 @@ async function runSubagent(
|
|
|
3681
3678
|
refreshUsageBudget();
|
|
3682
3679
|
}
|
|
3683
3680
|
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
3681
|
}
|
|
3692
3682
|
syncTopLevelCurrentTool();
|
|
3693
3683
|
step.lastActivityAt = now;
|
|
@@ -3916,7 +3906,7 @@ async function runSubagent(
|
|
|
3916
3906
|
let flatIndex = 0;
|
|
3917
3907
|
let stepCursor = 0;
|
|
3918
3908
|
while (true) {
|
|
3919
|
-
if (interrupted || timedOut || stopped
|
|
3909
|
+
if (interrupted || timedOut || stopped) break;
|
|
3920
3910
|
consumePendingAppendRequests();
|
|
3921
3911
|
if (stepCursor >= steps.length) break;
|
|
3922
3912
|
refreshUsageBudget();
|
|
@@ -4061,10 +4051,12 @@ async function runSubagent(
|
|
|
4061
4051
|
: step.parallel.outputPath;
|
|
4062
4052
|
const taskText = task.task ?? step.parallel.task;
|
|
4063
4053
|
const materializedTask = step.parallel.namespaceOutputPath ? injectSingleOutputInstruction(taskText, outputPath, step.parallel) : taskText;
|
|
4054
|
+
const sessionName = deriveChildSessionName({ agent: step.parallel.agent, task: taskText, label: task.label ?? step.parallel.label });
|
|
4064
4055
|
return omitUndefinedProperties({
|
|
4065
4056
|
...step.parallel,
|
|
4066
4057
|
runFanoutPath: `chain[${stepIndex}].expand[${itemIndex}]`,
|
|
4067
4058
|
task: materializedTask,
|
|
4059
|
+
...(sessionName ? { sessionName } : {}),
|
|
4068
4060
|
effectiveAcceptance: resolveEffectiveAcceptance(omitUndefinedProperties({
|
|
4069
4061
|
explicit: step.parallel.acceptanceInput,
|
|
4070
4062
|
agentName: step.parallel.agent,
|
|
@@ -4095,6 +4087,7 @@ async function runSubagent(
|
|
|
4095
4087
|
const transcriptPath = resolveAsyncStepTranscriptPath(omitUndefinedProperties({ artifactsDir, artifactConfig, runId: id, agent: task.agent, flatIndex: groupStartFlatIndex + itemIndex, flatStepCount: dynamicFlatStepCount }));
|
|
4096
4088
|
return omitUndefinedProperties({
|
|
4097
4089
|
agent: task.agent,
|
|
4090
|
+
...(task.sessionName ? { sessionName: task.sessionName } : {}),
|
|
4098
4091
|
...(statusStepDescription(task.task) ? { description: statusStepDescription(task.task) } : {}),
|
|
4099
4092
|
...(task.context ? { context: task.context } : {}),
|
|
4100
4093
|
...(task.phase ?? step.phase ? { phase: task.phase ?? step.phase } : {}),
|
|
@@ -4178,12 +4171,12 @@ async function runSubagent(
|
|
|
4178
4171
|
usageBudgetExceeded = true;
|
|
4179
4172
|
writeStatusPayload();
|
|
4180
4173
|
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 });
|
|
4174
|
+
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
4175
|
}
|
|
4183
|
-
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
4184
|
-
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
4176
|
+
if (timedOut) return timedOutStepResult(task.agent, task.context, task.sessionName);
|
|
4177
|
+
if (stopped) return stoppedStepResult(task.agent, task.context, task.sessionName);
|
|
4185
4178
|
if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
|
|
4186
|
-
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
4179
|
+
if (interrupted) return pausedStepResult(task.agent, task.context, task.sessionName);
|
|
4187
4180
|
if (aborted && failFast) {
|
|
4188
4181
|
const skippedAt = Date.now();
|
|
4189
4182
|
requiredStatusStep(statusPayload, fi).status = "failed";
|
|
@@ -4194,7 +4187,7 @@ async function runSubagent(
|
|
|
4194
4187
|
requiredStatusStep(statusPayload, fi).exitCode = -1;
|
|
4195
4188
|
statusPayload.lastUpdate = skippedAt;
|
|
4196
4189
|
writeStatusPayload();
|
|
4197
|
-
return omitUndefinedProperties({ agent: task.agent, context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true });
|
|
4190
|
+
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
4191
|
}
|
|
4199
4192
|
const taskStartTime = Date.now();
|
|
4200
4193
|
statusPayload.currentStep = fi;
|
|
@@ -4230,13 +4223,11 @@ async function runSubagent(
|
|
|
4230
4223
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4231
4224
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4232
4225
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
4233
|
-
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
4234
4226
|
timeoutSignal: timeoutAbortController.signal,
|
|
4235
4227
|
stopSignal: stopAbortController.signal,
|
|
4236
4228
|
trackedMutationEvidenceForCompletionGuard: false,
|
|
4237
4229
|
timeoutMessage,
|
|
4238
4230
|
stopMessage,
|
|
4239
|
-
turnBudget: config.turnBudget,
|
|
4240
4231
|
toolTimeoutMs: task.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4241
4232
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4242
4233
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
@@ -4244,6 +4235,7 @@ async function runSubagent(
|
|
|
4244
4235
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4245
4236
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4246
4237
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4238
|
+
usageBudgetExhausted: () => refreshUsageBudget()?.exhausted === true,
|
|
4247
4239
|
orcaProgressTab,
|
|
4248
4240
|
}), config.deadlineAt);
|
|
4249
4241
|
const taskEndTime = Date.now();
|
|
@@ -4255,16 +4247,11 @@ async function runSubagent(
|
|
|
4255
4247
|
requiredStatusStep(statusPayload, fi).exitCode = stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
4256
4248
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "timedOut", timedOut || singleResult.timedOut ? true : undefined);
|
|
4257
4249
|
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
4250
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudget", singleResult.toolBudget);
|
|
4262
4251
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudgetBlocked", singleResult.toolBudgetBlocked);
|
|
4263
4252
|
if (singleResult.toolBudget) statusPayload.toolBudget = singleResult.toolBudget;
|
|
4264
4253
|
if (singleResult.toolBudgetBlocked) statusPayload.toolBudgetBlocked = true;
|
|
4265
|
-
|
|
4266
|
-
if (singleResult.turnBudgetExceeded) statusPayload.turnBudgetExceeded = true;
|
|
4267
|
-
if (singleResult.wrapUpRequested) statusPayload.wrapUpRequested = true;
|
|
4254
|
+
setOptionalProperty(requiredStatusStep(statusPayload, fi), "sessionName", singleResult.sessionName);
|
|
4268
4255
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "model", singleResult.model);
|
|
4269
4256
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, fi).thinking));
|
|
4270
4257
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "attemptedModels", singleResult.attemptedModels);
|
|
@@ -4316,6 +4303,7 @@ async function runSubagent(
|
|
|
4316
4303
|
for (const pr of parallelResults) {
|
|
4317
4304
|
results.push(omitUndefinedProperties({
|
|
4318
4305
|
agent: pr.agent,
|
|
4306
|
+
...(pr.sessionName ? { sessionName: pr.sessionName } : {}),
|
|
4319
4307
|
context: pr.context,
|
|
4320
4308
|
agentContract: pr.agentContract,
|
|
4321
4309
|
launchContractDigest: pr.launchContractDigest,
|
|
@@ -4331,9 +4319,6 @@ async function runSubagent(
|
|
|
4331
4319
|
interrupted: pr.interrupted,
|
|
4332
4320
|
timedOut: pr.timedOut,
|
|
4333
4321
|
stopped: pr.stopped,
|
|
4334
|
-
turnBudget: pr.turnBudget,
|
|
4335
|
-
turnBudgetExceeded: pr.turnBudgetExceeded,
|
|
4336
|
-
wrapUpRequested: pr.wrapUpRequested,
|
|
4337
4322
|
toolBudget: pr.toolBudget,
|
|
4338
4323
|
toolBudgetBlocked: pr.toolBudgetBlocked,
|
|
4339
4324
|
sessionFile: pr.sessionFile,
|
|
@@ -4343,6 +4328,7 @@ async function runSubagent(
|
|
|
4343
4328
|
modelAttempts: pr.modelAttempts,
|
|
4344
4329
|
contextOverflow: pr.contextOverflow,
|
|
4345
4330
|
totalCost: pr.totalCost,
|
|
4331
|
+
usage: pr.usage,
|
|
4346
4332
|
artifactPaths: pr.artifactPaths,
|
|
4347
4333
|
transcriptPath: pr.transcriptPath,
|
|
4348
4334
|
transcriptError: pr.transcriptError,
|
|
@@ -4486,6 +4472,23 @@ async function runSubagent(
|
|
|
4486
4472
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
4487
4473
|
: undefined,
|
|
4488
4474
|
baseDir: config.worktreeBaseDir,
|
|
4475
|
+
beforeCreate: (plannedSetup) => {
|
|
4476
|
+
for (const worktree of plannedSetup.worktrees) setStatusWorktreeReference(requiredStatusStep(statusPayload, groupStartFlatIndex + worktree.index), worktree);
|
|
4477
|
+
const pendingHandoff = writePendingParallelHandoff({
|
|
4478
|
+
manifestPath: parallelHandoffPath(asyncDir),
|
|
4479
|
+
runId: id,
|
|
4480
|
+
mode: (config.resultMode ?? statusPayload.mode) === "parallel" ? "parallel" : "chain",
|
|
4481
|
+
source: "async",
|
|
4482
|
+
cwd,
|
|
4483
|
+
stepIndex,
|
|
4484
|
+
flatStartIndex: groupStartFlatIndex,
|
|
4485
|
+
setup: plannedSetup,
|
|
4486
|
+
laneBindings: handoffWorkflowKey || config.lane ? [{ index: groupStartFlatIndex, taskIndex: 0, ...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}), ...(handoffChildRunId ? { runId: handoffChildRunId } : {}), ...(config.lane ? { lane: config.lane } : {}) }] : undefined,
|
|
4487
|
+
});
|
|
4488
|
+
statusPayload.parallelHandoff = pendingHandoff;
|
|
4489
|
+
statusPayload.lastUpdate = Date.now();
|
|
4490
|
+
writeStatusPayload();
|
|
4491
|
+
},
|
|
4489
4492
|
}));
|
|
4490
4493
|
} catch (error) {
|
|
4491
4494
|
const setupError = error instanceof Error ? error.message : String(error);
|
|
@@ -4510,18 +4513,6 @@ async function runSubagent(
|
|
|
4510
4513
|
}
|
|
4511
4514
|
|
|
4512
4515
|
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
4516
|
if (group.worktree) ensureParallelProgressFile(cwd, group);
|
|
4526
4517
|
const groupStartTime = Date.now();
|
|
4527
4518
|
markParallelGroupRunning({
|
|
@@ -4558,12 +4549,12 @@ async function runSubagent(
|
|
|
4558
4549
|
appendJsonl(eventsPath, JSON.stringify({
|
|
4559
4550
|
type: "subagent.step.failed", ts: skippedAt, runId: id, stepIndex: fi, agent: task.agent, exitCode: 1, durationMs: 0,
|
|
4560
4551
|
}));
|
|
4561
|
-
return omitUndefinedProperties({ agent: task.agent, context: task.context, output: message, error: message, exitCode: 1 as number | null, skipped: true });
|
|
4552
|
+
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
4553
|
}
|
|
4563
|
-
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
4564
|
-
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
4554
|
+
if (timedOut) return timedOutStepResult(task.agent, task.context, task.sessionName);
|
|
4555
|
+
if (stopped) return stoppedStepResult(task.agent, task.context, task.sessionName);
|
|
4565
4556
|
if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
|
|
4566
|
-
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
4557
|
+
if (interrupted) return pausedStepResult(task.agent, task.context, task.sessionName);
|
|
4567
4558
|
if (aborted && failFast) {
|
|
4568
4559
|
const skippedAt = Date.now();
|
|
4569
4560
|
requiredStatusStep(statusPayload, fi).status = "failed";
|
|
@@ -4578,7 +4569,7 @@ async function runSubagent(
|
|
|
4578
4569
|
appendJsonl(eventsPath, JSON.stringify({
|
|
4579
4570
|
type: "subagent.step.failed", ts: skippedAt, runId: id, stepIndex: fi, agent: task.agent, exitCode: -1, durationMs: 0,
|
|
4580
4571
|
}));
|
|
4581
|
-
return omitUndefinedProperties({ agent: task.agent, context: task.context, output: "(skipped — fail-fast)", exitCode: -1 as number | null, skipped: true });
|
|
4572
|
+
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
4573
|
}
|
|
4583
4574
|
|
|
4584
4575
|
const taskStartTime = Date.now();
|
|
@@ -4626,13 +4617,11 @@ async function runSubagent(
|
|
|
4626
4617
|
registerInterrupt: (interrupt) => registerStepInterrupt(fi, interrupt),
|
|
4627
4618
|
registerTimeout: (interrupt) => registerStepTimeout(fi, interrupt),
|
|
4628
4619
|
registerStop: (stop) => registerStepStop(fi, stop),
|
|
4629
|
-
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
4630
4620
|
timeoutSignal: timeoutAbortController.signal,
|
|
4631
4621
|
stopSignal: stopAbortController.signal,
|
|
4632
4622
|
trackedMutationEvidenceForCompletionGuard: Boolean(worktreeSetup),
|
|
4633
4623
|
timeoutMessage,
|
|
4634
4624
|
stopMessage,
|
|
4635
|
-
turnBudget: config.turnBudget,
|
|
4636
4625
|
toolTimeoutMs: taskForRun.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4637
4626
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4638
4627
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
@@ -4640,6 +4629,7 @@ async function runSubagent(
|
|
|
4640
4629
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4641
4630
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4642
4631
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4632
|
+
usageBudgetExhausted: () => refreshUsageBudget()?.exhausted === true,
|
|
4643
4633
|
orcaProgressTab,
|
|
4644
4634
|
}), config.deadlineAt);
|
|
4645
4635
|
if (task.sessionFile) {
|
|
@@ -4657,16 +4647,11 @@ async function runSubagent(
|
|
|
4657
4647
|
requiredStatusStep(statusPayload, fi).exitCode = stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
4658
4648
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "timedOut", timedOut || singleResult.timedOut ? true : undefined);
|
|
4659
4649
|
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
4650
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudget", singleResult.toolBudget);
|
|
4664
4651
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "toolBudgetBlocked", singleResult.toolBudgetBlocked);
|
|
4665
4652
|
if (singleResult.toolBudget) statusPayload.toolBudget = singleResult.toolBudget;
|
|
4666
4653
|
if (singleResult.toolBudgetBlocked) statusPayload.toolBudgetBlocked = true;
|
|
4667
|
-
|
|
4668
|
-
if (singleResult.turnBudgetExceeded) statusPayload.turnBudgetExceeded = true;
|
|
4669
|
-
if (singleResult.wrapUpRequested) statusPayload.wrapUpRequested = true;
|
|
4654
|
+
setOptionalProperty(requiredStatusStep(statusPayload, fi), "sessionName", singleResult.sessionName);
|
|
4670
4655
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "model", singleResult.model);
|
|
4671
4656
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, fi).thinking));
|
|
4672
4657
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "attemptedModels", singleResult.attemptedModels);
|
|
@@ -4775,9 +4760,6 @@ async function runSubagent(
|
|
|
4775
4760
|
interrupted: pr.interrupted,
|
|
4776
4761
|
timedOut: pr.timedOut,
|
|
4777
4762
|
stopped: pr.stopped,
|
|
4778
|
-
turnBudget: pr.turnBudget,
|
|
4779
|
-
turnBudgetExceeded: pr.turnBudgetExceeded,
|
|
4780
|
-
wrapUpRequested: pr.wrapUpRequested,
|
|
4781
4763
|
toolBudget: pr.toolBudget,
|
|
4782
4764
|
toolBudgetBlocked: pr.toolBudgetBlocked,
|
|
4783
4765
|
sessionFile: pr.sessionFile,
|
|
@@ -4787,6 +4769,7 @@ async function runSubagent(
|
|
|
4787
4769
|
modelAttempts: pr.modelAttempts,
|
|
4788
4770
|
contextOverflow: pr.contextOverflow,
|
|
4789
4771
|
totalCost: pr.totalCost,
|
|
4772
|
+
usage: pr.usage,
|
|
4790
4773
|
artifactPaths: pr.artifactPaths,
|
|
4791
4774
|
transcriptPath: pr.transcriptPath,
|
|
4792
4775
|
transcriptError: pr.transcriptError,
|
|
@@ -4840,12 +4823,14 @@ async function runSubagent(
|
|
|
4840
4823
|
diffs: captured.diffs,
|
|
4841
4824
|
results: parallelResults.map((result) => ({
|
|
4842
4825
|
agent: result.agent,
|
|
4826
|
+
...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}),
|
|
4827
|
+
...(handoffChildRunId ? { runId: handoffChildRunId } : {}),
|
|
4828
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
4843
4829
|
status: result.stopped || (result.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
4844
4830
|
processSignal: result.processSignal,
|
|
4845
4831
|
interrupted: result.interrupted,
|
|
4846
4832
|
timedOut: result.timedOut,
|
|
4847
4833
|
stopped: result.stopped,
|
|
4848
|
-
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
4849
4834
|
}))) ? "stopped" as const : result.interrupted ? "paused" as const : result.exitCode === 0 ? "completed" as const : "failed" as const,
|
|
4850
4835
|
summary: result.output || result.error || "(no output)",
|
|
4851
4836
|
...(result.artifactPaths?.outputPath ? { outputPath: result.artifactPaths.outputPath } : {}),
|
|
@@ -4891,12 +4876,12 @@ async function runSubagent(
|
|
|
4891
4876
|
} else {
|
|
4892
4877
|
const seqStep = step as SubagentStep;
|
|
4893
4878
|
if (timedOut) {
|
|
4894
|
-
results.push(timedOutStepResult(seqStep.agent, seqStep.context));
|
|
4879
|
+
results.push(timedOutStepResult(seqStep.agent, seqStep.context, seqStep.sessionName));
|
|
4895
4880
|
flatIndex++;
|
|
4896
4881
|
continue;
|
|
4897
4882
|
}
|
|
4898
4883
|
if (stopped) {
|
|
4899
|
-
results.push(stoppedStepResult(seqStep.agent, seqStep.context));
|
|
4884
|
+
results.push(stoppedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName));
|
|
4900
4885
|
flatIndex++;
|
|
4901
4886
|
continue;
|
|
4902
4887
|
}
|
|
@@ -4906,7 +4891,7 @@ async function runSubagent(
|
|
|
4906
4891
|
continue;
|
|
4907
4892
|
}
|
|
4908
4893
|
if (interrupted) {
|
|
4909
|
-
results.push(pausedStepResult(seqStep.agent, seqStep.context));
|
|
4894
|
+
results.push(pausedStepResult(seqStep.agent, seqStep.context, seqStep.sessionName));
|
|
4910
4895
|
flatIndex++;
|
|
4911
4896
|
continue;
|
|
4912
4897
|
}
|
|
@@ -4919,17 +4904,27 @@ async function runSubagent(
|
|
|
4919
4904
|
? omitUndefinedProperties({ hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs })
|
|
4920
4905
|
: undefined,
|
|
4921
4906
|
baseDir: config.worktreeBaseDir,
|
|
4907
|
+
beforeCreate: (plannedSetup) => {
|
|
4908
|
+
const worktree = plannedSetup.worktrees[0];
|
|
4909
|
+
if (worktree) {
|
|
4910
|
+
setStatusWorktreeReference(requiredStatusStep(statusPayload, flatIndex), worktree);
|
|
4911
|
+
}
|
|
4912
|
+
const pendingHandoff = writePendingParallelHandoff({
|
|
4913
|
+
manifestPath: parallelHandoffPath(asyncDir),
|
|
4914
|
+
runId: id,
|
|
4915
|
+
mode: "single",
|
|
4916
|
+
source: "async",
|
|
4917
|
+
cwd,
|
|
4918
|
+
stepIndex,
|
|
4919
|
+
flatStartIndex: flatIndex,
|
|
4920
|
+
setup: plannedSetup,
|
|
4921
|
+
laneBindings: handoffWorkflowKey || config.lane ? [{ index: flatIndex, taskIndex: 0, ...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}), ...(handoffChildRunId ? { runId: handoffChildRunId } : {}), ...(config.lane ? { lane: config.lane } : {}) }] : undefined,
|
|
4922
|
+
});
|
|
4923
|
+
statusPayload.parallelHandoff = pendingHandoff;
|
|
4924
|
+
statusPayload.lastUpdate = Date.now();
|
|
4925
|
+
writeStatusPayload();
|
|
4926
|
+
},
|
|
4922
4927
|
}));
|
|
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
4928
|
} catch (error) {
|
|
4934
4929
|
if (singleWorktreeSetup) cleanupWorktrees(singleWorktreeSetup);
|
|
4935
4930
|
throw error;
|
|
@@ -4982,12 +4977,10 @@ async function runSubagent(
|
|
|
4982
4977
|
registerInterrupt: (interrupt) => registerStepInterrupt(flatIndex, interrupt),
|
|
4983
4978
|
registerTimeout: (interrupt) => registerStepTimeout(flatIndex, interrupt),
|
|
4984
4979
|
registerStop: (stop) => registerStepStop(flatIndex, stop),
|
|
4985
|
-
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(flatIndex, abort),
|
|
4986
4980
|
timeoutSignal: timeoutAbortController.signal,
|
|
4987
4981
|
stopSignal: stopAbortController.signal,
|
|
4988
4982
|
timeoutMessage,
|
|
4989
4983
|
stopMessage,
|
|
4990
|
-
turnBudget: config.turnBudget,
|
|
4991
4984
|
toolTimeoutMs: seqStep.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4992
4985
|
onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking, attempt.contextLimit),
|
|
4993
4986
|
onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
|
|
@@ -4995,6 +4988,7 @@ async function runSubagent(
|
|
|
4995
4988
|
onExternalProcess: (process) => updateExternalProcess(flatIndex, process),
|
|
4996
4989
|
onExternalJob: (externalJob) => updateExternalJob(flatIndex, externalJob),
|
|
4997
4990
|
skipAcceptance: () => timedOut || stopped || childStopRequests.has(flatIndex),
|
|
4991
|
+
usageBudgetExhausted: () => refreshUsageBudget()?.exhausted === true,
|
|
4998
4992
|
orcaProgressTab,
|
|
4999
4993
|
}), config.deadlineAt);
|
|
5000
4994
|
} catch (error) {
|
|
@@ -5009,6 +5003,7 @@ async function runSubagent(
|
|
|
5009
5003
|
const childStopped = singleResult.stopped === true;
|
|
5010
5004
|
results.push(omitUndefinedProperties({
|
|
5011
5005
|
agent: singleResult.agent,
|
|
5006
|
+
...(singleResult.sessionName ? { sessionName: singleResult.sessionName } : {}),
|
|
5012
5007
|
context: singleResult.context,
|
|
5013
5008
|
agentContract: singleResult.agentContract,
|
|
5014
5009
|
launchContractDigest: singleResult.launchContractDigest,
|
|
@@ -5027,6 +5022,7 @@ async function runSubagent(
|
|
|
5027
5022
|
modelAttempts: singleResult.modelAttempts,
|
|
5028
5023
|
contextOverflow: singleResult.contextOverflow,
|
|
5029
5024
|
totalCost: singleResult.totalCost,
|
|
5025
|
+
usage: singleResult.usage,
|
|
5030
5026
|
artifactPaths: singleResult.artifactPaths,
|
|
5031
5027
|
transcriptPath: singleResult.transcriptPath,
|
|
5032
5028
|
transcriptError: singleResult.transcriptError,
|
|
@@ -5044,9 +5040,6 @@ async function runSubagent(
|
|
|
5044
5040
|
interrupted: singleResult.interrupted,
|
|
5045
5041
|
timedOut: timedOut || singleResult.timedOut ? true : undefined,
|
|
5046
5042
|
stopped: stopped || childStopped ? true : undefined,
|
|
5047
|
-
turnBudget: singleResult.turnBudget,
|
|
5048
|
-
turnBudgetExceeded: singleResult.turnBudgetExceeded,
|
|
5049
|
-
wrapUpRequested: singleResult.wrapUpRequested,
|
|
5050
5043
|
toolBudget: singleResult.toolBudget,
|
|
5051
5044
|
toolBudgetBlocked: singleResult.toolBudgetBlocked,
|
|
5052
5045
|
runner: singleResult.runner,
|
|
@@ -5101,16 +5094,11 @@ async function runSubagent(
|
|
|
5101
5094
|
requiredStatusStep(statusPayload, flatIndex).exitCode = stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode;
|
|
5102
5095
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "timedOut", timedOut || singleResult.timedOut ? true : undefined);
|
|
5103
5096
|
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
5097
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "toolBudget", singleResult.toolBudget);
|
|
5108
5098
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "toolBudgetBlocked", singleResult.toolBudgetBlocked);
|
|
5109
5099
|
if (singleResult.toolBudget) statusPayload.toolBudget = singleResult.toolBudget;
|
|
5110
5100
|
if (singleResult.toolBudgetBlocked) statusPayload.toolBudgetBlocked = true;
|
|
5111
|
-
|
|
5112
|
-
if (singleResult.turnBudgetExceeded) statusPayload.turnBudgetExceeded = true;
|
|
5113
|
-
if (singleResult.wrapUpRequested) statusPayload.wrapUpRequested = true;
|
|
5101
|
+
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "sessionName", singleResult.sessionName);
|
|
5114
5102
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "model", singleResult.model);
|
|
5115
5103
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "thinking", resolveEffectiveThinking(singleResult.model, requiredStatusStep(statusPayload, flatIndex).thinking));
|
|
5116
5104
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "attemptedModels", singleResult.attemptedModels);
|
|
@@ -5171,6 +5159,9 @@ async function runSubagent(
|
|
|
5171
5159
|
diffs,
|
|
5172
5160
|
results: [{
|
|
5173
5161
|
agent: singleResult.agent,
|
|
5162
|
+
...(handoffWorkflowKey ? { workflowKey: handoffWorkflowKey } : {}),
|
|
5163
|
+
...(handoffChildRunId ? { runId: handoffChildRunId } : {}),
|
|
5164
|
+
...(config.lane ? { lane: config.lane } : {}),
|
|
5174
5165
|
status: singleResult.stopped ? "stopped" as const : singleResult.interrupted ? "paused" as const : singleResult.exitCode === 0 ? "completed" as const : "failed" as const,
|
|
5175
5166
|
summary: singleResult.output || singleResult.error || "(no output)",
|
|
5176
5167
|
...(singleResult.artifactPaths?.outputPath ? { outputPath: singleResult.artifactPaths.outputPath } : {}),
|
|
@@ -5291,14 +5282,14 @@ async function runSubagent(
|
|
|
5291
5282
|
if (!timedOut && !stopped && !interrupted && config.timeoutMs !== undefined && results.some((result) => result.timedOut === true && result.error === timeoutMessage)) {
|
|
5292
5283
|
timedOut = true;
|
|
5293
5284
|
}
|
|
5294
|
-
const signalTerminated = !stopped && !timedOut && !
|
|
5285
|
+
const signalTerminated = !stopped && !timedOut && !interrupted && results.some((result) => result.exitCode !== 0 && isUnexplainedProcessSignal(omitUndefinedProperties({
|
|
5295
5286
|
processSignal: result.processSignal,
|
|
5296
5287
|
interrupted: result.interrupted,
|
|
5297
5288
|
timedOut: result.timedOut,
|
|
5298
5289
|
stopped: result.stopped,
|
|
5299
|
-
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
5300
5290
|
})));
|
|
5301
|
-
|
|
5291
|
+
const partialWithEvidence = !stopped && !signalTerminated && !timedOut && !usageBudgetExceeded && !interrupted && results.some(partialEvidenceResult) && !results.some(concreteFailureResult);
|
|
5292
|
+
statusPayload.state = stopped || signalTerminated ? "stopped" : timedOut || usageBudgetExceeded ? "failed" : interrupted ? "paused" : results.every((r) => r.success) ? "complete" : partialWithEvidence ? "partial" : "failed";
|
|
5302
5293
|
closeSteerInbox(asyncDir, statusPayload.state, (filePath, payload) => runPersistence.write(filePath, payload));
|
|
5303
5294
|
disposeControlInbox();
|
|
5304
5295
|
for (const request of consumeSteerRequests(asyncDir)) deliverSteerRequest(request);
|
|
@@ -5328,13 +5319,17 @@ async function runSubagent(
|
|
|
5328
5319
|
statusPayload.timedOut = true;
|
|
5329
5320
|
statusPayload.error = timeoutMessage ?? "Subagent timed out.";
|
|
5330
5321
|
}
|
|
5331
|
-
if (turnBudgetExceeded && !statusPayload.error) {
|
|
5332
|
-
const budget = statusPayload.turnBudget;
|
|
5333
|
-
statusPayload.error = budget ? turnBudgetExceededMessage(budget, budget.turnCount) : "Subagent exceeded turn budget.";
|
|
5334
|
-
}
|
|
5335
5322
|
if (usageBudgetExceeded && statusPayload.usageBudget && !statusPayload.error) {
|
|
5336
5323
|
statusPayload.error = usageBudgetExceededMessage(statusPayload.usageBudget);
|
|
5337
5324
|
}
|
|
5325
|
+
if (partialWithEvidence) {
|
|
5326
|
+
const partialResult = results.find(partialEvidenceResult);
|
|
5327
|
+
statusPayload.activityState = "needs_attention";
|
|
5328
|
+
statusPayload.error = partialResult?.error ?? statusPayload.error;
|
|
5329
|
+
for (const step of statusPayload.steps) {
|
|
5330
|
+
if (step.status === "failed") step.activityState = "needs_attention";
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5338
5333
|
statusPayload.endedAt = runEndedAt;
|
|
5339
5334
|
statusPayload.lastUpdate = runEndedAt;
|
|
5340
5335
|
setOptionalProperty(statusPayload, "sessionFile", effectiveSessionFile);
|
|
@@ -5344,8 +5339,10 @@ async function runSubagent(
|
|
|
5344
5339
|
setOptionalProperty(statusPayload, "shareUrl", shareUrl);
|
|
5345
5340
|
setOptionalProperty(statusPayload, "gistUrl", gistUrl);
|
|
5346
5341
|
setOptionalProperty(statusPayload, "shareError", shareError);
|
|
5347
|
-
if (statusPayload.state === "failed" && !statusPayload.error) {
|
|
5348
|
-
const
|
|
5342
|
+
if ((statusPayload.state === "failed" || statusPayload.state === "partial") && !statusPayload.error) {
|
|
5343
|
+
const concreteFailure = results.find(concreteFailureResult);
|
|
5344
|
+
const failedStep = concreteFailure ? undefined : statusPayload.steps.find((s) => s.status === "failed");
|
|
5345
|
+
if (concreteFailure?.error) statusPayload.error = concreteFailure.error;
|
|
5349
5346
|
if (failedStep?.agent) {
|
|
5350
5347
|
statusPayload.error = `Step failed: ${failedStep.agent}`;
|
|
5351
5348
|
}
|
|
@@ -5357,20 +5354,18 @@ async function runSubagent(
|
|
|
5357
5354
|
id,
|
|
5358
5355
|
agent: agentName,
|
|
5359
5356
|
mode: resultMode,
|
|
5360
|
-
success:
|
|
5361
|
-
state:
|
|
5362
|
-
summary: stopped ? stopMessage : signalTerminated ? (statusPayload.error ?? "Subagent process terminated by signal.") : timedOut ? (timeoutMessage ?? "Subagent timed out.") :
|
|
5357
|
+
success: statusPayload.state === "complete",
|
|
5358
|
+
state: statusPayload.state,
|
|
5359
|
+
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
5360
|
...(config.timeoutMs !== undefined ? { timeoutMs: config.timeoutMs } : {}),
|
|
5364
5361
|
...(config.deadlineAt !== undefined ? { deadlineAt: config.deadlineAt } : {}),
|
|
5365
|
-
...(statusPayload.turnBudget ? { turnBudget: statusPayload.turnBudget } : {}),
|
|
5366
|
-
...(statusPayload.turnBudgetExceeded ? { turnBudgetExceeded: true } : {}),
|
|
5367
|
-
...(statusPayload.wrapUpRequested ? { wrapUpRequested: true } : {}),
|
|
5368
5362
|
...(statusPayload.toolBudget ? { toolBudget: statusPayload.toolBudget } : {}),
|
|
5369
5363
|
...(statusPayload.toolBudgetBlocked ? { toolBudgetBlocked: true } : {}),
|
|
5370
5364
|
...(statusPayload.usageBudget ? { usageBudget: statusPayload.usageBudget } : {}),
|
|
5371
|
-
...(stopped ? { stopped: true, error: stopMessage } : timedOut ? { timedOut: true, error: timeoutMessage ?? "Subagent timed out." } :
|
|
5365
|
+
...(stopped ? { stopped: true, error: stopMessage } : timedOut ? { timedOut: true, error: timeoutMessage ?? "Subagent timed out." } : usageBudgetExceeded ? { error: statusPayload.error ?? "Usage budget exhausted." } : {}),
|
|
5372
5366
|
results: results.map((r) => omitUndefinedProperties({
|
|
5373
5367
|
agent: r.agent,
|
|
5368
|
+
...(r.sessionName ? { sessionName: r.sessionName } : {}),
|
|
5374
5369
|
context: r.context,
|
|
5375
5370
|
output: r.output,
|
|
5376
5371
|
outputState: r.outputState,
|
|
@@ -5382,9 +5377,6 @@ async function runSubagent(
|
|
|
5382
5377
|
timedOut: r.timedOut || undefined,
|
|
5383
5378
|
stopped: r.stopped || undefined,
|
|
5384
5379
|
processSignal: r.processSignal || undefined,
|
|
5385
|
-
turnBudget: r.turnBudget,
|
|
5386
|
-
turnBudgetExceeded: r.turnBudgetExceeded || undefined,
|
|
5387
|
-
wrapUpRequested: r.wrapUpRequested || undefined,
|
|
5388
5380
|
toolBudget: r.toolBudget,
|
|
5389
5381
|
toolBudgetBlocked: r.toolBudgetBlocked || undefined,
|
|
5390
5382
|
sessionFile: r.sessionFile,
|
|
@@ -5394,6 +5386,7 @@ async function runSubagent(
|
|
|
5394
5386
|
modelAttempts: r.modelAttempts,
|
|
5395
5387
|
contextOverflow: r.contextOverflow,
|
|
5396
5388
|
totalCost: r.totalCost,
|
|
5389
|
+
usage: r.usage,
|
|
5397
5390
|
artifactPaths: r.artifactPaths,
|
|
5398
5391
|
outputSaveError: r.outputSaveError,
|
|
5399
5392
|
metadataSaveError: r.metadataSaveError,
|
|
@@ -5426,7 +5419,7 @@ async function runSubagent(
|
|
|
5426
5419
|
capabilityAudit: statusPayload.capabilityAudit,
|
|
5427
5420
|
...(config.parentWorkflowRunId ? { parentWorkflowRunId: config.parentWorkflowRunId } : {}),
|
|
5428
5421
|
...(config.workflowKey ? { workflowKey: config.workflowKey } : {}),
|
|
5429
|
-
exitCode:
|
|
5422
|
+
exitCode: statusPayload.state === "complete" || statusPayload.state === "paused" ? 0 : 1,
|
|
5430
5423
|
timestamp: runEndedAt,
|
|
5431
5424
|
durationMs: runEndedAt - overallStartTime,
|
|
5432
5425
|
totalTokens: statusPayload.totalTokens,
|