@thedevlabs-io/labd 0.5.0 → 0.6.0

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/AGENTS.md CHANGED
@@ -246,18 +246,41 @@ not fetch submodules for a git dependency. `scripts/prepare.mjs` says so rather
246
246
  than letting the install appear to succeed with no binary - which is what it did
247
247
  before, reporting "added 1 package" and installing nothing.
248
248
 
249
- ## Sub-agents - deliberately outside
250
-
251
- There is no `spawn_agent` tool, and adding one would be a permission mistake. Such
252
- a tool has to decide what mode the children run in: inherit the parent's and one
253
- approval becomes N unattended writes; give children their own and the model has
254
- been handed the setting it is specifically not allowed to influence.
255
-
256
- `--mode rpc` already makes every labd process a driveable agent, so orchestration
257
- lives in a script the operator writes - where the children's mode is set by a
258
- person, each child asks that script for approval over the wire, and there is no RPC
259
- method for changing a mode. `examples/orchestrate.mjs` is the reference, and
260
- `test/rpc-e2e.test.ts` runs two children at once to prove it.
249
+ ## Helpers - inside the permission model, not beside it
250
+
251
+ This section used to be "Sub-agents - deliberately outside", and its argument was
252
+ right about the design it rejected: a `spawn_agent` tool whose children ran in the
253
+ parent's mode would turn one approval into N unattended writes, and children with a
254
+ mode of their own would hand the model the one setting it must not touch. The
255
+ helpers labd has now avoid both, because **there is no approval to spawn and no
256
+ mode for a helper to have**:
257
+
258
+ - Spawning changes nothing, so it asks nothing. What a helper then DOES is checked
259
+ call by call: every write and command goes through `executeCall`
260
+ (`packages/core/src/calls.ts`) - the same code the turn's own calls go through -
261
+ and `needsApproval` against the learner's mode. So `auto` still asks about every
262
+ command a helper wants to run, and `ask` about every write.
263
+ - Its cards join the turn's line: one at a time, marked "from a helper", and once
264
+ the turn is stopped nothing still waiting is asked.
265
+ - **Unattended means declined, for helpers too.** `labd -p` has no one to ask, so a
266
+ helper's write is refused and the run exits 2. `e2e.test.ts` proves it.
267
+ - One level: a helper is offered every tool but `spawn_agent` and refuses it if
268
+ called. At most `MAX_SUBAGENTS` at once; the rest are refused with a reason.
269
+ - It shares the turn's ToolContext, so its writes are in the turn's checkpoint
270
+ and `/undo` puts them back with the rest.
271
+ - It writes nothing to the session but the parent's call and its report, and is
272
+ never handed to memory on its own.
273
+ - The helper's brief, the report bound and when to use one are the protocol's
274
+ (`subAgentBrief`, `SUBAGENT_REPORT_CHARS`, the prompt's "Helpers" section), not
275
+ this repo's.
276
+
277
+ `test/helpers.test.ts` pins these against the real tools and checkpoint store.
278
+
279
+ **Orchestration across processes is still outside, and still the right tool for
280
+ it.** `--mode rpc` makes every labd process a driveable agent, where each child's
281
+ mode is set by the person who wrote the script and each child asks that script for
282
+ approval. `examples/orchestrate.mjs` is the reference; `test/rpc-e2e.test.ts` runs
283
+ two children at once. An RPC host is told which helper is asking on `labd/approve`.
261
284
 
262
285
  ## Branches - promotion is by pull request, always
263
286
 
package/README.md CHANGED
@@ -93,6 +93,7 @@ arrived since (`End`, or scrolling back to the bottom, follows again).
93
93
  | `/` | a list of commands appears above the box; `↑↓` moves, `tab`/`enter` accepts, `esc` closes |
94
94
  | `@` | the same, for files in the project — `@src/index.ts` tags the file into the question |
95
95
  | `tab` | complete the command or path being typed |
96
+ | `shift+tab` | next mode: `ask`, `auto`, `agent` - the same as `/mode`, and it says what the new mode does |
96
97
  | `pgup` / `pgdn`, mouse wheel | scroll the transcript |
97
98
  | `home` / `end` | top / bottom of the transcript (with an empty box; otherwise they move within the line) |
98
99
  | `↑` / `↓` | earlier questions, when the caret is on the first / last line |
@@ -192,11 +193,23 @@ the last is how you run labd in CI without pairing. `NO_COLOR` turns colour off,
192
193
  `FORCE_COLOR` turns it on (`FORCE_COLOR=0` off) and wins over everything else, and
193
194
  `LABD_NO_MOTION` skips the opening animation.
194
195
 
195
- ## Sub-agents and orchestration
196
+ ## Helpers and orchestration
196
197
 
197
- labd has no `spawn_agent` tool, and that is a decision rather than a gap. It does
198
- have `--mode rpc`, which makes every labd process a driveable agent on
199
- stdin/stdout — so an orchestrator is a script that spawns several of them:
198
+ **Helpers.** labd can hand a self-contained piece of a job to a helper - another
199
+ labd with the same tools, working from a brief labd writes, whose report comes
200
+ back into the answer. Several can run at once, up to three. A helper is not a way
201
+ around your settings:
202
+
203
+ - a helper has no mode of its own. Its writes and commands ask you exactly when
204
+ labd's would, one card at a time, marked **from a helper**;
205
+ - under `labd -p` nothing can be approved, so a helper's changes are declined and
206
+ the run exits 2;
207
+ - what a helper changed is part of the answer, so `/undo` puts it back too;
208
+ - a helper cannot start helpers of its own.
209
+
210
+ **Orchestration.** `--mode rpc` makes every labd process a driveable agent on
211
+ stdin/stdout, so an orchestrator is a script that spawns several of them, each
212
+ with a mode you choose:
200
213
 
201
214
  ```bash
202
215
  node examples/orchestrate.mjs "what does this file do?" src/a.ts src/b.ts src/c.ts
@@ -205,14 +218,6 @@ node examples/orchestrate.mjs "what does this file do?" src/a.ts src/b.ts src/c.
205
218
  [`examples/orchestrate.mjs`](examples/orchestrate.mjs) is ~140 lines: spawn, send
206
219
  `labd/prompt`, read the notification stream, run three at a time.
207
220
 
208
- **Why not a tool inside labd.** A `spawn_agent` tool has to decide what mode the
209
- children run in, and every answer is bad. Inherit the parent's mode and one
210
- approval becomes N unattended writes. Give children their own mode and the model
211
- has been handed the one setting it is specifically not allowed to influence.
212
- Outside, the mode of each child is set by whoever wrote the script, each child
213
- asks *that script* for approval over the wire, and the orchestrator is code you
214
- can read. (Pi reaches the same conclusion and suggests tmux.)
215
-
216
221
  The protocol is small:
217
222
 
218
223
  | you send | |
@@ -223,9 +228,9 @@ The protocol is small:
223
228
  | labd sends | |
224
229
  | `labd/ready` | first, on start: protocol, framing, session id, mode, root |
225
230
  | `labd/text`, `labd/status`, `labd/done`, `labd/error` | notifications as the turn runs |
226
- | `labd/tool` `{name, status, input, detail, preview, output}` | each tool step; `preview` is the diff or the command, `output` what came back |
227
- | `labd/command_output` `{line}` | a running command's own output, as it happens |
228
- | `labd/approve` `{call, preview}` | **a request to you** — answer `{approved: true\|false}` |
231
+ | `labd/tool` `{name, status, input, detail, preview, output, helper?}` | each tool step; `preview` is the diff or the command, `output` what came back, `helper` `{id, label}` when a helper made the call |
232
+ | `labd/command_output` `{line, key?}` | a running command's own output, as it happens; `key` names a helper's call |
233
+ | `labd/approve` `{call, preview, helper?}` | **a request to you** - answer `{approved: true\|false}`; `helper` names the helper asking |
229
234
 
230
235
  Closing stdin ends the session: any turn in flight finishes, the conversation is
231
236
  handed over for memory, and the process exits 0.