@esso0428/pi-subagents 0.16.0 → 0.17.1
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/CHANGELOG.md +10 -0
- package/README.md +10 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -73
- package/dist/index.js.map +1 -1
- package/dist/settings.d.ts +1 -8
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +0 -5
- package/dist/settings.js.map +1 -1
- package/dist/ui/agent-widget.d.ts +55 -24
- package/dist/ui/agent-widget.d.ts.map +1 -1
- package/dist/ui/agent-widget.js +246 -121
- package/dist/ui/agent-widget.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +23 -71
- package/src/settings.ts +1 -12
- package/src/ui/agent-widget.ts +264 -114
- package/dist/ui/deferred-ui-refresh.d.ts +0 -15
- package/dist/ui/deferred-ui-refresh.d.ts.map +0 -1
- package/dist/ui/deferred-ui-refresh.js +0 -50
- package/dist/ui/deferred-ui-refresh.js.map +0 -1
- package/dist/ui/fleet-list.d.ts +0 -163
- package/dist/ui/fleet-list.d.ts.map +0 -1
- package/dist/ui/fleet-list.js +0 -509
- package/dist/ui/fleet-list.js.map +0 -1
- package/src/ui/deferred-ui-refresh.ts +0 -72
- package/src/ui/fleet-list.ts +0 -584
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.17.1] - 2026-09-15
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **The Agents panel now initializes during TUI session startup after restoring persisted agent records**, so openable completed-agent history is visible immediately when a session starts or resumes instead of waiting for the next tool execution.
|
|
14
|
+
|
|
15
|
+
## [0.17.0] - 2026-09-15
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **The above-editor Agents panel is now the single agent navigator.** It replaces the duplicate FleetView roster, uses focus-gated `↑`/`↓` navigation with `Enter` to open live or read-only history viewers, and removes FleetView's duplicate timer and deferred redraw path.
|
|
19
|
+
|
|
10
20
|
## [0.16.0] - 2026-09-15
|
|
11
21
|
|
|
12
22
|
> **⚠️ Breaking: pi 0.80.8 is now the minimum supported host version.** The package peer dependencies require `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` `>=0.80.8`; upgrade pi before installing this release. This floor matches the `ModelRuntime` API used by the runner and avoids installing a release against the pre-0.80.8 session-creation contract.
|
package/README.md
CHANGED
|
@@ -14,8 +14,7 @@ https://github.com/user-attachments/assets/8685261b-9338-4fea-8dfe-1c590d5df543
|
|
|
14
14
|
|
|
15
15
|
- **Claude Code look & feel** — same tool names, calling conventions, and UI patterns (`Agent`, `get_subagent_result`, `steer_subagent`) — feels native
|
|
16
16
|
- **Parallel background agents** — spawn multiple agents that run concurrently with automatic queuing (configurable concurrency limit, default 4) and smart group join (consolidated notifications)
|
|
17
|
-
- **
|
|
18
|
-
- **FleetView** — Claude Code-style navigable list of `main` + every running subagent rendered below the editor (earliest-launched first). Press `↓` (or `←`) at an empty prompt to jump in, `↑`/`↓`/wheel to move the selection, `Enter` to open the selected agent's live, auto-updating conversation, `Esc` to return. The selected row stays visible while the bounded list window moves through the full roster. Finished agents linger briefly before dropping out, and a viewer stays open through completion so you can read the final output. Toggle via `/agents → Settings → Fleet view`
|
|
17
|
+
- **Agents panel UI** — persistent above-editor widget with animated spinners, live tool activity, token counts, colored status icons, and one focus-gated navigator. It shows every agent by default; press `↓` at an empty prompt to activate the panel, `↑`/`↓` to select, `Enter` to open a live or read-only history viewer, and `Esc` to return. Configure via `/agents → Settings → Widget`: `all`, `background`, or `off`
|
|
19
18
|
- **Conversation viewer** — select any agent in `/agents` to open a live-scrolling overlay of its full conversation (auto-follows new content, scroll up to pause). Steer a running agent inline by pressing `Enter` to open a composer, typing, then `Enter` to send (`Esc` or an empty submit returns) — the message appears as a user message and redirects the agent after its current tool. Stop a still-running agent by pressing `x` (then `x` again to confirm) — both work for background agents too
|
|
20
19
|
- **Custom agent types** — define agents in `.pi/agents/<name>.md` or `.agents/agents/<name>.md` (project) or globally, with YAML frontmatter: custom system prompts, model selection, thinking levels, tool restrictions
|
|
21
20
|
- **Mid-run steering** — inject messages into running agents to redirect their work without restarting
|
|
@@ -95,7 +94,7 @@ Restrictions:
|
|
|
95
94
|
|
|
96
95
|
## UI
|
|
97
96
|
|
|
98
|
-
The extension renders a persistent widget above the editor
|
|
97
|
+
The extension renders a persistent Agents widget above the editor. It initializes during TUI `session_start`, after the session branch restores completed-agent history, so openable agents are visible immediately when a session starts or resumes. By default it shows all agents (`widgetMode: all`), including foreground and background runs. Switch to `background` or `off` via `/agents → Settings → Widget`:
|
|
99
98
|
|
|
100
99
|
```
|
|
101
100
|
● Agents
|
|
@@ -112,20 +111,19 @@ The token field is annotated with two optional signals inside parens:
|
|
|
112
111
|
- **`NN%`** — context-window utilization (color-coded: <70% dim, 70–85% warning, ≥85% error). Omitted when the model has no declared `contextWindow`, or briefly right after compaction.
|
|
113
112
|
- **`⇊N`** — number of times the session has compacted, when > 0. Stays dim; the percent's color carries urgency.
|
|
114
113
|
|
|
115
|
-
###
|
|
114
|
+
### Agents panel navigator
|
|
116
115
|
|
|
117
|
-
While
|
|
116
|
+
While agents are running or have openable history, the single Agents panel renders **above** the editor:
|
|
118
117
|
|
|
119
118
|
```
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
↓ 3 more
|
|
119
|
+
● Agents ↑↓ select · enter view · esc back
|
|
120
|
+
├─ ● Agent Refactor auth module · ↻5≤30 · 5 tool uses · 12.3s
|
|
121
|
+
│ ⎿ editing 2 files…
|
|
122
|
+
├─ ○ Explore Find auth files · ↻3 · 4.1s
|
|
123
|
+
└─ +1 more (↓ 1 more; 1 finished)
|
|
126
124
|
```
|
|
127
125
|
|
|
128
|
-
|
|
126
|
+
At an **empty prompt**, press `↓` to activate the panel. The selected row is marked `●`; `↑`/`↓` move through the full bounded roster, `Enter` opens a running agent live or a terminal agent's read-only history. A queued agent remains selectable, but has no live viewer until it starts; `Esc` returns to the prompt. `↑` while the first row is selected also returns to the prompt. Pressing `↑` at an inactive prompt, typing in a non-empty prompt, or using `j`/`k`/`←` behaves normally; the panel only captures the focus-gated arrow sequence described above. Disable it entirely via `/agents → Settings → Widget → off`.
|
|
129
127
|
|
|
130
128
|
Individual agent results render Claude Code-style in the conversation:
|
|
131
129
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAc,KAAK,YAAY,EAA0F,MAAM,iCAAiC,CAAC;AACxK,OAAO,EAAE,SAAS,EAAiE,MAAM,wBAAwB,CAAC;AAmBlH,OAAO,EAcL,KAAK,KAAK,EAEX,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAc,KAAK,YAAY,EAA0F,MAAM,iCAAiC,CAAC;AACxK,OAAO,EAAE,SAAS,EAAiE,MAAM,wBAAwB,CAAC;AAmBlH,OAAO,EAcL,KAAK,KAAK,EAEX,MAAM,sBAAsB,CAAC;AA4C9B,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GACvB,SAAS,CAKX;AAkKD,MAAM,CAAC,OAAO,WAAW,EAAE,EAAE,YAAY,QAuqExC"}
|
package/dist/index.js
CHANGED
|
@@ -31,8 +31,6 @@ import { resolveStorePath, ScheduleStore } from "./schedule-store.js";
|
|
|
31
31
|
import { applyAndEmitLoaded, saveAndEmitChanged } from "./settings.js";
|
|
32
32
|
import { getStatusNote } from "./status-note.js";
|
|
33
33
|
import { AgentWidget, buildInvocationTags, describeActivity, fgPreservingNestedStyles, formatDuration, formatMs, formatTokens, formatTurns, getDisplayName, getPromptModeLabel, SPINNER, } from "./ui/agent-widget.js";
|
|
34
|
-
import { createDeferredUiRefresh } from "./ui/deferred-ui-refresh.js";
|
|
35
|
-
import { FleetList } from "./ui/fleet-list.js";
|
|
36
34
|
import { showSchedulesMenu } from "./ui/schedule-menu.js";
|
|
37
35
|
import { addUsage, getLifetimeTotal, getSessionContextPercent } from "./usage.js";
|
|
38
36
|
// ---- Shared helpers ----
|
|
@@ -320,17 +318,14 @@ export default function (pi) {
|
|
|
320
318
|
function sendIndividualNudge(record) {
|
|
321
319
|
agentActivity.delete(record.id);
|
|
322
320
|
widget.markFinished(record.id);
|
|
323
|
-
fleet.onAgentFinished(record.id);
|
|
324
321
|
scheduleNudge(record.id, () => emitIndividualNudge(record));
|
|
325
322
|
widget.update();
|
|
326
|
-
scheduleUiRefresh();
|
|
327
323
|
}
|
|
328
324
|
// ---- Group join manager ----
|
|
329
325
|
const groupJoin = new GroupJoinManager((records, partial) => {
|
|
330
326
|
for (const r of records) {
|
|
331
327
|
agentActivity.delete(r.id);
|
|
332
328
|
widget.markFinished(r.id);
|
|
333
|
-
fleet.onAgentFinished(r.id);
|
|
334
329
|
}
|
|
335
330
|
const groupKey = `group:${records.map(r => r.id).join(",")}`;
|
|
336
331
|
scheduleNudge(groupKey, () => {
|
|
@@ -338,7 +333,6 @@ export default function (pi) {
|
|
|
338
333
|
const unconsumed = records.filter(r => !r.resultConsumed);
|
|
339
334
|
if (unconsumed.length === 0) {
|
|
340
335
|
widget.update();
|
|
341
|
-
scheduleUiRefresh();
|
|
342
336
|
return;
|
|
343
337
|
}
|
|
344
338
|
const notifications = unconsumed.map(r => formatTaskNotification(r, 300)).join('\n\n');
|
|
@@ -358,7 +352,6 @@ export default function (pi) {
|
|
|
358
352
|
}, { deliverAs: "followUp", triggerTurn: true });
|
|
359
353
|
});
|
|
360
354
|
widget.update();
|
|
361
|
-
scheduleUiRefresh();
|
|
362
355
|
}, 30_000);
|
|
363
356
|
/** Helper: build event data for lifecycle events from an AgentRecord. */
|
|
364
357
|
function buildEventData(record) {
|
|
@@ -409,16 +402,13 @@ export default function (pi) {
|
|
|
409
402
|
if (record.resultConsumed) {
|
|
410
403
|
agentActivity.delete(record.id);
|
|
411
404
|
widget.markFinished(record.id);
|
|
412
|
-
fleet.onAgentFinished(record.id);
|
|
413
405
|
widget.update();
|
|
414
|
-
scheduleUiRefresh();
|
|
415
406
|
return;
|
|
416
407
|
}
|
|
417
408
|
// If this agent is pending batch finalization (debounce window still open),
|
|
418
409
|
// don't send an individual nudge — finalizeBatch will pick it up retroactively.
|
|
419
410
|
if (currentBatchAgents.some(a => a.id === record.id)) {
|
|
420
411
|
widget.update();
|
|
421
|
-
scheduleUiRefresh();
|
|
422
412
|
return;
|
|
423
413
|
}
|
|
424
414
|
const result = groupJoin.onAgentComplete(record);
|
|
@@ -428,7 +418,6 @@ export default function (pi) {
|
|
|
428
418
|
// 'held' → do nothing, group will fire later
|
|
429
419
|
// 'delivered' → group callback already fired
|
|
430
420
|
widget.update();
|
|
431
|
-
scheduleUiRefresh();
|
|
432
421
|
}, undefined, (record) => {
|
|
433
422
|
// Emit started event when agent transitions to running (including from queue)
|
|
434
423
|
pi.events.emit("subagents:started", {
|
|
@@ -438,9 +427,6 @@ export default function (pi) {
|
|
|
438
427
|
});
|
|
439
428
|
widget.ensureTimer();
|
|
440
429
|
widget.update();
|
|
441
|
-
fleet.ensureTimer();
|
|
442
|
-
fleet.update();
|
|
443
|
-
scheduleUiRefresh();
|
|
444
430
|
}, (record, info) => {
|
|
445
431
|
// Emit compacted event when agent's session compacts (preserves count on record).
|
|
446
432
|
pi.events.emit("subagents:compacted", {
|
|
@@ -513,6 +499,12 @@ export default function (pi) {
|
|
|
513
499
|
manager.restoreCompleted(branch
|
|
514
500
|
.filter((entry) => entry?.type === "custom" && entry?.customType === "subagents:record")
|
|
515
501
|
.map((entry) => entry.data));
|
|
502
|
+
// Attach the panel during TUI startup, after restored records are present,
|
|
503
|
+
// so terminal agents from the session branch are immediately visible.
|
|
504
|
+
if (ctx.mode === "tui") {
|
|
505
|
+
widget.setUICtx(ctx.ui);
|
|
506
|
+
widget.update();
|
|
507
|
+
}
|
|
516
508
|
// Guard mirrors the `!scheduler.isActive()` pattern below: session_start
|
|
517
509
|
// fires once per activation, but a double-bind must not leak listeners.
|
|
518
510
|
if (!rpcHandle) {
|
|
@@ -531,7 +523,6 @@ export default function (pi) {
|
|
|
531
523
|
startScheduler(ctx);
|
|
532
524
|
});
|
|
533
525
|
pi.on("session_before_switch", () => {
|
|
534
|
-
deferredUiRefresh.cancel();
|
|
535
526
|
manager.clearCompleted(true);
|
|
536
527
|
scheduler.stop();
|
|
537
528
|
});
|
|
@@ -553,45 +544,23 @@ export default function (pi) {
|
|
|
553
544
|
for (const timer of pendingNudges.values())
|
|
554
545
|
clearTimeout(timer);
|
|
555
546
|
pendingNudges.clear();
|
|
556
|
-
|
|
557
|
-
fleet.dispose();
|
|
547
|
+
widget.dispose();
|
|
558
548
|
manager.dispose();
|
|
559
549
|
});
|
|
560
|
-
// Live widget: show
|
|
561
|
-
|
|
562
|
-
// every agent; "background" = hide foreground (they already render inline as
|
|
563
|
-
// the Agent tool result, so showing them here too is a duplicate, #118), keep
|
|
564
|
-
// everything else; "off" = hide the widget entirely. Read live at render time.
|
|
565
|
-
let widgetMode = "background";
|
|
550
|
+
// Live widget: show all agents above the editor. Read live at render time.
|
|
551
|
+
let widgetMode = "all";
|
|
566
552
|
function getWidgetMode() { return widgetMode; }
|
|
567
|
-
const widget = new AgentWidget(manager, agentActivity, getWidgetMode
|
|
553
|
+
const widget = new AgentWidget(manager, agentActivity, getWidgetMode, {
|
|
554
|
+
canOpenHistory: (record) => canOpenAgentHistory(record, currentCtx?.cwd),
|
|
555
|
+
onOpen: (record, mode) => {
|
|
556
|
+
const ctx = currentCtx;
|
|
557
|
+
if (ctx)
|
|
558
|
+
void viewAgentConversation(ctx, record, mode);
|
|
559
|
+
},
|
|
560
|
+
});
|
|
568
561
|
function setWidgetMode(m) {
|
|
569
|
-
const changed = widgetMode !== m;
|
|
570
562
|
widgetMode = m;
|
|
571
563
|
widget.update();
|
|
572
|
-
if (changed)
|
|
573
|
-
scheduleUiRefresh();
|
|
574
|
-
}
|
|
575
|
-
// Claude Code-style FleetView: navigable list of main + subagents below the editor.
|
|
576
|
-
const fleet = new FleetList(manager, agentActivity, () => currentCtx?.cwd, pi, () => currentCtx);
|
|
577
|
-
// One render-only scheduler is shared by both widgets. The callback resolves
|
|
578
|
-
// the live TUI target at fire time because session switches invalidate the
|
|
579
|
-
// previous widget context; both widgets use the same TUI, so one successful
|
|
580
|
-
// request is sufficient.
|
|
581
|
-
const deferredUiRefresh = createDeferredUiRefresh(() => {
|
|
582
|
-
if (widget.requestUiRefresh(true))
|
|
583
|
-
return;
|
|
584
|
-
fleet.requestUiRefresh(true);
|
|
585
|
-
});
|
|
586
|
-
const scheduleUiRefresh = () => deferredUiRefresh.schedule();
|
|
587
|
-
let fleetViewEnabled = true;
|
|
588
|
-
function isFleetViewEnabled() { return fleetViewEnabled; }
|
|
589
|
-
function setFleetViewEnabled(b) {
|
|
590
|
-
const changed = fleetViewEnabled !== b;
|
|
591
|
-
fleetViewEnabled = b;
|
|
592
|
-
fleet.setEnabled(b);
|
|
593
|
-
if (changed)
|
|
594
|
-
scheduleUiRefresh();
|
|
595
564
|
}
|
|
596
565
|
// Project/global default for writing the subagent .output transcript. A custom
|
|
597
566
|
// agent's `output_transcript` frontmatter overrides this per spawn; when the
|
|
@@ -684,10 +653,7 @@ export default function (pi) {
|
|
|
684
653
|
}
|
|
685
654
|
// Grab UI context from first tool execution + clear lingering widget on new turn
|
|
686
655
|
pi.on("tool_execution_start", async (_event, ctx) => {
|
|
687
|
-
|
|
688
|
-
const fleetContextChanged = fleet.setUICtx(ctx.ui);
|
|
689
|
-
if (widgetContextChanged || fleetContextChanged)
|
|
690
|
-
scheduleUiRefresh();
|
|
656
|
+
widget.setUICtx(ctx.ui);
|
|
691
657
|
widget.onTurnStart();
|
|
692
658
|
});
|
|
693
659
|
/** Format an agent's tool scope: "*" when it has all built-ins, else a comma-separated list. */
|
|
@@ -738,7 +704,6 @@ export default function (pi) {
|
|
|
738
704
|
setScopeModels: setScopeModelsEnabled,
|
|
739
705
|
setDisableDefaultAgents: setDisableDefaultAgents,
|
|
740
706
|
setToolDescriptionMode: setToolDescriptionMode,
|
|
741
|
-
setFleetView: setFleetViewEnabled,
|
|
742
707
|
setWidgetMode: setWidgetMode,
|
|
743
708
|
setOutputTranscript: setOutputTranscript,
|
|
744
709
|
}, (event, payload) => pi.events.emit(event, payload));
|
|
@@ -1217,9 +1182,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1217
1182
|
agentActivity.set(id, bgState);
|
|
1218
1183
|
widget.ensureTimer();
|
|
1219
1184
|
widget.update();
|
|
1220
|
-
fleet.ensureTimer();
|
|
1221
|
-
fleet.update();
|
|
1222
|
-
scheduleUiRefresh();
|
|
1223
1185
|
// Emit created event
|
|
1224
1186
|
pi.events.emit("subagents:created", {
|
|
1225
1187
|
id,
|
|
@@ -1272,9 +1234,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1272
1234
|
agentActivity.set(a.id, fgState);
|
|
1273
1235
|
widget.ensureTimer();
|
|
1274
1236
|
widget.update();
|
|
1275
|
-
fleet.ensureTimer();
|
|
1276
|
-
fleet.update();
|
|
1277
|
-
scheduleUiRefresh();
|
|
1278
1237
|
break;
|
|
1279
1238
|
}
|
|
1280
1239
|
}
|
|
@@ -1322,7 +1281,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1322
1281
|
if (fgId) {
|
|
1323
1282
|
agentActivity.delete(fgId);
|
|
1324
1283
|
widget.markFinished(fgId);
|
|
1325
|
-
fleet.onAgentFinished(fgId);
|
|
1326
1284
|
}
|
|
1327
1285
|
// Get final token count
|
|
1328
1286
|
const tokenText = formatLifetimeTokens(fgState);
|
|
@@ -2091,7 +2049,6 @@ ${systemPrompt}
|
|
|
2091
2049
|
scopeModels: isScopeModelsEnabled(),
|
|
2092
2050
|
disableDefaultAgents: isDefaultsDisabled(),
|
|
2093
2051
|
toolDescriptionMode: getToolDescriptionMode(),
|
|
2094
|
-
fleetView: isFleetViewEnabled(),
|
|
2095
2052
|
widgetMode: getWidgetMode(),
|
|
2096
2053
|
outputTranscript: getOutputTranscriptDefault(),
|
|
2097
2054
|
};
|
|
@@ -2159,13 +2116,6 @@ ${systemPrompt}
|
|
|
2159
2116
|
currentValue: getOutputTranscriptDefault() ? "on" : "off",
|
|
2160
2117
|
values: ["on", "off"],
|
|
2161
2118
|
},
|
|
2162
|
-
{
|
|
2163
|
-
id: "fleetView",
|
|
2164
|
-
label: "Fleet view",
|
|
2165
|
-
description: "Claude Code-style main+subagents list below the editor (↓/← to navigate, Enter to view)",
|
|
2166
|
-
currentValue: isFleetViewEnabled() ? "on" : "off",
|
|
2167
|
-
values: ["on", "off"],
|
|
2168
|
-
},
|
|
2169
2119
|
{
|
|
2170
2120
|
id: "widgetMode",
|
|
2171
2121
|
label: "Widget",
|
|
@@ -2243,11 +2193,6 @@ ${systemPrompt}
|
|
|
2243
2193
|
setToolDescriptionMode(value);
|
|
2244
2194
|
notifyApplied(ctx, `Tool description set to ${value}. Takes effect on next pi session.`);
|
|
2245
2195
|
}
|
|
2246
|
-
else if (id === "fleetView") {
|
|
2247
|
-
const enabled = value === "on";
|
|
2248
|
-
setFleetViewEnabled(enabled);
|
|
2249
|
-
notifyApplied(ctx, `Fleet view ${enabled ? "enabled" : "disabled"}`);
|
|
2250
|
-
}
|
|
2251
2196
|
else if (id === "widgetMode") {
|
|
2252
2197
|
setWidgetMode(value);
|
|
2253
2198
|
notifyApplied(ctx, `Widget set to ${value}`);
|