@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
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Incremental sync of derived, redacted SessionTrajectory blobs to the
3
+ * agents-traces R2 store.
4
+ *
5
+ * Security invariant: only the derived signal (steps + gaps + stats +
6
+ * errorCount + programTimeShare) is uploaded — no raw transcript text.
7
+ * redactSecrets() is called inside buildTrajectory() before we PUT.
8
+ *
9
+ * Sync gate: `sessions.db` `file_mtime_ms` is the source of truth. A ledger
10
+ * at `getRuntimeStateDir()/traces-sync.json` records the last sync timestamp
11
+ * per device so re-runs skip unchanged sessions.
12
+ */
13
+ import { type SessionTrajectory } from '../session/trajectory.js';
14
+ import { type BucketStats, type DriftSignal, type TraceFailureCause, type TraceTopicGroup } from './classify.js';
15
+ export interface SyncOpts {
16
+ /** Limit to N sessions (for testing / --dry-run); no limit when undefined. */
17
+ limit?: number;
18
+ /** When true, skip uploading the per-device index shard. */
19
+ skipIndex?: boolean;
20
+ /**
21
+ * Compute the derived shards from this device's real sessions.db and WRITE
22
+ * them to `outDir` instead of uploading. No Phoenix auth, no worker, no
23
+ * network — a local export for verifying the real signal. Ignores the
24
+ * incremental watermark so the export reflects every session.
25
+ */
26
+ dryRun?: boolean;
27
+ /** Directory to write `index.json` + `sessions/<id>.json` when `dryRun` is set. */
28
+ outDir?: string;
29
+ }
30
+ export interface SyncResult {
31
+ uploaded: number;
32
+ skipped: number;
33
+ errors: number;
34
+ }
35
+ /** Push derived, redacted trajectories for this device to the traces store. */
36
+ export declare function syncTraces(opts?: SyncOpts): Promise<SyncResult>;
37
+ export interface TracesIndexShard {
38
+ schema: 1;
39
+ device: string;
40
+ syncedAt: number;
41
+ owner: string;
42
+ stats: {
43
+ sessionsImported: number;
44
+ medianMs: number;
45
+ p90Ms: number;
46
+ needAttention: number;
47
+ toolErrorRate: number;
48
+ };
49
+ needsAttention: IndexedSession[];
50
+ topics: Array<{
51
+ key: string;
52
+ label: string;
53
+ count: number;
54
+ group: TraceTopicGroup;
55
+ }>;
56
+ failures: {
57
+ byToolError: Array<{
58
+ tool: string;
59
+ desc: string;
60
+ cause: TraceFailureCause;
61
+ count: number;
62
+ }>;
63
+ byCause: Record<TraceFailureCause, number>;
64
+ };
65
+ /** Rolling 14-day window of per-bucket daily stats. Each inner array is one day. */
66
+ bucketHistory: BucketStats[][];
67
+ /** Movement signals for buckets with ≥3 days of history. */
68
+ driftSignals: DriftSignal[];
69
+ }
70
+ export interface IndexedSession {
71
+ id: string;
72
+ title: string;
73
+ repo: string;
74
+ device: string;
75
+ agent: string;
76
+ model: string;
77
+ severity: number;
78
+ flags: string[];
79
+ }
80
+ /** Build the redacted rich console shard from indexed metadata and derived caches. */
81
+ export declare function buildIndexShard(rows: SyncRow[], device: string, owner: string, prevShard?: TracesIndexShard | null): TracesIndexShard;
82
+ /** Per-session drill-down detail the console consumes (sessions/<id>.json). */
83
+ export interface SessionDetail {
84
+ schema: 1;
85
+ id: string;
86
+ meta: {
87
+ spanMs: number;
88
+ turns: number;
89
+ tools: number;
90
+ errorCount: number;
91
+ tokens: number;
92
+ costUsd: number;
93
+ outcome: string;
94
+ repo: string;
95
+ agent: string;
96
+ model: string;
97
+ };
98
+ steps: SessionTrajectory['steps'];
99
+ gaps: SessionTrajectory['gaps'];
100
+ /** Steps dropped from `steps` when a run was too long — surfaced so truncation is never silent. */
101
+ truncatedSteps: number;
102
+ whereItWentWrong: string | null;
103
+ }
104
+ /**
105
+ * Map the derived trajectory to the console's SessionDetail shape, stripping
106
+ * local-machine PII (full cwd, account) that would expose filesystem paths if
107
+ * written to R2. `repo` is the cwd basename only.
108
+ */
109
+ export declare function buildSessionDetail(traj: SessionTrajectory): SessionDetail;
110
+ interface SyncLedger {
111
+ lastSyncMtime?: number;
112
+ }
113
+ export declare function readSyncLedger(): SyncLedger;
114
+ export declare function writeSyncLedger(ledger: SyncLedger): void;
115
+ export interface SyncRow {
116
+ id: string;
117
+ short_id: string;
118
+ agent: string;
119
+ origin: string | null;
120
+ routine_name: string | null;
121
+ routine_run_id: string | null;
122
+ version: string | null;
123
+ account: string | null;
124
+ account_key: string | null;
125
+ account_org: string | null;
126
+ mode: string | null;
127
+ timestamp: string;
128
+ last_activity: string | null;
129
+ project: string | null;
130
+ cwd: string | null;
131
+ git_branch: string | null;
132
+ topic: string | null;
133
+ label: string | null;
134
+ message_count: number | null;
135
+ token_count: number | null;
136
+ output_tokens: number | null;
137
+ input_tokens: number | null;
138
+ cache_read_tokens: number | null;
139
+ cache_write_tokens: number | null;
140
+ cost_usd: number | null;
141
+ cost_usd_nocache: number | null;
142
+ duration_ms: number | null;
143
+ model: string | null;
144
+ tool_call_count: number | null;
145
+ file_path: string;
146
+ file_mtime_ms: number | null;
147
+ file_size: number | null;
148
+ machine: string | null;
149
+ }
150
+ export {};
@@ -0,0 +1,447 @@
1
+ /**
2
+ * Incremental sync of derived, redacted SessionTrajectory blobs to the
3
+ * agents-traces R2 store.
4
+ *
5
+ * Security invariant: only the derived signal (steps + gaps + stats +
6
+ * errorCount + programTimeShare) is uploaded — no raw transcript text.
7
+ * redactSecrets() is called inside buildTrajectory() before we PUT.
8
+ *
9
+ * Sync gate: `sessions.db` `file_mtime_ms` is the source of truth. A ledger
10
+ * at `getRuntimeStateDir()/traces-sync.json` records the last sync timestamp
11
+ * per device so re-runs skip unchanged sessions.
12
+ */
13
+ import fs from 'node:fs';
14
+ import os from 'node:os';
15
+ import path from 'node:path';
16
+ import { getDB, readSessionInsights, readSessionTopics, writeSessionInsights, writeSessionTopics, } from '../session/db.js';
17
+ import { parseSession } from '../session/parse.js';
18
+ import { buildTrajectory } from '../session/trajectory.js';
19
+ import { computeInsightFacets } from '../session/insights.js';
20
+ import { knownSecretValuesFromEnv, redactSecrets } from '../redact.js';
21
+ import { getRuntimeStateDir } from '../state.js';
22
+ import { resolveTracesBackend } from './backend.js';
23
+ import { classifyCause, classifyTopic, computeDriftSignal, } from './classify.js';
24
+ /** Push derived, redacted trajectories for this device to the traces store. */
25
+ export async function syncTraces(opts = {}) {
26
+ const dryRun = opts.dryRun === true;
27
+ const outDir = opts.outDir;
28
+ if (dryRun && !outDir) {
29
+ throw new Error('traces sync --dry-run requires --out <dir>');
30
+ }
31
+ // A dry-run computes locally and writes to disk: no Phoenix backend needed.
32
+ const backend = dryRun ? null : resolveTracesBackend();
33
+ const owner = backend?.userId ?? 'local';
34
+ const ledger = readSyncLedger();
35
+ const db = getDB();
36
+ const device = localDevice();
37
+ // Scope to this machine only — the DB can contain peer rows mirrored from the
38
+ // fleet; uploading those under this device's prefix would corrupt the index.
39
+ // NULL machine rows are legacy local sessions (pre-machine-field). A dry-run
40
+ // ignores the incremental watermark so the export covers every session.
41
+ const sinceMtime = dryRun ? 0 : (ledger.lastSyncMtime ?? 0);
42
+ const rows = db
43
+ .prepare('SELECT * FROM sessions WHERE (machine = ? OR machine IS NULL) AND file_mtime_ms > ? ORDER BY file_mtime_ms ASC')
44
+ .all(device, sinceMtime);
45
+ const limited = opts.limit !== undefined ? rows.slice(0, opts.limit) : rows;
46
+ const knownSecrets = knownSecretValuesFromEnv();
47
+ let uploaded = 0;
48
+ let skipped = 0;
49
+ let errors = 0;
50
+ // Advance the watermark only to the max mtime of successfully uploaded sessions
51
+ // so failures are retried on the next run.
52
+ let maxSuccessMtime = ledger.lastSyncMtime ?? 0;
53
+ if (dryRun && outDir) {
54
+ fs.mkdirSync(path.join(outDir, 'sessions'), { recursive: true });
55
+ }
56
+ for (const row of limited) {
57
+ if (!row.file_path) {
58
+ skipped++;
59
+ maxSuccessMtime = Math.max(maxSuccessMtime, row.file_mtime_ms ?? 0);
60
+ continue;
61
+ }
62
+ let traj;
63
+ try {
64
+ const session = rowToMeta(row);
65
+ const events = parseSession(row.file_path, row.agent);
66
+ traj = buildTrajectory(events, session, { redact: true, knownSecrets });
67
+ }
68
+ catch {
69
+ errors++;
70
+ continue;
71
+ }
72
+ try {
73
+ if (dryRun && outDir) {
74
+ fs.writeFileSync(path.join(outDir, 'sessions', `${row.id}.json`), JSON.stringify(buildSessionDetail(traj)));
75
+ }
76
+ else {
77
+ await putSessionTrace(backend, device, row.id, traj);
78
+ }
79
+ uploaded++;
80
+ maxSuccessMtime = Math.max(maxSuccessMtime, row.file_mtime_ms ?? 0);
81
+ }
82
+ catch {
83
+ errors++;
84
+ }
85
+ }
86
+ if (!opts.skipIndex) {
87
+ try {
88
+ const allRows = db
89
+ .prepare('SELECT * FROM sessions WHERE machine = ? OR machine IS NULL')
90
+ .all(device);
91
+ // Seed rolling bucket history from the previous shard so drift signals accumulate.
92
+ let prevShard = null;
93
+ if (dryRun && outDir) {
94
+ const prevPath = path.join(outDir, 'index.json');
95
+ try {
96
+ prevShard = JSON.parse(fs.readFileSync(prevPath, 'utf8'));
97
+ }
98
+ catch { /* first run — no prior shard */ }
99
+ }
100
+ else if (backend) {
101
+ prevShard = await getIndexShard(backend, device);
102
+ }
103
+ const shard = buildIndexShard(allRows, device, owner, prevShard);
104
+ if (dryRun && outDir) {
105
+ fs.writeFileSync(path.join(outDir, 'index.json'), JSON.stringify(shard, null, 2));
106
+ }
107
+ else {
108
+ await putIndexShard(backend, device, owner, shard);
109
+ }
110
+ }
111
+ catch {
112
+ // index PUT/write failure is not fatal — the per-session data is already written
113
+ }
114
+ }
115
+ // A dry-run never advances the incremental watermark: it is a read-only export.
116
+ if (!dryRun) {
117
+ writeSyncLedger({ lastSyncMtime: maxSuccessMtime });
118
+ }
119
+ return { uploaded, skipped, errors };
120
+ }
121
+ function percentile(values, ratio) {
122
+ if (values.length === 0)
123
+ return 0;
124
+ const sorted = [...values].sort((a, b) => a - b);
125
+ return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * ratio) - 1)];
126
+ }
127
+ function failureDescription(call, cause) {
128
+ if (cause === 'guard')
129
+ return /main-branch-guard/i.test(`${call.error_code ?? ''} ${call.error ?? ''}`)
130
+ ? 'main branch guard' : 'git guard';
131
+ if (cause === 'hook')
132
+ return 'auto-mode hook denial';
133
+ if (call.status_code != null)
134
+ return `HTTP ${call.status_code}`;
135
+ if (call.exit_code != null)
136
+ return `exit ${call.exit_code}`;
137
+ if (call.error_code)
138
+ return 'tool error code';
139
+ if (call.parse_error)
140
+ return 'parse error';
141
+ return 'tool error';
142
+ }
143
+ function attentionFlags(errorCount, facets) {
144
+ const flags = [];
145
+ if (errorCount > 0)
146
+ flags.push(`${errorCount} error${errorCount === 1 ? '' : 's'}`);
147
+ const friction = facets?.frictionSignals ?? {};
148
+ const corrections = facets?.correctionSignals ?? {};
149
+ const retryCount = Object.entries(friction)
150
+ .filter(([key]) => key.startsWith('failed tool loop:'))
151
+ .reduce((sum, [, count]) => sum + count, 0);
152
+ if (retryCount > 0)
153
+ flags.push('retry loop');
154
+ const stall = Object.entries(friction).find(([key, count]) => key.startsWith('silent stall:') && count > 0);
155
+ if (stall)
156
+ flags.push(stall[0].replace('silent stall: ', 'stalled '));
157
+ const correctionCount = Object.values(corrections).reduce((sum, count) => sum + count, 0);
158
+ if (correctionCount > 0)
159
+ flags.push(`${correctionCount} correction${correctionCount === 1 ? '' : 's'}`);
160
+ return flags;
161
+ }
162
+ /** Build the redacted rich console shard from indexed metadata and derived caches. */
163
+ export function buildIndexShard(rows, device, owner, prevShard) {
164
+ const db = getDB();
165
+ const knownSecrets = knownSecretValuesFromEnv();
166
+ const ids = rows.map((row) => row.id);
167
+ const calls = [];
168
+ for (let i = 0; i < ids.length; i += 400) {
169
+ const chunk = ids.slice(i, i + 400);
170
+ calls.push(...db.prepare(`
171
+ SELECT session_id, tool, outcome, exit_code, status_code, error_code, error, parse_error
172
+ FROM tool_calls
173
+ WHERE session_id IN (${chunk.map(() => '?').join(',')})
174
+ `).all(...chunk));
175
+ }
176
+ const toolMix = new Map();
177
+ const errorCounts = new Map();
178
+ for (const call of calls) {
179
+ const mix = toolMix.get(call.session_id) ?? {};
180
+ mix[call.tool] = (mix[call.tool] ?? 0) + 1;
181
+ toolMix.set(call.session_id, mix);
182
+ if (call.outcome === 'error') {
183
+ errorCounts.set(call.session_id, (errorCounts.get(call.session_id) ?? 0) + 1);
184
+ }
185
+ }
186
+ const topics = readSessionTopics(ids);
187
+ const missingTopics = rows.filter((row) => !topics.has(row.id)).map((row) => {
188
+ const topic = classifyTopic({
189
+ cwd: row.cwd,
190
+ gitBranch: row.git_branch,
191
+ topic: row.topic,
192
+ label: row.label,
193
+ toolMix: toolMix.get(row.id),
194
+ });
195
+ topics.set(row.id, topic);
196
+ return { id: row.id, fileMtimeMs: row.file_mtime_ms, fileSize: row.file_size, topic };
197
+ });
198
+ writeSessionTopics(missingTopics);
199
+ const insights = readSessionInsights(ids);
200
+ const missingInsights = [];
201
+ for (const row of rows.filter((candidate) => !insights.has(candidate.id))) {
202
+ try {
203
+ const events = parseSession(row.file_path, row.agent);
204
+ const facets = computeInsightFacets(events);
205
+ insights.set(row.id, facets);
206
+ missingInsights.push({
207
+ id: row.id,
208
+ fileMtimeMs: row.file_mtime_ms,
209
+ fileSize: row.file_size,
210
+ facets,
211
+ });
212
+ }
213
+ catch {
214
+ continue;
215
+ }
216
+ }
217
+ writeSessionInsights(missingInsights);
218
+ const needsAttention = rows.flatMap((row) => {
219
+ const facets = insights.get(row.id);
220
+ const errorCount = errorCounts.get(row.id) ?? 0;
221
+ const flags = attentionFlags(errorCount, facets);
222
+ if (flags.length === 0)
223
+ return [];
224
+ const friction = Object.values(facets?.frictionSignals ?? {}).reduce((sum, count) => sum + count, 0);
225
+ const corrections = Object.values(facets?.correctionSignals ?? {}).reduce((sum, count) => sum + count, 0);
226
+ return [{
227
+ id: row.id,
228
+ title: redactSecrets(row.label ?? row.topic ?? topics.get(row.id)?.label ?? 'Untitled session', knownSecrets),
229
+ repo: row.project ?? (row.cwd ? path.basename(row.cwd) : 'unknown'),
230
+ device,
231
+ agent: row.agent,
232
+ model: row.model ?? 'unknown',
233
+ severity: errorCount * 2 + friction * 3 + corrections * 2,
234
+ flags,
235
+ }];
236
+ }).sort((a, b) => b.severity - a.severity || a.id.localeCompare(b.id));
237
+ const topicCounts = new Map();
238
+ for (const topic of topics.values()) {
239
+ const current = topicCounts.get(topic.key) ?? { ...topic, count: 0 };
240
+ current.count++;
241
+ topicCounts.set(topic.key, current);
242
+ }
243
+ const failedCalls = calls.filter((call) => call.outcome === 'error');
244
+ const byCause = { real: 0, guard: 0, hook: 0 };
245
+ const failureCounts = new Map();
246
+ for (const call of failedCalls) {
247
+ const cause = classifyCause(call);
248
+ const desc = failureDescription(call, cause);
249
+ byCause[cause]++;
250
+ const key = `${call.tool}\u0000${desc}\u0000${cause}`;
251
+ const current = failureCounts.get(key) ?? { tool: call.tool, desc, cause, count: 0 };
252
+ current.count++;
253
+ failureCounts.set(key, current);
254
+ }
255
+ const durations = rows.flatMap((row) => row.duration_ms == null ? [] : [row.duration_ms]);
256
+ // Build today's per-bucket stats for the rolling drift window.
257
+ const todayDate = new Date().toISOString().slice(0, 10);
258
+ const todayStats = [...topicCounts.values()].map(({ key }) => {
259
+ const sessionsInBucket = [...topics.entries()]
260
+ .filter(([, t]) => t.key === key)
261
+ .map(([id]) => id);
262
+ const bucketCalls = calls.filter((c) => sessionsInBucket.includes(c.session_id));
263
+ const bucketErrors = bucketCalls.filter((c) => c.outcome === 'error').length;
264
+ const errorRate = bucketCalls.length === 0 ? 0 : bucketErrors / bucketCalls.length;
265
+ const stallCount = sessionsInBucket.filter((id) => {
266
+ const facets = insights.get(id);
267
+ return Object.entries(facets?.frictionSignals ?? {})
268
+ .some(([k, v]) => k.startsWith('silent stall:') && v > 0);
269
+ }).length;
270
+ const stallRate = sessionsInBucket.length === 0 ? 0 : stallCount / sessionsInBucket.length;
271
+ return { key, date: todayDate, count: topicCounts.get(key).count, errorRate, stallRate };
272
+ });
273
+ const prevHistory = prevShard?.bucketHistory ?? [];
274
+ const bucketHistory = [...prevHistory, todayStats].slice(-14);
275
+ const driftSignals = computeDriftSignal(prevHistory, todayStats);
276
+ return {
277
+ schema: 1,
278
+ device,
279
+ syncedAt: Date.now(),
280
+ owner,
281
+ stats: {
282
+ sessionsImported: rows.length,
283
+ medianMs: percentile(durations, 0.5),
284
+ p90Ms: percentile(durations, 0.9),
285
+ needAttention: needsAttention.length,
286
+ toolErrorRate: calls.length === 0 ? 0 : failedCalls.length / calls.length,
287
+ },
288
+ needsAttention,
289
+ topics: [...topicCounts.values()].sort((a, b) => b.count - a.count || a.key.localeCompare(b.key)),
290
+ failures: {
291
+ byToolError: [...failureCounts.values()].sort((a, b) => b.count - a.count || a.tool.localeCompare(b.tool)),
292
+ byCause,
293
+ },
294
+ bucketHistory,
295
+ driftSignals,
296
+ };
297
+ }
298
+ /** Plain-language summary of the friction in a run, or null when it ran clean. */
299
+ function buildWhereItWentWrong(traj) {
300
+ const errorSteps = traj.steps.filter((s) => s.outcome === 'error');
301
+ const biggestGap = traj.gaps.reduce((max, g) => (!max || g.durationMs > max.durationMs ? g : max), null);
302
+ const parts = [];
303
+ if (errorSteps.length > 0) {
304
+ const first = errorSteps[0];
305
+ const who = first.tool ?? first.lane;
306
+ parts.push(`${errorSteps.length} tool error${errorSteps.length === 1 ? '' : 's'} (first: ${who} — ${first.label})`);
307
+ }
308
+ if (biggestGap && biggestGap.durationMs >= 60_000) {
309
+ parts.push(`stalled ${Math.round(biggestGap.durationMs / 60_000)}m`);
310
+ }
311
+ if (parts.length === 0)
312
+ return null;
313
+ return `This run hit ${parts.join('; ')}.`;
314
+ }
315
+ /**
316
+ * Map the derived trajectory to the console's SessionDetail shape, stripping
317
+ * local-machine PII (full cwd, account) that would expose filesystem paths if
318
+ * written to R2. `repo` is the cwd basename only.
319
+ */
320
+ export function buildSessionDetail(traj) {
321
+ const s = traj.session;
322
+ const stats = traj.stats;
323
+ const repo = s.project ?? (s.cwd ? path.basename(s.cwd) : 'unknown');
324
+ return {
325
+ schema: 1,
326
+ id: s.id,
327
+ meta: {
328
+ spanMs: traj.spanMs,
329
+ turns: (stats.userTurns ?? 0) + (stats.assistantTurns ?? 0),
330
+ tools: stats.toolCount ?? 0,
331
+ errorCount: traj.errorCount,
332
+ tokens: stats.outputTokens ?? 0,
333
+ costUsd: s.costUsd ?? 0,
334
+ outcome: traj.errorCount > 0 ? 'errored' : 'completed',
335
+ repo,
336
+ agent: s.agent,
337
+ model: s.model ?? 'unknown',
338
+ },
339
+ steps: traj.steps,
340
+ gaps: traj.gaps,
341
+ truncatedSteps: traj.truncatedSteps,
342
+ whereItWentWrong: buildWhereItWentWrong(traj),
343
+ };
344
+ }
345
+ async function getIndexShard(backend, device) {
346
+ const url = `${backend.baseUrl}/${backend.userId}/${device}/index.json`;
347
+ try {
348
+ const res = await fetch(url, {
349
+ headers: { authorization: `Bearer ${backend.token}` },
350
+ });
351
+ if (!res.ok)
352
+ return null;
353
+ return await res.json();
354
+ }
355
+ catch {
356
+ return null;
357
+ }
358
+ }
359
+ async function putSessionTrace(backend, device, sessionId, traj) {
360
+ const url = `${backend.baseUrl}/${backend.userId}/${device}/sessions/${sessionId}.json`;
361
+ const res = await fetch(url, {
362
+ method: 'PUT',
363
+ headers: {
364
+ authorization: `Bearer ${backend.token}`,
365
+ 'content-type': 'application/json; charset=utf-8',
366
+ },
367
+ body: JSON.stringify(buildSessionDetail(traj)),
368
+ });
369
+ if (!res.ok) {
370
+ throw new Error(`PUT ${url} → ${res.status}`);
371
+ }
372
+ }
373
+ async function putIndexShard(backend, device, owner, shard) {
374
+ const full = { ...shard, device, owner, syncedAt: Date.now() };
375
+ const url = `${backend.baseUrl}/${backend.userId}/${device}/index.json`;
376
+ const res = await fetch(url, {
377
+ method: 'PUT',
378
+ headers: {
379
+ authorization: `Bearer ${backend.token}`,
380
+ 'content-type': 'application/json; charset=utf-8',
381
+ },
382
+ body: JSON.stringify(full),
383
+ });
384
+ if (!res.ok) {
385
+ throw new Error(`PUT ${url} → ${res.status}`);
386
+ }
387
+ }
388
+ function ledgerPath() {
389
+ return path.join(getRuntimeStateDir(), 'traces-sync.json');
390
+ }
391
+ export function readSyncLedger() {
392
+ try {
393
+ const raw = fs.readFileSync(ledgerPath(), 'utf8');
394
+ return JSON.parse(raw);
395
+ }
396
+ catch {
397
+ return {};
398
+ }
399
+ }
400
+ export function writeSyncLedger(ledger) {
401
+ const p = ledgerPath();
402
+ fs.mkdirSync(path.dirname(p), { recursive: true });
403
+ fs.writeFileSync(p, JSON.stringify(ledger, null, 2) + '\n', 'utf8');
404
+ }
405
+ // ---------------------------------------------------------------------------
406
+ // Local device name
407
+ // ---------------------------------------------------------------------------
408
+ function localDevice() {
409
+ return (process.env['AGENTS_SYNC_MACHINE_ID'] ?? os.hostname()).toLowerCase().replace(/\.local$/, '');
410
+ }
411
+ /** Minimal subset of rowToMeta needed to satisfy buildTrajectory's SessionMeta param. */
412
+ function rowToMeta(row) {
413
+ const SESSION_RUN_MODES = ['plan', 'edit', 'auto', 'skip'];
414
+ return {
415
+ id: row.id,
416
+ shortId: row.short_id,
417
+ agent: row.agent,
418
+ origin: row.origin === 'routine' ? 'routine' : 'cli',
419
+ routineName: row.routine_name ?? undefined,
420
+ routineRunId: row.routine_run_id ?? undefined,
421
+ timestamp: row.timestamp,
422
+ lastActivity: row.last_activity ?? undefined,
423
+ project: row.project ?? undefined,
424
+ cwd: row.cwd ?? undefined,
425
+ filePath: row.file_path,
426
+ gitBranch: row.git_branch ?? undefined,
427
+ messageCount: row.message_count ?? undefined,
428
+ tokenCount: row.token_count ?? undefined,
429
+ outputTokens: row.output_tokens ?? undefined,
430
+ inputTokens: row.input_tokens ?? undefined,
431
+ cacheReadTokens: row.cache_read_tokens ?? undefined,
432
+ cacheWriteTokens: row.cache_write_tokens ?? undefined,
433
+ costUsd: row.cost_usd ?? undefined,
434
+ costUsdNoCache: row.cost_usd_nocache ?? undefined,
435
+ durationMs: row.duration_ms ?? undefined,
436
+ model: row.model ?? undefined,
437
+ toolCallCount: row.tool_call_count ?? undefined,
438
+ version: row.version ?? undefined,
439
+ account: row.account ?? undefined,
440
+ accountKey: row.account_key ?? undefined,
441
+ accountOrg: row.account_org ?? undefined,
442
+ mode: SESSION_RUN_MODES.includes(row.mode ?? '') ? row.mode : undefined,
443
+ topic: row.topic ?? undefined,
444
+ label: row.label ?? undefined,
445
+ machine: row.machine ?? undefined,
446
+ };
447
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Render the traces Worker source. Pure — bindings are wired at deploy time.
3
+ */
4
+ export declare function renderTracesWorkerScript(): string;