@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,26 +1,66 @@
1
- import { afterEach, describe, expect, test } from "bun:test";
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ // ── Module mocks ─────────────────────────────────────────────────────
4
+
5
+ mock.module("../util/logger.js", () => ({
6
+ getLogger: () =>
7
+ new Proxy({} as Record<string, unknown>, {
8
+ get: () => () => {},
9
+ }),
10
+ }));
11
+
12
+ /** Events published through the mock event hub. */
13
+ let publishedEvents: unknown[] = [];
14
+ let mockHasConnection = true;
15
+
16
+ mock.module("../runtime/assistant-event-hub.js", () => ({
17
+ assistantEventHub: {
18
+ publish: async (event: unknown, _options?: unknown) => {
19
+ publishedEvents.push(event);
20
+ },
21
+ getMostRecentClientByCapability: (cap: string) =>
22
+ cap === "host_browser" && mockHasConnection
23
+ ? {
24
+ type: "client",
25
+ clientId: "test-client",
26
+ interfaceId: "macos",
27
+ capabilities: ["host_browser"],
28
+ }
29
+ : undefined,
30
+ },
31
+ }));
32
+
33
+ mock.module("../runtime/assistant-event.js", () => ({
34
+ buildAssistantEvent: (message: unknown) => ({ message }),
35
+ }));
36
+
37
+ // ── Real imports (after mocks) ───────────────────────────────────────
2
38
 
3
39
  const { HostBrowserProxy } = await import("../daemon/host-browser-proxy.js");
4
40
 
41
+ /** Extract the ServerMessage payloads from published events. */
42
+ function getPublishedMessages(): unknown[] {
43
+ return publishedEvents.map((e) => (e as { message: unknown }).message);
44
+ }
45
+
46
+ // ── Tests ────────────────────────────────────────────────────────────
47
+
5
48
  describe("HostBrowserProxy", () => {
6
49
  let proxy: InstanceType<typeof HostBrowserProxy>;
7
- let sentMessages: unknown[];
8
- let sendToClient: (msg: unknown) => void;
9
50
 
10
- function setup(onInternalResolve?: (requestId: string) => void) {
11
- sentMessages = [];
12
- sendToClient = (msg: unknown) => sentMessages.push(msg);
13
- proxy = new HostBrowserProxy(sendToClient, onInternalResolve);
14
- }
51
+ beforeEach(() => {
52
+ HostBrowserProxy.reset();
53
+ publishedEvents = [];
54
+ mockHasConnection = true;
55
+ proxy = HostBrowserProxy.instance;
56
+ });
15
57
 
16
58
  afterEach(() => {
17
- proxy?.dispose();
59
+ HostBrowserProxy.reset();
18
60
  });
19
61
 
20
62
  describe("request/resolve lifecycle (happy path)", () => {
21
63
  test("sends host_browser_request and resolves with content", async () => {
22
- setup();
23
-
24
64
  const resultPromise = proxy.request(
25
65
  {
26
66
  cdpMethod: "Page.navigate",
@@ -29,9 +69,8 @@ describe("HostBrowserProxy", () => {
29
69
  "session-1",
30
70
  );
31
71
 
32
- // Verify the request was sent to the client
33
- expect(sentMessages).toHaveLength(1);
34
- const sent = sentMessages[0] as Record<string, unknown>;
72
+ expect(getPublishedMessages()).toHaveLength(1);
73
+ const sent = getPublishedMessages()[0] as Record<string, unknown>;
35
74
  expect(sent.type).toBe("host_browser_request");
36
75
  expect(sent.conversationId).toBe("session-1");
37
76
  expect(sent.cdpMethod).toBe("Page.navigate");
@@ -41,11 +80,7 @@ describe("HostBrowserProxy", () => {
41
80
  const requestId = sent.requestId as string;
42
81
  expect(proxy.hasPendingRequest(requestId)).toBe(true);
43
82
 
44
- // Simulate client response
45
- proxy.resolve(requestId, {
46
- content: "ok",
47
- isError: false,
48
- });
83
+ proxy.resolve(requestId, { content: "ok", isError: false });
49
84
 
50
85
  const result = await resultPromise;
51
86
  expect(result.content).toBe("ok");
@@ -54,8 +89,6 @@ describe("HostBrowserProxy", () => {
54
89
  });
55
90
 
56
91
  test("forwards cdpParams and cdpSessionId on the emitted envelope", async () => {
57
- setup();
58
-
59
92
  const resultPromise = proxy.request(
60
93
  {
61
94
  cdpMethod: "Runtime.evaluate",
@@ -65,8 +98,8 @@ describe("HostBrowserProxy", () => {
65
98
  "session-1",
66
99
  );
67
100
 
68
- expect(sentMessages).toHaveLength(1);
69
- const sent = sentMessages[0] as Record<string, unknown>;
101
+ expect(getPublishedMessages()).toHaveLength(1);
102
+ const sent = getPublishedMessages()[0] as Record<string, unknown>;
70
103
  expect(sent.type).toBe("host_browser_request");
71
104
  expect(sent.cdpMethod).toBe("Runtime.evaluate");
72
105
  expect(sent.cdpParams).toEqual({
@@ -75,8 +108,7 @@ describe("HostBrowserProxy", () => {
75
108
  });
76
109
  expect(sent.cdpSessionId).toBe("session-abc");
77
110
 
78
- const requestId = sent.requestId as string;
79
- proxy.resolve(requestId, {
111
+ proxy.resolve(sent.requestId as string, {
80
112
  content: "Example Domain",
81
113
  isError: false,
82
114
  });
@@ -85,17 +117,13 @@ describe("HostBrowserProxy", () => {
85
117
  });
86
118
 
87
119
  test("resolves error responses correctly", async () => {
88
- setup();
89
-
90
120
  const resultPromise = proxy.request(
91
121
  { cdpMethod: "Page.navigate", cdpParams: { url: "invalid://" } },
92
122
  "session-1",
93
123
  );
94
124
 
95
- const sent = sentMessages[0] as Record<string, unknown>;
96
- const requestId = sent.requestId as string;
97
-
98
- proxy.resolve(requestId, {
125
+ const sent = getPublishedMessages()[0] as Record<string, unknown>;
126
+ proxy.resolve(sent.requestId as string, {
99
127
  content: "Navigation failed",
100
128
  isError: true,
101
129
  });
@@ -108,14 +136,12 @@ describe("HostBrowserProxy", () => {
108
136
 
109
137
  describe("pending tracking", () => {
110
138
  test("hasPendingRequest returns true after request and false after resolve", async () => {
111
- setup();
112
-
113
139
  const resultPromise = proxy.request(
114
140
  { cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
115
141
  "session-1",
116
142
  );
117
143
 
118
- const sent = sentMessages[0] as Record<string, unknown>;
144
+ const sent = getPublishedMessages()[0] as Record<string, unknown>;
119
145
  const requestId = sent.requestId as string;
120
146
  expect(proxy.hasPendingRequest(requestId)).toBe(true);
121
147
 
@@ -128,38 +154,30 @@ describe("HostBrowserProxy", () => {
128
154
 
129
155
  describe("timeout", () => {
130
156
  test("resolves with timeout error when proxy timeout fires", async () => {
131
- const resolvedIds: string[] = [];
132
- setup((id) => resolvedIds.push(id));
133
-
134
157
  const resultPromise = proxy.request(
135
158
  {
136
159
  cdpMethod: "Page.navigate",
137
160
  cdpParams: { url: "https://slow.test" },
138
- // Sub-second timeout to trigger the timer quickly.
139
161
  timeout_seconds: 0.01,
140
162
  },
141
163
  "session-1",
142
164
  );
143
165
 
144
- const sent = sentMessages[0] as Record<string, unknown>;
166
+ const sent = getPublishedMessages()[0] as Record<string, unknown>;
145
167
  const requestId = sent.requestId as string;
146
168
  expect(proxy.hasPendingRequest(requestId)).toBe(true);
147
169
 
148
- // Wait long enough for the timer (10ms) to fire.
149
170
  await new Promise((r) => setTimeout(r, 50));
150
171
 
151
172
  const result = await resultPromise;
152
173
  expect(result.isError).toBe(true);
153
174
  expect(result.content).toContain("Host browser proxy timed out");
154
175
  expect(proxy.hasPendingRequest(requestId)).toBe(false);
155
- expect(resolvedIds).toEqual([requestId]);
156
176
  });
157
177
  });
158
178
 
159
179
  describe("abort signal", () => {
160
180
  test("returns immediately if signal already aborted", async () => {
161
- setup();
162
-
163
181
  const controller = new AbortController();
164
182
  controller.abort();
165
183
 
@@ -171,13 +189,10 @@ describe("HostBrowserProxy", () => {
171
189
 
172
190
  expect(result.content).toBe("Aborted");
173
191
  expect(result.isError).toBe(true);
174
- expect(sentMessages).toHaveLength(0); // No envelope emitted.
192
+ expect(getPublishedMessages()).toHaveLength(0);
175
193
  });
176
194
 
177
195
  test("mid-flight abort resolves with Aborted and emits host_browser_cancel", async () => {
178
- const resolvedIds: string[] = [];
179
- setup((id) => resolvedIds.push(id));
180
-
181
196
  const controller = new AbortController();
182
197
  const resultPromise = proxy.request(
183
198
  { cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
@@ -185,7 +200,7 @@ describe("HostBrowserProxy", () => {
185
200
  controller.signal,
186
201
  );
187
202
 
188
- const sent = sentMessages[0] as Record<string, unknown>;
203
+ const sent = getPublishedMessages()[0] as Record<string, unknown>;
189
204
  const requestId = sent.requestId as string;
190
205
  expect(proxy.hasPendingRequest(requestId)).toBe(true);
191
206
 
@@ -196,68 +211,28 @@ describe("HostBrowserProxy", () => {
196
211
  expect(result.isError).toBe(true);
197
212
  expect(proxy.hasPendingRequest(requestId)).toBe(false);
198
213
 
199
- // Second message should be the cancel envelope.
200
- expect(sentMessages).toHaveLength(2);
201
- const cancelMsg = sentMessages[1] as Record<string, unknown>;
214
+ // Cancel envelope should have been sent.
215
+ expect(getPublishedMessages()).toHaveLength(2);
216
+ const cancelMsg = getPublishedMessages()[1] as Record<string, unknown>;
202
217
  expect(cancelMsg.type).toBe("host_browser_cancel");
203
218
  expect(cancelMsg.requestId).toBe(requestId);
204
-
205
- // onInternalResolve should have been invoked.
206
- expect(resolvedIds).toEqual([requestId]);
207
219
  });
208
220
  });
209
221
 
210
222
  describe("isAvailable", () => {
211
- test("returns false by default (no client connected)", () => {
212
- setup();
213
- expect(proxy.isAvailable()).toBe(false);
214
- });
215
-
216
- test("returns true after updateSender with clientConnected=true", () => {
217
- setup();
218
- proxy.updateSender(sendToClient, true);
223
+ test("returns true when a connection exists in the registry", () => {
224
+ mockHasConnection = true;
219
225
  expect(proxy.isAvailable()).toBe(true);
220
226
  });
221
227
 
222
- test("returns false after updateSender with clientConnected=false", () => {
223
- setup();
224
- proxy.updateSender(sendToClient, true);
225
- expect(proxy.isAvailable()).toBe(true);
226
- proxy.updateSender(sendToClient, false);
228
+ test("returns false when no connection exists", () => {
229
+ mockHasConnection = false;
227
230
  expect(proxy.isAvailable()).toBe(false);
228
231
  });
229
232
  });
230
233
 
231
- describe("updateSender", () => {
232
- test("uses updated sender for new requests", async () => {
233
- setup();
234
-
235
- const newMessages: unknown[] = [];
236
- proxy.updateSender((msg) => newMessages.push(msg), true);
237
-
238
- const resultPromise = proxy.request(
239
- { cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
240
- "session-1",
241
- );
242
-
243
- expect(sentMessages).toHaveLength(0); // Old sender not used.
244
- expect(newMessages).toHaveLength(1); // New sender used.
245
-
246
- const sent = newMessages[0] as Record<string, unknown>;
247
- proxy.resolve(sent.requestId as string, {
248
- content: "ok",
249
- isError: false,
250
- });
251
-
252
- await resultPromise;
253
- });
254
- });
255
-
256
234
  describe("dispose", () => {
257
- test("rejects all pending requests, emits cancels, invokes onInternalResolve", async () => {
258
- const resolvedIds: string[] = [];
259
- setup((id) => resolvedIds.push(id));
260
-
235
+ test("rejects all pending requests and emits cancels", async () => {
261
236
  const p1 = proxy.request(
262
237
  { cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
263
238
  "session-1",
@@ -266,63 +241,70 @@ describe("HostBrowserProxy", () => {
266
241
  { cdpMethod: "Page.navigate", cdpParams: { url: "https://b.test" } },
267
242
  "session-1",
268
243
  );
269
- // Attach rejection handlers immediately so Bun doesn't flag the
270
- // promises as unhandled before the awaited assertions run.
271
244
  const p1Swallowed = p1.catch(() => {});
272
245
  const p2Swallowed = p2.catch(() => {});
273
246
 
274
- const requestIds = (sentMessages as Array<Record<string, unknown>>).map(
275
- (m) => m.requestId as string,
276
- );
247
+ const requestIds = (
248
+ getPublishedMessages() as Array<Record<string, unknown>>
249
+ ).map((m) => m.requestId as string);
277
250
  expect(requestIds).toHaveLength(2);
278
- expect(proxy.hasPendingRequest(requestIds[0]!)).toBe(true);
279
- expect(proxy.hasPendingRequest(requestIds[1]!)).toBe(true);
280
251
 
281
252
  proxy.dispose();
282
253
 
283
- // Both pending requests should no longer be tracked.
284
254
  expect(proxy.hasPendingRequest(requestIds[0]!)).toBe(false);
285
255
  expect(proxy.hasPendingRequest(requestIds[1]!)).toBe(false);
286
256
 
287
- // Both promises should reject with AssistantError message.
288
257
  await expect(p1).rejects.toThrow("Host browser proxy disposed");
289
258
  await expect(p2).rejects.toThrow("Host browser proxy disposed");
290
- // Drain the swallowed copies so the unhandled-rejection guard clears.
291
259
  await p1Swallowed;
292
260
  await p2Swallowed;
293
261
 
294
- // After the 2 request messages, dispose should have sent 2 cancel messages.
295
- const cancelMessages = sentMessages
262
+ const cancelMessages = getPublishedMessages()
296
263
  .slice(2)
297
264
  .filter(
298
265
  (m) => (m as Record<string, unknown>).type === "host_browser_cancel",
299
266
  ) as Array<Record<string, unknown>>;
300
267
  expect(cancelMessages).toHaveLength(2);
301
- expect(cancelMessages.map((m) => m.requestId)).toContain(requestIds[0]);
302
- expect(cancelMessages.map((m) => m.requestId)).toContain(requestIds[1]);
303
-
304
- // onInternalResolve fired for each pending request on dispose.
305
- expect(resolvedIds).toHaveLength(2);
306
- expect(resolvedIds).toContain(requestIds[0]!);
307
- expect(resolvedIds).toContain(requestIds[1]!);
308
268
  });
309
269
  });
310
270
 
311
271
  describe("resolve with unknown requestId", () => {
312
272
  test("silently ignores unknown requestId", () => {
313
- setup();
314
- // Should not throw.
315
- proxy.resolve("nonexistent", {
316
- content: "stale",
317
- isError: false,
318
- });
273
+ proxy.resolve("nonexistent", { content: "stale", isError: false });
274
+ });
275
+ });
276
+
277
+ describe("send failure", () => {
278
+ test("rejects when no connection exists at send time", async () => {
279
+ mockHasConnection = false;
280
+
281
+ const resultPromise = proxy.request(
282
+ { cdpMethod: "Page.navigate", cdpParams: { url: "https://x.test" } },
283
+ "session-1",
284
+ );
285
+
286
+ await expect(resultPromise).rejects.toThrow(
287
+ "no active extension connection",
288
+ );
289
+ });
290
+ });
291
+
292
+ describe("singleton", () => {
293
+ test("instance always returns the same proxy", () => {
294
+ const a = HostBrowserProxy.instance;
295
+ const b = HostBrowserProxy.instance;
296
+ expect(a).toBe(b);
297
+ });
298
+
299
+ test("reset() clears the singleton", () => {
300
+ const before = HostBrowserProxy.instance;
301
+ HostBrowserProxy.reset();
302
+ const after = HostBrowserProxy.instance;
303
+ expect(before).not.toBe(after);
319
304
  });
320
305
  });
321
306
 
322
307
  describe("abort listener lifecycle", () => {
323
- // Helper that wraps an AbortSignal to observe add/removeEventListener
324
- // invocations without tripping over tsc's strict overload matching on
325
- // AbortSignal itself.
326
308
  type Spied = {
327
309
  signal: AbortSignal;
328
310
  addCalls: string[];
@@ -334,17 +316,11 @@ describe("HostBrowserProxy", () => {
334
316
  const s = source as any;
335
317
  const origAdd = source.addEventListener.bind(source);
336
318
  const origRemove = source.removeEventListener.bind(source);
337
- s.addEventListener = (
338
- type: string,
339
- ...rest: any[]
340
- ) => {
319
+ s.addEventListener = (type: string, ...rest: any[]) => {
341
320
  addCalls.push(type);
342
321
  return (origAdd as any)(type, ...rest);
343
322
  };
344
- s.removeEventListener = (
345
- type: string,
346
- ...rest: any[]
347
- ) => {
323
+ s.removeEventListener = (type: string, ...rest: any[]) => {
348
324
  removeCalls.push(type);
349
325
  return (origRemove as any)(type, ...rest);
350
326
  };
@@ -352,7 +328,6 @@ describe("HostBrowserProxy", () => {
352
328
  }
353
329
 
354
330
  test("removes abort listener from signal after resolve completes", async () => {
355
- setup();
356
331
  const controller = new AbortController();
357
332
  const spy = spySignal(controller.signal);
358
333
 
@@ -365,22 +340,18 @@ describe("HostBrowserProxy", () => {
365
340
  expect(spy.addCalls).toEqual(["abort"]);
366
341
  expect(spy.removeCalls).toEqual([]);
367
342
 
368
- const requestId = (sentMessages[0] as Record<string, unknown>)
343
+ const requestId = (getPublishedMessages()[0] as Record<string, unknown>)
369
344
  .requestId as string;
370
345
  proxy.resolve(requestId, { content: "ok", isError: false });
371
346
  await resultPromise;
372
347
 
373
- // Listener is detached after normal completion.
374
348
  expect(spy.removeCalls).toEqual(["abort"]);
375
349
 
376
- // Subsequent aborts are harmless no-ops (no side effects on the proxy).
377
350
  controller.abort();
378
- // No additional emitted envelopes from the late abort.
379
- expect(sentMessages).toHaveLength(1);
351
+ expect(getPublishedMessages()).toHaveLength(1);
380
352
  });
381
353
 
382
354
  test("removes abort listener from signal after dispose", () => {
383
- setup();
384
355
  const controller = new AbortController();
385
356
  const spy = spySignal(controller.signal);
386
357
 
@@ -389,56 +360,11 @@ describe("HostBrowserProxy", () => {
389
360
  "session-1",
390
361
  spy.signal,
391
362
  );
392
- p.catch(() => {}); // Swallow expected rejection.
363
+ p.catch(() => {});
393
364
 
394
365
  proxy.dispose();
395
366
 
396
367
  expect(spy.removeCalls).toEqual(["abort"]);
397
368
  });
398
369
  });
399
-
400
- describe("sender throws synchronously", () => {
401
- test("rejects the promise, clears pending state and timer, invokes onInternalResolve", async () => {
402
- const resolvedIds: string[] = [];
403
- sentMessages = [];
404
- sendToClient = () => {
405
- throw new Error("transport down");
406
- };
407
- proxy = new HostBrowserProxy(sendToClient, (id) => resolvedIds.push(id));
408
-
409
- // request() synchronously calls sendToClient inside the Promise
410
- // executor. A throw there surfaces as a rejected promise.
411
- const resultPromise = proxy.request(
412
- { cdpMethod: "Page.navigate", cdpParams: { url: "https://x.test" } },
413
- "session-1",
414
- );
415
-
416
- await expect(resultPromise).rejects.toThrow("transport down");
417
-
418
- // No entries should have leaked into the pending map.
419
- // (We can't assert against a specific requestId because the sender
420
- // threw before any message was observed, so there's nothing to read
421
- // the id from. We can instead assert the internal resolve fired once
422
- // and that no pending entries remain for any id we issue next.)
423
- expect(resolvedIds).toHaveLength(1);
424
-
425
- // Issue a new request on a fresh (non-throwing) sender and verify
426
- // the proxy is still functional — no stale timers or bookkeeping
427
- // from the failed request.
428
- sentMessages = [];
429
- proxy.updateSender((msg) => sentMessages.push(msg), true);
430
- const okPromise = proxy.request(
431
- { cdpMethod: "Page.reload" },
432
- "session-1",
433
- );
434
- expect(sentMessages).toHaveLength(1);
435
- const okRequestId = (sentMessages[0] as Record<string, unknown>)
436
- .requestId as string;
437
- expect(proxy.hasPendingRequest(okRequestId)).toBe(true);
438
- proxy.resolve(okRequestId, { content: "reloaded", isError: false });
439
- const okResult = await okPromise;
440
- expect(okResult.content).toBe("reloaded");
441
- expect(okResult.isError).toBe(false);
442
- });
443
- });
444
370
  });
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * Unit tests for the /v1/host-browser-result route handler.
3
3
  *
4
- * Tests handleHostBrowserResult directly via RouteHandlerArgs with a
5
- * stub Conversation whose resolveHostBrowser method records calls.
4
+ * Resolution goes through HostBrowserProxy.instance (singleton). The
5
+ * mock below controls the proxy's pending request map and resolve spy.
6
6
  */
7
7
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
8
8
 
9
- import type { Conversation } from "../daemon/conversation.js";
10
-
11
9
  // ── Module mocks ─────────────────────────────────────────────────────
12
10
 
13
11
  mock.module("../config/env.js", () => ({
@@ -15,10 +13,36 @@ mock.module("../config/env.js", () => ({
15
13
  hasUngatedHttpAuthDisabled: () => false,
16
14
  }));
17
15
 
16
+ interface ResolveCall {
17
+ requestId: string;
18
+ response: { content: string; isError: boolean };
19
+ }
20
+
21
+ const resolveSpy: ResolveCall[] = [];
22
+ const pendingRequests = new Set<string>();
23
+
24
+ mock.module("../daemon/host-browser-proxy.js", () => ({
25
+ HostBrowserProxy: {
26
+ get instance() {
27
+ return {
28
+ hasPendingRequest(requestId: string) {
29
+ return pendingRequests.has(requestId);
30
+ },
31
+ resolve(
32
+ requestId: string,
33
+ response: { content: string; isError: boolean },
34
+ ) {
35
+ pendingRequests.delete(requestId);
36
+ resolveSpy.push({ requestId, response });
37
+ },
38
+ };
39
+ },
40
+ },
41
+ }));
42
+
18
43
  // ── Real imports (after mocks) ───────────────────────────────────────
19
44
 
20
- import * as pendingInteractions from "../runtime/pending-interactions.js";
21
- import { BadRequestError, ConflictError, NotFoundError } from "../runtime/routes/errors.js";
45
+ import { BadRequestError, NotFoundError } from "../runtime/routes/errors.js";
22
46
  import { ROUTES } from "../runtime/routes/host-browser-routes.js";
23
47
 
24
48
  afterAll(() => {
@@ -29,53 +53,28 @@ const handleHostBrowserResult = ROUTES.find(
29
53
  (r) => r.endpoint === "host-browser-result",
30
54
  )!.handler;
31
55
 
32
- // ── Helpers ──────────────────────────────────────────────────────────
33
-
34
- interface ResolveHostBrowserCall {
35
- requestId: string;
36
- response: { content: string; isError: boolean };
37
- }
38
-
39
- function makeStubConversation(spy: ResolveHostBrowserCall[]): Conversation {
40
- return {
41
- resolveHostBrowser(
42
- requestId: string,
43
- response: { content: string; isError: boolean },
44
- ) {
45
- spy.push({ requestId, response });
46
- },
47
- } as unknown as Conversation;
48
- }
49
-
50
56
  // ── Tests ────────────────────────────────────────────────────────────
51
57
 
52
58
  describe("handleHostBrowserResult", () => {
53
59
  beforeEach(() => {
54
- pendingInteractions.clear();
60
+ pendingRequests.clear();
61
+ resolveSpy.length = 0;
55
62
  });
56
63
 
57
- test("happy path: resolves a pending host_browser interaction", async () => {
58
- const spy: ResolveHostBrowserCall[] = [];
59
- const conversation = makeStubConversation(spy);
64
+ test("happy path: resolves a pending host_browser request via singleton", async () => {
60
65
  const requestId = "browser-req-happy";
61
-
62
- pendingInteractions.register(requestId, {
63
- conversation,
64
- conversationId: "conv-1",
65
- kind: "host_browser",
66
- });
66
+ pendingRequests.add(requestId);
67
67
 
68
68
  const result = await handleHostBrowserResult({
69
69
  body: { requestId, content: "ok", isError: false },
70
70
  });
71
71
 
72
72
  expect(result).toEqual({ accepted: true });
73
- expect(spy).toHaveLength(1);
74
- expect(spy[0].requestId).toBe(requestId);
75
- expect(spy[0].response).toEqual({ content: "ok", isError: false });
73
+ expect(resolveSpy).toHaveLength(1);
74
+ expect(resolveSpy[0].requestId).toBe(requestId);
75
+ expect(resolveSpy[0].response).toEqual({ content: "ok", isError: false });
76
76
 
77
- // Pending interaction should be consumed
78
- expect(pendingInteractions.get(requestId)).toBeUndefined();
77
+ expect(pendingRequests.has(requestId)).toBe(false);
79
78
  });
80
79
 
81
80
  test("missing body: throws BadRequestError", () => {
@@ -83,9 +82,9 @@ describe("handleHostBrowserResult", () => {
83
82
  });
84
83
 
85
84
  test("missing requestId: throws BadRequestError", () => {
86
- expect(() =>
87
- handleHostBrowserResult({ body: { content: "x" } }),
88
- ).toThrow(BadRequestError);
85
+ expect(() => handleHostBrowserResult({ body: { content: "x" } })).toThrow(
86
+ BadRequestError,
87
+ );
89
88
  });
90
89
 
91
90
  test("unknown requestId: throws NotFoundError", () => {
@@ -100,44 +99,15 @@ describe("handleHostBrowserResult", () => {
100
99
  ).toThrow(NotFoundError);
101
100
  });
102
101
 
103
- test("wrong kind: throws ConflictError with mismatch message", () => {
104
- const spy: ResolveHostBrowserCall[] = [];
105
- const conversation = makeStubConversation(spy);
106
- const requestId = "browser-req-wrong-kind";
107
-
108
- pendingInteractions.register(requestId, {
109
- conversation,
110
- conversationId: "conv-1",
111
- kind: "host_bash",
112
- });
113
-
114
- expect(() =>
115
- handleHostBrowserResult({
116
- body: { requestId, content: "x", isError: false },
117
- }),
118
- ).toThrow(ConflictError);
119
-
120
- // Pending interaction should NOT have been consumed
121
- expect(pendingInteractions.get(requestId)).toBeDefined();
122
- expect(spy).toHaveLength(0);
123
- });
124
-
125
102
  test("defaults: missing content/isError default to '' and false", async () => {
126
- const spy: ResolveHostBrowserCall[] = [];
127
- const conversation = makeStubConversation(spy);
128
103
  const requestId = "browser-req-defaults";
129
-
130
- pendingInteractions.register(requestId, {
131
- conversation,
132
- conversationId: "conv-1",
133
- kind: "host_browser",
134
- });
104
+ pendingRequests.add(requestId);
135
105
 
136
106
  const result = await handleHostBrowserResult({ body: { requestId } });
137
107
 
138
108
  expect(result).toEqual({ accepted: true });
139
- expect(spy).toHaveLength(1);
140
- expect(spy[0].requestId).toBe(requestId);
141
- expect(spy[0].response).toEqual({ content: "", isError: false });
109
+ expect(resolveSpy).toHaveLength(1);
110
+ expect(resolveSpy[0].requestId).toBe(requestId);
111
+ expect(resolveSpy[0].response).toEqual({ content: "", isError: false });
142
112
  });
143
113
  });