@narumitw/pi-subagents 3.4.3 → 3.4.4
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 +6 -0
- package/README.md +5 -4
- package/dist/index.ts +317 -34
- package/dist/index.ts.map +4 -4
- package/docs/conversation-display.md +10 -0
- package/package.json +1 -1
- package/src/conversation-view.ts +169 -0
- package/src/message-renderers.ts +90 -40
- package/src/tool-renderers.ts +156 -0
- package/src/tools.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @narumitw/pi-subagents
|
|
2
2
|
|
|
3
|
+
## 3.4.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 49c68d4: Show readable tool summaries and job notifications in the conversation. Keep job identity, state, and message direction visible when collapsed, and render expanded subagent results as Markdown without changing model data or widget behavior.
|
|
8
|
+
|
|
3
9
|
## 3.4.3
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -73,10 +73,8 @@ Use messaging only when needed:
|
|
|
73
73
|
- If `subagent_wait` returns `reason: "subagent_message"`, handle the visible request or response and wait for the job again only when needed.
|
|
74
74
|
- Answer a child-originated request by calling `subagent_send` with its `requestId`.
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
The hint is omitted when the action is unbound or an older host does not supply renderer key formatting.
|
|
79
|
-
Expanded messages show the full content; this display state does not change the content sent to the model.
|
|
76
|
+
Expand tool results and notifications with Pi's `app.tools.expand` binding (`Ctrl+O` by default).
|
|
77
|
+
See [Conversation presentation](./docs/conversation-display.md) for tool summaries, notification details, and expansion behavior.
|
|
80
78
|
|
|
81
79
|
## 💬 Commands
|
|
82
80
|
|
|
@@ -337,6 +335,9 @@ packages/pi-subagents/
|
|
|
337
335
|
├── src/ # Authoritative implementation and helpers
|
|
338
336
|
│ ├── index.ts # Thin Pi entrypoint
|
|
339
337
|
│ ├── subagents.ts # Commands and session lifecycle
|
|
338
|
+
│ ├── tool-renderers.ts # Conversation tool summaries and expanded details
|
|
339
|
+
│ ├── message-renderers.ts # Conversation completion and broker notifications
|
|
340
|
+
│ ├── conversation-view.ts # Shared bounded, sanitized conversation rendering
|
|
340
341
|
│ ├── menu.ts # Job manager and session-local display controls
|
|
341
342
|
│ ├── widget.ts # Retained widget ownership and event policies
|
|
342
343
|
│ ├── widget-renderer.ts # Bounded rows and active-job summaries
|
package/dist/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
import {
|
|
39
39
|
CHILD_CORE_TOOL_NAMES,
|
|
40
40
|
DEFAULT_SUBAGENT_TOOLS,
|
|
41
|
+
JOB_STATES,
|
|
41
42
|
SUBAGENT_THINKING_LEVELS,
|
|
42
43
|
TERMINAL_JOB_STATES
|
|
43
44
|
} from "./chunks/chunk-5OXDVXXO.ts";
|
|
@@ -53,49 +54,206 @@ function sanitizeSubagentDisplayText(value) {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
// src/message-renderers.ts
|
|
57
|
+
import { Box } from "@earendil-works/pi-tui";
|
|
58
|
+
|
|
59
|
+
// src/conversation-view.ts
|
|
56
60
|
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
57
|
-
import {
|
|
61
|
+
import { Markdown, truncateToWidth } from "@earendil-works/pi-tui";
|
|
62
|
+
import { hardWrapTerminalDocument, sanitizeTerminalDocument } from "@narumitw/pi-tui-kit/terminal-document";
|
|
63
|
+
function boundedView(render, invalidate = () => {
|
|
64
|
+
}) {
|
|
65
|
+
return {
|
|
66
|
+
render(width) {
|
|
67
|
+
if (!Number.isFinite(width) || width < 1) return [];
|
|
68
|
+
const columns = Math.floor(width);
|
|
69
|
+
return render(columns).map((line) => truncateToWidth(line, columns, ""));
|
|
70
|
+
},
|
|
71
|
+
invalidate
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function conversationView(children) {
|
|
75
|
+
return boundedView(
|
|
76
|
+
(width) => children.flatMap((child) => child.render(width)),
|
|
77
|
+
() => {
|
|
78
|
+
for (const child of children) child.invalidate();
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function singleLine(text) {
|
|
83
|
+
return sanitizeTerminalDocument(text).replace(/\s+/gu, " ").trim();
|
|
84
|
+
}
|
|
85
|
+
function displayLine(theme, text, role = "toolOutput") {
|
|
86
|
+
const safe = singleLine(text);
|
|
87
|
+
return boundedView((width) => [theme.fg(role, truncateToWidth(safe, width, "\u2026"))]);
|
|
88
|
+
}
|
|
89
|
+
function displayHeading(theme, title, detail = "", role = "toolTitle") {
|
|
90
|
+
const safeTitle = singleLine(title);
|
|
91
|
+
const safeDetail = singleLine(detail);
|
|
92
|
+
return boundedView((width) => [
|
|
93
|
+
truncateToWidth(
|
|
94
|
+
theme.fg(role, theme.bold(safeTitle)) + (safeDetail ? theme.fg("muted", ` \xB7 ${safeDetail}`) : ""),
|
|
95
|
+
width,
|
|
96
|
+
"\u2026"
|
|
97
|
+
)
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
function displayDocument(theme, text, role = "toolOutput") {
|
|
101
|
+
const safe = sanitizeTerminalDocument(text);
|
|
102
|
+
return boundedView((width) => hardWrapTerminalDocument(safe, width).map((line) => theme.fg(role, line)));
|
|
103
|
+
}
|
|
104
|
+
function displayJson(theme, value) {
|
|
105
|
+
const text = JSON.stringify(
|
|
106
|
+
value,
|
|
107
|
+
(_key, item) => typeof item === "string" ? sanitizeTerminalDocument(item) : item,
|
|
108
|
+
2
|
|
109
|
+
);
|
|
110
|
+
return displayDocument(theme, text ?? "");
|
|
111
|
+
}
|
|
112
|
+
function displayMarkdown(theme, text, role = "toolOutput") {
|
|
113
|
+
const safe = sanitizeTerminalDocument(text);
|
|
114
|
+
const markdown = new Markdown(safe, 0, 0, getMarkdownTheme(), { color: (value) => theme.fg(role, value) });
|
|
115
|
+
return boundedView(
|
|
116
|
+
(width) => markdown.render(width),
|
|
117
|
+
() => markdown.invalidate()
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
function expansionHint(theme, hints) {
|
|
121
|
+
const key = hints.keyText?.("app.tools.expand");
|
|
122
|
+
return conversationView(key ? [displayLine(theme, `(${key} to expand)`, "muted")] : []);
|
|
123
|
+
}
|
|
124
|
+
function textContent(content) {
|
|
125
|
+
return typeof content === "string" ? content : content.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n");
|
|
126
|
+
}
|
|
127
|
+
function asRecord(value) {
|
|
128
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
129
|
+
}
|
|
130
|
+
function parseRecord(text) {
|
|
131
|
+
try {
|
|
132
|
+
return asRecord(JSON.parse(text));
|
|
133
|
+
} catch {
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function jobPayload(value) {
|
|
138
|
+
const record = asRecord(value);
|
|
139
|
+
if (!record || typeof record.jobId !== "string" || !JOB_STATES.some((state) => state === record.state))
|
|
140
|
+
return void 0;
|
|
141
|
+
if (["result", "error"].some((key) => record[key] !== void 0 && typeof record[key] !== "string")) return void 0;
|
|
142
|
+
if (record.limitations !== void 0 && (!Array.isArray(record.limitations) || record.limitations.some((value2) => typeof value2 !== "string")))
|
|
143
|
+
return void 0;
|
|
144
|
+
if (["timedOut", "interrupted"].some((key) => record[key] !== void 0 && typeof record[key] !== "boolean"))
|
|
145
|
+
return void 0;
|
|
146
|
+
return record;
|
|
147
|
+
}
|
|
148
|
+
function stateLabel(state) {
|
|
149
|
+
return state === "timed_out" ? "timed out" : state;
|
|
150
|
+
}
|
|
151
|
+
function stateRole(state) {
|
|
152
|
+
switch (state) {
|
|
153
|
+
case "completed":
|
|
154
|
+
return "success";
|
|
155
|
+
case "failed":
|
|
156
|
+
case "timed_out":
|
|
157
|
+
return "error";
|
|
158
|
+
case "partial":
|
|
159
|
+
case "cancelled":
|
|
160
|
+
return "warning";
|
|
161
|
+
case "running":
|
|
162
|
+
return "accent";
|
|
163
|
+
case "queued":
|
|
164
|
+
return "muted";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function jobSummary(theme, payload) {
|
|
168
|
+
const waitStatus = payload.timedOut === true ? " \xB7 wait timed out (job not cancelled)" : "";
|
|
169
|
+
const interrupted = payload.interrupted === true ? " \xB7 interrupted by subagent message" : "";
|
|
170
|
+
const error = typeof payload.error === "string" && payload.error ? ` \xB7 ${payload.error}` : "";
|
|
171
|
+
const limitations = Array.isArray(payload.limitations) ? payload.limitations.length : 0;
|
|
172
|
+
return displayLine(
|
|
173
|
+
theme,
|
|
174
|
+
`${payload.jobId} \xB7 ${stateLabel(payload.state)}${waitStatus}${interrupted}${error}${limitations ? ` \xB7 ${limitations} limitation${limitations === 1 ? "" : "s"}` : ""}`,
|
|
175
|
+
payload.state === "failed" || payload.state === "timed_out" ? "error" : payload.timedOut === true || payload.interrupted === true ? "warning" : stateRole(payload.state)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
function jobBody(theme, payload) {
|
|
179
|
+
const children = [];
|
|
180
|
+
if (typeof payload.error === "string" && payload.error) {
|
|
181
|
+
children.push(displayDocument(theme, `Error: ${payload.error}`, "error"));
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(payload.limitations)) {
|
|
184
|
+
for (const limitation of payload.limitations) {
|
|
185
|
+
if (typeof limitation === "string") children.push(displayDocument(theme, `Limitation: ${limitation}`, "warning"));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (typeof payload.result === "string" && payload.result) children.push(displayMarkdown(theme, payload.result));
|
|
189
|
+
return conversationView(children);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/message-renderers.ts
|
|
58
193
|
var COMPLETION_MESSAGE_TYPE = "pi-subagents-completion";
|
|
59
194
|
var BROKER_MESSAGE_TYPE = "pi-subagents-message";
|
|
195
|
+
var COMPLETION_PREFIX = "Subagent job completion:\n";
|
|
196
|
+
function brokerDetails(value) {
|
|
197
|
+
const details = asRecord(value);
|
|
198
|
+
return details && (details.kind === "request" || details.kind === "response") && typeof details.jobId === "string" && typeof details.requestId === "string" ? details : void 0;
|
|
199
|
+
}
|
|
200
|
+
function legacyBrokerDetails(content) {
|
|
201
|
+
const [type, protocol, request, job] = content.split("\n", 4);
|
|
202
|
+
if (protocol !== "Protocol: pi-subagents:main-message:v1" || !request?.startsWith("Request ID: ") || !job?.startsWith("Job ID: "))
|
|
203
|
+
return void 0;
|
|
204
|
+
const kind = type === "Message Type: SUBAGENT_REQUEST" ? "request" : type === "Message Type: SUBAGENT_RESPONSE" ? "response" : void 0;
|
|
205
|
+
return brokerDetails({ kind, requestId: request.slice("Request ID: ".length), jobId: job.slice("Job ID: ".length) });
|
|
206
|
+
}
|
|
60
207
|
function createMessageBox(children, outputPad, background) {
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const box = new Box(Math.min(Math.max(0, outputPad), maxPadding), 1, background);
|
|
208
|
+
return boundedView(
|
|
209
|
+
(width) => {
|
|
210
|
+
const padding = Number.isFinite(outputPad) ? Math.max(0, Math.floor(outputPad)) : 0;
|
|
211
|
+
const box = new Box(Math.min(padding, Math.floor((width - 1) / 2)), 1, background);
|
|
66
212
|
for (const child of children) box.addChild(child);
|
|
67
213
|
return box.render(width);
|
|
68
214
|
},
|
|
69
|
-
|
|
215
|
+
() => {
|
|
70
216
|
for (const child of children) child.invalidate();
|
|
71
217
|
}
|
|
72
|
-
|
|
218
|
+
);
|
|
73
219
|
}
|
|
74
220
|
function registerMessageRenderers(pi) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
221
|
+
pi.registerMessageRenderer(COMPLETION_MESSAGE_TYPE, (message, options, theme) => {
|
|
222
|
+
const content = textContent(message.content);
|
|
223
|
+
const payload = jobPayload(message.details) ?? jobPayload(
|
|
224
|
+
parseRecord(content.startsWith(COMPLETION_PREFIX) ? content.slice(COMPLETION_PREFIX.length) : content)
|
|
225
|
+
);
|
|
226
|
+
const children = [displayHeading(theme, "Subagent job completion", "", "customMessageLabel")];
|
|
227
|
+
if (payload) children.push(jobSummary(theme, payload));
|
|
228
|
+
if (options.expanded) {
|
|
229
|
+
children.push(payload ? jobBody(theme, payload) : displayDocument(theme, content, "customMessageText"));
|
|
230
|
+
} else {
|
|
231
|
+
children.push(expansionHint(theme, options));
|
|
232
|
+
}
|
|
233
|
+
return createMessageBox(children, options.outputPad, (text) => theme.bg("customMessageBg", text));
|
|
234
|
+
});
|
|
235
|
+
pi.registerMessageRenderer(BROKER_MESSAGE_TYPE, (message, options, theme) => {
|
|
236
|
+
const content = textContent(message.content);
|
|
237
|
+
const details = brokerDetails(message.details) ?? legacyBrokerDetails(content);
|
|
238
|
+
const title = details ? `Subagent ${details.kind}` : "Subagent message";
|
|
239
|
+
const source = details ? `from ${details.jobId}` : "";
|
|
240
|
+
const children = [displayHeading(theme, title, source, "customMessageLabel")];
|
|
241
|
+
if (details) {
|
|
242
|
+
children.push(
|
|
243
|
+
displayLine(
|
|
244
|
+
theme,
|
|
245
|
+
`${details.requestId} \xB7 ${details.kind === "request" ? "needs reply" : "response received"}`,
|
|
246
|
+
"muted"
|
|
247
|
+
)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (options.expanded) {
|
|
251
|
+
children.push(displayDocument(theme, content, "customMessageText"));
|
|
252
|
+
} else {
|
|
253
|
+
children.push(expansionHint(theme, options));
|
|
254
|
+
}
|
|
255
|
+
return createMessageBox(children, options.outputPad, (text) => theme.bg("customMessageBg", text));
|
|
256
|
+
});
|
|
99
257
|
}
|
|
100
258
|
|
|
101
259
|
// src/settings.ts
|
|
@@ -2227,6 +2385,126 @@ function abortError2(message) {
|
|
|
2227
2385
|
return error;
|
|
2228
2386
|
}
|
|
2229
2387
|
|
|
2388
|
+
// src/tool-renderers.ts
|
|
2389
|
+
var LABELS = { spawn: "Spawn", inspect: "Inspect", cancel: "Cancel", wait: "Wait", send: "Send" };
|
|
2390
|
+
var PENDING = {
|
|
2391
|
+
spawn: "Starting subagent\u2026",
|
|
2392
|
+
inspect: "Inspecting jobs\u2026",
|
|
2393
|
+
cancel: "Cancelling job\u2026",
|
|
2394
|
+
wait: "Waiting for job or subagent message\u2026",
|
|
2395
|
+
send: "Sending message\u2026"
|
|
2396
|
+
};
|
|
2397
|
+
var INSPECT_PREVIEW_JOBS = 5;
|
|
2398
|
+
var STATE_PRIORITY = { failed: 0, timed_out: 1, partial: 2, running: 3, queued: 4, cancelled: 5, completed: 6 };
|
|
2399
|
+
function subagentToolRenderers(action) {
|
|
2400
|
+
return {
|
|
2401
|
+
renderCall(input, theme, context) {
|
|
2402
|
+
const args = asRecord(input) ?? {};
|
|
2403
|
+
const target = action === "send" ? typeof args.requestId === "string" ? `reply to ${args.requestId}` : typeof args.recipient === "string" ? `to ${args.recipient}` : "" : typeof args.jobId === "string" ? args.jobId : "";
|
|
2404
|
+
const children = [displayHeading(theme, `Subagent \xB7 ${LABELS[action]}`, target)];
|
|
2405
|
+
if (context.expanded) {
|
|
2406
|
+
for (const [key, value] of Object.entries(args)) {
|
|
2407
|
+
if (typeof value === "string") {
|
|
2408
|
+
children.push(displayDocument(theme, `${key}: ${value}`, "muted"));
|
|
2409
|
+
} else if (value !== void 0) {
|
|
2410
|
+
children.push(displayLine(theme, `${key}:`, "muted"), displayJson(theme, value));
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
} else {
|
|
2414
|
+
const preview = action === "spawn" ? args.task : action === "send" ? args.message : void 0;
|
|
2415
|
+
if (typeof preview === "string") children.push(displayLine(theme, preview, "muted"));
|
|
2416
|
+
}
|
|
2417
|
+
return conversationView(children);
|
|
2418
|
+
},
|
|
2419
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2420
|
+
const raw = textContent(result.content);
|
|
2421
|
+
const children = [];
|
|
2422
|
+
if (context.isError) {
|
|
2423
|
+
children.push(
|
|
2424
|
+
displayLine(theme, `${LABELS[action]} failed`, "error"),
|
|
2425
|
+
expanded ? displayDocument(theme, raw, "error") : displayLine(theme, raw, "error")
|
|
2426
|
+
);
|
|
2427
|
+
} else if (isPartial) {
|
|
2428
|
+
return displayLine(theme, PENDING[action], "muted");
|
|
2429
|
+
} else {
|
|
2430
|
+
const payload = toolPayload(action, result);
|
|
2431
|
+
if (!payload) {
|
|
2432
|
+
children.push(
|
|
2433
|
+
expanded ? displayDocument(theme, raw || "No result returned.") : displayLine(theme, raw || "No result returned.")
|
|
2434
|
+
);
|
|
2435
|
+
} else if (action === "inspect") {
|
|
2436
|
+
children.push(...inspectSummary(theme, payload, expanded));
|
|
2437
|
+
if (expanded) children.push(displayJson(theme, payload));
|
|
2438
|
+
} else if (action === "send") {
|
|
2439
|
+
const accepted = payload.accepted === true;
|
|
2440
|
+
const reply = typeof asRecord(context.args)?.requestId === "string";
|
|
2441
|
+
children.push(
|
|
2442
|
+
displayLine(
|
|
2443
|
+
theme,
|
|
2444
|
+
`${reply ? "Reply" : "Request"} ${accepted ? "accepted" : "not accepted"}${payload.duplicate === true ? " \xB7 duplicate" : ""} \xB7 ${payload.requestId}`,
|
|
2445
|
+
accepted ? "success" : "warning"
|
|
2446
|
+
)
|
|
2447
|
+
);
|
|
2448
|
+
if (expanded) children.push(displayJson(theme, payload));
|
|
2449
|
+
} else {
|
|
2450
|
+
const job = jobPayload(payload);
|
|
2451
|
+
if (job) children.push(jobSummary(theme, job));
|
|
2452
|
+
if (expanded) {
|
|
2453
|
+
if (action === "wait" && job) {
|
|
2454
|
+
const { result: _result, error: _error, limitations: _limitations, ...metadata } = job;
|
|
2455
|
+
children.push(displayJson(theme, metadata), jobBody(theme, job));
|
|
2456
|
+
} else {
|
|
2457
|
+
children.push(displayJson(theme, payload));
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
if (!expanded) children.push(expansionHint(theme, context));
|
|
2463
|
+
return conversationView(children);
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
function toolPayload(action, result) {
|
|
2468
|
+
for (const value of [result.details, parseRecord(textContent(result.content))]) {
|
|
2469
|
+
const record = asRecord(value);
|
|
2470
|
+
if (!record) continue;
|
|
2471
|
+
if (action === "inspect") {
|
|
2472
|
+
if (Array.isArray(record.jobs) && record.jobs.every((job) => jobPayload(job))) return record;
|
|
2473
|
+
} else if (action === "send") {
|
|
2474
|
+
if (typeof record.requestId === "string" && typeof record.accepted === "boolean") return record;
|
|
2475
|
+
} else if (jobPayload(record)) {
|
|
2476
|
+
return record;
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
return void 0;
|
|
2480
|
+
}
|
|
2481
|
+
function inspectSummary(theme, payload, expanded) {
|
|
2482
|
+
const jobs = payload.jobs.map(jobPayload).filter((job) => job !== void 0);
|
|
2483
|
+
const counts = JOB_STATES.map((state) => {
|
|
2484
|
+
const count = jobs.filter((job) => job.state === state).length;
|
|
2485
|
+
return count ? `${count} ${stateLabel(state)}` : void 0;
|
|
2486
|
+
}).filter(Boolean);
|
|
2487
|
+
const children = [
|
|
2488
|
+
displayLine(
|
|
2489
|
+
theme,
|
|
2490
|
+
jobs.length ? `${jobs.length} job${jobs.length === 1 ? "" : "s"} \xB7 ${counts.join(" \xB7 ")}` : "No retained jobs.",
|
|
2491
|
+
"muted"
|
|
2492
|
+
)
|
|
2493
|
+
];
|
|
2494
|
+
if (!expanded) {
|
|
2495
|
+
const sorted = [...jobs].sort((left, right) => STATE_PRIORITY[left.state] - STATE_PRIORITY[right.state]);
|
|
2496
|
+
for (const job of sorted.slice(0, INSPECT_PREVIEW_JOBS)) children.push(jobSummary(theme, job));
|
|
2497
|
+
if (jobs.length > INSPECT_PREVIEW_JOBS) {
|
|
2498
|
+
children.push(displayLine(theme, `${jobs.length - INSPECT_PREVIEW_JOBS} more jobs`, "muted"));
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
const omitted = asRecord(payload.omitted)?.jobs;
|
|
2502
|
+
if (typeof omitted === "number" && omitted > 0) {
|
|
2503
|
+
children.push(displayLine(theme, `${omitted} expired or removed jobs not retained`, "muted"));
|
|
2504
|
+
}
|
|
2505
|
+
return children;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2230
2508
|
// src/tools.ts
|
|
2231
2509
|
var MAX_TASK_BYTES = 50 * 1024;
|
|
2232
2510
|
var MAX_TOOLS = MAX_SELECTED_TOOLS;
|
|
@@ -2367,6 +2645,7 @@ function registerSubagentTools(pi, dependencies = {}) {
|
|
|
2367
2645
|
pi.registerTool({
|
|
2368
2646
|
name: "subagent_spawn",
|
|
2369
2647
|
label: "Subagent \xB7 Spawn",
|
|
2648
|
+
...subagentToolRenderers("spawn"),
|
|
2370
2649
|
description: "Use subagent_spawn to start one Pi subagent job and return its jobId immediately. The task defines the child's specialization; selected core tools and explicit trusted local skills or extensions define its initial capabilities. The job may ask the main agent questions and publishes one asynchronous completion when terminal.",
|
|
2371
2650
|
promptSnippet: "Use subagent_spawn to start one Pi subagent job",
|
|
2372
2651
|
parameters: SpawnParameters,
|
|
@@ -2432,6 +2711,7 @@ function registerSubagentTools(pi, dependencies = {}) {
|
|
|
2432
2711
|
pi.registerTool({
|
|
2433
2712
|
name: "subagent_inspect",
|
|
2434
2713
|
label: "Subagent \xB7 Inspect",
|
|
2714
|
+
...subagentToolRenderers("inspect"),
|
|
2435
2715
|
description: "Use subagent_inspect to return one privacy-filtered snapshot of retained jobs without exposing task text, complete child output, prompts, selected tools, context, credentials, or broker messages.",
|
|
2436
2716
|
promptSnippet: "Use subagent_inspect to inspect retained subagent jobs",
|
|
2437
2717
|
parameters: InspectParameters,
|
|
@@ -2444,6 +2724,7 @@ function registerSubagentTools(pi, dependencies = {}) {
|
|
|
2444
2724
|
pi.registerTool({
|
|
2445
2725
|
name: "subagent_cancel",
|
|
2446
2726
|
label: "Subagent \xB7 Cancel",
|
|
2727
|
+
...subagentToolRenderers("cancel"),
|
|
2447
2728
|
description: "Use subagent_cancel to idempotently cancel one queued or running job and release its process, timer, broker credentials, and temporary resources. Terminal jobs remain unchanged.",
|
|
2448
2729
|
promptSnippet: "Use subagent_cancel to cancel one active subagent job",
|
|
2449
2730
|
parameters: CancelParameters,
|
|
@@ -2455,6 +2736,7 @@ function registerSubagentTools(pi, dependencies = {}) {
|
|
|
2455
2736
|
pi.registerTool({
|
|
2456
2737
|
name: "subagent_wait",
|
|
2457
2738
|
label: "Subagent \xB7 Wait",
|
|
2739
|
+
...subagentToolRenderers("wait"),
|
|
2458
2740
|
description: "Use subagent_wait to wait for one job to become terminal. An incoming child request or response interrupts the wait without cancelling the job. A timeout or caller cancellation stops only this wait.",
|
|
2459
2741
|
promptSnippet: "Use subagent_wait to wait for one subagent job or incoming message",
|
|
2460
2742
|
parameters: WaitParameters,
|
|
@@ -2467,6 +2749,7 @@ function registerSubagentTools(pi, dependencies = {}) {
|
|
|
2467
2749
|
pi.registerTool({
|
|
2468
2750
|
name: "subagent_send",
|
|
2469
2751
|
label: "Subagent \xB7 Send",
|
|
2752
|
+
...subagentToolRenderers("send"),
|
|
2470
2753
|
description: "Use subagent_send to send one request to an active job or answer one pending child request. For a new request, provide recipient and omit requestId. To answer a request, provide requestId and omit recipient. Provide exactly one of recipient or requestId. An accepted new request interrupts any active child response wait so delivery can proceed without consuming the child's original request.",
|
|
2471
2754
|
promptSnippet: "Use subagent_send to send or answer one subagent message",
|
|
2472
2755
|
parameters: SendParameters,
|
|
@@ -2655,7 +2938,7 @@ import {
|
|
|
2655
2938
|
} from "@narumitw/pi-tui-kit/editor-status-view";
|
|
2656
2939
|
|
|
2657
2940
|
// src/widget-renderer.ts
|
|
2658
|
-
import { stripTerminalSequences, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
2941
|
+
import { stripTerminalSequences, truncateToWidth as truncateToWidth2, visibleWidth } from "@earendil-works/pi-tui";
|
|
2659
2942
|
import { EditorStatusWidget } from "@narumitw/pi-tui-kit/editor-status-widget";
|
|
2660
2943
|
var TASK_PREVIEW_WIDTH = 40;
|
|
2661
2944
|
var SUBAGENT_WIDGET_RESERVED_ROWS = 8;
|
|
@@ -2714,7 +2997,7 @@ function renderJob(job, theme, settings, compact) {
|
|
|
2714
2997
|
const state = theme.fg(running ? "accent" : "muted", job.state);
|
|
2715
2998
|
const jobId = sanitizeSubagentDisplayText(job.jobId);
|
|
2716
2999
|
const taskText = sanitizeSubagentDisplayText(job.task);
|
|
2717
|
-
const task = stripTerminalSequences(
|
|
3000
|
+
const task = stripTerminalSequences(truncateToWidth2(taskText, TASK_PREVIEW_WIDTH, "\u2026"));
|
|
2718
3001
|
const labels = [
|
|
2719
3002
|
...settings.showJobId && visibleWidth(jobId) > 0 ? [theme.fg("text", jobId)] : [],
|
|
2720
3003
|
...visibleWidth(task) > 0 ? [theme.fg("text", task)] : []
|