@phnx-labs/agents-cli 1.22.39 → 1.22.41

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 (569) hide show
  1. package/CHANGELOG.md +316 -0
  2. package/README.md +116 -70
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +45 -27
  5. package/dist/browser.js +2 -2
  6. package/dist/cli/command-registry.d.ts +135 -0
  7. package/dist/cli/command-registry.js +259 -0
  8. package/dist/commands/accounts.d.ts +20 -0
  9. package/dist/commands/accounts.js +186 -25
  10. package/dist/commands/apply.d.ts +3 -3
  11. package/dist/commands/apply.js +20 -63
  12. package/dist/commands/browser.d.ts +15 -0
  13. package/dist/commands/browser.js +318 -131
  14. package/dist/commands/budget.d.ts +7 -6
  15. package/dist/commands/budget.js +8 -7
  16. package/dist/commands/cloud.js +17 -20
  17. package/dist/commands/commands.js +1 -1
  18. package/dist/commands/computer-actions.d.ts +1 -35
  19. package/dist/commands/computer-actions.js +13 -181
  20. package/dist/commands/computer.d.ts +28 -3
  21. package/dist/commands/computer.js +93 -65
  22. package/dist/commands/config.js +16 -3
  23. package/dist/commands/cost.d.ts +2 -1
  24. package/dist/commands/cost.js +7 -6
  25. package/dist/commands/daemon.js +151 -4
  26. package/dist/commands/detach.js +1 -1
  27. package/dist/commands/doctor.js +19 -11
  28. package/dist/commands/events.js +1 -1
  29. package/dist/commands/exec.d.ts +2 -50
  30. package/dist/commands/exec.js +160 -215
  31. package/dist/commands/factory.js +1 -1
  32. package/dist/commands/feed.d.ts +2 -2
  33. package/dist/commands/feed.js +12 -23
  34. package/dist/commands/fleet-capture.js +25 -27
  35. package/dist/commands/focus.d.ts +2 -3
  36. package/dist/commands/focus.js +34 -9
  37. package/dist/commands/go.js +15 -5
  38. package/dist/commands/harness.d.ts +3 -3
  39. package/dist/commands/harness.js +53 -5
  40. package/dist/commands/hooks.js +1 -1
  41. package/dist/commands/import.js +2 -2
  42. package/dist/commands/insights.d.ts +5 -5
  43. package/dist/commands/insights.js +15 -9
  44. package/dist/commands/inspect.d.ts +2 -3
  45. package/dist/commands/inspect.js +4 -16
  46. package/dist/commands/logs.d.ts +2 -2
  47. package/dist/commands/logs.js +9 -9
  48. package/dist/commands/mcp.js +2 -2
  49. package/dist/commands/memory.js +1 -1
  50. package/dist/commands/message.d.ts +1 -1
  51. package/dist/commands/message.js +7 -7
  52. package/dist/commands/mine.d.ts +11 -8
  53. package/dist/commands/mine.js +11 -10
  54. package/dist/commands/models.js +1 -1
  55. package/dist/commands/modes.js +1 -1
  56. package/dist/commands/monitors.js +23 -6
  57. package/dist/commands/output.d.ts +7 -6
  58. package/dist/commands/output.js +11 -10
  59. package/dist/commands/packages.js +4 -4
  60. package/dist/commands/perf.js +1 -1
  61. package/dist/commands/permissions.js +49 -32
  62. package/dist/commands/plugins.d.ts +1 -1
  63. package/dist/commands/plugins.js +3 -3
  64. package/dist/commands/profiles.d.ts +6 -9
  65. package/dist/commands/profiles.js +8 -321
  66. package/dist/commands/projects.d.ts +9 -0
  67. package/dist/commands/projects.js +140 -8
  68. package/dist/commands/prune.js +5 -13
  69. package/dist/commands/refresh-rules.js +1 -1
  70. package/dist/commands/repo.d.ts +5 -7
  71. package/dist/commands/repo.js +34 -18
  72. package/dist/commands/resource-view.js +4 -0
  73. package/dist/commands/resume.d.ts +11 -7
  74. package/dist/commands/resume.js +82 -98
  75. package/dist/commands/route.d.ts +12 -0
  76. package/dist/commands/route.js +261 -0
  77. package/dist/commands/routines.d.ts +2 -3
  78. package/dist/commands/routines.js +120 -145
  79. package/dist/commands/rules.js +1 -1
  80. package/dist/commands/run-account-picker.d.ts +1 -1
  81. package/dist/commands/run-account-picker.js +2 -2
  82. package/dist/commands/run-broadcast.d.ts +21 -0
  83. package/dist/commands/run-broadcast.js +89 -0
  84. package/dist/commands/run-cloud.js +1 -1
  85. package/dist/commands/secrets-vault.d.ts +10 -0
  86. package/dist/commands/{login.js → secrets-vault.js} +34 -14
  87. package/dist/commands/secrets.d.ts +4 -4
  88. package/dist/commands/secrets.js +158 -159
  89. package/dist/commands/sessions-backfill.js +1 -1
  90. package/dist/commands/sessions-browser.d.ts +1 -1
  91. package/dist/commands/sessions-browser.js +9 -5
  92. package/dist/commands/sessions-export.js +2 -2
  93. package/dist/commands/sessions-inject.js +4 -5
  94. package/dist/commands/sessions-migrate.js +16 -27
  95. package/dist/commands/sessions-picker.d.ts +14 -0
  96. package/dist/commands/sessions-picker.js +209 -13
  97. package/dist/commands/sessions-resume.d.ts +7 -3
  98. package/dist/commands/sessions-resume.js +81 -33
  99. package/dist/commands/sessions-share.d.ts +25 -0
  100. package/dist/commands/sessions-share.js +166 -0
  101. package/dist/commands/sessions-stats.js +2 -2
  102. package/dist/commands/sessions.d.ts +75 -68
  103. package/dist/commands/sessions.js +144 -180
  104. package/dist/commands/setup-browser.js +1 -1
  105. package/dist/commands/setup-computer.js +1 -1
  106. package/dist/commands/setup-fleet.js +3 -0
  107. package/dist/commands/setup-mine.d.ts +7 -3
  108. package/dist/commands/setup-mine.js +13 -8
  109. package/dist/commands/setup-preferences.js +2 -2
  110. package/dist/commands/setup-secrets.js +2 -2
  111. package/dist/commands/setup-watchdog.js +2 -2
  112. package/dist/commands/setup.js +7 -2
  113. package/dist/commands/share.d.ts +65 -0
  114. package/dist/commands/share.js +253 -20
  115. package/dist/commands/skills.js +3 -3
  116. package/dist/commands/snapshot.d.ts +2 -2
  117. package/dist/commands/snapshot.js +9 -9
  118. package/dist/commands/ssh.d.ts +1 -1
  119. package/dist/commands/ssh.js +186 -64
  120. package/dist/commands/subagents.js +1 -1
  121. package/dist/commands/sync.js +24 -9
  122. package/dist/commands/teams.d.ts +2 -2
  123. package/dist/commands/teams.js +9 -18
  124. package/dist/commands/tmux.js +5 -1
  125. package/dist/commands/trash.js +1 -1
  126. package/dist/commands/usage.d.ts +1 -1
  127. package/dist/commands/usage.js +2 -2
  128. package/dist/commands/utils.d.ts +4 -14
  129. package/dist/commands/utils.js +6 -27
  130. package/dist/commands/versions.js +2 -2
  131. package/dist/commands/view.d.ts +9 -77
  132. package/dist/commands/view.js +23 -8
  133. package/dist/commands/watchdog.js +1 -1
  134. package/dist/commands/webhook.js +25 -34
  135. package/dist/commands/workflows.js +1 -1
  136. package/dist/index.js +1 -1
  137. package/dist/lib/account-capabilities.d.ts +39 -0
  138. package/dist/lib/account-capabilities.js +59 -0
  139. package/dist/lib/account-catalog.js +1 -1
  140. package/dist/lib/account-registry.d.ts +66 -0
  141. package/dist/lib/account-registry.js +151 -5
  142. package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
  143. package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
  144. package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
  145. package/dist/lib/{usage.js → accounting/usage.js} +11 -11
  146. package/dist/lib/agent-spec/provider.js +1 -1
  147. package/dist/lib/agents.d.ts +72 -4
  148. package/dist/lib/agents.js +231 -199
  149. package/dist/lib/answer-router.d.ts +2 -2
  150. package/dist/lib/ask-classifier.d.ts +1 -1
  151. package/dist/lib/ask-classifier.js +1 -1
  152. package/dist/lib/audit/log.js +1 -1
  153. package/dist/lib/auth-health.d.ts +1 -1
  154. package/dist/lib/auth-health.js +2 -2
  155. package/dist/lib/brand.js +1 -1
  156. package/dist/lib/browser/caller-identity.d.ts +28 -0
  157. package/dist/lib/browser/caller-identity.js +156 -0
  158. package/dist/lib/browser/cdp.js +5 -1
  159. package/dist/lib/browser/chrome.js +12 -1
  160. package/dist/lib/browser/drivers/ssh.d.ts +1 -1
  161. package/dist/lib/browser/drivers/ssh.js +3 -2
  162. package/dist/lib/browser/hygiene.d.ts +10 -2
  163. package/dist/lib/browser/hygiene.js +7 -4
  164. package/dist/lib/browser/ipc.d.ts +22 -2
  165. package/dist/lib/browser/ipc.js +185 -21
  166. package/dist/lib/browser/profiles.d.ts +69 -5
  167. package/dist/lib/browser/profiles.js +135 -13
  168. package/dist/lib/browser/remote-control.d.ts +2 -2
  169. package/dist/lib/browser/remote-control.js +3 -3
  170. package/dist/lib/browser/runtime-state.d.ts +79 -0
  171. package/dist/lib/browser/runtime-state.js +119 -1
  172. package/dist/lib/browser/service.d.ts +137 -2
  173. package/dist/lib/browser/service.js +492 -54
  174. package/dist/lib/browser/sessions-list.d.ts +2 -1
  175. package/dist/lib/browser/sessions-list.js +4 -13
  176. package/dist/lib/browser/types.d.ts +19 -1
  177. package/dist/lib/budget/config.js +1 -1
  178. package/dist/lib/budget/ledger.js +1 -1
  179. package/dist/lib/byok-usage.js +1 -1
  180. package/dist/lib/capabilities.js +3 -15
  181. package/dist/lib/catchup.d.ts +1 -1
  182. package/dist/lib/catchup.js +1 -1
  183. package/dist/lib/cloud/dispatch.js +1 -1
  184. package/dist/lib/cloud/host.d.ts +3 -3
  185. package/dist/lib/cloud/host.js +5 -5
  186. package/dist/lib/cloud/rush.d.ts +9 -21
  187. package/dist/lib/cloud/rush.js +33 -120
  188. package/dist/lib/cloud/types.d.ts +2 -2
  189. package/dist/lib/commands.js +3 -13
  190. package/dist/lib/computer/actions.d.ts +36 -0
  191. package/dist/lib/computer/actions.js +162 -0
  192. package/dist/lib/computer/dispatch.d.ts +2 -2
  193. package/dist/lib/computer/dispatch.js +2 -2
  194. package/dist/lib/computer/sessions-list.d.ts +3 -3
  195. package/dist/lib/computer/sessions-list.js +2 -2
  196. package/dist/lib/config-keys.d.ts +2 -1
  197. package/dist/lib/config-keys.js +8 -4
  198. package/dist/lib/config-machine-keys.d.ts +4 -2
  199. package/dist/lib/config-machine-keys.js +5 -2
  200. package/dist/lib/crabbox/lease.js +9 -1
  201. package/dist/lib/crabbox/runtimes.d.ts +32 -7
  202. package/dist/lib/crabbox/runtimes.js +72 -30
  203. package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
  204. package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
  205. package/dist/lib/daemon-services.d.ts +1 -1
  206. package/dist/lib/daemon-services.js +5 -0
  207. package/dist/lib/daemon-ticks.d.ts +25 -0
  208. package/dist/lib/daemon-ticks.js +31 -1
  209. package/dist/lib/daemon-webhooks.d.ts +104 -0
  210. package/dist/lib/daemon-webhooks.js +215 -0
  211. package/dist/lib/device-config.d.ts +85 -75
  212. package/dist/lib/device-config.js +280 -142
  213. package/dist/lib/devices/config-migration.d.ts +29 -34
  214. package/dist/lib/devices/config-migration.js +209 -130
  215. package/dist/lib/devices/connect.d.ts +2 -2
  216. package/dist/lib/devices/connect.js +2 -2
  217. package/dist/lib/devices/discovery-policy.d.ts +30 -0
  218. package/dist/lib/devices/discovery-policy.js +101 -0
  219. package/dist/lib/devices/fleet-inventory.js +8 -3
  220. package/dist/lib/devices/harness-inventory.d.ts +1 -1
  221. package/dist/lib/devices/harness-inventory.js +12 -3
  222. package/dist/lib/devices/known-hosts.js +1 -1
  223. package/dist/lib/devices/pool.d.ts +10 -5
  224. package/dist/lib/devices/pool.js +5 -7
  225. package/dist/lib/devices/registry.d.ts +1 -1
  226. package/dist/lib/devices/registry.js +1 -1
  227. package/dist/lib/devices/resolve-profile.d.ts +2 -2
  228. package/dist/lib/devices/resolve-profile.js +2 -2
  229. package/dist/lib/devices/resolve-target.d.ts +1 -1
  230. package/dist/lib/devices/resolve-target.js +4 -4
  231. package/dist/lib/devices/self-host.d.ts +1 -1
  232. package/dist/lib/devices/self-host.js +3 -3
  233. package/dist/lib/devices/ssh-config.d.ts +1 -1
  234. package/dist/lib/devices/stats-cache.js +1 -1
  235. package/dist/lib/devices/sync.d.ts +3 -1
  236. package/dist/lib/devices/sync.js +3 -3
  237. package/dist/lib/doctor-diff.js +4 -4
  238. package/dist/lib/drift-sync.js +2 -2
  239. package/dist/lib/drift.js +2 -2
  240. package/dist/lib/event-families.d.ts +1 -1
  241. package/dist/lib/event-stream.d.ts +1 -1
  242. package/dist/lib/event-stream.js +2 -2
  243. package/dist/lib/events-ingest.js +2 -2
  244. package/dist/lib/exec.d.ts +4 -4
  245. package/dist/lib/exec.js +11 -11
  246. package/dist/lib/factory/snapshot.d.ts +1 -1
  247. package/dist/lib/factory/snapshot.js +1 -1
  248. package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
  249. package/dist/lib/{activity.js → feed/activity.js} +7 -7
  250. package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
  251. package/dist/lib/{events.js → feed/events.js} +5 -5
  252. package/dist/lib/{feed.js → feed/feed.js} +4 -4
  253. package/dist/lib/feed-outcome.d.ts +1 -1
  254. package/dist/lib/feed-policy.d.ts +1 -1
  255. package/dist/lib/feed-policy.js +1 -1
  256. package/dist/lib/feed-post.d.ts +1 -1
  257. package/dist/lib/feed-post.js +1 -1
  258. package/dist/lib/feed-ranking.d.ts +1 -1
  259. package/dist/lib/fleet/apply.d.ts +0 -6
  260. package/dist/lib/fleet/apply.js +18 -45
  261. package/dist/lib/fleet/auth-sync.d.ts +16 -35
  262. package/dist/lib/fleet/auth-sync.js +16 -75
  263. package/dist/lib/fleet/capture.js +8 -6
  264. package/dist/lib/fleet/manifest.js +23 -11
  265. package/dist/lib/fleet/types.d.ts +19 -7
  266. package/dist/lib/fleet-cache.d.ts +1 -1
  267. package/dist/lib/fleet-cache.js +1 -1
  268. package/dist/lib/format.d.ts +19 -0
  269. package/dist/lib/format.js +44 -1
  270. package/dist/lib/friction-heuristics.d.ts +1 -1
  271. package/dist/lib/git.d.ts +24 -1
  272. package/dist/lib/git.js +82 -38
  273. package/dist/lib/heal.js +3 -3
  274. package/dist/lib/hooks.d.ts +18 -0
  275. package/dist/lib/hooks.js +113 -1
  276. package/dist/lib/hosts/credentials.d.ts +30 -10
  277. package/dist/lib/hosts/credentials.js +38 -26
  278. package/dist/lib/hosts/dispatch.d.ts +3 -3
  279. package/dist/lib/hosts/dispatch.js +3 -3
  280. package/dist/lib/hosts/option.d.ts +3 -3
  281. package/dist/lib/hosts/option.js +7 -8
  282. package/dist/lib/hosts/passthrough.d.ts +17 -17
  283. package/dist/lib/hosts/passthrough.js +67 -50
  284. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  285. package/dist/lib/hosts/providers/devices.js +1 -1
  286. package/dist/lib/hosts/reconcile.d.ts +1 -1
  287. package/dist/lib/hosts/reconcile.js +1 -1
  288. package/dist/lib/hosts/reconnect.d.ts +1 -1
  289. package/dist/lib/hosts/reconnect.js +2 -2
  290. package/dist/lib/hosts/registry.d.ts +5 -5
  291. package/dist/lib/hosts/registry.js +5 -5
  292. package/dist/lib/hosts/remote-cmd.d.ts +10 -10
  293. package/dist/lib/hosts/remote-cmd.js +25 -18
  294. package/dist/lib/hosts/remote-os.d.ts +4 -4
  295. package/dist/lib/hosts/remote-os.js +4 -4
  296. package/dist/lib/hosts/remote-session-id.d.ts +2 -2
  297. package/dist/lib/hosts/remote-session-id.js +2 -2
  298. package/dist/lib/hosts/routing-flag.d.ts +3 -3
  299. package/dist/lib/hosts/routing-flag.js +4 -5
  300. package/dist/lib/hosts/run-target.d.ts +4 -4
  301. package/dist/lib/hosts/run-target.js +4 -4
  302. package/dist/lib/hosts/session-index.d.ts +2 -2
  303. package/dist/lib/hosts/session-index.js +1 -1
  304. package/dist/lib/hosts/session-marker.d.ts +1 -1
  305. package/dist/lib/hosts/session-marker.js +1 -1
  306. package/dist/lib/hosts/types.d.ts +2 -2
  307. package/dist/lib/hosts/types.js +2 -2
  308. package/dist/lib/import.js +1 -1
  309. package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +13 -1
  310. package/dist/lib/{migrate.js → installations/migrate.js} +160 -40
  311. package/dist/lib/installations/resolve.js +1 -1
  312. package/dist/lib/installations/strategies.js +1 -1
  313. package/dist/lib/installations/update.js +2 -2
  314. package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
  315. package/dist/lib/{versions.js → installations/versions.js} +51 -31
  316. package/dist/lib/isolation-boundary-report.js +1 -1
  317. package/dist/lib/linear-projects.d.ts +28 -0
  318. package/dist/lib/linear-projects.js +30 -0
  319. package/dist/lib/loop.js +1 -1
  320. package/dist/lib/mailbox-gc.js +1 -1
  321. package/dist/lib/mailbox.js +1 -1
  322. package/dist/lib/mcp-registry.d.ts +62 -0
  323. package/dist/lib/mcp-registry.js +177 -0
  324. package/dist/lib/mcp.d.ts +5 -0
  325. package/dist/lib/mcp.js +124 -307
  326. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  327. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  328. package/dist/lib/menubar/install-menubar.d.ts +7 -0
  329. package/dist/lib/menubar/install-menubar.js +24 -6
  330. package/dist/lib/menubar/snapshot.d.ts +7 -0
  331. package/dist/lib/menubar/snapshot.js +8 -6
  332. package/dist/lib/models.js +1 -1
  333. package/dist/lib/monitors/config.d.ts +12 -0
  334. package/dist/lib/monitors/config.js +3 -0
  335. package/dist/lib/monitors/dispatch.js +26 -5
  336. package/dist/lib/monitors/engine.js +10 -0
  337. package/dist/lib/monitors/sources/webhook.d.ts +1 -1
  338. package/dist/lib/monitors/state.d.ts +42 -0
  339. package/dist/lib/monitors/state.js +30 -0
  340. package/dist/lib/notify.d.ts +1 -1
  341. package/dist/lib/observe-aliases.d.ts +3 -3
  342. package/dist/lib/observe-aliases.js +3 -21
  343. package/dist/lib/output/git-output.js +2 -2
  344. package/dist/lib/overdue.d.ts +1 -1
  345. package/dist/lib/overdue.js +1 -1
  346. package/dist/lib/permissions-registry.d.ts +65 -0
  347. package/dist/lib/permissions-registry.js +599 -0
  348. package/dist/lib/permissions.d.ts +24 -13
  349. package/dist/lib/permissions.js +52 -271
  350. package/dist/lib/picker.d.ts +16 -0
  351. package/dist/lib/picker.js +33 -3
  352. package/dist/lib/placement.d.ts +1 -1
  353. package/dist/lib/placement.js +4 -4
  354. package/dist/lib/platform/paths.d.ts +8 -0
  355. package/dist/lib/platform/paths.js +11 -0
  356. package/dist/lib/platform/posixpath.js +3 -3
  357. package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
  358. package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
  359. package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
  360. package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
  361. package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
  362. package/dist/lib/{skills.js → plugins/skills.js} +7 -7
  363. package/dist/lib/pricing/cost.d.ts +1 -1
  364. package/dist/lib/pricing/cost.js +1 -1
  365. package/dist/lib/profiles-presets.d.ts +1 -1
  366. package/dist/lib/profiles-presets.js +1 -1
  367. package/dist/lib/profiles.js +2 -2
  368. package/dist/lib/project-import.js +4 -1
  369. package/dist/lib/project-key.js +7 -0
  370. package/dist/lib/project-launch.d.ts +1 -1
  371. package/dist/lib/project-launch.js +3 -3
  372. package/dist/lib/project-pull.d.ts +128 -0
  373. package/dist/lib/project-pull.js +384 -0
  374. package/dist/lib/project-root.d.ts +2 -2
  375. package/dist/lib/project-root.js +2 -2
  376. package/dist/lib/project-status.js +1 -1
  377. package/dist/lib/projects.d.ts +23 -0
  378. package/dist/lib/projects.js +29 -6
  379. package/dist/lib/refresh.d.ts +12 -1
  380. package/dist/lib/refresh.js +16 -9
  381. package/dist/lib/registry.d.ts +9 -2
  382. package/dist/lib/registry.js +11 -4
  383. package/dist/lib/resources.d.ts +2 -2
  384. package/dist/lib/resources.js +2 -2
  385. package/dist/lib/routers.d.ts +81 -0
  386. package/dist/lib/routers.js +153 -0
  387. package/dist/lib/routine-context.d.ts +1 -0
  388. package/dist/lib/routine-context.js +7 -2
  389. package/dist/lib/routine-notify-owner.d.ts +1 -1
  390. package/dist/lib/routine-notify.d.ts +1 -1
  391. package/dist/lib/routine-notify.js +1 -1
  392. package/dist/lib/routine-process-cleanup.d.ts +1 -1
  393. package/dist/lib/routine-readiness.d.ts +1 -1
  394. package/dist/lib/routine-readiness.js +11 -4
  395. package/dist/lib/routines-placement.d.ts +12 -2
  396. package/dist/lib/routines-placement.js +20 -3
  397. package/dist/lib/routines-project.d.ts +1 -1
  398. package/dist/lib/routines-project.js +1 -1
  399. package/dist/lib/rules/compile.d.ts +1 -0
  400. package/dist/lib/rules/compile.js +10 -2
  401. package/dist/lib/rules/rules.js +1 -1
  402. package/dist/lib/run-notify.js +1 -1
  403. package/dist/lib/runner.d.ts +80 -2
  404. package/dist/lib/runner.js +261 -67
  405. package/dist/lib/sandbox.d.ts +1 -1
  406. package/dist/lib/scheduler.d.ts +1 -1
  407. package/dist/lib/scheduler.js +1 -1
  408. package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
  409. package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
  410. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  411. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  412. package/dist/lib/secrets/agent.js +2 -2
  413. package/dist/lib/secrets/audit.js +1 -1
  414. package/dist/lib/secrets/bundles.d.ts +4 -4
  415. package/dist/lib/secrets/bundles.js +8 -8
  416. package/dist/lib/secrets/headless.d.ts +16 -0
  417. package/dist/lib/secrets/headless.js +21 -0
  418. package/dist/lib/secrets/index.js +1 -1
  419. package/dist/lib/secrets/list-filter.d.ts +1 -1
  420. package/dist/lib/secrets/list-filter.js +2 -2
  421. package/dist/lib/secrets/push.d.ts +1 -1
  422. package/dist/lib/secrets/push.js +10 -12
  423. package/dist/lib/secrets/remote.d.ts +21 -17
  424. package/dist/lib/secrets/remote.js +39 -23
  425. package/dist/lib/secrets/vault.js +2 -2
  426. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  427. package/dist/lib/self-heal/checks/shims.js +1 -1
  428. package/dist/lib/self-update.d.ts +39 -4
  429. package/dist/lib/self-update.js +78 -25
  430. package/dist/lib/serve/control.d.ts +1 -1
  431. package/dist/lib/serve/control.js +4 -4
  432. package/dist/lib/serve/data.d.ts +1 -1
  433. package/dist/lib/serve/data.js +1 -1
  434. package/dist/lib/serve/stream.js +1 -1
  435. package/dist/lib/service-manifest.d.ts +29 -0
  436. package/dist/lib/service-manifest.js +80 -0
  437. package/dist/lib/session/active.d.ts +59 -7
  438. package/dist/lib/session/active.js +158 -17
  439. package/dist/lib/session/bash-command.js +1 -1
  440. package/dist/lib/session/claude-accounts.d.ts +1 -1
  441. package/dist/lib/session/claude-accounts.js +1 -1
  442. package/dist/lib/session/db.d.ts +10 -2
  443. package/dist/lib/session/db.js +25 -6
  444. package/dist/lib/session/discover.d.ts +77 -0
  445. package/dist/lib/session/discover.js +130 -25
  446. package/dist/lib/session/fork.js +1 -1
  447. package/dist/lib/session/inject.d.ts +1 -1
  448. package/dist/lib/session/insights.d.ts +1 -1
  449. package/dist/lib/session/insights.js +1 -1
  450. package/dist/lib/session/live-metadata.d.ts +15 -0
  451. package/dist/lib/session/live-metadata.js +76 -0
  452. package/dist/lib/session/migrate-targets.d.ts +1 -1
  453. package/dist/lib/session/recovery.d.ts +1 -1
  454. package/dist/lib/session/recovery.js +2 -2
  455. package/dist/lib/session/remote-active.d.ts +10 -1
  456. package/dist/lib/session/remote-active.js +8 -3
  457. package/dist/lib/session/remote-bundle.d.ts +1 -1
  458. package/dist/lib/session/remote-bundle.js +2 -2
  459. package/dist/lib/session/remote-list.d.ts +15 -4
  460. package/dist/lib/session/remote-list.js +40 -3
  461. package/dist/lib/session/remote.d.ts +5 -5
  462. package/dist/lib/session/remote.js +13 -17
  463. package/dist/lib/session/resume-command.d.ts +3 -2
  464. package/dist/lib/session/resume-command.js +4 -3
  465. package/dist/lib/session/resume-owner.d.ts +3 -3
  466. package/dist/lib/session/resume-owner.js +3 -3
  467. package/dist/lib/session/session-cache.d.ts +12 -0
  468. package/dist/lib/session/session-cache.js +2 -0
  469. package/dist/lib/session/share-html.d.ts +55 -0
  470. package/dist/lib/session/share-html.js +319 -0
  471. package/dist/lib/session/shell-programs.js +1 -1
  472. package/dist/lib/session/state.js +5 -5
  473. package/dist/lib/session/team-filter.d.ts +1 -1
  474. package/dist/lib/session/team-filter.js +1 -1
  475. package/dist/lib/session/types.d.ts +3 -3
  476. package/dist/lib/session/width.d.ts +2 -2
  477. package/dist/lib/session/width.js +2 -2
  478. package/dist/lib/settings-manifest.d.ts +2 -0
  479. package/dist/lib/settings-manifest.js +81 -3
  480. package/dist/lib/share/delete.d.ts +21 -0
  481. package/dist/lib/share/delete.js +42 -0
  482. package/dist/lib/share/publish.d.ts +120 -0
  483. package/dist/lib/share/publish.js +243 -18
  484. package/dist/lib/share/worker-template.js +172 -9
  485. package/dist/lib/shims.js +35 -7
  486. package/dist/lib/smart-launch.d.ts +4 -4
  487. package/dist/lib/smart-launch.js +6 -4
  488. package/dist/lib/snapshot.d.ts +4 -4
  489. package/dist/lib/snapshot.js +2 -2
  490. package/dist/lib/ssh-exec.d.ts +1 -1
  491. package/dist/lib/ssh-exec.js +2 -2
  492. package/dist/lib/ssh-tunnel.d.ts +10 -10
  493. package/dist/lib/ssh-tunnel.js +12 -12
  494. package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
  495. package/dist/lib/staleness/checkers/hooks.js +1 -1
  496. package/dist/lib/staleness/detectors/permissions.js +0 -23
  497. package/dist/lib/staleness/detectors/plugins.js +1 -1
  498. package/dist/lib/staleness/fingerprint.js +1 -1
  499. package/dist/lib/staleness/layers.d.ts +1 -1
  500. package/dist/lib/staleness/layers.js +1 -1
  501. package/dist/lib/staleness/writers/mcp.js +3 -1
  502. package/dist/lib/staleness/writers/plugins.js +1 -1
  503. package/dist/lib/staleness/writers/types.d.ts +8 -0
  504. package/dist/lib/startup/command-registry.d.ts +6 -147
  505. package/dist/lib/startup/command-registry.js +40 -303
  506. package/dist/lib/startup/dev-build.d.ts +0 -21
  507. package/dist/lib/startup/dev-build.js +29 -8
  508. package/dist/lib/state.d.ts +19 -1
  509. package/dist/lib/state.js +120 -51
  510. package/dist/lib/subagents.js +1 -1
  511. package/dist/lib/sync-status.js +1 -1
  512. package/dist/lib/sync-umbrella.d.ts +6 -1
  513. package/dist/lib/sync-umbrella.js +9 -4
  514. package/dist/lib/teams/agents.js +2 -2
  515. package/dist/lib/teams/registry.js +1 -1
  516. package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
  517. package/dist/lib/terminal/engine.js +1 -1
  518. package/dist/lib/terminal/index.d.ts +4 -0
  519. package/dist/lib/terminal/index.js +3 -0
  520. package/dist/lib/terminal/inject.d.ts +2 -2
  521. package/dist/lib/terminal/inject.js +2 -2
  522. package/dist/lib/terminal/transport.js +1 -1
  523. package/dist/lib/tmux/index.d.ts +1 -1
  524. package/dist/lib/tmux/index.js +1 -1
  525. package/dist/lib/tmux/session.d.ts +21 -5
  526. package/dist/lib/tmux/session.js +76 -22
  527. package/dist/lib/triggers/handlers.d.ts +38 -3
  528. package/dist/lib/triggers/handlers.js +58 -7
  529. package/dist/lib/triggers/webhook.d.ts +113 -5
  530. package/dist/lib/triggers/webhook.js +223 -44
  531. package/dist/lib/types.d.ts +19 -7
  532. package/dist/lib/usage-fleet.d.ts +1 -1
  533. package/dist/lib/usage-fleet.js +1 -1
  534. package/dist/lib/usage-refresh.d.ts +1 -1
  535. package/dist/lib/usage-refresh.js +3 -3
  536. package/dist/lib/version-duplicates.js +2 -2
  537. package/dist/lib/view-types.d.ts +56 -0
  538. package/dist/lib/view-types.js +1 -0
  539. package/dist/lib/watchdog/rotate.d.ts +2 -2
  540. package/dist/lib/watchdog/rotate.js +3 -3
  541. package/dist/lib/watchdog/runner.d.ts +2 -3
  542. package/dist/lib/watchdog/runner.js +3 -4
  543. package/dist/lib/workflows.js +1 -1
  544. package/dist/lib/wrap.d.ts +33 -0
  545. package/dist/lib/wrap.js +70 -0
  546. package/package.json +2 -1
  547. package/scripts/postinstall.js +2 -2
  548. package/dist/commands/bench.d.ts +0 -2
  549. package/dist/commands/bench.js +0 -101
  550. package/dist/commands/cp.d.ts +0 -84
  551. package/dist/commands/cp.js +0 -231
  552. package/dist/commands/login.d.ts +0 -2
  553. package/dist/lib/resources/commands.d.ts +0 -46
  554. package/dist/lib/resources/commands.js +0 -211
  555. package/dist/lib/resources/hooks.d.ts +0 -16
  556. package/dist/lib/resources/hooks.js +0 -153
  557. package/dist/lib/resources/mcp.d.ts +0 -34
  558. package/dist/lib/resources/mcp.js +0 -250
  559. package/dist/lib/resources/memory.d.ts +0 -15
  560. package/dist/lib/resources/memory.js +0 -46
  561. package/dist/lib/resources/permissions.d.ts +0 -13
  562. package/dist/lib/resources/permissions.js +0 -202
  563. package/dist/lib/resources/rules.d.ts +0 -46
  564. package/dist/lib/resources/rules.js +0 -179
  565. package/dist/lib/resources/skills.d.ts +0 -37
  566. package/dist/lib/resources/skills.js +0 -247
  567. package/dist/lib/resources/subagents.d.ts +0 -46
  568. package/dist/lib/resources/subagents.js +0 -202
  569. /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
@@ -1,41 +1,43 @@
1
1
  /**
2
- * Device/user config keys — typed read/write over the central agents.yaml store.
2
+ * Device/user config keys — typed read/write over the three-layer store.
3
3
  *
4
4
  * One registry (`CONFIG_KEYS`) maps each CLI dotted name to where it lives:
5
- * - user scope → central `~/.agents/agents.yaml` under `config:` (syncs
6
- * fleet-wide via `agents repo push/pull`)
7
- * - device scope split by `visibility`, which asks WHO READS IT:
8
- * · `shared` → central `fleet.devices.<name>.config`, because a PEER
9
- * reads it (the ssh keys and platform to dial the box,
10
- * agents.max-concurrent for placement, auto-launch and
11
- * notes for fleet views). Names and non-secret values only
12
- * (a secrets-bundle NAME is fine; a credential never is).
13
- * · `machine` the owning box's own doc, never the shared file, because
14
- * nothing off-box reads it (browser.profile,
15
- * browser.remote-control, scheduler.enabled,
16
- * daemon.enabled).
5
+ * - user scope → central `~/.agents/agents.yaml` under `config:` (syncs
6
+ * fleet-wide via `agents repo push/pull`)
7
+ * - device scope the per-device TRACKED doc
8
+ * `~/.agents/devices/<name>/agents.yaml` under `config:`
9
+ * conflict-free by construction (each machine writes only
10
+ * its own folder, and the churny auto-written pins no longer
11
+ * share the file)
12
+ * - fleet layer → central `~/.agents/agents.yaml` under
13
+ * `fleet.defaults.config` fleet-wide defaults written by
14
+ * `agents devices config --fleet <key> <value>`
17
15
  *
18
- * That split exists because "central for everything" put 13 machines on one
19
- * tracked path and they stopped being able to pull. `browser.remote-control` is
20
- * also a consent flag syncing one box's opt-in to the rest was a security bug,
21
- * not just churn.
16
+ * Read order for a device-scope key: built-in default < fleet.defaults.config
17
+ * < per-device config:. Names and non-secret values only (a secrets-bundle
18
+ * NAME is fine; a credential never is).
22
19
  *
23
20
  * The device registry (`~/.agents/.history/devices/registry.json`) stays the
24
21
  * DISCOVERY cache (address, tailscale snapshot, reachability); the profile
25
22
  * fields config can override (ssh.*, platform, user) are overlaid onto it at
26
23
  * read time by `lib/devices/resolve-profile.ts`.
27
24
  *
28
- * Legacy stores (per-device `devices/<host>/agents.yaml` config and
29
- * `.history/devices/auto-launch.json`) are folded into the central block once
30
- * by `lib/devices/config-migration.ts`, invoked on the first read/write in a
31
- * process — after migration there is ONE read path, no fallback branches.
25
+ * Legacy stores (central `fleet.devices.<name>.config`, legacy auto-launch.json,
26
+ * doc-level defaultBrowserProfile, pins in tracked docs) are folded into this
27
+ * layout once by `lib/devices/config-migration.ts`, invoked on the first
28
+ * read/write in a process — after migration there is ONE read path per layer,
29
+ * no fallback branches.
32
30
  *
33
31
  * Unset always means today's behavior (the documented default).
34
32
  */
35
- import { readMeta, updateMeta } from './state.js';
33
+ import * as fs from 'fs';
34
+ import * as path from 'path';
35
+ import * as yaml from 'yaml';
36
+ import { META_HEADER, getUserAgentsDir, readMeta, updateMeta, withMetaLock } from './state.js';
37
+ import { atomicWriteFileSync } from './fs-atomic.js';
36
38
  import { machineId } from './machine-id.js';
37
39
  import { assertValidDeviceName } from './devices/registry.js';
38
- import { fleetDevicesMapForWrite, migrateDeviceConfigToCentral } from './devices/config-migration.js';
40
+ import { migrateDeviceConfigStores } from './devices/config-migration.js';
39
41
  const DEVICE_PLATFORMS = ['windows', 'linux', 'macos', 'unknown'];
40
42
  const SSH_AUTH_METHODS = ['key', 'password'];
41
43
  /** Roles a device can be marked with — see the `role` key below. */
@@ -94,7 +96,7 @@ export const CONFIG_KEYS = [
94
96
  scope: 'device',
95
97
  visibility: 'machine',
96
98
  type: 'string',
97
- description: 'Browser profile `agents browser start` resolves to without --profile (set via `agents browser profiles set-default`).',
99
+ description: 'Browser profile `agents browser start` resolves to without --profile (set via `agents browser use`).',
98
100
  },
99
101
  {
100
102
  name: 'agents.max-concurrent',
@@ -155,9 +157,22 @@ export const CONFIG_KEYS = [
155
157
  visibility: 'machine',
156
158
  type: 'bool',
157
159
  defaultValue: false,
158
- description: "Whether other fleet machines may drive THIS device's browser over `browser --host <this-device>`. " +
160
+ description: "Whether other fleet machines may drive THIS device's browser over `browser --device <this-device>`. " +
159
161
  'Default off — a fleet-remote drive is refused until the owner runs `agents browser remote-control on`.',
160
162
  },
163
+ {
164
+ name: 'browser.task-idle-minutes',
165
+ yamlKey: 'browserTaskIdleMinutes',
166
+ scope: 'device',
167
+ visibility: 'machine',
168
+ type: 'int',
169
+ defaultValue: 30,
170
+ description: 'Minutes a browser task may sit with no IPC action (navigate, click, type, screenshot, …) before the daemon\'s ' +
171
+ 'abandoned-task reaper closes its tabs and marks it done (RUSH-2622). 0 disables idle reaping — the reaper still ' +
172
+ "closes a task whose owning agent session has exited, whatever this is set to. Read only on THIS box's own " +
173
+ 'reaper tick and `agents browser gc`, so it never applies to a peer.',
174
+ validate: (v) => v >= 0 ? null : 'browser.task-idle-minutes must be >= 0 (0 disables idle reaping).',
175
+ },
161
176
  {
162
177
  name: 'notes',
163
178
  yamlKey: 'notes',
@@ -292,57 +307,106 @@ function assertValidValue(spec, value) {
292
307
  // ─── Migration hook ───────────────────────────────────────────────────────────
293
308
  let migrationDone = false;
294
309
  /**
295
- * Fold the legacy per-device config stores into the central block, once per
310
+ * Fold the legacy config/pins stores into the current layout, once per
296
311
  * process. A failure is loud but non-fatal — config reads must keep working,
297
312
  * and the next process retries the fold. Honors AGENTS_SKIP_MIGRATION=1, the
298
313
  * same gate bootstrap's runMigration uses (tests pin it so a fork never folds
299
314
  * the developer's real ~/.agents as a side effect).
300
- *
301
- * Call this ONLY from a lifecycle entry point (daemon boot, `runMigration`).
302
- * It must never hang off a config read or write: `~/.agents/agents.yaml` is a
303
- * tracked file shared by every machine in the fleet, so a migration on the read
304
- * path means an ordinary `agents config get` can dirty the shared file. That is
305
- * what left yosemite-s0 unable to pull — 13 machines each rewriting one tracked
306
- * path, on nearly every command.
307
315
  */
308
316
  export function ensureDeviceConfigMigrated() {
309
317
  if (migrationDone || process.env.AGENTS_SKIP_MIGRATION === '1')
310
318
  return;
311
319
  try {
312
- migrateDeviceConfigToCentral();
320
+ migrateDeviceConfigStores();
313
321
  migrationDone = true;
314
322
  }
315
323
  catch (err) {
316
324
  console.error(`device config migration failed (${err?.message ?? err}); a later run retries`);
317
325
  }
318
326
  }
319
- // ─── Reads ────────────────────────────────────────────────────────────────────
327
+ // ─── Layer reads ──────────────────────────────────────────────────────────────
328
+ /** Path to a device's tracked operator doc (`devices/<name>/agents.yaml`). */
329
+ function deviceDocPath(device) {
330
+ return path.join(getUserAgentsDir(), 'devices', device, 'agents.yaml');
331
+ }
320
332
  /**
321
- * The raw device-scope config block for `device` from the central
322
- * `fleet.devices.<name>.config` map ({} when unset). This is the single read
323
- * path post-migration the profile resolver (`lib/devices/resolve-profile.ts`)
324
- * goes through here. Deliberately does NOT auto-trigger the migration: it
325
- * serves the hot dial/render paths, and the keys it resolves (ssh.*, platform)
326
- * are new — they never existed in the legacy stores. Keys with legacy data are
327
- * read through the public API below, which triggers the fold.
333
+ * Read a device's doc. Returns null when the file does not exist. A malformed
334
+ * file is a hard error silently returning null would let the next write wipe
335
+ * the device's routines/config (same contract as routine-activation's reader).
328
336
  */
329
- export function readDeviceConfigValues(device) {
330
- const devices = readMeta().fleet?.devices;
331
- if (!devices || devices === 'all')
337
+ function readDeviceDoc(device) {
338
+ const p = deviceDocPath(device);
339
+ let raw;
340
+ try {
341
+ raw = fs.readFileSync(p, 'utf-8');
342
+ }
343
+ catch (err) {
344
+ if (err && err.code === 'ENOENT')
345
+ return null;
346
+ throw err;
347
+ }
348
+ const corrupted = (detail) => new Error(`Device config corrupted at ${p}: ${detail}. Inspect and restore from backup.`);
349
+ let parsed;
350
+ try {
351
+ parsed = yaml.parse(raw);
352
+ }
353
+ catch (err) {
354
+ throw corrupted(err?.message ?? String(err));
355
+ }
356
+ if (parsed === null || parsed === undefined)
332
357
  return {};
333
- const config = devices[device]?.config;
358
+ if (typeof parsed !== 'object' || Array.isArray(parsed)) {
359
+ throw corrupted(`expected a YAML map, got ${Array.isArray(parsed) ? 'a list' : JSON.stringify(parsed)}`);
360
+ }
361
+ const doc = parsed;
362
+ if (doc.config !== undefined && (typeof doc.config !== 'object' || doc.config === null || Array.isArray(doc.config))) {
363
+ throw corrupted('config: must be a mapping');
364
+ }
365
+ return doc;
366
+ }
367
+ /** Write a device doc (atomic), preserving keys this module does not own
368
+ * (`routines:`). A doc left empty is removed instead of leaving an empty
369
+ * tracked file behind. */
370
+ function writeDeviceDoc(device, doc) {
371
+ const p = deviceDocPath(device);
372
+ if (Object.keys(doc).length === 0) {
373
+ try {
374
+ fs.rmSync(p, { force: true });
375
+ fs.rmdirSync(path.dirname(p));
376
+ }
377
+ catch { /* dir not empty, or the file was already gone */ }
378
+ return;
379
+ }
380
+ fs.mkdirSync(path.dirname(p), { recursive: true });
381
+ atomicWriteFileSync(p, META_HEADER + yaml.stringify(doc));
382
+ }
383
+ /** The fleet-defaults config layer (central `fleet.defaults.config`; {} when unset). */
384
+ export function readFleetConfigDefaults() {
385
+ const config = readMeta().fleet?.defaults?.config;
334
386
  return config && typeof config === 'object' && !Array.isArray(config) ? config : {};
335
387
  }
388
+ /** The device layer only: the doc's `config:` block ({} when unset). */
389
+ function readDeviceDocConfig(device) {
390
+ return readDeviceDoc(device)?.config ?? {};
391
+ }
392
+ /**
393
+ * The effective device-scope config block for `device`: fleet.defaults.config
394
+ * overlaid with the per-device doc's config:. This is the single read path
395
+ * post-migration — the profile resolver (`lib/devices/resolve-profile.ts`)
396
+ * goes through here. Deliberately does NOT auto-trigger the migration: it
397
+ * serves the hot dial/render paths. Sync and cheap (small local files).
398
+ */
399
+ export function readDeviceConfigValues(device) {
400
+ return { ...readFleetConfigDefaults(), ...readDeviceDocConfig(device) };
401
+ }
336
402
  /** The device a targeted read/write applies to (default: this machine). */
337
403
  function targetDevice(opts) {
338
404
  return opts?.device ?? machineId();
339
405
  }
340
406
  /**
341
- * A machine-visibility key is only ever readable for THIS box, because it lives
342
- * in this box's own gitignored doc. Asking for a peer's value is a mistake with a
343
- * concrete fix, so say so rather than silently returning this machine's answer
344
- * for another machine — that would make `agents devices config <peer>
345
- * browser.remote-control` look like it reported the peer's consent state.
407
+ * A machine-visibility key is only ever readable for THIS box. Asking for a
408
+ * peer's value is a mistake with a concrete fix, so say so rather than
409
+ * silently returning this machine's answer for another machine.
346
410
  */
347
411
  function assertLocalTarget(spec, device) {
348
412
  if (spec.scope !== 'device' || spec.visibility !== 'machine')
@@ -350,39 +414,39 @@ function assertLocalTarget(spec, device) {
350
414
  if (device === machineId())
351
415
  return;
352
416
  throw new Error(`${spec.name} is machine-local, so it can only be read or set on the device itself.\n` +
353
- `Run it on ${device}, e.g.: agents ssh ${device} 'agents config ${spec.name} <value>'`);
417
+ `Run it on ${device}, e.g.: agents ssh ${device} 'agents devices config ${device} ${spec.name} <value>'`);
354
418
  }
355
- /** Get one config key's value and the layer that set it. */
419
+ /** Get one config key's effective value and the layer that set it. */
356
420
  export function getConfigValue(name, opts) {
421
+ ensureDeviceConfigMigrated();
357
422
  const spec = configKeySpec(name);
358
423
  if (spec.scope === 'user') {
359
424
  const value = readMeta().config?.[spec.yamlKey];
360
- return { spec, value, layer: value !== undefined ? 'user' : undefined };
425
+ return { spec, value, source: value !== undefined ? 'user' : 'default' };
426
+ }
427
+ if (opts?.fleet) {
428
+ const value = readFleetConfigDefaults()[spec.yamlKey];
429
+ return { spec, value, source: value !== undefined ? 'fleet' : 'default' };
361
430
  }
362
431
  const device = targetDevice(opts);
363
432
  assertLocalTarget(spec, device);
364
- if (spec.visibility === 'machine') {
365
- // This machine's own doc, never the synced file. Fall back to the central
366
- // block so a value written by an older CLI is still honored until the
367
- // operator prunes it (the migration is additive and leaves it in place).
368
- const local = readMeta().deviceConfig?.[spec.yamlKey];
369
- const value = local !== undefined ? local : readDeviceConfigValues(device)[spec.yamlKey];
370
- return { spec, value, layer: value !== undefined ? 'device' : undefined };
371
- }
372
- const value = readDeviceConfigValues(device)[spec.yamlKey];
373
- return { spec, value, layer: value !== undefined ? 'device' : undefined };
433
+ const docConfig = readDeviceDocConfig(device);
434
+ if (spec.yamlKey in docConfig)
435
+ return { spec, value: docConfig[spec.yamlKey], source: 'device' };
436
+ const fleetConfig = readFleetConfigDefaults();
437
+ if (spec.yamlKey in fleetConfig)
438
+ return { spec, value: fleetConfig[spec.yamlKey], source: 'fleet' };
439
+ return { spec, value: undefined, source: 'default' };
374
440
  }
375
441
  /**
376
- * List every known key with its value and the layer that set it.
442
+ * List every known key with its effective value and the layer that set it.
377
443
  *
378
- * Listing a PEER omits its machine-local keys rather than throwing. Their values
379
- * live in that box's own doc and are genuinely unknowable from here, but a bulk
380
- * listing must not hard-fail because one key in the registry is unreadable
381
- * `agents devices config <peer>` should still show everything it CAN resolve.
382
- * Asking for such a key by name still errors, with the `agents ssh` fix named.
444
+ * Listing a PEER omits its machine-local keys rather than throwing those
445
+ * values live on that box and are unknowable from here, but a bulk listing
446
+ * must not hard-fail. Asking for such a key by name still errors.
383
447
  */
384
448
  export function listConfig(opts) {
385
- const isPeer = targetDevice(opts) !== machineId();
449
+ const isPeer = !opts?.fleet && targetDevice(opts) !== machineId();
386
450
  const visible = isPeer
387
451
  ? CONFIG_KEYS.filter((spec) => spec.scope !== 'device' || spec.visibility !== 'machine')
388
452
  : CONFIG_KEYS;
@@ -400,38 +464,42 @@ export function resolveUsagePrimaryHost() {
400
464
  ?? null;
401
465
  }
402
466
  // ─── Writes ───────────────────────────────────────────────────────────────────
403
- function setInCentralBlock(device, spec, value) {
467
+ /** The fleet manifest for a defaults write: `devices` materializes as an
468
+ * explicit empty map (NOT 'all') so `agents apply` targets nothing until the
469
+ * operator declares a roster. */
470
+ function fleetForDefaultsWrite(fleet) {
471
+ return { ...fleet, devices: fleet && fleet.devices !== undefined ? fleet.devices : {} };
472
+ }
473
+ function setInFleetDefaults(spec, value) {
404
474
  updateMeta((m) => {
405
- const devices = fleetDevicesMapForWrite(m.fleet);
406
- const prev = devices[device] ?? {};
407
- devices[device] = { ...prev, config: { ...prev.config, [spec.yamlKey]: value } };
408
- return { ...m, fleet: { ...m.fleet, devices } };
475
+ const fleet = fleetForDefaultsWrite(m.fleet);
476
+ const defaults = { ...fleet.defaults, config: { ...fleet.defaults?.config, [spec.yamlKey]: value } };
477
+ return { ...m, fleet: { ...fleet, defaults } };
409
478
  });
410
479
  }
411
- function unsetInCentralBlock(device, spec) {
480
+ function unsetInFleetDefaults(spec) {
412
481
  updateMeta((m) => {
413
- const stored = m.fleet?.devices;
414
- if (!stored || stored === 'all')
415
- return m; // nothing stored — unset is a no-op (never upgrade 'all' for one)
416
- const prev = stored[device];
417
- if (!prev?.config || !(spec.yamlKey in prev.config))
418
- return m; // key not present — no write needed
419
- const config = { ...prev.config };
482
+ const stored = m.fleet?.defaults?.config;
483
+ if (!stored || !(spec.yamlKey in stored))
484
+ return m; // nothing stored — no-op
485
+ const config = { ...stored };
420
486
  delete config[spec.yamlKey];
421
- const override = { ...prev };
487
+ const defaults = { ...m.fleet.defaults };
422
488
  if (Object.keys(config).length > 0)
423
- override.config = config;
489
+ defaults.config = config;
424
490
  else
425
- delete override.config;
426
- const devices = { ...stored };
427
- if (Object.keys(override).length > 0)
428
- devices[device] = override;
491
+ delete defaults.config;
492
+ const fleet = { ...m.fleet, devices: m.fleet.devices };
493
+ if (Object.keys(defaults).length > 0)
494
+ fleet.defaults = defaults;
429
495
  else
430
- delete devices[device];
431
- const fleet = { ...m.fleet, devices };
496
+ delete fleet.defaults;
432
497
  // Drop the fleet block entirely when the unset emptied a block that holds
433
498
  // nothing else — don't leave a vestigial `fleet: {devices: {}}` behind.
434
- if (Object.keys(devices).length === 0 && !fleet.defaults && !fleet.secrets && !fleet.routines) {
499
+ const devicesEmpty = fleet.devices == null || fleet.devices === 'all'
500
+ ? false
501
+ : Object.keys(fleet.devices).length === 0;
502
+ if (devicesEmpty && !fleet.defaults && !fleet.secrets && !fleet.routines) {
435
503
  const { fleet: _, ...rest } = m;
436
504
  void _;
437
505
  return rest;
@@ -439,26 +507,65 @@ function unsetInCentralBlock(device, spec) {
439
507
  return { ...m, fleet };
440
508
  });
441
509
  }
442
- /** Set a config key (validated). Device-scope keys target this machine unless `opts.device` names a peer. */
510
+ function setInDeviceDoc(device, spec, value) {
511
+ // The doc is shared with writeMetaUnlocked (which owns routines:) — the
512
+ // read-modify-write runs under the meta lock so the two writers can't lose
513
+ // each other's update across processes.
514
+ withMetaLock(() => {
515
+ const doc = readDeviceDoc(device) ?? {};
516
+ doc.config = { ...doc.config, [spec.yamlKey]: value };
517
+ writeDeviceDoc(device, doc);
518
+ });
519
+ }
520
+ function unsetInDeviceDoc(device, spec) {
521
+ withMetaLock(() => {
522
+ const doc = readDeviceDoc(device);
523
+ if (!doc)
524
+ return; // nothing stored — unset is a no-op
525
+ const config = doc.config;
526
+ if (!config || !(spec.yamlKey in config))
527
+ return; // key not present — no write needed
528
+ delete config[spec.yamlKey];
529
+ if (Object.keys(config).length > 0)
530
+ doc.config = config;
531
+ else
532
+ delete doc.config;
533
+ writeDeviceDoc(device, doc);
534
+ });
535
+ }
536
+ /**
537
+ * Set a config key (validated). Device-scope keys target this machine unless
538
+ * `opts.device` names a peer; `opts.fleet` writes the fleet-wide defaults layer
539
+ * instead. User-scope keys reject `fleet` (they are already fleet-wide).
540
+ */
443
541
  export function setConfigValue(name, value, opts) {
542
+ ensureDeviceConfigMigrated();
444
543
  const spec = configKeySpec(name);
445
544
  assertValidValue(spec, value);
446
545
  if (spec.scope === 'user') {
546
+ if (opts?.fleet) {
547
+ throw new Error(`Config key '${spec.name}' is user-scope (already fleet-wide) — --fleet does not apply.`);
548
+ }
447
549
  updateMeta((m) => ({ ...m, config: { ...m.config, [spec.yamlKey]: value } }));
448
550
  return;
449
551
  }
450
- const device = targetDevice(opts);
451
- assertLocalTarget(spec, device);
452
- if (spec.visibility === 'machine') {
453
- updateMeta((m) => ({ ...m, deviceConfig: { ...m.deviceConfig, [spec.yamlKey]: value } }));
552
+ if (opts?.fleet) {
553
+ setInFleetDefaults(spec, value);
454
554
  return;
455
555
  }
456
- setInCentralBlock(device, spec, value);
556
+ const device = targetDevice(opts);
557
+ assertLocalTarget(spec, device);
558
+ setInDeviceDoc(device, spec, value);
457
559
  }
458
- /** Unset a config key — restores default behavior. No-op when already unset. */
560
+ /** Unset a config key — restores the next layer down (fleet default, then the
561
+ * built-in default). No-op when already unset at that layer. */
459
562
  export function unsetConfigValue(name, opts) {
563
+ ensureDeviceConfigMigrated();
460
564
  const spec = configKeySpec(name);
461
565
  if (spec.scope === 'user') {
566
+ if (opts?.fleet) {
567
+ throw new Error(`Config key '${spec.name}' is user-scope (already fleet-wide) — --fleet does not apply.`);
568
+ }
462
569
  updateMeta((m) => {
463
570
  if (!m.config || !(spec.yamlKey in m.config))
464
571
  return m;
@@ -468,22 +575,13 @@ export function unsetConfigValue(name, opts) {
468
575
  });
469
576
  return;
470
577
  }
471
- const device = targetDevice(opts);
472
- assertLocalTarget(spec, device);
473
- if (spec.visibility === 'machine') {
474
- updateMeta((m) => {
475
- if (!m.deviceConfig || !(spec.yamlKey in m.deviceConfig))
476
- return m;
477
- const next = { ...m.deviceConfig };
478
- delete next[spec.yamlKey];
479
- return { ...m, deviceConfig: Object.keys(next).length > 0 ? next : undefined };
480
- });
481
- // Also clear any copy an older CLI left centrally, so the unset is not
482
- // shadowed by the fallback read above.
483
- unsetInCentralBlock(device, spec);
578
+ if (opts?.fleet) {
579
+ unsetInFleetDefaults(spec);
484
580
  return;
485
581
  }
486
- unsetInCentralBlock(device, spec);
582
+ const device = targetDevice(opts);
583
+ assertLocalTarget(spec, device);
584
+ unsetInDeviceDoc(device, spec);
487
585
  }
488
586
  /**
489
587
  * The role marked on one device, or undefined when the operator never marked it.
@@ -505,17 +603,31 @@ export function setConfiguredDeviceRole(name, role) {
505
603
  setConfigValue('role', role, { device: name });
506
604
  }
507
605
  /**
508
- * Every device an operator has marked, keyed by device name. Devices with no
509
- * mark are absent that absence is what makes the worker allowlist opt-in.
606
+ * Every device with an effective role, keyed by device name. Layers like every
607
+ * other device-scope key: the fleet default (central fleet.defaults.config)
608
+ * applies fleet-wide and the per-device doc wins on conflict. `roster` (the
609
+ * registered device names) lets a fleet default reach devices that have no doc
610
+ * of their own; without it only devices with docs are considered — so a
611
+ * fleet-wide `role` default would silently miss a doc-less device and drop it
612
+ * from the worker allowlist. Mirrors {@link loadAutoLaunchPreferences}.
510
613
  */
511
- export function listConfiguredDeviceRoles() {
614
+ export function listConfiguredDeviceRoles(roster) {
512
615
  ensureDeviceConfigMigrated();
513
- const devices = readMeta().fleet?.devices;
514
616
  const out = {};
515
- if (!devices || devices === 'all')
516
- return out;
517
- for (const [name, override] of Object.entries(devices)) {
518
- const role = override?.config?.role;
617
+ const fleetRole = readFleetConfigDefaults().role;
618
+ const names = new Set(roster ?? []);
619
+ if (!roster) {
620
+ const devicesRoot = path.join(getUserAgentsDir(), 'devices');
621
+ try {
622
+ for (const entry of fs.readdirSync(devicesRoot, { withFileTypes: true })) {
623
+ if (entry.isDirectory())
624
+ names.add(entry.name);
625
+ }
626
+ }
627
+ catch { /* no devices/ tree — roster stays empty */ }
628
+ }
629
+ for (const name of names) {
630
+ const role = readDeviceDocConfig(name).role ?? fleetRole;
519
631
  if (typeof role === 'string' && DEVICE_ROLES.includes(role)) {
520
632
  out[name] = role;
521
633
  }
@@ -533,7 +645,7 @@ export function isAutoLaunchEnabled(name) {
533
645
  return getConfigValue('auto-launch.enabled', { device: name }).value !== false;
534
646
  }
535
647
  /** Set whether a device is enabled for auto-launch. Setting the default
536
- * (enabled) removes the key to keep the block minimal. */
648
+ * (enabled) removes the key to keep the doc minimal. */
537
649
  export function setAutoLaunchEnabled(name, enabled) {
538
650
  assertValidDeviceName(name);
539
651
  if (enabled)
@@ -547,7 +659,7 @@ export function isAutoLaunchPreferred(name) {
547
659
  return getConfigValue('auto-launch.preferred', { device: name }).value === true;
548
660
  }
549
661
  /** Set whether a device is preferred for auto-launch. Setting the default
550
- * (not preferred) removes the key to keep the block minimal. */
662
+ * (not preferred) removes the key to keep the doc minimal. */
551
663
  export function setAutoLaunchPreferred(name, preferred) {
552
664
  assertValidDeviceName(name);
553
665
  if (preferred)
@@ -555,22 +667,37 @@ export function setAutoLaunchPreferred(name, preferred) {
555
667
  else
556
668
  unsetConfigValue('auto-launch.preferred', { device: name });
557
669
  }
558
- /** Every device's auto-launch flags, keyed by device name (set flags only) —
559
- * the shape the ext's launch ranking consumes. */
560
- export function loadAutoLaunchPreferences() {
670
+ /**
671
+ * Every device's effective auto-launch flags, keyed by device name — the shape
672
+ * the menu-bar snapshot consumes. Layers like every other device-scope key: the
673
+ * fleet default (central fleet.defaults.config) applies fleet-wide and the
674
+ * per-device doc wins on conflict. `roster` (the registered device names) lets
675
+ * a fleet default reach devices that have no doc of their own; without it only
676
+ * devices with docs are listed.
677
+ */
678
+ export function loadAutoLaunchPreferences(roster) {
561
679
  ensureDeviceConfigMigrated();
562
- const devices = readMeta().fleet?.devices;
680
+ const fleet = readFleetConfigDefaults();
681
+ const names = new Set(roster ?? []);
682
+ if (!roster) {
683
+ const devicesRoot = path.join(getUserAgentsDir(), 'devices');
684
+ try {
685
+ for (const entry of fs.readdirSync(devicesRoot, { withFileTypes: true })) {
686
+ if (entry.isDirectory())
687
+ names.add(entry.name);
688
+ }
689
+ }
690
+ catch { /* no devices/ tree — roster stays empty */ }
691
+ }
563
692
  const out = {};
564
- if (!devices || devices === 'all')
565
- return out;
566
- for (const [name, override] of Object.entries(devices)) {
567
- const config = override?.config;
568
- if (!config)
569
- continue;
693
+ for (const name of names) {
694
+ const doc = readDeviceDocConfig(name);
695
+ const enabled = (doc.autoLaunchEnabled ?? fleet.autoLaunchEnabled);
696
+ const preferred = (doc.autoLaunchPreferred ?? fleet.autoLaunchPreferred);
570
697
  const pref = {};
571
- if (config.autoLaunchEnabled === false)
698
+ if (enabled === false)
572
699
  pref.enabled = false;
573
- if (config.autoLaunchPreferred === true)
700
+ if (preferred === true)
574
701
  pref.preferred = true;
575
702
  if (pref.enabled !== undefined || pref.preferred !== undefined)
576
703
  out[name] = pref;
@@ -591,7 +718,7 @@ export function isSchedulerEnabled() {
591
718
  export function assertSchedulerEnabled() {
592
719
  if (isSchedulerEnabled())
593
720
  return;
594
- throw new Error(`The routines scheduler is disabled on this device (scheduler.enabled=false in ~/.agents/agents.yaml fleet.devices.${machineId()}.config). ` +
721
+ throw new Error(`The routines scheduler is disabled on this device (scheduler.enabled=false in ~/.agents/devices/${machineId()}/agents.yaml). ` +
595
722
  `Re-enable with: agents devices config ${machineId()} scheduler.enabled on`);
596
723
  }
597
724
  /**
@@ -619,14 +746,25 @@ export function isDaemonEnabled() {
619
746
  export function assertDaemonEnabled() {
620
747
  if (isDaemonEnabled())
621
748
  return;
622
- throw new Error(`The daemon is disabled on this device (daemon.enabled=false in ~/.agents/agents.yaml fleet.devices.${machineId()}.config). ` +
749
+ throw new Error(`The daemon is disabled on this device (daemon.enabled=false in ~/.agents/devices/${machineId()}/agents.yaml). ` +
623
750
  `Re-enable with: agents daemon enable`);
624
751
  }
625
752
  /**
626
- * Read the `agents.max-concurrent` cap for each named device from the central
627
- * config block (no SSH). Devices without a cap are omitted — uncapped is the
628
- * default. Used as an input to host ranking (teams placement, Factory
629
- * auto-launch), never as a remote probe.
753
+ * Idle window (ms) the browser-task reaper (`browser/hygiene.ts`) uses on THIS
754
+ * machine, or `null` when idle reaping is off (`browser.task-idle-minutes=0`)
755
+ * session-dead reaping is unaffected either way. Unset means the default 30
756
+ * minutes. Read by the daemon's periodic tick and, as the fallback when a
757
+ * caller omits `--idle-minutes`, by the `gc` IPC action.
758
+ */
759
+ export function resolveBrowserTaskIdleMs() {
760
+ const minutes = getConfigValue('browser.task-idle-minutes').value ?? 30;
761
+ return minutes === 0 ? null : minutes * 60_000;
762
+ }
763
+ /**
764
+ * Read the effective `agents.max-concurrent` cap for each named device (fleet
765
+ * defaults layered under the per-device doc; no SSH). Devices without a cap
766
+ * are omitted — uncapped is the default. Used as an input to host ranking
767
+ * (teams placement, AGI EXT auto-launch), never as a remote probe.
630
768
  */
631
769
  export function readMaxConcurrentCaps(devices) {
632
770
  const caps = {};