@phnx-labs/agents-cli 1.20.78 → 1.20.82

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 (176) hide show
  1. package/CHANGELOG.md +487 -0
  2. package/README.md +25 -25
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +9 -0
  5. package/dist/commands/activity.js +153 -24
  6. package/dist/commands/apply.js +11 -4
  7. package/dist/commands/cli.js +1 -1
  8. package/dist/commands/commands.d.ts +1 -1
  9. package/dist/commands/commands.js +7 -6
  10. package/dist/commands/defaults.js +3 -11
  11. package/dist/commands/doctor.d.ts +65 -0
  12. package/dist/commands/doctor.js +575 -31
  13. package/dist/commands/events.js +1 -1
  14. package/dist/commands/exec.js +24 -5
  15. package/dist/commands/feed.d.ts +4 -0
  16. package/dist/commands/feed.js +87 -11
  17. package/dist/commands/harness.js +2 -2
  18. package/dist/commands/hooks.js +1 -1
  19. package/dist/commands/import.js +8 -4
  20. package/dist/commands/logs.js +6 -0
  21. package/dist/commands/models.js +1 -1
  22. package/dist/commands/packages.js +6 -6
  23. package/dist/commands/permissions.js +1 -1
  24. package/dist/commands/profiles.js +1 -1
  25. package/dist/commands/pull.d.ts +1 -1
  26. package/dist/commands/pull.js +4 -4
  27. package/dist/commands/repo.d.ts +78 -0
  28. package/dist/commands/repo.js +187 -11
  29. package/dist/commands/routines.js +12 -2
  30. package/dist/commands/rules.js +13 -12
  31. package/dist/commands/secrets.d.ts +3 -1
  32. package/dist/commands/secrets.js +100 -32
  33. package/dist/commands/sessions-inject.d.ts +7 -4
  34. package/dist/commands/sessions-inject.js +18 -18
  35. package/dist/commands/sessions-picker.js +32 -3
  36. package/dist/commands/sessions-resume.d.ts +16 -0
  37. package/dist/commands/sessions-resume.js +14 -13
  38. package/dist/commands/sessions.d.ts +31 -0
  39. package/dist/commands/sessions.js +141 -16
  40. package/dist/commands/set.d.ts +15 -0
  41. package/dist/commands/set.js +79 -0
  42. package/dist/commands/setup-fleet.d.ts +21 -0
  43. package/dist/commands/setup-fleet.js +201 -0
  44. package/dist/commands/setup-secrets.d.ts +20 -0
  45. package/dist/commands/setup-secrets.js +223 -0
  46. package/dist/commands/setup.js +17 -3
  47. package/dist/commands/skills.js +1 -1
  48. package/dist/commands/sync.js +7 -2
  49. package/dist/commands/teams-picker.js +0 -1
  50. package/dist/commands/teams.d.ts +33 -1
  51. package/dist/commands/teams.js +208 -44
  52. package/dist/commands/versions.js +10 -5
  53. package/dist/commands/view.d.ts +1 -0
  54. package/dist/commands/view.js +11 -0
  55. package/dist/index.js +5 -8
  56. package/dist/lib/acp/harnesses.js +0 -7
  57. package/dist/lib/activity.d.ts +156 -0
  58. package/dist/lib/activity.js +282 -0
  59. package/dist/lib/agents.d.ts +9 -2
  60. package/dist/lib/agents.js +72 -63
  61. package/dist/lib/browser/service.js +3 -0
  62. package/dist/lib/browser/types.d.ts +7 -0
  63. package/dist/lib/capabilities.js +6 -2
  64. package/dist/lib/crabbox/lease.js +2 -2
  65. package/dist/lib/crabbox/setup-copy.d.ts +4 -4
  66. package/dist/lib/crabbox/setup-copy.js +4 -4
  67. package/dist/lib/daemon.d.ts +18 -0
  68. package/dist/lib/daemon.js +47 -6
  69. package/dist/lib/devices/fleet.d.ts +1 -1
  70. package/dist/lib/devices/fleet.js +1 -1
  71. package/dist/lib/doctor-diff.d.ts +20 -0
  72. package/dist/lib/doctor-diff.js +6 -1
  73. package/dist/lib/drift.d.ts +31 -11
  74. package/dist/lib/drift.js +58 -7
  75. package/dist/lib/events.d.ts +8 -1
  76. package/dist/lib/events.js +10 -1
  77. package/dist/lib/exec.js +35 -8
  78. package/dist/lib/feed-post.d.ts +28 -1
  79. package/dist/lib/feed-post.js +110 -2
  80. package/dist/lib/fleet/apply.js +8 -1
  81. package/dist/lib/fleet/auth-sync.d.ts +18 -1
  82. package/dist/lib/fleet/auth-sync.js +25 -11
  83. package/dist/lib/fleet/remote-login.js +5 -0
  84. package/dist/lib/git.d.ts +14 -14
  85. package/dist/lib/git.js +41 -44
  86. package/dist/lib/hooks.d.ts +42 -0
  87. package/dist/lib/hooks.js +137 -93
  88. package/dist/lib/hosts/dispatch.d.ts +28 -1
  89. package/dist/lib/hosts/dispatch.js +33 -5
  90. package/dist/lib/hosts/option.js +2 -2
  91. package/dist/lib/hosts/passthrough.d.ts +22 -1
  92. package/dist/lib/hosts/passthrough.js +238 -13
  93. package/dist/lib/hosts/run-target.d.ts +2 -0
  94. package/dist/lib/hosts/run-target.js +1 -0
  95. package/dist/lib/hq/floor.d.ts +1 -1
  96. package/dist/lib/hq/floor.js +6 -1
  97. package/dist/lib/mcp.js +3 -71
  98. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  99. package/dist/lib/menubar/notify-desktop.d.ts +15 -0
  100. package/dist/lib/menubar/notify-desktop.js +41 -5
  101. package/dist/lib/merged-resources.d.ts +11 -0
  102. package/dist/{commands/resources.js → lib/merged-resources.js} +11 -19
  103. package/dist/lib/permissions.d.ts +0 -32
  104. package/dist/lib/permissions.js +4 -157
  105. package/dist/lib/picker.js +1 -1
  106. package/dist/lib/platform/process.d.ts +16 -5
  107. package/dist/lib/platform/process.js +54 -0
  108. package/dist/lib/plugins.d.ts +0 -8
  109. package/dist/lib/plugins.js +4 -110
  110. package/dist/lib/project-resources.js +4 -1
  111. package/dist/lib/refresh.d.ts +5 -3
  112. package/dist/lib/refresh.js +7 -5
  113. package/dist/lib/resources/commands.js +4 -1
  114. package/dist/lib/resources/mcp.js +0 -4
  115. package/dist/lib/resources/permissions.d.ts +1 -1
  116. package/dist/lib/resources/permissions.js +1 -5
  117. package/dist/lib/resources/rules.js +4 -1
  118. package/dist/lib/resources/skills.js +4 -1
  119. package/dist/lib/resources/subagents.js +4 -0
  120. package/dist/lib/resources/types.d.ts +1 -1
  121. package/dist/lib/routines.d.ts +20 -0
  122. package/dist/lib/routines.js +6 -0
  123. package/dist/lib/run-defaults.d.ts +1 -0
  124. package/dist/lib/run-defaults.js +9 -0
  125. package/dist/lib/runner.d.ts +0 -12
  126. package/dist/lib/runner.js +49 -10
  127. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  128. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  129. package/dist/lib/secrets/agent.d.ts +16 -0
  130. package/dist/lib/secrets/agent.js +105 -3
  131. package/dist/lib/secrets/audit.d.ts +46 -0
  132. package/dist/lib/secrets/audit.js +56 -0
  133. package/dist/lib/secrets/bundles.d.ts +11 -8
  134. package/dist/lib/secrets/bundles.js +31 -23
  135. package/dist/lib/secrets/index.d.ts +2 -1
  136. package/dist/lib/secrets/index.js +8 -2
  137. package/dist/lib/secrets/remote.d.ts +75 -2
  138. package/dist/lib/secrets/remote.js +139 -5
  139. package/dist/lib/secrets/sync.js +3 -3
  140. package/dist/lib/session/active.d.ts +110 -26
  141. package/dist/lib/session/active.js +161 -50
  142. package/dist/lib/session/actor-sidecar.d.ts +23 -0
  143. package/dist/lib/session/actor-sidecar.js +101 -0
  144. package/dist/lib/session/db.d.ts +4 -1
  145. package/dist/lib/session/db.js +65 -8
  146. package/dist/lib/session/discover.d.ts +4 -0
  147. package/dist/lib/session/discover.js +13 -0
  148. package/dist/lib/session/hook-sessions.d.ts +9 -0
  149. package/dist/lib/session/hook-sessions.js +57 -8
  150. package/dist/lib/session/pid-registry.d.ts +13 -0
  151. package/dist/lib/session/render.d.ts +2 -0
  152. package/dist/lib/session/render.js +1 -1
  153. package/dist/lib/session/types.d.ts +16 -0
  154. package/dist/lib/staleness/detectors/commands.js +2 -2
  155. package/dist/lib/staleness/detectors/permissions.js +1 -45
  156. package/dist/lib/staleness/writers/commands.js +4 -5
  157. package/dist/lib/staleness/writers/hooks.js +1 -1
  158. package/dist/lib/startup/command-registry.d.ts +1 -2
  159. package/dist/lib/startup/command-registry.js +2 -4
  160. package/dist/lib/subagents-registry.js +5 -12
  161. package/dist/lib/subagents.d.ts +0 -10
  162. package/dist/lib/subagents.js +0 -12
  163. package/dist/lib/teams/agents.d.ts +0 -2
  164. package/dist/lib/teams/agents.js +10 -27
  165. package/dist/lib/teams/index.d.ts +1 -1
  166. package/dist/lib/teams/index.js +1 -1
  167. package/dist/lib/types.d.ts +17 -9
  168. package/dist/lib/usage.d.ts +28 -5
  169. package/dist/lib/usage.js +271 -8
  170. package/dist/lib/versions.js +7 -1
  171. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  172. package/dist/lib/watchdog/watchdog.js +25 -13
  173. package/package.json +1 -2
  174. package/dist/commands/check.d.ts +0 -15
  175. package/dist/commands/check.js +0 -180
  176. package/dist/commands/resources.d.ts +0 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,492 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.20.82
4
+
5
+ - **Codex hook sync no longer leaves startup warnings after upgrades.** The Codex
6
+ hook registrar now prunes hook commands from sibling Codex version homes before
7
+ writing `hooks.json`, so removed versions such as `0.142.0` cannot leave dead
8
+ PreToolUse/Stop handlers that exit `127`. It also writes `SessionEnd` hook
9
+ timeouts at Codex's 3-second limit instead of emitting `timeout: 5` and making
10
+ Codex warn that it is clamping the value on every startup. Source:
11
+ `apps/cli/src/lib/hooks.ts`.
12
+
13
+ - **`agents secrets export`/`list`/`view` now accept `--device`/`--devices` as
14
+ aliases for `--host`/`--hosts`, and a keychain-backed `export --host` push is
15
+ verified.** `--device mac-mini` used to fail with "unknown option" on the secrets
16
+ commands even though the rest of the fleet vocabulary (`agents activity`,
17
+ `agents run --device`) accepts it; it now resolves identically to `--host`. And a
18
+ default keychain-backend push to a macOS host over headless SSH — the sign host a
19
+ Linux-driven release offloads `apple.com` provisioning to — used to land the bundle
20
+ metadata but no readable secret items (the remote login keychain is locked over
21
+ SSH), then fail every later read with the confusing `Bundle 'X' key 'Y': stored
22
+ item '...' not found`. The push now reads the bundle back the way a headless release
23
+ will and **fails loudly** when the keys didn't persist, naming the locked-login-keychain
24
+ cause and steering to `--remote-backend file` (headless-readable) or unlocking the
25
+ remote keychain. This unblocks headless Linux-driven releases. Source:
26
+ `apps/cli/src/commands/secrets.ts`, `apps/cli/src/lib/secrets/remote.ts`.
27
+
28
+ - **`agents sessions resume` shows session previews immediately and opens one tab
29
+ per session by default (RUSH-2023).** The multi-select picker now starts its
30
+ preview pane open whenever the caller supplies preview content; `tab` still
31
+ toggles it. Batch resume now uses full-width tabs across terminal backends,
32
+ with side-by-side two-per-tab packing available explicitly via `--splits`.
33
+ Source: `apps/cli/src/lib/picker.ts`,
34
+ `apps/cli/src/commands/sessions-resume.ts`.
35
+
36
+ - **`agents sessions --active` now distinguishes dead and abandoned sessions
37
+ (RUSH-2066).** The active-session engine computes lifecycle from PID liveness and
38
+ transcript mtime: a dead process reports `closed`, a transcript stale for
39
+ `ABANDONED_STALE_MS` reports `abandoned`, and a live opaque harness still reports
40
+ `running` as its honest floor. The default list, grouped active tallies, and
41
+ `agents hq floor` render `closed` / `abandoned` distinctly, and Factory maps
42
+ `closed` to done and `abandoned` to failed so dead work no longer appears idle.
43
+ Source:
44
+ `apps/cli/src/lib/session/active.ts`, `apps/cli/src/commands/sessions.ts`,
45
+ `apps/factory/src/core/remoteSessions.ts`.
46
+
47
+ - **`agents view` now surfaces the Cursor account and its usage.** Cursor was
48
+ absent from the account view; it now shows the signed-in account (email/authId
49
+ from `~/.cursor/cli-config.json`, token from `~/.config/cursor/auth.json`) and,
50
+ for request-capped (free/legacy) plans, a monthly request bar (`M`) from
51
+ `cursor.com/api/usage`. Usage-based plans have no request cap, so they render the
52
+ account row without a bar. Source: `apps/cli/src/lib/usage.ts`,
53
+ `apps/cli/src/lib/agents.ts`.
54
+
55
+ - **The routines daemon now anchors its working directory to `$HOME` on startup,
56
+ so a deleted launch directory no longer crashes every scheduled routine.** The
57
+ daemon is long-lived and inherited whatever cwd it was launched from — commonly a
58
+ git worktree under `.agents/worktrees/`. When that directory was later removed
59
+ (`git worktree remove`, `rm -rf`), the daemon kept the deleted inode as its cwd
60
+ (a process cannot chdir out of a deleted directory on its own), and every job it
61
+ spawned inherited the dead cwd — `spawnJobAttempt` and command runs pass no
62
+ explicit `cwd`. Bun then failed `getcwd()` at startup and *every* routine died at
63
+ 0 seconds with `ENOENT: Bun could not find a file` (or `The current working
64
+ directory was deleted`) before the agent ran — a fleet-wide routine outage from a
65
+ single removed worktree. `runDaemon` now re-anchors to the home directory once at
66
+ startup (`anchorDaemonCwd`), making the scheduler immune regardless of how it was
67
+ launched. Source: `apps/cli/src/lib/daemon.ts` (`anchorDaemonCwd`, `runDaemon`).
68
+
69
+ - **`agents repo refresh` is deprecated in favor of `agents sync`.** The command is
70
+ now hidden from help and prints a deprecation notice on use, pointing at the
71
+ replacement: `agents sync --local` (reconcile all installed agents, no git) or
72
+ `agents sync <agent>` (one agent). It still runs for now so existing scripts and
73
+ muscle memory don't break — `refresh` was a partial variant of `sync` (it only
74
+ ever materialized the single global-default version, and silently no-op'd for an
75
+ agent with installed versions but no global default), whereas `sync` covers
76
+ every installed version. Internal callers (crabbox bootstrap, the `agents pull`
77
+ redirect, `agents setup` help) now use `agents sync --local`. The underlying
78
+ `refresh()` function stays — it is the reconcile stage behind `agents sync`.
79
+ Source: `apps/cli/src/commands/repo.ts`, `apps/cli/src/lib/crabbox/`.
80
+
81
+ - **`agents view` now shows Grok usage limits.** Grok's network usage endpoints
82
+ 404, so usage is parsed from the local `~/.grok/logs/unified.jsonl` log instead —
83
+ the latest billing-period config and subscription tier render as a `W` window,
84
+ matching the other agents' live-usage display. Source: `apps/cli/src/lib/usage.ts`.
85
+
86
+ - **`agents run --host` now starts in the same project you launched it from, not the remote `$HOME`.** A host run with neither `--cwd` nor `--remote-cwd` sent no `cd` at all, so the remote agent opened in the home directory with no project context — every launch from a repo (including every Factory "Pick Host" tab) began with a manual `cd`. The dispatch now derives a working directory from the local cwd when the caller named none: a cwd under the local home is re-rooted onto the *remote* home (`~/src/x` → the host's `$HOME/src/x`), which is the normal fleet layout where the same checkout sits at the same home-relative path on every box. Because a derived directory is a best-effort mirror rather than something the user asked for, a host that lacks that checkout falls back to its home instead of failing the run; an explicit `--cwd`/`--remote-cwd` is never mirrored, so a directory you named that does not exist still fails loudly. A cwd outside the local home is not mirrored — a path like `/opt/thing` says nothing about the target's filesystem. Source: `apps/cli/src/lib/hosts/dispatch.ts` (`deriveMirroredCwd`, `remoteCdPrefix`), `apps/cli/src/commands/exec.ts`.
87
+
88
+ - **The Cmd+Shift+O quick-dispatch bar is now Plan / Run, and never runs an agent
89
+ in your home directory.** The two spotlight modes were renamed from File
90
+ Ticket / Fix to **Plan** (investigate → file a Linear ticket) and **Run**
91
+ (headless `agents run`). A new repo dropdown is populated from your recent
92
+ session working directories with `$HOME` dropped, and the pick is passed as
93
+ `--cwd` to both modes, so an agent is always scoped to a real repo instead of
94
+ the too-broad home dir; the last-picked repo is remembered. Run now always uses
95
+ `--strategy balanced` (auto load-balance across signed-in versions with
96
+ headroom, skipping rate-limited), and `--name` is seeded from a slug of your
97
+ task text instead of an opaque `quick-<timestamp>`. Source:
98
+ `apps/cli/menubar/Sources/MenubarHelper/PromptPanel.swift`,
99
+ `apps/cli/menubar/Sources/MenubarHelper/AgentsCLI.swift`.
100
+
101
+ - **The keychain Touch ID prompt now names the session that triggered the read
102
+ (RUSH-1971).** When a bundle read pops Touch ID, the operation prompt already
103
+ named the requesting agent, bundle, and reason; it now also carries the
104
+ triggering session's 8-char short-id — e.g. *"Claude is requesting to unlock the
105
+ 'prod' bundle (session e0a1b2c3) for 7 days …"* — so an unexpected prompt is
106
+ attributable when an interactive agent, headless workers, and `secrets exec`
107
+ deploys all run at once. The short-id is derived from the `AGENT_SESSION_ID` the
108
+ exec env already exports; no keychain-helper re-sign is required (the enriched
109
+ string flows through the existing `AGENTS_KEYCHAIN_PROMPT` env). Source:
110
+ `apps/cli/src/lib/secrets/index.ts`, `apps/cli/src/lib/secrets/bundles.ts`.
111
+
112
+ - **`agents teams list` renders from cached team metadata instead of full status
113
+ probes (RUSH-1996).** The list and picker rows now read the team registry plus
114
+ teammate `meta.json` snapshots, so listing teams no longer blocks on remote log
115
+ pulls or unreachable hosts. Full teammate status is still loaded when a user picks
116
+ a team or runs `agents teams status <team>`. Source:
117
+ `apps/cli/src/commands/teams.ts`, `apps/cli/src/commands/teams.test.ts`,
118
+ `apps/cli/docs/teams.md`.
119
+
120
+ - **`agents setup secrets` now guides first-run secrets onboarding (RUSH-1999).**
121
+ The setup command registers a new `secrets` capability wizard that chooses a
122
+ default storage backend (`keychain`, encrypted `file`, or synced `vault`), sets
123
+ the existing default prompt policy (`daily`/`always`, with `never` gated for
124
+ explicit automation use), persists `secrets.backend` so future `agents
125
+ secrets create/import` commands use the selected backend when no backend flag is
126
+ passed, optionally delegates imports to `agents secrets import`, and writes
127
+ setup preferences under `~/.agents/.history/setup`. Source:
128
+ `apps/cli/src/commands/setup-secrets.ts`, `apps/cli/src/commands/secrets.ts`,
129
+ `apps/cli/src/commands/setup.ts`.
130
+
131
+ - **`agents setup fleet` now guides Tailscale device onboarding (RUSH-2000).**
132
+ The setup command registers a new `fleet` capability wizard that verifies
133
+ Tailscale, syncs discovered devices through the existing `agents devices sync`
134
+ path, applies SSH auth with `agents devices set`, optionally writes the
135
+ managed SSH config include, tests connectivity with `agents ssh <device>
136
+ uname`, and can run `agents fleet update` after registration. Source:
137
+ `apps/cli/src/commands/setup-fleet.ts`, `apps/cli/src/commands/setup.ts`.
138
+
139
+ - **`agents feed post` carries artifacts and a project chip, and progress posts
140
+ render rich (RUSH-2013 / RUSH-2014).** `feed post` gains `--attach <path-or-url…>`
141
+ (repeatable): a local file is copied under
142
+ `~/.agents/.history/attachments/<session>/<update>/` so the link survives a
143
+ worktree delete, and a URL is kept as a link — each classified to an
144
+ image/audio/video/file/link kind by extension. Every post is now stamped with its
145
+ project (basename of cwd, worktree-aware) on the activity event itself, so the
146
+ chip shows without a live-session join. A `status.posted` event renders as a
147
+ multi-line update — `agent · session · host · project` chips, the message, an
148
+ attachment row with per-kind glyphs, and a `↳ ag focus/sessions` hint — wherever
149
+ it appears (`feed post` echo, the feed activity lane, `agents feed --filter
150
+ updates`, and `agents activity`).
151
+ - **`agents feed --filter needs|updates|all` (RUSH-2015).** `needs` (default) is the
152
+ open-blocks inbox as before; `updates` shows only deliberate progress posts over
153
+ the local activity timeline (no block pipeline, no remote fan-out); `all` renders
154
+ the blocks then appends the updates view. `--json` under `--filter updates` emits
155
+ the raw `status.posted` events. Source:
156
+ `apps/cli/src/lib/activity.ts`, `apps/cli/src/lib/feed-post.ts`,
157
+ `apps/cli/src/commands/feed.ts`, `apps/cli/src/commands/activity.ts`.
158
+
159
+ - **Fix: actor attribution now actually reaches `agents sessions` and `--active`
160
+ for real runs (RUSH-2018/2019).** Two bugs, found by driving a real `agents run`
161
+ end-to-end: (1) the session index's `actor`/`initiated_by` were kept out of the
162
+ upsert `ON CONFLICT` entirely, so any row indexed *before* its actor sidecar
163
+ landed (an older scanner, or a scan racing the spawn-time write) was locked to
164
+ `NULL` forever — now `COALESCE(existing, incoming)` backfills a null while still
165
+ never clobbering a stored owner; (2) the live `--active` **owner** read only the
166
+ per-pid registry entry, which the SessionStart hook rewrites without an actor, so
167
+ real runs showed no owner — `--active` now falls back to the durable per-session
168
+ actor sidecar. Verified with a real `agents run`: the actor reaches `sessions.db`
169
+ and the `--active` owner resolves. Source: `apps/cli/src/lib/session/db.ts`,
170
+ `apps/cli/src/lib/session/active.ts` (`resolveOwner`).
171
+
172
+ - **Session lists expose the model and richer navigation metadata (RUSH-1981,
173
+ RUSH-1991, RUSH-1992, RUSH-1994).** Static flat rows add a compact model column
174
+ only when the result set has model data, with width sized to that set so an
175
+ 80-column terminal does not wrap. Local CWD and ticket/PR cells are clickable
176
+ in supporting terminals, previews identify browser/computer use and sub-agent
177
+ counts, and `agents sessions --active --json` adds an always-present `prLink`
178
+ key. Existing session indexes migrate to schema v20 and rescan transcripts to
179
+ backfill model data.
180
+
181
+ ## 1.20.81
182
+
183
+ - **Generic `--device all` / `--host all` fleet fan-out for every fleet-aware
184
+ command (RUSH-1969).** The passthrough now treats `all` as a sentinel value on
185
+ `--host`, `--device`, `--hosts`, and `--devices`. For any routable command
186
+ (`view`, `output`, `sync`, `doctor`, `list`, …) it runs `agents <cmd> --json`
187
+ on every registered device concurrently, then renders an OS-grouped roster
188
+ (`●` installed, `○` offline/skipped, `▸ … ← this machine`). Offline and
189
+ no-address devices render as rows instead of hanging the whole run. Add
190
+ `--json` to get a device-keyed object. Commands that already own `--all-hosts`
191
+ (`output`) keep their existing behavior. Source:
192
+ `apps/cli/src/lib/hosts/passthrough.ts`, `apps/cli/src/lib/hosts/option.ts`.
193
+
194
+ - **`agents apply` no longer propagates single-use rotating refresh tokens
195
+ (RUSH-1958).** Droid (WorkOS) credentials use a refresh token that rotates
196
+ server-side on every exchange; copying one credential file across N boxes
197
+ caused the first refresh on any box to invalidate every other holder, collapsing
198
+ the fleet to a single working login. `agents apply` now excludes droid — and any
199
+ future harness added to the shared `SINGLE_USE_ROTATING_REFRESH_AGENTS` set in
200
+ `src/lib/fleet/auth-sync.ts` — from credential propagation. The plan surfaces
201
+ these as `manual login needed (single-use rotating refresh token)` with the
202
+ device name, routing the user to log in on the target box itself. Source:
203
+ `src/lib/fleet/auth-sync.ts`, `src/lib/fleet/apply.ts`,
204
+ `src/commands/apply.ts`.
205
+
206
+ - **Non-Claude remote/tmux agent sessions now surface with their real id
207
+ (RUSH-2007).** `agents sessions --active` and the `agents sessions focus` picker
208
+ dropped every non-Claude tmux session (codex/gemini/kimi/grok/…) that lacked a
209
+ launch-minted id, so a live `agents run --device <host> <agent>` was invisible and
210
+ un-refocusable after an SSH drop. `listTmuxAgentSessions` now backfills the id from
211
+ the **deployed** SessionStart hook's own per-pid record at
212
+ `~/.agents/.cache/state/sessions/<pid>.json` — the CLI previously only read the
213
+ un-deployed session-tracker path (`terminals/sessions/`, empty on the fleet). A
214
+ targeted per-pid read (never a scan of that graveyard dir), freshness-guarded by
215
+ the launch's known start so a reused-pid record can't cross sessions. Source:
216
+ `apps/cli/src/lib/session/hook-sessions.ts`, `apps/cli/src/lib/session/active.ts`.
217
+
218
+ - **Releases publish the CI-tested tree, not a drifted merge.** On a busy default
219
+ branch, unrelated PRs merging during a release PR's CI window made the
220
+ squash-merge tree diverge from what CI actually tested, so `release.sh` refused
221
+ to publish (`merged tree != built tree`) and the release stalled — every attempt
222
+ merged a version bump it could never tag. The publish now tags the exact release
223
+ commit the full matrix went green on (the PR head), letting the intervening
224
+ commits ride the next release; the merge commit is still tagged when its tree
225
+ matches (no drift). The `wait_for_ci_green` gate is unchanged, so the published
226
+ tarball is always a tree the full matrix validated. The tree-comparison decision
227
+ is extracted into `scripts/select-publish-commit.sh` and unit-tested against a
228
+ real git repo. Source: `apps/cli/scripts/release.sh`,
229
+ `apps/cli/scripts/select-publish-commit.sh`.
230
+
231
+ - **Consolidate the observability + inspection commands into one role each; remove
232
+ `check` and `resources` (RUSH-1234).** Two overlapping command clusters had grown
233
+ ambiguous. `agents check` (the CI drift gate) is folded into `agents doctor --check`
234
+ — same drift engine, now with a scriptable exit code. `--check --quiet`,
235
+ `--check --json` (backward-compatible payload: every field the old `check --json`
236
+ emitted, plus additive `unwiredHookVersions`/`sourceBehind`), and `--check --devices`
237
+ all carry over; the standalone `check` command is removed. `agents resources` (the merged first-wins cross-layer
238
+ resource table) is folded into `agents view --merged`; the standalone `resources`
239
+ command is removed. The observability surfaces (`events`, `feed`, `activity`,
240
+ `output`, `sessions`) now have a documented one-role-each taxonomy — `events` is the
241
+ raw unified audit stream, `feed` the cross-agent decisions/status inbox, `activity`
242
+ the human milestone timeline, `output` productivity accounting, `sessions` the live
243
+ roster + transcripts. Running the removed `agents check` / `agents resources` now
244
+ reports `error: unknown command`. Source:
245
+ `apps/cli/src/commands/doctor.ts`, `apps/cli/src/commands/view.ts`,
246
+ `apps/cli/src/lib/merged-resources.ts`, `apps/cli/src/lib/startup/command-registry.ts`,
247
+ `apps/cli/docs/06-observability.md`.
248
+
249
+ - **`agents sessions --active` shows who launched each run (RUSH-2018).** New
250
+ **owner** column on the active-sessions table (and an `owner` field in
251
+ `--active --json`), sourced from the resolved actor stamped at spawn into the
252
+ per-pid registry and onto each teammate record. Displays the actor's short id
253
+ (an email's local-part) and stays honest — an unresolved local run shows `-`,
254
+ never a guessed box owner. The session index (`sessions.db`) also gains
255
+ write-once `actor` / `initiated_by` columns, kept out of the upsert
256
+ `ON CONFLICT` set so a content rescan never clobbers the original owner.
257
+ Source: `apps/cli/src/lib/session/pid-registry.ts`,
258
+ `apps/cli/src/lib/session/active.ts`, `apps/cli/src/commands/sessions.ts`
259
+ (`ownerLabel`), `apps/cli/src/lib/session/db.ts`, `apps/cli/src/lib/exec.ts`.
260
+
261
+ - **`agents sessions` attributes historical sessions to a person, and teams carry
262
+ spawn lineage (RUSH-2019).** Each run now writes a durable `sessionId -> actor`
263
+ sidecar at spawn (`~/.agents/.history/by-session/`, unlike the pruned pid
264
+ registry), and the session scanner joins it while indexing — so the write-once
265
+ `actor` / `initiated_by` columns added in RUSH-2018 populate automatically and the
266
+ durable `agents sessions` listing (not just `--active`) shows who launched each
267
+ session. Teammate spawns inherit the orchestrator's frozen actor and now record a
268
+ `parent_session_id` (the orchestrator's own `AGENTS_SESSION_ID`), so a team traces
269
+ back to the one human who started it and the spawn chain is walkable. Source:
270
+ `apps/cli/src/lib/session/actor-sidecar.ts`, `apps/cli/src/lib/exec.ts`,
271
+ `apps/cli/src/lib/session/db.ts`, `apps/cli/src/lib/teams/agents.ts`.
272
+
273
+ - **Actor provenance reaches events, routines, and browser tasks (RUSH-2020).**
274
+ Completes the actor layer's coverage beyond sessions: every emitted **event** now
275
+ records `actor` + `kind` through the audit origin (so `agents events` stats carry
276
+ a `byActor` breakdown in `agents logs stats`); a **routine** stamps its creator's
277
+ actor id at creation and seeds it into each fired run's env (`AGENTS_ACTOR`), so an
278
+ unattended cron's session and events attribute to the person who scheduled it
279
+ instead of `UNRESOLVED@<host>` — its run records gain `actor` (creator) and
280
+ `triggeredBy` (who kicked off that run); a **browser task** records the `owner` who
281
+ launched it, on the live task and in history. Source:
282
+ `apps/cli/src/lib/events.ts`, `apps/cli/src/lib/runner.ts`,
283
+ `apps/cli/src/lib/routines.ts`, `apps/cli/src/lib/browser/{types,service}.ts`.
284
+
285
+ - **`agents sessions` now shows an accurate working / waiting / idle status for
286
+ every harness, and shows it as text in the default list — not just a glyph.** Two
287
+ gaps are closed. (1) A live non-Claude/Codex agent (grok, droid, gemini, rush,
288
+ kimi, hermes, opencode, antigravity) used to fall through to a blanket `unknown`
289
+ because `findSessionFileForKind` / `computeLiveSignals` only resolved and parsed
290
+ Claude and Codex transcripts — a running Codex or grok session displayed
291
+ `unknown`. Every tracked harness whose transcript is locatable + parseable is now
292
+ wired into the same state engine: `findSessionFileForKind` resolves each kind's
293
+ transcript through the session index (`latestSessionFileForCwd`), and
294
+ `computeLiveSignals` parses it with that harness's own parser and runs it through
295
+ the same `inferSessionState`, so it gets a real `working` / `waiting_input` /
296
+ `idle` the principled way Claude/Codex do. For a genuinely opaque kind (cursor) or
297
+ an unreadable transcript, `resolveFallbackStatus` now reports `running` for any
298
+ live process — **a running agent never displays `unknown`** (that state is reserved
299
+ for the sole un-answerable case: a dead process whose transcript vanished
300
+ mid-read), and a live process is never downgraded to a fabricated `idle`. (2) The
301
+ default `agents sessions` list (flat, tree, and the project overview) showed only a
302
+ colored glyph for live rows; it now also prints the status **word** —
303
+ `working` / `waiting` / `idle` — next to the glyph, the same three states the
304
+ `--active` column shows, with `waiting` the unmistakable "needs you" case. The
305
+ single-session preview (`agents sessions <id> --preview`) leads with the same live
306
+ status line, flagging `← needs you` when the agent is waiting on a question,
307
+ permission, or plan review. Source: `apps/cli/src/lib/session/active.ts`
308
+ (`findSessionFileForKind`, `computeLiveSignals`, `resolveFallbackStatus`),
309
+ `apps/cli/src/commands/sessions.ts` (`liveStatusWord`, `flatSessionRow`,
310
+ `treeSessionRow`, `renderSessionPreview`).
311
+
312
+ - **`agents sessions inject` now addresses VSCodium / Cursor / VS Code and iTerm sessions, not just tmux.** It resolves targets through the same canonical resolver the watchdog uses (`resolveInjectTargetForSession`), so the manual unblock path and the watchdog agree on which sessions are reachable — and a failed resolve now surfaces the precise reason (host/rail) instead of a misleading "not running under tmux". Source: `apps/cli/src/commands/sessions-inject.ts`.
313
+ - **Watchdog brain focuses on driving idle agents to completion, with context-aware, tool-pointing nudges.** The decider prompt now reads the stalled agent's goal first, restates the conclusion it already reached, names the concrete next step (including a tool it forgot it has — `agents computer` / `agents browser` / `agents ssh <mac> "agents computer …"`), splits do-it-yourself from ask-the-human, and treats `idle` as its territory while leaving `waiting` prompts to the user's feed. Design + normative spec: `apps/cli/docs/watchdog.md`, `apps/cli/docs/specifications.md#watchdog`. Source: `apps/cli/src/lib/watchdog/watchdog.ts`.
314
+
315
+ ## 1.20.80
316
+
317
+ - **`agents activity` goes fleet-wide, grouped, and session-enriched.** The activity
318
+ lane was a flat, local-only, newest-first list; it now shows progress-so-far across
319
+ the whole fleet — who did what, where, on which project, for which ticket. New flags:
320
+ `--devices-all` (alias `--hosts-all`) fans the same `activity --json` payload out to
321
+ every reachable device (feed-style, via `gatherRemoteAgentsJson`) and merges each
322
+ peer's stream host-tagged; `-H/--host` / `--device` scope to specific boxes; `--local`
323
+ forces local-only (still the default). `--group-by project|device|agent` buckets the
324
+ stream (e.g. per project, what each agent did and for which ticket) and `--filter
325
+ <text>` narrows by project/device/agent/event/ticket. Each item is enriched by JOINING
326
+ to live sessions — the resolved project (repo/worktree slug from cwd), the execution
327
+ host (`provenance.host`), and the Linear ticket (`ActiveSession.ticket`) — never by
328
+ re-parsing transcripts. Milestone tiering (`--milestones`) and the default collapse are
329
+ unchanged, and `--json` stays a mergeable per-host payload (now carrying the enriched
330
+ fields). Source: `apps/cli/src/commands/activity.ts`, `apps/cli/src/lib/activity.ts`
331
+ (`enrichActivityEvents`, `mergeActivityEvents`, `parseActivityPayload`, `groupActivity`,
332
+ `filterActivityEvents`, `projectFromCwd`).
333
+
334
+ - **Add `agents set` — a short front door for per-version run defaults.** `agents set claude@2.1.220 --model opus-5` pins the default model (and/or `--mode`) that `agents run` uses for that agent version. It reads and writes the same store as `agents defaults run set` (`agents.yaml` -> `run.defaults`), so the two stay consistent. Bare `agents set` lists every default; `agents set <selector>` shows one. Source: `apps/cli/src/commands/set.ts`.
335
+
336
+ - **`agents doctor` now reads as a triaged health report, not neutral status.**
337
+ The verdict was terse status text ("Verdict: 1 divergent, source ~/.agents 16
338
+ commits behind…") a user had to decode. It is now a severity-ranked health block
339
+ that leads with what is unhealthy, why it matters, and the exact fix — one row
340
+ per finding, tagged with a restrained terminal glyph (`✓` `✗` `⚠` and a subtle
341
+ info dot, colored via chalk to match the man-page voice):
342
+ ```
343
+ Claude@2.1.220
344
+ ✗ unhealthy — 3 issues (1 critical · 2 warnings)
345
+
346
+ ✗ critical ask-user-question-guard — on disk but not wired into settings.json; the hook never fires
347
+ → agents sync claude@2.1.220 --yes
348
+ ⚠ warning ~/.agents — 16 commits behind origin/main; you're running stale config
349
+ → agents repo pull user
350
+ ⚠ warning 11-activity-log — differs from source
351
+ → agents doctor claude@2.1.220 --fix
352
+
353
+ heal what's auto-fixable: agents doctor claude@2.1.220 --fix
354
+ ```
355
+ A clean install collapses to one green line —
356
+ `✓ healthy — 34 resources reconciled · hooks wired · sources current`. Each
357
+ finding carries an agent-agnostic **severity**: **critical** (silent breakage —
358
+ an unwired hook, a missing/unparseable `settings.json`, a MISSING resource),
359
+ **warning** (stale/drift — a source layer behind origin, a DIVERGENT resource, a
360
+ stale/never-synced version), or **info** (an orphan/EXTRA resource →
361
+ `agents prune cleanup`). Both surfaces get the same treatment: the target report
362
+ `agents doctor <agent>@<version>` and the bare `agents doctor` overview, which
363
+ now opens with a `Health` banner aggregated across every installed version. The
364
+ existing per-resource detail rows are kept — the health block layers on top of
365
+ them as the verdict. `--json` gains a `verdict` field (target mode) and a
366
+ `health` field (overview), each carrying `severity`/`category`/`subject`/
367
+ `impact`/`fix` per issue; the existing `summary`/`kinds`/`hookWiring`/
368
+ `sourceBehind`/`sync`/`orphans` fields are unchanged. Source:
369
+ `apps/cli/src/commands/doctor.ts` (`computeVerdict`, `computeOverviewHealth`,
370
+ `healthBlockLines`, `renderHealthBlock`, `verdictIsAutoFixable`).
371
+
372
+ - **The daemon's `MenubarHelper --notify` one-shots can no longer pile up in the
373
+ menu bar.** Each routine notification (start/finish/overdue/heal) spawned a
374
+ fresh, detached, unsupervised `MenubarHelper --notify` process; on a stalled
375
+ delivery — a locked screen or a WindowServer/XPC hiccup — the helper's runloop
376
+ spin never reached its deadline and the process hung indefinitely, so duplicate
377
+ "Agents" instances accumulated. The one-shot is now bounded by two independent
378
+ watchdogs: `runOneShot` arms a background-thread force-exit at 3s (off the main
379
+ queue, so a wedged main thread can't starve it — unlike the 0.6s runloop
380
+ deadline it backs up), and the Node spawner (`spawnDetachedQuiet`) SIGKILLs the
381
+ child at 4s if it never self-exits. A notifier that posts normally (the common
382
+ sub-second path) is untouched; only a genuinely hung one is killed. Source:
383
+ `apps/cli/menubar/Sources/MenubarHelper/PromptPanel.swift` (`Notifier.runOneShot`),
384
+ `apps/cli/src/lib/menubar/notify-desktop.ts` (`spawnDetachedQuiet`,
385
+ `NOTIFY_TIMEOUT_MS`).
386
+
387
+ - **Removed the dead `commitOwnDeviceMeta` auto-commit from the pull path.** It
388
+ committed this machine's `devices/<host>/agents.yaml` pin snapshot to the user
389
+ repo's `main` on nearly every `pullRepo`, without pushing — so `main` diverged
390
+ N-ahead per machine and wedged `agents sync` across the fleet. Now that
391
+ per-device pins are gitignored (they are local runtime state — written by
392
+ `writeMetaUnlocked`, read on-disk by pinned-strategy resolution and the shim),
393
+ the function only ever no-ops, so it and its sole `pullRepo` call are deleted
394
+ along with their tests. `--strategy balanced` never read pins; the only behavior
395
+ removed is the never-reached auto-commit. Source: `apps/cli/src/lib/git.ts`
396
+ (`pullRepo`), `apps/cli/src/lib/git.test.ts`.
397
+
398
+ - **Remove Forge and hard-deprecate Gemini (RUSH-2060).** ForgeCode is no longer
399
+ an `AgentId`, install target, resource-sync target, subagent target, MCP target,
400
+ or permissions target. Gemini remains a legacy id so existing sessions/config can
401
+ still be read, but it is no longer a managed harness: `agents add gemini`,
402
+ `agents import gemini`, and `agents sync gemini` now fail and point users to
403
+ Antigravity. Gemini is also excluded from capability-driven resource writers,
404
+ staleness detectors, import choices, teams choices, model choices, fleet auth
405
+ sync, and plugin/MCP/permissions/subagent sync. Source:
406
+ `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/types.ts`,
407
+ `apps/cli/src/lib/capabilities.ts`, `apps/cli/src/commands/{versions,import,sync}.ts`,
408
+ and the resource writers under `apps/cli/src/lib/`.
409
+ (RUSH-2060)
410
+
411
+ - **Every agent secret access and unlock is now captured in the raw event stream and the audit log.** After the recent relaxation that lets agents read and unlock bundles more freely, `agents events --module secrets` now surfaces two typed, value-free events for the complete access picture. `secrets.get` records every path that resolves a secret VALUE out of a bundle (`run --secrets`, `secrets exec`/`export`, `view --reveal`, raw `get <item>`, `sync push`, remote `bundle@host`), and a new `secrets.unlocked` records the deliberate `agents secrets unlock` grant into the broker/durable session — the longer-lived grant a per-read event does not capture, carrying its TTL and the harness scope it was granted to (`*` = global). Every record is tagged with the resolving `agent` scope and lands at audit level in the append-only `~/.agents/events.jsonl` audit trail, but is non-milestone so it does not clutter `agents activity` / `agents feed`. The resolved value is never written — only bundle name, key NAMES, and counts. All value-read/unlock audits now funnel through one canonical `emitSecretAudit` helper (`apps/cli/src/lib/secrets/audit.ts`), wired into `lib/secrets/bundles.ts`, `commands/secrets.ts` (reveal / raw get / unlock), `lib/secrets/sync.ts`, and `lib/secrets/remote.ts`; the new event type is registered in `lib/events.ts`. Source: `apps/cli/src/lib/secrets/audit.ts`, `apps/cli/src/lib/events.ts`, `apps/cli/src/commands/secrets.ts`.
412
+
413
+ ## 1.20.79
414
+
415
+ - **A daemon bounce no longer orphans the secrets broker.** Installing agents-cli ran `stopDaemon()`, which sent SIGTERM, scheduled its hard-kill escalation on a `setTimeout`, and cleared the daemon pid file immediately — without waiting for the process to actually exit. In a short-lived process like the npm postinstall the timer never fired at all, and the cleared pid file made `isDaemonRunning()` report false, so `startDaemon()` launched a second daemon alongside the live one. Its hosted broker then found the socket in use, missed one 700ms ping against the busy owner, unlinked the live socket and rebound — leaving the first broker running with every unlocked bundle in RAM that no client could reach. On a machine with two installs (nvm + homebrew) this reproduced on every upgrade: `lsof` showed two processes on one socket path at two different kernel socket addresses. `stopDaemon` now waits for the process to actually stop serving before clearing the pid file (and escalates to a tree-kill only if it does not), and `bindBrokerSocket` probes an in-use socket several times and refuses to reclaim it while a live process still owns the broker pid file, surfacing a clear error instead of silently starting a second broker. A zombie counts as exited — it holds no socket — so a daemon that is the caller's own child is not hard-killed after it has already gone. The socket owner is recorded in a dedicated `agent.owner` file rather than the standalone service's `agent.pid` single-instance claim, so the signal is present for the daemon-hosted broker (the primary configuration) without making a losing standalone service exit into a launchd restart loop; `ensureAgentRunning`'s one-off fallback and `teardownStaleBroker` also wait for a broker to stop serving before unlinking its socket and ownership record, instead of destroying the evidence the check depends on. Source: `apps/cli/src/lib/platform/process.ts` (`waitForExit`, `hasExited`), `apps/cli/src/lib/daemon.ts` (`stopDaemon`), `apps/cli/src/lib/secrets/agent.ts` (`ownerPath`, `brokerPidAlive`, `releaseBrokerPid`, `bindBrokerSocket`, `ensureAgentRunning`, `teardownStaleBroker`).
416
+
417
+ - **`agents doctor` now checks hook WIRING, not just hook files — and treats a
418
+ stale source layer as unhealthy.** Two blind spots let a version home read
419
+ "healthy" while its hooks were dead. (1) Doctor only compared hook FILES against
420
+ source, never that `settings.json` actually references each hook in the right
421
+ event array — so a hook whose script was byte-identical to source but never
422
+ wired into `PreToolUse`/`Stop`/… reported `ok` and silently never fired
423
+ (reproduced on `yosemite-s1`: `Claude@2.1.207` printed `hooks 32 items 32 ok`
424
+ while its `settings.json` PreToolUse array omitted `ask-user-question-guard.sh`).
425
+ Doctor now inspects the version's native `settings.json` (Claude-family: claude,
426
+ droid), verifying each hook per `(event, matcher)` group, and reports a
427
+ present-but-not-wired hook as `UNWIRED <hook> event=<event> matcher=<matcher>`,
428
+ counted against the verdict; a missing/unparseable `settings.json` is surfaced
429
+ too. `--fix` re-wires via the same `registerHooksToSettings` path `agents sync`
430
+ uses. (2) A source layer behind `origin/main` means the home is reconciled
431
+ against stale truth, yet the "N commits behind" fact was a buried preamble while
432
+ the verdict still said healthy — it now flips the per-version verdict to unhealthy
433
+ with the `agents repo pull` remediation. Both checks run in every mode, not just
434
+ `agents doctor <agent>@<version>`: bare `agents doctor` (overview) and the CI gate
435
+ `agents check` now flag a present-but-unwired hook and a behind-origin source
436
+ layer, and `agents check` exits non-zero on them. Source:
437
+ `apps/cli/src/lib/hooks.ts` (`checkVersionHookWiring`), `apps/cli/src/lib/drift.ts`
438
+ (`checkSyncStatus`/`computeSourceBehind`/`computeDrift`),
439
+ `apps/cli/src/lib/doctor-diff.ts`, `apps/cli/src/commands/doctor.ts`
440
+ (`computeVerdict`), `apps/cli/src/commands/check.ts`, `apps/cli/src/lib/git.ts`
441
+ (`commitsBehindUpstream`).
442
+
443
+ - **`agents repo pull` reconciles a diverged repo instead of wedging on it.** It ran
444
+ `git merge --ff-only`, which refuses *any* divergence — conflict or not — so a
445
+ single local commit permanently blocked every later pull with nothing actually in
446
+ conflict. Since `pullRepo` itself auto-commits the machine's own
447
+ `devices/<host>/agents.yaml` before pulling, every device eventually created that
448
+ commit and stopped receiving updates: on one fleet, nine machines sat 9 commits
449
+ behind and merged rule changes never reached any of them. It now rebases, which is
450
+ what its own documentation has always described. Per-device paths are disjoint, so
451
+ they replay cleanly. A genuine conflict aborts the rebase and rolls the checkout
452
+ back untouched, so a failed pull can never leave the repo detached, mid-rebase, or
453
+ with conflict markers in live config; a rebase already in progress is reported as
454
+ itself rather than as a dirty tree.
455
+ - **`agents repo pull` / `push` exit non-zero when a repo fails.** Both printed a
456
+ failure line and returned 0, so `agents fleet run "agents repo pull user"` reported
457
+ `11 ok` across a fleet that pulled nothing. Any automation gating on the exit code
458
+ read a total no-op as success. Matches `agents sync <repo>`, which already did this.
459
+
460
+ - **`agents repo status` reports across the fleet.** New `--devices-all` (alias
461
+ `--hosts-all`) fans `repo status`/`repo list` out to every reachable device and
462
+ renders one aggregated table (device · repo · sync · changes); `--devices <who>`
463
+ (alias `--hosts`) takes `all` or a comma-separated device list. Unreachable peers
464
+ are skipped with a clear marker, never failing the command, and a single
465
+ `--device`/`--host` still streams that one box as before. Source:
466
+ `apps/cli/src/commands/repo.ts`.
467
+
468
+ - **Routines now always authenticate as the machine they run on, never on an inherited Claude token.** The daemon was already forbidden from *injecting* a Claude OAuth token into a routine, but nothing stopped it *inheriting* one: `buildExecEnv` spreads the ambient `process.env`, and `sanitizeProcessEnv` only strips loader/interpreter variables, never credentials. So on any box whose daemon environment happened to carry `CLAUDE_CODE_OAUTH_TOKEN` — a provisioned fleet machine, a shell that exported it — every routine spawn silently ran on that one shared, rotating token instead of the host's own login. That is the fleet-wide-logout path the no-token design exists to prevent, reached by inheritance rather than injection: when the server rotates a refresh token, every other holder drops to "run /login". No CI runner has a token to inherit, so the existing test passed everywhere and the leak only appeared on a real machine (it surfaced on the release VM, halting a release). `buildRoutineSpawnEnv` now drops the variable, and the routine still authenticates exactly as before — `CLAUDE_CONFIG_DIR` is pinned to that box's per-account version home, so a routine uses whatever agent login is set up there and needs no token of its own. Source: `apps/cli/src/lib/runner.ts` (`buildRoutineSpawnEnv`).
469
+
470
+ - **The `daily` secrets policy is now called `hold`, because it was never daily.** The default prompt policy holds a bundle for `secrets.agent.holdMs` — 7 days out of the box — yet it was named `daily`, so `agents secrets policy --help` read as "you will be asked once a day" while the code comment beside it said "one Touch ID per ~7d". Both the CLI help and `docs/secrets.md` had resorted to apologising for it in prose ("Name is historical", "Despite the name, it is not tied to one calendar day"), which is a name stating something false, not a name that is merely unclear. It is not one day, not one session, and not any fixed period — it is the configured hold window, so it is now named for that. **`daily` and the wire token `session` remain accepted everywhere** (`agents secrets policy <bundle> daily`, `secrets.policy: daily` in agents.yaml, and the `tier: session` key already written into every bundle on every synced machine), so no config or stored bundle changes behaviour on upgrade. **One machine-readable surface does change**: `agents secrets list --json` and `agents secrets view --json` now report `"policy": "hold"` where a default-tier bundle previously reported `"daily"`. Anything matching on that string needs updating — the CLI keeps accepting `daily` as input, but it no longer emits it, because a JSON field that reports a name the CLI itself has retired is a worse trap than a one-line change. The help text now also states what the tier actually depends on: the hold is a property of the running broker plus the durable session, not of the stored keychain item, so a broker that is down degrades `hold` to prompt-every-read; only `never` is prompt-free independently of the broker. Source: `apps/cli/src/lib/secrets/bundles.ts` (`SecretsPolicy`, `parsePolicy`, `secretsDefaultPolicy`), `apps/cli/src/commands/secrets.ts` (`parsePolicyOpt`, `policy` command help), `apps/cli/src/lib/secrets/index.ts` (legacy token mapping for the signed helper), `apps/cli/docs/secrets.md`.
471
+
472
+ - **Claude usage/probe reads can authenticate with a file-based setup-token
473
+ instead of the login keychain — no Touch ID.** On macOS, reading a Claude
474
+ account's usage went through Claude Code's ACL-bound
475
+ `Claude Code-credentials-<hash>` keychain item (`loadClaudeOauth` →
476
+ `/usr/bin/security`), popping a Touch ID sheet on every cold read — per account,
477
+ roughly every 8h, and again on the routines daemon's 3-minute auth-health probe
478
+ (`probeLocalFleetAuth`), so `ag view` and the background warm both prompted.
479
+ `loadClaudeOauth` now first resolves a per-account `claude setup-token` from the
480
+ reserved **file-based** `auth` secrets bundle (keyed by account email as
481
+ `CLAUDE_CODE_OAUTH_TOKEN_<slug>`); when present, the usage endpoint is
482
+ authenticated with that long-lived, non-rotating token and the keychain is never
483
+ touched — killing the prompt. This applies only to the read-only usage/probe
484
+ callers (`accessTokenCache`); the full-credential run/export path (which needs the
485
+ refresh token) is unchanged, and an account with no provisioned setup-token still
486
+ falls through to the keychain for now. Keyed strictly per-account (never a bare
487
+ shared key) so one account's token can't be misapplied to another. Source:
488
+ `apps/cli/src/lib/usage.ts`; design: `docs/design/credential-management.md`.
489
+
3
490
  ## 1.20.78
4
491
 
5
492
  - **`agents sessions <uuid>` now resolves a remote session exactly, across the