@phnx-labs/agents-cli 1.22.35 → 1.22.38

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 (500) hide show
  1. package/CHANGELOG.md +708 -2
  2. package/README.md +83 -43
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.d.ts +15 -0
  5. package/dist/bootstrap.js +1212 -0
  6. package/dist/commands/accounts.d.ts +4 -11
  7. package/dist/commands/accounts.js +159 -27
  8. package/dist/commands/artifacts-setup.d.ts +53 -0
  9. package/dist/commands/artifacts-setup.js +161 -0
  10. package/dist/commands/artifacts.d.ts +18 -0
  11. package/dist/commands/artifacts.js +58 -0
  12. package/dist/commands/attach.js +17 -4
  13. package/dist/commands/audit.d.ts +8 -8
  14. package/dist/commands/audit.js +57 -47
  15. package/dist/commands/browser.js +17 -11
  16. package/dist/commands/cloud.js +1 -1
  17. package/dist/commands/commands.js +0 -11
  18. package/dist/commands/computer-actions.js +33 -0
  19. package/dist/commands/computer-sessions-picker.d.ts +21 -0
  20. package/dist/commands/computer-sessions-picker.js +141 -0
  21. package/dist/commands/computer.d.ts +16 -0
  22. package/dist/commands/computer.js +37 -0
  23. package/dist/commands/config.d.ts +17 -0
  24. package/dist/commands/config.js +401 -0
  25. package/dist/commands/cp.d.ts +84 -0
  26. package/dist/commands/cp.js +231 -0
  27. package/dist/commands/daemon.js +243 -14
  28. package/dist/commands/detach-core.d.ts +1 -1
  29. package/dist/commands/detach-core.js +12 -2
  30. package/dist/commands/detach.js +1 -1
  31. package/dist/commands/doctor.js +64 -5
  32. package/dist/commands/events.d.ts +24 -12
  33. package/dist/commands/events.js +144 -28
  34. package/dist/commands/exec.js +86 -42
  35. package/dist/commands/feed.d.ts +1 -1
  36. package/dist/commands/feed.js +16 -8
  37. package/dist/commands/focus.d.ts +33 -1
  38. package/dist/commands/focus.js +177 -17
  39. package/dist/commands/funnel.d.ts +1 -1
  40. package/dist/commands/go.js +9 -8
  41. package/dist/commands/harness-wizard.d.ts +1 -0
  42. package/dist/commands/harness-wizard.js +18 -20
  43. package/dist/commands/harness.d.ts +2 -0
  44. package/dist/commands/harness.js +59 -26
  45. package/dist/commands/hooks.js +0 -11
  46. package/dist/commands/inspect.d.ts +94 -1
  47. package/dist/commands/inspect.js +866 -40
  48. package/dist/commands/lease.d.ts +4 -4
  49. package/dist/commands/lease.js +6 -6
  50. package/dist/commands/login.js +0 -13
  51. package/dist/commands/logs.d.ts +2 -2
  52. package/dist/commands/logs.js +2 -2
  53. package/dist/commands/mcp.js +7 -2
  54. package/dist/commands/memory.js +7 -2
  55. package/dist/commands/message.d.ts +1 -1
  56. package/dist/commands/message.js +37 -6
  57. package/dist/commands/models.js +71 -0
  58. package/dist/commands/monitors.d.ts +0 -1
  59. package/dist/commands/monitors.js +199 -16
  60. package/dist/commands/open.d.ts +18 -0
  61. package/dist/commands/open.js +57 -0
  62. package/dist/commands/packages.js +2 -107
  63. package/dist/commands/plugins.js +6 -3
  64. package/dist/commands/profiles.d.ts +1 -0
  65. package/dist/commands/profiles.js +16 -160
  66. package/dist/commands/projects.d.ts +28 -0
  67. package/dist/commands/projects.js +125 -5
  68. package/dist/commands/reconnect.js +9 -3
  69. package/dist/commands/repo.js +42 -1
  70. package/dist/commands/resource-view.d.ts +17 -0
  71. package/dist/commands/resource-view.js +36 -14
  72. package/dist/commands/resume.js +9 -2
  73. package/dist/commands/routines.d.ts +0 -2
  74. package/dist/commands/routines.js +473 -289
  75. package/dist/commands/secrets.js +52 -9
  76. package/dist/commands/sessions-export.d.ts +47 -0
  77. package/dist/commands/sessions-export.js +116 -6
  78. package/dist/commands/sessions-import.d.ts +17 -0
  79. package/dist/commands/sessions-import.js +114 -6
  80. package/dist/commands/sessions-migrate.d.ts +27 -0
  81. package/dist/commands/sessions-migrate.js +50 -9
  82. package/dist/commands/sessions-picker.js +23 -4
  83. package/dist/commands/sessions-resume.d.ts +5 -3
  84. package/dist/commands/sessions-resume.js +30 -8
  85. package/dist/commands/sessions-watch.d.ts +2 -0
  86. package/dist/commands/sessions-watch.js +53 -0
  87. package/dist/commands/sessions.d.ts +66 -3
  88. package/dist/commands/sessions.js +298 -47
  89. package/dist/commands/setup-browser.js +2 -2
  90. package/dist/commands/setup-mine.js +0 -1
  91. package/dist/commands/setup-preferences.js +1 -1
  92. package/dist/commands/setup.d.ts +5 -0
  93. package/dist/commands/setup.js +31 -5
  94. package/dist/commands/share.d.ts +87 -7
  95. package/dist/commands/share.js +320 -58
  96. package/dist/commands/skills.js +0 -11
  97. package/dist/commands/snapshot.js +1 -1
  98. package/dist/commands/ssh.d.ts +18 -0
  99. package/dist/commands/ssh.js +549 -210
  100. package/dist/commands/sync.d.ts +5 -0
  101. package/dist/commands/sync.js +212 -32
  102. package/dist/commands/teams.d.ts +13 -0
  103. package/dist/commands/teams.js +187 -30
  104. package/dist/commands/tickets.d.ts +2 -0
  105. package/dist/commands/tickets.js +43 -0
  106. package/dist/commands/update.js +2 -4
  107. package/dist/commands/usage.js +5 -3
  108. package/dist/commands/versions.js +11 -5
  109. package/dist/commands/view.js +15 -7
  110. package/dist/commands/webhook.d.ts +2 -2
  111. package/dist/commands/webhook.js +7 -7
  112. package/dist/index.d.ts +18 -2
  113. package/dist/index.js +53 -1264
  114. package/dist/lib/account-catalog.d.ts +18 -0
  115. package/dist/lib/account-catalog.js +38 -0
  116. package/dist/lib/account-provider-registry.d.ts +18 -0
  117. package/dist/lib/account-provider-registry.js +59 -0
  118. package/dist/lib/account-registry.d.ts +40 -0
  119. package/dist/lib/account-registry.js +239 -0
  120. package/dist/lib/account-schema.d.ts +44 -0
  121. package/dist/lib/account-schema.js +91 -0
  122. package/dist/lib/account-state-service.d.ts +21 -0
  123. package/dist/lib/account-state-service.js +60 -0
  124. package/dist/lib/activity.d.ts +1 -1
  125. package/dist/lib/activity.js +8 -1
  126. package/dist/lib/add-dir.d.ts +80 -0
  127. package/dist/lib/add-dir.js +241 -0
  128. package/dist/lib/agent-cli-commands.d.ts +11 -0
  129. package/dist/lib/agent-cli-commands.js +30 -0
  130. package/dist/lib/agents.js +44 -36
  131. package/dist/lib/answer-router.js +1 -1
  132. package/dist/lib/audit/log.d.ts +10 -27
  133. package/dist/lib/audit/log.js +20 -33
  134. package/dist/lib/auth-health.d.ts +9 -2
  135. package/dist/lib/auth-health.js +42 -9
  136. package/dist/lib/auto-pull-worker.js +7 -3
  137. package/dist/lib/auto-pull.d.ts +44 -3
  138. package/dist/lib/auto-pull.js +88 -5
  139. package/dist/lib/binary-shadow.d.ts +28 -0
  140. package/dist/lib/binary-shadow.js +121 -0
  141. package/dist/lib/brand.js +9 -3
  142. package/dist/lib/browser/hygiene.d.ts +90 -0
  143. package/dist/lib/browser/hygiene.js +146 -0
  144. package/dist/lib/browser/ipc.d.ts +30 -0
  145. package/dist/lib/browser/ipc.js +93 -4
  146. package/dist/lib/browser/profiles.d.ts +3 -2
  147. package/dist/lib/browser/profiles.js +65 -22
  148. package/dist/lib/browser/service.d.ts +79 -1
  149. package/dist/lib/browser/service.js +231 -14
  150. package/dist/lib/browser/sessions-list.d.ts +22 -2
  151. package/dist/lib/browser/sessions-list.js +54 -6
  152. package/dist/lib/browser/stream.d.ts +2 -0
  153. package/dist/lib/browser/stream.js +1 -0
  154. package/dist/lib/browser/types.d.ts +63 -1
  155. package/dist/lib/byok-usage.d.ts +9 -2
  156. package/dist/lib/byok-usage.js +133 -31
  157. package/dist/lib/cli-entry.d.ts +11 -0
  158. package/dist/lib/cli-entry.js +46 -0
  159. package/dist/lib/cloud/host.d.ts +1 -1
  160. package/dist/lib/cloud/host.js +2 -2
  161. package/dist/lib/cloud/registry.js +1 -1
  162. package/dist/lib/cloud/types.d.ts +1 -1
  163. package/dist/lib/commands.d.ts +2 -0
  164. package/dist/lib/commands.js +2 -0
  165. package/dist/lib/computer/sessions-list.d.ts +150 -0
  166. package/dist/lib/computer/sessions-list.js +363 -0
  167. package/dist/lib/config-keys.d.ts +86 -0
  168. package/dist/lib/config-keys.js +193 -0
  169. package/dist/lib/config-machine-keys.d.ts +12 -0
  170. package/dist/lib/config-machine-keys.js +17 -0
  171. package/dist/lib/config-transfer.js +3 -4
  172. package/dist/lib/crabbox/cli.d.ts +2 -2
  173. package/dist/lib/crabbox/cli.js +3 -3
  174. package/dist/lib/daemon-health.d.ts +22 -0
  175. package/dist/lib/daemon-health.js +33 -3
  176. package/dist/lib/daemon-services.d.ts +50 -0
  177. package/dist/lib/daemon-services.js +147 -0
  178. package/dist/lib/daemon-ticks.d.ts +35 -66
  179. package/dist/lib/daemon-ticks.js +77 -159
  180. package/dist/lib/daemon.d.ts +72 -4
  181. package/dist/lib/daemon.js +792 -202
  182. package/dist/lib/deeplink/register.d.ts +49 -0
  183. package/dist/lib/deeplink/register.js +253 -0
  184. package/dist/lib/deeplink/url.d.ts +34 -0
  185. package/dist/lib/deeplink/url.js +78 -0
  186. package/dist/lib/device-config.d.ts +124 -29
  187. package/dist/lib/device-config.js +346 -141
  188. package/dist/lib/devices/config-migration.d.ts +42 -0
  189. package/dist/lib/devices/config-migration.js +203 -0
  190. package/dist/lib/devices/connect.js +14 -7
  191. package/dist/lib/devices/doctor-findings.d.ts +4 -1
  192. package/dist/lib/devices/doctor-findings.js +20 -1
  193. package/dist/lib/devices/harness-inventory.d.ts +11 -2
  194. package/dist/lib/devices/harness-inventory.js +0 -0
  195. package/dist/lib/devices/health.d.ts +1 -1
  196. package/dist/lib/devices/health.js +1 -1
  197. package/dist/lib/devices/pending.d.ts +22 -1
  198. package/dist/lib/devices/pending.js +67 -2
  199. package/dist/lib/devices/registry.d.ts +0 -25
  200. package/dist/lib/devices/registry.js +2 -83
  201. package/dist/lib/devices/resolve-profile.d.ts +20 -0
  202. package/dist/lib/devices/resolve-profile.js +44 -0
  203. package/dist/lib/devices/rollout-verify.d.ts +101 -0
  204. package/dist/lib/devices/rollout-verify.js +193 -0
  205. package/dist/lib/devices/ssh-config.js +10 -6
  206. package/dist/lib/event-families.d.ts +28 -0
  207. package/dist/lib/event-families.js +159 -0
  208. package/dist/lib/event-stream.d.ts +9 -1
  209. package/dist/lib/event-stream.js +26 -9
  210. package/dist/lib/events.d.ts +9 -1
  211. package/dist/lib/events.js +21 -6
  212. package/dist/lib/exec.d.ts +30 -0
  213. package/dist/lib/exec.js +219 -121
  214. package/dist/lib/feed-broadcast.d.ts +25 -0
  215. package/dist/lib/feed-broadcast.js +33 -0
  216. package/dist/lib/feed.d.ts +1 -1
  217. package/dist/lib/feed.js +12 -2
  218. package/dist/lib/fleet/capture.js +15 -0
  219. package/dist/lib/fleet/manifest.js +9 -0
  220. package/dist/lib/fleet/types.d.ts +9 -0
  221. package/dist/lib/fleet-status.js +10 -8
  222. package/dist/lib/git.d.ts +21 -7
  223. package/dist/lib/git.js +122 -18
  224. package/dist/lib/hosts/dispatch.d.ts +8 -3
  225. package/dist/lib/hosts/dispatch.js +29 -3
  226. package/dist/lib/hosts/logs.d.ts +2 -2
  227. package/dist/lib/hosts/logs.js +2 -2
  228. package/dist/lib/hosts/option.js +1 -1
  229. package/dist/lib/hosts/passthrough.d.ts +2 -3
  230. package/dist/lib/hosts/passthrough.js +10 -24
  231. package/dist/lib/hosts/progress.js +2 -2
  232. package/dist/lib/hosts/providers/devices.d.ts +3 -3
  233. package/dist/lib/hosts/providers/devices.js +18 -12
  234. package/dist/lib/hosts/ready.d.ts +5 -1
  235. package/dist/lib/hosts/ready.js +9 -3
  236. package/dist/lib/hosts/reconcile.d.ts +2 -2
  237. package/dist/lib/hosts/reconcile.js +2 -2
  238. package/dist/lib/hosts/registry.js +14 -9
  239. package/dist/lib/hosts/remote-cmd.js +1 -1
  240. package/dist/lib/hosts/remote-os.d.ts +9 -6
  241. package/dist/lib/hosts/remote-os.js +13 -6
  242. package/dist/lib/hosts/routing-flag.d.ts +23 -0
  243. package/dist/lib/hosts/routing-flag.js +41 -0
  244. package/dist/lib/hosts/run-target.js +1 -1
  245. package/dist/lib/hosts/session-index.js +1 -1
  246. package/dist/lib/hosts/tasks.d.ts +6 -6
  247. package/dist/lib/hosts/tasks.js +4 -4
  248. package/dist/lib/hosts/types.js +1 -2
  249. package/dist/lib/installations/resolve.js +2 -8
  250. package/dist/lib/isolation-boundary-report.js +0 -1
  251. package/dist/lib/linear-cache.d.ts +7 -0
  252. package/dist/lib/linear-cache.js +31 -4
  253. package/dist/lib/linear-project-counts.d.ts +1 -1
  254. package/dist/lib/linear-project-counts.js +2 -3
  255. package/dist/lib/linear-projects.js +1 -1
  256. package/dist/lib/loop.d.ts +2 -2
  257. package/dist/lib/loop.js +1 -1
  258. package/dist/lib/mailbox-target.d.ts +1 -1
  259. package/dist/lib/mailbox-target.js +1 -1
  260. package/dist/lib/manifest.js +7 -4
  261. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  262. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  263. package/dist/lib/menubar/snapshot.d.ts +3 -2
  264. package/dist/lib/menubar/snapshot.js +6 -2
  265. package/dist/lib/migrate-fold.d.ts +33 -0
  266. package/dist/lib/migrate-fold.js +130 -0
  267. package/dist/lib/migrate.d.ts +34 -34
  268. package/dist/lib/migrate.js +106 -128
  269. package/dist/lib/model-tier-overrides.d.ts +1 -1
  270. package/dist/lib/model-tier-overrides.js +15 -2
  271. package/dist/lib/monitors/config.d.ts +17 -3
  272. package/dist/lib/monitors/config.js +52 -18
  273. package/dist/lib/monitors/engine.d.ts +32 -1
  274. package/dist/lib/monitors/engine.js +94 -11
  275. package/dist/lib/monitors/fingerprint.d.ts +46 -0
  276. package/dist/lib/monitors/fingerprint.js +100 -0
  277. package/dist/lib/monitors/remote.d.ts +47 -0
  278. package/dist/lib/monitors/remote.js +81 -0
  279. package/dist/lib/monitors/state.d.ts +39 -0
  280. package/dist/lib/monitors/state.js +56 -0
  281. package/dist/lib/onepassword.d.ts +17 -0
  282. package/dist/lib/onepassword.js +52 -22
  283. package/dist/lib/open-url.js +1 -1
  284. package/dist/lib/picker.d.ts +14 -2
  285. package/dist/lib/picker.js +35 -13
  286. package/dist/lib/placement.d.ts +1 -1
  287. package/dist/lib/placement.js +1 -1
  288. package/dist/lib/pr-land-detach.d.ts +32 -0
  289. package/dist/lib/pr-land-detach.js +79 -0
  290. package/dist/lib/profiles-presets.js +13 -0
  291. package/dist/lib/profiles.d.ts +2 -0
  292. package/dist/lib/profiles.js +44 -1
  293. package/dist/lib/project-focus.d.ts +1 -1
  294. package/dist/lib/project-focus.js +1 -1
  295. package/dist/lib/project-probe.d.ts +4 -1
  296. package/dist/lib/project-probe.js +5 -4
  297. package/dist/lib/project-resources.js +2 -18
  298. package/dist/lib/project-root.d.ts +16 -0
  299. package/dist/lib/project-root.js +20 -2
  300. package/dist/lib/projects.d.ts +20 -1
  301. package/dist/lib/projects.js +78 -1
  302. package/dist/lib/refresh-coordinator.d.ts +24 -0
  303. package/dist/lib/refresh-coordinator.js +52 -0
  304. package/dist/lib/registry.d.ts +2 -2
  305. package/dist/lib/registry.js +1 -1
  306. package/dist/lib/resource-aliases.d.ts +13 -0
  307. package/dist/lib/resource-aliases.js +26 -0
  308. package/dist/lib/resources/types.d.ts +1 -1
  309. package/dist/lib/resources.d.ts +16 -0
  310. package/dist/lib/resources.js +66 -10
  311. package/dist/lib/rotate.d.ts +3 -4
  312. package/dist/lib/rotate.js +4 -5
  313. package/dist/lib/routine-activation.d.ts +22 -2
  314. package/dist/lib/routine-activation.js +63 -16
  315. package/dist/lib/routine-context.d.ts +9 -2
  316. package/dist/lib/routine-context.js +21 -6
  317. package/dist/lib/routines-project.d.ts +58 -48
  318. package/dist/lib/routines-project.js +190 -159
  319. package/dist/lib/routines.d.ts +44 -7
  320. package/dist/lib/routines.js +92 -34
  321. package/dist/lib/run-defaults.d.ts +5 -0
  322. package/dist/lib/run-defaults.js +11 -1
  323. package/dist/lib/runner.d.ts +10 -8
  324. package/dist/lib/runner.js +122 -39
  325. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  326. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  327. package/dist/lib/secrets/agent.d.ts +10 -3
  328. package/dist/lib/secrets/agent.js +43 -8
  329. package/dist/lib/secrets/bundles.d.ts +1 -0
  330. package/dist/lib/secrets/bundles.js +92 -80
  331. package/dist/lib/secrets/index.js +32 -9
  332. package/dist/lib/secrets/install-helper.d.ts +4 -4
  333. package/dist/lib/secrets/install-helper.js +4 -4
  334. package/dist/lib/secrets/push.d.ts +40 -6
  335. package/dist/lib/secrets/push.js +58 -11
  336. package/dist/lib/secrets/read-backoff.d.ts +1 -1
  337. package/dist/lib/secrets/read-backoff.js +1 -1
  338. package/dist/lib/secrets/remote.d.ts +24 -0
  339. package/dist/lib/secrets/remote.js +54 -4
  340. package/dist/lib/self-update.d.ts +108 -0
  341. package/dist/lib/self-update.js +276 -1
  342. package/dist/lib/session/active.d.ts +89 -5
  343. package/dist/lib/session/active.js +165 -10
  344. package/dist/lib/session/actor-sidecar.d.ts +1 -1
  345. package/dist/lib/session/actor-sidecar.js +2 -1
  346. package/dist/lib/session/db.d.ts +176 -2
  347. package/dist/lib/session/db.js +484 -17
  348. package/dist/lib/session/discover.js +27 -4
  349. package/dist/lib/session/hook-sessions.js +2 -2
  350. package/dist/lib/session/host-link.d.ts +2 -2
  351. package/dist/lib/session/host-link.js +2 -2
  352. package/dist/lib/session/pid-registry.d.ts +21 -0
  353. package/dist/lib/session/pid-registry.js +57 -0
  354. package/dist/lib/session/remote-active.js +15 -1
  355. package/dist/lib/session/remote.js +2 -2
  356. package/dist/lib/session/session-cache.d.ts +14 -7
  357. package/dist/lib/session/session-cache.js +51 -9
  358. package/dist/lib/session/state.d.ts +2 -2
  359. package/dist/lib/session/state.js +1 -1
  360. package/dist/lib/session/sync/config.d.ts +10 -10
  361. package/dist/lib/session/sync/config.js +10 -10
  362. package/dist/lib/session/sync/r2.d.ts +34 -0
  363. package/dist/lib/session/sync/r2.js +123 -0
  364. package/dist/lib/session/throughput.d.ts +1 -1
  365. package/dist/lib/session/throughput.js +1 -1
  366. package/dist/lib/session/types.d.ts +23 -1
  367. package/dist/lib/session/types.js +15 -0
  368. package/dist/lib/session/viewing-in.d.ts +1 -1
  369. package/dist/lib/session/viewing-in.js +1 -1
  370. package/dist/lib/session/watch.d.ts +101 -0
  371. package/dist/lib/session/watch.js +242 -0
  372. package/dist/lib/share/analytics.js +1 -1
  373. package/dist/lib/share/capture.d.ts +1 -1
  374. package/dist/lib/share/capture.js +3 -3
  375. package/dist/lib/share/config.d.ts +6 -2
  376. package/dist/lib/share/config.js +6 -5
  377. package/dist/lib/share/delete.js +3 -3
  378. package/dist/lib/share/provision.d.ts +38 -0
  379. package/dist/lib/share/provision.js +51 -1
  380. package/dist/lib/share/publish.d.ts +40 -1
  381. package/dist/lib/share/publish.js +119 -6
  382. package/dist/lib/share/worker-template.d.ts +12 -1
  383. package/dist/lib/share/worker-template.js +83 -7
  384. package/dist/lib/shims.d.ts +17 -3
  385. package/dist/lib/shims.js +98 -26
  386. package/dist/lib/signin-badge.js +3 -2
  387. package/dist/lib/skills.js +2 -0
  388. package/dist/lib/smart-launch.d.ts +8 -8
  389. package/dist/lib/smart-launch.js +80 -59
  390. package/dist/lib/ssh-exec.d.ts +1 -1
  391. package/dist/lib/ssh-exec.js +1 -1
  392. package/dist/lib/staleness/prune.d.ts +88 -0
  393. package/dist/lib/staleness/prune.js +69 -0
  394. package/dist/lib/staleness/registry.d.ts +1 -1
  395. package/dist/lib/staleness/writers/commands.js +34 -2
  396. package/dist/lib/staleness/writers/hooks.js +16 -0
  397. package/dist/lib/staleness/writers/kinds.d.ts +6 -0
  398. package/dist/lib/staleness/writers/skills.js +19 -0
  399. package/dist/lib/staleness/writers/subagents.d.ts +2 -3
  400. package/dist/lib/staleness/writers/subagents.js +0 -4
  401. package/dist/lib/staleness/writers/types.d.ts +32 -0
  402. package/dist/lib/startup/command-registry.d.ts +16 -14
  403. package/dist/lib/startup/command-registry.js +26 -37
  404. package/dist/lib/startup/dev-build.d.ts +10 -0
  405. package/dist/lib/startup/dev-build.js +13 -1
  406. package/dist/lib/startup/spellcheck.d.ts +18 -0
  407. package/dist/lib/startup/spellcheck.js +45 -0
  408. package/dist/lib/state.d.ts +36 -9
  409. package/dist/lib/state.js +96 -46
  410. package/dist/lib/subagents-registry.d.ts +0 -7
  411. package/dist/lib/subagents-registry.js +38 -65
  412. package/dist/lib/subagents.d.ts +0 -33
  413. package/dist/lib/subagents.js +0 -75
  414. package/dist/lib/sync-umbrella.js +1 -1
  415. package/dist/lib/teams/agents.d.ts +41 -1
  416. package/dist/lib/teams/agents.js +187 -9
  417. package/dist/lib/teams/api.d.ts +11 -1
  418. package/dist/lib/teams/api.js +11 -2
  419. package/dist/lib/teams/delivery.d.ts +41 -0
  420. package/dist/lib/teams/delivery.js +60 -0
  421. package/dist/lib/teams/registry.d.ts +14 -0
  422. package/dist/lib/teams/registry.js +51 -1
  423. package/dist/lib/teams/remoteWorktree.d.ts +19 -0
  424. package/dist/lib/teams/remoteWorktree.js +29 -0
  425. package/dist/lib/teams/scheduler.d.ts +1 -1
  426. package/dist/lib/teams/scheduler.js +1 -1
  427. package/dist/lib/teams/worktree.d.ts +24 -0
  428. package/dist/lib/teams/worktree.js +42 -0
  429. package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
  430. package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
  431. package/dist/lib/terminal/engine.d.ts +4 -0
  432. package/dist/lib/terminal/engine.js +8 -2
  433. package/dist/lib/terminal/types.d.ts +21 -2
  434. package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
  435. package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
  436. package/dist/lib/tickets/list.d.ts +53 -0
  437. package/dist/lib/tickets/list.js +153 -0
  438. package/dist/lib/tmux/orphan-reap.d.ts +316 -0
  439. package/dist/lib/tmux/orphan-reap.js +638 -0
  440. package/dist/lib/tmux/session.d.ts +68 -3
  441. package/dist/lib/tmux/session.js +96 -5
  442. package/dist/lib/triggers/handlers.d.ts +20 -0
  443. package/dist/lib/triggers/handlers.js +16 -1
  444. package/dist/lib/triggers/webhook.js +10 -0
  445. package/dist/lib/types.d.ts +47 -35
  446. package/dist/lib/usage-fleet.d.ts +32 -0
  447. package/dist/lib/usage-fleet.js +125 -0
  448. package/dist/lib/usage-refresh.d.ts +3 -3
  449. package/dist/lib/usage-refresh.js +19 -16
  450. package/dist/lib/usage.d.ts +51 -47
  451. package/dist/lib/usage.js +145 -153
  452. package/dist/lib/version-duplicates.js +2 -2
  453. package/dist/lib/versions.d.ts +56 -13
  454. package/dist/lib/versions.js +195 -45
  455. package/dist/lib/watchdog/log.d.ts +1 -1
  456. package/dist/lib/watchdog/log.js +3 -3
  457. package/dist/lib/watchdog/rotate.d.ts +6 -6
  458. package/dist/lib/watchdog/rotate.js +6 -6
  459. package/dist/lib/workflows.d.ts +1 -1
  460. package/dist/lib/yaml-io.d.ts +47 -0
  461. package/dist/lib/yaml-io.js +55 -0
  462. package/package.json +2 -1
  463. package/scripts/install-helper.js +2 -2
  464. package/scripts/postinstall.js +23 -20
  465. package/dist/commands/defaults.d.ts +0 -7
  466. package/dist/commands/defaults.js +0 -107
  467. package/dist/commands/export.d.ts +0 -11
  468. package/dist/commands/export.js +0 -215
  469. package/dist/commands/helper.d.ts +0 -12
  470. package/dist/commands/helper.js +0 -87
  471. package/dist/commands/hosts.d.ts +0 -11
  472. package/dist/commands/hosts.js +0 -330
  473. package/dist/commands/lock.d.ts +0 -12
  474. package/dist/commands/lock.js +0 -70
  475. package/dist/commands/pull.d.ts +0 -17
  476. package/dist/commands/pull.js +0 -39
  477. package/dist/commands/push.d.ts +0 -14
  478. package/dist/commands/push.js +0 -30
  479. package/dist/commands/set.d.ts +0 -15
  480. package/dist/commands/set.js +0 -79
  481. package/dist/commands/setup-share.d.ts +0 -17
  482. package/dist/commands/setup-share.js +0 -85
  483. package/dist/commands/wallet.d.ts +0 -20
  484. package/dist/commands/wallet.js +0 -216
  485. package/dist/commands/worktree.d.ts +0 -19
  486. package/dist/commands/worktree.js +0 -272
  487. package/dist/lib/account-labels.d.ts +0 -24
  488. package/dist/lib/account-labels.js +0 -72
  489. package/dist/lib/auto-dispatch-linear.d.ts +0 -18
  490. package/dist/lib/auto-dispatch-linear.js +0 -107
  491. package/dist/lib/auto-dispatch-provider.d.ts +0 -10
  492. package/dist/lib/auto-dispatch-provider.js +0 -30
  493. package/dist/lib/auto-dispatch.d.ts +0 -93
  494. package/dist/lib/auto-dispatch.js +0 -128
  495. package/dist/lib/export.d.ts +0 -72
  496. package/dist/lib/export.js +0 -269
  497. package/dist/lib/lock.d.ts +0 -93
  498. package/dist/lib/lock.js +0 -207
  499. package/dist/lib/wallet/index.d.ts +0 -78
  500. package/dist/lib/wallet/index.js +0 -253
@@ -77,13 +77,66 @@ export declare function hasSession(name: string, socket?: string): Promise<boole
77
77
  * `replace` or `attachExisting` is set.
78
78
  */
79
79
  export declare function createSession(opts: CreateSessionOptions): Promise<SessionMeta>;
80
- /** Kill one named session. Idempotent — killing a non-existent session is a no-op. */
81
- export declare function killSession(name: string, socket?: string): Promise<boolean>;
80
+ /**
81
+ * Kill one named session. Idempotent — killing a non-existent session is a no-op.
82
+ *
83
+ * Destroying the tmux session only SIGHUPs each pane's FOREGROUND process group,
84
+ * so a helper the agent put in its own process group (an MCP server, a harness
85
+ * background daemon) survives and reparents to init. `reapOrphans` therefore
86
+ * defaults ON: the session's leftover helpers are terminated with it rather than
87
+ * waiting for the daemon's next sweep (RUSH-2521). {@link reapDeadTmuxPanes}
88
+ * passes `false` because it sweeps every session's leftovers in one pass instead
89
+ * of re-snapshotting the process table per session.
90
+ */
91
+ export declare function killSession(name: string, socket?: string, opts?: {
92
+ reapOrphans?: boolean;
93
+ }): Promise<boolean>;
82
94
  /**
83
95
  * Kill every session on the shared server AND the server itself, then prune
84
96
  * meta files. Wipes the socket so the next `new` starts from a clean slate.
85
97
  */
86
98
  export declare function killAll(socket?: string): Promise<number>;
99
+ /** Result returned by {@link reapDeadTmuxPanes}. */
100
+ export interface ReapDeadPanesResult {
101
+ /** Number of tmux sessions killed. */
102
+ reaped: number;
103
+ /** Names of reaped sessions. */
104
+ sessions: string[];
105
+ /** Human-readable lines describing each reap, for log output. */
106
+ details: string[];
107
+ /** Orphaned helper processes terminated (MCP servers, harness background daemons). */
108
+ processes: number;
109
+ /** One human-readable line per terminated helper process. */
110
+ processDetails: string[];
111
+ /** Non-fatal observations worth logging — e.g. a tier-1 sweep skipped this tick because a tmux query failed. */
112
+ warnings: string[];
113
+ }
114
+ /**
115
+ * Kill every tmux session whose panes are ALL dead (`pane_dead=1`), and
116
+ * terminate the helper processes whose owning agent has exited.
117
+ *
118
+ * Dead panes accumulate because `remain-on-exit on` is intentionally set
119
+ * per-pane so the harness can inspect exit status. Without a periodic sweep
120
+ * they pile up indefinitely — e.g. 48 of 127 sessions dead on yosemite-s0
121
+ * (RUSH-2501).
122
+ *
123
+ * Killing the pane is not enough on its own: it SIGHUPs only the pane's
124
+ * FOREGROUND process group, so an MCP server or harness background daemon that
125
+ * moved itself out of that group survives the session that spawned it and keeps
126
+ * its memory forever (RUSH-2521). The process sweep runs FIRST, while the dead
127
+ * sessions still exist, so every leftover is attributed to a named session
128
+ * rather than to a session tmux has already forgotten.
129
+ *
130
+ * Safety invariant: a session with ANY live pane (`pane_dead=0`) is never
131
+ * touched, and neither are the processes belonging to a session whose agent is
132
+ * still running or that has a client attached.
133
+ *
134
+ * `dryRun` reports both halves without killing anything.
135
+ */
136
+ export declare function reapDeadTmuxPanes(socket?: string, opts?: {
137
+ dryRun?: boolean;
138
+ pids?: number[];
139
+ }): Promise<ReapDeadPanesResult>;
87
140
  /**
88
141
  * Map every pane id (`%N`) on a socket to its `session:window.pane` attach
89
142
  * target, in one batched `tmux list-panes -a` call. `%116 -> main:2.0` is a
@@ -179,6 +232,18 @@ export declare const AGENT_HOOK_SCHEMA = 5;
179
232
  export declare function agentPaneDiedHook(sessionName: string, agentPane: string): string;
180
233
  /** Stamp a session's hook-schema marker to the current version. */
181
234
  export declare function markSessionHookSchema(name: string, socket?: string): Promise<void>;
235
+ /**
236
+ * The outcome of {@link prepareSessionForResume}. `attach` carries the pane it
237
+ * positively resolved so the caller can read that pane's exit status back after
238
+ * its attach client returns — without that, a resume-attach has no handle to ask
239
+ * tmux what happened and can only assume success (EXEC-23b).
240
+ */
241
+ export type ResumePreparation = {
242
+ decision: 'attach';
243
+ pane: string;
244
+ } | {
245
+ decision: 'create';
246
+ };
182
247
  /**
183
248
  * Decide whether a native resume may attach an existing managed tmux session.
184
249
  * Only a positively resolved, living agent pane is reusable. Metadata-less
@@ -186,7 +251,7 @@ export declare function markSessionHookSchema(name: string, socket?: string): Pr
186
251
  * tmux before the decision; a dead or unreadable session is reaped so the
187
252
  * caller can create a fresh wrapper for the resumed harness.
188
253
  */
189
- export declare function prepareSessionForResume(name: string, socket?: string): Promise<'attach' | 'create'>;
254
+ export declare function prepareSessionForResume(name: string, socket?: string): Promise<ResumePreparation>;
190
255
  /**
191
256
  * Retrofit the current guarded `pane-died` hook onto every managed `agents run`
192
257
  * session whose hook predates AGENT_HOOK_SCHEMA. Idempotent and NON-DESTRUCTIVE:
@@ -117,8 +117,18 @@ export async function createSession(opts) {
117
117
  writeSessionMeta(meta);
118
118
  return meta;
119
119
  }
120
- /** Kill one named session. Idempotent — killing a non-existent session is a no-op. */
121
- export async function killSession(name, socket) {
120
+ /**
121
+ * Kill one named session. Idempotent — killing a non-existent session is a no-op.
122
+ *
123
+ * Destroying the tmux session only SIGHUPs each pane's FOREGROUND process group,
124
+ * so a helper the agent put in its own process group (an MCP server, a harness
125
+ * background daemon) survives and reparents to init. `reapOrphans` therefore
126
+ * defaults ON: the session's leftover helpers are terminated with it rather than
127
+ * waiting for the daemon's next sweep (RUSH-2521). {@link reapDeadTmuxPanes}
128
+ * passes `false` because it sweeps every session's leftovers in one pass instead
129
+ * of re-snapshotting the process table per session.
130
+ */
131
+ export async function killSession(name, socket, opts = {}) {
122
132
  assertValidSessionName(name);
123
133
  const sock = socket ?? getDefaultSocketPath();
124
134
  const existed = await hasSession(name, sock);
@@ -137,6 +147,10 @@ export async function killSession(name, socket) {
137
147
  throw err;
138
148
  }
139
149
  }
150
+ if (opts.reapOrphans !== false) {
151
+ const { reapProcessesForTmuxSession } = await import('./orphan-reap.js');
152
+ await reapProcessesForTmuxSession(name, sock).catch(() => ({ killed: 0, details: [], candidates: [], warnings: [] }));
153
+ }
140
154
  removeSessionMeta(name);
141
155
  return true;
142
156
  }
@@ -172,6 +186,83 @@ export async function killAll(socket) {
172
186
  catch { /* may not exist */ }
173
187
  return count;
174
188
  }
189
+ /**
190
+ * Kill every tmux session whose panes are ALL dead (`pane_dead=1`), and
191
+ * terminate the helper processes whose owning agent has exited.
192
+ *
193
+ * Dead panes accumulate because `remain-on-exit on` is intentionally set
194
+ * per-pane so the harness can inspect exit status. Without a periodic sweep
195
+ * they pile up indefinitely — e.g. 48 of 127 sessions dead on yosemite-s0
196
+ * (RUSH-2501).
197
+ *
198
+ * Killing the pane is not enough on its own: it SIGHUPs only the pane's
199
+ * FOREGROUND process group, so an MCP server or harness background daemon that
200
+ * moved itself out of that group survives the session that spawned it and keeps
201
+ * its memory forever (RUSH-2521). The process sweep runs FIRST, while the dead
202
+ * sessions still exist, so every leftover is attributed to a named session
203
+ * rather than to a session tmux has already forgotten.
204
+ *
205
+ * Safety invariant: a session with ANY live pane (`pane_dead=0`) is never
206
+ * touched, and neither are the processes belonging to a session whose agent is
207
+ * still running or that has a client attached.
208
+ *
209
+ * `dryRun` reports both halves without killing anything.
210
+ */
211
+ export async function reapDeadTmuxPanes(socket, opts = {}) {
212
+ const sock = socket ?? getDefaultSocketPath();
213
+ const result = { reaped: 0, sessions: [], details: [], processes: 0, processDetails: [], warnings: [] };
214
+ // The process sweep may run with no server, but an absent session is not
215
+ // evidence that a still-live marked process is orphaned. Tier 1 only acts on
216
+ // a present pane owner confirmed dead; tier 2 independently verifies its
217
+ // declared spawner pid (RUSH-2603).
218
+ // `opts.pids` is a test-only process-table scope (see `readAgentProcesses`)
219
+ // — production callers never set it.
220
+ const { reapOrphanAgentProcesses } = await import('./orphan-reap.js');
221
+ const orphans = await reapOrphanAgentProcesses({ socket: sock, dryRun: opts.dryRun, pids: opts.pids });
222
+ result.warnings = orphans.warnings;
223
+ result.processes = opts.dryRun ? orphans.candidates.length : orphans.killed;
224
+ result.processDetails = orphans.details;
225
+ if (!fs.existsSync(sock))
226
+ return result;
227
+ const res = await runTmux({
228
+ socket: sock,
229
+ args: ['list-panes', '-a', '-F', '#{pane_id}\t#{session_name}\t#{pane_dead}'],
230
+ throwOnError: false,
231
+ });
232
+ // Nonzero exit means no server or no sessions — nothing to reap.
233
+ if (res.code !== 0)
234
+ return result;
235
+ // Group dead-flags by session name.
236
+ const sessionFlags = new Map();
237
+ for (const raw of res.stdout.split('\n')) {
238
+ const parts = raw.trim().split('\t');
239
+ const name = parts[1];
240
+ if (!name)
241
+ continue;
242
+ const dead = parts[2]?.trim() === '1';
243
+ if (!sessionFlags.has(name))
244
+ sessionFlags.set(name, []);
245
+ sessionFlags.get(name).push(dead);
246
+ }
247
+ // Kill only sessions where every pane is dead.
248
+ for (const [name, flags] of sessionFlags) {
249
+ if (flags.length === 0 || !flags.every(d => d))
250
+ continue;
251
+ try {
252
+ // The one process-table sweep above already collected this session's
253
+ // leftovers; a per-session reap here would re-snapshot `ps` N times.
254
+ if (!opts.dryRun)
255
+ await killSession(name, sock, { reapOrphans: false });
256
+ result.reaped++;
257
+ result.sessions.push(name);
258
+ result.details.push(`${name} (${flags.length} dead pane${flags.length === 1 ? '' : 's'})`);
259
+ }
260
+ catch {
261
+ // Best-effort: session may already be gone by the time we hit it.
262
+ }
263
+ }
264
+ return result;
265
+ }
175
266
  /**
176
267
  * Map every pane id (`%N`) on a socket to its `session:window.pane` attach
177
268
  * target, in one batched `tmux list-panes -a` call. `%116 -> main:2.0` is a
@@ -369,7 +460,7 @@ async function lowestPaneId(name, socket) {
369
460
  export async function prepareSessionForResume(name, socket) {
370
461
  const sock = socket ?? getDefaultSocketPath();
371
462
  if (!(await hasSession(name, sock)))
372
- return 'create';
463
+ return { decision: 'create' };
373
464
  const recordedPane = readSessionMeta(name)?.pane;
374
465
  const recordedState = recordedPane ? await paneExitStatus(recordedPane, sock) : undefined;
375
466
  const pane = recordedPane && recordedState?.found
@@ -377,9 +468,9 @@ export async function prepareSessionForResume(name, socket) {
377
468
  : await lowestPaneId(name, sock);
378
469
  const state = pane ? await paneExitStatus(pane, sock) : undefined;
379
470
  if (pane && state?.found && !state.dead)
380
- return 'attach';
471
+ return { decision: 'attach', pane };
381
472
  await killSession(name, sock);
382
- return 'create';
473
+ return { decision: 'create' };
383
474
  }
384
475
  /**
385
476
  * Retrofit the current guarded `pane-died` hook onto every managed `agents run`
@@ -28,6 +28,26 @@ export interface WebhookHandler {
28
28
  * to one platform. Omitted runs locally.
29
29
  */
30
30
  host?: string;
31
+ /**
32
+ * Named project (`agents projects`) whose base directory the dispatched
33
+ * agent/workflow run lands in — the execution anchor, mirroring a routine's
34
+ * `project`. Without it an agent handler runs at the target's `$HOME` with no
35
+ * repo checkout to edit. Applies to `run.agent`/`run.workflow` and the
36
+ * `routine:` delegate; ignored by `run.command` (put a `cd` in the command).
37
+ */
38
+ project?: string;
39
+ /**
40
+ * Portable execution directory for the dispatched run. A relative value
41
+ * resolves under `project` when set, otherwise the target's `$HOME`. Mirrors
42
+ * a routine's `cwd`.
43
+ */
44
+ cwd?: string;
45
+ /**
46
+ * Permission mode for a dispatched agent/workflow run. Defaults to `auto`
47
+ * (write with the classifier). Set `skip`/`full` for fully unattended edits,
48
+ * or `plan` for a read-only run.
49
+ */
50
+ mode?: JobConfig['mode'];
31
51
  run?: {
32
52
  agent?: AgentId;
33
53
  workflow?: string;
@@ -226,6 +226,16 @@ export function handlerMatchesWebhook(handler, webhook) {
226
226
  const current = data?.state?.name;
227
227
  if (current !== handler.stateTo)
228
228
  return false;
229
+ // RUSH-2539: `stateTo` is a TRANSITION predicate, not a current-state one.
230
+ // Linear carries the prior value of each changed field in `updatedFrom`, so a
231
+ // real state change has `updatedFrom.state` (this codebase's shape) or
232
+ // `updatedFrom.stateId` (Linear's scalar). With neither, this Issue/update
233
+ // touched something else (label, assignee, description) while the issue merely
234
+ // still sits in `stateTo` — matching there re-fires on every later edit
235
+ // (RUSH-1459 accumulated 11 duplicate plan comments).
236
+ const updatedTo = webhook.payload.updatedFrom;
237
+ if (!updatedTo || (updatedTo.state === undefined && updatedTo.stateId === undefined))
238
+ return false;
229
239
  }
230
240
  if (handler.stateFrom) {
231
241
  const updatedFrom = webhook.payload.updatedFrom;
@@ -341,7 +351,7 @@ async function executeHandlerAction(handler, webhook, context, opts) {
341
351
  if (handler.run?.agent || handler.run?.workflow) {
342
352
  const config = {
343
353
  name: handler.name,
344
- mode: 'auto',
354
+ mode: handler.mode ?? 'auto',
345
355
  effort: 'auto',
346
356
  timeout: '10m',
347
357
  enabled: true,
@@ -349,6 +359,8 @@ async function executeHandlerAction(handler, webhook, context, opts) {
349
359
  ...(handler.run.agent ? { agent: handler.run.agent } : { workflow: handler.run.workflow }),
350
360
  ...(handler.devices ? { devices: handler.devices } : {}),
351
361
  ...(handler.run.env ? { env: handler.run.env } : {}),
362
+ ...(handler.project ? { project: handler.project } : {}),
363
+ ...(handler.cwd ? { cwd: handler.cwd } : {}),
352
364
  ...(hostFields.host ? { host: hostFields.host } : {}),
353
365
  ...(hostFields.hostStrategy ? { hostStrategy: hostFields.hostStrategy } : {}),
354
366
  };
@@ -373,6 +385,9 @@ async function executeHandlerAction(handler, webhook, context, opts) {
373
385
  ...routine,
374
386
  prompt: substituteWebhookPrompt(routine.prompt, context),
375
387
  ...(handler.devices ? { devices: handler.devices } : {}),
388
+ ...(handler.project ? { project: handler.project } : {}),
389
+ ...(handler.cwd ? { cwd: handler.cwd } : {}),
390
+ ...(handler.mode ? { mode: handler.mode } : {}),
376
391
  ...(hostFields.host ? { host: hostFields.host } : {}),
377
392
  ...(hostFields.hostStrategy ? { hostStrategy: hostFields.hostStrategy } : {}),
378
393
  };
@@ -155,6 +155,16 @@ function linearTriggerMatches(trigger, webhook) {
155
155
  const current = data?.state?.name;
156
156
  if (current !== trigger.stateTo)
157
157
  return false;
158
+ // RUSH-2539: `stateTo` is a TRANSITION predicate, not a current-state one.
159
+ // Linear carries the prior value of each changed field in `updatedFrom`, so a
160
+ // real state change has `updatedFrom.state` (this codebase's shape) or
161
+ // `updatedFrom.stateId` (Linear's scalar). With neither, this Issue/update
162
+ // touched something else while the issue merely still sits in `stateTo` —
163
+ // matching there re-fires on every later edit (RUSH-1459 got 11 duplicate
164
+ // plan comments).
165
+ const updatedTo = webhook.payload.updatedFrom;
166
+ if (!updatedTo || (updatedTo.state === undefined && updatedTo.stateId === undefined))
167
+ return false;
158
168
  }
159
169
  if (trigger.stateFrom) {
160
170
  const updatedFrom = webhook.payload.updatedFrom;
@@ -381,6 +381,8 @@ export interface SkillMetadata {
381
381
  version?: string;
382
382
  license?: string;
383
383
  keywords?: string[];
384
+ /** Alternate names this skill also resolves under (frontmatter `aliases:`). */
385
+ aliases?: string[];
384
386
  }
385
387
  /** Record of how a skill was installed into an agent version. */
386
388
  export interface SkillInstallation {
@@ -487,9 +489,9 @@ export interface SkillEntry {
487
489
  /** Registry-specific trust signal (e.g. 'builtin', 'trusted', 'community'). */
488
490
  trustLevel?: string;
489
491
  /**
490
- * Lowercase hex sha256 of the skill's SKILL.md, as recorded by
491
- * `agents publish`. When present, install verifies the cloned SKILL.md
492
- * against it and aborts on mismatch.
492
+ * Lowercase hex sha256 of the skill's SKILL.md, as recorded by the registry
493
+ * index. When present, install verifies the cloned SKILL.md against it and
494
+ * aborts on mismatch.
493
495
  */
494
496
  sha256?: string;
495
497
  }
@@ -785,6 +787,10 @@ export interface ActorConfig {
785
787
  }
786
788
  /** Top-level structure of ~/.agents/.system/agents.yaml -- the CLI's persistent state. */
787
789
  export interface Meta {
790
+ /** Preferred provider account per harness. Explicit --account wins. */
791
+ accounts?: {
792
+ defaults?: Partial<Record<AgentId, string>>;
793
+ };
788
794
  agents?: Partial<Record<AgentId, string>>;
789
795
  /**
790
796
  * Per-agent preferred ISOLATED version — which copy a bare `agents run <agent>`
@@ -863,7 +869,7 @@ export interface Meta {
863
869
  * operator's config rather than an integration compiled into this CLI. When
864
870
  * this is unset/empty, an important-level post falls back to `notify.owner`
865
871
  * implicitly (RUSH-2123) — see lib/feed-broadcast.ts and
866
- * docs/06-observability.md.
872
+ * docs/observability.md.
867
873
  */
868
874
  feed?: {
869
875
  broadcast?: FeedBroadcastConfig;
@@ -931,31 +937,28 @@ export interface Meta {
931
937
  */
932
938
  browser?: Record<string, BrowserProfileConfig>;
933
939
  /**
934
- * Device-local pointer: the browser profile `agents browser start` resolves to
935
- * when no `--profile` is passed (and what an explicit `--profile default`
936
- * re-points to). Stored per-machine in `~/.agents/devices/<machine>/agents.yaml`,
937
- * NOT central — the target profile may carry machine-local logins, so the choice
938
- * must not ride `agents repo push/pull` to other machines. Unset = auto-detect an
939
- * installed Chromium-family browser (legacy behavior). Set via
940
- * `agents browser profiles set-default <name>`.
940
+ * Machine-local browser profiles written to `config:`-adjacent
941
+ * `browser:` in `~/.agents/devices/<machine>/agents.yaml`, never the synced
942
+ * file.
943
+ *
944
+ * The auto-detected `default` profile lives here because it is inherently
945
+ * machine-specific: it carries an absolute `binary:` path and a port chosen by
946
+ * scanning THIS machine. Sharing it fleet-wide meant a macOS box wrote
947
+ * `/Applications/...`, a Linux box found it unlaunchable and rewrote
948
+ * `/usr/bin/chromium-browser`, and the two flipped the tracked agents.yaml back
949
+ * and forth on every `agents browser start` — the single largest source of
950
+ * churn on that file. User-created named profiles stay central and still sync.
941
951
  */
942
- defaultBrowserProfile?: string;
952
+ deviceBrowser?: Record<string, BrowserProfileConfig>;
943
953
  /**
944
954
  * User-scope config block (`config:` in central agents.yaml). Holds the
945
955
  * user-scope keys from the device-config registry (`lib/device-config.ts`) —
946
- * today just `interactiveHost`. Syncs fleet-wide via `agents repo push/pull`.
947
- * Device-scope keys live in {@link Meta.deviceConfig} instead.
956
+ * currently `interactiveHost` and `usagePrimaryHost`. Syncs fleet-wide via
957
+ * `agents repo push/pull`.
958
+ * Device-scope keys live under `fleet.devices.<name>.config` instead (see
959
+ * {@link Meta.fleet}).
948
960
  */
949
961
  config?: Record<string, unknown>;
950
- /**
951
- * Device-scope config block, carried in memory under a distinct field so it
952
- * can never leak into the central (synced) agents.yaml: `writeMetaUnlocked`
953
- * routes it to `~/.agents/devices/<machine>/agents.yaml` under the `config:`
954
- * key (mirroring how `defaultBrowserProfile` is routed), and
955
- * `overlayMachineLocal` reads it back. Holds the device-scope keys from the
956
- * device-config registry (`maxAgents`, `schedulerEnabled`, `notes`). Per-machine by design — unset = today's behavior.
957
- */
958
- deviceConfig?: Record<string, unknown>;
959
962
  /**
960
963
  * Routine names enabled on this machine. In memory this stays distinct from
961
964
  * portable user config; state.ts writes it as top-level `routines:` in
@@ -964,7 +967,19 @@ export interface Meta {
964
967
  */
965
968
  deviceRoutines?: string[];
966
969
  /**
967
- * Agent-host registry keyed by host name (`agents hosts`). Portable user
970
+ * Machine-local operator config the device-scope keys whose `visibility` is
971
+ * `machine` (see lib/device-config.ts). state.ts writes it as `config:` inside
972
+ * `~/.agents/devices/<machine>/agents.yaml`, which is gitignored, so it never
973
+ * reaches the fleet-shared agents.yaml.
974
+ *
975
+ * These are the keys nothing off-box reads. Keeping them out of the shared file
976
+ * is both a churn fix (13 machines were writing one tracked path) and a
977
+ * security one: `browser.remote-control` gates whether OTHER machines may drive
978
+ * this box's browser, so syncing one box's opt-in to the rest was wrong.
979
+ */
980
+ deviceConfig?: Record<string, unknown>;
981
+ /**
982
+ * Agent-host registry keyed by host name (the `--host`/`--device` dispatch overlay). Portable user
968
983
  * config synced with `agents repo push/pull`. For `ssh-config` hosts this is
969
984
  * just an overlay (caps/os) — the connection details stay in ~/.ssh/config and
970
985
  * are never copied. `inline` hosts carry their own address/user.
@@ -974,10 +989,13 @@ export interface Meta {
974
989
  * Declarative fleet profile (`agents apply` / `ag apply`). Additive to the
975
990
  * schema — project `agents:` version-pins are untouched. Declares which agents
976
991
  * every device should have, which config to sync, and how login propagates.
992
+ * `fleet.devices.<name>.config` is also the central store for per-device
993
+ * operator config (`agents devices config <name>`) — the device-scope keys of
994
+ * the `lib/device-config.ts` registry.
977
995
  * Full shape in `lib/fleet/types.ts` (FleetManifest).
978
996
  */
979
997
  fleet?: import('./fleet/types.js').FleetManifest;
980
- /** `agents share` endpoint (Cloudflare R2 + Worker). Set by `agents share
998
+ /** Artifact share endpoint (Cloudflare R2 + Worker). Set by `agents artifacts
981
999
  * setup`/`join`; syncs fleet-wide via `agents repo push/pull`. The write token
982
1000
  * lives in the `share` secrets bundle, not here. */
983
1001
  share?: {
@@ -988,6 +1006,10 @@ export interface Meta {
988
1006
  domain?: string;
989
1007
  /** Cloudflare Web Analytics token injected into published HTML pages. */
990
1008
  analyticsToken?: string;
1009
+ /** sha256 of the Worker script deployed at the last provision/update, so
1010
+ * `agents artifacts share status` can tell current vs outdated vs unknown (a config
1011
+ * from before this field existed has no hash — always "unknown"). */
1012
+ templateHash?: string;
991
1013
  };
992
1014
  /**
993
1015
  * Owner/channel notification config for `agents send` / `agents notify`.
@@ -1004,16 +1026,6 @@ export interface Meta {
1004
1026
  };
1005
1027
  transports?: Record<string, string>;
1006
1028
  };
1007
- /**
1008
- * Routines daemon settings. `projects` is the allowlist of project roots
1009
- * whose `.agents/routines/*.yml` may be synced into `~/.agents/routines/`
1010
- * and fired by the daemon — never auto-discovered from a cloned public
1011
- * repo without an explicit opt-in (`agents routines enable-project`).
1012
- * Paths are stored absolute (or home-relative `~/…`).
1013
- */
1014
- routines?: {
1015
- projects?: string[];
1016
- };
1017
1029
  }
1018
1030
  /** A single delivery channel entry in humans.yaml. */
1019
1031
  export interface HumanChannel {
@@ -0,0 +1,32 @@
1
+ import { type HeadroomEntry } from './usage-refresh.js';
2
+ import { type UsageWindow } from './usage.js';
3
+ export declare const USAGE_FLEET_VERSION = 1;
4
+ export declare const USAGE_FLEET_FILE = ".usage-fleet.json";
5
+ export interface UsageFleetWindow {
6
+ key: UsageWindow['key'];
7
+ label: string;
8
+ shortLabel: string;
9
+ usedPercent: number;
10
+ resetsAt: string | null;
11
+ windowMinutes: number | null;
12
+ }
13
+ export interface UsageFleetSnapshot {
14
+ capturedAt: string | null;
15
+ plan: string | null;
16
+ windows: UsageFleetWindow[];
17
+ }
18
+ export interface UsageFleetExport {
19
+ version: 1;
20
+ publishedAt: number;
21
+ usage: Record<string, UsageFleetSnapshot>;
22
+ headroom: Record<string, HeadroomEntry>;
23
+ }
24
+ /** Publisher/subscriber role selection, kept pure for the daemon gate test. */
25
+ export declare function usageRefreshRole(primaryHost: string | undefined, self: string): 'publisher' | 'subscriber';
26
+ export declare function setUsageFleetExportPathForTest(value: string | null): string | null;
27
+ /** Build and persist the primary's safe cache envelope. */
28
+ export declare function exportUsageFleet(now?: number): UsageFleetExport;
29
+ /** Merge a primary-host envelope into the subscriber's existing caches. */
30
+ export declare function importUsageFleet(value: unknown): UsageFleetExport;
31
+ /** Fetch the primary's published envelope over the registered device SSH path. */
32
+ export declare function importUsageFleetFromHost(host: string): Promise<UsageFleetExport>;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Token-free usage replication from the configured primary host.
3
+ *
4
+ * The publisher writes only the already-derived usage windows and routing
5
+ * headroom. Credentials, OAuth material, and provider responses never enter
6
+ * this envelope. Subscribers fetch that file over the existing SSH trust path
7
+ * and merge it into their local caches.
8
+ */
9
+ import { spawn } from 'node:child_process';
10
+ import * as path from 'node:path';
11
+ import { buildSshInvocation, writeAskpassShim } from './devices/connect.js';
12
+ import { loadDevices } from './devices/registry.js';
13
+ import { atomicWriteFileSync } from './fs-atomic.js';
14
+ import { getCacheDir } from './state.js';
15
+ import { readHeadroomCache, writeHeadroomEntries, } from './usage-refresh.js';
16
+ import { readClaudeUsageCache, writeClaudeUsageCache, } from './usage.js';
17
+ export const USAGE_FLEET_VERSION = 1;
18
+ export const USAGE_FLEET_FILE = '.usage-fleet.json';
19
+ /** Publisher/subscriber role selection, kept pure for the daemon gate test. */
20
+ export function usageRefreshRole(primaryHost, self) {
21
+ return primaryHost && primaryHost !== self ? 'subscriber' : 'publisher';
22
+ }
23
+ let exportPathOverride = null;
24
+ export function setUsageFleetExportPathForTest(value) {
25
+ const previous = exportPathOverride;
26
+ exportPathOverride = value;
27
+ return previous;
28
+ }
29
+ function exportPath() {
30
+ return exportPathOverride ?? path.join(getCacheDir(), USAGE_FLEET_FILE);
31
+ }
32
+ function safeSnapshot(snapshot) {
33
+ return {
34
+ capturedAt: snapshot.capturedAt?.toISOString() ?? null,
35
+ plan: snapshot.plan ?? null,
36
+ windows: snapshot.windows.map((window) => ({
37
+ key: window.key,
38
+ label: window.label,
39
+ shortLabel: window.shortLabel,
40
+ usedPercent: window.usedPercent,
41
+ resetsAt: window.resetsAt?.toISOString() ?? null,
42
+ windowMinutes: window.windowMinutes,
43
+ })),
44
+ };
45
+ }
46
+ function liveSnapshot(snapshot) {
47
+ return {
48
+ source: 'last_seen',
49
+ sourceLabel: 'primary host cache',
50
+ capturedAt: snapshot.capturedAt ? new Date(snapshot.capturedAt) : null,
51
+ plan: snapshot.plan,
52
+ windows: snapshot.windows.map((window) => ({
53
+ ...window,
54
+ resetsAt: window.resetsAt ? new Date(window.resetsAt) : null,
55
+ })),
56
+ };
57
+ }
58
+ /** Build and persist the primary's safe cache envelope. */
59
+ export function exportUsageFleet(now = Date.now()) {
60
+ const headroom = readHeadroomCache();
61
+ const usage = {};
62
+ for (const usageKey of Object.keys(headroom)) {
63
+ const snapshot = readClaudeUsageCache(usageKey);
64
+ if (snapshot)
65
+ usage[usageKey] = safeSnapshot(snapshot);
66
+ }
67
+ const payload = {
68
+ version: USAGE_FLEET_VERSION,
69
+ publishedAt: now,
70
+ usage,
71
+ headroom,
72
+ };
73
+ atomicWriteFileSync(exportPath(), JSON.stringify(payload, null, 2), 'utf-8');
74
+ return payload;
75
+ }
76
+ function isUsageFleetExport(value) {
77
+ if (!value || typeof value !== 'object')
78
+ return false;
79
+ const payload = value;
80
+ return payload.version === USAGE_FLEET_VERSION
81
+ && typeof payload.publishedAt === 'number'
82
+ && !!payload.usage && typeof payload.usage === 'object'
83
+ && !!payload.headroom && typeof payload.headroom === 'object';
84
+ }
85
+ /** Merge a primary-host envelope into the subscriber's existing caches. */
86
+ export function importUsageFleet(value) {
87
+ if (!isUsageFleetExport(value))
88
+ throw new Error('usage fleet export has an unsupported shape');
89
+ for (const [usageKey, snapshot] of Object.entries(value.usage)) {
90
+ writeClaudeUsageCache(usageKey, liveSnapshot(snapshot));
91
+ }
92
+ writeHeadroomEntries(value.headroom);
93
+ return value;
94
+ }
95
+ /** Fetch the primary's published envelope over the registered device SSH path. */
96
+ export async function importUsageFleetFromHost(host) {
97
+ const device = (await loadDevices())[host];
98
+ if (!device)
99
+ throw new Error(`usage primary host '${host}' is not a registered device`);
100
+ const remoteRead = device.shell === 'powershell'
101
+ ? ['Get-Content', '-Raw', '(Join-Path $HOME \'.agents/.cache/.usage-fleet.json\')']
102
+ : ['cat', '"$HOME/.agents/.cache/.usage-fleet.json"'];
103
+ const { args, env } = buildSshInvocation(device, remoteRead, writeAskpassShim(), {}, { agentOnly: true });
104
+ const raw = await new Promise((resolve, reject) => {
105
+ const child = spawn('ssh', args, {
106
+ env: { ...process.env, ...env },
107
+ stdio: ['ignore', 'pipe', 'pipe'],
108
+ windowsHide: true,
109
+ });
110
+ let stdout = '';
111
+ let stderr = '';
112
+ child.stdout.setEncoding('utf-8');
113
+ child.stderr.setEncoding('utf-8');
114
+ child.stdout.on('data', (chunk) => { stdout += chunk; });
115
+ child.stderr.on('data', (chunk) => { stderr += chunk; });
116
+ child.on('error', reject);
117
+ child.on('close', (code) => {
118
+ if (code === 0)
119
+ resolve(stdout);
120
+ else
121
+ reject(new Error(`usage primary host '${host}' export failed: ${stderr.trim() || `ssh exited ${code}`}`));
122
+ });
123
+ });
124
+ return importUsageFleet(JSON.parse(raw));
125
+ }
@@ -72,7 +72,7 @@ export declare function shouldRefreshAccount(entry: HeadroomEntry | null | undef
72
72
  * projection, and record this call for the hourly cap.
73
73
  */
74
74
  export declare function nextHeadroomEntry(prev: HeadroomEntry | null | undefined, snapshot: UsageSnapshot | null, now: number): HeadroomEntry;
75
- /** An account whose credentials live on THIS host — the only ones we refresh. */
75
+ /** An account whose credentials live on the publisher host. */
76
76
  export interface LocalUsageAccount {
77
77
  usageKey: string;
78
78
  agentId: AgentId;
@@ -80,12 +80,12 @@ export interface LocalUsageAccount {
80
80
  fetch: () => Promise<UsageInfo>;
81
81
  }
82
82
  /**
83
- * Enumerate the network-usage accounts whose credentials live on THIS host — one
83
+ * Enumerate the usage accounts whose credentials live on THIS host — one
84
84
  * per unique usage key, deduped to the most-recently-active version (the same
85
85
  * canonicalization `getUsageInfoByIdentity` uses). Each carries a closure that
86
86
  * live-fetches its usage. This is the daemon's `listAccounts`; because it only
87
87
  * ever lists local, signed-in accounts, each host is the sole writer for its own
88
- * accounts' caches no cross-host coordination.
88
+ * accounts' caches. `runUsageRefreshTick` calls this only on the fleet publisher.
89
89
  */
90
90
  export declare function buildLocalUsageAccounts(): Promise<LocalUsageAccount[]>;
91
91
  /** Injectable side effects, so `runUsageRefresh` is drivable without the daemon. */