@narumitw/pi-subagents 3.4.2 → 3.4.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-subagents",
3
- "version": "3.4.2",
3
+ "version": "3.4.3",
4
4
  "description": "Subagent jobs with asynchronous main-agent messaging for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,9 +43,9 @@
43
43
  "@earendil-works/pi-ai": "1.5.0",
44
44
  "@earendil-works/pi-coding-agent": "1.5.0",
45
45
  "@earendil-works/pi-tui": "1.5.0",
46
- "@types/node": "26.6.5",
46
+ "@types/node": "26.6.6",
47
47
  "esbuild": "0.28.2",
48
- "typebox": "1.4.3",
48
+ "typebox": "1.4.4",
49
49
  "typescript": "7.0.2"
50
50
  },
51
51
  "repository": {
@@ -54,7 +54,7 @@
54
54
  "directory": "packages/pi-subagents"
55
55
  },
56
56
  "dependencies": {
57
- "@narumitw/pi-tui-kit": "^0.69.0",
57
+ "@narumitw/pi-tui-kit": "^0.70.0",
58
58
  "ignore": "^7.0.14"
59
59
  }
60
60
  }
@@ -25,15 +25,46 @@ export function subagentWidgetRowBudget(rows: number): number {
25
25
  : 0;
26
26
  }
27
27
 
28
+ export function subagentWidgetSummary(snapshot: SubagentWidgetSnapshot, maxRows: number): string {
29
+ const { jobs, items, mode } = snapshot;
30
+ const budget = normalizeRowBudget(maxRows);
31
+ if (!isVisible(snapshot) || budget === 0) return "";
32
+ // Compact mode intentionally has no job rows; only removed jobs are omitted.
33
+ const shown = mode === "compact" ? items.length : Math.min(items.length, budget - 1);
34
+ const omitted = jobs.length - shown;
35
+ const removed = jobs.length - items.length;
36
+ const running = jobs.filter((job) => job.state === "running").length;
37
+ const counts = `${running} running · ${jobs.length - running} queued`;
38
+ return `${counts}${omitted > 0 ? ` · ${omitted} not shown${removed > 0 ? ` (${removed} removed)` : ""}` : ""}`;
39
+ }
40
+
41
+ function normalizeRowBudget(maxRows: number): number {
42
+ return Number.isFinite(maxRows) ? Math.max(0, Math.floor(maxRows)) : 0;
43
+ }
44
+
45
+ function isVisible({ jobs, items, settings, mode }: SubagentWidgetSnapshot): boolean {
46
+ return settings.showWidget && mode !== "hidden" && jobs.length > 0 && items.length > 0;
47
+ }
48
+
28
49
  export function renderSubagentWidgetSnapshot(
29
50
  snapshot: SubagentWidgetSnapshot,
30
51
  theme: Theme,
31
52
  width: number,
32
53
  maxRows: number,
54
+ includeHeader = true,
33
55
  ): string[] {
34
56
  const { jobs, items, settings, mode } = snapshot;
35
- const budget = Number.isFinite(maxRows) ? Math.max(0, Math.floor(maxRows)) : 0;
36
- if (!settings.showWidget || mode === "hidden" || jobs.length === 0 || items.length === 0 || budget < 2) return [];
57
+ const budget = normalizeRowBudget(maxRows);
58
+ if (!isVisible(snapshot) || budget < (includeHeader ? 2 : 1)) return [];
59
+ // Named TUI publishers put the counts in the divider, not in a body heading.
60
+ if (!includeHeader) {
61
+ const shown = mode === "compact" ? [] : items.slice(0, budget - 1);
62
+ return new EditorStatusWidget({
63
+ theme,
64
+ renderBody: () =>
65
+ shown.map(({ value, mode: itemMode }) => renderJob(value, theme, settings, itemMode === "compact")),
66
+ }).render(width);
67
+ }
37
68
  const running = jobs.filter((job) => job.state === "running").length;
38
69
  const counts = `Subagents · ${jobs.length} active · ${running} running · ${jobs.length - running} queued`;
39
70
  const removed = jobs.length - items.length;
package/src/widget.ts CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  renderSubagentWidgetSnapshot,
18
18
  type SubagentWidgetSnapshot,
19
19
  subagentWidgetRowBudget,
20
+ subagentWidgetSummary,
20
21
  } from "./widget-renderer.js";
21
22
 
22
23
  export { renderSubagentWidget } from "./widget-renderer.js";
@@ -175,10 +176,12 @@ export function createSubagentWidgetController(runtime: SubagentRuntime): Subage
175
176
  view = createEditorStatusView({ onChange: publish, canCompact: () => settings.showWidget && jobs.length > 0 });
176
177
  publisher = createEditorStatusPublisher({
177
178
  key: SUBAGENT_WIDGET_KEY,
179
+ name: "Subagents",
178
180
  context: ctx,
179
181
  isCurrent,
182
+ summary: (snapshot, _width, rows) => subagentWidgetSummary(snapshot, subagentWidgetRowBudget(rows)),
180
183
  render: (snapshot, theme, width, rows) =>
181
- renderSubagentWidgetSnapshot(snapshot, theme, width, subagentWidgetRowBudget(rows)),
184
+ renderSubagentWidgetSnapshot(snapshot, theme, width, subagentWidgetRowBudget(rows), false),
182
185
  });
183
186
  // The owner records public preparation/run hooks while settings load.
184
187
  // General busy state also includes navigation and manual compaction.
@@ -189,7 +192,9 @@ export function createSubagentWidgetController(runtime: SubagentRuntime): Subage
189
192
  controls = {
190
193
  state: () => ({ mode: ownedView.mode, pinned: ownedView.pinned, trigger: ownedView.trigger }),
191
194
  setMode: (mode) => {
192
- if (isCurrent()) ownedView.setMode(mode);
195
+ if (!isCurrent()) return;
196
+ if (mode !== "hidden") publisher?.setCollapsed(false);
197
+ ownedView.setMode(mode);
193
198
  },
194
199
  setPinned: (pinned) => {
195
200
  if (isCurrent()) ownedView.setPinned(pinned);