@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
@@ -65,7 +65,15 @@ describe("first-greeting", () => {
65
65
 
66
66
  it("no-onboarding greeting uses two-paragraph structure", () => {
67
67
  expect(CANNED_FIRST_GREETING).toContain("\n\n");
68
- expect(CANNED_FIRST_GREETING).toContain("I can ask");
68
+ const paragraphs = CANNED_FIRST_GREETING.split("\n\n");
69
+ expect(paragraphs.length).toBe(2);
70
+ });
71
+
72
+ it("no-onboarding greeting does not contain old self-deprecation text", () => {
73
+ expect(CANNED_FIRST_GREETING).not.toContain("no name, no memories");
74
+ expect(CANNED_FIRST_GREETING).not.toContain("Brand new");
75
+ expect(CANNED_FIRST_GREETING).not.toContain("I can ask");
76
+ expect(CANNED_FIRST_GREETING).not.toContain("get sharper");
69
77
  });
70
78
  });
71
79
 
@@ -73,229 +81,227 @@ describe("first-greeting", () => {
73
81
  const base: OnboardingGreetingContext = {
74
82
  tools: [],
75
83
  tasks: [],
76
- tone: "balanced",
84
+ tone: "grounded",
77
85
  };
78
86
 
79
- it("full dev stack: GitHub + Linear, Building + PM", () => {
87
+ it("grounded + name + assistantName", () => {
80
88
  const greeting = getCannedFirstGreeting({
81
89
  ...base,
82
- tools: ["github", "linear"],
83
- tasks: ["code-building", "project-management"],
84
- userName: "Bob",
85
- assistantName: "Pip",
90
+ tone: "grounded",
91
+ userName: "Alice",
92
+ assistantName: "Pax",
86
93
  });
87
- expect(greeting).toContain("Hey Bob, I'm Pip.");
88
- expect(greeting).toContain("You mentioned using GitHub and Linear");
89
- expect(greeting).toContain(
90
- "shipping code or figuring out what to ship next",
94
+ expect(greeting).toBe(
95
+ "Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
91
96
  );
92
- expect(greeting).toContain("\n\n");
93
97
  });
94
98
 
95
- it("PM + comms: Linear + Notion, PM + Writing", () => {
99
+ it("warm + name + assistantName", () => {
96
100
  const greeting = getCannedFirstGreeting({
97
101
  ...base,
98
- tools: ["linear", "notion"],
99
- tasks: ["project-management", "writing"],
100
- userName: "Bob",
101
- assistantName: "Pip",
102
+ tone: "warm",
103
+ userName: "Alice",
104
+ assistantName: "Remy",
102
105
  });
103
- expect(greeting).toContain("You mentioned using Notion and Linear");
104
- expect(greeting).toContain("writing a spec or pushing something forward");
106
+ expect(greeting).toBe(
107
+ "Hey Alice, I'm Remy. Good to meet you.\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.",
108
+ );
105
109
  });
106
110
 
107
- it("writer: Notion + Google Drive, Writing only", () => {
111
+ it("energetic + no names", () => {
108
112
  const greeting = getCannedFirstGreeting({
109
113
  ...base,
110
- tools: ["notion", "google-drive"],
111
- tasks: ["writing"],
112
- userName: "Bob",
113
- assistantName: "Luna",
114
+ tone: "energetic",
115
+ assistantName: "Pax",
114
116
  });
115
- expect(greeting).toContain("You mentioned using Notion and Google Drive");
116
- expect(greeting).toContain("drafting something or cleaning up docs");
117
+ expect(greeting).toBe(
118
+ "Hey, I'm Pax. Let's see what you've got.\n\nWe can jump straight into whatever you've got, or take a few minutes to just talk first. What sounds right?",
119
+ );
117
120
  });
118
121
 
119
- it("single task no tools: Building only", () => {
122
+ it("poetic + name + assistantName", () => {
120
123
  const greeting = getCannedFirstGreeting({
121
124
  ...base,
122
- tasks: ["code-building"],
123
- userName: "Bob",
124
- assistantName: "Pip",
125
+ tone: "poetic",
126
+ userName: "Alice",
127
+ assistantName: "Pax",
125
128
  });
126
- expect(greeting).toContain("Probably shipping something or debugging");
127
- expect(greeting).not.toContain("You mentioned using");
129
+ expect(greeting).toBe(
130
+ "Hey Alice, I'm Pax.\n\nWe can start with whatever's in front of you, or just talk for a bit first. Either way.",
131
+ );
128
132
  });
129
133
 
130
- it("single tool single task: GitHub + Building", () => {
134
+ it("name only (no assistantName)", () => {
131
135
  const greeting = getCannedFirstGreeting({
132
136
  ...base,
133
- tools: ["github"],
134
- tasks: ["code-building"],
135
- userName: "Bob",
136
- assistantName: "Pip",
137
+ tone: "grounded",
138
+ userName: "Alice",
137
139
  });
138
- expect(greeting).toContain("You mentioned using GitHub");
140
+ expect(greeting).toBe(
141
+ "Hey Alice,\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
142
+ );
139
143
  });
140
144
 
141
- it("many hats: 4 selections", () => {
145
+ it("assistantName only (no userName)", () => {
142
146
  const greeting = getCannedFirstGreeting({
143
147
  ...base,
144
- tasks: ["code-building", "writing", "research", "project-management"],
145
- userName: "Bob",
146
- assistantName: "Pip",
148
+ tone: "grounded",
149
+ assistantName: "Pax",
147
150
  });
148
- expect(greeting).toContain("wear a lot of hats");
149
- expect(greeting).toContain("Where should we start?");
151
+ expect(greeting).toBe(
152
+ "Hey, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
153
+ );
150
154
  });
151
155
 
152
- it("many hats: 6 selections", () => {
156
+ it("no name, no assistantName, no tone returns CANNED_FIRST_GREETING", () => {
153
157
  const greeting = getCannedFirstGreeting({
154
- ...base,
155
- tasks: [
156
- "code-building",
157
- "writing",
158
- "research",
159
- "project-management",
160
- "scheduling",
161
- "personal",
162
- ],
163
- userName: "Bob",
164
- assistantName: "Pip",
158
+ tools: [],
159
+ tasks: [],
160
+ tone: "",
165
161
  });
166
- expect(greeting).toContain("wear a lot of hats");
162
+ expect(greeting).toBe(CANNED_FIRST_GREETING);
167
163
  });
168
164
 
169
- it("no tasks with tools: no-signal branch", () => {
165
+ it("no names but valid tone uses tone-aware greeting", () => {
170
166
  const greeting = getCannedFirstGreeting({
171
- ...base,
172
- tools: ["gmail", "linear"],
167
+ tools: [],
173
168
  tasks: [],
174
- userName: "Bob",
175
- assistantName: "Pip",
169
+ tone: "warm",
176
170
  });
177
- expect(greeting).toContain("What's on your plate?");
178
- expect(greeting).toContain("I can ask you a few questions");
171
+ expect(greeting).toBe(
172
+ "Hey,\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.",
173
+ );
179
174
  });
180
175
 
181
- it("missing name uses Hey comma opener", () => {
182
- const greeting = getCannedFirstGreeting({
183
- ...base,
184
- tasks: ["code-building"],
185
- assistantName: "Pip",
186
- });
187
- expect(greeting).toStartWith("Hey, I'm Pip.");
176
+ it("each valid tone with no names produces distinct invite", () => {
177
+ const greetings = ["grounded", "warm", "energetic", "poetic"].map(
178
+ (tone) => getCannedFirstGreeting({ tools: [], tasks: [], tone }),
179
+ );
180
+ const unique = new Set(greetings);
181
+ expect(unique.size).toBe(4);
188
182
  });
189
183
 
190
- it("3-selection falls back to highest-priority single template", () => {
184
+ it("unknown tone falls back to grounded defaults", () => {
191
185
  const greeting = getCannedFirstGreeting({
192
186
  ...base,
193
- tasks: ["writing", "research", "scheduling"],
194
- userName: "Bob",
195
- assistantName: "Pip",
187
+ tone: "mysterious-future-tone",
188
+ userName: "Alice",
189
+ assistantName: "Pax",
196
190
  });
197
- expect(greeting).toContain("drafting something or cleaning up docs");
191
+ expect(greeting).toBe(
192
+ "Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
193
+ );
198
194
  });
199
195
 
200
- it("unlisted 2-combo falls back to highest-priority single", () => {
196
+ it("two-paragraph structure preserved", () => {
201
197
  const greeting = getCannedFirstGreeting({
202
198
  ...base,
203
- tasks: ["research", "personal"],
204
- userName: "Bob",
205
- assistantName: "Pip",
199
+ tone: "grounded",
200
+ userName: "Alice",
201
+ assistantName: "Pax",
206
202
  });
207
- expect(greeting).toContain(
208
- "digging into a topic or making sense of something",
203
+ const paragraphs = greeting.split("\n\n");
204
+ expect(paragraphs.length).toBe(2);
205
+ });
206
+ });
207
+
208
+ describe("tone-specific greetings", () => {
209
+ const base: OnboardingGreetingContext = {
210
+ tools: [],
211
+ tasks: [],
212
+ tone: "grounded",
213
+ userName: "Bob",
214
+ assistantName: "Pax",
215
+ };
216
+
217
+ it("grounded intro close is empty, invite is grounded", () => {
218
+ const greeting = getCannedFirstGreeting({ ...base, tone: "grounded" });
219
+ const [intro, invite] = greeting.split("\n\n");
220
+ expect(intro).toBe("Hey Bob, I'm Pax.");
221
+ expect(invite).toBe(
222
+ "We can get into whatever you've got, or just talk first — that tends to go better. Up to you.",
209
223
  );
210
224
  });
211
225
 
212
- it("uses capital I throughout", () => {
213
- const greeting = getCannedFirstGreeting({
214
- ...base,
215
- tasks: ["code-building"],
216
- userName: "Bob",
217
- assistantName: "Pip",
218
- });
219
- expect(greeting).toContain("I'm Pip");
220
- expect(greeting).toContain("I'll get sharper");
221
- expect(greeting).toContain("Am I on the right track");
226
+ it("warm intro close is 'Good to meet you.', invite is warm", () => {
227
+ const greeting = getCannedFirstGreeting({ ...base, tone: "warm" });
228
+ const [intro, invite] = greeting.split("\n\n");
229
+ expect(intro).toBe("Hey Bob, I'm Pax. Good to meet you.");
230
+ expect(invite).toBe(
231
+ "We can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.",
232
+ );
222
233
  });
223
234
 
224
- it("two-paragraph structure with blank line", () => {
225
- const greeting = getCannedFirstGreeting({
226
- ...base,
227
- tasks: ["code-building"],
228
- userName: "Bob",
229
- assistantName: "Pip",
230
- });
231
- const paragraphs = greeting.split("\n\n");
232
- expect(paragraphs.length).toBe(2);
235
+ it("energetic intro close is 'Let's see what you've got.', invite is energetic", () => {
236
+ const greeting = getCannedFirstGreeting({ ...base, tone: "energetic" });
237
+ const [intro, invite] = greeting.split("\n\n");
238
+ expect(intro).toBe("Hey Bob, I'm Pax. Let's see what you've got.");
239
+ expect(invite).toBe(
240
+ "We can jump straight into whatever you've got, or take a few minutes to just talk first. What sounds right?",
241
+ );
233
242
  });
234
243
 
235
- it("picks relevant tools for guess, not arbitrary selection order", () => {
236
- const greeting = getCannedFirstGreeting({
237
- ...base,
238
- tools: [
239
- "notion",
240
- "linear",
241
- "gmail",
242
- "google-calendar",
243
- "github",
244
- "apple-notes",
245
- ],
246
- tasks: ["code-building", "project-management"],
247
- userName: "Bob",
248
- assistantName: "Pip",
249
- });
250
- expect(greeting).toContain("You mentioned using GitHub and Linear");
251
- expect(greeting).not.toContain("Apple Notes");
252
- expect(greeting).not.toContain("Notion");
244
+ it("poetic intro close is empty, invite is poetic", () => {
245
+ const greeting = getCannedFirstGreeting({ ...base, tone: "poetic" });
246
+ const [intro, invite] = greeting.split("\n\n");
247
+ expect(intro).toBe("Hey Bob, I'm Pax.");
248
+ expect(invite).toBe(
249
+ "We can start with whatever's in front of you, or just talk for a bit first. Either way.",
250
+ );
253
251
  });
254
252
 
255
- it("falls back to no-tool prefix when no relevant tools match", () => {
256
- const greeting = getCannedFirstGreeting({
257
- ...base,
258
- tools: ["notion", "apple-notes"],
259
- tasks: ["code-building"],
260
- userName: "Bob",
261
- assistantName: "Pip",
253
+ it("each tone produces a distinct full greeting", () => {
254
+ const tones = ["grounded", "warm", "energetic", "poetic"];
255
+ const greetings = tones.map((tone) => {
256
+ return getCannedFirstGreeting({ ...base, tone });
262
257
  });
263
- expect(greeting).toContain("Probably shipping something or debugging");
264
- expect(greeting).not.toContain("You mentioned using");
258
+ const unique = new Set(greetings);
259
+ expect(unique.size).toBe(tones.length);
265
260
  });
266
261
 
267
- it("life admin guess uses verb phrase", () => {
268
- const greeting = getCannedFirstGreeting({
269
- ...base,
270
- tools: ["gmail", "google-calendar"],
271
- tasks: ["personal"],
272
- userName: "Bob",
273
- assistantName: "Pip",
262
+ it("each tone produces distinct invite text", () => {
263
+ const tones = ["grounded", "warm", "energetic", "poetic"];
264
+ const invites = tones.map((tone) => {
265
+ const greeting = getCannedFirstGreeting({ ...base, tone });
266
+ return greeting.split("\n\n")[1];
274
267
  });
275
- expect(greeting).toContain(
276
- "You mentioned using Gmail and Google Calendar",
277
- );
278
- expect(greeting).toContain("juggling travel, bills, or household stuff");
268
+ const unique = new Set(invites);
269
+ expect(unique.size).toBe(tones.length);
279
270
  });
271
+ });
280
272
 
281
- it("uses personalized greeting when assistantName present but no user name/tasks/tools", () => {
273
+ describe("tasks and tools fields are ignored", () => {
274
+ it("tasks do not appear in output", () => {
282
275
  const greeting = getCannedFirstGreeting({
283
- ...base,
284
- assistantName: "Pip",
276
+ tools: ["github", "linear"],
277
+ tasks: ["code-building", "project-management"],
278
+ tone: "grounded",
279
+ userName: "Alice",
280
+ assistantName: "Pax",
285
281
  });
286
- expect(greeting).toContain("I'm Pip");
287
- expect(greeting).not.toContain("no name, no memories");
282
+ expect(greeting).not.toContain("GitHub");
283
+ expect(greeting).not.toContain("Linear");
284
+ expect(greeting).not.toContain("code");
285
+ expect(greeting).not.toContain("shipping");
286
+ expect(greeting).not.toContain("You mentioned using");
287
+ expect(greeting).not.toContain("wear a lot of hats");
288
+ expect(greeting).not.toContain("Am I on the right track");
288
289
  });
289
290
 
290
- it("falls back to no-signal branch for unknown task types", () => {
291
+ it("tools do not appear in output", () => {
291
292
  const greeting = getCannedFirstGreeting({
292
- ...base,
293
- tasks: ["future-task-type"],
293
+ tools: ["gmail", "google-calendar", "slack", "notion"],
294
+ tasks: ["scheduling", "personal", "writing", "research"],
295
+ tone: "warm",
294
296
  userName: "Bob",
295
- assistantName: "Pip",
297
+ assistantName: "Remy",
296
298
  });
297
- expect(greeting).toContain("What's on your plate?");
298
- expect(greeting).not.toContain("\n\n\n");
299
+ expect(greeting).not.toContain("Gmail");
300
+ expect(greeting).not.toContain("Google Calendar");
301
+ expect(greeting).not.toContain("Slack");
302
+ expect(greeting).not.toContain("Notion");
303
+ expect(greeting).not.toContain("scheduling");
304
+ expect(greeting).not.toContain("personal");
299
305
  });
300
306
  });
301
307
  });
@@ -1,18 +1,17 @@
1
1
  /**
2
2
  * Mock Chrome extension test fixture.
3
3
  *
4
- * Opens a WebSocket to the runtime's `/v1/browser-relay` endpoint using a
5
- * caller-supplied JWT (so the upgrade handler registers the connection
6
- * under the guardianId encoded in the token), handles incoming
7
- * `host_browser_request` frames by calling a mock CDP proxy, and POSTs
4
+ * Subscribes to the runtime's `/events` SSE endpoint (registering in the
5
+ * client registry with `interfaceId: "chrome-extension"`), handles incoming
6
+ * `host_browser_request` events by calling a mock CDP proxy, and POSTs
8
7
  * the result back to `/v1/host-browser-result`.
9
8
  *
10
- * Used by e2e tests (PR 15/16) to exercise the full round-trip without
11
- * requiring a real Chrome browser or the real extension worker.
9
+ * Optionally opens a WebSocket to `/v1/browser-relay` for sending inbound
10
+ * frames (events, session-invalidation, keepalive) and for the WS result
11
+ * transport variant.
12
12
  *
13
- * The fixture is intentionally minimal it does not implement heartbeats
14
- * or reconnect logic. It only needs to carry host_browser_request frames
15
- * end-to-end.
13
+ * Used by e2e tests to exercise the full round-trip without requiring a
14
+ * real Chrome browser or the real extension worker.
16
15
  */
17
16
 
18
17
  // ── Types ───────────────────────────────────────────────────────────
@@ -73,13 +72,14 @@ export interface MockChromeExtensionOptions {
73
72
  * - "http" (default): POST to `/v1/host-browser-result`.
74
73
  * - "ws": send a `host_browser_result` frame back over the same
75
74
  * `/v1/browser-relay` WebSocket that delivered the request.
76
- *
77
- * Both transports are expected to be fully functional in the runtime.
78
- * The HTTP path is the legacy transport; the WS path was added so the
79
- * extension can avoid an extra round-trip through the cloud ingress
80
- * stack for each CDP command.
81
75
  */
82
76
  resultTransport?: "http" | "ws";
77
+ /**
78
+ * Separate JWT for SSE `/events` auth. When the primary `token` is a
79
+ * capability token (not a JWT), provide a real JWT here so the SSE
80
+ * endpoint accepts the connection.
81
+ */
82
+ sseToken?: string;
83
83
  }
84
84
 
85
85
  export interface MockChromeExtension {
@@ -172,9 +172,12 @@ export function createMockChromeExtension(
172
172
  const baseHttp = options.runtimeBaseUrl.replace(/\/$/, "");
173
173
  const wsBase = baseHttp.replace(/^http/i, "ws");
174
174
  const wsUrl = `${wsBase}/v1/browser-relay?token=${encodeURIComponent(options.token)}`;
175
+ const clientId = `mock-ext-${crypto.randomUUID()}`;
175
176
 
176
177
  let ws: WebSocket | null = null;
177
- let connected = false;
178
+ let wsConnected = false;
179
+ let sseAbort: AbortController | null = null;
180
+ let sseConnected = false;
178
181
  let handler = options.cdpHandler ?? defaultCdpHandler;
179
182
  const receivedRequests: HostBrowserRequestFrame[] = [];
180
183
  const receivedCancels: HostBrowserCancelFrame[] = [];
@@ -197,9 +200,6 @@ export function createMockChromeExtension(
197
200
  } finally {
198
201
  inFlight.delete(frame.requestId);
199
202
  }
200
- // If the request was aborted mid-flight, drop the result entirely
201
- // (mirroring the production dispatcher, which doesn't POST a result
202
- // for cancelled requests).
203
203
  if (abortCtl.signal.aborted) return;
204
204
 
205
205
  const body: HostBrowserResultBody = {
@@ -208,21 +208,12 @@ export function createMockChromeExtension(
208
208
  isError: result.isError,
209
209
  };
210
210
  if (resultTransport === "ws") {
211
- // Send the result back over the same `/v1/browser-relay` socket
212
- // that delivered the request. The runtime WS message handler
213
- // parses `host_browser_result` frames and resolves the pending
214
- // interaction via the same core resolver the HTTP endpoint uses.
215
211
  const sock = ws;
216
212
  if (sock && sock.readyState === WebSocket.OPEN) {
217
213
  try {
218
- sock.send(
219
- JSON.stringify({
220
- type: "host_browser_result",
221
- ...body,
222
- }),
223
- );
214
+ sock.send(JSON.stringify({ type: "host_browser_result", ...body }));
224
215
  } catch {
225
- // Best-effort — mirrors the HTTP POST failure mode.
216
+ // best-effort
226
217
  }
227
218
  }
228
219
  return;
@@ -236,14 +227,13 @@ export function createMockChromeExtension(
236
227
  },
237
228
  body: JSON.stringify(body),
238
229
  });
239
- // Consume the body so Bun doesn't leak the response handle.
240
230
  await res.body?.cancel();
241
231
  } catch {
242
- // Best-effort — if the runtime has torn down the server, the POST
243
- // will throw. Tests assert on proxy behaviour, not POST success.
232
+ // best-effort
244
233
  }
245
234
  }
246
235
 
236
+ /** Handle an incoming message (from SSE event or WS frame). */
247
237
  function handleMessage(raw: string): void {
248
238
  let parsed: unknown;
249
239
  try {
@@ -269,43 +259,92 @@ export function createMockChromeExtension(
269
259
  }
270
260
  return;
271
261
  }
272
- // Ignore any other frames.
262
+ }
263
+
264
+ /** Start SSE connection to /events — registers in client registry and
265
+ * receives outbound host_browser events from the event hub. */
266
+ async function startSse(): Promise<void> {
267
+ sseAbort = new AbortController();
268
+ const sseUrl = `${baseHttp}/v1/events`;
269
+ const res = await fetch(sseUrl, {
270
+ headers: {
271
+ Accept: "text/event-stream",
272
+ Authorization: `Bearer ${options.sseToken ?? options.token}`,
273
+ "X-Vellum-Client-Id": clientId,
274
+ "X-Vellum-Interface-Id": "chrome-extension",
275
+ },
276
+ signal: sseAbort.signal,
277
+ });
278
+ if (!res.ok || !res.body) {
279
+ throw new Error(`SSE connect failed: ${res.status}`);
280
+ }
281
+ sseConnected = true;
282
+
283
+ // Read SSE stream in the background
284
+ const reader = res.body.getReader();
285
+ const decoder = new TextDecoder();
286
+ let buffer = "";
287
+ void (async () => {
288
+ try {
289
+ while (true) {
290
+ const { done, value } = await reader.read();
291
+ if (done) break;
292
+ buffer += decoder.decode(value, { stream: true });
293
+ // Parse SSE frames: "data: ...\n\n"
294
+ const parts = buffer.split("\n\n");
295
+ buffer = parts.pop() ?? "";
296
+ for (const part of parts) {
297
+ const lines = part.split("\n");
298
+ for (const line of lines) {
299
+ if (line.startsWith("data: ")) {
300
+ const data = line.slice(6);
301
+ // SSE data is JSON-encoded AssistantEvent; extract
302
+ // the message field
303
+ try {
304
+ const event = JSON.parse(data) as {
305
+ message?: unknown;
306
+ };
307
+ if (event.message) {
308
+ handleMessage(JSON.stringify(event.message));
309
+ }
310
+ } catch {
311
+ // skip malformed SSE data
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+ } catch {
318
+ // SSE stream ended (abort or server shutdown)
319
+ } finally {
320
+ sseConnected = false;
321
+ }
322
+ })();
323
+ }
324
+
325
+ /** Start the optional WS connection for sending inbound frames. */
326
+ async function startWs(): Promise<void> {
327
+ const wsOptions: { headers?: Record<string, string> } = {};
328
+ if (options.extraHandshakeHeaders) {
329
+ wsOptions.headers = options.extraHandshakeHeaders;
330
+ }
331
+ ws = new WebSocket(wsUrl, wsOptions as unknown as string | string[]);
332
+ ws.addEventListener("open", () => {
333
+ wsConnected = true;
334
+ });
335
+ ws.addEventListener("close", () => {
336
+ wsConnected = false;
337
+ });
273
338
  }
274
339
 
275
340
  return {
276
341
  async start() {
277
- if (ws) return;
278
- // Bun's `WebSocket` constructor accepts a second-argument options
279
- // object with a `headers` field (a Bun-specific extension of the
280
- // standard WebSocket API). We forward `extraHandshakeHeaders`
281
- // through it so tests using service tokens can supply the
282
- // `x-guardian-id` fallback expected by `/v1/browser-relay`.
283
- //
284
- // We cast through `unknown` because the DOM `WebSocket` type only
285
- // knows about `(url, protocols)`. If this fixture is ever run in
286
- // an environment that isn't Bun, the options object would be
287
- // silently ignored — acceptable for a test fixture.
288
- const wsOptions: { headers?: Record<string, string> } = {};
289
- if (options.extraHandshakeHeaders) {
290
- wsOptions.headers = options.extraHandshakeHeaders;
291
- }
292
- ws = new WebSocket(wsUrl, wsOptions as unknown as string | string[]);
293
- ws.addEventListener("open", () => {
294
- connected = true;
295
- });
296
- ws.addEventListener("message", (ev: MessageEvent) => {
297
- const data = ev.data;
298
- if (typeof data === "string") {
299
- handleMessage(data);
300
- } else if (data instanceof ArrayBuffer) {
301
- handleMessage(new TextDecoder().decode(data));
302
- }
303
- });
304
- ws.addEventListener("close", () => {
305
- connected = false;
306
- });
342
+ await startSse();
343
+ await startWs();
307
344
  },
308
345
  async stop() {
346
+ sseAbort?.abort();
347
+ sseAbort = null;
309
348
  const sock = ws;
310
349
  ws = null;
311
350
  if (sock) {
@@ -322,10 +361,10 @@ export function createMockChromeExtension(
322
361
  },
323
362
  async waitForConnection(timeoutMs = 2000) {
324
363
  const deadline = Date.now() + timeoutMs;
325
- while (!connected) {
364
+ while (!sseConnected || !wsConnected) {
326
365
  if (Date.now() > deadline) {
327
366
  throw new Error(
328
- `mock-chrome-extension: timed out waiting for WebSocket OPEN after ${timeoutMs}ms`,
367
+ `mock-chrome-extension: timed out waiting for connection (SSE=${sseConnected}, WS=${wsConnected}) after ${timeoutMs}ms`,
329
368
  );
330
369
  }
331
370
  await new Promise((r) => setTimeout(r, 10));
@@ -341,9 +380,12 @@ export function createMockChromeExtension(
341
380
  handler = next;
342
381
  },
343
382
  forceDisconnect() {
383
+ sseAbort?.abort();
384
+ sseAbort = null;
385
+ sseConnected = false;
344
386
  const sock = ws;
345
387
  ws = null;
346
- connected = false;
388
+ wsConnected = false;
347
389
  if (sock) {
348
390
  try {
349
391
  sock.close(4000, "forced disconnect");