pi-subagents 0.56.0 → 0.57.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 +53 -0
- package/agents/claude-code-writer.md +15 -0
- package/agents/claude-code.md +15 -0
- package/agents/codex-exec-writer.md +15 -0
- package/agents/codex-exec.md +15 -0
- package/agents/cursor-agent-writer.md +14 -0
- package/agents/cursor-agent.md +14 -0
- package/docs/agents.md +111 -15
- package/docs/configuration.md +37 -0
- package/docs/extension-api.md +2 -2
- package/docs/observability.md +8 -7
- package/docs/tool-reference.md +11 -1
- package/docs/workflows.md +20 -0
- package/package.json +1 -1
- package/skills/pi-subagents/references/execution-controls.md +1 -1
- package/src/agents/agent-management.ts +13 -5
- package/src/agents/agent-refinements.ts +4 -4
- package/src/agents/agents.ts +219 -48
- package/src/agents/builtin-names.ts +6 -0
- package/src/agents/runtime-agent-registry.ts +8 -2
- package/src/api/preflight.ts +2 -2
- package/src/extension/config.ts +70 -0
- package/src/extension/doctor.ts +3 -3
- package/src/extension/index.ts +18 -4
- package/src/extension/public-execution.ts +29 -13
- package/src/extension/rpc.ts +20 -5
- package/src/extension/schemas.ts +4 -3
- package/src/extension/tool-description.ts +11 -9
- package/src/inspectors/herdr/actions.ts +2 -1
- package/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/src/integrations/herdr-status.ts +46 -3
- package/src/runs/background/active-async-capacity.ts +77 -10
- package/src/runs/background/async-execution.ts +100 -9
- package/src/runs/background/async-job-tracker.ts +5 -0
- package/src/runs/background/async-retention.ts +20 -3
- package/src/runs/background/async-status.ts +5 -0
- package/src/runs/background/chain-root-attachment.ts +15 -1
- package/src/runs/background/fleet-view.ts +16 -10
- package/src/runs/background/inspect-rpc.ts +8 -8
- package/src/runs/background/result-files.ts +27 -14
- package/src/runs/background/result-watcher.ts +9 -5
- package/src/runs/background/run-status.ts +33 -6
- package/src/runs/background/scheduled-runs.ts +7 -1
- package/src/runs/background/subagent-runner.ts +177 -44
- package/src/runs/background/wait-completions.ts +4 -0
- package/src/runs/foreground/execution.ts +72 -10
- package/src/runs/foreground/foreground-control.ts +6 -0
- package/src/runs/foreground/foreground-history.ts +22 -1
- package/src/runs/foreground/subagent-executor.ts +270 -81
- package/src/runs/foreground/workflow-detach-reconcile.ts +16 -1
- package/src/runs/shared/child-protocol.ts +21 -7
- package/src/runs/shared/claude-code-adapter.ts +129 -0
- package/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/src/runs/shared/external-cli-contract.ts +167 -0
- package/src/runs/shared/external-cli-preflight.ts +122 -0
- package/src/runs/shared/external-cli-runner.ts +348 -55
- package/src/runs/shared/model-exclusions.ts +57 -7
- package/src/runs/shared/model-fallback.ts +23 -1
- package/src/runs/shared/mutation-evidence.ts +7 -2
- package/src/runs/shared/nested-events.ts +3 -1
- package/src/runs/shared/nested-render.ts +2 -2
- package/src/runs/shared/process-signal.ts +13 -0
- package/src/runs/shared/run-history.ts +21 -1
- package/src/shared/fork-context.ts +21 -0
- package/src/shared/formatters.ts +7 -1
- package/src/shared/pruned-fork.ts +450 -0
- package/src/shared/session-file-trust.ts +19 -0
- package/src/shared/session-tokens.ts +14 -3
- package/src/shared/settings.ts +10 -2
- package/src/shared/shortcuts.ts +17 -0
- package/src/shared/types.ts +118 -9
- package/src/shared/workflow-child-permit.ts +116 -0
- package/src/slash/delegation-adapters.ts +0 -1
- package/src/slash/slash-commands.ts +8 -6
- package/src/tui/fleet-status.ts +27 -10
- package/src/tui/fleet-transcript.ts +11 -5
- package/src/tui/fleet.ts +28 -13
- package/src/tui/render.ts +47 -21
- package/src/workflows/scripted-workflow.ts +248 -27
- package/src/workflows/workflow-child-summary.ts +117 -0
- package/src/workflows/workflow-receipt.ts +119 -4
|
@@ -81,13 +81,13 @@ import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolved
|
|
|
81
81
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
82
82
|
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
83
83
|
import { createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
84
|
-
import { formatProcessSignalError, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
84
|
+
import { formatMidToolExitError, formatProcessSignalError, isOrdinaryToolForMidToolExit, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
85
85
|
import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
|
|
86
86
|
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
87
87
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.ts";
|
|
88
88
|
import { claimRunFanoutBatch, getRunFanoutBudgetSnapshot } from "../shared/run-fanout-budget.ts";
|
|
89
89
|
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.ts";
|
|
90
|
-
import { formatModelAttemptNote, formatSubagentModelVerificationError, isContextOverflow,
|
|
90
|
+
import { formatModelAttemptNote, formatSubagentModelVerificationError, isContextOverflow, isRetryableModelFailureAttempt, recordRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
91
91
|
import {
|
|
92
92
|
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
93
93
|
formatSubagentExtensionConflictError,
|
|
@@ -141,9 +141,13 @@ import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey }
|
|
|
141
141
|
import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.ts";
|
|
142
142
|
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
|
|
143
143
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
144
|
-
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, PI_AGGREGATE_EVENT_PROJECTOR, projectChildLifecycle, type ChildLifecycleAction, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
144
|
+
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, PI_AGGREGATE_EVENT_PROJECTOR, projectChildLifecycle, type ChildLifecycleAction, type ChildLifecycleState, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
145
145
|
import { acquireSessionLease, type SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
146
146
|
import { buildExternalCliPrompt, runExternalCli } from "../shared/external-cli-runner.ts";
|
|
147
|
+
import { resolveClaudeCodeLaunch } from "../shared/claude-code-adapter.ts";
|
|
148
|
+
import { resolveCodexExecLaunch } from "../shared/codex-exec-adapter.ts";
|
|
149
|
+
import { resolveCursorAgentLaunch } from "../shared/cursor-agent-adapter.ts";
|
|
150
|
+
import { resolveExternalCliRunnerStatus } from "../shared/external-cli-contract.ts";
|
|
147
151
|
import { runExternalJob } from "../shared/external-job-runner.ts";
|
|
148
152
|
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
149
153
|
import { decodeSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
@@ -206,6 +210,7 @@ interface SubagentRunConfig {
|
|
|
206
210
|
launchResolvedExtensions?: LaunchResolvedChildExtensionsV1;
|
|
207
211
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
208
212
|
runnerProcessInstanceId?: string;
|
|
213
|
+
launchBarrierToken?: string;
|
|
209
214
|
parentWorkflowRunId?: string;
|
|
210
215
|
workflowKey?: string;
|
|
211
216
|
}
|
|
@@ -496,6 +501,7 @@ interface ChildUsage {
|
|
|
496
501
|
output?: number;
|
|
497
502
|
outputTokens?: number;
|
|
498
503
|
cacheRead?: number;
|
|
504
|
+
cacheReadTokens?: number;
|
|
499
505
|
cacheWrite?: number;
|
|
500
506
|
cost?: { total?: number };
|
|
501
507
|
}
|
|
@@ -546,6 +552,25 @@ interface RunPiStreamingResult {
|
|
|
546
552
|
currentTool?: string;
|
|
547
553
|
currentToolArgs?: string;
|
|
548
554
|
currentPath?: string;
|
|
555
|
+
afterCompactionSettlement?: boolean;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const MAX_CHILD_FAILURE_DIAGNOSTIC_CHARS = 8_192;
|
|
559
|
+
|
|
560
|
+
function formatChildFailureDiagnostic(input: {
|
|
561
|
+
error: string | undefined;
|
|
562
|
+
afterCompactionSettlement?: boolean;
|
|
563
|
+
missingFileOnlyOutput?: string;
|
|
564
|
+
}): string | undefined {
|
|
565
|
+
if (!input.error) return undefined;
|
|
566
|
+
const notes = [
|
|
567
|
+
input.afterCompactionSettlement ? "Child failure followed session compaction and agent settlement." : undefined,
|
|
568
|
+
input.missingFileOnlyOutput ? `Required file-only output was not produced: ${input.missingFileOnlyOutput.slice(0, 2_048)}` : undefined,
|
|
569
|
+
].filter((note): note is string => Boolean(note));
|
|
570
|
+
if (notes.length === 0) return input.error;
|
|
571
|
+
const context = notes.join("\n");
|
|
572
|
+
const errorLimit = MAX_CHILD_FAILURE_DIAGNOSTIC_CHARS - (context ? context.length + 1 : 0);
|
|
573
|
+
return `${input.error.slice(0, Math.max(0, errorLimit))}${context ? `\n${context}` : ""}`;
|
|
549
574
|
}
|
|
550
575
|
|
|
551
576
|
function runPiStreaming(
|
|
@@ -620,8 +645,50 @@ function runPiStreaming(
|
|
|
620
645
|
let currentToolArgs: string | undefined;
|
|
621
646
|
let currentPath: string | undefined;
|
|
622
647
|
let toolCount = 0;
|
|
648
|
+
type ActiveToolCall = { key: string; tool: string; args?: string; path?: string };
|
|
649
|
+
let activeToolSequence = 0;
|
|
650
|
+
const activeToolCalls = new Map<string, ActiveToolCall>();
|
|
651
|
+
const activeToolKeysByName = new Map<string, string[]>();
|
|
652
|
+
const refreshCurrentTool = (): void => {
|
|
653
|
+
const active = [...activeToolCalls.values()].at(-1);
|
|
654
|
+
currentTool = active?.tool;
|
|
655
|
+
currentToolArgs = active?.args;
|
|
656
|
+
currentPath = active?.path;
|
|
657
|
+
};
|
|
658
|
+
const recordActiveToolCall = (event: { toolCallId?: unknown; toolName: string; args?: Record<string, unknown> }): void => {
|
|
659
|
+
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
660
|
+
const active = omitUndefinedProperties({
|
|
661
|
+
key,
|
|
662
|
+
tool: event.toolName,
|
|
663
|
+
args: extractToolArgsPreview(event.args ?? {}),
|
|
664
|
+
path: resolveCurrentPath(event.toolName, event.args),
|
|
665
|
+
});
|
|
666
|
+
activeToolCalls.set(key, active);
|
|
667
|
+
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
668
|
+
keys.push(key);
|
|
669
|
+
activeToolKeysByName.set(active.tool, keys);
|
|
670
|
+
refreshCurrentTool();
|
|
671
|
+
};
|
|
672
|
+
const removeActiveToolCall = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
673
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
674
|
+
? `id:${event.toolCallId}`
|
|
675
|
+
: typeof event.toolName === "string"
|
|
676
|
+
? activeToolKeysByName.get(event.toolName)?.[0]
|
|
677
|
+
: activeToolCalls.size === 1
|
|
678
|
+
? [...activeToolCalls.keys()][0]
|
|
679
|
+
: undefined;
|
|
680
|
+
if (!key) return;
|
|
681
|
+
const active = activeToolCalls.get(key);
|
|
682
|
+
if (!active) return;
|
|
683
|
+
activeToolCalls.delete(key);
|
|
684
|
+
const keys = activeToolKeysByName.get(active.tool)?.filter((candidate) => candidate !== key) ?? [];
|
|
685
|
+
if (keys.length > 0) activeToolKeysByName.set(active.tool, keys);
|
|
686
|
+
else activeToolKeysByName.delete(active.tool);
|
|
687
|
+
refreshCurrentTool();
|
|
688
|
+
};
|
|
623
689
|
const childWatchdogConfig = decodeChildWatchdogConfig(env?.[CHILD_WATCHDOG_CONFIG_ENV]);
|
|
624
690
|
let childWatchdogState: ChildWatchdogStateSnapshot | undefined;
|
|
691
|
+
const childLifecycleState: ChildLifecycleState = { compactionRetryActive: false };
|
|
625
692
|
let applyChildLifecycle = (_action: ChildLifecycleAction): void => {};
|
|
626
693
|
const updateChildWatchdogState = (snapshot: ChildWatchdogStateSnapshot): void => {
|
|
627
694
|
childWatchdogState = snapshot;
|
|
@@ -672,8 +739,10 @@ function runPiStreaming(
|
|
|
672
739
|
|
|
673
740
|
appendChildEvent(event as unknown as Record<string, unknown>);
|
|
674
741
|
transcriptWriter?.writeChildEvent(event);
|
|
675
|
-
if (event.type === "
|
|
676
|
-
|
|
742
|
+
if (event.type === "compaction_start") compactionStartedReceived = true;
|
|
743
|
+
const lifecycleAction = projectChildLifecycle(event, false, childLifecycleState);
|
|
744
|
+
if (event.type === "agent_settled" && lifecycleAction === "start-drain") agentSettledReceived = true;
|
|
745
|
+
applyChildLifecycle(lifecycleAction);
|
|
677
746
|
|
|
678
747
|
if (isChildWatchdogStatusEvent(event)) {
|
|
679
748
|
if (!childWatchdogConfig) return;
|
|
@@ -710,18 +779,14 @@ function runPiStreaming(
|
|
|
710
779
|
|
|
711
780
|
if (event.type === "tool_execution_end") {
|
|
712
781
|
clearActiveToolTimeout(event);
|
|
713
|
-
|
|
714
|
-
currentToolArgs = undefined;
|
|
715
|
-
currentPath = undefined;
|
|
782
|
+
removeActiveToolCall(event);
|
|
716
783
|
return;
|
|
717
784
|
}
|
|
718
785
|
|
|
719
786
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
720
787
|
toolCount += 1;
|
|
721
788
|
armToolTimeout({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName });
|
|
722
|
-
|
|
723
|
-
currentToolArgs = extractToolArgsPreview(event.args ?? {});
|
|
724
|
-
currentPath = resolveCurrentPath(event.toolName, event.args);
|
|
789
|
+
recordActiveToolCall({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName, args: event.args });
|
|
725
790
|
if (event.toolName === "structured_output") {
|
|
726
791
|
structuredOutputToolInvoked = true;
|
|
727
792
|
structuredOutputMessageStartIndex = messages.length;
|
|
@@ -733,6 +798,13 @@ function runPiStreaming(
|
|
|
733
798
|
}
|
|
734
799
|
|
|
735
800
|
if ((event.type === "message_end" || event.type === "tool_result_end") && event.message) {
|
|
801
|
+
if (event.type === "tool_result_end") {
|
|
802
|
+
clearActiveToolTimeout(event);
|
|
803
|
+
removeActiveToolCall({
|
|
804
|
+
toolCallId: (event.message as { toolCallId?: unknown }).toolCallId ?? (event as { toolCallId?: unknown }).toolCallId,
|
|
805
|
+
toolName: (event.message as { toolName?: unknown }).toolName ?? event.toolName,
|
|
806
|
+
});
|
|
807
|
+
}
|
|
736
808
|
messages.push(event.message);
|
|
737
809
|
const text = extractTextFromContent(event.message.content);
|
|
738
810
|
if (text) writeOutputText(text);
|
|
@@ -759,7 +831,11 @@ function runPiStreaming(
|
|
|
759
831
|
if (isTerminalAssistantStop(event.message)) {
|
|
760
832
|
if (!event.message.errorMessage && extractTextFromContent(event.message.content).trim()) assistantError = undefined;
|
|
761
833
|
cleanTerminalAssistantStopReceived ||= !event.message.errorMessage;
|
|
762
|
-
|
|
834
|
+
clearAllToolTimeouts();
|
|
835
|
+
activeToolCalls.clear();
|
|
836
|
+
activeToolKeysByName.clear();
|
|
837
|
+
refreshCurrentTool();
|
|
838
|
+
applyChildLifecycle(projectChildLifecycle(event, true, childLifecycleState));
|
|
763
839
|
}
|
|
764
840
|
}
|
|
765
841
|
};
|
|
@@ -772,6 +848,7 @@ function runPiStreaming(
|
|
|
772
848
|
let forcedTerminationSignal = false;
|
|
773
849
|
let cleanTerminalAssistantStopReceived = false;
|
|
774
850
|
let agentSettledReceived = false;
|
|
851
|
+
let compactionStartedReceived = false;
|
|
775
852
|
let finalDrainTimer: NodeJS.Timeout | undefined;
|
|
776
853
|
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
777
854
|
let watchdogTailTimer: NodeJS.Timeout | undefined;
|
|
@@ -1047,6 +1124,7 @@ function runPiStreaming(
|
|
|
1047
1124
|
currentTool,
|
|
1048
1125
|
currentToolArgs,
|
|
1049
1126
|
currentPath,
|
|
1127
|
+
afterCompactionSettlement: (compactionStartedReceived && agentSettledReceived) || undefined,
|
|
1050
1128
|
}));
|
|
1051
1129
|
});
|
|
1052
1130
|
|
|
@@ -1381,21 +1459,29 @@ async function runSingleStepInner(
|
|
|
1381
1459
|
transcriptWriter?.writeInitialUserMessage(`${PROMPT_REDACTED}; live Prompt Audit only.`);
|
|
1382
1460
|
|
|
1383
1461
|
if (step.runner?.type === "external-cli") {
|
|
1384
|
-
const
|
|
1385
|
-
|
|
1386
|
-
command: step.runner.command,
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1462
|
+
const externalCwd = step.cwd ?? ctx.cwd;
|
|
1463
|
+
const adapterLaunch = step.runner.adapter === "codex-exec" || step.runner.adapter === "codex-exec-writer"
|
|
1464
|
+
? resolveCodexExecLaunch({ adapter: step.runner.adapter, command: step.runner.command, asyncDir: path.dirname(ctx.outputFile), stepIndex: ctx.flatIndex })
|
|
1465
|
+
: step.runner.adapter === "claude-code" || step.runner.adapter === "claude-code-writer"
|
|
1466
|
+
? resolveClaudeCodeLaunch({ adapter: step.runner.adapter, command: step.runner.command })
|
|
1467
|
+
: step.runner.adapter === "cursor-agent" || step.runner.adapter === "cursor-agent-writer"
|
|
1468
|
+
? resolveCursorAgentLaunch({ adapter: step.runner.adapter, command: step.runner.command, cwd: externalCwd, asyncDir: path.dirname(ctx.outputFile), stepIndex: ctx.flatIndex })
|
|
1469
|
+
: undefined;
|
|
1470
|
+
const runner = resolveExternalCliRunnerStatus({ ...step.runner, ...(adapterLaunch ? { args: adapterLaunch.args } : {}) });
|
|
1391
1471
|
const outputSnapshot = captureSingleOutputSnapshot(step.outputPath);
|
|
1392
1472
|
const external = await runExternalCli(omitUndefinedProperties({
|
|
1393
|
-
command: runner.command,
|
|
1394
|
-
args: runner.args,
|
|
1395
|
-
cwd:
|
|
1473
|
+
command: adapterLaunch?.command ?? runner.command,
|
|
1474
|
+
args: adapterLaunch?.args ?? runner.args,
|
|
1475
|
+
cwd: externalCwd,
|
|
1396
1476
|
prompt: buildExternalCliPrompt(step.systemPrompt ?? "", task),
|
|
1397
1477
|
asyncDir: path.dirname(ctx.outputFile),
|
|
1398
1478
|
stepIndex: ctx.flatIndex,
|
|
1479
|
+
environment: adapterLaunch?.environment,
|
|
1480
|
+
preflight: adapterLaunch?.preflight,
|
|
1481
|
+
parser: adapterLaunch?.parser,
|
|
1482
|
+
finalOutputPath: adapterLaunch?.finalOutputPath,
|
|
1483
|
+
promptFilePath: adapterLaunch?.promptFilePath,
|
|
1484
|
+
temporaryDirectories: adapterLaunch?.temporaryDirectories,
|
|
1399
1485
|
registerTimeout: ctx.registerTimeout,
|
|
1400
1486
|
registerStop: ctx.registerStop,
|
|
1401
1487
|
timeoutMessage: ctx.timeoutMessage,
|
|
@@ -1723,11 +1809,25 @@ async function runSingleStepInner(
|
|
|
1723
1809
|
: undefined;
|
|
1724
1810
|
const runtimeAcknowledgedExtensions = readRuntimeAcknowledgedExtensions(runtimeAcknowledgedExtensionsPath);
|
|
1725
1811
|
cleanupTempDir(tempDir);
|
|
1812
|
+
const midToolExitError = run.currentTool
|
|
1813
|
+
&& isOrdinaryToolForMidToolExit(run.currentTool)
|
|
1814
|
+
&& !run.interrupted
|
|
1815
|
+
&& !run.timedOut
|
|
1816
|
+
&& !run.stopped
|
|
1817
|
+
&& !run.turnBudgetExceeded
|
|
1818
|
+
&& !run.protocolError
|
|
1819
|
+
&& !toolAvailabilityError
|
|
1820
|
+
? formatMidToolExitError({
|
|
1821
|
+
toolName: run.currentTool,
|
|
1822
|
+
exitCode: run.exitCode,
|
|
1823
|
+
processSignal: run.processSignal,
|
|
1824
|
+
})
|
|
1825
|
+
: undefined;
|
|
1726
1826
|
|
|
1727
1827
|
let structuredOutput: unknown;
|
|
1728
1828
|
let structuredError: string | undefined;
|
|
1729
1829
|
let validatedStructuredOutput = false;
|
|
1730
|
-
if (effectiveStructuredOutput && run.exitCode === 0 && !run.error && !toolAvailabilityError) {
|
|
1830
|
+
if (effectiveStructuredOutput && run.exitCode === 0 && !run.error && !toolAvailabilityError && !midToolExitError) {
|
|
1731
1831
|
if (!run.structuredOutputToolInvoked) {
|
|
1732
1832
|
structuredError = MISSING_STRUCTURED_OUTPUT_CALL_ERROR;
|
|
1733
1833
|
} else {
|
|
@@ -1749,7 +1849,7 @@ async function runSingleStepInner(
|
|
|
1749
1849
|
const errorMessages = validatedStructuredOutput
|
|
1750
1850
|
? run.messages.slice(run.structuredOutputMessageStartIndex ?? run.messages.length)
|
|
1751
1851
|
: run.messages;
|
|
1752
|
-
const hiddenError = run.exitCode === 0 && !run.error && !toolAvailabilityError && !structuredError
|
|
1852
|
+
const hiddenError = run.exitCode === 0 && !run.error && !toolAvailabilityError && !structuredError && !midToolExitError
|
|
1753
1853
|
? detectSubagentError(errorMessages)
|
|
1754
1854
|
: null;
|
|
1755
1855
|
const emptyOutputError = run.exitCode === 0
|
|
@@ -1766,7 +1866,7 @@ async function runSingleStepInner(
|
|
|
1766
1866
|
const mutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
1767
1867
|
finalMutationEvidence = mutationEvidence;
|
|
1768
1868
|
const completionMutationEvidence = ctx.trackedMutationEvidenceForCompletionGuard === false ? undefined : mutationEvidence;
|
|
1769
|
-
const completionGuard = run.exitCode === 0 && !run.error && !structuredError && !hiddenError?.hasError && !emptyOutputError && completionGuardEnabled
|
|
1869
|
+
const completionGuard = run.exitCode === 0 && !run.error && !structuredError && !hiddenError?.hasError && !midToolExitError && !emptyOutputError && completionGuardEnabled
|
|
1770
1870
|
? evaluateCompletionMutationGuard(omitUndefinedProperties({
|
|
1771
1871
|
agent: step.agent,
|
|
1772
1872
|
task: taskForCompletionGuard,
|
|
@@ -1793,7 +1893,7 @@ async function runSingleStepInner(
|
|
|
1793
1893
|
const completionGuardError = completionGuardTriggered && !isAgentContractV1(step.agentContract)
|
|
1794
1894
|
? "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes."
|
|
1795
1895
|
: undefined;
|
|
1796
|
-
const effectiveExitCode = toolAvailabilityError || (completionGuardTriggered && !isAgentContractV1(step.agentContract)) || structuredError || emptyOutputError
|
|
1896
|
+
const effectiveExitCode = toolAvailabilityError || (completionGuardTriggered && !isAgentContractV1(step.agentContract)) || midToolExitError || structuredError || emptyOutputError
|
|
1797
1897
|
? 1
|
|
1798
1898
|
: hiddenError?.hasError
|
|
1799
1899
|
? (hiddenError.exitCode ?? 1)
|
|
@@ -1810,6 +1910,7 @@ async function runSingleStepInner(
|
|
|
1810
1910
|
const error = formatSubagentExtensionConflictError(
|
|
1811
1911
|
toolAvailabilityError
|
|
1812
1912
|
?? completionGuardError
|
|
1913
|
+
?? midToolExitError
|
|
1813
1914
|
?? structuredError
|
|
1814
1915
|
?? emptyOutputError
|
|
1815
1916
|
?? (hiddenError?.hasError
|
|
@@ -1889,7 +1990,7 @@ async function runSingleStepInner(
|
|
|
1889
1990
|
finalResult.finalOutput = startupError;
|
|
1890
1991
|
break modelAttemptsLoop;
|
|
1891
1992
|
}
|
|
1892
|
-
const retryableModelFailure =
|
|
1993
|
+
const retryableModelFailure = isRetryableModelFailureAttempt({ error, messages: run.messages, toolCount: run.toolCount });
|
|
1893
1994
|
if (retryableModelFailure) recordRetryableModelFailure(candidate ?? run.model ?? step.model, error);
|
|
1894
1995
|
if (isContextOverflow(error)) {
|
|
1895
1996
|
contextOverflow = true;
|
|
@@ -1993,7 +2094,7 @@ async function runSingleStepInner(
|
|
|
1993
2094
|
const acceptanceCanFailRun = acceptanceFailure && effectiveAcceptance?.explicit && (finalResult?.exitCode ?? 1) === 0 && !finalResult?.interrupted && !timedOutAfterAcceptance && !stoppedAfterAcceptance && !turnBudgetExceeded && !isAgentContractV1(step.agentContract);
|
|
1994
2095
|
const effectiveFinalExitCode = timedOutAfterAcceptance || stoppedAfterAcceptance || turnBudgetExceeded ? 1 : acceptanceCanFailRun ? 1 : finalResult?.exitCode ?? 1;
|
|
1995
2096
|
const intercomDetachReceipt = finalResult?.finalOutput === INTERCOM_DETACH_RECEIPT;
|
|
1996
|
-
const
|
|
2097
|
+
const baseFinalError = stoppedAfterAcceptance
|
|
1997
2098
|
? ctx.stopMessage ?? "Subagent stopped by user."
|
|
1998
2099
|
: timedOutAfterAcceptance
|
|
1999
2100
|
? finalResult?.error ?? ctx.timeoutMessage ?? "Subagent timed out."
|
|
@@ -2002,6 +2103,17 @@ async function runSingleStepInner(
|
|
|
2002
2103
|
: acceptanceCanFailRun
|
|
2003
2104
|
? (finalResult?.error ? `${finalResult.error}\n${acceptanceFailure}` : acceptanceFailure)
|
|
2004
2105
|
: finalResult?.error ?? (intercomDetachReceipt ? INTERCOM_DETACH_RECEIPT : undefined);
|
|
2106
|
+
const effectiveFinalError = formatChildFailureDiagnostic({
|
|
2107
|
+
error: baseFinalError,
|
|
2108
|
+
afterCompactionSettlement: finalResult?.afterCompactionSettlement,
|
|
2109
|
+
missingFileOnlyOutput: effectiveFinalExitCode !== 0
|
|
2110
|
+
&& finalResult?.afterCompactionSettlement
|
|
2111
|
+
&& step.outputMode === "file-only"
|
|
2112
|
+
&& step.outputPath
|
|
2113
|
+
&& !fs.existsSync(step.outputPath)
|
|
2114
|
+
? step.outputPath
|
|
2115
|
+
: undefined,
|
|
2116
|
+
});
|
|
2005
2117
|
|
|
2006
2118
|
const artifactErrors = artifactPaths && ctx.artifactConfig?.enabled !== false
|
|
2007
2119
|
? persistStepArtifacts({
|
|
@@ -2098,13 +2210,7 @@ type RunnerStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
|
2098
2210
|
|
|
2099
2211
|
function externalRunnerStatus(runner: SubagentStep["runner"]): ExternalCliRunnerStatus | ExternalJobRunnerStatus | undefined {
|
|
2100
2212
|
if (runner?.type === "external-cli") {
|
|
2101
|
-
return
|
|
2102
|
-
type: "external-cli",
|
|
2103
|
-
command: runner.command,
|
|
2104
|
-
args: runner.args ?? [],
|
|
2105
|
-
promptDelivery: runner.promptDelivery ?? "stdin",
|
|
2106
|
-
capabilities: { stop: true, steer: false, resume: false, structuredOutput: false, toolEvents: false },
|
|
2107
|
-
};
|
|
2213
|
+
return resolveExternalCliRunnerStatus(runner);
|
|
2108
2214
|
}
|
|
2109
2215
|
if (runner?.type === "external-job") {
|
|
2110
2216
|
return {
|
|
@@ -3516,12 +3622,13 @@ async function runSubagent(
|
|
|
3516
3622
|
if (usage) {
|
|
3517
3623
|
const input = usage.input ?? usage.inputTokens ?? 0;
|
|
3518
3624
|
const output = usage.output ?? usage.outputTokens ?? 0;
|
|
3625
|
+
const window = input + (usage.cacheRead ?? usage.cacheReadTokens ?? 0);
|
|
3519
3626
|
const previousInput = step.tokens?.input ?? 0;
|
|
3520
3627
|
const previousOutput = step.tokens?.output ?? 0;
|
|
3521
|
-
step.tokens = { input: previousInput + input, output: previousOutput + output, total: previousInput + previousOutput + input + output };
|
|
3628
|
+
step.tokens = { input: previousInput + input, output: previousOutput + output, total: previousInput + previousOutput + input + output, window, windowPeak: Math.max(step.tokens?.windowPeak ?? 0, window) };
|
|
3522
3629
|
const totalInput = statusPayload.totalTokens?.input ?? 0;
|
|
3523
3630
|
const totalOutput = statusPayload.totalTokens?.output ?? 0;
|
|
3524
|
-
statusPayload.totalTokens = { input: totalInput + input, output: totalOutput + output, total: totalInput + totalOutput + input + output };
|
|
3631
|
+
statusPayload.totalTokens = { input: totalInput + input, output: totalOutput + output, total: totalInput + totalOutput + input + output, window, windowPeak: Math.max(statusPayload.totalTokens?.windowPeak ?? 0, window) };
|
|
3525
3632
|
refreshUsageBudget();
|
|
3526
3633
|
}
|
|
3527
3634
|
statusPayload.turnCount = Math.max(statusPayload.turnCount ?? 0, step.turnCount);
|
|
@@ -4580,13 +4687,21 @@ async function runSubagent(
|
|
|
4580
4687
|
const sessionTokens = config.sessionDir
|
|
4581
4688
|
? parseSessionTokens(path.join(config.sessionDir, `parallel-${t}`))
|
|
4582
4689
|
: null;
|
|
4583
|
-
const
|
|
4690
|
+
const fallbackTokens = tokenUsageFromAttempts(parallelResults[t]?.modelAttempts);
|
|
4691
|
+
const observedTokens = requiredStatusStep(statusPayload, fi).tokens;
|
|
4692
|
+
const taskTokens = sessionTokens ?? (fallbackTokens
|
|
4693
|
+
? { ...fallbackTokens, ...(observedTokens?.window !== undefined ? { window: observedTokens.window } : {}), ...(observedTokens?.windowPeak !== undefined ? { windowPeak: observedTokens.windowPeak } : {}) }
|
|
4694
|
+
: null);
|
|
4584
4695
|
if (!taskTokens) continue;
|
|
4585
4696
|
requiredStatusStep(statusPayload, fi).tokens = taskTokens;
|
|
4586
4697
|
previousCumulativeTokens = {
|
|
4587
4698
|
input: previousCumulativeTokens.input + taskTokens.input,
|
|
4588
4699
|
output: previousCumulativeTokens.output + taskTokens.output,
|
|
4589
4700
|
total: previousCumulativeTokens.total + taskTokens.total,
|
|
4701
|
+
...(taskTokens.window !== undefined ? { window: taskTokens.window } : {}),
|
|
4702
|
+
...(previousCumulativeTokens.windowPeak !== undefined || taskTokens.windowPeak !== undefined
|
|
4703
|
+
? { windowPeak: Math.max(previousCumulativeTokens.windowPeak ?? 0, taskTokens.windowPeak ?? 0) }
|
|
4704
|
+
: {}),
|
|
4590
4705
|
};
|
|
4591
4706
|
}
|
|
4592
4707
|
statusPayload.totalTokens = { ...previousCumulativeTokens };
|
|
@@ -4903,17 +5018,27 @@ async function runSubagent(
|
|
|
4903
5018
|
input: cumulativeTokens.input - previousCumulativeTokens.input,
|
|
4904
5019
|
output: cumulativeTokens.output - previousCumulativeTokens.output,
|
|
4905
5020
|
total: cumulativeTokens.total - previousCumulativeTokens.total,
|
|
5021
|
+
...(cumulativeTokens.window !== undefined ? { window: cumulativeTokens.window } : {}),
|
|
5022
|
+
...(cumulativeTokens.windowPeak !== undefined ? { windowPeak: cumulativeTokens.windowPeak } : {}),
|
|
4906
5023
|
}
|
|
4907
5024
|
: null;
|
|
4908
5025
|
if (cumulativeTokens) {
|
|
4909
5026
|
previousCumulativeTokens = cumulativeTokens;
|
|
4910
5027
|
} else {
|
|
4911
|
-
|
|
5028
|
+
const fallbackTokens = tokenUsageFromAttempts(singleResult.modelAttempts);
|
|
5029
|
+
const observedTokens = requiredStatusStep(statusPayload, flatIndex).tokens;
|
|
5030
|
+
stepTokens = fallbackTokens
|
|
5031
|
+
? { ...fallbackTokens, ...(observedTokens?.window !== undefined ? { window: observedTokens.window } : {}), ...(observedTokens?.windowPeak !== undefined ? { windowPeak: observedTokens.windowPeak } : {}) }
|
|
5032
|
+
: null;
|
|
4912
5033
|
if (stepTokens) {
|
|
4913
5034
|
previousCumulativeTokens = {
|
|
4914
5035
|
input: previousCumulativeTokens.input + stepTokens.input,
|
|
4915
5036
|
output: previousCumulativeTokens.output + stepTokens.output,
|
|
4916
5037
|
total: previousCumulativeTokens.total + stepTokens.total,
|
|
5038
|
+
...(stepTokens.window !== undefined ? { window: stepTokens.window } : {}),
|
|
5039
|
+
...(previousCumulativeTokens.windowPeak !== undefined || stepTokens.windowPeak !== undefined
|
|
5040
|
+
? { windowPeak: Math.max(previousCumulativeTokens.windowPeak ?? 0, stepTokens.windowPeak ?? 0) }
|
|
5041
|
+
: {}),
|
|
4917
5042
|
};
|
|
4918
5043
|
}
|
|
4919
5044
|
}
|
|
@@ -5048,7 +5173,7 @@ async function runSubagent(
|
|
|
5048
5173
|
}
|
|
5049
5174
|
}
|
|
5050
5175
|
|
|
5051
|
-
let summary = results.map((r) => `${r.agent}:\n${r.output}`).join("\n\n");
|
|
5176
|
+
let summary = results.map((r) => `${r.agent}:\n${r.output || (r.exitCode !== 0 ? r.error : undefined) || "(no output)"}`).join("\n\n");
|
|
5052
5177
|
let truncated = false;
|
|
5053
5178
|
|
|
5054
5179
|
if (maxOutput) {
|
|
@@ -5359,7 +5484,7 @@ async function waitForStartupControl(
|
|
|
5359
5484
|
} catch (error) {
|
|
5360
5485
|
throw new Error(`Failed to read runner startup control '${controlPath}': ${error instanceof Error ? error.message : String(error)}`);
|
|
5361
5486
|
}
|
|
5362
|
-
if (payload.token !== token) throw new Error("Runner startup control token does not match
|
|
5487
|
+
if (payload.token !== token) throw new Error("Runner startup control token does not match.");
|
|
5363
5488
|
if (payload.action === action) return;
|
|
5364
5489
|
if (payload.action !== "ack" && payload.action !== "proceed") throw new Error("Runner startup control action is invalid.");
|
|
5365
5490
|
}
|
|
@@ -5370,7 +5495,7 @@ async function waitForStartupControl(
|
|
|
5370
5495
|
|
|
5371
5496
|
async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
5372
5497
|
let lease: ReturnType<typeof acquireSessionLease> | undefined;
|
|
5373
|
-
let startupCommitted = config.revivalLease === undefined;
|
|
5498
|
+
let startupCommitted = config.revivalLease === undefined && config.launchBarrierToken === undefined;
|
|
5374
5499
|
const startupPath = path.join(config.asyncDir, "runner-startup.json");
|
|
5375
5500
|
const startupAckPath = path.join(config.asyncDir, "runner-startup-ack.json");
|
|
5376
5501
|
const startupProceedPath = path.join(config.asyncDir, "runner-startup-proceed.json");
|
|
@@ -5383,7 +5508,15 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
5383
5508
|
};
|
|
5384
5509
|
process.once("exit", releaseOnExit);
|
|
5385
5510
|
try {
|
|
5386
|
-
if (config.
|
|
5511
|
+
if (config.launchBarrierToken) {
|
|
5512
|
+
await waitForStartupControl(startupProceedPath, config.launchBarrierToken, "proceed");
|
|
5513
|
+
startupCommitted = true;
|
|
5514
|
+
try {
|
|
5515
|
+
fs.rmSync(startupProceedPath, { force: true });
|
|
5516
|
+
} catch {
|
|
5517
|
+
// Startup control cleanup is best effort after the parent commits the run.
|
|
5518
|
+
}
|
|
5519
|
+
} else if (config.revivalLease) {
|
|
5387
5520
|
lease = acquireSessionLease(config.revivalLease);
|
|
5388
5521
|
config.revivalLeaseToken = lease.owner.token;
|
|
5389
5522
|
writeAtomicJson(startupPath, { state: "ready", token: lease.owner.token, pid: process.pid, owner: lease.owner });
|
|
@@ -5401,7 +5534,7 @@ async function runConfiguredSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
5401
5534
|
}
|
|
5402
5535
|
await runSubagent(config, lease ? (writer) => lease!.updateWriter(writer) : undefined);
|
|
5403
5536
|
} catch (error) {
|
|
5404
|
-
if (
|
|
5537
|
+
if (!startupCommitted) {
|
|
5405
5538
|
try {
|
|
5406
5539
|
writeAtomicJson(startupPath, { state: "error", pid: process.pid, error: error instanceof Error ? error.message : String(error) });
|
|
5407
5540
|
} catch {
|
|
@@ -3,6 +3,7 @@ import type { ArtifactPaths, SubagentState, WaitCompletion, WaitCompletionChild
|
|
|
3
3
|
import type { AsyncRunSummary } from "./async-status.ts";
|
|
4
4
|
import { readCompletionReplay, writeCompletionReplay } from "./completion-replay.ts";
|
|
5
5
|
import { fallbackResultPayloadPathForSessionRun, resultFilePath, resultPayloadPathForSessionRun } from "./result-files.ts";
|
|
6
|
+
import { parseWorkflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
6
7
|
|
|
7
8
|
function asNonEmptyString(value: unknown): string | undefined {
|
|
8
9
|
return typeof value === "string" && value ? value : undefined;
|
|
@@ -60,6 +61,8 @@ export function toWaitCompletion(data: Record<string, unknown>, runId: string):
|
|
|
60
61
|
const agent = asNonEmptyString(data.agent);
|
|
61
62
|
const mode = asNonEmptyString(data.mode);
|
|
62
63
|
const state = asNonEmptyString(data.state);
|
|
64
|
+
const workflowChildren = parseWorkflowChildSummary(data.workflowChildren);
|
|
65
|
+
if (workflowChildren && workflowChildren.workflowRunId !== runId) throw new Error("workflowChildren.workflowRunId does not match its completion run id.");
|
|
63
66
|
return {
|
|
64
67
|
runId,
|
|
65
68
|
...(agent ? { agent } : {}),
|
|
@@ -67,6 +70,7 @@ export function toWaitCompletion(data: Record<string, unknown>, runId: string):
|
|
|
67
70
|
...(state ? { state } : {}),
|
|
68
71
|
...(typeof data.success === "boolean" ? { success: data.success } : {}),
|
|
69
72
|
...(results && results.length > 0 ? { results } : {}),
|
|
73
|
+
...(workflowChildren ? { workflowChildren } : {}),
|
|
70
74
|
};
|
|
71
75
|
}
|
|
72
76
|
|