@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
@@ -16,17 +16,18 @@ import { query as queryEvents, queryToolUsageForSessions } from '../events.js';
16
16
  import { machineForSessionFile } from './origin-machine.js';
17
17
  import { loadSessionActorIndex, readSessionActorRecord } from './actor-sidecar.js';
18
18
  import { toolCallsFromEvents } from './tool-calls.js';
19
- import { persistToolCalls, purgeToolCalls, toolEvidenceSourcePath } from './tool-store.js';
19
+ import { persistToolCalls, toolEvidenceSourcePath } from './tool-store.js';
20
20
  import { buildClaudeAccountIndex, resolveClaudeAccount } from './claude-accounts.js';
21
21
  import { extractSkills, extractSlashCommands } from './highlights.js';
22
22
  import { resolveResource } from '../resources.js';
23
23
  import { discoverPlugins } from '../plugins.js';
24
+ import { machineId } from '../machine-id.js';
24
25
  const SESSIONS_DIR = getSessionsDir();
25
26
  const DB_PATH = getSessionsDbPath();
26
27
  /** Current schema version; bumped when migrations are added. Exported so tests
27
28
  * assert against the constant instead of hardcoding a number that every bump
28
- * then has to chase (docs/05-sessions.md calls the constant the source of truth). */
29
- export const SCHEMA_VERSION = 37;
29
+ * then has to chase (docs/sessions.md calls the constant the source of truth). */
30
+ export const SCHEMA_VERSION = 39;
30
31
  /**
31
32
  * Bump to force `agents sessions backfill resources` to re-derive every
32
33
  * session's skill/slash-command tallies on its next run (resource_scan_ledger
@@ -108,7 +109,14 @@ CREATE TABLE IF NOT EXISTS sessions (
108
109
  actor TEXT,
109
110
  initiated_by TEXT,
110
111
  used_browser INTEGER,
111
- used_computer INTEGER
112
+ used_computer INTEGER,
113
+ -- Epoch ms of the first time a previously-scanned transcript was confirmed
114
+ -- gone from disk while its user-turn content still lives in session_text
115
+ -- (RUSH-2436). Non-NULL means "archived": the row is served/rendered from the
116
+ -- DB and flagged, instead of being dropped when the file vanishes. A row whose
117
+ -- file is missing but which has NO cached content is a phantom (a stale/moved
118
+ -- file_path), never stamped, still suppressed — see querySessions.
119
+ archived_at INTEGER
112
120
  );
113
121
  CREATE INDEX IF NOT EXISTS idx_sessions_timestamp ON sessions(timestamp DESC);
114
122
  CREATE INDEX IF NOT EXISTS idx_sessions_cwd ON sessions(cwd);
@@ -317,6 +325,69 @@ CREATE TABLE IF NOT EXISTS session_preview_cache (
317
325
  computed_at INTEGER NOT NULL,
318
326
  preview_json TEXT NOT NULL
319
327
  );
328
+
329
+ -- Durable metadata for one browser task (RUSH-2549). The browser daemon's
330
+ -- tasks.json is LIVE state: saveTaskState writes the in-memory task map, so
331
+ -- stopping a task drops its entry and a daemon restart empties the file. That is
332
+ -- correct for live state and useless as history, which is why every finished task
333
+ -- listed as "unlinked". This row is written once at task start and is never
334
+ -- deleted, so the link from a capture back to the agent session that drove it
335
+ -- survives the task, the daemon, and a reboot.
336
+ --
337
+ -- METADATA ONLY: capture bytes are never stored here. capture_dir points at the
338
+ -- on-disk directory (.cache/browser/<profile>/sessions/<task>/) that already holds
339
+ -- them, and the per-kind counts are what a listing needs to render a row without
340
+ -- walking that tree. captures_remote is set only when the optional offload has
341
+ -- copied them through the existing encrypted r2.backups sync.
342
+ --
343
+ -- session_id is the agent session (AGENT_SESSION_ID, which every agent carries);
344
+ -- launch_id stays as the secondary join key for a caller that has only that. actor
345
+ -- is the HUMAN/tailnet identity and is deliberately NOT an agent id -- resolveActor
346
+ -- answers UNRESOLVED@<host> for any local run by design (lib/actor.ts).
347
+ CREATE TABLE IF NOT EXISTS browser_sessions (
348
+ task TEXT NOT NULL,
349
+ profile TEXT NOT NULL,
350
+ session_id TEXT,
351
+ launch_id TEXT,
352
+ actor TEXT,
353
+ machine TEXT NOT NULL,
354
+ started_at INTEGER NOT NULL,
355
+ last_activity INTEGER,
356
+ screenshot_count INTEGER NOT NULL DEFAULT 0,
357
+ pdf_count INTEGER NOT NULL DEFAULT 0,
358
+ recording_count INTEGER NOT NULL DEFAULT 0,
359
+ download_count INTEGER NOT NULL DEFAULT 0,
360
+ capture_dir TEXT,
361
+ captures_remote TEXT,
362
+ PRIMARY KEY (profile, task)
363
+ );
364
+ CREATE INDEX IF NOT EXISTS idx_browser_sessions_session ON browser_sessions(session_id);
365
+ CREATE INDEX IF NOT EXISTS idx_browser_sessions_started ON browser_sessions(started_at DESC);
366
+
367
+ -- Durable metadata for one "agents computer" invocation (RUSH-2549). Computer-use
368
+ -- already resolves identity correctly -- stampProvenance stamps AGENT_SESSION_ID
369
+ -- straight onto each computer.action event -- but those events live in the bounded
370
+ -- audit ledger, which prunes at 7 days / 50 MiB (events.ts). So a run's history
371
+ -- silently vanished on day 8. This row carries the same identity into the durable
372
+ -- store; the ledger is untouched and remains the audit log, with no second pruner.
373
+ --
374
+ -- Keyed on invocation_id, the id emitComputerAction stamps once per emitting CLI
375
+ -- process: one explicit verb is one row, and a whole "computer run" observe/act
376
+ -- loop is also one row. task_preview is already bounded by events.ts truncate()
377
+ -- before it is ever written, and typed-text content is never captured at all.
378
+ CREATE TABLE IF NOT EXISTS computer_sessions (
379
+ invocation_id TEXT PRIMARY KEY,
380
+ session_id TEXT,
381
+ launch_id TEXT,
382
+ actor TEXT,
383
+ machine TEXT NOT NULL,
384
+ started_at INTEGER NOT NULL,
385
+ last_activity INTEGER,
386
+ action_count INTEGER NOT NULL DEFAULT 0,
387
+ task_preview TEXT
388
+ );
389
+ CREATE INDEX IF NOT EXISTS idx_computer_sessions_session ON computer_sessions(session_id);
390
+ CREATE INDEX IF NOT EXISTS idx_computer_sessions_started ON computer_sessions(started_at DESC);
320
391
  `;
321
392
  /**
322
393
  * Bumping this invalidates every cached facet row without touching the schema
@@ -912,6 +983,60 @@ function migrateSchema(db, fromVersion) {
912
983
  if (!cols.has('cost_usd_nocache'))
913
984
  db.exec(`ALTER TABLE sessions ADD COLUMN cost_usd_nocache REAL`);
914
985
  }
986
+ if (fromVersion < 38) {
987
+ // v37 -> v38: archived_at (RUSH-2436). Makes the local DB authoritative for
988
+ // content: a session whose transcript file is gone but whose user turns still
989
+ // live in session_text is kept (flagged archived) instead of dropped from
990
+ // listings. New nullable column, so no ledger flush — it is populated lazily
991
+ // by querySessions the first time it confirms a scanned file is gone.
992
+ const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
993
+ if (!cols.some((c) => c.name === 'archived_at'))
994
+ db.exec(`ALTER TABLE sessions ADD COLUMN archived_at INTEGER`);
995
+ }
996
+ if (fromVersion < 39) {
997
+ // v38 -> v39: durable tool-session metadata (RUSH-2549). Browser task identity
998
+ // lived only in the daemon's tasks.json, which is rewritten from the live task
999
+ // map -- so stopping a task erased the link to the agent session that drove it
1000
+ // and every finished task listed as "unlinked". Computer-use had the identity
1001
+ // right but wrote it to the 7-day event ledger, so its history expired instead.
1002
+ // Both now persist metadata here. Pure additions, so no ledger flush: nothing
1003
+ // already indexed is invalidated, and the tables populate going forward.
1004
+ db.exec(`
1005
+ CREATE TABLE IF NOT EXISTS browser_sessions (
1006
+ task TEXT NOT NULL,
1007
+ profile TEXT NOT NULL,
1008
+ session_id TEXT,
1009
+ launch_id TEXT,
1010
+ actor TEXT,
1011
+ machine TEXT NOT NULL,
1012
+ started_at INTEGER NOT NULL,
1013
+ last_activity INTEGER,
1014
+ screenshot_count INTEGER NOT NULL DEFAULT 0,
1015
+ pdf_count INTEGER NOT NULL DEFAULT 0,
1016
+ recording_count INTEGER NOT NULL DEFAULT 0,
1017
+ download_count INTEGER NOT NULL DEFAULT 0,
1018
+ capture_dir TEXT,
1019
+ captures_remote TEXT,
1020
+ PRIMARY KEY (profile, task)
1021
+ );
1022
+ CREATE INDEX IF NOT EXISTS idx_browser_sessions_session ON browser_sessions(session_id);
1023
+ CREATE INDEX IF NOT EXISTS idx_browser_sessions_started ON browser_sessions(started_at DESC);
1024
+
1025
+ CREATE TABLE IF NOT EXISTS computer_sessions (
1026
+ invocation_id TEXT PRIMARY KEY,
1027
+ session_id TEXT,
1028
+ launch_id TEXT,
1029
+ actor TEXT,
1030
+ machine TEXT NOT NULL,
1031
+ started_at INTEGER NOT NULL,
1032
+ last_activity INTEGER,
1033
+ action_count INTEGER NOT NULL DEFAULT 0,
1034
+ task_preview TEXT
1035
+ );
1036
+ CREATE INDEX IF NOT EXISTS idx_computer_sessions_session ON computer_sessions(session_id);
1037
+ CREATE INDEX IF NOT EXISTS idx_computer_sessions_started ON computer_sessions(started_at DESC);
1038
+ `);
1039
+ }
915
1040
  }
916
1041
  /**
917
1042
  * Stamp `account_key` / `account_org` / `account` on every Claude row from its
@@ -2134,6 +2259,11 @@ function rowToMeta(row) {
2134
2259
  // instead of trusting a false "never used browser/computer".
2135
2260
  usedBrowser: row.used_browser === null ? undefined : row.used_browser === 1,
2136
2261
  usedComputer: row.used_computer === null ? undefined : row.used_computer === 1,
2262
+ // A stamped archived_at means the transcript file is gone but the session's
2263
+ // user turns still live in session_text — the row is served from the DB and
2264
+ // flagged, never dropped (RUSH-2436). NULL leaves both undefined (live row).
2265
+ archivedAt: row.archived_at ?? undefined,
2266
+ archived: row.archived_at != null ? true : undefined,
2137
2267
  };
2138
2268
  }
2139
2269
  function isSessionRunMode(value) {
@@ -2399,23 +2529,61 @@ export function querySessions(options = {}) {
2399
2529
  const trimmed = options.limit ? rows.slice(0, options.limit) : rows;
2400
2530
  return trimmed.map(rowToMeta);
2401
2531
  }
2402
- // Belt-and-suspenders: drop rows whose JSONL no longer exists on disk. The
2403
- // authoritative fix is to keep file_path in sync (see updateSessionFilePaths
2404
- // callers), but skipping vanished rows here prevents phantom sessions from
2405
- // surfacing in the Factory UI if any code path forgets to rewrite (#136).
2406
- // Synthetic rows (OpenClaw channels/cron see scanOpenClawIncremental) carry
2407
- // an empty file_path and are exempt; they're keyed by CLI output, not files.
2532
+ // A row whose transcript file is gone from disk is one of two things, and the
2533
+ // local DB is now authoritative for telling them apart (RUSH-2436):
2534
+ // - ARCHIVED its user turns still live in session_text. The session is
2535
+ // real; the file was just removed (agents remove trash, a manual rm, a
2536
+ // .history rotation, a box reimage). Keep it, stamped `archived`, so it
2537
+ // still lists and renders from the DB instead of silently vanishing.
2538
+ // - PHANTOM — a stale/moved file_path with NO cached content (#136), e.g. a
2539
+ // path a scan forgot to rewrite. There is nothing durable to serve, so it
2540
+ // stays suppressed exactly as before.
2541
+ // We NO LONGER purge tool-call evidence here: merely listing a file-gone
2542
+ // session used to DELETE its redacted tool calls (purgeToolCalls), destroying
2543
+ // durable data on a read. Synthetic rows (OpenClaw channels/cron — see
2544
+ // scanOpenClawIncremental) carry an empty file_path and are exempt.
2408
2545
  const missingPaths = findMissingFilePaths(rows.map(r => r.file_path).filter((p) => !!p));
2409
2546
  const missing = rows.filter(r => r.file_path && missingPaths.has(r.file_path));
2547
+ const missingIds = new Set(missing.map(r => r.id));
2548
+ const phantomIds = new Set();
2410
2549
  if (missing.length > 0) {
2411
- const purge = db.transaction(() => {
2412
- for (const row of missing)
2413
- purgeToolCalls(db, row.id);
2550
+ const readContent = db.prepare(`SELECT content FROM session_text WHERE session_id = ?`);
2551
+ const markArchived = db.prepare(`UPDATE sessions SET archived_at = ? WHERE id = ? AND archived_at IS NULL`);
2552
+ const now = Date.now();
2553
+ const classify = db.transaction(() => {
2554
+ for (const row of missing) {
2555
+ const content = readContent.get(row.id)?.content;
2556
+ if (content && content.trim() !== '') {
2557
+ // Genuine archived session: stamp archived_at the first time we confirm
2558
+ // the file is gone, and reflect it on the in-memory row we return.
2559
+ if (row.archived_at == null) {
2560
+ markArchived.run(now, row.id);
2561
+ row.archived_at = now;
2562
+ }
2563
+ }
2564
+ else {
2565
+ phantomIds.add(row.id);
2566
+ }
2567
+ }
2414
2568
  });
2415
- purge();
2569
+ classify();
2570
+ }
2571
+ // Un-archive a row whose file came back (a recoverable-trash restore, a re-sync):
2572
+ // it is present on disk now, so it must not keep reporting `archived` to a
2573
+ // machine consumer reading the --json listing. Rare, so the write only fires when
2574
+ // such a row actually exists (RUSH-2436).
2575
+ const resurrected = rows.filter(r => r.archived_at != null && !missingIds.has(r.id));
2576
+ if (resurrected.length > 0) {
2577
+ const clearArchived = db.prepare(`UPDATE sessions SET archived_at = NULL WHERE id = ?`);
2578
+ const clear = db.transaction(() => {
2579
+ for (const row of resurrected) {
2580
+ clearArchived.run(row.id);
2581
+ row.archived_at = null;
2582
+ }
2583
+ });
2584
+ clear();
2416
2585
  }
2417
- const missingIds = new Set(missing.map((row) => row.id));
2418
- const live = rows.filter(r => !r.file_path || !missingIds.has(r.id));
2586
+ const live = rows.filter(r => !phantomIds.has(r.id));
2419
2587
  const trimmed = options.limit ? live.slice(0, options.limit) : live;
2420
2588
  return trimmed.map(rowToMeta);
2421
2589
  }
@@ -2534,6 +2702,47 @@ export function writeSessionPreviewCache(entry) {
2534
2702
  preview_json = excluded.preview_json
2535
2703
  `).run(entry.id, entry.fileMtimeMs, entry.fileSize, PREVIEW_EXTRACTOR_VERSION, Date.now(), JSON.stringify(entry.preview));
2536
2704
  }
2705
+ /**
2706
+ * The session's durable user-turn text, as stored in the `session_text` FTS
2707
+ * `content` column at scan time (all harnesses, keyed by session_id). This is
2708
+ * the content that survives the transcript file being deleted (RUSH-2436): the
2709
+ * render path reads it so a file-gone session still shows its user prompts, and
2710
+ * querySessions uses its presence to tell a genuine archived session (has
2711
+ * content) from a phantom (a stale/moved file_path with none). Returns undefined
2712
+ * when no row exists; an empty string when a row exists but carried no content.
2713
+ */
2714
+ export function readSessionContent(id) {
2715
+ const row = getDB().prepare(`SELECT content FROM session_text WHERE session_id = ?`).get(id);
2716
+ return row?.content;
2717
+ }
2718
+ /**
2719
+ * Read the last-computed preview digest for a session by id, validated against
2720
+ * the session row's OWN stored file stamp rather than a live `fs.stat` — the
2721
+ * transcript file is gone, so there is nothing on disk to stat, but the preview
2722
+ * cache was written with the same `file_mtime_ms`/`file_size` the sessions row
2723
+ * still records, so the join re-establishes that stamp. Backs the file-gone
2724
+ * render/preview path (RUSH-2436). Returns undefined when no cached digest
2725
+ * survives for this session.
2726
+ */
2727
+ export function readArchivedSessionPreview(id) {
2728
+ const row = getDB().prepare(`
2729
+ SELECT pc.preview_json AS previewJson
2730
+ FROM session_preview_cache pc
2731
+ JOIN sessions s ON s.id = pc.session_id
2732
+ WHERE pc.session_id = ?
2733
+ AND pc.extractor_version = ?
2734
+ AND pc.file_mtime_ms IS s.file_mtime_ms
2735
+ AND pc.file_size IS s.file_size
2736
+ `).get(id, PREVIEW_EXTRACTOR_VERSION);
2737
+ if (!row)
2738
+ return undefined;
2739
+ try {
2740
+ return JSON.parse(row.previewJson);
2741
+ }
2742
+ catch {
2743
+ return undefined;
2744
+ }
2745
+ }
2537
2746
  /** Plugin provenance already indexed for resources used by one session. */
2538
2747
  export function getSessionPlugins(id) {
2539
2748
  const rows = getDB().prepare(`
@@ -2862,7 +3071,38 @@ export function topSessionsByCost(n, options = {}) {
2862
3071
  // Over-fetch a small buffer to survive the on-disk liveness filter below.
2863
3072
  const sql = `SELECT * FROM sessions ${whereCost} ORDER BY cost_usd DESC, timestamp DESC LIMIT ${limit + 16}`;
2864
3073
  const rows = db.prepare(sql).all(...params);
2865
- const live = rows.filter(r => !r.file_path || fs.existsSync(sessionFilePathContainer(r.file_path)));
3074
+ // Keep a row whose file is present OR whose transcript is already archived
3075
+ // (file gone but user turns still in session_text) — an expensive, real
3076
+ // session must not drop out of the cost rollup just because its file was
3077
+ // removed (RUSH-2436). A file-gone row with no durable content (a phantom) is
3078
+ // still excluded. archived_at is the persisted signal; stamp it here too (once)
3079
+ // so a session first surfaced through the cost rollup reports `archived`
3080
+ // consistently with the listing path.
3081
+ const readContent = db.prepare(`SELECT content FROM session_text WHERE session_id = ?`);
3082
+ const markArchived = db.prepare(`UPDATE sessions SET archived_at = ? WHERE id = ? AND archived_at IS NULL`);
3083
+ const now = Date.now();
3084
+ const toStamp = [];
3085
+ const live = rows.filter(r => {
3086
+ if (!r.file_path || fs.existsSync(sessionFilePathContainer(r.file_path)))
3087
+ return true;
3088
+ if (r.archived_at != null)
3089
+ return true;
3090
+ const content = readContent.get(r.id)?.content;
3091
+ if (!!content && content.trim() !== '') {
3092
+ toStamp.push(r);
3093
+ return true;
3094
+ }
3095
+ return false;
3096
+ });
3097
+ if (toStamp.length > 0) {
3098
+ const stamp = db.transaction(() => {
3099
+ for (const r of toStamp) {
3100
+ markArchived.run(now, r.id);
3101
+ r.archived_at = now;
3102
+ }
3103
+ });
3104
+ stamp();
3105
+ }
2866
3106
  return live.slice(0, limit).map(r => ({
2867
3107
  meta: rowToMeta(r),
2868
3108
  costUsd: r.cost_usd ?? 0,
@@ -2870,6 +3110,39 @@ export function topSessionsByCost(n, options = {}) {
2870
3110
  }));
2871
3111
  }
2872
3112
  /** Look up a single session by its unique ID. */
3113
+ /**
3114
+ * Batch-resolve session ids to the machine each one runs on, in ONE indexed
3115
+ * query. `getActiveSessions` needs only this column for every live row, and
3116
+ * `getSessionById` would re-`prepare` a `SELECT *` and materialize a full
3117
+ * `SessionMeta` per id to read it — mirrors {@link findSessionsByShortIds}'s
3118
+ * single-round-trip pattern. Ids absent from the index are simply absent from
3119
+ * the map. Best-effort: an unavailable DB yields an empty map, so the live view
3120
+ * still renders (the caller then leaves rows attributed to this box).
3121
+ */
3122
+ export function findSessionMachinesByIds(ids) {
3123
+ const out = new Map();
3124
+ const uniq = [...new Set(ids.filter(Boolean))];
3125
+ if (uniq.length === 0)
3126
+ return out;
3127
+ try {
3128
+ const db = getDB();
3129
+ const CHUNK = 500; // stay well under SQLite's default 999-variable limit
3130
+ for (let i = 0; i < uniq.length; i += CHUNK) {
3131
+ const batch = uniq.slice(i, i + CHUNK);
3132
+ const placeholders = batch.map(() => '?').join(',');
3133
+ const rows = db
3134
+ .prepare(`SELECT id, machine FROM sessions WHERE id IN (${placeholders})`)
3135
+ .all(...batch);
3136
+ for (const r of rows)
3137
+ if (r.machine)
3138
+ out.set(r.id, r.machine);
3139
+ }
3140
+ }
3141
+ catch {
3142
+ /* index read is best-effort — an unavailable DB leaves rows un-attributed */
3143
+ }
3144
+ return out;
3145
+ }
2873
3146
  export function getSessionById(id) {
2874
3147
  const db = getDB();
2875
3148
  const row = db.prepare(`SELECT * FROM sessions WHERE id = ?`).get(id);
@@ -2882,6 +3155,12 @@ export function getSessionById(id) {
2882
3155
  * short id. An exact hit short-circuits so a complete id never also drags in its
2883
3156
  * prefix siblings. `scope` narrows by agent / version / project (cwd) so an
2884
3157
  * ambiguous prefix disambiguates against the caller's context.
3158
+ *
3159
+ * Routes through the full querySessions existence check (NOT skipExistenceCheck)
3160
+ * on purpose (RUSH-2436): that check now KEEPS a file-gone session whose user
3161
+ * turns still live in session_text (flagged archived) and only suppresses a
3162
+ * contentless phantom — so `agents sessions <id>` resolves an archived session
3163
+ * instead of failing with "No session found", while a phantom id still misses.
2885
3164
  */
2886
3165
  export function findSessionsById(idQuery, scope = {}) {
2887
3166
  const q = idQuery.trim();
@@ -3100,3 +3379,191 @@ export function updateSessionFilePaths(oldPrefix, newPrefix) {
3100
3379
  txn();
3101
3380
  return rows.length;
3102
3381
  }
3382
+ /**
3383
+ * Upsert one browser task's durable metadata.
3384
+ *
3385
+ * Called at task START. This executes INSIDE the browser daemon (daemon.ts
3386
+ * constructs BrowserService; ipc.ts dispatches `start` to it), which makes the
3387
+ * shared daemon a writer of this DB — but the identity it writes is RESOLVED IN
3388
+ * THE CALLING CLI PROCESS and forwarded over IPC, never resolved here. That
3389
+ * distinction is the whole fix: resolving it daemon-side would attribute every
3390
+ * task to the daemon's own actor (the RUSH-2020 bug).
3391
+ *
3392
+ * `agents browser stop` deliberately does NOT delete this row: the whole point
3393
+ * is that the link outlives the task.
3394
+ *
3395
+ * Identity fields are only ever widened, never blanked. A later capture-count
3396
+ * update carries no session id, and `COALESCE(excluded.…, browser_sessions.…)`
3397
+ * keeps the one recorded at start rather than overwriting it with NULL --
3398
+ * otherwise the second write would undo exactly what the first was for.
3399
+ */
3400
+ export function recordBrowserSession(record) {
3401
+ const db = getDB();
3402
+ const now = Date.now();
3403
+ db.prepare(`
3404
+ INSERT INTO browser_sessions (
3405
+ task, profile, session_id, launch_id, actor, machine,
3406
+ started_at, last_activity,
3407
+ screenshot_count, pdf_count, recording_count, download_count,
3408
+ capture_dir, captures_remote
3409
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3410
+ ON CONFLICT(profile, task) DO UPDATE SET
3411
+ session_id = COALESCE(excluded.session_id, browser_sessions.session_id),
3412
+ launch_id = COALESCE(excluded.launch_id, browser_sessions.launch_id),
3413
+ actor = COALESCE(excluded.actor, browser_sessions.actor),
3414
+ last_activity = excluded.last_activity,
3415
+ screenshot_count = excluded.screenshot_count,
3416
+ pdf_count = excluded.pdf_count,
3417
+ recording_count = excluded.recording_count,
3418
+ download_count = excluded.download_count,
3419
+ capture_dir = COALESCE(excluded.capture_dir, browser_sessions.capture_dir),
3420
+ captures_remote = COALESCE(excluded.captures_remote, browser_sessions.captures_remote)
3421
+ `).run(record.task, record.profile, record.sessionId ?? null, record.launchId ?? null, record.actor ?? null, record.machine ?? machineId(), record.startedAt ?? now, record.lastActivity ?? now, record.counts?.screenshot ?? 0, record.counts?.pdf ?? 0, record.counts?.recording ?? 0, record.counts?.download ?? 0, record.captureDir ?? null, record.capturesRemote ?? null);
3422
+ }
3423
+ /**
3424
+ * Upsert one computer-use invocation's durable metadata.
3425
+ *
3426
+ * `action_count` accumulates: a `computer run` loop emits many actions under one
3427
+ * invocation id, and each arrives as its own call, so the count is incremented
3428
+ * rather than replaced. Identity is widened-only, for the same reason as above.
3429
+ */
3430
+ export function recordComputerSession(record) {
3431
+ const db = getDB();
3432
+ const now = Date.now();
3433
+ db.prepare(`
3434
+ INSERT INTO computer_sessions (
3435
+ invocation_id, session_id, launch_id, actor, machine,
3436
+ started_at, last_activity, action_count, task_preview
3437
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3438
+ ON CONFLICT(invocation_id) DO UPDATE SET
3439
+ session_id = COALESCE(excluded.session_id, computer_sessions.session_id),
3440
+ launch_id = COALESCE(excluded.launch_id, computer_sessions.launch_id),
3441
+ actor = COALESCE(excluded.actor, computer_sessions.actor),
3442
+ last_activity = excluded.last_activity,
3443
+ action_count = computer_sessions.action_count + excluded.action_count,
3444
+ task_preview = COALESCE(excluded.task_preview, computer_sessions.task_preview)
3445
+ `).run(record.invocationId, record.sessionId ?? null, record.launchId ?? null, record.actor ?? null, record.machine ?? machineId(), record.startedAt ?? now, record.lastActivity ?? now, record.actionCount ?? 1, record.taskPreview ?? null);
3446
+ }
3447
+ function toStoredBrowserSession(row) {
3448
+ return {
3449
+ task: row.task,
3450
+ profile: row.profile,
3451
+ sessionId: row.session_id ?? undefined,
3452
+ launchId: row.launch_id ?? undefined,
3453
+ actor: row.actor ?? undefined,
3454
+ machine: row.machine,
3455
+ startedAt: row.started_at,
3456
+ lastActivity: row.last_activity ?? undefined,
3457
+ counts: {
3458
+ screenshot: row.screenshot_count,
3459
+ pdf: row.pdf_count,
3460
+ recording: row.recording_count,
3461
+ download: row.download_count,
3462
+ },
3463
+ captureDir: row.capture_dir ?? undefined,
3464
+ capturesRemote: row.captures_remote ?? undefined,
3465
+ };
3466
+ }
3467
+ /**
3468
+ * Stored browser tasks, newest first; optionally scoped to one profile.
3469
+ *
3470
+ * A PROFILE-SCOPED read is deliberately unbounded. These rows are the identity
3471
+ * for capture dirs that still exist on disk, and dropping the oldest ones would
3472
+ * silently regress exactly those tasks to `unlinked` — the symptom this whole
3473
+ * change removes, reappearing at a higher threshold. The row count is already
3474
+ * bounded by the tasks that profile has ever run, and each row is a short
3475
+ * metadata record. Only the unscoped read (every profile, used for overviews)
3476
+ * takes a ceiling, since that one has no natural bound.
3477
+ */
3478
+ export function listBrowserSessionRecords(profile, opts = {}) {
3479
+ const db = getDB();
3480
+ const rows = (profile
3481
+ ? db.prepare(`SELECT * FROM browser_sessions WHERE profile = ? ORDER BY started_at DESC`).all(profile)
3482
+ : db.prepare(`SELECT * FROM browser_sessions ORDER BY started_at DESC LIMIT ?`)
3483
+ .all(opts.limit ?? TOOL_SESSION_LIST_LIMIT));
3484
+ return rows.map(toStoredBrowserSession);
3485
+ }
3486
+ /** One stored browser task by its (profile, task) key, or null. */
3487
+ export function getBrowserSessionRecord(profile, task) {
3488
+ const db = getDB();
3489
+ const row = db
3490
+ .prepare(`SELECT * FROM browser_sessions WHERE profile = ? AND task = ?`)
3491
+ .get(profile, task);
3492
+ return row ? toStoredBrowserSession(row) : null;
3493
+ }
3494
+ /**
3495
+ * Retention for the tool-session tables.
3496
+ *
3497
+ * These are the durable answer to a ledger that prunes at 7 days, so they are
3498
+ * deliberately long-lived — but "durable" is not "unbounded". `computer_sessions`
3499
+ * takes one row per `agents computer` CLI PROCESS (`COMPUTER_INVOCATION_ID` is
3500
+ * minted per process), and an agent driving a desktop invokes that hundreds of
3501
+ * times a day, so an unbounded table would grow without limit and be read in
3502
+ * full on every listing.
3503
+ */
3504
+ export const TOOL_SESSION_MAX_AGE_DAYS = 365;
3505
+ /** Default ceiling on rows one listing will read. */
3506
+ export const TOOL_SESSION_LIST_LIMIT = 2000;
3507
+ /**
3508
+ * Drop tool-session rows past {@link TOOL_SESSION_MAX_AGE_DAYS}.
3509
+ *
3510
+ * Called from the listing path, never from the write hot path: an
3511
+ * `agents computer` action must not pay for a table sweep (see the fail-soft
3512
+ * note on `recordComputerSession`'s caller). Returns rows deleted.
3513
+ */
3514
+ export function pruneToolSessions(maxAgeDays = TOOL_SESSION_MAX_AGE_DAYS) {
3515
+ const db = getDB();
3516
+ const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
3517
+ // Cheap indexed guard first, so the common case is one lookup rather than a
3518
+ // write transaction (the same shape as the account repair below). Every
3519
+ // listing calls this, and on any table younger than the retention window
3520
+ // there is nothing to delete — but a bare DELETE still opens a write
3521
+ // transaction, which under `busy_timeout = 30000` can make a read-only
3522
+ // listing wait on the session indexer for no reason at all.
3523
+ const stale = db.prepare(`
3524
+ SELECT 1 FROM computer_sessions WHERE started_at < ?
3525
+ UNION ALL
3526
+ SELECT 1 FROM browser_sessions WHERE started_at < ?
3527
+ LIMIT 1
3528
+ `).get(cutoff, cutoff);
3529
+ if (!stale)
3530
+ return 0;
3531
+ const computer = db.prepare(`DELETE FROM computer_sessions WHERE started_at < ?`).run(cutoff);
3532
+ const browser = db.prepare(`DELETE FROM browser_sessions WHERE started_at < ?`).run(cutoff);
3533
+ return Number(computer.changes ?? 0) + Number(browser.changes ?? 0);
3534
+ }
3535
+ /**
3536
+ * Stored computer-use invocations, newest first, bounded.
3537
+ *
3538
+ * The limit is a real ceiling, not a nicety: `--json` serializes whatever this
3539
+ * returns, so an unbounded read would dump the entire table on every call.
3540
+ */
3541
+ export function listComputerSessionRecords(opts = {}) {
3542
+ const db = getDB();
3543
+ const limit = opts.limit ?? TOOL_SESSION_LIST_LIMIT;
3544
+ // `startedBeforeMs` selects the COMPLEMENT of the caller's other source
3545
+ // rather than the newest N. The caller that recovers pruned runs already
3546
+ // holds every recent invocation from the event ledger and discards any row
3547
+ // it has seen — so a bare newest-N read hands it 2000 rows it is guaranteed
3548
+ // to throw away, and returns nothing at all once the table passes that size.
3549
+ // Bounding by "older than the ledger reaches" truncates the TAIL of
3550
+ // recoverable history instead of deleting all of it.
3551
+ const rows = (opts.startedBeforeMs === undefined
3552
+ ? db
3553
+ .prepare(`SELECT * FROM computer_sessions ORDER BY started_at DESC LIMIT ?`)
3554
+ .all(limit)
3555
+ : db
3556
+ .prepare(`SELECT * FROM computer_sessions WHERE started_at < ? ORDER BY started_at DESC LIMIT ?`)
3557
+ .all(opts.startedBeforeMs, limit));
3558
+ return rows.map((row) => ({
3559
+ invocationId: row.invocation_id,
3560
+ sessionId: row.session_id ?? undefined,
3561
+ launchId: row.launch_id ?? undefined,
3562
+ actor: row.actor ?? undefined,
3563
+ machine: row.machine,
3564
+ startedAt: row.started_at,
3565
+ lastActivity: row.last_activity ?? undefined,
3566
+ actionCount: row.action_count,
3567
+ taskPreview: row.task_preview ?? undefined,
3568
+ }));
3569
+ }
@@ -36,7 +36,7 @@ export { machineForSessionFile } from './origin-machine.js';
36
36
  import { mapBounded } from '../concurrency.js';
37
37
  import { getDB, getScanStampByPath, getScanStampsForPaths, getParserStatesForPaths, getDirLedgerForPaths, recordDirScans, recordScans, syncLabels, seedLabelsFromNames, syncTopics, upsertSessionsBatch, querySessions, countSessions, ftsSearch, tryClaimScan, releaseScan, cacheLinearProject, } from './db.js';
38
38
  import { buildRunNameMap } from './run-names.js';
39
- import { resolveLinearApiKey } from '../auto-dispatch-linear.js';
39
+ import { resolveLinearApiKey } from '../linear-cache.js';
40
40
  import { ToolCallCollector, collectClaudeToolCalls, collectCodexToolCalls, } from './tool-calls.js';
41
41
  import { purgeMissingToolCallsInDirectory } from './tool-store.js';
42
42
  const HOME = os.homedir();
@@ -187,8 +187,19 @@ export async function queryIndexedSessions(options, indexedOptions = {}) {
187
187
  const sessions = querySessions(buildQueryOptions({ ...options, skipExistenceCheck: indexedOptions.skipExistenceCheck ?? true }, agents, { includeLimit: true }));
188
188
  if (indexedOptions.resolveLinear !== false)
189
189
  await resolveLinearProjects(sessions);
190
- for (const s of sessions)
191
- s.machine = machineForSessionFile(s.filePath, s.agent);
190
+ for (const s of sessions) {
191
+ // A non-empty transcript path is authoritative for origin (a live-home file
192
+ // is this box; `backups/<agent>/<machine>/…` names that peer), so derive
193
+ // there. An EMPTY file_path carries no such signal — `machineForSessionFile`
194
+ // falls back to THIS box — so keep the machine the row was recorded with
195
+ // instead: a host dispatch stamps the EXECUTION host on its empty-file index
196
+ // row (`registerHostSession`), and re-deriving re-attributed the dispatcher's
197
+ // own pool row to itself, splitting it from the executing peer's fan-out row
198
+ // and making resume-by-id read as "ambiguous (2 sessions)" (RUSH-2486 /
199
+ // RUSH-2479 criterion 2). A recorded-less empty-file row still falls back.
200
+ if (s.filePath || !s.machine?.trim())
201
+ s.machine = machineForSessionFile(s.filePath, s.agent);
202
+ }
192
203
  return scopeToManaged(sessions, agents, options);
193
204
  }
194
205
  const linearProjectCache = new Map();
@@ -760,7 +771,7 @@ export function getAgentSessionDirs(agent, subdir) {
760
771
  * The (agent, subdir) pairs `discoverSessions` walks for JSONL transcripts —
761
772
  * the single source of truth for which directories hold live session files.
762
773
  * `getSessionRoots` expands each pair to its concrete directories so a consumer
763
- * (the Factory extension's fs.watch, see issue #741) can configure its watcher
774
+ * (AGI EXT's fs.watch, see issue #741) can configure its watcher
764
775
  * from the CLI instead of hardcoding `~/.claude|.codex|.gemini`. Adding a new
765
776
  * on-disk agent here makes every consumer watch it automatically.
766
777
  */
@@ -3568,6 +3579,18 @@ export function applyCodexLine(state, parsed) {
3568
3579
  state.model = payload.model || state.model;
3569
3580
  return;
3570
3581
  }
3582
+ // Codex rollouts put per-turn metadata (including the model) on
3583
+ // `turn_context` events. Use them as a fallback when session_meta itself
3584
+ // does not carry the field, otherwise `agents sessions` shows blank model
3585
+ // info for Codex.
3586
+ if (parsed.type === 'turn_context') {
3587
+ const payload = parsed.payload || {};
3588
+ if (!state.model && typeof payload.model === 'string')
3589
+ state.model = payload.model;
3590
+ if (!state.cwd && typeof payload.cwd === 'string')
3591
+ state.cwd = payload.cwd;
3592
+ return;
3593
+ }
3571
3594
  if (parsed.type === 'response_item' && parsed.payload?.type === 'message') {
3572
3595
  const role = parsed.payload.role === 'user' || parsed.payload.role === 'developer'
3573
3596
  ? 'user'
@@ -17,8 +17,8 @@
17
17
  * This module lets `sessions --active` reconcile a `ps`-discovered pid to that
18
18
  * authoritative id. It is deliberately a small hand-rolled reader — the CLI does
19
19
  * NOT import the session-tracker package (a separate, workspace-less package;
20
- * see packages/session-tracker/CLAUDE.md) — mirroring the Factory extension's own
21
- * hand-copied reader in apps/factory/src/core/liveSession.ts. It only READS the
20
+ * see packages/session-tracker/CLAUDE.md) — mirroring AGI EXT's own
21
+ * hand-copied reader in apps/ext/src/core/liveSession.ts. It only READS the
22
22
  * existing dir/schema; it never writes or moves anything, so it is safe across a
23
23
  * fleet where old hooks and a new CLI coexist.
24
24
  *
@@ -33,8 +33,8 @@ export type HostLink =
33
33
  | 'host-gone';
34
34
  /**
35
35
  * How long an IDE window's registry slice may go without a refresh before we
36
- * treat that window as gone. The Factory extension force-republishes its slice
37
- * every 4 minutes (`KEEPALIVE_FORCE_MS` in `apps/factory/src/vscode/foreman.registry.ts`)
36
+ * treat that window as gone. AGI EXT force-republishes its slice
37
+ * every 4 minutes (`KEEPALIVE_FORCE_MS` in `apps/ext/src/vscode/foreman.registry.ts`)
38
38
  * and on every terminal open/close, so a slice this old means the window is no
39
39
  * longer running — it is not merely quiet. Deliberately the same 10 minutes the
40
40
  * extension itself uses to garbage-collect a peer window's slice, so the CLI and