@mu-crew/mu 3.0.0 → 3.2.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.
@@ -21,7 +21,9 @@ concerns interactive attachment, not mu's automatic agent-state reads.
21
21
  --command 'ssh <host> -t "..."'` starts an ordinary tmux pane whose
22
22
  foreground process happens to be ssh. Everything mu does is
23
23
  pane-shaped, so `mu agent send`, `mu agent read`, and the reaper work
24
- unchanged across the network. murmur reports the remote process's agent state.
24
+ unchanged across the network. A pi agent's control socket rides the
25
+ same ssh as a `-L` forward, so its state is exact; murmur reports state
26
+ only for non-pi agents.
25
27
 
26
28
  **One orchestrator DB; panes may be remote.** All state stays on your
27
29
  machine. Do not run a second mu on the host to "coordinate": ownership
@@ -29,7 +31,7 @@ is machine-local by construction — `tasks.owner_id` is an FK into the
29
31
  `agents` table, which never syncs — so a remote mu could not claim
30
32
  tasks in your DAG. Two half-views, no benefit.
31
33
 
32
- **murmur sees the REMOTE pane** — the opposite of the line above. Its
34
+ **murmur, if you use it, sees the REMOTE pane** — the opposite of the line above. Its
33
35
  extension runs inside the agent's process, so it claims the pane on the
34
36
  HOST; mu's local pane holds an ssh client and reports nothing. Two
35
37
  addresses, one worker.
@@ -67,7 +69,7 @@ the only ssh channel**. While it is open, `git fetch`, `git push`,
67
69
 
68
70
  **Attach to look, then close immediately.** `mu agent close <name>`
69
71
  detaches without stopping a detached-tmux agent. mu reads agent state
70
- through murmur automatically; use mule for orchestrator commands because
72
+ automatically (pi: the forwarded control socket); use mule for orchestrator commands because
71
73
  its separate ControlPath cannot contend with the attach pane. See
72
74
  § When the host limits concurrent sessions for diagnosis and the
73
75
  remote-agent setup.
@@ -115,13 +117,16 @@ ssh dev 'git -C ~/repo worktree add ~/ws/worker-1'
115
117
  mu task note t1 -w big "REMOTE: dev:~/ws/worker-1
116
118
  REMOTE_BASE: worker-1:$(ssh dev 'cd ~/ws/worker-1 && git rev-parse HEAD')"
117
119
 
118
- # 3. SPAWN — env vars go INSIDE the command (tmux -e stops at the hop)
120
+ # 3. SPAWN — env goes INSIDE the command (tmux -e stops at the hop);
121
+ # remote-env prints it plus the control-socket forward (runs nothing)
122
+ eval "$(mu agent remote-env worker-1 -w big --shell)" # MU_SSH_ARGS, MU_REMOTE_ENV
123
+ # $SHELL -ilc: ssh's non-interactive shell skips ~/.zshrc (PATH, provider env)
119
124
  mu agent spawn worker-1 -w big --command \
120
- 'ssh dev -t "cd ~/ws/worker-1 && MU_MANAGED_AGENT=1 MU_AGENT_NAME=worker-1 MU_WORKSTREAM=big pi --approve"'
125
+ "ssh $MU_SSH_ARGS dev -t 'cd ~/ws/worker-1 && $MU_REMOTE_ENV \$SHELL -ilc \"pi --approve\"'"
121
126
 
122
127
  # 4. CLAIM + SEND — identical to a local agent
123
128
  mu task claim t1 -w big --for worker-1 --evidence 'remote on dev'
124
- mu agent send worker-1 -w big '...'
129
+ mu agent send worker-1 -w big --fresh '...'
125
130
 
126
131
  # 5. WAIT — run the claim's one-shot Next: command once per turn
127
132
  # Set a deadline; at expiry read the pane, then release or re-dispatch.
@@ -141,31 +146,37 @@ Local and remote agents mix freely in one workstream. The DAG, tracks,
141
146
  — task status is a row in YOUR database, written by you, so a wait on
142
147
  it is exact.
143
148
 
144
- **Agent state comes from murmur.** Its extension runs inside pi on the
145
- host and reports state without reading the nested tmux screen. mu reads
146
- murmur's remote rows and caches them for 10 seconds. Remote rows can lag
147
- by murmur's collect floor (30 seconds ± 10 seconds) plus that cache;
148
- their `since` value is the row's `updated_at`, so idle and stall age is
149
- approximate.
150
-
151
- | waiting on | remote? |
152
- | --- | --- |
153
- | `mu task wait` (task status) | exact — a DB poll, once something closes the task |
154
- | the reaper | fires, but see below |
155
- | stall detection | exact state from murmur; approximate age |
156
- | `mu agent wait --first` | exact state from murmur |
157
-
158
- Without murmur, remote agent state is `unknown`. `mu agent wait` does not
159
- fire on `unknown`, and `unknown` never counts as a task stall. Run
160
- `mu doctor` for the missing-source reason.
149
+ **Agent state comes from the forwarded control socket** for pi agents.
150
+ `-L <local>:<remote>` in `$MU_SSH_ARGS` makes the remote pi's mu
151
+ extension answer at the local path mu always uses, so send, state,
152
+ `mu agent wait --first` and `mu agent abort` are exact, as for a local
153
+ agent. Two prerequisites:
154
+
155
+ - The host has the extension: run `mu link pi` there (or copy
156
+ `dist/extension/mu-pi.js` into its pi extensions dir).
157
+ - The ssh is a direct connection. `$MU_SSH_ARGS` carries
158
+ `-o ControlMaster=no -o ControlPath=none`: with `ControlMaster auto`
159
+ in `~/.ssh/config` the pane's ssh joins an existing master and the
160
+ `-L` unix forward never binds locally (spawn reports `ctl missing`).
161
+ - sshd allows the forward. `ExitOnForwardFailure=yes` makes a refused
162
+ forward kill the pane at spawn, loudly; check
163
+ `AllowStreamLocalForwarding` in the host's `sshd_config`.
164
+
165
+ Spawn reports `ctl ok` when both hold; `mu doctor` has a ctl row per
166
+ agent. When the ssh dies, ssh leaves the local socket file behind: it
167
+ probes as `refused` (never a stale `ok`), and close or the reaper
168
+ deletes it.
169
+
170
+ Non-pi remote agents still get state from murmur, which lags by its
171
+ collect floor (30 seconds ± 10 seconds) plus a 10-second cache. Without
172
+ murmur their state is `unknown`, which `mu agent wait` and stall
173
+ detection never fire on.
161
174
 
162
175
  The reaper is right for a direct spawn — the connection dying really
163
176
  does kill that agent — and wrong for a detached-tmux one, where the
164
177
  agent outlives the ssh but mu reaps the task anyway. See § A dropped
165
178
  connection reaps the task but NOT the commit.
166
179
 
167
- The division is: mu owns the work; murmur reports what the agent is doing.
168
-
169
180
  ### On step 2 — the note is load-bearing
170
181
 
171
182
  The task note survives connection loss and context compaction. Keep the
@@ -345,20 +356,23 @@ exists:
345
356
  ssh dev 'git -C ~/repo worktree remove ~/ws/worker-1'
346
357
  ```
347
358
 
348
- `mu agent kick` signals the local pane's foreground process group —
349
- that is the ssh client, not the remote agent. Use `mu agent close` and
350
- respawn.
359
+ To stop a remote pi agent's turn, `mu agent abort <name>`: it goes
360
+ through the forwarded socket. `mu agent kick` signals the local pane's
361
+ foreground process group — that is the ssh client, not the remote
362
+ agent. For an unresponsive one, `mu agent close` and respawn.
351
363
 
352
364
  ---
353
365
 
354
366
  ## mu and murmur, and what you lose without it
355
367
 
356
- [murmur](https://github.com/mu-crew/murmur) provides agent state for
357
- mu's tmux backend. Without it, mu reports agent state as `unknown`.
358
- The DAG, claims, task completion waits, workspaces, spawn, send, read,
359
- the reaper, this remote recipe, and `git fetch` collection still work.
360
- Agent-state consumers such as `mu agent wait`, idle flags, and task-stall
361
- detection wait rather than treating `unknown` as completion or a stall.
368
+ pi agents, local or remote, need no murmur: mu reads their state from
369
+ the control socket. [murmur](https://github.com/mu-crew/murmur) provides
370
+ agent state for non-pi agents (claude-code, codex) on tmux. Without it
371
+ those report `unknown`; the DAG, claims, task completion waits,
372
+ workspaces, spawn, send, read, the reaper, this remote recipe, and
373
+ `git fetch` collection still work. Agent-state consumers such as
374
+ `mu agent wait`, idle flags, and task-stall detection wait rather than
375
+ treating `unknown` as completion or a stall.
362
376
 
363
377
  murmur also provides the tmux status pills, `prefix+a`, attachment hints,
364
378
  and `murmur peer list` for host reachability. The division is: **mu owns
@@ -371,13 +385,15 @@ work. Its interfaces used by mu are in murmur's
371
385
  | what should happen next | mu — the DAG |
372
386
  | who owns this task | mu — `claim` / `close` |
373
387
  | is the task done | mu — `task wait`, a DB poll |
374
- | what is this agent doing right now | murmur — pushed from inside pi |
388
+ | what is this pi agent doing right now | mu — the control socket |
389
+ | what is a non-pi agent doing right now | murmur |
375
390
  | is anything blocked on me, anywhere | murmur |
376
391
  | which host can I reach | murmur — `peer list` |
377
392
 
378
393
  **The seam is three env vars, and it is load-bearing.** `mu agent
379
394
  spawn` injects `MU_MANAGED_AGENT=1`, `MU_AGENT_NAME` and
380
- `MU_WORKSTREAM`; pi inherits them and murmur's extension reads them.
395
+ `MU_WORKSTREAM` (plus `MU_CTL_SOCK` for mu's own extension); pi
396
+ inherits them and murmur's extension reads them.
381
397
  That one mechanism gives you:
382
398
 
383
399
  1. `driver=orchestrated`, so crew stays out of the human's status bar
@@ -386,7 +402,7 @@ That one mechanism gives you:
386
402
  3. the attachment back-reference for a remote worker
387
403
 
388
404
  For a remote worker they go **inside** the ssh command (tmux `-e` stops
389
- at the hop). Miss them and the agent reports `driver=human`: it appears
405
+ at the hop); `$MU_REMOTE_ENV` from `mu agent remote-env` carries them. Miss them and the agent reports `driver=human`: it appears
390
406
  in your own counts as if it were yours.
391
407
 
392
408
  ## Picking a host
@@ -519,21 +535,25 @@ Run the agent in its own tmux session on the host, and attach to
519
535
  the session can be released.
520
536
 
521
537
  ```bash
538
+ eval "$(mu agent remote-env worker-1 -w big --shell)" # MU_SSH_ARGS, MU_REMOTE_ENV
522
539
  # Agent runs DETACHED on the host; the ssh returns immediately
523
- ssh dev 'tmux new-session -d -s mu-worker-1 -c ~/ws/worker-1 \
524
- "MU_MANAGED_AGENT=1 MU_AGENT_NAME=worker-1 MU_WORKSTREAM=big pi --approve"'
540
+ ssh dev "tmux new-session -d -s mu-worker-1 -c ~/ws/worker-1 \
541
+ '$MU_REMOTE_ENV \$SHELL -ilc \"pi --approve\"'"
525
542
 
526
- # Attach a local pane to it; claim/send are then normal
527
- mu agent spawn worker-1 -w big --command 'ssh dev -t "tmux attach -t mu-worker-1"'
543
+ # Attach a local pane to it (forwarding the socket); claim/send are then normal
544
+ mu agent spawn worker-1 -w big --command "ssh $MU_SSH_ARGS dev -t 'tmux attach -t mu-worker-1'"
528
545
 
529
546
  # COLLECT: detach FIRST to free the session, then fetch
530
547
  mu agent close worker-1 -w big
531
548
  git fetch "ssh://dev/~/ws/worker-1" HEAD && git cherry-pick FETCH_HEAD
532
549
 
533
- # Reattach — same session, LLM context intact
534
- mu agent spawn worker-1 -w big --command 'ssh dev -t "tmux attach -t mu-worker-1"'
550
+ # Reattach — same session, LLM context intact (same --command as above)
551
+ mu agent spawn worker-1 -w big --command "ssh $MU_SSH_ARGS dev -t 'tmux attach -t mu-worker-1'"
535
552
  ```
536
553
 
554
+ The pi keeps serving its socket at the remote path while detached; each
555
+ attach re-creates the local end of the forward.
556
+
537
557
  Two consequences, both counterintuitive:
538
558
 
539
559
  - **An attached pane blocks concurrent ssh just as much as a direct
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mu
3
- description: Manage AI agents in terminal-multiplexer panes (tmux or herdr) — from a single off-the-cuff helper to a persistent crew coordinated through a built-in task graph. Use when the user asks to "create/spin up a subagent to X", "run X in the background", "do this in parallel", "use one subagent per X to do Y", "kick off a helper to watch/investigate/draft X", or to spawn, send work to, observe, or coordinate one or many agents — especially work you'll keep talking to, long-lived agents, background tasks, or anything that benefits from a dependency graph and parallel-track detection. For zero-ceremony single helpers use the reserved `scratch` workstream; for one-shot "fire and get a result back" prefer pi-subagents.
3
+ description: Manage AI agents in terminal-multiplexer panes (tmux or herdr) — from a single off-the-cuff helper to a persistent crew coordinated through a built-in task graph. Use when the user asks to "create/spin up a subagent to X", "run X in the background", "do this in parallel", "use one subagent per X to do Y", "kick off a helper to watch/investigate/draft X", or to spawn, send work to, observe, or coordinate one or many agents — especially work you'll keep talking to, long-lived or background agents, or anything needing a dependency graph. For zero-ceremony helpers use the reserved `scratch` workstream; for one-shot "fire and get a result back" use the `mu_delegate` tool — its pane stays attachable.
4
4
  ---
5
5
 
6
6
  # mu — Multi-agent orchestration
@@ -32,11 +32,13 @@ both. `--json` exists on every verb:
32
32
  - **agent** — named worker in a pane (you may be one).
33
33
  - **mux** — the multiplexer mu drives: tmux, or herdr. One per
34
34
  invocation. `mu doctor` names the active one; `MU_MUX` forces it.
35
- Spawn, send, and read work on both. Agent state comes from murmur
36
- on tmux and from herdr on herdr; `mu agent kick` is Linux-only on herdr.
35
+ Spawn, send, and read work on both. `mu agent kick` is Linux-only on herdr.
36
+ - **control socket (ctl)** — how mu drives a pi agent: the mu pi
37
+ extension (`mu link pi`) serves exact send, state, wait and abort
38
+ inside pi's own TUI. `ctl missing|refused` means it does not answer.
37
39
  - **task** — DAG node with mandatory `impact` (1–100) and
38
40
  `effort_days`. Shown as status/substate: `OPEN/todo|parked`,
39
- `IN_PROGRESS/active`, `CLOSED/done|wontfix|duplicate|superseded`.
41
+ `IN_PROGRESS/active`, `CLOSED/done|rejected|wontfix|duplicate|superseded`.
40
42
  Any `CLOSED/*` satisfies `--blocked-by`.
41
43
  - **claim / release** — atomic take/clear of `tasks.owner`.
42
44
  - **note** — append-only task context; survives sessions.
@@ -48,26 +50,27 @@ both. `--json` exists on every verb:
48
50
  ## When to use mu
49
51
 
50
52
  Use mu for persistent helpers, parallel work, dependencies, gated review, or
51
- work that must survive context compaction. Use `pi-subagents` for one focused
52
- answer and no follow-up; stay in one context for tiny edits or inspection.
53
+ work that must survive context compaction. Stay in one context for tiny edits
54
+ or inspection.
53
55
 
54
56
  ### Off-the-cuff helpers (`scratch`)
55
57
 
56
- Use the reserved `scratch` workstream for a helper you will keep driving but
57
- that needs no task DAG. It auto-creates on spawn.
58
+ The reserved `scratch` workstream holds helpers that need no task DAG; it
59
+ auto-creates on spawn. `mu_delegate` (installed by `mu link pi`) is its tool
60
+ form for one answer back; the CLI form is spawn + send + `mu agent wait --json`
61
+ (`lastText`).
58
62
 
59
- - For task-less work, `mu agent wait <name> --first` waits for busy → idle;
60
- exit 0 means met, 5 timeout, 6 pane died.
63
+ - `mu agent wait <name> --first` waits for busy → idle; exit 0 met, 5
64
+ timeout, 6 pane died.
61
65
  - For a watcher, persist last-seen state in a log ledger: write `mu log -w
62
66
  scratch --kind pr-state 'pr=1234 sha=abc ci=red'`, then read `mu log -w
63
- scratch --kind pr-state -n 1 --json`. Act only on change; chat context is not
64
- durable.
65
- - Use one agent per independent unit and `--workspace` for any helper that may
66
- edit, build, or test the shared repo.
67
+ scratch --kind pr-state -n 1 --json`. Act only on change.
68
+ - One agent per independent unit; `--workspace` for any helper that may edit,
69
+ build, or test the shared repo.
67
70
 
68
- A helper stuck at `needs_input` immediately after spawn likely hit pi's project
69
- trust prompt. Add `--approve` to the existing `MU_<CLI>_COMMAND`; use
70
- `--command` only to replace that configured command deliberately.
71
+ A helper stuck at `needs_input` right after spawn likely hit pi's project trust
72
+ prompt: add `--approve` to `MU_<CLI>_COMMAND` rather than overriding it with
73
+ `--command`.
71
74
 
72
75
  Move off `scratch` when work gains dependencies or review gates.
73
76
 
@@ -99,8 +102,8 @@ that a refusal with `--strict-staleness`.
99
102
 
100
103
  Agents can run on another machine: the PANE is local, the PROCESS is
101
104
  remote (`--command 'ssh <host> -t "..."'`), so `send`, `read`, and the
102
- reaper keep working unchanged. murmur reports the remote agent's state.
103
- **One orchestrator
105
+ reaper keep working unchanged. A pi agent's control socket is forwarded
106
+ over that ssh (`mu agent remote-env`). **One orchestrator
104
107
  DB; panes may be remote** — never run a second mu on the host, since
105
108
  `tasks.owner_id` is an FK into the machine-local `agents` table and a
106
109
  remote mu could not claim your tasks anyway. You create the remote
@@ -108,8 +111,7 @@ workspace yourself (`--workspace` is local-only).
108
111
 
109
112
  **Read [REMOTE_WORKERS.md](REMOTE_WORKERS.md) before spawning your first
110
113
  remote agent, and again before waiting on one; poll once per turn and run
111
- the claim's one-shot `Next:` command.** These are the other three traps
112
- that cost real time when learned late:
114
+ the claim's one-shot `Next:` command.** Three more costly traps:
113
115
  - **On a session-capped host, route long commands and silent-failure
114
116
  polls through [mule](https://github.com/mu-crew/mule).** A refused
115
117
  bare `rev-parse` can return an empty sha that looks like progress;
@@ -161,8 +163,11 @@ Every turn:
161
163
  `--note 'REPRO: ...\nSCOPE: ...'` when the title alone is not
162
164
  enough. Agent state is runtime observation; task ownership is durable and
163
165
  waitable.
164
- 4. Send terse instructions: task id, files/notes to read, workspace
165
- path, validation command, scope guards, task note contract.
166
+ 4. Send each new task with `mu agent send <w> --fresh '...'` (new
167
+ session + prompt in one step; refuses while busy): task id,
168
+ files/notes to read, workspace path, validation command, scope
169
+ guards, task note contract. Follow the `Next:` block; it picks
170
+ `--fresh` vs `--steer` for you.
166
171
  5. End with a loud final-action block:
167
172
 
168
173
  ```text
@@ -214,11 +219,14 @@ Every turn:
214
219
  workstream; only task ownership crosses.
215
220
  - For an idle worker, read the pane to learn why it stopped, then answer,
216
221
  retry, or release its task. `MU_IDLE_THRESHOLD_MS` defaults to 5m.
217
- - `mu agent kick` targets a wedged foreground subprocess. It refuses to signal
218
- the wrapping CLI; close the agent if that is what must stop.
219
- - Use `mu agent send`, not raw mux input: mu preserves literal text and confirms
220
- submission. Single-quote prompts containing shell expansions, or use a quoted
221
- heredoc.
222
+ - **Never chain `/new` and a prompt as two sends to a pi agent:** the prompt
223
+ can land mid-reset and vanish (it did, twice). `--fresh` does both inside pi.
224
+ - **Stop a worker:** `mu agent abort <w>` first for pi (exact, local or
225
+ remote, keeps context, waits for idle; exit 5 = still busy; queued
226
+ follow-ups return to the editor unsent). Then `mu agent kick` (pi
227
+ unresponsive, or non-pi; local panes only), then `mu agent close`.
228
+ - Use `mu agent send`, not raw mux input. Single-quote prompts containing shell
229
+ expansions, or use a quoted heredoc.
222
230
 
223
231
  ## CLI gotchas
224
232
 
@@ -233,7 +241,7 @@ Every turn:
233
241
  5 timeout, 6 pane died.
234
242
  - **`task close --if-ready`** no-ops until every blocker is CLOSED; bare
235
243
  `task release` reopens IN_PROGRESS.
236
- - **`task close --as wontfix --why ...`** unblocks dependents (listed in
244
+ - **`task close --as rejected|wontfix --why ...`** (declined | valid, not worth it) unblocks dependents (listed in
237
245
  the output). To keep dependents waiting, `task park --why` instead: parked
238
246
  leaves `next`, and `claim` refuses it without `--force`. Park refuses
239
247
  IN_PROGRESS — `task release` first.
@@ -303,12 +311,10 @@ If an agent pane dies, or `mu agent close` kills it mid-task, owned
303
311
  IN_PROGRESS tasks revert to OPEN with a `[reaper]` note and `task
304
312
  reap` op. No manual release after crashes.
305
313
 
306
- Agent state comes from [murmur](https://github.com/mu-crew/murmur) on
307
- tmux and from herdr on herdr. `unknown` means no state source; run
308
- `mu doctor` for the reason. mu needs murmur only for agent state:
309
- tasks, claims, workspaces, spawn, send, read, and task completion waits
310
- work without it. **mu owns the work; murmur reports what an agent is
311
- doing.** See [REMOTE_WORKERS.md](REMOTE_WORKERS.md) § mu and murmur.
314
+ pi agents report state through the control socket; other CLIs through
315
+ [murmur](https://github.com/mu-crew/murmur) on tmux or herdr on herdr.
316
+ `unknown` means no state source; `mu doctor` says why. pi agents need
317
+ no murmur. See [REMOTE_WORKERS.md](REMOTE_WORKERS.md) § mu and murmur.
312
318
 
313
319
  For high-stakes decisions:
314
320
 
@@ -342,28 +348,16 @@ Skipping close makes the orchestrator's wait hang. Won't do it:
342
348
 
343
349
  ## Follow-on prompts
344
350
 
345
- A new `mu agent send` appends to prior LLM context. For unrelated
346
- work, clear first (`/new` for pi/claude-code; `/clear` for codex):
347
-
348
- ```bash
349
- mu agent send worker-1 '/new'
350
- mu agent send worker-1 'Claim and work on task_x. Read notes first...'
351
- ```
352
-
353
- No `sleep` needed. `mu agent send` waits for the pane to finish any
354
- re-initialisation before pasting, and re-submits if the TUI swallowed
355
- the Enter. If a send cannot be confirmed it prints a `warning:` to
356
- stderr naming the pane; exit 0 with no warning means submitted.
357
-
358
- Budget is `MU_SEND_READINESS_MS` (default 15000; 0 = fire-and-forget).
359
- Sending to a BUSY agent is not delayed — that input queues normally.
351
+ A plain `mu agent send` appends to prior context; use it to steer or
352
+ answer. Unrelated work to pi: `mu agent send worker-1 --fresh 'Claim
353
+ task_x...'`. claude-code/codex: send `/new` (codex: `/clear`), then the
354
+ prompt; a send it cannot confirm prints a `warning:` on stderr.
360
355
 
361
356
  ## Guardrails
362
357
 
363
358
  Task ownership outranks runtime agent state. Coordinate through task notes and the
364
359
  activity log. Keep edges within one workstream, role-name agents, and reserve
365
- the `mu_` task-id prefix. Give workers bounded paths and commands; use `mu
366
- agent kick` if a subprocess wedges.
360
+ the `mu_` task-id prefix. Give workers bounded paths and commands.
367
361
 
368
362
  ## See also
369
363