@phnx-labs/agents-cli 1.22.47 → 1.22.50

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 (272) hide show
  1. package/CHANGELOG.md +503 -17
  2. package/README.md +76 -32
  3. package/dist/bootstrap.js +4 -2
  4. package/dist/cli/command-registry.d.ts +1 -0
  5. package/dist/cli/command-registry.js +2 -0
  6. package/dist/commands/accounts.d.ts +58 -0
  7. package/dist/commands/accounts.js +201 -39
  8. package/dist/commands/artifacts.js +7 -6
  9. package/dist/commands/attach.js +1 -1
  10. package/dist/commands/browser.js +501 -103
  11. package/dist/commands/config.d.ts +0 -1
  12. package/dist/commands/config.js +0 -20
  13. package/dist/commands/daemon-test-harness.d.ts +42 -0
  14. package/dist/commands/daemon-test-harness.js +90 -0
  15. package/dist/commands/daemon.d.ts +21 -0
  16. package/dist/commands/daemon.js +147 -12
  17. package/dist/commands/device-config-test-harness.d.ts +11 -0
  18. package/dist/commands/device-config-test-harness.js +59 -0
  19. package/dist/commands/exec.js +76 -16
  20. package/dist/commands/focus.d.ts +14 -0
  21. package/dist/commands/focus.js +48 -5
  22. package/dist/commands/go.d.ts +3 -0
  23. package/dist/commands/go.js +16 -3
  24. package/dist/commands/inspect.js +2 -2
  25. package/dist/commands/lease.d.ts +1 -1
  26. package/dist/commands/lease.js +4 -3
  27. package/dist/commands/mailboxes.js +4 -3
  28. package/dist/commands/menubar.js +11 -4
  29. package/dist/commands/projects.d.ts +16 -1
  30. package/dist/commands/projects.js +58 -64
  31. package/dist/commands/resume.js +1 -1
  32. package/dist/commands/route.js +4 -4
  33. package/dist/commands/routines.js +3 -2
  34. package/dist/commands/run-broadcast.d.ts +1 -1
  35. package/dist/commands/run-broadcast.js +1 -1
  36. package/dist/commands/sessions-picker.d.ts +22 -0
  37. package/dist/commands/sessions-picker.js +48 -9
  38. package/dist/commands/sessions-trace.js +1 -1
  39. package/dist/commands/sessions.js +1 -0
  40. package/dist/commands/setup-secrets.js +23 -0
  41. package/dist/commands/share.d.ts +20 -5
  42. package/dist/commands/share.js +158 -53
  43. package/dist/commands/ssh.js +54 -7
  44. package/dist/commands/teams.d.ts +12 -0
  45. package/dist/commands/teams.js +44 -67
  46. package/dist/commands/traces.d.ts +2 -0
  47. package/dist/commands/traces.js +188 -0
  48. package/dist/commands/view.d.ts +1 -1
  49. package/dist/commands/view.js +17 -34
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.js +5 -0
  52. package/dist/lib/account-provider-registry.d.ts +9 -0
  53. package/dist/lib/account-provider-registry.js +20 -0
  54. package/dist/lib/accounting/account-pool-collect.d.ts +34 -0
  55. package/dist/lib/accounting/account-pool-collect.js +71 -0
  56. package/dist/lib/accounting/account-pool.d.ts +46 -0
  57. package/dist/lib/accounting/account-pool.js +33 -0
  58. package/dist/lib/accounting/capacity.d.ts +24 -0
  59. package/dist/lib/accounting/capacity.js +30 -0
  60. package/dist/lib/accounting/rotate.d.ts +11 -18
  61. package/dist/lib/accounting/rotate.js +6 -24
  62. package/dist/lib/accounting/usage.d.ts +14 -3
  63. package/dist/lib/accounting/usage.js +64 -18
  64. package/dist/lib/agent-spec/agents.js +11 -9
  65. package/dist/lib/browser/drivers/ssh.d.ts +16 -2
  66. package/dist/lib/browser/drivers/ssh.js +36 -9
  67. package/dist/lib/browser/ipc.js +8 -3
  68. package/dist/lib/browser/profiles.d.ts +35 -101
  69. package/dist/lib/browser/profiles.js +130 -301
  70. package/dist/lib/browser/registry.d.ts +39 -0
  71. package/dist/lib/browser/registry.js +149 -0
  72. package/dist/lib/browser/remote-control.d.ts +8 -6
  73. package/dist/lib/browser/remote-control.js +8 -6
  74. package/dist/lib/browser/resolve-target.d.ts +68 -0
  75. package/dist/lib/browser/resolve-target.js +250 -0
  76. package/dist/lib/browser/runtime-state.d.ts +40 -16
  77. package/dist/lib/browser/runtime-state.js +92 -25
  78. package/dist/lib/browser/service.d.ts +28 -9
  79. package/dist/lib/browser/service.js +139 -62
  80. package/dist/lib/browser/task-index.d.ts +70 -0
  81. package/dist/lib/browser/task-index.js +180 -0
  82. package/dist/lib/byok-usage.js +2 -2
  83. package/dist/lib/claude-statusline.d.ts +29 -0
  84. package/dist/lib/claude-statusline.js +139 -0
  85. package/dist/lib/cloud/host.d.ts +1 -1
  86. package/dist/lib/cloud/host.js +1 -1
  87. package/dist/lib/cloud/rush.js +1 -1
  88. package/dist/lib/computer/computer-rpc.js +2 -2
  89. package/dist/lib/computer/download.d.ts +15 -15
  90. package/dist/lib/computer/download.js +35 -98
  91. package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
  92. package/dist/lib/computer/ssh-tunnel.js +12 -22
  93. package/dist/lib/config-keys.d.ts +2 -8
  94. package/dist/lib/config-keys.js +2 -13
  95. package/dist/lib/crabbox/cli.d.ts +1 -1
  96. package/dist/lib/crabbox/cli.js +2 -2
  97. package/dist/lib/crabbox/lease.d.ts +9 -0
  98. package/dist/lib/crabbox/lease.js +70 -15
  99. package/dist/lib/crabbox/runtimes.js +1 -1
  100. package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
  101. package/dist/lib/daemon/account-state-daemon-service.js +26 -0
  102. package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
  103. package/dist/lib/daemon/browser-ipc-service.js +46 -0
  104. package/dist/lib/daemon/daemon.d.ts +3 -0
  105. package/dist/lib/daemon/daemon.js +212 -444
  106. package/dist/lib/daemon/device-probe-service.d.ts +26 -0
  107. package/dist/lib/daemon/device-probe-service.js +47 -0
  108. package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
  109. package/dist/lib/daemon/keychain-reap-service.js +32 -0
  110. package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
  111. package/dist/lib/daemon/monitor-engine-service.js +29 -0
  112. package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
  113. package/dist/lib/daemon/secrets-broker-service.js +66 -0
  114. package/dist/lib/daemon/self-heal-service.d.ts +27 -0
  115. package/dist/lib/daemon/self-heal-service.js +48 -0
  116. package/dist/lib/daemon/service.d.ts +102 -0
  117. package/dist/lib/daemon/service.js +98 -0
  118. package/dist/lib/daemon/session-index-service.d.ts +22 -0
  119. package/dist/lib/daemon/session-index-service.js +40 -0
  120. package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
  121. package/dist/lib/daemon/state-dir-check-service.js +63 -0
  122. package/dist/lib/daemon/supervisor.d.ts +87 -0
  123. package/dist/lib/daemon/supervisor.js +298 -0
  124. package/dist/lib/daemon/watchdog-service.d.ts +19 -0
  125. package/dist/lib/daemon/watchdog-service.js +40 -0
  126. package/dist/lib/daemon-health.d.ts +15 -0
  127. package/dist/lib/daemon-health.js +29 -5
  128. package/dist/lib/daemon-services.d.ts +8 -1
  129. package/dist/lib/daemon-services.js +55 -0
  130. package/dist/lib/daemon-ticks.d.ts +13 -2
  131. package/dist/lib/daemon-ticks.js +20 -18
  132. package/dist/lib/device-config.d.ts +0 -2
  133. package/dist/lib/device-config.js +1 -23
  134. package/dist/lib/devices/registry.d.ts +1 -1
  135. package/dist/lib/devices/registry.js +1 -1
  136. package/dist/lib/devices/stats-cache.d.ts +20 -0
  137. package/dist/lib/devices/stats-cache.js +49 -5
  138. package/dist/lib/devices/worker-pick.d.ts +48 -0
  139. package/dist/lib/devices/worker-pick.js +113 -0
  140. package/dist/lib/exec.d.ts +67 -13
  141. package/dist/lib/exec.js +85 -29
  142. package/dist/lib/harness/adapters/cursor.js +8 -11
  143. package/dist/lib/helper-download.d.ts +108 -0
  144. package/dist/lib/helper-download.js +194 -0
  145. package/dist/lib/helper-versions.d.ts +52 -0
  146. package/dist/lib/helper-versions.js +58 -0
  147. package/dist/lib/hooks/cache.js +11 -0
  148. package/dist/lib/hooks/install.js +2 -2
  149. package/dist/lib/hosts/credentials.d.ts +1 -1
  150. package/dist/lib/hosts/credentials.js +1 -1
  151. package/dist/lib/hosts/dispatch.d.ts +7 -1
  152. package/dist/lib/hosts/dispatch.js +24 -5
  153. package/dist/lib/hosts/passthrough.d.ts +8 -15
  154. package/dist/lib/hosts/passthrough.js +19 -16
  155. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  156. package/dist/lib/hosts/providers/devices.js +1 -1
  157. package/dist/lib/hosts/reconnect.d.ts +179 -21
  158. package/dist/lib/hosts/reconnect.js +266 -45
  159. package/dist/lib/installations/migrate.d.ts +4 -5
  160. package/dist/lib/installations/migrate.js +6 -7
  161. package/dist/lib/installations/shims.d.ts +1 -1
  162. package/dist/lib/installations/shims.js +23 -13
  163. package/dist/lib/installations/store.js +6 -3
  164. package/dist/lib/installations/versions.js +73 -48
  165. package/dist/lib/linear-autoclose.d.ts +1 -1
  166. package/dist/lib/linear-autoclose.js +1 -1
  167. package/dist/lib/loop.d.ts +2 -2
  168. package/dist/lib/loop.js +1 -1
  169. package/dist/lib/menubar/download-menubar.d.ts +47 -0
  170. package/dist/lib/menubar/download-menubar.js +60 -0
  171. package/dist/lib/menubar/install-menubar.d.ts +31 -7
  172. package/dist/lib/menubar/install-menubar.js +79 -21
  173. package/dist/lib/menubar/notify-desktop.d.ts +3 -3
  174. package/dist/lib/menubar/notify-desktop.js +6 -6
  175. package/dist/lib/model-tiers.d.ts +1 -1
  176. package/dist/lib/project-probe.d.ts +1 -1
  177. package/dist/lib/project-probe.js +1 -1
  178. package/dist/lib/project-pull.d.ts +1 -1
  179. package/dist/lib/project-pull.js +2 -2
  180. package/dist/lib/projects.d.ts +1 -1
  181. package/dist/lib/projects.js +1 -1
  182. package/dist/lib/sandbox.js +6 -7
  183. package/dist/lib/secrets/download-keychain.d.ts +47 -0
  184. package/dist/lib/secrets/download-keychain.js +70 -0
  185. package/dist/lib/secrets/install-helper.d.ts +20 -1
  186. package/dist/lib/secrets/install-helper.js +44 -3
  187. package/dist/lib/session/active.js +17 -1
  188. package/dist/lib/session/db.d.ts +11 -1
  189. package/dist/lib/session/db.js +138 -6
  190. package/dist/lib/session/discover.d.ts +21 -1
  191. package/dist/lib/session/discover.js +33 -5
  192. package/dist/lib/session/highlights.d.ts +49 -1
  193. package/dist/lib/session/highlights.js +95 -0
  194. package/dist/lib/session/host-link.d.ts +16 -2
  195. package/dist/lib/session/host-link.js +7 -1
  196. package/dist/lib/session/parse.js +33 -1
  197. package/dist/lib/session/remote/remote-list.d.ts +9 -0
  198. package/dist/lib/session/remote/remote-list.js +19 -2
  199. package/dist/lib/session/remote/watch.js +11 -2
  200. package/dist/lib/session/session-cache.d.ts +53 -0
  201. package/dist/lib/session/session-cache.js +88 -0
  202. package/dist/lib/session/state.js +1 -1
  203. package/dist/lib/session/tool-calls.d.ts +2 -0
  204. package/dist/lib/session/tool-calls.js +1 -1
  205. package/dist/lib/session/trajectory-html.d.ts +5 -1
  206. package/dist/lib/session/trajectory-html.js +42 -22
  207. package/dist/lib/session/trajectory-text.js +9 -2
  208. package/dist/lib/session/trajectory.js +53 -4
  209. package/dist/lib/session/types.d.ts +15 -0
  210. package/dist/lib/sha256-asset.d.ts +26 -0
  211. package/dist/lib/sha256-asset.js +41 -0
  212. package/dist/lib/share/backend.d.ts +95 -0
  213. package/dist/lib/share/backend.js +139 -0
  214. package/dist/lib/share/delete.d.ts +6 -2
  215. package/dist/lib/share/delete.js +8 -9
  216. package/dist/lib/share/html.d.ts +22 -0
  217. package/dist/lib/share/html.js +88 -0
  218. package/dist/lib/share/provision.d.ts +18 -4
  219. package/dist/lib/share/provision.js +38 -4
  220. package/dist/lib/share/publish.d.ts +44 -15
  221. package/dist/lib/share/publish.js +58 -26
  222. package/dist/lib/share/worker-template.js +402 -25
  223. package/dist/lib/sqlite.js +16 -3
  224. package/dist/lib/ssh-exec.d.ts +65 -0
  225. package/dist/lib/ssh-exec.js +120 -4
  226. package/dist/lib/startup/command-registry.js +1 -1
  227. package/dist/lib/startup/dev-build.js +3 -3
  228. package/dist/lib/state.d.ts +1 -1
  229. package/dist/lib/state.js +7 -3
  230. package/dist/lib/subagents-registry.js +1 -1
  231. package/dist/lib/teams/agents.d.ts +14 -12
  232. package/dist/lib/teams/agents.js +188 -73
  233. package/dist/lib/teams/api.d.ts +6 -1
  234. package/dist/lib/teams/api.js +2 -0
  235. package/dist/lib/teams/scheduler.d.ts +7 -0
  236. package/dist/lib/teams/scheduler.js +26 -8
  237. package/dist/lib/terminal/index.d.ts +1 -1
  238. package/dist/lib/terminal/types.d.ts +1 -1
  239. package/dist/lib/terminal/types.js +1 -1
  240. package/dist/lib/tmux/orphan-reap.js +1 -1
  241. package/dist/lib/tmux/session.js +31 -0
  242. package/dist/lib/traces/backend.d.ts +29 -0
  243. package/dist/lib/traces/backend.js +46 -0
  244. package/dist/lib/traces/classify.d.ts +52 -0
  245. package/dist/lib/traces/classify.js +72 -0
  246. package/dist/lib/traces/config.d.ts +10 -0
  247. package/dist/lib/traces/config.js +2 -0
  248. package/dist/lib/traces/provision.d.ts +19 -0
  249. package/dist/lib/traces/provision.js +30 -0
  250. package/dist/lib/traces/sync.d.ts +150 -0
  251. package/dist/lib/traces/sync.js +459 -0
  252. package/dist/lib/traces/worker-template.d.ts +4 -0
  253. package/dist/lib/traces/worker-template.js +159 -0
  254. package/dist/lib/types.d.ts +18 -19
  255. package/dist/lib/types.js +14 -0
  256. package/dist/lib/usage-refresh.d.ts +1 -1
  257. package/dist/lib/usage-refresh.js +11 -8
  258. package/dist/lib/workflows.d.ts +1 -1
  259. package/package.json +4 -6
  260. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  261. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
  262. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  263. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  264. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
  265. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  266. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -26
  267. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  268. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  269. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -135
  270. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  271. package/dist/lib/usage-fleet.d.ts +0 -32
  272. package/dist/lib/usage-fleet.js +0 -125
package/CHANGELOG.md CHANGED
@@ -1,6 +1,325 @@
1
1
  # Changelog
2
2
 
3
- ## 1.22.47
3
+ ## 1.22.50
4
+
5
+ ### Fixed
6
+
7
+ - **Teams now persist observed teammate failure evidence and keep independent DAG branches moving.** Placement, local/remote launch, cloud dispatch, dependency, and process-exit failures carry a stable code, sanitized message, exit code, retryability, and observation time in `teams status` text/JSON. A runnable node with a durable placement failure fails independently; a node blocked only by pool capacity or load stays pending with retryable evidence so a later wave can launch it. Descendants name failed or missing `--after` blockers instead of spinning until `--max-waves`. `teams start` (no `--watch`) reports teammates that failed during the wave in a `Failed this wave` section (JSON: `failed[]` with evidence) and exits non-zero when a wave produced only failures. Because a failed launch now keeps its record (evidence) instead of deleting it, re-running the identical `teams add --name <name>` after a failure requires `teams remove <team> <name>` first.
8
+ Successfully resuming a failed teammate clears the prior attempt's evidence as
9
+ the replacement enters `running`; a replacement that fails to launch restores
10
+ the original terminal state and evidence intact.
11
+
12
+ - **Register the Phoenix session with Prix after sync (PHNX-3257).** A successful
13
+ (non-dry-run) `agents traces sync` now fire-and-forget POSTs the Phoenix bearer
14
+ to `api.prix.dev/api/v1/traces/link`, so the Prix web console can mint a token
15
+ and serve live trajectories instead of fixtures. Scoped to the managed Phoenix
16
+ backend only — a BYO/self-hosted `AGENTS_TRACES_WRITE_TOKEN` is never sent to
17
+ Prix. Source: `cli/src/lib/traces/sync.ts`.
18
+
19
+ - **Ship `traces-daily-sync` routine (PHNX-3258).** Daily `agents traces sync` at 02:00 UTC
20
+ to accumulate `bucketHistory` for drift signals; requires a device pin
21
+ (`agents routines devices traces-daily-sync --set <name>`) before it fires.
22
+ Source: `cli/routines/traces-daily-sync.yml`.
23
+
24
+ - **`--strategy balanced` now spreads across your provider accounts, not just
25
+ native logins (RUSH-3182).** A setup-token or API-key account added with
26
+ `agents accounts add` is now a first-class balancing candidate for every harness
27
+ its provider can authenticate — claude, codex, grok, cursor, opencode (a harness
28
+ with only a native login and no provider adapter, like kimi, keeps balancing its
29
+ native logins). Before, balanced only rotated across accounts that sat in a version
30
+ home, so a worker's shared setup-tokens never participated and `--account`
31
+ couldn't select them. The run path folds those provider accounts into the
32
+ candidate list and injects the picked one through the existing `--account` path
33
+ (a setup-token still authenticates via `CLAUDE_CODE_OAUTH_TOKEN`); the other
34
+ candidate consumers — the watchdog, session recovery, teams placement — keep the
35
+ native-only list, so nothing else changes. On a box dominated by verified native
36
+ logins a usage-less provider account is deprioritized until the daemon fetches
37
+ its usage; on a worker (setup-token majority) it participates immediately.
38
+ Source: `cli/src/lib/accounting/account-pool.ts`,
39
+ `cli/src/lib/accounting/account-pool-collect.ts`,
40
+ `cli/src/lib/accounting/rotate.ts`, `cli/src/commands/exec.ts`.
41
+
42
+ - **The attestation producer skips the helper manifest by default too (RUSH-3216).**
43
+ `release.sh` gained `--with-helpers` so an ordinary release does no helper work, but
44
+ `release-attestation-produce.sh` kept the same unconditional
45
+ `release-manifest.sh` verification — so the coupling survived one step upstream. Found
46
+ live: a one-line **comment** fix in `native/computer-mac/scripts/build.sh` (an
47
+ `apps/cli/` → `cli/` path in prose) changed that helper's input digest and aborted an
48
+ otherwise-clean 1.22.49 attestation, for a helper the tarball no longer ships and the
49
+ CLI resolves from its own tag. The producer now takes the same `--with-helpers` flag,
50
+ default off. Source: `cli/scripts/release-attestation-produce.sh`.
51
+
52
+ - **`scripts/publish-computer-win.sh` — a publish path for the Windows helper (RUSH-3228).**
53
+ Its release now triggers on `computer-win/v<x.y.z>` rather than the CLI's `v*` tag, but
54
+ nothing in the repo cut such a tag, so the trigger would have been dead — trading a
55
+ wasteful 165 MB rebuild on every CLI release for no rebuild at all. The tag *is* the
56
+ publish action (`release-exe` builds, smokes on a real windows-latest runner, and
57
+ uploads the exe + sha256), so a mis-shaped tag is a silent no-op: the script refuses a
58
+ `v`-prefixed or non-semver version, refuses an existing tag because the upload uses
59
+ `--clobber` and an installed CLI may already pin it, and is dry-run by default.
60
+ Symmetric with `publish-computer-helper-mac.sh`. Source: `cli/scripts/publish-computer-win.sh`.
61
+
62
+ - **`scripts/test.sh --shard <n>` — fan the suite across n fleet workers (RUSH-3230).**
63
+ Uses vitest's own `--shard=i/n`, drawing workers from the same auto pool a single
64
+ `--device auto` run uses, so `role=worker`/`role=personal` marks govern the fan-out too.
65
+ This is the change that moves release time, and the reason is arithmetic: a measured full
66
+ run is 3,079s of CPU at 11.5× parallelism on one box, so wall equals CPU/workers (269s) —
67
+ the suite is **throughput-bound**, not bound by any single slow file. Adding boxes divides
68
+ the CPU: 3 ≈ 93s, 6 ≈ 47s, 9 ≈ 31s. Shards run concurrently and every one is waited on
69
+ before reporting, so a failure in one does not hide the others. Requires `agents` ≥ 1.22.49
70
+ for `devices pick --json`; an older CLI fails naming the version and the fix rather than
71
+ passing through a commander error. Source: `cli/scripts/test.sh`.
72
+ - **`scripts/test.sh --devices a,b,c` — name the shard workers explicitly (RUSH-3230).**
73
+ Pins the fan-out to known-idle boxes instead of auto-picking, and skips the
74
+ `devices pick --json` dependency, so sharding also works from a machine whose installed
75
+ CLI predates 1.22.49. Source: `cli/scripts/test.sh`.
76
+ - **`--shard` fails loud on a count below 2 and on a conflicting target flag (RUSH-3230).**
77
+ `--shard 0` previously passed the numeric check, ran zero shards, and still printed
78
+ `All 0 shards passed.` with exit 0 — a false green that reported success having run no
79
+ tests. It now requires at least 2 (a single worker is `--device auto`). Separately, `MODE`
80
+ was last-write-wins with no cross-flag validation, so `--shard 6 --device box` silently
81
+ dropped one of the two purely on argument order; conflicting target flags now die naming
82
+ both. Source: `cli/scripts/test.sh`.
83
+ - **`--devices` obeys the same 2-worker floor, and is validated before prerequisites (RUSH-3230).**
84
+ `--devices onebox` derived the shard count from the list length and skipped the floor
85
+ entirely, running a one-shard fan-out. The list is now resolved and checked immediately
86
+ after argument parsing rather than inside the dispatch branch, so a bad invocation reports
87
+ its own problem instead of dying on a missing `rsync` first. Source: `cli/scripts/test.sh`.
88
+
89
+ - **Split `ssh.device-config.test.ts`, the second-slowest file (RUSH-3230).** 18 subprocess
90
+ tests in one file at ~44s locally (151s on a loaded worker) — 8.4s *per test*. Now three
91
+ files along its existing `describe` boundaries (per-device config / fleet-wide defaults +
92
+ role + describe / retired-subcommand tombstones) over a shared
93
+ `device-config-test-harness.ts`: **45s → 19s wall, all 18 tests still passing**, none
94
+ changed, skipped, or dropped. Source: `cli/src/commands/device-config-test-harness.ts`.
95
+
96
+ - **Split `daemon.test.ts`, the suite's floor (RUSH-3230).** It was 35 subprocess tests in
97
+ one file at ~53s locally (159s on a loaded worker) — the slowest file in the repo, and
98
+ therefore the whole suite's floor, because vitest parallelises across *files* and runs
99
+ one file's tests sequentially in a single worker. Now three files by theme (command
100
+ surface / services + webhooks / doctor + logs) over a shared
101
+ `daemon-test-harness.ts`, so they run concurrently: **53.5s → 26s wall, all 35 tests
102
+ still passing**. No test was changed, skipped, or dropped. Source:
103
+ `cli/src/commands/daemon-test-harness.ts`.
104
+
105
+ ## 1.22.49
106
+
107
+ - **Check CLI benchmark numbers into `docs/benchmarks.md` (RUSH-2385).** Commander-bootstrap and audit-hook means from the yosemite-s1 vitest bench (PR #2349) plus pointers to OPT-01/OPT-02 live in git. Linear is not the ledger. Source: `cli/docs/benchmarks.md`.
108
+
109
+ fix(view): keep Claude session/week quota slots aligned, render omitted windows as unavailable, and show only the unlabeled last-active timestamp
110
+
111
+ - **No native binary ships in the npm tarball (RUSH-3100).** `Agents CLI.app` (2.6 MB) and
112
+ `MenubarHelper.app` (3.3 MB) were copied into `dist/` by `build`, listed in `files`, and
113
+ hard-gated by two `prepack` checks — so every Linux and Windows user downloaded 5.9 MB of
114
+ signed macOS bundles, and the tarball could only be packed on a Mac holding them. All
115
+ three are gone; helpers are fetched on demand from their own release tags and verified
116
+ exactly as before (sha256 + Developer ID team + notarization + designated-requirement
117
+ pin). Unpacked tarball: 21,337,724 → 15,629,419 bytes. `build` still clears any stale
118
+ bundle from `dist/` so an upgrade cannot leave one behind. This is what lets an ordinary
119
+ release be produced without a signing Mac: the prepack gates required the signed `.app`s
120
+ to be present at pack time, which is why the attestation was macOS-only. Both gate
121
+ scripts are retained — they remain the right check for cutting a *helper* release.
122
+ Source: `cli/package.json`.
123
+ - **The attestation producer no longer seeds signed helper apps into its worktree
124
+ (RUSH-3100).** That seeding existed only because `prepack` refused to pack without them,
125
+ so it was the workaround for the coupling above; its comments claimed "the prepack gates
126
+ still decide … fails the pack exactly as before", which stopped being true the moment
127
+ those gates were removed. Source: `cli/scripts/release-attestation-produce.sh`.
128
+
129
+ - **Trace storage can now be deployed from the CLI (RUSH-3140).** `agents traces setup` provisions the isolated `agents-traces` R2 bucket and Worker, uploads the canonical private trace Worker, binds its write and Phoenix identity secrets, enables its workers.dev route, and maps `traces.agents-cli.sh`.
130
+
131
+ - **`agents traces sync` emits the rich Phoenix Evals index shard (RUSH-3142 M3).** The per-device `index.json` now carries duration/error stats, ranked sessions needing attention, derived topic counts, and structured tool-failure counts split into real failures, repository guards, and auto-mode permission denials recorded on failed tool calls. Topic classification uses only session metadata plus tool mix and is lazily cached in a self-healing `session_topics` table keyed by transcript mtime + size; per-session trajectory JSON and the incremental upload ledger are unchanged. Source: `apps/cli/src/lib/traces/{classify,sync}.ts`, `apps/cli/src/lib/session/db.ts`.
132
+
133
+ - **The offloaded suite no longer unpacks inside your DotAgents repo.** `scripts/test.sh --device` shipped the tree to `~/.agents/test-runs/agents-cli` — but `~/.agents` is itself a git repo, so anything in the suite calling `git rev-parse --show-toplevel` resolved to `~/.agents` instead of the shipped tree (`release-manifest.test.ts` went looking for `~/.agents/native/computer-mac/Sources` and four tests failed with a confusing "helper input missing"), and the tree showed up as `?? test-runs/` in the operator's own repo status on every worker. The tree now lands in `~/.cache/agents-cli/test-runs/tree`, which has no git ancestor — the same reason `sandbox.sh`'s `~/workspaces` choice never hit this. The shipped tree is still given its own blank git repo, because parts of the suite resolve paths from a repo root and would otherwise hard-fail. Source: `apps/cli/scripts/test.sh`, `apps/cli/scripts/bound-repo-root.sh`.
134
+
135
+ - **`scripts/test.sh` — the suite is offloaded by default and can no longer land on your machine by accident.** The full vitest suite pins a box for several minutes, and the only offloaded path used to be the `test:remote` package.json alias. Offloading was opt-in *at each call site* because `scripts/sandbox.sh` took a hand-composed command string rather than a verb, so every call site opted out: `scripts/build.sh` and `scripts/release-attestation-produce.sh` both ran `bun run test` on whatever machine invoked them. New `scripts/test.sh` is the one entry point — it offloads to a crabbox by default, takes `--device <box>` to run on any fleet Linux box, and runs locally only under an explicit, loudly-warned `--here`. When the offload target is unavailable it **fails naming the exact `--device` command** instead of silently falling back. `build.sh` and the attestation producer now both call it (the producer gains `--test-device` / `--test-here`), so a macOS signing box no longer doubles as the test runner just because a native helper needs notarizing. `sandbox.sh` gains the sibling projects' verb vocabulary (`sandbox.sh test`), which also fixes its bare default running `bun install && bun run test` at the monorepo root, where no test script exists. Source: `apps/cli/scripts/test.sh`, `apps/cli/scripts/sandbox.sh`, `apps/cli/scripts/build.sh`, `apps/cli/scripts/release-attestation-produce.sh`.
136
+
137
+ - **A helper-only GitHub release no longer blocks the CLI release train.** `release-attestation-produce.sh` seeded its helper manifest from `gh release list --limit 1` — unconditionally the newest release. But not every release is a CLI release: the Windows computer-helper workflow publishes helper-only releases (assets `computer-helper-win.exe` + `.sha256`) into the same `v<version>` tag namespace. One of those shadows the last real CLI release, the manifest seed silently misses, every helper then reads as "changed", and the producer hard-fails on `computer-mac` — a helper it never rebuilds — with `helper computer-mac input changed but this producer never rebuilds it`. Observed live: `v1.22.48` (helper-only, published 09:54Z) shadowed `v1.22.47` and blocked the release. The seed now walks back through recent releases to the newest one that actually carries a `release-manifest.json`. Source: `apps/cli/scripts/release-attestation-produce.sh`.
138
+
139
+ - **Watchdog, device-probe, self-heal, keychain-reap, and state-dir-check are now supervised daemon services (RUSH-3193 #3).** These five bare `setInterval` timers in `runDaemon()` are now `PeriodicService`s registered on `ServiceSupervisor`, alongside secrets-broker, browser-ipc, account-state, session-index, and monitors — each gets the same per-tick deadline, error boundary, and park/backoff circuit breaker, and now reports measured health through `agents daemon services` instead of an inferred `running (unsupervised)` label. `scheduler` and `webhook-receiver` remain outside the supervisor (neither fits the `PeriodicService` shape — see `cli/src/lib/daemon/AGENTS.md`). Source: `cli/src/lib/daemon/watchdog-service.ts`, `device-probe-service.ts`, `self-heal-service.ts`, `keychain-reap-service.ts`, `state-dir-check-service.ts`, `cli/src/lib/daemon/daemon.ts`.
140
+
141
+ - **`agents daemon services` shows every service's live health, with live enable/disable/restart (RUSH-3193 #4).** The daemon `services` view now reports state / last-run / consecutive-failures / last-error for every supervised service (secrets-broker, browser-ipc, monitor-engine, account-state, session-index), not just the two socket services; `--json` gains the full service list while keeping the existing `secretsBroker`/`browserIpc` fields. `agents daemon services enable`/`disable`/`restart <id>` now take effect live through the supervisor instead of requiring a daemon restart. Source: `cli/src/commands/daemon.ts`, `cli/src/lib/daemon-services.ts`, `cli/src/lib/daemon/supervisor.ts`.
142
+
143
+ - **Self-heal's staggered boot-time tick is restored (RUSH-3193 #17).** The `ServiceSupervisor` migration (RUSH-3193 #3) fired every periodic service's first tick immediately at daemon start, including self-heal — dropping the ~30s post-boot stagger the old inline timer used so shims/PATH could settle before self-heal's first sweep, without making daemon launch itself busy. `PeriodicService` gains an optional `startupDelayMs` (default 0, no change for other services); `SelfHealService` sets it to 30s. Also fixes a stale comment on `watchdog-service.ts` claiming an SSH fan-out that `runWatchdogPass` does not do (it's host-local). Source: `cli/src/lib/daemon/service.ts`, `cli/src/lib/daemon/supervisor.ts`, `cli/src/lib/daemon/self-heal-service.ts`, `cli/src/lib/daemon/watchdog-service.ts`.
144
+
145
+ - **Usage refresh is per-device — the `usage.primary-host` broadcast is gone (RUSH-3193 #15).** Every host now reads its own usage directly from the provider APIs, so the old model where one primary device fetched fleet usage and SSH-broadcast a token-free derived envelope for subscribers to import is removed. The `usage.primary-host` config key (and its `interactive.host` fallback for usage) is deleted, along with the publisher/subscriber role split; each daemon runs the local usage refresh on every host. Source: `cli/src/lib/daemon-ticks.ts`, `cli/src/lib/usage-fleet.ts`, `cli/src/lib/usage-refresh.ts`, `cli/src/lib/device-config.ts`, `cli/src/lib/config-keys.ts`.
146
+
147
+ - **Daemon service supervisor: per-service contract, error boundaries, deadlines, health (RUSH-3193 P1).** The daemon's background services used to be bare `setInterval` closures sharing one event loop — a throw escaping a tick's local try/catch killed the whole daemon, and a hung tick (an unbounded SSH/keychain await) latched its overlap guard forever, silently freezing that one service for the daemon's life (observed ~51h). A new `DaemonService`/`PeriodicService` contract plus a `ServiceSupervisor` give every registered service its own timer, a per-service error boundary that can never propagate to `process.exit`, and a hard per-tick deadline that always releases the overlap guard even when the tick itself hangs; repeated failures park the service and retry it with exponential backoff while every sibling service keeps ticking, and `supervisor.health()` reports state/last-run/consecutive-failures for every registered service. The session-index warm service is migrated onto it as the first proof of concept — `agents daemon services list` now shows it as `session-index`. Other background services are unchanged; they migrate in follow-up PRs. Source: `cli/src/lib/daemon/{service,supervisor,session-index-service}.ts`, `cli/src/lib/daemon/daemon.ts`, `cli/src/lib/daemon-services.ts`.
148
+
149
+ - **Managed Cursor accounts now use the account they display (RUSH-3196).** Cursor runs and direct version aliases force Cursor's HOME-relative file credential store (`~/.cursor/auth.json`) inside the selected version home instead of silently falling through to one macOS Keychain login. Stale `cli-config.json` metadata no longer counts as signed in; existing Keychain credentials are left untouched and an unseeded managed version asks for Cursor's normal login flow.
150
+
151
+ - **Grok billing events now reach `agents view` (RUSH-3197).** The daemon publishes freshly collected local-event snapshots instead of rejecting every `last_seen` source as a failed refresh. Current Grok weekly meters cache normally; an expired or missing event tells the operator which `grok@version` to run once rather than leaving a silent plan-only gap.
152
+
153
+ - Rebuilt the internal documentation as a compact architecture and decisions corpus, with command syntax remaining generated from the CLI.
154
+
155
+ ### Added
156
+
157
+ - `agents devices pick` — prints the device automatic placement would choose for
158
+ offloaded machine work: the least-loaded reachable POSIX box from the same auto
159
+ pool `agents run --device auto` draws from, so `role=worker` / `role=personal`
160
+ marks govern it. The name alone goes to stdout so scripts can consume it
161
+ (`box="$(agents devices pick)"`); candidates and load go to stderr. `--json`
162
+ adds every candidate and each exclusion reason. Fails loud with the excluded
163
+ devices named when no worker is eligible — it never answers "run it locally".
164
+
165
+ ### Changed
166
+
167
+ - `scripts/test.sh` now **auto-picks a fleet worker by default** instead of
168
+ requiring crabbox. `--device auto` says the same thing explicitly, `--crabbox`
169
+ selects the previous disposable-crabbox path, and `--device <box>` / `--here`
170
+ are unchanged. The old default needed the crabbox binary plus provider
171
+ credentials, so the no-argument invocation failed on any box without them. An
172
+ auto-pick that lands on the local machine runs in place without the `--here`
173
+ warning when that machine is itself a pool worker.
174
+ - `scripts/release-attestation-produce.sh` gains `--test-crabbox`, so all three of
175
+ `test.sh`'s lanes are reachable from a release. It previously exposed only
176
+ `--test-device` and `--test-here`, leaving a release on a box with no fleet worker
177
+ in reach unable to ask for the disposable crabbox it can still use.
178
+
179
+ ### Changed
180
+
181
+ - **Native helper downloads are decoupled from the CLI release.** `helper-download.ts`
182
+ used to build `releases/download/v${cliVersion}/<asset>`, keying every helper to the
183
+ CLI's own tag. That was coupled in both directions: a CLI release had to re-stage every
184
+ helper asset onto its new tag or the download 404'd, and a helper fix could not reach
185
+ anyone without cutting a CLI release. Helpers now publish to their own tags
186
+ (`menubar/v1.0.0`, `keychain/v1.0.0`, `computer-mac/v…`) and the CLI records a
187
+ per-helper **floor** in `helper-versions.ts` — the build it was tested against, and the
188
+ immutable tag it falls back to offline.
189
+
190
+ ### Fixed
191
+
192
+ - **The keychain helper download could never have worked.** GitHub rewrites a space in a
193
+ release-asset name to a dot on upload, so an asset staged as `Agents CLI.app.zip` is
194
+ served as `Agents.CLI.app.zip` while the CLI requested the spaced name — a permanent
195
+ 404. The asset is now published as **`Agents_CLI.app.zip`**: a name GitHub preserves
196
+ verbatim, chosen deliberately rather than mirroring GitHub's rewrite (matching
197
+ `Agents.CLI.app.zip` would work only for as long as that normalization rule holds).
198
+ `helperAssetUrls` additionally refuses any spec whose asset name contains a space,
199
+ rather than minting a URL that cannot resolve. The extracted bundle directory keeps its
200
+ space (`Agents CLI.app`) — that is the on-disk bundle name macOS and the TCC grant key
201
+ on, and it is unchanged.
202
+
203
+ - **The Windows helper exe is decoupled too.** `ssh-tunnel.ts` is a separate
204
+ implementation that never routed through `helper-download.ts`, and it still keyed
205
+ `computer-helper-win.exe` to `v${cliVersion}` — the identical coupling, meaning every
206
+ CLI release had to re-stage a ~165 MB binary or the download 404'd. It now resolves
207
+ `computer-win/v<x.y.z>` from the same floor table.
208
+
209
+ ### Removed
210
+
211
+ - **`release.sh` no longer stages helper fallback assets onto the CLI's own tag, and no
212
+ longer gates a release on one.** With helpers resolving from their own tags, those
213
+ assets were work nothing reads — and the keychain one was republished on every release
214
+ under `Agents CLI.app.zip`, a name GitHub rewrites to a dot and no client can fetch.
215
+ The hard `die` on a missing `ComputerHelper.app.zip` is gone with them: it would have
216
+ failed an otherwise-good release over an asset the runtime no longer consults.
217
+
218
+ ### Fixed
219
+
220
+ - **The menu-bar helper build could emit a Developer-ID-signed bundle that was never
221
+ notarized, and say nothing.** Notarization was gated on
222
+ `[ "$MODE" = "release" ] && [ "$SIGN_ID" != "-" ]`, so any invocation without the
223
+ `release` argument on a machine holding a Developer ID produced a real signed bundle,
224
+ passed every self-test, and exited 0 — while `spctl` reported
225
+ `rejected / source=Unnotarized Developer ID` with no stapled ticket. Gatekeeper on
226
+ macOS 26+ rejects such a bundle as "damaged" and crashes AppKit at launch. The three
227
+ credential guards meant to fail loud on missing Apple creds also lived *inside* that
228
+ branch, so they were unreachable in exactly the case that needed them. The gate is now
229
+ the signature (`[ "$SIGN_ID" != "-" ]`): a Developer-ID signature always implies
230
+ notarization, whatever the mode. Ad-hoc builds are unchanged — they cannot be notarized
231
+ by construction and `prepack` already stops them shipping.
232
+
233
+ A credential-less **debug** build on a machine that holds a Developer ID now signs
234
+ ad-hoc (with the `.dev` bundle id) instead of failing: `SIGN_ID` is auto-detected from
235
+ the keychain, so demanding notary creds unconditionally would have broken the local
236
+ build loop on every signing-capable Mac. The downgrade is resolved before `Info.plist`
237
+ is written — a late one would emit an ad-hoc bundle carrying the *production* bundle id
238
+ and poison the user's Accessibility grant. `MENUBAR_HELPER_SIGN_ID=-` remains the
239
+ explicit opt-out and is now documented in the script.
240
+
241
+ ### Fixed
242
+
243
+ - **The full test suite was red on `main`, and pull-request CI could not see it.** A
244
+ top-level `await import('bun:sqlite')` in `src/lib/sqlite.ts` made that module
245
+ un-lowerable to CJS, so every test that spawns a subprocess through `tsx` died at
246
+ transform time with `Top-level await is currently not supported with the "cjs" output
247
+ format` — 49 failures across 7 files, none of which touch SQLite. Both runtime arms now
248
+ load through `createRequire`, so there is no top-level await to lower. The specifier is
249
+ held in a variable rather than written inline, keeping the off-Bun collector from trying
250
+ to resolve a module that only exists under Bun (what the old `as string` cast did for
251
+ the dynamic import).
252
+
253
+ This mattered beyond the suite: `release-attestation-produce.sh` runs the **full** suite
254
+ fail-closed, while PR CI runs only the tests selected for a diff. A failure outside every
255
+ recent diff's selection is therefore invisible until release time, where it blocks the
256
+ attestation and with it any publish.
257
+
258
+ ### Fixed
259
+
260
+ - **Restored operator-facing master-key custody guidance to `docs/secrets.md`.** The
261
+ docs reset (`2d96d05d6`) rewrote the page from a manual into an architecture summary and
262
+ dropped the section that names the machine-local key path
263
+ (`~/.agents/.secrets-key/passphrase`), states that resolution is non-interactive, points
264
+ headless sync at `AGENTS_SYNC_PASSPHRASE` rather than the master key, and **forbids
265
+ exporting the master key from a shell rc file** — the exact advice that caused RUSH-1968
266
+ on seven machines. Four `docs-hygiene` guards had been failing ever since, which is the
267
+ alarm working as designed rather than test staleness.
268
+ - **Restored the `**Severity rubric**` section to `docs/observability.md`**, removed by the
269
+ same reset. `doctor-findings.test.ts` pins this prose copy against `FINDING_SEVERITY`, so
270
+ its absence meant the docs-cannot-drift guard was simply not running.
271
+ - **`add-targets-user-repo.test.ts` no longer enumerates the `state.js` surface.** Its
272
+ partial mock hand-listed every export, so adding `getCliVersionCachePath` to `state.ts`
273
+ took the whole file down with `No "getCliVersionCachePath" export is defined on the mock`.
274
+ It now spreads `importActual` and overrides only the directory resolvers it redirects, so
275
+ a new export cannot break it again.
276
+
277
+ - **`release.sh --with-helpers` — an ordinary release publishes the CLI and nothing else
278
+ (RUSH-3216).** Two couplings survived helpers moving to their own tags, and both are now
279
+ behind this opt-in (default off): staging `ComputerHelper.app.zip` onto the CLI's
280
+ `v<version>` tag, which publishes an asset no client requests; and
281
+ `release-manifest.sh require`, which re-derives every helper's input digest and aborts
282
+ when one moved without a rebuild — failing a good CLI release over Swift the CLI does not
283
+ ship. Source: `cli/scripts/release.sh`.
284
+ - **A new `release.sh` flag is inert until it is merged (RUSH-3216).** `release.sh` execs
285
+ `release-worktree.sh`, which checks out `origin/<default>` and re-runs the script from
286
+ there, so the second parse is the merged copy and an unmerged flag dies as `unknown
287
+ flag` even though the local parser handles it. Pass `--orchestration-phase` to skip the
288
+ re-exec when exercising a new flag pre-merge. Source: `cli/scripts/release.sh`.
289
+
290
+ - **Managed share URLs use your Phoenix handle, not a UUID (RUSH-3224).** `agents artifacts share` now publishes to `share.agents-cli.sh/<handle>/<slug>` where `<handle>` is the local-part of the signed-in email (`muqsitnawaz@gmail.com` → `muqsitnawaz`), and the default slug is `<readable>-<16hex>` (a short view-id). Owner metadata is still the Phoenix userId; the Worker 403s a PUT to someone else's handle and 409s if two accounts collide on the same local-part. HTML publishes rewrite Chrome-saved `file://…#section` TOC links to in-page hashes and inline local sibling images as data URIs, so a self-contained plan (including a 1 MB page with embedded screenshots) stays viewable after upload. Source: `cli/src/lib/share/{backend,worker-template,html,publish}.ts`.
291
+
292
+ - **A remote interactive `--device` run now prints its session id as it connects (RUSH-3227).** When Claude (or a resume) already has a real id before the TTY is taken, stderr shows `Session <uuid> on <host>` and `agents sessions resume <uuid>` so the id exists while the connection is live, not only after OpenSSH closes. A launch id is not printed as a session id. Source: `cli/src/lib/hosts/reconnect.ts`, `cli/src/commands/exec.ts`.
293
+
294
+ - **A remote interactive session that closes now prints its id (RUSH-3227).** After `agents run --device` (including `--raw`), `sessions focus`/`resume` over SSH, or a remote tmux attach ends, the CLI writes the full session id and `agents sessions resume <id>` under OpenSSH's `Shared connection … closed.` line, so a dropped tab is not a bare shell. Auto-reconnect still swallows the 255 it will retry; this fires only when the user is actually back locally. Source: `cli/src/lib/hosts/reconnect.ts`, `cli/src/commands/{exec,go,focus,resume,attach,sessions}.ts`, `cli/src/lib/session/remote/remote-list.ts`.
295
+
296
+ - **Re-pin the keychain helper to the binary that actually shipped in 1.22.47.** `scripts/verify-keychain-helper.sh` compared `bin/Agents CLI.app` to `beb02d…`, but `@phnx-labs/agents-cli@1.22.47` and `keychain/v1.0.0` both contain `a49080…`. Linux `npm pack` therefore died at prepack and blocked every release attestation. Same class of fix as #835 / #912. Source: `cli/scripts/Agents CLI.app.sha256`.
297
+
298
+ - **A session with no observer no longer reports as healthy (RUSH-3125).** `classifyHostLink` decides a session's host link from two local signals — the owning IDE window's heartbeat and tmux's attached-client count — and when it had *neither* it fell through to `connected`. That is the wrong default for a detector: the rows it silently blessed were exactly the ones with no observer (a bare terminal, a team spawn, a cloud task, or any `--device` session whose pane lives on another machine, since every signal it reads is local). There is now an `unknown` link, which says the question is open rather than answering it; `connected` is returned only on positive evidence — a counted client or a republishing window. `unknown` is not a loss signal: it never promotes a status, and it no longer clears a derived `attached` presence, so a plain terminal session you are sitting in keeps its marker. Everything else is unchanged and deliberately so — a deliberate `agents sessions detach` is still excluded first, `tmuxClients === 0` is still authoritative, an absent client count still never reads as zero, and per SES-18a a `running` session still keeps its status. Source: `cli/src/lib/session/host-link.ts`, `cli/src/lib/session/active.ts`, spec SES-18a.
299
+
300
+ - **A tmux status bar can show which agent session a pane is running — and never a fabricated one.** `agents run` wraps an interactive agent in a tmux session named `ag-<agent>-<8 chars>`, built from `(options.sessionId ?? randomUUID()).slice(0, 8)` (`cli/src/lib/exec.ts`). Reading those 8 characters as a session id is quietly wrong: the launcher pre-assigns an id only for Claude, so on a box running two harnesses `ag-claude-c8c4a2c8` carries a real, resumable short id while `ag-codex-364cd550` carries a throwaway that resolves to nothing — and the two are indistinguishable on sight. `createSession` now stamps two session-scoped tmux user options, `@ag_session_id` and `@ag_agent`, so a format like `#{?#{@ag_session_id},#{@ag_session_id},#{@ag_agent}}` renders a real handle where one exists and falls back to the harness name where none does, instead of printing something `agents sessions <id>` would reject. The id is published only when the harness actually *received* it — a new `isHarnessKnownSessionId` mirrors the two branches in `buildExecCommand` that put an id on the command line (a native resume on any harness with a `resume` spec, and Claude's create-with-`--session-id`), so `agents run codex --session-id X` without `--resume`, where X never reaches codex, correctly publishes nothing. This is the in-tmux counterpart to the existing name-parsing recovery tier (`shortIdFromName` + `resolveNamesToSessionIds` in `cli/src/lib/session/active.ts`), which validates a short id against the session DB; a tmux format cannot do a DB lookup, so the option carries the answer instead. Best-effort throughout: a failed stamp never fails a launch, and an unset option renders empty. Source: `cli/src/lib/tmux/session.ts`, `cli/src/lib/exec.ts`.
301
+
302
+ ### traces: drift signal for topic buckets
303
+
304
+ `agents traces sync` now computes a **drift signal** for each topic bucket by comparing today's error and stall rates against a 14-day rolling history stored inside the shard. Buckets that cross a 0.20 absolute-delta threshold are marked `degrading` or `improving`; the rest are `stable`. Buckets with fewer than 3 historical days are skipped to avoid noise on fresh deployments.
305
+
306
+ The shard now carries two new fields:
307
+
308
+ - `bucketHistory` — rolling 14-day array of per-bucket `BucketStats` (errorRate, stallRate)
309
+ - `driftSignals` — `DriftSignal[]` for the current sync, sorted by errorDelta descending
310
+
311
+ `--dry-run --out <dir>` seeds history from the previously written `index.json` in the output directory, so successive local runs accumulate signal without hitting the network.
312
+
313
+ - `agents traces sync --dry-run --out <dir>` computes the derived trace shards
314
+ from your local `sessions.db` and writes them to a directory — no Phoenix
315
+ sign-in, no worker, no upload — so you can verify your real trajectories
316
+ before the hosted path is wired.
317
+ - The per-session drill-down (`sessions/<id>.json`) now emits a `SessionDetail`
318
+ (a `meta` summary — spanMs/turns/tools/errorCount/tokens/cost/outcome/repo —
319
+ plus a plain-language `whereItWentWrong`) that the Phoenix Evals console
320
+ consumes directly, instead of the raw internal trajectory shape.
321
+
322
+ ## 1.22.48
4
323
 
5
324
  - **On your own machine, every Claude run uses your normal login — not the
6
325
  worker setup-token (RUSH-2395).** The credential now follows **device role**,
@@ -89,8 +408,39 @@ type: fix
89
408
 
90
409
  Make opt-in tmux-wrapped runs default to mouse interaction, OSC 52 system clipboard integration, and 20,000 lines of scrollback while preserving the user's tmux configuration.
91
410
 
411
+ - **`agents sessions trace --no-redact` reads honestly in compare and lineage too (RUSH-3077).** The single-trajectory footer already said `Unredacted (local only)` under `--no-redact`, but the compare (`--compare`) and lineage (`--tree`) renderers hardcoded a `Secret-redacted` footer regardless of the flag — claiming a redaction that never happened. Both now share the single-trajectory labelling logic (`redactionLabel`): compare derives it from the two compared trajectories' own `redacted` state (a mixed pair still reads redacted, never a false safe-to-share claim), and lineage takes the trace's redaction flag. Redacted-by-default output is unchanged. Source: `apps/cli/src/lib/session/trajectory-html.ts`, `apps/cli/src/commands/sessions-trace.ts`.
412
+
92
413
  - **BREAKING: the top-level `agents usage` command is removed (RUSH-3079).** It was a second, worse surface for the same data `agents view` already renders — per-account quota/rate-limit usage with the account, version, and auth state beside it — and in practice most harnesses printed `stale` or "does not publish usage data" there. Use `agents view` (optionally `agents view <agent>`, `--refresh` for an explicit collection, `--json` for scripts). The name is retired: `agents usage` fails as an unknown command and never auto-corrects into a live command. The shared usage library behind `agents view` and rotation/backoff is untouched. Source: `apps/cli/src/commands/usage.ts` (deleted), `apps/cli/src/cli/command-registry.ts`, `apps/cli/src/lib/startup/command-registry.ts`.
93
414
 
415
+ - **`agents browser` binds `--device` at start (RUSH-3086, RUSH-3087).** `--device` stays on `agents browser start` and is rejected on later verbs; the starting machine records a local `task → device` index so `type`/`click`/`screenshot --task post` resolve the device without repeating the flag. A verb naming an unknown or killed task exits non-zero and lists the open tasks (real URL + device), and never opens a second browser. `screenshot -o /path/x.png` now writes exactly that path — the daemon still sandboxes its own cache, and the CLI copies out. Source: `apps/cli/src/lib/browser/task-index.ts`, `apps/cli/src/commands/browser.ts`.
416
+
417
+ ---
418
+ type: fix
419
+ ---
420
+
421
+ - **An offline device keeps its spec cell (RUSH-3096).** `agents devices list`
422
+ rendered a down box as a bare `ci-runner-fsn1 linux offline`, because a failed
423
+ probe wrote a row with no hardware facts over the cached one. Cores, total RAM,
424
+ and root-disk capacity now survive an unreachable probe and render beside the
425
+ offline marker — `ci-runner-fsn1 linux 8c 16G 500G offline`. Load, memory, and
426
+ disk-used stay blank (there is no current reading for a box that did not answer),
427
+ and the Fleet capacity footer still counts only reachable devices.
428
+ Source: `apps/cli/src/lib/devices/stats-cache.ts`, `apps/cli/src/commands/ssh.ts`.
429
+
430
+ - **The macOS keychain broker (`Agents CLI.app`) now has a download-on-demand fallback (RUSH-3100).** The signed + notarized keychain helper still ships inside the npm tarball, but a release now also publishes an `Agents CLI.app.zip` GitHub release asset per tagged version. A machine whose tarball lacks the bundle fetches that asset for its exact CLI version on the explicit `agents setup secrets` path (and the upgrade self-heal) — verified by sha256 + codesign + Developer ID Team + notarization before install — reusing the shared helper-download machinery already behind the computer and menu-bar helpers. The secrets hot path (`getKeychainHelperPath`) stays synchronous and network-free: when no bundled helper exists it now fails loud pointing at `agents setup secrets` rather than a bare "reinstall". Unlike the menu-bar helper it pins no designated requirement, since keychain items are gated by the access-group entitlement + biometry, not a DR-keyed grant. Source: `apps/cli/src/lib/secrets/download-keychain.ts`, `apps/cli/src/lib/secrets/install-helper.ts`, `apps/cli/scripts/release.sh`.
431
+
432
+ - **The menu-bar helper can be fetched on demand, mirroring the computer helper (RUSH-3100 Stage N).** `MenubarHelper.app` is now ALSO published as a signed + notarized `MenubarHelper.app.zip` GitHub release asset, so a machine whose npm tarball lacks the bundle can fetch + verify it from the `v<version>` release. `agents menubar enable` / `agents menubar setup` download it when no bundled or local `.app` is present; a downloaded bundle is verified (sha256 + `codesign --verify --deep --strict` + Developer ID Team `2HTP252L87` + a **designated-requirement pin** on bundle id `com.phnx-labs.agents-menubar` + `spctl` notarization) before it is ever installed — the DR pin is what keeps the Accessibility grant alive across upgrades, so a substituted bundle is refused loud, never silently accepted. The `.app` STILL ships inside the npm tarball this release (bundled-first, download-fallback); the download runs only on the explicit enable/setup path, never on the every-invocation startup self-heal, which stays synchronous and no-ops when no local source exists. The download and verify machinery is shared with the computer helper in a new `src/lib/helper-download.ts` (no duplicated code path). Source: `apps/cli/src/lib/helper-download.ts`, `apps/cli/src/lib/menubar/download-menubar.ts`, `apps/cli/src/lib/menubar/install-menubar.ts`, `apps/cli/src/lib/computer/download.ts`, `apps/cli/scripts/release.sh`.
433
+
434
+ - **Fix: the menu-bar helper's Accessibility row now reads "AGI Menu", not "MenubarHelper" (RUSH-3101).** Since the RUSH-3076 grant-persistence fix, the compiled executable was still named `MenubarHelper`, and launchd execs that Mach-O directly — bypassing LaunchServices name resolution — so macOS fell back to `CFBundleExecutable` for both the Accessibility list row (blank icon) and the "would like to control this computer" prompt. The executable inside the bundle is now named `AGI Menu`; the bundle folder (`MenubarHelper.app`), bundle id (`com.phnx-labs.agents-menubar`), and designated requirement are all unchanged, so the existing Accessibility grant is unaffected by the upgrade. Every basename-matching check moved with the rename: `classifyMenubarProcesses`/`installedExecutablePath` (`install-menubar.ts`), the `orphan-reap.ts` protected-service regex, and the Swift side's `SingleInstance.swift`/`ChildProcessSelfTest.swift`, now reading a shared `HelperIdentity.executableName` constant. Source: `apps/cli/menubar/Package.swift`, `apps/cli/menubar/scripts/build.sh`, `apps/cli/menubar/Sources/MenubarHelper/HelperIdentity.swift`, `apps/cli/src/lib/menubar/install-menubar.ts`, `apps/cli/src/lib/tmux/orphan-reap.ts`.
435
+
436
+ - **Fixed a module-initialization cycle that crashed any entry point reaching `helper-download` first.** `helper-download.ts` imported two pure sha256 helpers (`parseSha256Asset`, `sha256File`) from `computer/ssh-tunnel.ts`, whose import graph runs `browser/drivers/ssh` → `browser/chrome` → `secrets/*` → `secrets/download-keychain`, which imports back into `helper-download` while it is still evaluating — before `EXPECTED_TEAM_ID` is bound. The result was a hard `ReferenceError: Cannot access 'EXPECTED_TEAM_ID' before initialization` thrown from `secrets/download-keychain.ts:45`, which took down the `drift-sync` apply path and the `self-heal` isolated-home sweep (both run real subprocesses) and blocked release attestation, since the producer runs the full suite fail-closed. The two helpers now live in a dependency-free leaf module, `lib/sha256-asset.ts`, so nothing in the download path pulls the ssh/browser/secrets graph in at module-init time. Source: `apps/cli/src/lib/sha256-asset.ts`, `apps/cli/src/lib/helper-download.ts`, `apps/cli/src/lib/computer/ssh-tunnel.ts`.
437
+
438
+ - **`agents accounts label` reads like the task: label by `<harness>@<version>`, or pick from a list (RUSH-3126).** Labeling the account behind a specific install used to require knowing its email — `agents accounts label codex work --account you@example.com` — even though the user is looking at `agents view codex`, which is organized by version. The first argument now also accepts `<harness>@<version>`: `agents accounts label codex@0.146.0 work` labels whatever account is signed in there, and the label stays bound to the account identity, so `codex#work` keeps selecting it after that account moves to a newer install. A bare-harness call with several signed-in logins opens an interactive picker (identity · default marker · versions) instead of erroring; the same account signed into multiple versions counts as ONE login, so it never demands a selector. Non-interactive callers keep the loud error, now naming both script-safe forms, and `--account <email|id>` still works. Passing both `@<version>` and `--account` is refused as contradictory. Source: `apps/cli/src/commands/accounts.ts` (`groupLabelIdentities`).
439
+
440
+ - **Managed artifact sharing: sign in and publish with zero Cloudflare setup (RUSH-3135, Phase 1).** Signed-in users (`agents auth login`) publish to the already-live managed endpoint at `share.agents-cli.sh` — the Phoenix access_token from `phoenix-session.json` is the bearer, so there is no R2 bucket, Worker, or write token to provision. `share status`, `share list`, `share revisions`, and `unshare`/`share delete` resolve through the same `resolveShareBackend` seam, so a managed-only user can see and take down what they just published instead of being told to run `agents artifacts setup`. `share analytics` is a scoped P1 exclusion (Cloudflare Web Analytics is BYO): a managed user gets a message naming that, not the generic "Not configured" error. The Worker verifies `Authorization: Bearer` against Phoenix ID (`GET ${PHOENIX_ID_BASE}/api/v1/auth/me`), stamps `customMetadata.owner` with the verified `userId`, and namespaces the R2 key by that userId so one user cannot write another's prefix. `--visibility public|unlisted` (default `public`) replaces the `--unlisted`/`--private` booleans; those flags stay as hidden aliases mapping to `--visibility unlisted`. Unlisted GET now sends `X-Robots-Tag: noindex` (it was already hidden from the gallery/list). `org` visibility is 400 — that is Phase 2. `--meta owner=` / `visibility=` / `expires-at=` is rejected client-side (those keys are Worker-stamped). BYO Cloudflare is unchanged: a static `WRITE_TOKEN` is still honored when the bearer matches it (checked first, so the platform endpoint may set both principals); `AGENTS_SHARE_BACKEND=byo` or a caller-supplied write token forces the BYO path while signed in. Already-provisioned BYO endpoints need `agents artifacts share update` to pick up the new Worker (template hash changed). Source: `apps/cli/src/lib/share/{backend,worker-template,publish,delete,config}.ts`, `apps/cli/src/commands/share.ts`.
441
+
442
+ - **Managed share deploy wires `PHOENIX_ID_BASE` so Phoenix-bearer auth survives `agents artifacts share update` (RUSH-3138).** Cloudflare's script-upload API clears Worker bindings/secrets wholesale; the managed Worker reads `env.PHOENIX_ID_BASE` to verify bearers at `${PHOENIX_ID_BASE}/api/v1/auth/me`, so every update wiped it and Phoenix PUTs 401'd while BYO `WRITE_TOKEN` kept working. The deploy path now re-applies `PHOENIX_ID_BASE` as a `secret_text` after every script upload (same Secrets API as `WRITE_TOKEN`), sourced from the CLI's `PHOENIX_ID_BASE` (env, else the deployed Phoenix ID service). Pure-BYO deploys do not set it. Source: `apps/cli/src/lib/share/{provision,backend}.ts`, `apps/cli/src/commands/share.ts`.
443
+
94
444
  - **Security: the remote browser-control consent gate now covers the whole launch path.**
95
445
  `agents browser remote-control off` (the default) was only enforced on the
96
446
  `browser start` command, but `navigate`, `click`, `screenshot`, `evaluate` and
@@ -110,23 +460,22 @@ Make opt-in tmux-wrapped runs default to mouse interaction, OSC 52 system clipbo
110
460
  Source: `src/lib/browser/service.ts`, `src/lib/browser/remote-control.ts`,
111
461
  `src/lib/browser/ipc.ts`, `src/lib/browser/types.ts`.
112
462
 
113
- - **`agents browser profiles edit` and `profiles scope`.** An existing profile's
114
- description, endpoints, secrets, viewport, and binary could not be changed
115
- without deleting and recreating it — `-d/--description` existed only on
116
- `create`, and `updateProfile()` had no CLI caller at all. `profiles edit <name>`
117
- reuses `create`'s flag spellings (minus `-b/--browser` and `--fleet`, which key
118
- the on-disk profile cache and the store) and validates the merged record, so a
119
- binary edit re-resolves the browser path and a `--target-filter` edit re-checks
120
- the `--electron` gate. `profiles scope <name> <local|fleet>` moves a profile
121
- between the synced store and this machine.
463
+ - **`agents browser profiles edit`.** An existing profile's description,
464
+ endpoints, secrets, viewport, and binary could not be changed without deleting
465
+ and recreating it — `-d/--description` existed only on `create`, and
466
+ `updateProfile()` had no CLI caller at all. `profiles edit <name>` reuses
467
+ `create`'s flag spellings (minus `-b/--browser`, which keys the on-disk profile
468
+ cache) and validates the merged record, so a binary edit re-resolves the
469
+ browser path and a `--target-filter` edit re-checks the `--electron` gate.
122
470
  Source: `src/lib/browser/profiles.ts`, `src/commands/browser.ts`.
123
- - **`profiles doctor` flags a fleet profile with a loopback endpoint.** A
124
- `--fleet` profile whose endpoint is `cdp://localhost:PORT` is evaluated on the
125
- machine running the command, so the name resolved to a different browser on
126
- every box — silently handing an agent a logged-out stranger instead of the
127
- credentialed profile it asked for. Now a failing `scope` check naming the
128
- repair. `ssh://` profiles are unaffected: they address a host, so fleet scope is
129
- correct for them. Source: `src/lib/browser/profiles.ts` (`misfiledFleetProfile`).
471
+ - **`profiles doctor` flags a profile that resolves to the wrong machine's
472
+ browser.** A profile declared by one device but bound to `cdp://localhost:PORT`
473
+ is evaluated on the machine running the command, so the name meant a different
474
+ browser on every box — silently handing an agent a logged-out stranger instead
475
+ of the credentialed profile it asked for. The check now names both the
476
+ declaring device and this one. `ssh://` profiles are unaffected: they address a
477
+ host, so they mean the same browser from anywhere.
478
+ Source: `src/lib/browser/runtime-state.ts` (`identityLoopbackMismatch`).
130
479
  - **Fixed: editing a profile's endpoint could collide with itself.** The local
131
480
  port scan `createProfile` runs was never applied on update, and applying it
132
481
  naively would have failed every edit against the profile's own stored port.
@@ -208,6 +557,48 @@ Make opt-in tmux-wrapped runs default to mouse interaction, OSC 52 system clipbo
208
557
  - New verb `agents sessions stop <id>` on the session-lifecycle axis (beside `detach`/`attach`): it stops the interactive process and tears down its tmux/mux session — reusing `detach`'s exact `stopInteractive` teardown (`kill-session` when tmux-hosted, else SIGTERM→SIGKILL the pid) and resolution (remote sessions stopped over SSH, cloud/team refused) — but does NOT resume it headless. Use `detach` to keep an agent working unattended, `stop` when the work is over. AGI EXT calls it when a user genuinely closes an agent tab so the agent + mux shut down instead of lingering idle. Source: `apps/cli/src/commands/sessions-stop.ts`.
209
558
  - The tmux-wrap `pane-died` hook is now `session_attached`-aware (`AGENT_HOOK_SCHEMA` v6): with a client attached it `detach-client`s as before (so `resolveAfterAttach` reads the exit status, EXEC-23b unchanged); with **no** client attached it `kill-session`s outright. A wrapped agent that exits unattended — a closed terminal, or a `/exit` after the user detached — previously left a dead `remain-on-exit` husk on the socket until the daemon's periodic reap, showing as an orphaned idle session. Source: `apps/cli/src/lib/tmux/session.ts`, `apps/cli/src/lib/exec.ts`.
210
559
 
560
+ - **`agents accounts list` is grouped, aligned, and label-first (RUSH-3053).** The native-logins section was a ragged, ungrouped wall — 15 logins in one flat list, no columns, no labels, opaque ids dumped raw. It now groups logins **by harness** (printed once per group), aligns **label · identity · version** into columns, marks the **default account per harness** with `*`, and prints the `run <harness>#<label>` / `run <harness> --account <name>` selector hints inline. Provider bundles keep their own aligned section. `--json` output is unchanged. This completes RUSH-3053's list-redesign track — the `<harness>#<label>` account selector itself shipped in 1.22.47. The renderer is extracted as the pure, unit-tested `renderAccountList`. Source: `apps/cli/src/commands/accounts.ts`.
561
+
562
+ ---
563
+ type: breaking
564
+ ---
565
+
566
+ Browser profiles now live only in each declaring machine's `devices/<machine>/agents.yaml`. The fleet registry is the read-time union of those files: a name declared once is identity-bearing, while the same name declared by several devices is fungible. Leftover central `browser:` entries are not claimed on first read — run `agents browser profiles claim` on the machine that hosts the browser. Only profiles that machine can actually launch are moved into its device file; the rest stay central until that machine claims them. A configured default that no device declares is now an error on `agents browser start`, not a silent fallback to a logged-out `auto-chrome`. `profiles prune` only considers profiles this device declares (`--fleet` is gone; deleting a peer's declaration is not possible).
567
+
568
+ ---
569
+ type: feat
570
+ ---
571
+
572
+ - **`agents browser profiles list` shows WHERE, not a stored scope.** The
573
+ column is the devices whose own `devices/<machine>/agents.yaml` declares the
574
+ name — true by construction, no field to drift. `--json` adds `devices` and
575
+ `kind` (`identity` when exactly one device declares the name, `fungible` when
576
+ several do). `profiles add` is an alias of `create` and prints
577
+ `Added "<name>" on <device> (port N).`
578
+ Source: `src/commands/browser.ts`.
579
+ - **`profiles doctor` fails `where` on the original comet-local shape.** An
580
+ identity-bearing name whose endpoint is loopback, viewed from a box that is
581
+ not the declaring device, exits non-zero and names both machines. Local
582
+ binary/port/onboarding checks are skipped so they cannot paint a green local
583
+ chromium over someone else's logins. `ssh://` endpoints and fungible names
584
+ are unaffected.
585
+ Source: `src/lib/browser/runtime-state.ts` (`identityLoopbackMismatch`),
586
+ `src/commands/browser.ts`.
587
+
588
+ ---
589
+ type: breaking
590
+ ---
591
+
592
+ `agents browser` no longer asks the caller which machine a profile lives on. The daemon reads the device-declaration registry: a name this machine declares connects locally; a name only other machines declare is tunnelled to a reachable declaring device (and the command output names which one); a name nobody declares fails loudly, listing similar names, and never auto-creates a logged-out local browser. Identity-bearing profiles share one connection (no Electron fork, no second chrome-data). Runtime keys are `<profile>@<device>` instead of `<profile>@endpoint-N`; leftover `@endpoint-N` dirs are renamed onto the new key.
593
+
594
+ - **`browser remote-control off` now gates *attaching* to a running browser, not just *launching* one (RUSH-3064).** PR #2932 moved the consent gate to `BrowserService.start` and the create branch of `resolveOrCreateTask`, closing the implicit-launch bypass — but the two early-return attach paths (an explicit `--task`, or a lone caller-identity match) returned before it, and `tabAdd` reached `createPageTarget` unconditionally. So on a machine whose browser was already running with live tasks, a fleet-remote caller could act in the owner's *authenticated* profile with consent off (`agents browser tab-add --device <box> --task <name>` — and task names are discoverable through the ungated `status`). The gate now sits at the top of `resolveOrCreateTask`, the one chokepoint every page/close verb resolves through, so a fleet-remote attach is refused exactly as a fleet-remote create already was. It stays per-request (`IPCRequest.fleetRemote`), never the daemon's env, so an auto-started daemon that inherited `AGENTS_FLEET_REMOTE=1` never refuses a subsequent local drive. Local drives are unaffected. Source: `apps/cli/src/lib/browser/service.ts`.
595
+
596
+ ---
597
+ type: fixed
598
+ ---
599
+
600
+ A leftover local `comet-local@endpoint-N` chrome-data on a worker is no longer renamed onto the declaring device's key and attached over localhost when the daemon should tunnel. Identity-bearing browsers stay on the declaring machine.
601
+
211
602
  ---
212
603
  type: fix
213
604
  ---
@@ -218,6 +609,28 @@ type: fix
218
609
  type: feat
219
610
  ---
220
611
 
612
+ - **Deletion verbs normalize on `remove` (specific item) and `prune` (bulk
613
+ stale), old spellings kept as hidden aliases.** The CLI had drifted into seven
614
+ different words for "delete" across groups — `remove`, `rm`, `delete`, `gc`,
615
+ `cleanup`, plus `prune`. The canonical pair `remove <name>` / `prune` (already
616
+ the pattern in `commands`, `hooks`, `skills`, `versions`) is now applied to the
617
+ stragglers: `agents route rm` / `agents devices rm` / `agents projects rm` make
618
+ `remove` the primary spelling (`rm` stays an alias); `agents browser profiles
619
+ delete` → `remove` (alias `delete`); and the bulk-stale sweeps `agents browser
620
+ gc`, `agents lease gc`, `agents mailboxes gc`, and `agents routines cleanup`
621
+ become `prune` (each old verb stays a hidden alias). No invocation breaks — the
622
+ old verbs still resolve; they just no longer appear in `--help`. `agents
623
+ secrets remove`/`delete` (key vs whole-bundle) and `agents artifacts share
624
+ delete`/`unshare` are deliberately left as-is: those pairs encode a real
625
+ distinction, not drift. Source: `apps/cli/src/commands/{route,ssh,projects,
626
+ browser,lease,mailboxes,routines}.ts`.
627
+
628
+ - **An interactive `--device` agent now survives a dropped connection, and one blink no longer takes out every tab (RUSH-3125).** A remote interactive agent was a direct child of the sshd session holding its TTY, so a brief network disruption SIGHUPed it and the in-flight turn was lost — while the auto-reconnect layer re-attached on the stated premise that "the agent is still running there." Durability had been gated on the peer's `tmux.enabled`, which defaults **off** (and was off across the whole fleet), even though that toggle is an ergonomics preference about tmux's mouse, clipboard, and scrollback at the local operator's keyboard. The two are now separate: the interactive dispatch exports `AGENTS_REMOTE_INTERACTIVE=1` and the peer wraps the run in a detached tmux session regardless of the local toggle, so the agent outlives the link that carries it. `--raw`, `--no-tmux`, and `AGENTS_NO_TMUX=1` still win, so the escape hatch keeps working over `--device`; a remote run on a peer with **no tmux installed** is now refused with an install hint rather than started as something a blink would kill. Separately, the interactive stream no longer rides the shared `ControlMaster` — `ControlPath=cm-%C` hashes only host/port/user, so every agent tab pointed at a peer shared one master and OpenSSH closes all its channels when it dies (six tabs on one socket, observed live). Probes and fan-outs keep multiplexing. Source: `apps/cli/src/lib/exec.ts` (`shouldWrapInTmux` → `resolveTmuxWrap`), `apps/cli/src/lib/hosts/dispatch.ts`, `apps/cli/src/lib/types.ts`.
629
+
630
+ ---
631
+ type: feat
632
+ ---
633
+
221
634
  Feed attention is now modeled as an explicit lifecycle the CLI reconciles, the foundation for a feed-driven Needs-You surface. `OpenBlock` carries `generation`/`source`/`state`/`sourceCursor` (back-compatible — derived for existing blocks), the answer/continue/clear paths write a resolution tombstone before the block clears so a resolved ask can no longer silently resurrect, and a new pure `reconcileAttention` merges the open-block ledger, session lifecycle, and a CLI-supplied PR signal into one canonical `AttentionItem`.
222
635
 
223
636
  - **Stream and answer the canonical operator feed.** `agents feed watch --json`
@@ -235,6 +648,12 @@ Feed attention is now modeled as an explicit lifecycle the CLI reconciles, the f
235
648
  READMEs and docs updated to match, and license references now name
236
649
  FSL-1.1-Apache-2.0. Source: `apps/cli/src/commands/auth.ts`.
237
650
 
651
+ - **The `git-output` tests no longer fail on any box that exports a git identity (unblocks the release attestation).** `git-output.test.ts`'s fixture authored its commits with `-c user.email=…`, but git's `GIT_AUTHOR_EMAIL` / `GIT_COMMITTER_EMAIL` environment variables **outrank** `-c` config — so on a machine that exports them (developer laptops and the agent fleet do) every fixture commit was silently re-authored to the ambient identity, `collectCommits` matched none of them, and four tests failed with a bare `expected +0 to be 2`. CI never caught it because its runners export no such vars, which made this a local-only failure that also wedged `release-attestation-produce.sh` — that script is fail-closed, so no attestation was written and no release could be cut from an affected box. The fixture now passes identity through the environment it builds for git, and always assigns those four vars so an ambient value can never decide fixture authorship. Verified by causation: the unchanged test is 4-failed with the vars set and 9-passed with them cleared. Source: `apps/cli/src/lib/output/__tests__/git-output.test.ts`.
652
+
653
+ - **`permission_mode_not` was typed, documented, and silently inert — every gated hook fired anyway (RUSH-3116).** The predicate shipped in `shouldFire()` (`src/lib/hooks/match.ts:156`), but nothing at runtime calls that function: the generated hook shim decides fire/skip with a **hand-mirrored Python copy** of the same logic embedded in `src/lib/hooks/cache.ts`, and the mirror was never updated. So `matches: {permission_mode_not: plan}` fell through to `return True` and the hook ran in plan mode regardless — fail-open, so nothing was left unguarded, but the predicate did nothing while still charging a Python gate subprocess per fire. Verified on published 1.22.47: `MATCHES_JSON='{"permission_mode_not":"plan"}'` against a `permission_mode: plan` payload printed `FIRE`, while the positive `permission_mode` control correctly printed `SKIP`. The Python gate now implements the negative form with the same fail-open-on-absence and camelCase (`permissionMode`) handling as the TS reference. Source: `apps/cli/src/lib/hooks/cache.ts`.
654
+
655
+ - **The shim/TS conformance suite now pins itself to the `HookMatches` surface, so a predicate cannot ship half-implemented again.** `cache-matches.test.ts` already cross-checked the shim's real bash+Python decision against `shouldFire()` "so the two implementations can't drift" — but only over a hand-written fixture list, and `permission_mode_not` had no fixture, so the suite stayed green through the drift. A new test reads the `HookMatches` interface out of `types.ts` and fails with the offending key name when any declared predicate has no conformance fixture; `permission_mode_not`, `git_dirty` and `project_has` were all uncovered and now have fixtures. Source: `apps/cli/src/lib/hooks/cache-matches.test.ts`.
656
+
238
657
  - **New hook predicate `matches.permission_mode_not` — gate a hook OFF in one
239
658
  mode without enumerating every other one.** The existing `permission_mode` is
240
659
  an allowlist, so "run everywhere except plan mode" had to be spelled as a list
@@ -253,6 +672,14 @@ Feed attention is now modeled as an explicit lifecycle the CLI reconciles, the f
253
672
  during a planning turn. Source: `apps/cli/src/lib/hooks/match.ts`,
254
673
  `apps/cli/src/lib/types.ts`, `apps/cli/docs/hooks.md`.
255
674
 
675
+ - **Interactive Claude on a keychain-less Linux worker now authenticates from an attached setup-token, instead of falling back to an empty per-version login and printing "looks logged out".** Two gaps combined to break it: (1) `generateVersionedAliasScript` — the `claude@<version>` alias shim `agents run` actually invokes for a pinned/balanced version — carried a **hand-copied subset** of the config env that had dropped the Linux `.oauth_token` fallback block the main shim's `claudeAdapter.shimConfigEnvBash` has (the alias env now reuses that adapter block verbatim via `VERSION_DIR`, so the two can't drift again); and (2) **nothing wrote the `.oauth_token` file** the shim reads — it was referenced only by the read in the shim, never created — so the fallback could never fire. `agents accounts attach <setup-token-account> claude@<version>` now writes the resolved setup-token to `<version-home>/.claude/.oauth_token` (mode 0600) on Linux, a no-op on macOS where the credential lives in the keychain. Headless dispatch was unaffected (it injects the token via `buildExecEnv`); this fixes the interactive path only. Source: `apps/cli/src/lib/installations/shims.ts`, `apps/cli/src/commands/accounts.ts`.
676
+
677
+ - **A regression test now pins the `claude@<version>` alias shim to the `.oauth_token` fallback**, so the alias env cannot silently drop it again. `generateVersionedAliasScript('claude', …)` is asserted to contain the block, alongside the existing main-shim assertion. Source: `apps/cli/src/lib/installations/shims.test.ts`.
678
+
679
+ - **`agents run claude --interactive` on a worker no longer prints a false "⚠ claude looks logged out" banner** when it authenticates from a setup-token. The pre-launch check probed only native credentials (empty on a keychain-less worker), so it warned even though the shim's `.oauth_token` fallback authenticates the run. It now also treats a resolvable per-version setup-token as signed in (no-op on macOS, where the credential is in the keychain). Source: `apps/cli/src/commands/exec.ts`.
680
+
681
+ - **A leased box that can't finish setup fails loud and is stopped, instead of provisioning a billed box that dies at "agents-cli is not set up".** `agents run … --lease` ran `agents setup` on the fresh box with `>/dev/null 2>&1 || true` — output and exit both discarded — so a setup that never left `~/.agents/.system` a git repo was invisible, and the agent then ran straight into the run-side gate (`ensureInitialized`) that refuses with *"agents-cli is not set up"*. The bootstrap now gates on that exact postcondition: it captures `agents setup`'s output, and if the system repo still isn't a git repo it prints the real cause and aborts (exit 97) rather than running the agent. A box **this run provisioned** whose bootstrap failed is stopped (even without `--fresh`/`--keep-box`) so unusable capacity doesn't bill until the idle-GC window; a **reused** box is never auto-stopped. A `~/.agents` config-copy failure is now surfaced instead of silently swallowed. Source: `apps/cli/src/lib/crabbox/lease.ts`.
682
+
256
683
  - **New versions are licensed FSL-1.1-Apache-2.0.** Use, modification, and redistribution remain free for every user. Offering agents-cli as a competing commercial product or service is barred. Each version converts to Apache-2.0 two years after it is made available. Already-shipped Apache-2.0 versions are unchanged. Source: `LICENSE`, `apps/cli/LICENSE`.
257
684
 
258
685
  ---
@@ -261,8 +688,35 @@ type: fix
261
688
 
262
689
  AGI Menu no longer re-prompts for Accessibility on dev machines, and the clip-paste hotkey (Cmd-Shift-V) no longer depends on the grant at all. A locally-built (ad-hoc) menu-bar helper now signs under a distinct `com.phnx-labs.agents-menubar.dev` bundle id, so it can never poison the shipped app's Accessibility grant (macOS keys the grant to the bundle id and revokes it when a same-id binary fails the stored Developer-ID code requirement). An ad-hoc build also can no longer overwrite a healthy Developer-ID install when the recorded owner path vanishes. A release now hard-fails if the shipped helper's designated requirement ever drops the pinned bundle id or Developer ID team, since that would silently revoke every user's grant on upgrade. And when the grant is missing, Cmd-Shift-V now silently copies the `host:path` reference to the clipboard (press Cmd-V) instead of re-showing the system permission modal on every paste — auto-type stays on only where Accessibility is already granted.
263
690
 
691
+ ---
692
+ type: fix
693
+ ---
694
+
695
+ Cmd-Shift-V once again pastes the clip reference in a single keystroke. 1.22.47 decoupled the paste from Accessibility so it never prompted — but that meant an ungranted machine silently fell back to copying the token to the clipboard, so you had to press Cmd-V yourself (two keystrokes). The helper now prompts for Accessibility **once** per launch to restore the one-keystroke auto-type, then falls back to the clipboard silently if you decline (no per-paste nagging). Because dev builds now use a distinct `.dev` bundle id and the release pins the helper's designated requirement, granting it once sticks across upgrades — so a single prompt is all it takes.
696
+
697
+ - **Auto-reconnect now works for every harness, not just Claude (RUSH-3125).** When an interactive `--device` run lost its link, only Claude ever reconnected. Claude is handed a `--session-id` before launch, so its id survives the drop; every other harness's real session id is coined on the peer and was read back over SSH *after* the interactive stream returned — that is, over the link that had just died. The read failed exactly when it was needed, so the run had no id to reconnect with and the process exited straight to a bare shell. A Grok tab therefore showed nothing at all while a Claude tab beside it counted down. Reconnect now falls back to the launcher-minted `AGENT_LAUNCH_ID`, which is known before the connection exists and so cannot be lost with it, and the peer maps it to the real session with a purely local lookup. Codex, Grok, Kimi, Droid, Cursor, OpenCode and the rest recover for the first time. Source: `apps/cli/src/lib/hosts/reconnect.ts` (`pickReconnectTarget`), `apps/cli/src/commands/exec.ts`.
698
+ - **`agents sessions focus --launch-id <id>`** targets a run by the launch id its dispatcher minted instead of a session id, resolving it from that machine's own hook records. This is what makes the reconnect above work with no network at the moment there is none; it is also usable directly when you know the launch id but not the session. An unknown launch id fails loudly rather than opening the picker, so an automated reattach can never strand itself at an interactive prompt. Source: `apps/cli/src/commands/focus.ts`.
699
+ - **The reconnect give-up notices name a command that still exists (RUSH-3125).** When the retry window closed, all three notices told you to run `agents reconnect <id>` — a command deprecated and hidden in favour of `agents sessions resume`, so the advice printed at the one moment you needed something that worked was stale. They now name `agents sessions resume <id>`, or for a launch-id target the peer-side resolver, since no local verb accepts a launch id. Source: `apps/cli/src/lib/hosts/reconnect.ts` (`recoveryHint`).
700
+
701
+ - **Reconnect waits minutes instead of 90 seconds, counts down, and Ctrl-C works (RUSH-3125).** The retry budget was 6 attempts over a 2/4/8/16/30/30 backoff — about **90 seconds**, shorter than a laptop lid close, a Wi-Fi handoff, a VPN or Tailscale re-auth, or a router reboot. Worse, timers are suspended across sleep, so on wake the whole backoff fired back-to-back before the network was up and the budget was gone in seconds. The bound is now a **15-minute wall-clock window** over an unproductive streak, and it still resets the moment a reattach reconnects and holds — so a session that blinks all day keeps reconnecting, exactly as before. The notice counts the window down (`Reconnecting in 30s · 12m14s left · attempt 7 · Ctrl-C to stop`) rather than printing an attempt fraction that no longer says when it stops. **Ctrl-C during the wait** previously hit node's default handler and killed the whole process mid-notice, dropping you at a bare shell with no hint the agent was still alive on the peer; it now exits the loop cleanly (130) and prints where the agent is and how to get back.
702
+ - **The terminal is restored after an interactive remote stream dies (RUSH-3125).** `ssh -tt` leaves the local tty in raw mode, and an agent TUI killed by a dropped link never sends its own exit sequences — so focus reporting and the mode/colour-scheme reports stayed armed and the terminal answered back at a shell that was not expecting it, littering the screen with `^[[?997;1n ^[[I ^[[O`. Those bytes were also still queued on the tty, so the next reattach handed them to the agent as if they had been typed. A `stty -g` snapshot is now taken before the spawn and restored after, the DEC modes a TUI arms are reset (focus, bracketed paste, alt screen, mouse tracking, cursor), and the input buffer is drained. Done in `sshStream` itself, so every caller that opens an interactive remote stream is covered. Source: `apps/cli/src/lib/ssh-exec.ts`, `apps/cli/src/lib/hosts/reconnect.ts`.
703
+
264
704
  - **A release no longer needs `main` to hold still — publish is decoupled from live `main` (RUSH-2395 audit).** `release.sh` used to squash-merge the release PR into `origin/main` and then refuse to publish unless `main`'s tree byte-matched the attested release tree — so any commit that landed on `main` during the release (or a `CHANGELOG` merge conflict) killed it, forcing a ~15-minute quiet window that a busy fleet rarely offers. It now **tags and publishes the attested release commit itself** — the exact tree CI attested and the tarball was packed from — and merges the version-bump PR **asynchronously, after publish, best-effort**. The published bytes are the attested tree by construction, so `main` can churn freely and the bump-merge can be deferred or hand-resolved without ever wedging the release. The attestation of the release-commit tree remains the sole functional gate; the tag push and publish routing stay lease-gated. The catch-up recovery path (registry behind a `main` already at the target version) is unchanged. Source: `apps/cli/scripts/release.sh`.
265
705
 
706
+ - **Fixed: `resolve-target.test.ts` failed on any machine actually named `mac-mini` or `zion`.** The
707
+ fixtures wrote device declarations for two real fleet hostnames and then asserted the resolver
708
+ would tunnel to them. On a box with that `machineId()` the resolver correctly reports the profile
709
+ as locally declared, so the tunnel assertions failed — green on Linux CI, red on both Macs, which
710
+ is where releases run. The suite is now hermetic: fixtures use `peer-alpha`/`peer-zulu`, keeping
711
+ the sort order the "first reachable declaring device" assertions depend on.
712
+ Source: `src/lib/browser/resolve-target.test.ts`.
713
+
714
+ - **A session launched with a skill is no longer named after the skill's install path.** Claude derives its generated `ai-title` from the first turn, so a session opened with `/continue` (or any skill) was named `Base directory for this skill: /home/…/.claude/skills/continue` — the scaffolding line the skill injects, not the task. That name lands in `SessionMeta.label`, which wins on **every** surface for the session's whole life: the interactive `agents sessions` preview header, `--flat` / `--tree`, the `agents feed watch` / `sessions watch` streams, the AGI EXT Fleet row, and the editor tab title. The generated title now goes through `classifyUserPrompt` and collapses to `/<skill>` when — and only when — the classifier reports that injected line. A user's `/rename` (`custom-title`) is never rewritten, including one that merely names a `skills/…` path. Fixed at the one point where the label is composed, so every reader inherits it with no reader-side special-casing. **Not retroactive:** transcript rescans are `(mtime, size)`-gated with no scan-version invalidation, so a session already indexed under the old derivation keeps its stored label until that file next changes and triggers a rescan — tracked in RUSH-3122. Scoped to Claude's `ai-title`; a harness that supplies its own title verbatim (e.g. Cursor's `chatMeta.title`) is unaffected and untouched (RUSH-3123). Source: `apps/cli/src/lib/session/discover.ts` (`finalizeClaudeScan`).
715
+
716
+ - **`agents sessions preview` shows the fan-out a session left behind, on remote rows too (RUSH-3091, RUSH-3095).** The Doing line now carries `N sub-agents · N background shells`. Both counts are persisted at scan time (`sessions.sub_agent_count` / `background_shell_count`, schema v40) rather than only recomputed per render, which is what makes them visible on a **remote or unindexed row** — that path renders from `SessionMeta` alone through `formatMetaOnlyBody`, so it had no events to derive from and silently showed no fan-out at all. A freshly derived count still wins when the caller has parsed events, since the column lags the transcript by one scan. Background-shell detection is a per-harness registry probed against real transcripts, not assumed: claude/kimi flag `Bash` with `run_in_background`, grok flags `run_terminal_command` with `background`; codex and droid record no such concept and cursor persists no tool calls locally, so those render **nothing** rather than `0 background shells` — a zero would assert "none running" where the truth is "cannot know", and `NULL` (not scanned) stays distinct from `0` (scanned, none found) for the same reason. The counts mean "started / left behind", never "still running": a transcript records a start and never a death, the same trap `agents devices ps` documents, so live status stays with `sessions --active`. Source: `apps/cli/src/lib/session/highlights.ts`, `apps/cli/src/lib/session/{db,discover}.ts`, `apps/cli/src/commands/sessions-picker.ts`.
717
+
718
+ - **`agents sessions trace` now reads newer Codex sessions by program, not a wall of "exec".** Codex `gpt-5.6-sol` (codex ~0.145+) runs every shell command inside a JS cell — a `custom_tool_call` named `exec` whose code is `await tools.exec_command({cmd:"git status …"})`. The trace now unwraps that cell to the real shell command, so a Codex trajectory reads `agents 68% · git 15% · scp 13%` with `git fetch origin` / `sed -n …` steps and exit codes, exactly like Claude's `Bash` and Droid's `Execute` — instead of `exec 100%` with every step labeled `exec`. Genuine non-shell cells (`tools.view_image`, raw JS) stay labeled by their code. Source: `apps/cli/src/lib/session/parse.ts` (`extractCodexExecCommand`), `trajectory.ts` (`SHELL_TOOLS`).
719
+
266
720
  - **`agents sessions trace` compares two sessions.** Pass exactly two selectors and the same command renders a **compare**: the two sessions' tool sequences aligned by tool name, the first divergence point (where the runs' tool order stops lining up), the steps each session ran that the other never did, and a per-session summary — in the same three renderings (HTML with stacked lanes on a shared time axis, compact text, and `--json` with `layout: 'compare'`). Three or more selectors, or `--tree`, still fail loud — lineage (a parent + its team) lands in a follow-up PR. Source: `apps/cli/src/lib/session/trajectory-compare.ts`, `apps/cli/src/commands/sessions-trace.ts`.
267
721
 
268
722
  - **`agents sessions trace <id> --tree` renders a team's lineage.** The third layout of the trace surface, after the single trajectory and the two-session compare: the selected session and every session it spawned, drawn as a delegation graph. The edges are read from the session index, not inferred — a teammate's `meta.json` `parent_session_id` (`teamOrigin.parentSessionId`), with the team's agreed-on spawner (`groupSessionsByTeam().spawnerSessionId`) filling in for a teammate whose own record names none, bounded to that run's own spawn window so a second run of the same team name never adopts the first run's teammates; the edge carries which record established it. A node is always a real session: an inline `Task`/`Agent` sub-agent is a step inside one transcript and produces no session, so it is never drawn as a node. Each node carries its handle, harness, role, indexed tool count, span, PR number, and a recency class (`active`/`idle`/`stale`) — recency, not a success verdict, because nothing on a session row records whether the work landed. HTML draws a self-contained inline-SVG graph with clickable per-node summaries; `--text` prints an indented tree; `--json` emits `layout: 'lineage'` with a `lineage: { rootId, nodes, edges, teams, unresolvedParentIds }` block plus the root's trajectory. Selecting a child roots the graph at its topmost ancestor, so the whole team is always shown; a referenced parent outside the scanned pool is reported rather than dropped. Source: `apps/cli/src/lib/session/trajectory-lineage.ts`, `apps/cli/src/lib/session/trajectory-html.ts`, `apps/cli/src/lib/session/trajectory-text.ts`, `apps/cli/src/commands/sessions-trace.ts`.
@@ -271,6 +725,26 @@ AGI Menu no longer re-prompts for Accessibility on dev machines, and the clip-pa
271
725
 
272
726
  - **`agents sessions trace` (alias `agents trace`) — visualize a session's trajectory.** A tool-call waterfall over a real time axis (durations, errors, idle stalls, delegations) instead of scrolling the Markdown wall. One `buildTrajectory()` model, rendered three ways and auto-selected by audience: an interactive HTML page on your interactive host for a person, a compact token-bounded text trajectory for an agent (`--text`, `--errors-only`), and the versioned `sessions-trace` JSON envelope (`--json`) for tools. Single-session in this release; multi-session compare and team lineage follow. Redacted by default, self-contained HTML (no CDN). Source: `apps/cli/src/commands/sessions-trace.ts`, `apps/cli/src/lib/session/trajectory.ts`.
273
727
 
728
+ ---
729
+ type: fix
730
+ ---
731
+
732
+ - **`agents sessions trace` steps read by what the command DID, not their `cd`
733
+ prefix.** A shell step's label now strips the leading throwaway statements —
734
+ `cd <repo> &&`, `export X=Y;`, `set -e`, `source …`, and bare `VAR=val`
735
+ assignments — so `cd /long/path && git fetch origin` renders as
736
+ `git fetch origin`, and a multi-line script whose first line is `cd <repo>`
737
+ shows its real command instead. Before this, most rows in a coding session
738
+ rendered an identical `cd [HOME]/…/<repo>` and the trajectory was unreadable;
739
+ now every row is distinct and agrees with its program badge. Pipelines are left
740
+ intact and a command that is nothing but `cd` is shown as-is. Fixed at the
741
+ model (`buildTrajectory`), so the HTML, text, and `--json` renderings all
742
+ benefit. Source: `apps/cli/src/lib/session/trajectory.ts`.
743
+ - **Trace durations roll into hours past 60 minutes.** An overnight idle gap now
744
+ reads `24h01m` instead of `1441m18s`, in both the HTML and the compact text
745
+ renderings. Source: `apps/cli/src/lib/session/trajectory-html.ts`,
746
+ `trajectory-text.ts`.
747
+
274
748
  - **`agents view` stops printing "usage unavailable" for a harness that reports a plan and no meters.** Grok's collector writes a subscription tier with no usage windows (`{plan: 'SuperGrok Heavy', windows: []}`), and the cache deserializer treated "no fresh windows" as "nothing cached" — so `--refresh` rendered `SuperGrok Heavy`, the very next plain `agents view grok` rendered `usage unavailable`, and reading the row also **deleted** it. Both grok accounts were permanently stuck in the wrong state because the daemon's periodic refresh re-wrote a row that the next read destroyed. A cached row that carries a plan now survives with no windows; a row with no windows, no plan, and no refusal is still dropped, so an all-expired snapshot keeps pruning (RUSH-2858) and a meterless row can never read as a 0% bar or an `available` badge — `deriveUsageStatusFromSnapshot` still returns null for zero windows. Routing keeps the same guarantee from the other side: a windowless snapshot no longer counts as verified usage, so a meterless pool still spreads across its accounts instead of pinning to whichever one ran most recently. Source: `apps/cli/src/lib/accounting/usage.ts`, `apps/cli/src/lib/accounting/rotate.ts` (RUSH-3060).
275
749
 
276
750
  ---
@@ -278,6 +752,18 @@ type: feat
278
752
  ---
279
753
 
280
754
  The watchdog decider is now an agent, not a heuristic script. Every idle session on the machine (its originating task + transcript tail) is handed to ONE `agents run --mode plan` call per tick, which judges each: idle-but-unfinished → nudge that drives it to finish; idle-and-done or genuinely-needs-human → skip. The deterministic pre-filter (`isLikelyTrulyBlocked`, completion/promise regex) and the per-session LLM spawn are gone — one bounded call per tick, only when something is actually idle. A nudge is booked in the cooldown ledger and logged `nudge` ONLY when delivery is confirmed; tmux/iterm/pty self-confirm, while vscodium's fire-and-forget `--open-url` is recorded `undelivered` until the swarm-ext extension acks the verb, ending the phantom-nudge ledger. `agents watchdog history` gains an `undelivered` row; the `--smart` flag is removed (the agent is always the decider). Defaults stay OFF.
755
+ - **Claude usage now comes from normal Claude Code sessions.** Managed Claude
756
+ homes install a status line that records Claude's native five-hour and
757
+ seven-day rate limits for the active account, so `agents view claude` gains
758
+ fresh bars without reading or copying OAuth credentials. The status line also
759
+ shows the hostname and active model and preserves any existing status-line
760
+ command. Compact usage bars now render at one-eighth-cell resolution instead
761
+ of exaggerating every nonzero value to at least 20%, and the trailing activity
762
+ and authentication-probe ages identify which event they measure.
763
+
764
+ ## 1.22.47
765
+
766
+ - **`agents traces sync` pushes derived, redacted trajectories to your Phoenix account (RUSH-3140).** A new `agents traces` command group increments over `sessions.db` via the `file_mtime_ms` gate (only sessions modified since the last sync are uploaded), computes a `SessionTrajectory` for each (steps + gaps + stats, no raw transcript text), applies `redactSecrets()` before PUT, and stores the result under `<userId>/<device>/sessions/<id>.json` in an R2 bucket guarded by Phoenix bearer auth — no public GET path exists anywhere. A per-device index shard (`index.json`) is updated on each run. Three subcommands: `agents traces sync` (incremental push), `agents traces status` (show last sync time), `agents traces open` (open the Phoenix Evals console). The traces Worker uses `cache-control: private, no-store` on every response. Source: `apps/cli/src/lib/traces/{backend,sync,worker-template}.ts`, `apps/cli/src/commands/traces.ts`.
281
767
 
282
768
  ## 1.22.46
283
769