@phnx-labs/agents-cli 1.22.22 → 1.22.24

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 (138) hide show
  1. package/CHANGELOG.md +402 -0
  2. package/README.md +13 -6
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/attach.d.ts +2 -0
  5. package/dist/commands/attach.js +32 -7
  6. package/dist/commands/defaults.js +2 -0
  7. package/dist/commands/doctor.js +20 -7
  8. package/dist/commands/exec.js +100 -34
  9. package/dist/commands/feed.d.ts +18 -0
  10. package/dist/commands/feed.js +44 -1
  11. package/dist/commands/focus.d.ts +108 -5
  12. package/dist/commands/focus.js +455 -32
  13. package/dist/commands/fork.d.ts +20 -2
  14. package/dist/commands/fork.js +91 -64
  15. package/dist/commands/go.d.ts +39 -1
  16. package/dist/commands/go.js +112 -7
  17. package/dist/commands/harness-wizard.d.ts +206 -0
  18. package/dist/commands/harness-wizard.js +403 -0
  19. package/dist/commands/harness.d.ts +12 -0
  20. package/dist/commands/harness.js +97 -101
  21. package/dist/commands/insights.js +6 -2
  22. package/dist/commands/inspect.js +39 -5
  23. package/dist/commands/menubar.js +6 -1
  24. package/dist/commands/models.js +1 -0
  25. package/dist/commands/modes.d.ts +12 -0
  26. package/dist/commands/modes.js +147 -0
  27. package/dist/commands/resume.js +10 -3
  28. package/dist/commands/secrets-sync.js +11 -13
  29. package/dist/commands/secrets.d.ts +2 -0
  30. package/dist/commands/secrets.js +79 -47
  31. package/dist/commands/sessions-browser.d.ts +35 -0
  32. package/dist/commands/sessions-browser.js +140 -14
  33. package/dist/commands/sessions-resume.d.ts +23 -3
  34. package/dist/commands/sessions-resume.js +73 -19
  35. package/dist/commands/sessions.d.ts +91 -5
  36. package/dist/commands/sessions.js +359 -125
  37. package/dist/commands/setup-secrets.js +1 -1
  38. package/dist/commands/sync.js +246 -42
  39. package/dist/commands/view.js +2 -0
  40. package/dist/commands/watchdog.js +13 -2
  41. package/dist/index.js +2 -1
  42. package/dist/lib/agent-modes.d.ts +49 -0
  43. package/dist/lib/agent-modes.js +70 -0
  44. package/dist/lib/agents.d.ts +1 -1
  45. package/dist/lib/agents.js +132 -0
  46. package/dist/lib/codex-policy.d.ts +17 -0
  47. package/dist/lib/codex-policy.js +48 -0
  48. package/dist/lib/crabbox/lease.d.ts +25 -0
  49. package/dist/lib/crabbox/lease.js +62 -0
  50. package/dist/lib/daemon.js +70 -0
  51. package/dist/lib/doctor-diff.d.ts +3 -0
  52. package/dist/lib/doctor-diff.js +15 -13
  53. package/dist/lib/event-stream.d.ts +3 -1
  54. package/dist/lib/event-stream.js +14 -1
  55. package/dist/lib/exec.d.ts +16 -0
  56. package/dist/lib/exec.js +175 -62
  57. package/dist/lib/feed-broadcast.d.ts +1 -20
  58. package/dist/lib/feed-broadcast.js +31 -1
  59. package/dist/lib/hooks/cache.js +36 -3
  60. package/dist/lib/hooks.d.ts +19 -7
  61. package/dist/lib/hooks.js +100 -40
  62. package/dist/lib/hosts/session-index.d.ts +4 -0
  63. package/dist/lib/hosts/session-index.js +7 -0
  64. package/dist/lib/manifest.d.ts +12 -2
  65. package/dist/lib/manifest.js +60 -5
  66. package/dist/lib/mcp.js +44 -0
  67. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  68. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  69. package/dist/lib/menubar/install-menubar.d.ts +52 -2
  70. package/dist/lib/menubar/install-menubar.js +128 -6
  71. package/dist/lib/models.d.ts +0 -5
  72. package/dist/lib/models.js +48 -0
  73. package/dist/lib/plugin-marketplace.js +9 -0
  74. package/dist/lib/pricing/prices.json +119 -92
  75. package/dist/lib/pricing/table.js +13 -0
  76. package/dist/lib/refresh.d.ts +5 -0
  77. package/dist/lib/refresh.js +37 -33
  78. package/dist/lib/remote-agents-json.d.ts +29 -1
  79. package/dist/lib/remote-agents-json.js +47 -10
  80. package/dist/lib/resource-inventory.d.ts +79 -0
  81. package/dist/lib/resource-inventory.js +122 -0
  82. package/dist/lib/resources/mcp.js +2 -0
  83. package/dist/lib/resources/permissions.js +3 -0
  84. package/dist/lib/resources/types.d.ts +2 -1
  85. package/dist/lib/resources.js +8 -5
  86. package/dist/lib/run-defaults.d.ts +2 -0
  87. package/dist/lib/run-defaults.js +23 -2
  88. package/dist/lib/runner.js +50 -36
  89. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  90. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  91. package/dist/lib/secrets/bundles.js +157 -65
  92. package/dist/lib/secrets/filestore.d.ts +5 -3
  93. package/dist/lib/secrets/filestore.js +12 -8
  94. package/dist/lib/secrets/index.d.ts +15 -1
  95. package/dist/lib/secrets/index.js +148 -53
  96. package/dist/lib/secrets/reaper.d.ts +97 -0
  97. package/dist/lib/secrets/reaper.js +219 -0
  98. package/dist/lib/secrets/remote.d.ts +29 -0
  99. package/dist/lib/secrets/remote.js +37 -1
  100. package/dist/lib/secrets/sync-passphrase.d.ts +27 -0
  101. package/dist/lib/secrets/sync-passphrase.js +78 -0
  102. package/dist/lib/session/active.d.ts +36 -1
  103. package/dist/lib/session/active.js +60 -19
  104. package/dist/lib/session/actor-sidecar.d.ts +14 -0
  105. package/dist/lib/session/actor-sidecar.js +67 -4
  106. package/dist/lib/session/db.d.ts +1 -1
  107. package/dist/lib/session/db.js +32 -1
  108. package/dist/lib/session/discover.js +168 -0
  109. package/dist/lib/session/parse.d.ts +10 -0
  110. package/dist/lib/session/parse.js +98 -0
  111. package/dist/lib/session/recovery.d.ts +37 -0
  112. package/dist/lib/session/recovery.js +95 -0
  113. package/dist/lib/session/remote-list.d.ts +10 -1
  114. package/dist/lib/session/remote-list.js +2 -8
  115. package/dist/lib/session/remote.d.ts +57 -3
  116. package/dist/lib/session/remote.js +90 -26
  117. package/dist/lib/session/resume-command.d.ts +6 -0
  118. package/dist/lib/session/resume-command.js +8 -0
  119. package/dist/lib/session/session-cache.d.ts +173 -0
  120. package/dist/lib/session/session-cache.js +399 -0
  121. package/dist/lib/session/types.d.ts +1 -1
  122. package/dist/lib/session/types.js +1 -1
  123. package/dist/lib/session/width.d.ts +1 -1
  124. package/dist/lib/session/width.js +12 -2
  125. package/dist/lib/shims.d.ts +2 -2
  126. package/dist/lib/shims.js +62 -8
  127. package/dist/lib/staleness/writers/hooks.js +1 -1
  128. package/dist/lib/startup/command-registry.d.ts +1 -0
  129. package/dist/lib/startup/command-registry.js +2 -0
  130. package/dist/lib/sync-umbrella.d.ts +5 -0
  131. package/dist/lib/sync-umbrella.js +5 -4
  132. package/dist/lib/tmux/session.d.ts +4 -2
  133. package/dist/lib/tmux/session.js +5 -5
  134. package/dist/lib/types.d.ts +3 -1
  135. package/dist/lib/usage.d.ts +13 -0
  136. package/dist/lib/usage.js +215 -0
  137. package/dist/lib/versions.js +13 -2
  138. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,407 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.24
4
+
5
+ - **`agents sessions focus` recovers dead panes and shares the sessions browser's selectors (GH-2108).** A retained tmux `remain-on-exit` pane is probed through `#{pane_dead}` immediately before attach, so dead or missing panes no longer open a `Pane is dead` screen. `focus` accepts session ids, topic/path searches, `agent@version` selectors (including per-device `latest`/`oldest`), device, project/time, team/routine, skill/plugin, favorites, and the complete live-state union. Focus, resume, attach, and `run --resume` now use one recovery decision on the origin device: a healthy exact origin performs native resume; otherwise balanced selection chooses a healthy version of the same harness and sends `/continue <id>` to read the indexed transcript, including transcripts retained under version trash. Host-dispatched rows persist the dispatch host as their origin, and `attach` routes its detach-record cleanup there before resuming. No usable same-harness version fails with the device, origin version, and account-health reason. Source: `apps/cli/src/commands/focus.ts`, `apps/cli/src/commands/sessions-browser.ts`, `apps/cli/src/lib/session/recovery.ts`.
6
+
7
+ - **`agents secrets setup` no longer tells you to set `AGENTS_SECRETS_PASSPHRASE`, and
8
+ `docs/secrets.md` stops recommending the shell-rc export it flags as a leak
9
+ (RUSH-1968).** The file-backend note read `set AGENTS_SECRETS_PASSPHRASE for headless
10
+ encrypted-file reads`, implying a requirement; headless reads have worked with no
11
+ passphrase since the store began auto-provisioning a 0600 machine-local key, so it now
12
+ says so and names the real path. The docs were worse than merely stale: they called an
13
+ rc export *"Recommended for shared/CI machines"* and the 0600 key file *"identical to"*
14
+ it, which is how a master key ended up in `~/.zshenv` on seven worker boxes. That
15
+ equivalence is inverted — the key file is read by one process, an rc export is inherited
16
+ by every child and readable from `/proc/<pid>/environ` — and the section also named a
17
+ pre-#479 key path (`~/.agents/.cache/secrets/.passphrase`, now
18
+ `~/.agents/.secrets-key/passphrase`) and a TTY prompt step `getPassphrase` no longer
19
+ has. A new `docs-hygiene.test.ts` pins those claims against the shipped doc so the
20
+ advice cannot drift back. Source: `apps/cli/docs/secrets.md`,
21
+ `apps/cli/src/commands/setup-secrets.ts`, `apps/cli/src/lib/secrets/filestore.ts`.
22
+
23
+ - **`agents secrets push`/`pull`, `agents sync --secrets`, and `agents secrets
24
+ export --to-file` / `import --from-file` read `AGENTS_SYNC_PASSPHRASE` now;
25
+ `AGENTS_SECRETS_PASSPHRASE` is the file store's master key and nothing else
26
+ (RUSH-1968).** One variable meant two different secrets: the local file store's master
27
+ key, and the passphrase that seals a bundle for transport. The store stopped needing a
28
+ passphrase once it auto-provisioned a machine-local key, but headless `push`/`pull`
29
+ still hard-failed without one — so the only way to get unattended sync on a worker box
30
+ was to export the **master key** fleet-wide, handing every same-user process the key to
31
+ the whole store. Splitting them means a box that only needs headless sync sets
32
+ `AGENTS_SYNC_PASSPHRASE` and never has the master key in its environment. The old name
33
+ still works for sync as a deprecated fallback — warned exactly once per process, so a
34
+ `push --all` over many bundles does not flood stderr — so scripted CI and release
35
+ automation keep working across the upgrade. The headless error now names the new
36
+ variable (`A sync passphrase is required. Run from a TTY, or set
37
+ AGENTS_SYNC_PASSPHRASE.`), and `agents sync`'s skip line with it — it previously read
38
+ `no passphrase available`, naming nothing an operator could act on. Note the legacy
39
+ fallback only works where that value is also the store's master key, since the old name
40
+ still keys the store; that coupling is the thing being retired. Resolution moved to
41
+ one chokepoint so the once-per-process promise holds rather than being per-call-site. Also
42
+ corrects `SEC-29a`, which claimed the variable applied "exclusively" to the file and
43
+ age-vault backends: the age-vault backend never reads it (it is gated by `agents
44
+ login`), and sync plus the portable `--to-file` envelope were two more consumers — the
45
+ new `SEC-29b` states the split as a normative invariant. Source:
46
+ `apps/cli/src/lib/secrets/sync-passphrase.ts`, `apps/cli/src/commands/secrets-sync.ts`,
47
+ `apps/cli/src/commands/sync.ts`, `apps/cli/src/lib/sync-umbrella.ts`.
48
+
49
+ - Added `--team` as an alias for `agents sessions --teams`. (RUSH-1995)
50
+ - Added absolute `agents secrets unlock --until <date>` expiry, mutually exclusive with relative `--ttl`. (RUSH-1960)
51
+ - Added persisted per-agent-version reasoning effort defaults and surfaced them in `agents view`. (RUSH-2005)
52
+
53
+ - **`agents feed` no longer crashes when the session index is locked (RUSH-2006).**
54
+ Outcome enrichment calls `discoverSessions` against `sessions.db`; under concurrent
55
+ scanner/daemon pressure that open can throw `SQLITE_BUSY` / "database is locked"
56
+ and take down the whole feed. A lock error now degrades to an empty meta set with a
57
+ stderr warning so blocks still render. Source: `apps/cli/src/commands/feed.ts`.
58
+
59
+ - **`agents events --event` / filtered activity reads no longer miss matches under
60
+ `--limit` (RUSH-2093).** The unified reader capped activity records *before*
61
+ applying the event-type filter, so a rare match older than the newest-N routine
62
+ rows (e.g. one `pr.opened` under twenty `file.edited`) was silently dropped. Event
63
+ types are now passed into `readRecentActivity` so the cap counts matching rows; a
64
+ non-`activity` `--module` skips the activity half entirely. Source:
65
+ `apps/cli/src/lib/event-stream.ts`.
66
+
67
+ - **`agents mcp add` no longer strips comments from `agents.yaml`.** The
68
+ write path (`writeManifest` / `serializeManifest` in `src/lib/manifest.ts`)
69
+ used plain `yaml.stringify`, which dropped every hand-written comment on
70
+ every add. It now round-trips via `yaml.parseDocument` and edits only the
71
+ keys that changed — the same approach `serializeCentral` already uses for
72
+ the central meta file (RUSH-2090).
73
+
74
+ - **Hook cache refresh is now single-flight, backs off on failure, and logs its
75
+ real exit code (RUSH-2121).** The background refresh in the hook cache shim
76
+ acquires an atomic `mkdir` lock, so concurrent hook invocations no longer
77
+ stampede into N parallel refreshes; a persistently-failing refresh records a
78
+ failure timestamp and is skipped until a 60s backoff elapses instead of
79
+ re-firing on every invocation; and the refresh now emits a `hook.cache.refresh`
80
+ event carrying the subshell's real exit code instead of a hardcoded `0`.
81
+ Source: `apps/cli/src/lib/hooks/cache.ts`.
82
+
83
+ - **`agents sync --host all` no longer fails every peer with `unknown option
84
+ '--json'`.** Fleet fan-out injects `--json` on each remote so the roster can
85
+ parse per-device results, but `sync` never registered the flag. Register
86
+ `--json` on `agents sync` and emit a machine-readable umbrella/agent/repo
87
+ payload so peers accept the flag and return parseable stdout (RUSH-2216).
88
+ Source: `apps/cli/src/commands/sync.ts`.
89
+
90
+ - **A wedged keychain can no longer pile up `agents` processes (RUSH-2231, RUSH-2232).**
91
+ A stalled macOS `coreauthd` used to hang the signed keychain helper's XPC receive
92
+ forever, so every secrets-touching `agents` command blocked and dozens of helper
93
+ processes plus their `<defunct>` zombies accumulated and made the machine sluggish.
94
+ Two fixes: **(Layer 1)** every keychain-helper `spawnSync` is now bounded and
95
+ hard-killed (SIGKILL) on timeout — 8s for never-prompt verbs (`has`/`set`/`delete`/
96
+ `list*`), 60s for the may-prompt reads (`get`/`get-batch`/`migrate-*`) — surfacing a
97
+ typed timeout error and arming the read back-off instead of hanging.
98
+ **(Layer 3)** the daemon reaps the backlog: a 5-minute tick kills orphaned helpers
99
+ (reparented to PID 1, past a 30s grace) and, two-sweep-debounced, the helper child of
100
+ an `agents` proc stuck past 90s (child first, escalating to the parent only if it
101
+ stays wedged) — never touching a process whose start-time can't be captured or whose
102
+ path doesn't match the helper. Any keychain touch now also opportunistically starts
103
+ the daemon so the reaper runs even on a secrets-only box. Source:
104
+ `apps/cli/src/lib/secrets/index.ts`, `apps/cli/src/lib/secrets/reaper.ts`,
105
+ `apps/cli/src/lib/daemon.ts`.
106
+
107
+ - **`agents inspect` and `agents doctor` now report harness-scoped hook inventory as capable, on-disk, wired, and unmanaged state.** Their JSON output carries the same state sets from `getResourceInventory`, so a harness with hook files or native wiring no longer collapses to a misleading `Hooks (0)` summary. Inventory is keyed by agent harness and installed version, not by configured model. Source: `apps/cli/src/lib/resource-inventory.ts`, `apps/cli/src/commands/inspect.ts`, `apps/cli/src/lib/doctor-diff.ts`.
108
+
109
+ - **Fix: the keychain-helper reaper never reaped on macOS.** The daemon reaper shelled `ps -o etimes` — a GNU/Linux procps keyword that macOS `ps` rejects with a non-zero exit — so `execFileSync` threw on every tick and `reapOrphanedKeychainProcesses` returned `reaped: 0` on its only supported platform, leaving orphaned/wedged `Agents CLI` helper processes to pile up. Switched to the portable BSD `etime` keyword (`[[dd-]hh:]mm:ss`) with a parser to seconds. This also un-quarantines the darwin integration test that #2153 had to `it.skip` to unblock releases: that test's symlink fixture was correct all along — it read `reaped: 0` only because the reaper's own `ps etimes` call threw before parsing anything. Verified on macOS 15.4.1: 20/20 reaper tests pass, the integration test reaping a real orphaned sleeper. Source: `apps/cli/src/lib/secrets/reaper.ts`, `reaper.test.ts`.
110
+
111
+ - **`agents modes [agent[@version]]` lists the permission modes a harness accepts.** The modes analog of `agents models`: for Claude / Codex / Cursor / …, shows which `--mode plan|edit|auto|skip` values work, the native CLI flags, the native default (`*`), any configured `run.defaults` mode, and degrade notes (e.g. plan→edit on Cursor). `agents inspect` also prints the mode list next to capabilities, and `agents run --help` / `agents models` point at the new command. Source: `apps/cli/src/commands/modes.ts`, `apps/cli/src/lib/agent-modes.ts`.
112
+
113
+ - **`agents insights --all` now works as an alias for `--since all`.** It previously
114
+ exited with `unknown option '--all'` — a hard failure in the middle of a report the
115
+ user asked for, on the spelling most people reach for first. An explicit `--since`
116
+ still wins, so `--all --since 7d` resolves to 7d rather than silently contradicting
117
+ itself. Source: `apps/cli/src/commands/insights.ts`.
118
+
119
+ - **Coexisting agents-cli installs no longer fight over the menu-bar helper.** The
120
+ helper lives at one path in Application Support, but every install on the box runs
121
+ the startup self-heal, and both the version stamp and the plist's baked
122
+ `AGENTS_ENTRY` record whichever copy acted last — so each copy read the others'
123
+ marks as drift and recopied the app bundle over them. Recopying replaces the
124
+ executable under the running helper and kills it, launchd `KeepAlive` restarts it,
125
+ and the next copy repeats it: a new pid every 5-15 seconds, 578 launches in one
126
+ observed helper log, and a status item that never stayed visible while
127
+ `agents menubar status` still reported `running: yes` (a pid always existed). The
128
+ plist's `AGENTS_ENTRY` is now treated as the owner and only the owner reinstalls
129
+ freely; a same-install upgrade keeps its entry path, so `npm update` still installs
130
+ the new helper normally. Another install still gets there — immediately if the
131
+ recorded owner is gone from disk, otherwise at most once an hour — so a stale copy
132
+ that merely still sits on disk can't freeze the menu bar for whichever install the
133
+ user actually upgrades. Repairs (a missing helper executable, a Developer-ID heal)
134
+ are never gated, and `agents menubar setup` bypasses the gate as the immediate
135
+ manual fix. An ad-hoc/dev-signed build never wins the timed takeover — recopying
136
+ an un-notarized bundle over a good one gets it rejected as "damaged" — though it
137
+ can still adopt a helper whose owner is gone. Two installs that are both invoked
138
+ regularly still trade ownership at the cooldown, so the helper restarts about once
139
+ an hour until one is removed; that is bounded rather than converged, and the real
140
+ fix remains a single install. `agents menubar status` no longer promises that a
141
+ stale helper "runs on next `agents` startup", which is not guaranteed on a
142
+ multi-install box. Fixes #2109. Source:
143
+ `apps/cli/src/lib/menubar/install-menubar.ts`.
144
+
145
+ - **Muse Code global binary is visible to `agents view` / `isVersionInstalled`.**
146
+ Muse installs a single self-updating launcher at `~/.local/bin/muse` (same
147
+ shape as droid), but `getBinaryPath` still resolved a version-home
148
+ `node_modules/.bin/muse` that never exists. After `agents import muse` /
149
+ `agents add muse`, version dirs now resolve to that global path so managed
150
+ view, collapse, and live-version bookkeeping match what actually executes;
151
+ install no longer writes a self-referential shim symlink. Source:
152
+ `apps/cli/src/lib/versions.ts`.
153
+
154
+ - **Muse multi-version isolation matches Claude/Codex (XDG pin, not bare symlink).**
155
+ Claude/Codex isolate per version with `CLAUDE_CONFIG_DIR` / `CODEX_HOME`.
156
+ Muse has no dedicated config env; it reads `$XDG_CONFIG_HOME/muse` and
157
+ `$XDG_DATA_HOME/muse`. After `agents import muse`, `~/.config/muse` is a
158
+ symlink into the version home — Muse refuses that path with
159
+ `Agent Definition filesystem source failed: SymlinkOrReparse` and exits 1
160
+ (the Zion `agents run muse@0.1.0` failure). Managed launches now pin
161
+ `XDG_CONFIG_HOME` + `XDG_DATA_HOME` into the version home from `buildExecEnv`,
162
+ the main shim, and versioned aliases (`muse@0.1.0`), the same way Claude pins
163
+ `CLAUDE_CONFIG_DIR`. Muse is also listed in `CONFIG_ENV_ISOLATED_AGENTS` so
164
+ `--isolated` installs are honest. Source: `apps/cli/src/lib/{exec,shims}.ts`.
165
+
166
+ - **Scheduled routines now run on a per-account `claude setup-token` when one is
167
+ provisioned, instead of throwing it away.** `buildRoutineSpawnEnv` unconditionally
168
+ deleted `CLAUDE_CODE_OAUTH_TOKEN`, so even after `buildExecEnv` injected a long-lived,
169
+ non-rotating setup-token from the reserved file-backed `auth` bundle
170
+ (`resolveClaudeSetupToken`), a routine still fell back to the version home's rotating
171
+ `.credentials.json` login. With one Claude account signed into several version homes
172
+ (or several fleet boxes), that rotating login is the single-use-refresh-token
173
+ revocation storm — one home's refresh silently revokes every sibling copy, so an
174
+ unattended routine keeps landing on a just-revoked token and dies with `auth_failed:
175
+ OAuth access token has been revoked` / `Please run /login`, even though `agents view`
176
+ shows the account healthy. The delete now distinguishes the two flavours: a
177
+ per-account setup-token (keyed to this home's own account) is re-asserted and KEPT; an
178
+ inherited *ambient* token (a shared value the daemon env happened to carry — the
179
+ RUSH-1822 fleet-logout path) is still stripped so a routine never runs on it.
180
+ Provision the token with `/fleet:mint-auth`. Source:
181
+ `apps/cli/src/lib/runner.ts` (`buildRoutineSpawnEnv`).
182
+
183
+ - **Resolved secrets no longer appear in `ps` for tmux-launched agents
184
+ (RUSH-2100).** An interactive `agents run` wraps the agent in tmux via
185
+ `exec env K=V … <agent>`, which put the entire exec env — every resolved
186
+ secrets-bundle value — into the pane's command line, readable by any process of
187
+ the same user. On one fleet box six live processes carried
188
+ `AGENTS_SECRETS_PASSPHRASE`, the key that decrypts every file-backed bundle on
189
+ that machine. The pane now sources a `0600` env file and unlinks it before
190
+ `exec`, so only the file path is argv-visible; a missing file aborts the pane
191
+ rather than launching half-configured. Every key routes through the file, not a
192
+ curated "secret-bearing" subset, so a newly added credential is covered without
193
+ anyone maintaining a list. Source: `apps/cli/src/lib/exec.ts`,
194
+ `apps/cli/docs/specifications.md` (SEC-8a).
195
+
196
+ ## 1.22.23
197
+
198
+ - **Daemon-warmed cross-surface session-status cache (RUSH-2062).** Menubar,
199
+ Factory, watchdog, and CLI used to each re-run a full `sessions --active`
200
+ gather (~9s / ~170MB) with no sharing. The daemon now warms a local active-
201
+ session snapshot every 15s; those surfaces read the warm file when fresh.
202
+ `session/remote.ts` is cache-first too — a reachable host skips SSH while its
203
+ cache is within the freshness window (it used to replay only on
204
+ unreachable). Immutable per-session fields (topic, label, cwd, …) are memoized
205
+ by transcript mtime; live status never rides that memo. Source:
206
+ `apps/cli/src/lib/session/session-cache.ts`, `apps/cli/src/lib/session/remote.ts`,
207
+ `apps/cli/src/lib/daemon.ts`.
208
+
209
+ - **`agents sessions <id>` / `agents resume <id>` resolve across the fleet fast, with
210
+ early-exit and SSH cancellation (RUSH-2203).** The cross-machine resolve fanned out
211
+ with `await Promise.all` and no cancellation, so one slow or unreachable peer stalled
212
+ the whole lookup to the 12s per-peer timeout even after a fast peer already returned
213
+ the exact match — a full-UUID resolve on a fleet with an offline box took ~16-40s. A
214
+ **full-UUID** resolve (globally unique) now opts into a cancellable, early-exit
215
+ fan-out: the first peer holding it resolves the sweep and SIGTERMs every
216
+ still-outstanding peer, and a local UUID hit resolves with **zero** SSH.
217
+ `agents resume <label>` auto-resumes the one exact-label match; labels are not globally
218
+ unique, so they stay all-settle (a same-label session may live on another peer) — a
219
+ unique label resumes, a cross-machine collision surfaces as an ambiguity. The not-found
220
+ message reports the actual sweep result (devices searched, which were unreachable)
221
+ instead of the misleading "search with `--device <host>`" — fleet search is already the
222
+ default. `--device all` / `--devices` are accepted and mean "the whole fleet". The
223
+ all-settle default is unchanged for the shared fan-out's other callers (tool-search,
224
+ program-count) and for labels / ambiguous short-id prefixes, which still wait for every
225
+ peer. Source:
226
+ `apps/cli/src/lib/remote-agents-json.ts`, `apps/cli/src/lib/session/remote-list.ts`,
227
+ `apps/cli/src/commands/sessions.ts`, `apps/cli/src/commands/resume.ts`.
228
+
229
+ - **`agents sessions focus` gains device + live-state scoping and multi-select tabs
230
+ (RUSH-2206).** `focus` now honors `--device/--host <name>` (scope the picker to a
231
+ device's live sessions) and the same live-state filters `sessions --active` defines
232
+ (`--orphan`/`--crashed`/`--waiting`/`--idle`/`--working`/`--closed`/`--abandoned`/
233
+ `--queued`/`--unknown`) — they compose, so `agents sessions focus --orphan --device
234
+ yosemite-s0` lists only that host's orphans. The interactive picker is now
235
+ multi-select: check several sessions and each opens as a new tab in the terminal
236
+ you're in (Ghostty / iTerm / tmux, auto-detected), reusing `resume`'s batch open and
237
+ flood guard. Per tab, live semantics hold — a tmux session is joined (a second
238
+ client, no fork), local or remote over SSH; a session with no attach rail resumes a
239
+ copy in the tab, reported never silently dropped. `focus <id>` direct-jump and
240
+ `--attach-only` (old `go`) behavior are unchanged. Source:
241
+ `apps/cli/src/commands/focus.ts`, `apps/cli/src/commands/go.ts`.
242
+
243
+ - **`agents harness edit` is now wizard-capable, on a shared step engine (RUSH-2219).**
244
+ Run `agents harness edit <name>` with no flags in a terminal and it walks each field
245
+ (model, endpoint, auth, version, fallback, description) pre-filled with the harness's
246
+ current values — where before it was flag-only and errored without one. Both `add`/`fork`
247
+ and `edit` now drive one shared step engine: every step is skippable by the matching flag,
248
+ so scripting stays non-interactive and a flagless non-interactive `edit` still errors as
249
+ before. Endpoint and version prompts are gated by the host's API format (a host with no
250
+ custom-endpoint slot, or one that self-updates, shows the field disabled with a reason
251
+ instead of silently accepting a value the run would drop). This is the foundation the
252
+ model catalog, connection test, edit matrix, and cross-host portability build on.
253
+ Source: `apps/cli/src/commands/harness-wizard.ts`, `apps/cli/src/commands/harness.ts`.
254
+
255
+ - **Codex launches now default to safe writable access instead of an offline
256
+ read-only sandbox.** When no configured run default exists, omitting `--mode`
257
+ selects a managed workspace profile with network access, on-request approvals,
258
+ `~/.agents`, and regenerable build caches. Explicit `--mode plan` remains
259
+ filesystem-read-only but now retains network access. The same policy covers
260
+ `agents run`, fallback attempts, resumes, routines, direct `codex` shims,
261
+ versioned aliases, and Windows shim passthrough; only explicit `skip` disables
262
+ the sandbox and approvals. Source: `apps/cli/src/lib/codex-policy.ts`.
263
+
264
+ - **Phone forwards are shaped to a text, not a wall.** `agents feed post`
265
+ (`--level important` / `--blocked`), `agents notify`, and `agents send --to owner`
266
+ all forward to the owner's phone through one seam, `composeBroadcastMessage`; it now
267
+ keeps the post **title** (the scannable headline) and truncates a long **body** to a
268
+ short excerpt marked `… (full in feed)` (caps: 500 chars / 8 lines). The full post is
269
+ untouched in the feed — only the outbound phone copy is shortened — so a requested
270
+ long write is not lost, and enforcing it at this seam covers every sink (owner alias,
271
+ in-process `channel:`, spawned `command:` via `{message}`), which a per-command shell
272
+ hook cannot. Source: `apps/cli/src/lib/feed-broadcast.ts` (`truncateBroadcastBody`).
273
+
274
+ - **`agents run --lease` self-cleans its warm pool.** Each lease now auto-stops **expired, idle** boxes in the run's profile pool (an expired box can never be reused, so it was pure cost the 1h-idle `agents lease gc` window left running). Conservative: only a box that is running, in the same profile+netMode pool, past its lease expiry, and untouched for a grace window is stopped — a mid-boot or in-use box is never touched. Skipped when an explicit `--box <slug>` is named. Source: `apps/cli/src/lib/crabbox/lease.ts`.
275
+
276
+ - **Muse Code (Meta) harness support.** `muse` is a first-class agent: install via
277
+ `curl -fsSL https://dev.meta.ai/install.sh | sh`, run with `agents run muse`,
278
+ and use in teams. Modes map to Muse safety flags (`--disable-write` /
279
+ `--disable-approval` / `--yolo`); `--model` and `--reasoning-effort` are
280
+ forwarded; headless is `muse exec` with `--json`; interactive resume is
281
+ `muse resume <id>` (id immediately after the verb), headless resume is
282
+ `--session-id`. Sessions under `~/.local/share/muse/sessions/` (plus version
283
+ homes) are discovered and parsed; usage shows Meta Model API rate limits when
284
+ a key is present, otherwise local 7-day token totals. MCP writes to
285
+ `~/.config/muse/settings.json` (`mcp_servers`, `schema_version: 1`). Model
286
+ catalog: `muse-spark-1.2` (default), `1.1`, `1.2-contributor` with offline
287
+ pricing. Aliases: `muse-code`, `meta-muse`. Hooks write Claude-shaped
288
+ matcher groups into `~/.config/muse/settings.json` (`schema_version: 1`);
289
+ plugins use the Claude marketplace layout under the XDG data plugin store
290
+ (`~/.local/share/muse/plugins`) with `.muse-plugin` manifests. Allowlist
291
+ stays false (Muse uses approval-mode + sandbox, not tool-name allow/deny).
292
+ Source: `apps/cli/src/lib/{agents,exec,models,usage,mcp,hooks,plugins,
293
+ plugin-marketplace,runner,shims}.ts`, `apps/cli/src/lib/session/*`.
294
+
295
+ - **Release CI no longer re-runs the six-job cross-platform matrix on `v*` tags.**
296
+ The expensive `ci.yml` matrix (ubuntu + macOS + Windows × Node 22/24) still
297
+ runs on `release/**` branch pushes and manual `workflow_dispatch`, but not when
298
+ a version tag is pushed. `release.sh` tags the exact commit that already passed
299
+ the release-branch matrix, so the post-tag matrix was pure cost. Source:
300
+ `.github/workflows/ci.yml`.
301
+
302
+ - **`claude-opus-5` and `claude-sonnet-5` were unpriced, so every session using them
303
+ cost $0.** The pricing table carried `claude-opus-4`, `claude-sonnet-4`,
304
+ `claude-fable-5` and `claude-mythos-5` but not the Opus/Sonnet 5 line. Matching is
305
+ dash-bounded (`getModelPricing`), so `claude-opus-5` cannot fall back to the
306
+ `claude-opus-4` entry — it resolved to null, and an unpriced model contributes
307
+ nothing rather than erroring. On one real index that silently zeroed **526 sessions**,
308
+ 478 of them the current default model, understating `agents cost`, `agents output`
309
+ and `agents insights` alike. Rates from the published table: Opus 5 $5/$25 per MTok
310
+ (cache write $6.25, cache read $0.50); Sonnet 5 $2/$10 (cache write $2.50, cache read
311
+ $0.20). Source: `apps/cli/src/lib/pricing/prices.json`.
312
+
313
+ - **Schema v34 reprices the sessions that were zeroed.** Adding prices alone fixes
314
+ nothing already indexed: `cost_usd` is computed at scan time and the scanner skips any
315
+ transcript whose `(file_mtime_ms, file_size)` is unchanged, so those rows would keep
316
+ their NULL forever. They cannot be repaired in place either — the row stores
317
+ `token_count` and `output_tokens` but not the uncached-input / cache-read / cache-write
318
+ split the price table needs. So v34 flushes `scan_ledger`, the same remedy v5 → v6 used
319
+ for this exact column when cost was introduced. One slower scan, then correct numbers.
320
+ Source: `apps/cli/src/lib/session/db.ts`.
321
+
322
+ **Sonnet 5's $2/$10 is introductory pricing that ends 2026-08-31.** From 2026-09-01 the
323
+ standard rate is $3/$15. The table holds one current rate per model with no notion of an
324
+ effective date, so that entry must be updated then or Sonnet 5 spend reads ~33% low.
325
+ Called out at the top of `table.ts`.
326
+
327
+ - **`agents sessions --orphan`/`--active`: cross-harness live discovery + richer rows (RUSH-2205).**
328
+ Two fixes. (1) The headless `ps`-scan recognized only 6 agent executables, so a
329
+ bare-headless `grok`/`kimi`/`antigravity`/`openclaw`/`hermes`/`rush` run was
330
+ silently dropped from the live views; the comm→kind map is now derived from
331
+ `SESSION_AGENTS` × the AGENTS registry (`cliCommand`), so every discoverable
332
+ harness surfaces. (2) Each live row now shows the agent **version** and a human
333
+ **created · idle** time cell, and backfills the **ticket/PR/label** from the
334
+ indexed session history onto orphan rows that lack them, with the label/topic on
335
+ its own line — grouped-by-directory layout unchanged, rows stay width-safe.
336
+ Source: `apps/cli/src/lib/session/active.ts`, `apps/cli/src/commands/sessions.ts`.
337
+
338
+ - **`agents doctor --check` verdict lines now carry a `check:` prefix and a total
339
+ version count**, so CI logs and log-scrapers get one consistent, grep-alike shape
340
+ whether the result is clean or drifted: `check: ok — 3 version(s) in sync` /
341
+ `check: drift — 2 stale, 1 never-synced across 3 version(s)`. The per-version
342
+ status badge for a never-synced version now reads `never-synced` (its real
343
+ status) instead of the unrelated `cold` label, and all three badges
344
+ (`stale`/`never-synced`/`unwired`) share one fixed-width column so the rows
345
+ align. Source: `apps/cli/src/commands/doctor.ts` (`runCheckGate`).
346
+
347
+ - **`agents secrets export --host --remote-backend file` no longer requires
348
+ `AGENTS_SECRETS_PASSPHRASE`.** Since the file store became passphrase-free
349
+ (auto-provisioning a 0600 machine-local key under `~/.agents/.secrets-key/`), the
350
+ remote `import --backend file` reads headlessly under that key with no passphrase
351
+ and no Touch ID — but the export path still hard-failed unless a passphrase was
352
+ set, and forcing one made the remote bundle require that shared passphrase to
353
+ read, defeating the headless use it was for. The passphrase is now **optional**:
354
+ with none set, the remote command carries no `AGENTS_SECRETS_PASSPHRASE`
355
+ read/export prologue and the .env is the only stdin, so the remote keys the bundle
356
+ under its own machine-local key (headless reads); set `AGENTS_SECRETS_PASSPHRASE`
357
+ locally only to opt into a shared off-disk key, which is still forwarded over ssh
358
+ stdin (never argv). This unblocks hands-off Linux-driven releases that provision
359
+ `apple.com` on a headless macOS sign host. Windows targets are still refused
360
+ cleanly. Source: `apps/cli/src/lib/secrets/remote.ts`,
361
+ `apps/cli/src/commands/secrets.ts`.
362
+
363
+ - **Docs: the per-command secrets Touch-ID contract is now written down and
364
+ accurate.** The `view --reveal` (1.22.14) and `exec` (1.22.21) interactive-unlock
365
+ changes shipped in code + CHANGELOG only; the reference docs still implied every
366
+ value command behaves the same. `docs/secrets.md` and the `secrets` skill now
367
+ carry an explicit Touch-ID matrix (deliberate `view --reveal`/`exec` at a real
368
+ terminal → one sheet on a locked bundle; `get`/`export` automation primitives →
369
+ never prompt, fail closed to `agents secrets unlock`; anything an agent launches →
370
+ broker-only), `specifications.md` adds **SEC-13b** + a `Prompts?` column on the
371
+ materialization table + `GWT-S2b`, and the skill's stale hold-window facts are
372
+ corrected (7-day default, screen-lock does not drop the hold). Docs-only; no
373
+ behavior change. Source: `apps/cli/docs/secrets.md`,
374
+ `apps/cli/docs/specifications.md`, `skills/secrets/SKILL.md`.
375
+
376
+ - **Resume session identities through one live-first path across the fleet.**
377
+ `agents sessions resume <selector>` accepts a full UUID, unique UUID prefix,
378
+ durable tmux name (`ag-codex-c1f3d813`), or unique alias suffix (`c1f3d813`). It
379
+ resolves the owning device, rechecks whether the process and pane are alive,
380
+ attaches a live pane, and otherwise resumes the harness-native conversation with
381
+ its recorded version, cwd, and mode. Retained dead tmux panes no longer count as
382
+ attachable, and native resume no longer collides with an existing live wrapper.
383
+ Source: `apps/cli/src/commands/focus.ts`,
384
+ `apps/cli/src/commands/sessions-resume.ts`, `apps/cli/src/lib/exec.ts`.
385
+
386
+ - **`agents fork` is now `agents sessions fork`.** Forking is a session operation, so
387
+ it lives under the `sessions` group next to `resume`/`focus` — `agents sessions fork
388
+ <id>` branches a conversation into a new, independent copy you can continue separately,
389
+ leaving the original untouched. The old top-level `agents fork` keeps working as a
390
+ hidden alias, so nothing that scripted it breaks. For a harness without a native copy
391
+ (anything but Claude today), the command now fails loud and names the manual branch —
392
+ start a fresh agent and seed it with `/continue <id>` — instead of only saying
393
+ "unsupported". Source: `apps/cli/src/commands/fork.ts`,
394
+ `apps/cli/src/commands/sessions.ts`.
395
+
396
+ - **Muse Code global binary is visible to `agents view` / `isVersionInstalled`.**
397
+ Muse installs a single self-updating launcher at `~/.local/bin/muse` (same
398
+ shape as droid), but `getBinaryPath` still resolved a version-home
399
+ `node_modules/.bin/muse` that never exists. After `agents import muse` /
400
+ `agents add muse`, version dirs now resolve to that global path so managed
401
+ view, collapse, and live-version bookkeeping match what actually executes;
402
+ install no longer writes a self-referential shim symlink. Source:
403
+ `apps/cli/src/lib/versions.ts`.
404
+
3
405
  ## 1.22.22
4
406
 
5
407
  - **New: `agents insights` — how you work, split by the Claude account that did the
package/README.md CHANGED
@@ -32,6 +32,8 @@
32
32
  &nbsp;&nbsp;&nbsp;&nbsp;
33
33
  <a href="https://factory.ai" title="Factory AI Droid"><strong>Droid</strong></a>
34
34
  &nbsp;&nbsp;&nbsp;&nbsp;
35
+ <a href="https://dev.meta.ai/docs/muse-code" title="Meta Muse Code"><strong>Muse</strong></a>
36
+ &nbsp;&nbsp;&nbsp;&nbsp;
35
37
  <a href="https://omp.sh" title="Oh My Pi"><strong>Pi</strong></a>
36
38
  </p>
37
39
 
@@ -218,7 +220,11 @@ otherwise the first permission option offered by the server. The same last-resor
218
220
  warning applies.
219
221
 
220
222
  Codex has no native smart-classifier mode, so `agents run codex --mode auto` resolves
221
- to sandboxed `edit` and can still prompt. `agents run codex --mode skip` is different:
223
+ to sandboxed `edit` and can still prompt. When `--mode` is omitted for Codex, the same
224
+ safe writable mode is used: the workspace, `~/.agents`, and regenerable toolchain caches
225
+ are writable; network access is enabled; and approvals are requested on demand. An
226
+ explicit `--mode plan` keeps the filesystem read-only while leaving network access on.
227
+ `agents run codex --mode skip` is different:
222
228
  it bypasses approvals **and** removes the sandbox. `full` remains an alias for `skip`,
223
229
  but new scripts should use the explicit `skip` name.
224
230
 
@@ -297,6 +303,7 @@ agents sessions --idle # stopped between turns (fleet-wide)
297
303
  agents sessions --orphan # agent outlived its terminal client
298
304
  agents sessions --crashed # terminal and agent disappeared uncleanly
299
305
  agents sessions focus a1b2c3d4 # jump back into one — attach in place, or resume
306
+ agents sessions focus claude@latest --device yosemite-s0 # pick latest there
300
307
  ```
301
308
 
302
309
  On a terminal, `agents sessions --active` (and a bare `agents sessions`) open the **interactive session browser** — one filter you drive with single keys, re-pulled live across the fleet:
@@ -307,7 +314,7 @@ On a terminal, `agents sessions --active` (and a bare `agents sessions`) open th
307
314
  | `r` | running only | `--active` |
308
315
  | `f` | favorites only | `--favorites` |
309
316
  | `*` | star / unstar the highlighted session | `agents sessions favorite <id>` |
310
- | `c` | team sessions | `--teams` |
317
+ | `c` | team sessions | `--team` (alias: `--teams`) |
311
318
  | `a` | agent (cycles) | `-a` |
312
319
  | `d` | device (cycles) | `--device` |
313
320
  | `p` | this repo ↔ all dirs | `--all` |
@@ -326,7 +333,7 @@ Filters **stack** (they AND together), the active set shows in the header, and t
326
333
  | --- | --- |
327
334
  | ![sessions browser, preview hidden](assets/demos/sessions-preview-before.png) | ![sessions browser, preview open with a links line](assets/demos/sessions-preview-after.png) |
328
335
 
329
- Each live session resolves to `working`, `waiting_input` (with why -- a question, a plan review, or a permission prompt), `idle`, or a lifecycle state such as `orphaned`, `crashed`, `closed`, `abandoned`, `queued`, or `unknown`. Pass the matching flag (`--working`, `--idle`, `--waiting`, `--orphan`, `--crashed`, `--closed`, `--abandoned`, `--queued`, `--unknown`) directly; each implies `--active`, and several flags form a union. The fleet fan-out is already the default; `--local` opts out. `--all` instead widens historical directory and time scope. Rows also carry badges for the PR, worktree, and ticket. `agents sessions focus [id]` attaches the live pane in place -- the tmux split locally or over SSH, or its Ghostty tab -- and falls back to a fresh tab + resume when the terminal is gone.
336
+ Each live session resolves to `working`, `waiting_input` (with why -- a question, a plan review, or a permission prompt), `idle`, or a lifecycle state such as `orphaned`, `crashed`, `closed`, `abandoned`, `queued`, or `unknown`. Pass the matching flag (`--working`, `--idle`, `--waiting`, `--orphan`, `--crashed`, `--closed`, `--abandoned`, `--queued`, `--unknown`) directly; each implies `--active`, and several flags form a union. The fleet fan-out is already the default; `--local` opts out. `--all` instead widens historical directory and time scope. Rows also carry badges for the PR, worktree, and ticket. `agents sessions focus [selector]` accepts the same agent/version, device, time, team, project, skill/plugin, favorite, and live-state filters as the session browser. A unique id focuses directly; an agent/version or text selector always opens the preview picker. Immediately before attach it checks the tmux pane process: a living pane is joined in place, while a dead/missing pane enters recovery instead of showing tmux's `Pane is dead` screen.
330
337
 
331
338
  Landing on a session cold? `agents sessions <id>` prints a catch-up digest: an inferred title, files changed grouped by directory (created / modified / deleted), a histogram of which tools did the work (including parsed Bash commands -- `git`, `npm`, `ffmpeg`, `ssh`, and so on), and the last test verdict -- the signals to reload a task in seconds.
332
339
 
@@ -345,11 +352,11 @@ agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 # original harness/version/d
345
352
  agents run auto --resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 # adapt if its account is unavailable
346
353
  ```
347
354
 
348
- `agents sessions resume` reopens several sessions in whatever terminal you're in -- auto-detected across iTerm, Ghostty, tmux, and the VSCodium agent-terminal, or forced with `--iterm` / `--ghostty` / `--tmux` / `--vscodium`. `agents resume <id>` resumes one session without requiring you to name its harness: exact IDs take a local SQLite fast path, then resolve fleet-wide and restore the source harness, version, device, cwd, and recorded launch mode. Back them with **tmux** and the runs turn durable: detach, close your editor, reboot the GUI -- the session is still alive to `agents tmux attach`. The whole `agents tmux` subsystem (persistent multiplexer sessions that survive editor restarts and can be shared with other tools) sits underneath.
355
+ `agents sessions resume` reopens several sessions in whatever terminal you're in -- auto-detected across iTerm, Ghostty, tmux, and the VSCodium agent-terminal, or forced with `--iterm` / `--ghostty` / `--tmux` / `--vscodium`. `agents resume <id>` resumes one session without requiring you to name its harness: exact IDs take a local SQLite fast path, then resolve fleet-wide and recover on the source device. If the origin version is installed, signed in, and healthy, its isolated home performs native resume. Otherwise a healthy version of the **same harness** starts with `/continue <id>`, which reads the indexed transcript even when the old version home is retained under version trash. It never native-resumes from a different isolated home. Back them with **tmux** and the runs turn durable: detach, close your editor, reboot the GUI -- the session is still alive to `agents tmux attach`. The whole `agents tmux` subsystem (persistent multiplexer sessions that survive editor restarts and can be shared with other tools) sits underneath.
349
356
 
350
357
  ### Send an agent to the background — and bring it back
351
358
 
352
- Running 30 agents and drowning in terminal tabs? `agents sessions detach <id>` stops a session's interactive process and keeps it working **headless** in the background -- it drives its task to done unattended, no tab, lower cost. `agents sessions attach <id>` brings it back: version-pinned resume into a live TUI, the same session and full history (including whatever it did while backgrounded).
359
+ Running 30 agents and drowning in terminal tabs? `agents sessions detach <id>` stops a session's interactive process and keeps it working **headless** in the background -- it drives its task to done unattended, no tab, lower cost. `agents sessions attach <id>` brings it back through the same origin-device recovery decision: native resume in the exact healthy origin home, or same-harness `/continue` when that home is unavailable, with the full indexed history (including whatever it did while backgrounded).
353
360
 
354
361
  ```
355
362
  agents sessions detach a1b2c3d4 # go headless in the background, keep working
@@ -684,7 +691,7 @@ tools:
684
691
  ---
685
692
  ```
686
693
 
687
- Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit`, or `--mode auto` on Claude Code and GitHub Copilot. Reserve `--mode skip` (legacy alias: `full`) for last-resort bypasses. `agents run` defaults to `--mode plan` (read-only), which deadlocks at `ExitPlanMode` in headless runs.
694
+ Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit`, or `--mode auto` on Claude Code and GitHub Copilot. Reserve `--mode skip` (legacy alias: `full`) for last-resort bypasses. `agents run` defaults to `--mode plan` for other harnesses; Codex defaults to its safe writable profile. An explicit Codex `--mode plan` is read-only with network access.
688
695
 
689
696
  Resolution is project > user > system: a `<repo>/.agents/workflows/<name>/` overrides a same-named workflow in `~/.agents/workflows/`. Commit project workflows with your repo so teammates get the same pipeline.
690
697
 
package/dist/bin/agents CHANGED
Binary file
@@ -6,5 +6,7 @@
6
6
  * history, the exact inverse of `agents sessions detach`.
7
7
  */
8
8
  import type { Command } from 'commander';
9
+ import type { SessionMeta } from '../lib/session/types.js';
9
10
  export declare function registerAttachCommand(program: Command): void;
10
11
  export declare function attachAction(id: string): Promise<void>;
12
+ export declare function attachRecoveryArgs(session: Pick<SessionMeta, 'id'>): string[];
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
- import { discoverSessions } from '../lib/session/discover.js';
3
- import { resumeSessionInPlace } from './sessions.js';
2
+ import { runOnPeer } from '../lib/session/remote-list.js';
3
+ import { sessionRecoveryPeer } from '../lib/session/recovery.js';
4
+ import { resolveSessionMetadataValue, resumeSessionInPlace } from './sessions.js';
4
5
  import { readDetachRecord, clearDetachRecord, isHeadlessAlive } from '../lib/session/detached.js';
5
6
  export function registerAttachCommand(program) {
6
7
  program
@@ -12,16 +13,37 @@ export function registerAttachCommand(program) {
12
13
  });
13
14
  }
14
15
  export async function attachAction(id) {
15
- const q = id.toLowerCase();
16
- // Rich meta carries the pinned version + origin cwd the resume needs.
17
- const metas = await discoverSessions({ all: true, since: '90d', limit: 2000 });
18
- const meta = metas.find((m) => m.id === id) ?? metas.find((m) => m.id.toLowerCase().startsWith(q));
19
- if (!meta) {
16
+ const outcome = await resolveSessionMetadataValue(id);
17
+ if (outcome.kind === 'partial') {
18
+ console.error(chalk.red(`Could not resolve session while these devices were unavailable: ${outcome.failedPeers.join(', ')}`));
19
+ process.exitCode = 2;
20
+ return;
21
+ }
22
+ if (outcome.kind === 'not-found') {
20
23
  console.error(chalk.red(`No session matching "${id}".`));
21
24
  console.error(chalk.gray(' See your sessions: agents sessions'));
22
25
  process.exitCode = 1;
23
26
  return;
24
27
  }
28
+ if (outcome.kind === 'ambiguous') {
29
+ console.error(chalk.red(`"${id}" matches ${outcome.candidates.length} sessions. Pass the full session id.`));
30
+ process.exitCode = 1;
31
+ return;
32
+ }
33
+ const meta = outcome.session;
34
+ // Route the WHOLE attach operation to the origin, not just its eventual
35
+ // resume. The detach record and headless PID are device-local; clearing them
36
+ // here would leave the real background continuation running beside a second
37
+ // process on the owning machine.
38
+ const peer = sessionRecoveryPeer(meta);
39
+ if (peer) {
40
+ const routed = await runOnPeer(attachRecoveryArgs(meta), peer, { tty: true });
41
+ if (routed === 'no-target') {
42
+ console.error(chalk.red(`Cannot attach ${meta.shortId}: origin device ${peer} is not a registered reachable peer.`));
43
+ process.exitCode = 1;
44
+ }
45
+ return;
46
+ }
25
47
  // If it's backgrounded, stop the headless continuation first so two processes
26
48
  // don't resume the same transcript at once.
27
49
  const rec = readDetachRecord(meta.id);
@@ -39,3 +61,6 @@ export async function attachAction(id) {
39
61
  console.log(chalk.gray(`Attaching ${meta.agent} ${meta.id.slice(0, 8)} — resuming interactively…`));
40
62
  await resumeSessionInPlace(meta);
41
63
  }
64
+ export function attachRecoveryArgs(session) {
65
+ return ['sessions', 'attach', session.id];
66
+ }
@@ -47,11 +47,13 @@ export function registerDefaultsCommands(program) {
47
47
  .description('Set defaults for an agent/version selector')
48
48
  .option('--mode <mode>', "Default mode: plan, edit, auto, skip. 'full' accepted as alias for skip.")
49
49
  .option('--model <model>', 'Default model or model alias, forwarded via --model')
50
+ .option('--effort <effort>', 'Default reasoning effort: low, medium, high, xhigh, max, or auto')
50
51
  .action((selector, options) => {
51
52
  try {
52
53
  const entry = setRunDefault(selector, {
53
54
  ...(options.mode !== undefined ? { mode: options.mode } : {}),
54
55
  ...(options.model !== undefined ? { model: options.model } : {}),
56
+ ...(options.effort !== undefined ? { effort: options.effort } : {}),
55
57
  });
56
58
  console.log(chalk.green('Set run default:'));
57
59
  console.log(` ${formatRunDefaultEntry(entry)}`);