@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
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
/** Theme operations used by the Minimal Subagents widget renderer. */
|
|
4
|
+
export type MinimalSubagentsWidgetTheme = Pick<Theme, "fg" | "bold">;
|
|
2
5
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
sliceByColumn,
|
|
8
|
+
truncateToWidth,
|
|
9
|
+
visibleWidth,
|
|
10
|
+
type Component,
|
|
11
|
+
type TUI,
|
|
12
|
+
} from "@earendil-works/pi-tui";
|
|
4
13
|
import type { MinimalSubagentsCoordinator } from "./minimal-subagents-coordinator.js";
|
|
5
14
|
import {
|
|
6
15
|
formatSubagentDuration,
|
|
7
16
|
renderSubagentStatusLabel,
|
|
8
17
|
renderSubagentStatusSymbol,
|
|
9
18
|
} from "./minimal-subagents-rendering.js";
|
|
10
|
-
import type {
|
|
19
|
+
import type {
|
|
20
|
+
AgentSummary,
|
|
21
|
+
HierarchyStatusResult,
|
|
22
|
+
RuntimeProfile,
|
|
23
|
+
TurnStatus,
|
|
24
|
+
} from "./minimal-subagents-types.js";
|
|
11
25
|
|
|
12
26
|
const MINIMAL_SUBAGENTS_UI_KEY = "minimal-subagents";
|
|
13
27
|
const MINIMAL_SUBAGENTS_RECENT_LIMIT = 3;
|
|
@@ -20,6 +34,7 @@ export interface MinimalSubagentsWidgetRow {
|
|
|
20
34
|
depth: number;
|
|
21
35
|
status: TurnStatus | "idle" | "unavailable";
|
|
22
36
|
elapsedMs?: number;
|
|
37
|
+
runtimeProfile: RuntimeProfile;
|
|
23
38
|
task?: string;
|
|
24
39
|
structural: boolean;
|
|
25
40
|
}
|
|
@@ -122,6 +137,10 @@ export function buildMinimalSubagentsWidgetView(
|
|
|
122
137
|
depth: item.depth,
|
|
123
138
|
status: agentTerminalStatus(item.agent),
|
|
124
139
|
elapsedMs: item.agent.elapsed_ms,
|
|
140
|
+
runtimeProfile: {
|
|
141
|
+
model: item.agent.model,
|
|
142
|
+
thinking_level: item.agent.thinking_level,
|
|
143
|
+
},
|
|
125
144
|
task: structural ? undefined : item.agent.task,
|
|
126
145
|
structural,
|
|
127
146
|
};
|
|
@@ -135,14 +154,195 @@ export function buildMinimalSubagentsWidgetView(
|
|
|
135
154
|
};
|
|
136
155
|
}
|
|
137
156
|
|
|
157
|
+
const MINIMAL_SUBAGENTS_WIDGET_SEPARATOR_TEXT = " · ";
|
|
158
|
+
const MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS = "…";
|
|
159
|
+
|
|
160
|
+
interface MinimalSubagentsWidgetRowParts {
|
|
161
|
+
identity: string;
|
|
162
|
+
status: string;
|
|
163
|
+
profile: string;
|
|
164
|
+
task?: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function joinMinimalSubagentsWidgetRow(
|
|
168
|
+
parts: MinimalSubagentsWidgetRowParts,
|
|
169
|
+
separator: string,
|
|
170
|
+
): string {
|
|
171
|
+
return [parts.identity, parts.status, parts.profile, parts.task]
|
|
172
|
+
.filter((part): part is string => part !== undefined)
|
|
173
|
+
.join(separator);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function formatMinimalSubagentsRuntimeProfile(
|
|
177
|
+
profile: RuntimeProfile,
|
|
178
|
+
maxWidth?: number,
|
|
179
|
+
): string | undefined {
|
|
180
|
+
const suffix = `:${profile.thinking_level}`;
|
|
181
|
+
const fullProfile = `${profile.model}${suffix}`;
|
|
182
|
+
if (maxWidth === undefined || visibleWidth(fullProfile) <= maxWidth) return fullProfile;
|
|
183
|
+
|
|
184
|
+
const shortestProfile = `${MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS}${suffix}`;
|
|
185
|
+
if (maxWidth < visibleWidth(shortestProfile)) return undefined;
|
|
186
|
+
const modelWidth = maxWidth - visibleWidth(suffix);
|
|
187
|
+
const prefix = sliceByColumn(
|
|
188
|
+
profile.model,
|
|
189
|
+
0,
|
|
190
|
+
Math.max(0, modelWidth - visibleWidth(MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS)),
|
|
191
|
+
true,
|
|
192
|
+
);
|
|
193
|
+
return `${prefix}${MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS}${suffix}`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function renderMinimalSubagentsWidgetRowParts(
|
|
197
|
+
row: MinimalSubagentsWidgetRow,
|
|
198
|
+
task: string | undefined,
|
|
199
|
+
duration: string | undefined,
|
|
200
|
+
profile: string,
|
|
201
|
+
theme: MinimalSubagentsWidgetTheme,
|
|
202
|
+
): MinimalSubagentsWidgetRowParts {
|
|
203
|
+
const branch = row.depth > 0 ? `${" ".repeat(row.depth)}╰─ ` : " ";
|
|
204
|
+
const styledBranch = theme.fg("borderMuted", branch);
|
|
205
|
+
const agentId = row.structural ? theme.fg("muted", row.agentId) : theme.bold(row.agentId);
|
|
206
|
+
const identity = `${styledBranch}${renderSubagentStatusSymbol(theme, row.status)} ${agentId}`;
|
|
207
|
+
const status = `${renderSubagentStatusLabel(theme, row.status)}${
|
|
208
|
+
duration ? ` ${theme.fg("muted", duration)}` : ""
|
|
209
|
+
}`;
|
|
210
|
+
return {
|
|
211
|
+
identity,
|
|
212
|
+
status,
|
|
213
|
+
profile: theme.fg("muted", profile),
|
|
214
|
+
task: task ? theme.fg("muted", task) : undefined,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function minimalSubagentsWidgetRowFits(
|
|
219
|
+
parts: MinimalSubagentsWidgetRowParts,
|
|
220
|
+
separator: string,
|
|
221
|
+
width: number,
|
|
222
|
+
): boolean {
|
|
223
|
+
return visibleWidth(joinMinimalSubagentsWidgetRow(parts, separator)) <= width;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function minimalSubagentsWidgetProfileBudget(
|
|
227
|
+
row: MinimalSubagentsWidgetRow,
|
|
228
|
+
task: string | undefined,
|
|
229
|
+
duration: string | undefined,
|
|
230
|
+
separator: string,
|
|
231
|
+
theme: MinimalSubagentsWidgetTheme,
|
|
232
|
+
width: number,
|
|
233
|
+
): number {
|
|
234
|
+
const fixedParts = renderMinimalSubagentsWidgetRowParts(row, task, duration, "", theme);
|
|
235
|
+
return width - visibleWidth(joinMinimalSubagentsWidgetRow(fixedParts, separator));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function renderMinimalSubagentsWidgetRow(
|
|
239
|
+
row: MinimalSubagentsWidgetRow,
|
|
240
|
+
width: number,
|
|
241
|
+
theme: MinimalSubagentsWidgetTheme,
|
|
242
|
+
): string {
|
|
243
|
+
const separator = theme.fg("dim", MINIMAL_SUBAGENTS_WIDGET_SEPARATOR_TEXT);
|
|
244
|
+
const task = row.task?.replace(/\s+/g, " ").trim() || undefined;
|
|
245
|
+
const duration = row.status === "unavailable" ? undefined : formatSubagentDuration(row.elapsedMs);
|
|
246
|
+
const fullProfile = formatMinimalSubagentsRuntimeProfile(row.runtimeProfile);
|
|
247
|
+
if (!fullProfile) return "";
|
|
248
|
+
|
|
249
|
+
const completeParts = renderMinimalSubagentsWidgetRowParts(
|
|
250
|
+
row,
|
|
251
|
+
task,
|
|
252
|
+
duration,
|
|
253
|
+
fullProfile,
|
|
254
|
+
theme,
|
|
255
|
+
);
|
|
256
|
+
if (minimalSubagentsWidgetRowFits(completeParts, separator, width)) {
|
|
257
|
+
return joinMinimalSubagentsWidgetRow(completeParts, separator);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const partsWithoutTask = renderMinimalSubagentsWidgetRowParts(
|
|
261
|
+
row,
|
|
262
|
+
undefined,
|
|
263
|
+
duration,
|
|
264
|
+
fullProfile,
|
|
265
|
+
theme,
|
|
266
|
+
);
|
|
267
|
+
if (minimalSubagentsWidgetRowFits(partsWithoutTask, separator, width)) {
|
|
268
|
+
return joinMinimalSubagentsWidgetRow(partsWithoutTask, separator);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const profileBudget = minimalSubagentsWidgetProfileBudget(
|
|
272
|
+
row,
|
|
273
|
+
undefined,
|
|
274
|
+
duration,
|
|
275
|
+
separator,
|
|
276
|
+
theme,
|
|
277
|
+
width,
|
|
278
|
+
);
|
|
279
|
+
const shortenedProfile = formatMinimalSubagentsRuntimeProfile(row.runtimeProfile, profileBudget);
|
|
280
|
+
if (shortenedProfile) {
|
|
281
|
+
const shortenedParts = renderMinimalSubagentsWidgetRowParts(
|
|
282
|
+
row,
|
|
283
|
+
undefined,
|
|
284
|
+
duration,
|
|
285
|
+
shortenedProfile,
|
|
286
|
+
theme,
|
|
287
|
+
);
|
|
288
|
+
if (minimalSubagentsWidgetRowFits(shortenedParts, separator, width)) {
|
|
289
|
+
return joinMinimalSubagentsWidgetRow(shortenedParts, separator);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (duration) {
|
|
294
|
+
const noDurationBudget = minimalSubagentsWidgetProfileBudget(
|
|
295
|
+
row,
|
|
296
|
+
undefined,
|
|
297
|
+
undefined,
|
|
298
|
+
separator,
|
|
299
|
+
theme,
|
|
300
|
+
width,
|
|
301
|
+
);
|
|
302
|
+
const noDurationProfile = formatMinimalSubagentsRuntimeProfile(
|
|
303
|
+
row.runtimeProfile,
|
|
304
|
+
noDurationBudget,
|
|
305
|
+
);
|
|
306
|
+
if (noDurationProfile) {
|
|
307
|
+
const noDurationParts = renderMinimalSubagentsWidgetRowParts(
|
|
308
|
+
row,
|
|
309
|
+
undefined,
|
|
310
|
+
undefined,
|
|
311
|
+
noDurationProfile,
|
|
312
|
+
theme,
|
|
313
|
+
);
|
|
314
|
+
if (minimalSubagentsWidgetRowFits(noDurationParts, separator, width)) {
|
|
315
|
+
return joinMinimalSubagentsWidgetRow(noDurationParts, separator);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const shortestProfile = formatMinimalSubagentsRuntimeProfile(
|
|
321
|
+
row.runtimeProfile,
|
|
322
|
+
visibleWidth(`${MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS}:${row.runtimeProfile.thinking_level}`),
|
|
323
|
+
)!;
|
|
324
|
+
const lastResortParts = renderMinimalSubagentsWidgetRowParts(
|
|
325
|
+
row,
|
|
326
|
+
undefined,
|
|
327
|
+
undefined,
|
|
328
|
+
shortestProfile,
|
|
329
|
+
theme,
|
|
330
|
+
);
|
|
331
|
+
return truncateToWidth(
|
|
332
|
+
joinMinimalSubagentsWidgetRow(lastResortParts, separator),
|
|
333
|
+
width,
|
|
334
|
+
MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
138
338
|
/** Render a responsive widget snapshot with ANSI-safe terminal-width truncation. */
|
|
139
339
|
export function renderMinimalSubagentsWidgetLines(
|
|
140
340
|
view: MinimalSubagentsWidgetView,
|
|
141
341
|
width: number,
|
|
142
|
-
theme:
|
|
342
|
+
theme: MinimalSubagentsWidgetTheme,
|
|
143
343
|
): string[] {
|
|
144
344
|
if (width <= 0) return [];
|
|
145
|
-
const separator = theme.fg("dim",
|
|
345
|
+
const separator = theme.fg("dim", MINIMAL_SUBAGENTS_WIDGET_SEPARATOR_TEXT);
|
|
146
346
|
const activity =
|
|
147
347
|
view.runningCount > 0
|
|
148
348
|
? theme.fg("accent", `${view.runningCount} running`)
|
|
@@ -159,27 +359,18 @@ export function renderMinimalSubagentsWidgetLines(
|
|
|
159
359
|
.filter((part): part is string => Boolean(part))
|
|
160
360
|
.join(separator),
|
|
161
361
|
width,
|
|
162
|
-
|
|
362
|
+
MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS,
|
|
163
363
|
),
|
|
164
364
|
];
|
|
165
|
-
for (const row of view.rows)
|
|
166
|
-
const duration = formatSubagentDuration(row.elapsedMs);
|
|
167
|
-
const task = row.task?.replace(/\s+/g, " ").trim();
|
|
168
|
-
const branch =
|
|
169
|
-
row.depth > 0
|
|
170
|
-
? theme.fg("borderMuted", `${" ".repeat(row.depth)}╰─ `)
|
|
171
|
-
: theme.fg("borderMuted", " ");
|
|
172
|
-
const agentId = row.structural ? theme.fg("muted", row.agentId) : theme.bold(row.agentId);
|
|
173
|
-
const parts = [
|
|
174
|
-
`${branch}${renderSubagentStatusSymbol(theme, row.status)} ${agentId}`,
|
|
175
|
-
row.structural ? undefined : renderSubagentStatusLabel(theme, row.status),
|
|
176
|
-
task ? theme.fg("muted", task) : undefined,
|
|
177
|
-
duration ? theme.fg("muted", duration) : undefined,
|
|
178
|
-
].filter((part): part is string => Boolean(part));
|
|
179
|
-
lines.push(truncateToWidth(parts.join(separator), width, "…"));
|
|
180
|
-
}
|
|
365
|
+
for (const row of view.rows) lines.push(renderMinimalSubagentsWidgetRow(row, width, theme));
|
|
181
366
|
if (view.overflowCount > 0) {
|
|
182
|
-
lines.push(
|
|
367
|
+
lines.push(
|
|
368
|
+
truncateToWidth(
|
|
369
|
+
theme.fg("dim", ` … +${view.overflowCount} more`),
|
|
370
|
+
width,
|
|
371
|
+
MINIMAL_SUBAGENTS_WIDGET_ELLIPSIS,
|
|
372
|
+
),
|
|
373
|
+
);
|
|
183
374
|
}
|
|
184
375
|
return lines;
|
|
185
376
|
}
|
|
@@ -188,7 +379,7 @@ class MinimalSubagentsWidgetComponent implements Component {
|
|
|
188
379
|
constructor(
|
|
189
380
|
private view: MinimalSubagentsWidgetView,
|
|
190
381
|
private readonly tui: TUI,
|
|
191
|
-
private readonly theme:
|
|
382
|
+
private readonly theme: MinimalSubagentsWidgetTheme,
|
|
192
383
|
) {}
|
|
193
384
|
|
|
194
385
|
update(view: MinimalSubagentsWidgetView): void {
|