@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
@@ -0,0 +1,42 @@
1
+ /**
2
+ * One-time migration: fold per-device config from the legacy stores into the
3
+ * central `fleet.devices.<name>.config` block of `~/.agents/agents.yaml`.
4
+ *
5
+ * Only SHARED-visibility keys move — the ones a peer reads. Machine-visibility
6
+ * keys (see lib/config-machine-keys.ts) stay in the per-device doc, which is
7
+ * exactly where the machine-local read path now looks for them.
8
+ *
9
+ * Legacy sources (both pre-unification):
10
+ * - `~/.agents/devices/<name>/agents.yaml` — the `config:` map and the
11
+ * top-level `defaultBrowserProfile:` field. Agent pins (`agents:`,
12
+ * `isolatedAgents:`), `routines:`, and machine-visibility config STAY in the
13
+ * per-device doc — only shared operator config moves.
14
+ * - `~/.agents/.history/devices/auto-launch.json` — AGI EXT auto-launch
15
+ * enabled/preferred flags, becoming `autoLaunchEnabled` /
16
+ * `autoLaunchPreferred` config keys.
17
+ *
18
+ * The fold is ADDITIVE: the central block is written and the legacy stores are
19
+ * left exactly as they were, so a box still on the previous CLI keeps reading
20
+ * what it always read. The central-wins merge makes a re-run a byte no-op.
21
+ *
22
+ * Invoked only from a lifecycle entry point — `runMigration()` (fresh installs)
23
+ * and daemon boot. It must NEVER hang off a config read or write: agents.yaml is
24
+ * tracked and shared by the whole fleet, so a migration on the read path lets an
25
+ * ordinary `agents config get` dirty it on every machine.
26
+ */
27
+ import type { FleetDeviceOverride, FleetManifest } from '../fleet/types.js';
28
+ /**
29
+ * The `fleet.devices` map a config write should edit. A missing block starts
30
+ * empty; the literal `'all'` upgrades to an explicit map of the
31
+ * currently-registered roster so a config write never silently narrows what
32
+ * `agents apply` targets. (The dynamic "new devices join automatically"
33
+ * semantics of `'all'` do not survive the upgrade — per-device config is
34
+ * inherently a map.)
35
+ */
36
+ export declare function fleetDevicesMapForWrite(fleet: FleetManifest | undefined): Record<string, FleetDeviceOverride>;
37
+ /**
38
+ * Fold the legacy per-device config stores into the central
39
+ * `fleet.devices.<name>.config` block. Safe to call on every boot / config
40
+ * access: cheap no-op once the legacy stores are stripped.
41
+ */
42
+ export declare function migrateDeviceConfigToCentral(): void;
@@ -0,0 +1,203 @@
1
+ /**
2
+ * One-time migration: fold per-device config from the legacy stores into the
3
+ * central `fleet.devices.<name>.config` block of `~/.agents/agents.yaml`.
4
+ *
5
+ * Only SHARED-visibility keys move — the ones a peer reads. Machine-visibility
6
+ * keys (see lib/config-machine-keys.ts) stay in the per-device doc, which is
7
+ * exactly where the machine-local read path now looks for them.
8
+ *
9
+ * Legacy sources (both pre-unification):
10
+ * - `~/.agents/devices/<name>/agents.yaml` — the `config:` map and the
11
+ * top-level `defaultBrowserProfile:` field. Agent pins (`agents:`,
12
+ * `isolatedAgents:`), `routines:`, and machine-visibility config STAY in the
13
+ * per-device doc — only shared operator config moves.
14
+ * - `~/.agents/.history/devices/auto-launch.json` — AGI EXT auto-launch
15
+ * enabled/preferred flags, becoming `autoLaunchEnabled` /
16
+ * `autoLaunchPreferred` config keys.
17
+ *
18
+ * The fold is ADDITIVE: the central block is written and the legacy stores are
19
+ * left exactly as they were, so a box still on the previous CLI keeps reading
20
+ * what it always read. The central-wins merge makes a re-run a byte no-op.
21
+ *
22
+ * Invoked only from a lifecycle entry point — `runMigration()` (fresh installs)
23
+ * and daemon boot. It must NEVER hang off a config read or write: agents.yaml is
24
+ * tracked and shared by the whole fleet, so a migration on the read path lets an
25
+ * ordinary `agents config get` dirty it on every machine.
26
+ */
27
+ import * as fs from 'fs';
28
+ import * as path from 'path';
29
+ import * as yaml from 'yaml';
30
+ import { getDevicesAutoLaunchPath, getUserAgentsDir, updateMeta, } from '../state.js';
31
+ import { loadDevicesSync } from './registry.js';
32
+ import { MACHINE_LOCAL_YAML_KEYS } from '../config-machine-keys.js';
33
+ /**
34
+ * The `fleet.devices` map a config write should edit. A missing block starts
35
+ * empty; the literal `'all'` upgrades to an explicit map of the
36
+ * currently-registered roster so a config write never silently narrows what
37
+ * `agents apply` targets. (The dynamic "new devices join automatically"
38
+ * semantics of `'all'` do not survive the upgrade — per-device config is
39
+ * inherently a map.)
40
+ */
41
+ export function fleetDevicesMapForWrite(fleet) {
42
+ if (fleet && fleet.devices !== 'all')
43
+ return { ...fleet.devices };
44
+ if (fleet?.devices !== 'all')
45
+ return {};
46
+ // devices === 'all': expand to the registered roster.
47
+ const roster = loadDevicesSync();
48
+ const devices = {};
49
+ for (const name of Object.keys(roster))
50
+ devices[name] = {};
51
+ return devices;
52
+ }
53
+ function mergeFold(folds, device, values) {
54
+ folds[device] = { ...folds[device], ...values };
55
+ }
56
+ /**
57
+ * Read every legacy device doc, returning the config to fold per device.
58
+ * A doc that fails to parse is LOUDLY skipped (and left untouched) so the next
59
+ * run retries — never silently emptied, same corruption contract the device
60
+ * registry keeps.
61
+ */
62
+ function collectDeviceDocFolds(devicesRoot) {
63
+ const folds = {};
64
+ const docs = [];
65
+ let entries;
66
+ try {
67
+ entries = fs.readdirSync(devicesRoot, { withFileTypes: true });
68
+ }
69
+ catch {
70
+ return { folds, docs }; // no devices/ tree — nothing to fold
71
+ }
72
+ for (const entry of entries) {
73
+ if (!entry.isDirectory())
74
+ continue;
75
+ const docPath = path.join(devicesRoot, entry.name, 'agents.yaml');
76
+ if (!fs.existsSync(docPath))
77
+ continue;
78
+ let parsed;
79
+ try {
80
+ parsed = yaml.parse(fs.readFileSync(docPath, 'utf-8'));
81
+ }
82
+ catch (err) {
83
+ console.error(`device config migration: could not parse ${docPath} (${err.message}); leaving it for a later retry`);
84
+ continue;
85
+ }
86
+ if (parsed === null || parsed === undefined)
87
+ continue; // empty doc
88
+ if (typeof parsed !== 'object' || Array.isArray(parsed)) {
89
+ console.error(`device config migration: ${docPath} is not a YAML map; leaving it for manual repair`);
90
+ continue;
91
+ }
92
+ const doc = parsed;
93
+ const config = {};
94
+ if (doc.config !== undefined) {
95
+ if (typeof doc.config !== 'object' || doc.config === null || Array.isArray(doc.config)) {
96
+ console.error(`device config migration: ${docPath} has a non-map config: block; leaving it for manual repair`);
97
+ continue;
98
+ }
99
+ Object.assign(config, doc.config);
100
+ }
101
+ if (typeof doc.defaultBrowserProfile === 'string') {
102
+ config.defaultBrowserProfile = doc.defaultBrowserProfile;
103
+ }
104
+ // Machine-visibility keys are NOT folded. Two reasons, both load-bearing:
105
+ //
106
+ // 1. They already live exactly where they belong. `config:` in this doc is
107
+ // the machine-local store the new read path uses (state.ts overlays it
108
+ // as `deviceConfig`), so folding would only copy them into the shared
109
+ // file this whole change exists to keep clean.
110
+ // 2. Copying a PEER's would be a leak. `browserRemoteControl` is a consent
111
+ // flag gating whether other machines may drive that box's browser;
112
+ // hoisting one box's opt-in into the synced file spreads it to the fleet.
113
+ // Each machine keeps its own, in its own doc.
114
+ for (const key of Object.keys(config)) {
115
+ if (MACHINE_LOCAL_YAML_KEYS.has(key))
116
+ delete config[key];
117
+ }
118
+ if (Object.keys(config).length === 0)
119
+ continue;
120
+ mergeFold(folds, entry.name, config);
121
+ docs.push({ name: entry.name, path: docPath, doc });
122
+ }
123
+ return { folds, docs };
124
+ }
125
+ /** Read the legacy auto-launch.json, returning the flags to fold per device. */
126
+ function collectAutoLaunchFolds(autoLaunchPath) {
127
+ const folds = {};
128
+ let raw;
129
+ try {
130
+ raw = fs.readFileSync(autoLaunchPath, 'utf-8');
131
+ }
132
+ catch {
133
+ return folds; // absent — nothing to fold
134
+ }
135
+ let parsed;
136
+ try {
137
+ parsed = JSON.parse(raw);
138
+ }
139
+ catch (err) {
140
+ console.error(`device config migration: could not parse ${autoLaunchPath} (${err.message}); leaving it for a later retry`);
141
+ return folds;
142
+ }
143
+ const devices = parsed?.devices;
144
+ if (devices && typeof devices === 'object' && !Array.isArray(devices)) {
145
+ for (const [name, pref] of Object.entries(devices)) {
146
+ const config = {};
147
+ if (pref?.enabled === false)
148
+ config.autoLaunchEnabled = false;
149
+ if (pref?.preferred === true)
150
+ config.autoLaunchPreferred = true;
151
+ if (Object.keys(config).length > 0)
152
+ mergeFold(folds, name, config);
153
+ }
154
+ }
155
+ return folds;
156
+ }
157
+ /**
158
+ * Fold the legacy per-device config stores into the central
159
+ * `fleet.devices.<name>.config` block. Safe to call on every boot / config
160
+ * access: cheap no-op once the legacy stores are stripped.
161
+ */
162
+ export function migrateDeviceConfigToCentral() {
163
+ const devicesRoot = path.join(getUserAgentsDir(), 'devices');
164
+ const autoLaunchPath = getDevicesAutoLaunchPath();
165
+ const { folds: docFolds, docs } = collectDeviceDocFolds(devicesRoot);
166
+ const autoFolds = collectAutoLaunchFolds(autoLaunchPath);
167
+ const folds = {};
168
+ for (const [name, config] of Object.entries(docFolds))
169
+ mergeFold(folds, name, config);
170
+ for (const [name, config] of Object.entries(autoFolds))
171
+ mergeFold(folds, name, config);
172
+ if (Object.keys(folds).length === 0)
173
+ return;
174
+ // 1. Central write FIRST. A `fleet.devices: all` declaration upgrades to an
175
+ // explicit map of the currently-registered roster so `agents apply` keeps
176
+ // targeting the same devices. A key already present centrally WINS over
177
+ // the legacy value — a re-fold after a mid-migration crash is then a
178
+ // no-op, and a value written by a newer CLI is never clobbered.
179
+ updateMeta((m) => {
180
+ const devices = fleetDevicesMapForWrite(m.fleet);
181
+ for (const [name, config] of Object.entries(folds)) {
182
+ const prev = devices[name] ?? {};
183
+ devices[name] = { ...prev, config: { ...config, ...prev.config } };
184
+ }
185
+ const fleet = { ...m.fleet, devices };
186
+ return { ...m, fleet };
187
+ });
188
+ // 2. The legacy stores are deliberately LEFT IN PLACE.
189
+ //
190
+ // This migration used to delete them — `fs.rmSync` on the device doc and
191
+ // `fs.rmdirSync` on its directory. Deleting is what made the fold unsafe
192
+ // to run anywhere: it had to win a race against every other machine's copy
193
+ // of the same shared file, and a box on an older CLI that re-created the
194
+ // doc would be stripped again on the next command.
195
+ //
196
+ // An additive fold has neither problem. The central block is written; the
197
+ // source is untouched; a box still running the previous CLI keeps reading
198
+ // what it always read. The now-redundant legacy copy is pruned in a later
199
+ // release by one explicit operator command, not by 13 machines each
200
+ // deciding to delete on their own schedule.
201
+ void docs;
202
+ void autoLaunchPath;
203
+ }
@@ -22,6 +22,7 @@ import { homeRemainder, remoteCdPrefix } from '../project-root.js';
22
22
  import { getCacheDir } from '../state.js';
23
23
  import { hostKeyCheckingOpts } from './known-hosts.js';
24
24
  import { hostNameFor } from './ssh-config.js';
25
+ import { resolveDeviceProfile } from './resolve-profile.js';
25
26
  /** Env var the askpass shim reads to know which bundle holds the password. */
26
27
  export const ASKPASS_BUNDLE_ENV = 'AGENTS_SSH_BUNDLE';
27
28
  /** Env var the askpass shim reads to know which key in the bundle is the password. */
@@ -39,11 +40,12 @@ export const ASKPASS_AGENT_ONLY_ENV = 'AGENTS_SSH_AGENT_ONLY';
39
40
  * it against the shared injection guard. Throws if the device has no address.
40
41
  */
41
42
  export function sshTargetFor(device) {
42
- const host = hostNameFor(device);
43
+ const resolved = resolveDeviceProfile(device);
44
+ const host = hostNameFor(resolved);
43
45
  if (!host) {
44
- throw new Error(`Device '${device.name}' has no address (dnsName/ip). Run \`agents devices sync\` or \`agents devices add\`.`);
46
+ throw new Error(`Device '${resolved.name}' has no address (dnsName/ip). Run \`agents devices sync\` or \`agents devices add\`.`);
45
47
  }
46
- const target = device.user ? `${device.user}@${host}` : host;
48
+ const target = resolved.user ? `${resolved.user}@${host}` : host;
47
49
  assertValidSshTarget(target);
48
50
  return target;
49
51
  }
@@ -64,7 +66,8 @@ export function fleetDialTarget(device) {
64
66
  return sshTargetFor(device);
65
67
  }
66
68
  catch {
67
- return device.user ? `${device.user}@${device.name}` : device.name;
69
+ const resolved = resolveDeviceProfile(device);
70
+ return resolved.user ? `${resolved.user}@${resolved.name}` : resolved.name;
68
71
  }
69
72
  }
70
73
  /**
@@ -127,8 +130,9 @@ export function buildInteractiveShellCommand(device, mirrorCwd) {
127
130
  }
128
131
  /** OpenSSH argv that makes an explicit device key authoritative. */
129
132
  export function deviceIdentityArgs(device) {
130
- return device.auth?.method === 'key' && device.auth.identityFile
131
- ? ['-i', device.auth.identityFile, '-o', 'IdentitiesOnly=yes']
133
+ const resolved = resolveDeviceProfile(device);
134
+ return resolved.auth?.method === 'key' && resolved.auth.identityFile
135
+ ? ['-i', resolved.auth.identityFile, '-o', 'IdentitiesOnly=yes']
132
136
  : [];
133
137
  }
134
138
  /**
@@ -154,6 +158,9 @@ export function deviceIdentityArgs(device) {
154
158
  * behavior unchanged (RUSH-2412).
155
159
  */
156
160
  export function buildSshInvocation(device, cmd, askpassShimPath, hostKey = {}, opts = {}) {
161
+ // The effective profile: central config (ssh.*/platform/user) overlaid on
162
+ // the registry's discovery record.
163
+ device = resolveDeviceProfile(device);
157
164
  const target = sshTargetFor(device);
158
165
  // No cmd ⇒ interactive login. It may still carry a derived cd+login-shell
159
166
  // wrapper (interactiveCwd), which is an interactive login too and still needs
@@ -169,7 +176,7 @@ export function buildSshInvocation(device, cmd, askpassShimPath, hostKey = {}, o
169
176
  ];
170
177
  if (device.auth.method === 'password') {
171
178
  if (!device.auth.bundle) {
172
- throw new Error(`Device '${device.name}' uses password auth but has no secrets bundle. Set one with \`agents devices set ${device.name} --bundle <name>\`.`);
179
+ throw new Error(`Device '${device.name}' uses password auth but has no secrets bundle. Set one with \`agents devices config ${device.name} ssh.bundle <name>\`.`);
173
180
  }
174
181
  env.SSH_ASKPASS = askpassShimPath;
175
182
  env.SSH_ASKPASS_REQUIRE = 'force';
@@ -1,5 +1,6 @@
1
1
  import type { AgentId } from '../types.js';
2
2
  import type { DuplicateVersionHook } from '../hooks.js';
3
+ import type { AgentsBinaryShadow } from '../binary-shadow.js';
3
4
  import type { RcSecretFinding } from '../secrets/rc-hygiene.js';
4
5
  import type { OwnerSinkStatus } from '../channels/owner-sink.js';
5
6
  import { type WindowsSshEnrollmentAudit } from './windows-ssh-enrollment.js';
@@ -12,7 +13,7 @@ export type FindingSeverity = 'critical' | 'warning';
12
13
  * the JSON consumer group by kind. */
13
14
  /** Every finding class. Severity is NOT annotated here — {@link FINDING_SEVERITY}
14
15
  * below owns it, and a second copy in these comments is a fourth place to drift. */
15
- export declare const ALL_FINDING_KINDS: readonly ["logged-out", "logout-unprovable", "missing-hook", "missing-plugin", "unwired-hook", "hook-runtime-broken", "hook-runtime-visibility-unavailable", "cli-missing", "missing-resource", "content-drift", "never-synced", "stale", "repo-behind", "repo-drift", "fleet-resource-gap", "host-cli-missing", "host-cli-invalid", "version-skew", "orphan", "duplicate-hook", "duplicate-hook-drift", "rc-secret-export", "env-secret-export", "exec-policy", "ssh-key-enrollment", "stale-cli", "owner-sink-unreachable"];
16
+ export declare const ALL_FINDING_KINDS: readonly ["logged-out", "logout-unprovable", "missing-hook", "missing-plugin", "unwired-hook", "hook-runtime-broken", "hook-runtime-visibility-unavailable", "cli-missing", "missing-resource", "content-drift", "never-synced", "stale", "repo-behind", "repo-drift", "fleet-resource-gap", "host-cli-missing", "host-cli-invalid", "version-skew", "orphan", "duplicate-hook", "duplicate-hook-drift", "rc-secret-export", "env-secret-export", "exec-policy", "ssh-key-enrollment", "stale-cli", "binary-shadow", "owner-sink-unreachable"];
16
17
  /**
17
18
  * The severity each kind is emitted with - the SINGLE source of truth, read by
18
19
  * the builders below and asserted against both prose rubrics by
@@ -117,6 +118,8 @@ export interface LocalFindingInputs {
117
118
  * `rush` transport, so it stays out of this pure module). Absent → no probe ran
118
119
  * → no finding. */
119
120
  ownerSink?: OwnerSinkStatus;
121
+ /** `agents` binaries that shadow the currently running CLI (RUSH-2431). */
122
+ binaryShadows?: AgentsBinaryShadow[];
120
123
  }
121
124
  /**
122
125
  * Fold this machine's signals into findings. Missing hooks/plugins and unwired
@@ -26,7 +26,8 @@
26
26
  * never-synced · stale · repo-behind · repo-drift · version-skew ·
27
27
  * fleet-resource-gap · hook-runtime-visibility-unavailable · orphan · duplicate-hook ·
28
28
  * duplicate-hook-drift · host-cli-missing · host-cli-invalid ·
29
- * rc-secret-export · env-secret-export · exec-policy · stale-cli.
29
+ * rc-secret-export · env-secret-export · exec-policy · stale-cli ·
30
+ * binary-shadow.
30
31
  * (RUSH-2162 moved never-synced and duplicate-hook-drift to WARNING: both are
31
32
  * stale-sync states one `agents sync` resolves, not "needs you now".)
32
33
  *
@@ -104,6 +105,7 @@ export const ALL_FINDING_KINDS = [
104
105
  'exec-policy', // Windows execution policy blocks agents.ps1
105
106
  'ssh-key-enrollment', // Windows OpenSSH public-key path/content/ACL is invalid
106
107
  'stale-cli',
108
+ 'binary-shadow', // another agents binary on PATH or in a well-known dir shadows the running copy
107
109
  'owner-sink-unreachable', // the feed/notify owner-delivery lane can't reach the owner from this box
108
110
  ];
109
111
  /**
@@ -151,6 +153,7 @@ export const FINDING_SEVERITY = {
151
153
  'exec-policy': 'warning',
152
154
  'ssh-key-enrollment': 'critical',
153
155
  'stale-cli': 'warning',
156
+ 'binary-shadow': 'warning',
154
157
  };
155
158
  function agentName(agent) {
156
159
  return AGENT_NAMES[agent] || agent;
@@ -272,6 +275,8 @@ export function remediationFor(finding) {
272
275
  return 'repair the reported AuthorizedKeysFile, then rerun agents doctor';
273
276
  case 'stale-cli':
274
277
  return 'upgrade';
278
+ case 'binary-shadow':
279
+ return 'remove or repoint the shadowing agents install(s)';
275
280
  case 'owner-sink-unreachable':
276
281
  // The lane delivers over the rush-backed owner channel, which needs rush on
277
282
  // PATH AND a usable session in THIS context. Non-interactive shells miss a
@@ -531,6 +536,18 @@ export function buildLocalFindings(input) {
531
536
  // (warning). Signed-in versions produce no finding — the accounts line shows
532
537
  // them. Agents that can't be inspected never yield a logout finding.
533
538
  out.push(...signInToFindings(device, input.signIn));
539
+ // agents binary shadows (warning) — another install could be resolved by a
540
+ // scheduled routine or by a different shell PATH, running stale code.
541
+ const shadows = input.binaryShadows ?? [];
542
+ if (shadows.length > 0) {
543
+ const examples = shadows.slice(0, 2).map((s) => `${s.path}${s.version ? ` (${s.version})` : ''}`).join(', ');
544
+ out.push(finding({
545
+ severity: FINDING_SEVERITY['binary-shadow'], kind: 'binary-shadow', device,
546
+ message: shadows.length === 1
547
+ ? `agents binary shadowed by ${examples}`
548
+ : `${shadows.length} agents binaries may shadow the running copy (incl. ${examples})`,
549
+ }));
550
+ }
534
551
  return collapseAcrossVersions(out, new Set(input.isolatedVersions ?? []));
535
552
  }
536
553
  /**
@@ -1013,6 +1030,8 @@ function warningSubject(f) {
1013
1030
  // so a self-referential subject reads as the local machine in fleet mode.
1014
1031
  if (f.kind === 'stale-cli')
1015
1032
  return 'agents-cli';
1033
+ if (f.kind === 'binary-shadow')
1034
+ return 'agents-cli';
1016
1035
  if (f.kind === 'orphan')
1017
1036
  return 'orphans';
1018
1037
  if (f.kind === 'rc-secret-export')
@@ -1,3 +1,4 @@
1
+ import { type AccountInfo } from '../agents.js';
1
2
  import type { AgentId } from '../types.js';
2
3
  import { type UsageSnapshot } from '../usage.js';
3
4
  /** An account's usage headroom rolled into one glanceable summary. */
@@ -6,11 +7,19 @@ export interface QuotaSummary {
6
7
  * `available` / `rate_limited` from the live usage windows, or `null` when
7
8
  * there is no snapshot at all (no data yet, or the agent has no usage source).
8
9
  */
9
- status: 'available' | 'rate_limited' | null;
10
+ status: 'available' | 'rate_limited' | 'out_of_credits' | null;
11
+ /** Canonical launch verdict. Kept separate from utilization for JSON clients. */
12
+ verdict: 'available' | 'rate_limited' | 'out_of_credits' | 'unavailable';
10
13
  /** Max utilization across blocking windows (0-100, rounded), or null when unknown. */
11
14
  usedPercent: number | null;
12
15
  /** True when the snapshot is a cached `last_seen`, not a fresh `live` read. */
13
16
  stale: boolean;
17
+ /** ISO-8601 timestamp for the underlying usage observation. */
18
+ capturedAt: string | null;
19
+ /** Earliest reset across blocking windows, ISO-8601. */
20
+ resetsAt: string | null;
21
+ /** Why quota could not be evaluated; null when a verdict is available. */
22
+ unavailableReason: string | null;
14
23
  }
15
24
  /** One installed (agent, version) on a host, fully resolved. */
16
25
  export interface HarnessRow {
@@ -55,7 +64,7 @@ export interface AccountGroup {
55
64
  * `sonnet_week` sub-limit is excluded so hitting it doesn't read as a throttled
56
65
  * account) and takes the highest utilization across those windows. Pure.
57
66
  */
58
- export declare function summarizeQuota(snapshot: UsageSnapshot | null | undefined): QuotaSummary;
67
+ export declare function summarizeQuota(snapshot: UsageSnapshot | null | undefined, unavailableReason?: string | null, accountStatus?: AccountInfo['usageStatus']): QuotaSummary;
59
68
  /**
60
69
  * "Ready" = signed in AND not rate-limited. A missing quota snapshot does NOT
61
70
  * block readiness: the account is signed in and usable, we just have no live
@@ -8,7 +8,7 @@
8
8
  * of blocking the whole table.
9
9
  *
10
10
  * The parsers are pure and unit-tested (health.test.ts). They mirror the ones in
11
- * the Factory extension (apps/factory/src/core/deviceHealth.ts) — kept as a
11
+ * AGI EXT (apps/ext/src/core/deviceHealth.ts) — kept as a
12
12
  * separate copy on purpose: the CLI does not import across packages.
13
13
  */
14
14
  import type { DeviceProfile } from './registry.js';
@@ -8,7 +8,7 @@
8
8
  * of blocking the whole table.
9
9
  *
10
10
  * The parsers are pure and unit-tested (health.test.ts). They mirror the ones in
11
- * the Factory extension (apps/factory/src/core/deviceHealth.ts) — kept as a
11
+ * AGI EXT (apps/ext/src/core/deviceHealth.ts) — kept as a
12
12
  * separate copy on purpose: the CLI does not import across packages.
13
13
  */
14
14
  import { execFile } from 'child_process';
@@ -2,14 +2,35 @@ export interface PendingDevice {
2
2
  name: string;
3
3
  platform: string;
4
4
  }
5
+ /**
6
+ * Drop pending sentinels for devices that are already registered or ignored.
7
+ * Safe without a live tailscale probe — only removes names we know are no
8
+ * longer "new". Used when soft-fail discovery cannot recompute the full
9
+ * pending set, so a test leak or race cannot leave registered fleet boxes in
10
+ * the menu bar's NEW DEVICES section forever.
11
+ */
12
+ export declare function pruneDismissedPendingSentinels(): Promise<void>;
5
13
  /**
6
14
  * Make the sentinel dir exactly match `pending`: create a file per pending
7
15
  * device (content = platform), and delete any leftover sentinel whose device is
8
16
  * no longer pending (it got registered, ignored, or left the tailnet). Best-
9
17
  * effort — a filesystem error here must never crash the daemon, so callers pass
10
18
  * this through their existing try/catch.
19
+ *
20
+ * The sentinel writer is the single authoritative choke point every discovery
21
+ * path flows through (the daemon device-probe timer and `agents sync`), so it
22
+ * re-subtracts the persisted ignore-list AND the registered roster rather than
23
+ * trusting the caller's `pending` set. That closes two races:
24
+ * - probe/ignore (RUSH-2495): a probe that computed `pending` BEFORE the user
25
+ * pressed "Ignore" would otherwise re-create the just-dismissed device's
26
+ * sentinel and the menu bar would re-surface it.
27
+ * - registry-empty pollution: a process that had AGENTS_DEVICES_DIR redirected
28
+ * (tests / hermetic runs) but still wrote the live devices-pending dir
29
+ * treated every tailnet node as new — including already-registered fleet
30
+ * boxes. Re-subtracting the live registry means those phantoms are never
31
+ * written and are removed if they already exist.
11
32
  */
12
- export declare function reconcilePendingSentinels(pending: PendingDevice[]): void;
33
+ export declare function reconcilePendingSentinels(pending: PendingDevice[]): Promise<void>;
13
34
  /** Remove one device's pending sentinel (after the user registers or ignores it).
14
35
  * No-op if it doesn't exist. */
15
36
  export declare function clearPendingSentinel(name: string): void;
@@ -16,21 +16,86 @@
16
16
  import * as fs from 'fs';
17
17
  import * as path from 'path';
18
18
  import { getDevicesPendingDir } from '../state.js';
19
+ import { loadDevices, loadIgnored } from './registry.js';
19
20
  /** Device-name sentinels must be safe filenames (no path traversal). The device
20
21
  * name charset is already the ssh-alias set, but guard defensively. */
21
22
  function isSafeName(name) {
22
23
  return /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name);
23
24
  }
25
+ /**
26
+ * Best-effort load of names that must never appear as "NEW DEVICES": the
27
+ * ignore-list and the registered roster. A corrupted registry/ignore file
28
+ * (both throw by design) must not crash the daemon loop — treat it as empty
29
+ * and let the next probe recover once the file is fixed.
30
+ */
31
+ async function loadDismissedNames() {
32
+ const out = new Set();
33
+ try {
34
+ for (const name of await loadIgnored())
35
+ out.add(name);
36
+ }
37
+ catch { /* treat as nothing ignored */ }
38
+ try {
39
+ for (const name of Object.keys(await loadDevices()))
40
+ out.add(name);
41
+ }
42
+ catch { /* treat as nothing registered */ }
43
+ return out;
44
+ }
45
+ /**
46
+ * Drop pending sentinels for devices that are already registered or ignored.
47
+ * Safe without a live tailscale probe — only removes names we know are no
48
+ * longer "new". Used when soft-fail discovery cannot recompute the full
49
+ * pending set, so a test leak or race cannot leave registered fleet boxes in
50
+ * the menu bar's NEW DEVICES section forever.
51
+ */
52
+ export async function pruneDismissedPendingSentinels() {
53
+ const dir = getDevicesPendingDir();
54
+ const dismissed = await loadDismissedNames();
55
+ if (dismissed.size === 0)
56
+ return;
57
+ let existing;
58
+ try {
59
+ existing = fs.readdirSync(dir).filter((n) => !n.startsWith('.'));
60
+ }
61
+ catch {
62
+ return;
63
+ }
64
+ for (const name of existing) {
65
+ if (!dismissed.has(name))
66
+ continue;
67
+ try {
68
+ fs.unlinkSync(path.join(dir, name));
69
+ }
70
+ catch { /* already gone */ }
71
+ }
72
+ }
24
73
  /**
25
74
  * Make the sentinel dir exactly match `pending`: create a file per pending
26
75
  * device (content = platform), and delete any leftover sentinel whose device is
27
76
  * no longer pending (it got registered, ignored, or left the tailnet). Best-
28
77
  * effort — a filesystem error here must never crash the daemon, so callers pass
29
78
  * this through their existing try/catch.
79
+ *
80
+ * The sentinel writer is the single authoritative choke point every discovery
81
+ * path flows through (the daemon device-probe timer and `agents sync`), so it
82
+ * re-subtracts the persisted ignore-list AND the registered roster rather than
83
+ * trusting the caller's `pending` set. That closes two races:
84
+ * - probe/ignore (RUSH-2495): a probe that computed `pending` BEFORE the user
85
+ * pressed "Ignore" would otherwise re-create the just-dismissed device's
86
+ * sentinel and the menu bar would re-surface it.
87
+ * - registry-empty pollution: a process that had AGENTS_DEVICES_DIR redirected
88
+ * (tests / hermetic runs) but still wrote the live devices-pending dir
89
+ * treated every tailnet node as new — including already-registered fleet
90
+ * boxes. Re-subtracting the live registry means those phantoms are never
91
+ * written and are removed if they already exist.
30
92
  */
31
- export function reconcilePendingSentinels(pending) {
93
+ export async function reconcilePendingSentinels(pending) {
32
94
  const dir = getDevicesPendingDir();
33
- const want = new Map(pending.filter((p) => isSafeName(p.name)).map((p) => [p.name, p.platform]));
95
+ const dismissed = await loadDismissedNames();
96
+ const want = new Map(pending
97
+ .filter((p) => isSafeName(p.name) && !dismissed.has(p.name))
98
+ .map((p) => [p.name, p.platform]));
34
99
  // Whole body is best-effort: a filesystem error here must never propagate into
35
100
  // the daemon loop or `agents sync`. The top-level mkdir/readdir are guarded
36
101
  // too, so no caller needs its own try/catch.
@@ -158,28 +158,3 @@ export declare function addIgnored(name: string): Promise<Set<string>>;
158
158
  /** Remove a node name from the ignore-list (un-ignore). Returns false if it was
159
159
  * not ignored. */
160
160
  export declare function removeIgnored(name: string): Promise<boolean>;
161
- /**
162
- * Auto-launch preferences: which registered devices are eligible for Factory's
163
- * auto-host selection, and which are preferred. Stored as a sibling to the
164
- * registry and ignore-list under ~/.agents/.history/devices/.
165
- */
166
- export interface AutoLaunchPreference {
167
- enabled?: boolean;
168
- preferred?: boolean;
169
- }
170
- export interface AutoLaunchPreferences {
171
- devices: Record<string, AutoLaunchPreference>;
172
- updatedAt: string;
173
- }
174
- /** Load auto-launch preferences. Missing or malformed file => empty map. */
175
- export declare function loadAutoLaunchPreferences(): Promise<Record<string, AutoLaunchPreference>>;
176
- /** True if the device is enabled for auto-launch. Missing entry defaults to true. */
177
- export declare function isAutoLaunchEnabled(name: string): Promise<boolean>;
178
- /** Set whether a device is enabled for auto-launch. Setting to the default
179
- * (enabled) removes the entry to keep the file minimal. */
180
- export declare function setAutoLaunchEnabled(name: string, enabled: boolean): Promise<void>;
181
- /** True if the device is preferred for auto-launch ranking. */
182
- export declare function isAutoLaunchPreferred(name: string): Promise<boolean>;
183
- /** Set whether a device is preferred for auto-launch. Setting to the default
184
- * (not preferred) removes the flag to keep the file minimal. */
185
- export declare function setAutoLaunchPreferred(name: string, preferred: boolean): Promise<void>;