pi-subagents 0.49.0 → 0.50.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 +35 -0
- package/agents/oracle.md +7 -5
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +1 -1
- package/agents/worker.md +1 -1
- package/docs/agents.md +2 -0
- package/docs/configuration.md +47 -3
- package/docs/extension-api.md +36 -0
- package/docs/missions.md +1 -1
- package/docs/observability.md +16 -0
- package/docs/tool-reference.md +19 -2
- package/docs/workflows.md +2 -2
- package/package.json +1 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +5 -4
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +31 -15
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +10 -0
- package/src/api/external-runs.ts +174 -84
- package/src/api/preflight.ts +12 -6
- package/src/extension/config.ts +33 -0
- package/src/extension/index.ts +48 -5
- package/src/extension/public-execution.ts +3 -1
- package/src/extension/rpc.ts +5 -1
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +4 -2
- package/src/inspectors/herdr/actions.ts +3 -7
- package/src/inspectors/herdr/project-panes.ts +2 -6
- package/src/inspectors/herdr/shell-command.ts +16 -0
- package/src/intercom/intercom-bridge.ts +2 -3
- package/src/intercom/native-supervisor-channel.ts +19 -42
- package/src/missions/goal-driver.ts +3 -1
- package/src/runs/background/active-run-index.ts +71 -1
- package/src/runs/background/async-execution.ts +49 -7
- package/src/runs/background/async-job-tracker.ts +1 -0
- package/src/runs/background/async-resume.ts +3 -5
- package/src/runs/background/async-status-snapshot.ts +277 -0
- package/src/runs/background/async-status.ts +8 -3
- package/src/runs/background/chain-root-attachment.ts +2 -2
- package/src/runs/background/completion-replay.ts +11 -1
- package/src/runs/background/fleet-view.ts +3 -1
- package/src/runs/background/result-files.ts +437 -0
- package/src/runs/background/result-watcher.ts +188 -41
- package/src/runs/background/retained-children.ts +69 -20
- package/src/runs/background/run-id-resolver.ts +30 -24
- package/src/runs/background/run-status.ts +7 -2
- package/src/runs/background/scheduled-runs.ts +54 -28
- package/src/runs/background/stale-run-reconciler.ts +27 -13
- package/src/runs/background/subagent-runner.ts +294 -32
- package/src/runs/background/subagent-wait.ts +2 -0
- package/src/runs/background/wait-completions.ts +5 -2
- package/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/src/runs/foreground/chain-execution.ts +16 -0
- package/src/runs/foreground/execution.ts +219 -15
- package/src/runs/foreground/subagent-executor.ts +114 -28
- package/src/runs/shared/completion-guard.ts +96 -6
- package/src/runs/shared/external-cli-runner.ts +4 -0
- package/src/runs/shared/model-fallback.ts +16 -2
- package/src/runs/shared/nested-events.ts +66 -62
- package/src/runs/shared/orca-progress-tabs.ts +375 -0
- package/src/runs/shared/parallel-utils.ts +2 -0
- package/src/runs/shared/subagent-control.ts +15 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/src/runs/shared/tool-timeout.ts +93 -0
- package/src/shared/types.ts +29 -2
- package/src/slash/slash-commands.ts +34 -25
- package/src/slash/slash-live-state.ts +3 -0
- package/src/tui/fleet-status.ts +160 -45
- package/src/tui/fleet.ts +125 -15
- package/src/tui/render.ts +41 -10
- package/src/workflows/chat-progress.ts +6 -4
- package/src/workflows/scripted-workflow.ts +227 -70
|
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import type { Message } from "@earendil-works/pi-ai";
|
|
7
7
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
8
|
+
import { writeAsyncResultFile, writePendingAsyncResultFile } from "./result-files.ts";
|
|
8
9
|
import { createFileCoalescer } from "../../shared/file-coalescer.ts";
|
|
9
10
|
import { isActiveAsyncState, updateActiveRunIndex } from "./active-run-index.ts";
|
|
10
11
|
import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
claimControlNotification,
|
|
58
59
|
formatControlIntercomMessage,
|
|
59
60
|
formatControlNoticeMessage,
|
|
61
|
+
shouldEmitOpenToolAttention,
|
|
60
62
|
} from "../shared/subagent-control.ts";
|
|
61
63
|
import {
|
|
62
64
|
type RunnerSubagentStep as SubagentStep,
|
|
@@ -83,6 +85,7 @@ import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDi
|
|
|
83
85
|
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
84
86
|
import {
|
|
85
87
|
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
88
|
+
formatSubagentExtensionConflictError,
|
|
86
89
|
formatSubagentStartupRetryExhaustedError,
|
|
87
90
|
formatSubagentStartupRetryNote,
|
|
88
91
|
isRetryableSubagentStartupFailure,
|
|
@@ -126,12 +129,14 @@ import { waitForImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
|
126
129
|
import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests, statusStepDescription } from "./chain-append.ts";
|
|
127
130
|
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
128
131
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
132
|
+
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey } from "../shared/tool-timeout.ts";
|
|
129
133
|
import { usageBudgetExceededMessage, usageBudgetState } from "../shared/usage-budget.ts";
|
|
130
134
|
import { formatParallelHandoffError, formatParallelHandoffReference, parallelHandoffPath, writeParallelHandoffGroup, writePendingParallelHandoff } from "../shared/parallel-handoff.ts";
|
|
131
135
|
import { resolveWatchdogConfig } from "../../watchdog/settings.ts";
|
|
132
136
|
import { createBoundedByteTail, createBoundedLineReader, formatProtocolOutputLimit, MAX_CHILD_STDERR_BYTES, PI_AGGREGATE_EVENT_PROJECTOR, projectChildLifecycle, type ChildLifecycleAction, type ProtocolOutputLimit } from "../shared/child-protocol.ts";
|
|
133
137
|
import { acquireSessionLease, type SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
134
138
|
import { buildExternalCliPrompt, runExternalCli } from "../shared/external-cli-runner.ts";
|
|
139
|
+
import { createOrcaProgressTab, type OrcaProgressTab } from "../shared/orca-progress-tabs.ts";
|
|
135
140
|
import { decodeSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
136
141
|
import {
|
|
137
142
|
CHILD_WATCHDOG_CONFIG_ENV,
|
|
@@ -176,6 +181,8 @@ interface SubagentRunConfig {
|
|
|
176
181
|
nestedSelf?: { parentRunId: string; parentStepIndex?: number; depth: number; path?: Array<{ runId: string; stepIndex?: number; agent?: string }> };
|
|
177
182
|
timeoutMs?: number;
|
|
178
183
|
deadlineAt?: number;
|
|
184
|
+
/** Resolved configured hard per-tool-call timeout (ms); fast tools still have a default when undefined. */
|
|
185
|
+
toolTimeoutMs?: number;
|
|
179
186
|
turnBudget?: ResolvedTurnBudget;
|
|
180
187
|
toolBudget?: ResolvedToolBudget;
|
|
181
188
|
usageBudget?: UsageBudgetConfig;
|
|
@@ -541,6 +548,9 @@ function runPiStreaming(
|
|
|
541
548
|
stopMessage?: string,
|
|
542
549
|
registerTurnBudgetAbort?: (abort: ((message: string, state?: TurnBudgetState) => void) | undefined) => void,
|
|
543
550
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void,
|
|
551
|
+
toolTimeoutMs?: number,
|
|
552
|
+
runDeadlineAt?: number,
|
|
553
|
+
orcaProgressTab?: OrcaProgressTab,
|
|
544
554
|
): Promise<RunPiStreamingResult> {
|
|
545
555
|
return new Promise((resolve) => {
|
|
546
556
|
const startedAt = Date.now();
|
|
@@ -597,6 +607,7 @@ function runPiStreaming(
|
|
|
597
607
|
const writeOutputLine = (line: string) => {
|
|
598
608
|
if (!line.trim()) return;
|
|
599
609
|
outputStream.write(`${line}\n`);
|
|
610
|
+
orcaProgressTab?.append(`${line}\n`);
|
|
600
611
|
};
|
|
601
612
|
|
|
602
613
|
const writeOutputText = (text: string) => {
|
|
@@ -674,8 +685,14 @@ function runPiStreaming(
|
|
|
674
685
|
|
|
675
686
|
onChildEvent?.(event);
|
|
676
687
|
|
|
688
|
+
if (event.type === "tool_execution_end") {
|
|
689
|
+
clearActiveToolTimeout(event);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
677
693
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
678
694
|
toolCount += 1;
|
|
695
|
+
armToolTimeout({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName });
|
|
679
696
|
if (event.toolName === "structured_output") {
|
|
680
697
|
structuredOutputToolInvoked = true;
|
|
681
698
|
structuredOutputMessageStartIndex = messages.length;
|
|
@@ -771,6 +788,7 @@ function runPiStreaming(
|
|
|
771
788
|
stderrTail.push(chunk);
|
|
772
789
|
stderrReader.push(chunk);
|
|
773
790
|
outputStream.write(chunk);
|
|
791
|
+
orcaProgressTab?.append(chunk.toString("utf-8"));
|
|
774
792
|
});
|
|
775
793
|
registerInterrupt?.(() => {
|
|
776
794
|
if (settled || timedOut || stopped) return;
|
|
@@ -781,14 +799,60 @@ function runPiStreaming(
|
|
|
781
799
|
if (!settled && !timedOut && !stopped) trySignalChild(child, "SIGTERM");
|
|
782
800
|
}, 1000).unref?.();
|
|
783
801
|
});
|
|
784
|
-
|
|
802
|
+
const terminateForTimeout = (message: string): void => {
|
|
785
803
|
if (settled || timedOut || stopped) return;
|
|
786
804
|
timedOut = true;
|
|
805
|
+
// runPiStreaming's terminal result derives the timeout error from this
|
|
806
|
+
// message, so retain the tool-specific reason through finalization.
|
|
807
|
+
timeoutMessage = message;
|
|
787
808
|
interrupted = false;
|
|
788
|
-
error =
|
|
809
|
+
error = message;
|
|
789
810
|
if (processTreeController) void processTreeController.terminate();
|
|
790
811
|
else trySignalChild(child, "SIGTERM");
|
|
791
|
-
}
|
|
812
|
+
};
|
|
813
|
+
let toolTimeoutSequence = 0;
|
|
814
|
+
const activeToolTimeouts = new Map<string, { toolName: string; timer: ReturnType<typeof setTimeout> }>();
|
|
815
|
+
const activeToolTimeoutKeysByName = new Map<string, string[]>();
|
|
816
|
+
const removeToolTimeoutKey = (key: string): void => {
|
|
817
|
+
const active = activeToolTimeouts.get(key);
|
|
818
|
+
if (!active) return;
|
|
819
|
+
clearTimeout(active.timer);
|
|
820
|
+
activeToolTimeouts.delete(key);
|
|
821
|
+
const keys = activeToolTimeoutKeysByName.get(active.toolName)?.filter((candidate) => candidate !== key) ?? [];
|
|
822
|
+
if (keys.length > 0) activeToolTimeoutKeysByName.set(active.toolName, keys);
|
|
823
|
+
else activeToolTimeoutKeysByName.delete(active.toolName);
|
|
824
|
+
};
|
|
825
|
+
const clearActiveToolTimeout = (event: { toolCallId?: unknown; toolName?: unknown }): void => {
|
|
826
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
827
|
+
? `id:${event.toolCallId}`
|
|
828
|
+
: typeof event.toolName === "string"
|
|
829
|
+
? activeToolTimeoutKeysByName.get(event.toolName)?.[0]
|
|
830
|
+
: activeToolTimeouts.size === 1
|
|
831
|
+
? [...activeToolTimeouts.keys()][0]
|
|
832
|
+
: undefined;
|
|
833
|
+
if (key) removeToolTimeoutKey(key);
|
|
834
|
+
};
|
|
835
|
+
const clearAllToolTimeouts = (): void => {
|
|
836
|
+
for (const key of [...activeToolTimeouts.keys()]) removeToolTimeoutKey(key);
|
|
837
|
+
};
|
|
838
|
+
const armToolTimeout = (event: { toolCallId?: unknown; toolName: string }): void => {
|
|
839
|
+
const timeoutForTool = effectiveToolTimeoutMs(event.toolName, toolTimeoutMs);
|
|
840
|
+
if (timeoutForTool === undefined) return;
|
|
841
|
+
const runRemaining = runDeadlineAt === undefined ? undefined : Math.max(0, runDeadlineAt - Date.now());
|
|
842
|
+
if (runRemaining !== undefined && timeoutForTool >= runRemaining) return;
|
|
843
|
+
const key = toolTimeoutCallKey(event, ++toolTimeoutSequence);
|
|
844
|
+
const toolName = event.toolName;
|
|
845
|
+
const timer = setTimeout(() => {
|
|
846
|
+
removeToolTimeoutKey(key);
|
|
847
|
+
terminateForTimeout(formatToolTimeoutMessage(toolName, timeoutForTool));
|
|
848
|
+
}, timeoutForTool);
|
|
849
|
+
timer.unref?.();
|
|
850
|
+
activeToolTimeouts.set(key, { toolName, timer });
|
|
851
|
+
const keys = activeToolTimeoutKeysByName.get(toolName) ?? [];
|
|
852
|
+
keys.push(key);
|
|
853
|
+
activeToolTimeoutKeysByName.set(toolName, keys);
|
|
854
|
+
};
|
|
855
|
+
registerTimeout?.(() => terminateForTimeout(timeoutMessage ?? "Subagent timed out."));
|
|
792
856
|
registerStop?.(() => {
|
|
793
857
|
if (settled || timedOut || stopped) return;
|
|
794
858
|
stopped = true;
|
|
@@ -815,6 +879,7 @@ function runPiStreaming(
|
|
|
815
879
|
turnBudgetHardKillTimer.unref?.();
|
|
816
880
|
});
|
|
817
881
|
const clearDrainTimers = () => {
|
|
882
|
+
clearAllToolTimeouts();
|
|
818
883
|
if (finalDrainTimer) {
|
|
819
884
|
clearTimeout(finalDrainTimer);
|
|
820
885
|
finalDrainTimer = undefined;
|
|
@@ -1104,6 +1169,10 @@ interface SingleStepContext {
|
|
|
1104
1169
|
stopSignal?: AbortSignal;
|
|
1105
1170
|
timeoutMessage?: string;
|
|
1106
1171
|
stopMessage?: string;
|
|
1172
|
+
/** Resolved configured hard per-tool-call timeout (ms); fast tools still have a default when undefined. */
|
|
1173
|
+
toolTimeoutMs?: number;
|
|
1174
|
+
/** Effective step deadline (Date.now() + effective timeout) when a run budget exists. */
|
|
1175
|
+
deadlineAt?: number;
|
|
1107
1176
|
turnBudget?: ResolvedTurnBudget;
|
|
1108
1177
|
childIntercomTarget?: string;
|
|
1109
1178
|
orchestratorIntercomTarget?: string;
|
|
@@ -1115,10 +1184,11 @@ interface SingleStepContext {
|
|
|
1115
1184
|
onWriterProcess?: (writer: { state: "none" | "spawning" } | { state: "running"; pid: number }) => void;
|
|
1116
1185
|
onExternalProcess?: (process: ExternalProcessStatus) => void;
|
|
1117
1186
|
skipAcceptance?: () => boolean;
|
|
1187
|
+
orcaProgressTab?: OrcaProgressTab;
|
|
1118
1188
|
}
|
|
1119
1189
|
|
|
1120
1190
|
/** Run a single pi agent step, returning output and metadata */
|
|
1121
|
-
async function
|
|
1191
|
+
async function runSingleStepInner(
|
|
1122
1192
|
step: SubagentStep,
|
|
1123
1193
|
ctx: SingleStepContext,
|
|
1124
1194
|
): Promise<StepResult & { completionGuardTriggered?: boolean }> {
|
|
@@ -1247,6 +1317,8 @@ async function runSingleStep(
|
|
|
1247
1317
|
timeoutMessage: ctx.timeoutMessage,
|
|
1248
1318
|
stopMessage: ctx.stopMessage,
|
|
1249
1319
|
onProcess: ctx.onExternalProcess,
|
|
1320
|
+
onStdout: (chunk) => ctx.orcaProgressTab?.append(chunk.toString("utf-8")),
|
|
1321
|
+
onStderr: (chunk) => ctx.orcaProgressTab?.append(chunk.toString("utf-8")),
|
|
1250
1322
|
}));
|
|
1251
1323
|
try { fs.writeFileSync(ctx.outputFile, external.output, "utf-8"); } catch { /* Observability output is best-effort. */ }
|
|
1252
1324
|
const resolvedOutput = step.outputPath && external.exitCode === 0
|
|
@@ -1433,6 +1505,9 @@ async function runSingleStep(
|
|
|
1433
1505
|
ctx.stopMessage,
|
|
1434
1506
|
ctx.registerTurnBudgetAbort,
|
|
1435
1507
|
ctx.onWriterProcess,
|
|
1508
|
+
ctx.toolTimeoutMs,
|
|
1509
|
+
ctx.deadlineAt,
|
|
1510
|
+
ctx.orcaProgressTab,
|
|
1436
1511
|
);
|
|
1437
1512
|
if (run.processCloseObservedAt !== undefined) {
|
|
1438
1513
|
writerProcesses.push({
|
|
@@ -1540,15 +1615,21 @@ async function runSingleStep(
|
|
|
1540
1615
|
stopped: run.stopped,
|
|
1541
1616
|
turnBudgetExceeded: run.turnBudgetExceeded,
|
|
1542
1617
|
})) ? formatProcessSignalError(run.processSignal!) : undefined;
|
|
1543
|
-
const error =
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
?
|
|
1550
|
-
|
|
1551
|
-
|
|
1618
|
+
const error = formatSubagentExtensionConflictError(
|
|
1619
|
+
toolAvailabilityError
|
|
1620
|
+
?? completionGuardError
|
|
1621
|
+
?? structuredError
|
|
1622
|
+
?? emptyOutputError
|
|
1623
|
+
?? (hiddenError?.hasError
|
|
1624
|
+
? hiddenError.details
|
|
1625
|
+
? `${hiddenError.errorType} failed (exit ${effectiveExitCode}): ${hiddenError.details}`
|
|
1626
|
+
: `${hiddenError.errorType} failed with exit code ${effectiveExitCode}`
|
|
1627
|
+
: run.error || signalError || (run.exitCode !== 0 && run.stderr.trim() ? run.stderr.trim() : undefined)),
|
|
1628
|
+
{
|
|
1629
|
+
agent: step.agent,
|
|
1630
|
+
ambientExtensionsEnabled: launchResolvedExtensions?.disableAmbientExtensions === false,
|
|
1631
|
+
},
|
|
1632
|
+
);
|
|
1552
1633
|
const attempt: ModelAttempt = omitUndefinedProperties({
|
|
1553
1634
|
model: candidate ?? run.model ?? step.model ?? "default",
|
|
1554
1635
|
success: effectiveExitCode === 0 && !error,
|
|
@@ -1699,7 +1780,7 @@ async function runSingleStep(
|
|
|
1699
1780
|
const effectiveFinalError = stoppedAfterAcceptance
|
|
1700
1781
|
? ctx.stopMessage ?? "Subagent stopped by user."
|
|
1701
1782
|
: timedOutAfterAcceptance
|
|
1702
|
-
? ctx.timeoutMessage ?? "Subagent timed out."
|
|
1783
|
+
? finalResult?.error ?? ctx.timeoutMessage ?? "Subagent timed out."
|
|
1703
1784
|
: turnBudgetExceeded
|
|
1704
1785
|
? finalResult?.error ?? (turnBudget ? turnBudgetExceededMessage(turnBudget, turnBudget.turnCount) : "Subagent exceeded turn budget.")
|
|
1705
1786
|
: acceptanceCanFailRun
|
|
@@ -1784,6 +1865,27 @@ async function runSingleStep(
|
|
|
1784
1865
|
return isAgentContractV1(step.agentContract) ? attachContractProjections(result as unknown as import("../../shared/types.ts").SingleResult) as unknown as typeof result : result;
|
|
1785
1866
|
}
|
|
1786
1867
|
|
|
1868
|
+
async function runSingleStep(
|
|
1869
|
+
step: SubagentStep,
|
|
1870
|
+
ctx: SingleStepContext,
|
|
1871
|
+
): Promise<StepResult & { completionGuardTriggered?: boolean }> {
|
|
1872
|
+
if (step.importAsyncRoot) return runSingleStepInner(step, ctx);
|
|
1873
|
+
const orcaProgressTab = createOrcaProgressTab({
|
|
1874
|
+
cwd: step.cwd ?? ctx.cwd,
|
|
1875
|
+
runId: ctx.id,
|
|
1876
|
+
agent: step.agent,
|
|
1877
|
+
index: ctx.flatIndex,
|
|
1878
|
+
});
|
|
1879
|
+
try {
|
|
1880
|
+
const result = await runSingleStepInner(step, { ...ctx, orcaProgressTab });
|
|
1881
|
+
orcaProgressTab?.finish(result.stopped ? "stopped" : result.exitCode === 0 && !result.error ? "completed" : "failed", result.sessionFile);
|
|
1882
|
+
return result;
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
orcaProgressTab?.finish("failed");
|
|
1885
|
+
throw error;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1787
1889
|
type RunnerStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
1788
1890
|
exitCode?: number | null;
|
|
1789
1891
|
description?: string;
|
|
@@ -2008,6 +2110,7 @@ async function runSingleStepWithTimeout(
|
|
|
2008
2110
|
return await runSingleStep(step, {
|
|
2009
2111
|
...ctx,
|
|
2010
2112
|
registerTimeout,
|
|
2113
|
+
deadlineAt: Date.now() + timeoutMs,
|
|
2011
2114
|
timeoutSignal: combinedAbortSignal([ctx.timeoutSignal, timeoutController.signal]),
|
|
2012
2115
|
timeoutMessage,
|
|
2013
2116
|
});
|
|
@@ -2202,7 +2305,7 @@ async function runSubagent(
|
|
|
2202
2305
|
|
|
2203
2306
|
fs.mkdirSync(asyncDir, { recursive: true });
|
|
2204
2307
|
writeAtomicJson(statusPath, statusPayload);
|
|
2205
|
-
updateActiveRunIndex(asyncDir, statusPayload.state);
|
|
2308
|
+
updateActiveRunIndex(asyncDir, statusPayload.state, statusPayload.toolCallId);
|
|
2206
2309
|
let pendingParallelUsageCost: CostSummary = { inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
2207
2310
|
const currentUsageTotals = (): CostSummary => {
|
|
2208
2311
|
const cost = results.reduce<CostSummary>((sum, result) => ({
|
|
@@ -2275,13 +2378,70 @@ async function runSubagent(
|
|
|
2275
2378
|
for (const node of graph.nodes) updateNode(node);
|
|
2276
2379
|
statusPayload.workflowGraph = graph;
|
|
2277
2380
|
};
|
|
2381
|
+
let finalResultCommitted = false;
|
|
2278
2382
|
let lastIndexedActiveState = isActiveAsyncState(statusPayload.state);
|
|
2383
|
+
const statusResultState = (): AsyncStatus["state"] | undefined => {
|
|
2384
|
+
if (statusPayload.state === "running" || statusPayload.state === "queued") return undefined;
|
|
2385
|
+
if (statusPayload.state === "paused" && statusPayload.checkpoint?.status === "pending") return undefined;
|
|
2386
|
+
return statusPayload.state;
|
|
2387
|
+
};
|
|
2388
|
+
const statusResultSummary = (state: AsyncStatus["state"]): string => {
|
|
2389
|
+
if (statusPayload.error) return statusPayload.error;
|
|
2390
|
+
if (state === "paused") return "Paused after interrupt. Waiting for explicit next action.";
|
|
2391
|
+
if (state === "stopped") return stopMessage;
|
|
2392
|
+
if (state === "rejected") return statusPayload.checkpoint?.name ? `Checkpoint '${statusPayload.checkpoint.name}' rejected.` : "Subagent rejected.";
|
|
2393
|
+
return state === "complete" ? "Subagent completed." : "Subagent failed.";
|
|
2394
|
+
};
|
|
2395
|
+
const statusResultSuccess = (state: AsyncStatus["state"], step: RunnerStatusStep): boolean | undefined => {
|
|
2396
|
+
if (step.status === "complete" || step.status === "completed") return true;
|
|
2397
|
+
if (step.status === "failed" || step.status === "stopped" || step.status === "rejected") return false;
|
|
2398
|
+
if (state === "complete") return true;
|
|
2399
|
+
if (state === "failed" || state === "stopped" || state === "rejected") return false;
|
|
2400
|
+
return undefined;
|
|
2401
|
+
};
|
|
2402
|
+
const writeRecoverableStatusResult = (): void => {
|
|
2403
|
+
const state = statusResultState();
|
|
2404
|
+
if (!state || finalResultCommitted || !config.sessionId) return;
|
|
2405
|
+
const now = statusPayload.endedAt ?? statusPayload.lastUpdate ?? Date.now();
|
|
2406
|
+
const summary = statusResultSummary(state);
|
|
2407
|
+
writePendingAsyncResultFile(resultPath, omitUndefinedProperties({
|
|
2408
|
+
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
2409
|
+
id,
|
|
2410
|
+
runId: id,
|
|
2411
|
+
agent: statusPayload.steps.length === 1 ? statusPayload.steps[0]!.agent : statusPayload.mode === "parallel" ? `parallel:${statusPayload.steps.map((step) => step.agent).join("+")}` : `chain:${statusPayload.steps.map((step) => step.agent).join("->")}`,
|
|
2412
|
+
mode: statusPayload.mode,
|
|
2413
|
+
success: state === "complete",
|
|
2414
|
+
state,
|
|
2415
|
+
summary,
|
|
2416
|
+
error: state === "failed" || state === "stopped" || state === "rejected" ? summary : undefined,
|
|
2417
|
+
stopped: state === "stopped" ? true : undefined,
|
|
2418
|
+
checkpoint: statusPayload.checkpoint,
|
|
2419
|
+
results: statusPayload.steps.map((step) => omitUndefinedProperties({
|
|
2420
|
+
agent: step.agent,
|
|
2421
|
+
output: step.status === "complete" || step.status === "completed" ? "" : step.error ?? summary,
|
|
2422
|
+
error: step.error,
|
|
2423
|
+
success: statusResultSuccess(state, step),
|
|
2424
|
+
sessionFile: step.sessionFile,
|
|
2425
|
+
model: step.model,
|
|
2426
|
+
attemptedModels: step.attemptedModels,
|
|
2427
|
+
modelAttempts: step.modelAttempts,
|
|
2428
|
+
})),
|
|
2429
|
+
exitCode: state === "complete" || state === "paused" ? 0 : 1,
|
|
2430
|
+
timestamp: now,
|
|
2431
|
+
durationMs: Math.max(0, now - overallStartTime),
|
|
2432
|
+
asyncDir,
|
|
2433
|
+
cwd,
|
|
2434
|
+
sessionId: config.sessionId,
|
|
2435
|
+
sessionFile: statusPayload.sessionFile ?? latestSessionFile,
|
|
2436
|
+
}));
|
|
2437
|
+
};
|
|
2279
2438
|
const writeStatusPayloadNow = (): void => {
|
|
2280
2439
|
refreshWorkflowGraph();
|
|
2440
|
+
writeRecoverableStatusResult();
|
|
2281
2441
|
writeAtomicJson(statusPath, statusPayload);
|
|
2282
2442
|
const activeState = isActiveAsyncState(statusPayload.state);
|
|
2283
|
-
if (activeState
|
|
2284
|
-
updateActiveRunIndex(asyncDir, statusPayload.state);
|
|
2443
|
+
if (activeState !== lastIndexedActiveState) {
|
|
2444
|
+
updateActiveRunIndex(asyncDir, statusPayload.state, statusPayload.toolCallId);
|
|
2285
2445
|
}
|
|
2286
2446
|
lastIndexedActiveState = activeState;
|
|
2287
2447
|
emitNestedSelfEvent(statusPayload.state === "running" || statusPayload.state === "queued" ? "subagent.nested.updated" : "subagent.nested.completed");
|
|
@@ -2526,6 +2686,72 @@ async function runSubagent(
|
|
|
2526
2686
|
const activeLongRunningSteps = new Set<number>();
|
|
2527
2687
|
const mutatingFailureStates = initialStatusSteps.map(() => createMutatingFailureState());
|
|
2528
2688
|
const pendingToolResults: Array<{ tool: string; path?: string; mutates: boolean; startedAt?: number } | undefined> = initialStatusSteps.map(() => undefined);
|
|
2689
|
+
type ActiveToolCall = { key: string; tool: string; args: string; startedAt: number; path?: string; blocksSupervisor: boolean };
|
|
2690
|
+
const activeToolCalls = initialStatusSteps.map(() => new Map<string, ActiveToolCall>());
|
|
2691
|
+
const activeToolKeysByName = initialStatusSteps.map(() => new Map<string, string[]>());
|
|
2692
|
+
const activeToolSequences = initialStatusSteps.map(() => 0);
|
|
2693
|
+
const latestActiveToolCall = (flatIndex: number): ActiveToolCall | undefined => [...(activeToolCalls[flatIndex]?.values() ?? [])].sort((left, right) => right.startedAt - left.startedAt)[0];
|
|
2694
|
+
const refreshStepCurrentTool = (flatIndex: number): void => {
|
|
2695
|
+
const step = statusPayload.steps[flatIndex];
|
|
2696
|
+
if (!step) return;
|
|
2697
|
+
const active = latestActiveToolCall(flatIndex);
|
|
2698
|
+
if (!active) {
|
|
2699
|
+
delete step.currentTool;
|
|
2700
|
+
delete step.currentToolArgs;
|
|
2701
|
+
delete step.currentToolStartedAt;
|
|
2702
|
+
delete step.currentPath;
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
step.currentTool = active.tool;
|
|
2706
|
+
step.currentToolArgs = active.args;
|
|
2707
|
+
step.currentToolStartedAt = active.startedAt;
|
|
2708
|
+
setOptionalProperty(step, "currentPath", active.path);
|
|
2709
|
+
};
|
|
2710
|
+
const recordActiveToolCall = (flatIndex: number, event: { toolCallId?: unknown; toolName: string }, input: { argsPreview: string; currentPath?: string; blocksSupervisor: boolean; now: number }): ActiveToolCall => {
|
|
2711
|
+
const sequence = (activeToolSequences[flatIndex] ?? 0) + 1;
|
|
2712
|
+
activeToolSequences[flatIndex] = sequence;
|
|
2713
|
+
const key = toolTimeoutCallKey(event, sequence);
|
|
2714
|
+
const active: ActiveToolCall = {
|
|
2715
|
+
key,
|
|
2716
|
+
tool: event.toolName,
|
|
2717
|
+
args: input.argsPreview,
|
|
2718
|
+
startedAt: input.now,
|
|
2719
|
+
blocksSupervisor: input.blocksSupervisor,
|
|
2720
|
+
...(input.currentPath !== undefined ? { path: input.currentPath } : {}),
|
|
2721
|
+
};
|
|
2722
|
+
activeToolCalls[flatIndex]?.set(key, active);
|
|
2723
|
+
const keysByName = activeToolKeysByName[flatIndex];
|
|
2724
|
+
const keys = keysByName?.get(active.tool) ?? [];
|
|
2725
|
+
keys.push(key);
|
|
2726
|
+
keysByName?.set(active.tool, keys);
|
|
2727
|
+
refreshStepCurrentTool(flatIndex);
|
|
2728
|
+
return active;
|
|
2729
|
+
};
|
|
2730
|
+
const removeActiveToolCallKey = (flatIndex: number, key: string): ActiveToolCall | undefined => {
|
|
2731
|
+
const calls = activeToolCalls[flatIndex];
|
|
2732
|
+
const active = calls?.get(key);
|
|
2733
|
+
if (!active) return undefined;
|
|
2734
|
+
calls?.delete(key);
|
|
2735
|
+
const keysByName = activeToolKeysByName[flatIndex];
|
|
2736
|
+
const keys = keysByName?.get(active.tool)?.filter((candidate) => candidate !== key) ?? [];
|
|
2737
|
+
if (keys.length > 0) keysByName?.set(active.tool, keys);
|
|
2738
|
+
else keysByName?.delete(active.tool);
|
|
2739
|
+
return active;
|
|
2740
|
+
};
|
|
2741
|
+
const removeActiveToolCall = (flatIndex: number, event: { toolCallId?: unknown; toolName?: unknown }): ActiveToolCall | undefined => {
|
|
2742
|
+
const calls = activeToolCalls[flatIndex];
|
|
2743
|
+
const key = typeof event.toolCallId === "string" && event.toolCallId.length > 0
|
|
2744
|
+
? `id:${event.toolCallId}`
|
|
2745
|
+
: typeof event.toolName === "string"
|
|
2746
|
+
? activeToolKeysByName[flatIndex]?.get(event.toolName)?.[0]
|
|
2747
|
+
: calls?.size === 1
|
|
2748
|
+
? [...calls.keys()][0]
|
|
2749
|
+
: undefined;
|
|
2750
|
+
return key ? removeActiveToolCallKey(flatIndex, key) : undefined;
|
|
2751
|
+
};
|
|
2752
|
+
const openToolAttentionTarget = (flatIndex: number, now: number): ActiveToolCall | undefined => [...(activeToolCalls[flatIndex]?.values() ?? [])]
|
|
2753
|
+
.filter((active) => shouldEmitOpenToolAttention({ config: controlConfig, currentTool: active.tool, currentToolStartedAt: active.startedAt, now }))
|
|
2754
|
+
.sort((left, right) => left.startedAt - right.startedAt)[0];
|
|
2529
2755
|
const supervisorAttentionSteps = new Map<number, ActivityState | undefined>();
|
|
2530
2756
|
const mutatingFailureWindowMs = 5 * 60_000;
|
|
2531
2757
|
const appendControlEvent = (event: ReturnType<typeof buildControlEvent>) => {
|
|
@@ -2566,6 +2792,34 @@ async function runSubagent(
|
|
|
2566
2792
|
currentActivityState = nextRunState;
|
|
2567
2793
|
setOptionalProperty(statusPayload, "activityState", nextRunState);
|
|
2568
2794
|
};
|
|
2795
|
+
const maybeEmitOpenToolAttention = (flatIndex: number, now: number): boolean => {
|
|
2796
|
+
const step = statusPayload.steps[flatIndex];
|
|
2797
|
+
if (!step || step.status !== "running" || step.activityState === "needs_attention") return false;
|
|
2798
|
+
const target = openToolAttentionTarget(flatIndex, now);
|
|
2799
|
+
if (!target) return false;
|
|
2800
|
+
const previous = step.activityState;
|
|
2801
|
+
step.activityState = "needs_attention";
|
|
2802
|
+
statusPayload.activityState = "needs_attention";
|
|
2803
|
+
const toolDurationMs = Math.max(0, now - target.startedAt);
|
|
2804
|
+
appendControlEvent(buildControlEvent(omitUndefinedProperties({
|
|
2805
|
+
type: "needs_attention",
|
|
2806
|
+
from: previous,
|
|
2807
|
+
to: "needs_attention",
|
|
2808
|
+
runId: id,
|
|
2809
|
+
agent: step.agent,
|
|
2810
|
+
index: flatIndex,
|
|
2811
|
+
ts: now,
|
|
2812
|
+
message: `${step.agent} has had tool '${target.tool}' open for ${Math.floor(toolDurationMs / 1000)}s`,
|
|
2813
|
+
reason: "tool_open_threshold",
|
|
2814
|
+
turns: step.turnCount,
|
|
2815
|
+
tokens: step.tokens?.total,
|
|
2816
|
+
toolCount: step.toolCount,
|
|
2817
|
+
currentTool: target.tool,
|
|
2818
|
+
currentToolDurationMs: toolDurationMs,
|
|
2819
|
+
currentPath: target.path,
|
|
2820
|
+
})));
|
|
2821
|
+
return true;
|
|
2822
|
+
};
|
|
2569
2823
|
const maybeEmitActiveLongRunning = (flatIndex: number, now: number): boolean => {
|
|
2570
2824
|
if (!controlConfig.enabled || activeLongRunningSteps.has(flatIndex)) return false;
|
|
2571
2825
|
const step = statusPayload.steps[flatIndex];
|
|
@@ -2826,20 +3080,19 @@ async function runSubagent(
|
|
|
2826
3080
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
2827
3081
|
const mutates = isMutatingTool(event.toolName, event.args);
|
|
2828
3082
|
const currentPath = resolveCurrentPath(event.toolName, event.args);
|
|
3083
|
+
const argsPreview = extractToolArgsPreview(event.args ?? {});
|
|
3084
|
+
const blocksSupervisor = isBlockingSupervisorTool(event.toolName, event.args);
|
|
2829
3085
|
step.toolCount = (step.toolCount ?? 0) + 1;
|
|
2830
3086
|
const configuredToolBudget = flatSteps[flatIndex]?.toolBudget;
|
|
2831
3087
|
if (configuredToolBudget) {
|
|
2832
3088
|
step.toolBudget = toolBudgetState(configuredToolBudget, step.toolCount);
|
|
2833
3089
|
statusPayload.toolBudget = step.toolBudget;
|
|
2834
3090
|
}
|
|
2835
|
-
|
|
2836
|
-
step.currentToolArgs = extractToolArgsPreview(event.args ?? {});
|
|
2837
|
-
step.currentToolStartedAt = now;
|
|
2838
|
-
setOptionalProperty(step, "currentPath", currentPath);
|
|
3091
|
+
recordActiveToolCall(flatIndex, { toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName }, { argsPreview, currentPath, blocksSupervisor, now });
|
|
2839
3092
|
pendingToolResults[flatIndex] = omitUndefinedProperties({ tool: event.toolName, path: currentPath, mutates, startedAt: now });
|
|
2840
3093
|
statusPayload.toolCount = (statusPayload.toolCount ?? 0) + 1;
|
|
2841
3094
|
syncTopLevelCurrentTool();
|
|
2842
|
-
if (controlConfig.enabled &&
|
|
3095
|
+
if (controlConfig.enabled && blocksSupervisor && step.activityState !== "needs_attention") {
|
|
2843
3096
|
const previous = step.activityState;
|
|
2844
3097
|
step.activityState = "needs_attention";
|
|
2845
3098
|
supervisorAttentionSteps.set(flatIndex, previous);
|
|
@@ -2864,16 +3117,15 @@ async function runSubagent(
|
|
|
2864
3117
|
})));
|
|
2865
3118
|
}
|
|
2866
3119
|
} else if (event.type === "tool_execution_end") {
|
|
2867
|
-
|
|
3120
|
+
const endedTool = removeActiveToolCall(flatIndex, event);
|
|
3121
|
+
if (endedTool) {
|
|
2868
3122
|
step.recentTools ??= [];
|
|
2869
|
-
step.recentTools.push({ tool:
|
|
3123
|
+
step.recentTools.push({ tool: endedTool.tool, args: endedTool.args, endMs: now });
|
|
2870
3124
|
}
|
|
3125
|
+
refreshStepCurrentTool(flatIndex);
|
|
2871
3126
|
const supervisorPreviousActivity = supervisorAttentionSteps.get(flatIndex);
|
|
2872
|
-
const
|
|
2873
|
-
delete
|
|
2874
|
-
delete step.currentToolArgs;
|
|
2875
|
-
delete step.currentToolStartedAt;
|
|
2876
|
-
delete step.currentPath;
|
|
3127
|
+
const stillBlockingSupervisor = [...(activeToolCalls[flatIndex]?.values() ?? [])].some((active) => active.blocksSupervisor);
|
|
3128
|
+
const clearedSupervisorAttention = endedTool?.blocksSupervisor && !stillBlockingSupervisor ? supervisorAttentionSteps.delete(flatIndex) : false;
|
|
2877
3129
|
if (clearedSupervisorAttention && step.activityState === "needs_attention") {
|
|
2878
3130
|
setOptionalProperty(step, "activityState", supervisorPreviousActivity);
|
|
2879
3131
|
syncAggregateActivityState();
|
|
@@ -2993,6 +3245,8 @@ async function runSubagent(
|
|
|
2993
3245
|
})));
|
|
2994
3246
|
changed = true;
|
|
2995
3247
|
}
|
|
3248
|
+
} else if (maybeEmitOpenToolAttention(index, now)) {
|
|
3249
|
+
changed = true;
|
|
2996
3250
|
} else if (maybeEmitActiveLongRunning(index, now)) {
|
|
2997
3251
|
changed = true;
|
|
2998
3252
|
}
|
|
@@ -3555,6 +3809,7 @@ async function runSubagent(
|
|
|
3555
3809
|
timeoutMessage,
|
|
3556
3810
|
stopMessage,
|
|
3557
3811
|
turnBudget: config.turnBudget,
|
|
3812
|
+
toolTimeoutMs: task.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
3558
3813
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
|
|
3559
3814
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
3560
3815
|
onWriterProcess,
|
|
@@ -3938,6 +4193,7 @@ async function runSubagent(
|
|
|
3938
4193
|
timeoutMessage,
|
|
3939
4194
|
stopMessage,
|
|
3940
4195
|
turnBudget: config.turnBudget,
|
|
4196
|
+
toolTimeoutMs: taskForRun.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
3941
4197
|
onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
|
|
3942
4198
|
onChildEvent: (event) => updateStepFromChildEvent(fi, event),
|
|
3943
4199
|
onWriterProcess,
|
|
@@ -4228,6 +4484,7 @@ async function runSubagent(
|
|
|
4228
4484
|
timeoutMessage,
|
|
4229
4485
|
stopMessage,
|
|
4230
4486
|
turnBudget: config.turnBudget,
|
|
4487
|
+
toolTimeoutMs: seqStep.toolTimeoutMs ?? config.toolTimeoutMs,
|
|
4231
4488
|
onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking),
|
|
4232
4489
|
onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
|
|
4233
4490
|
onWriterProcess,
|
|
@@ -4525,9 +4782,8 @@ async function runSubagent(
|
|
|
4525
4782
|
statusPayload.error = `Step failed: ${failedStep.agent}`;
|
|
4526
4783
|
}
|
|
4527
4784
|
}
|
|
4528
|
-
writeStatusPayload();
|
|
4529
4785
|
try {
|
|
4530
|
-
|
|
4786
|
+
writeAsyncResultFile(resultPath, {
|
|
4531
4787
|
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
4532
4788
|
id,
|
|
4533
4789
|
agent: agentName,
|
|
@@ -4622,9 +4878,15 @@ async function runSubagent(
|
|
|
4622
4878
|
...(taskIndex !== undefined && { taskIndex }),
|
|
4623
4879
|
...(totalTasks !== undefined && { totalTasks }),
|
|
4624
4880
|
});
|
|
4881
|
+
finalResultCommitted = true;
|
|
4625
4882
|
} catch (err) {
|
|
4626
|
-
|
|
4883
|
+
const message = `Failed to write result file ${resultPath}: ${err instanceof Error ? err.message : String(err)}`;
|
|
4884
|
+
console.error(message, err);
|
|
4885
|
+
statusPayload.state = "failed";
|
|
4886
|
+
statusPayload.error = message;
|
|
4887
|
+
statusPayload.lastUpdate = Date.now();
|
|
4627
4888
|
}
|
|
4889
|
+
writeStatusPayload();
|
|
4628
4890
|
appendJsonl(
|
|
4629
4891
|
eventsPath,
|
|
4630
4892
|
JSON.stringify({
|
|
@@ -267,6 +267,7 @@ function activeRunsForSession(params: SubagentWaitParams, deps: SubagentWaitDeps
|
|
|
267
267
|
resultsDir,
|
|
268
268
|
kill: deps.kill,
|
|
269
269
|
now: deps.now,
|
|
270
|
+
includeNested: false,
|
|
270
271
|
...(params.id ? { runId: params.id } : {}),
|
|
271
272
|
});
|
|
272
273
|
return params.id ? runs.filter((run) => matchesId(run, params.id!)) : runs;
|
|
@@ -286,6 +287,7 @@ function allRunsForSession(params: SubagentWaitParams, deps: SubagentWaitDeps):
|
|
|
286
287
|
resultsDir,
|
|
287
288
|
kill: deps.kill,
|
|
288
289
|
now: deps.now,
|
|
290
|
+
includeNested: false,
|
|
289
291
|
...(params.id ? { runId: params.id } : {}),
|
|
290
292
|
});
|
|
291
293
|
return params.id ? runs.filter((run) => matchesId(run, params.id!)) : runs;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
2
|
import type { ArtifactPaths, SubagentState, WaitCompletion, WaitCompletionChild } from "../../shared/types.ts";
|
|
4
3
|
import type { AsyncRunSummary } from "./async-status.ts";
|
|
5
4
|
import { readCompletionReplay, writeCompletionReplay } from "./completion-replay.ts";
|
|
5
|
+
import { resultFilePath, resultPayloadPathForSessionRun } from "./result-files.ts";
|
|
6
6
|
|
|
7
7
|
function asNonEmptyString(value: unknown): string | undefined {
|
|
8
8
|
return typeof value === "string" && value ? value : undefined;
|
|
@@ -114,7 +114,10 @@ export function collectWaitCompletions(terminal: AsyncRunSummary[], state: Subag
|
|
|
114
114
|
completions.push(recorded.completion);
|
|
115
115
|
continue;
|
|
116
116
|
}
|
|
117
|
-
const
|
|
117
|
+
const publicResultPath = resultFilePath(resultsDir, run.id);
|
|
118
|
+
const resultPath = run.sessionId
|
|
119
|
+
? resultPayloadPathForSessionRun(resultsDir, run.sessionId, run.id) ?? publicResultPath
|
|
120
|
+
: publicResultPath;
|
|
118
121
|
try {
|
|
119
122
|
const raw = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as Record<string, unknown>;
|
|
120
123
|
completions.push(toWaitCompletion(raw, run.id));
|
|
@@ -6,6 +6,7 @@ import { DIRS, type Details, type SubagentState } from "../../shared/types.ts";
|
|
|
6
6
|
import { readStatus } from "../../shared/utils.ts";
|
|
7
7
|
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
8
8
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
9
|
+
import { resultFilePath } from "../background/result-files.ts";
|
|
9
10
|
|
|
10
11
|
export function dismissRecoveredWorkflow(
|
|
11
12
|
state: SubagentState,
|
|
@@ -50,7 +51,7 @@ export function dismissRecoveredWorkflow(
|
|
|
50
51
|
};
|
|
51
52
|
}
|
|
52
53
|
let latestStatus = status;
|
|
53
|
-
const resultPath =
|
|
54
|
+
const resultPath = resultFilePath(DIRS.results, status.runId);
|
|
54
55
|
if (fs.existsSync(resultPath)) {
|
|
55
56
|
const reconciled = reconcileAsyncRun(asyncDir).status;
|
|
56
57
|
if (reconciled && reconciled.state !== "running") {
|