pi-subagents 0.65.1 → 0.67.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 +123 -0
- package/README.md +5 -4
- package/agents/evidence-auditor.md +34 -0
- package/agents/researcher.md +23 -13
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +20 -3
- package/docs/configuration.md +25 -5
- package/docs/extension-api.md +124 -18
- package/docs/missions.md +8 -0
- package/docs/models.md +59 -2
- package/docs/observability.md +46 -6
- package/docs/standalone-background.md +49 -0
- package/docs/tool-reference.md +20 -10
- package/docs/watchdog.md +35 -4
- package/docs/workflows.md +40 -19
- package/inspector-runner.mjs +2 -2
- package/package.json +2 -1
- package/prompts/parallel-review.md +1 -1
- package/{runner-server-preload.mjs → runner-peer-preload.mjs} +8 -3
- package/skills/pi-subagents/SKILL.md +14 -0
- package/skills/pi-subagents/references/execution-controls.md +20 -5
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
- package/src/agents/advertised-agent-prompt.ts +94 -0
- package/src/agents/agent-management.ts +14 -1
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +14 -0
- package/src/agents/builtin-names.ts +1 -0
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +76 -45
- package/src/api/shared-types.ts +3 -1
- package/src/api/workflow-resources.ts +6 -0
- package/src/extension/fanout-child.ts +63 -4
- package/src/extension/index.ts +58 -8
- package/src/extension/public-execution.ts +4 -3
- package/src/extension/rpc.ts +8 -21
- package/src/extension/schemas.ts +71 -80
- package/src/extension/tool-description.ts +29 -81
- package/src/inspectors/actions.ts +148 -0
- package/src/inspectors/ghostty/actions.ts +74 -0
- package/src/inspectors/ghostty/plugin.ts +17 -0
- package/src/inspectors/herdr/actions.ts +99 -179
- package/src/inspectors/herdr/plugin.ts +20 -0
- package/src/inspectors/herdr/project-panes.ts +1 -1
- package/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +12 -12
- package/src/inspectors/plugins.ts +8 -0
- package/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +3 -14
- package/src/inspectors/types.ts +51 -0
- package/src/intercom/intercom-bridge.ts +50 -8
- package/src/intercom/native-supervisor-channel.ts +104 -67
- package/src/runs/background/active-async-capacity.ts +22 -18
- package/src/runs/background/async-execution.ts +45 -56
- package/src/runs/background/async-job-tracker.ts +35 -3
- package/src/runs/background/async-resume.ts +5 -9
- package/src/runs/background/async-status-snapshot.ts +10 -12
- package/src/runs/background/async-status.ts +17 -9
- package/src/runs/background/auto-drain.ts +44 -30
- package/src/runs/background/binary-bootstrap.ts +33 -0
- package/src/runs/background/chain-root-attachment.ts +8 -0
- package/src/runs/background/control-channel.ts +78 -44
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +117 -13
- package/src/runs/background/owned-process-tree.ts +35 -8
- package/src/runs/background/process-terminal.ts +23 -23
- package/src/runs/background/run-child-session.ts +121 -36
- package/src/runs/background/run-status.ts +78 -5
- package/src/runs/background/runner-aliases.ts +28 -9
- package/src/runs/background/runner-child-launch.ts +88 -0
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/scheduled-runs.ts +40 -13
- package/src/runs/background/stale-run-reconciler.ts +3 -1
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +458 -239
- package/src/runs/background/subagent-wait.ts +54 -8
- package/src/runs/background/wait-completions.ts +4 -0
- package/src/runs/background/wait-tool.ts +1 -1
- package/src/runs/foreground/async-steering-action.ts +37 -7
- package/src/runs/foreground/execution.ts +145 -56
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +584 -297
- package/src/runs/foreground/workflow-detach-reconcile.ts +10 -5
- package/src/runs/foreground/workflow-foreground-steering.ts +57 -2
- package/src/runs/shared/acceptance.ts +7 -4
- package/src/runs/shared/agent-contract.ts +1 -1
- package/src/runs/shared/async-status-projection.ts +51 -47
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +167 -3
- package/src/runs/shared/child-launch.ts +28 -13
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +3 -1
- package/src/runs/shared/child-session.ts +75 -8
- package/src/runs/shared/child-tool-plan.ts +124 -5
- package/src/runs/shared/completion-evidence.ts +2 -2
- package/src/runs/shared/completion-guard.ts +6 -3
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/host-step-status.ts +11 -11
- package/src/runs/shared/llm-intent-arbiter.ts +21 -11
- package/src/runs/shared/model-fallback.ts +12 -6
- package/src/runs/shared/nested-events.ts +5 -5
- package/src/runs/shared/orca-progress-tabs.ts +7 -1
- package/src/runs/shared/parallel-handoff.ts +57 -12
- package/src/runs/shared/parallel-utils.ts +2 -2
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/readonly-drain-observation.ts +42 -0
- package/src/runs/shared/readonly-model-continuation.ts +69 -0
- package/src/runs/shared/readonly-session-evidence.ts +307 -0
- package/src/runs/shared/run-fanout-budget.ts +8 -8
- package/src/runs/shared/runtime-acknowledged-extensions.ts +3 -3
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -4
- package/src/runs/shared/task-intent.ts +46 -13
- package/src/runs/shared/workflow-async-child-guidance.ts +18 -0
- package/src/runs/shared/worktree-setup-command.ts +190 -0
- package/src/runs/shared/worktree.ts +366 -208
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +65 -2
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/types.ts +85 -61
- package/src/shared/utils.ts +7 -2
- package/src/shared/workflow-child-permit.ts +18 -13
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -1
- package/src/slash/subagents-admin.ts +11 -4
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +27 -19
- package/src/tui/render.ts +172 -33
- package/src/watchdog/child-status.ts +8 -0
- package/src/watchdog/model-selection.ts +20 -0
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +1 -0
- package/src/watchdog/register-main.ts +31 -27
- package/src/watchdog/review.ts +132 -67
- package/src/watchdog/runtime.ts +82 -20
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +9 -3
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +4 -0
- package/src/workflows/chat-progress.ts +3 -3
- package/src/workflows/scripted-workflow.ts +275 -17
- package/src/workflows/workflow-checklist.ts +13 -17
- package/src/workflows/workflow-child-summary.ts +57 -8
- package/src/workflows/workflow-preflight.ts +19 -19
- package/src/workflows/workflow-receipt.ts +3 -3
- package/src/workflows/workflow-resources.ts +96 -21
- package/src/workflows/workflow-settlement.ts +3 -0
- /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
package/src/tui/fleet-status.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { contextModeLabel } from "../runs/shared/context-mode.ts";
|
|
|
8
8
|
import { formatWorkflowJsonPreview } from "../workflows/scripted-workflow.ts";
|
|
9
9
|
import { hostStepReportName, hostStepVerdictLabel } from "../runs/shared/host-step-status.ts";
|
|
10
10
|
import { isStaleExtensionContextError } from "../shared/extension-context.ts";
|
|
11
|
+
import { inlineWorkflowRenderKey } from "./render.ts";
|
|
11
12
|
import { formatWorkflowChecklistBottleneck, formatWorkflowChecklistPhase, formatWorkflowChecklistSummary, projectWorkflowChecklist, type WorkflowChecklistPhase, type WorkflowChecklistProjection } from "../workflows/workflow-checklist.ts";
|
|
12
13
|
|
|
13
14
|
export const FLEET_STATUS_WIDGET_KEY = "subagent-fleet-status";
|
|
@@ -17,6 +18,35 @@ const MAX_AGENT_ROWS = 6;
|
|
|
17
18
|
const REFRESH_MS = 500;
|
|
18
19
|
|
|
19
20
|
type Theme = ExtensionContext["ui"]["theme"];
|
|
21
|
+
|
|
22
|
+
const FLEET_AGENT_IDENTITY_COLORS = [
|
|
23
|
+
"mdLink",
|
|
24
|
+
"mdHeading",
|
|
25
|
+
"syntaxFunction",
|
|
26
|
+
"syntaxKeyword",
|
|
27
|
+
"syntaxNumber",
|
|
28
|
+
"syntaxType",
|
|
29
|
+
"syntaxVariable",
|
|
30
|
+
"customMessageLabel",
|
|
31
|
+
"toolTitle",
|
|
32
|
+
"thinkingMedium",
|
|
33
|
+
"thinkingHigh",
|
|
34
|
+
"mdQuote",
|
|
35
|
+
"bashMode",
|
|
36
|
+
"userMessageText",
|
|
37
|
+
"mdCode",
|
|
38
|
+
"syntaxOperator",
|
|
39
|
+
] as const satisfies readonly Exclude<Parameters<Theme["fg"]>[0], "accent" | "success" | "error" | "warning" | "muted" | "dim">[];
|
|
40
|
+
|
|
41
|
+
export function fleetAgentIdentityColor(identity: string): (typeof FLEET_AGENT_IDENTITY_COLORS)[number] {
|
|
42
|
+
let hash = 2166136261;
|
|
43
|
+
for (let i = 0; i < identity.length; i++) {
|
|
44
|
+
hash ^= identity.charCodeAt(i);
|
|
45
|
+
hash = Math.imul(hash, 16777619);
|
|
46
|
+
}
|
|
47
|
+
return FLEET_AGENT_IDENTITY_COLORS[(hash >>> 0) % FLEET_AGENT_IDENTITY_COLORS.length]!;
|
|
48
|
+
}
|
|
49
|
+
|
|
20
50
|
type FleetStatusTui = {
|
|
21
51
|
requestRender(): void;
|
|
22
52
|
};
|
|
@@ -26,6 +56,7 @@ type FleetStatusEntry = {
|
|
|
26
56
|
parentKey?: string;
|
|
27
57
|
workflowWrapper?: boolean;
|
|
28
58
|
agent: string;
|
|
59
|
+
displayLabel?: string;
|
|
29
60
|
modelThinking?: string;
|
|
30
61
|
description?: string;
|
|
31
62
|
startedAt: number;
|
|
@@ -41,10 +72,12 @@ type FleetStatusEntry = {
|
|
|
41
72
|
|
|
42
73
|
type FleetNestedRow = {
|
|
43
74
|
name: string;
|
|
75
|
+
agentIdentity?: string;
|
|
44
76
|
state: NestedRunSummary["state"] | NestedStepSummary["status"];
|
|
45
77
|
modelThinking?: string;
|
|
46
78
|
activity?: string;
|
|
47
79
|
startedAt?: number;
|
|
80
|
+
endedAt?: number;
|
|
48
81
|
depth: number;
|
|
49
82
|
overflow?: number;
|
|
50
83
|
};
|
|
@@ -60,6 +93,7 @@ export interface FleetStatusOptions {
|
|
|
60
93
|
refreshMs?: number;
|
|
61
94
|
maxAgentRows?: number;
|
|
62
95
|
placement?: FleetViewPlacement;
|
|
96
|
+
onWorkflowCoverageChange?: (ui: ExtensionContext["ui"], coverage: ReadonlyMap<string, string>) => void;
|
|
63
97
|
}
|
|
64
98
|
|
|
65
99
|
export function resolveFleetViewPlacement(value: unknown): FleetViewPlacement {
|
|
@@ -70,14 +104,21 @@ export function formatFleetElapsed(ms: number): string {
|
|
|
70
104
|
return `${Math.max(0, Math.round(ms / 1000))}s`;
|
|
71
105
|
}
|
|
72
106
|
|
|
73
|
-
|
|
107
|
+
function detailElapsed(row: Pick<AsyncStatusWorkflowRow, "startedAt" | "endedAt" | "durationMs"> & { state: string }): string | undefined {
|
|
108
|
+
const duration = row.state === "running" && row.startedAt !== undefined
|
|
109
|
+
? Date.now() - row.startedAt
|
|
110
|
+
: row.durationMs ?? (row.startedAt !== undefined && row.endedAt !== undefined ? row.endedAt - row.startedAt : undefined);
|
|
111
|
+
return duration !== undefined ? formatFleetElapsed(duration) : undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function formatFleetTokens(count: number, window?: number, windowCount = 1): string {
|
|
74
115
|
const compact = (value: number): string => value >= 1_000_000
|
|
75
116
|
? `${(value / 1_000_000).toFixed(1)}M`
|
|
76
117
|
: value >= 1_000
|
|
77
118
|
? `${(value / 1_000).toFixed(1)}k`
|
|
78
119
|
: `${Math.max(0, Math.round(value))}`;
|
|
79
120
|
return window !== undefined
|
|
80
|
-
? `↓ ${compact(window)} window · ${compact(count)} spent`
|
|
121
|
+
? `↓ ${compact(window)} ${windowCount > 1 ? "Σ windows" : "window"} · ${compact(count)} spent`
|
|
81
122
|
: `↓ ${compact(count)} tokens`;
|
|
82
123
|
}
|
|
83
124
|
|
|
@@ -184,11 +225,13 @@ function nestedFleetRows(children: NestedRunSummary[] | undefined, visibleLimit:
|
|
|
184
225
|
const activity = nestedActivity(step);
|
|
185
226
|
rows.push({
|
|
186
227
|
name: step.agent,
|
|
228
|
+
agentIdentity: step.agent,
|
|
187
229
|
state: step.status,
|
|
188
230
|
depth,
|
|
189
231
|
...(modelThinking ? { modelThinking } : {}),
|
|
190
232
|
...(activity ? { activity } : {}),
|
|
191
233
|
...(step.startedAt !== undefined ? { startedAt: step.startedAt } : {}),
|
|
234
|
+
...(step.endedAt !== undefined ? { endedAt: step.endedAt } : {}),
|
|
192
235
|
});
|
|
193
236
|
if (!appendRuns(step.children, depth + 1)) {
|
|
194
237
|
omitted += nestedStepDisplayCount(steps, stepIndex + 1);
|
|
@@ -206,11 +249,13 @@ function nestedFleetRows(children: NestedRunSummary[] | undefined, visibleLimit:
|
|
|
206
249
|
const activity = nestedActivity(child);
|
|
207
250
|
rows.push({
|
|
208
251
|
name: nestedRunLabel(child),
|
|
252
|
+
agentIdentity: child.agent ?? child.agents?.join("\0") ?? child.id,
|
|
209
253
|
state: child.state,
|
|
210
254
|
depth,
|
|
211
255
|
...(modelThinking ? { modelThinking } : {}),
|
|
212
256
|
...(activity ? { activity } : {}),
|
|
213
257
|
...(child.startedAt !== undefined ? { startedAt: child.startedAt } : {}),
|
|
258
|
+
...(child.endedAt !== undefined ? { endedAt: child.endedAt } : {}),
|
|
214
259
|
});
|
|
215
260
|
}
|
|
216
261
|
if (!appendRuns(child.children, depth + 1)) {
|
|
@@ -320,6 +365,7 @@ function projectPaneEntries(state: SubagentState): FleetStatusEntry[] {
|
|
|
320
365
|
}
|
|
321
366
|
|
|
322
367
|
export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntry[] {
|
|
368
|
+
const now = Date.now();
|
|
323
369
|
const entries: FleetStatusEntry[] = [];
|
|
324
370
|
const activeWorkflowKeys = new Set([...state.asyncJobs.values()]
|
|
325
371
|
.filter((job) => job.mode === "workflow" && isActiveState(job.status))
|
|
@@ -380,7 +426,7 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
380
426
|
|
|
381
427
|
for (const job of state.asyncJobs.values()) {
|
|
382
428
|
if (!isActiveState(job.status)) continue;
|
|
383
|
-
const startedAt = job.startedAt ?? job.updatedAt ??
|
|
429
|
+
const startedAt = job.startedAt ?? job.updatedAt ?? now;
|
|
384
430
|
const linkedParentKey = linkedWorkflowParentKey(job.parentWorkflowRunId, activeWorkflowKeys);
|
|
385
431
|
if (job.mode === "workflow") {
|
|
386
432
|
const latestEmit = job.workflow?.emits?.length ? formatWorkflowJsonPreview(job.workflow.emits.at(-1), 120) : undefined;
|
|
@@ -392,7 +438,7 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
392
438
|
hostSteps: job.hostSteps,
|
|
393
439
|
preflight: job.preflight,
|
|
394
440
|
trace: job.workflow?.trace,
|
|
395
|
-
now
|
|
441
|
+
now,
|
|
396
442
|
});
|
|
397
443
|
entries.push({
|
|
398
444
|
key: `async:${job.asyncId}`,
|
|
@@ -439,7 +485,8 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
439
485
|
entries.push({
|
|
440
486
|
key: `async:${job.asyncId}:${index}`,
|
|
441
487
|
...(linkedParentKey ? { parentKey: linkedParentKey } : {}),
|
|
442
|
-
agent: step.
|
|
488
|
+
agent: step.agent,
|
|
489
|
+
...(step.label ? { displayLabel: `${step.label} (${step.agent})` } : {}),
|
|
443
490
|
...(modelThinking ? { modelThinking } : {}),
|
|
444
491
|
description: step.description ?? job.description,
|
|
445
492
|
startedAt: step.startedAt ?? startedAt,
|
|
@@ -492,6 +539,8 @@ export class SubagentFleetStatus {
|
|
|
492
539
|
private inspectorOpen = false;
|
|
493
540
|
private lastRenderKey = "";
|
|
494
541
|
private entries: FleetStatusEntry[] = [];
|
|
542
|
+
private workflowSnapshots = new Map<string, { snapshot: string; childRows: Set<string> }>();
|
|
543
|
+
private readonly onWorkflowCoverageChange: FleetStatusOptions["onWorkflowCoverageChange"];
|
|
495
544
|
private readonly state: SubagentState;
|
|
496
545
|
private readonly openInspector: (itemKey: string) => Promise<void> | void;
|
|
497
546
|
private readonly refreshMs: number;
|
|
@@ -508,10 +557,14 @@ export class SubagentFleetStatus {
|
|
|
508
557
|
this.refreshMs = options.refreshMs ?? REFRESH_MS;
|
|
509
558
|
this.maxAgentRows = options.maxAgentRows ?? MAX_AGENT_ROWS;
|
|
510
559
|
this.placement = options.placement ?? "belowEditor";
|
|
560
|
+
this.onWorkflowCoverageChange = options.onWorkflowCoverageChange;
|
|
511
561
|
}
|
|
512
562
|
|
|
513
563
|
setContext(ctx: ExtensionContext): void {
|
|
514
|
-
if (!ctx.hasUI)
|
|
564
|
+
if (!ctx.hasUI) {
|
|
565
|
+
this.clearUiRegistration();
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
515
568
|
const ui = ctx.ui;
|
|
516
569
|
if (this.ui === ui) {
|
|
517
570
|
this.ctx = ctx;
|
|
@@ -548,6 +601,52 @@ export class SubagentFleetStatus {
|
|
|
548
601
|
return;
|
|
549
602
|
}
|
|
550
603
|
this.entries = collectFleetStatusEntries(this.state);
|
|
604
|
+
this.workflowSnapshots.clear();
|
|
605
|
+
if (this.active && !this.inspectorOpen && !this.state.fleetInspectorOpen && this.onWorkflowCoverageChange) {
|
|
606
|
+
const childrenByParent = new Map<string, AsyncJobState[]>();
|
|
607
|
+
for (const child of this.state.asyncJobs.values()) {
|
|
608
|
+
if (!child.parentWorkflowRunId) continue;
|
|
609
|
+
const children = childrenByParent.get(child.parentWorkflowRunId) ?? [];
|
|
610
|
+
children.push(child);
|
|
611
|
+
childrenByParent.set(child.parentWorkflowRunId, children);
|
|
612
|
+
}
|
|
613
|
+
const attachedByParent = new Map<string, FleetStatusEntry[]>();
|
|
614
|
+
for (const entry of this.entries) {
|
|
615
|
+
if (!entry.parentKey) continue;
|
|
616
|
+
const attached = attachedByParent.get(entry.parentKey) ?? [];
|
|
617
|
+
attached.push(entry);
|
|
618
|
+
attachedByParent.set(entry.parentKey, attached);
|
|
619
|
+
}
|
|
620
|
+
for (const job of this.state.asyncJobs.values()) {
|
|
621
|
+
// Fleet does not expand attached workflow wrappers or step descendants.
|
|
622
|
+
// Unknown/unsupported coverage deliberately retains the async tree.
|
|
623
|
+
if (job.mode !== "workflow" || !isActiveState(job.status) || job.parentWorkflowRunId
|
|
624
|
+
|| job.nestedChildren?.length || job.steps?.some((step) => step.children?.length)) continue;
|
|
625
|
+
const key = `async:${job.asyncId}`;
|
|
626
|
+
const children = childrenByParent.get(job.asyncId) ?? [];
|
|
627
|
+
const attached = attachedByParent.get(key) ?? [];
|
|
628
|
+
if (children.length && job.status !== "running") continue;
|
|
629
|
+
// Even without workflow rows, these children plus their owner cannot fit.
|
|
630
|
+
if (attached.length >= this.maxAgentRows) continue;
|
|
631
|
+
const rowKeys = new Set<string>();
|
|
632
|
+
let unsupported = false;
|
|
633
|
+
for (const child of children) {
|
|
634
|
+
if (!isActiveState(child.status) || (child.mode !== "single" && child.mode !== "parallel" && child.mode !== "chain")
|
|
635
|
+
|| child.hostSteps?.length || child.workflowGraph
|
|
636
|
+
|| childrenByParent.has(child.asyncId) || child.nestedChildren?.length
|
|
637
|
+
|| child.steps?.some((step) => step.children?.length || step.runner || !isActiveState(step.status))) {
|
|
638
|
+
unsupported = true; break;
|
|
639
|
+
}
|
|
640
|
+
const count = child.steps?.length || child.agents?.length || 0;
|
|
641
|
+
if (!count || (child.stepsTotal ?? 0) > count || (child.chainStepCount ?? 0) > count) {
|
|
642
|
+
unsupported = true; break;
|
|
643
|
+
}
|
|
644
|
+
for (let index = 0; index < count; index++) rowKeys.add(`async:${child.asyncId}:${child.steps?.[index]?.index ?? index}`);
|
|
645
|
+
}
|
|
646
|
+
if (unsupported || attached.length !== rowKeys.size || attached.some((entry) => !rowKeys.has(entry.key))) continue;
|
|
647
|
+
this.workflowSnapshots.set(key, { snapshot: inlineWorkflowRenderKey(job, children), childRows: rowKeys });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
551
650
|
this.clampSelection();
|
|
552
651
|
if (this.inspectorOpen || this.state.fleetInspectorOpen) {
|
|
553
652
|
this.lastRenderKey = "";
|
|
@@ -563,6 +662,7 @@ export class SubagentFleetStatus {
|
|
|
563
662
|
}
|
|
564
663
|
|
|
565
664
|
const renderKey = this.getRenderKey();
|
|
665
|
+
if (!this.active || renderKey !== this.lastRenderKey) this.clearWorkflowCoverage();
|
|
566
666
|
if (!this.widgetRegistered) {
|
|
567
667
|
ctx.ui.setWidget(FLEET_STATUS_WIDGET_KEY, (tui, theme) => {
|
|
568
668
|
this.tui = tui;
|
|
@@ -573,6 +673,7 @@ export class SubagentFleetStatus {
|
|
|
573
673
|
},
|
|
574
674
|
dispose: () => {
|
|
575
675
|
if (this.tui !== tui) return;
|
|
676
|
+
this.clearWorkflowCoverage();
|
|
576
677
|
this.widgetRegistered = false;
|
|
577
678
|
this.tui = undefined;
|
|
578
679
|
},
|
|
@@ -654,17 +755,24 @@ export class SubagentFleetStatus {
|
|
|
654
755
|
}
|
|
655
756
|
|
|
656
757
|
render(width: number, theme: Theme): string[] {
|
|
657
|
-
if (!this.hasInlineSurface())
|
|
758
|
+
if (!this.hasInlineSurface() || this.state.widgetsSuspended || this.inspectorOpen || this.state.fleetInspectorOpen) {
|
|
759
|
+
this.clearWorkflowCoverage();
|
|
760
|
+
return [];
|
|
761
|
+
}
|
|
658
762
|
if (!this.active) {
|
|
763
|
+
this.clearWorkflowCoverage();
|
|
659
764
|
const workEntries = this.entries.filter((entry) => !entry.surface);
|
|
660
765
|
const projectEntries = this.entries.filter((entry) => entry.surface === "project-pane");
|
|
661
|
-
|
|
662
|
-
|
|
766
|
+
// Workflow totals can overlap child usage and omit live lanes. Do not
|
|
767
|
+
// present either wrapper totals or an active-only sum as workflow spend.
|
|
768
|
+
const hasWorkflow = workEntries.some((entry) => entry.workflowWrapper);
|
|
769
|
+
const nativeEntries = workEntries.filter((entry) => !entry.external && !entry.workflowWrapper && !entry.parentKey);
|
|
770
|
+
const tokens = nativeEntries.reduce((total, entry) => total + entry.tokens, 0);
|
|
663
771
|
const window = nativeEntries.length > 0 && nativeEntries.every((entry) => entry.window !== undefined)
|
|
664
772
|
? nativeEntries.reduce((total, entry) => total + entry.window!, 0)
|
|
665
773
|
: undefined;
|
|
666
774
|
const capacity = this.state.activeAsyncCapacity;
|
|
667
|
-
const hasNativeRows =
|
|
775
|
+
const hasNativeRows = nativeEntries.length > 0 || hasWorkflow;
|
|
668
776
|
const showNativeSummary = hasNativeRows || Boolean(capacity?.used);
|
|
669
777
|
const asyncRuns = capacity && showNativeSummary && (capacity.used > 0 || capacity.limit > 0) ? `Async runs ${capacity.used}/${capacity.limit || "∞"}` : "";
|
|
670
778
|
const activeEntries = activeLeafAgentCount(workEntries);
|
|
@@ -673,7 +781,11 @@ export class SubagentFleetStatus {
|
|
|
673
781
|
const paneAttention = projectEntries.filter((entry) => entry.projectPane && projectPaneNeedsAttention(entry.projectPane)).length;
|
|
674
782
|
const panes = projectEntries.length > 0 ? `${projectEntries.length} pane${projectEntries.length === 1 ? "" : "s"}${paneAttention ? ` (${paneAttention} ⚠)` : ""}` : "";
|
|
675
783
|
const label = [agents, asyncRuns, panes].filter(Boolean).join(" · ");
|
|
676
|
-
const
|
|
784
|
+
const nativeUsage = formatFleetTokens(tokens, window, nativeEntries.length);
|
|
785
|
+
const usage = hasWorkflow
|
|
786
|
+
? nativeEntries.length > 0 ? `standalone: ${nativeUsage} · workflow usage on child rows` : "usage on child rows"
|
|
787
|
+
: nativeUsage;
|
|
788
|
+
const detail = [showNativeSummary ? usage : undefined, "↓/← to inspect"].filter(Boolean).join(" · ");
|
|
677
789
|
return [truncateToWidth(` ${theme.fg("muted", label)}${label && detail ? " · " : ""}${theme.fg("dim", detail)}`, width)];
|
|
678
790
|
}
|
|
679
791
|
const roster = this.rosterKeys();
|
|
@@ -706,6 +818,26 @@ export class SubagentFleetStatus {
|
|
|
706
818
|
}
|
|
707
819
|
}
|
|
708
820
|
if (hiddenBelow > 0) lines.push(rightAlign("", theme.fg("dim", `↓ ${hiddenBelow} more`), width));
|
|
821
|
+
if (this.ui && this.widgetRegistered && this.onWorkflowCoverageChange) {
|
|
822
|
+
const coverage = new Map<string, string>();
|
|
823
|
+
for (const [key, { snapshot, childRows }] of this.workflowSnapshots) {
|
|
824
|
+
const ownerIndex = tree.findIndex((row) => row.kind === "owner" && row.entry.key === key);
|
|
825
|
+
const owner = tree[ownerIndex];
|
|
826
|
+
if (owner?.kind !== "owner" || ownerIndex < start) continue;
|
|
827
|
+
const count = childRows.size + (owner.entry.workflowRows?.length ?? 0) + (owner.entry.workflowChecklist?.phases.length ?? 0);
|
|
828
|
+
if (!count || ownerIndex + count >= start + visibleCount) continue;
|
|
829
|
+
const descendants = tree.slice(ownerIndex + 1, ownerIndex + count + 1);
|
|
830
|
+
if (!descendants.every((row) => (row.kind === "workflow" && row.ownerKey === key && !row.row.overflow
|
|
831
|
+
&& visibleWidth(this.renderWorkflowRow(row.row, row.last, Infinity, theme)) <= width)
|
|
832
|
+
|| (row.kind === "workflow-phase" && row.ownerKey === key
|
|
833
|
+
&& visibleWidth(this.renderWorkflowPhaseRow(row.phase, row.last, Infinity, theme)) <= width)
|
|
834
|
+
|| (row.kind === "child" && childRows.has(row.entry.key)
|
|
835
|
+
&& visibleWidth(this.renderEntry(rosterIndexByKey.get(row.entry.key) ?? 0, selectedIndex, row.entry, 0, theme, row.last ? "└─" : "├─", true)) <= width))) continue;
|
|
836
|
+
if (childRows.size && visibleWidth(this.renderEntry(rosterIndexByKey.get(key) ?? 0, selectedIndex, owner.entry, 0, theme, undefined, true)) > width) continue;
|
|
837
|
+
coverage.set(key.slice("async:".length), snapshot);
|
|
838
|
+
}
|
|
839
|
+
this.onWorkflowCoverageChange(this.ui, coverage);
|
|
840
|
+
}
|
|
709
841
|
this.renderProjectPaneSection(lines, selectedIndex, width, theme, rosterIndexByKey);
|
|
710
842
|
return lines;
|
|
711
843
|
}
|
|
@@ -721,18 +853,20 @@ export class SubagentFleetStatus {
|
|
|
721
853
|
}
|
|
722
854
|
|
|
723
855
|
|
|
724
|
-
private renderEntry(rosterIndex: number, selectedIndex: number, entry: FleetStatusEntry, width: number, theme: Theme, branch?: string): string {
|
|
725
|
-
const
|
|
856
|
+
private renderEntry(rosterIndex: number, selectedIndex: number, entry: FleetStatusEntry, width: number, theme: Theme, branch?: string, unclipped = false): string {
|
|
857
|
+
const label = entry.displayLabel ?? entry.agent;
|
|
858
|
+
const agent = entry.modelThinking ? `${label} (${entry.modelThinking})` : label;
|
|
726
859
|
const prefix = branch ? ` ${branch}` : " ";
|
|
727
860
|
const checklist = entry.workflowWrapper && entry.workflowChecklist
|
|
728
861
|
? ` · checklist ${formatWorkflowChecklistSummary(entry.workflowChecklist)}${entry.workflowChecklist.bottleneck ? ` · bottleneck ${formatWorkflowChecklistBottleneck(entry.workflowChecklist.bottleneck)}` : ""}`
|
|
729
862
|
: "";
|
|
730
|
-
const left = `${prefix} ${this.bullet(rosterIndex, selectedIndex, theme)} ${theme.fg(
|
|
863
|
+
const left = `${prefix} ${this.bullet(rosterIndex, selectedIndex, theme)} ${theme.fg(fleetAgentIdentityColor(entry.agent), agent)} · ${entry.state}${checklist}`;
|
|
731
864
|
const elapsed = Date.now() - entry.startedAt;
|
|
732
865
|
const rightText = entry.projectPane
|
|
733
866
|
? `${entry.projectPane.summary ?? "—"} · ${formatFleetElapsed(Date.now() - entry.projectPane.refreshedAt)} ago`
|
|
734
|
-
: entry.external ? formatFleetElapsed(elapsed) : `${formatFleetElapsed(elapsed)} · ${formatFleetTokens(entry.tokens, entry.window)}`;
|
|
867
|
+
: entry.external ? formatFleetElapsed(elapsed) : `${formatFleetElapsed(elapsed)} · ${entry.workflowWrapper ? "usage on child rows" : formatFleetTokens(entry.tokens, entry.window)}`;
|
|
735
868
|
const right = theme.fg("dim", rightText);
|
|
869
|
+
if (unclipped) return `${left} ${right}`;
|
|
736
870
|
return rightAlign(left, right, width);
|
|
737
871
|
}
|
|
738
872
|
|
|
@@ -742,9 +876,9 @@ export class SubagentFleetStatus {
|
|
|
742
876
|
if (row.overflow !== undefined) return truncateToWidth(`${indent}${marker} ${theme.fg("dim", `+${row.overflow} nested leaves`)}`, width);
|
|
743
877
|
const modelThinking = row.modelThinking ? ` (${row.modelThinking})` : "";
|
|
744
878
|
const activity = row.activity ? ` · ${row.activity}` : "";
|
|
745
|
-
const left = `${indent}${marker} ${nestedStatusGlyph(row.state, theme)} ${theme.fg(
|
|
746
|
-
const elapsed =
|
|
747
|
-
return truncateToWidth(`${left}${theme.fg("dim", elapsed)}`, width);
|
|
879
|
+
const left = `${indent}${marker} ${nestedStatusGlyph(row.state, theme)} ${theme.fg(fleetAgentIdentityColor(row.agentIdentity ?? row.name), `${row.name}${modelThinking}`)} · ${row.state}${activity}`;
|
|
880
|
+
const elapsed = detailElapsed(row);
|
|
881
|
+
return truncateToWidth(`${left}${elapsed !== undefined ? theme.fg("dim", ` · ${elapsed}`) : ""}`, width);
|
|
748
882
|
}
|
|
749
883
|
|
|
750
884
|
private workflowRowGlyph(row: AsyncStatusWorkflowRow, theme: Theme): string {
|
|
@@ -797,7 +931,7 @@ export class SubagentFleetStatus {
|
|
|
797
931
|
].filter((value): value is string => Boolean(value)).join(" · ") : "";
|
|
798
932
|
const left = `${indent}${marker} ${this.workflowRowGlyph(row, theme)} ${theme.fg("muted", `${kind}${row.name}${context ? ` ${context}` : ""}${modelThinking}`)} · ${this.workflowRowStateLabel(row, theme)}${activity}${hints ? ` · ${hints}` : ""}`;
|
|
799
933
|
const details = [
|
|
800
|
-
|
|
934
|
+
detailElapsed(row),
|
|
801
935
|
row.tokens !== undefined ? formatFleetTokens(row.tokens, row.window) : undefined,
|
|
802
936
|
row.provider ? `provider:${row.provider}` : undefined,
|
|
803
937
|
row.role ? `role:${row.role}` : undefined,
|
|
@@ -853,6 +987,7 @@ export class SubagentFleetStatus {
|
|
|
853
987
|
entry.surface,
|
|
854
988
|
entry.parentKey,
|
|
855
989
|
entry.agent,
|
|
990
|
+
entry.displayLabel,
|
|
856
991
|
entry.state,
|
|
857
992
|
entry.modelThinking,
|
|
858
993
|
entry.description,
|
|
@@ -876,6 +1011,8 @@ export class SubagentFleetStatus {
|
|
|
876
1011
|
row.modelThinking,
|
|
877
1012
|
row.activity,
|
|
878
1013
|
row.startedAt,
|
|
1014
|
+
row.endedAt,
|
|
1015
|
+
row.durationMs,
|
|
879
1016
|
row.tokens,
|
|
880
1017
|
row.provider,
|
|
881
1018
|
row.role,
|
|
@@ -889,10 +1026,12 @@ export class SubagentFleetStatus {
|
|
|
889
1026
|
]),
|
|
890
1027
|
nestedFleetRows(entry.nestedChildren, entry.parentKey ? 3 : 4).map((row) => [
|
|
891
1028
|
row.name,
|
|
1029
|
+
row.agentIdentity,
|
|
892
1030
|
row.state,
|
|
893
1031
|
row.modelThinking,
|
|
894
1032
|
row.activity,
|
|
895
1033
|
row.startedAt,
|
|
1034
|
+
row.endedAt,
|
|
896
1035
|
row.depth,
|
|
897
1036
|
row.overflow,
|
|
898
1037
|
]),
|
|
@@ -918,6 +1057,7 @@ export class SubagentFleetStatus {
|
|
|
918
1057
|
}
|
|
919
1058
|
|
|
920
1059
|
private clearWidget(): void {
|
|
1060
|
+
this.clearWorkflowCoverage();
|
|
921
1061
|
if (!this.widgetRegistered) return;
|
|
922
1062
|
try {
|
|
923
1063
|
this.ui?.setWidget(FLEET_STATUS_WIDGET_KEY, undefined);
|
|
@@ -931,6 +1071,7 @@ export class SubagentFleetStatus {
|
|
|
931
1071
|
}
|
|
932
1072
|
|
|
933
1073
|
private clearUiRegistration(): void {
|
|
1074
|
+
this.clearWorkflowCoverage();
|
|
934
1075
|
if (this.timer) clearInterval(this.timer);
|
|
935
1076
|
this.timer = undefined;
|
|
936
1077
|
|
|
@@ -961,4 +1102,8 @@ export class SubagentFleetStatus {
|
|
|
961
1102
|
throw new AggregateError(cleanupErrors, "Failed to clean up FleetView UI registration");
|
|
962
1103
|
}
|
|
963
1104
|
}
|
|
1105
|
+
|
|
1106
|
+
private clearWorkflowCoverage(): void {
|
|
1107
|
+
if (this.ui) this.onWorkflowCoverageChange?.(this.ui, new Map());
|
|
1108
|
+
}
|
|
964
1109
|
}
|
package/src/tui/fleet.ts
CHANGED
|
@@ -18,8 +18,8 @@ import { resolveWorkflowForegroundSteeringTarget, steerWorkflowForegroundTarget
|
|
|
18
18
|
import { contextModeBadge, contextModeLabel } from "../runs/shared/context-mode.ts";
|
|
19
19
|
import { FLEET_STATUS_WIDGET_KEY } from "./fleet-status.ts";
|
|
20
20
|
import { readFleetTranscript, renderFleetTranscript, type FleetTranscript } from "./fleet-transcript.ts";
|
|
21
|
-
import {
|
|
22
|
-
import type {
|
|
21
|
+
import { handleInspectorAction } from "../inspectors/actions.ts";
|
|
22
|
+
import type { InspectorPlugin } from "../inspectors/types.ts";
|
|
23
23
|
import { getLivePromptAudit, type LivePromptAudit, type PromptAuditView } from "../runs/foreground/prompt-audit.ts";
|
|
24
24
|
|
|
25
25
|
const REFRESH_MS = 750;
|
|
@@ -110,7 +110,8 @@ export interface FleetViewOptions {
|
|
|
110
110
|
fleetKeybindings?: FleetKeybindingsConfig;
|
|
111
111
|
actions?: FleetActionHandlers;
|
|
112
112
|
copyText?: (text: string) => Promise<void> | void;
|
|
113
|
-
|
|
113
|
+
inspectorPlugins?: readonly InspectorPlugin[];
|
|
114
|
+
inspectorEnv?: NodeJS.ProcessEnv;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
function belongsToCurrentSession(sessionId: string | undefined, currentSessionId: string | null): boolean {
|
|
@@ -179,10 +180,16 @@ function asyncItems(run: AsyncRunSummary, description?: string): FleetItem[] {
|
|
|
179
180
|
}));
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
function orderFleetAsyncRuns(runs: AsyncRunSummary[], terminalLimit: number): AsyncRunSummary[] {
|
|
183
|
+
function orderFleetAsyncRuns(runs: AsyncRunSummary[], terminalLimit: number, trackedJobs: Map<string, AsyncJobState>): AsyncRunSummary[] {
|
|
183
184
|
const updatedAt = (run: AsyncRunSummary) => run.lastUpdate ?? run.endedAt ?? run.startedAt;
|
|
184
185
|
const byNewest = (left: AsyncRunSummary, right: AsyncRunSummary) => updatedAt(right) - updatedAt(left);
|
|
185
|
-
|
|
186
|
+
// Missing tracked starts must not inherit the summary's heartbeat-based display fallback.
|
|
187
|
+
const startedAt = (run: AsyncRunSummary) => {
|
|
188
|
+
const job = trackedJobs.get(run.id);
|
|
189
|
+
return job ? job.startedAt ?? 0 : run.startedAt;
|
|
190
|
+
};
|
|
191
|
+
const byStartedAt = (left: AsyncRunSummary, right: AsyncRunSummary) => startedAt(left) - startedAt(right) || left.id.localeCompare(right.id);
|
|
192
|
+
const active = runs.filter((run) => run.state === "queued" || run.state === "running").sort(byStartedAt);
|
|
186
193
|
const terminal = runs.filter((run) => run.state !== "queued" && run.state !== "running").sort(byNewest);
|
|
187
194
|
return [...active, ...terminal.slice(0, terminalLimit)];
|
|
188
195
|
}
|
|
@@ -208,7 +215,7 @@ export function collectFleetSnapshot(
|
|
|
208
215
|
liveWorkflowForegroundControls.add(control);
|
|
209
216
|
workflowForegroundChildCounts.set(control.parentWorkflowRunId, (workflowForegroundChildCounts.get(control.parentWorkflowRunId) ?? 0) + activeChildCount);
|
|
210
217
|
}
|
|
211
|
-
for (const control of [...state.foregroundControls.values()].sort((left, right) =>
|
|
218
|
+
for (const control of [...state.foregroundControls.values()].sort((left, right) => left.startedAt - right.startedAt || left.runId.localeCompare(right.runId))) {
|
|
212
219
|
activeForegroundIds.add(control.runId);
|
|
213
220
|
if (control.parentWorkflowRunId && workflowParentIds.has(control.parentWorkflowRunId)
|
|
214
221
|
&& ((workflowForegroundChildCounts.get(control.parentWorkflowRunId) ?? 0) <= 1 || !liveWorkflowForegroundControls.has(control))) continue;
|
|
@@ -249,7 +256,7 @@ export function collectFleetSnapshot(
|
|
|
249
256
|
const tracked = [...trackedJobs.values()]
|
|
250
257
|
.filter((job) => belongsToCurrentSession(job.sessionId, state.currentSessionId));
|
|
251
258
|
const byUpdate = (left: AsyncJobState, right: AsyncJobState) => (right.updatedAt ?? right.startedAt ?? 0) - (left.updatedAt ?? left.startedAt ?? 0);
|
|
252
|
-
const active = tracked.filter((job) => job.status === "queued" || job.status === "running")
|
|
259
|
+
const active = tracked.filter((job) => job.status === "queued" || job.status === "running");
|
|
253
260
|
const recent = tracked.filter((job) => job.status !== "queued" && job.status !== "running").sort(byUpdate).slice(0, options.limit ?? MAX_RECENT_ASYNC_RUNS);
|
|
254
261
|
const trackedRuns: AsyncRunSummary[] = [];
|
|
255
262
|
for (const job of [...active, ...recent]) {
|
|
@@ -272,7 +279,7 @@ export function collectFleetSnapshot(
|
|
|
272
279
|
} else {
|
|
273
280
|
runs = trackedRuns;
|
|
274
281
|
}
|
|
275
|
-
for (const run of orderFleetAsyncRuns(runs, options.limit ?? MAX_RECENT_ASYNC_RUNS)) {
|
|
282
|
+
for (const run of orderFleetAsyncRuns(runs, options.limit ?? MAX_RECENT_ASYNC_RUNS, trackedJobs)) {
|
|
276
283
|
items.push(...asyncItems(run, descriptions.get(run.id)));
|
|
277
284
|
}
|
|
278
285
|
} catch (cause) {
|
|
@@ -937,23 +944,23 @@ export class SubagentFleetComponent implements Component {
|
|
|
937
944
|
return { runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}) };
|
|
938
945
|
}
|
|
939
946
|
|
|
940
|
-
private
|
|
947
|
+
private selectedInspectAction(): { runId: string; asyncDir: string; index?: number } | { reason: string } {
|
|
941
948
|
const item = this.snapshot.items[this.selected];
|
|
942
949
|
if (!item) return { reason: "No child is selected." };
|
|
943
|
-
if (item.kind === "external") return { reason: "External jobs are display-only and have no
|
|
950
|
+
if (item.kind === "external") return { reason: "External jobs are display-only and have no inspector controls." };
|
|
944
951
|
if (item.kind === "async") {
|
|
945
952
|
if (!isActionableAsyncState(item.run.state) || !isActionableAsyncState(item.state)) return { reason: `Selected child is ${item.state}; controls require a running or queued async child.` };
|
|
946
953
|
return { runId: item.runId, asyncDir: item.run.asyncDir, ...(item.index !== undefined ? { index: item.index } : {}) };
|
|
947
954
|
}
|
|
948
955
|
if (item.kind !== "foreground-active" || !item.control.parentWorkflowRunId) return { reason: "Fleet controls are available for current-session top-level async runs only." };
|
|
949
956
|
const parent = this.state.asyncJobs.get(item.control.parentWorkflowRunId) ?? this.state.fleetJobs?.get(item.control.parentWorkflowRunId);
|
|
950
|
-
if (!parent || !isActionableAsyncState(parent.status)) return { reason: "The parent workflow is no longer available for
|
|
957
|
+
if (!parent || !isActionableAsyncState(parent.status)) return { reason: "The parent workflow is no longer available for inspection." };
|
|
951
958
|
return { runId: parent.asyncId, asyncDir: parent.asyncDir };
|
|
952
959
|
}
|
|
953
960
|
|
|
954
|
-
private
|
|
955
|
-
const target = this.
|
|
956
|
-
if ("reason" in target || !this.options.actions?.inspect) this.setActionNotice({ text: "reason" in target ? target.reason : "
|
|
961
|
+
private inspectSelected(): void {
|
|
962
|
+
const target = this.selectedInspectAction();
|
|
963
|
+
if ("reason" in target || !this.options.actions?.inspect) this.setActionNotice({ text: "reason" in target ? target.reason : "Inspector controls are unavailable in this context.", isError: true });
|
|
957
964
|
else this.runAction(() => this.options.actions!.inspect!(target));
|
|
958
965
|
}
|
|
959
966
|
|
|
@@ -1203,7 +1210,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
1203
1210
|
return;
|
|
1204
1211
|
}
|
|
1205
1212
|
if (matchesFleetAction(data, this.keybindings, "inspect")) {
|
|
1206
|
-
this.
|
|
1213
|
+
this.inspectSelected();
|
|
1207
1214
|
return;
|
|
1208
1215
|
}
|
|
1209
1216
|
}
|
|
@@ -1358,7 +1365,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
1358
1365
|
? ` j/k child · 1/2/3 view · g redo with guidance · c copy · Esc close Prompt Audit · ${position}`
|
|
1359
1366
|
: selected?.kind === "external"
|
|
1360
1367
|
? ` ${bindingLabel(this.keybindings, "selectUp")}/${bindingLabel(this.keybindings, "selectDown")} job · display-only · ${bindingLabel(this.keybindings, "refresh")} refresh · ${bindingLabel(this.keybindings, "close")} close · ${position}`
|
|
1361
|
-
: ` ${bindingLabel(this.keybindings, "selectUp")}/${bindingLabel(this.keybindings, "selectDown")} agent · p Prompt Audit · ${bindingLabel(this.keybindings, "inspect")}
|
|
1368
|
+
: ` ${bindingLabel(this.keybindings, "selectUp")}/${bindingLabel(this.keybindings, "selectDown")} agent · p Prompt Audit · ${bindingLabel(this.keybindings, "inspect")} Inspect · ${bindingLabel(this.keybindings, "steer")} steer · ${bindingLabel(this.keybindings, "stop")} stop · ${bindingLabel(this.keybindings, "toggleTools")} tools · ${bindingLabel(this.keybindings, "refresh")} refresh · ${bindingLabel(this.keybindings, "close")} close · ${position}`;
|
|
1362
1369
|
lines.push(this.theme.fg("border", "│") + fit(this.theme.fg("dim", footer), innerWidth) + this.theme.fg("border", "│"));
|
|
1363
1370
|
lines.push(this.theme.fg("border", `╰${"─".repeat(innerWidth)}╯`));
|
|
1364
1371
|
return lines.map((line) => truncateToWidth(line, width));
|
|
@@ -1407,7 +1414,7 @@ export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentSt
|
|
|
1407
1414
|
}), `Failed to steer async run ${input.runId}.`);
|
|
1408
1415
|
},
|
|
1409
1416
|
stop: (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(stopAsyncRun(state, input.runId, undefined, { asyncDir: input.asyncDir, resolvedId: input.runId }), `Failed to stop async run ${input.runId}.`),
|
|
1410
|
-
inspect: async (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(await
|
|
1417
|
+
inspect: async (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(await handleInspectorAction("inspector.open", {
|
|
1411
1418
|
id: input.runId,
|
|
1412
1419
|
dir: input.asyncDir,
|
|
1413
1420
|
focus: true,
|
|
@@ -1416,10 +1423,11 @@ export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentSt
|
|
|
1416
1423
|
state,
|
|
1417
1424
|
sessionRoots: state.trustedSessionRoots,
|
|
1418
1425
|
cwd: state.baseCwd,
|
|
1419
|
-
...(options.herdrClient ? { client: options.herdrClient } : {}),
|
|
1420
1426
|
...(state.authorityPolicy ? { authorityPolicy: state.authorityPolicy } : {}),
|
|
1421
1427
|
...(state.missionStoreConfig ? { missions: state.missionStoreConfig } : {}),
|
|
1422
|
-
|
|
1428
|
+
...(options.inspectorPlugins ? { plugins: options.inspectorPlugins } : {}),
|
|
1429
|
+
...(options.inspectorEnv ? { env: options.inspectorEnv } : {}),
|
|
1430
|
+
}), `Failed to open inspector for async run ${input.runId}.`),
|
|
1423
1431
|
redoPrompt: async (input: { runId: string; index: number; guidance: string; control?: ForegroundRunControl }) => {
|
|
1424
1432
|
const control = input.control ?? state.foregroundControls.get(input.runId);
|
|
1425
1433
|
if (!control?.promptAuditRedo) return { text: "Redo is not available for this live child.", isError: true };
|