@phnx-labs/agents-cli 1.22.34 → 1.22.37

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 (493) hide show
  1. package/CHANGELOG.md +672 -2
  2. package/README.md +82 -35
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.d.ts +15 -0
  5. package/dist/bootstrap.js +1211 -0
  6. package/dist/commands/accounts.d.ts +4 -11
  7. package/dist/commands/accounts.js +159 -27
  8. package/dist/commands/attach.js +17 -4
  9. package/dist/commands/audit.d.ts +8 -8
  10. package/dist/commands/audit.js +57 -47
  11. package/dist/commands/browser.js +15 -11
  12. package/dist/commands/cloud.js +1 -1
  13. package/dist/commands/commands.js +0 -11
  14. package/dist/commands/computer-actions.js +33 -0
  15. package/dist/commands/computer-sessions-picker.d.ts +21 -0
  16. package/dist/commands/computer-sessions-picker.js +141 -0
  17. package/dist/commands/computer.d.ts +16 -0
  18. package/dist/commands/computer.js +37 -0
  19. package/dist/commands/config.d.ts +17 -0
  20. package/dist/commands/config.js +401 -0
  21. package/dist/commands/cp.d.ts +84 -0
  22. package/dist/commands/cp.js +231 -0
  23. package/dist/commands/daemon.js +243 -14
  24. package/dist/commands/detach-core.d.ts +1 -1
  25. package/dist/commands/detach-core.js +12 -2
  26. package/dist/commands/detach.js +1 -1
  27. package/dist/commands/doctor.js +64 -5
  28. package/dist/commands/events.d.ts +24 -12
  29. package/dist/commands/events.js +144 -28
  30. package/dist/commands/exec.js +85 -41
  31. package/dist/commands/feed.d.ts +1 -1
  32. package/dist/commands/feed.js +16 -8
  33. package/dist/commands/focus.d.ts +33 -1
  34. package/dist/commands/focus.js +177 -17
  35. package/dist/commands/funnel.d.ts +1 -1
  36. package/dist/commands/go.js +9 -8
  37. package/dist/commands/harness-wizard.d.ts +1 -0
  38. package/dist/commands/harness-wizard.js +18 -20
  39. package/dist/commands/harness.d.ts +2 -0
  40. package/dist/commands/harness.js +59 -26
  41. package/dist/commands/hooks.js +0 -11
  42. package/dist/commands/inspect.d.ts +94 -1
  43. package/dist/commands/inspect.js +866 -40
  44. package/dist/commands/lease.d.ts +4 -4
  45. package/dist/commands/lease.js +6 -6
  46. package/dist/commands/login.js +0 -13
  47. package/dist/commands/logs.d.ts +2 -2
  48. package/dist/commands/logs.js +2 -2
  49. package/dist/commands/mcp.js +7 -2
  50. package/dist/commands/memory.js +7 -2
  51. package/dist/commands/message.d.ts +1 -1
  52. package/dist/commands/message.js +37 -6
  53. package/dist/commands/models.js +4 -0
  54. package/dist/commands/monitors.d.ts +0 -1
  55. package/dist/commands/monitors.js +199 -16
  56. package/dist/commands/open.d.ts +18 -0
  57. package/dist/commands/open.js +57 -0
  58. package/dist/commands/packages.js +2 -107
  59. package/dist/commands/plugins.js +6 -3
  60. package/dist/commands/profiles.d.ts +1 -0
  61. package/dist/commands/profiles.js +16 -160
  62. package/dist/commands/projects.d.ts +28 -0
  63. package/dist/commands/projects.js +125 -5
  64. package/dist/commands/reconnect.js +9 -3
  65. package/dist/commands/repo.js +42 -1
  66. package/dist/commands/resource-view.d.ts +17 -0
  67. package/dist/commands/resource-view.js +36 -14
  68. package/dist/commands/resume.js +9 -2
  69. package/dist/commands/routines.d.ts +0 -2
  70. package/dist/commands/routines.js +473 -289
  71. package/dist/commands/secrets.js +52 -9
  72. package/dist/commands/sessions-export.d.ts +47 -0
  73. package/dist/commands/sessions-export.js +116 -6
  74. package/dist/commands/sessions-import.d.ts +17 -0
  75. package/dist/commands/sessions-import.js +114 -6
  76. package/dist/commands/sessions-migrate.d.ts +27 -0
  77. package/dist/commands/sessions-migrate.js +50 -9
  78. package/dist/commands/sessions-picker.js +23 -4
  79. package/dist/commands/sessions-resume.d.ts +5 -3
  80. package/dist/commands/sessions-resume.js +30 -8
  81. package/dist/commands/sessions-watch.d.ts +2 -0
  82. package/dist/commands/sessions-watch.js +53 -0
  83. package/dist/commands/sessions.d.ts +66 -3
  84. package/dist/commands/sessions.js +298 -47
  85. package/dist/commands/set.d.ts +1 -1
  86. package/dist/commands/set.js +2 -2
  87. package/dist/commands/setup-browser.js +2 -2
  88. package/dist/commands/setup-mine.js +0 -1
  89. package/dist/commands/setup-preferences.js +1 -1
  90. package/dist/commands/setup-share.js +34 -4
  91. package/dist/commands/setup.d.ts +5 -0
  92. package/dist/commands/setup.js +26 -0
  93. package/dist/commands/share.d.ts +85 -0
  94. package/dist/commands/share.js +379 -8
  95. package/dist/commands/skills.js +0 -11
  96. package/dist/commands/snapshot.js +1 -1
  97. package/dist/commands/ssh.d.ts +18 -0
  98. package/dist/commands/ssh.js +549 -210
  99. package/dist/commands/sync.d.ts +5 -0
  100. package/dist/commands/sync.js +212 -32
  101. package/dist/commands/teams.d.ts +13 -0
  102. package/dist/commands/teams.js +187 -30
  103. package/dist/commands/tickets.d.ts +2 -0
  104. package/dist/commands/tickets.js +43 -0
  105. package/dist/commands/update.js +2 -4
  106. package/dist/commands/usage.js +5 -3
  107. package/dist/commands/versions.js +11 -5
  108. package/dist/commands/view.js +15 -7
  109. package/dist/commands/webhook.d.ts +2 -2
  110. package/dist/commands/webhook.js +7 -7
  111. package/dist/index.d.ts +18 -2
  112. package/dist/index.js +53 -1264
  113. package/dist/lib/account-catalog.d.ts +18 -0
  114. package/dist/lib/account-catalog.js +38 -0
  115. package/dist/lib/account-provider-registry.d.ts +18 -0
  116. package/dist/lib/account-provider-registry.js +59 -0
  117. package/dist/lib/account-registry.d.ts +40 -0
  118. package/dist/lib/account-registry.js +239 -0
  119. package/dist/lib/account-schema.d.ts +44 -0
  120. package/dist/lib/account-schema.js +91 -0
  121. package/dist/lib/account-state-service.d.ts +21 -0
  122. package/dist/lib/account-state-service.js +60 -0
  123. package/dist/lib/activity.d.ts +1 -1
  124. package/dist/lib/activity.js +8 -1
  125. package/dist/lib/add-dir.d.ts +80 -0
  126. package/dist/lib/add-dir.js +241 -0
  127. package/dist/lib/agent-cli-commands.d.ts +11 -0
  128. package/dist/lib/agent-cli-commands.js +30 -0
  129. package/dist/lib/agents.js +44 -36
  130. package/dist/lib/answer-router.js +1 -1
  131. package/dist/lib/audit/log.d.ts +10 -27
  132. package/dist/lib/audit/log.js +20 -33
  133. package/dist/lib/auth-health.d.ts +9 -2
  134. package/dist/lib/auth-health.js +42 -9
  135. package/dist/lib/auto-pull-worker.js +7 -3
  136. package/dist/lib/auto-pull.d.ts +44 -3
  137. package/dist/lib/auto-pull.js +88 -5
  138. package/dist/lib/binary-shadow.d.ts +28 -0
  139. package/dist/lib/binary-shadow.js +121 -0
  140. package/dist/lib/brand.js +9 -3
  141. package/dist/lib/browser/ipc.d.ts +30 -0
  142. package/dist/lib/browser/ipc.js +81 -4
  143. package/dist/lib/browser/profiles.d.ts +3 -2
  144. package/dist/lib/browser/profiles.js +65 -22
  145. package/dist/lib/browser/service.d.ts +4 -0
  146. package/dist/lib/browser/service.js +30 -3
  147. package/dist/lib/browser/sessions-list.d.ts +22 -2
  148. package/dist/lib/browser/sessions-list.js +54 -6
  149. package/dist/lib/browser/stream.d.ts +2 -0
  150. package/dist/lib/browser/stream.js +1 -0
  151. package/dist/lib/browser/types.d.ts +19 -0
  152. package/dist/lib/byok-usage.d.ts +9 -2
  153. package/dist/lib/byok-usage.js +133 -31
  154. package/dist/lib/cli-entry.d.ts +11 -0
  155. package/dist/lib/cli-entry.js +46 -0
  156. package/dist/lib/cloud/host.d.ts +1 -1
  157. package/dist/lib/cloud/host.js +2 -2
  158. package/dist/lib/cloud/registry.js +1 -1
  159. package/dist/lib/cloud/types.d.ts +1 -1
  160. package/dist/lib/codex-policy.d.ts +9 -1
  161. package/dist/lib/codex-policy.js +17 -2
  162. package/dist/lib/commands.d.ts +2 -0
  163. package/dist/lib/commands.js +2 -0
  164. package/dist/lib/computer/sessions-list.d.ts +150 -0
  165. package/dist/lib/computer/sessions-list.js +363 -0
  166. package/dist/lib/config-keys.d.ts +86 -0
  167. package/dist/lib/config-keys.js +193 -0
  168. package/dist/lib/config-machine-keys.d.ts +12 -0
  169. package/dist/lib/config-machine-keys.js +17 -0
  170. package/dist/lib/config-transfer.js +3 -4
  171. package/dist/lib/crabbox/cli.d.ts +2 -2
  172. package/dist/lib/crabbox/cli.js +3 -3
  173. package/dist/lib/daemon-health.d.ts +22 -0
  174. package/dist/lib/daemon-health.js +33 -3
  175. package/dist/lib/daemon-services.d.ts +50 -0
  176. package/dist/lib/daemon-services.js +147 -0
  177. package/dist/lib/daemon-ticks.d.ts +35 -66
  178. package/dist/lib/daemon-ticks.js +77 -159
  179. package/dist/lib/daemon.d.ts +72 -4
  180. package/dist/lib/daemon.js +792 -202
  181. package/dist/lib/deeplink/register.d.ts +49 -0
  182. package/dist/lib/deeplink/register.js +253 -0
  183. package/dist/lib/deeplink/url.d.ts +34 -0
  184. package/dist/lib/deeplink/url.js +78 -0
  185. package/dist/lib/device-config.d.ts +124 -29
  186. package/dist/lib/device-config.js +346 -141
  187. package/dist/lib/devices/config-migration.d.ts +42 -0
  188. package/dist/lib/devices/config-migration.js +203 -0
  189. package/dist/lib/devices/connect.js +14 -7
  190. package/dist/lib/devices/doctor-findings.d.ts +4 -1
  191. package/dist/lib/devices/doctor-findings.js +20 -1
  192. package/dist/lib/devices/harness-inventory.d.ts +11 -2
  193. package/dist/lib/devices/harness-inventory.js +0 -0
  194. package/dist/lib/devices/health.d.ts +1 -1
  195. package/dist/lib/devices/health.js +1 -1
  196. package/dist/lib/devices/pending.d.ts +22 -1
  197. package/dist/lib/devices/pending.js +67 -2
  198. package/dist/lib/devices/registry.d.ts +0 -25
  199. package/dist/lib/devices/registry.js +2 -83
  200. package/dist/lib/devices/resolve-profile.d.ts +20 -0
  201. package/dist/lib/devices/resolve-profile.js +44 -0
  202. package/dist/lib/devices/rollout-verify.d.ts +101 -0
  203. package/dist/lib/devices/rollout-verify.js +193 -0
  204. package/dist/lib/devices/ssh-config.js +10 -6
  205. package/dist/lib/event-families.d.ts +28 -0
  206. package/dist/lib/event-families.js +159 -0
  207. package/dist/lib/event-stream.d.ts +9 -1
  208. package/dist/lib/event-stream.js +26 -9
  209. package/dist/lib/events.d.ts +9 -1
  210. package/dist/lib/events.js +21 -6
  211. package/dist/lib/exec.d.ts +30 -0
  212. package/dist/lib/exec.js +220 -122
  213. package/dist/lib/feed-broadcast.d.ts +25 -0
  214. package/dist/lib/feed-broadcast.js +33 -0
  215. package/dist/lib/feed.d.ts +1 -1
  216. package/dist/lib/feed.js +12 -2
  217. package/dist/lib/fleet/capture.js +15 -0
  218. package/dist/lib/fleet/manifest.js +9 -0
  219. package/dist/lib/fleet/types.d.ts +9 -0
  220. package/dist/lib/fleet-status.js +10 -8
  221. package/dist/lib/git.d.ts +20 -6
  222. package/dist/lib/git.js +121 -17
  223. package/dist/lib/hosts/dispatch.d.ts +8 -3
  224. package/dist/lib/hosts/dispatch.js +29 -3
  225. package/dist/lib/hosts/logs.d.ts +2 -2
  226. package/dist/lib/hosts/logs.js +2 -2
  227. package/dist/lib/hosts/option.js +1 -1
  228. package/dist/lib/hosts/passthrough.d.ts +2 -3
  229. package/dist/lib/hosts/passthrough.js +10 -24
  230. package/dist/lib/hosts/progress.js +2 -2
  231. package/dist/lib/hosts/providers/devices.d.ts +3 -3
  232. package/dist/lib/hosts/providers/devices.js +18 -12
  233. package/dist/lib/hosts/ready.d.ts +5 -1
  234. package/dist/lib/hosts/ready.js +9 -3
  235. package/dist/lib/hosts/reconcile.d.ts +2 -2
  236. package/dist/lib/hosts/reconcile.js +2 -2
  237. package/dist/lib/hosts/registry.js +14 -9
  238. package/dist/lib/hosts/remote-cmd.js +1 -1
  239. package/dist/lib/hosts/remote-os.d.ts +9 -6
  240. package/dist/lib/hosts/remote-os.js +13 -6
  241. package/dist/lib/hosts/routing-flag.d.ts +23 -0
  242. package/dist/lib/hosts/routing-flag.js +41 -0
  243. package/dist/lib/hosts/run-target.js +1 -1
  244. package/dist/lib/hosts/session-index.js +1 -1
  245. package/dist/lib/hosts/tasks.d.ts +6 -6
  246. package/dist/lib/hosts/tasks.js +4 -4
  247. package/dist/lib/hosts/types.js +1 -2
  248. package/dist/lib/installations/resolve.js +2 -8
  249. package/dist/lib/isolation-boundary-report.js +0 -1
  250. package/dist/lib/linear-cache.d.ts +7 -0
  251. package/dist/lib/linear-cache.js +31 -4
  252. package/dist/lib/linear-project-counts.d.ts +1 -1
  253. package/dist/lib/linear-project-counts.js +2 -3
  254. package/dist/lib/linear-projects.js +1 -1
  255. package/dist/lib/loop.d.ts +2 -2
  256. package/dist/lib/loop.js +1 -1
  257. package/dist/lib/mailbox-target.d.ts +1 -1
  258. package/dist/lib/mailbox-target.js +1 -1
  259. package/dist/lib/manifest.js +7 -4
  260. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  261. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  262. package/dist/lib/menubar/snapshot.d.ts +3 -2
  263. package/dist/lib/menubar/snapshot.js +6 -2
  264. package/dist/lib/migrate-fold.d.ts +33 -0
  265. package/dist/lib/migrate-fold.js +130 -0
  266. package/dist/lib/migrate.d.ts +34 -34
  267. package/dist/lib/migrate.js +106 -128
  268. package/dist/lib/model-tier-overrides.d.ts +1 -1
  269. package/dist/lib/model-tier-overrides.js +15 -2
  270. package/dist/lib/monitors/config.d.ts +17 -3
  271. package/dist/lib/monitors/config.js +52 -18
  272. package/dist/lib/monitors/engine.d.ts +32 -1
  273. package/dist/lib/monitors/engine.js +94 -11
  274. package/dist/lib/monitors/fingerprint.d.ts +46 -0
  275. package/dist/lib/monitors/fingerprint.js +100 -0
  276. package/dist/lib/monitors/remote.d.ts +47 -0
  277. package/dist/lib/monitors/remote.js +81 -0
  278. package/dist/lib/monitors/state.d.ts +39 -0
  279. package/dist/lib/monitors/state.js +56 -0
  280. package/dist/lib/onepassword.d.ts +17 -0
  281. package/dist/lib/onepassword.js +52 -22
  282. package/dist/lib/open-url.js +1 -1
  283. package/dist/lib/picker.d.ts +14 -2
  284. package/dist/lib/picker.js +35 -13
  285. package/dist/lib/placement.d.ts +1 -1
  286. package/dist/lib/placement.js +1 -1
  287. package/dist/lib/pr-land-detach.d.ts +32 -0
  288. package/dist/lib/pr-land-detach.js +79 -0
  289. package/dist/lib/profiles-presets.js +13 -0
  290. package/dist/lib/profiles.d.ts +2 -0
  291. package/dist/lib/profiles.js +44 -1
  292. package/dist/lib/project-focus.d.ts +1 -1
  293. package/dist/lib/project-focus.js +1 -1
  294. package/dist/lib/project-key.d.ts +17 -0
  295. package/dist/lib/project-key.js +26 -0
  296. package/dist/lib/project-probe.d.ts +4 -1
  297. package/dist/lib/project-probe.js +5 -4
  298. package/dist/lib/project-resources.js +2 -18
  299. package/dist/lib/project-root.d.ts +16 -0
  300. package/dist/lib/project-root.js +20 -2
  301. package/dist/lib/projects.d.ts +20 -1
  302. package/dist/lib/projects.js +78 -1
  303. package/dist/lib/refresh-coordinator.d.ts +24 -0
  304. package/dist/lib/refresh-coordinator.js +52 -0
  305. package/dist/lib/registry.d.ts +2 -2
  306. package/dist/lib/registry.js +1 -1
  307. package/dist/lib/resource-aliases.d.ts +13 -0
  308. package/dist/lib/resource-aliases.js +26 -0
  309. package/dist/lib/resources/types.d.ts +1 -1
  310. package/dist/lib/resources.d.ts +16 -0
  311. package/dist/lib/resources.js +66 -10
  312. package/dist/lib/rotate.d.ts +3 -4
  313. package/dist/lib/rotate.js +4 -5
  314. package/dist/lib/routine-activation.d.ts +22 -2
  315. package/dist/lib/routine-activation.js +63 -16
  316. package/dist/lib/routine-context.d.ts +9 -2
  317. package/dist/lib/routine-context.js +21 -6
  318. package/dist/lib/routines-project.d.ts +58 -48
  319. package/dist/lib/routines-project.js +190 -159
  320. package/dist/lib/routines.d.ts +41 -6
  321. package/dist/lib/routines.js +92 -34
  322. package/dist/lib/run-defaults.d.ts +5 -0
  323. package/dist/lib/run-defaults.js +11 -1
  324. package/dist/lib/runner.d.ts +10 -8
  325. package/dist/lib/runner.js +122 -39
  326. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  327. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  328. package/dist/lib/secrets/agent.d.ts +10 -3
  329. package/dist/lib/secrets/agent.js +43 -8
  330. package/dist/lib/secrets/bundles.d.ts +1 -0
  331. package/dist/lib/secrets/bundles.js +92 -80
  332. package/dist/lib/secrets/index.js +32 -9
  333. package/dist/lib/secrets/install-helper.d.ts +4 -4
  334. package/dist/lib/secrets/install-helper.js +4 -4
  335. package/dist/lib/secrets/push.d.ts +40 -6
  336. package/dist/lib/secrets/push.js +58 -11
  337. package/dist/lib/secrets/read-backoff.d.ts +1 -1
  338. package/dist/lib/secrets/read-backoff.js +1 -1
  339. package/dist/lib/secrets/remote.d.ts +24 -0
  340. package/dist/lib/secrets/remote.js +54 -4
  341. package/dist/lib/self-update.d.ts +108 -0
  342. package/dist/lib/self-update.js +276 -1
  343. package/dist/lib/session/active.d.ts +89 -5
  344. package/dist/lib/session/active.js +165 -10
  345. package/dist/lib/session/actor-sidecar.d.ts +1 -1
  346. package/dist/lib/session/actor-sidecar.js +2 -1
  347. package/dist/lib/session/db.d.ts +176 -2
  348. package/dist/lib/session/db.js +484 -17
  349. package/dist/lib/session/discover.js +27 -4
  350. package/dist/lib/session/hook-sessions.js +2 -2
  351. package/dist/lib/session/host-link.d.ts +2 -2
  352. package/dist/lib/session/host-link.js +2 -2
  353. package/dist/lib/session/pid-registry.d.ts +21 -0
  354. package/dist/lib/session/pid-registry.js +57 -0
  355. package/dist/lib/session/remote-active.js +15 -1
  356. package/dist/lib/session/remote.js +2 -2
  357. package/dist/lib/session/session-cache.d.ts +14 -7
  358. package/dist/lib/session/session-cache.js +51 -9
  359. package/dist/lib/session/state.d.ts +2 -2
  360. package/dist/lib/session/state.js +1 -1
  361. package/dist/lib/session/sync/config.d.ts +10 -10
  362. package/dist/lib/session/sync/config.js +10 -10
  363. package/dist/lib/session/sync/r2.d.ts +34 -0
  364. package/dist/lib/session/sync/r2.js +123 -0
  365. package/dist/lib/session/throughput.d.ts +1 -1
  366. package/dist/lib/session/throughput.js +1 -1
  367. package/dist/lib/session/types.d.ts +23 -1
  368. package/dist/lib/session/types.js +15 -0
  369. package/dist/lib/session/viewing-in.d.ts +1 -1
  370. package/dist/lib/session/viewing-in.js +1 -1
  371. package/dist/lib/session/watch.d.ts +101 -0
  372. package/dist/lib/session/watch.js +242 -0
  373. package/dist/lib/share/config.d.ts +4 -0
  374. package/dist/lib/share/config.js +1 -0
  375. package/dist/lib/share/delete.d.ts +93 -0
  376. package/dist/lib/share/delete.js +127 -0
  377. package/dist/lib/share/provision.d.ts +38 -0
  378. package/dist/lib/share/provision.js +50 -0
  379. package/dist/lib/share/publish.d.ts +40 -1
  380. package/dist/lib/share/publish.js +116 -3
  381. package/dist/lib/share/worker-template.js +71 -6
  382. package/dist/lib/shims.d.ts +17 -3
  383. package/dist/lib/shims.js +136 -27
  384. package/dist/lib/signin-badge.js +3 -2
  385. package/dist/lib/skills.js +2 -0
  386. package/dist/lib/smart-launch.d.ts +8 -8
  387. package/dist/lib/smart-launch.js +80 -59
  388. package/dist/lib/ssh-exec.d.ts +1 -1
  389. package/dist/lib/ssh-exec.js +1 -1
  390. package/dist/lib/staleness/prune.d.ts +88 -0
  391. package/dist/lib/staleness/prune.js +69 -0
  392. package/dist/lib/staleness/registry.d.ts +1 -1
  393. package/dist/lib/staleness/writers/commands.js +34 -2
  394. package/dist/lib/staleness/writers/hooks.js +16 -0
  395. package/dist/lib/staleness/writers/kinds.d.ts +6 -0
  396. package/dist/lib/staleness/writers/skills.js +19 -0
  397. package/dist/lib/staleness/writers/subagents.d.ts +2 -3
  398. package/dist/lib/staleness/writers/subagents.js +0 -4
  399. package/dist/lib/staleness/writers/types.d.ts +32 -0
  400. package/dist/lib/startup/command-registry.d.ts +6 -12
  401. package/dist/lib/startup/command-registry.js +14 -31
  402. package/dist/lib/startup/dev-build.d.ts +10 -0
  403. package/dist/lib/startup/dev-build.js +13 -1
  404. package/dist/lib/startup/spellcheck.d.ts +18 -0
  405. package/dist/lib/startup/spellcheck.js +45 -0
  406. package/dist/lib/state.d.ts +36 -9
  407. package/dist/lib/state.js +96 -46
  408. package/dist/lib/subagents-registry.d.ts +0 -7
  409. package/dist/lib/subagents-registry.js +38 -65
  410. package/dist/lib/subagents.d.ts +0 -33
  411. package/dist/lib/subagents.js +0 -75
  412. package/dist/lib/sync-umbrella.js +1 -1
  413. package/dist/lib/teams/agents.d.ts +41 -1
  414. package/dist/lib/teams/agents.js +187 -9
  415. package/dist/lib/teams/api.d.ts +11 -1
  416. package/dist/lib/teams/api.js +11 -2
  417. package/dist/lib/teams/delivery.d.ts +41 -0
  418. package/dist/lib/teams/delivery.js +60 -0
  419. package/dist/lib/teams/registry.d.ts +14 -0
  420. package/dist/lib/teams/registry.js +51 -1
  421. package/dist/lib/teams/remoteWorktree.d.ts +19 -0
  422. package/dist/lib/teams/remoteWorktree.js +29 -0
  423. package/dist/lib/teams/scheduler.d.ts +1 -1
  424. package/dist/lib/teams/scheduler.js +1 -1
  425. package/dist/lib/teams/worktree.d.ts +24 -0
  426. package/dist/lib/teams/worktree.js +42 -0
  427. package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
  428. package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
  429. package/dist/lib/terminal/engine.d.ts +4 -0
  430. package/dist/lib/terminal/engine.js +8 -2
  431. package/dist/lib/terminal/types.d.ts +21 -2
  432. package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
  433. package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
  434. package/dist/lib/tickets/list.d.ts +53 -0
  435. package/dist/lib/tickets/list.js +153 -0
  436. package/dist/lib/tmux/orphan-reap.d.ts +320 -0
  437. package/dist/lib/tmux/orphan-reap.js +644 -0
  438. package/dist/lib/tmux/session.d.ts +68 -3
  439. package/dist/lib/tmux/session.js +95 -5
  440. package/dist/lib/triggers/handlers.d.ts +20 -0
  441. package/dist/lib/triggers/handlers.js +6 -1
  442. package/dist/lib/types.d.ts +46 -34
  443. package/dist/lib/usage-fleet.d.ts +32 -0
  444. package/dist/lib/usage-fleet.js +125 -0
  445. package/dist/lib/usage-refresh.d.ts +3 -3
  446. package/dist/lib/usage-refresh.js +19 -16
  447. package/dist/lib/usage.d.ts +51 -47
  448. package/dist/lib/usage.js +145 -153
  449. package/dist/lib/version-duplicates.js +2 -2
  450. package/dist/lib/versions.d.ts +56 -13
  451. package/dist/lib/versions.js +195 -45
  452. package/dist/lib/watchdog/log.d.ts +1 -1
  453. package/dist/lib/watchdog/log.js +3 -3
  454. package/dist/lib/watchdog/rotate.d.ts +6 -6
  455. package/dist/lib/watchdog/rotate.js +6 -6
  456. package/dist/lib/workflows.d.ts +1 -1
  457. package/dist/lib/yaml-io.d.ts +47 -0
  458. package/dist/lib/yaml-io.js +55 -0
  459. package/package.json +2 -1
  460. package/scripts/install-helper.js +2 -2
  461. package/scripts/postinstall.js +23 -20
  462. package/dist/commands/defaults.d.ts +0 -7
  463. package/dist/commands/defaults.js +0 -107
  464. package/dist/commands/export.d.ts +0 -11
  465. package/dist/commands/export.js +0 -215
  466. package/dist/commands/helper.d.ts +0 -12
  467. package/dist/commands/helper.js +0 -87
  468. package/dist/commands/hosts.d.ts +0 -11
  469. package/dist/commands/hosts.js +0 -330
  470. package/dist/commands/lock.d.ts +0 -12
  471. package/dist/commands/lock.js +0 -70
  472. package/dist/commands/pull.d.ts +0 -17
  473. package/dist/commands/pull.js +0 -39
  474. package/dist/commands/push.d.ts +0 -14
  475. package/dist/commands/push.js +0 -30
  476. package/dist/commands/wallet.d.ts +0 -20
  477. package/dist/commands/wallet.js +0 -216
  478. package/dist/commands/worktree.d.ts +0 -19
  479. package/dist/commands/worktree.js +0 -272
  480. package/dist/lib/account-labels.d.ts +0 -24
  481. package/dist/lib/account-labels.js +0 -72
  482. package/dist/lib/auto-dispatch-linear.d.ts +0 -18
  483. package/dist/lib/auto-dispatch-linear.js +0 -107
  484. package/dist/lib/auto-dispatch-provider.d.ts +0 -10
  485. package/dist/lib/auto-dispatch-provider.js +0 -30
  486. package/dist/lib/auto-dispatch.d.ts +0 -93
  487. package/dist/lib/auto-dispatch.js +0 -128
  488. package/dist/lib/export.d.ts +0 -72
  489. package/dist/lib/export.js +0 -269
  490. package/dist/lib/lock.d.ts +0 -93
  491. package/dist/lib/lock.js +0 -207
  492. package/dist/lib/wallet/index.d.ts +0 -78
  493. package/dist/lib/wallet/index.js +0 -253
package/CHANGELOG.md CHANGED
@@ -1,5 +1,675 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.37
4
+
5
+ - **`agents.yaml`'s five writers now emit identical bytes, closing the rest of the sync-blocking drift (RUSH-2505).**
6
+ Unpadding flow sequences fixed one half of this; the other half was that the five in-place writers of
7
+ `agents.yaml` disagreed on collection style. `state.ts` and `manifest.ts` pass `collectionStyle: 'block'`,
8
+ while `feed.ts`, `activity.ts` and the `notify.owner` migration in `migrate.ts` did not — so an empty map
9
+ rendered as `mcp:` with an indented `{}` from one group and `mcp: {}` from the other, and the two rewrote
10
+ each other forever. Measured against the real committed `~/.agents/agents.yaml`, the block group
11
+ round-tripped and the other group did not. All five now serialize through one `stringifyDoc()` helper that
12
+ pins the committed shape, so a write produces no diff regardless of which command ran. `routines.ts` keeps
13
+ its padding-only form: it writes routine YAML, which has no second writer to disagree with.
14
+ Source: `apps/cli/src/lib/yaml-io.ts`.
15
+
16
+ - `agents routines add <file>` no longer rewrites the file you hand it. When the source is already the canonical routine YAML — the normal case for a definition tracked in `~/.agents` — the definition is left byte-for-byte alone instead of being re-serialized with its `devices:` pin stripped. A source from anywhere else is still copied in.
17
+ - A `devices:` pin now says it applies to this box only and names `agents routines devices <name> --set`, instead of silently reading as `Devices: all` on every peer.
18
+ - `agents routines edit <name> --cwd <path>` and `--project-anchor <name>` apply and save without opening `$EDITOR`, so the repair the readiness gate prints is one an agent can actually run.
19
+ - A paused routine now names `agents routines resume <name>` as the follow-up.
20
+
21
+ - **Agent helper processes are reaped when the agent exits (RUSH-2521).** An interactive
22
+ agent runs as the leaf of a detached tmux pane, and tearing that pane down only SIGHUPs
23
+ its foreground process group — so an MCP server or a harness background daemon that had
24
+ moved out of that group survived its session and kept its memory indefinitely. Measured
25
+ on the fleet: one pane holding 2.5 GB of Claude Code background daemons 22 days after
26
+ its session ended, and 34 orphaned `cgraph-mcp --daemon` processes on one worker.
27
+ `agents sessions reap` and the routines daemon's 5-minute sweep now terminate those
28
+ helpers as well as the dead panes, and `killSession` collects a session's helpers as it
29
+ tears the session down. A helper is attributed to its session (tier 1) through the
30
+ `AGENT_TMUX_SESSION_NAME` the pane exports and every descendant inherits — the one
31
+ handle that survives reparenting — and is killed only when that session is gone, or has
32
+ no attached client AND its agent process has exited; a tmux query that never actually
33
+ answered (missing/unsupported tmux, a spawn failure, a timed-out server) disables tier 1
34
+ for that sweep entirely rather than being treated as "no sessions", and
35
+ `agents sessions reap --json`'s `warnings` array (plus a daemon log line) says when that
36
+ happens. Tier 1 needs another process's environment, which is a plain `/proc` read on
37
+ Linux; on macOS, modern `ps -E` no longer exposes it at all, so tier 1 safely finds
38
+ nothing there today (known gap, not a regression — only tier 2 reaps on macOS). A
39
+ harness that detaches its daemons from the environment (Claude Code's `daemon run`) is
40
+ matched instead (tier 2) on the spawner pid it declares in its own argv, anchored on the
41
+ process's real executable rather than a substring match anywhere in its command line,
42
+ and excludes anything still owned by a live/attached pane — checked two independent
43
+ ways, tmux's own live pane-pid data (expanded to that pane leaf's current process-tree
44
+ descendants, so it protects a live agent's own subprocesses too, and works even with no
45
+ env marker readable anywhere in that tree, covering macOS and any `claude` started
46
+ outside an agents-cli pane) and the env marker when one is present — so a
47
+ `grep`/`cat`/pager, or an agent's own prompt (or a subprocess it spawns) quoting this
48
+ exact pattern, can never become a kill seed. A process that has genuinely reparented
49
+ away from every live pane leaf remains reapable. Reaped
50
+ only once the declared spawner pid is dead. `agents sessions reap --dry-run` lists what
51
+ would be collected without killing anything. Source:
52
+ `apps/cli/src/lib/tmux/orphan-reap.ts`, `apps/cli/src/lib/tmux/session.ts`,
53
+ `apps/cli/src/commands/sessions-reap.ts`.
54
+ - **`sessions migrate --host` stops leaving `remain-on-exit` on server-wide, and lands the
55
+ migrated session where its reaper can see it (RUSH-2521).** The remote launch set the
56
+ tmux server default to `on` and never put it back, so every later pane on that box —
57
+ user splits included — retained a dead corpse when its command finished; it now keeps
58
+ the option on the migrated agent's own pane (which the liveness probe reads) and
59
+ restores the server default to `off`, matching `createSession`. Separately, the remote
60
+ session was created on tmux's bare default OS socket instead of the target's agents
61
+ socket, so the reaper above never found it and killed the just-migrated, still-live
62
+ agent as `tmux-session-gone` on its next tick — it now targets the same agents socket the
63
+ reaper queries. Source: `apps/cli/src/commands/sessions-migrate.ts`.
64
+
65
+ - **`release.sh` fails fast when the `--device` home base cannot sign, instead of merging + tagging and then dying at publish.** The privileged phase (build + sign + notarize + npm publish) runs only at the very end, after the release PR is merged and the tag pushed. A `--device <box>` fallback that was never provisioned as a signing home base -- the documented "mac-mini is down, use `--device zion`" path on a box that has a Developer ID cert only in its *login* keychain, no headless-unlockable `rush-signing.keychain-db`, and no `apple.com`/`npmjs.com` secrets bundles -- failed there, *after* both irreversible acts, leaving a tagged-but-**unpublished** release (RUSH-2535: npm stuck at 1.22.35 with `v1.22.36` tagged). A new preflight runs a read-only readiness probe (`scripts/signing-home-base-probe.sh`) ON the resolved home base, over the same `agents ssh` hop the publish uses, BEFORE the crabbox/PR/merge/tag phases; an unprovisioned box aborts at the preflight naming the exact missing piece. Provisioning a new signing home base remains RUSH-2541. Source: `apps/cli/scripts/release.sh`, `apps/cli/scripts/signing-home-base-probe.sh`, `apps/cli/scripts/signing-home-base-probe.test.ts`.
66
+
67
+ - **Collapse routine enablement to one enabled/disabled flag; remove `enable-project` (RUSH-2540).** A routine now has exactly one state — enabled or disabled — owned by this device (`meta.deviceRoutines`). `agents routines enable <name>` materialises a project routine (from the current project or a registered one) and turns it on in one step; `agents routines disable <name>` turns it off. The separate project opt-in (`agents routines enable-project` / `disable-project` / `projects` and the `meta.routines.projects` allowlist) is gone, and `agents routines resume`/`pause` remain as hidden aliases. `agents routines list` now shows discoverable project routines from your registered projects as disabled rows. Safety is unchanged: a cloned repo's `.agents/routines/*.yml` can never auto-fire — enablement lives in user-owned local state and a project YAML's own `enabled:` is never trusted for firing, and `sync` only refreshes definitions, never enablement. Source: `apps/cli/src/lib/routines-project.ts`, `apps/cli/src/commands/routines.ts`, `apps/cli/src/lib/types.ts`, `apps/cli/src/lib/state.ts`.
68
+
69
+ - **Project / `--add-dir` grants reach Claude, Codex, Cursor, Kimi, and Grok — not just Claude and Codex.** Multi-repo projects already attached sibling checkouts as grants on `agents run --project` / `agents teams --project`; only Claude (`--add-dir`) and Codex (`workspace_roots`) consumed them, so a Cursor / Kimi / Grok agent on the same project saw the primary cwd alone. Now: Cursor and Kimi take the native `--add-dir` flag (same as Claude); Grok always gets a short `--rules` note naming the siblings, and when a non-off OS sandbox is active (`GROK_SANDBOX` / `--sandbox`) writes a project-local `.grok/sandbox.toml` profile (`agents-project`) with those paths as `read_write` and selects it. Codex is unchanged. Harnesses with no multi-root surface (OpenCode, Droid, …) still ignore the grants — that is a harness limitation, stated in the capability map (`lib/add-dir.ts`) rather than papered over. Source: `apps/cli/src/lib/add-dir.ts`, `apps/cli/src/lib/exec.ts`, `apps/cli/docs/projects.md`, `apps/cli/docs/teams.md`.
70
+
71
+ - **Writing `agents.yaml` no longer pads flow sequences, so `~/.agents` keeps syncing fleet-wide.** The YAML emitter defaults to padded flow output (`[ a, b ]`), but the committed `agents.yaml` uses `[a, b]`. Installing the feed / activity-log hooks (`ensureFeedPublishHook`, `ensureActivityLogHook` — run back-to-back on `agents feed`) re-serialized the whole document, flipping any committed flow node — e.g. a notify hook's `command: [agents, notify, "{message}"]` — to the padded form. That left the git-backed `~/.agents` working tree permanently dirty on that one file, so `agents repo pull` refused and seven fleet boxes silently fell 37-52 commits behind, receiving no new project definitions, routines, or rules (RUSH-2505). All the writers that round-trip committed `agents.yaml`/routine YAML now pass `flowCollectionPadding: false`, matching the committed formatting exactly so a write produces no diff: `feed.ts`, `activity.ts`, the `notify.owner` migration in `migrate.ts`, and the routine-definition writer `serializeJob` (same synced repo).
72
+
73
+ - **The generated HTML command reference is now browsable, not search-only.** `docs/command-reference.html` gains a sticky sidebar tree covering all 104 command groups and every subcommand — collapsed by default, one entry per card, so the surface can be explored without knowing what to type. Search filters the tree alongside the cards and auto-expands the groups holding matches; clicking a group scrolls to it, expands it, and highlights it, and the active entry follows as you scroll. The root `agents` card is anchorable at `#agents` instead of shipping an empty `id=""`. Still one self-contained file with no external assets. Source: `apps/cli/scripts/gen-command-index.ts`, `apps/cli/scripts/gen-command-index.test.ts`.
74
+
75
+ - **`agents daemon services` exposes every hosted responsibility as an independent, toggleable service.** The daemon previously ran all of its hosted subsystems together — secrets broker, browser IPC, scheduler, monitors, watchdog, device probe, self-heal, keychain reap, account-state refresh, and state-dir checks — with only coarse `daemon.enabled` and `scheduler.enabled` kill switches. Now each responsibility is listed under `agents daemon services`, and `agents daemon services enable|disable <id>` persists per-service state in `~/.agents/daemon/services.yaml`. Missing keys default to enabled, so upgrades are no-ops. The running daemon reads the catalog at startup and gates each timer or hosted process by its toggle; a few services (scheduler, monitor engine) also re-evaluate on `SIGHUP` reload. Source: `apps/cli/src/lib/daemon-services.ts`, `apps/cli/src/commands/daemon.ts`, `apps/cli/src/lib/daemon.ts`.
76
+
77
+ - **A disabled secrets broker fails loud instead of falling back to per-request Touch ID.** When `secrets-broker` is disabled, `agents secrets unlock/status/start` report the disabled state up front, and any keychain-backed bundle read throws a clear error naming the `agents daemon services enable secrets-broker` recovery step. This closes the path where disabling the daemon's broker silently turned every secret read into a fresh biometric prompt. Direct keychain reads remain available when explicitly opted in via `AGENTS_SECRETS_NO_AGENT=1`. Source: `apps/cli/src/lib/secrets/agent.ts`, `apps/cli/src/lib/secrets/bundles.ts`, `apps/cli/src/commands/secrets.ts`.
78
+
79
+ - **One event engine: `events` is the source of truth; `audit` and `logs` are aliases.** The unified event stream already covered ops + agent activity; run-dispatch outcomes now land there too as `run.dispatched` (written from the same exec chokepoint that used to append the separate hash-chained `~/.agents/.history/audit/log.jsonl`). New CLI surface:
80
+ - `agents events --include` / `--exclude` families: `ops`, `activity`, `commands`, `runs`, `security` (sessions-style, mutually exclusive).
81
+ - `agents events --exclude commands` drops high-churn `command.start`/`command.end` noise.
82
+ - `agents events --include runs` lists dispatched-run outcomes (what `audit list` used to show).
83
+ - `agents events stats` / `agents events rotate` own housekeeping (moved off the logs tree as the canonical home).
84
+ - `agents audit` ≡ `events --include runs`; `agents audit list` same; `agents audit verify` still walks a *legacy* hash-chain file if present, else reports clean and points at the events stream.
85
+ - `agents logs` ≡ `events`; `logs audit` ≡ `events --include ops`; `logs stats`/`rotate` re-dispatch. A bare `logs <id>` redirects to `sessions` / `hosts logs` for content.
86
+ - Coverage: secrets/keychain (`secrets.*`), browser, computer, daemon lifecycle (`daemon.start`/`stop`/`error`/`info` mirrored from the daemon log), plus every module already covered by the CLI `command.start`/`command.end` choke point. Source: `apps/cli/src/lib/event-families.ts`, `event-stream.ts`, `events.ts`, `audit/log.ts`, `daemon.ts`, `commands/events.ts`, `commands/audit.ts`, `commands/logs.ts`.
87
+
88
+ - **Nest disposable leases under devices.** Manage crabbox leases with `agents devices lease setup|list|stop|gc`; the removed top-level `agents lease` command now fails as unknown, while `agents run --lease` and `--box` keep their existing behavior. Source: `apps/cli/src/commands/lease.ts`.
89
+
90
+ - **Nest Funnel management under the daemon command.** Use `agents daemon funnel status|up|down` for webhook ingress; the former top-level `agents funnel` command has been removed. Source: `apps/cli/src/commands/daemon.ts`.
91
+
92
+ ### Fixed
93
+
94
+ - **Menu bar NEW DEVICES no longer lists already-registered or ignored boxes.**
95
+ The daemon's pending-device sentinel writer (`reconcilePendingSentinels`) now
96
+ re-subtracts the registered roster as well as the ignore-list, so a hermetic
97
+ run that empties the registry view while writing the live `devices-pending/`
98
+ dir cannot surface every fleet box as "new". Soft-fail probe ticks (no
99
+ tailscale) still prune dismissed sentinels, and the probe fires once on
100
+ daemon start so leftover pollution clears without waiting for the 3-minute
101
+ interval.
102
+
103
+ - **`release.sh` no longer deadlocks when the stuck release is one nothing can finish.** Two guards could each name the other as the way out, leaving no version publishable: the stuck-tag guard refused to bump past an unpublished `v<main-version>` ("finish that release first"), while the catch-up guard refused that same version ("no complete merged release PR ... cut the next patch through the normal release PR flow"). Hit on 2026-08-10 with npm at 1.22.35 and `v1.22.36` tagged: 1.22.36 could not be finished at all, because its CI-tested tree predates the prepack version-gate fix (`1dffc78bc`) and so its own `npm publish` rejects a correct binary. `scripts/stuck-release.sh` now takes the resolved bump kind and main's version, and exempts exactly one case — `patch-from-main` stepping over main's own version, which is precisely what `validate-bump.sh` documents that bump kind for. Every other stuck version still blocks, and every other bump kind still blocks on this one, so a genuine died-between-tag-and-publish jam is reported exactly as before. Source: `apps/cli/scripts/stuck-release.sh`, `apps/cli/scripts/release.sh`, `apps/cli/scripts/stuck-release.test.ts`.
104
+
105
+ ---
106
+ type: breaking
107
+ ---
108
+
109
+ Remove the top-level `agents wallet` command and its payment-card storage implementation. Payment-card management will return under `agents secrets` in RUSH-2532.
110
+
111
+ Removed the top-level `agents whoami` command. Use `agents login` and `agents logout` to manage synced-secrets access.
112
+
113
+ ---
114
+ type: breaking
115
+ ---
116
+
117
+ Remove the top-level `agents worktree` command. Team worktree isolation remains available through `agents teams --worktree` and `--enable-worktrees`.
118
+
119
+ - Remove the top-level `agents defaults` and `agents export` commands. Configure run defaults and the projects root through `agents config`; isolated installs are removed with `agents remove <agent>@<version> --isolated`.
120
+
121
+ - **Removed the top-level `agents hosts` command group.** `agents devices` is now
122
+ the sole user-facing fleet registry. The registry subcommands map to devices:
123
+ `agents hosts list|add|remove` → `agents devices list|add|rm`. The per-host
124
+ `agents hosts check <name>` probe has no standalone replacement — fleet health
125
+ is `agents devices status` (a rollup over every registered device, no per-host
126
+ argument), and the per-host readiness probe still runs internally via
127
+ `ensureHostReady` at dispatch time. The
128
+ two dispatched-task commands with no devices equivalent moved under devices:
129
+ `agents hosts ps` → `agents devices ps` (list tasks dispatched with
130
+ `agents run --device <name> --no-follow`; reconciles running records; `--json`),
131
+ and `agents hosts stop <id>` → `agents devices stop <id>` (alias `kill`).
132
+ `agents hosts logs <id>` is dropped because `agents logs <id>` already views and
133
+ follows host-dispatch task logs. The dispatch fabric is unchanged: `agents run
134
+ --host/--device`, teams remote, and cloud `--provider host` all still work.
135
+ Source: `apps/cli/src/commands/ssh.ts`, `apps/cli/src/lib/startup/command-registry.ts`.
136
+
137
+ - **Removed the top-level `agents lock` and `agents helper` commands.** `agents lock` (the `agents.lock` SHA-256 resource manifest, generation + `--frozen` verification) and `agents helper` (the macOS Keychain `.app` install/status/where surface) are gone, along with `src/lib/lock.ts` and its tests. The signed Keychain helper still installs and repairs itself automatically — the postinstall installs it, and `getKeychainHelperPath()`'s lazy staleness check reinstalls it on the next `agents secrets` use (or when you reinstall agents-cli) — so no user action replaces `agents helper install`. Verify: `agents lock` and `agents helper` now print an unknown-command error.
138
+
139
+ - **Removed the top-level `agents publish` command.** Skill registry search and install remain available for existing indexes, but generating and pushing an index is no longer a built-in agents-cli surface.
140
+
141
+ - **Remove the resource-profile command tree.** `agents profile` and the resource-profile aliases `agents profiles use/status` no longer register; `agents profiles` remains the provider/host profile surface for Kimi, DeepSeek, and other custom harnesses. Source: `apps/cli/src/commands/profiles.ts`, `apps/cli/src/lib/startup/command-registry.ts`.
142
+
143
+ - **Remove top-level pull and push commands.** `agents pull` and `agents push` are no longer registered; use `agents repo pull <alias>` and `agents repo push <alias>` instead. Source: `apps/cli/src/lib/startup/command-registry.ts`.
144
+
145
+ - **`agents sessions watch --json` is the canonical incremental session-state stream for AGI EXT and other long-lived consumers (RUSH-2484).** It reads one startup snapshot, then tails the canonical publisher's row-delta journal rather than re-running live-session or fleet gathers. Versioned NDJSON `reset`, `upsert`, `remove`, device-scope availability, and heartbeat envelopes carry opaque row keys and monotonic per-stream sequence numbers. Rows include CLI-owned recovery metadata, and unavailable device scopes retain their last rows. `--local` disables fleet aggregation.
146
+
147
+ - **Fleet readiness now comes from the existing device/account JSON contracts (RUSH-2484).** `agents devices list --json` includes effective config and resource health; `agents devices status --json` includes each row's effective profile/config; and `agents devices accounts --json` exposes quota verdict, capture/reset timestamps, and an explicit unavailable reason. `agents run auto --interactive --device auto --strategy balanced --mode auto` excludes unreachable, overloaded, signed-out, rate-limited, and out-of-credits choices and fails loud when no eligible placement exists.
148
+
149
+ - **`agents tickets list --json` is the canonical Linear/GitHub backlog read for UI consumers (RUSH-2484).** It returns one stable task shape, cycle metadata, and independent availability/error state for each tracker so one failed source does not erase another source's rows.
150
+
151
+ - **Skills and commands can declare `aliases:` in their frontmatter.** Resource
152
+ resolution now matches a declared alias in addition to the canonical file/dir
153
+ name, so `resolveResource('skills', 'browser')` finds a skill that lists
154
+ `browser` among its `aliases:`. The canonical name always wins a collision — a
155
+ real resource named `browser` beats any resource that merely aliases it, in any
156
+ layer — and layer precedence (project > user > system) still applies among
157
+ aliases. `listResources` surfaces each resource's `aliases`. This is the
158
+ prerequisite for housing a resource under a plugin namespace (e.g. `agi:browser`)
159
+ while a bare name keeps resolving. (RUSH-2504)
160
+
161
+ - **Credential pushes now ride a hardened SSH posture: pinned host keys + no
162
+ connection reuse (RUSH-2527).** Every `agents secrets` operation that moves
163
+ credential bytes across the fleet — `agents accounts sync <name> --device`,
164
+ `agents secrets export <bundle> --host`, `agents fleet apply
165
+ --provision-secrets`, and a remote bundle resolve (`run --secrets b@host` /
166
+ `secrets exec --host`) — now verifies the destination against the CLI-managed
167
+ known_hosts store (a **changed** host key is refused) and never leaves a reusable
168
+ 60s SSH `ControlMaster` socket behind that an unrelated later `agents` invocation
169
+ could silently reuse. This matches the posture the `--copy-creds` dispatch
170
+ already used, extended to the explicit provider-account and secrets-export
171
+ transports. Read-only browse calls (`secrets list --host`) are unaffected and
172
+ keep the fast multiplexed baseline. Source:
173
+ `apps/cli/src/lib/secrets/remote.ts` (`credentialTransportSshOpts`),
174
+ `apps/cli/src/lib/secrets/push.ts`.
175
+
176
+ - **Daemon routines and watchdog emit first-class events.** Scheduler fires write `routine.start` / `routine.end` (including spawn failures, host-monitor finalization, and terminal cloud dispatch) and each watchdog pass writes `watchdog.action` with live/stalled/nudged counts onto the unified stream — filterable via `agents events --module routine|watchdog` or `--event routine.start`. Daemon process lifecycle remains the existing log mirror (`daemon.start`/`stop`/`error`/`info`). Source: `apps/cli/src/lib/daemon.ts`, `runner.ts`, `events.ts`.
177
+
178
+ - **One verb gets you back into a session.** Getting back in used to mean choosing among seven commands, where the right one depended on internal state you could not see — `sessions focus`, `sessions attach`, `sessions reconnect`, `sessions resume`, `sessions go`, top-level `agents resume`, and raw `agents tmux attach`. `agents sessions resume <id-or-alias>` now detects the state instead: it attaches a **live tmux pane**, brings a **headless** session to the foreground, or **recovers** an ended one on its owning device. It accepts a full id, an id prefix, or an `ag-<agent>-<shortid>` tmux alias, and takes `--attach-only` (attach one living process or refuse, never fork a copy). Bare `agents sessions resume` keeps the multi-select history picker. `agents sessions --help` drops from 21 commands to 16.
179
+ - **Retired, hidden but still working for one release** — each prints the replacement on stderr: `agents sessions attach <id>` and `agents reconnect [id]` → `agents sessions resume`; `agents sessions go <id>` → `agents sessions resume <id> --attach-only`. `agents sessions focus` is hidden and becomes the internal lifecycle dispatcher (`resume` delegates to it), so it is deliberately not warned. **One behaviour is deliberately dropped:** bare `agents reconnect` (no id) auto-attached the most recent session started in the current directory with no prompt; bare `agents sessions resume` opens the picker instead, so that zero-typing path goes away when `reconnect` is deleted after its deprecation window (tracked on RUSH-2498). **Kept:** `agents sessions detach` (the genuine inverse — it stops the interactive process and respawns the agent headless, which nothing else does), `fork`, `migrate`, and the raw `agents tmux attach <name>` escape hatch.
180
+ - **A live tmux alias now attaches instead of being keyword-searched.** `agents sessions resume ag-kimi-632c1fbc` attached nothing before: the alias fell through to a keyword query and returned unrelated text matches while the pane was alive and attachable. The alias's hex is the *launch* id, not the harness session id, and a harness that writes no `state/sessions/<pid>.json` record cannot be mapped back to a session at all — so such a pane had no working selector and raw `tmux -S … attach` was the only way in. The pane name is now a sufficient handle. `#{pane_dead}` is still queried at attach time, so a dead pane routes to recovery rather than attaching a corpse.
181
+ - **A full UUID is one session, not an ambiguity.** `agents sessions focus <full-uuid>` could answer `"…" is ambiguous (2 sessions). Use more of the id.` with no longer id to give: a transcript that had synced to a second machine counted twice, and one copy had no transcript file left. Synced copies sharing one full id now count as one logical session (SES-IF-2a). A genuine prefix collision between *different* ids still reports both.
182
+ - **`agents sessions reap` is removed** (it shipped in 1.22.36 alongside the dead-pane reaper). The 5-minute daemon sweep stays — it is the part that matters, and it only kills sessions where *all* panes are dead. The daemon calls the reaper directly, and `agents tmux kill <name>` (idempotent) already covers the manual case.
183
+
184
+ Source: `apps/cli/src/commands/sessions-resume.ts`, `apps/cli/src/commands/focus.ts`, `apps/cli/src/commands/{attach,reconnect,go}.ts`, `apps/cli/src/lib/session/types.ts`.
185
+
186
+ - **A tmux-wrapped run killed mid-work no longer reports success (EXEC-23b).** `agents run` wraps an interactive run in tmux and reads the agent's exit status back off the pane. On the three paths where tmux could not report one — the pane unreadable because the server or session went away, or dead with no `#{pane_dead_status}` — it returned `0`. So an interactive run whose tmux server died under it (`[server exited unexpectedly]`, the agent stranded at an approval prompt) printed a failure banner reading `exit 1` and handed its caller `0`: anything scripting `agents run` counted a killed run as a clean finish. Those cases now resolve `1` and print an explicit "outcome unknown" banner, matching the rule the `--host` follow path already used ("the remote's own exit code, or 1 if unknown"). Exit `0` is reserved for an outcome tmux actually reported: a confirmed-alive pane (a clean `Ctrl-b d` detach) or a dead pane whose status tmux read as `0`. One helper, `tmuxRunExitCode(pane, knownAlive)`, is now the single decision behind **every** `runInTmux` return path, so the banner and the returned code cannot disagree. That includes the native-resume attach (`agents run <agent> --resume`), which previously returned a hardcoded `0` without ever querying tmux — `prepareSessionForResume` now returns the pane it resolved so the resumed run can be asked about too. Known cost, accepted deliberately: the daemon's 5-minute dead-pane reap can remove a cleanly-exited session in the moment between its pane dying and the status read, so such a run reports `1`; once the pane is gone there is no other evidence, and a false unknown beats a false success. Source: `apps/cli/src/lib/exec.ts`, `apps/cli/src/lib/tmux/session.ts`, `apps/cli/docs/specifications.md`.
187
+
188
+ - **`agents sessions --browser` / `--computer` now name the agent session that drove the task (RUSH-2549).** Every browser row used to read `unlinked` with `last known owner: UNRESOLVED@<host>`, because task identity lived only in the browser daemon's `tasks.json` — a file `saveTaskState` rewrites from the LIVE task map, so `agents browser stop` erased the link and a daemon restart emptied it. Observed on a real machine: a profile's `tasks.json` was `{}` while its `sessions/` dir held dozens of task dirs full of captures. Identity is now written once at task start to a durable `browser_sessions` row and is **never deleted**, so a finished task still resolves its session. The link also keys on `AGENT_SESSION_ID` — carried by every agent, measured on 5 of 5 live agent processes — in addition to `AGENT_LAUNCH_ID`, which was on only 2 of 5 and was previously the sole join key. Computer-use invocations get the same durable `computer_sessions` row: they already resolved identity correctly (`stampProvenance` reads the session env) but recorded it only in the event ledger, which prunes at 7 days / 50 MiB, so a run vanished on day 8; the row keeps it listed with identity, timing and a total action count (rendered as `N actions (per-verb detail pruned from the event log)`), and never reconstructs the pruned per-verb detail. The table carries its own long retention (`TOOL_SESSION_MAX_AGE_DAYS`, swept from the listing path) so it cannot grow without bound, and reads are limit-bounded. Both tables are **metadata only** — screenshots, PDFs and recordings stay on disk under `.cache/browser/<profile>/sessions/<task>/`, referenced by path; nothing copies bytes into SQLite. Identity is resolved in the calling CLI process, never daemon-side (the shared daemon would attribute every task to itself). Schema v39, additive. Forward-only: captures taken before this cannot be linked retroactively, because their identity was already discarded — those rows read `unlinked` honestly. Known gap: `agents browser start --host <device>` still records no session, because the SSH dispatch forwards `AGENTS_ACTOR*` / `AGENT_TERMINAL_ID` but not `AGENT_SESSION_ID` (`lib/hosts/dispatch.ts` `withActorEnv`); tracked on RUSH-2549, not fixed here. Source: `apps/cli/src/lib/session/db.ts`, `apps/cli/src/lib/browser/service.ts`, `apps/cli/src/lib/browser/sessions-list.ts`, `apps/cli/src/lib/computer/sessions-list.ts`, `apps/cli/src/commands/browser.ts`, `apps/cli/src/commands/computer-actions.ts`, `apps/cli/docs/browser.md`, `apps/cli/docs/computer.md`.
189
+
190
+ ---
191
+ type: feature
192
+ ---
193
+
194
+ Add `usage.primary-host`, a user-scope `agents config` key that pins the device authoritative for fleet usage while falling back to `interactive.host` when unset.
195
+
196
+ - **Fleet usage primary runtime.** When `usage.primary-host` (or `interactive.host`) is set, only that device runs frequent live usage API refreshes; peers pull a token-free snapshot. Standalone mode keeps per-host refresh when no pin is set.
197
+
198
+ - **VSCodium agent tabs get the right chip on focus/resume (#2478).** The
199
+ `vscodium-agent` spawn URI now carries `agent` / `sessionId` / `title` from
200
+ `sessions focus` and `sessions resume`, so remote `ssh … tmux attach` tabs open
201
+ with the harness icon and status bar instead of a generic shell. The terminal
202
+ engine's `SurfaceItem` / `LaunchRequest` plumb the optional identity through;
203
+ other backends ignore it.
204
+
205
+ - Rename the top-level webhook receiver command to `agents webhooks`. The singular spelling is no longer registered; use `agents webhooks serve`.
206
+
207
+ ## 1.22.36
208
+
209
+ - **`agents cp <src> <dst>` — first-class fleet file transfer (RUSH-2297).** New
210
+ top-level command that copies files and directories between fleet hosts
211
+ (local-to-remote, remote-to-local, and remote-to-remote) using the same
212
+ SSH/device fabric as `agents ssh`. Either endpoint is `host:path` (remote) or
213
+ an absolute local path. Remote `~` and literal `$HOME` in a path are resolved
214
+ **on the remote host** before transfer — never in the caller's shell —
215
+ preventing the silent-failure class where a shell variable expands to the
216
+ local user's home directory instead of the remote one. Unknown devices fail
217
+ loudly with a human-readable error before any SSH connection is attempted.
218
+ Recursive directory copies use `-r`; two-remote transfers route through the
219
+ local machine (`-3`) so no direct SSH trust between fleet boxes is required.
220
+ Source: `apps/cli/src/commands/cp.ts`.
221
+
222
+ - **Faster warm CLI bootstrap: skip redundant sync spawn + multi-install PATH scan (RUSH-2324).** Ordinary (non-`--help`/`--version`) invocations no longer fork the detached auto-pull worker when a cycle finished in the last five minutes (parent inspects the `.last-sync` stamp and fresh `*.lock` mtimes under the fetch cache), and the multi-install install-root scan is memoized beside `.update-check` for the same window. Saves the measured ~7.3ms spawn + ~1ms PATH walk on the warm path without changing sync semantics. Source: `apps/cli/src/lib/auto-pull.ts`, `apps/cli/src/lib/auto-pull-worker.ts`, `apps/cli/src/lib/self-update.ts`, `apps/cli/src/bootstrap.ts`.
223
+
224
+ - **Unknown / typo'd top-level commands no longer load every command module (RUSH-2329).**
225
+ A misspelled name used to call `registerAllEagerCommands` (and the lazy
226
+ sessions/teams/cloud tree) solely to build the "did you mean" candidate list —
227
+ ~250–330ms of dynamic import on cold start. Spellcheck now walks the
228
+ plain-string `KNOWN_TOP_LEVEL_COMMANDS` set (same first-seen order for
229
+ tie-breaks) and registers only the auto-corrected command before reparse.
230
+ Distance-1 auto-correct and `--host` re-routing after correction are
231
+ unchanged. Source: `apps/cli/src/index.ts`, `apps/cli/src/lib/startup/spellcheck.ts`.
232
+
233
+ - **CLI bootstrap no longer loads `versions.ts` via the brand edge (RUSH-2331).**
234
+ Every `agents` invocation statically imports `brand.js` for `resolveBrandName` /
235
+ `disabledCommandsForActiveBrand`. That module used to import `agents.js` solely
236
+ for `reservedBrandNames()` / `validateBrandName()` (mine/setup only), and
237
+ `agents.js` pulls the full `versions.ts` graph — ~90ms of module evaluation on
238
+ the `--version` / secrets-broker / bare-help path. `brand.ts` now reads the
239
+ zero-dep `agent-cli-commands` leaf (pinned equal to `AGENTS[*].cliCommand` by
240
+ test); the self-update → primitives redirect from the same ticket remains.
241
+ Source: `apps/cli/src/lib/brand.ts`, `apps/cli/src/lib/agent-cli-commands.ts`.
242
+
243
+ - **Synchronous secrets-broker reads no longer pay ~140ms of CLI bootstrap (RUSH-2335).** `dist/index.js` is now a slim shell that statically imports only the leaf `lib/secrets/sync-commands.js` and dispatches `__secrets-get` / `__secrets-ping` / `__secrets-lock` (plus `__vault-age-helper`, `__shim`, `__daemon-run`, `__daemon-tick`) before loading the full commander graph. Everything else arrives via `await import('./bootstrap.js')`. Cold `node dist/index.js __secrets-ping` was measured at ~160–180ms against a ~22ms bare-node floor; the leaf-only path evaluates in roughly one bare-node spawn plus the agent handler. Source: `apps/cli/src/index.ts`, `apps/cli/src/bootstrap.ts`.
244
+
245
+ - **`agents --help` / `--version` no longer run the macOS menu-bar self-heal (RUSH-2346).** On darwin the startup self-heal (`installMenubarLaunchAgentOnUpgrade()`) ran on every invocation, including the pure documentation paths, dynamically importing `lib/menubar/install-menubar.js` and doing its filesystem checks before deciding to no-op. It now carries the same `helpOrVersionRequested` gate the update check, background sync, and `ensureInitialized` already use, so help and version skip the self-heal's filesystem work and its `installMenubarLaunchAgentOnUpgrade()` call. (On the default path the `lib/menubar/install-menubar.js` module is still pulled into the import graph via the static `migrate.ts` → routine-readiness → hosts chain; shedding that residual import cost is tracked separately.) Source: `apps/cli/src/index.ts`.
246
+
247
+ - **1Password import now captures each item's notes as a description (RUSH-2348).** `agents secrets import --from 1password:<vault>` previously read only an item's credential value and dropped its `notesPlain` notes entirely. The importer now extracts a NOTES-purpose field (1Password's `notesPlain`, or a field labelled notes) as descriptive metadata on the imported secret, while the CONCEALED credential remains the value — notes are never selected as the secret itself. Source: `apps/cli/src/lib/onepassword.ts`.
248
+
249
+ - **`buildExecEnv` strips an ambient `CLAUDE_CODE_OAUTH_TOKEN` on non-interactive Claude runs when no per-account setup-token resolves (RUSH-2360).** A run dispatched on a provisioned box inherits the launcher's shared, rotating `CLAUDE_CODE_OAUTH_TOKEN` through `sanitizeProcessEnv(process.env)`. When the reserved `auth` bundle carried this version home's own setup-token it was injected over that ambient value, but when NONE resolved the ambient token was left in place — so `agents run claude "<prompt>"` could silently authenticate as the shared token, the RUSH-1822 fleet-wide-logout hazard. The non-interactive path now mirrors the routines path (`runner.ts:1017-1021`) unconditionally: inject the resolved setup-token, else delete the ambient one, so a missing login fails loud (401) against this home's own credential rather than borrowing another's. The strip also runs when no version resolves (`version === null`), matching the routines guard. Interactive runs are unchanged — they still keep a deliberately-exported token and drop only an inherited copy of their own setup-token by value (#2383). Source: `apps/cli/src/lib/exec.ts` (`buildExecEnv`).
250
+
251
+ - **Add DeepInfra profiles, accounts, and usage (RUSH-2362).** `agents accounts add deepinfra --provider deepinfra --auth api-key` stores the API key in the existing prompt-free account bundle, and `agents profiles add deepinfra --account deepinfra` configures Codex for `https://api.deepinfra.com/v1/openai` with `deepseek-ai/DeepSeek-V3`. The daemon-owned BYOK refresh path reads DeepInfra's documented `/payment/checklist` endpoint so `agents view` can show current usage and prepaid credit without raising a credential prompt. Source: `apps/cli/src/lib/profiles-presets.ts`, `apps/cli/src/lib/account-provider-registry.ts`, `apps/cli/src/lib/byok-usage.ts`.
252
+
253
+ - **Faster CLI bootstrap: skip `--host` passthrough module graph when no routing flag (RUSH-2374).** Ordinary named invocations (`agents view`, `agents sync`, …) no longer dynamically import `lib/hosts/passthrough.js` (~187ms cold module graph measured on yosemite-s1) just for `maybeRunOnHost` to return false. Bootstrap now gates that import on a leaf `hasHostRoutingFlag` scan of `--host`/`-H`/`--device`/`--hosts`/`--devices` (~0.001ms). Also import `machineId` from the leaf `machine-id.js` instead of the `session/sync/config.js` re-export on the routed path. Source: `apps/cli/src/bootstrap.ts`, `apps/cli/src/lib/hosts/routing-flag.ts`, `apps/cli/src/lib/hosts/passthrough.ts`.
254
+
255
+ - **`agents teams status` shows `PR OPEN` instead of bare `COMPLETED` when a
256
+ teammate finished with an unmerged PR (RUSH-2380).** Process-exit success was
257
+ reported as COMPLETED even when the PR was still open, so orchestrators
258
+ composed on top of unlanded work (3/3 edit-mode teammates one day). Delivery
259
+ is now a separate postcondition (`delivery: pr_open | pr_merged | no_pr | …`)
260
+ on status JSON; the human status label is `PR OPEN` (magenta) when process
261
+ status is completed and a PR URL is present without a known merge. Process
262
+ status still drives the DAG (`--after`); only the display/JSON delivery
263
+ signal changed. Source: `apps/cli/src/lib/teams/delivery.ts`,
264
+ `apps/cli/src/lib/teams/api.ts`, `apps/cli/src/commands/teams.ts`.
265
+
266
+ - **Live agents no longer leave the active set (and become unreachable by `agents message`) when the by-pid registry is empty (RUSH-2384).** A mid-run headless/teams process can keep working with `--session-id` on its argv while `~/.agents/.cache/terminals/by-pid/` has zero files (wrapper pid exited and was pruned, or the launch never recorded one). The headless scan only consulted the registry / SessionStart hook / newest-jsonl heuristic, so foldSubordinateAgents could collapse that process into a parent and drop its id from `agents sessions --active` — and `agents message <id>` resolved purely from that set, so the only recovery was kill-and-redispatch. The scan now reads `--session-id` from the live process argv (Linux `/proc/<pid>/cmdline`, Darwin `ps`), treats argv identity as "own session" so those children are not folded away, and `agents message` falls back to the same process-table proof before reporting no match. Source: `apps/cli/src/lib/session/pid-registry.ts`, `apps/cli/src/lib/session/active.ts`, `apps/cli/src/commands/message.ts`.
267
+
268
+ - **`agents view` names the setup-token usage-scope gap instead of painting healthy headless Claude accounts as broken (RUSH-2392).** `claude setup-token` mints `user:inference` only; Anthropic's usage endpoint requires `user:profile` and returns HTTP 403. The probe and live usage read previously treated that 403 like any other rejection — auth-health mapped it to `revoked`, and `formatUsageSummary` stamped cached bars `unverified`. The accounts best provisioned for unattended work therefore looked the least healthy. The Claude usage path now detects the known scope-denial body (`user:profile` / "scope requirement"), surfaces `usage unavailable (headless)`, and auth-health classifies `reason: usage_scope` as `unverified` (benign) rather than `revoked`. Minting another setup-token cannot fix bars; interactive login (or device-role strategy, RUSH-2395) is the only path to usage meters. Source: `apps/cli/src/lib/usage.ts`, `apps/cli/src/lib/auth-health.ts`, `apps/cli/src/commands/view.ts`, `apps/cli/src/commands/versions.ts`.
269
+
270
+ - **A headless run that exits with its branch on an OPEN PR now says so (RUSH-2394).** A headless `agents run` that backgrounds `gh pr checks --watch` and exits strands its own PR: that watcher is a child of the agent process tree and dies with it, so nothing merges on green and the PR sits open with nobody watching. A headless writable run that exits (or crashes) with the branch still on an OPEN pull request now prints a loud stderr warning naming the PR. Advisory only — it never throws and never blocks the run's exit. Source: `apps/cli/src/lib/pr-land-detach.ts`, `apps/cli/src/commands/exec.ts`.
271
+
272
+ ## Breaking
273
+
274
+ - Replace version-bound account labels with durable credential accounts. Create API-key, Claude setup-token, or bearer-token accounts with `agents accounts add`; native OAuth logins remain harness-managed.
275
+
276
+ ## New
277
+
278
+ - Reuse one named provider credential across compatible native and custom harnesses with `agents run --account <name>` or a harness `account:` default, and rotate it without changing the account id.
279
+
280
+ - **The daemon can no longer crash-loop unbounded (RUSH-2418).** Crash-loop prevention existed only as the OS supervisor's retry, uncapped: the launchd plist set `KeepAlive` with no `ThrottleInterval`, so a daemon dying during startup was relaunched on launchd's ~10s default — six times a minute, forever — and the systemd unit's `Restart=always` had no `StartLimitIntervalSec`/`StartLimitBurst` to ever give up. Three layers now bound it. The plist carries a `ThrottleInterval` (the same fix the menu-bar helper already ships), the unit carries `StartLimitIntervalSec`/`StartLimitBurst` in `[Unit]` (where systemd 229+ actually reads them) with `RestartSec` paced so the bursts fit inside the window, and `agents __daemon-run` installs top-level `uncaughtException`/`unhandledRejection` handlers — there were none anywhere in the CLI — that route the failure into the daemon's own `logs.jsonl` and exit non-zero deterministically instead of dying on Node's default handler with a raw stack. Separately, `daemon-health.ts`'s `consecutiveFailures` was write-only telemetry; a new `daemon-start` record now drives an auto-start circuit breaker. Starts are counted up front and cleared only by a daemon that finishes booting, because the crash loop is invisible from the launcher's side — a daemon that spawns and then dies returns a perfectly real pid, so there is no error to observe. After 5 consecutive starts that never report healthy, the implicit auto-start (`ensureDaemonStarted`, reached from `secrets unlock`, `browser start`, the watchdog) refuses instead of relaunching a broken daemon on every foreground command, and `agents daemon doctor` reports the streak and its recorded cause. An already-running daemon is still reported, and the explicit `agents daemon start` override is never gated. Source: `apps/cli/src/lib/daemon.ts`, `apps/cli/src/lib/daemon-health.ts`, `apps/cli/src/commands/daemon.ts`, `apps/cli/src/index.ts`.
281
+
282
+ - **`agents daemon stop` now proves the daemon's state files were released, not just its sockets (RUSH-2421).** The stop postcondition checked the daemon process and the two sockets (browser IPC, secrets broker) but not the three files a graceful shutdown removes: the lifetime marker, the heartbeat, and the daemon's instance-registry entry. Those are cleaned up in `handleShutdown`, which never runs on the escalated `killTree` path or on Windows — so a wedged daemon that had to be killed left all three behind while the stop reported `ok: true`. That is not cosmetic: a leftover heartbeat is what `resolveLiveDaemonPid` consults to re-adopt a daemon whose pid file is gone, so a dead daemon could read as running, and a leftover registry entry is what the stray-daemon reaper enumerates. Each is now checked and reclaimed the same way the sockets are, and reported in `released`/`surviving` — with the same ownership rule the broker socket already used: a marker naming a pid that is alive and is not the daemon just stopped belongs to a live successor and is left untouched. Separately, `BrowserIPCServer.stop()` called `net.Server.close()` without awaiting its `'close'` event, so it resolved while the socket was still bound; since the daemon awaits it before exiting and a successor treats that exit as proof the predecessor's resources are free (SING-11), the proof was false. It now waits for the real release — ending held client connections so the binding is freed immediately, with a 1.5s backstop kept well below the daemon's SIGTERM grace window so a slow close can never be what gets the daemon killed. Source: `apps/cli/src/lib/daemon.ts`, `apps/cli/src/lib/browser/ipc.ts`.
283
+
284
+ - **A graceful `agents daemon stop` no longer escalates to a kill when a browser client is attached (RUSH-2421 follow-up).** The awaited-close fix shipped with its timeout set to 5s — exactly the daemon's own SIGTERM grace window. `net.Server.close()` does not complete while any connection is open, and browser clients hold one open on purpose (the socket stays warm between actions), so `handleShutdown` was still inside `browserIPC.stop()` when `stopDaemon` gave up waiting and ran `killTree`. Measured: 5512ms for a stop that should take milliseconds. `stop()` now ends the tracked client connections instead of waiting for them, so the binding is released immediately; the timeout is a backstop only, cut to 1.5s so a slow close can never be what gets the daemon killed. `stop()` is also idempotent now — a second SIGTERM awaits the same close rather than racing past it because the server handle was already cleared. Separately, the stop's registry-entry check treated its own pid as residue unconditionally, so a daemon that survived the kill lost the record `findSurvivingStateDirDaemons` reads and the *next* stop would report `ok: true` with it still running; ownership is now decided by the stop's own survivor scan rather than `isAlive`, which also fixes the zombie case (a SIGKILLed daemon stays signalable until reaped, so `kill(pid, 0)` reports it alive). Source: `apps/cli/src/lib/browser/ipc.ts`, `apps/cli/src/lib/daemon.ts`.
285
+
286
+ - **Repeated SIGTERMs during daemon shutdown now run the shutdown once (RUSH-2423).** `handleShutdown` is reachable from SIGTERM, SIGINT, and the state-dir self-check, and two can arrive together — a service manager SIGTERMing a daemon whose state dir was just removed. It was safe only because every step inside happens to be idempotent, which is a property each newly added step would silently have to re-earn; single-shot is now a property of the function. Alongside it, three internals cleanups with no behaviour change: the daemon's log path is exported as `getDaemonLogPath()` and the two commands that rebuilt it from a hardcoded `'logs.jsonl'` literal (`agents daemon logs`, `agents routines`) now call it, so renaming the file cannot silently point them at nothing; the background-tick cadences that were inline literals at their `setInterval` are named beside the existing tick constants, each with why that cadence and not another; and the comments settle on one term, "state dir", replacing a daemon-dir/state-dir/state-tree mix. Source: `apps/cli/src/lib/daemon.ts`, `apps/cli/src/commands/daemon.ts`, `apps/cli/src/commands/routines.ts`.
287
+
288
+ - **A torn `meta.json` write no longer permanently disables orphan-worktree cleanup for every worktree name in every team (RUSH-2429).** `saveMeta()` wrote with a bare `fs.writeFile` — no tmp file, no rename — so a process killed mid-write left a truncated, unparseable `meta.json`. `loadFromDisk()` returned `null` for that file from a bare catch, indistinguishable from the file never having existed, so `loadExistingAgents()`/`rescanFromDisk()` silently skipped it forever and `isWorktreeClaimed()` (which reads `meta.json` directly, not through the cache) then failed CLOSED on it globally: it scans every record and answers "claimed" the first time it can't read one, so one corrupt record permanently blocked `teams add`'s orphan-worktree teardown for every name in every team. `saveMeta()` now writes to a sibling tmp file and `fs.rename`s it into place — atomic on POSIX, so a killed write leaves either the previous valid record or the new one, never a torn one. `loadFromDisk()` now quarantines a record whose *content* is corrupt — a torn or unparseable `meta.json`, the case that actually breaks cleanup — by renaming it to `meta.json.corrupt` with a warning, so it stops masquerading as "no record" and a subsequent `isWorktreeClaimed()` scan sees real absence for that entry instead of failing closed on it forever. A transient read error (EACCES/EIO/EMFILE) is NOT corruption: the file is intact and simply could not be read that moment, so `loadFromDisk()` returns `null` without renaming it — renaming a valid record away would itself be the fail-open this fix exists to prevent. `isWorktreeClaimed()`'s fail-closed behavior for a record that is genuinely present-but-unreadable at decision time is unchanged — a spurious "unclaimed" would run `git worktree remove --force` over a live teammate's checkout, so the guard stays as strict as before; only a corrupt record's *permanence* is fixed. Source: `apps/cli/src/lib/teams/agents.ts`.
289
+
290
+ - **Daemon routines can no longer resolve a stale `agents` binary from the Node prefix (RUSH-2431).** The daemon service manifest now puts the directory containing the running `agents` launcher first on `PATH`, ahead of the Node bin directory. This stops scheduled routines from shadowing the current binary with an older `agents` installed inside the same Node prefix. As a runtime backstop for already-running daemons, `command` routines also inject a shell-function guard (and a `PATH` prepend fallback) so bare `agents` invocations use the same binary that launched the routine. `agents doctor` now reports a `binary-shadow` warning when another `agents` binary on `PATH` or in a well-known install directory could preempt the running one. Source: `apps/cli/src/lib/daemon.ts`, `apps/cli/src/lib/runner.ts`, `apps/cli/src/lib/cli-entry.ts`, `apps/cli/src/lib/binary-shadow.ts`, `apps/cli/src/lib/devices/doctor-findings.ts`, `apps/cli/src/commands/doctor.ts`.
291
+
292
+ - **`agents computer sessions` / `agents sessions --computer` is task-first, grouped by run (RUSH-2432).** The computer counterpart of `agents browser sessions` (RUSH-2407): every `computer.action` event (already written by every `agents computer <verb>` and the embedded `run --task` loop) is read back from the durable event ledger and grouped by a unique CLI invocation identity — one row per invocation without conflating later OS pid reuse. Each row shows machine, target app/window when known, per-verb action counts, and — when the run's `sessionId`/`launchId` resolves — the owning agent session's canonical digest. A run with an unindexable identity shows unresolved; one with no agent-session identity at all shows unlinked. On a TTY the picker is interactive with search and an action-list drill-down (no artifact to open, unlike browser); `--no-interactive`/`--json` print the flat table. The `agents sessions --computer` alias also preserves `--limit`, `--host`, and `--device`. Nothing sensitive is added to the ledger: typed text stays length-only, and a `--task` description is bounded to 200 characters before it is ever written. Source: `apps/cli/src/lib/computer/sessions-list.ts`, `apps/cli/src/commands/computer-sessions-picker.ts`, `apps/cli/src/commands/computer.ts`.
293
+
294
+ - **A session no longer vanishes when its transcript file is deleted — the local DB is authoritative for its user turns (RUSH-2436).** Session history was a file-anchored index: delete the `.jsonl` (via `agents remove`'s trash, a manual `rm`, a `.history` version-home rotation, or a box reimage) and the session silently disappeared from every `agents sessions` listing and could not render, even though a DB row and its user-turn text still existed. The user-turn content for every harness is already stored durably in the `session_text` FTS `content` column, so `querySessions`/`topSessionsByCost` now KEEP a file-gone session whose content survives — flagged `archived` — instead of dropping it, `agents sessions <id> --include user` renders those user turns from the DB (with an `archived — transcript file removed` banner) instead of a bare metadata dump, and the picker preview does the same. A row whose file is gone and whose content is empty (a phantom — a stale/moved `file_path`) stays suppressed. Merely listing a file-gone session also no longer PURGES its redacted tool-call evidence: that destructive purge-on-read (`purgeToolCalls` from the `querySessions` missing-file branch) is removed. A row whose file returns (a recoverable-trash restore) is un-archived on the next listing. A new persisted `archived_at` column (schema v38) stamps the first confirmation that a scanned file went missing. Source: `apps/cli/src/lib/session/db.ts`, `apps/cli/src/lib/session/tool-store.ts`, `apps/cli/src/commands/sessions.ts`, `apps/cli/src/commands/sessions-picker.ts`.
295
+
296
+ - **Optional off-box session backup to Cloudflare R2 (`agents sessions export --to-r2` / `import --from-r2`).** A durable, on-demand backup target for session transcripts — protection against losing the whole box, not just a file on it. `export --to-r2` uploads each selected session as its own encrypted object under `sessions/<machine>/<agent>/<session>` in the `r2.backups` bucket; `import --from-r2` downloads and restores them through the normal placement (deduped, local-always-wins). Bodies are sealed client-side with AES-256-GCM under the shared `R2_SYNC_ENC_KEY` (zero-knowledge — Cloudflare only ever stores ciphertext); credentials come only from the `r2.backups` secrets bundle (keychain, never env/disk). It is a pure backup target: on-demand only, no daemon cycle, and not the retired multi-writer sync. A missing/locked `r2.backups` bundle fails loud with an actionable message, never a silent no-op. Source: `apps/cli/src/lib/session/sync/r2.ts`, `apps/cli/src/commands/sessions-export.ts`, `apps/cli/src/commands/sessions-import.ts`.
297
+
298
+ - **`agents sync` now prunes resources removed from source, instead of only ever adding (RUSH-2438).** A command or skill deleted from a DotAgent repo used to linger in every installed version home forever — observed: 8 removed commands stayed live in the `/` menu of all 7 Claude homes, and `--force` did not clear them. The gap was that the repo-scope reconcile forms (`agents sync <agent>@all system`, `agents sync <agent> system --force`) pass a selection, which turned off the full-sync orphan sweeps; those forms now reconcile deletions too. The prune is **manifest-bounded** so it never over-deletes: the removal set is `(names the last full sync recorded) − (names still present in source across ALL layers)`, intersected with what is actually in the home. Three guarantees follow — a file you hand-authored into `~/.claude/…` was never recorded so is never touched; a system command removed while a same-named **user** command still exists is kept (still provided by a layer); and with no sync manifest yet the reconcile prints a one-line notice and prunes **nothing** rather than guessing. Every harness prunes through the same writer that installed the resource — a native command file (Claude/Grok/Cursor), a command-as-skill dir (Codex ≥ 0.117, Kimi), a Goose recipe. Removals are shown under a `Pruned from <agent>@<version>` block and carried in the `--json` payload as `pruned: { commands, skills }`. **Hooks are out of scope here** — pruning a hook must also GC its `settings.json`/`hooks.json` registration, a Windows-portable-path surface; tracked in **RUSH-2456**. Hook files stay reconciled by the existing in-write orphan sweep. Source: `apps/cli/src/lib/staleness/prune.ts`, `apps/cli/src/lib/staleness/writers/{commands,skills}.ts`, `apps/cli/src/lib/versions.ts`, `apps/cli/src/commands/sync.ts`.
299
+
300
+ - **Stop `agents run` from triggering a broad Keychain scan (RUSH-2440).** Agent-only bundle reads now resolve exact items from bundle metadata instead of enumerating the macOS Keychain, preventing unrelated biometry-protected secrets from raising a Touch ID sheet on every launch. Interactive reads retain the existing enumeration-plus-declared-item union. Source: `apps/cli/src/lib/secrets/bundles.ts`.
301
+
302
+ - **Restore the stale-ACL'd `hmackey` heal on the hot read path (RUSH-2441).** v1.22.7 moved the once-per-machine no-ACL re-store of `agents-cli.hmackey` onto `readHmacKeyRecord` so every hashed keychain lookup (including the SessionStart `agents devices list` stats probe) converged a biometry-ACL'd key to silent. v1.22.10 (`bf79dc885` / #1995) put the heal back inside `maybeAutoRekey`, which is bypassed for the hmackey and hashed-name lookups themselves (`prepareServiceName` returns early for `HMAC_KEY_ITEM`), so an already-migrated machine prompted forever again — and the 1.22.7 changelog still claimed the read-path guarantee. The heal is back on `readHmacKeyRecord`: first hashed lookup re-stores no-ACL once (guarded by `healedNoAcl`), silent forever after. Source: `apps/cli/src/lib/secrets/index.ts`.
303
+
304
+ - **`scripts/install.sh` restarts a running routines daemon after a dev install (RUSH-2442).** The dev install strips the npm `postinstall` hook (so it doesn't nudge PATH for a side-by-side prefix), which was also the only place that bounced the daemon onto the just-staged code. A long-lived daemon kept hosting a secrets broker built from the previous install; version skew then wiped held bundles and re-armed Touch ID on the next secrets read. After linking bins, `install.sh` now reloads a *running* daemon onto the just-linked binary (best-effort, skipped in CI / `AGENTS_NO_HEAL=1`, never starts a daemon that wasn't already up). Source: `apps/cli/scripts/install.sh`.
305
+
306
+ - **`agents share` gains `--unlisted`/`--private`, a 30-day default expiry, and a pre-publish sensitive-content scan (RUSH-2443).** Root cause of the RUSH-2428 incident: every publish was world-readable with no private option, no default expiry, and no content gate. Unflagged publishes now expire in 30 days (`--expire never` for permanent). `--unlisted` (alias `--private`) stores `visibility=unlisted` so the public gallery and `agents share list` omit the page while the direct URL still works (capability URL — unlisted, not secret). Before upload the CLI refuses files that contain email addresses or credential-shaped strings (`ghp_…`, `sk-…`, `AKIA…`, `Bearer …`) unless `--force`. Requires `agents share update` on already-provisioned endpoints so the Worker filters unlisted objects. Source: `apps/cli/src/lib/share/{publish,worker-template}.ts`, `apps/cli/src/commands/share.ts`.
307
+
308
+ - **`agents share list` shows what you've published (RUSH-2444).** There was no way to ask the CLI which pages are live under your share namespace — during the RUSH-2428 incident the only way to answer "is anything else of mine public?" was to fetch the gallery HTML and grep it. The Worker now exposes a machine-readable listing at `GET /<user>?format=json` (a `?format=json` discriminator on the existing single-segment gallery path, gated on the SAME "does `<user>/` hold objects" check the gallery uses — so the HTML gallery is untouched AND a legacy flat slug with `?format=json` still serves its real page rather than a fake empty listing), and `agents share list` reads it: a human table newest-first by default, or the raw listing with `--json` (each object carries `slug`, `url`, `size`, `contentType`, `publishedAt`, and `expiresAt`). It lists the ACTIVE pages only — expired links and the sibling `<slug>.png` OG covers are omitted, mirroring the public gallery. An empty namespace 404s at the Worker, which `list` reads as "nothing published" (via the template-hash signal) and reports cleanly. Because this adds a route to `worker-template.ts`, the feature only reaches an endpoint after `agents share update` (RUSH-2449) deploys the current template; an endpoint that predates the route makes `list` fail loud with a "run `agents share update`" hint (reusing the `agents share status` template-hash signal, and detecting a live 404 on an unknown-template endpoint / a non-JSON gallery body) rather than returning a wrong or empty result. Source: `apps/cli/src/lib/share/worker-template.ts`, `apps/cli/src/commands/share.ts`.
309
+
310
+ - **`agents fleet update` now verifies each box actually runs the new version, instead of trusting `exit 0` (RUSH-2446).** The rollout ran `agents upgrade --yes` per device and called a box `ok` on the exit code alone. That exit code only says the npm global moved — it says nothing about which copy `agents` resolves to on that box, and on any dev box `scripts/install.sh` puts a side-by-side build at `~/.local/agents-cli-dev` with `~/.local/bin/agents` pointing at it, earlier on PATH than the npm global. So the rollout reported a clean sweep while those boxes kept running old code, and nothing in the fleet surfaces answered "which boxes actually run the shipped version?" — `agents fleet status` reports version skew as a separate glance, and the local multi-install banner is advisory stderr on the box itself that never crosses the SSH hop. After each successful upgrade the rollout now asks the box what `agents` resolves to (symlinks followed) and what version that copy reports. A box on the target reads `ok runs <version>`; a box whose resolved `agents` is a dev build reads `stale NOT upgraded — agents resolves to a dev build (0.0.0-dev.<sha>) at <path>, shadowing the upgraded <version> global`; a box on some other released version reads `stale` with both versions and its resolved path. Those boxes are counted in a new `N not upgraded` tally, excluded from the `ok` count, and make the command exit non-zero. A box whose probe cannot answer — no POSIX shell, or `agents` unresolvable — reads `unverified` and is likewise not counted as a success, because "we could not check" and "it is upgraded" are different answers. A `failed` or `skipped` box is not re-probed, so one fault still produces one row. With no explicit version argument the target is derived as the highest released version any probed box reports; dev stamps are never elected as the target, so a fleet of dev builds cannot declare itself upgraded. Source: `apps/cli/src/lib/devices/rollout-verify.ts`, `apps/cli/src/commands/ssh.ts`, `apps/cli/src/lib/startup/dev-build.ts`.
311
+
312
+ - **`agents share update` — push a `worker-template.ts` change out to an already-provisioned
313
+ endpoint (RUSH-2449).** `agents share setup` only ever wrote the Worker script during first
314
+ provisioning, so every endpoint was pinned to whatever template it got on day one — there was
315
+ no way to ship a Worker-side fix or feature (private publishing, `share list`, access logging)
316
+ to someone who already ran setup. `agents share update` re-deploys the current template to the
317
+ existing account/worker/bucket with no re-provisioning (no bucket creation, no route/domain
318
+ changes) and never regenerates `WRITE_TOKEN`: Cloudflare's script-upload endpoint replaces a
319
+ Worker's bindings/secrets wholesale on every upload, so the existing token is re-applied via the
320
+ Secrets API immediately after the script upload. Idempotent — re-running it when the deployed
321
+ template already matches is a no-op (`--force` to redeploy anyway). `ShareConfig` now records a
322
+ `templateHash`, and `agents share status` reports `current` / `outdated` / `unknown` (a config
323
+ from before this field existed reads as `unknown`, never as stale). `agents setup share`, run
324
+ against an already-configured endpoint, now offers "update the deployed Worker" alongside
325
+ "keep" and "reconfigure from scratch". Source: `apps/cli/src/lib/share/provision.ts`
326
+ (`hashWorkerScript`, `updateWorker`), `apps/cli/src/commands/share.ts` (`runShareUpdate`,
327
+ `shareTemplateStatus`), `apps/cli/src/commands/setup-share.ts`.
328
+
329
+ - **`teams disband` is terminal — PENDING teammates cannot resurrect or re-start (RUSH-2450).** Disband deleted log dirs and the registry entry but left the in-memory `AgentManager` cache intact; a concurrent `teams start --watch` supervisor then re-persisted those records via `saveMeta`, so a second disband still found N logs to clear and `teams start` could re-launch already-merged work. Disband now drops the registry entry first (writing a durable disband tombstone), then purges every teammate record from disk **and** the manager cache (`AgentManager.purgeByTask`). `saveMeta` refuses to re-persist a teammate whose team carries a disband tombstone. A second disband is a clean no-op; `teams start` on a disbanded name fails loud. Source: `apps/cli/src/lib/teams/agents.ts`, `apps/cli/src/lib/teams/registry.ts`, `apps/cli/src/commands/teams.ts`.
330
+
331
+ - **Usage and authentication refresh now have one device-level owner (RUSH-2451).** The daemon maintains shared per-account usage snapshots and auth-health metadata; `agents run`, `view`, `versions`, teams, device inventory, and Factory remain cache-only readers. `agents usage <agent> --refresh` and other explicit refresh paths enter the same cross-process lease, so simultaneous CLI processes reuse one provider request or local-log scan. BYOK budget reads use the same persisted, atomic cache model. OAuth stays harness-managed and per-device; only safe health/account metadata is shared, while named API keys, setup tokens, and bearer tokens continue to use device-local credential storage. Source: `apps/cli/src/lib/account-state-service.ts`, `apps/cli/src/lib/refresh-coordinator.ts`, `apps/cli/src/lib/usage.ts`, `apps/cli/src/lib/byok-usage.ts`, `apps/cli/src/lib/auth-health.ts`, `apps/cli/src/lib/fleet-status.ts`, `apps/cli/src/commands/usage.ts`.
332
+
333
+ - **`agents share update` names the partial-failure window when the write token fails to re-apply (RUSH-2453).** Cloudflare's script upload clears Worker secrets; `update` re-applies `WRITE_TOKEN` immediately after. If that second call fails (network blip, expired API token, rate limit), the live endpoint has no write token and every publish/delete 401s. The error now says so and tells you to re-run `agents share update`. Config is only rewritten after both steps succeed, so a re-run does not short-circuit on a matching hash — a test pins that self-heal property. Source: `apps/cli/src/lib/share/provision.ts`, `apps/cli/src/commands/share.ts`.
334
+
335
+ - **`agents --help` / `--version` no longer load the migration graph (RUSH-2454).** Follow-up from RUSH-2346: the menu-bar self-heal was already gated, but `foldLegacySystemRepo()` and the v19 `runMigration()` hop still ran on pure documentation paths and dynamically imported `lib/migrate.js` — whose static imports pull the hosts/routine/teams/daemon/menubar graph (~287 modules). Both hops now share the same `helpOrVersionRequested` gate. The always-on fold itself moved to a leaf `lib/migrate-fold.ts` (fs + `createLink` only), and the full `migrate.js` import is deferred until a missing/stale v19 sentinel actually requires `runMigration()`. Source: `apps/cli/src/index.ts`, `apps/cli/src/lib/migrate-fold.ts`, `apps/cli/src/lib/migrate.ts`.
336
+
337
+ - **Grok's binary resolver could silently launch a different agent's binary (RUSH-2459).** When no `grok-*` filename in a version-home's `.grok/downloads` carried the pinned version string — which happens routinely, since grok self-updates its binary in place — the dispatcher shim, the direct `grok@<version>` alias, and `getBinaryPath` all fell back to whatever a plain directory listing returned first, with no validation that the candidate was actually a grok binary. On one machine a stale, unrelated 99-byte wrapper script (`exec cursor-agent "$@"`) sorted before the real ~127MB self-updated binary and was launched instead, so `agents run grok` and Factory's "New Grok" silently ran Cursor. A second, compounding bug made the shim's "exact version match" a no-op for the versioned home: it grepped `ls`'s full path output, and the version-home's own path always contains the version string, so it matched every candidate unconditionally. Both are fixed: the exact-match check now scopes to each candidate's filename, and when no filename genuinely carries the version, the fallback rejects any candidate under 1MB (real grok binaries are ~100MB+) and prefers the most recently modified survivor — never guessing among untrusted artifacts. Source: `apps/cli/src/lib/versions.ts`, `apps/cli/src/lib/shims.ts`.
338
+
339
+ - **`agents add warp` installs the new Warp Agent CLI (curl installer), not the removed `oz` brew cask (RUSH-2461).** `agents add warp@latest` failed with `Error: Cask 'oz' is unavailable: No Cask with this name exists.` — the registry modeled Warp's older `oz` platform runner via `brew install --cask oz`, a cask that only lives in the `warpdotdev/warp` tap, not homebrew-core. The `warp` entry now points at Warp's current standalone agent CLI (docs.warp.dev/cli): install via the official cross-platform `curl -fsSL https://app.warp.dev/download/agent-cli | bash`, binary `warp` at `~/.local/bin/warp` (self-updating, like droid/muse). It is an interactive TUI — bare `warp` opens the agent and there is no headless one-shot form (the documented flags are `--api-key`/`--auto-approve`/`--resume <token>`/`--version`, with no `-p`/`--model`/JSON output), so `agents run warp` launches the TUI and the exec spec drops the stale `oz agent run -p --output-format json` mapping. The oz→warp binary rename is propagated across version detection (`findInPath('warp')`), the runtime + generated shims, the reserved-brand list, and the sign-in hint (bare `warp` opens browser sign-in, or set `WARP_API_KEY`). Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/exec.ts`, `apps/cli/src/lib/versions.ts`, `apps/cli/src/lib/shims.ts`, `apps/cli/src/lib/agent-cli-commands.ts`, `apps/cli/src/lib/signin-badge.ts`.
340
+
341
+ - **The 8 daemon-housekeeping routines are now daemon-owned built-ins instead of `.agents-system` config-repo files (RUSH-2465).** `usage-refresh`, `fleet-cache-warm`, `session-cache-warm`, `device-probe`, `auto-dispatch`, `watchdog`, `tmux-reconcile`, and `launch-health` were shipped as `routines/*.yml` in `gh:phnx-labs/.agents-system` (RUSH-2353, to gain declaration, run history, pause, and device pin over the old hardcoded `setInterval`s). A daemon's own housekeeping does not belong in the config repo every install pulls, so the definitions now live in daemon code (`lib/builtin-routines.ts`) and are injected as the **lowest** layer of `listJobs()` — below project > user > system. The same pid-claimed `JobScheduler` schedules and fires them via `agents __daemon-tick <name>` (`lib/daemon-ticks.ts`) exactly as before, so scheduling, run-tracking, pausability (`agents routines pause <name>`), and device pinning (`agents routines devices <name> --set`) are unchanged — the singularity guarantees (auto-dispatch's owner-pin, the cache-warms' publish-own/read-union, usage-refresh's per-account cadence/backoff) are preserved because they were never properties of the definition file. Because built-ins are the lowest layer, a same-named `~/.agents/routines/` file still overrides one, and a still-shipped `.agents-system` YAML shadows it during the removal transition (so exactly one definition ever fires). `agents routines list` tags each with `(built-in)` and the `--json` output carries `builtin: true`. `.agents-system` keeps only `check-updates.yml`; its removal PR (`phnx-labs/.agents-system#272`) merges only **after** this ships, or fleet/session/usage caches, the watchdog, and auto-dispatch would stop fleet-wide. Source: `apps/cli/src/lib/builtin-routines.ts`, `apps/cli/src/lib/routines.ts`, `apps/cli/src/lib/daemon.ts`, `apps/cli/src/lib/daemon-ticks.ts`, `apps/cli/src/commands/routines.ts`.
342
+
343
+ - **Use one secret bundle as an account across compatible agents.** `agents accounts` now keeps provider, authentication type, optional URL, stable identity, and API key or long-term setup token in the account's own `agents secrets` bundle. Account bundles are permanently prompt-free (`policy: never`), can be selected explicitly or as a per-harness default, and can be explicitly synced to a worker with `agents accounts sync <name> --device <device>`. Harness-native auth remains owned by each harness and is never copied. Existing v2 `accounts.yaml` entries migrate transactionally into bundles and retain their stable IDs. Source: RUSH-2470.
344
+
345
+ - **`scripts/release.sh` takes `--device <name>` to pick the release Mac, instead of a hardcoded `mac-mini`.** The build/sign/notarize/npm-publish phase still needs a Mac (codesign/notarytool + the signed binaries in the tarball), but which Mac is no longer welded into the script: it defaults to `mac-mini` and `--device zion` (alias `--host`) routes the privileged phase to any capable Mac at the same tagged commit — so a release is no longer stuck when mac-mini is offline. The git/PR/merge/tag orchestration still runs on whatever box invoked it, and the origin-side lease still guarantees a single releaser. `scripts/remote-sign-mac.sh` gained the same flag. Source: `apps/cli/scripts/release.sh`, `apps/cli/scripts/remote-sign-mac.sh`, `apps/cli/scripts/release.test.ts`.
346
+
347
+ - **`agents sessions --active --device <box>` lists only sessions actually running on that box, and an offloaded session previews again (RUSH-2479).** A run dispatched with `agents run --device <peer>` leaves a live shim process on the *dispatching* machine carrying the remote run's session id, and nothing attributed that row to the peer — so `--device zion` listed sessions executing on `yosemite-s0`, tagged `[host/yosemite-s0]`, and every one of them rendered an empty preview ("Live session — full transcript not indexed here") because the transcript lives on the peer. Three things changed. `machine` is now the **execution** host: `foldExecutionMachine` folds the machine the dispatch already recorded in the session index back onto the live row before it leaves the box, and the cross-machine fan-out no longer overwrites a peer-reported machine that names a third box. A host scope is then enforced on that field — `--host`/`--device` means *where the session runs*, not which box answered — applied inside the single gather so the interactive browser and `--active --json` cannot disagree. And because the dispatcher's shim and the executing machine's own row now share a dedupe key, the merged fleet view keeps the row that owns the transcript instead of the `[host/<peer>]` placeholder. A peer's own self-report still outranks this box's index copy, so a genuinely remote row is never re-tagged. New field `offloadedFrom` on `ActiveSession` / `--active --json` marks the dispatcher's shim row. Contract: `docs/specifications.md` SES-23a; narrows SES-GAP-5. Source: `apps/cli/src/lib/session/active.ts`, `apps/cli/src/lib/session/remote-active.ts`, `apps/cli/src/commands/sessions.ts`.
348
+
349
+ - **Monitors record a per-poll liveness heartbeat, so a watcher that never fires is no longer indistinguishable from one that never ran (RUSH-2485).** Change-detection state (`lastValue`/`lastFiredAt`) was written only when a monitor fired or set a baseline, so a `--match` monitor that polled steadily but never matched left `state.json` empty — and `agents monitors view <name>` showed `state: null`, exactly what a monitor the engine never touched looks like. A monitor added as the durable lander for a PR read healthy (enabled, daemon active, `test` evaluating on demand) while doing nothing for ~40 minutes. The engine now writes a liveness record on **every** poll — `lastCheckedAt`, `checkCount`, `lastError`, `consecutiveErrors` — kept in its own `liveness.json` so it never perturbs the baseline logic. `agents monitors list` and `view` surface it: `never polled` (yellow), `checked Nx · no match yet`, `STALLED — last poll <ago>` (red, when an enabled+owned monitor's last poll falls >3 intervals behind), and per-poll errors. `--json` on both gains `lastCheckedAt`, `checkCount`, `lastError`, `consecutiveErrors`, and `stalled`. `agents monitors add` now asserts the postcondition — it waits for the engine's first poll and reports whether the monitor was actually picked up, instead of reporting success on config acceptance. And after 5 consecutive failed checks (a source that errors every poll, or an action that fails every fire), the engine notifies the owner once that the monitor is doing nothing. Source: `apps/cli/src/lib/monitors/state.ts`, `apps/cli/src/lib/monitors/engine.ts`, `apps/cli/src/commands/monitors.ts`.
350
+
351
+ - **`agents sessions` attributes offloaded work to the box it runs on, consistently.** Resuming a live host-dispatched run by id (`agents sessions <id>` for an `agents run --device <peer>` session) no longer fails with "ambiguous (2 sessions)": `queryIndexedSessions` kept the machine derived from the transcript path for every row, and an empty file path (a remote transcript) fell back to *this* box — re-attributing the dispatcher's own pool row to itself so it no longer deduped against the executing peer's fan-out row. It now keeps the execution host the dispatch recorded on that row. And a remote teams teammate (`agents teams add … --device <peer>`) is now attributed to its execution host too: `listTeamsActive` folds the teammate's `hostName` into `machine`/`offloadedFrom` the same way `agents run --device` rows are folded, so `agents sessions --active --device <orchestrator>` no longer lists a teammate that is executing on a peer (RUSH-2486, closing SES-GAP-10; the residual of RUSH-2479). Source: `apps/cli/src/lib/session/discover.ts`, `apps/cli/src/lib/session/active.ts`.
352
+
353
+ - **A project is now a set of directories, and every spawn path reaches all of them (RUSH-2489).**
354
+ `ProjectDef.repos[]` could already name additional checkouts, but nothing wrote it and nothing
355
+ that started an agent read it: `agents run --project rush` landed in `defaultPath ?? root` and
356
+ every other repo the project bound was invisible to the agent. Work spanning a CLI, its website,
357
+ and the DotAgents repo it ships into needed three agents or a hand-passed `--add-dir`.
358
+
359
+ **Writing.** `agents projects add` gains a repeatable `--dir <path...>`; `agents projects set`
360
+ gains repeatable `--add-dir <path>` / `--rm-dir <path>` plus `--slug <owner/repo>`. Each value
361
+ names a **local directory** and the slug is read from **that directory's own `origin` remote**,
362
+ never inferred from its path — a checkout at `~/src/github.com/muqsitnawaz/agents-cli` whose
363
+ origin is `phnx-labs/agents-cli` records what it actually pushes to. `--slug` covers a directory
364
+ with no origin and applies to a single `--add-dir`. Removals apply before additions, so
365
+ `--rm-dir old --add-dir new` re-points a directory in one command. Binding a directory twice,
366
+ unbinding one that is not bound, and naming a path that does not exist are all refused with the
367
+ fix named.
368
+
369
+ **Spawning.** `agents run --project` keeps returning the primary directory as cwd — unchanged —
370
+ and merges the project's other directories into `--add-dir`, deduped against anything passed
371
+ explicitly, locally and over `--host` (forwarded `~/…`, which the receiving `agents run` expands
372
+ against the host's own `$HOME`).
373
+ `agents teams create` gains `--project <slug>`: it is validated at create time rather than at the
374
+ first `teams add`, the project's primary directory becomes a local teammate's base cwd, and the
375
+ sibling directories are attached as grants. The teammate record stores the project **name**, not
376
+ a frozen list of directories, and the grants resolve per launch — an unpinned teammate on a
377
+ `--devices` pool only learns its host from the scheduler at launch time, so add-time resolution
378
+ would have handed it this box's absolute paths (and already dropped any directory that exists
379
+ only on the host it landed on). The team cwd precedence is now
380
+ `worktree → --cwd → project directory → process.cwd()`, so an explicit `--cwd` still wins; the
381
+ grants are attached either way, since the siblings are what the project binds rather than where
382
+ the teammate sits. A `--project slug@worktree` run keeps the worktree as cwd and grants the main
383
+ checkout alongside the siblings. A teammate staged behind `--after` resolves its grants when the
384
+ supervisor finally launches it, so a restart in between changes nothing.
385
+
386
+ **Two honest limits, stated rather than papered over.** Only Claude and Codex consume
387
+ `--add-dir` (Claude takes the native flag, Codex folds the paths into `workspace_roots`); every
388
+ other harness ignores the grants and sees the cwd alone. And a bound directory that is absent
389
+ from the current box is skipped for a local spawn rather than erroring — but is **kept** for a
390
+ `--host` run, because the target machine has its own checkouts and this box's filesystem must not
391
+ decide what exists there.
392
+
393
+ **`--add-dir` values are now `~`-expanded at the consumer** (`buildExecCommand`, for both the
394
+ Claude flag and Codex's `workspace_roots`). Nothing was doing it: a forwarded grant crosses the
395
+ SSH boundary single-quoted, so the remote login shell left `~/…` literal and the harness resolved
396
+ it as a directory actually named `~` — a silent no-op. This also fixes the pre-existing case of a
397
+ user typing `--add-dir '~/x'` on a `--host` run.
398
+
399
+ The `repos[]` walk is now one function shared by the spawn path and the fleet workspace probe
400
+ (`projects status`), which previously had its own copy. The probe keeps its own primary (`root`,
401
+ not `defaultPath`, so a monorepo subproject still probes its checkout) and keeps missing
402
+ directories so it can still report `✗ missing`. Also corrects the `agents projects` help note,
403
+ which still told users to sync definitions with the long-removed `agents push/pull` instead of
404
+ `agents repo push user`. Source: `apps/cli/src/lib/projects.ts` (`projectDirsAbs`,
405
+ `projectProbeTargets`), `apps/cli/src/lib/project-root.ts` (`resolveProjectDirs`),
406
+ `apps/cli/src/commands/projects.ts` (`projectRepoFromDir`), `apps/cli/src/commands/exec.ts`,
407
+ `apps/cli/src/commands/teams.ts`, `apps/cli/src/lib/teams/{registry,agents,api}.ts`,
408
+ `apps/cli/docs/projects.md`.
409
+
410
+ - **`agents daemon status` now reports a daemon running code that has been deleted (RUSH-2493).** A daemon keeps running from memory after its entry file is removed — so it answers every probe, `systemctl --user is-active` reads `active`, and the status command reported healthy, while the process cannot restart and is executing whatever was loaded before the delete. Observed on a dev box: a daemon ran 4h14m from a worktree that had been deleted, silently holding a second routine scheduler (the double-fire class the one-scheduler-one-executor rule exists to prevent), and nothing anywhere named it. `status` now carries a `Stale code` section listing each such pid with its missing entry, marks the `Binary:` line `(MISSING from disk)` when it is this device's own daemon, and `daemon doctor` raises it as a problem with the right remediation — `agents daemon restart` for your own, `kill <pid>` for a stray this install owns. `--json` gains `binaryMissing` and `staleBinaries`, each stale row carrying `actionable` so a machine caller can tell a ghost it may act on from one that is merely visible.
411
+
412
+ **Detection and accusation are scoped separately.** Anything actionable — a `doctor` problem, a non-zero exit, a `kill` instruction — is limited to this device's daemon plus this install's instance registry, the scope RUSH-2368 established after a leaked test fixture was reported as a stray to `kill`. Merely *showing* a stale daemon extends to any process running as the same uid, because the incident that motivated this was neither the tracked pid nor registered: it ran from an ephemeral `/tmp` cwd, which `lib/daemon.ts` documents as invisible to the registry by design, so registry-gating the display would have left the command silent on the exact case it exists for. A different uid is never named at all — its entry cannot be reliably stat'd and could not be signalled anyway.
413
+
414
+ Three further limits keep it from accusing a healthy daemon: it reports only a **provable `ENOENT`** (`statSync` with `throwIfNoEntry`, since `existsSync` also returns false on a permission error — a root-owned daemon under an unreadable parent would otherwise be named); it considers **absolute entry paths only**, so `node -e '<code>' __daemon-run` is never mistaken for deleted code; and that last guard also exempts entry paths containing spaces, which `ps` renders unquoted — a healthy daemon on such a path is never accused, at the cost of not detecting a genuinely deleted one. Source: `apps/cli/src/commands/daemon.ts`, `apps/cli/src/commands/daemon.test.ts`.
415
+
416
+ - **Fixed a Windows-only CI failure in the binary-shadow test.** `detectAgentsBinaryShadows`
417
+ was already comparing files by identity, but its test still compared two path
418
+ spellings through `fs.realpathSync`. On Windows `realpathSync` does not expand an
419
+ 8.3 short name, so a `where`-resolved path and one built from `os.tmpdir()` compare
420
+ unequal even when they name the same file — which is why every PR touching
421
+ `apps/cli` saw `windows` fail on a GitHub runner (`C:\Users\RUNNER~1\...` vs
422
+ `C:\Users\runneradmin\...`). The test now identifies the file by basename plus
423
+ contents, which is spelling-independent and asserts the stronger property.
424
+
425
+ - **The auto-detected browser `default` profile no longer churns the fleet-shared
426
+ `agents.yaml`.** `ensureDefaultBrowserProfile` regenerates that profile on every
427
+ `agents browser start` without `--profile`, writing an absolute `binary:` path
428
+ and a port chosen by probing the local machine. Because it lived in the synced
429
+ file, a macOS box wrote `/Applications/Google Chrome.app/...`, a Linux box found
430
+ that unlaunchable and rewrote `/usr/bin/chromium-browser`, and the two flipped
431
+ the tracked file back and forth forever — the single largest source of churn on
432
+ it. The auto `default` now lives in that box's own
433
+ `~/.agents/devices/<machine>/agents.yaml`, which is gitignored. **User-created
434
+ named profiles stay central and still sync**; reads merge both maps, with the
435
+ machine-local copy winning a name collision (it was written by this box, for
436
+ this box). Source: `apps/cli/src/lib/browser/profiles.ts`,
437
+ `apps/cli/src/lib/state.ts`.
438
+ - **`projectRoot` is machine-local too.** `ensureProjectRoot` infers it from
439
+ whatever directory the CLI happened to run in, so it is machine state rather
440
+ than fleet policy; it was being cached into the file every machine syncs.
441
+ Source: `apps/cli/src/lib/state.ts`.
442
+ - **Browser profile writes no longer clobber a concurrent write.**
443
+ `createProfile` / `updateProfile` / `deleteProfile` did an unlocked `readMeta()`,
444
+ mutated, then `writeMeta(meta)` — persisting a snapshot taken before the lock, so
445
+ a newer write from another process was silently lost. They now go through
446
+ `updateMeta`, which re-reads under the lock.
447
+ Source: `apps/cli/src/lib/browser/profiles.ts`.
448
+
449
+ - **A config read no longer rewrites the fleet-shared `agents.yaml`.** The legacy
450
+ device-config fold hung off `getConfigValue` / `setConfigValue` /
451
+ `unsetConfigValue`, so an ordinary `agents config get` could rewrite
452
+ `~/.agents/agents.yaml` — a file every machine in the fleet tracks and syncs.
453
+ With 13 machines each dirtying one shared path on nearly every command, boxes
454
+ stopped being able to pull at all (`yosemite-s0` sat 4 commits behind, unable to
455
+ fast-forward past its own local rewrite). The fold now runs only from a
456
+ lifecycle entry point (daemon boot, `runMigration`), never from a read or write.
457
+ Source: `apps/cli/src/lib/device-config.ts`.
458
+ - **The device-config migration is additive instead of destructive.** It used to
459
+ `fs.rmSync` the per-device `devices/<host>/agents.yaml` and `fs.rmdirSync` its
460
+ directory after folding. Deleting the source mid-rollout meant a box still on the
461
+ previous CLI lost the config it was still reading, and a box that re-created the
462
+ doc got stripped again on its next command. The fold now leaves every legacy
463
+ store in place; the redundant copy is pruned later by one explicit operator
464
+ command rather than by each machine independently.
465
+ Source: `apps/cli/src/lib/devices/config-migration.ts`.
466
+ - **`agents devices capture` no longer erases a peer's config.** `captureFleet`
467
+ rebuilt `fleet.devices` from the captured roster alone, so a device the capturing
468
+ box had not seen was dropped along with its `config:` block — observed for real,
469
+ a capture on `yosemite-s0` deleted `zion`'s entire config from the shared file. A
470
+ dropped device now carries its `config:` forward; the roster fields still reflect
471
+ live state.
472
+ Source: `apps/cli/src/lib/fleet/capture.ts`.
473
+ - **Clearing the last model-tier override no longer leaves `model: {tiers: {}}`.**
474
+ The emptied container was written back to the shared `agents.yaml`, showing up as
475
+ a spurious local change on whichever box ran the command. It now drops the key,
476
+ matching how an emptied `hosts:` is already handled.
477
+ Source: `apps/cli/src/lib/model-tier-overrides.ts`.
478
+
479
+ - **The agents daemon starts at install/upgrade and on first `agents setup` / `setup --force`, not only after `routines add`.** `scripts/postinstall.js` `healLongRunningProcesses` calls `startDaemon` on darwin and linux when `daemon.enabled` is not false (bounce when already running so upgrades load the new binary; cold start writes the LaunchAgent/systemd unit so KeepAlive/Restart=always apply). If the daemon was running under `daemon.enabled=false`, postinstall stops it and leaves it down. First-run / `--force` `agents setup` calls `startDaemon()` after the system repo is ready (hub re-entry without `--force` does not). The CLI hot path is unchanged — no per-invoke ensure. Companion: [phnx-labs/.agents-system#291](https://github.com/phnx-labs/.agents-system/pull/291) (`check-updates` daily + `agents repo sync system` + `agents sync --local -y` when HEAD moved). Source: `apps/cli/scripts/postinstall.js`, `apps/cli/src/commands/setup.ts`.
480
+
481
+ - **A dev build no longer takes over the installed `agents` command.** `scripts/install.sh` published its side-by-side build into `~/.local/bin` under the production names `agents`, `ag`, and `browser`, so which code ran was decided by PATH order rather than by what you typed — and once the dev prefix was cleaned, those links dangled and the production command failed with `no such file or directory`. The dev build is now exposed as **`agents-dev`** (and `ag-dev`); the script never creates or overwrites `agents`, `ag`, or `browser`, and on each run it removes any such shadow link an earlier revision of itself left pointing into the dev prefix — including a dangling one, which `[[ -e ]]` cannot see. `browser` leaves the dev link set entirely: `agents-dev browser …` reaches the same code. The restart of the **shared** routines daemon (secrets broker, browser IPC, scheduler) is now opt-in behind `--bounce-daemon` instead of automatic, because pinning that daemon to a working-tree build changes what the everyday `agents` talks to while `agents` itself still looks untouched. The PATH-precedence warning is gone — a distinct name makes ordering irrelevant. Root `AGENTS.md` now states the rule for agents working in this repo: never `npm i -g` from the working tree, and never claim the production bin names. This supersedes the dev-build shadow described in the RUSH-2446 entry above — that rollout probe stays as the general backstop for any other install that takes the `agents` name. If removing the shadow leaves `agents` unresolvable (npm skipped its own `~/.local/bin` link because the shadow already answered the probe), the install says so and prints the one command that restores it, instead of claiming `agents` is untouched. Source: `apps/cli/scripts/install.sh`, `apps/cli/scripts/install.test.ts`, `AGENTS.md`, `apps/cli/AGENTS.md`.
482
+
483
+ - **`agents devices config <name> [key] [value]` is the one settings surface for a device.** Bare opens an interactive settings menu on a TTY (and prints the resolved config when piped or given `--json`); `key` reads a value back, `key value` sets it with validation (booleans take on/off/true/false), `key --unset` restores the default, and `notes <text>` appends an operator note. Device-scope settings — the existing `agents.max-concurrent` / `scheduler.enabled` / `daemon.enabled` / `watchdog.enabled` / `browser.remote-control` / `browser.profile` / `notes`, plus new `ssh.user`, `ssh.auth`, `ssh.bundle`, `ssh.bundle-key`, `ssh.identity-file`, `platform`, `auto-launch.enabled`, and `auto-launch.preferred` keys — now live centrally in `~/.agents/agents.yaml` under `fleet.devices.<name>.config` (synced and backed up with the repo; a `fleet.devices: all` declaration upgrades to an explicit roster map on the first write). The `ssh.*` / `platform` / user values overlay the registry's discovery record at dial time, so `agents ssh`, the ssh_config render, host dispatch, and `devices list` all honor them. The retired subcommands (`configure`, `note`, `set`, `set-interactive`, `enable`/`disable`/`prefer`/`unprefer`) keep working as hidden tombstones that print a deprecation notice on stderr and forward into `devices config`. Existing per-device config (`devices/<name>/agents.yaml` config, `defaultBrowserProfile`) and `.history/devices/auto-launch.json` fold into the central block via a one-time migration (first config read/write, daemon boot, or `runMigration`); device docs keep their agent pins. Source: `apps/cli/src/commands/ssh.ts`, `apps/cli/src/lib/device-config.ts`, `apps/cli/src/lib/devices/resolve-profile.ts`, `apps/cli/src/lib/devices/config-migration.ts`.
484
+
485
+ - **`agents devices --help` is an intent-based menu instead of a flat 27-command list.** The subcommands are grouped by what the operator is trying to do — Discover & register, Inspect, Configure a device, Factory auto-launch, Fleet operations — via the existing `registerCommandGroups` formatter (the same pattern `browser`, `computer`, and `secrets` already use), and the "Typical workflow" block moves from the bottom of the help (`.addHelpText('after', …)`) into a workflow-first `Examples:` section right under the description via `setHelpSections`. No commands, flags, or behavior change. Source: `apps/cli/src/commands/ssh.ts`, `apps/cli/src/lib/help.test.ts`.
486
+
487
+ - **The VS Code extension is now AGI EXT; its dashboard is Fleet.** `apps/factory`
488
+ moved to `apps/ext` — the component is a thin UI wrapper over the CLI, not a
489
+ factory. The webview tab and navbar read **AGI EXT**, and the agent-status
490
+ dashboard formerly called "Factory Floor" is now **Fleet**. A dashboard tab
491
+ restored from a pre-rename build is reclaimed rather than left beside the new
492
+ one. Marketplace identity is unchanged (publisher `swarmify`, name `swarm-ext`),
493
+ so installs and the `swarm-ext://` URI keep working. Unrelated systems that share
494
+ the word keep every identifier, path, and env var they had — Factory.ai/droid
495
+ (`~/.factory`, `FACTORY_API_KEY`, the `factory` cloud provider), the beta-gated
496
+ `agents factory` Software Factory command (`FACTORY_FLOOR_URL`,
497
+ `~/.agents/factory.yml`), and Rush Cloud's own Factory Floor. Their comments and
498
+ their user-facing labels still name them — `agents teams --task-type` remains a
499
+ "Factory label" because it configures the Software Factory worker, not this
500
+ dashboard.
501
+
502
+ - **`agents feed post --notify` raises a local desktop banner on top of any configured broadcast.**
503
+ A feed post already forwards outward through `feed.broadcast` sinks (a Linear comment, an
504
+ owner-channel iMessage), but reaching the local desktop meant hand-declaring a `channel: desktop`
505
+ sink in `agents.yaml`. `--notify` is the per-post, config-free equivalent — the same
506
+ `notifyDesktop` banner `agents run --notify` fires — added **on top of** whatever delivery is
507
+ already configured, never replacing it. It routes through the real `desktop` channel provider like
508
+ every other `channel:` sink, so it appears in the post's broadcast outcomes and the `--json`
509
+ payload rather than a side path. Two properties follow: it carries no `minLevel`, so a routine
510
+ milestone post can raise a local heads-up without an `important`-gated phone sink buzzing the
511
+ phone; and the banner is local (it reaches whoever is at the machine the post was authored on, a
512
+ no-op with a stated reason where no notifier exists), so a headless post never mis-fires at the
513
+ operator's Mac — reaching a phone stays the job of an `important`-level owner/broadcast sink.
514
+ Source: `apps/cli/src/lib/feed-broadcast.ts` (`withDesktopNotify`), `apps/cli/src/commands/feed.ts`.
515
+
516
+ - **`agents subagents list` no longer reports every Codex target as `missing` while the `.toml` files are present (#2399).** Codex custom agents are flat TOML (`name` / `description` / `developer_instructions`) under `~/.codex/agents/`, but the registry reader used the markdown YAML-frontmatter parser, which returns null on TOML and dropped every entry from the rich listing. Codex still loaded the files at runtime — only the listing surface was wrong. The Codex target now uses a TOML metadata reader (same escape hatch as Kiro JSON / Goose YAML). Source: `apps/cli/src/lib/subagents-registry.ts`.
517
+
518
+ - **`agents sessions focus <id>` reaches a session on another device without `--device`.** Focus resolves an id/identity selector across the reachable fleet (the same resolver `resume` and `preview` use), but it then required the resolved session to ALSO appear in its filtered candidate pool — which is scoped by project, time window, and device — so a peer-owned, older, or other-project session was found and then rejected with `Session <id> does not match the selected focus filters` unless you passed `--device <host>` to pull it into the pool. Focus now uses the fleet-resolved session directly (`focusTargetForResolved`), hopping to the owning device via the existing recovery path; the display filters scope the browsable picker, not an exact id lookup. `resume` already resolved fleet-wide and is unchanged. Source: `apps/cli/src/commands/focus.ts`.
519
+
520
+ # Grok usage bars ignore expired / missing last-seen billing
521
+
522
+ `agents view grok` reads weekly usage from each machine's local
523
+ `.grok/logs/unified.jsonl` (`network: false`). Two bugs made the bars disagree
524
+ across devices and lie after a weekly reset:
525
+
526
+ 1. A billing line with no `creditUsagePercent` was coerced to **0%**, so a fresh
527
+ period looked empty instead of unknown.
528
+ 2. An **expired** period (e.g. last week's 100%) was still rendered, so one box
529
+ showed `rate-limited` after the window had already reset.
530
+
531
+ Expired windows are dropped via the same freshness check used for the Claude
532
+ usage cache; missing percents no longer invent a bar. Live cross-device parity
533
+ still requires a Grok session on that box to write a new billing line — there is
534
+ no network usage probe for Grok yet.
535
+
536
+ - **`agents inspect --<kind>` now uses the same interactive picker + live preview as `agents skills list`.** The drill-down listings were the last ones printing a bespoke two-line-per-entry dump with a `[source]` tag repeated on every row — 13 of 80 columns spent on one bit of information — and a description truncated mid-`Triggers on:` boilerplate, which is what 15 of the 20 skills in `.system` append to theirs. Rows are now one line (`Name · Size · Description`) showing the description's first sentence, and the preview pane below refreshes as the selection moves, carrying the full text plus per-kind metadata: a hook shows what fires it and whether it is wired (previously the drill-down showed only a size, less than the summary view printed); a skill shows its frontmatter triggers, model and tools; a plugin lists its bundled skills and commands one per line. The piped path keeps its plain table plus each plugin's bundled skills and commands, and the picker only appears on a TTY. `--json` keeps its shape (one `description` value changes, per the next entry). Also fixes the overview's resource preview cutting its own `…(+16)` tail at any terminal width, `1 files`, and the Hooks section leading with `*_test` scaffolding instead of registered hooks. Source: `apps/cli/src/commands/inspect.ts`, `apps/cli/src/commands/resource-view.ts`.
537
+ - **`agents inspect --hooks` no longer shows a line of shell as each hook's description.** All 53 hooks in `.system` rendered `!/usr/bin/env bash` — the "first prose line" fallback is a Markdown heuristic, and on a script it returned the shebang with its `#` stripped. Skipping that line only promoted the next one (`set -euo pipefail`, a Python docstring), so the agent path now reports a hook as having no description at all — matching what the repo path already did — and the column shows the hook's firing events instead, which is the information that view is for. Descriptions read from the first line of non-Markdown resources (an `mcp` `.yaml`, a `.toml`) are unaffected. Source: `apps/cli/src/commands/inspect.ts`.
538
+ - **`agents inspect --hooks` now shows what fires each hook, in the column that was blank.** The Description column for a hook was either code or nothing; it now carries the same event summary the overview prints (`PreToolUse(Bash)`, `SessionStart`), and a hook the manifest does not register shows a blank events cell, with the preview pane spelling out `not registered`. A repo target resolves those events from that repo's own `agents.yaml` rather than whatever is installed centrally. Source: `apps/cli/src/commands/inspect.ts`.
539
+
540
+ - **`agents inspect <repo> --routines` shows what a DotAgents repo schedules, and whether it actually fires.** `inspect` could drill into eight resource kinds but not routines, so the one kind carrying live operational state was the one you could not inspect — `agents inspect . --routines` errored with `unknown option`. `--routines` lists every routine the repo declares with its schedule, what it runs, the devices whose allowlist enables it, and how it last ran; `--routine <name>` drills into one; and the plain overview gains a `Routines` section beside Hooks/Plugins/MCP. The listing folds in live state deliberately, because the YAML alone is misleading: a routine's own `devices:` pin does **not** enable it — membership in a device's `routines:` allowlist does (`applyDeviceActivation` → `routineEnabledOnThisDevice`) — so a definition-only view reports `git-review → zion` for a routine that has not fired in days. When routines sit on no device's allowlist the section says so outright and names the `agents routines devices <name> --set <device>` fix; `--json` reports `devices` (the YAML pin) beside `enabledDevices` (the live fleet answer), reusing `agents routines list --json` field names where they overlap so one script consumes both. Rows sort broken-first (inert config, then fires-nowhere, then disabled, then failing) because the overview section shows only the first six — with 24 routines the sort *is* the section. Three supporting fixes: a config that fails closed (unparseable YAML, a legacy `device:` key, a non-list `devices:`) is now listed **with the reason** instead of silently vanishing (`readJobFileResult`, extracted from the private `readJobFile`); a corrupt peer device file is collected and reported rather than thrown, so one bad file cannot blank the whole listing (`routineDeviceIndex`, a non-throwing single-pass sibling of `devicesWithRoutineEnabled`); and `ResourceItem.extra` now reaches `--json` for every kind, not just plugins, so the detail view and its own preview pane cannot disagree. The collector reads `*.yml` files only — `routines/` also holds `<name>/home/` sandbox overlay HOMEs, and a flat readdir counted 49 routines where the repo declares 24. A bare `routines/` directory is deliberately **not** a DotAgents marker: shipping example routines is a normal pattern (this repo's own `apps/cli/routines/`), and counting it made `agents inspect apps/cli` resolve a source tree as a repo. Source: `apps/cli/src/commands/inspect.ts`, `apps/cli/src/lib/routines.ts`, `apps/cli/src/lib/routine-activation.ts`.
541
+
542
+ - **Kimi subagents are written in the format kimi-code actually reads, and gated at 0.29.0.** The
543
+ integration emitted a `<name>.yaml` + `<name>.system.md` pair plus a managed `_agents-cli.yaml`
544
+ parent index, targeting the `version: 1` / `agent:` agentspec of the older, separate `kimi-cli`
545
+ product. `@moonshot-ai/kimi-code` has no loader for that schema — `system_prompt_path` appears
546
+ nowhere in its bundle — so every synced Kimi subagent was written to disk and never loaded by any
547
+ session. Kimi now gets one Claude-shaped `<name>.md` (frontmatter `name`/`description` + body),
548
+ which kimi-code discovers from its brand home's `agents/` dir. Discovery landed in kimi-code
549
+ 0.29.0; 0.28.x and earlier compile their four agent profiles into the bundle with no filesystem
550
+ loader, so the `subagents` capability is now `>= 0.29.0` and older installs skip with a stated
551
+ reason instead of writing files nothing reads. Verified against a real kimi 0.29.0:
552
+ `--agent no-such-agent` reports `Available profiles: plan, agent, coder, explore, code-reviewer`.
553
+ Source: `apps/cli/src/lib/subagents-registry.ts`, `apps/cli/src/lib/agents.ts`.
554
+
555
+ Homes synced before this fix carry stale `<name>.yaml`, `<name>.system.md`, and
556
+ `_agents-cli.yaml` files in `~/.kimi-code/agents/`. A one-shot migration
557
+ (`migrateKimiSubagentsToMarkdown`, migration schema `v19`) removes them from every kimi
558
+ version home on the first run after upgrading —
559
+ not `agents prune cleanup`, which could never reach the two `.yaml` files because they match
560
+ no subagent enumerator. Without it the leftover `<name>.system.md` would be listed as a
561
+ phantom subagent named `<name>.system` and warned about by kimi-code once per session, since
562
+ it ends in `.md` and carries no frontmatter. A legacy pair is matched by its signature (a
563
+ `<name>.yaml` with a sibling `<name>.system.md`), so a subagent you named `<x>.system`
564
+ yourself is left alone.
565
+
566
+ - **Per-machine config no longer lives in the fleet-shared `agents.yaml`.** Every
567
+ device-scope config key now declares `visibility`, which asks who READS it.
568
+ `shared` keys stay in the synced `fleet.devices.<name>.config` because a peer
569
+ resolves them — the ssh fields and `platform` are needed to dial a box before it
570
+ is reachable, `agents.max-concurrent` drives teams placement, `auto-launch.*`
571
+ and `notes` feed fleet views. `machine` keys — `browser.profile`,
572
+ `browser.remote-control`, `scheduler.enabled`, `daemon.enabled` — move to that
573
+ box's own `~/.agents/devices/<machine>/agents.yaml`, which is gitignored. That
574
+ is what stops 13 machines writing one tracked path.
575
+ Source: `apps/cli/src/lib/device-config.ts`, `apps/cli/src/lib/state.ts`.
576
+ - **`browser.remote-control` was a consent leak.** It gates whether OTHER machines
577
+ may drive this box's browser, and its own help text promised "device-local,
578
+ never synced" — but it was stored in the file the fleet syncs, so one box's
579
+ opt-in propagated to the rest on pull. It is now machine-local, and setting or
580
+ reading a machine-local key for a peer is refused outright with the
581
+ `agents ssh <device>` form to use instead.
582
+ Source: `apps/cli/src/lib/device-config.ts`.
583
+ - **A new config key cannot silently pick the wrong home.** `ConfigKeySpec` is a
584
+ discriminated union, so omitting `visibility` on a device-scope key is a COMPILE
585
+ error — the same discipline `META_KEY_SCOPE` already applies to `Meta`. The
586
+ migration no longer folds machine-local keys at all: they already sit where the
587
+ new read path looks, and copying a peer's would spread that consent flag
588
+ fleet-wide. Values an older CLI wrote centrally are still honored until
589
+ overwritten, so a mixed-version fleet keeps working.
590
+ Source: `apps/cli/src/lib/config-machine-keys.ts`,
591
+ `apps/cli/src/lib/devices/config-migration.ts`.
592
+
593
+ - **Monitor `condition: { mode: every }` no longer fires on an empty observation (RUSH-2488).** `every`
594
+ previously returned `fire: true` for every tick regardless of the observation, so a poll whose
595
+ command produced no rows still dispatched the action with an empty `{event}`. It now stays silent on
596
+ an empty or whitespace-only observation and fires on every tick that carries real output. This gives
597
+ a poll-driven monitor the "re-fire while the watched set is non-empty" semantics that a
598
+ silently-failed action dispatch needs to be retried — an action failure leaves the same non-empty
599
+ observation next tick, so `every` re-fires (bounded by `rateLimit`), where `match`/`on-change` would
600
+ dedupe and never retry. No shipped monitor used `every`, so there is no behavior change to an existing
601
+ monitor. Source: `apps/cli/src/lib/monitors/engine.ts`, `apps/cli/src/lib/monitors/engine.test.ts`.
602
+
603
+ - **`agents monitors add` refuses a monitor that duplicates one you already have.** A monitor's NAME was not its identity: `writeMonitor` overwrites by name and nothing compared arguments, so two watchers polling the same source on the same interval and firing the same action were one trigger fired twice — under different names, with no warning. One real box accumulated `open-pr-watch`, `pr-ci-fail`, three stale `pr2222-*` watchers and an agent-added lander all polling the same PR queue. `add` now refuses a same-name collision (which would silently overwrite) and a same-behavior collision — **checked across the fleet, not just this box**, because the case that actually bites is two agents on two different machines creating a watcher for the same work item with the same arguments, and neither can see the other's monitors dir. The check reuses the cross-machine fan-out `sessions --active` already runs, names the device and monitor it clashed with, and when a peer is unreachable says so explicitly rather than treating "could not ask" as "no duplicate"; `--force` proceeds anyway. Identity is a fingerprint over source + condition + action, deliberately excluding name, description, `enabled`, and placement (`device`/`devices`/`runOn`) — placement is who executes, not what runs, and hashing it would let the same watcher be re-added by varying only the owner. Different arguments still coexist — a watcher for PR #2517 and one for #2600 are two monitors, not a clash; that is the common case. Also documents the split the subsystem already had but never stated: a **durable** monitor is config, an agent's **per-work-item** watcher is running state, and runtime (last-seen value, fires, rate-limit counters) lives under `~/.agents/.history/monitors/`. Source: `apps/cli/src/lib/monitors/fingerprint.ts`, `apps/cli/src/lib/monitors/remote.ts`, `apps/cli/src/commands/monitors.ts`, `apps/cli/docs/10-monitors.md`.
604
+
605
+ - **`agents sessions focus`/`attach`/`resume`/`preview`, the bare `agents sessions <id>`,
606
+ `run --resume`, and `sessions --resolve` no longer hard-fail when a fleet device is
607
+ offline.** Resolving a session id used to abort the moment any registered device was
608
+ unreachable — even when the session lived on a box that WAS reachable — printing
609
+ `Could not resolve session while these devices were unavailable`. Now an unreachable
610
+ peer is a one-line warning: the session resolves against the reachable fleet and
611
+ attaches, and the command fails only when the id is found on no reachable device
612
+ (worded so the offline, unchecked peers are named, not blamed). The exit code for
613
+ that offline-peer resolution failure changes from `2` to `1` (an ordinary not-found
614
+ failure, no longer a distinct "could not decide" code) across every resolver
615
+ consumer — `focus.ts`, `attach.ts`, `resume.ts`, `exec.ts`, and all three
616
+ `resolveSessionMetadataValue` call sites in `sessions.ts`.
617
+ (RUSH-2492)
618
+
619
+ - **Dead tmux sessions are now reaped automatically every 5 minutes.** Sessions
620
+ stay open after their process exits because `remain-on-exit on` is set so the
621
+ harness can inspect the exit status. Previously they accumulated indefinitely —
622
+ 127 tmux sessions with 48 dead (~38%) observed on a production fleet machine.
623
+ The daemon now cleans them up on a 5-minute timer (same cadence as the keychain
624
+ reaper) and immediately on startup to clear the backlog. Safety invariant: only
625
+ sessions where **all** panes are dead (`pane_dead=1`) are killed; any session
626
+ with a live pane is never touched. On-demand cleanup is also available:
627
+ `agents sessions reap [--json] [--socket <path>]`. Source: `apps/cli/src/lib/tmux/session.ts`, `apps/cli/src/lib/daemon.ts`, `apps/cli/src/commands/sessions-reap.ts`.
628
+
629
+ - **`agents sessions --active` now attributes cursor/grok/kimi/droid sessions.**
630
+ The ps-scan identity path fell back to `loadHookSessionIndex()`, which scans
631
+ `terminals/sessions/` — a directory populated only by the `@agents/session-tracker`
632
+ package, which is not deployed on most fleet machines. The SessionStart hook that
633
+ IS deployed writes `state/sessions/<pid>.json` instead. The ps-scan path now also
634
+ tries `readStateSessionRecord(pid)` after the index lookup finds nothing, so
635
+ non-Claude harnesses that carry no `--session-id` argv can be attributed from the
636
+ deployed hook's state file. Source: `apps/cli/src/lib/session/active.ts`.
637
+
638
+ - **`agents routines` is now an interactive browser.** On a terminal, the bare
639
+ command opens a filterable, grouped picker (reusing the same picker primitive
640
+ behind `agents sessions`) instead of printing nothing but help. The project /
641
+ device group headers show as inline dividers, and selecting a routine drills into
642
+ four blocks — Definition, Next fire, Recent runs, Stats. `agents routines --json`
643
+ and any non-interactive shell keep the exact `agents routines list` output, so
644
+ pipes and the menu bar are unaffected. (RUSH-2503)
645
+
646
+ - **`agents secrets export --host` raises Touch ID at a terminal instead of demanding a prior `agents secrets unlock`.** Pushing a locked keychain-backed bundle to another machine failed with *"Secrets bundle '<name>' is not unlocked in the secrets agent"* even for a human typing the command at a TTY — `resolveBundleForPush` passed `agentOnly: true` unconditionally, while its sibling reads (`view --reveal`, `exec`) already decided this per-invocation with `isHeadlessSecretsContext() || !isInteractiveTerminal()`. The push is strictly less exposed than the `view --reveal` that already prompts: it prints a key count, never a value, and nothing captures its stdout, so the prior-unlock requirement was an inconsistency rather than a boundary. An agent launch, a piped/CI run, and `pushBundleToHost` (fleet apply) are unchanged — they stay broker-only and still fail fast with the unlock hint, which is what keeps the RUSH-2440 keychain-enumeration prompt storm off the agent path. `export --plaintext` / `--to-file` / `--to-1password` are also unchanged: they emit values into a pipeline or onto the screen, so they remain unconditional automation primitives. Spec updated in lockstep (SEC-13b and the §4.2 materialization table). Source: `apps/cli/src/lib/secrets/push.ts`, `apps/cli/src/commands/secrets.ts`, `apps/cli/docs/secrets.md`, `apps/cli/docs/specifications.md`.
647
+
648
+ - **A short session id resolves even when a fleet peer is offline.** `agents sessions preview <shortid>`, `agents resume <shortid>`, `agents sessions attach`, and `sessions --resolve --json` previously refused to resolve whenever any registered device failed to answer the fan-out — printing `Partial session resolution: <peers> did not answer` and rendering nothing, even though exactly one session on the reachable fleet matched — so on a fleet carrying any permanently offline device, every short-id lookup was voided. Once the sweep is over, a selector that is a complete id or at least 8 hex characters wide (the printed `shortId` width) now resolves from a single reachable match. **Deliberately unchanged:** a keyword-shaped query still fails closed even when it is all hex characters (`facade`, `decade` — searches, not identifiers); a **label** still fails closed at any length, because labels are free-form and collide by design; a prefix collision between peers that *did* answer still never resolves — it fails closed while a peer is missing, and surfaces as an ambiguity listing both machines once every peer has answered; and RUSH-2203's mid-sweep early-exit stays full-UUID-only. This amends the normative **SES-9a** requirement, which previously mandated fail-closed for every short prefix; the accepted collision risk is stated there, including that a time-ordered UUIDv7/ULID prefix collides far more readily than a random UUIDv4 one and the residual exposure is a collision hiding on the peer that did not answer. Source: `apps/cli/src/commands/sessions.ts` (`isUniqueEnoughSelector`, `metadataResolveOutcome`, `renderSessionPreview`), `apps/cli/docs/specifications.md` §SES-9a / §SES-IF-2a, `apps/cli/docs/sessions.md`.
649
+
650
+ - **`agents teams add` warns and blocks when the base checkout is behind
651
+ `origin/main`, requiring `--confirm` to proceed.** Pointing a team at a stale
652
+ repo (local cwd, or the repo provisioned on a `--device` host) meant teammates
653
+ reasoned and built against code that had already moved on — the real incident
654
+ was a 71-commit-stale checkout on another box that nobody had fetched. `teams
655
+ add` now fetches origin, counts how far behind `origin/<default>` the base is,
656
+ and refuses with a sync command (`git … merge --ff-only origin/main`) unless you
657
+ pass `--confirm`; with `--confirm` it prints a one-line advisory and continues.
658
+ An offline/unreachable/non-git base can't be assessed and never blocks. Cloud
659
+ teammates clone fresh in the provider and are skipped. Source:
660
+ `apps/cli/src/commands/teams.ts`, `apps/cli/src/lib/teams/worktree.ts`,
661
+ `apps/cli/src/lib/teams/remoteWorktree.ts`.
662
+
663
+ - **Webhook handlers (`~/.agents/webhooks/*.yml`) can now anchor a dispatched agent in a project.** A handler's `run.agent`/`run.workflow` action previously always ran at the target box's `$HOME` with no repo checkout and a hard-coded `auto` mode, so an inbound GitHub/Linear event could not fire an agent that actually edits a repo without routing through a routine. Handlers gain three top-level fields mirroring a routine's job config: `project` (named `agents projects` execution anchor — the run lands in that project's base directory), `cwd` (portable execution directory, resolved under `project` or `$HOME`), and `mode` (`plan`/`edit`/`auto`/`skip`/`full`, default `auto`). They thread into the dispatched `JobConfig` for the `run.agent`/`run.workflow` path and override the `routine:` delegate; `run.command` is unaffected (use a `cd` in the command). Source: `apps/cli/src/lib/triggers/handlers.ts`.
664
+
665
+ ## 1.22.35
666
+
667
+ - **`agents doctor --fix` and `agents upgrade` purge stale multi-install agents-cli copies instead of only warning (RUSH-2415).** Pre-1.22.30 installs (and "unsafe legacy helper installer" / npx-cache trees) re-introduce the Touch ID storm + usage-API revocation class fixed in v1.22.30. Detection already existed (`findAgentsCliInstalls` / multi-install warning) but left remediation to the user. Bare `agents doctor --fix` and a successful `agents upgrade` now delete npx-cache installs, non-atomic helper installs, and pre-1.22.30 package roots when a fixed peer already exists on the box — never the running copy, never a lone pre-fixed install that would strand the machine. The multi-install warning points at `agents doctor --fix`. Source: `apps/cli/src/lib/self-update.ts`, `apps/cli/src/commands/doctor.ts`, `apps/cli/src/index.ts`.
668
+
669
+ - **`agents share delete` (alias `agents unshare`) takes down a published page (RUSH-2428).** `agents share` publishes to a public URL with no way to take one down — the Cloudflare Worker already implements an authed `DELETE`, the CLI just never exposed it. The new command accepts a full share URL, `<user>/<slug>`, or a bare `<slug>` (resolved against your own namespace exactly as publish does), takes several targets at once, and by default also deletes the sibling `<slug>.png` OG cover (`--keep-cover` opts out) — without it, republishing over a slug replaced the page but left the old cover screenshot publicly readable, which is what made a real takedown slow. `{"ok":true}` from the Worker is not treated as proof: a follow-up check must resolve 404 before the command reports success, and it errors loudly (non-zero) instead of if it can't verify the object is actually gone. An already-missing target is an error by default; `--if-exists` treats it as a no-op success. Source: `apps/cli/src/lib/share/delete.ts`, `apps/cli/src/commands/share.ts`.
670
+
671
+ - **Codex `edit` runs can write under the repo's `.agents/` again.** Codex's `workspace-write` sandbox hardcodes any `.agents/` (and `.codex/`) directory as read-only, but agents-cli keeps every git worktree at `<repo>/.agents/worktrees/<slug>` — so a Codex session whose cwd was the repo root hit `EROFS: read-only file system` on any write into a worktree (a build's `dist/`, generated files) and then had to prompt for per-command approval to escalate. Every interactive, headless, and direct-launch Codex path now makes the run's `<repo-root>/.agents` writable: `agents run codex` (and the Windows shim delegate) add it to the `agents-edit` profile's `workspace_roots`, and the adopted POSIX `codex` shim resolves the repo's `.agents` from `$PWD` at run time (worktree-aware) and passes it via Codex's own `--add-dir`. (Routines use a separate overlay-HOME sandbox with their own `allow.dirs` and are unchanged.) Naming the `.agents` directory itself is the only override Codex honors — a nested sub-path makes bwrap refuse the mount. Only an existing `.agents` is added; out-of-workspace and `~/.config` writes stay gated, network unchanged. Source: `apps/cli/src/lib/codex-policy.ts`, `apps/cli/src/lib/project-key.ts`, `apps/cli/src/lib/exec.ts`, `apps/cli/src/lib/shims.ts`.
672
+
3
673
  ## 1.22.34
4
674
 
5
675
  - **A failed `agents teams add` no longer strands an `agents/<name>` branch that breaks every retry (RUSH-2356).** The worktree was created before the teammate record was persisted and nothing removed it when the add failed, so the next `teams add` with the same `--worktree` name died on `fatal: a branch named 'agents/<name>' already exists` — observed 2026-08-07, forcing three renames before a teammate could be created. Two guarantees now hold. Name uniqueness and the `--after` dependency graph are validated **before** `createWorktree` runs (`AgentManager.validateAddPreconditions`), so a duplicate name, unknown dependency, or cycle never creates a branch at all. A failure **after** the worktree exists — a missing harness CLI, a launch error, a cloud dispatch failure — removes the worktree and its branch before the command exits non-zero, and prints the exact `git worktree remove` / `git branch -D` pair if that teardown itself fails. Teardown is scoped twice over, because deleting a live teammate's worktree would destroy real work: only a worktree that same add created is a candidate, and only when no live teammate claims it (`AgentManager.isWorktreeClaimed`, a raw meta.json scan across every team, counting only non-terminal records). That second check matters because the add can fail *after* the record is durably saved — `spawn()` saves a staged teammate and only then runs the retention pass, which refreshes every sibling and can throw on a distributed one — so a teammate already recorded and waiting on an `--after` dependency keeps its worktree. If we can't prove a worktree is an orphan, it is left in place with the manual removal printed — the claim check **fails closed**, so an unreadable or half-written record answers "claimed" rather than "free": only a genuinely absent record (ENOENT) proves nothing claims the worktree, because the alternative error deletes a running teammate's uncommitted work. The pr-watch fixer path (`reactWithTeammate`) and a partially-failed `createWorktree` (branch ref created, checkout not) run the same guarded teardown — the fixer path matters because it stages its teammate with `--after` when it follows a source teammate, so it can reach the failure branch with a live, durably-recorded, merely-pending teammate already owning the worktree.
@@ -199,7 +869,7 @@
199
869
 
200
870
  - **`agents run <agent>` now says the harness is not installed instead of exiting 127 (RUSH-2339).** On a machine without that harness the launch used to exec the bare CLI name and die with `sh: 1: exec: cursor-agent: not found`, behind a `⚠ <agent> looks logged out` banner that was also wrong. `agents run` now probes the executable it is about to spawn and exits `1` with `agents: <agent> is not installed on this machine.` plus the `agents add <agent>` fix, before any spawn. The probe is existence-based, so a harness you installed yourself (Homebrew, a vendor `curl | sh`, a distro package) with no agents-cli version home still launches, and a machine with managed versions but no pinned default still gets the shim's own `agents use <agent> <version>` guidance rather than a wrong "not installed".
201
871
 
202
- - **The Claude usage/auth-health probe no longer reads your interactive Claude Code login — the fix for repeated Anthropic logouts.** The daemon's usage (~60s) and auth-health (~3min) warms authenticated with a file-based setup-token when one was provisioned, but otherwise fell through to reading Claude Code's interactive OAuth token from the keychain / `.credentials.json` and firing it at `api.anthropic.com/api/oauth/usage`. Anthropic sees an interactive login used programmatically from a background loop and revokes it (the fleet-wide-logout class, RUSH-1822). `loadClaudeOauth`'s read-only `accessTokenCache` path now returns nothing when no setup-token is provisioned — it never reads the interactive login, in the keychain or in a file — matching the "interactive/rotating login is untouchable" invariant in docs/design/credential-management.md. The now-obsolete no-ACL access-token cache is removed with it. An account without a minted setup-token shows "usage pending" in `agents view` (seed one via the mint-auth path) but still runs normally; account rotation is unaffected. Source: `apps/cli/src/lib/usage.ts`.
872
+ - **The Claude usage/auth-health probe no longer reads your interactive Claude Code login — the fix for repeated Anthropic logouts.** The daemon's usage (~60s) and auth-health (~3min) warms authenticated with a file-based setup-token when one was provisioned, but otherwise fell through to reading Claude Code's interactive OAuth token from the keychain / `.credentials.json` and firing it at `api.anthropic.com/api/oauth/usage`. Anthropic sees an interactive login used programmatically from a background loop and revokes it (the fleet-wide-logout class, RUSH-1822). `loadClaudeOauth`'s read-only `accessTokenCache` path now returns nothing when no setup-token is provisioned — it never reads the interactive login, in the keychain or in a file — matching the "interactive/rotating login is untouchable" invariant in docs/credential-management.md. The now-obsolete no-ACL access-token cache is removed with it. An account without a minted setup-token shows "usage pending" in `agents view` (seed one via the mint-auth path) but still runs normally; account rotation is unaffected. Source: `apps/cli/src/lib/usage.ts`.
203
873
 
204
874
  - **Sort `agents view <agent>` accounts by email after the default.** Managed installs previously followed semantic-version order after the default, forcing users with several Claude, Codex, or other harness accounts to scan a shuffled email column. Rows without an email remain deterministic in version-descending order, and `--json` keeps its existing default-then-version order for automation compatibility. Source: `apps/cli/src/commands/view.ts`.
205
875
 
@@ -4276,7 +4946,7 @@ Add Cursor Cloud Agents as a native cloud provider so `agents run cursor --cloud
4276
4946
  refresh token) is unchanged, and an account with no provisioned setup-token still
4277
4947
  falls through to the keychain for now. Keyed strictly per-account (never a bare
4278
4948
  shared key) so one account's token can't be misapplied to another. Source:
4279
- `apps/cli/src/lib/usage.ts`; design: `docs/design/credential-management.md`.
4949
+ `apps/cli/src/lib/usage.ts`; design: `docs/credential-management.md`.
4280
4950
 
4281
4951
  ## 1.20.78
4282
4952