@ian-pascoe/pi-minimal-subagents 0.1.0 → 0.2.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/README.md +77 -9
- package/package.json +6 -6
- package/src/minimal-subagents-capabilities.ts +8 -8
- package/src/minimal-subagents-config.ts +222 -73
- package/src/minimal-subagents-context.ts +7 -1
- package/src/minimal-subagents-coordinator.ts +943 -116
- package/src/minimal-subagents-delivery-ledger.ts +529 -0
- package/src/minimal-subagents-extension.ts +382 -64
- package/src/minimal-subagents-fork-lifecycle.ts +22 -12
- package/src/minimal-subagents-message-envelope.ts +20 -0
- package/src/minimal-subagents-registry-wire.ts +269 -0
- package/src/minimal-subagents-registry.ts +1505 -132
- package/src/minimal-subagents-render-contract.ts +301 -0
- package/src/minimal-subagents-rendering.ts +513 -372
- package/src/minimal-subagents-session-wire.ts +42 -0
- package/src/minimal-subagents-sessions.ts +437 -101
- package/src/minimal-subagents-shutdown.ts +1 -1
- package/src/minimal-subagents-tool-schemas.ts +21 -7
- package/src/minimal-subagents-tools.ts +75 -25
- package/src/minimal-subagents-types.ts +76 -21
- package/src/minimal-subagents-ui.ts +214 -23
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
getMarkdownTheme,
|
|
4
4
|
keyHint,
|
|
5
5
|
type AgentToolResult,
|
|
6
|
+
type MessageRenderer,
|
|
7
|
+
type MessageRenderOptions,
|
|
6
8
|
type Theme,
|
|
7
9
|
type ThemeColor,
|
|
8
10
|
type ToolRenderResultOptions,
|
|
@@ -17,25 +19,55 @@ import {
|
|
|
17
19
|
visibleWidth,
|
|
18
20
|
type Component,
|
|
19
21
|
} from "@earendil-works/pi-tui";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
import {
|
|
23
|
+
parseCoordinatorMessageDetails,
|
|
24
|
+
parseCoordinatorToolCall,
|
|
25
|
+
parseCoordinatorToolResult,
|
|
26
|
+
type CancelRenderDetails,
|
|
27
|
+
type CoordinatorMessageRenderDetails,
|
|
28
|
+
type CoordinatorToolCallInput,
|
|
29
|
+
type CoordinatorToolName,
|
|
30
|
+
type DeleteRenderDetails,
|
|
31
|
+
type ManagementCallArguments,
|
|
32
|
+
type MessageCallArguments,
|
|
33
|
+
type MessageRenderDetails,
|
|
34
|
+
type RenderStatusAgent,
|
|
35
|
+
type SpawnCallArguments,
|
|
36
|
+
type SpawnRenderDetails,
|
|
37
|
+
type StatusCallArguments,
|
|
38
|
+
type StatusRenderDetails,
|
|
39
|
+
type WaitCallArguments,
|
|
40
|
+
type WaitRenderDetails,
|
|
41
|
+
} from "./minimal-subagents-render-contract.js";
|
|
42
|
+
import { stripCoordinatorMessageEnvelope } from "./minimal-subagents-message-envelope.js";
|
|
43
|
+
|
|
44
|
+
export type { CoordinatorToolName } from "./minimal-subagents-render-contract.js";
|
|
45
|
+
|
|
46
|
+
/** Theme operations used by Minimal Subagents transcript renderers. */
|
|
47
|
+
export type MinimalSubagentsRenderTheme = Pick<Theme, "fg" | "bg" | "bold">;
|
|
48
|
+
|
|
49
|
+
/** Theme operation shared by transcript and widget status renderers. */
|
|
50
|
+
export type MinimalSubagentsStatusTheme = Pick<Theme, "fg">;
|
|
51
|
+
|
|
52
|
+
type RenderableCoordinatorMessage = Pick<Parameters<MessageRenderer>[0], "content" | "details">;
|
|
53
|
+
|
|
54
|
+
type SubagentPresentationStatus =
|
|
55
|
+
| "running"
|
|
56
|
+
| "waiting"
|
|
57
|
+
| "completed"
|
|
58
|
+
| "failed"
|
|
59
|
+
| "cancelled"
|
|
60
|
+
| "interrupted"
|
|
61
|
+
| "unavailable"
|
|
62
|
+
| "idle"
|
|
63
|
+
| "delivered"
|
|
64
|
+
| "delivered-via-wait"
|
|
65
|
+
| "queued"
|
|
66
|
+
| "message";
|
|
67
|
+
|
|
68
|
+
type SubagentStatusPresentation = { readonly symbol: string; readonly color: ThemeColor };
|
|
69
|
+
|
|
70
|
+
const SUBAGENT_STATUS_PRESENTATION = {
|
|
39
71
|
running: { symbol: "◉", color: "accent" },
|
|
40
72
|
waiting: { symbol: "◌", color: "accent" },
|
|
41
73
|
completed: { symbol: "✓", color: "success" },
|
|
@@ -45,38 +77,19 @@ const SUBAGENT_STATUS_PRESENTATION: Record<string, { symbol: string; color: Them
|
|
|
45
77
|
unavailable: { symbol: "!", color: "warning" },
|
|
46
78
|
idle: { symbol: "○", color: "dim" },
|
|
47
79
|
delivered: { symbol: "→", color: "accent" },
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return Array.isArray(value)
|
|
62
|
-
? value.filter((item): item is string => typeof item === "string")
|
|
63
|
-
: [];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function asNumber(value: unknown): number | undefined {
|
|
67
|
-
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function coordinatorMessageText(content: unknown): string {
|
|
71
|
-
if (typeof content === "string") return content;
|
|
72
|
-
if (!Array.isArray(content)) return "";
|
|
73
|
-
return content
|
|
74
|
-
.map((item) => {
|
|
75
|
-
const block = asRecord(item);
|
|
76
|
-
return block?.type === "text" ? (asString(block.text) ?? "") : "";
|
|
77
|
-
})
|
|
78
|
-
.filter(Boolean)
|
|
79
|
-
.join("\n");
|
|
80
|
+
"delivered-via-wait": { symbol: "→", color: "accent" },
|
|
81
|
+
queued: { symbol: "↗", color: "accent" },
|
|
82
|
+
message: { symbol: "→", color: "accent" },
|
|
83
|
+
} satisfies { readonly [Status in SubagentPresentationStatus]: SubagentStatusPresentation };
|
|
84
|
+
|
|
85
|
+
function coordinatorMessageText(content: RenderableCoordinatorMessage["content"]): string {
|
|
86
|
+
if (!Array.isArray(content)) return stripCoordinatorMessageEnvelope(content);
|
|
87
|
+
return stripCoordinatorMessageEnvelope(
|
|
88
|
+
content
|
|
89
|
+
.map((item) => (item.type === "text" ? item.text : ""))
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join("\n"),
|
|
92
|
+
);
|
|
80
93
|
}
|
|
81
94
|
|
|
82
95
|
function toolResultText(result: AgentToolResult<unknown>): string {
|
|
@@ -84,24 +97,37 @@ function toolResultText(result: AgentToolResult<unknown>): string {
|
|
|
84
97
|
return text?.type === "text" ? text.text : "";
|
|
85
98
|
}
|
|
86
99
|
|
|
100
|
+
function subagentStatusPresentation(status: string): SubagentStatusPresentation {
|
|
101
|
+
for (const [knownStatus, presentation] of Object.entries(SUBAGENT_STATUS_PRESENTATION)) {
|
|
102
|
+
if (knownStatus === status) return presentation;
|
|
103
|
+
}
|
|
104
|
+
return SUBAGENT_STATUS_PRESENTATION.idle;
|
|
105
|
+
}
|
|
106
|
+
|
|
87
107
|
/** Render the shared semantic symbol and color for one subagent status. */
|
|
88
|
-
export function renderSubagentStatusSymbol(
|
|
89
|
-
|
|
108
|
+
export function renderSubagentStatusSymbol(
|
|
109
|
+
theme: MinimalSubagentsStatusTheme,
|
|
110
|
+
status: string,
|
|
111
|
+
): string {
|
|
112
|
+
const presentation = subagentStatusPresentation(status);
|
|
90
113
|
return theme.fg(presentation.color, presentation.symbol);
|
|
91
114
|
}
|
|
92
115
|
|
|
93
116
|
/** Render a subagent status label with the same semantic color as its symbol. */
|
|
94
|
-
export function renderSubagentStatusLabel(
|
|
95
|
-
|
|
117
|
+
export function renderSubagentStatusLabel(
|
|
118
|
+
theme: MinimalSubagentsStatusTheme,
|
|
119
|
+
status: string,
|
|
120
|
+
): string {
|
|
121
|
+
const presentation = subagentStatusPresentation(status);
|
|
96
122
|
return theme.fg(presentation.color, status);
|
|
97
123
|
}
|
|
98
124
|
|
|
99
|
-
function renderSubagentSeparator(theme:
|
|
125
|
+
function renderSubagentSeparator(theme: MinimalSubagentsRenderTheme): string {
|
|
100
126
|
return theme.fg("dim", " · ");
|
|
101
127
|
}
|
|
102
128
|
|
|
103
129
|
function renderSubagentSummary(
|
|
104
|
-
theme:
|
|
130
|
+
theme: MinimalSubagentsRenderTheme,
|
|
105
131
|
status: string,
|
|
106
132
|
agentId: string,
|
|
107
133
|
metrics: readonly string[] = [],
|
|
@@ -114,32 +140,49 @@ function renderSubagentSummary(
|
|
|
114
140
|
].join(renderSubagentSeparator(theme));
|
|
115
141
|
}
|
|
116
142
|
|
|
117
|
-
function renderLabelValue(theme:
|
|
118
|
-
|
|
119
|
-
return new Text(`${theme.fg("muted", `${label}:`)} ${text ?? ""}`, 0, 0);
|
|
143
|
+
function renderLabelValue(theme: MinimalSubagentsRenderTheme, label: string, value: string): Text {
|
|
144
|
+
return new Text(`${theme.fg("muted", `${label}:`)} ${value}`, 0, 0);
|
|
120
145
|
}
|
|
121
146
|
|
|
122
|
-
function
|
|
147
|
+
function appendSectionHeading(
|
|
123
148
|
container: Container,
|
|
124
|
-
theme:
|
|
149
|
+
theme: MinimalSubagentsRenderTheme,
|
|
125
150
|
label: string,
|
|
126
|
-
content: string | Component,
|
|
127
151
|
): void {
|
|
128
152
|
container.addChild(new Spacer(1));
|
|
129
153
|
container.addChild(new Text(theme.fg("muted", theme.bold(label)), 0, 0));
|
|
130
|
-
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function appendTextSection(
|
|
157
|
+
container: Container,
|
|
158
|
+
theme: MinimalSubagentsRenderTheme,
|
|
159
|
+
label: string,
|
|
160
|
+
content: string,
|
|
161
|
+
): void {
|
|
162
|
+
appendSectionHeading(container, theme, label);
|
|
163
|
+
container.addChild(new Text(content, 0, 0));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function appendComponentSection(
|
|
167
|
+
container: Container,
|
|
168
|
+
theme: MinimalSubagentsRenderTheme,
|
|
169
|
+
label: string,
|
|
170
|
+
content: Component,
|
|
171
|
+
): void {
|
|
172
|
+
appendSectionHeading(container, theme, label);
|
|
173
|
+
container.addChild(content);
|
|
131
174
|
}
|
|
132
175
|
|
|
133
176
|
function renderFallbackToolResult(
|
|
134
177
|
result: AgentToolResult<unknown>,
|
|
135
|
-
theme:
|
|
178
|
+
theme: MinimalSubagentsRenderTheme,
|
|
136
179
|
isError: boolean,
|
|
137
180
|
): Component {
|
|
138
181
|
const content = toolResultText(result) || "(no output)";
|
|
139
182
|
return new Text(isError ? theme.fg("error", content) : content, 0, 0);
|
|
140
183
|
}
|
|
141
184
|
|
|
142
|
-
function collapsedExpansionHint(theme:
|
|
185
|
+
function collapsedExpansionHint(theme: MinimalSubagentsRenderTheme): string {
|
|
143
186
|
return theme.fg("dim", ` · ${keyHint("app.tools.expand", "to expand")}`);
|
|
144
187
|
}
|
|
145
188
|
|
|
@@ -165,8 +208,8 @@ export function formatSubagentTokenCount(tokens: number | undefined): string | u
|
|
|
165
208
|
}
|
|
166
209
|
|
|
167
210
|
/** Collapse multiline task or message text into one terminal-friendly preview. */
|
|
168
|
-
export function formatSubagentPreview(content: string, maxWidth = 72): string {
|
|
169
|
-
const singleLine = content.replace(/\s+/g, " ").trim();
|
|
211
|
+
export function formatSubagentPreview(content: string | undefined, maxWidth = 72): string {
|
|
212
|
+
const singleLine = (content ?? "").replace(/\s+/g, " ").trim();
|
|
170
213
|
const boundedWidth = Math.max(1, maxWidth);
|
|
171
214
|
if (visibleWidth(singleLine) <= boundedWidth) return singleLine;
|
|
172
215
|
if (boundedWidth === 1) return "…";
|
|
@@ -179,83 +222,106 @@ function currentSubagentPreviewWidth(reservedWidth: number): number {
|
|
|
179
222
|
|
|
180
223
|
/** Format complete Pi usage metrics for expanded subagent output. */
|
|
181
224
|
export function formatSubagentUsage(usage: Usage | undefined): string | undefined {
|
|
182
|
-
|
|
183
|
-
if (!usageRecord) return undefined;
|
|
225
|
+
if (usage === undefined) return undefined;
|
|
184
226
|
const values = [
|
|
185
|
-
`input ${formatSubagentTokenCount(
|
|
186
|
-
`output ${formatSubagentTokenCount(
|
|
187
|
-
`cache read ${formatSubagentTokenCount(
|
|
188
|
-
`cache write ${formatSubagentTokenCount(
|
|
189
|
-
`total ${formatSubagentTokenCount(
|
|
227
|
+
`input ${formatSubagentTokenCount(usage.input) ?? "0"}`,
|
|
228
|
+
`output ${formatSubagentTokenCount(usage.output) ?? "0"}`,
|
|
229
|
+
`cache read ${formatSubagentTokenCount(usage.cacheRead) ?? "0"}`,
|
|
230
|
+
`cache write ${formatSubagentTokenCount(usage.cacheWrite) ?? "0"}`,
|
|
231
|
+
`total ${formatSubagentTokenCount(usage.totalTokens) ?? "0"}`,
|
|
190
232
|
];
|
|
191
|
-
|
|
192
|
-
if (totalCost !== undefined && totalCost > 0) values.push(`cost $${totalCost.toFixed(4)}`);
|
|
233
|
+
if (usage.cost.total > 0) values.push(`cost $${usage.cost.total.toFixed(4)}`);
|
|
193
234
|
return values.join(" · ");
|
|
194
235
|
}
|
|
195
236
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
function coordinatorToolCallTitle(theme: Theme, label: string): string {
|
|
237
|
+
function coordinatorToolCallTitle(theme: MinimalSubagentsRenderTheme, label: string): string {
|
|
199
238
|
return theme.fg("toolTitle", theme.bold(label));
|
|
200
239
|
}
|
|
201
240
|
|
|
202
|
-
function coordinatorToolCallPreview(
|
|
203
|
-
|
|
241
|
+
function coordinatorToolCallPreview(
|
|
242
|
+
theme: MinimalSubagentsRenderTheme,
|
|
243
|
+
value: string | undefined,
|
|
244
|
+
): string {
|
|
245
|
+
return value && value.length > 0
|
|
204
246
|
? ` · ${theme.fg("dim", `“${formatSubagentPreview(value, currentSubagentPreviewWidth(36))}”`)}`
|
|
205
247
|
: "";
|
|
206
248
|
}
|
|
207
249
|
|
|
208
250
|
function renderManagementToolCall(
|
|
209
251
|
label: string,
|
|
210
|
-
args:
|
|
211
|
-
theme:
|
|
252
|
+
args: ManagementCallArguments,
|
|
253
|
+
theme: MinimalSubagentsRenderTheme,
|
|
212
254
|
): Component {
|
|
213
255
|
return new Text(
|
|
214
|
-
`${coordinatorToolCallTitle(theme, label)} ${theme.fg("accent",
|
|
256
|
+
`${coordinatorToolCallTitle(theme, label)} ${theme.fg("accent", args.agent_id ?? "agent")} ${theme.fg("dim", args.recursive === false ? "· target only" : "· recursive")}`,
|
|
215
257
|
0,
|
|
216
258
|
0,
|
|
217
259
|
);
|
|
218
260
|
}
|
|
219
261
|
|
|
220
|
-
|
|
262
|
+
type CoordinatorToolCallRenderers = {
|
|
263
|
+
readonly subagent: (args: SpawnCallArguments, theme: MinimalSubagentsRenderTheme) => Component;
|
|
264
|
+
readonly agent_message: (
|
|
265
|
+
args: MessageCallArguments,
|
|
266
|
+
theme: MinimalSubagentsRenderTheme,
|
|
267
|
+
) => Component;
|
|
268
|
+
readonly subagent_wait: (
|
|
269
|
+
args: WaitCallArguments,
|
|
270
|
+
theme: MinimalSubagentsRenderTheme,
|
|
271
|
+
) => Component;
|
|
272
|
+
readonly subagent_status: (
|
|
273
|
+
args: StatusCallArguments,
|
|
274
|
+
theme: MinimalSubagentsRenderTheme,
|
|
275
|
+
) => Component;
|
|
276
|
+
readonly subagent_cancel: (
|
|
277
|
+
args: ManagementCallArguments,
|
|
278
|
+
theme: MinimalSubagentsRenderTheme,
|
|
279
|
+
) => Component;
|
|
280
|
+
readonly subagent_delete: (
|
|
281
|
+
args: ManagementCallArguments,
|
|
282
|
+
theme: MinimalSubagentsRenderTheme,
|
|
283
|
+
) => Component;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const COORDINATOR_TOOL_CALL_RENDERERS = {
|
|
221
287
|
subagent: (args, theme) =>
|
|
222
288
|
new Text(
|
|
223
|
-
`${coordinatorToolCallTitle(theme, "Subagent")} ${theme.fg("accent",
|
|
289
|
+
`${coordinatorToolCallTitle(theme, "Subagent")} ${theme.fg("accent", args.agent_id ?? "generated")}${coordinatorToolCallPreview(theme, args.task)}`,
|
|
224
290
|
0,
|
|
225
291
|
0,
|
|
226
292
|
),
|
|
227
293
|
agent_message: (args, theme) =>
|
|
228
294
|
new Text(
|
|
229
|
-
`${coordinatorToolCallTitle(theme, "Message")} ${theme.fg("accent",
|
|
295
|
+
`${coordinatorToolCallTitle(theme, "Message")} ${theme.fg("accent", args.agent_id ?? "parent")}${coordinatorToolCallPreview(theme, args.message)}`,
|
|
230
296
|
0,
|
|
231
297
|
0,
|
|
232
298
|
),
|
|
233
299
|
subagent_wait: (args, theme) =>
|
|
234
300
|
new Text(
|
|
235
|
-
`${coordinatorToolCallTitle(theme, "Wait")} ${theme.fg("accent",
|
|
301
|
+
`${coordinatorToolCallTitle(theme, "Wait")} ${theme.fg("accent", args.agent_id ?? "agent")}`,
|
|
236
302
|
0,
|
|
237
303
|
0,
|
|
238
304
|
),
|
|
239
305
|
subagent_status: (args, theme) =>
|
|
240
306
|
new Text(
|
|
241
|
-
`${coordinatorToolCallTitle(theme, "Status")} ${theme.fg("accent",
|
|
307
|
+
`${coordinatorToolCallTitle(theme, "Status")} ${theme.fg("accent", args.agent_id ?? "children")}`,
|
|
242
308
|
0,
|
|
243
309
|
0,
|
|
244
310
|
),
|
|
245
311
|
subagent_cancel: (args, theme) => renderManagementToolCall("Cancel", args, theme),
|
|
246
312
|
subagent_delete: (args, theme) => renderManagementToolCall("Delete", args, theme),
|
|
247
|
-
};
|
|
313
|
+
} satisfies CoordinatorToolCallRenderers;
|
|
248
314
|
|
|
249
315
|
function renderSpawnResult(
|
|
250
|
-
details:
|
|
316
|
+
details: SpawnRenderDetails,
|
|
251
317
|
options: ToolRenderResultOptions,
|
|
252
|
-
theme:
|
|
253
|
-
args:
|
|
318
|
+
theme: MinimalSubagentsRenderTheme,
|
|
319
|
+
args: SpawnCallArguments,
|
|
254
320
|
): Component {
|
|
255
|
-
const agentId =
|
|
256
|
-
const status =
|
|
257
|
-
const agent =
|
|
258
|
-
const launchContract =
|
|
321
|
+
const agentId = details.agent_id;
|
|
322
|
+
const status = details.status;
|
|
323
|
+
const agent = details.agent;
|
|
324
|
+
const launchContract = agent?.launch_contract;
|
|
259
325
|
if (!options.expanded) {
|
|
260
326
|
return new Text(
|
|
261
327
|
`${renderSubagentSummary(theme, status, agentId)}${collapsedExpansionHint(theme)}`,
|
|
@@ -265,64 +331,72 @@ function renderSpawnResult(
|
|
|
265
331
|
}
|
|
266
332
|
const container = new Container();
|
|
267
333
|
container.addChild(new Text(renderSubagentSummary(theme, status, agentId), 0, 0));
|
|
268
|
-
container.addChild(renderLabelValue(theme, "Turn",
|
|
269
|
-
|
|
270
|
-
const resolvedModel =
|
|
271
|
-
const resolvedThinking =
|
|
272
|
-
asString(launchContract?.thinking_level) ?? asString(args.thinking_level);
|
|
334
|
+
container.addChild(renderLabelValue(theme, "Turn", details.turn_id));
|
|
335
|
+
appendTextSection(container, theme, "Task", args.task ?? "(task unavailable)");
|
|
336
|
+
const resolvedModel = launchContract?.model ?? args.model;
|
|
337
|
+
const resolvedThinking = launchContract?.thinking_level ?? args.thinking_level;
|
|
273
338
|
const launch = [
|
|
274
|
-
`delegation ${
|
|
275
|
-
`session context ${
|
|
276
|
-
`project context ${
|
|
339
|
+
`delegation ${launchContract?.delegation ?? args.delegation ?? "none"}`,
|
|
340
|
+
`session context ${launchContract?.session_context ?? args.session_context ?? "inherit"}`,
|
|
341
|
+
`project context ${launchContract?.project_context ?? args.project_context ?? "inherit"}`,
|
|
277
342
|
resolvedModel ? `model ${resolvedModel}` : undefined,
|
|
278
343
|
resolvedThinking ? `thinking ${resolvedThinking}` : undefined,
|
|
279
|
-
].filter(
|
|
280
|
-
|
|
281
|
-
|
|
344
|
+
].filter((value): value is string => value !== undefined);
|
|
345
|
+
appendTextSection(container, theme, "Launch", launch.join(" · "));
|
|
346
|
+
appendTextSection(
|
|
282
347
|
container,
|
|
283
348
|
theme,
|
|
284
349
|
"Resolved tools",
|
|
285
|
-
|
|
350
|
+
(launchContract?.ordinary_tools ?? agent?.tools ?? []).join(", ") || "none",
|
|
286
351
|
);
|
|
287
352
|
return container;
|
|
288
353
|
}
|
|
289
354
|
|
|
355
|
+
function messageDisposition(details: MessageRenderDetails): string {
|
|
356
|
+
return "disposition" in details
|
|
357
|
+
? details.disposition
|
|
358
|
+
: details.delivered
|
|
359
|
+
? "delivered"
|
|
360
|
+
: "failed";
|
|
361
|
+
}
|
|
362
|
+
|
|
290
363
|
function renderMessageResult(
|
|
291
|
-
details:
|
|
364
|
+
details: MessageRenderDetails,
|
|
292
365
|
options: ToolRenderResultOptions,
|
|
293
|
-
theme:
|
|
294
|
-
args:
|
|
366
|
+
theme: MinimalSubagentsRenderTheme,
|
|
367
|
+
args: MessageCallArguments,
|
|
295
368
|
): Component {
|
|
296
|
-
const agentId =
|
|
297
|
-
const historicalBehavior =
|
|
369
|
+
const agentId = details.agent_id ?? args.agent_id ?? "parent";
|
|
370
|
+
const historicalBehavior = details.behavior ?? args.behavior;
|
|
298
371
|
const metrics = historicalBehavior ? [historicalBehavior] : [];
|
|
299
|
-
const
|
|
300
|
-
const summary =
|
|
301
|
-
? renderSubagentSummary(theme, "delivered", agentId, metrics)
|
|
302
|
-
: renderSubagentSummary(theme, "failed", agentId, metrics);
|
|
372
|
+
const disposition = messageDisposition(details);
|
|
373
|
+
const summary = renderSubagentSummary(theme, disposition, agentId, metrics);
|
|
303
374
|
if (!options.expanded) return new Text(`${summary}${collapsedExpansionHint(theme)}`, 0, 0);
|
|
304
375
|
const container = new Container();
|
|
305
376
|
container.addChild(new Text(summary, 0, 0));
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (error)
|
|
377
|
+
appendTextSection(container, theme, "Message", args.message ?? "(message unavailable)");
|
|
378
|
+
appendTextSection(container, theme, "Recipient", agentId);
|
|
379
|
+
appendTextSection(container, theme, "Disposition", disposition);
|
|
380
|
+
if (details.error) appendTextSection(container, theme, "Error", details.error);
|
|
310
381
|
return container;
|
|
311
382
|
}
|
|
312
383
|
|
|
313
384
|
function renderWaitResult(
|
|
314
|
-
details:
|
|
385
|
+
details: WaitRenderDetails,
|
|
315
386
|
options: ToolRenderResultOptions,
|
|
316
|
-
theme:
|
|
317
|
-
args:
|
|
387
|
+
theme: MinimalSubagentsRenderTheme,
|
|
388
|
+
args: WaitCallArguments,
|
|
318
389
|
): Component {
|
|
319
|
-
const agentId =
|
|
320
|
-
const status = options.isPartial
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
390
|
+
const agentId = details.agent_id ?? args.agent_id ?? "agent";
|
|
391
|
+
const status = options.isPartial
|
|
392
|
+
? "waiting"
|
|
393
|
+
: details.event === "message"
|
|
394
|
+
? "message"
|
|
395
|
+
: details.status;
|
|
396
|
+
const duration = formatSubagentDuration(details.elapsed_ms);
|
|
397
|
+
const tokens = formatSubagentTokenCount(details.usage?.totalTokens);
|
|
324
398
|
const metrics = [duration, tokens ? `${tokens} tokens` : undefined].filter(
|
|
325
|
-
(metric): metric is string =>
|
|
399
|
+
(metric): metric is string => metric !== undefined,
|
|
326
400
|
);
|
|
327
401
|
const summary = renderSubagentSummary(theme, status, agentId, metrics);
|
|
328
402
|
if (options.isPartial || !options.expanded) {
|
|
@@ -330,310 +404,329 @@ function renderWaitResult(
|
|
|
330
404
|
}
|
|
331
405
|
const container = new Container();
|
|
332
406
|
container.addChild(new Text(summary, 0, 0));
|
|
333
|
-
container.addChild(renderLabelValue(theme, "Turn",
|
|
334
|
-
|
|
407
|
+
container.addChild(renderLabelValue(theme, "Turn", details.turn_id ?? "unknown"));
|
|
408
|
+
if (details.event === "message") {
|
|
409
|
+
appendTextSection(container, theme, "Message", details.message);
|
|
410
|
+
container.addChild(renderLabelValue(theme, "Message ID", details.message_id));
|
|
411
|
+
return container;
|
|
412
|
+
}
|
|
413
|
+
const output = details.output ?? "";
|
|
335
414
|
if (status === "completed") {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
415
|
+
if (output.length > 0) {
|
|
416
|
+
appendComponentSection(
|
|
417
|
+
container,
|
|
418
|
+
theme,
|
|
419
|
+
"Output",
|
|
420
|
+
new Markdown(output, 0, 0, getMarkdownTheme()),
|
|
421
|
+
);
|
|
422
|
+
} else {
|
|
423
|
+
appendTextSection(container, theme, "Output", "(no output)");
|
|
424
|
+
}
|
|
342
425
|
} else {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
theme,
|
|
346
|
-
"Error",
|
|
347
|
-
(asString(details.error) ?? output) || "(no error detail)",
|
|
348
|
-
);
|
|
349
|
-
appendSection(container, theme, "Diagnostics", JSON.stringify(details, null, 2));
|
|
426
|
+
appendTextSection(container, theme, "Error", details.error ?? (output || "(no error detail)"));
|
|
427
|
+
appendTextSection(container, theme, "Diagnostics", JSON.stringify(details, null, 2));
|
|
350
428
|
}
|
|
351
|
-
const usageText = formatSubagentUsage(usage);
|
|
352
|
-
if (usageText)
|
|
429
|
+
const usageText = formatSubagentUsage(details.usage);
|
|
430
|
+
if (usageText) appendTextSection(container, theme, "Usage", usageText);
|
|
353
431
|
return container;
|
|
354
432
|
}
|
|
355
433
|
|
|
356
|
-
|
|
357
|
-
|
|
434
|
+
interface DirectStatusCounts {
|
|
435
|
+
children: number;
|
|
436
|
+
running: number;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function countDirectStatusAgents(agents: readonly RenderStatusAgent[]): DirectStatusCounts {
|
|
358
440
|
let running = 0;
|
|
359
|
-
for (const
|
|
360
|
-
const agent = asRecord(item);
|
|
361
|
-
if (!agent) continue;
|
|
362
|
-
children++;
|
|
441
|
+
for (const agent of agents) {
|
|
363
442
|
if (agent.state === "running") running++;
|
|
364
443
|
}
|
|
365
|
-
return { children, running };
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const duration = formatSubagentDuration(asNumber(agent.elapsed_ms));
|
|
382
|
-
const childCount = asNumber(agent.child_count) ?? 0;
|
|
444
|
+
return { children: agents.length, running };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function statusAgentPresentation(agent: RenderStatusAgent): string {
|
|
448
|
+
if (agent.availability === "unavailable") return "unavailable";
|
|
449
|
+
if (agent.state === "running") return "running";
|
|
450
|
+
return agent.latest_turn?.status ?? "idle";
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function renderDirectStatusRows(
|
|
454
|
+
agents: readonly RenderStatusAgent[],
|
|
455
|
+
theme: MinimalSubagentsRenderTheme,
|
|
456
|
+
): string[] {
|
|
457
|
+
return agents.map((agent) => {
|
|
458
|
+
const duration = formatSubagentDuration(agent.elapsed_ms);
|
|
459
|
+
const childCount = agent.child_count ?? 0;
|
|
383
460
|
const metrics = [duration, childCount > 0 ? `${childCount} children` : undefined].filter(
|
|
384
|
-
(metric): metric is string =>
|
|
461
|
+
(metric): metric is string => metric !== undefined,
|
|
385
462
|
);
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
463
|
+
return renderSubagentSummary(
|
|
464
|
+
theme,
|
|
465
|
+
statusAgentPresentation(agent),
|
|
466
|
+
agent.agent_id ?? "unknown",
|
|
467
|
+
metrics,
|
|
468
|
+
);
|
|
469
|
+
});
|
|
389
470
|
}
|
|
390
471
|
|
|
391
|
-
|
|
392
|
-
|
|
472
|
+
type StatusLabelValue = { readonly label: string; readonly value: string | undefined };
|
|
473
|
+
|
|
474
|
+
function renderDetailedStatusAgent(
|
|
475
|
+
agent: RenderStatusAgent,
|
|
393
476
|
options: ToolRenderResultOptions,
|
|
394
|
-
theme:
|
|
477
|
+
theme: MinimalSubagentsRenderTheme,
|
|
395
478
|
): Component {
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (!options.expanded) return new Text(`${summary}${collapsedExpansionHint(theme)}`, 0, 0);
|
|
404
|
-
return new Text(
|
|
405
|
-
`${summary}\n${renderDirectStatusRows(agents, theme).join("\n") || theme.fg("dim", "(no agents)")}`,
|
|
406
|
-
0,
|
|
407
|
-
0,
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
const agent = asRecord(details.agent);
|
|
411
|
-
if (!agent) {
|
|
412
|
-
return new Text(
|
|
413
|
-
[theme.fg("muted", "0 children"), theme.fg("dim", "0 running")].join(
|
|
414
|
-
renderSubagentSeparator(theme),
|
|
415
|
-
),
|
|
416
|
-
0,
|
|
417
|
-
0,
|
|
418
|
-
);
|
|
419
|
-
}
|
|
420
|
-
const availability = asString(agent.availability) ?? "available";
|
|
421
|
-
const latestTurn = asRecord(agent.latest_turn);
|
|
422
|
-
const status =
|
|
423
|
-
availability === "unavailable"
|
|
424
|
-
? "unavailable"
|
|
425
|
-
: asString(agent.state) === "running"
|
|
426
|
-
? "running"
|
|
427
|
-
: (asString(latestTurn?.status) ?? "idle");
|
|
428
|
-
const id = asString(agent.agent_id) ?? "agent";
|
|
429
|
-
const childCount = asNumber(agent.child_count) ?? 0;
|
|
430
|
-
const duration = formatSubagentDuration(asNumber(agent.elapsed_ms));
|
|
431
|
-
const summary = renderSubagentSummary(
|
|
432
|
-
theme,
|
|
433
|
-
status,
|
|
434
|
-
id,
|
|
435
|
-
[duration, `${childCount} children`].filter((metric): metric is string => Boolean(metric)),
|
|
479
|
+
const availability = agent.availability ?? "available";
|
|
480
|
+
const status = statusAgentPresentation(agent);
|
|
481
|
+
const id = agent.agent_id ?? "agent";
|
|
482
|
+
const childCount = agent.child_count ?? 0;
|
|
483
|
+
const duration = formatSubagentDuration(agent.elapsed_ms);
|
|
484
|
+
const metrics = [duration, `${childCount} children`].filter(
|
|
485
|
+
(metric): metric is string => metric !== undefined,
|
|
436
486
|
);
|
|
487
|
+
const summary = renderSubagentSummary(theme, status, id, metrics);
|
|
437
488
|
if (!options.expanded) return new Text(`${summary}${collapsedExpansionHint(theme)}`, 0, 0);
|
|
438
489
|
const container = new Container();
|
|
439
490
|
container.addChild(new Text(summary, 0, 0));
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
]
|
|
491
|
+
const labels = [
|
|
492
|
+
{ label: "Parent", value: agent.parent_id },
|
|
493
|
+
{ label: "Availability", value: availability },
|
|
494
|
+
{ label: "Turn", value: agent.active_turn_id ?? agent.latest_turn?.turn_id },
|
|
495
|
+
{ label: "Duration", value: duration },
|
|
496
|
+
{ label: "Model", value: agent.model },
|
|
497
|
+
{ label: "Thinking", value: agent.thinking_level },
|
|
498
|
+
{ label: "Session", value: agent.session_file },
|
|
499
|
+
{ label: "Spawn entry", value: agent.spawn_entry_id },
|
|
500
|
+
] satisfies readonly StatusLabelValue[];
|
|
501
|
+
for (const { label, value } of labels) {
|
|
450
502
|
if (value !== undefined) container.addChild(renderLabelValue(theme, label, value));
|
|
451
503
|
}
|
|
452
|
-
if (
|
|
453
|
-
const launchContract =
|
|
504
|
+
if (agent.task) appendTextSection(container, theme, "Task", agent.task);
|
|
505
|
+
const launchContract = agent.launch_contract;
|
|
454
506
|
if (launchContract) {
|
|
455
507
|
const launchValues = [
|
|
456
|
-
`session context ${
|
|
457
|
-
`project context ${
|
|
458
|
-
`model ${
|
|
459
|
-
`thinking ${
|
|
460
|
-
`delegation ${
|
|
508
|
+
`session context ${launchContract.session_context ?? "inherit"}`,
|
|
509
|
+
`project context ${launchContract.project_context ?? "inherit"}`,
|
|
510
|
+
`model ${launchContract.model ?? agent.model ?? "unknown"}`,
|
|
511
|
+
`thinking ${launchContract.thinking_level ?? agent.thinking_level ?? "unknown"}`,
|
|
512
|
+
`delegation ${launchContract.delegation ?? "none"}`,
|
|
461
513
|
];
|
|
462
|
-
|
|
514
|
+
appendTextSection(container, theme, "Launch contract", launchValues.join(" · "));
|
|
463
515
|
}
|
|
464
|
-
|
|
465
|
-
|
|
516
|
+
appendTextSection(container, theme, "Tools", (agent.tools ?? []).join(", ") || "none");
|
|
517
|
+
appendTextSection(
|
|
466
518
|
container,
|
|
467
519
|
theme,
|
|
468
520
|
"Capability ceiling",
|
|
469
|
-
|
|
521
|
+
(agent.capability_ceiling ?? []).join(", ") || "none",
|
|
470
522
|
);
|
|
471
|
-
const missing =
|
|
472
|
-
if (missing.length > 0)
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
523
|
+
const missing = agent.missing_dependencies ?? [];
|
|
524
|
+
if (missing.length > 0) {
|
|
525
|
+
appendTextSection(container, theme, "Missing dependencies", missing.join("\n"));
|
|
526
|
+
}
|
|
527
|
+
if (agent.unavailable_reason) {
|
|
528
|
+
appendTextSection(container, theme, "Unavailable reason", agent.unavailable_reason);
|
|
476
529
|
}
|
|
477
|
-
const recentMessages =
|
|
478
|
-
? agent.recent_messages
|
|
479
|
-
.map(asRecord)
|
|
480
|
-
.filter((item): item is Record<string, unknown> => Boolean(item))
|
|
481
|
-
: [];
|
|
530
|
+
const recentMessages = agent.recent_messages ?? [];
|
|
482
531
|
if (recentMessages.length > 0) {
|
|
483
|
-
|
|
532
|
+
appendTextSection(
|
|
484
533
|
container,
|
|
485
534
|
theme,
|
|
486
535
|
"Recent messages",
|
|
487
536
|
recentMessages
|
|
488
|
-
.map(
|
|
489
|
-
(message) =>
|
|
490
|
-
`${asString(message.source_agent_id) ?? "unknown"}: ${asString(message.content) ?? ""}`,
|
|
491
|
-
)
|
|
537
|
+
.map((message) => `${message.source_agent_id ?? "unknown"}: ${message.content ?? ""}`)
|
|
492
538
|
.join("\n"),
|
|
493
539
|
);
|
|
494
540
|
}
|
|
495
|
-
const latestResult =
|
|
541
|
+
const latestResult = agent.latest_result;
|
|
496
542
|
if (latestResult) {
|
|
497
|
-
const output =
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
543
|
+
const output = latestResult.output ?? "";
|
|
544
|
+
if (latestResult.status === "completed" && output) {
|
|
545
|
+
appendComponentSection(
|
|
546
|
+
container,
|
|
547
|
+
theme,
|
|
548
|
+
"Latest result",
|
|
549
|
+
new Markdown(output, 0, 0, getMarkdownTheme()),
|
|
550
|
+
);
|
|
551
|
+
} else {
|
|
552
|
+
appendTextSection(
|
|
553
|
+
container,
|
|
554
|
+
theme,
|
|
555
|
+
"Latest result",
|
|
556
|
+
output || JSON.stringify(latestResult, null, 2),
|
|
557
|
+
);
|
|
558
|
+
}
|
|
506
559
|
}
|
|
507
|
-
const usageText = formatSubagentUsage(
|
|
508
|
-
if (usageText)
|
|
560
|
+
const usageText = formatSubagentUsage(agent.usage);
|
|
561
|
+
if (usageText) appendTextSection(container, theme, "Usage", usageText);
|
|
509
562
|
return container;
|
|
510
563
|
}
|
|
511
564
|
|
|
565
|
+
function renderStatusResult(
|
|
566
|
+
details: StatusRenderDetails,
|
|
567
|
+
options: ToolRenderResultOptions,
|
|
568
|
+
theme: MinimalSubagentsRenderTheme,
|
|
569
|
+
): Component {
|
|
570
|
+
if ("agents" in details) {
|
|
571
|
+
const counts = countDirectStatusAgents(details.agents);
|
|
572
|
+
const summary = [
|
|
573
|
+
theme.fg("muted", `${counts.children} children`),
|
|
574
|
+
theme.fg(counts.running > 0 ? "accent" : "dim", `${counts.running} running`),
|
|
575
|
+
].join(renderSubagentSeparator(theme));
|
|
576
|
+
if (!options.expanded) return new Text(`${summary}${collapsedExpansionHint(theme)}`, 0, 0);
|
|
577
|
+
return new Text(
|
|
578
|
+
`${summary}\n${renderDirectStatusRows(details.agents, theme).join("\n") || theme.fg("dim", "(no agents)")}`,
|
|
579
|
+
0,
|
|
580
|
+
0,
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
return renderDetailedStatusAgent(details.agent, options, theme);
|
|
584
|
+
}
|
|
585
|
+
|
|
512
586
|
function renderCancelResult(
|
|
513
|
-
details:
|
|
587
|
+
details: CancelRenderDetails,
|
|
514
588
|
options: ToolRenderResultOptions,
|
|
515
|
-
theme:
|
|
589
|
+
theme: MinimalSubagentsRenderTheme,
|
|
516
590
|
): Component {
|
|
517
|
-
const
|
|
518
|
-
const turns = asStringArray(details.cancelled_turn_ids);
|
|
591
|
+
const turns = details.cancelled_turn_ids;
|
|
519
592
|
const summary =
|
|
520
593
|
turns.length > 0
|
|
521
|
-
? renderSubagentSummary(theme, "cancelled",
|
|
522
|
-
|
|
594
|
+
? renderSubagentSummary(theme, "cancelled", details.agent_id, [
|
|
595
|
+
`${turns.length} turns cancelled`,
|
|
596
|
+
])
|
|
597
|
+
: renderSubagentSummary(theme, "completed", details.agent_id, ["no active turns"]);
|
|
523
598
|
if (!options.expanded) return new Text(`${summary}${collapsedExpansionHint(theme)}`, 0, 0);
|
|
524
599
|
const container = new Container();
|
|
525
600
|
container.addChild(new Text(summary, 0, 0));
|
|
526
|
-
container.addChild(renderLabelValue(theme, "Requested target",
|
|
601
|
+
container.addChild(renderLabelValue(theme, "Requested target", details.agent_id));
|
|
527
602
|
container.addChild(
|
|
528
|
-
renderLabelValue(theme, "Mode", details.recursive
|
|
603
|
+
renderLabelValue(theme, "Mode", details.recursive ? "recursive" : "target only"),
|
|
529
604
|
);
|
|
530
|
-
|
|
605
|
+
appendTextSection(
|
|
531
606
|
container,
|
|
532
607
|
theme,
|
|
533
608
|
"Affected agents",
|
|
534
|
-
|
|
609
|
+
details.affected_agent_ids.join("\n") || "(none)",
|
|
535
610
|
);
|
|
536
|
-
|
|
611
|
+
appendTextSection(container, theme, "Cancelled turns", turns.join("\n") || "(none)");
|
|
537
612
|
return container;
|
|
538
613
|
}
|
|
539
614
|
|
|
540
615
|
function renderDeleteResult(
|
|
541
|
-
details:
|
|
616
|
+
details: DeleteRenderDetails,
|
|
542
617
|
options: ToolRenderResultOptions,
|
|
543
|
-
theme:
|
|
618
|
+
theme: MinimalSubagentsRenderTheme,
|
|
544
619
|
): Component {
|
|
545
|
-
const
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
status,
|
|
553
|
-
id,
|
|
554
|
-
[
|
|
555
|
-
`${deleted.length} agents deleted`,
|
|
556
|
-
`${tombstoned.length} tombstoned`,
|
|
557
|
-
failures.length > 0 ? `${failures.length} failed` : undefined,
|
|
558
|
-
].filter((metric): metric is string => Boolean(metric)),
|
|
559
|
-
);
|
|
620
|
+
const status = details.failures.length > 0 ? "failed" : "completed";
|
|
621
|
+
const metrics = [
|
|
622
|
+
`${details.deleted_agent_ids.length} agents deleted`,
|
|
623
|
+
`${details.tombstoned_agent_ids.length} tombstoned`,
|
|
624
|
+
details.failures.length > 0 ? `${details.failures.length} failed` : undefined,
|
|
625
|
+
].filter((metric): metric is string => metric !== undefined);
|
|
626
|
+
const summary = renderSubagentSummary(theme, status, details.agent_id, metrics);
|
|
560
627
|
if (!options.expanded) return new Text(`${summary}${collapsedExpansionHint(theme)}`, 0, 0);
|
|
561
628
|
const container = new Container();
|
|
562
629
|
container.addChild(new Text(summary, 0, 0));
|
|
563
|
-
container.addChild(renderLabelValue(theme, "Requested target",
|
|
630
|
+
container.addChild(renderLabelValue(theme, "Requested target", details.agent_id));
|
|
564
631
|
container.addChild(
|
|
565
|
-
renderLabelValue(theme, "Mode", details.recursive
|
|
632
|
+
renderLabelValue(theme, "Mode", details.recursive ? "recursive" : "target only"),
|
|
633
|
+
);
|
|
634
|
+
appendTextSection(
|
|
635
|
+
container,
|
|
636
|
+
theme,
|
|
637
|
+
"Deleted agents",
|
|
638
|
+
details.deleted_agent_ids.join("\n") || "(none)",
|
|
639
|
+
);
|
|
640
|
+
appendTextSection(
|
|
641
|
+
container,
|
|
642
|
+
theme,
|
|
643
|
+
"Tombstones",
|
|
644
|
+
details.tombstoned_agent_ids.join("\n") || "(none)",
|
|
566
645
|
);
|
|
567
|
-
|
|
568
|
-
appendSection(container, theme, "Tombstones", tombstoned.join("\n") || "(none)");
|
|
569
|
-
appendSection(
|
|
646
|
+
appendTextSection(
|
|
570
647
|
container,
|
|
571
648
|
theme,
|
|
572
649
|
"Trashed sessions",
|
|
573
|
-
|
|
650
|
+
details.trashed_session_files.join("\n") || "(none)",
|
|
574
651
|
);
|
|
575
|
-
if (failures.length > 0) {
|
|
576
|
-
|
|
652
|
+
if (details.failures.length > 0) {
|
|
653
|
+
appendTextSection(
|
|
577
654
|
container,
|
|
578
655
|
theme,
|
|
579
656
|
"Failures",
|
|
580
|
-
theme.fg("error", JSON.stringify(failures, null, 2)),
|
|
657
|
+
theme.fg("error", JSON.stringify(details.failures, null, 2)),
|
|
581
658
|
);
|
|
582
659
|
}
|
|
583
660
|
return container;
|
|
584
661
|
}
|
|
585
662
|
|
|
586
|
-
type
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
663
|
+
type CoordinatorToolResultRenderers = {
|
|
664
|
+
readonly subagent: (
|
|
665
|
+
details: SpawnRenderDetails,
|
|
666
|
+
options: ToolRenderResultOptions,
|
|
667
|
+
theme: MinimalSubagentsRenderTheme,
|
|
668
|
+
args: SpawnCallArguments,
|
|
669
|
+
) => Component;
|
|
670
|
+
readonly agent_message: (
|
|
671
|
+
details: MessageRenderDetails,
|
|
672
|
+
options: ToolRenderResultOptions,
|
|
673
|
+
theme: MinimalSubagentsRenderTheme,
|
|
674
|
+
args: MessageCallArguments,
|
|
675
|
+
) => Component;
|
|
676
|
+
readonly subagent_wait: (
|
|
677
|
+
details: WaitRenderDetails,
|
|
678
|
+
options: ToolRenderResultOptions,
|
|
679
|
+
theme: MinimalSubagentsRenderTheme,
|
|
680
|
+
args: WaitCallArguments,
|
|
681
|
+
) => Component;
|
|
682
|
+
readonly subagent_status: (
|
|
683
|
+
details: StatusRenderDetails,
|
|
684
|
+
options: ToolRenderResultOptions,
|
|
685
|
+
theme: MinimalSubagentsRenderTheme,
|
|
686
|
+
) => Component;
|
|
687
|
+
readonly subagent_cancel: (
|
|
688
|
+
details: CancelRenderDetails,
|
|
689
|
+
options: ToolRenderResultOptions,
|
|
690
|
+
theme: MinimalSubagentsRenderTheme,
|
|
691
|
+
) => Component;
|
|
692
|
+
readonly subagent_delete: (
|
|
693
|
+
details: DeleteRenderDetails,
|
|
694
|
+
options: ToolRenderResultOptions,
|
|
695
|
+
theme: MinimalSubagentsRenderTheme,
|
|
696
|
+
) => Component;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
const COORDINATOR_TOOL_RESULT_RENDERERS = {
|
|
597
700
|
subagent: renderSpawnResult,
|
|
598
701
|
agent_message: renderMessageResult,
|
|
599
702
|
subagent_wait: renderWaitResult,
|
|
600
|
-
subagent_status:
|
|
601
|
-
subagent_cancel:
|
|
602
|
-
subagent_delete:
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
const COORDINATOR_DETAIL_VALIDATORS: Record<
|
|
606
|
-
CoordinatorToolName,
|
|
607
|
-
(details: Record<string, unknown>) => boolean
|
|
608
|
-
> = {
|
|
609
|
-
subagent: (details) =>
|
|
610
|
-
asString(details.agent_id) !== undefined &&
|
|
611
|
-
asString(details.turn_id) !== undefined &&
|
|
612
|
-
asString(details.status) !== undefined,
|
|
613
|
-
agent_message: (details) =>
|
|
614
|
-
asString(details.agent_id) !== undefined && typeof details.delivered === "boolean",
|
|
615
|
-
subagent_wait: (details) =>
|
|
616
|
-
asString(details.agent_id) !== undefined && asString(details.status) !== undefined,
|
|
617
|
-
subagent_status: (details) =>
|
|
618
|
-
Array.isArray(details.agents) || asRecord(details.agent) !== undefined,
|
|
619
|
-
subagent_cancel: (details) =>
|
|
620
|
-
asString(details.agent_id) !== undefined &&
|
|
621
|
-
Array.isArray(details.affected_agent_ids) &&
|
|
622
|
-
Array.isArray(details.cancelled_turn_ids),
|
|
623
|
-
subagent_delete: (details) =>
|
|
624
|
-
asString(details.agent_id) !== undefined &&
|
|
625
|
-
Array.isArray(details.deleted_agent_ids) &&
|
|
626
|
-
Array.isArray(details.tombstoned_agent_ids) &&
|
|
627
|
-
Array.isArray(details.failures),
|
|
628
|
-
};
|
|
703
|
+
subagent_status: renderStatusResult,
|
|
704
|
+
subagent_cancel: renderCancelResult,
|
|
705
|
+
subagent_delete: renderDeleteResult,
|
|
706
|
+
} satisfies CoordinatorToolResultRenderers;
|
|
629
707
|
|
|
630
708
|
/** Render one of the six coordinator tool calls with a shared native Pi grammar. */
|
|
631
709
|
export function renderCoordinatorToolCall(
|
|
632
710
|
toolName: CoordinatorToolName,
|
|
633
|
-
args:
|
|
634
|
-
theme:
|
|
711
|
+
args: CoordinatorToolCallInput,
|
|
712
|
+
theme: MinimalSubagentsRenderTheme,
|
|
635
713
|
): Component {
|
|
636
|
-
|
|
714
|
+
const parsed = parseCoordinatorToolCall(toolName, args);
|
|
715
|
+
if (parsed === undefined) return new Text(coordinatorToolCallTitle(theme, toolName), 0, 0);
|
|
716
|
+
switch (parsed.toolName) {
|
|
717
|
+
case "subagent":
|
|
718
|
+
return COORDINATOR_TOOL_CALL_RENDERERS.subagent(parsed.args, theme);
|
|
719
|
+
case "agent_message":
|
|
720
|
+
return COORDINATOR_TOOL_CALL_RENDERERS.agent_message(parsed.args, theme);
|
|
721
|
+
case "subagent_wait":
|
|
722
|
+
return COORDINATOR_TOOL_CALL_RENDERERS.subagent_wait(parsed.args, theme);
|
|
723
|
+
case "subagent_status":
|
|
724
|
+
return COORDINATOR_TOOL_CALL_RENDERERS.subagent_status(parsed.args, theme);
|
|
725
|
+
case "subagent_cancel":
|
|
726
|
+
return COORDINATOR_TOOL_CALL_RENDERERS.subagent_cancel(parsed.args, theme);
|
|
727
|
+
case "subagent_delete":
|
|
728
|
+
return COORDINATOR_TOOL_CALL_RENDERERS.subagent_delete(parsed.args, theme);
|
|
729
|
+
}
|
|
637
730
|
}
|
|
638
731
|
|
|
639
732
|
/** Render one coordinator tool result in native collapsed, expanded, or partial mode. */
|
|
@@ -641,22 +734,61 @@ export function renderCoordinatorToolResult(
|
|
|
641
734
|
toolName: CoordinatorToolName,
|
|
642
735
|
result: AgentToolResult<unknown>,
|
|
643
736
|
options: ToolRenderResultOptions,
|
|
644
|
-
theme:
|
|
645
|
-
args:
|
|
737
|
+
theme: MinimalSubagentsRenderTheme,
|
|
738
|
+
args: CoordinatorToolCallInput,
|
|
646
739
|
isError = false,
|
|
647
740
|
): Component {
|
|
648
|
-
const
|
|
649
|
-
if (
|
|
650
|
-
|
|
741
|
+
const parsedResult = parseCoordinatorToolResult(toolName, result.details);
|
|
742
|
+
if (parsedResult === undefined) return renderFallbackToolResult(result, theme, isError);
|
|
743
|
+
const parsedCall = parseCoordinatorToolCall(toolName, args);
|
|
744
|
+
switch (parsedResult.toolName) {
|
|
745
|
+
case "subagent":
|
|
746
|
+
return COORDINATOR_TOOL_RESULT_RENDERERS.subagent(
|
|
747
|
+
parsedResult.details,
|
|
748
|
+
options,
|
|
749
|
+
theme,
|
|
750
|
+
parsedCall?.toolName === "subagent" ? parsedCall.args : {},
|
|
751
|
+
);
|
|
752
|
+
case "agent_message":
|
|
753
|
+
return COORDINATOR_TOOL_RESULT_RENDERERS.agent_message(
|
|
754
|
+
parsedResult.details,
|
|
755
|
+
options,
|
|
756
|
+
theme,
|
|
757
|
+
parsedCall?.toolName === "agent_message" ? parsedCall.args : {},
|
|
758
|
+
);
|
|
759
|
+
case "subagent_wait":
|
|
760
|
+
return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_wait(
|
|
761
|
+
parsedResult.details,
|
|
762
|
+
options,
|
|
763
|
+
theme,
|
|
764
|
+
parsedCall?.toolName === "subagent_wait" ? parsedCall.args : {},
|
|
765
|
+
);
|
|
766
|
+
case "subagent_status":
|
|
767
|
+
return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_status(
|
|
768
|
+
parsedResult.details,
|
|
769
|
+
options,
|
|
770
|
+
theme,
|
|
771
|
+
);
|
|
772
|
+
case "subagent_cancel":
|
|
773
|
+
return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_cancel(
|
|
774
|
+
parsedResult.details,
|
|
775
|
+
options,
|
|
776
|
+
theme,
|
|
777
|
+
);
|
|
778
|
+
case "subagent_delete":
|
|
779
|
+
return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_delete(
|
|
780
|
+
parsedResult.details,
|
|
781
|
+
options,
|
|
782
|
+
theme,
|
|
783
|
+
);
|
|
651
784
|
}
|
|
652
|
-
return COORDINATOR_TOOL_RESULT_RENDERERS[toolName](details, options, theme, args);
|
|
653
785
|
}
|
|
654
786
|
|
|
655
787
|
/** Render explicit agent messages with compact source/destination metadata. */
|
|
656
788
|
export function renderMinimalSubagentsMessage(
|
|
657
789
|
message: RenderableCoordinatorMessage,
|
|
658
|
-
options:
|
|
659
|
-
theme:
|
|
790
|
+
options: MessageRenderOptions,
|
|
791
|
+
theme: MinimalSubagentsRenderTheme,
|
|
660
792
|
): Component {
|
|
661
793
|
return renderCoordinatorMessage("Agent message", "→", message, options, theme);
|
|
662
794
|
}
|
|
@@ -664,31 +796,42 @@ export function renderMinimalSubagentsMessage(
|
|
|
664
796
|
/** Render automatic successful agent results with expandable Markdown output. */
|
|
665
797
|
export function renderMinimalSubagentsResult(
|
|
666
798
|
message: RenderableCoordinatorMessage,
|
|
667
|
-
options:
|
|
668
|
-
theme:
|
|
799
|
+
options: MessageRenderOptions,
|
|
800
|
+
theme: MinimalSubagentsRenderTheme,
|
|
669
801
|
): Component {
|
|
670
802
|
return renderCoordinatorMessage("Agent result", "✓", message, options, theme);
|
|
671
803
|
}
|
|
672
804
|
|
|
805
|
+
function messageSource(details: CoordinatorMessageRenderDetails | undefined): string {
|
|
806
|
+
return details?.source_agent_id ?? details?.agent_id ?? "unknown";
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function messageSourceTurn(
|
|
810
|
+
details: CoordinatorMessageRenderDetails | undefined,
|
|
811
|
+
): string | undefined {
|
|
812
|
+
return details?.source_turn_id ?? details?.turn_id;
|
|
813
|
+
}
|
|
814
|
+
|
|
673
815
|
function renderCoordinatorMessage(
|
|
674
816
|
label: string,
|
|
675
817
|
symbol: string,
|
|
676
818
|
message: RenderableCoordinatorMessage,
|
|
677
|
-
options:
|
|
678
|
-
theme:
|
|
819
|
+
options: MessageRenderOptions,
|
|
820
|
+
theme: MinimalSubagentsRenderTheme,
|
|
679
821
|
): Component {
|
|
680
|
-
const details =
|
|
822
|
+
const details = parseCoordinatorMessageDetails(message.details);
|
|
681
823
|
const content = coordinatorMessageText(message.content);
|
|
682
|
-
const source =
|
|
683
|
-
const destination =
|
|
684
|
-
const
|
|
824
|
+
const source = messageSource(details);
|
|
825
|
+
const destination = details?.destination_agent_id ?? "recipient";
|
|
826
|
+
const sourceTurn = messageSourceTurn(details);
|
|
685
827
|
const route = `${theme.fg("accent", theme.bold(source))} ${theme.fg("dim", "→")} ${theme.fg("accent", theme.bold(destination))}`;
|
|
686
828
|
const heading = [
|
|
687
829
|
`${theme.fg(symbol === "✓" ? "success" : "accent", symbol)} ${theme.bold(label)}`,
|
|
688
830
|
route,
|
|
689
|
-
|
|
831
|
+
sourceTurn ? theme.fg("dim", `turn ${sourceTurn}`) : undefined,
|
|
832
|
+
details?.status ? renderSubagentStatusLabel(theme, details.status) : undefined,
|
|
690
833
|
]
|
|
691
|
-
.filter((part): part is string =>
|
|
834
|
+
.filter((part): part is string => part !== undefined)
|
|
692
835
|
.join(renderSubagentSeparator(theme));
|
|
693
836
|
const box = new Box(options.outputPad, 1, (text) => theme.bg("customMessageBg", text));
|
|
694
837
|
if (!options.expanded) {
|
|
@@ -703,15 +846,13 @@ function renderCoordinatorMessage(
|
|
|
703
846
|
}
|
|
704
847
|
const container = new Container();
|
|
705
848
|
container.addChild(new Text(heading, 0, 0));
|
|
706
|
-
if (
|
|
707
|
-
|
|
708
|
-
}
|
|
709
|
-
const duration = formatSubagentDuration(asNumber(details?.elapsed_ms));
|
|
849
|
+
if (sourceTurn) container.addChild(renderLabelValue(theme, "Source turn", sourceTurn));
|
|
850
|
+
const duration = formatSubagentDuration(details?.elapsed_ms);
|
|
710
851
|
if (duration) container.addChild(renderLabelValue(theme, "Duration", duration));
|
|
711
852
|
container.addChild(new Spacer(1));
|
|
712
853
|
container.addChild(new Markdown(content, 0, 0, getMarkdownTheme()));
|
|
713
|
-
const usageText = formatSubagentUsage(
|
|
714
|
-
if (usageText)
|
|
854
|
+
const usageText = formatSubagentUsage(details?.usage);
|
|
855
|
+
if (usageText) appendTextSection(container, theme, "Usage", usageText);
|
|
715
856
|
box.addChild(container);
|
|
716
857
|
return box;
|
|
717
858
|
}
|