@arhen/pi-core-subagent 1.0.3 → 1.0.5
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 +36 -16
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.5",
|
|
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
|
@@ -383,15 +383,23 @@ class SubagentManager {
|
|
|
383
383
|
private mailboxes: Mailbox = createMailbox();
|
|
384
384
|
private runControllers = new Map<string, AbortController>();
|
|
385
385
|
private widgetTimer: ReturnType<typeof setTimeout> | undefined;
|
|
386
|
-
private
|
|
386
|
+
private widgetRuns: RunSnapshot[] = [];
|
|
387
387
|
|
|
388
388
|
turnActivity = false;
|
|
389
389
|
|
|
390
390
|
constructor(private readonly pi: ExtensionAPI) {}
|
|
391
391
|
|
|
392
|
-
/**
|
|
392
|
+
/** Any run still has queued/running tasks? */
|
|
393
|
+
hasActiveRun(): boolean {
|
|
394
|
+
for (const run of this.runs.values()) {
|
|
395
|
+
if (run.tasks.some((t) => !TERMINAL.includes(t.status))) return true;
|
|
396
|
+
}
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Hide the widget. */
|
|
393
401
|
clearWidget(ctx: ExtensionContext): void {
|
|
394
|
-
this.
|
|
402
|
+
this.widgetRuns = [];
|
|
395
403
|
this.widgetTui = null;
|
|
396
404
|
if (ctx.hasUI) {
|
|
397
405
|
try {
|
|
@@ -417,7 +425,7 @@ class SubagentManager {
|
|
|
417
425
|
this.widgetTui = null; // force re-registration on the next session
|
|
418
426
|
if (this.widgetTimer) clearTimeout(this.widgetTimer);
|
|
419
427
|
this.widgetTimer = undefined;
|
|
420
|
-
this.
|
|
428
|
+
this.widgetRuns = [];
|
|
421
429
|
}
|
|
422
430
|
|
|
423
431
|
// ── persistence (sidecar per parent session) ────────────────────────
|
|
@@ -500,19 +508,27 @@ class SubagentManager {
|
|
|
500
508
|
// The component self-animates the spinner via its own 100ms interval;
|
|
501
509
|
// scheduleWidget just throttles status changes into requestRender calls.
|
|
502
510
|
private widgetTui: TUI | null = null;
|
|
511
|
+
/** Upsert a run into the widget's visible set (all runs, not just the latest). */
|
|
512
|
+
private upsertWidgetRun(run: RunSnapshot | undefined): void {
|
|
513
|
+
if (!run) return;
|
|
514
|
+
const idx = this.widgetRuns.findIndex((r) => r.id === run.id);
|
|
515
|
+
if (idx >= 0) this.widgetRuns[idx] = run;
|
|
516
|
+
else this.widgetRuns.push(run);
|
|
517
|
+
}
|
|
503
518
|
private scheduleWidget(run: RunSnapshot | undefined, ctx?: ExtensionContext, onUpdate?: (partial: any) => void): void {
|
|
504
|
-
|
|
505
|
-
if (this.widgetTimer ||
|
|
519
|
+
this.upsertWidgetRun(run);
|
|
520
|
+
if (this.widgetTimer || this.widgetRuns.length === 0) return;
|
|
506
521
|
this.widgetTimer = setTimeout(() => {
|
|
507
522
|
this.widgetTimer = undefined;
|
|
508
|
-
|
|
509
|
-
|
|
523
|
+
if (this.widgetRuns.length === 0) return;
|
|
524
|
+
const targets = [...this.widgetRuns]; // read at fire: never render stale runs
|
|
510
525
|
if (ctx?.hasUI) {
|
|
511
|
-
|
|
526
|
+
const live = targets.reduce((n, r) => n + r.tasks.filter((t) => !TERMINAL.includes(t.status)).length, 0);
|
|
527
|
+
ctx.ui.setStatus("subagents", `subagents: ${live} running`);
|
|
512
528
|
this.ensureWidget(ctx);
|
|
513
529
|
this.widgetTui?.requestRender();
|
|
514
530
|
}
|
|
515
|
-
onUpdate?.({ content: [{ type: "text", text: compactLines
|
|
531
|
+
onUpdate?.({ content: [{ type: "text", text: targets.flatMap(compactLines).join("\n") }] });
|
|
516
532
|
}, WIDGET_THROTTLE_MS);
|
|
517
533
|
}
|
|
518
534
|
private flushWidget(ctx?: ExtensionContext, onUpdate?: (partial: any) => void): void {
|
|
@@ -520,14 +536,12 @@ class SubagentManager {
|
|
|
520
536
|
clearTimeout(this.widgetTimer);
|
|
521
537
|
this.widgetTimer = undefined;
|
|
522
538
|
}
|
|
523
|
-
|
|
524
|
-
if (!run) return;
|
|
539
|
+
if (this.widgetRuns.length === 0) return;
|
|
525
540
|
if (ctx?.hasUI) {
|
|
526
|
-
ctx.ui.setStatus("subagents", `subagents: ${run.status}`);
|
|
527
541
|
this.ensureWidget(ctx);
|
|
528
542
|
this.widgetTui?.requestRender();
|
|
529
543
|
}
|
|
530
|
-
onUpdate?.({ content: [{ type: "text", text: compactLines
|
|
544
|
+
onUpdate?.({ content: [{ type: "text", text: this.widgetRuns.flatMap(compactLines).join("\n") }] });
|
|
531
545
|
}
|
|
532
546
|
private ensureWidget(ctx: ExtensionContext): void {
|
|
533
547
|
if (this.widgetTui !== null || !ctx.hasUI) return;
|
|
@@ -535,7 +549,7 @@ class SubagentManager {
|
|
|
535
549
|
"subagents",
|
|
536
550
|
(tui, theme) => {
|
|
537
551
|
this.widgetTui = tui;
|
|
538
|
-
return new SubagentsWidget(() => this.
|
|
552
|
+
return new SubagentsWidget(() => [...this.widgetRuns], theme);
|
|
539
553
|
},
|
|
540
554
|
{ placement: "aboveEditor" },
|
|
541
555
|
);
|
|
@@ -875,7 +889,13 @@ class SubagentManager {
|
|
|
875
889
|
run.status = aborted ? "aborted" : failed ? "failed" : "completed";
|
|
876
890
|
run.endedAt = Date.now();
|
|
877
891
|
this.flushWidget(ctx, onUpdate);
|
|
878
|
-
|
|
892
|
+
// Run done: keep the widget only while another run is still live.
|
|
893
|
+
const live = this.listRuns().find((r) => !TERMINAL.includes(r.status));
|
|
894
|
+
if (live) {
|
|
895
|
+
this.scheduleWidget(live, ctx, onUpdate);
|
|
896
|
+
} else {
|
|
897
|
+
this.clearWidget(ctx);
|
|
898
|
+
}
|
|
879
899
|
this.emit("subagent:run-completed", { runId: run.id, status: run.status, run: cloneRun(run), aggregateUsage: run.aggregateUsage });
|
|
880
900
|
this.settleRun(run.id, run);
|
|
881
901
|
this.runControllers.delete(run.id);
|