@phnx-labs/agents-cli 1.22.46 → 1.22.49

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 (348) hide show
  1. package/CHANGELOG.md +663 -0
  2. package/LICENSE +102 -182
  3. package/README.md +96 -49
  4. package/dist/bootstrap.js +4 -2
  5. package/dist/cli/command-registry.d.ts +2 -1
  6. package/dist/cli/command-registry.js +6 -2
  7. package/dist/commands/accounts.d.ts +58 -0
  8. package/dist/commands/accounts.js +243 -25
  9. package/dist/commands/artifacts.js +7 -6
  10. package/dist/commands/attach.js +1 -1
  11. package/dist/commands/auth.js +7 -5
  12. package/dist/commands/browser-sessions-picker.js +9 -7
  13. package/dist/commands/browser.js +711 -62
  14. package/dist/commands/config.d.ts +0 -1
  15. package/dist/commands/config.js +26 -24
  16. package/dist/commands/cost.d.ts +1 -1
  17. package/dist/commands/daemon.d.ts +21 -0
  18. package/dist/commands/daemon.js +147 -12
  19. package/dist/commands/detach.d.ts +13 -0
  20. package/dist/commands/detach.js +11 -8
  21. package/dist/commands/exec.js +107 -53
  22. package/dist/commands/feed-watch.d.ts +2 -0
  23. package/dist/commands/feed-watch.js +28 -0
  24. package/dist/commands/feed.js +27 -0
  25. package/dist/commands/feedback.js +3 -19
  26. package/dist/commands/focus.d.ts +14 -0
  27. package/dist/commands/focus.js +48 -5
  28. package/dist/commands/go.d.ts +3 -0
  29. package/dist/commands/go.js +16 -3
  30. package/dist/commands/insights.d.ts +1 -1
  31. package/dist/commands/insights.js +2 -2
  32. package/dist/commands/inspect.js +2 -2
  33. package/dist/commands/lease.d.ts +1 -1
  34. package/dist/commands/lease.js +7 -6
  35. package/dist/commands/mailboxes.js +4 -3
  36. package/dist/commands/menubar.js +11 -4
  37. package/dist/commands/projects.d.ts +16 -1
  38. package/dist/commands/projects.js +58 -64
  39. package/dist/commands/resume.js +1 -1
  40. package/dist/commands/route.js +4 -4
  41. package/dist/commands/routines.js +4 -3
  42. package/dist/commands/run-broadcast.d.ts +1 -1
  43. package/dist/commands/run-broadcast.js +1 -1
  44. package/dist/commands/sessions-picker-factory.d.ts +2 -1
  45. package/dist/commands/sessions-picker-factory.js +1 -1
  46. package/dist/commands/sessions-picker.d.ts +22 -0
  47. package/dist/commands/sessions-picker.js +48 -9
  48. package/dist/commands/sessions-stop.d.ts +24 -0
  49. package/dist/commands/sessions-stop.js +66 -0
  50. package/dist/commands/sessions-trace.d.ts +82 -0
  51. package/dist/commands/sessions-trace.js +387 -0
  52. package/dist/commands/sessions.js +5 -0
  53. package/dist/commands/setup-secrets.js +23 -0
  54. package/dist/commands/share.d.ts +20 -5
  55. package/dist/commands/share.js +152 -52
  56. package/dist/commands/ssh.d.ts +15 -0
  57. package/dist/commands/ssh.js +243 -18
  58. package/dist/commands/teams.js +34 -2
  59. package/dist/commands/traces.d.ts +2 -0
  60. package/dist/commands/traces.js +184 -0
  61. package/dist/commands/view.d.ts +2 -0
  62. package/dist/commands/view.js +32 -62
  63. package/dist/commands/watchdog.js +9 -14
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.js +5 -0
  66. package/dist/lib/account-capabilities.d.ts +4 -0
  67. package/dist/lib/account-capabilities.js +13 -9
  68. package/dist/lib/account-registry.d.ts +2 -0
  69. package/dist/lib/account-registry.js +30 -2
  70. package/dist/lib/accounting/rotate.d.ts +9 -0
  71. package/dist/lib/accounting/rotate.js +12 -2
  72. package/dist/lib/accounting/usage.d.ts +84 -6
  73. package/dist/lib/accounting/usage.js +293 -75
  74. package/dist/lib/agent-modes.js +29 -1
  75. package/dist/lib/agent-spec/agents.d.ts +4 -3
  76. package/dist/lib/agent-spec/agents.js +25 -15
  77. package/dist/lib/analytics/mix-commands.d.ts +1 -1
  78. package/dist/lib/analytics/mix-commands.js +2 -2
  79. package/dist/lib/auth-health.d.ts +13 -3
  80. package/dist/lib/auth-health.js +50 -8
  81. package/dist/lib/browser/devices.d.ts +19 -0
  82. package/dist/lib/browser/devices.js +23 -0
  83. package/dist/lib/browser/drivers/ssh.d.ts +16 -2
  84. package/dist/lib/browser/drivers/ssh.js +36 -9
  85. package/dist/lib/browser/ipc.d.ts +1 -1
  86. package/dist/lib/browser/ipc.js +40 -9
  87. package/dist/lib/browser/profiles.d.ts +104 -65
  88. package/dist/lib/browser/profiles.js +277 -165
  89. package/dist/lib/browser/registry.d.ts +39 -0
  90. package/dist/lib/browser/registry.js +149 -0
  91. package/dist/lib/browser/remote-control.d.ts +28 -2
  92. package/dist/lib/browser/remote-control.js +35 -3
  93. package/dist/lib/browser/resolve-target.d.ts +68 -0
  94. package/dist/lib/browser/resolve-target.js +250 -0
  95. package/dist/lib/browser/runtime-state.d.ts +44 -11
  96. package/dist/lib/browser/runtime-state.js +98 -15
  97. package/dist/lib/browser/service.d.ts +65 -9
  98. package/dist/lib/browser/service.js +196 -61
  99. package/dist/lib/browser/sessions-list.d.ts +1 -3
  100. package/dist/lib/browser/sessions-list.js +3 -16
  101. package/dist/lib/browser/task-index.d.ts +70 -0
  102. package/dist/lib/browser/task-index.js +180 -0
  103. package/dist/lib/browser/types.d.ts +12 -1
  104. package/dist/lib/byok-usage.js +2 -2
  105. package/dist/lib/claude-statusline.d.ts +29 -0
  106. package/dist/lib/claude-statusline.js +139 -0
  107. package/dist/lib/cloud/host.d.ts +1 -1
  108. package/dist/lib/cloud/host.js +1 -1
  109. package/dist/lib/cloud/rush.js +1 -1
  110. package/dist/lib/codex-policy.d.ts +11 -1
  111. package/dist/lib/codex-policy.js +20 -5
  112. package/dist/lib/computer/computer-rpc.js +2 -2
  113. package/dist/lib/computer/download.d.ts +15 -15
  114. package/dist/lib/computer/download.js +35 -98
  115. package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
  116. package/dist/lib/computer/ssh-tunnel.js +12 -22
  117. package/dist/lib/config-keys.d.ts +6 -11
  118. package/dist/lib/config-keys.js +18 -19
  119. package/dist/lib/crabbox/cli.d.ts +1 -1
  120. package/dist/lib/crabbox/cli.js +2 -2
  121. package/dist/lib/crabbox/lease.d.ts +9 -0
  122. package/dist/lib/crabbox/lease.js +70 -15
  123. package/dist/lib/crabbox/runtimes.js +1 -1
  124. package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
  125. package/dist/lib/daemon/account-state-daemon-service.js +26 -0
  126. package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
  127. package/dist/lib/daemon/browser-ipc-service.js +46 -0
  128. package/dist/lib/daemon/daemon.d.ts +3 -0
  129. package/dist/lib/daemon/daemon.js +212 -444
  130. package/dist/lib/daemon/device-probe-service.d.ts +26 -0
  131. package/dist/lib/daemon/device-probe-service.js +47 -0
  132. package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
  133. package/dist/lib/daemon/keychain-reap-service.js +32 -0
  134. package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
  135. package/dist/lib/daemon/monitor-engine-service.js +29 -0
  136. package/dist/lib/daemon/runner.js +15 -2
  137. package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
  138. package/dist/lib/daemon/secrets-broker-service.js +66 -0
  139. package/dist/lib/daemon/self-heal-service.d.ts +27 -0
  140. package/dist/lib/daemon/self-heal-service.js +48 -0
  141. package/dist/lib/daemon/service.d.ts +102 -0
  142. package/dist/lib/daemon/service.js +98 -0
  143. package/dist/lib/daemon/session-index-service.d.ts +22 -0
  144. package/dist/lib/daemon/session-index-service.js +40 -0
  145. package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
  146. package/dist/lib/daemon/state-dir-check-service.js +63 -0
  147. package/dist/lib/daemon/supervisor.d.ts +87 -0
  148. package/dist/lib/daemon/supervisor.js +298 -0
  149. package/dist/lib/daemon/watchdog-service.d.ts +19 -0
  150. package/dist/lib/daemon/watchdog-service.js +40 -0
  151. package/dist/lib/daemon-health.d.ts +15 -0
  152. package/dist/lib/daemon-health.js +29 -5
  153. package/dist/lib/daemon-services.d.ts +8 -1
  154. package/dist/lib/daemon-services.js +55 -0
  155. package/dist/lib/daemon-ticks.d.ts +13 -2
  156. package/dist/lib/daemon-ticks.js +22 -20
  157. package/dist/lib/device-config.d.ts +25 -10
  158. package/dist/lib/device-config.js +90 -39
  159. package/dist/lib/devices/config-migration.d.ts +7 -0
  160. package/dist/lib/devices/config-migration.js +67 -3
  161. package/dist/lib/devices/health.d.ts +27 -2
  162. package/dist/lib/devices/health.js +39 -9
  163. package/dist/lib/devices/interactive-host.d.ts +15 -0
  164. package/dist/lib/devices/interactive-host.js +53 -0
  165. package/dist/lib/devices/registry.d.ts +61 -4
  166. package/dist/lib/devices/registry.js +94 -38
  167. package/dist/lib/devices/stats-cache.d.ts +26 -0
  168. package/dist/lib/devices/stats-cache.js +59 -7
  169. package/dist/lib/devices/worker-pick.d.ts +48 -0
  170. package/dist/lib/devices/worker-pick.js +113 -0
  171. package/dist/lib/exec.d.ts +70 -14
  172. package/dist/lib/exec.js +110 -38
  173. package/dist/lib/feed/answer.d.ts +29 -0
  174. package/dist/lib/feed/answer.js +145 -0
  175. package/dist/lib/feed/attention.d.ts +83 -0
  176. package/dist/lib/feed/attention.js +0 -0
  177. package/dist/lib/feed/feed.d.ts +109 -0
  178. package/dist/lib/feed/feed.js +154 -3
  179. package/dist/lib/feed/pr-status.d.ts +14 -0
  180. package/dist/lib/feed/pr-status.js +47 -0
  181. package/dist/lib/feed/watch.d.ts +59 -0
  182. package/dist/lib/feed/watch.js +180 -0
  183. package/dist/lib/fleet/capture.js +6 -0
  184. package/dist/lib/fleet/remote-login.js +8 -3
  185. package/dist/lib/fleet/types.d.ts +17 -0
  186. package/dist/lib/harness/adapter.d.ts +11 -0
  187. package/dist/lib/harness/adapters/claude.js +38 -24
  188. package/dist/lib/harness/adapters/codex.js +2 -4
  189. package/dist/lib/harness/adapters/cursor.js +8 -11
  190. package/dist/lib/helper-download.d.ts +108 -0
  191. package/dist/lib/helper-download.js +194 -0
  192. package/dist/lib/helper-versions.d.ts +52 -0
  193. package/dist/lib/helper-versions.js +58 -0
  194. package/dist/lib/hooks/cache.js +19 -0
  195. package/dist/lib/hooks/install.js +2 -2
  196. package/dist/lib/hooks/match.d.ts +3 -0
  197. package/dist/lib/hooks/match.js +25 -0
  198. package/dist/lib/hosts/credentials.d.ts +1 -1
  199. package/dist/lib/hosts/credentials.js +1 -1
  200. package/dist/lib/hosts/dispatch.d.ts +7 -1
  201. package/dist/lib/hosts/dispatch.js +24 -5
  202. package/dist/lib/hosts/passthrough.d.ts +8 -15
  203. package/dist/lib/hosts/passthrough.js +35 -16
  204. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  205. package/dist/lib/hosts/providers/devices.js +1 -1
  206. package/dist/lib/hosts/reconnect.d.ts +179 -21
  207. package/dist/lib/hosts/reconnect.js +266 -45
  208. package/dist/lib/hosts/registry.js +7 -0
  209. package/dist/lib/installations/migrate.d.ts +4 -5
  210. package/dist/lib/installations/migrate.js +6 -7
  211. package/dist/lib/installations/shims.d.ts +1 -1
  212. package/dist/lib/installations/shims.js +23 -13
  213. package/dist/lib/installations/versions.js +25 -2
  214. package/dist/lib/linear-autoclose.d.ts +1 -1
  215. package/dist/lib/linear-autoclose.js +1 -1
  216. package/dist/lib/loop.d.ts +2 -2
  217. package/dist/lib/loop.js +1 -1
  218. package/dist/lib/menubar/download-menubar.d.ts +47 -0
  219. package/dist/lib/menubar/download-menubar.js +60 -0
  220. package/dist/lib/menubar/install-menubar.d.ts +44 -7
  221. package/dist/lib/menubar/install-menubar.js +105 -23
  222. package/dist/lib/menubar/notify-desktop.d.ts +3 -3
  223. package/dist/lib/menubar/notify-desktop.js +6 -6
  224. package/dist/lib/model-tiers.d.ts +1 -1
  225. package/dist/lib/open-url.d.ts +60 -2
  226. package/dist/lib/open-url.js +167 -12
  227. package/dist/lib/project-probe.d.ts +1 -1
  228. package/dist/lib/project-probe.js +1 -1
  229. package/dist/lib/project-pull.d.ts +1 -1
  230. package/dist/lib/project-pull.js +2 -2
  231. package/dist/lib/projects.d.ts +1 -1
  232. package/dist/lib/projects.js +1 -1
  233. package/dist/lib/refresh-coordinator.d.ts +1 -1
  234. package/dist/lib/refresh-coordinator.js +1 -1
  235. package/dist/lib/sandbox.js +6 -7
  236. package/dist/lib/secrets/download-keychain.d.ts +47 -0
  237. package/dist/lib/secrets/download-keychain.js +70 -0
  238. package/dist/lib/secrets/install-helper.d.ts +20 -1
  239. package/dist/lib/secrets/install-helper.js +44 -3
  240. package/dist/lib/session/active.d.ts +67 -0
  241. package/dist/lib/session/active.js +86 -2
  242. package/dist/lib/session/db.d.ts +11 -1
  243. package/dist/lib/session/db.js +142 -8
  244. package/dist/lib/session/discover.d.ts +23 -1
  245. package/dist/lib/session/discover.js +44 -10
  246. package/dist/lib/session/highlights.d.ts +49 -1
  247. package/dist/lib/session/highlights.js +95 -0
  248. package/dist/lib/session/host-link.d.ts +16 -2
  249. package/dist/lib/session/host-link.js +7 -1
  250. package/dist/lib/session/parse.js +33 -1
  251. package/dist/lib/session/prompt.d.ts +35 -0
  252. package/dist/lib/session/prompt.js +56 -0
  253. package/dist/lib/session/remote/remote-list.d.ts +9 -0
  254. package/dist/lib/session/remote/remote-list.js +19 -2
  255. package/dist/lib/session/remote/watch.d.ts +1 -1
  256. package/dist/lib/session/remote/watch.js +17 -3
  257. package/dist/lib/session/render.js +7 -3
  258. package/dist/lib/session/session-cache.d.ts +53 -0
  259. package/dist/lib/session/session-cache.js +93 -1
  260. package/dist/lib/session/state.js +1 -1
  261. package/dist/lib/session/tool-calls.d.ts +2 -0
  262. package/dist/lib/session/tool-calls.js +1 -1
  263. package/dist/lib/session/trajectory-compare.d.ts +47 -0
  264. package/dist/lib/session/trajectory-compare.js +116 -0
  265. package/dist/lib/session/trajectory-html.d.ts +19 -0
  266. package/dist/lib/session/trajectory-html.js +770 -0
  267. package/dist/lib/session/trajectory-lineage.d.ts +92 -0
  268. package/dist/lib/session/trajectory-lineage.js +195 -0
  269. package/dist/lib/session/trajectory-text.d.ts +37 -0
  270. package/dist/lib/session/trajectory-text.js +284 -0
  271. package/dist/lib/session/trajectory.d.ts +102 -0
  272. package/dist/lib/session/trajectory.js +384 -0
  273. package/dist/lib/session/types.d.ts +15 -0
  274. package/dist/lib/sha256-asset.d.ts +26 -0
  275. package/dist/lib/sha256-asset.js +41 -0
  276. package/dist/lib/share/backend.d.ts +95 -0
  277. package/dist/lib/share/backend.js +139 -0
  278. package/dist/lib/share/delete.d.ts +6 -2
  279. package/dist/lib/share/delete.js +8 -9
  280. package/dist/lib/share/html.d.ts +22 -0
  281. package/dist/lib/share/html.js +88 -0
  282. package/dist/lib/share/provision.d.ts +18 -4
  283. package/dist/lib/share/provision.js +38 -4
  284. package/dist/lib/share/publish.d.ts +39 -15
  285. package/dist/lib/share/publish.js +55 -26
  286. package/dist/lib/share/worker-template.js +171 -11
  287. package/dist/lib/sqlite.js +16 -3
  288. package/dist/lib/ssh-exec.d.ts +65 -0
  289. package/dist/lib/ssh-exec.js +120 -4
  290. package/dist/lib/startup/command-registry.d.ts +3 -1
  291. package/dist/lib/startup/command-registry.js +6 -3
  292. package/dist/lib/startup/dev-build.js +3 -3
  293. package/dist/lib/state.d.ts +2 -2
  294. package/dist/lib/state.js +11 -5
  295. package/dist/lib/subagents-registry.js +1 -1
  296. package/dist/lib/teams/agents.js +9 -0
  297. package/dist/lib/terminal/index.d.ts +1 -1
  298. package/dist/lib/terminal/inject.d.ts +19 -0
  299. package/dist/lib/terminal/inject.js +19 -9
  300. package/dist/lib/terminal/types.d.ts +1 -1
  301. package/dist/lib/terminal/types.js +1 -1
  302. package/dist/lib/tmux/orphan-reap.js +1 -1
  303. package/dist/lib/tmux/session.d.ts +46 -13
  304. package/dist/lib/tmux/session.js +204 -27
  305. package/dist/lib/traces/backend.d.ts +29 -0
  306. package/dist/lib/traces/backend.js +46 -0
  307. package/dist/lib/traces/classify.d.ts +52 -0
  308. package/dist/lib/traces/classify.js +72 -0
  309. package/dist/lib/traces/config.d.ts +10 -0
  310. package/dist/lib/traces/config.js +2 -0
  311. package/dist/lib/traces/provision.d.ts +19 -0
  312. package/dist/lib/traces/provision.js +30 -0
  313. package/dist/lib/traces/sync.d.ts +150 -0
  314. package/dist/lib/traces/sync.js +447 -0
  315. package/dist/lib/traces/worker-template.d.ts +4 -0
  316. package/dist/lib/traces/worker-template.js +159 -0
  317. package/dist/lib/types.d.ts +34 -19
  318. package/dist/lib/types.js +14 -0
  319. package/dist/lib/usage-backoff.d.ts +2 -1
  320. package/dist/lib/usage-backoff.js +60 -19
  321. package/dist/lib/usage-refresh.d.ts +18 -4
  322. package/dist/lib/usage-refresh.js +68 -19
  323. package/dist/lib/watchdog/log.d.ts +1 -1
  324. package/dist/lib/watchdog/log.js +1 -1
  325. package/dist/lib/watchdog/runner.d.ts +36 -45
  326. package/dist/lib/watchdog/runner.js +150 -119
  327. package/dist/lib/watchdog/service.d.ts +0 -1
  328. package/dist/lib/watchdog/service.js +0 -1
  329. package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
  330. package/dist/lib/watchdog/watchdog-agent.js +59 -0
  331. package/dist/lib/watchdog/watchdog.d.ts +11 -2
  332. package/dist/lib/watchdog/watchdog.js +37 -104
  333. package/dist/lib/workflows.d.ts +1 -1
  334. package/package.json +5 -7
  335. package/dist/commands/usage.d.ts +0 -27
  336. package/dist/commands/usage.js +0 -96
  337. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  338. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
  339. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  340. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  341. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
  342. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  343. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -24
  344. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  345. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -123
  346. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  347. package/dist/lib/usage-fleet.d.ts +0 -32
  348. package/dist/lib/usage-fleet.js +0 -125
@@ -9,7 +9,7 @@
9
9
  import chalk from 'chalk';
10
10
  import { truncate } from '../format.js';
11
11
  import { summarizeToolUse } from './parse.js';
12
- import { cleanSessionPrompt, extractSessionTopic } from './prompt.js';
12
+ import { cleanSessionPrompt, classifyUserPrompt, extractSessionTopic } from './prompt.js';
13
13
  import { renderMarkdown } from '../markdown.js';
14
14
  import { redactSecrets } from '../redact.js';
15
15
  import { classifyFileChanges, changeCounts, toolHistogram, detectTestResult } from './digest.js';
@@ -635,8 +635,12 @@ export function renderSummary(events, cwd) {
635
635
  // 1. Prompt
636
636
  if (firstUserMessage) {
637
637
  const cleaned = cleanSessionPrompt(firstUserMessage);
638
- if (cleaned) {
639
- lines.push(chalk.bold('Prompt:') + ' ' + cleaned.split('\n')[0]);
638
+ // Show the classified prompt (a screenshot path folds to `[image]`, a
639
+ // pasted `$ cmd` to the command, a skill install path to `/<name>`) so the
640
+ // recap's first line is intent, never path noise (RUSH-3011).
641
+ const { clean } = classifyUserPrompt(firstUserMessage, { hasImageAttachment: attachments.length > 0 });
642
+ if (clean || cleaned) {
643
+ lines.push(chalk.bold('Prompt:') + ' ' + (clean || cleaned.split('\n')[0]));
640
644
  const secondLine = cleaned.split('\n')[1]?.trim();
641
645
  if (secondLine)
642
646
  lines.push(' ' + secondLine);
@@ -59,6 +59,16 @@ export declare const IMMUTABLE_FIELD_KEYS: readonly ["topic", "label", "name", "
59
59
  * only cache that may carry them — and only as a whole-row snapshot.
60
60
  */
61
61
  export declare const LIVE_STATUS_KEYS: readonly ["status", "activity", "preview", "tokPerSec", "awaitingReason", "question", "todos", "tail", "lastActivityMs", "hostLink", "presence", "pidAlive", "tmuxClients", "windowHeartbeatMs", "provenance", "rateLimited", "plan"];
62
+ /**
63
+ * How long after the last reader heartbeat the daemon considers the journal
64
+ * to have no active consumer and skips the expensive `ps`+`lsof` gather.
65
+ *
66
+ * Must be significantly larger than {@link SESSION_WATCH_HEARTBEAT_MS} (15s)
67
+ * so a single delayed heartbeat does not cause a false-idle. Three tick-lengths
68
+ * gives adequate margin while still cutting load within ~45s of the last
69
+ * watcher disconnecting.
70
+ */
71
+ export declare const ACTIVE_SESSIONS_READER_IDLE_WINDOW_MS = 45000;
62
72
  /** Test seam: redirect the snapshot file. Returns the previous override. */
63
73
  export declare function setActiveSessionsSnapshotPathForTest(p: string | null): string | null;
64
74
  /** Test seam: isolate process-local entries between fixtures. */
@@ -67,7 +77,50 @@ export declare function clearActiveSnapshotMemoryForTest(): void;
67
77
  export declare function setImmutableMemoPathForTest(p: string | null): string | null;
68
78
  /** Test seam: redirect the append-only live-state journal. */
69
79
  export declare function setActiveSessionsJournalPathForTest(p: string | null): string | null;
80
+ /** Test seam: redirect the reader-presence file. Returns the previous override. */
81
+ export declare function setActiveSessionsReaderPresencePathForTest(p: string | null): string | null;
70
82
  export declare function activeSessionsJournalPath(): string;
83
+ /**
84
+ * Record that a journal consumer is active right now.
85
+ *
86
+ * Called by {@link watchLocalSessions} on startup and on each heartbeat so
87
+ * the daemon warm tick can gate the expensive `ps`+`lsof` gather on reader
88
+ * presence and skip it when no watcher has checked in recently.
89
+ */
90
+ export declare function noteActiveSessionsJournalReader(nowMs?: number): void;
91
+ /**
92
+ * True when a journal consumer has signalled its presence within
93
+ * {@link ACTIVE_SESSIONS_READER_IDLE_WINDOW_MS}.
94
+ *
95
+ * Used by the daemon warm tick to decide whether to run the expensive
96
+ * `ps`+`lsof` gather. On an idle box with no watcher the tick skips the
97
+ * gather entirely; the moment a new watcher connects it calls
98
+ * {@link noteActiveSessionsJournalReader} and the following tick gathers.
99
+ */
100
+ export declare function isActiveSessionsJournalReaderRecent(nowMs?: number, idleWindowMs?: number): boolean;
101
+ /** Default poll cadence for {@link watchActiveSessionsReaderPresence}. */
102
+ export declare const ACTIVE_SESSIONS_READER_TRANSITION_POLL_MS = 1000;
103
+ /**
104
+ * Watch for a reader going from absent/idle to present and fire `onConnect`
105
+ * out of band, instead of waiting for the next scheduled warm tick (RUSH-2484).
106
+ *
107
+ * {@link noteActiveSessionsJournalReader} only writes a timestamp — it has no
108
+ * path back to the daemon process that owns the warm tick's `setInterval`. A
109
+ * watcher connecting to a cold or long-idle daemon therefore used to wait up
110
+ * to one full {@link ACTIVE_SESSIONS_WARM_TICK_MS} tick for its first fresh
111
+ * rows. This polls the tiny presence file (not the expensive `ps`+`lsof`
112
+ * gather) at {@link ACTIVE_SESSIONS_READER_TRANSITION_POLL_MS} and calls
113
+ * `onConnect` only on the idle→recent edge, so a steadily-connected reader's
114
+ * repeated heartbeats never re-trigger it and an idle box with no reader never
115
+ * gathers — the CPU win from RUSH-3193 is preserved.
116
+ *
117
+ * Returns a disposer that stops the poll.
118
+ */
119
+ export declare function watchActiveSessionsReaderPresence(onConnect: () => void, opts?: {
120
+ pollMs?: number;
121
+ idleWindowMs?: number;
122
+ nowMs?: () => number;
123
+ }): () => void;
71
124
  /** Read one scope from the snapshot file (best-effort; missing/corrupt → null). */
72
125
  export declare function readActiveSessionsCache(scope: ActiveCacheScope): ActiveSessionsSnapshot | null;
73
126
  /**
@@ -27,7 +27,7 @@ import * as fs from 'fs';
27
27
  import * as path from 'path';
28
28
  import { createMemoryCache } from '../memory-cache.js';
29
29
  import { getCacheDir } from '../state.js';
30
- import { sessionProcessIsLocal } from './active.js';
30
+ import { backfillActiveRowsFromIndex, sessionProcessIsLocal } from './active.js';
31
31
  /** Snapshot file under `getCacheDir()` (regenerable, gitignored). */
32
32
  const SNAPSHOT_FILE = '.active-sessions.json';
33
33
  /** Immutable-field memo file (keyed by sessionId + transcript mtime). */
@@ -94,10 +94,21 @@ export const LIVE_STATUS_KEYS = [
94
94
  'rateLimited',
95
95
  'plan',
96
96
  ];
97
+ /**
98
+ * How long after the last reader heartbeat the daemon considers the journal
99
+ * to have no active consumer and skips the expensive `ps`+`lsof` gather.
100
+ *
101
+ * Must be significantly larger than {@link SESSION_WATCH_HEARTBEAT_MS} (15s)
102
+ * so a single delayed heartbeat does not cause a false-idle. Three tick-lengths
103
+ * gives adequate margin while still cutting load within ~45s of the last
104
+ * watcher disconnecting.
105
+ */
106
+ export const ACTIVE_SESSIONS_READER_IDLE_WINDOW_MS = 45_000;
97
107
  // ── path overrides (test seam) ─────────────────────────────────────────────
98
108
  let snapshotPathOverride = null;
99
109
  let immutablePathOverride = null;
100
110
  let journalPathOverride = null;
111
+ let readerPresencePathOverride = null;
101
112
  /** Test seam: redirect the snapshot file. Returns the previous override. */
102
113
  export function setActiveSessionsSnapshotPathForTest(p) {
103
114
  const prev = snapshotPathOverride;
@@ -121,6 +132,12 @@ export function setActiveSessionsJournalPathForTest(p) {
121
132
  journalPathOverride = p;
122
133
  return prev;
123
134
  }
135
+ /** Test seam: redirect the reader-presence file. Returns the previous override. */
136
+ export function setActiveSessionsReaderPresencePathForTest(p) {
137
+ const prev = readerPresencePathOverride;
138
+ readerPresencePathOverride = p;
139
+ return prev;
140
+ }
124
141
  function snapshotPath() {
125
142
  return snapshotPathOverride ?? path.join(getCacheDir(), SNAPSHOT_FILE);
126
143
  }
@@ -131,6 +148,77 @@ export function activeSessionsJournalPath() {
131
148
  return journalPathOverride
132
149
  ?? (snapshotPathOverride ? `${snapshotPathOverride}.journal.jsonl` : path.join(getCacheDir(), JOURNAL_FILE));
133
150
  }
151
+ const READER_PRESENCE_FILE = '.active-sessions-reader.presence';
152
+ function readerPresencePath() {
153
+ return readerPresencePathOverride ?? path.join(getCacheDir(), READER_PRESENCE_FILE);
154
+ }
155
+ /**
156
+ * Record that a journal consumer is active right now.
157
+ *
158
+ * Called by {@link watchLocalSessions} on startup and on each heartbeat so
159
+ * the daemon warm tick can gate the expensive `ps`+`lsof` gather on reader
160
+ * presence and skip it when no watcher has checked in recently.
161
+ */
162
+ export function noteActiveSessionsJournalReader(nowMs = Date.now()) {
163
+ try {
164
+ const p = readerPresencePath();
165
+ fs.mkdirSync(path.dirname(p), { recursive: true });
166
+ fs.writeFileSync(p, String(nowMs));
167
+ }
168
+ catch { /* best-effort: a failed write does not block the caller */ }
169
+ }
170
+ /**
171
+ * True when a journal consumer has signalled its presence within
172
+ * {@link ACTIVE_SESSIONS_READER_IDLE_WINDOW_MS}.
173
+ *
174
+ * Used by the daemon warm tick to decide whether to run the expensive
175
+ * `ps`+`lsof` gather. On an idle box with no watcher the tick skips the
176
+ * gather entirely; the moment a new watcher connects it calls
177
+ * {@link noteActiveSessionsJournalReader} and the following tick gathers.
178
+ */
179
+ export function isActiveSessionsJournalReaderRecent(nowMs = Date.now(), idleWindowMs = ACTIVE_SESSIONS_READER_IDLE_WINDOW_MS) {
180
+ try {
181
+ const content = fs.readFileSync(readerPresencePath(), 'utf8').trim();
182
+ const ts = Number(content);
183
+ if (!Number.isFinite(ts))
184
+ return false;
185
+ return nowMs - ts < idleWindowMs;
186
+ }
187
+ catch {
188
+ return false;
189
+ }
190
+ }
191
+ /** Default poll cadence for {@link watchActiveSessionsReaderPresence}. */
192
+ export const ACTIVE_SESSIONS_READER_TRANSITION_POLL_MS = 1_000;
193
+ /**
194
+ * Watch for a reader going from absent/idle to present and fire `onConnect`
195
+ * out of band, instead of waiting for the next scheduled warm tick (RUSH-2484).
196
+ *
197
+ * {@link noteActiveSessionsJournalReader} only writes a timestamp — it has no
198
+ * path back to the daemon process that owns the warm tick's `setInterval`. A
199
+ * watcher connecting to a cold or long-idle daemon therefore used to wait up
200
+ * to one full {@link ACTIVE_SESSIONS_WARM_TICK_MS} tick for its first fresh
201
+ * rows. This polls the tiny presence file (not the expensive `ps`+`lsof`
202
+ * gather) at {@link ACTIVE_SESSIONS_READER_TRANSITION_POLL_MS} and calls
203
+ * `onConnect` only on the idle→recent edge, so a steadily-connected reader's
204
+ * repeated heartbeats never re-trigger it and an idle box with no reader never
205
+ * gathers — the CPU win from RUSH-3193 is preserved.
206
+ *
207
+ * Returns a disposer that stops the poll.
208
+ */
209
+ export function watchActiveSessionsReaderPresence(onConnect, opts = {}) {
210
+ const pollMs = opts.pollMs ?? ACTIVE_SESSIONS_READER_TRANSITION_POLL_MS;
211
+ const now = opts.nowMs ?? Date.now;
212
+ let lastRecent = isActiveSessionsJournalReaderRecent(now(), opts.idleWindowMs);
213
+ const timer = setInterval(() => {
214
+ const recent = isActiveSessionsJournalReaderRecent(now(), opts.idleWindowMs);
215
+ if (recent && !lastRecent)
216
+ onConnect();
217
+ lastRecent = recent;
218
+ }, pollMs);
219
+ timer.unref?.();
220
+ return () => clearInterval(timer);
221
+ }
134
222
  // ── snapshot read / write ──────────────────────────────────────────────────
135
223
  /** Read one scope from the snapshot file (best-effort; missing/corrupt → null). */
136
224
  export function readActiveSessionsCache(scope) {
@@ -376,6 +464,10 @@ export async function loadLocalActiveSessions(opts = {}) {
376
464
  return getActiveSessions({ localOnly: true });
377
465
  });
378
466
  const sessions = await gather();
467
+ // The index owns transcript-derived labels (Claude custom-title / ai-title).
468
+ // Enrich before publishing so the canonical watch snapshot carries the same
469
+ // label as one-shot `sessions --active` output.
470
+ backfillActiveRowsFromIndex(sessions);
379
471
  for (const s of sessions)
380
472
  applyImmutableMemo(s);
381
473
  updateImmutableMemos(sessions, now);
@@ -174,7 +174,7 @@ export function extractRecentDirectoriesTouched(events, cwd) {
174
174
  if (['Edit', 'Write', 'edit_file', 'write_file', 'create_file', 'edit', 'write'].includes(tool)) {
175
175
  add(args.file_path ?? args.filePath ?? args.path ?? event.path, true);
176
176
  }
177
- else if (['Bash', 'exec_command', 'run_shell_command', 'shell', 'Execute'].includes(tool)) {
177
+ else if (['Bash', 'exec_command', 'exec', 'run_shell_command', 'shell', 'Execute'].includes(tool)) {
178
178
  add(args.cwd ?? args.Cwd ?? args.workdir ?? args.working_directory ?? cwd);
179
179
  }
180
180
  }
@@ -34,6 +34,8 @@ export interface ToolCallCollectorSnapshot {
34
34
  pending: IndexedToolCall[];
35
35
  }
36
36
  export declare function sanitizeToolEvidenceText(value: string, maxBytes: number, knownValues?: readonly string[]): string;
37
+ /** Extract literal shell commands from the Codex orchestration tool without evaluating transcript code. */
38
+ export declare function commandsFromCodexExec(source: string): string[];
37
39
  export declare function structuredToolResult(value: unknown): {
38
40
  text: string;
39
41
  outcome: ToolCallOutcome;
@@ -181,7 +181,7 @@ function canonicalInput(tool, args, command) {
181
181
  }
182
182
  }
183
183
  /** Extract literal shell commands from the Codex orchestration tool without evaluating transcript code. */
184
- function commandsFromCodexExec(source) {
184
+ export function commandsFromCodexExec(source) {
185
185
  const commands = [];
186
186
  let root;
187
187
  try {
@@ -0,0 +1,47 @@
1
+ import type { SessionMeta } from './types.js';
2
+ import type { SessionTrajectory, TrajectoryStep } from './trajectory.js';
3
+ /** Where two sessions' tool sequences first stop lining up. */
4
+ export interface TrajectoryDivergence {
5
+ /** Ordinal of the last step both sessions still agree on (0 if none). */
6
+ afterOrdinalA: number;
7
+ afterOrdinalB: number;
8
+ /** ms into each session's own timeline where the divergence begins. */
9
+ startMsA: number;
10
+ startMsB: number;
11
+ /** One-line description of what differs — the next tool step on each side. */
12
+ detail: string;
13
+ }
14
+ /** Aggregate numbers for one side of a compare — the summary table row. */
15
+ export interface TrajectorySummary {
16
+ session: SessionMeta;
17
+ toolCount: number;
18
+ errorCount: number;
19
+ spanMs: number;
20
+ outputTokens: number;
21
+ }
22
+ /** The result of diffing two trajectories. */
23
+ export interface TrajectoryComparison {
24
+ a: SessionTrajectory;
25
+ b: SessionTrajectory;
26
+ /** Undefined only when the tool sequences are identical (or one/both are empty and equal). */
27
+ divergence?: TrajectoryDivergence;
28
+ /** Tool steps present in `b` with no counterpart in `a`, in `b`'s order. */
29
+ added: TrajectoryStep[];
30
+ /** Tool steps present in `a` with no counterpart in `b`, in `a`'s order. */
31
+ removed: TrajectoryStep[];
32
+ summaryA: TrajectorySummary;
33
+ summaryB: TrajectorySummary;
34
+ /** Tool steps dropped from the diff computation by the `maxDiffSteps` cap, per side. */
35
+ truncatedA: number;
36
+ truncatedB: number;
37
+ }
38
+ export interface DiffTrajectoriesOptions {
39
+ /** Cap on tool steps considered per side (the LCS table is O(n*m)). Default 1000. */
40
+ maxDiffSteps?: number;
41
+ }
42
+ /**
43
+ * Diff two trajectories: align their tool-step sequences, find the first
44
+ * divergence, and collect the steps each session ran that the other never
45
+ * did. Never mutates either input.
46
+ */
47
+ export declare function diffTrajectories(a: SessionTrajectory, b: SessionTrajectory, options?: DiffTrajectoriesOptions): TrajectoryComparison;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Diff two {@link SessionTrajectory} models for `agents sessions trace <a> <b>` —
3
+ * the second selector turns the single-session trajectory into a **compare**.
4
+ *
5
+ * Aligns the two sessions' TOOL steps (thinking steps carry no comparable
6
+ * identity) with the `diff` package's `diffArrays` (already a dependency,
7
+ * `lib/diff-text.ts`), comparator-keyed on tool name, in order — cheap,
8
+ * deterministic, and matches how a human reads "where did these two runs
9
+ * diverge": the first point their tool sequence stops lining up.
10
+ *
11
+ * Pure and framework-free, exactly like `trajectory.ts`: takes two already-built
12
+ * `SessionTrajectory` models (never re-parses a transcript) and returns a plain
13
+ * diff object the HTML/text/JSON renderers project.
14
+ */
15
+ import { diffArrays } from 'diff';
16
+ const DEFAULT_MAX_DIFF_STEPS = 1000;
17
+ /**
18
+ * Turn `diffArrays`' run-length change objects into a per-step edit script.
19
+ *
20
+ * `diffArrays(a, b, { comparator })` (Myers' diff under the hood) returns
21
+ * consecutive-run chunks, not individual elements, and — per its own docs —
22
+ * an unchanged chunk's `value` is drawn from `b`, not `a`. Neither shape is
23
+ * what a divergence marker needs (a stepA/stepB PAIR per matched step, with
24
+ * both sides' own ordinals/timestamps), so this walks two cursors over the
25
+ * original arrays in lockstep with the run lengths `diffArrays` reports,
26
+ * recovering the exact `a[i]`/`b[j]` pairing for every `same` step.
27
+ */
28
+ function computeEditScript(a, b) {
29
+ const changes = diffArrays(a, b, { comparator: (x, y) => x.tool === y.tool });
30
+ const ops = [];
31
+ let i = 0;
32
+ let j = 0;
33
+ for (const change of changes) {
34
+ const count = change.count ?? change.value.length;
35
+ if (change.removed) {
36
+ for (let k = 0; k < count; k++)
37
+ ops.push({ type: 'remove', stepA: a[i++] });
38
+ }
39
+ else if (change.added) {
40
+ for (let k = 0; k < count; k++)
41
+ ops.push({ type: 'add', stepB: b[j++] });
42
+ }
43
+ else {
44
+ for (let k = 0; k < count; k++)
45
+ ops.push({ type: 'same', stepA: a[i++], stepB: b[j++] });
46
+ }
47
+ }
48
+ return ops;
49
+ }
50
+ function describeDivergence(op) {
51
+ if (op.type === 'remove')
52
+ return `${op.stepA.tool ?? op.stepA.kind} — only in the first session`;
53
+ if (op.type === 'add')
54
+ return `${op.stepB.tool ?? op.stepB.kind} — only in the second session`;
55
+ return '';
56
+ }
57
+ function summarize(t) {
58
+ return {
59
+ session: t.session,
60
+ toolCount: t.stats.toolCount,
61
+ errorCount: t.errorCount,
62
+ spanMs: t.spanMs,
63
+ outputTokens: t.stats.outputTokens,
64
+ };
65
+ }
66
+ /**
67
+ * Diff two trajectories: align their tool-step sequences, find the first
68
+ * divergence, and collect the steps each session ran that the other never
69
+ * did. Never mutates either input.
70
+ */
71
+ export function diffTrajectories(a, b, options = {}) {
72
+ const maxDiffSteps = options.maxDiffSteps ?? DEFAULT_MAX_DIFF_STEPS;
73
+ const allToolStepsA = a.steps.filter((s) => s.kind === 'tool');
74
+ const allToolStepsB = b.steps.filter((s) => s.kind === 'tool');
75
+ const toolStepsA = allToolStepsA.slice(0, maxDiffSteps);
76
+ const toolStepsB = allToolStepsB.slice(0, maxDiffSteps);
77
+ const truncatedA = allToolStepsA.length - toolStepsA.length;
78
+ const truncatedB = allToolStepsB.length - toolStepsB.length;
79
+ const ops = computeEditScript(toolStepsA, toolStepsB);
80
+ const added = [];
81
+ const removed = [];
82
+ let divergence;
83
+ let lastSameA;
84
+ let lastSameB;
85
+ for (const op of ops) {
86
+ if (op.type === 'same') {
87
+ lastSameA = op.stepA;
88
+ lastSameB = op.stepB;
89
+ continue;
90
+ }
91
+ if (op.type === 'remove')
92
+ removed.push(op.stepA);
93
+ else
94
+ added.push(op.stepB);
95
+ if (!divergence) {
96
+ divergence = {
97
+ afterOrdinalA: lastSameA?.ordinal ?? 0,
98
+ afterOrdinalB: lastSameB?.ordinal ?? 0,
99
+ startMsA: op.type === 'remove' ? op.stepA.startMs : (lastSameA?.startMs ?? 0),
100
+ startMsB: op.type === 'add' ? op.stepB.startMs : (lastSameB?.startMs ?? 0),
101
+ detail: describeDivergence(op),
102
+ };
103
+ }
104
+ }
105
+ return {
106
+ a,
107
+ b,
108
+ divergence,
109
+ added,
110
+ removed,
111
+ summaryA: summarize(a),
112
+ summaryB: summarize(b),
113
+ truncatedA,
114
+ truncatedB,
115
+ };
116
+ }
@@ -0,0 +1,19 @@
1
+ import type { SessionTrajectory } from './trajectory.js';
2
+ import type { TrajectoryComparison } from './trajectory-compare.js';
3
+ import type { SessionLineage } from './trajectory-lineage.js';
4
+ /** Render one session's trajectory as a self-contained HTML page. */
5
+ export declare function renderTrajectoryHtml(model: SessionTrajectory): string;
6
+ /** Render a two-session {@link TrajectoryComparison} as a self-contained HTML page. */
7
+ export declare function renderTrajectoryCompareHtml(cmp: TrajectoryComparison): string;
8
+ /**
9
+ * Render a {@link SessionLineage} as ONE self-contained HTML page: the inline-SVG
10
+ * delegation graph (orchestrator on top, the sessions it spawned below, edges
11
+ * auto-discovered from the team records) plus a per-node summary card revealed
12
+ * by clicking a node. Same shell, redaction, and no-CDN rule as the other two
13
+ * layouts; local paths (`filePath`/`cwd`) are deliberately never rendered.
14
+ *
15
+ * `redacted` follows the trace's `--no-redact` flag so the footer states the
16
+ * honest label, exactly as the single-trajectory and compare renderers do; it
17
+ * defaults to the redacted claim to keep the shared/safe default.
18
+ */
19
+ export declare function renderLineageHtml(lineage: SessionLineage, redacted?: boolean): string;