pi-subagents 0.56.0 → 0.58.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 +92 -0
- package/agents/claude-code-writer.md +15 -0
- package/agents/claude-code.md +15 -0
- package/agents/codex-exec-writer.md +15 -0
- package/agents/codex-exec.md +15 -0
- package/agents/cursor-agent-writer.md +14 -0
- package/agents/cursor-agent.md +14 -0
- package/docs/agents.md +124 -21
- package/docs/configuration.md +37 -0
- package/docs/extension-api.md +41 -2
- package/docs/models.md +3 -3
- package/docs/observability.md +8 -7
- package/docs/tool-reference.md +14 -3
- package/docs/workflows.md +44 -0
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +2 -0
- package/skills/pi-subagents/references/execution-controls.md +2 -2
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
- package/src/agents/agent-management.ts +36 -5
- package/src/agents/agent-refinements.ts +4 -4
- package/src/agents/agent-serializer.ts +5 -0
- package/src/agents/agents.ts +257 -51
- package/src/agents/builtin-names.ts +6 -0
- package/src/agents/runtime-agent-events.ts +70 -0
- package/src/agents/runtime-agent-registry.ts +18 -4
- package/src/api/agents.ts +10 -5
- package/src/api/preflight.ts +28 -3
- package/src/extension/config.ts +70 -0
- package/src/extension/doctor.ts +3 -3
- package/src/extension/index.ts +33 -8
- package/src/extension/public-execution.ts +29 -13
- package/src/extension/rpc.ts +55 -19
- package/src/extension/schemas.ts +6 -5
- package/src/extension/tool-description.ts +18 -12
- package/src/inspectors/herdr/actions.ts +2 -1
- package/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/src/integrations/herdr-status.ts +51 -3
- package/src/runs/background/active-async-capacity.ts +77 -10
- package/src/runs/background/async-execution.ts +127 -19
- package/src/runs/background/async-job-tracker.ts +5 -0
- package/src/runs/background/async-resume.ts +6 -2
- package/src/runs/background/async-retention.ts +20 -3
- package/src/runs/background/async-status.ts +7 -0
- package/src/runs/background/chain-append.ts +2 -0
- package/src/runs/background/chain-root-attachment.ts +15 -1
- package/src/runs/background/fleet-view.ts +16 -10
- package/src/runs/background/inspect-rpc.ts +8 -8
- package/src/runs/background/notify.ts +26 -3
- package/src/runs/background/result-delivery-ownership.ts +45 -0
- package/src/runs/background/result-files.ts +27 -14
- package/src/runs/background/result-watcher.ts +36 -15
- package/src/runs/background/run-status.ts +33 -6
- package/src/runs/background/scheduled-runs.ts +7 -1
- package/src/runs/background/subagent-runner.ts +239 -56
- package/src/runs/background/wait-completions.ts +4 -0
- package/src/runs/foreground/execution.ts +92 -11
- package/src/runs/foreground/foreground-control.ts +6 -0
- package/src/runs/foreground/foreground-history.ts +22 -1
- package/src/runs/foreground/subagent-executor.ts +322 -102
- package/src/runs/foreground/workflow-detach-reconcile.ts +144 -18
- package/src/runs/shared/child-protocol.ts +21 -7
- package/src/runs/shared/claude-code-adapter.ts +129 -0
- package/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/src/runs/shared/completion-guard.ts +4 -3
- package/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/src/runs/shared/dynamic-fanout.ts +3 -3
- package/src/runs/shared/external-cli-contract.ts +167 -0
- package/src/runs/shared/external-cli-preflight.ts +122 -0
- package/src/runs/shared/external-cli-runner.ts +348 -55
- package/src/runs/shared/fast-mode-extension.ts +5 -5
- package/src/runs/shared/launch-cwd.ts +16 -0
- package/src/runs/shared/long-running-guard.ts +2 -1
- package/src/runs/shared/mcp-config-sources.ts +386 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +155 -42
- package/src/runs/shared/model-exclusions.ts +69 -7
- package/src/runs/shared/model-fallback.ts +39 -5
- package/src/runs/shared/mutation-evidence.ts +7 -2
- package/src/runs/shared/nested-events.ts +3 -1
- package/src/runs/shared/nested-render.ts +2 -2
- package/src/runs/shared/parallel-utils.ts +8 -1
- package/src/runs/shared/pi-args.ts +61 -6
- package/src/runs/shared/process-signal.ts +13 -0
- package/src/runs/shared/run-history.ts +21 -1
- package/src/runs/shared/single-output.ts +17 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +85 -9
- package/src/shared/fork-context.ts +21 -0
- package/src/shared/formatters.ts +13 -1
- package/src/shared/launch-contract.ts +4 -0
- package/src/shared/pruned-fork.ts +450 -0
- package/src/shared/session-file-trust.ts +19 -0
- package/src/shared/session-tokens.ts +14 -3
- package/src/shared/settings.ts +10 -2
- package/src/shared/shortcuts.ts +17 -0
- package/src/shared/types.ts +160 -10
- package/src/shared/utils.ts +6 -29
- package/src/shared/workflow-child-permit.ts +116 -0
- package/src/slash/delegation-adapters.ts +0 -1
- package/src/slash/slash-commands.ts +8 -6
- package/src/slash/subagents-admin.ts +3 -0
- package/src/tui/fleet-status.ts +27 -10
- package/src/tui/fleet-transcript.ts +11 -5
- package/src/tui/fleet.ts +28 -13
- package/src/tui/render.ts +55 -21
- package/src/workflows/scripted-workflow.ts +299 -31
- package/src/workflows/workflow-child-summary.ts +117 -0
- package/src/workflows/workflow-receipt.ts +155 -5
|
@@ -89,6 +89,7 @@ function buildBuiltinBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
89
89
|
...(agent.thinking !== undefined ? { thinking: agent.thinking } : {}),
|
|
90
90
|
systemPromptMode: agent.systemPromptMode,
|
|
91
91
|
inheritProjectContext: agent.inheritProjectContext,
|
|
92
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
92
93
|
inheritSkills: agent.inheritSkills,
|
|
93
94
|
...(agent.defaultContext !== undefined ? { defaultContext: agent.defaultContext } : {}),
|
|
94
95
|
...(agent.acceptanceRole !== undefined ? { acceptanceRole: agent.acceptanceRole } : {}),
|
|
@@ -98,6 +99,7 @@ function buildBuiltinBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
98
99
|
...(agent.tools !== undefined ? { tools: [...agent.tools] } : {}),
|
|
99
100
|
...(agent.mcpDirectTools !== undefined ? { mcpDirectTools: [...agent.mcpDirectTools] } : {}),
|
|
100
101
|
...(agent.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...agent.subagentOnlyExtensions] } : {}),
|
|
102
|
+
...(agent.mutationTools !== undefined ? { mutationTools: [...agent.mutationTools] } : {}),
|
|
101
103
|
...(agent.completionGuard !== undefined ? { completionGuard: agent.completionGuard } : {}),
|
|
102
104
|
...(agent.toolBudget !== undefined ? { toolBudget: agent.toolBudget } : {}),
|
|
103
105
|
};
|
|
@@ -189,6 +191,7 @@ function metadataFor(agent: AgentConfig): string {
|
|
|
189
191
|
if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
|
|
190
192
|
lines.push(`System prompt mode: ${agent.systemPromptMode}`);
|
|
191
193
|
lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
|
|
194
|
+
lines.push(`Inherit global context: ${agent.inheritGlobalContext ? "true" : "false"}`);
|
|
192
195
|
lines.push(`Inherit skills: ${agent.inheritSkills ? "true" : "false"}`);
|
|
193
196
|
if (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);
|
|
194
197
|
if (agent.output) lines.push(`Output: ${agent.output}`);
|
package/src/tui/fleet-status.ts
CHANGED
|
@@ -25,6 +25,7 @@ type FleetStatusEntry = {
|
|
|
25
25
|
description?: string;
|
|
26
26
|
startedAt: number;
|
|
27
27
|
tokens: number;
|
|
28
|
+
window?: number;
|
|
28
29
|
state: string;
|
|
29
30
|
external?: true;
|
|
30
31
|
projectPane?: HerdrProjectPaneSnapshot;
|
|
@@ -39,6 +40,7 @@ type FleetWorkflowRow = {
|
|
|
39
40
|
activity?: string;
|
|
40
41
|
startedAt?: number;
|
|
41
42
|
tokens?: number;
|
|
43
|
+
window?: number;
|
|
42
44
|
overflow?: number;
|
|
43
45
|
};
|
|
44
46
|
|
|
@@ -72,12 +74,15 @@ export function formatFleetElapsed(ms: number): string {
|
|
|
72
74
|
return `${Math.max(0, Math.round(ms / 1000))}s`;
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
export function formatFleetTokens(count: number): string {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
export function formatFleetTokens(count: number, window?: number): string {
|
|
78
|
+
const compact = (value: number): string => value >= 1_000_000
|
|
79
|
+
? `${(value / 1_000_000).toFixed(1)}M`
|
|
80
|
+
: value >= 1_000
|
|
81
|
+
? `${(value / 1_000).toFixed(1)}k`
|
|
82
|
+
: `${Math.max(0, Math.round(value))}`;
|
|
83
|
+
return window !== undefined
|
|
84
|
+
? `↓ ${compact(window)} window · ${compact(count)} spent`
|
|
85
|
+
: `↓ ${compact(count)} tokens`;
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
function rightAlign(left: string, right: string, width: number): string {
|
|
@@ -134,6 +139,7 @@ function workflowFleetRows(steps: AsyncJobStep[] | undefined): FleetWorkflowRow[
|
|
|
134
139
|
...(activity ? { activity } : {}),
|
|
135
140
|
...(step.startedAt !== undefined ? { startedAt: step.startedAt } : {}),
|
|
136
141
|
...(step.tokens?.total !== undefined ? { tokens: step.tokens.total } : {}),
|
|
142
|
+
...(step.tokens?.window !== undefined ? { window: step.tokens.window } : {}),
|
|
137
143
|
};
|
|
138
144
|
});
|
|
139
145
|
}
|
|
@@ -332,7 +338,8 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
332
338
|
...(modelThinking ? { modelThinking } : {}),
|
|
333
339
|
description: foregroundDescription(control, child.description),
|
|
334
340
|
startedAt: child.startedAt,
|
|
335
|
-
|
|
341
|
+
tokens: child.tokens ?? 0,
|
|
342
|
+
...(child.window !== undefined ? { window: child.window } : {}),
|
|
336
343
|
state: "running",
|
|
337
344
|
...((control.nestedChildren?.filter((nested) => nested.parentStepIndex === child.index).length ?? 0) > 0
|
|
338
345
|
? { nestedChildren: control.nestedChildren!.filter((nested) => nested.parentStepIndex === child.index) }
|
|
@@ -350,6 +357,7 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
350
357
|
description: foregroundDescription(control, control.description),
|
|
351
358
|
startedAt: control.startedAt,
|
|
352
359
|
tokens: control.tokens ?? 0,
|
|
360
|
+
...(control.window !== undefined ? { window: control.window } : {}),
|
|
353
361
|
state: "running",
|
|
354
362
|
...(control.nestedChildren?.length ? { nestedChildren: control.nestedChildren } : {}),
|
|
355
363
|
});
|
|
@@ -367,6 +375,7 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
367
375
|
description: latestEmit !== undefined ? `latest emit: ${latestEmit}` : job.description,
|
|
368
376
|
startedAt,
|
|
369
377
|
tokens: job.totalTokens?.total ?? 0,
|
|
378
|
+
...(job.totalTokens?.window !== undefined ? { window: job.totalTokens.window } : {}),
|
|
370
379
|
state: job.status,
|
|
371
380
|
...(job.steps?.length ? { workflowRows: workflowFleetRows(job.steps) } : {}),
|
|
372
381
|
...(job.nestedChildren?.length ? { nestedChildren: job.nestedChildren } : {}),
|
|
@@ -387,6 +396,7 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
387
396
|
description: job.description,
|
|
388
397
|
startedAt,
|
|
389
398
|
tokens: job.totalTokens?.total ?? 0,
|
|
399
|
+
...(job.totalTokens?.window !== undefined ? { window: job.totalTokens.window } : {}),
|
|
390
400
|
state: job.status,
|
|
391
401
|
...(job.nestedChildren?.length ? { nestedChildren: job.nestedChildren } : {}),
|
|
392
402
|
});
|
|
@@ -404,6 +414,9 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
404
414
|
description: step.description ?? job.description,
|
|
405
415
|
startedAt: step.startedAt ?? startedAt,
|
|
406
416
|
tokens: step.tokens?.total ?? (steps.length === 1 ? job.totalTokens?.total ?? 0 : 0),
|
|
417
|
+
...((step.tokens?.window ?? (steps.length === 1 ? job.totalTokens?.window : undefined)) !== undefined
|
|
418
|
+
? { window: step.tokens?.window ?? job.totalTokens?.window }
|
|
419
|
+
: {}),
|
|
407
420
|
state: step.status,
|
|
408
421
|
...((step.children?.length ?? 0) > 0
|
|
409
422
|
? { nestedChildren: step.children }
|
|
@@ -611,6 +624,10 @@ export class SubagentFleetStatus {
|
|
|
611
624
|
const workEntries = this.entries.filter((entry) => !entry.surface);
|
|
612
625
|
const projectEntries = this.entries.filter((entry) => entry.surface === "project-pane");
|
|
613
626
|
const tokens = workEntries.reduce((total, entry) => total + entry.tokens, 0);
|
|
627
|
+
const nativeEntries = workEntries.filter((entry) => !entry.external);
|
|
628
|
+
const window = nativeEntries.length > 0 && nativeEntries.every((entry) => entry.window !== undefined)
|
|
629
|
+
? nativeEntries.reduce((total, entry) => total + entry.window!, 0)
|
|
630
|
+
: undefined;
|
|
614
631
|
const capacity = this.state.activeAsyncCapacity;
|
|
615
632
|
const hasNativeRows = workEntries.some((entry) => !entry.external);
|
|
616
633
|
const showNativeSummary = hasNativeRows || Boolean(capacity?.used);
|
|
@@ -621,7 +638,7 @@ export class SubagentFleetStatus {
|
|
|
621
638
|
const paneAttention = projectEntries.filter((entry) => entry.projectPane && projectPaneNeedsAttention(entry.projectPane)).length;
|
|
622
639
|
const panes = projectEntries.length > 0 ? `${projectEntries.length} pane${projectEntries.length === 1 ? "" : "s"}${paneAttention ? ` (${paneAttention} ⚠)` : ""}` : "";
|
|
623
640
|
const label = [agents, asyncRuns, panes].filter(Boolean).join(" · ");
|
|
624
|
-
const detail = [showNativeSummary ? formatFleetTokens(tokens) : undefined, "↓/← to inspect"].filter(Boolean).join(" · ");
|
|
641
|
+
const detail = [showNativeSummary ? formatFleetTokens(tokens, window) : undefined, "↓/← to inspect"].filter(Boolean).join(" · ");
|
|
625
642
|
return [truncateToWidth(` ${theme.fg("muted", label)}${label && detail ? " · " : ""}${theme.fg("dim", detail)}`, width)];
|
|
626
643
|
}
|
|
627
644
|
const roster = this.rosterKeys();
|
|
@@ -670,7 +687,7 @@ export class SubagentFleetStatus {
|
|
|
670
687
|
const elapsed = Date.now() - entry.startedAt;
|
|
671
688
|
const rightText = entry.projectPane
|
|
672
689
|
? `${entry.projectPane.summary ?? "—"} · ${formatFleetElapsed(Date.now() - entry.projectPane.refreshedAt)} ago`
|
|
673
|
-
: entry.external ? formatFleetElapsed(elapsed) : `${formatFleetElapsed(elapsed)} · ${formatFleetTokens(entry.tokens)}`;
|
|
690
|
+
: entry.external ? formatFleetElapsed(elapsed) : `${formatFleetElapsed(elapsed)} · ${formatFleetTokens(entry.tokens, entry.window)}`;
|
|
674
691
|
const right = theme.fg("dim", rightText);
|
|
675
692
|
return rightAlign(left, right, width);
|
|
676
693
|
}
|
|
@@ -695,7 +712,7 @@ export class SubagentFleetStatus {
|
|
|
695
712
|
const left = `${indent}${marker} ${nestedStatusGlyph(row.state, theme)} ${theme.fg("muted", `${row.name}${modelThinking}`)} · ${row.state}${activity}`;
|
|
696
713
|
const details = [
|
|
697
714
|
row.startedAt !== undefined ? formatFleetElapsed(Date.now() - row.startedAt) : undefined,
|
|
698
|
-
row.tokens !== undefined ? formatFleetTokens(row.tokens) : undefined,
|
|
715
|
+
row.tokens !== undefined ? formatFleetTokens(row.tokens, row.window) : undefined,
|
|
699
716
|
].filter(Boolean).join(" · ");
|
|
700
717
|
return truncateToWidth(`${left}${details ? theme.fg("dim", ` · ${details}`) : ""}`, width);
|
|
701
718
|
}
|
|
@@ -3,6 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { getLanguageFromPath, highlightCode, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Markdown, truncateToWidth, visibleWidth, wrapTextWithAnsi, type MarkdownTheme } from "@earendil-works/pi-tui";
|
|
5
5
|
import { safeTerminalText as safeDisplayText } from "../shared/display-text.ts";
|
|
6
|
+
import { isTrustedRecordedSessionFile } from "../shared/session-file-trust.ts";
|
|
6
7
|
|
|
7
8
|
const DEFAULT_MAX_RECORDS = 240;
|
|
8
9
|
const DEFAULT_MAX_BYTES = 2 * 1024 * 1024;
|
|
@@ -61,6 +62,8 @@ export interface FleetTranscript {
|
|
|
61
62
|
|
|
62
63
|
interface FleetTranscriptReadOptions {
|
|
63
64
|
trustedRoots: string[];
|
|
65
|
+
trustedFiles?: string[];
|
|
66
|
+
trustedFileRoot?: string;
|
|
64
67
|
maxRecords?: number;
|
|
65
68
|
maxBytes?: number;
|
|
66
69
|
}
|
|
@@ -109,10 +112,13 @@ function isNotFoundError(error: unknown): boolean {
|
|
|
109
112
|
return Boolean(objectValue(error)?.code === "ENOENT");
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
function validateTranscriptPath(filePath: string, trustedRoots: string[]): { resolvedPath?: string; warning?: string } {
|
|
113
|
-
if (trustedRoots.length === 0) return { warning: `Transcript preview has no trusted root: ${filePath}` };
|
|
115
|
+
function validateTranscriptPath(filePath: string, trustedRoots: string[], trustedFiles: string[] = [], trustedFileRoot?: string): { resolvedPath?: string; warning?: string } {
|
|
116
|
+
if (trustedRoots.length === 0 && (!trustedFileRoot || trustedFiles.length === 0)) return { warning: `Transcript preview has no trusted root: ${filePath}` };
|
|
114
117
|
const resolvedPath = path.resolve(filePath);
|
|
115
|
-
|
|
118
|
+
const recordedCandidate = trustedFileRoot
|
|
119
|
+
&& pathWithin(trustedFileRoot, resolvedPath)
|
|
120
|
+
&& trustedFiles.some((file) => path.resolve(file) === resolvedPath);
|
|
121
|
+
if (!trustedRoots.some((root) => pathWithin(root, resolvedPath)) && !recordedCandidate) {
|
|
116
122
|
return { warning: `Transcript is outside trusted roots: ${filePath}` };
|
|
117
123
|
}
|
|
118
124
|
let stat: fs.Stats;
|
|
@@ -129,7 +135,7 @@ function validateTranscriptPath(filePath: string, trustedRoots: string[]): { res
|
|
|
129
135
|
const realRoots = trustedRoots
|
|
130
136
|
.filter((root) => fs.existsSync(root))
|
|
131
137
|
.map((root) => fs.realpathSync(root));
|
|
132
|
-
if (!realRoots.some((root) => pathWithin(root, realPath))) {
|
|
138
|
+
if (!realRoots.some((root) => pathWithin(root, realPath)) && !isTrustedRecordedSessionFile(realPath, trustedFiles, trustedFileRoot)) {
|
|
133
139
|
return { warning: `Transcript resolves outside trusted roots: ${filePath}` };
|
|
134
140
|
}
|
|
135
141
|
return { resolvedPath: realPath };
|
|
@@ -335,7 +341,7 @@ function parseTranscriptLines(lines: string[], conversationStarted = false): { e
|
|
|
335
341
|
}
|
|
336
342
|
|
|
337
343
|
export function readFleetTranscript(filePath: string, options: FleetTranscriptReadOptions): FleetTranscript {
|
|
338
|
-
const validated = validateTranscriptPath(filePath, options.trustedRoots);
|
|
344
|
+
const validated = validateTranscriptPath(filePath, options.trustedRoots, options.trustedFiles, options.trustedFileRoot);
|
|
339
345
|
if (!validated.resolvedPath) {
|
|
340
346
|
return { path: filePath, events: [], truncated: false, ...(validated.warning ? { warning: safeDisplayText(validated.warning) } : {}) };
|
|
341
347
|
}
|
package/src/tui/fleet.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { getMarkdownTheme, type ExtensionContext } from "@earendil-works/pi-codi
|
|
|
5
5
|
import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component, type MarkdownTheme } from "@earendil-works/pi-tui";
|
|
6
6
|
import { snapshotExternalRuns, type ExternalRun } from "../api/external-runs.ts";
|
|
7
7
|
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
|
|
8
|
-
import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../shared/formatters.ts";
|
|
8
|
+
import { formatDuration, formatModelThinking, formatTokens, formatTokenUsage, shortenPath } from "../shared/formatters.ts";
|
|
9
9
|
import { DIRS, type AsyncJobState, type AsyncJobStep, type Details, type FleetKeybindingAction, type FleetKeybindingsConfig, type ForegroundChildControl, type ForegroundResumeChild, type ForegroundResumeRun, type ForegroundRunControl, type SubagentState } from "../shared/types.ts";
|
|
10
10
|
import { decodeUtf8Tail } from "../shared/utf8.ts";
|
|
11
11
|
import { readStatus } from "../shared/utils.ts";
|
|
@@ -403,7 +403,9 @@ function foregroundActiveDetail(item: Extract<FleetItem, { kind: "foreground-act
|
|
|
403
403
|
live.currentTool ? `Current tool: ${live.currentTool}${live.currentPath ? ` · ${shortenPath(live.currentPath)}` : ""}` : undefined,
|
|
404
404
|
live.turnCount !== undefined ? `Turns: ${live.turnCount}` : undefined,
|
|
405
405
|
live.toolCount !== undefined ? `Tools: ${live.toolCount}` : undefined,
|
|
406
|
-
live.tokens !== undefined
|
|
406
|
+
live.tokens !== undefined
|
|
407
|
+
? `Tokens: ${formatTokenUsage({ input: live.inputTokens ?? 0, output: live.outputTokens ?? 0, total: live.tokens, ...(live.window !== undefined ? { window: live.window } : {}), ...(live.windowPeak !== undefined ? { windowPeak: live.windowPeak } : {}) }, "tokens")}`
|
|
408
|
+
: undefined,
|
|
407
409
|
promptAuditCount > 0 ? `Prompt audit: ${promptAuditCount} live · 3 views · p opens` : undefined,
|
|
408
410
|
"",
|
|
409
411
|
"Transcript",
|
|
@@ -533,7 +535,7 @@ function workflowProgressLines(steps: AsyncJobStep[] | undefined): string[] {
|
|
|
533
535
|
}
|
|
534
536
|
const activity = workflowStepActivity(row.step);
|
|
535
537
|
const context = contextModeLabel(row.step.context);
|
|
536
|
-
const details = [row.step.status, activity, context, row.step.tokens
|
|
538
|
+
const details = [row.step.status, activity, context, row.step.tokens ? formatTokenUsage(row.step.tokens) : undefined].filter(Boolean).join(" · ");
|
|
537
539
|
lines.push(` ${row.index + 1}. ${workflowStepLabel(row.step, row.index)}${details ? ` — ${details}` : ""}`);
|
|
538
540
|
}
|
|
539
541
|
return lines;
|
|
@@ -547,6 +549,8 @@ function asyncDetail(item: Extract<FleetItem, { kind: "async" }>, state: Subagen
|
|
|
547
549
|
index: item.index,
|
|
548
550
|
lines: TRANSCRIPT_LINES,
|
|
549
551
|
sessionRoots: uniquePaths([...(state.trustedSessionRoots ?? []), trackedJob?.sessionRoot]),
|
|
552
|
+
trustedSessionFiles: [item.step?.sessionFile ?? item.run.sessionFile].filter((value): value is string => Boolean(value)),
|
|
553
|
+
trustedSessionFileRoot: state.trustedSessionFileRoot,
|
|
550
554
|
}).split("\n");
|
|
551
555
|
if (status.mode === "workflow" && item.index === undefined) {
|
|
552
556
|
const progress = workflowProgressLines((status.steps ?? item.run.steps) as AsyncJobStep[]);
|
|
@@ -628,7 +632,7 @@ function fleetArtifactsRoot(state: SubagentState, cwd: string): string {
|
|
|
628
632
|
);
|
|
629
633
|
}
|
|
630
634
|
|
|
631
|
-
function transcriptTarget(item: FleetItem, state: SubagentState): { path: string; trustedRoots: string[] } | undefined {
|
|
635
|
+
function transcriptTarget(item: FleetItem, state: SubagentState): { path: string; trustedRoots: string[]; trustedFiles?: string[]; trustedFileRoot?: string } | undefined {
|
|
632
636
|
if (item.kind === "external") return undefined;
|
|
633
637
|
if (item.kind === "foreground-active") {
|
|
634
638
|
const artifactsRoot = fleetArtifactsRoot(state, item.control.cwd ?? state.baseCwd);
|
|
@@ -651,10 +655,12 @@ function transcriptTarget(item: FleetItem, state: SubagentState): { path: string
|
|
|
651
655
|
};
|
|
652
656
|
}
|
|
653
657
|
const step = item.step ?? (item.run.steps.length === 1 ? item.run.steps[0] : undefined);
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
+
const recordedSessionFile = step?.sessionFile ?? item.run.sessionFile;
|
|
659
|
+
const recordedPath = step?.transcriptPath ?? recordedSessionFile;
|
|
660
|
+
if (!recordedPath) return undefined;
|
|
661
|
+
const transcriptPath = path.isAbsolute(recordedPath)
|
|
662
|
+
? recordedPath
|
|
663
|
+
: path.resolve(item.run.asyncDir, recordedPath);
|
|
658
664
|
const trackedJob = state.fleetJobs?.get(item.runId) ?? state.asyncJobs.get(item.runId);
|
|
659
665
|
return {
|
|
660
666
|
path: transcriptPath,
|
|
@@ -664,6 +670,7 @@ function transcriptTarget(item: FleetItem, state: SubagentState): { path: string
|
|
|
664
670
|
trackedJob?.cwd ? fleetArtifactsRoot(state, trackedJob.cwd) : undefined,
|
|
665
671
|
item.run.sessionFile ? getArtifactsDir(item.run.sessionFile, item.run.cwd ?? state.baseCwd, state.artifactDirPreference) : undefined,
|
|
666
672
|
]),
|
|
673
|
+
...(!step?.transcriptPath && recordedSessionFile ? { trustedFiles: [recordedSessionFile], trustedFileRoot: state.trustedSessionFileRoot } : {}),
|
|
667
674
|
};
|
|
668
675
|
}
|
|
669
676
|
|
|
@@ -687,23 +694,27 @@ function itemSource(item: FleetItem): string {
|
|
|
687
694
|
function itemStats(item: FleetItem): string[] {
|
|
688
695
|
let model: string | undefined;
|
|
689
696
|
let tokens: number | undefined;
|
|
697
|
+
let tokenUsage: AsyncJobStep["tokens"];
|
|
690
698
|
let tools: number | undefined;
|
|
691
699
|
let durationMs: number | undefined;
|
|
692
700
|
if (item.kind === "foreground-active") {
|
|
693
701
|
const live = item.activeChild ?? item.control;
|
|
694
702
|
model = formatModelThinking(live.model, live.thinking) || undefined;
|
|
695
703
|
tokens = live.tokens;
|
|
704
|
+
if (tokens !== undefined) tokenUsage = { input: live.inputTokens ?? 0, output: live.outputTokens ?? 0, total: tokens, ...(live.window !== undefined ? { window: live.window } : {}), ...(live.windowPeak !== undefined ? { windowPeak: live.windowPeak } : {}) };
|
|
696
705
|
tools = live.toolCount;
|
|
697
706
|
durationMs = Math.max(0, Date.now() - live.startedAt);
|
|
698
707
|
} else if (item.kind === "foreground-recent") {
|
|
699
708
|
model = formatModelThinking(item.child.model, item.child.thinking) || undefined;
|
|
700
709
|
tokens = item.child.tokens;
|
|
710
|
+
if (tokens !== undefined) tokenUsage = { input: 0, output: 0, total: tokens, ...(item.child.window !== undefined ? { window: item.child.window } : {}), ...(item.child.windowPeak !== undefined ? { windowPeak: item.child.windowPeak } : {}) };
|
|
701
711
|
tools = item.child.toolCount;
|
|
702
712
|
} else if (item.kind === "external") {
|
|
703
713
|
durationMs = Math.max(0, externalElapsedEnd(item.run) - item.run.startedAt);
|
|
704
714
|
} else {
|
|
705
715
|
model = formatModelThinking(item.step?.model, item.step?.thinking) || undefined;
|
|
706
|
-
|
|
716
|
+
tokenUsage = item.step?.tokens ?? (item.index === undefined ? item.run.totalTokens : undefined);
|
|
717
|
+
tokens = tokenUsage?.total;
|
|
707
718
|
tools = item.step?.toolCount ?? (item.index === undefined ? item.run.toolCount : undefined);
|
|
708
719
|
const terminalRun = item.state !== "queued" && item.state !== "running" && item.state !== "pending";
|
|
709
720
|
const endTime = item.run.endedAt ?? (terminalRun ? item.run.lastUpdate : undefined) ?? Date.now();
|
|
@@ -711,7 +722,7 @@ function itemStats(item: FleetItem): string[] {
|
|
|
711
722
|
}
|
|
712
723
|
return [
|
|
713
724
|
model,
|
|
714
|
-
tokens !== undefined ? `${formatTokens(tokens)} tok` : undefined,
|
|
725
|
+
tokenUsage ? formatTokenUsage(tokenUsage) : tokens !== undefined ? `${formatTokens(tokens)} tok` : undefined,
|
|
715
726
|
tools !== undefined ? `${tools} tool${tools === 1 ? "" : "s"}` : undefined,
|
|
716
727
|
durationMs !== undefined ? formatDuration(durationMs) : undefined,
|
|
717
728
|
].filter((value): value is string => Boolean(value));
|
|
@@ -1201,8 +1212,8 @@ export class SubagentFleetComponent implements Component {
|
|
|
1201
1212
|
});
|
|
1202
1213
|
}
|
|
1203
1214
|
|
|
1204
|
-
private renderedTranscript(target: { path: string; trustedRoots: string[] }, width: number): { transcript: FleetTranscript; body: string[] } {
|
|
1205
|
-
const fingerprint = `${target.trustedRoots.join("\0")}|${transcriptFingerprint(target.path)}`;
|
|
1215
|
+
private renderedTranscript(target: { path: string; trustedRoots: string[]; trustedFiles?: string[]; trustedFileRoot?: string }, width: number): { transcript: FleetTranscript; body: string[] } {
|
|
1216
|
+
const fingerprint = `${target.trustedRoots.join("\0")}|${target.trustedFiles?.join("\0") ?? ""}|${target.trustedFileRoot ?? ""}|${transcriptFingerprint(target.path)}`;
|
|
1206
1217
|
if (this.transcriptCache
|
|
1207
1218
|
&& this.transcriptCache.path === target.path
|
|
1208
1219
|
&& this.transcriptCache.fingerprint === fingerprint
|
|
@@ -1210,7 +1221,11 @@ export class SubagentFleetComponent implements Component {
|
|
|
1210
1221
|
&& this.transcriptCache.expandedTools === this.expandedTools) {
|
|
1211
1222
|
return { transcript: this.transcriptCache.transcript, body: [...this.transcriptCache.body] };
|
|
1212
1223
|
}
|
|
1213
|
-
const transcript = readFleetTranscript(target.path, {
|
|
1224
|
+
const transcript = readFleetTranscript(target.path, {
|
|
1225
|
+
trustedRoots: target.trustedRoots,
|
|
1226
|
+
...(target.trustedFiles ? { trustedFiles: target.trustedFiles } : {}),
|
|
1227
|
+
...(target.trustedFileRoot ? { trustedFileRoot: target.trustedFileRoot } : {}),
|
|
1228
|
+
});
|
|
1214
1229
|
const body = transcript.events.length > 0
|
|
1215
1230
|
? renderFleetTranscript(transcript, width, this.theme, this.markdownTheme, { expandedTools: this.expandedTools })
|
|
1216
1231
|
: [];
|
package/src/tui/render.ts
CHANGED
|
@@ -21,9 +21,10 @@ import {
|
|
|
21
21
|
WIDGET_ANIMATION_INTERVAL_MS,
|
|
22
22
|
WIDGET_KEY,
|
|
23
23
|
} from "../shared/types.ts";
|
|
24
|
-
import { sanitizeDisplayText, truncateDisplayText } from "../shared/display-text.ts";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
24
|
+
import { previewDisplayText, sanitizeDisplayText, truncateDisplayText } from "../shared/display-text.ts";
|
|
25
|
+
import { FLEET_OPEN_SHORTCUT, formatShortcutLabel } from "../shared/shortcuts.ts";
|
|
26
|
+
import { formatContextUsage, formatTokens, formatUsage, formatDuration, formatModelThinking, formatToolCall, formatTokenUsage, shortenPath } from "../shared/formatters.ts";
|
|
27
|
+
import { getDisplayItems, getSingleResultOutput, PROMPT_REDACTED } from "../shared/utils.ts";
|
|
27
28
|
import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.ts";
|
|
28
29
|
import { formatNestedAggregate } from "../runs/shared/nested-render.ts";
|
|
29
30
|
import { aggregateStepStatus, formatActivityLabel, formatAgentRunningLabel, formatParallelOutcome } from "../shared/status-format.ts";
|
|
@@ -69,23 +70,13 @@ function liveDetailKeyText(): string {
|
|
|
69
70
|
return keyText("app.tools.expand");
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
function liveDetailHintText(): string {
|
|
73
|
-
return `Press ${liveDetailKeyText()} for live detail`;
|
|
73
|
+
export function liveDetailHintText(): string {
|
|
74
|
+
return `Press ${liveDetailKeyText()} for live detail · ${formatShortcutLabel(FLEET_OPEN_SHORTCUT)} Fleet`;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
function foregroundSingleHintText(shortcut?: string): string {
|
|
77
78
|
if (!shortcut) return liveDetailHintText();
|
|
78
|
-
const label = shortcut
|
|
79
|
-
.split("+")
|
|
80
|
-
.map((part) => {
|
|
81
|
-
const normalized = part.trim().toLowerCase();
|
|
82
|
-
if (normalized === "ctrl") return "Ctrl";
|
|
83
|
-
if (normalized === "alt") return "Alt";
|
|
84
|
-
if (normalized === "shift") return "Shift";
|
|
85
|
-
if (normalized === "super") return "Super";
|
|
86
|
-
return normalized.length === 1 ? normalized.toUpperCase() : part.trim();
|
|
87
|
-
})
|
|
88
|
-
.join("+");
|
|
79
|
+
const label = formatShortcutLabel(shortcut);
|
|
89
80
|
return `${liveDetailHintText()} · ${label} to run in background`;
|
|
90
81
|
}
|
|
91
82
|
|
|
@@ -273,6 +264,33 @@ function oneLine(text: string): string {
|
|
|
273
264
|
return text.replace(ansiEscapePattern, "").replace(/\s+/g, " ").trim();
|
|
274
265
|
}
|
|
275
266
|
|
|
267
|
+
const COMPACT_TASK_MAX_CHARS = 96;
|
|
268
|
+
|
|
269
|
+
export function compactTaskText(task: string | undefined, label?: string): string | undefined {
|
|
270
|
+
const taskText = task?.trim();
|
|
271
|
+
const labelText = label?.trim();
|
|
272
|
+
const normalizedTask = taskText && taskText !== PROMPT_REDACTED ? oneLine(taskText) : "";
|
|
273
|
+
const normalizedLabel = labelText && labelText !== PROMPT_REDACTED ? oneLine(labelText) : "";
|
|
274
|
+
const normalized = normalizedLabel && normalizedTask && normalizedLabel !== normalizedTask
|
|
275
|
+
? `${normalizedLabel} — ${normalizedTask}`
|
|
276
|
+
: normalizedLabel || normalizedTask;
|
|
277
|
+
if (!normalized) return undefined;
|
|
278
|
+
return previewDisplayText(normalized, COMPACT_TASK_MAX_CHARS);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function workflowLabelForResult(details: Details, resultIndex: number): string | undefined {
|
|
282
|
+
const flatIndex = details.results[resultIndex]?.progress?.index ?? resultIndex;
|
|
283
|
+
const visit = (nodes: NonNullable<Details["workflowGraph"]>["nodes"]): string | undefined => {
|
|
284
|
+
for (const node of nodes) {
|
|
285
|
+
if (node.flatIndex === flatIndex && node.label.trim()) return node.label;
|
|
286
|
+
const nested = node.children ? visit(node.children) : undefined;
|
|
287
|
+
if (nested) return nested;
|
|
288
|
+
}
|
|
289
|
+
return undefined;
|
|
290
|
+
};
|
|
291
|
+
return details.workflowGraph ? visit(details.workflowGraph.nodes) : undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
276
294
|
function hasLiveOutputSignal(line: string): boolean {
|
|
277
295
|
const clean = oneLine(line);
|
|
278
296
|
return liveOutputWordSignalPattern.test(clean) || liveOutputCodeSignalPattern.test(clean);
|
|
@@ -740,7 +758,7 @@ function widgetStepActivity(step: NonNullable<AsyncJobState["steps"]>[number], s
|
|
|
740
758
|
if (step.currentPath) facts.push(shortenPath(step.currentPath));
|
|
741
759
|
if (step.turnCount !== undefined) facts.push(`${step.turnCount} turns`);
|
|
742
760
|
if (step.toolCount !== undefined) facts.push(`${step.toolCount} tools`);
|
|
743
|
-
if (step.tokens?.total) facts.push(
|
|
761
|
+
if (step.tokens?.total) facts.push(formatTokenUsage(step.tokens, "token"));
|
|
744
762
|
const activity = buildLiveStatusLine(step, snapshotNow);
|
|
745
763
|
if (activity && facts.length) return `${activity} · ${facts.join(" · ")}`;
|
|
746
764
|
if (activity) return activity;
|
|
@@ -780,7 +798,9 @@ function widgetParallelAgentDetails(job: AsyncJobState, theme: Theme, expanded =
|
|
|
780
798
|
const activity = widgetStepActivity(step, job.updatedAt);
|
|
781
799
|
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
782
800
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
783
|
-
|
|
801
|
+
const label = compactTaskText(step.description, step.label);
|
|
802
|
+
const display = label ? `${label} (${step.agent})` : step.agent;
|
|
803
|
+
lines.push(` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index), frame)} ${itemTitle} ${index + 1}/${total}: ${display} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`);
|
|
784
804
|
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 6)) lines.push(` ${nestedLine}`);
|
|
785
805
|
}
|
|
786
806
|
return lines;
|
|
@@ -1058,7 +1078,7 @@ function widgetStats(job: AsyncJobState, theme: Theme): string {
|
|
|
1058
1078
|
parts.push(`steps ${stepsTotal}`);
|
|
1059
1079
|
}
|
|
1060
1080
|
if (job.toolCount !== undefined) parts.push(formatToolUseStat(job.toolCount));
|
|
1061
|
-
if (job.totalTokens?.total) parts.push(
|
|
1081
|
+
if (job.totalTokens?.total) parts.push(formatTokenUsage(job.totalTokens, "token"));
|
|
1062
1082
|
if (job.startedAt !== undefined && job.updatedAt !== undefined) parts.push(formatDuration(Math.max(0, job.updatedAt - job.startedAt)));
|
|
1063
1083
|
return statJoin(theme, parts);
|
|
1064
1084
|
}
|
|
@@ -1067,7 +1087,11 @@ function widgetStepStats(theme: Theme, step: NonNullable<AsyncJobState["steps"]>
|
|
|
1067
1087
|
return statJoin(theme, [
|
|
1068
1088
|
step.turnCount !== undefined ? `${step.turnCount} turns` : "",
|
|
1069
1089
|
step.toolCount !== undefined ? formatToolUseStat(step.toolCount) : "",
|
|
1070
|
-
step.tokens
|
|
1090
|
+
step.tokens
|
|
1091
|
+
? step.contextLimit !== undefined
|
|
1092
|
+
? formatContextUsage(step.tokens, step.contextLimit) ?? formatTokenUsage(step.tokens, "token")
|
|
1093
|
+
: step.tokens.total ? formatTokenUsage(step.tokens, "token") : ""
|
|
1094
|
+
: "",
|
|
1071
1095
|
step.durationMs !== undefined ? formatDuration(step.durationMs) : "",
|
|
1072
1096
|
]);
|
|
1073
1097
|
}
|
|
@@ -1259,6 +1283,8 @@ function foregroundStyleWidgetStepLines(
|
|
|
1259
1283
|
const stats = widgetStepStats(theme, step);
|
|
1260
1284
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1261
1285
|
const lines = [` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1), frame)} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`];
|
|
1286
|
+
const task = compactTaskText(step.description, step.label);
|
|
1287
|
+
if (task) lines.push(` ${theme.fg("dim", `task: ${task}`)}`);
|
|
1262
1288
|
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
1263
1289
|
if (activity) lines.push(` ${theme.fg("dim", `⎿ ${activity}`)}`);
|
|
1264
1290
|
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 12 : 6)) {
|
|
@@ -1329,7 +1355,9 @@ function compactSingleWidgetLines(job: AsyncJobState, theme: Theme, width: numbe
|
|
|
1329
1355
|
const stepStats = widgetStepStats(theme, step);
|
|
1330
1356
|
const activitySuffix = activity ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", activity)}` : "";
|
|
1331
1357
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1332
|
-
|
|
1358
|
+
const task = compactTaskText(step.description, step.label);
|
|
1359
|
+
const taskSuffix = task ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", `task: ${task}`)}` : "";
|
|
1360
|
+
lines.push(` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index), frame)} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step.agent)}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${taskSuffix}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`);
|
|
1333
1361
|
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, false, job.updatedAt, 6)) lines.push(` ${nestedLine}`);
|
|
1334
1362
|
}
|
|
1335
1363
|
if (job.steps.some((step) => step.status === "running")) lines.push(theme.fg("accent", ` ${liveDetailHintText()}`));
|
|
@@ -1703,6 +1731,8 @@ function renderSingleCompact(
|
|
|
1703
1731
|
c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning, undefined, frame)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1704
1732
|
|
|
1705
1733
|
if (isRunning && r.progress) {
|
|
1734
|
+
const task = compactTaskText(r.task);
|
|
1735
|
+
if (task) c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}task: ${task}`), width), 0, 0));
|
|
1706
1736
|
const progressSnapshotNow = snapshotNowForProgress(r.progress);
|
|
1707
1737
|
const activity = compactCurrentActivity(r.progress);
|
|
1708
1738
|
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}⎿ ${activity}`), width), 0, 0));
|
|
@@ -1872,6 +1902,10 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1872
1902
|
const rowModelDisplay = modelThinkingBadge(theme, r.model ?? rowProgressModel?.model, r.thinking ?? rowProgressModel?.thinking);
|
|
1873
1903
|
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${rowModelDisplay}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1874
1904
|
c.addChild(new Text(truncLine(`${rowIndent}${line}`, width), 0, 0));
|
|
1905
|
+
if (rRunning || rPending) {
|
|
1906
|
+
const task = compactTaskText(r.task, workflowLabelForResult(d, i));
|
|
1907
|
+
if (task) c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}task: ${task}`), width), 0, 0));
|
|
1908
|
+
}
|
|
1875
1909
|
if (rRunning && rProg && "status" in rProg) {
|
|
1876
1910
|
const liveProgress = rProg as AgentProgress;
|
|
1877
1911
|
const activity = compactCurrentActivity(liveProgress);
|