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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../../shared/formatters.ts";
|
|
4
|
+
import { previewDisplayText } from "../../shared/display-text.ts";
|
|
4
5
|
import { formatActivityLabel, formatParallelOutcome } from "../../shared/status-format.ts";
|
|
5
|
-
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type Details, type LaunchResolvedChildExtensionsV1, type RuntimeAcknowledgedChildExtensionsV1, type NestedRunSummary, type SteeringStatus, type SubagentRunMode, type TokenUsage, type TurnBudgetState, type UsageBudgetState } from "../../shared/types.ts";
|
|
6
|
+
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type Details, type HostStepNodeV1, type HostStepState, type LaunchResolvedChildExtensionsV1, type RuntimeAcknowledgedChildExtensionsV1, type NestedRunSummary, type SteeringStatus, type SubagentRunMode, type TimeoutRecoveryProjection, type TokenUsage, type TurnBudgetState, type UsageBudgetState, type WorkflowPreflightV1, type WorkflowGraphSnapshot } from "../../shared/types.ts";
|
|
6
7
|
import type { ResolvedSubagentCapabilityCeiling, SubagentCapabilityAudit } from "../shared/capability-ceiling.ts";
|
|
7
8
|
import { readStatus } from "../../shared/utils.ts";
|
|
8
9
|
import { attachRootChildrenToSteps, buildNestedRouteIndex, findNestedRouteForRootId, type NestedRoute, projectNestedEvents } from "../shared/nested-events.ts";
|
|
@@ -17,16 +18,27 @@ import { readRecentTerminalRunIndex, TERMINAL_RUN_INDEX_DIR } from "./terminal-r
|
|
|
17
18
|
import { canScanAsyncRunPrefix } from "./run-id-query.ts";
|
|
18
19
|
import { asyncStatusChildIdentity } from "../shared/child-identity.ts";
|
|
19
20
|
import { parseWorkflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
21
|
+
import { assertWorkflowGraphHostSteps, hostStepReportName, hostStepVerdictLabel, validHostStepNodes } from "../shared/host-step-status.ts";
|
|
22
|
+
import { projectAsyncWorkflowRows } from "../shared/async-status-projection.ts";
|
|
23
|
+
import { validateAsyncStatusLaneMetadata } from "../shared/lane-metadata.ts";
|
|
24
|
+
import { formatWorkflowPreflightPlanSummary, formatWorkflowPreflightWarningSummary } from "../../workflows/workflow-preflight.ts";
|
|
25
|
+
import { workflowGraphStageNodes } from "../shared/workflow-graph.ts";
|
|
26
|
+
import { formatTimeoutRecoveryLines, projectTimeoutRecovery } from "../shared/mutation-evidence.ts";
|
|
20
27
|
|
|
21
28
|
interface AsyncRunStepSummary {
|
|
22
29
|
index: number;
|
|
23
30
|
childId?: string;
|
|
24
31
|
agent: string;
|
|
32
|
+
/** Human-readable display name for the child session, when derived at launch. */
|
|
33
|
+
sessionName?: string;
|
|
25
34
|
context?: ContextMode;
|
|
26
35
|
label?: string;
|
|
27
36
|
description?: string;
|
|
28
37
|
phase?: string;
|
|
29
38
|
workflowKey?: string;
|
|
39
|
+
lane?: AsyncJobStep["lane"];
|
|
40
|
+
worktreePath?: string;
|
|
41
|
+
branch?: string;
|
|
30
42
|
runId?: string;
|
|
31
43
|
outputName?: string;
|
|
32
44
|
structured?: boolean;
|
|
@@ -58,6 +70,7 @@ interface AsyncRunStepSummary {
|
|
|
58
70
|
stopped?: boolean;
|
|
59
71
|
turnBudget?: TurnBudgetState;
|
|
60
72
|
turnBudgetExceeded?: boolean;
|
|
73
|
+
toolBudgetBlocked?: boolean;
|
|
61
74
|
wrapUpRequested?: boolean;
|
|
62
75
|
acceptance?: AsyncJobStep["acceptance"];
|
|
63
76
|
agentContract?: AsyncJobStep["agentContract"];
|
|
@@ -65,6 +78,7 @@ interface AsyncRunStepSummary {
|
|
|
65
78
|
review?: AsyncJobStep["review"];
|
|
66
79
|
effects?: AsyncJobStep["effects"];
|
|
67
80
|
processTerminal?: AsyncJobStep["processTerminal"];
|
|
81
|
+
timeoutRecovery?: TimeoutRecoveryProjection;
|
|
68
82
|
launchResolvedExtensions?: LaunchResolvedChildExtensionsV1;
|
|
69
83
|
runtimeAcknowledgedExtensions?: RuntimeAcknowledgedChildExtensionsV1;
|
|
70
84
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
@@ -77,7 +91,7 @@ export interface AsyncRunSummary {
|
|
|
77
91
|
asyncDir: string;
|
|
78
92
|
toolCallId?: string;
|
|
79
93
|
sessionId?: string;
|
|
80
|
-
state: "queued" | "running" | "complete" | "failed" | "paused" | "stopped" | "rejected";
|
|
94
|
+
state: "queued" | "running" | "complete" | "failed" | "partial" | "paused" | "stopped" | "rejected";
|
|
81
95
|
error?: string;
|
|
82
96
|
activityState?: ActivityState;
|
|
83
97
|
lastActivityAt?: number;
|
|
@@ -105,6 +119,8 @@ export interface AsyncRunSummary {
|
|
|
105
119
|
chainStepCount?: number;
|
|
106
120
|
pendingAppends?: number;
|
|
107
121
|
parallelGroups?: AsyncParallelGroupStatus[];
|
|
122
|
+
hostSteps?: HostStepNodeV1[];
|
|
123
|
+
workflowGraph?: AsyncStatus["workflowGraph"];
|
|
108
124
|
steps: AsyncRunStepSummary[];
|
|
109
125
|
sessionDir?: string;
|
|
110
126
|
outputFile?: string;
|
|
@@ -122,8 +138,10 @@ export interface AsyncRunSummary {
|
|
|
122
138
|
capabilityAudit?: SubagentCapabilityAudit;
|
|
123
139
|
parentWorkflowRunId?: string;
|
|
124
140
|
workflowKey?: string;
|
|
141
|
+
lane?: AsyncStatus["lane"];
|
|
125
142
|
workflow?: Details["workflow"];
|
|
126
143
|
workflowChildren?: Details["workflowChildren"];
|
|
144
|
+
preflight?: WorkflowPreflightV1;
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
interface AsyncRunListOptions {
|
|
@@ -232,21 +250,32 @@ function deriveAsyncActivityState(asyncDir: string, status: AsyncStatus): { acti
|
|
|
232
250
|
}
|
|
233
251
|
|
|
234
252
|
function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string }, nestedWarnings: string[] = [], nestedRoute?: NestedRoute): AsyncRunSummary {
|
|
253
|
+
validateAsyncStatusLaneMetadata(status, `Invalid async status '${path.join(asyncDir, "status.json")}'`);
|
|
235
254
|
const workflowChildren = parseWorkflowChildSummary(status.workflowChildren);
|
|
236
255
|
if (workflowChildren && workflowChildren.workflowRunId !== status.runId) throw new Error(`Invalid async status '${path.join(asyncDir, "status.json")}': workflowChildren.workflowRunId does not match.`);
|
|
256
|
+
assertWorkflowGraphHostSteps(status.workflowGraph, path.join(asyncDir, "status.json"), status.runId);
|
|
257
|
+
const hostSteps = validHostStepNodes(status.workflowGraph);
|
|
237
258
|
if (status.sessionId !== undefined && typeof status.sessionId !== "string") {
|
|
238
259
|
throw new Error(`Invalid async status '${path.join(asyncDir, "status.json")}': sessionId must be a string.`);
|
|
239
260
|
}
|
|
240
261
|
const { activityState, lastActivityAt } = deriveAsyncActivityState(asyncDir, status);
|
|
241
262
|
const processTerminal = readProcessTerminal(asyncDir, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId })
|
|
242
263
|
?? sanitizeProcessTerminal(status.processTerminal, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId }, path.join(asyncDir, "status.json"));
|
|
243
|
-
const
|
|
244
|
-
|
|
264
|
+
const nestedProjectionAllowed = nestedWarnings.length === 0;
|
|
265
|
+
// Degrade to the status-stored snapshot when the persisted budget is unavailable (e.g. removed
|
|
266
|
+
// by OS temp cleanup) instead of failing the whole run list; admission paths stay strict.
|
|
267
|
+
let runFanoutBudget: AsyncStatus["runFanoutBudget"] = status.runFanoutBudget;
|
|
268
|
+
try {
|
|
269
|
+
const runFanoutBudgetDescriptor = readRunFanoutBudgetDescriptor(asyncDir);
|
|
270
|
+
if (runFanoutBudgetDescriptor) runFanoutBudget = getRunFanoutBudgetSnapshot(runFanoutBudgetDescriptor);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
nestedWarnings.push(`Run fan-out status unavailable: ${getErrorMessage(error)}`);
|
|
273
|
+
}
|
|
245
274
|
const steps = status.steps ?? [];
|
|
246
275
|
const chainStepCount = status.chainStepCount ?? steps.length;
|
|
247
276
|
const parallelGroups = normalizeParallelGroups(status.parallelGroups, steps.length, chainStepCount);
|
|
248
277
|
let nestedChildren: NestedRunSummary[] = [];
|
|
249
|
-
if (
|
|
278
|
+
if (nestedProjectionAllowed && nestedRoute) {
|
|
250
279
|
try {
|
|
251
280
|
// The route is resolved by the caller via buildNestedRouteIndex, so this
|
|
252
281
|
// avoids a fresh scan of the nested-events directory per run.
|
|
@@ -258,15 +287,20 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
258
287
|
const summarizedSteps = steps.map((step, index) => {
|
|
259
288
|
const stepActivityState = step.activityState;
|
|
260
289
|
const stepLastActivityAt = step.lastActivityAt;
|
|
290
|
+
const timeoutRecovery = projectTimeoutRecovery(step.timeoutRecovery);
|
|
261
291
|
return {
|
|
262
292
|
index,
|
|
263
293
|
childId: asyncStatusChildIdentity(step, index),
|
|
264
294
|
agent: step.agent,
|
|
295
|
+
...(step.sessionName ? { sessionName: step.sessionName } : {}),
|
|
265
296
|
...(step.context ? { context: step.context } : {}),
|
|
266
297
|
...(step.label ? { label: step.label } : {}),
|
|
267
298
|
...(step.description ? { description: step.description } : {}),
|
|
268
299
|
...(step.phase ? { phase: step.phase } : {}),
|
|
269
300
|
...(step.workflowKey ? { workflowKey: step.workflowKey } : {}),
|
|
301
|
+
...(step.lane ? { lane: step.lane } : {}),
|
|
302
|
+
...(step.worktreePath ? { worktreePath: step.worktreePath } : {}),
|
|
303
|
+
...(step.branch ? { branch: step.branch } : {}),
|
|
270
304
|
...(step.runId ? { runId: step.runId } : {}),
|
|
271
305
|
...(step.outputName ? { outputName: step.outputName } : {}),
|
|
272
306
|
...(step.structured ? { structured: step.structured } : {}),
|
|
@@ -301,6 +335,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
301
335
|
...(step.stopRequestedAt !== undefined ? { stopRequestedAt: step.stopRequestedAt } : {}),
|
|
302
336
|
...(step.turnBudget ? { turnBudget: step.turnBudget } : {}),
|
|
303
337
|
...(step.turnBudgetExceeded !== undefined ? { turnBudgetExceeded: step.turnBudgetExceeded } : {}),
|
|
338
|
+
...(step.toolBudgetBlocked !== undefined ? { toolBudgetBlocked: step.toolBudgetBlocked } : {}),
|
|
304
339
|
...(step.wrapUpRequested !== undefined ? { wrapUpRequested: step.wrapUpRequested } : {}),
|
|
305
340
|
...(step.acceptance ? { acceptance: step.acceptance } : {}),
|
|
306
341
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
@@ -310,7 +345,9 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
310
345
|
...(step.execution ? { execution: step.execution } : {}),
|
|
311
346
|
...(step.review ? { review: step.review } : {}),
|
|
312
347
|
...(step.effects ? { effects: step.effects } : {}),
|
|
348
|
+
...(step.watchdog ? { watchdog: step.watchdog } : {}),
|
|
313
349
|
...(step.processTerminal ? { processTerminal: sanitizeProcessTerminal(step.processTerminal, { runId: status.runId, runnerProcessInstanceId: step.processTerminal.runnerProcessInstanceId }, `${path.join(asyncDir, "status.json")} step ${index}`) } : {}),
|
|
350
|
+
...(timeoutRecovery ? { timeoutRecovery } : {}),
|
|
314
351
|
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
315
352
|
...(step.capabilityAudit ? { capabilityAudit: step.capabilityAudit } : {}),
|
|
316
353
|
...(step.children?.length ? { children: step.children } : {}),
|
|
@@ -350,6 +387,8 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
350
387
|
...(status.chainStepCount !== undefined ? { chainStepCount: status.chainStepCount } : {}),
|
|
351
388
|
...(status.pendingAppends !== undefined ? { pendingAppends: status.pendingAppends } : {}),
|
|
352
389
|
...(parallelGroups.length ? { parallelGroups } : {}),
|
|
390
|
+
...(hostSteps.length ? { hostSteps } : {}),
|
|
391
|
+
...(status.mode === "workflow" && status.workflowGraph ? { workflowGraph: status.workflowGraph } : {}),
|
|
353
392
|
steps: summarizedSteps,
|
|
354
393
|
...(nestedChildren.length ? { nestedChildren } : {}),
|
|
355
394
|
...(nestedWarnings.length ? { nestedWarnings } : {}),
|
|
@@ -362,8 +401,10 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
362
401
|
...(status.capabilityAudit ? { capabilityAudit: status.capabilityAudit } : {}),
|
|
363
402
|
...(status.parentWorkflowRunId ? { parentWorkflowRunId: status.parentWorkflowRunId } : {}),
|
|
364
403
|
...(status.workflowKey ? { workflowKey: status.workflowKey } : {}),
|
|
404
|
+
...(status.lane ? { lane: status.lane } : {}),
|
|
365
405
|
...(status.workflow ? { workflow: status.workflow } : {}),
|
|
366
406
|
...(workflowChildren ? { workflowChildren } : {}),
|
|
407
|
+
...(status.preflight ? { preflight: status.preflight } : {}),
|
|
367
408
|
...(status.sessionDir ? { sessionDir: status.sessionDir } : {}),
|
|
368
409
|
...(status.outputFile ? { outputFile: status.outputFile } : {}),
|
|
369
410
|
...(status.totalTokens ? { totalTokens: status.totalTokens } : {}),
|
|
@@ -383,6 +424,7 @@ function sortRuns(runs: AsyncRunSummary[]): AsyncRunSummary[] {
|
|
|
383
424
|
case "running": return 0;
|
|
384
425
|
case "queued": return 1;
|
|
385
426
|
case "failed": return 2;
|
|
427
|
+
case "partial": return 2;
|
|
386
428
|
case "stopped": return 2;
|
|
387
429
|
case "paused": return 2;
|
|
388
430
|
case "complete": return 3;
|
|
@@ -511,7 +553,7 @@ function formatActivityFacts(input: { activityState?: ActivityState; lastActivit
|
|
|
511
553
|
}
|
|
512
554
|
|
|
513
555
|
function formatStepLine(step: AsyncRunStepSummary): string {
|
|
514
|
-
const display = step.label ? `${step.label} (${step.agent})` : step.agent;
|
|
556
|
+
const display = step.sessionName?.trim() || (step.label ? `${step.label} (${step.agent})` : step.agent);
|
|
515
557
|
const context = contextModeLabel(step.context);
|
|
516
558
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
517
559
|
const parts = [`${step.index + 1}. ${phase}${display}${context ? ` ${context}` : ""}`, step.status];
|
|
@@ -521,15 +563,61 @@ function formatStepLine(step: AsyncRunStepSummary): string {
|
|
|
521
563
|
if (modelThinking) parts.push(modelThinking);
|
|
522
564
|
if (step.durationMs !== undefined) parts.push(formatDuration(step.durationMs));
|
|
523
565
|
if (step.tokens) parts.push(`${formatTokens(step.tokens.total)} tok`);
|
|
566
|
+
if (step.lane) parts.push(`lane ${step.lane.key}`);
|
|
567
|
+
if (step.worktreePath) parts.push(`worktree ${shortenPath(step.worktreePath)} · branch ${step.branch ?? "unknown"}`);
|
|
524
568
|
return parts.join(" | ");
|
|
525
569
|
}
|
|
526
570
|
|
|
571
|
+
function formatHostStepLine(row: ReturnType<typeof projectAsyncWorkflowRows>[number]): string {
|
|
572
|
+
if (!row.kind) return "";
|
|
573
|
+
const state = hostStepVerdictLabel(row.state as HostStepState, row.verdict);
|
|
574
|
+
const details = [
|
|
575
|
+
row.provider ? `provider:${row.provider}` : undefined,
|
|
576
|
+
row.role ? `role:${row.role}` : undefined,
|
|
577
|
+
row.target,
|
|
578
|
+
row.detail,
|
|
579
|
+
row.reasonCode ? `reason:${row.reasonCode}` : undefined,
|
|
580
|
+
row.freshness?.stale ? "stale" : row.freshness?.observedRef ? `ref:${row.freshness.observedRef}` : undefined,
|
|
581
|
+
row.reportPath ? `out:${hostStepReportName(row.reportPath)}` : undefined,
|
|
582
|
+
].filter((value): value is string => Boolean(value));
|
|
583
|
+
return `host ${row.kind}: ${row.name} | ${state}${details.length ? ` | ${details.join(" | ")}` : ""}`;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function workflowStageStateLabel(status: WorkflowGraphSnapshot["nodes"][number]["status"]): string {
|
|
587
|
+
switch (status) {
|
|
588
|
+
case "completed":
|
|
589
|
+
return "complete";
|
|
590
|
+
case "detached":
|
|
591
|
+
return "paused";
|
|
592
|
+
default:
|
|
593
|
+
return status;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export function formatWorkflowStageLine(node: WorkflowGraphSnapshot["nodes"][number], index: number, total: number): string {
|
|
598
|
+
const state = workflowStageStateLabel(node.status);
|
|
599
|
+
const id = previewDisplayText(node.id, 160);
|
|
600
|
+
const label = node.label ? previewDisplayText(node.label, 160) : "";
|
|
601
|
+
const display = label && label !== id ? ` | ${label}` : "";
|
|
602
|
+
const agent = node.agent ? ` | ${previewDisplayText(node.agent, 80)}` : "";
|
|
603
|
+
const error = node.error ? ` | ${previewDisplayText(node.error, 240)}` : "";
|
|
604
|
+
return `stage ${index + 1}/${total}: ${id}${display}${agent} | ${state}${error}`;
|
|
605
|
+
}
|
|
606
|
+
|
|
527
607
|
export function formatAsyncRunOutputPath(run: Pick<AsyncRunSummary, "asyncDir" | "outputFile">): string | undefined {
|
|
528
608
|
if (!run.outputFile) return undefined;
|
|
529
609
|
return path.isAbsolute(run.outputFile) ? run.outputFile : path.join(run.asyncDir, run.outputFile);
|
|
530
610
|
}
|
|
531
611
|
|
|
532
|
-
export function formatAsyncRunProgressLabel(run: Pick<AsyncRunSummary, "mode" | "state" | "currentStep" | "chainStepCount" | "parallelGroups" | "steps">): string {
|
|
612
|
+
export function formatAsyncRunProgressLabel(run: Pick<AsyncRunSummary, "mode" | "state" | "currentStep" | "chainStepCount" | "parallelGroups" | "steps"> & { workflowGraph?: WorkflowGraphSnapshot }): string {
|
|
613
|
+
const graphStages = run.mode === "workflow" ? workflowGraphStageNodes(run.workflowGraph) : [];
|
|
614
|
+
if (graphStages.length > 0) {
|
|
615
|
+
const currentNode = graphStages.find((node) => node.id === run.workflowGraph?.currentNodeId);
|
|
616
|
+
if (currentNode && currentNode.status !== "completed") return `stage ${graphStages.indexOf(currentNode) + 1}/${graphStages.length}`;
|
|
617
|
+
const activeNode = graphStages.find((node) => node.status === "running") ?? graphStages.find((node) => node.status !== "completed");
|
|
618
|
+
if (activeNode) return `stage ${graphStages.indexOf(activeNode) + 1}/${graphStages.length}`;
|
|
619
|
+
return `stage ${graphStages.length}/${graphStages.length}`;
|
|
620
|
+
}
|
|
533
621
|
const stepCount = run.steps.length || 1;
|
|
534
622
|
const chainStepCount = run.chainStepCount ?? stepCount;
|
|
535
623
|
const groups = normalizeParallelGroups(run.parallelGroups, run.steps.length, chainStepCount);
|
|
@@ -556,7 +644,8 @@ function formatRunHeader(run: AsyncRunSummary): string {
|
|
|
556
644
|
const activity = formatActivityFacts(run);
|
|
557
645
|
const pending = run.pendingAppends ? ` | ${run.pendingAppends} pending append${run.pendingAppends === 1 ? "" : "s"}` : "";
|
|
558
646
|
const context = contextModeLabel(run.context);
|
|
559
|
-
|
|
647
|
+
const lane = run.lane ? ` | lane ${run.lane.key}` : "";
|
|
648
|
+
return `${run.id} | ${run.state}${activity ? ` | ${activity}` : ""} | ${run.mode}${context ? ` ${context}` : ""} | ${stepLabel}${pending}${lane} | ${cwd}`;
|
|
560
649
|
}
|
|
561
650
|
|
|
562
651
|
export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active async runs"): string {
|
|
@@ -565,10 +654,23 @@ export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active as
|
|
|
565
654
|
const lines = [`${heading}: ${runs.length}`, ""];
|
|
566
655
|
for (const run of runs) {
|
|
567
656
|
lines.push(`- ${formatRunHeader(run)}`);
|
|
657
|
+
if (run.preflight) lines.push(formatWorkflowPreflightPlanSummary(run.preflight, { indent: " " }));
|
|
658
|
+
const preflightWarning = formatWorkflowPreflightWarningSummary(run.workflow?.preflightWarnings, { indent: " " });
|
|
659
|
+
if (preflightWarning) lines.push(preflightWarning);
|
|
568
660
|
for (const step of run.steps) {
|
|
569
661
|
lines.push(` ${formatStepLine(step)}`);
|
|
662
|
+
lines.push(...formatTimeoutRecoveryLines(step.timeoutRecovery, " "));
|
|
570
663
|
lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", maxLines: 12 }));
|
|
571
664
|
}
|
|
665
|
+
const loadedWorkflowKeys = new Set(run.steps.flatMap((step) => step.workflowKey ? [step.workflowKey] : []));
|
|
666
|
+
const graphStages = run.mode === "workflow" ? workflowGraphStageNodes(run.workflowGraph) : [];
|
|
667
|
+
for (const [index, node] of graphStages.entries()) {
|
|
668
|
+
if (!loadedWorkflowKeys.has(node.id)) lines.push(` ${formatWorkflowStageLine(node, index, graphStages.length)}`);
|
|
669
|
+
}
|
|
670
|
+
for (const row of projectAsyncWorkflowRows([], run.hostSteps)) {
|
|
671
|
+
const line = formatHostStepLine(row);
|
|
672
|
+
if (line) lines.push(` ${line}`);
|
|
673
|
+
}
|
|
572
674
|
const attached = new Set(run.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
573
675
|
const unattached = run.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
574
676
|
lines.push(...formatNestedRunStatusLines(unattached, { indent: " ", maxLines: 12 }));
|
|
@@ -3,8 +3,9 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
5
5
|
import { appendJsonl } from "../../shared/artifacts.ts";
|
|
6
|
-
import type {
|
|
6
|
+
import type { AsyncStatus, WorkflowGraphNode, WorkflowGraphSnapshot } from "../../shared/types.ts";
|
|
7
7
|
import { PROMPT_REDACTED, readStatus } from "../../shared/utils.ts";
|
|
8
|
+
import { deriveChildSessionName } from "../../shared/child-session-name.ts";
|
|
8
9
|
import type { DynamicRunnerGroup, ParallelStepGroup, RunnerStep, RunnerSubagentStep } from "../shared/parallel-utils.ts";
|
|
9
10
|
import { isDynamicRunnerGroup, isParallelGroup } from "../shared/parallel-utils.ts";
|
|
10
11
|
|
|
@@ -160,8 +161,10 @@ export function statusStepDescription(task: string | undefined): string | undefi
|
|
|
160
161
|
|
|
161
162
|
function statusStepForTask(task: RunnerSubagentStep): StatusStep {
|
|
162
163
|
const description = statusStepDescription(task.task);
|
|
164
|
+
const sessionName = task.sessionName ?? deriveChildSessionName({ agent: task.agent, task: task.task, label: task.label });
|
|
163
165
|
return {
|
|
164
166
|
agent: task.agent,
|
|
167
|
+
...(sessionName ? { sessionName } : {}),
|
|
165
168
|
...(description ? { description } : {}),
|
|
166
169
|
...(task.context ? { context: task.context } : {}),
|
|
167
170
|
phase: task.phase,
|
|
@@ -180,7 +183,7 @@ function statusStepForTask(task: RunnerSubagentStep): StatusStep {
|
|
|
180
183
|
};
|
|
181
184
|
}
|
|
182
185
|
|
|
183
|
-
function statusStepsForRunnerStep(step: RunnerStep
|
|
186
|
+
function statusStepsForRunnerStep(step: RunnerStep): StatusStep[] {
|
|
184
187
|
if (isParallelGroup(step)) return step.parallel.map(statusStepForTask);
|
|
185
188
|
if (isDynamicRunnerGroup(step)) {
|
|
186
189
|
return [{
|
|
@@ -297,7 +300,7 @@ export function appendRunnerStepsToStatus(input: {
|
|
|
297
300
|
for (const step of input.steps) {
|
|
298
301
|
const stepIndex = input.status.chainStepCount ?? input.status.steps?.length ?? 0;
|
|
299
302
|
const flatIndex = input.status.steps?.length ?? 0;
|
|
300
|
-
const statusSteps = statusStepsForRunnerStep(step
|
|
303
|
+
const statusSteps = statusStepsForRunnerStep(step);
|
|
301
304
|
input.status.steps ??= [];
|
|
302
305
|
input.status.steps.push(...statusSteps);
|
|
303
306
|
if (isParallelGroup(step)) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { resultFilePath, resultPayloadPathForSessionRun } from "./result-files.ts";
|
|
4
|
+
import type { AcceptanceLedger, ArtifactPaths, AsyncStatus, CostSummary, EffectsProjection, ExecutionProjection, ModelAttempt, Usage } from "../../shared/types.ts";
|
|
4
5
|
import { readStatus } from "../../shared/utils.ts";
|
|
5
6
|
|
|
6
7
|
export interface ImportedAsyncRoot {
|
|
@@ -12,6 +13,8 @@ export interface ImportedAsyncRoot {
|
|
|
12
13
|
|
|
13
14
|
export interface ImportedAsyncRootResult {
|
|
14
15
|
agent: string;
|
|
16
|
+
/** Human-readable display name for the child session, when derived at launch. */
|
|
17
|
+
sessionName?: string;
|
|
15
18
|
output: string;
|
|
16
19
|
success: boolean;
|
|
17
20
|
exitCode: number;
|
|
@@ -23,6 +26,7 @@ export interface ImportedAsyncRootResult {
|
|
|
23
26
|
modelAttempts?: ModelAttempt[];
|
|
24
27
|
contextOverflow?: boolean;
|
|
25
28
|
totalCost?: CostSummary;
|
|
29
|
+
usage?: Usage;
|
|
26
30
|
structuredOutput?: unknown;
|
|
27
31
|
structuredOutputPath?: string;
|
|
28
32
|
structuredOutputSchemaPath?: string;
|
|
@@ -33,6 +37,8 @@ export interface ImportedAsyncRootResult {
|
|
|
33
37
|
transcriptError?: string;
|
|
34
38
|
timedOut?: boolean;
|
|
35
39
|
stopped?: boolean;
|
|
40
|
+
execution?: ExecutionProjection;
|
|
41
|
+
effects?: EffectsProjection;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
interface AsyncResultFile {
|
|
@@ -44,11 +50,14 @@ interface AsyncResultFile {
|
|
|
44
50
|
stopped?: boolean;
|
|
45
51
|
results?: Array<{
|
|
46
52
|
agent?: string;
|
|
53
|
+
sessionName?: string;
|
|
47
54
|
output?: string;
|
|
48
55
|
error?: string;
|
|
49
56
|
success?: boolean;
|
|
50
57
|
timedOut?: boolean;
|
|
51
58
|
stopped?: boolean;
|
|
59
|
+
execution?: ExecutionProjection;
|
|
60
|
+
effects?: EffectsProjection;
|
|
52
61
|
sessionFile?: string;
|
|
53
62
|
intercomTarget?: string;
|
|
54
63
|
model?: string;
|
|
@@ -56,6 +65,7 @@ interface AsyncResultFile {
|
|
|
56
65
|
modelAttempts?: ModelAttempt[];
|
|
57
66
|
contextOverflow?: boolean;
|
|
58
67
|
totalCost?: CostSummary;
|
|
68
|
+
usage?: Usage;
|
|
59
69
|
structuredOutput?: unknown;
|
|
60
70
|
structuredOutputPath?: string;
|
|
61
71
|
structuredOutputSchemaPath?: string;
|
|
@@ -67,8 +77,25 @@ interface AsyncResultFile {
|
|
|
67
77
|
}>;
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
const TERMINAL_STATES = new Set(["complete", "failed", "paused", "stopped"]);
|
|
71
|
-
const TERMINAL_STEP_STATUSES = new Set(["complete", "completed", "failed", "paused", "stopped"]);
|
|
80
|
+
const TERMINAL_STATES = new Set(["complete", "failed", "partial", "paused", "stopped"]);
|
|
81
|
+
const TERMINAL_STEP_STATUSES = new Set(["complete", "completed", "failed", "partial", "paused", "stopped"]);
|
|
82
|
+
|
|
83
|
+
function usageFromAttempts(attempts: ModelAttempt[] | undefined): Usage | undefined {
|
|
84
|
+
if (!attempts?.length) return undefined;
|
|
85
|
+
const usage: Usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
86
|
+
for (const attempt of attempts) {
|
|
87
|
+
if (!attempt.usage) continue;
|
|
88
|
+
usage.input += attempt.usage.input;
|
|
89
|
+
usage.output += attempt.usage.output;
|
|
90
|
+
usage.cacheRead += attempt.usage.cacheRead;
|
|
91
|
+
usage.cacheWrite += attempt.usage.cacheWrite;
|
|
92
|
+
usage.cost += attempt.usage.cost;
|
|
93
|
+
usage.turns += attempt.usage.turns;
|
|
94
|
+
}
|
|
95
|
+
return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0
|
|
96
|
+
? usage
|
|
97
|
+
: undefined;
|
|
98
|
+
}
|
|
72
99
|
|
|
73
100
|
function readResultFile(resultPath: string): AsyncResultFile | undefined {
|
|
74
101
|
try {
|
|
@@ -81,6 +108,13 @@ function readResultFile(resultPath: string): AsyncResultFile | undefined {
|
|
|
81
108
|
}
|
|
82
109
|
}
|
|
83
110
|
|
|
111
|
+
function readImportedResultFile(root: ImportedAsyncRoot, status: AsyncStatus | null): AsyncResultFile | undefined {
|
|
112
|
+
const direct = readResultFile(root.resultPath);
|
|
113
|
+
if (direct || !status?.sessionId) return direct;
|
|
114
|
+
const indexedPath = resultPayloadPathForSessionRun(path.dirname(root.resultPath), status.sessionId, root.runId);
|
|
115
|
+
return indexedPath ? readResultFile(indexedPath) : undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
84
118
|
function selectedStatusStep(status: AsyncStatus | null, index: number): NonNullable<AsyncStatus["steps"]>[number] | undefined {
|
|
85
119
|
return status?.steps?.[index];
|
|
86
120
|
}
|
|
@@ -92,12 +126,12 @@ function isTerminalStatus(status: AsyncStatus | null, index: number): boolean {
|
|
|
92
126
|
return TERMINAL_STATES.has(status.state);
|
|
93
127
|
}
|
|
94
128
|
|
|
95
|
-
function resultState(result: AsyncResultFile | undefined, child: NonNullable<AsyncResultFile["results"]>[number] | undefined): "complete" | "failed" | "paused" | "stopped" | undefined {
|
|
129
|
+
function resultState(result: AsyncResultFile | undefined, child: NonNullable<AsyncResultFile["results"]>[number] | undefined): "complete" | "failed" | "partial" | "paused" | "stopped" | undefined {
|
|
96
130
|
if (!result) return undefined;
|
|
97
131
|
if (child?.stopped === true) return "stopped";
|
|
98
132
|
if (child?.success === true) return "complete";
|
|
99
|
-
if (child?.success === false) return result.state === "stopped" ? "stopped" : result.state === "paused" ? "paused" : "failed";
|
|
100
|
-
if (result.state === "complete" || result.state === "failed" || result.state === "paused" || result.state === "stopped") return result.state;
|
|
133
|
+
if (child?.success === false) return result.state === "stopped" ? "stopped" : result.state === "paused" ? "paused" : result.state === "partial" ? "partial" : "failed";
|
|
134
|
+
if (result.state === "complete" || result.state === "failed" || result.state === "partial" || result.state === "paused" || result.state === "stopped") return result.state;
|
|
101
135
|
if (result.success === true) return "complete";
|
|
102
136
|
if (result.success === false) return "failed";
|
|
103
137
|
return undefined;
|
|
@@ -108,6 +142,11 @@ function outputFromTerminalStatus(root: ImportedAsyncRoot, status: AsyncStatus,
|
|
|
108
142
|
const timedOut = step?.timedOut === true || status.timedOut === true;
|
|
109
143
|
const stopped = step?.stopped === true || status.stopped === true || status.state === "stopped";
|
|
110
144
|
const message = step?.error ?? status.error ?? (stopped ? "Subagent stopped by user." : `Attached async root ${root.runId} ended without a result file at ${root.resultPath}.`);
|
|
145
|
+
const partialStatus = status.state === "partial" || step?.status === "partial";
|
|
146
|
+
const execution = step?.execution ?? (partialStatus
|
|
147
|
+
? { status: "partial" as const, success: false, exitCode: 1, error: message }
|
|
148
|
+
: undefined);
|
|
149
|
+
const usage = usageFromAttempts(step?.modelAttempts);
|
|
111
150
|
return {
|
|
112
151
|
agent,
|
|
113
152
|
output: message,
|
|
@@ -116,22 +155,27 @@ function outputFromTerminalStatus(root: ImportedAsyncRoot, status: AsyncStatus,
|
|
|
116
155
|
error: message,
|
|
117
156
|
...(timedOut ? { timedOut: true } : {}),
|
|
118
157
|
...(stopped ? { stopped: true } : {}),
|
|
158
|
+
...(step?.sessionName ? { sessionName: step.sessionName } : {}),
|
|
119
159
|
...(step?.sessionFile ?? status.sessionFile ? { sessionFile: step?.sessionFile ?? status.sessionFile } : {}),
|
|
120
160
|
...(step?.model ? { model: step.model } : {}),
|
|
121
161
|
...(step?.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
|
|
122
162
|
...(step?.modelAttempts ? { modelAttempts: step.modelAttempts } : {}),
|
|
123
163
|
...(step?.contextOverflow ? { contextOverflow: true } : {}),
|
|
124
164
|
...(step?.totalCost ? { totalCost: step.totalCost } : {}),
|
|
165
|
+
...(usage ? { usage } : {}),
|
|
125
166
|
...(step?.structuredOutput !== undefined ? { structuredOutput: step.structuredOutput } : {}),
|
|
126
167
|
...(step?.structuredOutputPath ? { structuredOutputPath: step.structuredOutputPath } : {}),
|
|
127
168
|
...(step?.structuredOutputSchemaPath ? { structuredOutputSchemaPath: step.structuredOutputSchemaPath } : {}),
|
|
128
169
|
...(step?.acceptance ? { acceptance: step.acceptance } : {}),
|
|
170
|
+
...(execution ? { execution } : {}),
|
|
171
|
+
...(step?.effects ? { effects: step.effects } : {}),
|
|
129
172
|
...(step?.transcriptPath ? { transcriptPath: step.transcriptPath } : {}),
|
|
130
173
|
};
|
|
131
174
|
}
|
|
132
175
|
|
|
133
176
|
function outputFromTimeout(root: ImportedAsyncRoot, status: AsyncStatus | null, message: string): ImportedAsyncRootResult {
|
|
134
177
|
const step = selectedStatusStep(status, root.index);
|
|
178
|
+
const usage = usageFromAttempts(step?.modelAttempts);
|
|
135
179
|
return {
|
|
136
180
|
agent: step?.agent ?? status?.steps?.[root.index]?.agent ?? "subagent",
|
|
137
181
|
output: message,
|
|
@@ -139,12 +183,14 @@ function outputFromTimeout(root: ImportedAsyncRoot, status: AsyncStatus | null,
|
|
|
139
183
|
exitCode: 1,
|
|
140
184
|
error: message,
|
|
141
185
|
timedOut: true,
|
|
186
|
+
...(step?.sessionName ? { sessionName: step.sessionName } : {}),
|
|
142
187
|
...(step?.sessionFile ?? status?.sessionFile ? { sessionFile: step?.sessionFile ?? status?.sessionFile } : {}),
|
|
143
188
|
...(step?.model ? { model: step.model } : {}),
|
|
144
189
|
...(step?.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
|
|
145
190
|
...(step?.modelAttempts ? { modelAttempts: step.modelAttempts } : {}),
|
|
146
191
|
...(step?.contextOverflow ? { contextOverflow: true } : {}),
|
|
147
192
|
...(step?.totalCost ? { totalCost: step.totalCost } : {}),
|
|
193
|
+
...(usage ? { usage } : {}),
|
|
148
194
|
...(step?.transcriptPath ? { transcriptPath: step.transcriptPath } : {}),
|
|
149
195
|
};
|
|
150
196
|
}
|
|
@@ -159,6 +205,8 @@ function buildImportedResult(root: ImportedAsyncRoot, status: AsyncStatus | null
|
|
|
159
205
|
const stopped = child?.stopped === true || step?.stopped === true || result.stopped === true || status?.stopped === true || state === "stopped";
|
|
160
206
|
const success = state === "complete" && !timedOut && !stopped;
|
|
161
207
|
const error = child?.error ?? (success ? undefined : stopped ? "Subagent stopped by user." : result.error ?? result.summary ?? status?.error ?? `Attached async root ${root.runId} did not complete successfully.`);
|
|
208
|
+
const execution = state ? { status: state === "complete" ? "completed" as const : state, success, exitCode: success ? 0 : 1, ...(error ? { error } : {}) } : undefined;
|
|
209
|
+
const usage = child?.usage ?? usageFromAttempts(step?.modelAttempts);
|
|
162
210
|
return {
|
|
163
211
|
agent,
|
|
164
212
|
output: success ? output : (output || error || ""),
|
|
@@ -167,6 +215,7 @@ function buildImportedResult(root: ImportedAsyncRoot, status: AsyncStatus | null
|
|
|
167
215
|
...(error ? { error } : {}),
|
|
168
216
|
...(timedOut ? { timedOut: true } : {}),
|
|
169
217
|
...(stopped ? { stopped: true } : {}),
|
|
218
|
+
...(child?.sessionName ?? step?.sessionName ? { sessionName: child?.sessionName ?? step?.sessionName } : {}),
|
|
170
219
|
...(child?.sessionFile ?? step?.sessionFile ?? status?.sessionFile ? { sessionFile: child?.sessionFile ?? step?.sessionFile ?? status?.sessionFile } : {}),
|
|
171
220
|
...(child?.intercomTarget ? { intercomTarget: child.intercomTarget } : {}),
|
|
172
221
|
...(child?.model ?? step?.model ? { model: child?.model ?? step?.model } : {}),
|
|
@@ -174,6 +223,7 @@ function buildImportedResult(root: ImportedAsyncRoot, status: AsyncStatus | null
|
|
|
174
223
|
...(child?.modelAttempts ?? step?.modelAttempts ? { modelAttempts: child?.modelAttempts ?? step?.modelAttempts } : {}),
|
|
175
224
|
...(child?.contextOverflow || step?.contextOverflow ? { contextOverflow: true } : {}),
|
|
176
225
|
...(child?.totalCost ?? step?.totalCost ? { totalCost: child?.totalCost ?? step?.totalCost } : {}),
|
|
226
|
+
...(usage ? { usage } : {}),
|
|
177
227
|
...(child?.structuredOutput !== undefined ? { structuredOutput: child.structuredOutput } : step?.structuredOutput !== undefined ? { structuredOutput: step.structuredOutput } : {}),
|
|
178
228
|
...(child?.structuredOutputPath ?? step?.structuredOutputPath ? { structuredOutputPath: child?.structuredOutputPath ?? step?.structuredOutputPath } : {}),
|
|
179
229
|
...(child?.structuredOutputSchemaPath ?? step?.structuredOutputSchemaPath ? { structuredOutputSchemaPath: child?.structuredOutputSchemaPath ?? step?.structuredOutputSchemaPath } : {}),
|
|
@@ -182,6 +232,8 @@ function buildImportedResult(root: ImportedAsyncRoot, status: AsyncStatus | null
|
|
|
182
232
|
...(child?.outputSaveError ? { outputSaveError: child.outputSaveError } : {}),
|
|
183
233
|
...(child?.transcriptPath ?? step?.transcriptPath ? { transcriptPath: child?.transcriptPath ?? step?.transcriptPath } : {}),
|
|
184
234
|
...(child?.transcriptError ? { transcriptError: child.transcriptError } : {}),
|
|
235
|
+
...(execution ? { execution } : {}),
|
|
236
|
+
...(child?.effects ?? step?.effects ? { effects: child?.effects ?? step?.effects } : {}),
|
|
185
237
|
};
|
|
186
238
|
}
|
|
187
239
|
|
|
@@ -196,7 +248,7 @@ export async function waitForImportedAsyncRoot(
|
|
|
196
248
|
for (;;) {
|
|
197
249
|
const status = readStatus(root.asyncDir);
|
|
198
250
|
if (options.shouldAbort?.()) return outputFromTimeout(root, status, options.timeoutMessage ?? "Subagent timed out.");
|
|
199
|
-
const result =
|
|
251
|
+
const result = readImportedResultFile(root, status);
|
|
200
252
|
if (result) return buildImportedResult(root, status, result);
|
|
201
253
|
if (isTerminalStatus(status, root.index)) {
|
|
202
254
|
terminalSince ??= now();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-OS control channel for async subagent runs.
|
|
3
3
|
*
|
|
4
|
-
* Background runs
|
|
5
|
-
*
|
|
4
|
+
* Background runs use child processes. Unix detaches them from the parent process.
|
|
5
|
+
* The original control path delivered an interrupt with
|
|
6
|
+
* `process.kill(pid, SIGUSR2|SIGBREAK)`, but Windows cannot
|
|
6
7
|
* deliver those signals cross-process via `process.kill` and throws `ENOSYS`,
|
|
7
8
|
* which left async runs uninterruptible (no stop, no live steer) on Windows.
|
|
8
9
|
*
|