@yusukeshib/pi-babysit 0.6.24 → 0.6.26

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.
Files changed (3) hide show
  1. package/README.md +10 -4
  2. package/index.ts +57 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -167,10 +167,12 @@ sessions, and click again to return to running-only. Both modes show newest firs
167
167
  ten per page; `< 1/4 >` shows the current/total page when needed. Click the
168
168
  arrows to page. Rows occupy one line, previewing the description (or the command
169
169
  for older processes and task for older agents), with `…` when clipped. Click an
170
- AGENT or PROCESS row to replace the list with a **log viewer in the same widget area**, not an
171
- overlay or separate screen. The viewer is capped at ten lines including its header
172
- and footer; all saved history remains available by scrolling. Click anywhere on
173
- the viewer's header row to restore the list, preserving its mode and page.
170
+ AGENT or PROCESS row to expand a **log viewer directly below that row**. The summary
171
+ title, list header, and all other rows remain visible with their original colors.
172
+ Only one row is expanded at a time; selecting another closes the previous one.
173
+ The viewer is capped at ten lines including its existing row header and footer;
174
+ all saved history remains available by scrolling. Click the same row again to
175
+ collapse it, preserving the list's mode and page.
174
176
  There is no separate Close button or Esc binding. The viewer uses Pi's `ScrollView` for
175
177
  scroll position and follow behavior, with a widget-local scrollbar styled using
176
178
  Pi's scrollbar theme. Wheel, scrollbar drag/track clicks, and ↑/↓ navigate the
@@ -233,6 +235,10 @@ targeted pattern over a broad tail, and do not read a potentially large log file
233
235
  in full. Subagent crashes return structured errors plus the full log path, never
234
236
  a raw RPC JSON tail.
235
237
 
238
+ Subagents run in RPC mode with normal Pi session persistence enabled, using
239
+ Pi's configured session storage (by default `~/.pi/agent/sessions/`). Each worker
240
+ has its own Pi session, separate from the parent and from babysit's output log.
241
+
236
242
  Subagent logs compact Pi's streaming `message_update` events before they are
237
243
  recorded. Pi repeats the complete growing assistant message and partial snapshot
238
244
  on every token; pi-babysit retains only the incremental delta. Compact RPC
package/index.ts CHANGED
@@ -2319,7 +2319,7 @@ async function spawnSubagent(
2319
2319
  // Long-lived RPC worker: the task is NOT passed as argv — it is injected
2320
2320
  // below as an RPC `prompt` command, whose response we validate so spawn
2321
2321
  // failures (bad model, missing API key) are loud instead of a silent exit=1.
2322
- const piArgs: string[] = ["--mode", "rpc", "--no-session"];
2322
+ const piArgs: string[] = ["--mode", "rpc"];
2323
2323
  const model = opts.model ?? opts.agent?.model;
2324
2324
  if (model) piArgs.push("--model", model);
2325
2325
  const tools = opts.tools ?? opts.agent?.tools;
@@ -2625,7 +2625,7 @@ interface WidgetMouseEvent {
2625
2625
  }
2626
2626
 
2627
2627
  /**
2628
- * Compact session list. Item presses open a viewer; rows never expand inline.
2628
+ * Compact session list. Item presses toggle the session's inline viewer.
2629
2629
  */
2630
2630
  export function createWidgetComponent(
2631
2631
  summaryLines: string[],
@@ -2683,6 +2683,9 @@ export function createWidgetComponent(
2683
2683
  }
2684
2684
  return lines;
2685
2685
  },
2686
+ getSessionLine(id: string) {
2687
+ return targetByLine.findIndex((target) => target?.type === "session" && target.id === id);
2688
+ },
2686
2689
  handleMouse(event: WidgetMouseEvent) {
2687
2690
  if (event.button !== "left") return undefined;
2688
2691
  const target = targetByLine[event.y];
@@ -2727,7 +2730,19 @@ export function createSessionWidget(options: {
2727
2730
  let displays: WidgetSessionDisplay[] = [];
2728
2731
  let viewer: ReturnType<typeof createLogViewer> | undefined;
2729
2732
  let source: FileLogSource | undefined;
2730
- let list = createWidgetComponent([], [], open, listState);
2733
+ let summaryLines: string[] = [];
2734
+ let listHeight = 0;
2735
+ let viewerRow = -1;
2736
+ let viewerLines = 0;
2737
+ let dragging = false;
2738
+ let list = createWidgetComponent([], [], toggle, listState);
2739
+ function rebuildList() {
2740
+ list = createWidgetComponent(summaryLines, displays, toggle, listState);
2741
+ }
2742
+ function toggle(id: string) {
2743
+ if (selectedId === id) close();
2744
+ else open(id);
2745
+ }
2731
2746
  function refreshViewer() {
2732
2747
  if (!selectedId || !viewer) return;
2733
2748
  const log = options.readLog(selectedId);
@@ -2747,18 +2762,30 @@ export function createSessionWidget(options: {
2747
2762
  }
2748
2763
  function close() {
2749
2764
  selectedId = undefined;
2765
+ viewerRow = -1;
2766
+ viewerLines = 0;
2767
+ dragging = false;
2750
2768
  viewer?.dispose();
2751
2769
  viewer = undefined;
2752
2770
  source = undefined;
2753
2771
  options.requestRender();
2754
2772
  }
2755
2773
  function open(id: string) {
2774
+ if (!displays.some((session) => session.id === id)) {
2775
+ displays = [...displays, { id, header: id, tail: [], viewable: true, active: true }];
2776
+ }
2777
+ if (listState.mode === "running" && !displays.find((session) => session.id === id)?.active)
2778
+ listState.mode = "all";
2779
+ const visible = listState.mode === "running" ? displays.filter((session) => session.active) : displays;
2780
+ listState.page = Math.floor(visible.findIndex((session) => session.id === id) / WIDGET_SESSION_PAGE_SIZE);
2781
+ rebuildList();
2782
+ dragging = false;
2756
2783
  viewer?.dispose();
2757
2784
  source = undefined;
2758
2785
  selectedId = id;
2759
2786
  viewer = createLogViewer({
2760
2787
  ...options,
2761
- getHeight: () => Math.min(WIDGET_VIEWER_MAX_LINES, options.getHeight()),
2788
+ getHeight: () => Math.max(3, Math.min(WIDGET_VIEWER_MAX_LINES, options.getHeight() - listHeight + 1)),
2762
2789
  onClose: close,
2763
2790
  });
2764
2791
  refreshViewer();
@@ -2774,15 +2801,36 @@ export function createSessionWidget(options: {
2774
2801
  },
2775
2802
  update(summary: string[], sessions: WidgetSessionDisplay[]) {
2776
2803
  displays = sessions;
2777
- list = createWidgetComponent(summary, sessions, open, listState);
2804
+ summaryLines = summary;
2805
+ rebuildList();
2806
+ if (selectedId && !sessions.some((session) => session.id === selectedId && (listState.mode === "all" || session.active))) close();
2778
2807
  refreshViewer();
2779
2808
  options.requestRender();
2780
2809
  },
2781
- render(width: number) { return (viewer ?? list).render(width); },
2810
+ render(width: number) {
2811
+ const lines = list.render(width);
2812
+ listHeight = lines.length;
2813
+ viewerRow = selectedId ? list.getSessionLine(selectedId) : -1;
2814
+ viewerLines = 0;
2815
+ if (viewer && viewerRow >= 0) {
2816
+ // Keep the original styled row as the toggle header.
2817
+ const body = viewer.render(width).slice(1);
2818
+ viewerLines = body.length;
2819
+ lines.splice(viewerRow + 1, 0, ...body);
2820
+ }
2821
+ return lines;
2822
+ },
2782
2823
  handleMouse(event: TuiMouseEvent) {
2783
- // Stock Pi widget hosts forward mouse input to this component, not
2784
- // to a nested native layout tree. Keep the full wheel/drag event.
2785
- return (viewer ?? list).handleMouse(event);
2824
+ if (viewer && viewerRow >= 0 && (dragging || (event.y > viewerRow && event.y <= viewerRow + viewerLines))) {
2825
+ const result = viewer.handleMouse({ ...event, y: event.y - viewerRow });
2826
+ if (result && "capture" in result && result.capture) dragging = true;
2827
+ if (event.type === "release") dragging = false;
2828
+ return result;
2829
+ }
2830
+ const mode = listState.mode, page = listState.page;
2831
+ const result = list.handleMouse({ ...event, y: event.y > viewerRow && viewerRow >= 0 ? event.y - viewerLines : event.y });
2832
+ if (mode !== listState.mode || page !== listState.page) close();
2833
+ return result;
2786
2834
  },
2787
2835
  handleKey(data: string) { return viewer?.handleKey(data) ?? false; },
2788
2836
  invalidate() { list.invalidate(); viewer?.invalidate(); },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yusukeshib/pi-babysit",
3
- "version": "0.6.24",
3
+ "version": "0.6.26",
4
4
  "description": "Run any shell command and pi subagents under babysit, with context-safe captured output.",
5
5
  "keywords": [
6
6
  "pi-package",