@phnx-labs/agents-cli 1.22.35 → 1.22.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/CHANGELOG.md +708 -2
  2. package/README.md +83 -43
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.d.ts +15 -0
  5. package/dist/bootstrap.js +1212 -0
  6. package/dist/commands/accounts.d.ts +4 -11
  7. package/dist/commands/accounts.js +159 -27
  8. package/dist/commands/artifacts-setup.d.ts +53 -0
  9. package/dist/commands/artifacts-setup.js +161 -0
  10. package/dist/commands/artifacts.d.ts +18 -0
  11. package/dist/commands/artifacts.js +58 -0
  12. package/dist/commands/attach.js +17 -4
  13. package/dist/commands/audit.d.ts +8 -8
  14. package/dist/commands/audit.js +57 -47
  15. package/dist/commands/browser.js +17 -11
  16. package/dist/commands/cloud.js +1 -1
  17. package/dist/commands/commands.js +0 -11
  18. package/dist/commands/computer-actions.js +33 -0
  19. package/dist/commands/computer-sessions-picker.d.ts +21 -0
  20. package/dist/commands/computer-sessions-picker.js +141 -0
  21. package/dist/commands/computer.d.ts +16 -0
  22. package/dist/commands/computer.js +37 -0
  23. package/dist/commands/config.d.ts +17 -0
  24. package/dist/commands/config.js +401 -0
  25. package/dist/commands/cp.d.ts +84 -0
  26. package/dist/commands/cp.js +231 -0
  27. package/dist/commands/daemon.js +243 -14
  28. package/dist/commands/detach-core.d.ts +1 -1
  29. package/dist/commands/detach-core.js +12 -2
  30. package/dist/commands/detach.js +1 -1
  31. package/dist/commands/doctor.js +64 -5
  32. package/dist/commands/events.d.ts +24 -12
  33. package/dist/commands/events.js +144 -28
  34. package/dist/commands/exec.js +86 -42
  35. package/dist/commands/feed.d.ts +1 -1
  36. package/dist/commands/feed.js +16 -8
  37. package/dist/commands/focus.d.ts +33 -1
  38. package/dist/commands/focus.js +177 -17
  39. package/dist/commands/funnel.d.ts +1 -1
  40. package/dist/commands/go.js +9 -8
  41. package/dist/commands/harness-wizard.d.ts +1 -0
  42. package/dist/commands/harness-wizard.js +18 -20
  43. package/dist/commands/harness.d.ts +2 -0
  44. package/dist/commands/harness.js +59 -26
  45. package/dist/commands/hooks.js +0 -11
  46. package/dist/commands/inspect.d.ts +94 -1
  47. package/dist/commands/inspect.js +866 -40
  48. package/dist/commands/lease.d.ts +4 -4
  49. package/dist/commands/lease.js +6 -6
  50. package/dist/commands/login.js +0 -13
  51. package/dist/commands/logs.d.ts +2 -2
  52. package/dist/commands/logs.js +2 -2
  53. package/dist/commands/mcp.js +7 -2
  54. package/dist/commands/memory.js +7 -2
  55. package/dist/commands/message.d.ts +1 -1
  56. package/dist/commands/message.js +37 -6
  57. package/dist/commands/models.js +71 -0
  58. package/dist/commands/monitors.d.ts +0 -1
  59. package/dist/commands/monitors.js +199 -16
  60. package/dist/commands/open.d.ts +18 -0
  61. package/dist/commands/open.js +57 -0
  62. package/dist/commands/packages.js +2 -107
  63. package/dist/commands/plugins.js +6 -3
  64. package/dist/commands/profiles.d.ts +1 -0
  65. package/dist/commands/profiles.js +16 -160
  66. package/dist/commands/projects.d.ts +28 -0
  67. package/dist/commands/projects.js +125 -5
  68. package/dist/commands/reconnect.js +9 -3
  69. package/dist/commands/repo.js +42 -1
  70. package/dist/commands/resource-view.d.ts +17 -0
  71. package/dist/commands/resource-view.js +36 -14
  72. package/dist/commands/resume.js +9 -2
  73. package/dist/commands/routines.d.ts +0 -2
  74. package/dist/commands/routines.js +473 -289
  75. package/dist/commands/secrets.js +52 -9
  76. package/dist/commands/sessions-export.d.ts +47 -0
  77. package/dist/commands/sessions-export.js +116 -6
  78. package/dist/commands/sessions-import.d.ts +17 -0
  79. package/dist/commands/sessions-import.js +114 -6
  80. package/dist/commands/sessions-migrate.d.ts +27 -0
  81. package/dist/commands/sessions-migrate.js +50 -9
  82. package/dist/commands/sessions-picker.js +23 -4
  83. package/dist/commands/sessions-resume.d.ts +5 -3
  84. package/dist/commands/sessions-resume.js +30 -8
  85. package/dist/commands/sessions-watch.d.ts +2 -0
  86. package/dist/commands/sessions-watch.js +53 -0
  87. package/dist/commands/sessions.d.ts +66 -3
  88. package/dist/commands/sessions.js +298 -47
  89. package/dist/commands/setup-browser.js +2 -2
  90. package/dist/commands/setup-mine.js +0 -1
  91. package/dist/commands/setup-preferences.js +1 -1
  92. package/dist/commands/setup.d.ts +5 -0
  93. package/dist/commands/setup.js +31 -5
  94. package/dist/commands/share.d.ts +87 -7
  95. package/dist/commands/share.js +320 -58
  96. package/dist/commands/skills.js +0 -11
  97. package/dist/commands/snapshot.js +1 -1
  98. package/dist/commands/ssh.d.ts +18 -0
  99. package/dist/commands/ssh.js +549 -210
  100. package/dist/commands/sync.d.ts +5 -0
  101. package/dist/commands/sync.js +212 -32
  102. package/dist/commands/teams.d.ts +13 -0
  103. package/dist/commands/teams.js +187 -30
  104. package/dist/commands/tickets.d.ts +2 -0
  105. package/dist/commands/tickets.js +43 -0
  106. package/dist/commands/update.js +2 -4
  107. package/dist/commands/usage.js +5 -3
  108. package/dist/commands/versions.js +11 -5
  109. package/dist/commands/view.js +15 -7
  110. package/dist/commands/webhook.d.ts +2 -2
  111. package/dist/commands/webhook.js +7 -7
  112. package/dist/index.d.ts +18 -2
  113. package/dist/index.js +53 -1264
  114. package/dist/lib/account-catalog.d.ts +18 -0
  115. package/dist/lib/account-catalog.js +38 -0
  116. package/dist/lib/account-provider-registry.d.ts +18 -0
  117. package/dist/lib/account-provider-registry.js +59 -0
  118. package/dist/lib/account-registry.d.ts +40 -0
  119. package/dist/lib/account-registry.js +239 -0
  120. package/dist/lib/account-schema.d.ts +44 -0
  121. package/dist/lib/account-schema.js +91 -0
  122. package/dist/lib/account-state-service.d.ts +21 -0
  123. package/dist/lib/account-state-service.js +60 -0
  124. package/dist/lib/activity.d.ts +1 -1
  125. package/dist/lib/activity.js +8 -1
  126. package/dist/lib/add-dir.d.ts +80 -0
  127. package/dist/lib/add-dir.js +241 -0
  128. package/dist/lib/agent-cli-commands.d.ts +11 -0
  129. package/dist/lib/agent-cli-commands.js +30 -0
  130. package/dist/lib/agents.js +44 -36
  131. package/dist/lib/answer-router.js +1 -1
  132. package/dist/lib/audit/log.d.ts +10 -27
  133. package/dist/lib/audit/log.js +20 -33
  134. package/dist/lib/auth-health.d.ts +9 -2
  135. package/dist/lib/auth-health.js +42 -9
  136. package/dist/lib/auto-pull-worker.js +7 -3
  137. package/dist/lib/auto-pull.d.ts +44 -3
  138. package/dist/lib/auto-pull.js +88 -5
  139. package/dist/lib/binary-shadow.d.ts +28 -0
  140. package/dist/lib/binary-shadow.js +121 -0
  141. package/dist/lib/brand.js +9 -3
  142. package/dist/lib/browser/hygiene.d.ts +90 -0
  143. package/dist/lib/browser/hygiene.js +146 -0
  144. package/dist/lib/browser/ipc.d.ts +30 -0
  145. package/dist/lib/browser/ipc.js +93 -4
  146. package/dist/lib/browser/profiles.d.ts +3 -2
  147. package/dist/lib/browser/profiles.js +65 -22
  148. package/dist/lib/browser/service.d.ts +79 -1
  149. package/dist/lib/browser/service.js +231 -14
  150. package/dist/lib/browser/sessions-list.d.ts +22 -2
  151. package/dist/lib/browser/sessions-list.js +54 -6
  152. package/dist/lib/browser/stream.d.ts +2 -0
  153. package/dist/lib/browser/stream.js +1 -0
  154. package/dist/lib/browser/types.d.ts +63 -1
  155. package/dist/lib/byok-usage.d.ts +9 -2
  156. package/dist/lib/byok-usage.js +133 -31
  157. package/dist/lib/cli-entry.d.ts +11 -0
  158. package/dist/lib/cli-entry.js +46 -0
  159. package/dist/lib/cloud/host.d.ts +1 -1
  160. package/dist/lib/cloud/host.js +2 -2
  161. package/dist/lib/cloud/registry.js +1 -1
  162. package/dist/lib/cloud/types.d.ts +1 -1
  163. package/dist/lib/commands.d.ts +2 -0
  164. package/dist/lib/commands.js +2 -0
  165. package/dist/lib/computer/sessions-list.d.ts +150 -0
  166. package/dist/lib/computer/sessions-list.js +363 -0
  167. package/dist/lib/config-keys.d.ts +86 -0
  168. package/dist/lib/config-keys.js +193 -0
  169. package/dist/lib/config-machine-keys.d.ts +12 -0
  170. package/dist/lib/config-machine-keys.js +17 -0
  171. package/dist/lib/config-transfer.js +3 -4
  172. package/dist/lib/crabbox/cli.d.ts +2 -2
  173. package/dist/lib/crabbox/cli.js +3 -3
  174. package/dist/lib/daemon-health.d.ts +22 -0
  175. package/dist/lib/daemon-health.js +33 -3
  176. package/dist/lib/daemon-services.d.ts +50 -0
  177. package/dist/lib/daemon-services.js +147 -0
  178. package/dist/lib/daemon-ticks.d.ts +35 -66
  179. package/dist/lib/daemon-ticks.js +77 -159
  180. package/dist/lib/daemon.d.ts +72 -4
  181. package/dist/lib/daemon.js +792 -202
  182. package/dist/lib/deeplink/register.d.ts +49 -0
  183. package/dist/lib/deeplink/register.js +253 -0
  184. package/dist/lib/deeplink/url.d.ts +34 -0
  185. package/dist/lib/deeplink/url.js +78 -0
  186. package/dist/lib/device-config.d.ts +124 -29
  187. package/dist/lib/device-config.js +346 -141
  188. package/dist/lib/devices/config-migration.d.ts +42 -0
  189. package/dist/lib/devices/config-migration.js +203 -0
  190. package/dist/lib/devices/connect.js +14 -7
  191. package/dist/lib/devices/doctor-findings.d.ts +4 -1
  192. package/dist/lib/devices/doctor-findings.js +20 -1
  193. package/dist/lib/devices/harness-inventory.d.ts +11 -2
  194. package/dist/lib/devices/harness-inventory.js +0 -0
  195. package/dist/lib/devices/health.d.ts +1 -1
  196. package/dist/lib/devices/health.js +1 -1
  197. package/dist/lib/devices/pending.d.ts +22 -1
  198. package/dist/lib/devices/pending.js +67 -2
  199. package/dist/lib/devices/registry.d.ts +0 -25
  200. package/dist/lib/devices/registry.js +2 -83
  201. package/dist/lib/devices/resolve-profile.d.ts +20 -0
  202. package/dist/lib/devices/resolve-profile.js +44 -0
  203. package/dist/lib/devices/rollout-verify.d.ts +101 -0
  204. package/dist/lib/devices/rollout-verify.js +193 -0
  205. package/dist/lib/devices/ssh-config.js +10 -6
  206. package/dist/lib/event-families.d.ts +28 -0
  207. package/dist/lib/event-families.js +159 -0
  208. package/dist/lib/event-stream.d.ts +9 -1
  209. package/dist/lib/event-stream.js +26 -9
  210. package/dist/lib/events.d.ts +9 -1
  211. package/dist/lib/events.js +21 -6
  212. package/dist/lib/exec.d.ts +30 -0
  213. package/dist/lib/exec.js +219 -121
  214. package/dist/lib/feed-broadcast.d.ts +25 -0
  215. package/dist/lib/feed-broadcast.js +33 -0
  216. package/dist/lib/feed.d.ts +1 -1
  217. package/dist/lib/feed.js +12 -2
  218. package/dist/lib/fleet/capture.js +15 -0
  219. package/dist/lib/fleet/manifest.js +9 -0
  220. package/dist/lib/fleet/types.d.ts +9 -0
  221. package/dist/lib/fleet-status.js +10 -8
  222. package/dist/lib/git.d.ts +21 -7
  223. package/dist/lib/git.js +122 -18
  224. package/dist/lib/hosts/dispatch.d.ts +8 -3
  225. package/dist/lib/hosts/dispatch.js +29 -3
  226. package/dist/lib/hosts/logs.d.ts +2 -2
  227. package/dist/lib/hosts/logs.js +2 -2
  228. package/dist/lib/hosts/option.js +1 -1
  229. package/dist/lib/hosts/passthrough.d.ts +2 -3
  230. package/dist/lib/hosts/passthrough.js +10 -24
  231. package/dist/lib/hosts/progress.js +2 -2
  232. package/dist/lib/hosts/providers/devices.d.ts +3 -3
  233. package/dist/lib/hosts/providers/devices.js +18 -12
  234. package/dist/lib/hosts/ready.d.ts +5 -1
  235. package/dist/lib/hosts/ready.js +9 -3
  236. package/dist/lib/hosts/reconcile.d.ts +2 -2
  237. package/dist/lib/hosts/reconcile.js +2 -2
  238. package/dist/lib/hosts/registry.js +14 -9
  239. package/dist/lib/hosts/remote-cmd.js +1 -1
  240. package/dist/lib/hosts/remote-os.d.ts +9 -6
  241. package/dist/lib/hosts/remote-os.js +13 -6
  242. package/dist/lib/hosts/routing-flag.d.ts +23 -0
  243. package/dist/lib/hosts/routing-flag.js +41 -0
  244. package/dist/lib/hosts/run-target.js +1 -1
  245. package/dist/lib/hosts/session-index.js +1 -1
  246. package/dist/lib/hosts/tasks.d.ts +6 -6
  247. package/dist/lib/hosts/tasks.js +4 -4
  248. package/dist/lib/hosts/types.js +1 -2
  249. package/dist/lib/installations/resolve.js +2 -8
  250. package/dist/lib/isolation-boundary-report.js +0 -1
  251. package/dist/lib/linear-cache.d.ts +7 -0
  252. package/dist/lib/linear-cache.js +31 -4
  253. package/dist/lib/linear-project-counts.d.ts +1 -1
  254. package/dist/lib/linear-project-counts.js +2 -3
  255. package/dist/lib/linear-projects.js +1 -1
  256. package/dist/lib/loop.d.ts +2 -2
  257. package/dist/lib/loop.js +1 -1
  258. package/dist/lib/mailbox-target.d.ts +1 -1
  259. package/dist/lib/mailbox-target.js +1 -1
  260. package/dist/lib/manifest.js +7 -4
  261. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  262. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  263. package/dist/lib/menubar/snapshot.d.ts +3 -2
  264. package/dist/lib/menubar/snapshot.js +6 -2
  265. package/dist/lib/migrate-fold.d.ts +33 -0
  266. package/dist/lib/migrate-fold.js +130 -0
  267. package/dist/lib/migrate.d.ts +34 -34
  268. package/dist/lib/migrate.js +106 -128
  269. package/dist/lib/model-tier-overrides.d.ts +1 -1
  270. package/dist/lib/model-tier-overrides.js +15 -2
  271. package/dist/lib/monitors/config.d.ts +17 -3
  272. package/dist/lib/monitors/config.js +52 -18
  273. package/dist/lib/monitors/engine.d.ts +32 -1
  274. package/dist/lib/monitors/engine.js +94 -11
  275. package/dist/lib/monitors/fingerprint.d.ts +46 -0
  276. package/dist/lib/monitors/fingerprint.js +100 -0
  277. package/dist/lib/monitors/remote.d.ts +47 -0
  278. package/dist/lib/monitors/remote.js +81 -0
  279. package/dist/lib/monitors/state.d.ts +39 -0
  280. package/dist/lib/monitors/state.js +56 -0
  281. package/dist/lib/onepassword.d.ts +17 -0
  282. package/dist/lib/onepassword.js +52 -22
  283. package/dist/lib/open-url.js +1 -1
  284. package/dist/lib/picker.d.ts +14 -2
  285. package/dist/lib/picker.js +35 -13
  286. package/dist/lib/placement.d.ts +1 -1
  287. package/dist/lib/placement.js +1 -1
  288. package/dist/lib/pr-land-detach.d.ts +32 -0
  289. package/dist/lib/pr-land-detach.js +79 -0
  290. package/dist/lib/profiles-presets.js +13 -0
  291. package/dist/lib/profiles.d.ts +2 -0
  292. package/dist/lib/profiles.js +44 -1
  293. package/dist/lib/project-focus.d.ts +1 -1
  294. package/dist/lib/project-focus.js +1 -1
  295. package/dist/lib/project-probe.d.ts +4 -1
  296. package/dist/lib/project-probe.js +5 -4
  297. package/dist/lib/project-resources.js +2 -18
  298. package/dist/lib/project-root.d.ts +16 -0
  299. package/dist/lib/project-root.js +20 -2
  300. package/dist/lib/projects.d.ts +20 -1
  301. package/dist/lib/projects.js +78 -1
  302. package/dist/lib/refresh-coordinator.d.ts +24 -0
  303. package/dist/lib/refresh-coordinator.js +52 -0
  304. package/dist/lib/registry.d.ts +2 -2
  305. package/dist/lib/registry.js +1 -1
  306. package/dist/lib/resource-aliases.d.ts +13 -0
  307. package/dist/lib/resource-aliases.js +26 -0
  308. package/dist/lib/resources/types.d.ts +1 -1
  309. package/dist/lib/resources.d.ts +16 -0
  310. package/dist/lib/resources.js +66 -10
  311. package/dist/lib/rotate.d.ts +3 -4
  312. package/dist/lib/rotate.js +4 -5
  313. package/dist/lib/routine-activation.d.ts +22 -2
  314. package/dist/lib/routine-activation.js +63 -16
  315. package/dist/lib/routine-context.d.ts +9 -2
  316. package/dist/lib/routine-context.js +21 -6
  317. package/dist/lib/routines-project.d.ts +58 -48
  318. package/dist/lib/routines-project.js +190 -159
  319. package/dist/lib/routines.d.ts +44 -7
  320. package/dist/lib/routines.js +92 -34
  321. package/dist/lib/run-defaults.d.ts +5 -0
  322. package/dist/lib/run-defaults.js +11 -1
  323. package/dist/lib/runner.d.ts +10 -8
  324. package/dist/lib/runner.js +122 -39
  325. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  326. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  327. package/dist/lib/secrets/agent.d.ts +10 -3
  328. package/dist/lib/secrets/agent.js +43 -8
  329. package/dist/lib/secrets/bundles.d.ts +1 -0
  330. package/dist/lib/secrets/bundles.js +92 -80
  331. package/dist/lib/secrets/index.js +32 -9
  332. package/dist/lib/secrets/install-helper.d.ts +4 -4
  333. package/dist/lib/secrets/install-helper.js +4 -4
  334. package/dist/lib/secrets/push.d.ts +40 -6
  335. package/dist/lib/secrets/push.js +58 -11
  336. package/dist/lib/secrets/read-backoff.d.ts +1 -1
  337. package/dist/lib/secrets/read-backoff.js +1 -1
  338. package/dist/lib/secrets/remote.d.ts +24 -0
  339. package/dist/lib/secrets/remote.js +54 -4
  340. package/dist/lib/self-update.d.ts +108 -0
  341. package/dist/lib/self-update.js +276 -1
  342. package/dist/lib/session/active.d.ts +89 -5
  343. package/dist/lib/session/active.js +165 -10
  344. package/dist/lib/session/actor-sidecar.d.ts +1 -1
  345. package/dist/lib/session/actor-sidecar.js +2 -1
  346. package/dist/lib/session/db.d.ts +176 -2
  347. package/dist/lib/session/db.js +484 -17
  348. package/dist/lib/session/discover.js +27 -4
  349. package/dist/lib/session/hook-sessions.js +2 -2
  350. package/dist/lib/session/host-link.d.ts +2 -2
  351. package/dist/lib/session/host-link.js +2 -2
  352. package/dist/lib/session/pid-registry.d.ts +21 -0
  353. package/dist/lib/session/pid-registry.js +57 -0
  354. package/dist/lib/session/remote-active.js +15 -1
  355. package/dist/lib/session/remote.js +2 -2
  356. package/dist/lib/session/session-cache.d.ts +14 -7
  357. package/dist/lib/session/session-cache.js +51 -9
  358. package/dist/lib/session/state.d.ts +2 -2
  359. package/dist/lib/session/state.js +1 -1
  360. package/dist/lib/session/sync/config.d.ts +10 -10
  361. package/dist/lib/session/sync/config.js +10 -10
  362. package/dist/lib/session/sync/r2.d.ts +34 -0
  363. package/dist/lib/session/sync/r2.js +123 -0
  364. package/dist/lib/session/throughput.d.ts +1 -1
  365. package/dist/lib/session/throughput.js +1 -1
  366. package/dist/lib/session/types.d.ts +23 -1
  367. package/dist/lib/session/types.js +15 -0
  368. package/dist/lib/session/viewing-in.d.ts +1 -1
  369. package/dist/lib/session/viewing-in.js +1 -1
  370. package/dist/lib/session/watch.d.ts +101 -0
  371. package/dist/lib/session/watch.js +242 -0
  372. package/dist/lib/share/analytics.js +1 -1
  373. package/dist/lib/share/capture.d.ts +1 -1
  374. package/dist/lib/share/capture.js +3 -3
  375. package/dist/lib/share/config.d.ts +6 -2
  376. package/dist/lib/share/config.js +6 -5
  377. package/dist/lib/share/delete.js +3 -3
  378. package/dist/lib/share/provision.d.ts +38 -0
  379. package/dist/lib/share/provision.js +51 -1
  380. package/dist/lib/share/publish.d.ts +40 -1
  381. package/dist/lib/share/publish.js +119 -6
  382. package/dist/lib/share/worker-template.d.ts +12 -1
  383. package/dist/lib/share/worker-template.js +83 -7
  384. package/dist/lib/shims.d.ts +17 -3
  385. package/dist/lib/shims.js +98 -26
  386. package/dist/lib/signin-badge.js +3 -2
  387. package/dist/lib/skills.js +2 -0
  388. package/dist/lib/smart-launch.d.ts +8 -8
  389. package/dist/lib/smart-launch.js +80 -59
  390. package/dist/lib/ssh-exec.d.ts +1 -1
  391. package/dist/lib/ssh-exec.js +1 -1
  392. package/dist/lib/staleness/prune.d.ts +88 -0
  393. package/dist/lib/staleness/prune.js +69 -0
  394. package/dist/lib/staleness/registry.d.ts +1 -1
  395. package/dist/lib/staleness/writers/commands.js +34 -2
  396. package/dist/lib/staleness/writers/hooks.js +16 -0
  397. package/dist/lib/staleness/writers/kinds.d.ts +6 -0
  398. package/dist/lib/staleness/writers/skills.js +19 -0
  399. package/dist/lib/staleness/writers/subagents.d.ts +2 -3
  400. package/dist/lib/staleness/writers/subagents.js +0 -4
  401. package/dist/lib/staleness/writers/types.d.ts +32 -0
  402. package/dist/lib/startup/command-registry.d.ts +16 -14
  403. package/dist/lib/startup/command-registry.js +26 -37
  404. package/dist/lib/startup/dev-build.d.ts +10 -0
  405. package/dist/lib/startup/dev-build.js +13 -1
  406. package/dist/lib/startup/spellcheck.d.ts +18 -0
  407. package/dist/lib/startup/spellcheck.js +45 -0
  408. package/dist/lib/state.d.ts +36 -9
  409. package/dist/lib/state.js +96 -46
  410. package/dist/lib/subagents-registry.d.ts +0 -7
  411. package/dist/lib/subagents-registry.js +38 -65
  412. package/dist/lib/subagents.d.ts +0 -33
  413. package/dist/lib/subagents.js +0 -75
  414. package/dist/lib/sync-umbrella.js +1 -1
  415. package/dist/lib/teams/agents.d.ts +41 -1
  416. package/dist/lib/teams/agents.js +187 -9
  417. package/dist/lib/teams/api.d.ts +11 -1
  418. package/dist/lib/teams/api.js +11 -2
  419. package/dist/lib/teams/delivery.d.ts +41 -0
  420. package/dist/lib/teams/delivery.js +60 -0
  421. package/dist/lib/teams/registry.d.ts +14 -0
  422. package/dist/lib/teams/registry.js +51 -1
  423. package/dist/lib/teams/remoteWorktree.d.ts +19 -0
  424. package/dist/lib/teams/remoteWorktree.js +29 -0
  425. package/dist/lib/teams/scheduler.d.ts +1 -1
  426. package/dist/lib/teams/scheduler.js +1 -1
  427. package/dist/lib/teams/worktree.d.ts +24 -0
  428. package/dist/lib/teams/worktree.js +42 -0
  429. package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
  430. package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
  431. package/dist/lib/terminal/engine.d.ts +4 -0
  432. package/dist/lib/terminal/engine.js +8 -2
  433. package/dist/lib/terminal/types.d.ts +21 -2
  434. package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
  435. package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
  436. package/dist/lib/tickets/list.d.ts +53 -0
  437. package/dist/lib/tickets/list.js +153 -0
  438. package/dist/lib/tmux/orphan-reap.d.ts +316 -0
  439. package/dist/lib/tmux/orphan-reap.js +638 -0
  440. package/dist/lib/tmux/session.d.ts +68 -3
  441. package/dist/lib/tmux/session.js +96 -5
  442. package/dist/lib/triggers/handlers.d.ts +20 -0
  443. package/dist/lib/triggers/handlers.js +16 -1
  444. package/dist/lib/triggers/webhook.js +10 -0
  445. package/dist/lib/types.d.ts +47 -35
  446. package/dist/lib/usage-fleet.d.ts +32 -0
  447. package/dist/lib/usage-fleet.js +125 -0
  448. package/dist/lib/usage-refresh.d.ts +3 -3
  449. package/dist/lib/usage-refresh.js +19 -16
  450. package/dist/lib/usage.d.ts +51 -47
  451. package/dist/lib/usage.js +145 -153
  452. package/dist/lib/version-duplicates.js +2 -2
  453. package/dist/lib/versions.d.ts +56 -13
  454. package/dist/lib/versions.js +195 -45
  455. package/dist/lib/watchdog/log.d.ts +1 -1
  456. package/dist/lib/watchdog/log.js +3 -3
  457. package/dist/lib/watchdog/rotate.d.ts +6 -6
  458. package/dist/lib/watchdog/rotate.js +6 -6
  459. package/dist/lib/workflows.d.ts +1 -1
  460. package/dist/lib/yaml-io.d.ts +47 -0
  461. package/dist/lib/yaml-io.js +55 -0
  462. package/package.json +2 -1
  463. package/scripts/install-helper.js +2 -2
  464. package/scripts/postinstall.js +23 -20
  465. package/dist/commands/defaults.d.ts +0 -7
  466. package/dist/commands/defaults.js +0 -107
  467. package/dist/commands/export.d.ts +0 -11
  468. package/dist/commands/export.js +0 -215
  469. package/dist/commands/helper.d.ts +0 -12
  470. package/dist/commands/helper.js +0 -87
  471. package/dist/commands/hosts.d.ts +0 -11
  472. package/dist/commands/hosts.js +0 -330
  473. package/dist/commands/lock.d.ts +0 -12
  474. package/dist/commands/lock.js +0 -70
  475. package/dist/commands/pull.d.ts +0 -17
  476. package/dist/commands/pull.js +0 -39
  477. package/dist/commands/push.d.ts +0 -14
  478. package/dist/commands/push.js +0 -30
  479. package/dist/commands/set.d.ts +0 -15
  480. package/dist/commands/set.js +0 -79
  481. package/dist/commands/setup-share.d.ts +0 -17
  482. package/dist/commands/setup-share.js +0 -85
  483. package/dist/commands/wallet.d.ts +0 -20
  484. package/dist/commands/wallet.js +0 -216
  485. package/dist/commands/worktree.d.ts +0 -19
  486. package/dist/commands/worktree.js +0 -272
  487. package/dist/lib/account-labels.d.ts +0 -24
  488. package/dist/lib/account-labels.js +0 -72
  489. package/dist/lib/auto-dispatch-linear.d.ts +0 -18
  490. package/dist/lib/auto-dispatch-linear.js +0 -107
  491. package/dist/lib/auto-dispatch-provider.d.ts +0 -10
  492. package/dist/lib/auto-dispatch-provider.js +0 -30
  493. package/dist/lib/auto-dispatch.d.ts +0 -93
  494. package/dist/lib/auto-dispatch.js +0 -128
  495. package/dist/lib/export.d.ts +0 -72
  496. package/dist/lib/export.js +0 -269
  497. package/dist/lib/lock.d.ts +0 -93
  498. package/dist/lib/lock.js +0 -207
  499. package/dist/lib/wallet/index.d.ts +0 -78
  500. package/dist/lib/wallet/index.js +0 -253
@@ -33,7 +33,7 @@ import { remoteShellFor } from '../hosts/remote-cmd.js';
33
33
  import { resolveRemoteOsSync } from '../hosts/remote-os.js';
34
34
  import { pullRemoteLogDelta, REMOTE_MIRROR_MAX_BYTES } from '../hosts/progress.js';
35
35
  import { createRemoteWorktree, ensureRemoteRepo } from './remoteWorktree.js';
36
- import { getTeam } from './registry.js';
36
+ import { getTeam, isTeamDisbanded } from './registry.js';
37
37
  import { resolvePlacement, classifyExclusions, NoViableDeviceError } from './scheduler.js';
38
38
  import { probePoolSignals } from './placement-probe.js';
39
39
  import { readMaxConcurrentCaps } from '../device-config.js';
@@ -321,6 +321,24 @@ function extractTimestamp(raw) {
321
321
  }
322
322
  return null;
323
323
  }
324
+ /**
325
+ * Atomic JSON write: writes to a unique sibling tmp file then renames over the
326
+ * target. rename(2) is atomic on POSIX, so a process killed mid-write leaves
327
+ * either the previous valid meta.json or the new one, never a torn,
328
+ * unparseable file (RUSH-2429). Mirrors the identical pattern already used by
329
+ * `teams/registry.ts`'s `atomicWriteJson`.
330
+ */
331
+ async function atomicWriteJson(p, data) {
332
+ const tmp = `${p}.tmp.${process.pid}.${randomUUID()}`;
333
+ await fs.writeFile(tmp, JSON.stringify(data, null, 2));
334
+ try {
335
+ await fs.rename(tmp, p);
336
+ }
337
+ catch (err) {
338
+ await fs.unlink(tmp).catch(() => { });
339
+ throw err;
340
+ }
341
+ }
324
342
  /** Resolve a mode string to a validated Mode, falling back to the given default. */
325
343
  export function resolveMode(requestedMode, defaultMode = 'plan') {
326
344
  const normalizedDefault = normalizeModeValue(defaultMode);
@@ -526,6 +544,13 @@ export class AgentProcess {
526
544
  // Worktree isolation: when non-null, this teammate runs in its own git worktree.
527
545
  worktreeName = null;
528
546
  worktreePath = null;
547
+ // The team's `--project`, if it has one. Stored as the NAME, not as resolved
548
+ // directories: an unpinned teammate on a `--devices` pool is placed at LAUNCH
549
+ // (maybeSchedulePlacement), so grants resolved at add time would carry this
550
+ // box's absolute paths onto whatever host the scheduler later picked — and
551
+ // would already have dropped any directory that exists only there. Resolving
552
+ // per launch is what makes local and remote placement both correct.
553
+ project = null;
529
554
  // Distributed teams: when hostName is non-null, this teammate runs on another
530
555
  // machine over SSH (the "remote-host" backend), not as a local process. These
531
556
  // are set post-construction (like startTime/pid) — placement config at add
@@ -930,6 +955,18 @@ export class AgentProcess {
930
955
  }
931
956
  }
932
957
  async saveMeta() {
958
+ // RUSH-2450: a long-lived supervisor (teams start --watch) holds AgentProcess
959
+ // objects in memory. After another process disbands the team and deletes
960
+ // every meta.json, the supervisor's next status refresh would re-write those
961
+ // files via saveMeta — resurrecting PENDING teammates the operator just
962
+ // disbanded, and making `teams start` re-launch already-merged work. Refuse
963
+ // when a disband tombstone is present (set by removeTeam / markTeamDisbanded).
964
+ // Not gated on "team not in registry" alone: tests and mid-add paths write
965
+ // meta before/without a registry entry, and those must keep working.
966
+ if (this.taskName && (await isTeamDisbanded(this.taskName))) {
967
+ debug(`saveMeta: refusing to re-persist ${this.agentId} — team '${this.taskName}' was disbanded`);
968
+ return;
969
+ }
933
970
  const agentDir = await this.getAgentDir();
934
971
  await fs.mkdir(agentDir, { recursive: true });
935
972
  const meta = {
@@ -963,6 +1000,7 @@ export class AgentProcess {
963
1000
  cloud_branch: this.cloudBranch,
964
1001
  worktree_name: this.worktreeName,
965
1002
  worktree_path: this.worktreePath,
1003
+ project: this.project,
966
1004
  host_name: this.hostName,
967
1005
  host_target: this.hostTarget,
968
1006
  host_identity_file: this.hostIdentityFile,
@@ -973,20 +1011,61 @@ export class AgentProcess {
973
1011
  remote_log_offset: this.remoteLogOffset,
974
1012
  };
975
1013
  const metaPath = await this.getMetaPath();
976
- await fs.writeFile(metaPath, JSON.stringify(meta, null, 2));
1014
+ await atomicWriteJson(metaPath, meta);
1015
+ }
1016
+ /**
1017
+ * Rename an unreadable meta.json out of the way so it stops silently
1018
+ * masquerading as "no record" (RUSH-2429). Before saveMeta() wrote atomically,
1019
+ * a process killed mid-write left a truncated, unparseable meta.json that
1020
+ * loadFromDisk() returned null for -- indistinguishable from ENOENT -- so
1021
+ * retention (loadExistingAgents/rescanFromDisk) never reaped it and
1022
+ * isWorktreeClaimed() (which reads meta.json directly, not through this
1023
+ * method) failed CLOSED on it forever: it scans every record and answers
1024
+ * "claimed" for every worktree name in every team the first time it cannot
1025
+ * read one. Quarantining removes meta.json so the NEXT read of this record
1026
+ * sees ENOENT (genuinely absent) instead of "unreadable" -- that is what lets
1027
+ * isWorktreeClaimed's fail-closed guard recover once the corrupt record is
1028
+ * gone, without weakening the guard itself for a record that is still
1029
+ * present-but-unreadable at decision time.
1030
+ *
1031
+ * Best-effort: if the rename itself fails (e.g. EACCES on the directory),
1032
+ * the record is left in place and the fail-closed guard keeps protecting
1033
+ * worktree removal -- quarantine only ever ADDS a recovery path.
1034
+ */
1035
+ static async quarantineCorruptMeta(metaPath, cause) {
1036
+ const quarantinePath = `${metaPath}.corrupt`;
1037
+ const reason = cause instanceof Error ? cause.message : String(cause);
1038
+ try {
1039
+ await fs.rename(metaPath, quarantinePath);
1040
+ console.warn(`[teams] quarantined unreadable meta.json (${reason}): ${metaPath} -> ${quarantinePath}`);
1041
+ }
1042
+ catch (renameErr) {
1043
+ console.warn(`[teams] found unreadable meta.json but could not quarantine it (${reason}): ${metaPath}: ` +
1044
+ `${renameErr?.message ?? renameErr}`);
1045
+ }
977
1046
  }
978
1047
  static async loadFromDisk(agentId, baseDir = null) {
979
1048
  const base = baseDir || await getAgentsDir();
980
1049
  const agentDir = path.join(base, agentId);
981
1050
  const metaPath = path.join(agentDir, 'meta.json');
1051
+ let metaContent;
982
1052
  try {
983
- await fs.access(metaPath);
1053
+ metaContent = await fs.readFile(metaPath, 'utf-8');
984
1054
  }
985
- catch {
1055
+ catch (err) {
1056
+ // A READ error is not a corrupt record. ENOENT proves the record is
1057
+ // genuinely ABSENT; anything else -- EACCES, EIO, a transient EMFILE
1058
+ // under fd pressure -- means the file exists and could not be read THIS
1059
+ // time, but its contents are intact. We MUST NOT quarantine (rename) it:
1060
+ // renaming a valid record away is exactly the fail-open that RUSH-2429
1061
+ // forbids -- isWorktreeClaimed() reads meta.json directly and fails
1062
+ // CLOSED on the same read error (safe), but a rename here would delete
1063
+ // the record it relies on and turn a live teammate's worktree into
1064
+ // "unclaimed", re-arming `git worktree remove --force` over uncommitted
1065
+ // work. Return null (skip this scan); the file stays for the next read.
986
1066
  return null;
987
1067
  }
988
1068
  try {
989
- const metaContent = await fs.readFile(metaPath, 'utf-8');
990
1069
  const meta = JSON.parse(metaContent);
991
1070
  // Legacy teammates may have mode='ralph', 'cloud', or 'full' from before
992
1071
  // modes were narrowed/renamed. Coerce to the closest current mode so they
@@ -1026,9 +1105,15 @@ export class AgentProcess {
1026
1105
  agent.remoteLog = meta.remote_log || null;
1027
1106
  agent.remoteExit = meta.remote_exit || null;
1028
1107
  agent.remoteLogOffset = typeof meta.remote_log_offset === 'number' ? meta.remote_log_offset : 0;
1108
+ // The team's project. Absent on every teammate added before `--project`.
1109
+ agent.project = typeof meta.project === 'string' ? meta.project : null;
1029
1110
  return agent;
1030
1111
  }
1031
- catch {
1112
+ catch (err) {
1113
+ // The file exists but is not valid JSON (or fails a constructor
1114
+ // invariant) -- most likely a torn write from before saveMeta() became
1115
+ // atomic. Quarantine it; see quarantineCorruptMeta() above.
1116
+ await AgentProcess.quarantineCorruptMeta(metaPath, err);
1032
1117
  return null;
1033
1118
  }
1034
1119
  }
@@ -1262,6 +1347,34 @@ export class AgentProcess {
1262
1347
  }
1263
1348
  }
1264
1349
  }
1350
+ /**
1351
+ * The directories, beyond its cwd, a teammate may reach because of the team's
1352
+ * `--project`. Resolved at LAUNCH, from the project name on the record.
1353
+ *
1354
+ * Resolving here rather than at `teams add` is load-bearing for a pooled team:
1355
+ * an unpinned teammate has no host until `maybeSchedulePlacement` runs, so add
1356
+ * time cannot know whether to produce absolute local paths or `~/…` — and the
1357
+ * local form would additionally have dropped any directory that exists only on
1358
+ * the host it later landed on.
1359
+ *
1360
+ * A project that no longer resolves (renamed, definition deleted) yields no
1361
+ * grants rather than failing the launch: the teammate still gets its cwd.
1362
+ */
1363
+ async function resolveTeammateGrants(agent, opts) {
1364
+ if (!agent.project)
1365
+ return [];
1366
+ try {
1367
+ const { resolveProjectDirs } = await import('../project-root.js');
1368
+ const { extraDirs } = await resolveProjectDirs(agent.project, opts);
1369
+ return extraDirs;
1370
+ }
1371
+ catch (err) {
1372
+ // Degrading is deliberate, but silently degrading is not: a teammate that
1373
+ // lost its grants to a renamed or deleted definition should leave a trace.
1374
+ debug(`teammate ${agent.agentId}: project '${agent.project}' did not resolve, no grants: ${err.message}`);
1375
+ return [];
1376
+ }
1377
+ }
1265
1378
  export async function beginResumeLogTransaction(agent) {
1266
1379
  const stdoutPath = await agent.getStdoutPath();
1267
1380
  const backupPath = `${stdoutPath}.resume-backup-${randomUUID()}`;
@@ -1608,7 +1721,7 @@ export class AgentManager {
1608
1721
  }
1609
1722
  return false;
1610
1723
  }
1611
- async spawn(taskName, agentType, prompt, cwd = null, mode = null, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null, profileName = null, hostName = null, hostTarget = null, repoPath = null) {
1724
+ async spawn(taskName, agentType, prompt, cwd = null, mode = null, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null, profileName = null, hostName = null, hostTarget = null, repoPath = null, project = null) {
1612
1725
  await this.initialize();
1613
1726
  const resolvedMode = resolveMode(mode, this.defaultMode);
1614
1727
  // Lineage (RUSH-2019): when the caller didn't name a parent, inherit the
@@ -1666,6 +1779,10 @@ export class AgentManager {
1666
1779
  agent.hostName = hostName;
1667
1780
  agent.hostTarget = hostTarget;
1668
1781
  agent.repoPath = repoPath;
1782
+ // Must be set BEFORE the launch below — this method launches inline for a
1783
+ // teammate with no unmet --after deps, so assigning it after spawn()
1784
+ // returns would miss the only launch that matters.
1785
+ agent.project = project;
1669
1786
  const agentDir = await agent.getAgentDir();
1670
1787
  try {
1671
1788
  await fs.mkdir(agentDir, { recursive: true });
@@ -1818,7 +1935,7 @@ export class AgentManager {
1818
1935
  // forwarded). Effort is a separate knob wired into buildReasoningFlags
1819
1936
  // inside buildCommand.
1820
1937
  const resolvedModel = agent.model ?? null;
1821
- const cmd = this.buildCommand(agent.agentType, agent.prompt, agent.mode, resolvedModel, agent.cwd, agent.agentId, effort, agent.version, agent.profileName, resume);
1938
+ const cmd = this.buildCommand(agent.agentType, agent.prompt, agent.mode, resolvedModel, agent.cwd, agent.agentId, effort, agent.version, agent.profileName, resume, await resolveTeammateGrants(agent, { forRemote: false }));
1822
1939
  debug(`Launching ${agent.agentType} agent ${agent.agentId} [${agent.mode}]${resume ? ' (resume)' : ''}: ${cmd.slice(0, 3).join(' ')}...`);
1823
1940
  let childProcess = null;
1824
1941
  let stdoutFile = null;
@@ -1954,6 +2071,15 @@ export class AgentManager {
1954
2071
  // — the supervisor polls the host, we don't block here.
1955
2072
  const effort = agent.effort ?? 'medium';
1956
2073
  const forwardedArgs = this.buildRunArgv(agent.agentType, agent.prompt, agent.mode, agent.model ?? null, effort, agent.version, agent.profileName, resume);
2074
+ // Project grants for a remote teammate, resolved HERE rather than at add
2075
+ // time: an unpinned teammate only learns its host from the scheduler, which
2076
+ // runs at launch. `forRemote: true` keeps them `~/…` and skips this box's
2077
+ // existence check — the host has its own checkouts. The remote `agents run`
2078
+ // expands `~` against the host's HOME before handing it to the harness.
2079
+ for (const dir of await resolveTeammateGrants(agent, { forRemote: true })) {
2080
+ if (dir !== remoteCwd)
2081
+ forwardedArgs.push('--add-dir', dir);
2082
+ }
1957
2083
  let dispatchedTask = null;
1958
2084
  let resumeLog = null;
1959
2085
  try {
@@ -2305,7 +2431,7 @@ export class AgentManager {
2305
2431
  args.push('--env', 'AGENTS_RUNTIME=teams');
2306
2432
  return args;
2307
2433
  }
2308
- buildCommand(agentType, prompt, mode, model, cwd = null, sessionId = null, effort = 'medium', version = null, profileName = null, resume) {
2434
+ buildCommand(agentType, prompt, mode, model, cwd = null, sessionId = null, effort = 'medium', version = null, profileName = null, resume, addDirs = []) {
2309
2435
  // Route through getAgentsInvocation so a teammate launched by the compiled
2310
2436
  // standalone binary (#315) doesn't relaunch as `agents /$bunfs/root/agents …`
2311
2437
  // (process.argv[1] is the bun virtual entry there) → "unknown command".
@@ -2326,6 +2452,14 @@ export class AgentManager {
2326
2452
  if (agentType === 'claude' && cwd) {
2327
2453
  cmd.push('--add-dir', cwd);
2328
2454
  }
2455
+ // The team's project directories, beyond the one the teammate sits in.
2456
+ // `agents run` re-dedupes, but skipping cwd here keeps the launch line
2457
+ // readable. Codex folds these into its workspace_roots; other harnesses
2458
+ // ignore --add-dir entirely.
2459
+ for (const dir of new Set(addDirs)) {
2460
+ if (dir !== cwd)
2461
+ cmd.push('--add-dir', dir);
2462
+ }
2329
2463
  // Codex's workspace-write sandbox blocks writes outside cwd. Factory
2330
2464
  // teammates need to run further `agents teams add` commands, which
2331
2465
  // write to ~/.agents/. Grant that root so subprocess-issued
@@ -2407,6 +2541,50 @@ export class AgentManager {
2407
2541
  const all = await this.listAll();
2408
2542
  return all.filter(a => a.taskName === taskName);
2409
2543
  }
2544
+ /**
2545
+ * Terminal removal of every teammate record for a team (RUSH-2450).
2546
+ *
2547
+ * `teams disband` used to delete log dirs and the registry entry, but left
2548
+ * the in-memory AgentProcess cache intact. A concurrent `teams start --watch`
2549
+ * supervisor then re-persisted those records via saveMeta, so a second
2550
+ * disband still found N logs to clear and `teams start` could re-launch
2551
+ * PENDING work that had already merged.
2552
+ *
2553
+ * This drops every matching record from the manager map AND removes its
2554
+ * durable state. With `keepLogs`, only `meta.json` is removed so the
2555
+ * teammate is no longer discoverable/startable while stdout/stderr logs
2556
+ * remain for postmortem.
2557
+ *
2558
+ * Returns the agent_ids that were purged.
2559
+ */
2560
+ async purgeByTask(taskName, opts) {
2561
+ await this.initialize();
2562
+ const roster = await this.listByTask(taskName);
2563
+ const purged = [];
2564
+ const base = this.agentsDir || (await getAgentsDir());
2565
+ for (const agent of roster) {
2566
+ this.agents.delete(agent.agentId);
2567
+ const agentDir = path.join(base, agent.agentId);
2568
+ try {
2569
+ if (opts?.keepLogs) {
2570
+ // Keep log files; remove only the record that makes the teammate
2571
+ // reappear in list/status/start.
2572
+ await fs.rm(path.join(agentDir, 'meta.json'), { force: true });
2573
+ }
2574
+ else {
2575
+ await fs.rm(agentDir, { recursive: true, force: true });
2576
+ }
2577
+ purged.push(agent.agentId);
2578
+ }
2579
+ catch (err) {
2580
+ debug(`purgeByTask: failed to remove ${agent.agentId}: ${err}`);
2581
+ // Still count as purged from the roster — the in-memory drop above is
2582
+ // the load-bearing half against same-process resurrection.
2583
+ purged.push(agent.agentId);
2584
+ }
2585
+ }
2586
+ return purged;
2587
+ }
2410
2588
  async listByParentSession(parentSessionId) {
2411
2589
  const all = await this.listAll();
2412
2590
  return all.filter(a => a.parentSessionId === parentSessionId);
@@ -46,6 +46,12 @@ export interface AgentStatusDetail {
46
46
  cloud_session_id?: string | null;
47
47
  cloud_provider?: string | null;
48
48
  pr_url?: string | null;
49
+ /**
50
+ * Delivery postcondition (RUSH-2380), distinct from process `status`.
51
+ * `pr_open` when the process completed but left an unmerged PR — orchestrators
52
+ * must not treat that as "work landed on main".
53
+ */
54
+ delivery?: string;
49
55
  version?: string | null;
50
56
  remote_session_id?: string | null;
51
57
  session_label?: string | null;
@@ -84,6 +90,8 @@ export interface AgentStatusSummary {
84
90
  name: string | null;
85
91
  agent_type: string;
86
92
  status: string;
93
+ /** Delivery postcondition — see {@link AgentStatusDetail.delivery}. */
94
+ delivery?: string;
87
95
  duration: string | null;
88
96
  tool_count: number;
89
97
  has_errors: boolean;
@@ -162,7 +170,9 @@ export interface TasksResult {
162
170
  tasks: TaskInfo[];
163
171
  }
164
172
  /** Spawn a new teammate in a task and return its initial metadata. */
165
- export declare function handleSpawn(manager: AgentManager, taskName: string, agentType: AgentType, prompt: string, cwd: string | null, mode: string | null, effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto' | null, parentSessionId?: string | null, workspaceDir?: string | null, version?: string | null, name?: string | null, after?: string[], model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudProvider?: string | null, cloudSessionId?: string | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null, profileName?: string | null, hostName?: string | null, hostTarget?: string | null, repoPath?: string | null): Promise<SpawnResult>;
173
+ export declare function handleSpawn(manager: AgentManager, taskName: string, agentType: AgentType, prompt: string, cwd: string | null, mode: string | null, effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto' | null, parentSessionId?: string | null, workspaceDir?: string | null, version?: string | null, name?: string | null, after?: string[], model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudProvider?: string | null, cloudSessionId?: string | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null, profileName?: string | null, hostName?: string | null, hostTarget?: string | null, repoPath?: string | null,
174
+ /** The team's `--project`; grants resolve from it at launch. */
175
+ project?: string | null): Promise<SpawnResult>;
166
176
  /** Retrieve the current status of all teammates in a task, with optional timestamp-based delta filtering. */
167
177
  export declare function handleStatus(manager: AgentManager, taskName: string | null | undefined, filter?: string, since?: string, // Optional ISO timestamp - return only events after this time
168
178
  parentSessionId?: string | null): Promise<TaskStatusResult>;
@@ -2,6 +2,7 @@ import { AgentStatus, resolveMode } from './agents.js';
2
2
  import { getDelta } from './summarizer.js';
3
3
  import { debug } from './debug.js';
4
4
  import { buildClaudeLabelMap } from '../session/discover.js';
5
+ import { resolveTeammateDelivery } from './delivery.js';
5
6
  /**
6
7
  * Truncate a bash command for status output.
7
8
  * Handles heredocs specially - shows the redirect target instead of contents.
@@ -107,6 +108,8 @@ export function toAgentStatusSummary(detail) {
107
108
  name: detail.name ?? null,
108
109
  agent_type: detail.agent_type,
109
110
  status: detail.status,
111
+ delivery: detail.delivery ??
112
+ resolveTeammateDelivery({ status: detail.status, prUrl: detail.pr_url }),
110
113
  duration: detail.duration,
111
114
  tool_count: detail.tool_count,
112
115
  has_errors: detail.has_errors,
@@ -135,7 +138,9 @@ export function toTaskStatusSummary(result) {
135
138
  };
136
139
  }
137
140
  /** Spawn a new teammate in a task and return its initial metadata. */
138
- export async function handleSpawn(manager, taskName, agentType, prompt, cwd, mode, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null, profileName = null, hostName = null, hostTarget = null, repoPath = null) {
141
+ export async function handleSpawn(manager, taskName, agentType, prompt, cwd, mode, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null, profileName = null, hostName = null, hostTarget = null, repoPath = null,
142
+ /** The team's `--project`; grants resolve from it at launch. */
143
+ project = null) {
139
144
  const defaultMode = manager.getDefaultMode();
140
145
  const resolvedMode = resolveMode(mode, defaultMode);
141
146
  const resolvedEffort = effort ?? 'medium';
@@ -160,7 +165,7 @@ export async function handleSpawn(manager, taskName, agentType, prompt, cwd, mod
160
165
  throw new Error(`[budget] BLOCKED teammate "${taskName}" (${agentType}): ${gate.decision.reason}`);
161
166
  }
162
167
  }
163
- const agent = await manager.spawn(taskName, agentType, prompt, cwd, resolvedMode, resolvedEffort, parentSessionId, workspaceDir, version, name, after, model, envOverrides, taskType, cloudProvider, cloudSessionId, cloudRepo, cloudBranch, worktreeName, worktreePath, profileName, hostName, hostTarget, repoPath);
168
+ const agent = await manager.spawn(taskName, agentType, prompt, cwd, resolvedMode, resolvedEffort, parentSessionId, workspaceDir, version, name, after, model, envOverrides, taskType, cloudProvider, cloudSessionId, cloudRepo, cloudBranch, worktreeName, worktreePath, profileName, hostName, hostTarget, repoPath, project);
164
169
  debug(`[spawn] Spawned ${agentType} agent ${agent.agentId} for task "${taskName}"`);
165
170
  return {
166
171
  task_name: taskName,
@@ -252,6 +257,10 @@ parentSessionId) {
252
257
  cloud_session_id: agent.cloudSessionId,
253
258
  cloud_provider: agent.cloudProvider,
254
259
  pr_url: agent.prUrl,
260
+ delivery: resolveTeammateDelivery({
261
+ status: agent.status,
262
+ prUrl: agent.prUrl,
263
+ }),
255
264
  files_created: delta.new_files_created,
256
265
  files_modified: delta.new_files_modified,
257
266
  files_read: delta.new_files_read,
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Teammate *delivery* vs process *lifecycle* (RUSH-2380).
3
+ *
4
+ * `AgentStatus.COMPLETED` means the process exited 0 — not that the PR merged.
5
+ * Orchestrators that treat COMPLETED as "work landed on main" compose on top of
6
+ * unmerged PRs. This module derives a delivery signal from process status +
7
+ * whether a PR URL is still open, so `teams status` can surface `PR OPEN`
8
+ * instead of bare COMPLETED.
9
+ *
10
+ * Process status still drives the DAG (`--after` deps): a finished process
11
+ * unblocks dependents. Delivery is the postcondition for "done end-to-end".
12
+ */
13
+ import { AgentStatus } from './agents.js';
14
+ /** Postcondition-facing delivery of a teammate's work. */
15
+ export type TeammateDelivery = 'pending' | 'in_progress' | 'pr_open' | 'pr_merged' | 'no_pr' | 'failed' | 'stopped';
16
+ /**
17
+ * Derive delivery from process status and PR state.
18
+ *
19
+ * When the process completed with a `prUrl` and merge is unknown or false,
20
+ * delivery is `pr_open` — pessimistic: assume open until proven merged so an
21
+ * orchestrator never mistakes "agent stopped" for "work on main".
22
+ */
23
+ export declare function resolveTeammateDelivery(opts: {
24
+ status: AgentStatus | string;
25
+ prUrl?: string | null;
26
+ /**
27
+ * When known from a probe or meta: true = merged, false = still open.
28
+ * `null`/`undefined` = unknown; with a `prUrl` that means `pr_open`.
29
+ */
30
+ prMerged?: boolean | null;
31
+ }): TeammateDelivery;
32
+ /**
33
+ * Human label for `teams status` rows. Replaces bare COMPLETED with PR OPEN
34
+ * when delivery is still pending merge.
35
+ */
36
+ export declare function deliveryDisplayLabel(delivery: TeammateDelivery, processStatus: AgentStatus | string): string;
37
+ /**
38
+ * Color key for statusColor-style switches. `pr_open` is its own key so the
39
+ * row is visually distinct from green COMPLETED.
40
+ */
41
+ export declare function deliveryColorKey(delivery: TeammateDelivery, processStatus: string): string;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Teammate *delivery* vs process *lifecycle* (RUSH-2380).
3
+ *
4
+ * `AgentStatus.COMPLETED` means the process exited 0 — not that the PR merged.
5
+ * Orchestrators that treat COMPLETED as "work landed on main" compose on top of
6
+ * unmerged PRs. This module derives a delivery signal from process status +
7
+ * whether a PR URL is still open, so `teams status` can surface `PR OPEN`
8
+ * instead of bare COMPLETED.
9
+ *
10
+ * Process status still drives the DAG (`--after` deps): a finished process
11
+ * unblocks dependents. Delivery is the postcondition for "done end-to-end".
12
+ */
13
+ import { AgentStatus } from './agents.js';
14
+ /**
15
+ * Derive delivery from process status and PR state.
16
+ *
17
+ * When the process completed with a `prUrl` and merge is unknown or false,
18
+ * delivery is `pr_open` — pessimistic: assume open until proven merged so an
19
+ * orchestrator never mistakes "agent stopped" for "work on main".
20
+ */
21
+ export function resolveTeammateDelivery(opts) {
22
+ const status = String(opts.status);
23
+ if (status === AgentStatus.PENDING || status === 'pending')
24
+ return 'pending';
25
+ if (status === AgentStatus.RUNNING || status === 'running')
26
+ return 'in_progress';
27
+ if (status === AgentStatus.FAILED || status === 'failed')
28
+ return 'failed';
29
+ if (status === AgentStatus.STOPPED || status === 'stopped')
30
+ return 'stopped';
31
+ if (status === AgentStatus.COMPLETED || status === 'completed') {
32
+ const prUrl = opts.prUrl?.trim();
33
+ if (!prUrl)
34
+ return 'no_pr';
35
+ if (opts.prMerged === true)
36
+ return 'pr_merged';
37
+ return 'pr_open';
38
+ }
39
+ return 'in_progress';
40
+ }
41
+ /**
42
+ * Human label for `teams status` rows. Replaces bare COMPLETED with PR OPEN
43
+ * when delivery is still pending merge.
44
+ */
45
+ export function deliveryDisplayLabel(delivery, processStatus) {
46
+ if (delivery === 'pr_open')
47
+ return 'PR OPEN';
48
+ if (delivery === 'pr_merged')
49
+ return 'COMPLETED';
50
+ return String(processStatus).toUpperCase();
51
+ }
52
+ /**
53
+ * Color key for statusColor-style switches. `pr_open` is its own key so the
54
+ * row is visually distinct from green COMPLETED.
55
+ */
56
+ export function deliveryColorKey(delivery, processStatus) {
57
+ if (delivery === 'pr_open')
58
+ return 'pr_open';
59
+ return String(processStatus);
60
+ }
@@ -18,6 +18,12 @@ export interface TeamMeta {
18
18
  * `origin` at create time. Used by `ensureRemoteRepo` to provision the repo.
19
19
  */
20
20
  repo?: string;
21
+ /**
22
+ * The project (`agents projects`) this team works on. Its primary directory
23
+ * is a local teammate's base cwd when neither `--cwd` nor a worktree gives
24
+ * one, and its other bound directories become `--add-dir` grants.
25
+ */
26
+ project?: string;
21
27
  }
22
28
  /** Map of team name to team metadata. */
23
29
  export type TeamRegistry = Record<string, TeamMeta>;
@@ -37,11 +43,19 @@ export interface CreateTeamOptions {
37
43
  devices?: string[];
38
44
  /** How each device gets the code (git URL to clone, or a path on the host). */
39
45
  repo?: string;
46
+ /** The `agents projects` project this team works on. */
47
+ project?: string;
40
48
  }
41
49
  /** Create a new team. Throws if a team with the same name already exists. */
42
50
  export declare function createTeam(name: string, options?: CreateTeamOptions): Promise<TeamMeta>;
43
51
  /** Return existing team metadata or create a new team if it does not exist. */
44
52
  export declare function ensureTeam(name: string): Promise<TeamMeta>;
53
+ /** Mark a team as disbanded so saveMeta will not resurrect its teammates. */
54
+ export declare function markTeamDisbanded(name: string): Promise<void>;
55
+ /** Clear a disband tombstone — called when the name is re-created. */
56
+ export declare function clearTeamDisbanded(name: string): Promise<void>;
57
+ /** True when the team was disbanded and has not been re-created since. */
58
+ export declare function isTeamDisbanded(name: string): Promise<boolean>;
45
59
  /** Remove a team from the registry. Returns false if the team did not exist. */
46
60
  export declare function removeTeam(name: string): Promise<boolean>;
47
61
  /** Check whether a team with the given name exists in the registry. */
@@ -111,11 +111,15 @@ export async function createTeam(name, options) {
111
111
  ...(options?.useWorktree ? { use_worktree: options.useWorktree } : {}),
112
112
  ...(options?.devices && options.devices.length ? { devices: options.devices } : {}),
113
113
  ...(options?.repo ? { repo: options.repo } : {}),
114
+ ...(options?.project ? { project: options.project } : {}),
114
115
  };
115
116
  reg[name] = m;
116
117
  await saveTeams(reg);
117
118
  return m;
118
119
  });
120
+ // A re-created name is a fresh team — drop any prior disband tombstone so
121
+ // saveMeta will persist new teammates again (RUSH-2450).
122
+ await clearTeamDisbanded(name);
119
123
  // Audit the lifecycle boundary, not the CLI shell — captures every creation
120
124
  // path (create + ensure) with team metadata the generic command log lacks.
121
125
  emit('teams.create', { module: 'teams', team: name, worktrees: Boolean(options?.enableWorktrees || options?.useWorktree) });
@@ -137,10 +141,52 @@ export async function ensureTeam(name) {
137
141
  });
138
142
  // `teams add` auto-creates the team on first teammate — audit that creation
139
143
  // too, but only when it actually happened (not the get-existing path).
140
- if (created)
144
+ if (created) {
145
+ await clearTeamDisbanded(name);
141
146
  emit('teams.create', { module: 'teams', team: name, worktrees: false });
147
+ }
142
148
  return meta;
143
149
  }
150
+ /**
151
+ * Tombstone dir for disbanded teams (RUSH-2450). Lives next to the registry so
152
+ * a long-lived supervisor can see that a team was deliberately removed and
153
+ * refuse to re-persist its in-memory AgentProcess objects via saveMeta.
154
+ * Cleared on create/ensure so a re-used name is a fresh team.
155
+ */
156
+ function disbandedDir() {
157
+ return path.join(path.dirname(getTeamsRegistryPath()), 'disbanded');
158
+ }
159
+ function disbandedPath(name) {
160
+ // Encode the name so a team called `../x` cannot escape the dir.
161
+ const safe = Buffer.from(name, 'utf8').toString('base64url');
162
+ return path.join(disbandedDir(), `${safe}.json`);
163
+ }
164
+ /** Mark a team as disbanded so saveMeta will not resurrect its teammates. */
165
+ export async function markTeamDisbanded(name) {
166
+ const dir = disbandedDir();
167
+ await fs.mkdir(dir, { recursive: true });
168
+ await fs.writeFile(disbandedPath(name), JSON.stringify({ team: name, disbanded_at: new Date().toISOString() }), 'utf-8');
169
+ }
170
+ /** Clear a disband tombstone — called when the name is re-created. */
171
+ export async function clearTeamDisbanded(name) {
172
+ try {
173
+ await fs.unlink(disbandedPath(name));
174
+ }
175
+ catch (err) {
176
+ if (err?.code !== 'ENOENT')
177
+ throw err;
178
+ }
179
+ }
180
+ /** True when the team was disbanded and has not been re-created since. */
181
+ export async function isTeamDisbanded(name) {
182
+ try {
183
+ await fs.access(disbandedPath(name));
184
+ return true;
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
144
190
  /** Remove a team from the registry. Returns false if the team did not exist. */
145
191
  export async function removeTeam(name) {
146
192
  const p = await registryPath();
@@ -152,6 +198,10 @@ export async function removeTeam(name) {
152
198
  await saveTeams(reg);
153
199
  return true;
154
200
  });
201
+ // Always stamp the tombstone when removeTeam is invoked from disband — even
202
+ // if the registry entry was already gone, so a concurrent supervisor still
203
+ // sees the disband. Idempotent.
204
+ await markTeamDisbanded(name);
155
205
  // "Disband" — only fires when a real team was removed, not a no-op.
156
206
  if (existed)
157
207
  emit('teams.disband', { module: 'teams', team: name });
@@ -34,6 +34,25 @@ export declare function remoteDefaultBranch(target: string, repoPath: string, op
34
34
  * teammate off old code.
35
35
  */
36
36
  export declare function createRemoteWorktree(target: string, repoPath: string, worktreeName: string, opts?: RemoteSshOptions): string;
37
+ /**
38
+ * How many commits the host checkout at `repoPath` is behind `origin/<default>` —
39
+ * the remote analog of the local {@link commitsBehindDefault} staleness probe, and
40
+ * the exact case from the incident this guard exists for (a distributed team
41
+ * pointed at a 71-commit-stale checkout on another box).
42
+ *
43
+ * `ensureRemoteRepo` already fetched `origin` when it provisioned/reused the
44
+ * checkout right before this runs, so the remote-tracking ref is usually fresh and
45
+ * this reads it WITHOUT a second fetch: resolve the default branch, then
46
+ * `rev-list --count HEAD..origin/<default>`. (That reuse fetch is best-effort —
47
+ * an offline host leaves the ref stale, so this under-reports rather than blocks;
48
+ * `createRemoteWorktree` re-fetches at launch so the teammate's base is still
49
+ * fresh regardless.) Returns null on any git/ssh error (unreachable host, non-repo
50
+ * path) — the caller treats null as "can't tell, don't block".
51
+ */
52
+ export declare function remoteCommitsBehindDefault(target: string, repoPath: string, opts?: RemoteSshOptions): {
53
+ behind: number;
54
+ base: string;
55
+ } | null;
37
56
  /**
38
57
  * Ensure the team's repo is present on the host and return its absolute git
39
58
  * root. The canonical location is `~/.agents/repos/<slug>` (slug = sanitized