@tintinweb/pi-subagents 0.4.9 → 0.4.11
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/.github/workflows/ci.yml +21 -0
- package/CHANGELOG.md +18 -0
- package/README.md +11 -11
- package/biome.json +26 -0
- package/dist/agent-manager.d.ts +18 -4
- package/dist/agent-manager.js +111 -9
- package/dist/agent-runner.d.ts +10 -6
- package/dist/agent-runner.js +80 -26
- package/dist/agent-types.d.ts +10 -0
- package/dist/agent-types.js +23 -1
- package/dist/cross-extension-rpc.d.ts +30 -0
- package/dist/cross-extension-rpc.js +33 -0
- package/dist/custom-agents.js +36 -8
- package/dist/index.js +335 -66
- package/dist/memory.d.ts +49 -0
- package/dist/memory.js +151 -0
- package/dist/output-file.d.ts +17 -0
- package/dist/output-file.js +66 -0
- package/dist/prompts.d.ts +12 -1
- package/dist/prompts.js +15 -3
- package/dist/skill-loader.d.ts +19 -0
- package/dist/skill-loader.js +67 -0
- package/dist/types.d.ts +45 -1
- package/dist/ui/agent-widget.d.ts +21 -0
- package/dist/ui/agent-widget.js +205 -127
- package/dist/ui/conversation-viewer.d.ts +2 -2
- package/dist/ui/conversation-viewer.js +2 -2
- package/dist/ui/conversation-viewer.test.d.ts +1 -0
- package/dist/ui/conversation-viewer.test.js +254 -0
- package/dist/worktree.d.ts +36 -0
- package/dist/worktree.js +139 -0
- package/package.json +7 -2
- package/src/agent-manager.ts +7 -5
- package/src/agent-runner.ts +24 -19
- package/src/agent-types.ts +5 -5
- package/src/custom-agents.ts +4 -4
- package/src/index.ts +54 -33
- package/src/memory.ts +2 -2
- package/src/output-file.ts +1 -1
- package/src/skill-loader.ts +1 -1
- package/src/types.ts +3 -1
- package/src/ui/agent-widget.ts +18 -2
- package/src/ui/conversation-viewer.ts +4 -4
- package/src/worktree.ts +2 -2
package/dist/ui/agent-widget.js
CHANGED
|
@@ -32,6 +32,10 @@ export function formatTokens(count) {
|
|
|
32
32
|
return `${(count / 1_000).toFixed(1)}k token`;
|
|
33
33
|
return `${count} token`;
|
|
34
34
|
}
|
|
35
|
+
/** Format turn count with optional max limit: "⟳5≤30" or "⟳5". */
|
|
36
|
+
export function formatTurns(turnCount, maxTurns) {
|
|
37
|
+
return maxTurns != null ? `⟳${turnCount}≤${maxTurns}` : `⟳${turnCount}`;
|
|
38
|
+
}
|
|
35
39
|
/** Format milliseconds as human-readable duration. */
|
|
36
40
|
export function formatMs(ms) {
|
|
37
41
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
@@ -94,13 +98,26 @@ export class AgentWidget {
|
|
|
94
98
|
finishedTurnAge = new Map();
|
|
95
99
|
/** How many extra turns errors/aborted agents linger (completed agents clear after 1 turn). */
|
|
96
100
|
static ERROR_LINGER_TURNS = 2;
|
|
101
|
+
/** Whether the widget callback is currently registered with the TUI. */
|
|
102
|
+
widgetRegistered = false;
|
|
103
|
+
/** Cached TUI reference from widget factory callback, used for requestRender(). */
|
|
104
|
+
tui;
|
|
105
|
+
/** Last status bar text, used to avoid redundant setStatus calls. */
|
|
106
|
+
lastStatusText;
|
|
97
107
|
constructor(manager, agentActivity) {
|
|
98
108
|
this.manager = manager;
|
|
99
109
|
this.agentActivity = agentActivity;
|
|
100
110
|
}
|
|
101
111
|
/** Set the UI context (grabbed from first tool execution). */
|
|
102
112
|
setUICtx(ctx) {
|
|
103
|
-
this.uiCtx
|
|
113
|
+
if (ctx !== this.uiCtx) {
|
|
114
|
+
// UICtx changed — the widget registered on the old context is gone.
|
|
115
|
+
// Force re-registration on next update().
|
|
116
|
+
this.uiCtx = ctx;
|
|
117
|
+
this.widgetRegistered = false;
|
|
118
|
+
this.tui = undefined;
|
|
119
|
+
this.lastStatusText = undefined;
|
|
120
|
+
}
|
|
104
121
|
}
|
|
105
122
|
/**
|
|
106
123
|
* Called on each new turn (tool_execution_start).
|
|
@@ -162,16 +179,20 @@ export class AgentWidget {
|
|
|
162
179
|
statusText = theme.fg("warning", " aborted");
|
|
163
180
|
}
|
|
164
181
|
const parts = [];
|
|
182
|
+
const activity = this.agentActivity.get(a.id);
|
|
183
|
+
if (activity)
|
|
184
|
+
parts.push(formatTurns(activity.turnCount, activity.maxTurns));
|
|
165
185
|
if (a.toolUses > 0)
|
|
166
186
|
parts.push(`${a.toolUses} tool use${a.toolUses === 1 ? "" : "s"}`);
|
|
167
187
|
parts.push(duration);
|
|
168
188
|
const modeTag = modeLabel ? ` ${theme.fg("dim", `(${modeLabel})`)}` : "";
|
|
169
189
|
return `${icon} ${theme.fg("dim", name)}${modeTag} ${theme.fg("dim", a.description)} ${theme.fg("dim", "·")} ${theme.fg("dim", parts.join(" · "))}${statusText}`;
|
|
170
190
|
}
|
|
171
|
-
/**
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Render the widget content. Called from the registered widget's render() callback,
|
|
193
|
+
* reading live state each time instead of capturing it in a closure.
|
|
194
|
+
*/
|
|
195
|
+
renderWidget(tui, theme) {
|
|
175
196
|
const allAgents = this.manager.listAgents();
|
|
176
197
|
const running = allAgents.filter(a => a.status === "running");
|
|
177
198
|
const queued = allAgents.filter(a => a.status === "queued");
|
|
@@ -179,10 +200,153 @@ export class AgentWidget {
|
|
|
179
200
|
&& this.shouldShowFinished(a.id, a.status));
|
|
180
201
|
const hasActive = running.length > 0 || queued.length > 0;
|
|
181
202
|
const hasFinished = finished.length > 0;
|
|
203
|
+
// Nothing to show — return empty (widget will be unregistered by update())
|
|
204
|
+
if (!hasActive && !hasFinished)
|
|
205
|
+
return [];
|
|
206
|
+
const w = tui.terminal.columns;
|
|
207
|
+
const truncate = (line) => truncateToWidth(line, w);
|
|
208
|
+
const headingColor = hasActive ? "accent" : "dim";
|
|
209
|
+
const headingIcon = hasActive ? "●" : "○";
|
|
210
|
+
const frame = SPINNER[this.widgetFrame % SPINNER.length];
|
|
211
|
+
// Build sections separately for overflow-aware assembly.
|
|
212
|
+
// Each running agent = 2 lines (header + activity), finished = 1 line, queued = 1 line.
|
|
213
|
+
const finishedLines = [];
|
|
214
|
+
for (const a of finished) {
|
|
215
|
+
finishedLines.push(truncate(theme.fg("dim", "├─") + " " + this.renderFinishedLine(a, theme)));
|
|
216
|
+
}
|
|
217
|
+
const runningLines = []; // each entry is [header, activity]
|
|
218
|
+
for (const a of running) {
|
|
219
|
+
const name = getDisplayName(a.type);
|
|
220
|
+
const modeLabel = getPromptModeLabel(a.type);
|
|
221
|
+
const modeTag = modeLabel ? ` ${theme.fg("dim", `(${modeLabel})`)}` : "";
|
|
222
|
+
const elapsed = formatMs(Date.now() - a.startedAt);
|
|
223
|
+
const bg = this.agentActivity.get(a.id);
|
|
224
|
+
const toolUses = bg?.toolUses ?? a.toolUses;
|
|
225
|
+
let tokenText = "";
|
|
226
|
+
if (bg?.session) {
|
|
227
|
+
try {
|
|
228
|
+
tokenText = formatTokens(bg.session.getSessionStats().tokens.total);
|
|
229
|
+
}
|
|
230
|
+
catch { /* */ }
|
|
231
|
+
}
|
|
232
|
+
const parts = [];
|
|
233
|
+
if (bg)
|
|
234
|
+
parts.push(formatTurns(bg.turnCount, bg.maxTurns));
|
|
235
|
+
if (toolUses > 0)
|
|
236
|
+
parts.push(`${toolUses} tool use${toolUses === 1 ? "" : "s"}`);
|
|
237
|
+
if (tokenText)
|
|
238
|
+
parts.push(tokenText);
|
|
239
|
+
parts.push(elapsed);
|
|
240
|
+
const statsText = parts.join(" · ");
|
|
241
|
+
const activity = bg ? describeActivity(bg.activeTools, bg.responseText) : "thinking…";
|
|
242
|
+
runningLines.push([
|
|
243
|
+
truncate(theme.fg("dim", "├─") + ` ${theme.fg("accent", frame)} ${theme.bold(name)}${modeTag} ${theme.fg("muted", a.description)} ${theme.fg("dim", "·")} ${theme.fg("dim", statsText)}`),
|
|
244
|
+
truncate(theme.fg("dim", "│ ") + theme.fg("dim", ` ⎿ ${activity}`)),
|
|
245
|
+
]);
|
|
246
|
+
}
|
|
247
|
+
const queuedLine = queued.length > 0
|
|
248
|
+
? truncate(theme.fg("dim", "├─") + ` ${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`)
|
|
249
|
+
: undefined;
|
|
250
|
+
// Assemble with overflow cap (heading + overflow indicator = 2 reserved lines).
|
|
251
|
+
const maxBody = MAX_WIDGET_LINES - 1; // heading takes 1 line
|
|
252
|
+
const totalBody = finishedLines.length + runningLines.length * 2 + (queuedLine ? 1 : 0);
|
|
253
|
+
const lines = [truncate(theme.fg(headingColor, headingIcon) + " " + theme.fg(headingColor, "Agents"))];
|
|
254
|
+
if (totalBody <= maxBody) {
|
|
255
|
+
// Everything fits — add all lines and fix up connectors for the last item.
|
|
256
|
+
lines.push(...finishedLines);
|
|
257
|
+
for (const pair of runningLines)
|
|
258
|
+
lines.push(...pair);
|
|
259
|
+
if (queuedLine)
|
|
260
|
+
lines.push(queuedLine);
|
|
261
|
+
// Fix last connector: swap ├─ → └─ and │ → space for activity lines.
|
|
262
|
+
if (lines.length > 1) {
|
|
263
|
+
const last = lines.length - 1;
|
|
264
|
+
lines[last] = lines[last].replace("├─", "└─");
|
|
265
|
+
// If last item is a running agent activity line, fix indent of that line
|
|
266
|
+
// and fix the header line above it.
|
|
267
|
+
if (runningLines.length > 0 && !queuedLine) {
|
|
268
|
+
// The last two lines are the last running agent's header + activity.
|
|
269
|
+
if (last >= 2) {
|
|
270
|
+
lines[last - 1] = lines[last - 1].replace("├─", "└─");
|
|
271
|
+
lines[last] = lines[last].replace("│ ", " ");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// Overflow — prioritize: running > queued > finished.
|
|
278
|
+
// Reserve 1 line for overflow indicator.
|
|
279
|
+
let budget = maxBody - 1;
|
|
280
|
+
let hiddenRunning = 0;
|
|
281
|
+
let hiddenFinished = 0;
|
|
282
|
+
// 1. Running agents (2 lines each)
|
|
283
|
+
for (const pair of runningLines) {
|
|
284
|
+
if (budget >= 2) {
|
|
285
|
+
lines.push(...pair);
|
|
286
|
+
budget -= 2;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
hiddenRunning++;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// 2. Queued line
|
|
293
|
+
if (queuedLine && budget >= 1) {
|
|
294
|
+
lines.push(queuedLine);
|
|
295
|
+
budget--;
|
|
296
|
+
}
|
|
297
|
+
// 3. Finished agents
|
|
298
|
+
for (const fl of finishedLines) {
|
|
299
|
+
if (budget >= 1) {
|
|
300
|
+
lines.push(fl);
|
|
301
|
+
budget--;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
hiddenFinished++;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Overflow summary
|
|
308
|
+
const overflowParts = [];
|
|
309
|
+
if (hiddenRunning > 0)
|
|
310
|
+
overflowParts.push(`${hiddenRunning} running`);
|
|
311
|
+
if (hiddenFinished > 0)
|
|
312
|
+
overflowParts.push(`${hiddenFinished} finished`);
|
|
313
|
+
const overflowText = overflowParts.join(", ");
|
|
314
|
+
lines.push(truncate(theme.fg("dim", "└─") + ` ${theme.fg("dim", `+${hiddenRunning + hiddenFinished} more (${overflowText})`)}`));
|
|
315
|
+
}
|
|
316
|
+
return lines;
|
|
317
|
+
}
|
|
318
|
+
/** Force an immediate widget update. */
|
|
319
|
+
update() {
|
|
320
|
+
if (!this.uiCtx)
|
|
321
|
+
return;
|
|
322
|
+
const allAgents = this.manager.listAgents();
|
|
323
|
+
// Lightweight existence checks — full categorization happens in renderWidget()
|
|
324
|
+
let runningCount = 0;
|
|
325
|
+
let queuedCount = 0;
|
|
326
|
+
let hasFinished = false;
|
|
327
|
+
for (const a of allAgents) {
|
|
328
|
+
if (a.status === "running") {
|
|
329
|
+
runningCount++;
|
|
330
|
+
}
|
|
331
|
+
else if (a.status === "queued") {
|
|
332
|
+
queuedCount++;
|
|
333
|
+
}
|
|
334
|
+
else if (a.completedAt && this.shouldShowFinished(a.id, a.status)) {
|
|
335
|
+
hasFinished = true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const hasActive = runningCount > 0 || queuedCount > 0;
|
|
182
339
|
// Nothing to show — clear widget
|
|
183
340
|
if (!hasActive && !hasFinished) {
|
|
184
|
-
this.
|
|
185
|
-
|
|
341
|
+
if (this.widgetRegistered) {
|
|
342
|
+
this.uiCtx.setWidget("agents", undefined);
|
|
343
|
+
this.widgetRegistered = false;
|
|
344
|
+
this.tui = undefined;
|
|
345
|
+
}
|
|
346
|
+
if (this.lastStatusText !== undefined) {
|
|
347
|
+
this.uiCtx.setStatus("subagents", undefined);
|
|
348
|
+
this.lastStatusText = undefined;
|
|
349
|
+
}
|
|
186
350
|
if (this.widgetInterval) {
|
|
187
351
|
clearInterval(this.widgetInterval);
|
|
188
352
|
this.widgetInterval = undefined;
|
|
@@ -194,131 +358,42 @@ export class AgentWidget {
|
|
|
194
358
|
}
|
|
195
359
|
return;
|
|
196
360
|
}
|
|
197
|
-
// Status bar
|
|
361
|
+
// Status bar — only call setStatus when the text actually changes
|
|
362
|
+
let newStatusText;
|
|
198
363
|
if (hasActive) {
|
|
199
364
|
const statusParts = [];
|
|
200
|
-
if (
|
|
201
|
-
statusParts.push(`${
|
|
202
|
-
if (
|
|
203
|
-
statusParts.push(`${
|
|
204
|
-
const total =
|
|
205
|
-
|
|
365
|
+
if (runningCount > 0)
|
|
366
|
+
statusParts.push(`${runningCount} running`);
|
|
367
|
+
if (queuedCount > 0)
|
|
368
|
+
statusParts.push(`${queuedCount} queued`);
|
|
369
|
+
const total = runningCount + queuedCount;
|
|
370
|
+
newStatusText = `${statusParts.join(", ")} agent${total === 1 ? "" : "s"}`;
|
|
206
371
|
}
|
|
207
|
-
|
|
208
|
-
this.uiCtx.setStatus("subagents",
|
|
372
|
+
if (newStatusText !== this.lastStatusText) {
|
|
373
|
+
this.uiCtx.setStatus("subagents", newStatusText);
|
|
374
|
+
this.lastStatusText = newStatusText;
|
|
209
375
|
}
|
|
210
376
|
this.widgetFrame++;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
let tokenText = "";
|
|
232
|
-
if (bg?.session) {
|
|
233
|
-
try {
|
|
234
|
-
tokenText = formatTokens(bg.session.getSessionStats().tokens.total);
|
|
235
|
-
}
|
|
236
|
-
catch { /* */ }
|
|
237
|
-
}
|
|
238
|
-
const parts = [];
|
|
239
|
-
if (toolUses > 0)
|
|
240
|
-
parts.push(`${toolUses} tool use${toolUses === 1 ? "" : "s"}`);
|
|
241
|
-
if (tokenText)
|
|
242
|
-
parts.push(tokenText);
|
|
243
|
-
parts.push(elapsed);
|
|
244
|
-
const statsText = parts.join(" · ");
|
|
245
|
-
const activity = bg ? describeActivity(bg.activeTools, bg.responseText) : "thinking…";
|
|
246
|
-
runningLines.push([
|
|
247
|
-
truncate(theme.fg("dim", "├─") + ` ${theme.fg("accent", frame)} ${theme.bold(name)}${modeTag} ${theme.fg("muted", a.description)} ${theme.fg("dim", "·")} ${theme.fg("dim", statsText)}`),
|
|
248
|
-
truncate(theme.fg("dim", "│ ") + theme.fg("dim", ` ⎿ ${activity}`)),
|
|
249
|
-
]);
|
|
250
|
-
}
|
|
251
|
-
const queuedLine = queued.length > 0
|
|
252
|
-
? truncate(theme.fg("dim", "├─") + ` ${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`)
|
|
253
|
-
: undefined;
|
|
254
|
-
// Assemble with overflow cap (heading + overflow indicator = 2 reserved lines).
|
|
255
|
-
const maxBody = MAX_WIDGET_LINES - 1; // heading takes 1 line
|
|
256
|
-
const totalBody = finishedLines.length + runningLines.length * 2 + (queuedLine ? 1 : 0);
|
|
257
|
-
const lines = [truncate(theme.fg(headingColor, headingIcon) + " " + theme.fg(headingColor, "Agents"))];
|
|
258
|
-
if (totalBody <= maxBody) {
|
|
259
|
-
// Everything fits — add all lines and fix up connectors for the last item.
|
|
260
|
-
lines.push(...finishedLines);
|
|
261
|
-
for (const pair of runningLines)
|
|
262
|
-
lines.push(...pair);
|
|
263
|
-
if (queuedLine)
|
|
264
|
-
lines.push(queuedLine);
|
|
265
|
-
// Fix last connector: swap ├─ → └─ and │ → space for activity lines.
|
|
266
|
-
if (lines.length > 1) {
|
|
267
|
-
const last = lines.length - 1;
|
|
268
|
-
lines[last] = lines[last].replace("├─", "└─");
|
|
269
|
-
// If last item is a running agent activity line, fix indent of that line
|
|
270
|
-
// and fix the header line above it.
|
|
271
|
-
if (runningLines.length > 0 && !queuedLine) {
|
|
272
|
-
// The last two lines are the last running agent's header + activity.
|
|
273
|
-
if (last >= 2) {
|
|
274
|
-
lines[last - 1] = lines[last - 1].replace("├─", "└─");
|
|
275
|
-
lines[last] = lines[last].replace("│ ", " ");
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
// Overflow — prioritize: running > queued > finished.
|
|
282
|
-
// Reserve 1 line for overflow indicator.
|
|
283
|
-
let budget = maxBody - 1;
|
|
284
|
-
let hiddenRunning = 0;
|
|
285
|
-
let hiddenFinished = 0;
|
|
286
|
-
// 1. Running agents (2 lines each)
|
|
287
|
-
for (const pair of runningLines) {
|
|
288
|
-
if (budget >= 2) {
|
|
289
|
-
lines.push(...pair);
|
|
290
|
-
budget -= 2;
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
hiddenRunning++;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
// 2. Queued line
|
|
297
|
-
if (queuedLine && budget >= 1) {
|
|
298
|
-
lines.push(queuedLine);
|
|
299
|
-
budget--;
|
|
300
|
-
}
|
|
301
|
-
// 3. Finished agents
|
|
302
|
-
for (const fl of finishedLines) {
|
|
303
|
-
if (budget >= 1) {
|
|
304
|
-
lines.push(fl);
|
|
305
|
-
budget--;
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
hiddenFinished++;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
// Overflow summary
|
|
312
|
-
const overflowParts = [];
|
|
313
|
-
if (hiddenRunning > 0)
|
|
314
|
-
overflowParts.push(`${hiddenRunning} running`);
|
|
315
|
-
if (hiddenFinished > 0)
|
|
316
|
-
overflowParts.push(`${hiddenFinished} finished`);
|
|
317
|
-
const overflowText = overflowParts.join(", ");
|
|
318
|
-
lines.push(truncate(theme.fg("dim", "└─") + ` ${theme.fg("dim", `+${hiddenRunning + hiddenFinished} more (${overflowText})`)}`));
|
|
319
|
-
}
|
|
320
|
-
return { render: () => lines, invalidate: () => { } };
|
|
321
|
-
}, { placement: "aboveEditor" });
|
|
377
|
+
// Register widget callback once; subsequent updates use requestRender()
|
|
378
|
+
// which re-invokes render() without replacing the component (avoids layout thrashing).
|
|
379
|
+
if (!this.widgetRegistered) {
|
|
380
|
+
this.uiCtx.setWidget("agents", (tui, theme) => {
|
|
381
|
+
this.tui = tui;
|
|
382
|
+
return {
|
|
383
|
+
render: () => this.renderWidget(tui, theme),
|
|
384
|
+
invalidate: () => {
|
|
385
|
+
// Theme changed — force re-registration so factory captures fresh theme.
|
|
386
|
+
this.widgetRegistered = false;
|
|
387
|
+
this.tui = undefined;
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}, { placement: "aboveEditor" });
|
|
391
|
+
this.widgetRegistered = true;
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// Widget already registered — just request a re-render of existing components.
|
|
395
|
+
this.tui?.requestRender();
|
|
396
|
+
}
|
|
322
397
|
}
|
|
323
398
|
dispose() {
|
|
324
399
|
if (this.widgetInterval) {
|
|
@@ -329,5 +404,8 @@ export class AgentWidget {
|
|
|
329
404
|
this.uiCtx.setWidget("agents", undefined);
|
|
330
405
|
this.uiCtx.setStatus("subagents", undefined);
|
|
331
406
|
}
|
|
407
|
+
this.widgetRegistered = false;
|
|
408
|
+
this.tui = undefined;
|
|
409
|
+
this.lastStatusText = undefined;
|
|
332
410
|
}
|
|
333
411
|
}
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* Displays a scrollable, live-updating view of an agent's conversation.
|
|
5
5
|
* Subscribes to session events for real-time streaming updates.
|
|
6
6
|
*/
|
|
7
|
-
import { type Component, type TUI } from "@mariozechner/pi-tui";
|
|
8
7
|
import type { AgentSession } from "@mariozechner/pi-coding-agent";
|
|
8
|
+
import { type Component, type TUI } from "@mariozechner/pi-tui";
|
|
9
|
+
import type { AgentRecord } from "../types.js";
|
|
9
10
|
import type { Theme } from "./agent-widget.js";
|
|
10
11
|
import { type AgentActivity } from "./agent-widget.js";
|
|
11
|
-
import type { AgentRecord } from "../types.js";
|
|
12
12
|
export declare class ConversationViewer implements Component {
|
|
13
13
|
private tui;
|
|
14
14
|
private session;
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Subscribes to session events for real-time streaming updates.
|
|
6
6
|
*/
|
|
7
7
|
import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
|
|
8
|
-
import { formatTokens, formatDuration, getDisplayName, getPromptModeLabel, describeActivity } from "./agent-widget.js";
|
|
9
8
|
import { extractText } from "../context.js";
|
|
9
|
+
import { describeActivity, formatDuration, formatTokens, getDisplayName, getPromptModeLabel } from "./agent-widget.js";
|
|
10
10
|
/** Lines consumed by chrome: top border + header + header sep + footer sep + footer + bottom border. */
|
|
11
11
|
const CHROME_LINES = 6;
|
|
12
12
|
const MIN_VIEWPORT = 3;
|
|
@@ -231,6 +231,6 @@ export class ConversationViewer {
|
|
|
231
231
|
lines.push("");
|
|
232
232
|
lines.push(truncateToWidth(th.fg("accent", "▍ ") + th.fg("dim", act), width));
|
|
233
233
|
}
|
|
234
|
-
return lines;
|
|
234
|
+
return lines.map(l => truncateToWidth(l, width));
|
|
235
235
|
}
|
|
236
236
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|