pi-subagents 0.58.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/docs/agents.md +5 -3
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +1 -1
- package/docs/models.md +24 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +89 -4
- package/docs/workflows.md +93 -2
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +1 -1
- package/skills/pi-subagents/SKILL.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +5 -1
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +24 -3
- package/src/agents/agent-management.ts +5 -17
- package/src/agents/agent-serializer.ts +4 -2
- package/src/agents/agents.ts +67 -26
- package/src/agents/runtime-agent-registry.ts +9 -16
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +6 -9
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +51 -23
- package/src/extension/public-execution.ts +15 -2
- package/src/extension/schemas.ts +35 -10
- package/src/extension/tool-description.ts +18 -3
- package/src/intercom/result-intercom.ts +2 -0
- package/src/profiles/profiles.ts +5 -6
- package/src/runs/background/active-async-capacity.ts +2 -2
- package/src/runs/background/async-execution.ts +50 -35
- package/src/runs/background/async-job-tracker.ts +14 -12
- package/src/runs/background/async-resume.ts +23 -25
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +66 -7
- package/src/runs/background/chain-append.ts +6 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- package/src/runs/background/fleet-view.ts +21 -11
- package/src/runs/background/notify.ts +158 -6
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +2 -0
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/retained-children.ts +1 -1
- package/src/runs/background/run-status.ts +18 -8
- package/src/runs/background/scheduled-runs.ts +86 -7
- package/src/runs/background/stale-run-reconciler.ts +10 -4
- package/src/runs/background/steering.ts +4 -14
- package/src/runs/background/subagent-runner.ts +346 -357
- package/src/runs/background/subagent-wait.ts +58 -10
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +22 -1
- package/src/runs/background/wait-config.ts +23 -9
- package/src/runs/background/wait-tool.ts +9 -2
- package/src/runs/foreground/async-steering-action.ts +2 -2
- package/src/runs/foreground/execution.ts +114 -120
- package/src/runs/foreground/foreground-control.ts +3 -0
- package/src/runs/foreground/foreground-history.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +478 -239
- package/src/runs/foreground/workflow-detach-reconcile.ts +99 -200
- package/src/runs/shared/abort-recovery.ts +119 -0
- package/src/runs/shared/async-status-projection.ts +463 -0
- package/src/runs/shared/child-identity.ts +19 -4
- package/src/runs/shared/child-launch-plan.ts +151 -0
- package/src/runs/shared/completion-evidence.ts +89 -0
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/mcp-config-sources.ts +42 -6
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +93 -143
- package/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/src/runs/shared/model-fallback.ts +9 -2
- package/src/runs/shared/nested-events.ts +6 -2
- package/src/runs/shared/nested-render.ts +7 -3
- package/src/runs/shared/parallel-handoff.ts +419 -7
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +20 -2
- package/src/runs/shared/single-output.ts +27 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -4
- package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/src/runs/shared/worktree.ts +18 -0
- package/src/shared/child-session-name.ts +46 -0
- package/src/shared/extension-context.ts +24 -0
- package/src/shared/formatters.ts +5 -2
- package/src/shared/launch-contract.ts +1 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +198 -29
- package/src/shared/utils.ts +35 -55
- package/src/slash/delegation-adapters.ts +1 -8
- package/src/slash/delegation-request.ts +0 -4
- package/src/slash/slash-bridge.ts +1 -2
- package/src/slash/slash-commands.ts +369 -90
- package/src/slash/slash-live-state.ts +22 -11
- package/src/tui/fleet-status.ts +125 -74
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render.ts +316 -27
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +230 -0
- package/src/workflows/scripted-workflow.ts +452 -38
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +43 -4
- package/src/workflows/workflow-settlement.ts +246 -0
- package/src/runs/shared/turn-budget.ts +0 -98
|
@@ -2,7 +2,7 @@ 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
4
|
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";
|
|
5
|
+
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 TokenUsage, type TurnBudgetState, type UsageBudgetState, type WorkflowPreflightV1 } from "../../shared/types.ts";
|
|
6
6
|
import type { ResolvedSubagentCapabilityCeiling, SubagentCapabilityAudit } from "../shared/capability-ceiling.ts";
|
|
7
7
|
import { readStatus } from "../../shared/utils.ts";
|
|
8
8
|
import { attachRootChildrenToSteps, buildNestedRouteIndex, findNestedRouteForRootId, type NestedRoute, projectNestedEvents } from "../shared/nested-events.ts";
|
|
@@ -17,16 +17,25 @@ import { readRecentTerminalRunIndex, TERMINAL_RUN_INDEX_DIR } from "./terminal-r
|
|
|
17
17
|
import { canScanAsyncRunPrefix } from "./run-id-query.ts";
|
|
18
18
|
import { asyncStatusChildIdentity } from "../shared/child-identity.ts";
|
|
19
19
|
import { parseWorkflowChildSummary } from "../../workflows/workflow-child-summary.ts";
|
|
20
|
+
import { assertWorkflowGraphHostSteps, hostStepReportName, hostStepVerdictLabel, validHostStepNodes } from "../shared/host-step-status.ts";
|
|
21
|
+
import { projectAsyncWorkflowRows } from "../shared/async-status-projection.ts";
|
|
22
|
+
import { validateAsyncStatusLaneMetadata } from "../shared/lane-metadata.ts";
|
|
23
|
+
import { formatWorkflowPreflightPlanSummary, formatWorkflowPreflightWarningSummary } from "../../workflows/workflow-preflight.ts";
|
|
20
24
|
|
|
21
25
|
interface AsyncRunStepSummary {
|
|
22
26
|
index: number;
|
|
23
27
|
childId?: string;
|
|
24
28
|
agent: string;
|
|
29
|
+
/** Human-readable display name for the child session, when derived at launch. */
|
|
30
|
+
sessionName?: string;
|
|
25
31
|
context?: ContextMode;
|
|
26
32
|
label?: string;
|
|
27
33
|
description?: string;
|
|
28
34
|
phase?: string;
|
|
29
35
|
workflowKey?: string;
|
|
36
|
+
lane?: AsyncJobStep["lane"];
|
|
37
|
+
worktreePath?: string;
|
|
38
|
+
branch?: string;
|
|
30
39
|
runId?: string;
|
|
31
40
|
outputName?: string;
|
|
32
41
|
structured?: boolean;
|
|
@@ -58,6 +67,7 @@ interface AsyncRunStepSummary {
|
|
|
58
67
|
stopped?: boolean;
|
|
59
68
|
turnBudget?: TurnBudgetState;
|
|
60
69
|
turnBudgetExceeded?: boolean;
|
|
70
|
+
toolBudgetBlocked?: boolean;
|
|
61
71
|
wrapUpRequested?: boolean;
|
|
62
72
|
acceptance?: AsyncJobStep["acceptance"];
|
|
63
73
|
agentContract?: AsyncJobStep["agentContract"];
|
|
@@ -77,7 +87,7 @@ export interface AsyncRunSummary {
|
|
|
77
87
|
asyncDir: string;
|
|
78
88
|
toolCallId?: string;
|
|
79
89
|
sessionId?: string;
|
|
80
|
-
state: "queued" | "running" | "complete" | "failed" | "paused" | "stopped" | "rejected";
|
|
90
|
+
state: "queued" | "running" | "complete" | "failed" | "partial" | "paused" | "stopped" | "rejected";
|
|
81
91
|
error?: string;
|
|
82
92
|
activityState?: ActivityState;
|
|
83
93
|
lastActivityAt?: number;
|
|
@@ -105,6 +115,7 @@ export interface AsyncRunSummary {
|
|
|
105
115
|
chainStepCount?: number;
|
|
106
116
|
pendingAppends?: number;
|
|
107
117
|
parallelGroups?: AsyncParallelGroupStatus[];
|
|
118
|
+
hostSteps?: HostStepNodeV1[];
|
|
108
119
|
steps: AsyncRunStepSummary[];
|
|
109
120
|
sessionDir?: string;
|
|
110
121
|
outputFile?: string;
|
|
@@ -122,8 +133,10 @@ export interface AsyncRunSummary {
|
|
|
122
133
|
capabilityAudit?: SubagentCapabilityAudit;
|
|
123
134
|
parentWorkflowRunId?: string;
|
|
124
135
|
workflowKey?: string;
|
|
136
|
+
lane?: AsyncStatus["lane"];
|
|
125
137
|
workflow?: Details["workflow"];
|
|
126
138
|
workflowChildren?: Details["workflowChildren"];
|
|
139
|
+
preflight?: WorkflowPreflightV1;
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
interface AsyncRunListOptions {
|
|
@@ -232,21 +245,32 @@ function deriveAsyncActivityState(asyncDir: string, status: AsyncStatus): { acti
|
|
|
232
245
|
}
|
|
233
246
|
|
|
234
247
|
function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string }, nestedWarnings: string[] = [], nestedRoute?: NestedRoute): AsyncRunSummary {
|
|
248
|
+
validateAsyncStatusLaneMetadata(status, `Invalid async status '${path.join(asyncDir, "status.json")}'`);
|
|
235
249
|
const workflowChildren = parseWorkflowChildSummary(status.workflowChildren);
|
|
236
250
|
if (workflowChildren && workflowChildren.workflowRunId !== status.runId) throw new Error(`Invalid async status '${path.join(asyncDir, "status.json")}': workflowChildren.workflowRunId does not match.`);
|
|
251
|
+
assertWorkflowGraphHostSteps(status.workflowGraph, path.join(asyncDir, "status.json"), status.runId);
|
|
252
|
+
const hostSteps = validHostStepNodes(status.workflowGraph);
|
|
237
253
|
if (status.sessionId !== undefined && typeof status.sessionId !== "string") {
|
|
238
254
|
throw new Error(`Invalid async status '${path.join(asyncDir, "status.json")}': sessionId must be a string.`);
|
|
239
255
|
}
|
|
240
256
|
const { activityState, lastActivityAt } = deriveAsyncActivityState(asyncDir, status);
|
|
241
257
|
const processTerminal = readProcessTerminal(asyncDir, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId })
|
|
242
258
|
?? sanitizeProcessTerminal(status.processTerminal, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId }, path.join(asyncDir, "status.json"));
|
|
243
|
-
const
|
|
244
|
-
|
|
259
|
+
const nestedProjectionAllowed = nestedWarnings.length === 0;
|
|
260
|
+
// Degrade to the status-stored snapshot when the persisted budget is unavailable (e.g. removed
|
|
261
|
+
// by OS temp cleanup) instead of failing the whole run list; admission paths stay strict.
|
|
262
|
+
let runFanoutBudget: AsyncStatus["runFanoutBudget"] = status.runFanoutBudget;
|
|
263
|
+
try {
|
|
264
|
+
const runFanoutBudgetDescriptor = readRunFanoutBudgetDescriptor(asyncDir);
|
|
265
|
+
if (runFanoutBudgetDescriptor) runFanoutBudget = getRunFanoutBudgetSnapshot(runFanoutBudgetDescriptor);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
nestedWarnings.push(`Run fan-out status unavailable: ${getErrorMessage(error)}`);
|
|
268
|
+
}
|
|
245
269
|
const steps = status.steps ?? [];
|
|
246
270
|
const chainStepCount = status.chainStepCount ?? steps.length;
|
|
247
271
|
const parallelGroups = normalizeParallelGroups(status.parallelGroups, steps.length, chainStepCount);
|
|
248
272
|
let nestedChildren: NestedRunSummary[] = [];
|
|
249
|
-
if (
|
|
273
|
+
if (nestedProjectionAllowed && nestedRoute) {
|
|
250
274
|
try {
|
|
251
275
|
// The route is resolved by the caller via buildNestedRouteIndex, so this
|
|
252
276
|
// avoids a fresh scan of the nested-events directory per run.
|
|
@@ -262,11 +286,15 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
262
286
|
index,
|
|
263
287
|
childId: asyncStatusChildIdentity(step, index),
|
|
264
288
|
agent: step.agent,
|
|
289
|
+
...(step.sessionName ? { sessionName: step.sessionName } : {}),
|
|
265
290
|
...(step.context ? { context: step.context } : {}),
|
|
266
291
|
...(step.label ? { label: step.label } : {}),
|
|
267
292
|
...(step.description ? { description: step.description } : {}),
|
|
268
293
|
...(step.phase ? { phase: step.phase } : {}),
|
|
269
294
|
...(step.workflowKey ? { workflowKey: step.workflowKey } : {}),
|
|
295
|
+
...(step.lane ? { lane: step.lane } : {}),
|
|
296
|
+
...(step.worktreePath ? { worktreePath: step.worktreePath } : {}),
|
|
297
|
+
...(step.branch ? { branch: step.branch } : {}),
|
|
270
298
|
...(step.runId ? { runId: step.runId } : {}),
|
|
271
299
|
...(step.outputName ? { outputName: step.outputName } : {}),
|
|
272
300
|
...(step.structured ? { structured: step.structured } : {}),
|
|
@@ -301,6 +329,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
301
329
|
...(step.stopRequestedAt !== undefined ? { stopRequestedAt: step.stopRequestedAt } : {}),
|
|
302
330
|
...(step.turnBudget ? { turnBudget: step.turnBudget } : {}),
|
|
303
331
|
...(step.turnBudgetExceeded !== undefined ? { turnBudgetExceeded: step.turnBudgetExceeded } : {}),
|
|
332
|
+
...(step.toolBudgetBlocked !== undefined ? { toolBudgetBlocked: step.toolBudgetBlocked } : {}),
|
|
304
333
|
...(step.wrapUpRequested !== undefined ? { wrapUpRequested: step.wrapUpRequested } : {}),
|
|
305
334
|
...(step.acceptance ? { acceptance: step.acceptance } : {}),
|
|
306
335
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
@@ -310,6 +339,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
310
339
|
...(step.execution ? { execution: step.execution } : {}),
|
|
311
340
|
...(step.review ? { review: step.review } : {}),
|
|
312
341
|
...(step.effects ? { effects: step.effects } : {}),
|
|
342
|
+
...(step.watchdog ? { watchdog: step.watchdog } : {}),
|
|
313
343
|
...(step.processTerminal ? { processTerminal: sanitizeProcessTerminal(step.processTerminal, { runId: status.runId, runnerProcessInstanceId: step.processTerminal.runnerProcessInstanceId }, `${path.join(asyncDir, "status.json")} step ${index}`) } : {}),
|
|
314
344
|
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
315
345
|
...(step.capabilityAudit ? { capabilityAudit: step.capabilityAudit } : {}),
|
|
@@ -350,6 +380,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
350
380
|
...(status.chainStepCount !== undefined ? { chainStepCount: status.chainStepCount } : {}),
|
|
351
381
|
...(status.pendingAppends !== undefined ? { pendingAppends: status.pendingAppends } : {}),
|
|
352
382
|
...(parallelGroups.length ? { parallelGroups } : {}),
|
|
383
|
+
...(hostSteps.length ? { hostSteps } : {}),
|
|
353
384
|
steps: summarizedSteps,
|
|
354
385
|
...(nestedChildren.length ? { nestedChildren } : {}),
|
|
355
386
|
...(nestedWarnings.length ? { nestedWarnings } : {}),
|
|
@@ -362,8 +393,10 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
362
393
|
...(status.capabilityAudit ? { capabilityAudit: status.capabilityAudit } : {}),
|
|
363
394
|
...(status.parentWorkflowRunId ? { parentWorkflowRunId: status.parentWorkflowRunId } : {}),
|
|
364
395
|
...(status.workflowKey ? { workflowKey: status.workflowKey } : {}),
|
|
396
|
+
...(status.lane ? { lane: status.lane } : {}),
|
|
365
397
|
...(status.workflow ? { workflow: status.workflow } : {}),
|
|
366
398
|
...(workflowChildren ? { workflowChildren } : {}),
|
|
399
|
+
...(status.preflight ? { preflight: status.preflight } : {}),
|
|
367
400
|
...(status.sessionDir ? { sessionDir: status.sessionDir } : {}),
|
|
368
401
|
...(status.outputFile ? { outputFile: status.outputFile } : {}),
|
|
369
402
|
...(status.totalTokens ? { totalTokens: status.totalTokens } : {}),
|
|
@@ -383,6 +416,7 @@ function sortRuns(runs: AsyncRunSummary[]): AsyncRunSummary[] {
|
|
|
383
416
|
case "running": return 0;
|
|
384
417
|
case "queued": return 1;
|
|
385
418
|
case "failed": return 2;
|
|
419
|
+
case "partial": return 2;
|
|
386
420
|
case "stopped": return 2;
|
|
387
421
|
case "paused": return 2;
|
|
388
422
|
case "complete": return 3;
|
|
@@ -511,7 +545,7 @@ function formatActivityFacts(input: { activityState?: ActivityState; lastActivit
|
|
|
511
545
|
}
|
|
512
546
|
|
|
513
547
|
function formatStepLine(step: AsyncRunStepSummary): string {
|
|
514
|
-
const display = step.label ? `${step.label} (${step.agent})` : step.agent;
|
|
548
|
+
const display = step.sessionName?.trim() || (step.label ? `${step.label} (${step.agent})` : step.agent);
|
|
515
549
|
const context = contextModeLabel(step.context);
|
|
516
550
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
517
551
|
const parts = [`${step.index + 1}. ${phase}${display}${context ? ` ${context}` : ""}`, step.status];
|
|
@@ -521,9 +555,26 @@ function formatStepLine(step: AsyncRunStepSummary): string {
|
|
|
521
555
|
if (modelThinking) parts.push(modelThinking);
|
|
522
556
|
if (step.durationMs !== undefined) parts.push(formatDuration(step.durationMs));
|
|
523
557
|
if (step.tokens) parts.push(`${formatTokens(step.tokens.total)} tok`);
|
|
558
|
+
if (step.lane) parts.push(`lane ${step.lane.key}`);
|
|
559
|
+
if (step.worktreePath) parts.push(`worktree ${shortenPath(step.worktreePath)} · branch ${step.branch ?? "unknown"}`);
|
|
524
560
|
return parts.join(" | ");
|
|
525
561
|
}
|
|
526
562
|
|
|
563
|
+
function formatHostStepLine(row: ReturnType<typeof projectAsyncWorkflowRows>[number]): string {
|
|
564
|
+
if (!row.kind) return "";
|
|
565
|
+
const state = hostStepVerdictLabel(row.state as HostStepState, row.verdict);
|
|
566
|
+
const details = [
|
|
567
|
+
row.provider ? `provider:${row.provider}` : undefined,
|
|
568
|
+
row.role ? `role:${row.role}` : undefined,
|
|
569
|
+
row.target,
|
|
570
|
+
row.detail,
|
|
571
|
+
row.reasonCode ? `reason:${row.reasonCode}` : undefined,
|
|
572
|
+
row.freshness?.stale ? "stale" : row.freshness?.observedRef ? `ref:${row.freshness.observedRef}` : undefined,
|
|
573
|
+
row.reportPath ? `out:${hostStepReportName(row.reportPath)}` : undefined,
|
|
574
|
+
].filter((value): value is string => Boolean(value));
|
|
575
|
+
return `host ${row.kind}: ${row.name} | ${state}${details.length ? ` | ${details.join(" | ")}` : ""}`;
|
|
576
|
+
}
|
|
577
|
+
|
|
527
578
|
export function formatAsyncRunOutputPath(run: Pick<AsyncRunSummary, "asyncDir" | "outputFile">): string | undefined {
|
|
528
579
|
if (!run.outputFile) return undefined;
|
|
529
580
|
return path.isAbsolute(run.outputFile) ? run.outputFile : path.join(run.asyncDir, run.outputFile);
|
|
@@ -556,7 +607,8 @@ function formatRunHeader(run: AsyncRunSummary): string {
|
|
|
556
607
|
const activity = formatActivityFacts(run);
|
|
557
608
|
const pending = run.pendingAppends ? ` | ${run.pendingAppends} pending append${run.pendingAppends === 1 ? "" : "s"}` : "";
|
|
558
609
|
const context = contextModeLabel(run.context);
|
|
559
|
-
|
|
610
|
+
const lane = run.lane ? ` | lane ${run.lane.key}` : "";
|
|
611
|
+
return `${run.id} | ${run.state}${activity ? ` | ${activity}` : ""} | ${run.mode}${context ? ` ${context}` : ""} | ${stepLabel}${pending}${lane} | ${cwd}`;
|
|
560
612
|
}
|
|
561
613
|
|
|
562
614
|
export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active async runs"): string {
|
|
@@ -565,10 +617,17 @@ export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active as
|
|
|
565
617
|
const lines = [`${heading}: ${runs.length}`, ""];
|
|
566
618
|
for (const run of runs) {
|
|
567
619
|
lines.push(`- ${formatRunHeader(run)}`);
|
|
620
|
+
if (run.preflight) lines.push(formatWorkflowPreflightPlanSummary(run.preflight, { indent: " " }));
|
|
621
|
+
const preflightWarning = formatWorkflowPreflightWarningSummary(run.workflow?.preflightWarnings, { indent: " " });
|
|
622
|
+
if (preflightWarning) lines.push(preflightWarning);
|
|
568
623
|
for (const step of run.steps) {
|
|
569
624
|
lines.push(` ${formatStepLine(step)}`);
|
|
570
625
|
lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", maxLines: 12 }));
|
|
571
626
|
}
|
|
627
|
+
for (const row of projectAsyncWorkflowRows([], run.hostSteps)) {
|
|
628
|
+
const line = formatHostStepLine(row);
|
|
629
|
+
if (line) lines.push(` ${line}`);
|
|
630
|
+
}
|
|
572
631
|
const attached = new Set(run.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
573
632
|
const unattached = run.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
574
633
|
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();
|
|
@@ -72,6 +72,14 @@ function uniqueStrings(values: Array<string | undefined>): string[] {
|
|
|
72
72
|
return result;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
function fleetChildDisplayName(child: { agent?: string; sessionName?: string }, fallback = "subagent"): string {
|
|
76
|
+
return child.sessionName?.trim() || child.agent || fallback;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function fleetStepDisplayName(step: Pick<AsyncJobStep, "agent" | "sessionName" | "label">): string {
|
|
80
|
+
return step.sessionName?.trim() || (step.label ? `${step.label} (${step.agent})` : step.agent);
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
function resolveMaybeRelative(asyncDir: string, filePath: string | undefined): string | undefined {
|
|
76
84
|
if (!filePath) return undefined;
|
|
77
85
|
return path.resolve(asyncDir, filePath);
|
|
@@ -283,7 +291,8 @@ function formatActivityFacts(input: {
|
|
|
283
291
|
}
|
|
284
292
|
|
|
285
293
|
function foregroundModeName(control: ForegroundControl): string {
|
|
286
|
-
|
|
294
|
+
const currentDisplayName = control.sessionName?.trim() || control.currentAgent;
|
|
295
|
+
if (control.mode === "single" && currentDisplayName) return currentDisplayName;
|
|
287
296
|
return control.mode;
|
|
288
297
|
}
|
|
289
298
|
|
|
@@ -302,7 +311,8 @@ function formatForegroundFleetLines(controls: ForegroundControl[]): string[] {
|
|
|
302
311
|
toolCount: control.toolCount,
|
|
303
312
|
...(control.tokens !== undefined ? { tokens: { total: control.tokens } } : {}),
|
|
304
313
|
});
|
|
305
|
-
const
|
|
314
|
+
const currentDisplayName = control.sessionName?.trim() || control.currentAgent;
|
|
315
|
+
const current = currentDisplayName ? ` | ${currentDisplayName}${control.currentIndex !== undefined ? ` #${control.currentIndex}` : ""}` : "";
|
|
306
316
|
lines.push(`- ${control.runId} | running | ${foregroundModeName(control)}${current}${activity ? ` | ${activity}` : ""}`);
|
|
307
317
|
lines.push(` status: subagent({ action: "status", id: "${control.runId}" })`);
|
|
308
318
|
lines.push(" transcript: live in the expanded foreground result; persisted session transcript appears after completion when sessions are enabled.");
|
|
@@ -317,7 +327,7 @@ function formatDetachedForegroundFleetLines(runs: ForegroundRun[]): string[] {
|
|
|
317
327
|
const ordered = [...runs].sort((left, right) => right.updatedAt - left.updatedAt);
|
|
318
328
|
for (const run of ordered) {
|
|
319
329
|
const detachedChildren = run.children.filter((child) => child.status === "detached");
|
|
320
|
-
const childSummary = detachedChildren.map((child) => `${child
|
|
330
|
+
const childSummary = detachedChildren.map((child) => `${fleetChildDisplayName(child)} #${child.index}`).join(", ");
|
|
321
331
|
lines.push(`- ${run.runId} | detached | ${run.mode}${childSummary ? ` | ${childSummary}` : ""}`);
|
|
322
332
|
lines.push(` status: subagent({ action: "status", id: "${run.runId}" })`);
|
|
323
333
|
lines.push(` recovery: reply to the supervisor request first, then wait with subagent_wait({ id: "${run.runId}" }); do not resume or launch a replacement while any child remains detached.`);
|
|
@@ -338,7 +348,7 @@ function formatAsyncFleetLines(runs: AsyncRunSummary[]): string[] {
|
|
|
338
348
|
lines.push(` status: subagent({ action: "status", id: "${run.id}" })`);
|
|
339
349
|
lines.push(` transcript: subagent({ action: "status", id: "${run.id}", view: "transcript" })`);
|
|
340
350
|
for (const step of run.steps) {
|
|
341
|
-
const display =
|
|
351
|
+
const display = fleetStepDisplayName(step);
|
|
342
352
|
const stepContext = contextModeLabel(step.context);
|
|
343
353
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
344
354
|
const stepActivity = formatActivityFacts(step);
|
|
@@ -435,7 +445,7 @@ function selectTranscriptStep(status: AsyncStatus, options: TranscriptOptions):
|
|
|
435
445
|
}
|
|
436
446
|
const step = selectedIndex !== undefined ? steps[selectedIndex] : undefined;
|
|
437
447
|
const hint = options.index === undefined && steps.length > 1
|
|
438
|
-
? `Tip: pass index to inspect a specific child transcript (${steps.map((candidate, index) => `${index}=${candidate
|
|
448
|
+
? `Tip: pass index to inspect a specific child transcript (${steps.map((candidate, index) => `${index}=${fleetChildDisplayName(candidate)}`).join(", ")}).`
|
|
439
449
|
: undefined;
|
|
440
450
|
return { index: selectedIndex, step, hint };
|
|
441
451
|
}
|
|
@@ -445,7 +455,7 @@ function stepStateLine(mode: SubagentRunMode, index: number | undefined, step: A
|
|
|
445
455
|
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
446
456
|
const context = contextModeLabel(step.context);
|
|
447
457
|
const parts = [
|
|
448
|
-
`${mode === "parallel" ? "Agent" : "Step"}: ${index} (${step
|
|
458
|
+
`${mode === "parallel" ? "Agent" : "Step"}: ${index} (${fleetChildDisplayName(step)})${context ? ` ${context}` : ""}`,
|
|
449
459
|
step.status,
|
|
450
460
|
formatActivityFacts(step),
|
|
451
461
|
modelThinking,
|
|
@@ -553,7 +563,7 @@ export function formatNestedRunTranscript(run: NestedRunSummary, options: Transc
|
|
|
553
563
|
`Nested run: ${run.id}`,
|
|
554
564
|
`State: ${run.state}`,
|
|
555
565
|
run.mode ? `Mode: ${run.mode}` : undefined,
|
|
556
|
-
run.agent ? `Agent: ${run.agent}` : run.agents?.length ? `Agents: ${run.agents.join(", ")}` : undefined,
|
|
566
|
+
run.sessionName?.trim() ? `Agent: ${run.sessionName.trim()}` : run.agent ? `Agent: ${run.agent}` : run.agents?.length ? `Agents: ${run.agents.join(", ")}` : undefined,
|
|
557
567
|
].filter((line): line is string => Boolean(line));
|
|
558
568
|
appendKnownArtifacts(lines, { outputPaths: [], sessionFile: run.sessionFile });
|
|
559
569
|
if (!run.sessionFile) {
|
|
@@ -579,14 +589,14 @@ export function formatAsyncResultTranscript(data: {
|
|
|
579
589
|
sessionFile?: string;
|
|
580
590
|
agent?: string;
|
|
581
591
|
exitCode?: number | null;
|
|
582
|
-
results?: Array<{ agent?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null }>;
|
|
592
|
+
results?: Array<{ agent?: string; sessionName?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null }>;
|
|
583
593
|
}, resultPath: string, options: TranscriptOptions = {}): string {
|
|
584
594
|
const lineLimit = transcriptLineLimit(options.lines);
|
|
585
595
|
const runId = data.runId ?? data.id ?? path.basename(resultPath, ".json");
|
|
586
596
|
const children = Array.isArray(data.results)
|
|
587
597
|
? data.results
|
|
588
598
|
: data.agent
|
|
589
|
-
? [{ agent: data.agent, output: data.output, summary: data.summary, sessionFile: data.sessionFile, state: data.state, success: data.success, exitCode: data.exitCode }]
|
|
599
|
+
? [{ agent: data.agent, sessionName: undefined, output: data.output, summary: data.summary, sessionFile: data.sessionFile, state: data.state, success: data.success, exitCode: data.exitCode }]
|
|
590
600
|
: [];
|
|
591
601
|
let index = options.index;
|
|
592
602
|
if (index !== undefined && !Number.isInteger(index)) throw new Error("Transcript index must be an integer.");
|
|
@@ -601,8 +611,8 @@ export function formatAsyncResultTranscript(data: {
|
|
|
601
611
|
const lines = [
|
|
602
612
|
`Run: ${runId}`,
|
|
603
613
|
`State: ${data.state ?? (data.success ? "complete" : "failed")}`,
|
|
604
|
-
index !== undefined && child ? `Child: ${index} (${child
|
|
605
|
-
index === undefined && children.length > 1 ? `Tip: pass index to inspect a specific child transcript (${children.map((candidate, childIndex) => `${childIndex}=${candidate
|
|
614
|
+
index !== undefined && child ? `Child: ${index} (${fleetChildDisplayName(child)})` : undefined,
|
|
615
|
+
index === undefined && children.length > 1 ? `Tip: pass index to inspect a specific child transcript (${children.map((candidate, childIndex) => `${childIndex}=${fleetChildDisplayName(candidate)}`).join(", ")}).` : undefined,
|
|
606
616
|
].filter((line): line is string => Boolean(line));
|
|
607
617
|
appendKnownArtifacts(lines, { outputPaths: [], sessionFile, resultPath });
|
|
608
618
|
appendTranscriptBody(lines, "Result transcript tail", transcriptLines.filter((line) => line.trim()), output.split(/\r?\n/).length > lineLimit);
|