@mu-crew/mu 3.6.2 → 3.7.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/dist/cli.js +208 -117
- package/dist/cli.js.map +1 -1
- package/dist/extension/mu-pi.js +37 -10
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.d.ts +20 -4
- package/dist/index.js +130 -57
- package/dist/index.js.map +1 -1
- package/docs/architecture/control-socket.md +2 -2
- package/docs/architecture/mux.md +4 -1
- package/docs/guide/delegate.md +7 -3
- package/docs/guide/dispatch.md +5 -2
- package/docs/guide/getting-started.md +1 -1
- package/docs/guide/recovery.md +11 -6
- package/docs/guide/upgrade.md +4 -4
- package/docs/reference/env.md +1 -1
- package/package.json +1 -1
- package/skills/mu/SKILL.md +6 -3
- package/skills/mu/recipes/codemode-driver.md +2 -2
- package/skills/mu/recipes/orchestrator-loop.md +10 -6
- package/skills/mu/recipes/remote-workers.md +23 -6
- package/skills/mu/recipes/tasks-or-calls.md +6 -2
|
@@ -28,8 +28,8 @@ reply carries `v: 1`; the client (`src/ctl/client.ts`) throws
|
|
|
28
28
|
| op | does |
|
|
29
29
|
| --- | --- |
|
|
30
30
|
| `hello` | identity plus `ops` (served ops) and `extVersion` (the mu version the extension was built from) |
|
|
31
|
-
| `status` | `state` (`busy` / `idle`
|
|
32
|
-
| `send` | `pi.sendUserMessage`; when busy, `mode` is `steer` or `followUp` (default) |
|
|
31
|
+
| `status` | `state` (`busy` / `idle`), `since`, `runs`, `pending` |
|
|
32
|
+
| `send` | `pi.sendUserMessage`; when busy, `mode` is `steer` or `followUp` (default). Replies with the `status` fields measured before the dispatch, so `runs` is the baseline for a later `wait` |
|
|
33
33
|
| `wait` | resolves on pi's `agent_settled` after `afterRuns`, with the run's `lastText` |
|
|
34
34
|
| `abort` | `ctx.abort()` (pi's Esc) |
|
|
35
35
|
| `fresh` | new session plus prompt as one operation, through the internal `/mu-fresh` command; replies once the new run starts; refused with `busy` unless `force` |
|
package/docs/architecture/mux.md
CHANGED
|
@@ -104,7 +104,10 @@ as a capability, not a name check:
|
|
|
104
104
|
- **tmux.** After `MU_SPAWN_LIVENESS_MS` (default 1500, `0` skips) mu
|
|
105
105
|
verifies the pane survived and scans its tail for provider or auth
|
|
106
106
|
startup errors. mu does not wait for murmur to claim the pane: state
|
|
107
|
-
reads before the first claim report `unknown`.
|
|
107
|
+
reads before the first claim report `unknown`. A pi agent skips the
|
|
108
|
+
fixed wait: mu checks the pane on every handshake tick (a dead pane
|
|
109
|
+
rolls back at once) and scans for startup errors when the handshake
|
|
110
|
+
ends.
|
|
108
111
|
- **herdr.** mu creates a bare pane and calls `startAgentInPane`, which
|
|
109
112
|
subsumes both checks.
|
|
110
113
|
- **pi, either backend.** After the agent starts, mu polls its control
|
package/docs/guide/delegate.md
CHANGED
|
@@ -51,15 +51,19 @@ From a script or other shell, run what the tool runs:
|
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
mu agent spawn helper-1 -w scratch
|
|
54
|
-
mu agent send helper-1 -w scratch --fresh 'Investigate why foo.spec.ts fails. Report the cause.'
|
|
55
|
-
mu agent wait helper-1 -w scratch --json
|
|
54
|
+
runs=$(mu agent send helper-1 -w scratch --fresh --json 'Investigate why foo.spec.ts fails. Report the cause.' | jq .runs)
|
|
55
|
+
mu agent wait helper-1 -w scratch --after-runs "$runs" --json
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
`--after-runs` waits for the first run past the count the send
|
|
59
|
+
reported, so a run that settles before the wait starts still counts.
|
|
60
|
+
|
|
58
61
|
Use `--fresh`: a plain send to a reused agent carries the previous
|
|
59
62
|
task's context.
|
|
60
63
|
|
|
61
64
|
`mu agent wait` fires when the agent leaves busy (including to
|
|
62
|
-
`needs_input`), never on an already idle agent
|
|
65
|
+
`needs_input`), never on an already idle agent unless it finished a run
|
|
66
|
+
past `--after-runs`. For pi, the `--json`
|
|
63
67
|
row carries:
|
|
64
68
|
|
|
65
69
|
- `outcome`: `done`, `empty`, `error`, `died`, `timeout`, or `pending`.
|
package/docs/guide/dispatch.md
CHANGED
|
@@ -118,8 +118,11 @@ mu task wait auth/build auth/docs --first --on-stall exit --json
|
|
|
118
118
|
polling instead.
|
|
119
119
|
|
|
120
120
|
Exit 7 has several causes: the worker finished but did not close, it
|
|
121
|
-
asked a question, or it hit a prompt. In each case, run
|
|
122
|
-
`
|
|
121
|
+
asked a question, or it hit a prompt. In each case, run the exit's
|
|
122
|
+
`Next:` first. For a pi owner, it starts with
|
|
123
|
+
`mu agent wait <owner> --after-runs <runs-1> --json`, which prints the
|
|
124
|
+
last answer at once. Then it shows `mu agent read <owner>`, for the
|
|
125
|
+
dialogs and crashes that only the pane shows.
|
|
123
126
|
|
|
124
127
|
Pipeline the work: merge one task, run your tests on the merged tree,
|
|
125
128
|
then dispatch the next task. Waiting for a whole wave hides partial
|
|
@@ -126,7 +126,7 @@ The exit code tells you what happened:
|
|
|
126
126
|
| `0` | the task closed | merge the work |
|
|
127
127
|
| `5` | `--timeout` expired (default 600 s) | wait again |
|
|
128
128
|
| `6` | the worker's pane died and the task went back to `OPEN` | spawn and claim again |
|
|
129
|
-
| `7` | the worker sat in `needs_input` for
|
|
129
|
+
| `7` | the worker sat in `needs_input` for `--stuck-after` seconds (120, or 5 for a pi worker read over its control socket) | run its `Next:` (for a pi worker, `mu agent wait worker-1 --after-runs <runs-1> --json -w auth` returns the last answer; otherwise `mu agent read worker-1 -w auth`), then answer it |
|
|
130
130
|
|
|
131
131
|
`--on-stall exit` is the default; it is spelled out above so older mu
|
|
132
132
|
versions behave the same. With `--on-stall warn`, a stalled worker only
|
package/docs/guide/recovery.md
CHANGED
|
@@ -41,22 +41,27 @@ back to pasting.
|
|
|
41
41
|
|
|
42
42
|
| State | Means | Fix |
|
|
43
43
|
| ----- | ----- | --- |
|
|
44
|
-
| `ctl missing` | no socket: the extension is not loaded | run `mu link pi`, then respawn the agent |
|
|
44
|
+
| `ctl missing` | no socket: the extension is not loaded, or a new pi waits at its trust prompt | answer the [trust prompt](#a-new-pi-agent-shows-ctl-missing), or run `mu link pi`, then respawn the agent |
|
|
45
45
|
| `ctl refused` | the socket exists but does not answer | respawn the agent; for a remote agent, check the ssh forward |
|
|
46
|
-
| `extension X older than installed Y` | pi loaded an older mu build |
|
|
46
|
+
| `extension X older than installed Y` or `extension lacks ops` | pi loaded an older mu build | send `/reload`, or respawn |
|
|
47
47
|
|
|
48
48
|
To send `/reload` without attaching:
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
mu agent send worker-1 -w auth '/reload'
|
|
51
|
+
mu agent send worker-1 -w auth '/reload' # extension serves op command
|
|
52
|
+
mu agent send worker-1 -w auth '/reload' --via mux # older: type it into the pane
|
|
52
53
|
```
|
|
53
54
|
|
|
54
|
-
`/reload` keeps the pi session. A `send --fresh` or `abort` against an
|
|
55
|
+
The error names the right form. `/reload` keeps the pi session. A `send --fresh` or `abort` against an
|
|
55
56
|
outdated extension fails with exit 4 and prints the same two fixes.
|
|
56
57
|
|
|
57
|
-
## A new pi agent
|
|
58
|
+
## A new pi agent shows `ctl missing`
|
|
58
59
|
|
|
59
|
-
pi asks whether to trust a project folder on first start.
|
|
60
|
+
pi asks whether to trust a project folder on first start. It asks
|
|
61
|
+
before it loads extensions, so the agent has no control socket yet:
|
|
62
|
+
mu shows `unknown (ctl missing)` and spawn warns after 30 s. A pi
|
|
63
|
+
launched through a wrapper whose name is not `pi` or `pi-meta` shows
|
|
64
|
+
`needs_input` instead. Run
|
|
60
65
|
`/trust` in the pane once, or spawn with
|
|
61
66
|
`--command 'pi --approve'`. `--approve` trusts every directory,
|
|
62
67
|
including workspace forks, so use it only on your own code.
|
package/docs/guide/upgrade.md
CHANGED
|
@@ -25,15 +25,15 @@ For agents other than pi, see [README § Install](../../README.md#install).
|
|
|
25
25
|
## Reload running pi agents
|
|
26
26
|
|
|
27
27
|
A running pi keeps the extension it loaded at start. After an upgrade,
|
|
28
|
-
|
|
29
|
-
shell:
|
|
28
|
+
send `/reload` to each agent, or respawn the agent. From a shell:
|
|
30
29
|
|
|
31
30
|
```bash
|
|
32
|
-
mu agent send worker-1 -w auth '/reload'
|
|
31
|
+
mu agent send worker-1 -w auth '/reload' # extension serves op command
|
|
32
|
+
mu agent send worker-1 -w auth '/reload' --via mux # older extension
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
Until then, the `ctl` row of `mu doctor` warns
|
|
36
|
-
`extension X older than installed Y`. Ops the old extension lacks,
|
|
36
|
+
`extension X older than installed Y` or `extension lacks ops`. Ops the old extension lacks,
|
|
37
37
|
such as `send --fresh`, fail with exit 4.
|
|
38
38
|
|
|
39
39
|
## Upgrade from 3.1 or earlier
|
package/docs/reference/env.md
CHANGED
|
@@ -29,7 +29,7 @@ milliseconds; `0` disables a wait unless noted.
|
|
|
29
29
|
| --- | --- | --- |
|
|
30
30
|
| `MU_<CLI>_COMMAND` | Executable for `--cli <cli>`; hyphens become underscores (`--cli pi-meta` reads `MU_PI_META_COMMAND`). May hold arguments. `--command` wins; the spawn line names the env var when it supplied the command. | the cli value |
|
|
31
31
|
| `MU_SPAWN_CTL_MS` | Budget for the control-socket handshake with a new pi agent, polled every 250ms. On timeout spawn still exits 0, reports `ctl: missing` or `ctl: refused`, and warns. `--no-ctl` skips it per spawn. | `30000` |
|
|
32
|
-
| `MU_SPAWN_LIVENESS_MS` | tmux only. Wait this long, then check the pane is alive and its scrollback has no startup error (auth failure, `command not found`). Failure rolls back the row and throws `AgentDiedOnSpawnError` or `AgentSpawnStartupError`. | `1500` |
|
|
32
|
+
| `MU_SPAWN_LIVENESS_MS` | tmux only. Wait this long, then check the pane is alive and its scrollback has no startup error (auth failure, `command not found`). Failure rolls back the row and throws `AgentDiedOnSpawnError` or `AgentSpawnStartupError`. A pi agent skips the wait: the pane is checked on each ctl handshake tick and scanned when it ends. | `1500` |
|
|
33
33
|
| `MU_SPAWN_LOCK_TIMEOUT_MS` | How long a spawn waits for the per-workstream spawn lock. | `15000` |
|
|
34
34
|
|
|
35
35
|
## Send and state (tmux path)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mu-crew/mu",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "A persistent, observable crew of pi agents running in one tmux or herdr session per workstream, coordinated through a built-in task DAG.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
package/skills/mu/SKILL.md
CHANGED
|
@@ -69,7 +69,8 @@ or inspection.
|
|
|
69
69
|
|
|
70
70
|
For one-shot work inside pi, call `mu_delegate` (installed by `mu link pi`).
|
|
71
71
|
Outside pi: spawn into the reserved `scratch` workstream (no task DAG,
|
|
72
|
-
auto-created), `send --fresh`, then `mu agent wait --json`
|
|
72
|
+
auto-created), `send --fresh --json`, then `mu agent wait --after-runs <its runs> --json`
|
|
73
|
+
(`lastText`); without `--after-runs` a run that ends first hangs the wait.
|
|
73
74
|
Recipes call either form a **delegate call**.
|
|
74
75
|
|
|
75
76
|
- `mu agent wait <names...> --first` waits for busy → idle instead of a
|
|
@@ -78,8 +79,10 @@ Recipes call either form a **delegate call**.
|
|
|
78
79
|
- One agent per independent unit; `--workspace` for any helper that may edit,
|
|
79
80
|
build, or test the shared repo.
|
|
80
81
|
|
|
81
|
-
A helper
|
|
82
|
-
|
|
82
|
+
A pi helper showing `unknown (ctl missing)` right after spawn (spawn
|
|
83
|
+
warns after 30 s) likely sits at pi's project trust prompt (through a
|
|
84
|
+
wrapper whose argv0 is not `pi`/`pi-meta` it shows `needs_input`): add
|
|
85
|
+
`--approve` to `MU_<CLI>_COMMAND`. Move off
|
|
83
86
|
`scratch` when work gains dependencies or review gates.
|
|
84
87
|
|
|
85
88
|
## Mental model
|
|
@@ -67,8 +67,8 @@ const sh = async (c) => (await tools.bash({ command: c })).output;
|
|
|
67
67
|
const rows = await Promise.all(findings.map(async (id, i) => {
|
|
68
68
|
const a = `refuter-${i + 1}`;
|
|
69
69
|
await sh(`mu agent spawn ${a} -w scratch`);
|
|
70
|
-
await sh(`mu agent send ${a} -w scratch --fresh '<brief for ${id}: ... end with VERDICT: ...>'`);
|
|
71
|
-
const w = JSON.parse(await sh(`mu agent wait ${a} -w scratch --json --timeout 1200`));
|
|
70
|
+
const { runs } = JSON.parse(await sh(`mu agent send ${a} -w scratch --fresh --json '<brief for ${id}: ... end with VERDICT: ...>'`));
|
|
71
|
+
const w = JSON.parse(await sh(`mu agent wait ${a} -w scratch --after-runs ${runs} --json --timeout 1200`));
|
|
72
72
|
const text = w.agents?.[0]?.lastText ?? "";
|
|
73
73
|
await sh(`mu agent close ${a} -w scratch`);
|
|
74
74
|
return `${id}\t${(text.match(/VERDICT:.*$/m) ?? ["VERDICT: UNVERIFIED no verdict line"])[0]}`;
|
|
@@ -54,12 +54,16 @@ keeps it.
|
|
|
54
54
|
Use `--first --on-stall exit`: `--first` populates `.firing`, and
|
|
55
55
|
`--on-stall exit` stops an unattended wait when a worker needs attention.
|
|
56
56
|
Exit 6: the owner's pane died and the reaper reopened the task; exit 7:
|
|
57
|
-
the owner sat in `needs_input`.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
the owner sat in `needs_input`. Run the exit's `Next:` and answer: the
|
|
58
|
+
worker may be waiting on you. For a pi owner it leads with
|
|
59
|
+
`mu agent wait <owner> --after-runs <runs-1> --json`, which returns the
|
|
60
|
+
last answer at once. The pane read follows because dialogs and crashes
|
|
61
|
+
show only in the pane.
|
|
62
|
+
|
|
63
|
+
For an idle worker, read its last answer (the same `--after-runs`
|
|
64
|
+
wait for pi, else the pane), then answer, retry, or release its task.
|
|
65
|
+
`MU_IDLE_THRESHOLD_MS` defaults to 5m. A remote owner: for pi,
|
|
66
|
+
`mu agent wait --after-runs` first, then poll once; otherwise poll once
|
|
63
67
|
per turn ([remote-workers](remote-workers.md#step-5-poll-once-per-turn)).
|
|
64
68
|
|
|
65
69
|
## Merging
|
|
@@ -44,7 +44,8 @@ mu agent spawn worker-1 -w big --command \
|
|
|
44
44
|
mu task claim t1 -w big --for worker-1 --evidence 'remote on dev'
|
|
45
45
|
mu agent send worker-1 -w big --fresh '...'
|
|
46
46
|
|
|
47
|
-
# 5. WAIT:
|
|
47
|
+
# 5. WAIT: pi: mu agent wait worker-1 -w big --after-runs <send's runs> --json,
|
|
48
|
+
# then the claim's Next: once. non-pi: run the claim's Next: once per turn
|
|
48
49
|
|
|
49
50
|
# 6. COLLECT: fetch straight from the remote worktree
|
|
50
51
|
git fetch "ssh://dev/~/ws/worker-1" HEAD && git cherry-pick FETCH_HEAD
|
|
@@ -72,13 +73,13 @@ retry next turn, not progress.
|
|
|
72
73
|
|
|
73
74
|
### Step 3: the control-socket forward
|
|
74
75
|
|
|
75
|
-
Spawn reports `ctl ok` when all of these hold (`mu doctor` has a ctl
|
|
76
|
+
Spawn reports `ctl: ok` when all of these hold (`mu doctor` has a ctl
|
|
76
77
|
row per agent):
|
|
77
78
|
|
|
78
79
|
- The host has the mu extension: run `mu link pi` there.
|
|
79
80
|
- The ssh is direct. `$MU_SSH_ARGS` sets `ControlMaster=no
|
|
80
81
|
ControlPath=none`; a multiplexed ssh never binds the forward and spawn
|
|
81
|
-
reports `ctl missing`.
|
|
82
|
+
reports `ctl: missing`.
|
|
82
83
|
- sshd allows the forward. A refused forward kills the pane at spawn
|
|
83
84
|
(`ExitOnForwardFailure=yes`). Check `AllowStreamLocalForwarding`.
|
|
84
85
|
|
|
@@ -88,6 +89,16 @@ plus a 10 s cache.
|
|
|
88
89
|
|
|
89
90
|
### Step 5: poll once per turn
|
|
90
91
|
|
|
92
|
+
The claim's `Next:` mule poll is mandatory. It proves the commit is on
|
|
93
|
+
the host and closes the task. For a pi worker, first block on
|
|
94
|
+
`mu agent wait <name> --after-runs <runs> --json`, with `runs` from
|
|
95
|
+
`mu agent send --json`. It settles exactly over the forwarded socket.
|
|
96
|
+
Without `--after-runs`, a worker that is already idle blocks until
|
|
97
|
+
`--timeout`. If the worker may already be done, pass `<runs-1>`: the
|
|
98
|
+
wait then returns the last answer at once. Then run `Next:` once instead
|
|
99
|
+
of on every turn. On exit 5 (timeout), or on a wait that returns while
|
|
100
|
+
the worker asks a question, run `Next:` anyway.
|
|
101
|
+
|
|
91
102
|
- Never `sleep` in a tool call. Aborting the loop can leave remote work
|
|
92
103
|
and a capped channel running.
|
|
93
104
|
- Set a deadline before dispatch. At expiry, read the pane, then release
|
|
@@ -123,14 +134,20 @@ the final gate before the push that matters.
|
|
|
123
134
|
|
|
124
135
|
## Traps
|
|
125
136
|
|
|
126
|
-
### A spawn on an auth prompt looks healthy
|
|
137
|
+
### A non-pi spawn on an auth prompt looks healthy
|
|
127
138
|
|
|
128
139
|
With the channel busy, the attach ssh stopped at `Enter a passcode:`.
|
|
129
140
|
Spawn succeeded, the agent showed `needs_input`, and the prompt went
|
|
130
|
-
into the passcode field.
|
|
131
|
-
|
|
141
|
+
into the passcode field. This applies to non-pi CLIs and to
|
|
142
|
+
`--via mux`, which paste into the pane. Confirm such an agent got the
|
|
143
|
+
work (`mu agent read <name> -n 20`) before trusting it. Send nothing
|
|
132
144
|
sensitive to an unconfirmed pane.
|
|
133
145
|
|
|
146
|
+
A pi agent does not paste. Spawn reports `ctl: ok` only when pi
|
|
147
|
+
answers on the socket; otherwise it warns `ctl: missing` or
|
|
148
|
+
`ctl: refused`, and `mu agent send` fails instead of typing into the
|
|
149
|
+
pane.
|
|
150
|
+
|
|
134
151
|
### Use the host's real CLI command
|
|
135
152
|
|
|
136
153
|
If your local `$MU_PI_COMMAND` is a wrapper such as `pi-meta
|
|
@@ -12,10 +12,14 @@ tool (another harness, a script):
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
mu agent spawn refuter-1 -w scratch
|
|
15
|
-
mu agent send refuter-1 -w scratch --fresh '<brief>'
|
|
16
|
-
mu agent wait refuter-1 -w scratch --json # answer in lastText
|
|
15
|
+
runs=$(mu agent send refuter-1 -w scratch --fresh '<brief>' --json | jq .runs)
|
|
16
|
+
mu agent wait refuter-1 -w scratch --after-runs "$runs" --json # answer in lastText
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
`--after-runs` takes the run count the send reported, so a run that
|
|
20
|
+
finished before the wait started still counts. A plain wait would sit
|
|
21
|
+
until `--timeout`.
|
|
22
|
+
|
|
19
23
|
- **It starts empty.** The brief carries everything: the finding or
|
|
20
24
|
claim, file paths, the criteria, and the answer line to end with
|
|
21
25
|
(`VERDICT: ...`, `WINNER: ...`). See [brief](brief.md).
|