@phnx-labs/agents-cli 1.22.38 → 1.22.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (554) hide show
  1. package/CHANGELOG.md +302 -0
  2. package/README.md +111 -70
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +46 -27
  5. package/dist/browser.js +2 -2
  6. package/dist/cli/command-registry.d.ts +135 -0
  7. package/dist/cli/command-registry.js +260 -0
  8. package/dist/commands/accounts.d.ts +20 -0
  9. package/dist/commands/accounts.js +186 -25
  10. package/dist/commands/apply.d.ts +3 -3
  11. package/dist/commands/apply.js +20 -63
  12. package/dist/commands/browser.js +206 -94
  13. package/dist/commands/budget.d.ts +7 -6
  14. package/dist/commands/budget.js +8 -7
  15. package/dist/commands/cloud.js +17 -20
  16. package/dist/commands/commands.js +1 -1
  17. package/dist/commands/computer-actions.d.ts +1 -35
  18. package/dist/commands/computer-actions.js +13 -181
  19. package/dist/commands/computer.d.ts +28 -3
  20. package/dist/commands/computer.js +93 -65
  21. package/dist/commands/config.js +47 -4
  22. package/dist/commands/cost.d.ts +2 -1
  23. package/dist/commands/cost.js +7 -6
  24. package/dist/commands/daemon.js +151 -4
  25. package/dist/commands/detach.js +1 -1
  26. package/dist/commands/doctor.js +19 -11
  27. package/dist/commands/events.js +1 -1
  28. package/dist/commands/exec.d.ts +2 -50
  29. package/dist/commands/exec.js +161 -216
  30. package/dist/commands/factory.js +1 -1
  31. package/dist/commands/feed.d.ts +2 -2
  32. package/dist/commands/feed.js +9 -12
  33. package/dist/commands/fleet-capture.js +25 -27
  34. package/dist/commands/focus.d.ts +2 -3
  35. package/dist/commands/focus.js +34 -9
  36. package/dist/commands/go.js +15 -5
  37. package/dist/commands/harness.d.ts +3 -3
  38. package/dist/commands/harness.js +53 -5
  39. package/dist/commands/hooks.js +1 -1
  40. package/dist/commands/import.js +2 -2
  41. package/dist/commands/insights.d.ts +5 -5
  42. package/dist/commands/insights.js +15 -9
  43. package/dist/commands/inspect.d.ts +2 -3
  44. package/dist/commands/inspect.js +4 -16
  45. package/dist/commands/logs.d.ts +2 -2
  46. package/dist/commands/logs.js +9 -9
  47. package/dist/commands/mcp.js +2 -2
  48. package/dist/commands/memory.js +1 -1
  49. package/dist/commands/message.d.ts +1 -1
  50. package/dist/commands/message.js +7 -7
  51. package/dist/commands/mine.d.ts +11 -8
  52. package/dist/commands/mine.js +11 -10
  53. package/dist/commands/models.js +1 -1
  54. package/dist/commands/modes.js +1 -1
  55. package/dist/commands/monitors.js +23 -6
  56. package/dist/commands/output.d.ts +7 -6
  57. package/dist/commands/output.js +11 -10
  58. package/dist/commands/packages.js +4 -4
  59. package/dist/commands/perf.js +1 -1
  60. package/dist/commands/permissions.js +49 -32
  61. package/dist/commands/plugins.d.ts +1 -1
  62. package/dist/commands/plugins.js +3 -3
  63. package/dist/commands/profiles.d.ts +6 -9
  64. package/dist/commands/profiles.js +8 -321
  65. package/dist/commands/projects.d.ts +9 -0
  66. package/dist/commands/projects.js +140 -8
  67. package/dist/commands/prune.js +5 -13
  68. package/dist/commands/refresh-rules.js +1 -1
  69. package/dist/commands/repo.d.ts +5 -7
  70. package/dist/commands/repo.js +34 -18
  71. package/dist/commands/resource-view.js +4 -0
  72. package/dist/commands/resume.d.ts +11 -7
  73. package/dist/commands/resume.js +82 -98
  74. package/dist/commands/route.d.ts +12 -0
  75. package/dist/commands/route.js +261 -0
  76. package/dist/commands/routines.d.ts +2 -3
  77. package/dist/commands/routines.js +120 -145
  78. package/dist/commands/rules.js +1 -1
  79. package/dist/commands/run-account-picker.d.ts +1 -1
  80. package/dist/commands/run-account-picker.js +2 -2
  81. package/dist/commands/run-broadcast.d.ts +21 -0
  82. package/dist/commands/run-broadcast.js +89 -0
  83. package/dist/commands/run-cloud.js +1 -1
  84. package/dist/commands/secrets-vault.d.ts +10 -0
  85. package/dist/commands/{login.js → secrets-vault.js} +34 -14
  86. package/dist/commands/secrets.d.ts +4 -4
  87. package/dist/commands/secrets.js +72 -63
  88. package/dist/commands/sessions-backfill.js +1 -1
  89. package/dist/commands/sessions-browser.d.ts +1 -1
  90. package/dist/commands/sessions-browser.js +9 -5
  91. package/dist/commands/sessions-export.js +2 -2
  92. package/dist/commands/sessions-inject.js +4 -5
  93. package/dist/commands/sessions-migrate.js +16 -27
  94. package/dist/commands/sessions-picker.d.ts +13 -0
  95. package/dist/commands/sessions-picker.js +182 -8
  96. package/dist/commands/sessions-resume.d.ts +7 -3
  97. package/dist/commands/sessions-resume.js +81 -33
  98. package/dist/commands/sessions-stats.js +2 -2
  99. package/dist/commands/sessions.d.ts +75 -68
  100. package/dist/commands/sessions.js +142 -180
  101. package/dist/commands/setup-computer.js +1 -1
  102. package/dist/commands/setup-fleet.js +3 -0
  103. package/dist/commands/setup-mine.d.ts +7 -3
  104. package/dist/commands/setup-mine.js +13 -8
  105. package/dist/commands/setup-secrets.js +2 -2
  106. package/dist/commands/setup-watchdog.js +2 -2
  107. package/dist/commands/setup.js +7 -2
  108. package/dist/commands/share.d.ts +65 -0
  109. package/dist/commands/share.js +253 -20
  110. package/dist/commands/skills.js +3 -3
  111. package/dist/commands/snapshot.d.ts +2 -2
  112. package/dist/commands/snapshot.js +9 -9
  113. package/dist/commands/ssh.d.ts +1 -1
  114. package/dist/commands/ssh.js +318 -48
  115. package/dist/commands/subagents.js +1 -1
  116. package/dist/commands/sync.js +24 -9
  117. package/dist/commands/teams.d.ts +2 -2
  118. package/dist/commands/teams.js +9 -18
  119. package/dist/commands/tmux.js +5 -1
  120. package/dist/commands/trash.js +1 -1
  121. package/dist/commands/usage.d.ts +1 -1
  122. package/dist/commands/usage.js +2 -2
  123. package/dist/commands/utils.d.ts +4 -14
  124. package/dist/commands/utils.js +6 -27
  125. package/dist/commands/versions.js +2 -2
  126. package/dist/commands/view.d.ts +9 -77
  127. package/dist/commands/view.js +23 -8
  128. package/dist/commands/watchdog.js +1 -1
  129. package/dist/commands/webhook.js +11 -25
  130. package/dist/commands/workflows.js +1 -1
  131. package/dist/index.js +1 -1
  132. package/dist/lib/account-capabilities.d.ts +39 -0
  133. package/dist/lib/account-capabilities.js +59 -0
  134. package/dist/lib/account-catalog.js +1 -1
  135. package/dist/lib/account-registry.d.ts +66 -0
  136. package/dist/lib/account-registry.js +151 -5
  137. package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
  138. package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
  139. package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
  140. package/dist/lib/{usage.js → accounting/usage.js} +11 -11
  141. package/dist/lib/agent-spec/provider.js +1 -1
  142. package/dist/lib/agents.d.ts +72 -4
  143. package/dist/lib/agents.js +231 -199
  144. package/dist/lib/answer-router.d.ts +2 -2
  145. package/dist/lib/ask-classifier.d.ts +1 -1
  146. package/dist/lib/ask-classifier.js +1 -1
  147. package/dist/lib/audit/log.js +1 -1
  148. package/dist/lib/auth-health.d.ts +1 -1
  149. package/dist/lib/auth-health.js +2 -2
  150. package/dist/lib/brand.js +1 -1
  151. package/dist/lib/browser/caller-identity.d.ts +28 -0
  152. package/dist/lib/browser/caller-identity.js +156 -0
  153. package/dist/lib/browser/cdp.js +1 -1
  154. package/dist/lib/browser/drivers/ssh.d.ts +1 -1
  155. package/dist/lib/browser/drivers/ssh.js +2 -2
  156. package/dist/lib/browser/hygiene.d.ts +10 -2
  157. package/dist/lib/browser/hygiene.js +7 -4
  158. package/dist/lib/browser/ipc.d.ts +22 -2
  159. package/dist/lib/browser/ipc.js +185 -21
  160. package/dist/lib/browser/profiles.d.ts +67 -3
  161. package/dist/lib/browser/profiles.js +129 -7
  162. package/dist/lib/browser/remote-control.d.ts +2 -2
  163. package/dist/lib/browser/remote-control.js +3 -3
  164. package/dist/lib/browser/runtime-state.d.ts +79 -0
  165. package/dist/lib/browser/runtime-state.js +119 -1
  166. package/dist/lib/browser/service.d.ts +113 -1
  167. package/dist/lib/browser/service.js +454 -40
  168. package/dist/lib/browser/sessions-list.d.ts +2 -1
  169. package/dist/lib/browser/sessions-list.js +4 -13
  170. package/dist/lib/browser/types.d.ts +18 -0
  171. package/dist/lib/budget/config.js +1 -1
  172. package/dist/lib/budget/ledger.js +1 -1
  173. package/dist/lib/byok-usage.js +1 -1
  174. package/dist/lib/capabilities.js +3 -15
  175. package/dist/lib/catchup.d.ts +1 -1
  176. package/dist/lib/catchup.js +1 -1
  177. package/dist/lib/cloud/dispatch.js +1 -1
  178. package/dist/lib/cloud/host.d.ts +3 -3
  179. package/dist/lib/cloud/host.js +5 -5
  180. package/dist/lib/cloud/rush.d.ts +9 -21
  181. package/dist/lib/cloud/rush.js +33 -120
  182. package/dist/lib/cloud/types.d.ts +2 -2
  183. package/dist/lib/commands.js +3 -13
  184. package/dist/lib/computer/actions.d.ts +36 -0
  185. package/dist/lib/computer/actions.js +162 -0
  186. package/dist/lib/computer/dispatch.d.ts +2 -2
  187. package/dist/lib/computer/dispatch.js +2 -2
  188. package/dist/lib/computer/sessions-list.d.ts +3 -3
  189. package/dist/lib/computer/sessions-list.js +2 -2
  190. package/dist/lib/config-keys.d.ts +12 -3
  191. package/dist/lib/config-keys.js +29 -6
  192. package/dist/lib/config-machine-keys.d.ts +4 -2
  193. package/dist/lib/config-machine-keys.js +6 -2
  194. package/dist/lib/crabbox/lease.js +9 -1
  195. package/dist/lib/crabbox/runtimes.d.ts +32 -7
  196. package/dist/lib/crabbox/runtimes.js +72 -30
  197. package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
  198. package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
  199. package/dist/lib/daemon-services.d.ts +1 -1
  200. package/dist/lib/daemon-services.js +5 -0
  201. package/dist/lib/daemon-ticks.d.ts +25 -0
  202. package/dist/lib/daemon-ticks.js +31 -1
  203. package/dist/lib/daemon-webhooks.d.ts +102 -0
  204. package/dist/lib/daemon-webhooks.js +210 -0
  205. package/dist/lib/device-config.d.ts +116 -72
  206. package/dist/lib/device-config.js +366 -132
  207. package/dist/lib/devices/config-migration.d.ts +29 -34
  208. package/dist/lib/devices/config-migration.js +209 -130
  209. package/dist/lib/devices/connect.d.ts +2 -2
  210. package/dist/lib/devices/connect.js +2 -2
  211. package/dist/lib/devices/discovery-policy.d.ts +30 -0
  212. package/dist/lib/devices/discovery-policy.js +101 -0
  213. package/dist/lib/devices/fleet-inventory.js +8 -3
  214. package/dist/lib/devices/harness-inventory.d.ts +1 -1
  215. package/dist/lib/devices/harness-inventory.js +12 -3
  216. package/dist/lib/devices/known-hosts.js +1 -1
  217. package/dist/lib/devices/pool.d.ts +61 -0
  218. package/dist/lib/devices/pool.js +83 -0
  219. package/dist/lib/devices/registry.d.ts +1 -1
  220. package/dist/lib/devices/registry.js +1 -1
  221. package/dist/lib/devices/resolve-profile.d.ts +2 -2
  222. package/dist/lib/devices/resolve-profile.js +2 -2
  223. package/dist/lib/devices/resolve-target.d.ts +1 -1
  224. package/dist/lib/devices/resolve-target.js +4 -4
  225. package/dist/lib/devices/self-host.d.ts +1 -1
  226. package/dist/lib/devices/self-host.js +3 -3
  227. package/dist/lib/devices/ssh-config.d.ts +1 -1
  228. package/dist/lib/devices/stats-cache.js +1 -1
  229. package/dist/lib/devices/sync.d.ts +3 -1
  230. package/dist/lib/devices/sync.js +3 -3
  231. package/dist/lib/doctor-diff.js +4 -4
  232. package/dist/lib/drift-sync.js +2 -2
  233. package/dist/lib/drift.js +2 -2
  234. package/dist/lib/event-families.d.ts +1 -1
  235. package/dist/lib/event-stream.d.ts +1 -1
  236. package/dist/lib/event-stream.js +2 -2
  237. package/dist/lib/events-ingest.js +2 -2
  238. package/dist/lib/exec.d.ts +8 -5
  239. package/dist/lib/exec.js +19 -13
  240. package/dist/lib/factory/snapshot.d.ts +1 -1
  241. package/dist/lib/factory/snapshot.js +1 -1
  242. package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
  243. package/dist/lib/{activity.js → feed/activity.js} +7 -7
  244. package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
  245. package/dist/lib/{events.js → feed/events.js} +5 -5
  246. package/dist/lib/{feed.js → feed/feed.js} +4 -4
  247. package/dist/lib/feed-outcome.d.ts +1 -1
  248. package/dist/lib/feed-policy.d.ts +1 -1
  249. package/dist/lib/feed-policy.js +1 -1
  250. package/dist/lib/feed-post.d.ts +1 -1
  251. package/dist/lib/feed-post.js +1 -1
  252. package/dist/lib/feed-ranking.d.ts +1 -1
  253. package/dist/lib/fleet/apply.d.ts +0 -6
  254. package/dist/lib/fleet/apply.js +18 -45
  255. package/dist/lib/fleet/auth-sync.d.ts +16 -35
  256. package/dist/lib/fleet/auth-sync.js +16 -75
  257. package/dist/lib/fleet/capture.js +8 -6
  258. package/dist/lib/fleet/manifest.js +23 -11
  259. package/dist/lib/fleet/types.d.ts +19 -7
  260. package/dist/lib/fleet-cache.d.ts +1 -1
  261. package/dist/lib/fleet-cache.js +1 -1
  262. package/dist/lib/format.d.ts +19 -0
  263. package/dist/lib/format.js +44 -1
  264. package/dist/lib/friction-heuristics.d.ts +1 -1
  265. package/dist/lib/git.d.ts +24 -1
  266. package/dist/lib/git.js +82 -38
  267. package/dist/lib/heal.js +3 -3
  268. package/dist/lib/hooks.d.ts +18 -0
  269. package/dist/lib/hooks.js +113 -1
  270. package/dist/lib/hosts/credentials.d.ts +30 -10
  271. package/dist/lib/hosts/credentials.js +38 -26
  272. package/dist/lib/hosts/dispatch.d.ts +3 -3
  273. package/dist/lib/hosts/dispatch.js +3 -3
  274. package/dist/lib/hosts/option.d.ts +3 -3
  275. package/dist/lib/hosts/option.js +7 -8
  276. package/dist/lib/hosts/passthrough.d.ts +17 -17
  277. package/dist/lib/hosts/passthrough.js +67 -50
  278. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  279. package/dist/lib/hosts/providers/devices.js +1 -1
  280. package/dist/lib/hosts/reconcile.d.ts +1 -1
  281. package/dist/lib/hosts/reconcile.js +1 -1
  282. package/dist/lib/hosts/reconnect.d.ts +1 -1
  283. package/dist/lib/hosts/reconnect.js +2 -2
  284. package/dist/lib/hosts/registry.d.ts +5 -5
  285. package/dist/lib/hosts/registry.js +5 -5
  286. package/dist/lib/hosts/remote-cmd.d.ts +10 -10
  287. package/dist/lib/hosts/remote-cmd.js +25 -18
  288. package/dist/lib/hosts/remote-os.d.ts +4 -4
  289. package/dist/lib/hosts/remote-os.js +4 -4
  290. package/dist/lib/hosts/remote-session-id.d.ts +2 -2
  291. package/dist/lib/hosts/remote-session-id.js +2 -2
  292. package/dist/lib/hosts/routing-flag.d.ts +3 -3
  293. package/dist/lib/hosts/routing-flag.js +4 -5
  294. package/dist/lib/hosts/run-target.d.ts +4 -4
  295. package/dist/lib/hosts/run-target.js +4 -4
  296. package/dist/lib/hosts/session-index.d.ts +2 -2
  297. package/dist/lib/hosts/session-index.js +1 -1
  298. package/dist/lib/hosts/session-marker.d.ts +1 -1
  299. package/dist/lib/hosts/session-marker.js +1 -1
  300. package/dist/lib/hosts/types.d.ts +2 -2
  301. package/dist/lib/hosts/types.js +2 -2
  302. package/dist/lib/import.js +1 -1
  303. package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +49 -1
  304. package/dist/lib/{migrate.js → installations/migrate.js} +265 -38
  305. package/dist/lib/installations/resolve.js +1 -1
  306. package/dist/lib/installations/strategies.js +1 -1
  307. package/dist/lib/installations/update.js +2 -2
  308. package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
  309. package/dist/lib/{versions.js → installations/versions.js} +51 -31
  310. package/dist/lib/isolation-boundary-report.js +1 -1
  311. package/dist/lib/linear-projects.d.ts +28 -0
  312. package/dist/lib/linear-projects.js +30 -0
  313. package/dist/lib/loop.js +1 -1
  314. package/dist/lib/mailbox-gc.js +1 -1
  315. package/dist/lib/mailbox.js +1 -1
  316. package/dist/lib/mcp-registry.d.ts +62 -0
  317. package/dist/lib/mcp-registry.js +177 -0
  318. package/dist/lib/mcp.d.ts +5 -0
  319. package/dist/lib/mcp.js +124 -307
  320. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  321. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  322. package/dist/lib/menubar/install-menubar.d.ts +7 -0
  323. package/dist/lib/menubar/install-menubar.js +24 -6
  324. package/dist/lib/menubar/snapshot.d.ts +7 -0
  325. package/dist/lib/menubar/snapshot.js +8 -6
  326. package/dist/lib/models.js +1 -1
  327. package/dist/lib/monitors/config.d.ts +12 -0
  328. package/dist/lib/monitors/config.js +3 -0
  329. package/dist/lib/monitors/dispatch.js +26 -5
  330. package/dist/lib/monitors/engine.js +10 -0
  331. package/dist/lib/monitors/sources/webhook.d.ts +1 -1
  332. package/dist/lib/monitors/state.d.ts +42 -0
  333. package/dist/lib/monitors/state.js +30 -0
  334. package/dist/lib/notify.d.ts +1 -1
  335. package/dist/lib/observe-aliases.d.ts +2 -2
  336. package/dist/lib/observe-aliases.js +2 -11
  337. package/dist/lib/output/git-output.js +2 -2
  338. package/dist/lib/overdue.d.ts +1 -1
  339. package/dist/lib/overdue.js +1 -1
  340. package/dist/lib/permissions-registry.d.ts +65 -0
  341. package/dist/lib/permissions-registry.js +599 -0
  342. package/dist/lib/permissions.d.ts +24 -13
  343. package/dist/lib/permissions.js +52 -271
  344. package/dist/lib/picker.d.ts +16 -0
  345. package/dist/lib/picker.js +33 -3
  346. package/dist/lib/placement.d.ts +1 -1
  347. package/dist/lib/placement.js +4 -4
  348. package/dist/lib/platform/paths.d.ts +8 -0
  349. package/dist/lib/platform/paths.js +11 -0
  350. package/dist/lib/platform/posixpath.js +3 -3
  351. package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
  352. package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
  353. package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
  354. package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
  355. package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
  356. package/dist/lib/{skills.js → plugins/skills.js} +7 -7
  357. package/dist/lib/pricing/cost.d.ts +1 -1
  358. package/dist/lib/pricing/cost.js +1 -1
  359. package/dist/lib/profiles-presets.d.ts +1 -1
  360. package/dist/lib/profiles-presets.js +1 -1
  361. package/dist/lib/profiles.js +2 -2
  362. package/dist/lib/project-import.js +4 -1
  363. package/dist/lib/project-launch.d.ts +1 -1
  364. package/dist/lib/project-launch.js +3 -3
  365. package/dist/lib/project-pull.d.ts +128 -0
  366. package/dist/lib/project-pull.js +384 -0
  367. package/dist/lib/project-root.d.ts +2 -2
  368. package/dist/lib/project-root.js +2 -2
  369. package/dist/lib/project-status.js +1 -1
  370. package/dist/lib/projects.d.ts +23 -0
  371. package/dist/lib/projects.js +29 -6
  372. package/dist/lib/refresh.d.ts +12 -1
  373. package/dist/lib/refresh.js +16 -9
  374. package/dist/lib/registry.d.ts +9 -2
  375. package/dist/lib/registry.js +11 -4
  376. package/dist/lib/resources.d.ts +2 -2
  377. package/dist/lib/resources.js +2 -2
  378. package/dist/lib/routers.d.ts +81 -0
  379. package/dist/lib/routers.js +153 -0
  380. package/dist/lib/routine-context.d.ts +1 -0
  381. package/dist/lib/routine-context.js +7 -2
  382. package/dist/lib/routine-notify-owner.d.ts +1 -1
  383. package/dist/lib/routine-notify.d.ts +1 -1
  384. package/dist/lib/routine-notify.js +1 -1
  385. package/dist/lib/routine-process-cleanup.d.ts +1 -1
  386. package/dist/lib/routine-readiness.d.ts +1 -1
  387. package/dist/lib/routine-readiness.js +11 -4
  388. package/dist/lib/routines-placement.d.ts +12 -2
  389. package/dist/lib/routines-placement.js +20 -3
  390. package/dist/lib/routines-project.d.ts +1 -1
  391. package/dist/lib/routines-project.js +1 -1
  392. package/dist/lib/rules/compile.d.ts +1 -0
  393. package/dist/lib/rules/compile.js +10 -2
  394. package/dist/lib/rules/rules.js +1 -1
  395. package/dist/lib/run-notify.js +1 -1
  396. package/dist/lib/runner.d.ts +80 -2
  397. package/dist/lib/runner.js +241 -62
  398. package/dist/lib/sandbox.d.ts +1 -1
  399. package/dist/lib/scheduler.d.ts +1 -1
  400. package/dist/lib/scheduler.js +1 -1
  401. package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
  402. package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
  403. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  404. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  405. package/dist/lib/secrets/agent.js +2 -2
  406. package/dist/lib/secrets/audit.js +1 -1
  407. package/dist/lib/secrets/bundles.d.ts +3 -3
  408. package/dist/lib/secrets/bundles.js +7 -7
  409. package/dist/lib/secrets/index.js +1 -1
  410. package/dist/lib/secrets/list-filter.d.ts +1 -1
  411. package/dist/lib/secrets/list-filter.js +2 -2
  412. package/dist/lib/secrets/push.d.ts +1 -1
  413. package/dist/lib/secrets/push.js +10 -12
  414. package/dist/lib/secrets/remote.d.ts +12 -10
  415. package/dist/lib/secrets/remote.js +21 -14
  416. package/dist/lib/secrets/vault.js +2 -2
  417. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  418. package/dist/lib/self-heal/checks/shims.js +1 -1
  419. package/dist/lib/self-update.d.ts +39 -4
  420. package/dist/lib/self-update.js +78 -25
  421. package/dist/lib/serve/control.d.ts +1 -1
  422. package/dist/lib/serve/control.js +4 -4
  423. package/dist/lib/serve/data.d.ts +1 -1
  424. package/dist/lib/serve/data.js +1 -1
  425. package/dist/lib/serve/stream.js +1 -1
  426. package/dist/lib/service-manifest.d.ts +29 -0
  427. package/dist/lib/service-manifest.js +80 -0
  428. package/dist/lib/session/active.d.ts +59 -7
  429. package/dist/lib/session/active.js +158 -17
  430. package/dist/lib/session/bash-command.js +1 -1
  431. package/dist/lib/session/claude-accounts.d.ts +1 -1
  432. package/dist/lib/session/claude-accounts.js +1 -1
  433. package/dist/lib/session/db.d.ts +10 -2
  434. package/dist/lib/session/db.js +25 -6
  435. package/dist/lib/session/discover.d.ts +77 -0
  436. package/dist/lib/session/discover.js +130 -25
  437. package/dist/lib/session/fork.js +1 -1
  438. package/dist/lib/session/inject.d.ts +1 -1
  439. package/dist/lib/session/insights.d.ts +1 -1
  440. package/dist/lib/session/insights.js +1 -1
  441. package/dist/lib/session/live-metadata.d.ts +15 -0
  442. package/dist/lib/session/live-metadata.js +76 -0
  443. package/dist/lib/session/migrate-targets.d.ts +1 -1
  444. package/dist/lib/session/recovery.d.ts +1 -1
  445. package/dist/lib/session/recovery.js +2 -2
  446. package/dist/lib/session/remote-active.d.ts +10 -1
  447. package/dist/lib/session/remote-active.js +8 -3
  448. package/dist/lib/session/remote-bundle.d.ts +1 -1
  449. package/dist/lib/session/remote-bundle.js +2 -2
  450. package/dist/lib/session/remote-list.d.ts +15 -4
  451. package/dist/lib/session/remote-list.js +40 -3
  452. package/dist/lib/session/remote.d.ts +5 -5
  453. package/dist/lib/session/remote.js +13 -17
  454. package/dist/lib/session/resume-command.d.ts +3 -2
  455. package/dist/lib/session/resume-command.js +4 -3
  456. package/dist/lib/session/resume-owner.d.ts +3 -3
  457. package/dist/lib/session/resume-owner.js +3 -3
  458. package/dist/lib/session/session-cache.d.ts +12 -0
  459. package/dist/lib/session/session-cache.js +2 -0
  460. package/dist/lib/session/shell-programs.js +1 -1
  461. package/dist/lib/session/state.js +5 -5
  462. package/dist/lib/session/team-filter.d.ts +1 -1
  463. package/dist/lib/session/team-filter.js +1 -1
  464. package/dist/lib/session/types.d.ts +3 -3
  465. package/dist/lib/session/width.d.ts +2 -2
  466. package/dist/lib/session/width.js +2 -2
  467. package/dist/lib/share/delete.d.ts +21 -0
  468. package/dist/lib/share/delete.js +42 -0
  469. package/dist/lib/share/publish.d.ts +82 -0
  470. package/dist/lib/share/publish.js +170 -18
  471. package/dist/lib/share/worker-template.js +172 -9
  472. package/dist/lib/shims.js +35 -7
  473. package/dist/lib/smart-launch.d.ts +36 -6
  474. package/dist/lib/smart-launch.js +65 -8
  475. package/dist/lib/snapshot.d.ts +4 -4
  476. package/dist/lib/snapshot.js +2 -2
  477. package/dist/lib/ssh-exec.d.ts +1 -1
  478. package/dist/lib/ssh-exec.js +2 -2
  479. package/dist/lib/ssh-tunnel.d.ts +10 -10
  480. package/dist/lib/ssh-tunnel.js +12 -12
  481. package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
  482. package/dist/lib/staleness/checkers/hooks.js +1 -1
  483. package/dist/lib/staleness/detectors/permissions.js +0 -23
  484. package/dist/lib/staleness/detectors/plugins.js +1 -1
  485. package/dist/lib/staleness/fingerprint.js +1 -1
  486. package/dist/lib/staleness/layers.d.ts +1 -1
  487. package/dist/lib/staleness/layers.js +1 -1
  488. package/dist/lib/staleness/writers/mcp.js +3 -1
  489. package/dist/lib/staleness/writers/plugins.js +1 -1
  490. package/dist/lib/staleness/writers/types.d.ts +8 -0
  491. package/dist/lib/startup/command-registry.d.ts +5 -147
  492. package/dist/lib/startup/command-registry.js +38 -303
  493. package/dist/lib/startup/dev-build.d.ts +0 -21
  494. package/dist/lib/startup/dev-build.js +29 -8
  495. package/dist/lib/state.d.ts +19 -1
  496. package/dist/lib/state.js +120 -51
  497. package/dist/lib/subagents.js +1 -1
  498. package/dist/lib/sync-status.js +1 -1
  499. package/dist/lib/sync-umbrella.d.ts +6 -1
  500. package/dist/lib/sync-umbrella.js +9 -4
  501. package/dist/lib/teams/agents.js +2 -2
  502. package/dist/lib/teams/registry.js +1 -1
  503. package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
  504. package/dist/lib/terminal/engine.js +1 -1
  505. package/dist/lib/terminal/index.d.ts +4 -0
  506. package/dist/lib/terminal/index.js +3 -0
  507. package/dist/lib/terminal/inject.d.ts +2 -2
  508. package/dist/lib/terminal/inject.js +2 -2
  509. package/dist/lib/terminal/transport.js +1 -1
  510. package/dist/lib/tmux/index.d.ts +1 -1
  511. package/dist/lib/tmux/index.js +1 -1
  512. package/dist/lib/tmux/session.d.ts +21 -5
  513. package/dist/lib/tmux/session.js +76 -22
  514. package/dist/lib/triggers/handlers.d.ts +1 -1
  515. package/dist/lib/triggers/handlers.js +2 -2
  516. package/dist/lib/triggers/webhook.d.ts +36 -2
  517. package/dist/lib/triggers/webhook.js +97 -42
  518. package/dist/lib/types.d.ts +18 -6
  519. package/dist/lib/usage-fleet.d.ts +1 -1
  520. package/dist/lib/usage-fleet.js +1 -1
  521. package/dist/lib/usage-refresh.d.ts +1 -1
  522. package/dist/lib/usage-refresh.js +3 -3
  523. package/dist/lib/version-duplicates.js +2 -2
  524. package/dist/lib/view-types.d.ts +56 -0
  525. package/dist/lib/view-types.js +1 -0
  526. package/dist/lib/watchdog/rotate.d.ts +2 -2
  527. package/dist/lib/watchdog/rotate.js +3 -3
  528. package/dist/lib/watchdog/runner.d.ts +2 -3
  529. package/dist/lib/watchdog/runner.js +3 -4
  530. package/dist/lib/workflows.js +1 -1
  531. package/package.json +2 -1
  532. package/scripts/postinstall.js +2 -2
  533. package/dist/commands/bench.d.ts +0 -2
  534. package/dist/commands/bench.js +0 -101
  535. package/dist/commands/cp.d.ts +0 -84
  536. package/dist/commands/cp.js +0 -231
  537. package/dist/commands/login.d.ts +0 -2
  538. package/dist/lib/resources/commands.d.ts +0 -46
  539. package/dist/lib/resources/commands.js +0 -211
  540. package/dist/lib/resources/hooks.d.ts +0 -16
  541. package/dist/lib/resources/hooks.js +0 -153
  542. package/dist/lib/resources/mcp.d.ts +0 -34
  543. package/dist/lib/resources/mcp.js +0 -250
  544. package/dist/lib/resources/memory.d.ts +0 -15
  545. package/dist/lib/resources/memory.js +0 -46
  546. package/dist/lib/resources/permissions.d.ts +0 -13
  547. package/dist/lib/resources/permissions.js +0 -202
  548. package/dist/lib/resources/rules.d.ts +0 -46
  549. package/dist/lib/resources/rules.js +0 -179
  550. package/dist/lib/resources/skills.d.ts +0 -37
  551. package/dist/lib/resources/skills.js +0 -247
  552. package/dist/lib/resources/subagents.d.ts +0 -46
  553. package/dist/lib/resources/subagents.js +0 -202
  554. /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
@@ -34,10 +34,16 @@ function validateDefaults(v, where) {
34
34
  if (o.sync !== undefined && !isStringArray(o.sync)) {
35
35
  throw new Error(`fleet: ${where}.sync must be a list of scope names (e.g. [user]).`);
36
36
  }
37
+ if (o.config !== undefined && (typeof o.config !== 'object' || o.config === null || Array.isArray(o.config))) {
38
+ throw new Error(`fleet: ${where}.config must be a mapping of config keys to values.`);
39
+ }
37
40
  return {
38
41
  agents: o.agents,
39
42
  sync: o.sync,
40
43
  login: validateLogin(o.login, where),
44
+ // `config:` is operator config (`agents devices config`); inert to the
45
+ // reconcile engine but part of the manifest shape.
46
+ config: o.config,
41
47
  };
42
48
  }
43
49
  /** Validate a raw `fleet:` object (already YAML-parsed) into a typed manifest. */
@@ -58,17 +64,10 @@ export function parseFleetManifest(raw) {
58
64
  const map = {};
59
65
  for (const [name, ov] of Object.entries(o.devices)) {
60
66
  // An empty entry (`device: {}`) inherits defaults — represent as {}.
61
- const merged = ov == null ? {} : validateDefaults(ov, `devices.${name}`);
62
- // `config:` is the operator-config block (`agents devices config`) —
63
- // inert to the reconcile engine but part of the manifest shape.
64
- const cfg = ov != null ? ov.config : undefined;
65
- if (cfg !== undefined) {
66
- if (typeof cfg !== 'object' || cfg === null || Array.isArray(cfg)) {
67
- throw new Error(`fleet: devices.${name}.config must be a mapping of config keys to values.`);
68
- }
69
- merged.config = cfg;
70
- }
71
- map[name] = merged;
67
+ // (A per-device `config:` here is the LEGACY #2458 store — parsed through
68
+ // so reads stay lossless, folded into the per-device doc by the config
69
+ // migration, never written by current code.)
70
+ map[name] = ov == null ? {} : validateDefaults(ov, `devices.${name}`);
72
71
  }
73
72
  devices = map;
74
73
  }
@@ -76,6 +75,19 @@ export function parseFleetManifest(raw) {
76
75
  throw new Error(`fleet: devices must be the string 'all' or a mapping of device -> overrides (got ${JSON.stringify(o.devices)}).`);
77
76
  }
78
77
  const manifest = { defaults, devices };
78
+ if (o.discovery !== undefined) {
79
+ if (typeof o.discovery !== 'object' || o.discovery === null || Array.isArray(o.discovery)) {
80
+ throw new Error('fleet: discovery must be a mapping of device name to approved or ignored.');
81
+ }
82
+ const discovery = {};
83
+ for (const [name, status] of Object.entries(o.discovery)) {
84
+ if (status !== 'approved' && status !== 'ignored') {
85
+ throw new Error(`fleet: discovery.${name} must be approved or ignored.`);
86
+ }
87
+ discovery[name] = status;
88
+ }
89
+ manifest.discovery = discovery;
90
+ }
79
91
  // Additive, backward-compatible extras (captured by `agents fleet capture`).
80
92
  if (o.secrets !== undefined) {
81
93
  if (typeof o.secrets !== 'object' || o.secrets === null || Array.isArray(o.secrets)) {
@@ -27,6 +27,14 @@ export interface FleetDefaults {
27
27
  sync?: string[];
28
28
  /** Login propagation strategy. Default `'sync'`. */
29
29
  login?: FleetLoginMode;
30
+ /**
31
+ * Fleet-wide config defaults (`agents devices config --fleet <key> <value>`)
32
+ * — the middle layer of the device-config store: built-in default <
33
+ * `fleet.defaults.config` < per-device `devices/<name>/agents.yaml`
34
+ * `config:`. Inert to the reconcile engine (apply never pushes it; it takes
35
+ * effect through the config read path). Names and non-secret values only.
36
+ */
37
+ config?: Record<string, unknown>;
30
38
  }
31
39
  /** Per-device override; any omitted field inherits from `defaults`. */
32
40
  export interface FleetDeviceOverride {
@@ -34,12 +42,10 @@ export interface FleetDeviceOverride {
34
42
  sync?: string[];
35
43
  login?: FleetLoginMode;
36
44
  /**
37
- * Operator config for this device, written by `agents devices config <name>`
38
- * — the device-scope keys of the `lib/device-config.ts` registry (maxAgents,
39
- * schedulerEnabled, ssh*, autoLaunch*, notes, …). This is the ONE store for
40
- * per-device settings: central, synced, and backed up with agents.yaml.
41
- * LEAK-FREE like the rest of the manifest — names and non-secret values only
42
- * (a secrets-bundle NAME is fine; a credential value never is).
45
+ * LEGACY home of per-device operator config (#2458); the current store is the
46
+ * per-device doc `devices/<name>/agents.yaml` `config:` block. Existing
47
+ * values are folded into the device doc by lib/devices/config-migration.ts
48
+ * and stripped here current code never writes this field.
43
49
  */
44
50
  config?: Record<string, unknown>;
45
51
  }
@@ -68,6 +74,12 @@ export interface FleetManifest {
68
74
  };
69
75
  /** Routine NAMES that should be active on the fleet (files sync via the repo). */
70
76
  routines?: string[];
77
+ /**
78
+ * Portable user decisions for Tailscale discovery. A name maps to `approved`
79
+ * or `ignored`; absence means pending. Connection metadata remains in each
80
+ * machine's local device registry and is never committed.
81
+ */
82
+ discovery?: Record<string, 'approved' | 'ignored'>;
71
83
  }
72
84
  /**
73
85
  * A device's desired state after merging defaults with its override and
@@ -118,7 +130,7 @@ export interface DeviceProbe {
118
130
  note?: string;
119
131
  }
120
132
  /** One planned action against a device, in a single reconcile dimension. */
121
- export type FleetActionKind = 'install-cli' | 'upgrade-cli' | 'add-agent' | 'sync-config' | 'push-login' | 'needs-login'
133
+ export type FleetActionKind = 'install-cli' | 'upgrade-cli' | 'add-agent' | 'sync-config' | 'needs-login'
122
134
  /** Push a declared secrets bundle to the device over SSH. Opt-in only
123
135
  * (`--provision-secrets`) and gated on a pinned host key, because this moves
124
136
  * credential VALUES to another machine (RUSH-1968). */
@@ -12,7 +12,7 @@
12
12
  * `runFleetStatusPublish` for the fleet-status mirror); this module is the read
13
13
  * side only.
14
14
  */
15
- import { type UsageSnapshot } from './usage.js';
15
+ import { type UsageSnapshot } from './accounting/usage.js';
16
16
  import { type FleetStatusRow } from './fleet-status.js';
17
17
  /**
18
18
  * The fleet-status union the daemon publishes (own row) and the fleet-status
@@ -12,7 +12,7 @@
12
12
  * `runFleetStatusPublish` for the fleet-status mirror); this module is the read
13
13
  * side only.
14
14
  */
15
- import { readClaudeUsageCache } from './usage.js';
15
+ import { readClaudeUsageCache } from './accounting/usage.js';
16
16
  import { readHeadroomEntry } from './usage-refresh.js';
17
17
  import { readFleetStatus as readFleetStatusMirror } from './fleet-status.js';
18
18
  /**
@@ -50,6 +50,25 @@ export declare function truncate(s: string, max: number): string;
50
50
  export declare function relTime(iso: string): string;
51
51
  /** Format a millisecond duration as "45s", "3m", "2h 5m", "1d 3h". */
52
52
  export declare function humanDuration(ms: number): string;
53
+ /**
54
+ * Human-readable byte size. Previously copy-pasted into five files
55
+ * (`commands/prune.ts`, `commands/share.ts`, `commands/inspect.ts`,
56
+ * `commands/sessions.ts`, `lib/browser/sessions-list.ts`) — this is the
57
+ * canonical home.
58
+ */
59
+ export declare function formatBytes(n: number): string;
60
+ /**
61
+ * True when an error came from the user cancelling a prompt (Ctrl+C).
62
+ *
63
+ * Lives here rather than in `commands/utils.ts` so `lib/` callers
64
+ * (`drift-sync.ts`, `refresh.ts`) don't have to import upward out of `lib/`
65
+ * into the command layer. `commands/utils.ts` re-exports it.
66
+ */
67
+ export declare function isPromptCancelled(err: unknown): boolean;
68
+ /** True when stdin/stdout are attached to a real terminal. */
69
+ export declare function isInteractiveTerminal(): boolean;
70
+ /** Parse a comma-separated CLI list, trimming whitespace and dropping empties. */
71
+ export declare function parseCommaSeparatedList(value: string | undefined): string[];
53
72
  /**
54
73
  * Visible column width of `s`, ignoring ANSI SGR color codes (e.g. chalk
55
74
  * wrappers). Matches the full CSI sequence including the `\x1b` escape.
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import chalk from 'chalk';
11
11
  import { readSync } from 'node:fs';
12
- import { emitFriction } from './events.js';
12
+ import { emitFriction } from './feed/events.js';
13
13
  /**
14
14
  * Render a fatal error to the right stream. Pure — no I/O, no `process.exit` — so
15
15
  * the human-vs-agent split is unit-testable. A `--json` caller gets
@@ -97,6 +97,49 @@ export function humanDuration(ms) {
97
97
  const hh = h % 24;
98
98
  return hh ? `${d}d ${hh}h` : `${d}d`;
99
99
  }
100
+ /**
101
+ * Human-readable byte size. Previously copy-pasted into five files
102
+ * (`commands/prune.ts`, `commands/share.ts`, `commands/inspect.ts`,
103
+ * `commands/sessions.ts`, `lib/browser/sessions-list.ts`) — this is the
104
+ * canonical home.
105
+ */
106
+ export function formatBytes(n) {
107
+ if (n < 1024)
108
+ return `${n} B`;
109
+ const units = ['KB', 'MB', 'GB', 'TB'];
110
+ let size = n / 1024;
111
+ let unit = 0;
112
+ while (size >= 1024 && unit < units.length - 1) {
113
+ size /= 1024;
114
+ unit++;
115
+ }
116
+ return `${size < 10 ? size.toFixed(1) : Math.round(size)} ${units[unit]}`;
117
+ }
118
+ /**
119
+ * True when an error came from the user cancelling a prompt (Ctrl+C).
120
+ *
121
+ * Lives here rather than in `commands/utils.ts` so `lib/` callers
122
+ * (`drift-sync.ts`, `refresh.ts`) don't have to import upward out of `lib/`
123
+ * into the command layer. `commands/utils.ts` re-exports it.
124
+ */
125
+ export function isPromptCancelled(err) {
126
+ return err instanceof Error && (err.name === 'ExitPromptError' ||
127
+ err.message.includes('force closed') ||
128
+ err.message.includes('User force closed'));
129
+ }
130
+ /** True when stdin/stdout are attached to a real terminal. */
131
+ export function isInteractiveTerminal() {
132
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
133
+ }
134
+ /** Parse a comma-separated CLI list, trimming whitespace and dropping empties. */
135
+ export function parseCommaSeparatedList(value) {
136
+ if (!value)
137
+ return [];
138
+ return value
139
+ .split(',')
140
+ .map((item) => item.trim())
141
+ .filter(Boolean);
142
+ }
100
143
  /**
101
144
  * Visible column width of `s`, ignoring ANSI SGR color codes (e.g. chalk
102
145
  * wrappers). Matches the full CSI sequence including the `\x1b` escape.
@@ -6,7 +6,7 @@
6
6
  * yet. This is a starting point: one detector for the most actionable pattern,
7
7
  * an agent stuck retrying the SAME denied action instead of adapting.
8
8
  */
9
- import type { EventRecord } from './events.js';
9
+ import type { EventRecord } from './feed/events.js';
10
10
  export interface RepeatedGuardBlockFinding {
11
11
  /** Session id the repeated blocks happened in, or 'unknown' when the
12
12
  * friction event carried no session (e.g. a guard fired outside any
package/dist/lib/git.d.ts CHANGED
@@ -207,6 +207,10 @@ export declare function isGitRepo(dir: string): boolean;
207
207
  * Shells out to `git rev-parse --show-toplevel`, so it resolves correctly from
208
208
  * any subdirectory and for linked worktrees (unlike the root-only, synchronous
209
209
  * {@link isGitRepo} above). Throws if `dir` is not inside a git repository.
210
+ *
211
+ * Git prints POSIX separators even on Windows (`C:/Users/...`); we fold them to
212
+ * the native separator so the result is a real filesystem path that compares
213
+ * equal to one built with `path.*`. On POSIX this is a no-op.
210
214
  */
211
215
  export declare function getGitRoot(dir: string): Promise<string>;
212
216
  /**
@@ -216,6 +220,10 @@ export declare function getGitRoot(dir: string): Promise<string>;
216
220
  * worktree: `--show-toplevel` there returns the worktree's own path, but the
217
221
  * common git dir (`--git-common-dir`) always points at the primary repo's
218
222
  * `.git`, whose parent is the main checkout. Throws if `dir` is not in a repo.
223
+ *
224
+ * Git prints POSIX separators even on Windows (`C:/Users/...`); we fold them to
225
+ * the native separator so the result is a real filesystem path that compares
226
+ * equal to one built with `path.*`. On POSIX this is a no-op.
219
227
  */
220
228
  export declare function getMainRepoRoot(dir: string): Promise<string>;
221
229
  /**
@@ -281,7 +289,22 @@ export declare function displayHomePath(dir: string): string;
281
289
  * 3. Genuinely diverged (local commits not on tracking) → `rebase` onto the
282
290
  * tracking ref (same outcome as {@link syncRepoGit}, without a second pull).
283
291
  */
284
- export declare function pullRepo(dir: string): Promise<{
292
+ export interface PullRepoOptions {
293
+ /**
294
+ * `'default-branch-fast-forward'` — strict mode for `projects pull`:
295
+ * - Blocks immediately if the tree is dirty (no remote read needed).
296
+ * - Fetches origin, resolves the remote default branch, and refuses if the
297
+ * current branch is not the remote default.
298
+ * - Refuses if HEAD is ahead of the upstream (local commits not on remote).
299
+ * - Fast-forwards only (`merge --ff-only`). NEVER rebases.
300
+ * - NEVER installs git hook symlinks (read-only model path).
301
+ *
302
+ * `'preserve-local'` (default) — the existing behavior: tolerates dirty trees
303
+ * when the incoming diff does not collide, and rebases a diverged branch.
304
+ */
305
+ mode?: 'preserve-local' | 'default-branch-fast-forward';
306
+ }
307
+ export declare function pullRepo(dir: string, options?: PullRepoOptions): Promise<{
285
308
  success: boolean;
286
309
  commit: string;
287
310
  error?: string;
package/dist/lib/git.js CHANGED
@@ -10,7 +10,7 @@ import { execFileSync } from 'node:child_process';
10
10
  import * as fs from 'fs';
11
11
  import * as os from 'os';
12
12
  import * as path from 'path';
13
- import { IS_WINDOWS, isWindowsAbsolutePath } from './platform/index.js';
13
+ import { IS_WINDOWS, isWindowsAbsolutePath, toNativePath } from './platform/index.js';
14
14
  import { getPackageLocalPath } from './state.js';
15
15
  import { DEFAULT_SYSTEM_REPO, systemRepoSlug } from './types.js';
16
16
  /**
@@ -658,10 +658,14 @@ export function isGitRepo(dir) {
658
658
  * Shells out to `git rev-parse --show-toplevel`, so it resolves correctly from
659
659
  * any subdirectory and for linked worktrees (unlike the root-only, synchronous
660
660
  * {@link isGitRepo} above). Throws if `dir` is not inside a git repository.
661
+ *
662
+ * Git prints POSIX separators even on Windows (`C:/Users/...`); we fold them to
663
+ * the native separator so the result is a real filesystem path that compares
664
+ * equal to one built with `path.*`. On POSIX this is a no-op.
661
665
  */
662
666
  export async function getGitRoot(dir) {
663
667
  const root = await simpleGit(dir).revparse(['--show-toplevel']);
664
- return root.trim();
668
+ return toNativePath(root.trim());
665
669
  }
666
670
  /**
667
671
  * Return the absolute path to the **main** working-tree root for `dir`.
@@ -670,10 +674,14 @@ export async function getGitRoot(dir) {
670
674
  * worktree: `--show-toplevel` there returns the worktree's own path, but the
671
675
  * common git dir (`--git-common-dir`) always points at the primary repo's
672
676
  * `.git`, whose parent is the main checkout. Throws if `dir` is not in a repo.
677
+ *
678
+ * Git prints POSIX separators even on Windows (`C:/Users/...`); we fold them to
679
+ * the native separator so the result is a real filesystem path that compares
680
+ * equal to one built with `path.*`. On POSIX this is a no-op.
673
681
  */
674
682
  export async function getMainRepoRoot(dir) {
675
683
  const common = await simpleGit(dir).raw(['rev-parse', '--path-format=absolute', '--git-common-dir']);
676
- return path.dirname(common.trim());
684
+ return toNativePath(path.dirname(common.trim()));
677
685
  }
678
686
  /**
679
687
  * Initialize a git repo in an existing directory.
@@ -863,23 +871,8 @@ export function displayHomePath(dir) {
863
871
  const rel = dir.startsWith(home) ? '~' + dir.slice(home.length) : dir;
864
872
  return rel.replace(/\\/g, '/');
865
873
  }
866
- /**
867
- * Pull changes in an existing repo.
868
- * A dirty working tree no longer refuses outright: a fast-forward that touches
869
- * no uncommitted path still runs (see `dirtyTreeRefusal`). It refuses when the
870
- * branch has local commits to rebase, or when an incoming path is also dirty.
871
- *
872
- * Strategy (RUSH-2282):
873
- * 1. Fetch, then compare HEAD to the resolved tracking ref.
874
- * 2. Clean behind-only (HEAD is an ancestor of tracking) → `merge --ff-only`
875
- * against the tracking ref. Never re-enter `git pull` after a bare fetch —
876
- * multi-entry FETCH_HEAD (concurrent fetch, multi-branch remote) makes
877
- * `git pull --rebase` die with "Cannot rebase onto multiple branches" even
878
- * when the checkout is a pure fast-forward.
879
- * 3. Genuinely diverged (local commits not on tracking) → `rebase` onto the
880
- * tracking ref (same outcome as {@link syncRepoGit}, without a second pull).
881
- */
882
- export async function pullRepo(dir) {
874
+ export async function pullRepo(dir, options = {}) {
875
+ const strict = options.mode === 'default-branch-fast-forward';
883
876
  try {
884
877
  const git = simpleGit(dir);
885
878
  // A rebase left in progress by an earlier run must be reported as itself.
@@ -916,14 +909,24 @@ export async function pullRepo(dir) {
916
909
  };
917
910
  }
918
911
  const status = await git.status();
912
+ // Strict mode (projects pull): refuse a dirty tree immediately — no fetch
913
+ // needed to know the answer, and the caller must not risk touching staged
914
+ // or modified files with an incoming fast-forward.
915
+ const isDirty = !status.isClean();
916
+ if (strict && isDirty) {
917
+ return {
918
+ success: false,
919
+ commit: '',
920
+ error: `Blocked: dirty working tree. Commit or discard local changes before pulling.\n\n cd ${displayHomePath(dir)} && git status`,
921
+ };
922
+ }
919
923
  // A dirty tree is not decided here: whether a fast-forward is safe depends
920
924
  // on what is actually incoming, and that needs a fetched upstream ref. The
921
925
  // gate therefore sits just before the integrate step below. The exception is
922
926
  // a repo with no remote at all — there is nothing to fast-forward from, so
923
927
  // the dirt is the whole answer and the resolution below would only fail
924
928
  // with a less useful message.
925
- const isDirty = !status.isClean();
926
- if (isDirty && (await git.getRemotes()).length === 0) {
929
+ if (!strict && isDirty && (await git.getRemotes()).length === 0) {
927
930
  return {
928
931
  success: false,
929
932
  commit: '',
@@ -931,13 +934,13 @@ export async function pullRepo(dir) {
931
934
  };
932
935
  }
933
936
  const branch = status.current || 'main';
934
- // Resolve the upstream ref to fast-forward against. Prefer the local
935
- // branch's tracking config; otherwise ask origin for its default branch.
937
+ // Resolve the upstream ref to fast-forward against.
938
+ // Strict mode: always fetch origin and resolve its default branch — then
939
+ // verify the current branch IS the remote default (refuse otherwise).
940
+ // Preserve-local mode: prefer the local branch's tracking config; only
941
+ // fetch when no tracking is set.
936
942
  let tracking = status.tracking;
937
- if (!tracking) {
938
- // No tracking config: fetch origin so its HEAD is known, then ask which
939
- // branch it points at. This path only ever concerns origin — a branch with
940
- // no upstream has no other remote to consult.
943
+ if (strict || !tracking) {
941
944
  try {
942
945
  await git.fetch('origin');
943
946
  await git.raw(['remote', 'set-head', 'origin', '--auto']);
@@ -948,6 +951,19 @@ export async function pullRepo(dir) {
948
951
  tracking = `origin/${branch}`;
949
952
  }
950
953
  }
954
+ // Strict: the checkout must be on the remote default branch — never pull a
955
+ // feature branch across the fleet unattended.
956
+ if (strict) {
957
+ const sep = tracking.indexOf('/');
958
+ const expectedBranch = sep > 0 ? tracking.slice(sep + 1) : tracking;
959
+ if (branch !== expectedBranch) {
960
+ return {
961
+ success: false,
962
+ commit: '',
963
+ error: `Blocked: HEAD is on "${branch}" but origin defaults to "${expectedBranch}". Switch to "${expectedBranch}" before pulling.`,
964
+ };
965
+ }
966
+ }
951
967
  // Split the remote-tracking ref (<remote>/<branch>) so callers/logs can name
952
968
  // the remote. Branch names may contain slashes, so split on the FIRST
953
969
  // separator only. The integrate step below uses `tracking` directly (not a
@@ -983,15 +999,32 @@ export async function pullRepo(dir) {
983
999
  // around that call would always report "can ff" (RUSH-2282).
984
1000
  const aheadCount = parseInt((await git.raw(['rev-list', '--count', `${tracking}..HEAD`])).trim(), 10);
985
1001
  const behindCount = parseInt((await git.raw(['rev-list', '--count', `HEAD..${tracking}`])).trim(), 10);
986
- const canFastForward = Number.isFinite(aheadCount) &&
987
- Number.isFinite(behindCount) &&
988
- aheadCount === 0 &&
989
- behindCount > 0;
990
- // Dirty tree: same rule `syncRepoGit` applies, from the same function — a
991
- // fast-forward that touches nothing the author is holding may proceed; a
992
- // rebase or a colliding path may not. Refusing on any dirt at all is what
993
- // used to strand merged changes on every box that had an unrelated edit.
994
- if (isDirty) {
1002
+ // Both counts are the ONLY inputs to the fast-forward decision, so a count
1003
+ // we cannot read is a refusal, not a reason to guess. Fail here rather than
1004
+ // downstream: an unreadable count used to fall through to the rebase arm in
1005
+ // preserve-local mode, and to a "HEAD diverged" message in strict mode that
1006
+ // named the wrong cause.
1007
+ if (!Number.isFinite(aheadCount) || !Number.isFinite(behindCount)) {
1008
+ return {
1009
+ success: false,
1010
+ commit: '',
1011
+ error: `Could not read how far HEAD is from ${tracking} — 'git rev-list --count' returned no usable count. Check the checkout, then pull again:\n\n cd ${displayHomePath(dir)} && git status`,
1012
+ };
1013
+ }
1014
+ const canFastForward = aheadCount === 0 && behindCount > 0;
1015
+ // Strict: block if HEAD has local commits not on the remote — fast-forward
1016
+ // requires the local tip to be an ancestor of the remote tip.
1017
+ if (strict && aheadCount > 0) {
1018
+ return {
1019
+ success: false,
1020
+ commit: '',
1021
+ error: `Blocked: HEAD is ${aheadCount} commit${aheadCount === 1 ? '' : 's'} ahead of ${tracking}. Push or discard local commits before pulling.`,
1022
+ };
1023
+ }
1024
+ // Dirty tree (preserve-local only): same rule `syncRepoGit` applies —
1025
+ // a fast-forward that touches nothing the author is holding may proceed; a
1026
+ // rebase or a colliding path may not. Strict mode already refused above.
1027
+ if (!strict && isDirty) {
995
1028
  const refusal = await dirtyTreeRefusal(git, status, tracking);
996
1029
  if (refusal) {
997
1030
  return {
@@ -1010,6 +1043,14 @@ export async function pullRepo(dir) {
1010
1043
  // Diverged (or local-only commits). Rebase onto the tracking tip —
1011
1044
  // same outcome as `git pull --rebase <remote> <branch>` without
1012
1045
  // re-fetching or consulting FETCH_HEAD.
1046
+ //
1047
+ // PRESERVE-LOCAL ONLY — strict mode can never reach this arm, so the
1048
+ // fleet pull never rewrites history. Strict has already returned for
1049
+ // every case that leaves `canFastForward` false: an identical local and
1050
+ // remote ref, `aheadCount > 0` (which is also what "diverged" means),
1051
+ // and a count that would not parse. That leaves `aheadCount === 0` with
1052
+ // a differing ref, i.e. `behindCount > 0` — a fast-forward. Keep those
1053
+ // three returns above intact if you change this.
1013
1054
  await git.raw(['rebase', tracking]);
1014
1055
  }
1015
1056
  }
@@ -1028,7 +1069,10 @@ export async function pullRepo(dir) {
1028
1069
  error: `${verb} onto ${tracking} failed — the pull was rolled back, nothing changed.\n\nResolve the divergence, then pull again:\n\n cd ${displayHomePath(dir)} && git log --oneline HEAD...${tracking}\n\n${err.message}`,
1029
1070
  };
1030
1071
  }
1031
- installGithooksSymlinks(dir);
1072
+ // Strict mode never installs git hook symlinks — the command is a read-model
1073
+ // operation (update pointers only; never reconfigure the checkout).
1074
+ if (!strict)
1075
+ installGithooksSymlinks(dir);
1032
1076
  const log = await git.log({ maxCount: 1 });
1033
1077
  return {
1034
1078
  success: true,
package/dist/lib/heal.js CHANGED
@@ -23,10 +23,10 @@
23
23
  * `agents prune cleanup`, so a heal pass can never lose work.
24
24
  */
25
25
  import { ALL_AGENT_IDS } from './agents.js';
26
- import { syncResourcesToVersion, listInstalledVersions, isVersionIsolated, getVersionHomePath, getActuallySyncedResources, compareVersions, } from './versions.js';
26
+ import { syncResourcesToVersion, listInstalledVersions, isVersionIsolated, getVersionHomePath, getActuallySyncedResources, compareVersions, } from './installations/versions.js';
27
27
  import { diffVersionResources, } from './doctor-diff.js';
28
- import { discoverPlugins, updatePlugin, readPluginSourceInfo, getUpstreamManifestVersion, } from './plugins.js';
29
- import { repairPluginManifestFile } from './plugin-marketplace.js';
28
+ import { discoverPlugins, updatePlugin, readPluginSourceInfo, getUpstreamManifestVersion, } from './plugins/plugins.js';
29
+ import { repairPluginManifestFile } from './plugins/plugin-marketplace.js';
30
30
  import * as fs from 'fs';
31
31
  import * as path from 'path';
32
32
  import * as os from 'os';
@@ -386,3 +386,21 @@ export declare function registerHooksToSettings(agentId: AgentId, versionHome: s
386
386
  * entries removed.
387
387
  */
388
388
  export declare function pruneVersionHomeHookEntriesFromSettings(settingsPath: string, agent: AgentId, removedVersion: string): number;
389
+ export interface InstallSessionTrackerHookResult {
390
+ installed: boolean;
391
+ error?: string;
392
+ }
393
+ /**
394
+ * Install the shared SessionStart state-writer hook (`packages/session-tracker`)
395
+ * into the harness's native config. Gated by `supports(agent, 'hooks', version)`;
396
+ * unsupported agents return `installed: false` with a reason instead of throwing.
397
+ *
398
+ * This is the bridge that makes bare `claude` / `codex` / `kimi` launches write
399
+ * `~/.agents/.cache/terminals/sessions/<pid>.json`, not just `agents run` launches.
400
+ */
401
+ export declare function installSessionTrackerHook(agent: AgentId, version?: string, home?: string): Promise<InstallSessionTrackerHookResult>;
402
+ /**
403
+ * Synchronous variant for callers that cannot be made async (e.g.
404
+ * `syncResourcesToVersion`). Best-effort: failures are returned, not thrown.
405
+ */
406
+ export declare function installSessionTrackerHookSync(agent: AgentId, version?: string, home?: string): InstallSessionTrackerHookResult;
package/dist/lib/hooks.js CHANGED
@@ -13,6 +13,9 @@ import * as os from 'os';
13
13
  import * as path from 'path';
14
14
  import * as yaml from 'yaml';
15
15
  import * as TOML from 'smol-toml';
16
+ import { execFile, execFileSync } from 'child_process';
17
+ import { promisify } from 'util';
18
+ import { fileURLToPath } from 'url';
16
19
  import { AGENTS, agentConfigDirName, isAgentHardDeprecated } from './agents.js';
17
20
  import { supports, explainSkip, capableAgents } from './capabilities.js';
18
21
  import { getHooksDir as getSystemHooksDir, getUserHooksDir, getUserAgentsDir, getSystemAgentsDir, getProjectAgentsDir, getTrashHooksDir, getEnabledExtraRepos, getResolvedRulesDir, getUserRulesDir, getPerfDir } from './state.js';
@@ -264,7 +267,7 @@ function collapseVersionHookEntries(entries, activeVersionHome) {
264
267
  return true;
265
268
  });
266
269
  }
267
- import { getEffectiveHome, getGlobalDefault, getVersionHomePath, isVersionIsolated, listInstalledVersions, resolveVersion, } from './versions.js';
270
+ import { getEffectiveHome, getGlobalDefault, getVersionHomePath, isVersionIsolated, listInstalledVersions, resolveVersion, } from './installations/versions.js';
268
271
  import { generateHookShim, getHookShimPath, isValidHookShimName, parseCacheConfig, removeHookShim } from './hooks/cache.js';
269
272
  import { getHookShimsDir } from './state.js';
270
273
  function hookContentHash(scriptPath) {
@@ -3244,3 +3247,112 @@ function registerHooksForHermes(versionHome, manifest, resolveScript, managedPre
3244
3247
  }
3245
3248
  return { registered, errors };
3246
3249
  }
3250
+ const execFileAsync = promisify(execFile);
3251
+ function resolveSessionTrackerRoot() {
3252
+ const here = path.dirname(fileURLToPath(import.meta.url));
3253
+ // Built/installed layout: dist/lib/ -> dist/session-tracker
3254
+ const built = path.resolve(here, '..', 'session-tracker');
3255
+ if (fs.existsSync(path.join(built, 'dist', 'hook.sh'))) {
3256
+ return built;
3257
+ }
3258
+ // Source layout: apps/cli/src/lib/ -> repo root -> packages/session-tracker
3259
+ const source = path.resolve(here, '..', '..', '..', '..', 'packages', 'session-tracker');
3260
+ if (fs.existsSync(path.join(source, 'src', 'hook.sh'))) {
3261
+ return source;
3262
+ }
3263
+ return null;
3264
+ }
3265
+ function resolveTsxLoader(trackerRoot) {
3266
+ const loader = path.join(trackerRoot, 'node_modules', 'tsx', 'dist', 'cli.mjs');
3267
+ if (fs.existsSync(loader))
3268
+ return loader;
3269
+ return null;
3270
+ }
3271
+ function buildInstallHookInvocation(trackerRoot, agent) {
3272
+ const distScript = path.join(trackerRoot, 'dist', 'install-hook.js');
3273
+ if (fs.existsSync(distScript)) {
3274
+ return { command: process.execPath, args: [distScript, agent] };
3275
+ }
3276
+ const loader = resolveTsxLoader(trackerRoot);
3277
+ if (!loader)
3278
+ return null;
3279
+ return {
3280
+ command: process.execPath,
3281
+ args: [loader, path.join(trackerRoot, 'src', 'install-hook.ts'), agent],
3282
+ };
3283
+ }
3284
+ /**
3285
+ * Install the shared SessionStart state-writer hook (`packages/session-tracker`)
3286
+ * into the harness's native config. Gated by `supports(agent, 'hooks', version)`;
3287
+ * unsupported agents return `installed: false` with a reason instead of throwing.
3288
+ *
3289
+ * This is the bridge that makes bare `claude` / `codex` / `kimi` launches write
3290
+ * `~/.agents/.cache/terminals/sessions/<pid>.json`, not just `agents run` launches.
3291
+ */
3292
+ export async function installSessionTrackerHook(agent, version, home) {
3293
+ const gate = supports(agent, 'hooks', version);
3294
+ if (!gate.ok) {
3295
+ return { installed: false, error: explainSkip(agent, 'hooks', gate, version) };
3296
+ }
3297
+ const trackerRoot = resolveSessionTrackerRoot();
3298
+ if (!trackerRoot) {
3299
+ return { installed: false, error: 'session-tracker package not found' };
3300
+ }
3301
+ const invocation = buildInstallHookInvocation(trackerRoot, agent);
3302
+ if (!invocation) {
3303
+ return { installed: false, error: 'session-tracker not built and tsx is unavailable' };
3304
+ }
3305
+ try {
3306
+ await execFileAsync(invocation.command, invocation.args, {
3307
+ env: { ...process.env, HOME: home ?? process.env.HOME },
3308
+ encoding: 'utf8',
3309
+ });
3310
+ return { installed: true };
3311
+ }
3312
+ catch (err) {
3313
+ return { installed: false, error: installFailureMessage(err) };
3314
+ }
3315
+ }
3316
+ /**
3317
+ * The child reports its refusal reason on stdout (its HOOK_SUPPORT table), not
3318
+ * stderr — and a Buffer-typed empty stderr is truthy, so a bare `err.stderr ??
3319
+ * err.message` surfaced an empty string. Prefer the first NON-EMPTY stream.
3320
+ */
3321
+ function installFailureMessage(err) {
3322
+ const e = err;
3323
+ for (const stream of [e.stderr, e.stdout]) {
3324
+ const text = stream == null ? '' : String(stream).trim();
3325
+ if (text.length > 0)
3326
+ return text;
3327
+ }
3328
+ return e.message;
3329
+ }
3330
+ /**
3331
+ * Synchronous variant for callers that cannot be made async (e.g.
3332
+ * `syncResourcesToVersion`). Best-effort: failures are returned, not thrown.
3333
+ */
3334
+ export function installSessionTrackerHookSync(agent, version, home) {
3335
+ const gate = supports(agent, 'hooks', version);
3336
+ if (!gate.ok) {
3337
+ return { installed: false, error: explainSkip(agent, 'hooks', gate, version) };
3338
+ }
3339
+ const trackerRoot = resolveSessionTrackerRoot();
3340
+ if (!trackerRoot) {
3341
+ return { installed: false, error: 'session-tracker package not found' };
3342
+ }
3343
+ const invocation = buildInstallHookInvocation(trackerRoot, agent);
3344
+ if (!invocation) {
3345
+ return { installed: false, error: 'session-tracker not built and tsx is unavailable' };
3346
+ }
3347
+ try {
3348
+ execFileSync(invocation.command, invocation.args, {
3349
+ env: { ...process.env, HOME: home ?? process.env.HOME },
3350
+ stdio: ['ignore', 'pipe', 'pipe'],
3351
+ encoding: 'utf8',
3352
+ });
3353
+ return { installed: true };
3354
+ }
3355
+ catch (err) {
3356
+ return { installed: false, error: installFailureMessage(err) };
3357
+ }
3358
+ }