@fosterg4/pi-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/index.ts +14 -6
- package/package.json +1 -1
- package/ui.ts +51 -53
package/index.ts
CHANGED
|
@@ -647,10 +647,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
647
647
|
let widgetHandle: ReturnType<typeof pi.ui.custom> | undefined;
|
|
648
648
|
|
|
649
649
|
const closeWidget = () => {
|
|
650
|
+
if (widgetTimer) { clearInterval(widgetTimer); widgetTimer = undefined; }
|
|
650
651
|
if (widgetHandle) {
|
|
651
652
|
widgetHandle.close();
|
|
652
653
|
widgetHandle = undefined;
|
|
653
654
|
}
|
|
655
|
+
widgetRef = undefined;
|
|
654
656
|
};
|
|
655
657
|
|
|
656
658
|
for (let i = 0; i < params.chain.length; i++) {
|
|
@@ -673,16 +675,22 @@ export default function (pi: ExtensionAPI) {
|
|
|
673
675
|
}
|
|
674
676
|
|
|
675
677
|
// Spawn live widget for this step
|
|
678
|
+
let widgetRef: AgentWidget | undefined;
|
|
679
|
+
let widgetTimer: ReturnType<typeof setInterval> | undefined;
|
|
676
680
|
if (ctx.hasUI) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
widgetHandle = ctx.ui.custom(
|
|
681
|
+
widgetRef = new AgentWidget();
|
|
682
|
+
widgetRef.addAgent(step.agent, step.task.replace(/\{[^}]+\}/g, "").trim());
|
|
683
|
+
widgetHandle = ctx.ui.custom(
|
|
684
|
+
(_tui, _theme, _kb, done) => {
|
|
685
|
+
widgetTimer = setInterval(() => widgetRef?.invalidate(), 200);
|
|
686
|
+
return widgetRef!;
|
|
687
|
+
},
|
|
688
|
+
{ overlay: true },
|
|
689
|
+
);
|
|
680
690
|
}
|
|
681
691
|
|
|
682
692
|
const stepStats = (stats: { turns: number; tokens: number }) => {
|
|
683
|
-
|
|
684
|
-
widgetHandle.requestRender();
|
|
685
|
-
}
|
|
693
|
+
widgetRef?.invalidate();
|
|
686
694
|
};
|
|
687
695
|
|
|
688
696
|
const chainUpdate: OnUpdateCallback | undefined = onUpdate
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fosterg4/pi-subagent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Delegate tasks to specialized subagents with isolated context windows, structured JSON handoff, contract schemas, and live TUI streaming",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
package/ui.ts
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* handle.close();
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { Container, Spacer, Text } from "@earendil-works/pi-tui";
|
|
16
15
|
import { fmt } from "./utils.ts";
|
|
17
16
|
|
|
18
17
|
// ---------------------------------------------------------------------------
|
|
@@ -71,8 +70,6 @@ export class AgentWidget {
|
|
|
71
70
|
private entries: WidgetEntry[] = [];
|
|
72
71
|
private frame = 0;
|
|
73
72
|
private startTimes: Map<string, number> = new Map();
|
|
74
|
-
private cachedWidth?: number;
|
|
75
|
-
private cachedLines?: string[];
|
|
76
73
|
|
|
77
74
|
addAgent(name: string, task: string): void {
|
|
78
75
|
const exists = this.entries.find((e) => e.name === name);
|
|
@@ -93,7 +90,6 @@ export class AgentWidget {
|
|
|
93
90
|
const entry = this.entries.find((e) => e.name === name);
|
|
94
91
|
if (!entry) return;
|
|
95
92
|
Object.assign(entry, update);
|
|
96
|
-
entry.elapsedMs = Date.now() - (this.startTimes.get(name) ?? Date.now());
|
|
97
93
|
this.invalidate();
|
|
98
94
|
}
|
|
99
95
|
|
|
@@ -107,70 +103,72 @@ export class AgentWidget {
|
|
|
107
103
|
return this.entries.find((e) => e.name === name);
|
|
108
104
|
}
|
|
109
105
|
|
|
110
|
-
|
|
111
|
-
this.
|
|
112
|
-
|
|
106
|
+
markDone(name: string): void {
|
|
107
|
+
const entry = this.entries.find((e) => e.name === name);
|
|
108
|
+
if (entry) { entry.status = "done"; }
|
|
109
|
+
this.invalidate();
|
|
113
110
|
}
|
|
114
111
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
112
|
+
invalidate(): void {
|
|
113
|
+
// No cache to invalidate — render() is always fresh
|
|
114
|
+
}
|
|
119
115
|
|
|
116
|
+
private entryLine(entry: WidgetEntry, width: number): string[] {
|
|
120
117
|
const lines: string[] = [];
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
const elapsedMs = entry.elapsedMs || (now - (this.startTimes.get(entry.name) ?? now));
|
|
121
120
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.cachedLines = [];
|
|
125
|
-
return [];
|
|
126
|
-
}
|
|
121
|
+
const icon = statusIcon(entry.status, this.frame);
|
|
122
|
+
const parts: string[] = [];
|
|
127
123
|
|
|
128
|
-
//
|
|
129
|
-
|
|
124
|
+
// Always show icon + name
|
|
125
|
+
let line = `\u00A0${icon} ${entry.name}`;
|
|
130
126
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const tokStr = fmt(entry.tokens);
|
|
141
|
-
parts.push(`${tokStr} token`);
|
|
142
|
-
if (entry.contextUsagePct !== undefined && entry.contextUsagePct > 0) {
|
|
143
|
-
parts.push(`(${entry.contextUsagePct}%)`);
|
|
144
|
-
}
|
|
127
|
+
// Turns
|
|
128
|
+
parts.push(entry.turns > 0 ? `↻${entry.turns}` : "↻0");
|
|
129
|
+
|
|
130
|
+
// Tokens
|
|
131
|
+
if (entry.tokens > 0) {
|
|
132
|
+
const tokStr = fmt(entry.tokens);
|
|
133
|
+
parts.push(`${tokStr} token`);
|
|
134
|
+
if (entry.contextUsagePct !== undefined && entry.contextUsagePct > 0) {
|
|
135
|
+
parts.push(`(${entry.contextUsagePct}%)`);
|
|
145
136
|
}
|
|
137
|
+
}
|
|
146
138
|
|
|
147
|
-
|
|
148
|
-
|
|
139
|
+
// Time
|
|
140
|
+
if (elapsedMs > 99) parts.push(formatTime(elapsedMs));
|
|
141
|
+
else parts.push("0.0s");
|
|
149
142
|
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
// Model
|
|
144
|
+
if (entry.model) parts.push(entry.model);
|
|
152
145
|
|
|
153
|
-
|
|
146
|
+
line += ` · ${parts.join(" · ")}`;
|
|
147
|
+
if (line.length > width) line = line.slice(0, width - 3) + "…";
|
|
148
|
+
lines.push(line);
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
150
|
+
// Activity sub-line (running agents only)
|
|
151
|
+
if (entry.task && entry.status === "running") {
|
|
152
|
+
const preview = entry.task.length > 60 ? `${entry.task.slice(0, 57)}…` : entry.task;
|
|
153
|
+
lines.push(` ⎿ ${preview}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return lines;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
render(width: number): string[] {
|
|
160
|
+
const lines: string[] = [];
|
|
161
|
+
|
|
162
|
+
if (this.entries.length === 0) return [];
|
|
163
|
+
|
|
164
|
+
// Header
|
|
165
|
+
lines.push("\u25CF Agents");
|
|
166
|
+
|
|
167
|
+
for (const entry of this.entries) {
|
|
168
|
+
lines.push(...this.entryLine(entry, width));
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
this.frame++;
|
|
172
|
-
this.cachedWidth = width;
|
|
173
|
-
this.cachedLines = lines;
|
|
174
172
|
return lines;
|
|
175
173
|
}
|
|
176
174
|
}
|