@phnx-labs/agents-cli 1.22.39 → 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 +294 -0
  2. package/README.md +111 -70
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +45 -26
  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 +16 -3
  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 +160 -215
  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 +186 -64
  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 +2 -1
  191. package/dist/lib/config-keys.js +8 -4
  192. package/dist/lib/config-machine-keys.d.ts +4 -2
  193. package/dist/lib/config-machine-keys.js +5 -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 +85 -75
  206. package/dist/lib/device-config.js +279 -141
  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 +10 -5
  218. package/dist/lib/devices/pool.js +5 -7
  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 +4 -4
  239. package/dist/lib/exec.js +11 -11
  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} +13 -1
  304. package/dist/lib/{migrate.js → installations/migrate.js} +160 -40
  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 +4 -4
  474. package/dist/lib/smart-launch.js +6 -4
  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
@@ -18,12 +18,12 @@ import * as fs from 'fs';
18
18
  import * as path from 'path';
19
19
  import * as os from 'os';
20
20
  import { getCliLaunch, getAgentsBinDir } from './cli-entry.js';
21
- import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, resolveJobExecutionContext, slotRunId, claimRunSlot, readRunMeta, resolveHostStrategy, } from './routines.js';
22
- import { getRunsDir, getUserAgentsDir, readMeta } from './state.js';
21
+ import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, resolveJobExecutionContext, slotRunId, claimRunSlot, readRunMeta, resolveHostStrategy, } from './scheduling/routines.js';
22
+ import { getRunsDir, getUserAgentsDir, readMeta, getDaemonDir } from './state.js';
23
23
  import { shortCodexHome } from './codex-home.js';
24
24
  import { prepareJobHome, buildSpawnEnv, getJobHomePath } from './sandbox.js';
25
25
  import { resolveModel, buildReasoningFlags } from './models.js';
26
- import { createTimer, redactPrompt, emitRoutineEnd } from './events.js';
26
+ import { createTimer, redactPrompt, emitRoutineEnd } from './feed/events.js';
27
27
  import { codexEditWritableRoots, codexPolicyArgs } from './codex-policy.js';
28
28
  import { applyAddDirs } from './add-dir.js';
29
29
  import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, } from './exec.js';
@@ -34,12 +34,12 @@ import { backgroundSpawnOptions, killTree } from './platform/process.js';
34
34
  import lockfile from 'proper-lockfile';
35
35
  import { ensureLockTarget } from './fs-atomic.js';
36
36
  import { walkForFiles } from './fs-walk.js';
37
- import { getBinaryPath, isVersionInstalled, resolveVersion, getVersionHomePath } from './versions.js';
37
+ import { getBinaryPath, isVersionInstalled, resolveVersion, getVersionHomePath } from './installations/versions.js';
38
38
  import { resolveClaudeSetupToken } from './claude-account-token.js';
39
- import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from './rotate.js';
39
+ import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from './accounting/rotate.js';
40
40
  import { readAuthHealth, isDeadVerdict } from './auth-health.js';
41
41
  import { machineId } from './machine-id.js';
42
- import { isSelfUpdatingAgent, ROUTINE_AGENT_COMMANDS as AGENT_COMMANDS, ROUTINE_AGENT_IDS, isAgentHardDeprecated, hardDeprecationError } from './agents.js';
42
+ import { isSelfUpdatingAgent, ROUTINE_AGENT_COMMANDS as AGENT_COMMANDS, isAgentHardDeprecated, hardDeprecationError } from './agents.js';
43
43
  export class RoutineAlreadyRunningError extends Error {
44
44
  constructor(jobName, runId) {
45
45
  super(`Routine '${jobName}' already has a running execution (${runId})`);
@@ -48,24 +48,97 @@ export class RoutineAlreadyRunningError extends Error {
48
48
  }
49
49
  const ROUTINE_LAUNCH_LOCK_STALE_MS = 30_000;
50
50
  const ROUTINE_LAUNCH_LOCK_WAIT_MS = 10_000;
51
+ /**
52
+ * The prior run of this routine that still holds the active-run slot, or null.
53
+ *
54
+ * ONLY a run still marked `running` can hold the slot. Every terminal status —
55
+ * completed / failed / timeout / missed / blocked / skipped — releases it the
56
+ * moment it is reached (RUSH-2640). A `failed`/`timeout` record used to keep the
57
+ * slot while its pid stayed alive, but that conflated two different operations:
58
+ * cleaning up a leftover process group (reapTerminalRoutineProcesses's job) with
59
+ * occupying the slot. A daemon-launched run that reached a terminal state while
60
+ * still carrying the daemon's own pid (a host run that threw `target_unreachable`
61
+ * before spawning a child) then held the slot forever, because the daemon never
62
+ * dies and `isPidOurs` never went false — every later scheduled slot was refused
63
+ * with `already has an active run` while the routine was silently dead.
64
+ *
65
+ * A `running` record is also aged out past its own timeout: a run cannot
66
+ * legitimately outlive its configured deadline, so past that window it is a
67
+ * wedged record (a daemon that died mid-run, a missed child-exit event, a reused
68
+ * pid an old record without `spawnedAt` reads as "ours"), not a live run — and it
69
+ * never holds the slot regardless of what its recorded pid now points at.
70
+ */
51
71
  function activeRoutineRun(config) {
52
72
  const timeoutMs = parseTimeout(config.timeout) || 10 * 60 * 1000;
53
73
  const now = Date.now();
54
74
  const runs = listRuns(config.name);
55
75
  for (let i = runs.length - 1; i >= 0; i--) {
56
76
  const run = runs[i];
57
- if (!['running', 'failed', 'timeout'].includes(run.status))
77
+ if (run.status !== 'running')
78
+ continue;
79
+ const startedAt = Date.parse(run.startedAt);
80
+ const limit = run.timeoutMs ?? timeoutMs;
81
+ if (Number.isFinite(startedAt) && now - startedAt >= limit)
58
82
  continue;
59
- if (run.pid && isPidOurs(run.pid, run.spawnedAt))
83
+ // A provisional launcher claim carries no child pid yet — trust it until the
84
+ // timeout window above elapses. A record with a child pid is active only
85
+ // while that pid is genuinely ours (alive with the same birth time).
86
+ if (!run.pid)
87
+ return run;
88
+ if (isPidOurs(run.pid, run.spawnedAt))
60
89
  return run;
61
- if (!run.pid) {
62
- const startedAt = Date.parse(run.startedAt);
63
- if (Number.isFinite(startedAt) && now - startedAt < (run.timeoutMs ?? timeoutMs))
64
- return run;
65
- }
66
90
  }
67
91
  return null;
68
92
  }
93
+ /**
94
+ * Consecutive trailing `skipped`/`active_run` records — a routine that keeps
95
+ * being refused because a prior run "still owns" the slot. After the slot is
96
+ * released correctly on terminal states (see {@link activeRoutineRun}) this can
97
+ * only accumulate for a genuine long-overlap or a new wedge; either way it means
98
+ * the routine has stopped firing on schedule and should be surfaced, not left to
99
+ * pile up silently. `runs` is oldest→newest (listRuns sorts by run id).
100
+ */
101
+ export function activeRunSkipStreak(runs) {
102
+ let streak = 0;
103
+ for (let i = runs.length - 1; i >= 0; i--) {
104
+ const run = runs[i];
105
+ if (run.status === 'skipped' && run.skipReason === 'active_run') {
106
+ streak++;
107
+ continue;
108
+ }
109
+ break;
110
+ }
111
+ return streak;
112
+ }
113
+ /** How many consecutive active-run skips before a routine's stall is surfaced. */
114
+ const SKIP_STREAK_ALERT_THRESHOLD = 3;
115
+ /**
116
+ * The pid to stamp on a launcher's provisional active claim, or null when the
117
+ * launcher is this box's routines daemon.
118
+ *
119
+ * The claim pid exists so a launcher that dies between claiming the slot and
120
+ * spawning the child releases the slot quickly (`isPidOurs` goes false) instead
121
+ * of wedging it for the full timeout. That fast recovery only helps a SHORT-LIVED
122
+ * foreground launcher (`agents routines run`): the daemon never dies between
123
+ * claim and spawn, and recording ITS pid is actively harmful — the daemon
124
+ * outlives every run, so `isPidOurs` never goes false and a claim finalized to a
125
+ * terminal state before its child spawned kept the slot and drew
126
+ * `reapTerminalRoutineProcesses` at the daemon's own process group on every tick
127
+ * (RUSH-2640, requirement: never record the daemon's own pid as a run's pid). So
128
+ * the daemon records no claim pid; the timeout window bounds a daemon that
129
+ * crashes mid-claim instead.
130
+ */
131
+ export function launcherClaimPid() {
132
+ try {
133
+ // Mirrors daemon.ts readDaemonPid() (PID_FILE = 'daemon.pid'); read directly
134
+ // rather than importing it to avoid a runner<->daemon import cycle.
135
+ const daemonPid = parseInt(fs.readFileSync(path.join(getDaemonDir(), 'daemon.pid'), 'utf-8').trim(), 10);
136
+ if (Number.isFinite(daemonPid) && daemonPid === process.pid)
137
+ return null;
138
+ }
139
+ catch { /* no daemon pid file — a foreground launcher; record its pid */ }
140
+ return process.pid;
141
+ }
69
142
  /** Serialize concurrent launches of one routine on this box (the on-disk claim
70
143
  * primitive `claimRunSlot` is what holds across processes; this lock only makes
71
144
  * the allocate→spawn window atomic within this process's launch paths). */
@@ -150,10 +223,13 @@ function writeActiveClaim(config, attempt) {
150
223
  ...attempt.stamp,
151
224
  ...(config.workflow ? { workflow: config.workflow } : config.command ? { command: config.command } : config.agent ? { agent: config.agent } : {}),
152
225
  // The launcher owns this provisional claim until the command/agent child
153
- // replaces it with its own pid. Recording the owner makes a crash between
154
- // lock release and child spawn recoverable instead of wedging the routine
155
- // for its full configured timeout.
156
- pid: process.pid,
226
+ // replaces it with its own pid. Recording a SHORT-LIVED foreground launcher's
227
+ // pid makes a crash between lock release and child spawn recoverable instead
228
+ // of wedging the routine for its full configured timeout. The long-lived
229
+ // daemon records no pid here (see launcherClaimPid): its pid never dies, so
230
+ // recording it wedged the slot forever and mis-aimed the process reaper at the
231
+ // daemon itself (RUSH-2640).
232
+ pid: launcherClaimPid(),
157
233
  // `isPidOurs` compares this value with the OS process birth time. The
158
234
  // daemon may have been alive for days before claiming a routine, so the
159
235
  // claim must carry the launcher's birth, not the claim timestamp.
@@ -228,7 +304,11 @@ function allocateRoutineAttempt(config, trigger) {
228
304
  }),
229
305
  };
230
306
  }
231
- if (!config.workflow && config.agent && ROUTINE_AGENT_IDS.includes(config.agent) && isAgentHardDeprecated(config.agent)) {
307
+ // Gate on deprecation alone, not ROUTINE_AGENT_IDS membership a retired
308
+ // harness leaves the command table (gemini did), and the legacy routine must
309
+ // still land a visible 'blocked' record, never a generic buildJobCommand
310
+ // failure (RUSH-2202).
311
+ if (!config.workflow && config.agent && isAgentHardDeprecated(config.agent)) {
232
312
  const reason = hardDeprecationError(config.agent);
233
313
  return {
234
314
  proceed: false,
@@ -278,6 +358,24 @@ function allocateRoutineAttempt(config, trigger) {
278
358
  }
279
359
  return { proceed: true, attempt: { runId, stamp } };
280
360
  }
361
+ /**
362
+ * Emit a loud, once-per-wedge warning when a routine has been refused its slot
363
+ * for {@link SKIP_STREAK_ALERT_THRESHOLD} consecutive scheduled runs. Fires only
364
+ * when the streak first reaches the threshold (not on every later skip), so a
365
+ * genuinely-stalled routine surfaces once instead of piling up silent `skipped`
366
+ * records the way RUSH-2640 did. Runs on the daemon's own stderr, so it lands in
367
+ * the daemon log the operator reads.
368
+ */
369
+ function surfaceWedgedRoutine(config, terminal) {
370
+ if (terminal.status !== 'skipped' || terminal.skipReason !== 'active_run')
371
+ return;
372
+ const streak = activeRunSkipStreak(listRuns(config.name));
373
+ if (streak !== SKIP_STREAK_ALERT_THRESHOLD)
374
+ return;
375
+ const stuck = terminal.activeRunId ?? 'unknown';
376
+ process.stderr.write(`[agents] routine '${config.name}' skipped ${streak} consecutive scheduled runs — ` +
377
+ `its active-run slot is still held by ${stuck}; the routine is not firing on schedule.\n`);
378
+ }
281
379
  /**
282
380
  * Claim one routine attempt under the short launch lock, then execute after
283
381
  * releasing it. The persisted `running` claim makes a concurrent entry point
@@ -293,8 +391,10 @@ async function runWithAttempt(config, trigger, run, wrapTerminal) {
293
391
  writeActiveClaim(config, alloc.attempt);
294
392
  return { attempt: alloc.attempt };
295
393
  });
296
- if ('terminal' in claimed)
394
+ if ('terminal' in claimed) {
395
+ surfaceWedgedRoutine(config, claimed.terminal);
297
396
  return wrapTerminal(claimed.terminal);
397
+ }
298
398
  try {
299
399
  return await run(claimed.attempt);
300
400
  }
@@ -320,9 +420,16 @@ function terminateRoutineTree(pid) {
320
420
  killTree(pid);
321
421
  return;
322
422
  }
423
+ // Detached spawn makes the child a new process-group leader; kill the group.
424
+ // If setsid has not landed yet (or the pid is not a leader), -pid fails with
425
+ // ESRCH and the child stays up — also kill the pid itself.
323
426
  try {
324
427
  process.kill(-pid, 'SIGKILL');
325
428
  }
429
+ catch { /* not a group leader, or already exited */ }
430
+ try {
431
+ process.kill(pid, 'SIGKILL');
432
+ }
326
433
  catch { /* already exited */ }
327
434
  }
328
435
  /**
@@ -433,15 +540,6 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
433
540
  cmd.push(...codexPolicyArgs(policyMode, [...codexEditWritableRoots(), ...routineRoots]));
434
541
  appendModelAndReasoning(cmd, config);
435
542
  }
436
- if (config.agent === 'gemini') {
437
- if (mode === 'edit') {
438
- cmd.push('--approval-mode', 'auto_edit');
439
- }
440
- else if (mode === 'skip') {
441
- cmd.push('--yolo');
442
- }
443
- appendModelAndReasoning(cmd, config);
444
- }
445
543
  if (config.agent === 'cursor') {
446
544
  if (mode === 'plan') {
447
545
  cmd.push('--plan');
@@ -772,18 +870,31 @@ export async function resolveRoutineLaunch(config, cwd = process.cwd(), deps = {
772
870
  // resolveRoutineLaunch is only called for agent jobs (workflow returns above;
773
871
  // command jobs branch out of execute*Job before reaching this).
774
872
  const agent = config.agent;
775
- const { findAccount, resolveAccountSelection, resolveCredentialAccount } = await import('./account-registry.js');
873
+ const { findAccount, findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('./account-registry.js');
874
+ const meta = (deps.readMeta ?? readMeta)();
776
875
  const explicitCredential = config.account
777
876
  ? (deps.findCredentialAccount?.(config.account) ?? (deps.resolveCredentialAccount !== undefined || findAccount(config.account) !== null))
778
877
  : false;
779
878
  const selectedCredential = config.account
780
879
  ? (explicitCredential ? config.account : undefined)
781
- : resolveAccountSelection(undefined, agent, (deps.readMeta ?? readMeta)());
880
+ : resolveAccountSelection(undefined, agent, meta);
782
881
  if (selectedCredential) {
783
- (deps.resolveCredentialAccount ?? resolveCredentialAccount)(selectedCredential, agent);
882
+ // A default/binding may resolve to a native account — never route that
883
+ // through the provider credential path (it has no bundle to resolve).
884
+ const unified = findUnifiedAccount(selectedCredential, meta);
885
+ if (unified?.kind !== 'native')
886
+ (deps.resolveCredentialAccount ?? resolveCredentialAccount)(selectedCredential, agent);
784
887
  }
785
888
  if (config.account && !explicitCredential) {
786
- const accountVersion = await (deps.resolveAccountVersion ?? resolveAccountVersion)(agent, config.account);
889
+ // A native routine account is named by its durable name; the version matcher
890
+ // keys on the identity (email/accountKey), so translate before resolving,
891
+ // and refuse a login that belongs to a different harness.
892
+ const unified = findUnifiedAccount(config.account, meta);
893
+ if (unified?.kind === 'native' && unified.agent !== agent) {
894
+ throw new Error(`Routine '${config.name}' account '${config.account}' is a ${unified.agent} login and cannot authenticate ${agent}.`);
895
+ }
896
+ const identity = unified?.kind === 'native' ? unified.identityKey : config.account;
897
+ const accountVersion = await (deps.resolveAccountVersion ?? resolveAccountVersion)(agent, identity);
787
898
  if (accountVersion) {
788
899
  if (config.version && config.version !== accountVersion) {
789
900
  throw new Error(`Routine '${config.name}' account '${config.account}' is signed in at ${agent}@${accountVersion}, not pinned ${agent}@${config.version}.`);
@@ -809,7 +920,10 @@ export async function resolveRoutineLaunch(config, cwd = process.cwd(), deps = {
809
920
  ...(config.account ? { forwardAccount: explicitCredential } : {}),
810
921
  };
811
922
  }
812
- const strategy = getConfiguredRunStrategy(agent, cwd);
923
+ // A per-routine strategy travels with the definition and beats the firing
924
+ // box's ambient run.<agent>.strategy — otherwise selection policy silently
925
+ // depends on whichever device fires the job (RUSH-2719).
926
+ const strategy = config.strategy ?? getConfiguredRunStrategy(agent, cwd);
813
927
  let version;
814
928
  let rotation = null;
815
929
  let exhausted;
@@ -892,6 +1006,70 @@ export function pinJobBinary(cmd, agent, version) {
892
1006
  * Such commands must NOT be binary-pinned (pinning rewrites cmd[0] to the agent binary,
893
1007
  * producing a broken `<binary> run …`) and must not receive a version-pinned spawn env.
894
1008
  */
1009
+ /**
1010
+ * Assert a routine's account can be dispatched to the resolved placement, BEFORE
1011
+ * any off-box dispatch (placement is resolved before {@link resolveRoutineLaunch}),
1012
+ * at the top of both the foreground and detached paths:
1013
+ *
1014
+ * - **native** account → rejected for host AND cloud: a native login is a
1015
+ * device-local harness credential that cannot be forwarded off-box.
1016
+ * - **provider** account + **cloud** → rejected (fail loud): the cloud dispatch
1017
+ * has no secure way to inject a device-local provider bundle yet.
1018
+ * - **provider** account + **host** → allowed: the host dispatch forwards the
1019
+ * account NAME (the remote resolves its own local bundle — no secret copied).
1020
+ *
1021
+ * `account` is injectable so the guard is unit-tested for both modes without a
1022
+ * registry or a real dispatch.
1023
+ */
1024
+ export async function assertRoutineAccountLocalForPlacement(config, mode, deps = {}) {
1025
+ if (!config.account)
1026
+ return;
1027
+ let account = deps.account;
1028
+ if (account === undefined) {
1029
+ const { findUnifiedAccount } = await import('./account-registry.js');
1030
+ account = findUnifiedAccount(config.account, (deps.readMeta ?? readMeta)());
1031
+ }
1032
+ if (!account) {
1033
+ throw new Error(`Routine '${config.name}' account '${config.account}' is unknown.`);
1034
+ }
1035
+ if (account?.kind === 'native') {
1036
+ throw new Error(`Routine '${config.name}' account '${config.account}' is a device-local ${account.agent} login and cannot run on a ${mode} placement. Use a provider account, or place this routine on the device that holds the login.`);
1037
+ }
1038
+ if (mode === 'cloud' && account?.kind === 'provider') {
1039
+ // RUSH-2689: cloud+provider injection not yet implemented.
1040
+ throw new Error(`Routine '${config.name}' account '${config.account}' is a provider credential; cloud placement cannot securely inject it (RUSH-2689). Run this routine locally or on a host that holds the bundle.`);
1041
+ }
1042
+ }
1043
+ export async function dispatchPlacedJob(config, target, attempt, deps = {}) {
1044
+ if (target.mode !== 'host' && target.mode !== 'cloud')
1045
+ return undefined;
1046
+ await assertRoutineAccountLocalForPlacement(config, target.mode, { account: deps.account });
1047
+ if (target.mode === 'host') {
1048
+ return (deps.host ?? executeJobOnHost)({ ...config, host: target.host }, { detached: false }, attempt);
1049
+ }
1050
+ return (deps.cloud ?? executeJobOnCloud)(config, { detached: false }, attempt);
1051
+ }
1052
+ /**
1053
+ * Build the options passed to `dispatchPromptToHost` for a host routine. Split
1054
+ * out so the execution boundary is unit-testable: it MUST forward the routine's
1055
+ * `account` by name (the remote resolves its own local bundle; no secret copied)
1056
+ * — dropping it silently ran the remote under the wrong identity.
1057
+ */
1058
+ export function buildHostDispatchOptions(config, ctx) {
1059
+ return {
1060
+ agent: config.agent,
1061
+ prompt: resolveJobPrompt(config),
1062
+ mode: normalizeMode(config.mode),
1063
+ effort: config.effort,
1064
+ model: config.config?.model,
1065
+ account: config.account,
1066
+ timeout: config.timeout, // enforced by the REMOTE agents run
1067
+ remoteCwd: ctx.remoteCwd,
1068
+ name: config.name,
1069
+ cwd: ctx.runDir,
1070
+ follow: !ctx.detached,
1071
+ };
1072
+ }
895
1073
  export function dispatchesViaAgentsRun(config) {
896
1074
  return Boolean(config.workflow || config.resume);
897
1075
  }
@@ -1069,13 +1247,10 @@ async function executeJobPlaced(config, deps, attempt) {
1069
1247
  // remote run to completion when possible.
1070
1248
  {
1071
1249
  const { resolvePlacementTarget } = await import('./routines-placement.js');
1072
- const target = resolvePlacementTarget(config);
1073
- if (target.mode === 'host') {
1074
- return executeJobOnHost({ ...config, host: target.host }, { detached: false }, attempt);
1075
- }
1076
- if (target.mode === 'cloud') {
1077
- return executeJobOnCloud(config, { detached: false }, attempt);
1078
- }
1250
+ const target = await resolvePlacementTarget(config);
1251
+ const placed = await dispatchPlacedJob(config, target, attempt);
1252
+ if (placed)
1253
+ return placed;
1079
1254
  }
1080
1255
  // Command-mode: run a plain shell command directly (no agent, no rotation,
1081
1256
  // no pinning, no sandbox overlay). Reuses the run-record machinery so
@@ -1111,12 +1286,16 @@ async function executeJobPlaced(config, deps, attempt) {
1111
1286
  // (command jobs branched out earlier, so config.agent is set on the non-workflow path.)
1112
1287
  const effectiveAgent = config.workflow ? 'claude' : config.agent;
1113
1288
  if (!dispatchesViaAgentsRun(config)) {
1114
- const { findAccount, resolveAccountSelection, resolveCredentialAccount } = await import('./account-registry.js');
1115
- const selectedAccount = config.account && !findAccount(config.account)
1116
- ? undefined
1117
- : resolveAccountSelection(config.account, effectiveAgent, readMeta());
1118
- if (selectedAccount)
1119
- Object.assign(baseEnv, resolveCredentialAccount(selectedAccount, effectiveAgent).env);
1289
+ const { findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('./account-registry.js');
1290
+ const meta = readMeta();
1291
+ const selectedAccount = resolveAccountSelection(config.account, effectiveAgent, meta);
1292
+ if (selectedAccount) {
1293
+ // Only a provider account injects env; a native account (explicit or via a
1294
+ // device-scoped binding) is read from the harness home and forwards nothing.
1295
+ const unified = findUnifiedAccount(selectedAccount, meta);
1296
+ if (unified?.kind === 'provider')
1297
+ Object.assign(baseEnv, resolveCredentialAccount(selectedAccount, effectiveAgent).env);
1298
+ }
1120
1299
  }
1121
1300
  const meta = {
1122
1301
  jobName: config.name,
@@ -1443,18 +1622,7 @@ async function executeJobOnHost(config, opts, attempt) {
1443
1622
  host: host.name,
1444
1623
  };
1445
1624
  writeRunMeta(meta);
1446
- const { task, exitCode } = await dispatchPromptToHost(host, {
1447
- agent: config.agent,
1448
- prompt: resolveJobPrompt(config),
1449
- mode: normalizeMode(config.mode),
1450
- effort: config.effort,
1451
- model: config.config?.model,
1452
- timeout: config.timeout, // enforced by the REMOTE agents run
1453
- remoteCwd,
1454
- name: config.name,
1455
- cwd: runDir,
1456
- follow: !opts.detached,
1457
- });
1625
+ const { task, exitCode } = await dispatchPromptToHost(host, buildHostDispatchOptions(config, { remoteCwd, runDir, detached: opts.detached }));
1458
1626
  meta.hostTaskId = task.id;
1459
1627
  // Sync path: a real exit code finalizes now. -1 (follow window closed) and
1460
1628
  // the detached path leave the meta `running` for the monitor to reconcile.
@@ -1572,7 +1740,10 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
1572
1740
  // sends the finish notification only for local detached runs below (RUSH-2030).
1573
1741
  {
1574
1742
  const { resolvePlacementTarget } = await import('./routines-placement.js');
1575
- const target = resolvePlacementTarget(config);
1743
+ const target = await resolvePlacementTarget(config);
1744
+ if (target.mode === 'host' || target.mode === 'cloud') {
1745
+ await assertRoutineAccountLocalForPlacement(config, target.mode);
1746
+ }
1576
1747
  if (target.mode === 'host') {
1577
1748
  const { meta } = await executeJobOnHost({ ...config, host: target.host }, { detached: true }, attempt);
1578
1749
  if (meta.status !== 'running')
@@ -2072,17 +2243,23 @@ export function monitorRunningJobs() {
2072
2243
  finalizeHostRun(meta);
2073
2244
  continue;
2074
2245
  }
2075
- if (!meta.pid)
2076
- continue;
2077
2246
  const runDirPath = path.join(jobRunsPath, runDirEntry.name);
2078
2247
  const stdoutPath = path.join(runDirPath, 'stdout.log');
2079
2248
  // Command-mode records carry no agent; there is no stream-json report to
2080
2249
  // parse or extract. Reap them on pid liveness alone.
2081
2250
  const isCommandRun = Boolean(meta.command) || !meta.agent;
2251
+ // Age-out runs FIRST, before the null-pid guard below, so a wedged record
2252
+ // still marked `running` past its deadline is always finalized — a
2253
+ // provisional launcher claim whose daemon crashed before spawning a child
2254
+ // (pid null), or an old record whose recorded pid was reused, would
2255
+ // otherwise linger as `running` forever (RUSH-2640). Only kill a process
2256
+ // group that is genuinely still ours; terminating a reused pid would kill
2257
+ // an unrelated process, and a null pid has nothing to kill.
2082
2258
  const wallClockMs = Date.now() - Date.parse(meta.startedAt);
2083
2259
  const timeoutMs = meta.timeoutMs ?? MAX_WALL_CLOCK_MS;
2084
2260
  if (Number.isFinite(wallClockMs) && wallClockMs > timeoutMs) {
2085
- terminateRoutineTree(meta.pid);
2261
+ if (meta.pid && isPidOurs(meta.pid, meta.spawnedAt))
2262
+ terminateRoutineTree(meta.pid);
2086
2263
  finalizeRunMeta(meta, 'timeout', null, { errorMessage: 'exceeded configured timeout' });
2087
2264
  writeRunMeta(meta);
2088
2265
  emitRoutineEnd(meta);
@@ -2092,6 +2269,8 @@ export function monitorRunningJobs() {
2092
2269
  }
2093
2270
  continue;
2094
2271
  }
2272
+ if (!meta.pid)
2273
+ continue;
2095
2274
  if (!isPidOurs(meta.pid, meta.spawnedAt)) {
2096
2275
  if (isCommandRun) {
2097
2276
  // Command routines normally record their own terminal status via
@@ -6,7 +6,7 @@
6
6
  * The spawned agent process sees only the overlay, limiting filesystem
7
7
  * access to explicitly allowed paths.
8
8
  */
9
- import type { JobConfig } from './routines.js';
9
+ import type { JobConfig } from './scheduling/routines.js';
10
10
  /** Build a restricted environment for a sandboxed process, setting HOME to the overlay. */
11
11
  export declare function buildSpawnEnv(overlayHome: string, extraEnv?: Record<string, string>): Record<string, string>;
12
12
  /**
@@ -5,7 +5,7 @@
5
5
  * process creates a single JobScheduler instance that loads enabled jobs
6
6
  * on startup and reloads them on SIGHUP.
7
7
  */
8
- import type { JobConfig } from './routines.js';
8
+ import type { JobConfig } from './scheduling/routines.js';
9
9
  /** How a fire was triggered, carrying the scheduler's intended UTC slot time. */
10
10
  export interface TriggerContext {
11
11
  /** The cron slot this callback fires for (croner `currentRun()`), for the
@@ -6,7 +6,7 @@
6
6
  * on startup and reloads them on SIGHUP.
7
7
  */
8
8
  import { Cron } from 'croner';
9
- import { listJobs, deleteJob, isPastEndAt, isPastOneShotRoutine, isOneShotRoutine, setJobEnabled, shouldPurgeCompletedOneShotRoutine, jobRunsOnThisDevice, hasAmbiguousDevicePin, routineOwnerDevice, } from './routines.js';
9
+ import { listJobs, deleteJob, isPastEndAt, isPastOneShotRoutine, isOneShotRoutine, setJobEnabled, shouldPurgeCompletedOneShotRoutine, jobRunsOnThisDevice, hasAmbiguousDevicePin, routineOwnerDevice, } from './scheduling/routines.js';
10
10
  /** In-memory cron scheduler that triggers a callback when jobs fire. */
11
11
  export class JobScheduler {
12
12
  jobs = new Map();
@@ -6,9 +6,16 @@
6
6
  * run metadata persistence, prompt variable expansion, and one-shot "at" time
7
7
  * scheduling.
8
8
  */
9
- import { type ResolvedExecutionContext, type ProjectResolution, type PlacementMode, type RoutineKind, type ContextFsProbe } from './routine-context.js';
10
- import type { AgentId } from './types.js';
11
- import type { LoopConfig } from './loop.js';
9
+ import { type ResolvedExecutionContext, type ProjectResolution, type PlacementMode, type RoutineKind, type ContextFsProbe } from '../routine-context.js';
10
+ import type { AgentId, RunStrategy } from '../types.js';
11
+ import type { LoopConfig } from '../loop.js';
12
+ import { JobScheduler } from '../scheduler.js';
13
+ export declare function fireConditionLabel(job: JobConfig): string;
14
+ export declare function nextRunForDisplay(job: JobConfig, scheduler: JobScheduler): Date | null;
15
+ export declare function nextRunLabel(job: JobConfig, scheduler: JobScheduler, now: Date): string;
16
+ export declare function localLatestRun(job: JobConfig): RunMeta | null;
17
+ export declare function listJobsForDisplay(cwd?: string): JobConfig[];
18
+ export declare function buildRoutineListJson(): Record<string, unknown>[];
12
19
  /** Tool/site/directory allow-list for sandboxed job execution. */
13
20
  export interface JobAllowConfig {
14
21
  tools?: string[];
@@ -17,7 +24,7 @@ export interface JobAllowConfig {
17
24
  }
18
25
  /**
19
26
  * Where a routine's job body executes when the daemon fires it.
20
- * Distinct from `devices` (which daemon may *fire*) and from the CLI `--host`
27
+ * Distinct from `devices` (which daemon may *fire*) and from the CLI `--device`
21
28
  * remote-management passthrough (manage routines *on* another machine).
22
29
  */
23
30
  export type HostStrategy = 'local' | 'host' | 'fleet' | 'cloud';
@@ -184,7 +191,7 @@ export interface JobConfig {
184
191
  * registered host, device, capability tag, or user@host) instead of locally.
185
192
  * Distinct from `devices`: `devices` says which daemon may FIRE the job,
186
193
  * `host` says where the dispatched run EXECUTES. CLI flag: `--run-on`
187
- * (`--host` on routines commands already means "manage routines on that
194
+ * (`--device` on routines commands already means "manage routines on that
188
195
  * machine" via the remote passthrough).
189
196
  *
190
197
  * When `hostStrategy` is set, it owns placement semantics; `host` is then
@@ -200,7 +207,7 @@ export interface JobConfig {
200
207
  * double-fire; the firing pin stays on `devices`)
201
208
  * - `cloud` — dispatch via the agent's native cloud provider
202
209
  *
203
- * CLI flag: `--placement` (not `--host`, which is the remote-management
210
+ * CLI flag: `--placement` (not `--device`, which is the remote-management
204
211
  * passthrough). Omitted strategy falls back to `host` when `host:` is set,
205
212
  * otherwise `local`.
206
213
  */
@@ -218,6 +225,16 @@ export interface JobConfig {
218
225
  allow?: JobAllowConfig;
219
226
  config?: Record<string, unknown>;
220
227
  version?: string;
228
+ /**
229
+ * Explicit per-routine version/account selection strategy — the same
230
+ * vocabulary as `agents run --strategy` (RUN_STRATEGIES). Overrides
231
+ * `run.<agent>.strategy` from the FIRING device's own agents.yaml
232
+ * (getConfiguredRunStrategy) so a routine's selection policy travels with the
233
+ * definition instead of depending on whichever box happens to fire it.
234
+ * Conflicts with `version:` (an exact pin leaves nothing to select) —
235
+ * validateJob rejects the pair.
236
+ */
237
+ strategy?: RunStrategy;
221
238
  /**
222
239
  * Pin this routine to a signed-in account by identity (its login email, or its
223
240
  * account key) instead of rotating. At launch it resolves to whichever
@@ -268,6 +285,29 @@ export interface JobConfig {
268
285
  * - Absent/empty — routine belongs to no project ("Operations" group).
269
286
  */
270
287
  projects?: string[];
288
+ /**
289
+ * Set only by `lib/monitors/dispatch.ts` on the one-off job it synthesizes for
290
+ * a monitor's `run` action. Such a job is NOT a routine: it has no definition
291
+ * file, `agents routines` never lists it, and its name can therefore never
292
+ * appear in this device's routine activation manifest
293
+ * (`~/.agents/devices/<machine>/agents.yaml` → `routines:`). Gating it on that
294
+ * manifest refused every monitor action fleet-wide with `wrong_owner` and an
295
+ * empty allowlist (RUSH-2681), so {@link jobRunsOnThisDevice} skips the
296
+ * manifest for it. Exactly-once ownership is already resolved BEFORE dispatch
297
+ * by the monitor's own `device:` pin (`monitorRunsOnThisDevice`,
298
+ * `lib/monitors/config.ts`) — re-gating here was double-gating on the wrong
299
+ * key.
300
+ *
301
+ * Deliberately narrow: a monitor's `routine` action fires a REAL routine, which
302
+ * keeps its activation gate, so a routine defined but not activated on this
303
+ * device is still refused.
304
+ *
305
+ * Runtime-only, enforced at both ends of the schema boundary: `writeJob`
306
+ * strips it, and `readJobFileResult` refuses a definition that carries it
307
+ * (a hand-authored one would otherwise fire on every box regardless of
308
+ * activation, since the daemon's load path never calls `validateJob`).
309
+ */
310
+ dispatchedBy?: 'monitor';
271
311
  }
272
312
  /**
273
313
  * Canonical form of a routine's `projects` field: drop non-string and empty
@@ -463,8 +503,13 @@ export declare function finalizeRunMeta(meta: RunMeta, status: RunMeta['status']
463
503
  * `normalizeHost` so `Yosemite-S0`, `yosemite-s0.tailnet.ts.net`, and
464
504
  * `yosemite-s0` all agree. Every fire path (cron scheduler, webhook,
465
505
  * catchup/overdue, manual run) gates on this.
506
+ *
507
+ * A monitor-dispatched job ({@link JobConfig.dispatchedBy} `=== 'monitor'`) skips
508
+ * the routine activation manifest: it is not a routine, so its name can never be
509
+ * a member and the lookup could only ever answer "not activated here"
510
+ * (RUSH-2681). Its ownership was already decided by the monitor's `device:` pin.
466
511
  */
467
- export declare function jobRunsOnThisDevice(config: Pick<JobConfig, 'name' | 'devices'>): boolean;
512
+ export declare function jobRunsOnThisDevice(config: Pick<JobConfig, 'name' | 'devices' | 'dispatchedBy'>): boolean;
468
513
  /**
469
514
  * The ONE device that owns a routine — the single daemon allowed to fire it.
470
515
  *
@@ -528,7 +573,7 @@ export declare function placementRequiresFiringPin(strategy: HostStrategy): bool
528
573
  export interface JobEligibilityResult {
529
574
  /** Full human message, e.g. "Job 'NAME' can only run on: a, b". */
530
575
  message: string;
531
- /** One-line copy-paste suggestion, e.g. "agents routines run NAME --host a". */
576
+ /** One-line copy-paste suggestion, e.g. "agents routines run NAME --device a". */
532
577
  suggestion: string;
533
578
  /** Comma-separated allowed devices label, e.g. "a, b". */
534
579
  allowedLabel: string;
@@ -541,7 +586,7 @@ export interface JobEligibilityResult {
541
586
  * construction so manual run, executeJob, and executeJobDetached stay in
542
587
  * sync. Scheduler/webhook/overdue paths continue to use jobRunsOnThisDevice.
543
588
  */
544
- export declare function checkJobDeviceEligibility(config: Pick<JobConfig, 'name' | 'devices'>): JobEligibilityResult | null;
589
+ export declare function checkJobDeviceEligibility(config: Pick<JobConfig, 'name' | 'devices' | 'dispatchedBy'>): JobEligibilityResult | null;
545
590
  /**
546
591
  * List all job configs, scanning project > user > system routine dirs.
547
592
  * Higher layers shadow lower ones of the same name (first-seen wins): a project