@arhen/pi-core-subagent 1.0.4 → 1.0.6
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/package.json +1 -1
- package/src/index.ts +47 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arhen/pi-core-subagent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "pi extension: fast in-process subagents with single/parallel/chain, background runs, intercom and agent-to-agent mailbox. Leader defines agents inline.",
|
|
6
6
|
"license": "MIT",
|
package/src/index.ts
CHANGED
|
@@ -253,7 +253,7 @@ function compactLines(run: RunSnapshot): string[] {
|
|
|
253
253
|
*/
|
|
254
254
|
class SubagentsWidget implements Component {
|
|
255
255
|
constructor(
|
|
256
|
-
private readonly
|
|
256
|
+
private readonly getRuns: () => RunSnapshot[],
|
|
257
257
|
private readonly theme: Theme,
|
|
258
258
|
) {}
|
|
259
259
|
|
|
@@ -262,28 +262,33 @@ class SubagentsWidget implements Component {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
render(width: number): string[] {
|
|
265
|
-
const
|
|
266
|
-
if (
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
265
|
+
const runs = this.getRuns();
|
|
266
|
+
if (runs.length === 0) return [];
|
|
267
|
+
const lines: string[] = [];
|
|
268
|
+
runs.forEach((run, ri) => {
|
|
269
|
+
if (run.tasks.length === 0) return;
|
|
270
|
+
if (ri > 0) lines.push("");
|
|
271
|
+
const done = run.tasks.filter((t) => TERMINAL.includes(t.status)).length;
|
|
272
|
+
const active = !TERMINAL.includes(run.status);
|
|
273
|
+
const head = active ? "accent" : "dim";
|
|
274
|
+
lines.push(truncateToWidth(`${this.theme.fg(head, active ? "●" : "○")} ${this.theme.fg(head, `Subagents (${done}/${run.tasks.length})`)}`, width, "…"));
|
|
275
|
+
const allDone = TERMINAL.includes(run.status);
|
|
276
|
+
const visible = run.tasks.slice(0, MAX_TASKS);
|
|
277
|
+
visible.forEach((task, i) => {
|
|
278
|
+
const last = i === visible.length - 1 && run.tasks.length <= MAX_TASKS;
|
|
279
|
+
const conn = this.theme.fg("dim", last ? "└─" : "├─");
|
|
280
|
+
const activity =
|
|
281
|
+
!TERMINAL.includes(task.status) && task.lastActivity
|
|
282
|
+
? `${this.theme.fg("dim", `→ ${task.lastActivity}`)} · `
|
|
283
|
+
: "";
|
|
284
|
+
// Finished run: dim everything except the agent name.
|
|
285
|
+
const line = allDone
|
|
286
|
+
? `${this.theme.fg("dim", `${statusIcon(task.status)} `)}${task.agent} ${this.theme.fg("dim", `· ${taskStatsWithUsage(task)} · ${taskTimer(task)}`)}`
|
|
287
|
+
: `${statusIcon(task.status)} ${task.agent} · ${activity}${taskStatsWithUsage(task)} · ${taskTimer(task)}`;
|
|
288
|
+
lines.push(truncateToWidth(`${conn} ${line}`, width, "…"));
|
|
289
|
+
});
|
|
290
|
+
if (run.tasks.length > MAX_TASKS) lines.push(`${this.theme.fg("dim", "└─")} ${this.theme.fg("dim", `+${run.tasks.length - MAX_TASKS} more`)}`);
|
|
285
291
|
});
|
|
286
|
-
if (run.tasks.length > MAX_TASKS) lines.push(`${this.theme.fg("dim", "└─")} ${this.theme.fg("dim", `+${run.tasks.length - MAX_TASKS} more`)}`);
|
|
287
292
|
return lines;
|
|
288
293
|
}
|
|
289
294
|
}
|
|
@@ -383,7 +388,7 @@ class SubagentManager {
|
|
|
383
388
|
private mailboxes: Mailbox = createMailbox();
|
|
384
389
|
private runControllers = new Map<string, AbortController>();
|
|
385
390
|
private widgetTimer: ReturnType<typeof setTimeout> | undefined;
|
|
386
|
-
private
|
|
391
|
+
private widgetRuns: RunSnapshot[] = [];
|
|
387
392
|
|
|
388
393
|
turnActivity = false;
|
|
389
394
|
|
|
@@ -399,7 +404,7 @@ class SubagentManager {
|
|
|
399
404
|
|
|
400
405
|
/** Hide the widget. */
|
|
401
406
|
clearWidget(ctx: ExtensionContext): void {
|
|
402
|
-
this.
|
|
407
|
+
this.widgetRuns = [];
|
|
403
408
|
this.widgetTui = null;
|
|
404
409
|
if (ctx.hasUI) {
|
|
405
410
|
try {
|
|
@@ -425,7 +430,7 @@ class SubagentManager {
|
|
|
425
430
|
this.widgetTui = null; // force re-registration on the next session
|
|
426
431
|
if (this.widgetTimer) clearTimeout(this.widgetTimer);
|
|
427
432
|
this.widgetTimer = undefined;
|
|
428
|
-
this.
|
|
433
|
+
this.widgetRuns = [];
|
|
429
434
|
}
|
|
430
435
|
|
|
431
436
|
// ── persistence (sidecar per parent session) ────────────────────────
|
|
@@ -508,19 +513,27 @@ class SubagentManager {
|
|
|
508
513
|
// The component self-animates the spinner via its own 100ms interval;
|
|
509
514
|
// scheduleWidget just throttles status changes into requestRender calls.
|
|
510
515
|
private widgetTui: TUI | null = null;
|
|
516
|
+
/** Upsert a run into the widget's visible set (all runs, not just the latest). */
|
|
517
|
+
private upsertWidgetRun(run: RunSnapshot | undefined): void {
|
|
518
|
+
if (!run) return;
|
|
519
|
+
const idx = this.widgetRuns.findIndex((r) => r.id === run.id);
|
|
520
|
+
if (idx >= 0) this.widgetRuns[idx] = run;
|
|
521
|
+
else this.widgetRuns.push(run);
|
|
522
|
+
}
|
|
511
523
|
private scheduleWidget(run: RunSnapshot | undefined, ctx?: ExtensionContext, onUpdate?: (partial: any) => void): void {
|
|
512
|
-
|
|
513
|
-
if (this.widgetTimer ||
|
|
524
|
+
this.upsertWidgetRun(run);
|
|
525
|
+
if (this.widgetTimer || this.widgetRuns.length === 0) return;
|
|
514
526
|
this.widgetTimer = setTimeout(() => {
|
|
515
527
|
this.widgetTimer = undefined;
|
|
516
|
-
|
|
517
|
-
|
|
528
|
+
if (this.widgetRuns.length === 0) return;
|
|
529
|
+
const targets = [...this.widgetRuns]; // read at fire: never render stale runs
|
|
518
530
|
if (ctx?.hasUI) {
|
|
519
|
-
|
|
531
|
+
const live = targets.reduce((n, r) => n + r.tasks.filter((t) => !TERMINAL.includes(t.status)).length, 0);
|
|
532
|
+
ctx.ui.setStatus("subagents", `subagents: ${live} running`);
|
|
520
533
|
this.ensureWidget(ctx);
|
|
521
534
|
this.widgetTui?.requestRender();
|
|
522
535
|
}
|
|
523
|
-
onUpdate?.({ content: [{ type: "text", text: compactLines
|
|
536
|
+
onUpdate?.({ content: [{ type: "text", text: targets.flatMap(compactLines).join("\n") }] });
|
|
524
537
|
}, WIDGET_THROTTLE_MS);
|
|
525
538
|
}
|
|
526
539
|
private flushWidget(ctx?: ExtensionContext, onUpdate?: (partial: any) => void): void {
|
|
@@ -528,14 +541,12 @@ class SubagentManager {
|
|
|
528
541
|
clearTimeout(this.widgetTimer);
|
|
529
542
|
this.widgetTimer = undefined;
|
|
530
543
|
}
|
|
531
|
-
|
|
532
|
-
if (!run) return;
|
|
544
|
+
if (this.widgetRuns.length === 0) return;
|
|
533
545
|
if (ctx?.hasUI) {
|
|
534
|
-
ctx.ui.setStatus("subagents", `subagents: ${run.status}`);
|
|
535
546
|
this.ensureWidget(ctx);
|
|
536
547
|
this.widgetTui?.requestRender();
|
|
537
548
|
}
|
|
538
|
-
onUpdate?.({ content: [{ type: "text", text: compactLines
|
|
549
|
+
onUpdate?.({ content: [{ type: "text", text: this.widgetRuns.flatMap(compactLines).join("\n") }] });
|
|
539
550
|
}
|
|
540
551
|
private ensureWidget(ctx: ExtensionContext): void {
|
|
541
552
|
if (this.widgetTui !== null || !ctx.hasUI) return;
|
|
@@ -543,7 +554,7 @@ class SubagentManager {
|
|
|
543
554
|
"subagents",
|
|
544
555
|
(tui, theme) => {
|
|
545
556
|
this.widgetTui = tui;
|
|
546
|
-
return new SubagentsWidget(() => this.
|
|
557
|
+
return new SubagentsWidget(() => [...this.widgetRuns], theme);
|
|
547
558
|
},
|
|
548
559
|
{ placement: "aboveEditor" },
|
|
549
560
|
);
|