@yusukeshib/pi-babysit 0.3.11 → 0.3.13

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/README.md CHANGED
@@ -33,7 +33,7 @@ reachable from anywhere (`~/.pi-babysit/<pi-session-id>/`). Two kinds:
33
33
  | kind | started by | completion | on completion |
34
34
  | ---- | ---------- | ---------- | ------------- |
35
35
  | **process** | `babysit_run { command }` | process **exit** | automatic notification message (`triggerTurn`), batched for all exits observed in the same poll — the agent may end its turn after starting and is resumed on exit, same contract as the old `process` tool |
36
- | **subagent** | `babysit_run { profile: "subagent", task }` | `agent_end` in the RPC event stream (process stays alive) | none — the agent polls `babysit_check` or blocks on `babysit_wait`; the idle session accepts follow-up tasks |
36
+ | **subagent** | `babysit_run { profile: "subagent", task }` | `agent_settled` in the RPC event stream (process stays alive) | none — the agent polls `babysit_check` or blocks on `babysit_wait`; the idle session accepts follow-up tasks |
37
37
 
38
38
  The **profile is a tool parameter, not a separate tool set**: domain knowledge
39
39
  (RPC bookkeeping, per-task byte offsets, parked-turn detection, PTY-safe
@@ -62,8 +62,11 @@ programs** (installers, wizards, REPLs): type with `babysit_send`
62
62
  | `babysit_wait` | Block until done: process exit (or `expect: "regex"` readiness marker), subagent task completion. Multi-wait: `ids` + `mode: "any"\|"all"` |
63
63
  | `babysit_kill` | Terminate a session, verify terminal state, then suppress the exit notification |
64
64
 
65
- A `tool_call` hook blocks shell backgrounding (`… &`, `nohup`, `setsid`,
66
- `disown`) and redirects all direct `bash` commands to `babysit_run`.
65
+ The built-in `bash` tool is removed from the active tool set so the model does
66
+ not waste a failed tool turn before choosing `babysit_run`. A fallback
67
+ `tool_call` hook still blocks direct shell calls if another extension or preset
68
+ re-enables `bash`, including shell backgrounding (`… &`, `nohup`, `setsid`,
69
+ `disown`). Set `PI_BABYSIT_ALLOW_BASH=1` to retain direct `bash` explicitly.
67
70
 
68
71
  ## Commands (human)
69
72
 
@@ -91,9 +94,10 @@ babysit_check { id: "cargo-test", lines: 50 }
91
94
  babysit_check { id: "cargo-test", pattern: "FAIL|ERROR", lines: 50 }
92
95
  ```
93
96
 
94
- Tail and search results are capped at 200 lines and clipped to 8 KB. Pattern
95
- search returns the latest matching lines with line numbers. Do not read a
96
- potentially large log file in full.
97
+ Tail and search results are capped at 200 lines, and the complete returned tool
98
+ result (including lifecycle headers) is clipped to 8 KB. Pattern search returns
99
+ the latest matching lines with line numbers. Prefer a targeted pattern over a
100
+ broad tail, and do not read a potentially large log file in full.
97
101
 
98
102
  Subagent logs also compact Pi's streaming `message_update` events before they
99
103
  are recorded. Pi repeats the complete growing assistant message and partial
@@ -103,7 +107,8 @@ events untouched. This keeps long RPC sessions approximately linear in emitted
103
107
  content without changing final answers, completion detection, or follow-up
104
108
  behavior. Live `/babysit` and attach views render the retained deltas.
105
109
 
106
- All shell commands, including `pwd` and Git, are redirected to `babysit_run`.
110
+ All shell commands, including `pwd` and Git, go through `babysit_run`. Bundle
111
+ closely related tiny observations when doing so safely reduces tool turns.
107
112
  Set `PI_BABYSIT_ALLOW_BASH=1` only as an explicit emergency escape hatch.
108
113
 
109
114
  ## Unexpected worker loss
@@ -117,18 +122,24 @@ because blindly rerunning an arbitrary command can duplicate side effects.
117
122
 
118
123
  ## How completion detection works
119
124
 
120
- - **Process**: a 2.5s poller watches for running→exited transitions and injects
121
- one `pi.sendMessage(…, { triggerTurn: true, deliverAs: "steer" })` containing
122
- every deliverable exit observed in that poll (deduped via `meta/<id>.json`).
125
+ - **Process**: a 2.5s poller watches for running→exited transitions and, once the
126
+ parent agent is idle, injects one
127
+ `pi.sendMessage(…, { triggerTurn: true, deliverAs: "steer" })` containing every
128
+ deliverable exit observed in that poll (deduped via `meta/<id>.json`). Waiting
129
+ for idleness prevents an immediately-following `babysit_wait` from racing the
130
+ poller and receiving a duplicate completion.
123
131
  `babysit_kill` and an exit already reported by `babysit_wait` suppress the
124
132
  notification.
125
- - **Subagent**: `babysit_wait` blocks on `babysit expect '"type":"agent_end"'`.
126
- An `agent_end` whose last message is a **parked** toolResult — a
127
- `babysit_run { command }` result carrying the `[notify-on-exit]` marker (or
128
- the legacy `process` tool) — only means "turn parked awaiting a process-exit
129
- notification; pi resumes on its own", so the wait continues. Any other
130
- `agent_end` is real completion. Per-task byte offsets scope check/wait to the
131
- CURRENT task, which is what makes follow-up tasks work.
133
+ - **Subagent**: `babysit_wait` blocks on `babysit expect '"type":"agent_settled"'`.
134
+ Unlike `agent_end`, `agent_settled` cannot precede an automatic retry,
135
+ compaction retry, or queued continuation. A settled run containing a
136
+ **parked** toolResult — a `babysit_run { command }` result carrying the
137
+ `[notify-on-exit]` marker (or the legacy `process` tool) — only means "turn
138
+ parked awaiting a process-exit notification; pi resumes on its own", so the
139
+ wait continues. The marker-bearing result may be followed by a short
140
+ assistant note without defeating parked detection. Per-task byte offsets
141
+ scope check/wait to the CURRENT task, and appended log bytes are parsed
142
+ incrementally, which keeps follow-up tasks efficient.
132
143
 
133
144
  Subagents load `self-reap.ts`, which exits an idle finished subagent after a
134
145
  grace window (`PI_BABYSIT_REAP_AFTER`, default 120s) using the same parked-turn
package/agents.ts CHANGED
@@ -56,20 +56,34 @@ function loadAgentsFromDir(
56
56
  continue;
57
57
  }
58
58
 
59
- const { frontmatter, body } =
60
- parseFrontmatter<Record<string, string>>(content);
61
- if (!frontmatter.name || !frontmatter.description) continue;
59
+ let parsed: ReturnType<typeof parseFrontmatter<Record<string, unknown>>>;
60
+ try {
61
+ parsed = parseFrontmatter<Record<string, unknown>>(content);
62
+ } catch {
63
+ // One malformed definition must not prevent every other named agent
64
+ // from being discovered.
65
+ continue;
66
+ }
67
+ const { frontmatter, body } = parsed;
68
+ if (
69
+ typeof frontmatter.name !== "string" ||
70
+ typeof frontmatter.description !== "string"
71
+ ) {
72
+ continue;
73
+ }
62
74
 
63
- const tools = frontmatter.tools
64
- ?.split(",")
65
- .map((t) => t.trim())
66
- .filter(Boolean);
75
+ const tools = Array.isArray(frontmatter.tools)
76
+ ? frontmatter.tools.filter((tool): tool is string => typeof tool === "string")
77
+ : typeof frontmatter.tools === "string"
78
+ ? frontmatter.tools.split(",")
79
+ : [];
80
+ const normalizedTools = tools.map((tool) => tool.trim()).filter(Boolean);
67
81
 
68
82
  agents.push({
69
83
  name: frontmatter.name,
70
84
  description: frontmatter.description,
71
- tools: tools && tools.length > 0 ? tools : undefined,
72
- model: frontmatter.model,
85
+ tools: normalizedTools.length > 0 ? normalizedTools : undefined,
86
+ model: typeof frontmatter.model === "string" ? frontmatter.model : undefined,
73
87
  systemPrompt: body,
74
88
  source,
75
89
  filePath,