@phnx-labs/agents-cli 1.22.38 → 1.22.40

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 (554) hide show
  1. package/CHANGELOG.md +302 -0
  2. package/README.md +111 -70
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +46 -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 +260 -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.js +206 -94
  13. package/dist/commands/budget.d.ts +7 -6
  14. package/dist/commands/budget.js +8 -7
  15. package/dist/commands/cloud.js +17 -20
  16. package/dist/commands/commands.js +1 -1
  17. package/dist/commands/computer-actions.d.ts +1 -35
  18. package/dist/commands/computer-actions.js +13 -181
  19. package/dist/commands/computer.d.ts +28 -3
  20. package/dist/commands/computer.js +93 -65
  21. package/dist/commands/config.js +47 -4
  22. package/dist/commands/cost.d.ts +2 -1
  23. package/dist/commands/cost.js +7 -6
  24. package/dist/commands/daemon.js +151 -4
  25. package/dist/commands/detach.js +1 -1
  26. package/dist/commands/doctor.js +19 -11
  27. package/dist/commands/events.js +1 -1
  28. package/dist/commands/exec.d.ts +2 -50
  29. package/dist/commands/exec.js +161 -216
  30. package/dist/commands/factory.js +1 -1
  31. package/dist/commands/feed.d.ts +2 -2
  32. package/dist/commands/feed.js +9 -12
  33. package/dist/commands/fleet-capture.js +25 -27
  34. package/dist/commands/focus.d.ts +2 -3
  35. package/dist/commands/focus.js +34 -9
  36. package/dist/commands/go.js +15 -5
  37. package/dist/commands/harness.d.ts +3 -3
  38. package/dist/commands/harness.js +53 -5
  39. package/dist/commands/hooks.js +1 -1
  40. package/dist/commands/import.js +2 -2
  41. package/dist/commands/insights.d.ts +5 -5
  42. package/dist/commands/insights.js +15 -9
  43. package/dist/commands/inspect.d.ts +2 -3
  44. package/dist/commands/inspect.js +4 -16
  45. package/dist/commands/logs.d.ts +2 -2
  46. package/dist/commands/logs.js +9 -9
  47. package/dist/commands/mcp.js +2 -2
  48. package/dist/commands/memory.js +1 -1
  49. package/dist/commands/message.d.ts +1 -1
  50. package/dist/commands/message.js +7 -7
  51. package/dist/commands/mine.d.ts +11 -8
  52. package/dist/commands/mine.js +11 -10
  53. package/dist/commands/models.js +1 -1
  54. package/dist/commands/modes.js +1 -1
  55. package/dist/commands/monitors.js +23 -6
  56. package/dist/commands/output.d.ts +7 -6
  57. package/dist/commands/output.js +11 -10
  58. package/dist/commands/packages.js +4 -4
  59. package/dist/commands/perf.js +1 -1
  60. package/dist/commands/permissions.js +49 -32
  61. package/dist/commands/plugins.d.ts +1 -1
  62. package/dist/commands/plugins.js +3 -3
  63. package/dist/commands/profiles.d.ts +6 -9
  64. package/dist/commands/profiles.js +8 -321
  65. package/dist/commands/projects.d.ts +9 -0
  66. package/dist/commands/projects.js +140 -8
  67. package/dist/commands/prune.js +5 -13
  68. package/dist/commands/refresh-rules.js +1 -1
  69. package/dist/commands/repo.d.ts +5 -7
  70. package/dist/commands/repo.js +34 -18
  71. package/dist/commands/resource-view.js +4 -0
  72. package/dist/commands/resume.d.ts +11 -7
  73. package/dist/commands/resume.js +82 -98
  74. package/dist/commands/route.d.ts +12 -0
  75. package/dist/commands/route.js +261 -0
  76. package/dist/commands/routines.d.ts +2 -3
  77. package/dist/commands/routines.js +120 -145
  78. package/dist/commands/rules.js +1 -1
  79. package/dist/commands/run-account-picker.d.ts +1 -1
  80. package/dist/commands/run-account-picker.js +2 -2
  81. package/dist/commands/run-broadcast.d.ts +21 -0
  82. package/dist/commands/run-broadcast.js +89 -0
  83. package/dist/commands/run-cloud.js +1 -1
  84. package/dist/commands/secrets-vault.d.ts +10 -0
  85. package/dist/commands/{login.js → secrets-vault.js} +34 -14
  86. package/dist/commands/secrets.d.ts +4 -4
  87. package/dist/commands/secrets.js +72 -63
  88. package/dist/commands/sessions-backfill.js +1 -1
  89. package/dist/commands/sessions-browser.d.ts +1 -1
  90. package/dist/commands/sessions-browser.js +9 -5
  91. package/dist/commands/sessions-export.js +2 -2
  92. package/dist/commands/sessions-inject.js +4 -5
  93. package/dist/commands/sessions-migrate.js +16 -27
  94. package/dist/commands/sessions-picker.d.ts +13 -0
  95. package/dist/commands/sessions-picker.js +182 -8
  96. package/dist/commands/sessions-resume.d.ts +7 -3
  97. package/dist/commands/sessions-resume.js +81 -33
  98. package/dist/commands/sessions-stats.js +2 -2
  99. package/dist/commands/sessions.d.ts +75 -68
  100. package/dist/commands/sessions.js +142 -180
  101. package/dist/commands/setup-computer.js +1 -1
  102. package/dist/commands/setup-fleet.js +3 -0
  103. package/dist/commands/setup-mine.d.ts +7 -3
  104. package/dist/commands/setup-mine.js +13 -8
  105. package/dist/commands/setup-secrets.js +2 -2
  106. package/dist/commands/setup-watchdog.js +2 -2
  107. package/dist/commands/setup.js +7 -2
  108. package/dist/commands/share.d.ts +65 -0
  109. package/dist/commands/share.js +253 -20
  110. package/dist/commands/skills.js +3 -3
  111. package/dist/commands/snapshot.d.ts +2 -2
  112. package/dist/commands/snapshot.js +9 -9
  113. package/dist/commands/ssh.d.ts +1 -1
  114. package/dist/commands/ssh.js +318 -48
  115. package/dist/commands/subagents.js +1 -1
  116. package/dist/commands/sync.js +24 -9
  117. package/dist/commands/teams.d.ts +2 -2
  118. package/dist/commands/teams.js +9 -18
  119. package/dist/commands/tmux.js +5 -1
  120. package/dist/commands/trash.js +1 -1
  121. package/dist/commands/usage.d.ts +1 -1
  122. package/dist/commands/usage.js +2 -2
  123. package/dist/commands/utils.d.ts +4 -14
  124. package/dist/commands/utils.js +6 -27
  125. package/dist/commands/versions.js +2 -2
  126. package/dist/commands/view.d.ts +9 -77
  127. package/dist/commands/view.js +23 -8
  128. package/dist/commands/watchdog.js +1 -1
  129. package/dist/commands/webhook.js +11 -25
  130. package/dist/commands/workflows.js +1 -1
  131. package/dist/index.js +1 -1
  132. package/dist/lib/account-capabilities.d.ts +39 -0
  133. package/dist/lib/account-capabilities.js +59 -0
  134. package/dist/lib/account-catalog.js +1 -1
  135. package/dist/lib/account-registry.d.ts +66 -0
  136. package/dist/lib/account-registry.js +151 -5
  137. package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
  138. package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
  139. package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
  140. package/dist/lib/{usage.js → accounting/usage.js} +11 -11
  141. package/dist/lib/agent-spec/provider.js +1 -1
  142. package/dist/lib/agents.d.ts +72 -4
  143. package/dist/lib/agents.js +231 -199
  144. package/dist/lib/answer-router.d.ts +2 -2
  145. package/dist/lib/ask-classifier.d.ts +1 -1
  146. package/dist/lib/ask-classifier.js +1 -1
  147. package/dist/lib/audit/log.js +1 -1
  148. package/dist/lib/auth-health.d.ts +1 -1
  149. package/dist/lib/auth-health.js +2 -2
  150. package/dist/lib/brand.js +1 -1
  151. package/dist/lib/browser/caller-identity.d.ts +28 -0
  152. package/dist/lib/browser/caller-identity.js +156 -0
  153. package/dist/lib/browser/cdp.js +1 -1
  154. package/dist/lib/browser/drivers/ssh.d.ts +1 -1
  155. package/dist/lib/browser/drivers/ssh.js +2 -2
  156. package/dist/lib/browser/hygiene.d.ts +10 -2
  157. package/dist/lib/browser/hygiene.js +7 -4
  158. package/dist/lib/browser/ipc.d.ts +22 -2
  159. package/dist/lib/browser/ipc.js +185 -21
  160. package/dist/lib/browser/profiles.d.ts +67 -3
  161. package/dist/lib/browser/profiles.js +129 -7
  162. package/dist/lib/browser/remote-control.d.ts +2 -2
  163. package/dist/lib/browser/remote-control.js +3 -3
  164. package/dist/lib/browser/runtime-state.d.ts +79 -0
  165. package/dist/lib/browser/runtime-state.js +119 -1
  166. package/dist/lib/browser/service.d.ts +113 -1
  167. package/dist/lib/browser/service.js +454 -40
  168. package/dist/lib/browser/sessions-list.d.ts +2 -1
  169. package/dist/lib/browser/sessions-list.js +4 -13
  170. package/dist/lib/browser/types.d.ts +18 -0
  171. package/dist/lib/budget/config.js +1 -1
  172. package/dist/lib/budget/ledger.js +1 -1
  173. package/dist/lib/byok-usage.js +1 -1
  174. package/dist/lib/capabilities.js +3 -15
  175. package/dist/lib/catchup.d.ts +1 -1
  176. package/dist/lib/catchup.js +1 -1
  177. package/dist/lib/cloud/dispatch.js +1 -1
  178. package/dist/lib/cloud/host.d.ts +3 -3
  179. package/dist/lib/cloud/host.js +5 -5
  180. package/dist/lib/cloud/rush.d.ts +9 -21
  181. package/dist/lib/cloud/rush.js +33 -120
  182. package/dist/lib/cloud/types.d.ts +2 -2
  183. package/dist/lib/commands.js +3 -13
  184. package/dist/lib/computer/actions.d.ts +36 -0
  185. package/dist/lib/computer/actions.js +162 -0
  186. package/dist/lib/computer/dispatch.d.ts +2 -2
  187. package/dist/lib/computer/dispatch.js +2 -2
  188. package/dist/lib/computer/sessions-list.d.ts +3 -3
  189. package/dist/lib/computer/sessions-list.js +2 -2
  190. package/dist/lib/config-keys.d.ts +12 -3
  191. package/dist/lib/config-keys.js +29 -6
  192. package/dist/lib/config-machine-keys.d.ts +4 -2
  193. package/dist/lib/config-machine-keys.js +6 -2
  194. package/dist/lib/crabbox/lease.js +9 -1
  195. package/dist/lib/crabbox/runtimes.d.ts +32 -7
  196. package/dist/lib/crabbox/runtimes.js +72 -30
  197. package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
  198. package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
  199. package/dist/lib/daemon-services.d.ts +1 -1
  200. package/dist/lib/daemon-services.js +5 -0
  201. package/dist/lib/daemon-ticks.d.ts +25 -0
  202. package/dist/lib/daemon-ticks.js +31 -1
  203. package/dist/lib/daemon-webhooks.d.ts +102 -0
  204. package/dist/lib/daemon-webhooks.js +210 -0
  205. package/dist/lib/device-config.d.ts +116 -72
  206. package/dist/lib/device-config.js +366 -132
  207. package/dist/lib/devices/config-migration.d.ts +29 -34
  208. package/dist/lib/devices/config-migration.js +209 -130
  209. package/dist/lib/devices/connect.d.ts +2 -2
  210. package/dist/lib/devices/connect.js +2 -2
  211. package/dist/lib/devices/discovery-policy.d.ts +30 -0
  212. package/dist/lib/devices/discovery-policy.js +101 -0
  213. package/dist/lib/devices/fleet-inventory.js +8 -3
  214. package/dist/lib/devices/harness-inventory.d.ts +1 -1
  215. package/dist/lib/devices/harness-inventory.js +12 -3
  216. package/dist/lib/devices/known-hosts.js +1 -1
  217. package/dist/lib/devices/pool.d.ts +61 -0
  218. package/dist/lib/devices/pool.js +83 -0
  219. package/dist/lib/devices/registry.d.ts +1 -1
  220. package/dist/lib/devices/registry.js +1 -1
  221. package/dist/lib/devices/resolve-profile.d.ts +2 -2
  222. package/dist/lib/devices/resolve-profile.js +2 -2
  223. package/dist/lib/devices/resolve-target.d.ts +1 -1
  224. package/dist/lib/devices/resolve-target.js +4 -4
  225. package/dist/lib/devices/self-host.d.ts +1 -1
  226. package/dist/lib/devices/self-host.js +3 -3
  227. package/dist/lib/devices/ssh-config.d.ts +1 -1
  228. package/dist/lib/devices/stats-cache.js +1 -1
  229. package/dist/lib/devices/sync.d.ts +3 -1
  230. package/dist/lib/devices/sync.js +3 -3
  231. package/dist/lib/doctor-diff.js +4 -4
  232. package/dist/lib/drift-sync.js +2 -2
  233. package/dist/lib/drift.js +2 -2
  234. package/dist/lib/event-families.d.ts +1 -1
  235. package/dist/lib/event-stream.d.ts +1 -1
  236. package/dist/lib/event-stream.js +2 -2
  237. package/dist/lib/events-ingest.js +2 -2
  238. package/dist/lib/exec.d.ts +8 -5
  239. package/dist/lib/exec.js +19 -13
  240. package/dist/lib/factory/snapshot.d.ts +1 -1
  241. package/dist/lib/factory/snapshot.js +1 -1
  242. package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
  243. package/dist/lib/{activity.js → feed/activity.js} +7 -7
  244. package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
  245. package/dist/lib/{events.js → feed/events.js} +5 -5
  246. package/dist/lib/{feed.js → feed/feed.js} +4 -4
  247. package/dist/lib/feed-outcome.d.ts +1 -1
  248. package/dist/lib/feed-policy.d.ts +1 -1
  249. package/dist/lib/feed-policy.js +1 -1
  250. package/dist/lib/feed-post.d.ts +1 -1
  251. package/dist/lib/feed-post.js +1 -1
  252. package/dist/lib/feed-ranking.d.ts +1 -1
  253. package/dist/lib/fleet/apply.d.ts +0 -6
  254. package/dist/lib/fleet/apply.js +18 -45
  255. package/dist/lib/fleet/auth-sync.d.ts +16 -35
  256. package/dist/lib/fleet/auth-sync.js +16 -75
  257. package/dist/lib/fleet/capture.js +8 -6
  258. package/dist/lib/fleet/manifest.js +23 -11
  259. package/dist/lib/fleet/types.d.ts +19 -7
  260. package/dist/lib/fleet-cache.d.ts +1 -1
  261. package/dist/lib/fleet-cache.js +1 -1
  262. package/dist/lib/format.d.ts +19 -0
  263. package/dist/lib/format.js +44 -1
  264. package/dist/lib/friction-heuristics.d.ts +1 -1
  265. package/dist/lib/git.d.ts +24 -1
  266. package/dist/lib/git.js +82 -38
  267. package/dist/lib/heal.js +3 -3
  268. package/dist/lib/hooks.d.ts +18 -0
  269. package/dist/lib/hooks.js +113 -1
  270. package/dist/lib/hosts/credentials.d.ts +30 -10
  271. package/dist/lib/hosts/credentials.js +38 -26
  272. package/dist/lib/hosts/dispatch.d.ts +3 -3
  273. package/dist/lib/hosts/dispatch.js +3 -3
  274. package/dist/lib/hosts/option.d.ts +3 -3
  275. package/dist/lib/hosts/option.js +7 -8
  276. package/dist/lib/hosts/passthrough.d.ts +17 -17
  277. package/dist/lib/hosts/passthrough.js +67 -50
  278. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  279. package/dist/lib/hosts/providers/devices.js +1 -1
  280. package/dist/lib/hosts/reconcile.d.ts +1 -1
  281. package/dist/lib/hosts/reconcile.js +1 -1
  282. package/dist/lib/hosts/reconnect.d.ts +1 -1
  283. package/dist/lib/hosts/reconnect.js +2 -2
  284. package/dist/lib/hosts/registry.d.ts +5 -5
  285. package/dist/lib/hosts/registry.js +5 -5
  286. package/dist/lib/hosts/remote-cmd.d.ts +10 -10
  287. package/dist/lib/hosts/remote-cmd.js +25 -18
  288. package/dist/lib/hosts/remote-os.d.ts +4 -4
  289. package/dist/lib/hosts/remote-os.js +4 -4
  290. package/dist/lib/hosts/remote-session-id.d.ts +2 -2
  291. package/dist/lib/hosts/remote-session-id.js +2 -2
  292. package/dist/lib/hosts/routing-flag.d.ts +3 -3
  293. package/dist/lib/hosts/routing-flag.js +4 -5
  294. package/dist/lib/hosts/run-target.d.ts +4 -4
  295. package/dist/lib/hosts/run-target.js +4 -4
  296. package/dist/lib/hosts/session-index.d.ts +2 -2
  297. package/dist/lib/hosts/session-index.js +1 -1
  298. package/dist/lib/hosts/session-marker.d.ts +1 -1
  299. package/dist/lib/hosts/session-marker.js +1 -1
  300. package/dist/lib/hosts/types.d.ts +2 -2
  301. package/dist/lib/hosts/types.js +2 -2
  302. package/dist/lib/import.js +1 -1
  303. package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +49 -1
  304. package/dist/lib/{migrate.js → installations/migrate.js} +265 -38
  305. package/dist/lib/installations/resolve.js +1 -1
  306. package/dist/lib/installations/strategies.js +1 -1
  307. package/dist/lib/installations/update.js +2 -2
  308. package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
  309. package/dist/lib/{versions.js → installations/versions.js} +51 -31
  310. package/dist/lib/isolation-boundary-report.js +1 -1
  311. package/dist/lib/linear-projects.d.ts +28 -0
  312. package/dist/lib/linear-projects.js +30 -0
  313. package/dist/lib/loop.js +1 -1
  314. package/dist/lib/mailbox-gc.js +1 -1
  315. package/dist/lib/mailbox.js +1 -1
  316. package/dist/lib/mcp-registry.d.ts +62 -0
  317. package/dist/lib/mcp-registry.js +177 -0
  318. package/dist/lib/mcp.d.ts +5 -0
  319. package/dist/lib/mcp.js +124 -307
  320. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  321. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  322. package/dist/lib/menubar/install-menubar.d.ts +7 -0
  323. package/dist/lib/menubar/install-menubar.js +24 -6
  324. package/dist/lib/menubar/snapshot.d.ts +7 -0
  325. package/dist/lib/menubar/snapshot.js +8 -6
  326. package/dist/lib/models.js +1 -1
  327. package/dist/lib/monitors/config.d.ts +12 -0
  328. package/dist/lib/monitors/config.js +3 -0
  329. package/dist/lib/monitors/dispatch.js +26 -5
  330. package/dist/lib/monitors/engine.js +10 -0
  331. package/dist/lib/monitors/sources/webhook.d.ts +1 -1
  332. package/dist/lib/monitors/state.d.ts +42 -0
  333. package/dist/lib/monitors/state.js +30 -0
  334. package/dist/lib/notify.d.ts +1 -1
  335. package/dist/lib/observe-aliases.d.ts +2 -2
  336. package/dist/lib/observe-aliases.js +2 -11
  337. package/dist/lib/output/git-output.js +2 -2
  338. package/dist/lib/overdue.d.ts +1 -1
  339. package/dist/lib/overdue.js +1 -1
  340. package/dist/lib/permissions-registry.d.ts +65 -0
  341. package/dist/lib/permissions-registry.js +599 -0
  342. package/dist/lib/permissions.d.ts +24 -13
  343. package/dist/lib/permissions.js +52 -271
  344. package/dist/lib/picker.d.ts +16 -0
  345. package/dist/lib/picker.js +33 -3
  346. package/dist/lib/placement.d.ts +1 -1
  347. package/dist/lib/placement.js +4 -4
  348. package/dist/lib/platform/paths.d.ts +8 -0
  349. package/dist/lib/platform/paths.js +11 -0
  350. package/dist/lib/platform/posixpath.js +3 -3
  351. package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
  352. package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
  353. package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
  354. package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
  355. package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
  356. package/dist/lib/{skills.js → plugins/skills.js} +7 -7
  357. package/dist/lib/pricing/cost.d.ts +1 -1
  358. package/dist/lib/pricing/cost.js +1 -1
  359. package/dist/lib/profiles-presets.d.ts +1 -1
  360. package/dist/lib/profiles-presets.js +1 -1
  361. package/dist/lib/profiles.js +2 -2
  362. package/dist/lib/project-import.js +4 -1
  363. package/dist/lib/project-launch.d.ts +1 -1
  364. package/dist/lib/project-launch.js +3 -3
  365. package/dist/lib/project-pull.d.ts +128 -0
  366. package/dist/lib/project-pull.js +384 -0
  367. package/dist/lib/project-root.d.ts +2 -2
  368. package/dist/lib/project-root.js +2 -2
  369. package/dist/lib/project-status.js +1 -1
  370. package/dist/lib/projects.d.ts +23 -0
  371. package/dist/lib/projects.js +29 -6
  372. package/dist/lib/refresh.d.ts +12 -1
  373. package/dist/lib/refresh.js +16 -9
  374. package/dist/lib/registry.d.ts +9 -2
  375. package/dist/lib/registry.js +11 -4
  376. package/dist/lib/resources.d.ts +2 -2
  377. package/dist/lib/resources.js +2 -2
  378. package/dist/lib/routers.d.ts +81 -0
  379. package/dist/lib/routers.js +153 -0
  380. package/dist/lib/routine-context.d.ts +1 -0
  381. package/dist/lib/routine-context.js +7 -2
  382. package/dist/lib/routine-notify-owner.d.ts +1 -1
  383. package/dist/lib/routine-notify.d.ts +1 -1
  384. package/dist/lib/routine-notify.js +1 -1
  385. package/dist/lib/routine-process-cleanup.d.ts +1 -1
  386. package/dist/lib/routine-readiness.d.ts +1 -1
  387. package/dist/lib/routine-readiness.js +11 -4
  388. package/dist/lib/routines-placement.d.ts +12 -2
  389. package/dist/lib/routines-placement.js +20 -3
  390. package/dist/lib/routines-project.d.ts +1 -1
  391. package/dist/lib/routines-project.js +1 -1
  392. package/dist/lib/rules/compile.d.ts +1 -0
  393. package/dist/lib/rules/compile.js +10 -2
  394. package/dist/lib/rules/rules.js +1 -1
  395. package/dist/lib/run-notify.js +1 -1
  396. package/dist/lib/runner.d.ts +80 -2
  397. package/dist/lib/runner.js +241 -62
  398. package/dist/lib/sandbox.d.ts +1 -1
  399. package/dist/lib/scheduler.d.ts +1 -1
  400. package/dist/lib/scheduler.js +1 -1
  401. package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
  402. package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
  403. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  404. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  405. package/dist/lib/secrets/agent.js +2 -2
  406. package/dist/lib/secrets/audit.js +1 -1
  407. package/dist/lib/secrets/bundles.d.ts +3 -3
  408. package/dist/lib/secrets/bundles.js +7 -7
  409. package/dist/lib/secrets/index.js +1 -1
  410. package/dist/lib/secrets/list-filter.d.ts +1 -1
  411. package/dist/lib/secrets/list-filter.js +2 -2
  412. package/dist/lib/secrets/push.d.ts +1 -1
  413. package/dist/lib/secrets/push.js +10 -12
  414. package/dist/lib/secrets/remote.d.ts +12 -10
  415. package/dist/lib/secrets/remote.js +21 -14
  416. package/dist/lib/secrets/vault.js +2 -2
  417. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  418. package/dist/lib/self-heal/checks/shims.js +1 -1
  419. package/dist/lib/self-update.d.ts +39 -4
  420. package/dist/lib/self-update.js +78 -25
  421. package/dist/lib/serve/control.d.ts +1 -1
  422. package/dist/lib/serve/control.js +4 -4
  423. package/dist/lib/serve/data.d.ts +1 -1
  424. package/dist/lib/serve/data.js +1 -1
  425. package/dist/lib/serve/stream.js +1 -1
  426. package/dist/lib/service-manifest.d.ts +29 -0
  427. package/dist/lib/service-manifest.js +80 -0
  428. package/dist/lib/session/active.d.ts +59 -7
  429. package/dist/lib/session/active.js +158 -17
  430. package/dist/lib/session/bash-command.js +1 -1
  431. package/dist/lib/session/claude-accounts.d.ts +1 -1
  432. package/dist/lib/session/claude-accounts.js +1 -1
  433. package/dist/lib/session/db.d.ts +10 -2
  434. package/dist/lib/session/db.js +25 -6
  435. package/dist/lib/session/discover.d.ts +77 -0
  436. package/dist/lib/session/discover.js +130 -25
  437. package/dist/lib/session/fork.js +1 -1
  438. package/dist/lib/session/inject.d.ts +1 -1
  439. package/dist/lib/session/insights.d.ts +1 -1
  440. package/dist/lib/session/insights.js +1 -1
  441. package/dist/lib/session/live-metadata.d.ts +15 -0
  442. package/dist/lib/session/live-metadata.js +76 -0
  443. package/dist/lib/session/migrate-targets.d.ts +1 -1
  444. package/dist/lib/session/recovery.d.ts +1 -1
  445. package/dist/lib/session/recovery.js +2 -2
  446. package/dist/lib/session/remote-active.d.ts +10 -1
  447. package/dist/lib/session/remote-active.js +8 -3
  448. package/dist/lib/session/remote-bundle.d.ts +1 -1
  449. package/dist/lib/session/remote-bundle.js +2 -2
  450. package/dist/lib/session/remote-list.d.ts +15 -4
  451. package/dist/lib/session/remote-list.js +40 -3
  452. package/dist/lib/session/remote.d.ts +5 -5
  453. package/dist/lib/session/remote.js +13 -17
  454. package/dist/lib/session/resume-command.d.ts +3 -2
  455. package/dist/lib/session/resume-command.js +4 -3
  456. package/dist/lib/session/resume-owner.d.ts +3 -3
  457. package/dist/lib/session/resume-owner.js +3 -3
  458. package/dist/lib/session/session-cache.d.ts +12 -0
  459. package/dist/lib/session/session-cache.js +2 -0
  460. package/dist/lib/session/shell-programs.js +1 -1
  461. package/dist/lib/session/state.js +5 -5
  462. package/dist/lib/session/team-filter.d.ts +1 -1
  463. package/dist/lib/session/team-filter.js +1 -1
  464. package/dist/lib/session/types.d.ts +3 -3
  465. package/dist/lib/session/width.d.ts +2 -2
  466. package/dist/lib/session/width.js +2 -2
  467. package/dist/lib/share/delete.d.ts +21 -0
  468. package/dist/lib/share/delete.js +42 -0
  469. package/dist/lib/share/publish.d.ts +82 -0
  470. package/dist/lib/share/publish.js +170 -18
  471. package/dist/lib/share/worker-template.js +172 -9
  472. package/dist/lib/shims.js +35 -7
  473. package/dist/lib/smart-launch.d.ts +36 -6
  474. package/dist/lib/smart-launch.js +65 -8
  475. package/dist/lib/snapshot.d.ts +4 -4
  476. package/dist/lib/snapshot.js +2 -2
  477. package/dist/lib/ssh-exec.d.ts +1 -1
  478. package/dist/lib/ssh-exec.js +2 -2
  479. package/dist/lib/ssh-tunnel.d.ts +10 -10
  480. package/dist/lib/ssh-tunnel.js +12 -12
  481. package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
  482. package/dist/lib/staleness/checkers/hooks.js +1 -1
  483. package/dist/lib/staleness/detectors/permissions.js +0 -23
  484. package/dist/lib/staleness/detectors/plugins.js +1 -1
  485. package/dist/lib/staleness/fingerprint.js +1 -1
  486. package/dist/lib/staleness/layers.d.ts +1 -1
  487. package/dist/lib/staleness/layers.js +1 -1
  488. package/dist/lib/staleness/writers/mcp.js +3 -1
  489. package/dist/lib/staleness/writers/plugins.js +1 -1
  490. package/dist/lib/staleness/writers/types.d.ts +8 -0
  491. package/dist/lib/startup/command-registry.d.ts +5 -147
  492. package/dist/lib/startup/command-registry.js +38 -303
  493. package/dist/lib/startup/dev-build.d.ts +0 -21
  494. package/dist/lib/startup/dev-build.js +29 -8
  495. package/dist/lib/state.d.ts +19 -1
  496. package/dist/lib/state.js +120 -51
  497. package/dist/lib/subagents.js +1 -1
  498. package/dist/lib/sync-status.js +1 -1
  499. package/dist/lib/sync-umbrella.d.ts +6 -1
  500. package/dist/lib/sync-umbrella.js +9 -4
  501. package/dist/lib/teams/agents.js +2 -2
  502. package/dist/lib/teams/registry.js +1 -1
  503. package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
  504. package/dist/lib/terminal/engine.js +1 -1
  505. package/dist/lib/terminal/index.d.ts +4 -0
  506. package/dist/lib/terminal/index.js +3 -0
  507. package/dist/lib/terminal/inject.d.ts +2 -2
  508. package/dist/lib/terminal/inject.js +2 -2
  509. package/dist/lib/terminal/transport.js +1 -1
  510. package/dist/lib/tmux/index.d.ts +1 -1
  511. package/dist/lib/tmux/index.js +1 -1
  512. package/dist/lib/tmux/session.d.ts +21 -5
  513. package/dist/lib/tmux/session.js +76 -22
  514. package/dist/lib/triggers/handlers.d.ts +1 -1
  515. package/dist/lib/triggers/handlers.js +2 -2
  516. package/dist/lib/triggers/webhook.d.ts +36 -2
  517. package/dist/lib/triggers/webhook.js +97 -42
  518. package/dist/lib/types.d.ts +18 -6
  519. package/dist/lib/usage-fleet.d.ts +1 -1
  520. package/dist/lib/usage-fleet.js +1 -1
  521. package/dist/lib/usage-refresh.d.ts +1 -1
  522. package/dist/lib/usage-refresh.js +3 -3
  523. package/dist/lib/version-duplicates.js +2 -2
  524. package/dist/lib/view-types.d.ts +56 -0
  525. package/dist/lib/view-types.js +1 -0
  526. package/dist/lib/watchdog/rotate.d.ts +2 -2
  527. package/dist/lib/watchdog/rotate.js +3 -3
  528. package/dist/lib/watchdog/runner.d.ts +2 -3
  529. package/dist/lib/watchdog/runner.js +3 -4
  530. package/dist/lib/workflows.js +1 -1
  531. package/package.json +2 -1
  532. package/scripts/postinstall.js +2 -2
  533. package/dist/commands/bench.d.ts +0 -2
  534. package/dist/commands/bench.js +0 -101
  535. package/dist/commands/cp.d.ts +0 -84
  536. package/dist/commands/cp.js +0 -231
  537. package/dist/commands/login.d.ts +0 -2
  538. package/dist/lib/resources/commands.d.ts +0 -46
  539. package/dist/lib/resources/commands.js +0 -211
  540. package/dist/lib/resources/hooks.d.ts +0 -16
  541. package/dist/lib/resources/hooks.js +0 -153
  542. package/dist/lib/resources/mcp.d.ts +0 -34
  543. package/dist/lib/resources/mcp.js +0 -250
  544. package/dist/lib/resources/memory.d.ts +0 -15
  545. package/dist/lib/resources/memory.js +0 -46
  546. package/dist/lib/resources/permissions.d.ts +0 -13
  547. package/dist/lib/resources/permissions.js +0 -202
  548. package/dist/lib/resources/rules.d.ts +0 -46
  549. package/dist/lib/resources/rules.js +0 -179
  550. package/dist/lib/resources/skills.d.ts +0 -37
  551. package/dist/lib/resources/skills.js +0 -247
  552. package/dist/lib/resources/subagents.d.ts +0 -46
  553. package/dist/lib/resources/subagents.js +0 -202
  554. /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
@@ -1,43 +1,49 @@
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'];
43
+ /** Roles a device can be marked with — see the `role` key below. */
44
+ const DEVICE_ROLES = ['worker', 'personal'];
45
+ /** Which devices automatic placement may pick — see the `auto.pool` key below. */
46
+ const AUTO_POOL_MODES = ['workers', 'all'];
41
47
  export const CONFIG_KEYS = [
42
48
  {
43
49
  name: 'interactive.host',
@@ -71,6 +77,19 @@ export const CONFIG_KEYS = [
71
77
  }
72
78
  },
73
79
  },
80
+ {
81
+ name: 'auto.pool',
82
+ yamlKey: 'autoPool',
83
+ scope: 'user',
84
+ type: 'string',
85
+ description: "Which devices automatic placement (`--device auto`) may pick: 'workers' (default — only devices marked role=worker, " +
86
+ "once at least one is marked) or 'all' (every online device, ignoring worker marks). A device marked personal is " +
87
+ 'never picked automatically under either mode.',
88
+ defaultValue: 'workers',
89
+ validate: (v) => AUTO_POOL_MODES.includes(v)
90
+ ? null
91
+ : `auto.pool must be one of ${AUTO_POOL_MODES.join(' | ')}.`,
92
+ },
74
93
  {
75
94
  name: 'browser.profile',
76
95
  yamlKey: 'defaultBrowserProfile',
@@ -119,6 +138,18 @@ export const CONFIG_KEYS = [
119
138
  defaultValue: false,
120
139
  description: 'Whether the daemon runs the watchdog pass on this device.',
121
140
  },
141
+ {
142
+ name: 'tmux.enabled',
143
+ yamlKey: 'tmuxEnabled',
144
+ scope: 'device',
145
+ visibility: 'machine',
146
+ type: 'bool',
147
+ defaultValue: true,
148
+ description: 'Whether an interactive `agents run` on this device is wrapped in the shared-socket tmux session. ' +
149
+ 'On gives every agent an addressable pane (`agents sessions --active` tells co-located agents apart, ' +
150
+ '`agents focus` re-attaches without forking). Off spawns the agent directly on this box — the durable ' +
151
+ 'form of `--no-tmux`, for a machine whose tmux is broken or unwanted.',
152
+ },
122
153
  {
123
154
  name: 'browser.remote-control',
124
155
  yamlKey: 'browserRemoteControl',
@@ -126,9 +157,22 @@ export const CONFIG_KEYS = [
126
157
  visibility: 'machine',
127
158
  type: 'bool',
128
159
  defaultValue: false,
129
- 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>`. " +
130
161
  'Default off — a fleet-remote drive is refused until the owner runs `agents browser remote-control on`.',
131
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
+ },
132
176
  {
133
177
  name: 'notes',
134
178
  yamlKey: 'notes',
@@ -191,6 +235,20 @@ export const CONFIG_KEYS = [
191
235
  ? null
192
236
  : `platform must be one of ${DEVICE_PLATFORMS.join(' | ')}.`,
193
237
  },
238
+ {
239
+ name: 'role',
240
+ yamlKey: 'role',
241
+ scope: 'device',
242
+ visibility: 'shared',
243
+ type: 'string',
244
+ description: "What this device is for, fleet-wide: 'worker' (a box agents run on) or 'personal' (a machine you sit at — never " +
245
+ 'picked automatically). Marking ANY device worker turns automatic placement into an allowlist: `--device auto` then ' +
246
+ 'picks only from the marked workers. (A paired iPhone/iPad cockpit is marked control by `agents devices pair-ios` ' +
247
+ 'and is excluded from placement by that role, not this key.)',
248
+ validate: (v) => DEVICE_ROLES.includes(v)
249
+ ? null
250
+ : `role must be one of ${DEVICE_ROLES.join(' | ')}.`,
251
+ },
194
252
  {
195
253
  name: 'auto-launch.enabled',
196
254
  yamlKey: 'autoLaunchEnabled',
@@ -249,57 +307,106 @@ function assertValidValue(spec, value) {
249
307
  // ─── Migration hook ───────────────────────────────────────────────────────────
250
308
  let migrationDone = false;
251
309
  /**
252
- * 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
253
311
  * process. A failure is loud but non-fatal — config reads must keep working,
254
312
  * and the next process retries the fold. Honors AGENTS_SKIP_MIGRATION=1, the
255
313
  * same gate bootstrap's runMigration uses (tests pin it so a fork never folds
256
314
  * the developer's real ~/.agents as a side effect).
257
- *
258
- * Call this ONLY from a lifecycle entry point (daemon boot, `runMigration`).
259
- * It must never hang off a config read or write: `~/.agents/agents.yaml` is a
260
- * tracked file shared by every machine in the fleet, so a migration on the read
261
- * path means an ordinary `agents config get` can dirty the shared file. That is
262
- * what left yosemite-s0 unable to pull — 13 machines each rewriting one tracked
263
- * path, on nearly every command.
264
315
  */
265
316
  export function ensureDeviceConfigMigrated() {
266
317
  if (migrationDone || process.env.AGENTS_SKIP_MIGRATION === '1')
267
318
  return;
268
319
  try {
269
- migrateDeviceConfigToCentral();
320
+ migrateDeviceConfigStores();
270
321
  migrationDone = true;
271
322
  }
272
323
  catch (err) {
273
324
  console.error(`device config migration failed (${err?.message ?? err}); a later run retries`);
274
325
  }
275
326
  }
276
- // ─── 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
+ }
277
332
  /**
278
- * The raw device-scope config block for `device` from the central
279
- * `fleet.devices.<name>.config` map ({} when unset). This is the single read
280
- * path post-migration the profile resolver (`lib/devices/resolve-profile.ts`)
281
- * goes through here. Deliberately does NOT auto-trigger the migration: it
282
- * serves the hot dial/render paths, and the keys it resolves (ssh.*, platform)
283
- * are new — they never existed in the legacy stores. Keys with legacy data are
284
- * 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).
285
336
  */
286
- export function readDeviceConfigValues(device) {
287
- const devices = readMeta().fleet?.devices;
288
- 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)
289
357
  return {};
290
- 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;
291
386
  return config && typeof config === 'object' && !Array.isArray(config) ? config : {};
292
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
+ }
293
402
  /** The device a targeted read/write applies to (default: this machine). */
294
403
  function targetDevice(opts) {
295
404
  return opts?.device ?? machineId();
296
405
  }
297
406
  /**
298
- * A machine-visibility key is only ever readable for THIS box, because it lives
299
- * in this box's own gitignored doc. Asking for a peer's value is a mistake with a
300
- * concrete fix, so say so rather than silently returning this machine's answer
301
- * for another machine — that would make `agents devices config <peer>
302
- * 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.
303
410
  */
304
411
  function assertLocalTarget(spec, device) {
305
412
  if (spec.scope !== 'device' || spec.visibility !== 'machine')
@@ -307,39 +414,39 @@ function assertLocalTarget(spec, device) {
307
414
  if (device === machineId())
308
415
  return;
309
416
  throw new Error(`${spec.name} is machine-local, so it can only be read or set on the device itself.\n` +
310
- `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>'`);
311
418
  }
312
- /** 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. */
313
420
  export function getConfigValue(name, opts) {
421
+ ensureDeviceConfigMigrated();
314
422
  const spec = configKeySpec(name);
315
423
  if (spec.scope === 'user') {
316
424
  const value = readMeta().config?.[spec.yamlKey];
317
- 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' };
318
430
  }
319
431
  const device = targetDevice(opts);
320
432
  assertLocalTarget(spec, device);
321
- if (spec.visibility === 'machine') {
322
- // This machine's own doc, never the synced file. Fall back to the central
323
- // block so a value written by an older CLI is still honored until the
324
- // operator prunes it (the migration is additive and leaves it in place).
325
- const local = readMeta().deviceConfig?.[spec.yamlKey];
326
- const value = local !== undefined ? local : readDeviceConfigValues(device)[spec.yamlKey];
327
- return { spec, value, layer: value !== undefined ? 'device' : undefined };
328
- }
329
- const value = readDeviceConfigValues(device)[spec.yamlKey];
330
- 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' };
331
440
  }
332
441
  /**
333
- * 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.
334
443
  *
335
- * Listing a PEER omits its machine-local keys rather than throwing. Their values
336
- * live in that box's own doc and are genuinely unknowable from here, but a bulk
337
- * listing must not hard-fail because one key in the registry is unreadable
338
- * `agents devices config <peer>` should still show everything it CAN resolve.
339
- * 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.
340
447
  */
341
448
  export function listConfig(opts) {
342
- const isPeer = targetDevice(opts) !== machineId();
449
+ const isPeer = !opts?.fleet && targetDevice(opts) !== machineId();
343
450
  const visible = isPeer
344
451
  ? CONFIG_KEYS.filter((spec) => spec.scope !== 'device' || spec.visibility !== 'machine')
345
452
  : CONFIG_KEYS;
@@ -357,38 +464,42 @@ export function resolveUsagePrimaryHost() {
357
464
  ?? null;
358
465
  }
359
466
  // ─── Writes ───────────────────────────────────────────────────────────────────
360
- 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) {
361
474
  updateMeta((m) => {
362
- const devices = fleetDevicesMapForWrite(m.fleet);
363
- const prev = devices[device] ?? {};
364
- devices[device] = { ...prev, config: { ...prev.config, [spec.yamlKey]: value } };
365
- 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 } };
366
478
  });
367
479
  }
368
- function unsetInCentralBlock(device, spec) {
480
+ function unsetInFleetDefaults(spec) {
369
481
  updateMeta((m) => {
370
- const stored = m.fleet?.devices;
371
- if (!stored || stored === 'all')
372
- return m; // nothing stored — unset is a no-op (never upgrade 'all' for one)
373
- const prev = stored[device];
374
- if (!prev?.config || !(spec.yamlKey in prev.config))
375
- return m; // key not present — no write needed
376
- 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 };
377
486
  delete config[spec.yamlKey];
378
- const override = { ...prev };
487
+ const defaults = { ...m.fleet.defaults };
379
488
  if (Object.keys(config).length > 0)
380
- override.config = config;
489
+ defaults.config = config;
381
490
  else
382
- delete override.config;
383
- const devices = { ...stored };
384
- if (Object.keys(override).length > 0)
385
- 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;
386
495
  else
387
- delete devices[device];
388
- const fleet = { ...m.fleet, devices };
496
+ delete fleet.defaults;
389
497
  // Drop the fleet block entirely when the unset emptied a block that holds
390
498
  // nothing else — don't leave a vestigial `fleet: {devices: {}}` behind.
391
- 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) {
392
503
  const { fleet: _, ...rest } = m;
393
504
  void _;
394
505
  return rest;
@@ -396,26 +507,65 @@ function unsetInCentralBlock(device, spec) {
396
507
  return { ...m, fleet };
397
508
  });
398
509
  }
399
- /** 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
+ */
400
541
  export function setConfigValue(name, value, opts) {
542
+ ensureDeviceConfigMigrated();
401
543
  const spec = configKeySpec(name);
402
544
  assertValidValue(spec, value);
403
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
+ }
404
549
  updateMeta((m) => ({ ...m, config: { ...m.config, [spec.yamlKey]: value } }));
405
550
  return;
406
551
  }
407
- const device = targetDevice(opts);
408
- assertLocalTarget(spec, device);
409
- if (spec.visibility === 'machine') {
410
- updateMeta((m) => ({ ...m, deviceConfig: { ...m.deviceConfig, [spec.yamlKey]: value } }));
552
+ if (opts?.fleet) {
553
+ setInFleetDefaults(spec, value);
411
554
  return;
412
555
  }
413
- setInCentralBlock(device, spec, value);
556
+ const device = targetDevice(opts);
557
+ assertLocalTarget(spec, device);
558
+ setInDeviceDoc(device, spec, value);
414
559
  }
415
- /** 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. */
416
562
  export function unsetConfigValue(name, opts) {
563
+ ensureDeviceConfigMigrated();
417
564
  const spec = configKeySpec(name);
418
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
+ }
419
569
  updateMeta((m) => {
420
570
  if (!m.config || !(spec.yamlKey in m.config))
421
571
  return m;
@@ -425,22 +575,69 @@ export function unsetConfigValue(name, opts) {
425
575
  });
426
576
  return;
427
577
  }
578
+ if (opts?.fleet) {
579
+ unsetInFleetDefaults(spec);
580
+ return;
581
+ }
428
582
  const device = targetDevice(opts);
429
583
  assertLocalTarget(spec, device);
430
- if (spec.visibility === 'machine') {
431
- updateMeta((m) => {
432
- if (!m.deviceConfig || !(spec.yamlKey in m.deviceConfig))
433
- return m;
434
- const next = { ...m.deviceConfig };
435
- delete next[spec.yamlKey];
436
- return { ...m, deviceConfig: Object.keys(next).length > 0 ? next : undefined };
437
- });
438
- // Also clear any copy an older CLI left centrally, so the unset is not
439
- // shadowed by the fallback read above.
440
- unsetInCentralBlock(device, spec);
441
- return;
584
+ unsetInDeviceDoc(device, spec);
585
+ }
586
+ /**
587
+ * The role marked on one device, or undefined when the operator never marked it.
588
+ *
589
+ * Undefined is meaningful and is NOT the same as `worker`: an unmarked device is
590
+ * eligible for automatic placement only while no device anywhere carries an
591
+ * explicit `worker` mark (see {@link listConfiguredDeviceRoles}).
592
+ */
593
+ export function configuredDeviceRole(name) {
594
+ assertValidDeviceName(name);
595
+ return getConfigValue('role', { device: name }).value;
596
+ }
597
+ /** Mark a device's role fleet-wide; `undefined` clears the mark. */
598
+ export function setConfiguredDeviceRole(name, role) {
599
+ assertValidDeviceName(name);
600
+ if (role === undefined)
601
+ unsetConfigValue('role', { device: name });
602
+ else
603
+ setConfigValue('role', role, { device: name });
604
+ }
605
+ /**
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}.
613
+ */
614
+ export function listConfiguredDeviceRoles(roster) {
615
+ ensureDeviceConfigMigrated();
616
+ const out = {};
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 */ }
442
628
  }
443
- unsetInCentralBlock(device, spec);
629
+ for (const name of names) {
630
+ const role = readDeviceDocConfig(name).role ?? fleetRole;
631
+ if (typeof role === 'string' && DEVICE_ROLES.includes(role)) {
632
+ out[name] = role;
633
+ }
634
+ }
635
+ return out;
636
+ }
637
+ /** The configured automatic-placement pool mode. Unset means `workers`. */
638
+ export function autoPoolMode() {
639
+ const value = getConfigValue('auto.pool').value;
640
+ return value === 'all' ? 'all' : 'workers';
444
641
  }
445
642
  /** True if the device is enabled for auto-launch. Unset defaults to true. */
446
643
  export function isAutoLaunchEnabled(name) {
@@ -448,7 +645,7 @@ export function isAutoLaunchEnabled(name) {
448
645
  return getConfigValue('auto-launch.enabled', { device: name }).value !== false;
449
646
  }
450
647
  /** Set whether a device is enabled for auto-launch. Setting the default
451
- * (enabled) removes the key to keep the block minimal. */
648
+ * (enabled) removes the key to keep the doc minimal. */
452
649
  export function setAutoLaunchEnabled(name, enabled) {
453
650
  assertValidDeviceName(name);
454
651
  if (enabled)
@@ -462,7 +659,7 @@ export function isAutoLaunchPreferred(name) {
462
659
  return getConfigValue('auto-launch.preferred', { device: name }).value === true;
463
660
  }
464
661
  /** Set whether a device is preferred for auto-launch. Setting the default
465
- * (not preferred) removes the key to keep the block minimal. */
662
+ * (not preferred) removes the key to keep the doc minimal. */
466
663
  export function setAutoLaunchPreferred(name, preferred) {
467
664
  assertValidDeviceName(name);
468
665
  if (preferred)
@@ -470,22 +667,37 @@ export function setAutoLaunchPreferred(name, preferred) {
470
667
  else
471
668
  unsetConfigValue('auto-launch.preferred', { device: name });
472
669
  }
473
- /** Every device's auto-launch flags, keyed by device name (set flags only) —
474
- * the shape the ext's launch ranking consumes. */
475
- 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) {
476
679
  ensureDeviceConfigMigrated();
477
- 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
+ }
478
692
  const out = {};
479
- if (!devices || devices === 'all')
480
- return out;
481
- for (const [name, override] of Object.entries(devices)) {
482
- const config = override?.config;
483
- if (!config)
484
- 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);
485
697
  const pref = {};
486
- if (config.autoLaunchEnabled === false)
698
+ if (enabled === false)
487
699
  pref.enabled = false;
488
- if (config.autoLaunchPreferred === true)
700
+ if (preferred === true)
489
701
  pref.preferred = true;
490
702
  if (pref.enabled !== undefined || pref.preferred !== undefined)
491
703
  out[name] = pref;
@@ -506,9 +718,20 @@ export function isSchedulerEnabled() {
506
718
  export function assertSchedulerEnabled() {
507
719
  if (isSchedulerEnabled())
508
720
  return;
509
- 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). ` +
510
722
  `Re-enable with: agents devices config ${machineId()} scheduler.enabled on`);
511
723
  }
724
+ /**
725
+ * True unless this machine's config turns off the managed tmux wrap for
726
+ * interactive `agents run` launches (`tmux.enabled=false`).
727
+ *
728
+ * Read as one of the guards in `shouldWrapInTmux` (lib/exec.ts) — the durable,
729
+ * per-machine form of `--no-tmux` / `AGENTS_NO_TMUX=1`, for a box whose tmux is
730
+ * broken or unwanted. Unset means today's behavior: wrap.
731
+ */
732
+ export function isTmuxEnabled() {
733
+ return getConfigValue('tmux.enabled').value !== false;
734
+ }
512
735
  /** True unless this machine's config disables the daemon outright (top-level kill switch). */
513
736
  export function isDaemonEnabled() {
514
737
  return getConfigValue('daemon.enabled').value !== false;
@@ -523,14 +746,25 @@ export function isDaemonEnabled() {
523
746
  export function assertDaemonEnabled() {
524
747
  if (isDaemonEnabled())
525
748
  return;
526
- 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). ` +
527
750
  `Re-enable with: agents daemon enable`);
528
751
  }
529
752
  /**
530
- * Read the `agents.max-concurrent` cap for each named device from the central
531
- * config block (no SSH). Devices without a cap are omitted — uncapped is the
532
- * default. Used as an input to host ranking (teams placement, Factory
533
- * 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.
534
768
  */
535
769
  export function readMaxConcurrentCaps(devices) {
536
770
  const caps = {};