@vellumai/assistant 0.7.0 → 0.7.1

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 (666) hide show
  1. package/ARCHITECTURE.md +6 -7
  2. package/Dockerfile +1 -0
  3. package/README.md +2 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +79 -139
  5. package/bun.lock +3 -0
  6. package/docs/architecture/security.md +18 -16
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
  9. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -5
  10. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
  11. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
  12. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
  13. package/node_modules/@vellumai/slack-text/bun.lock +24 -0
  14. package/node_modules/@vellumai/slack-text/package.json +18 -0
  15. package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
  16. package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
  17. package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
  18. package/openapi.yaml +294 -107
  19. package/package.json +4 -2
  20. package/scripts/generate-openapi.ts +16 -111
  21. package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
  22. package/src/__tests__/anthropic-provider.test.ts +56 -13
  23. package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
  24. package/src/__tests__/app-conversation-ids.test.ts +151 -0
  25. package/src/__tests__/approval-cascade.test.ts +0 -15
  26. package/src/__tests__/approval-routes-http.test.ts +6 -17
  27. package/src/__tests__/assistant-event-hub.test.ts +126 -77
  28. package/src/__tests__/assistant-event.test.ts +0 -5
  29. package/src/__tests__/assistant-events-sse-hardening.test.ts +37 -15
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -29
  31. package/src/__tests__/background-shell-host-bash.test.ts +34 -43
  32. package/src/__tests__/call-controller.test.ts +1 -1
  33. package/src/__tests__/call-site-routing-provider.test.ts +193 -0
  34. package/src/__tests__/channel-approval-routes.test.ts +10 -296
  35. package/src/__tests__/channel-approvals.test.ts +25 -17
  36. package/src/__tests__/channel-guardian.test.ts +100 -146
  37. package/src/__tests__/checker.test.ts +20 -34
  38. package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
  39. package/src/__tests__/compaction-events.test.ts +2 -0
  40. package/src/__tests__/config-schema.test.ts +6 -48
  41. package/src/__tests__/config-watcher.test.ts +12 -0
  42. package/src/__tests__/connection-policy.test.ts +1 -52
  43. package/src/__tests__/contacts-write.test.ts +2 -64
  44. package/src/__tests__/context-image-dimensions.test.ts +1 -1
  45. package/src/__tests__/context-search-memory-source.test.ts +120 -1
  46. package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
  47. package/src/__tests__/context-search-pkb-source.test.ts +49 -0
  48. package/src/__tests__/context-search-workspace-source.test.ts +9 -22
  49. package/src/__tests__/context-window-manager.test.ts +46 -0
  50. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  51. package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
  52. package/src/__tests__/conversation-agent-loop.test.ts +980 -13
  53. package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
  54. package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
  55. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
  56. package/src/__tests__/conversation-history-web-search.test.ts +4 -3
  57. package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
  58. package/src/__tests__/conversation-lifecycle.test.ts +4 -4
  59. package/src/__tests__/conversation-process-callsite.test.ts +79 -2
  60. package/src/__tests__/conversation-queue.test.ts +3 -8
  61. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -161
  62. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -32
  63. package/src/__tests__/conversation-routes-slash-commands.test.ts +75 -66
  64. package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
  65. package/src/__tests__/conversation-slash-commands.test.ts +24 -4
  66. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  68. package/src/__tests__/conversation-starter-routes.test.ts +79 -2
  69. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
  70. package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
  71. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
  72. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
  73. package/src/__tests__/conversation-usage.test.ts +253 -3
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
  75. package/src/__tests__/credential-health-service.test.ts +68 -0
  76. package/src/__tests__/credential-security-e2e.test.ts +4 -3
  77. package/src/__tests__/credential-security-invariants.test.ts +1 -5
  78. package/src/__tests__/credential-token-resolver.test.ts +180 -0
  79. package/src/__tests__/cu-unified-flow.test.ts +33 -16
  80. package/src/__tests__/daemon-assistant-events.test.ts +34 -21
  81. package/src/__tests__/daemon-credential-client.test.ts +4 -1
  82. package/src/__tests__/db-connection-isolation.test.ts +125 -0
  83. package/src/__tests__/db-migration-rollback.test.ts +101 -0
  84. package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
  85. package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
  86. package/src/__tests__/document-conversations.test.ts +332 -0
  87. package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
  88. package/src/__tests__/emit-event-signal.test.ts +4 -6
  89. package/src/__tests__/events-client-registration.test.ts +193 -49
  90. package/src/__tests__/filing-service.test.ts +58 -7
  91. package/src/__tests__/first-greeting.test.ts +156 -150
  92. package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
  93. package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
  94. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  95. package/src/__tests__/guardian-dispatch.test.ts +1 -1
  96. package/src/__tests__/guardian-grant-minting.test.ts +7 -2
  97. package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
  98. package/src/__tests__/guardian-routing-state.test.ts +1 -1
  99. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +32 -11
  100. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -83
  101. package/src/__tests__/headless-browser-mode.test.ts +4 -9
  102. package/src/__tests__/headless-browser-navigate.test.ts +21 -20
  103. package/src/__tests__/heartbeat-service.test.ts +289 -7
  104. package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
  105. package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
  106. package/src/__tests__/host-bash-proxy.test.ts +46 -122
  107. package/src/__tests__/host-browser-e2e-cloud.test.ts +36 -497
  108. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +26 -96
  109. package/src/__tests__/host-browser-proxy.test.ts +111 -185
  110. package/src/__tests__/host-browser-routes.test.ts +45 -75
  111. package/src/__tests__/host-browser-ws-events-e2e.test.ts +26 -30
  112. package/src/__tests__/host-cu-proxy.test.ts +56 -111
  113. package/src/__tests__/host-file-proxy.test.ts +44 -98
  114. package/src/__tests__/host-file-read-tool.test.ts +42 -21
  115. package/src/__tests__/host-shell-tool.test.ts +33 -68
  116. package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
  117. package/src/__tests__/host-transfer-proxy.test.ts +43 -53
  118. package/src/__tests__/http-user-message-parity.test.ts +0 -6
  119. package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
  120. package/src/__tests__/injector-chain.test.ts +10 -5
  121. package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
  122. package/src/__tests__/inline-command-runner.test.ts +0 -66
  123. package/src/__tests__/inline-skill-load-permissions.test.ts +0 -2
  124. package/src/__tests__/install-skill-routing.test.ts +1 -13
  125. package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
  126. package/src/__tests__/llm-catalog-parity.test.ts +90 -0
  127. package/src/__tests__/llm-context-resolution.test.ts +180 -0
  128. package/src/__tests__/llm-resolver.test.ts +80 -12
  129. package/src/__tests__/llm-usage-store.test.ts +269 -4
  130. package/src/__tests__/log-export-routes.test.ts +89 -0
  131. package/src/__tests__/managed-profile-guard.test.ts +225 -0
  132. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -10
  133. package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
  134. package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
  135. package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
  136. package/src/__tests__/migration-export-http.test.ts +33 -26
  137. package/src/__tests__/migration-export-streaming.test.ts +18 -10
  138. package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
  139. package/src/__tests__/migration-import-commit-http.test.ts +66 -21
  140. package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
  141. package/src/__tests__/migration-import-from-url.test.ts +20 -6
  142. package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
  143. package/src/__tests__/migration-parity-persistence.test.ts +62 -25
  144. package/src/__tests__/migration-transport.test.ts +115 -23
  145. package/src/__tests__/migration-validate-http.test.ts +105 -80
  146. package/src/__tests__/migration-wizard.test.ts +133 -27
  147. package/src/__tests__/non-member-access-request.test.ts +1 -1
  148. package/src/__tests__/notification-guardian-path.test.ts +1 -1
  149. package/src/__tests__/oauth-store.test.ts +19 -0
  150. package/src/__tests__/platform-bash-auto-approve.test.ts +21 -12
  151. package/src/__tests__/prechat-onboarding-contract.test.ts +31 -7
  152. package/src/__tests__/pricing.test.ts +68 -4
  153. package/src/__tests__/process-message-background-slack.test.ts +331 -0
  154. package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
  155. package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
  156. package/src/__tests__/provider-usage-tracking.test.ts +208 -0
  157. package/src/__tests__/reaction-persistence.test.ts +9 -6
  158. package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
  159. package/src/__tests__/recording-handler.test.ts +64 -81
  160. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
  161. package/src/__tests__/relay-server.test.ts +18 -13
  162. package/src/__tests__/require-fresh-approval.test.ts +13 -22
  163. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  164. package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
  165. package/src/__tests__/runtime-events-sse.test.ts +3 -12
  166. package/src/__tests__/search-skills-unified.test.ts +9 -15
  167. package/src/__tests__/secret-ingress-cli.test.ts +2 -5
  168. package/src/__tests__/secret-ingress-http.test.ts +0 -4
  169. package/src/__tests__/secret-onetime-send.test.ts +4 -2
  170. package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
  171. package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
  172. package/src/__tests__/secret-response-routing.test.ts +29 -15
  173. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
  174. package/src/__tests__/secret-scanner.test.ts +2 -545
  175. package/src/__tests__/send-endpoint-busy.test.ts +9 -24
  176. package/src/__tests__/settings-routes.test.ts +1 -1
  177. package/src/__tests__/shell-credential-ref.test.ts +0 -8
  178. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -56
  179. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -11
  180. package/src/__tests__/skill-tool-factory.test.ts +97 -0
  181. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
  182. package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
  183. package/src/__tests__/slack-inbound-verification.test.ts +1 -62
  184. package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
  185. package/src/__tests__/subagent-manager-notify.test.ts +70 -70
  186. package/src/__tests__/subagent-notify-parent.test.ts +80 -83
  187. package/src/__tests__/system-prompt.test.ts +115 -13
  188. package/src/__tests__/terminal-tools.test.ts +0 -89
  189. package/src/__tests__/thread-backfill.test.ts +945 -31
  190. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
  191. package/src/__tests__/tool-execute-pipeline.test.ts +0 -6
  192. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -16
  193. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
  194. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -7
  195. package/src/__tests__/tool-executor.test.ts +12 -19
  196. package/src/__tests__/tool-metrics-listener.test.ts +0 -35
  197. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  198. package/src/__tests__/tool-trace-listener.test.ts +0 -17
  199. package/src/__tests__/transfer-progress-screen.test.ts +63 -26
  200. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
  201. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
  202. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  203. package/src/__tests__/tts-catalog-parity.test.ts +16 -5
  204. package/src/__tests__/usage-attribution.test.ts +247 -0
  205. package/src/__tests__/usage-cli.test.ts +143 -0
  206. package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
  207. package/src/__tests__/usage-routes.test.ts +150 -0
  208. package/src/__tests__/validation-results-screen.test.ts +39 -16
  209. package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
  210. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +49 -137
  211. package/src/__tests__/verification-control-plane-policy.test.ts +4 -7
  212. package/src/__tests__/voice-session-bridge.test.ts +5 -5
  213. package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
  214. package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
  215. package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
  216. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
  217. package/src/acp/index.ts +0 -15
  218. package/src/acp/session-manager.ts +37 -34
  219. package/src/agent/loop.ts +16 -1
  220. package/src/approvals/AGENTS.md +4 -0
  221. package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
  222. package/src/approvals/guardian-request-resolvers.ts +10 -2
  223. package/src/backup/__tests__/backup-worker.test.ts +36 -8
  224. package/src/backup/__tests__/paths.test.ts +2 -2
  225. package/src/backup/__tests__/restore.test.ts +45 -28
  226. package/src/backup/backup-worker.ts +36 -2
  227. package/src/backup/paths.ts +9 -6
  228. package/src/browser-session/events.ts +0 -9
  229. package/src/calls/call-store.ts +1 -34
  230. package/src/calls/guardian-question-copy.ts +0 -108
  231. package/src/calls/relay-server.ts +0 -24
  232. package/src/calls/twilio-rest.ts +0 -38
  233. package/src/calls/twilio-routes.ts +1 -1
  234. package/src/calls/voice-session-bridge.ts +7 -38
  235. package/src/channels/types.ts +1 -36
  236. package/src/cli/commands/__tests__/cache.test.ts +152 -5
  237. package/src/cli/commands/__tests__/memory-v2.test.ts +14 -28
  238. package/src/cli/commands/__tests__/trust.test.ts +21 -387
  239. package/src/cli/commands/backup.ts +4 -4
  240. package/src/cli/commands/cache-fs.ts +8 -0
  241. package/src/cli/commands/cache.ts +153 -82
  242. package/src/cli/commands/clients.ts +63 -5
  243. package/src/cli/commands/completions.ts +3 -3
  244. package/src/cli/commands/contacts.ts +231 -76
  245. package/src/cli/commands/keys.ts +4 -1
  246. package/src/cli/commands/memory-v2.ts +24 -52
  247. package/src/cli/commands/oauth/shared.ts +2 -29
  248. package/src/cli/commands/pending.ts +102 -0
  249. package/src/cli/commands/skills.ts +77 -35
  250. package/src/cli/commands/trust.ts +70 -430
  251. package/src/cli/commands/usage.ts +25 -16
  252. package/src/cli/lib/daemon-credential-client.ts +14 -0
  253. package/src/cli/program.ts +2 -0
  254. package/src/cli.ts +0 -21
  255. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  256. package/src/config/bundled-skills/messaging/TOOLS.json +14 -4
  257. package/src/config/env-registry.ts +12 -2
  258. package/src/config/env.ts +3 -14
  259. package/src/config/feature-flag-registry.json +30 -30
  260. package/src/config/llm-callsite-catalog.ts +12 -0
  261. package/src/config/llm-context-resolution.ts +80 -0
  262. package/src/config/llm-resolver.ts +58 -22
  263. package/src/config/loader.ts +3 -3
  264. package/src/config/schema.ts +2 -158
  265. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  266. package/src/config/schemas/call-site-catalog.ts +271 -0
  267. package/src/config/schemas/calls.ts +5 -5
  268. package/src/config/schemas/inference.ts +1 -1
  269. package/src/config/schemas/ingress.ts +1 -1
  270. package/src/config/schemas/llm.ts +31 -3
  271. package/src/config/schemas/memory-retrieval.ts +2 -2
  272. package/src/config/schemas/memory-v2.ts +9 -0
  273. package/src/config/schemas/security.ts +1 -42
  274. package/src/config/schemas/services.ts +6 -6
  275. package/src/config/schemas/skills.ts +5 -5
  276. package/src/config/schemas/tts.ts +1 -1
  277. package/src/config/seed-inference-profiles.ts +117 -0
  278. package/src/config/skills.ts +0 -90
  279. package/src/config/types.ts +3 -6
  280. package/src/contacts/contact-store.ts +0 -17
  281. package/src/contacts/contacts-write.ts +1 -105
  282. package/src/context/window-manager.ts +44 -5
  283. package/src/credential-execution/process-manager.ts +34 -10
  284. package/src/credential-health/credential-health-service.ts +21 -16
  285. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
  286. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
  287. package/src/daemon/connection-policy.ts +1 -26
  288. package/src/daemon/conversation-agent-loop-handlers.ts +53 -4
  289. package/src/daemon/conversation-agent-loop.ts +277 -36
  290. package/src/daemon/conversation-history.ts +8 -8
  291. package/src/daemon/conversation-launch.ts +20 -135
  292. package/src/daemon/conversation-lifecycle.ts +1 -1
  293. package/src/daemon/conversation-messaging.ts +1 -0
  294. package/src/daemon/conversation-process.ts +83 -163
  295. package/src/daemon/conversation-runtime-assembly.ts +219 -76
  296. package/src/daemon/conversation-slash.ts +47 -5
  297. package/src/daemon/conversation-store.ts +7 -31
  298. package/src/daemon/conversation-surfaces.ts +22 -28
  299. package/src/daemon/conversation-tool-setup.ts +3 -33
  300. package/src/daemon/conversation-usage.ts +36 -0
  301. package/src/daemon/conversation.ts +117 -233
  302. package/src/daemon/daemon-control.ts +3 -71
  303. package/src/daemon/daemon-skill-host.ts +8 -11
  304. package/src/daemon/dictation-profile-store.ts +2 -26
  305. package/src/daemon/first-greeting.ts +44 -156
  306. package/src/daemon/handlers/config-channels.ts +12 -12
  307. package/src/daemon/handlers/config-ingress.ts +4 -165
  308. package/src/daemon/handlers/config-model.ts +1 -1
  309. package/src/daemon/handlers/config-voice.ts +0 -42
  310. package/src/daemon/handlers/conversations.ts +11 -190
  311. package/src/daemon/handlers/recording.ts +26 -158
  312. package/src/daemon/handlers/shared.ts +23 -71
  313. package/src/daemon/handlers/skills.ts +42 -93
  314. package/src/daemon/host-bash-proxy.ts +67 -45
  315. package/src/daemon/host-browser-proxy.ts +65 -27
  316. package/src/daemon/host-cu-proxy.ts +40 -39
  317. package/src/daemon/host-file-proxy.ts +58 -37
  318. package/src/daemon/host-transfer-proxy.ts +84 -46
  319. package/src/daemon/lifecycle.ts +49 -15
  320. package/src/daemon/message-types/conversations.ts +7 -0
  321. package/src/daemon/message-types/host-bash.ts +1 -0
  322. package/src/daemon/message-types/host-cu.ts +1 -0
  323. package/src/daemon/message-types/host-file.ts +1 -0
  324. package/src/daemon/message-types/host-transfer.ts +1 -0
  325. package/src/daemon/message-types/messages.ts +10 -9
  326. package/src/daemon/message-types/workspace.ts +1 -1
  327. package/src/daemon/process-message.ts +102 -239
  328. package/src/daemon/server.ts +13 -462
  329. package/src/daemon/shutdown-handlers.ts +2 -2
  330. package/src/daemon/tool-side-effects.ts +125 -107
  331. package/src/daemon/trust-context.ts +13 -0
  332. package/src/daemon/wake-target-adapter.ts +4 -9
  333. package/src/events/domain-events.ts +0 -8
  334. package/src/events/tool-audit-listener.ts +3 -1
  335. package/src/events/tool-domain-event-publisher.ts +0 -10
  336. package/src/events/tool-metrics-listener.ts +0 -17
  337. package/src/events/tool-trace-listener.ts +0 -14
  338. package/src/filing/filing-service.ts +13 -1
  339. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +6 -2
  340. package/src/heartbeat/heartbeat-service.ts +23 -5
  341. package/src/home/__tests__/feed-writer.test.ts +0 -4
  342. package/src/home/__tests__/relationship-state-writer.test.ts +30 -0
  343. package/src/home/feed-writer.ts +1 -2
  344. package/src/home/relationship-state-writer.ts +16 -3
  345. package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
  346. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
  347. package/src/ipc/assistant-server.ts +3 -10
  348. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
  349. package/src/ipc/routes/route-adapter.ts +1 -1
  350. package/src/ipc/routes/trust-rules.test.ts +0 -95
  351. package/src/ipc/skill-ipc-types.ts +41 -0
  352. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
  353. package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
  354. package/src/ipc/skill-routes/events.ts +12 -23
  355. package/src/ipc/skill-routes/identity.ts +4 -17
  356. package/src/ipc/skill-routes/index.ts +1 -1
  357. package/src/ipc/skill-server.ts +6 -39
  358. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
  359. package/src/live-voice/protocol.ts +4 -13
  360. package/src/mcp/manager.ts +0 -5
  361. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
  362. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
  363. package/src/memory/app-git-service.ts +0 -32
  364. package/src/memory/app-store.ts +154 -0
  365. package/src/memory/attachments-store.ts +6 -0
  366. package/src/memory/context-search/sources/memory-v2.ts +578 -0
  367. package/src/memory/context-search/sources/memory.ts +5 -0
  368. package/src/memory/context-search/sources/pkb.ts +10 -1
  369. package/src/memory/context-search/sources/workspace.ts +3 -2
  370. package/src/memory/conversation-crud.ts +29 -4
  371. package/src/memory/conversation-disk-view.ts +1 -5
  372. package/src/memory/conversation-starter-checkpoints.ts +63 -0
  373. package/src/memory/db-connection.ts +62 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +3 -21
  376. package/src/memory/embedding-gemini.ts +0 -2
  377. package/src/memory/embedding-local.ts +6 -6
  378. package/src/memory/embedding-ollama.ts +6 -6
  379. package/src/memory/embedding-openai.ts +6 -6
  380. package/src/memory/embedding-types.ts +21 -0
  381. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +3 -7
  382. package/src/memory/graph/conversation-graph-memory.ts +35 -13
  383. package/src/memory/graph/injection.test.ts +2 -2
  384. package/src/memory/graph/injection.ts +1 -1
  385. package/src/memory/guardian-action-store.ts +0 -83
  386. package/src/memory/guardian-approvals.ts +0 -48
  387. package/src/memory/indexer.ts +1 -15
  388. package/src/memory/job-handlers/conversation-starters.ts +36 -53
  389. package/src/memory/job-utils.ts +0 -6
  390. package/src/memory/jobs-store.ts +0 -1
  391. package/src/memory/jobs-worker.ts +2 -16
  392. package/src/memory/llm-request-log-store.ts +0 -41
  393. package/src/memory/llm-usage-store.ts +129 -43
  394. package/src/memory/memory-v2-activation-log-store.ts +115 -0
  395. package/src/memory/migrations/233-document-conversations.ts +54 -0
  396. package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
  397. package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
  398. package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
  399. package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
  400. package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
  401. package/src/memory/migrations/index.ts +14 -0
  402. package/src/memory/migrations/registry.ts +24 -0
  403. package/src/memory/raw-query.ts +2 -68
  404. package/src/memory/schema/conversations.ts +7 -0
  405. package/src/memory/schema/infrastructure.ts +25 -0
  406. package/src/memory/search/semantic.ts +5 -16
  407. package/src/memory/tool-usage-store.ts +2 -0
  408. package/src/memory/usage-buckets.ts +40 -1
  409. package/src/memory/usage-grouped-buckets.ts +127 -0
  410. package/src/memory/v2/__tests__/activation.test.ts +289 -90
  411. package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
  412. package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
  413. package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
  414. package/src/memory/v2/__tests__/injection.test.ts +384 -15
  415. package/src/memory/v2/__tests__/migration.test.ts +64 -36
  416. package/src/memory/v2/__tests__/page-store.test.ts +191 -8
  417. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
  418. package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
  419. package/src/memory/v2/__tests__/static-context.test.ts +153 -0
  420. package/src/memory/v2/activation.ts +168 -97
  421. package/src/memory/v2/backfill-jobs.ts +15 -100
  422. package/src/memory/v2/consolidation-job.ts +14 -12
  423. package/src/memory/v2/edge-index.ts +191 -0
  424. package/src/memory/v2/injection.ts +182 -58
  425. package/src/memory/v2/migration.ts +57 -64
  426. package/src/memory/v2/now-text.ts +2 -3
  427. package/src/memory/v2/page-store.ts +168 -31
  428. package/src/memory/v2/prompts/consolidation.ts +118 -42
  429. package/src/memory/v2/prompts/sweep.ts +3 -3
  430. package/src/memory/v2/skill-store.ts +55 -7
  431. package/src/memory/v2/static-context.ts +62 -0
  432. package/src/memory/v2/types.ts +10 -20
  433. package/src/memory/validation.ts +0 -11
  434. package/src/messaging/draft-store.ts +0 -6
  435. package/src/messaging/provider-types.ts +8 -0
  436. package/src/messaging/provider.ts +7 -0
  437. package/src/messaging/providers/gmail/client.ts +1 -121
  438. package/src/messaging/providers/outlook/client.ts +0 -73
  439. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
  440. package/src/messaging/providers/slack/adapter.ts +122 -21
  441. package/src/messaging/providers/slack/backfill.test.ts +95 -6
  442. package/src/messaging/providers/slack/backfill.ts +89 -11
  443. package/src/messaging/providers/slack/client.ts +10 -124
  444. package/src/messaging/providers/slack/message-metadata.ts +12 -2
  445. package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
  446. package/src/messaging/providers/slack/render-transcript.ts +126 -25
  447. package/src/messaging/providers/slack/types.ts +1 -0
  448. package/src/oauth/connection-resolver.test.ts +8 -0
  449. package/src/oauth/connection-resolver.ts +8 -16
  450. package/src/oauth/credential-token-resolver.ts +97 -0
  451. package/src/oauth/manual-token-connection.ts +30 -34
  452. package/src/oauth/oauth-store.ts +6 -4
  453. package/src/outbound-proxy/certs.ts +0 -7
  454. package/src/outbound-proxy/config.ts +0 -74
  455. package/src/outbound-proxy/health.ts +0 -44
  456. package/src/outbound-proxy/index.ts +0 -22
  457. package/src/permissions/approval-provenance.test.ts +184 -0
  458. package/src/permissions/approval-provenance.ts +70 -0
  459. package/src/permissions/checker.ts +4 -1
  460. package/src/permissions/gateway-threshold-reader.ts +4 -1
  461. package/src/permissions/prompter.ts +9 -2
  462. package/src/permissions/secret-prompter.ts +21 -48
  463. package/src/permissions/types.ts +33 -0
  464. package/src/permissions/workspace-policy.ts +0 -5
  465. package/src/platform/sync-identity.ts +0 -8
  466. package/src/plugins/defaults/injectors.ts +69 -2
  467. package/src/plugins/defaults/overflow-reduce.ts +3 -2
  468. package/src/plugins/types.ts +8 -0
  469. package/src/prompts/system-prompt.ts +34 -70
  470. package/src/prompts/templates/BOOTSTRAP.md +52 -6
  471. package/src/prompts/update-bulletin-job.ts +2 -0
  472. package/src/providers/__tests__/retry-callsite.test.ts +138 -1
  473. package/src/providers/anthropic/client.ts +72 -33
  474. package/src/providers/call-site-routing.ts +42 -3
  475. package/src/providers/gemini/client.ts +18 -2
  476. package/src/providers/managed-proxy/context.ts +0 -5
  477. package/src/providers/model-catalog.ts +105 -19
  478. package/src/providers/openai/chat-completions-provider.ts +6 -0
  479. package/src/providers/openai/responses-provider.ts +7 -1
  480. package/src/providers/provider-send-message.ts +45 -2
  481. package/src/providers/ratelimit.ts +7 -2
  482. package/src/providers/registry.ts +14 -9
  483. package/src/providers/retry.ts +96 -8
  484. package/src/providers/types.ts +13 -0
  485. package/src/providers/usage-tracking.ts +96 -0
  486. package/src/runtime/AGENTS.md +10 -6
  487. package/src/runtime/__tests__/agent-wake.test.ts +89 -0
  488. package/src/runtime/agent-wake.ts +39 -2
  489. package/src/runtime/assistant-event-hub.ts +541 -45
  490. package/src/runtime/assistant-event.ts +1 -6
  491. package/src/runtime/auth/context.ts +0 -9
  492. package/src/runtime/auth/middleware.ts +1 -1
  493. package/src/runtime/auth/route-policy.ts +11 -9
  494. package/src/runtime/auth/token-service.ts +0 -11
  495. package/src/runtime/channel-approvals.ts +6 -2
  496. package/src/runtime/channel-verification-service.ts +3 -5
  497. package/src/runtime/http-errors.ts +0 -34
  498. package/src/runtime/http-router.ts +6 -3
  499. package/src/runtime/http-server.ts +22 -82
  500. package/src/runtime/http-types.ts +5 -0
  501. package/src/runtime/interactive-ui.ts +0 -1
  502. package/src/runtime/middleware/auth.ts +0 -20
  503. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
  504. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
  505. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
  506. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
  507. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
  508. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
  509. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +143 -79
  510. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
  511. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
  512. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +371 -0
  513. package/src/runtime/migrations/migration-transport.ts +46 -13
  514. package/src/runtime/migrations/migration-wizard.ts +2 -2
  515. package/src/runtime/migrations/origin-mode.ts +40 -0
  516. package/src/runtime/migrations/vbundle-builder.ts +133 -79
  517. package/src/runtime/migrations/vbundle-import-analyzer.ts +9 -7
  518. package/src/runtime/migrations/vbundle-importer.ts +7 -7
  519. package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
  520. package/src/runtime/migrations/vbundle-streaming-importer.ts +3 -3
  521. package/src/runtime/migrations/vbundle-streaming-validator.ts +48 -26
  522. package/src/runtime/migrations/vbundle-validator.ts +214 -41
  523. package/src/runtime/pending-interactions.ts +13 -4
  524. package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
  525. package/src/runtime/routes/__tests__/backup-routes.test.ts +28 -19
  526. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +235 -0
  527. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
  528. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
  529. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
  530. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
  531. package/src/runtime/routes/acp-routes.test.ts +0 -3
  532. package/src/runtime/routes/acp-routes.ts +3 -7
  533. package/src/runtime/routes/app-management-routes.ts +18 -9
  534. package/src/runtime/routes/approval-routes.ts +55 -14
  535. package/src/runtime/routes/avatar-routes.ts +3 -5
  536. package/src/runtime/routes/browser-routes.ts +1 -15
  537. package/src/runtime/routes/channel-guardian-routes.ts +1 -5
  538. package/src/runtime/routes/channel-readiness-routes.ts +3 -7
  539. package/src/runtime/routes/channel-route-shared.ts +2 -28
  540. package/src/runtime/routes/client-routes.ts +45 -12
  541. package/src/runtime/routes/consolidation-routes.ts +115 -0
  542. package/src/runtime/routes/conversation-list-routes.ts +12 -29
  543. package/src/runtime/routes/conversation-management-routes.ts +14 -51
  544. package/src/runtime/routes/conversation-query-routes.ts +120 -8
  545. package/src/runtime/routes/conversation-routes.ts +44 -528
  546. package/src/runtime/routes/conversation-starter-routes.ts +19 -40
  547. package/src/runtime/routes/documents-routes.ts +53 -18
  548. package/src/runtime/routes/events-routes.ts +59 -91
  549. package/src/runtime/routes/filing-routes.ts +18 -1
  550. package/src/runtime/routes/guardian-action-routes.ts +4 -9
  551. package/src/runtime/routes/host-bash-routes.ts +3 -2
  552. package/src/runtime/routes/host-browser-routes.ts +9 -33
  553. package/src/runtime/routes/host-cu-routes.ts +6 -1
  554. package/src/runtime/routes/host-file-routes.ts +3 -2
  555. package/src/runtime/routes/host-transfer-routes.ts +11 -15
  556. package/src/runtime/routes/identity-routes.ts +78 -6
  557. package/src/runtime/routes/inbound-message-handler.ts +580 -137
  558. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -88
  559. package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
  560. package/src/runtime/routes/index.ts +4 -0
  561. package/src/runtime/routes/integrations/slack/channel.ts +0 -24
  562. package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
  563. package/src/runtime/routes/memory-v2-routes.ts +10 -15
  564. package/src/runtime/routes/migration-routes.ts +188 -31
  565. package/src/runtime/routes/playground/guard.ts +1 -1
  566. package/src/runtime/routes/playground/index.ts +0 -2
  567. package/src/runtime/routes/recording-routes.ts +4 -24
  568. package/src/runtime/routes/rename-conversation-routes.ts +2 -6
  569. package/src/runtime/routes/schedule-routes.ts +3 -6
  570. package/src/runtime/routes/secret-routes.ts +87 -18
  571. package/src/runtime/routes/settings-routes.ts +29 -28
  572. package/src/runtime/routes/skills-routes.ts +12 -31
  573. package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
  574. package/src/runtime/routes/task-routes.ts +6 -6
  575. package/src/runtime/routes/trust-rules-routes.ts +3 -94
  576. package/src/runtime/routes/types.ts +4 -4
  577. package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
  578. package/src/runtime/routes/usage-routes.ts +87 -10
  579. package/src/runtime/routes/user-routes.ts +17 -31
  580. package/src/runtime/routes/work-items-routes.ts +1 -4
  581. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
  582. package/src/runtime/services/analyze-conversation.ts +7 -17
  583. package/src/runtime/services/conversation-serializer.ts +2 -4
  584. package/src/runtime/verification-outbound-actions.ts +1 -1
  585. package/src/runtime/verification-rate-limiter.ts +1 -1
  586. package/src/schedule/schedule-store.ts +0 -16
  587. package/src/security/secret-scanner.ts +14 -547
  588. package/src/security/secure-keys.ts +31 -11
  589. package/src/security/token-manager.ts +7 -3
  590. package/src/signals/cancel.ts +16 -25
  591. package/src/signals/conversation-undo.ts +2 -27
  592. package/src/signals/emit-event.ts +1 -2
  593. package/src/signals/user-message.ts +108 -22
  594. package/src/skills/catalog-install.ts +1 -0
  595. package/src/skills/clawhub.ts +2 -2
  596. package/src/skills/inline-command-runner.ts +1 -7
  597. package/src/subagent/manager.ts +67 -84
  598. package/src/tasks/task-store.ts +1 -28
  599. package/src/telemetry/types.ts +6 -0
  600. package/src/telemetry/usage-telemetry-reporter.test.ts +38 -15
  601. package/src/telemetry/usage-telemetry-reporter.ts +3 -5
  602. package/src/tools/acp/spawn.test.ts +1 -2
  603. package/src/tools/acp/steer.test.ts +1 -2
  604. package/src/tools/browser/__tests__/browser-status.test.ts +44 -127
  605. package/src/tools/browser/browser-execution.ts +31 -147
  606. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +92 -68
  607. package/src/tools/browser/cdp-client/factory.ts +48 -76
  608. package/src/tools/browser/cdp-client/index.ts +1 -14
  609. package/src/tools/executor.ts +44 -31
  610. package/src/tools/host-filesystem/edit.ts +3 -2
  611. package/src/tools/host-filesystem/read.ts +3 -2
  612. package/src/tools/host-filesystem/transfer.test.ts +45 -42
  613. package/src/tools/host-filesystem/transfer.ts +4 -3
  614. package/src/tools/host-filesystem/write.ts +3 -2
  615. package/src/tools/host-terminal/host-shell.ts +4 -3
  616. package/src/tools/network/script-proxy/index.ts +1 -10
  617. package/src/tools/permission-checker.ts +66 -1
  618. package/src/tools/skills/sandbox-runner.ts +1 -6
  619. package/src/tools/skills/skill-tool-factory.ts +32 -0
  620. package/src/tools/terminal/safe-env.ts +1 -0
  621. package/src/tools/terminal/shell.ts +2 -78
  622. package/src/tools/types.ts +12 -39
  623. package/src/tts/__tests__/provider-catalog.test.ts +2 -2
  624. package/src/tts/provider-catalog.ts +1 -1
  625. package/src/usage/actors.ts +2 -1
  626. package/src/usage/attribution.ts +185 -0
  627. package/src/usage/pricing.ts +166 -0
  628. package/src/usage/types.ts +14 -0
  629. package/src/util/json.ts +13 -0
  630. package/src/util/logger.ts +3 -3
  631. package/src/util/pricing.ts +50 -3
  632. package/src/work-items/work-item-runner.ts +15 -42
  633. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +4 -3
  634. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
  635. package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
  636. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +59 -0
  637. package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
  638. package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
  639. package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
  640. package/src/workspace/migrations/registry.ts +8 -0
  641. package/src/workspace/provider-commit-message-generator.ts +3 -3
  642. package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
  643. package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
  644. package/src/__tests__/secret-detection-handler.test.ts +0 -67
  645. package/src/__tests__/secret-scanner-executor.test.ts +0 -450
  646. package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
  647. package/src/__tests__/terminal-sandbox.test.ts +0 -374
  648. package/src/__tests__/tool-notification-listener.test.ts +0 -65
  649. package/src/context/__tests__/microcompact.test.ts +0 -805
  650. package/src/context/microcompact.ts +0 -443
  651. package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
  652. package/src/events/tool-notification-listener.ts +0 -17
  653. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
  654. package/src/memory/v2/__tests__/edges.test.ts +0 -435
  655. package/src/memory/v2/edges.ts +0 -217
  656. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
  657. package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
  658. package/src/runtime/__tests__/client-registry.test.ts +0 -271
  659. package/src/runtime/chrome-extension-registry.ts +0 -368
  660. package/src/runtime/client-registry.ts +0 -254
  661. package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
  662. package/src/tools/secret-detection-handler.ts +0 -269
  663. package/src/tools/terminal/backends/native.ts +0 -327
  664. package/src/tools/terminal/backends/types.ts +0 -37
  665. package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
  666. package/src/tools/terminal/sandbox.ts +0 -40
@@ -1,271 +0,0 @@
1
- import { beforeEach, describe, expect, test } from "bun:test";
2
-
3
- import {
4
- __resetClientRegistryForTests,
5
- ClientRegistry,
6
- getClientRegistry,
7
- } from "../client-registry.js";
8
-
9
- describe("ClientRegistry", () => {
10
- beforeEach(() => {
11
- __resetClientRegistryForTests();
12
- });
13
-
14
- // ── register ──────────────────────────────────────────────────────────────
15
-
16
- test("register creates a new entry with derived capabilities", () => {
17
- const registry = new ClientRegistry();
18
- const entry = registry.register({
19
- clientId: "mac-1",
20
- interfaceId: "macos",
21
- });
22
-
23
- expect(entry.clientId).toBe("mac-1");
24
- expect(entry.interfaceId).toBe("macos");
25
- expect(entry.capabilities).toContain("host_bash");
26
- expect(entry.capabilities).toContain("host_file");
27
- expect(entry.capabilities).toContain("host_cu");
28
- expect(entry.capabilities).toContain("host_browser");
29
- expect(entry.connectedAt).toBeGreaterThan(0);
30
- expect(entry.lastActiveAt).toBeGreaterThanOrEqual(entry.connectedAt);
31
- });
32
-
33
- test("register derives empty capabilities for cli interface", () => {
34
- const registry = new ClientRegistry();
35
- const entry = registry.register({
36
- clientId: "cli-1",
37
- interfaceId: "cli",
38
- });
39
-
40
- expect(entry.capabilities).toEqual([]);
41
- });
42
-
43
- test("register derives host_browser capability for chrome-extension", () => {
44
- const registry = new ClientRegistry();
45
- const entry = registry.register({
46
- clientId: "ext-1",
47
- interfaceId: "chrome-extension",
48
- });
49
-
50
- expect(entry.capabilities).toEqual(["host_browser"]);
51
- });
52
-
53
- test("register refreshes lastActiveAt on re-register with same clientId", () => {
54
- const registry = new ClientRegistry();
55
- const first = registry.register({
56
- clientId: "mac-1",
57
- interfaceId: "macos",
58
- });
59
- const firstActive = first.lastActiveAt;
60
-
61
- // Advance time slightly
62
- const second = registry.register({
63
- clientId: "mac-1",
64
- interfaceId: "macos",
65
- });
66
-
67
- // Same object reference — refreshed in place
68
- expect(second).toBe(first);
69
- expect(second.lastActiveAt).toBeGreaterThanOrEqual(firstActive);
70
- // connectedAt should NOT change on refresh
71
- expect(second.connectedAt).toBe(first.connectedAt);
72
- });
73
-
74
- test("register does not increase size on re-register", () => {
75
- const registry = new ClientRegistry();
76
- registry.register({ clientId: "mac-1", interfaceId: "macos" });
77
- registry.register({ clientId: "mac-1", interfaceId: "macos" });
78
- expect(registry.size).toBe(1);
79
- });
80
-
81
- // ── unregister ────────────────────────────────────────────────────────────
82
-
83
- test("unregister removes the entry", () => {
84
- const registry = new ClientRegistry();
85
- registry.register({ clientId: "mac-1", interfaceId: "macos" });
86
- expect(registry.size).toBe(1);
87
-
88
- registry.unregister("mac-1");
89
- expect(registry.size).toBe(0);
90
- expect(registry.get("mac-1")).toBeUndefined();
91
- });
92
-
93
- test("unregister is a no-op for unknown clientId", () => {
94
- const registry = new ClientRegistry();
95
- registry.register({ clientId: "mac-1", interfaceId: "macos" });
96
- registry.unregister("unknown");
97
- expect(registry.size).toBe(1);
98
- });
99
-
100
- // ── touch ─────────────────────────────────────────────────────────────────
101
-
102
- test("touch updates lastActiveAt", () => {
103
- const registry = new ClientRegistry();
104
- const entry = registry.register({
105
- clientId: "mac-1",
106
- interfaceId: "macos",
107
- });
108
- const before = entry.lastActiveAt;
109
- registry.touch("mac-1");
110
- expect(entry.lastActiveAt).toBeGreaterThanOrEqual(before);
111
- });
112
-
113
- test("touch is a no-op for unknown clientId", () => {
114
- const registry = new ClientRegistry();
115
- // Should not throw
116
- registry.touch("unknown");
117
- });
118
-
119
- // ── get ───────────────────────────────────────────────────────────────────
120
-
121
- test("get returns the entry for a registered clientId", () => {
122
- const registry = new ClientRegistry();
123
- const entry = registry.register({
124
- clientId: "mac-1",
125
- interfaceId: "macos",
126
- });
127
- expect(registry.get("mac-1")).toBe(entry);
128
- });
129
-
130
- test("get returns undefined for unknown clientId", () => {
131
- const registry = new ClientRegistry();
132
- expect(registry.get("unknown")).toBeUndefined();
133
- });
134
-
135
- // ── listAll ───────────────────────────────────────────────────────────────
136
-
137
- test("listAll returns entries sorted by lastActiveAt descending", () => {
138
- const registry = new ClientRegistry();
139
- registry.register({ clientId: "old", interfaceId: "cli" });
140
- // Touch the second one to make it most recent
141
- registry.register({ clientId: "new", interfaceId: "macos" });
142
-
143
- const all = registry.listAll();
144
- expect(all.length).toBe(2);
145
- expect(all[0].lastActiveAt).toBeGreaterThanOrEqual(all[1].lastActiveAt);
146
- });
147
-
148
- test("listAll returns empty array when no clients registered", () => {
149
- const registry = new ClientRegistry();
150
- expect(registry.listAll()).toEqual([]);
151
- });
152
-
153
- // ── listByCapability ──────────────────────────────────────────────────────
154
-
155
- test("listByCapability filters to clients with matching capability", () => {
156
- const registry = new ClientRegistry();
157
- registry.register({ clientId: "mac-1", interfaceId: "macos" });
158
- registry.register({ clientId: "cli-1", interfaceId: "cli" });
159
- registry.register({ clientId: "ext-1", interfaceId: "chrome-extension" });
160
-
161
- const bashCapable = registry.listByCapability("host_bash");
162
- expect(bashCapable.length).toBe(1);
163
- expect(bashCapable[0].clientId).toBe("mac-1");
164
-
165
- const browserCapable = registry.listByCapability("host_browser");
166
- expect(browserCapable.length).toBe(2);
167
- // macOS now supports host_browser too
168
- const ids = browserCapable.map((e) => e.clientId).sort();
169
- expect(ids).toEqual(["ext-1", "mac-1"]);
170
- });
171
-
172
- test("listByCapability returns empty when no clients match", () => {
173
- const registry = new ClientRegistry();
174
- registry.register({ clientId: "cli-1", interfaceId: "cli" });
175
- expect(registry.listByCapability("host_bash")).toEqual([]);
176
- });
177
-
178
- // ── getMostRecentByCapability ─────────────────────────────────────────────
179
-
180
- test("getMostRecentByCapability returns the most recently active client", () => {
181
- const registry = new ClientRegistry();
182
- const mac1 = registry.register({ clientId: "mac-1", interfaceId: "macos" });
183
- const mac2 = registry.register({ clientId: "mac-2", interfaceId: "macos" });
184
-
185
- // Ensure deterministic ordering — both may register within the same ms
186
- mac1.lastActiveAt = Date.now() - 5000;
187
- mac2.lastActiveAt = Date.now();
188
-
189
- const best = registry.getMostRecentByCapability("host_bash");
190
- expect(best).toBeDefined();
191
- expect(best!.clientId).toBe("mac-2");
192
- });
193
-
194
- test("getMostRecentByCapability returns undefined when no clients match", () => {
195
- const registry = new ClientRegistry();
196
- registry.register({ clientId: "cli-1", interfaceId: "cli" });
197
- expect(registry.getMostRecentByCapability("host_bash")).toBeUndefined();
198
- });
199
-
200
- // ── toJSON ────────────────────────────────────────────────────────────────
201
-
202
- test("toJSON serializes with ISO timestamps", () => {
203
- const registry = new ClientRegistry();
204
- const entry = registry.register({
205
- clientId: "mac-1",
206
- interfaceId: "macos",
207
- });
208
-
209
- const json = ClientRegistry.toJSON(entry);
210
- expect(json.clientId).toBe("mac-1");
211
- expect(json.interfaceId).toBe("macos");
212
- expect(json.capabilities).toContain("host_bash");
213
- expect(json.connectedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
214
- expect(json.lastActiveAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
215
- });
216
-
217
- // ── evictStale ─────────────────────────────────────────────────────────────
218
-
219
- test("evictStale removes entries older than maxAgeMs", () => {
220
- const registry = new ClientRegistry();
221
- const entry = registry.register({
222
- clientId: "old-mac",
223
- interfaceId: "macos",
224
- });
225
- // Backdate lastActiveAt by 1 hour
226
- entry.lastActiveAt = Date.now() - 60 * 60 * 1000;
227
-
228
- const evicted = registry.evictStale(30 * 60 * 1000); // 30 min threshold
229
- expect(evicted).toBe(1);
230
- expect(registry.size).toBe(0);
231
- });
232
-
233
- test("evictStale keeps fresh entries", () => {
234
- const registry = new ClientRegistry();
235
- registry.register({ clientId: "fresh", interfaceId: "macos" });
236
-
237
- const evicted = registry.evictStale(30 * 60 * 1000);
238
- expect(evicted).toBe(0);
239
- expect(registry.size).toBe(1);
240
- });
241
-
242
- test("listAll triggers lazy eviction", () => {
243
- const registry = new ClientRegistry();
244
- const entry = registry.register({
245
- clientId: "stale-cli",
246
- interfaceId: "cli",
247
- });
248
- entry.lastActiveAt = Date.now() - 60 * 60 * 1000;
249
-
250
- registry.register({ clientId: "fresh-mac", interfaceId: "macos" });
251
-
252
- const all = registry.listAll();
253
- expect(all.length).toBe(1);
254
- expect(all[0].clientId).toBe("fresh-mac");
255
- });
256
-
257
- // ── singleton ─────────────────────────────────────────────────────────────
258
-
259
- test("getClientRegistry returns a singleton", () => {
260
- const a = getClientRegistry();
261
- const b = getClientRegistry();
262
- expect(a).toBe(b);
263
- });
264
-
265
- test("__resetClientRegistryForTests clears the singleton", () => {
266
- const a = getClientRegistry();
267
- __resetClientRegistryForTests();
268
- const b = getClientRegistry();
269
- expect(a).not.toBe(b);
270
- });
271
- });
@@ -1,368 +0,0 @@
1
- /**
2
- * Registry mapping guardianId → active Chrome extension WebSocket connections.
3
- *
4
- * Populated by the `/v1/browser-relay` WebSocket upgrade handler when a
5
- * chrome-extension client connects; drained on close. Used by
6
- * conversation-routes.ts to route `host_browser_request` frames to the
7
- * connected extension for the appropriate guardian.
8
- *
9
- * This is the chrome-extension counterpart to the SSE hub used by the macOS
10
- * client for the same purpose.
11
- *
12
- * A single guardian may have multiple simultaneous extension connections —
13
- * e.g. the same guardian signed in on two Chrome profiles, two desktops
14
- * sharing a sync identity, or a power user with parallel installs for
15
- * testing. Each install generates a stable `clientInstanceId` on first
16
- * run and sends it on every handshake; the registry keys inner entries by
17
- * that id so sibling instances don't evict each other on register/unregister.
18
- *
19
- * Routing semantics:
20
- * - When a caller does not pin a specific instance, the registry picks
21
- * the "most recently active" instance for the guardian (highest
22
- * `lastActiveAt` timestamp). `lastActiveAt` is bumped on register and
23
- * on every successful `send()` — but NOT by keepalive pings. Keepalive
24
- * frames update a separate `lastKeepaliveAt` field that is used only
25
- * for liveness checks, preventing idle instances from stealing the
26
- * routing default via periodic keepalive traffic.
27
- * - When no `clientInstanceId` is present on the handshake (older
28
- * extension builds, dev bypass paths), we synthesize a placeholder
29
- * `legacy:<connectionId>` key. The send/lookup path treats it the
30
- * same as any other instance so the call site stays uniform. The
31
- * synthesized key is deliberately connection-scoped so a stale
32
- * legacy unregister can never evict a newer entry.
33
- */
34
-
35
- import type { ServerWebSocket } from "bun";
36
-
37
- import type { ServerMessage } from "../daemon/message-protocol.js";
38
- import { getLogger } from "../util/logger.js";
39
-
40
- const log = getLogger("chrome-extension-registry");
41
-
42
- /** Prefix applied to synthesized instance ids for connections that did not
43
- * provide a client-supplied `clientInstanceId` at handshake time. Kept
44
- * distinct from any plausible client-generated value so it never collides
45
- * with a real install id. */
46
- const LEGACY_INSTANCE_PREFIX = "legacy:";
47
-
48
- export interface ChromeExtensionConnection {
49
- /** Stable identifier for this WebSocket connection (used for unregister). */
50
- id: string;
51
- /** Guardian identity this connection is authenticated as. */
52
- guardianId: string;
53
- /**
54
- * Stable per-extension-install identifier (persists across browser
55
- * restarts, survives service-worker teardown). Absent on older
56
- * extension builds and on dev-bypass paths — the registry synthesizes
57
- * a `legacy:<connectionId>` key in that case so sibling multi-instance
58
- * semantics degrade gracefully to single-instance behavior.
59
- */
60
- clientInstanceId?: string;
61
- /** Underlying Bun WebSocket. */
62
- ws: ServerWebSocket<unknown>;
63
- /** Wall-clock timestamp (ms) when the connection was registered. */
64
- connectedAt: number;
65
- /**
66
- * Wall-clock timestamp (ms) of the most recent activity on this
67
- * connection — updated on register and on each successful `send()`.
68
- * Used by the default-send routing path to pick the "most recently
69
- * active" instance when the caller does not pin a specific one.
70
- *
71
- * Crucially, this is NOT updated by keepalive pings — those update
72
- * `lastKeepaliveAt` instead. This separation prevents idle instances
73
- * from stealing the routing default via periodic keepalive traffic.
74
- */
75
- lastActiveAt: number;
76
- /**
77
- * Wall-clock timestamp (ms) of the most recent keepalive ping
78
- * received on this connection. Updated exclusively by `touch()`
79
- * (i.e. keepalive frames). Does NOT affect routing — used only for
80
- * liveness checks (e.g. a future stale-connection sweep can evict
81
- * connections whose `lastKeepaliveAt` is too far in the past).
82
- */
83
- lastKeepaliveAt?: number;
84
- /**
85
- * Monotonic registration sequence number assigned by the registry
86
- * on each `register()` call. Used as a tuple-secondary tiebreaker
87
- * after `lastActiveAt` when picking the default active instance, so
88
- * two sibling instances that share the same millisecond timestamp
89
- * deterministically resolve to the most recently registered entry.
90
- *
91
- * Not used when the registry routes to an explicit instance via
92
- * `sendToInstance` / `getInstance`. Clients should treat this as an
93
- * internal field — it's assigned by `register()` and callers don't
94
- * need to populate it when constructing a connection.
95
- */
96
- registrationSeq?: number;
97
- }
98
-
99
- /**
100
- * Module-level registry of active chrome-extension connections keyed by
101
- * guardianId, then by clientInstanceId. Multiple concurrent connections
102
- * under the same guardian are supported; reconnects of the *same* install
103
- * supersede the prior entry for that instance only, leaving sibling
104
- * instances untouched.
105
- */
106
- export class ChromeExtensionRegistry {
107
- private byGuardian = new Map<
108
- string,
109
- Map<string, ChromeExtensionConnection>
110
- >();
111
- /**
112
- * Monotonic counter stamped onto each registration as
113
- * `registrationSeq`, used as a secondary tiebreaker after
114
- * `lastActiveAt` in the default routing path. Starts at 0 and
115
- * increments before stamping so the first registered connection has
116
- * `registrationSeq === 1`.
117
- *
118
- * This exists because `lastActiveAt` is `Date.now()`, which has
119
- * millisecond resolution at best; two sibling instances that
120
- * register (or transmit) in the same millisecond would otherwise
121
- * resolve ties via insertion order of `Map#values()`, which is
122
- * technically well-defined but is not the "most recently registered"
123
- * behavior a caller would expect. A monotonic counter fixes that
124
- * deterministically.
125
- */
126
- private seqCounter = 0;
127
-
128
- /**
129
- * Resolve the effective instance key for a connection. Prefers the
130
- * client-supplied `clientInstanceId`; falls back to a
131
- * connection-scoped `legacy:<connectionId>` placeholder when absent
132
- * so older clients get stable single-instance semantics without
133
- * clobbering sibling instances for the same guardian.
134
- */
135
- private keyFor(conn: ChromeExtensionConnection): string {
136
- return conn.clientInstanceId ?? `${LEGACY_INSTANCE_PREFIX}${conn.id}`;
137
- }
138
-
139
- /**
140
- * Register a chrome-extension WebSocket for a guardian + instance.
141
- * If a prior connection already exists for the same guardianId AND
142
- * clientInstanceId (i.e. the same install reconnecting), it is closed
143
- * and replaced with the new one. Sibling instances for the same
144
- * guardian are left untouched.
145
- */
146
- register(conn: ChromeExtensionConnection): void {
147
- let instances = this.byGuardian.get(conn.guardianId);
148
- if (!instances) {
149
- instances = new Map();
150
- this.byGuardian.set(conn.guardianId, instances);
151
- }
152
-
153
- const instanceKey = this.keyFor(conn);
154
- const prior = instances.get(instanceKey);
155
- if (prior && prior.id !== conn.id) {
156
- try {
157
- prior.ws.close(1000, "superseded by new connection");
158
- } catch {
159
- // Best-effort — the prior socket may already be closed.
160
- }
161
- }
162
- // Stamp lastActiveAt on (re-)register so the "most recently active"
163
- // routing heuristic treats a fresh connection as the current default
164
- // target for its guardian. Stamp a monotonic registrationSeq
165
- // alongside it so two registrations that land in the same
166
- // millisecond resolve deterministically to the newer one.
167
- conn.lastActiveAt = Date.now();
168
- conn.registrationSeq = ++this.seqCounter;
169
- instances.set(instanceKey, conn);
170
- log.info(
171
- {
172
- guardianId: conn.guardianId,
173
- connectionId: conn.id,
174
- clientInstanceId: conn.clientInstanceId,
175
- registrationSeq: conn.registrationSeq,
176
- instanceCount: instances.size,
177
- },
178
- "chrome extension registered",
179
- );
180
- }
181
-
182
- /**
183
- * Remove the entry with the given connectionId. No-op if no connection
184
- * with that id is currently registered — the entry may already have been
185
- * superseded by a newer registration for the same instance, or the
186
- * guardian may have zero active instances remaining.
187
- *
188
- * Importantly, this does not evict sibling instances under the same
189
- * guardian — multi-instance routing depends on keeping them alive
190
- * across single-instance reconnects.
191
- */
192
- unregister(connectionId: string): void {
193
- for (const [guardianId, instances] of this.byGuardian) {
194
- for (const [instanceKey, conn] of instances) {
195
- if (conn.id === connectionId) {
196
- instances.delete(instanceKey);
197
- if (instances.size === 0) {
198
- this.byGuardian.delete(guardianId);
199
- }
200
- log.info(
201
- {
202
- guardianId,
203
- connectionId,
204
- clientInstanceId: conn.clientInstanceId,
205
- remaining: instances.size,
206
- },
207
- "chrome extension unregistered",
208
- );
209
- return;
210
- }
211
- }
212
- }
213
- }
214
-
215
- /**
216
- * Update the `lastKeepaliveAt` timestamp for the connection identified
217
- * by `connectionId`, without changing routing semantics or registration
218
- * state. Used by keepalive frames to signal that the extension is still
219
- * alive and reachable. Deliberately does NOT update `lastActiveAt` — that
220
- * field is reserved for actual CDP traffic (register, send) so that idle
221
- * instances cannot steal the routing default via periodic keepalive pings.
222
- *
223
- * No-op if no connection with the given id is currently registered
224
- * (e.g. after a race between disconnect and a trailing keepalive frame).
225
- */
226
- touch(connectionId: string): void {
227
- for (const instances of this.byGuardian.values()) {
228
- for (const conn of instances.values()) {
229
- if (conn.id === connectionId) {
230
- conn.lastKeepaliveAt = Date.now();
231
- return;
232
- }
233
- }
234
- }
235
- }
236
-
237
- /**
238
- * Return the default "active" connection for a guardian — the
239
- * instance with the most recent `lastActiveAt` timestamp. Ties on
240
- * `lastActiveAt` (common when two sibling instances register or
241
- * transmit within the same millisecond) are broken by the monotonic
242
- * `registrationSeq` counter, which deterministically prefers the
243
- * most recently registered instance. Returns `undefined` when no
244
- * instances are registered for the guardian.
245
- *
246
- * Callers that want to pin a specific instance should use
247
- * {@link getInstance} instead.
248
- */
249
- get(guardianId: string): ChromeExtensionConnection | undefined {
250
- const instances = this.byGuardian.get(guardianId);
251
- if (!instances || instances.size === 0) return undefined;
252
- let best: ChromeExtensionConnection | undefined;
253
- for (const conn of instances.values()) {
254
- if (!best) {
255
- best = conn;
256
- continue;
257
- }
258
- if (conn.lastActiveAt > best.lastActiveAt) {
259
- best = conn;
260
- } else if (
261
- conn.lastActiveAt === best.lastActiveAt &&
262
- (conn.registrationSeq ?? 0) > (best.registrationSeq ?? 0)
263
- ) {
264
- best = conn;
265
- }
266
- }
267
- return best;
268
- }
269
-
270
- /**
271
- * Return the connection for an explicit (guardianId, clientInstanceId)
272
- * pair, or `undefined` if no such instance is registered. Used by
273
- * routing callers that need to target a specific extension install
274
- * (e.g. sending a follow-up frame to the same instance that produced
275
- * the prior result).
276
- */
277
- getInstance(
278
- guardianId: string,
279
- clientInstanceId: string,
280
- ): ChromeExtensionConnection | undefined {
281
- return this.byGuardian.get(guardianId)?.get(clientInstanceId);
282
- }
283
-
284
- /**
285
- * Return all currently registered connections for a guardian. Order
286
- * is insertion order of the inner map — callers that care about
287
- * recency should sort by `lastActiveAt` themselves.
288
- */
289
- listInstances(guardianId: string): ChromeExtensionConnection[] {
290
- const instances = this.byGuardian.get(guardianId);
291
- if (!instances) return [];
292
- return Array.from(instances.values());
293
- }
294
-
295
- /**
296
- * Send a ServerMessage to the default active chrome-extension
297
- * connection for the given guardian. The "default active" instance
298
- * is the one with the most recent `lastActiveAt` timestamp — i.e.
299
- * the install the user is currently driving.
300
- *
301
- * Returns `true` when a connection exists and the send succeeds;
302
- * `false` when no connection is registered or when the underlying
303
- * `ws.send` throws. On a successful send, the connection's
304
- * `lastActiveAt` is bumped so subsequent default sends stay on the
305
- * same instance unless another sibling becomes more recent.
306
- */
307
- send(guardianId: string, msg: ServerMessage): boolean {
308
- const conn = this.get(guardianId);
309
- if (!conn) return false;
310
- return this.sendTo(conn, msg);
311
- }
312
-
313
- /**
314
- * Send a ServerMessage to an explicit (guardianId, clientInstanceId)
315
- * pair. Returns `false` when no matching instance is registered or
316
- * when the underlying `ws.send` throws.
317
- */
318
- sendToInstance(
319
- guardianId: string,
320
- clientInstanceId: string,
321
- msg: ServerMessage,
322
- ): boolean {
323
- const conn = this.getInstance(guardianId, clientInstanceId);
324
- if (!conn) return false;
325
- return this.sendTo(conn, msg);
326
- }
327
-
328
- /**
329
- * Low-level send helper — writes to the socket and bumps
330
- * `lastActiveAt` on success. Callers should use {@link send} or
331
- * {@link sendToInstance} unless they already hold a resolved
332
- * connection handle.
333
- */
334
- private sendTo(conn: ChromeExtensionConnection, msg: ServerMessage): boolean {
335
- try {
336
- conn.ws.send(JSON.stringify(msg));
337
- conn.lastActiveAt = Date.now();
338
- return true;
339
- } catch (err) {
340
- log.warn(
341
- {
342
- guardianId: conn.guardianId,
343
- connectionId: conn.id,
344
- clientInstanceId: conn.clientInstanceId,
345
- err,
346
- },
347
- "failed to send to chrome extension",
348
- );
349
- return false;
350
- }
351
- }
352
- }
353
-
354
- // ── Module-level singleton (same pattern as assistant-event-hub) ──────────
355
- let instance: ChromeExtensionRegistry | null = null;
356
-
357
- export function getChromeExtensionRegistry(): ChromeExtensionRegistry {
358
- if (!instance) instance = new ChromeExtensionRegistry();
359
- return instance;
360
- }
361
-
362
- /**
363
- * Test helper: reset the module-level singleton so each test starts with a
364
- * fresh registry. Not exported from any public index — test-only.
365
- */
366
- export function __resetChromeExtensionRegistryForTests(): void {
367
- instance = null;
368
- }