@vellumai/assistant 0.6.3 → 0.6.4

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 (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -4,6 +4,7 @@ import { isAbsolute, join, relative, resolve } from "node:path";
4
4
  import { getLogger } from "../../util/logger.js";
5
5
  import { getDataDir } from "../../util/platform.js";
6
6
  import { authSessionCache } from "./auth-cache.js";
7
+ import type { CdpClientKind } from "./cdp-client/types.js";
7
8
  import type { ExtractedCredential } from "./network-recording-types.js";
8
9
  import { importPlaywright } from "./runtime-check.js";
9
10
 
@@ -210,6 +211,16 @@ class BrowserManager {
210
211
  * resulting backendNodeId.
211
212
  */
212
213
  private snapshotBackendNodeMaps = new Map<string, Map<string, number>>();
214
+ /**
215
+ * The CDP backend kind that a conversation resolved to on an earlier
216
+ * tool call. When a subsequent browser_* call arrives with
217
+ * `browser_mode: "auto"`, the factory is pinned to this kind so
218
+ * navigate and screenshot cannot land on different Chromes within
219
+ * the same conversation. Explicit non-auto modes overwrite the entry.
220
+ * Cleared when the conversation's page is closed or all pages are
221
+ * closed.
222
+ */
223
+ private preferredBackendKinds = new Map<string, CdpClientKind>();
213
224
  private browserCdpSession: CDPSession | null = null;
214
225
  private browserWindowId: number | null = null;
215
226
  private interactiveModeSessions = new Set<string>();
@@ -444,6 +455,7 @@ class BrowserManager {
444
455
  this.pages.delete(conversationId);
445
456
  this.rawPages.delete(conversationId);
446
457
  this.snapshotBackendNodeMaps.delete(conversationId);
458
+ this.preferredBackendKinds.delete(conversationId);
447
459
  this.downloads.delete(conversationId);
448
460
  // Reject any pending download waiters
449
461
  const pending = this.pendingDownloads.get(conversationId);
@@ -477,6 +489,7 @@ class BrowserManager {
477
489
  this.pages.clear();
478
490
  this.rawPages.clear();
479
491
  this.snapshotBackendNodeMaps.clear();
492
+ this.preferredBackendKinds.clear();
480
493
  this.downloads.clear();
481
494
  for (const pending of this.pendingDownloads.values()) {
482
495
  for (const waiter of pending) waiter.reject(new Error("Browser closed"));
@@ -532,6 +545,38 @@ class BrowserManager {
532
545
  }
533
546
  }
534
547
 
548
+ /**
549
+ * Look up the backend kind this conversation has been using. Returns
550
+ * `null` before the first successful CDP send or after an explicit
551
+ * teardown. Callers should pass the result to the factory as a
552
+ * pinned mode so `auto`-mode calls stay consistent across tool
553
+ * invocations.
554
+ */
555
+ getPreferredBackendKind(conversationId: string): CdpClientKind | null {
556
+ return this.preferredBackendKinds.get(conversationId) ?? null;
557
+ }
558
+
559
+ /**
560
+ * Record the backend kind that a tool call resolved to. Overwrites
561
+ * any prior value so an explicit `browser_mode` override on one call
562
+ * becomes the default for subsequent `auto`-mode calls.
563
+ */
564
+ setPreferredBackendKind(
565
+ conversationId: string,
566
+ kind: CdpClientKind,
567
+ ): void {
568
+ this.preferredBackendKinds.set(conversationId, kind);
569
+ }
570
+
571
+ /**
572
+ * Drop the remembered backend kind for a conversation. Called on
573
+ * explicit teardown (browser_close, closeAllPages) so the next call
574
+ * re-runs the auto priority list.
575
+ */
576
+ clearPreferredBackendKind(conversationId: string): void {
577
+ this.preferredBackendKinds.delete(conversationId);
578
+ }
579
+
535
580
  /**
536
581
  * Store the backendNodeId lookup produced by the AX-tree-based
537
582
  * `executeBrowserSnapshot`. Callers overwrite any prior map for the
@@ -0,0 +1,12 @@
1
+ import type { BrowserMode } from "./cdp-client/types.js";
2
+
3
+ /**
4
+ * Canonical browser-mode identifiers shared across parsing and status
5
+ * reporting paths.
6
+ */
7
+ export const BROWSER_MODE = {
8
+ AUTO: "auto",
9
+ EXTENSION: "extension",
10
+ CDP_INSPECT: "cdp-inspect",
11
+ LOCAL: "local",
12
+ } as const satisfies Record<string, BrowserMode>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Normalization helper for the `browser_mode` tool input parameter.
3
+ *
4
+ * Canonical values map directly to {@link CdpClientKind}:
5
+ * - `auto` -- let the factory pick the best backend (default)
6
+ * - `extension` -- force the Chrome extension transport
7
+ * - `cdp-inspect` -- force the CDP inspect/debugger transport
8
+ * - `local` -- force the Playwright-managed local browser
9
+ *
10
+ * Aliases are accepted and normalized to their canonical form:
11
+ * - `cdp-debugger` -> `cdp-inspect`
12
+ * - `playwright` -> `local`
13
+ */
14
+
15
+ import { BROWSER_MODE } from "./browser-mode-constants.js";
16
+ import type { BrowserMode } from "./cdp-client/types.js";
17
+
18
+ /** Canonical browser mode values. Re-exported from cdp-client/types. */
19
+ export type { BrowserMode } from "./cdp-client/types.js";
20
+
21
+ /** All accepted values (canonical + aliases). */
22
+ const ALIAS_MAP: Record<string, BrowserMode> = {
23
+ [BROWSER_MODE.AUTO]: BROWSER_MODE.AUTO,
24
+ [BROWSER_MODE.EXTENSION]: BROWSER_MODE.EXTENSION,
25
+ [BROWSER_MODE.CDP_INSPECT]: BROWSER_MODE.CDP_INSPECT,
26
+ "cdp-debugger": BROWSER_MODE.CDP_INSPECT,
27
+ [BROWSER_MODE.LOCAL]: BROWSER_MODE.LOCAL,
28
+ playwright: BROWSER_MODE.LOCAL,
29
+ };
30
+
31
+ /** Ordered list of accepted values for error messages. */
32
+ const ACCEPTED_VALUES = Object.keys(ALIAS_MAP);
33
+
34
+ /**
35
+ * Human-readable alias mapping for error messages.
36
+ * Only includes entries where the alias differs from the canonical value.
37
+ */
38
+ const ALIAS_DISPLAY: Record<string, string> = {
39
+ "cdp-debugger": "cdp-inspect",
40
+ playwright: "local",
41
+ };
42
+
43
+ export interface NormalizeBrowserModeResult {
44
+ /** The normalized canonical mode. */
45
+ mode: BrowserMode;
46
+ }
47
+
48
+ export interface NormalizeBrowserModeError {
49
+ /** Deterministic error message describing the invalid value. */
50
+ error: string;
51
+ }
52
+
53
+ /**
54
+ * Normalize a raw `browser_mode` input value to a canonical {@link BrowserMode}.
55
+ *
56
+ * - `undefined` / `null` / empty string -> `{ mode: "auto" }`
57
+ * - Valid canonical value or alias -> `{ mode: <canonical> }`
58
+ * - Invalid value -> `{ error: "..." }` with accepted list and alias mapping
59
+ */
60
+ export function normalizeBrowserMode(
61
+ raw: unknown,
62
+ ): NormalizeBrowserModeResult | NormalizeBrowserModeError {
63
+ if (raw === undefined || raw === null || raw === "") {
64
+ return { mode: BROWSER_MODE.AUTO };
65
+ }
66
+
67
+ if (typeof raw !== "string") {
68
+ return buildError(String(raw));
69
+ }
70
+
71
+ const lower = raw.toLowerCase().trim();
72
+ const canonical = ALIAS_MAP[lower];
73
+
74
+ if (canonical !== undefined) {
75
+ return { mode: canonical };
76
+ }
77
+
78
+ return buildError(raw);
79
+ }
80
+
81
+ function buildError(value: string): NormalizeBrowserModeError {
82
+ const aliasHints = Object.entries(ALIAS_DISPLAY)
83
+ .map(([alias, canonical]) => `${alias}->${canonical}`)
84
+ .join(", ");
85
+
86
+ return {
87
+ error:
88
+ `Invalid browser_mode "${value}". ` +
89
+ `Accepted values: ${ACCEPTED_VALUES.join(", ")}. ` +
90
+ `Aliases: ${aliasHints}.`,
91
+ };
92
+ }
@@ -0,0 +1,33 @@
1
+ import { BROWSER_MODE } from "./browser-mode-constants.js";
2
+ import { DEVTOOLS_DISCOVERY_CODE } from "./cdp-client/cdp-inspect/discovery.js";
3
+
4
+ export const BROWSER_STATUS_MODE = {
5
+ EXTENSION: BROWSER_MODE.EXTENSION,
6
+ CDP_INSPECT: BROWSER_MODE.CDP_INSPECT,
7
+ LOCAL: BROWSER_MODE.LOCAL,
8
+ } as const;
9
+
10
+ export type BrowserStatusMode =
11
+ (typeof BROWSER_STATUS_MODE)[keyof typeof BROWSER_STATUS_MODE];
12
+
13
+ export const BROWSER_STATUS_MODES: readonly BrowserStatusMode[] = [
14
+ BROWSER_STATUS_MODE.EXTENSION,
15
+ BROWSER_STATUS_MODE.CDP_INSPECT,
16
+ BROWSER_STATUS_MODE.LOCAL,
17
+ ] as const;
18
+
19
+ export const BROWSER_STATUS_INPUT_FIELD = {
20
+ CHECK_LOCAL_LAUNCH: "check_local_launch",
21
+ } as const;
22
+
23
+ export const EXTENSION_STATUS_ERROR_MARKER = {
24
+ UNAUTHORIZED_ORIGIN: "unauthorized_origin",
25
+ NATIVE_MESSAGING_HOST: "native messaging host",
26
+ HTTP_401: "401",
27
+ } as const;
28
+
29
+ export const CDP_INSPECT_STATUS_DISCOVERY_CODE = {
30
+ NO_TARGETS: DEVTOOLS_DISCOVERY_CODE.NO_TARGETS,
31
+ INVALID_RESPONSE: DEVTOOLS_DISCOVERY_CODE.INVALID_RESPONSE,
32
+ WS_FALLBACK_FAILED: DEVTOOLS_DISCOVERY_CODE.WS_FALLBACK_FAILED,
33
+ } as const;
@@ -16,6 +16,7 @@ const { CdpInspectClient, createCdpInspectClient } =
16
16
  await import("../cdp-inspect-client.js");
17
17
  const { CdpError } = await import("../errors.js");
18
18
  const { CdpWsTransportError } = await import("../cdp-inspect/ws-transport.js");
19
+ const { DevToolsDiscoveryError } = await import("../cdp-inspect/discovery.js");
19
20
 
20
21
  type CdpInspectClientInstance = InstanceType<typeof CdpInspectClient>;
21
22
 
@@ -868,3 +869,395 @@ describe("CdpInspectClient", () => {
868
869
  expect(harness.attachCallCount()).toBe(1);
869
870
  });
870
871
  });
872
+
873
+ // ---------------------------------------------------------------------------
874
+ // WS-only fallback — HTTP discovery absent, WS fallback succeeds.
875
+ // ---------------------------------------------------------------------------
876
+
877
+ describe("CdpInspectClient — WS-only fallback", () => {
878
+ test("falls back to WS when HTTP discovery returns invalid_response", async () => {
879
+ const sends: Array<{
880
+ method: string;
881
+ params?: Record<string, unknown>;
882
+ sessionId?: string;
883
+ }> = [];
884
+ let connectCount = 0;
885
+ let discoverViaWsCalls = 0;
886
+
887
+ const client = createCdpInspectClient("conv-ws-fallback", {
888
+ host: "127.0.0.1",
889
+ port: 9222,
890
+ discoveryTimeoutMs: 100,
891
+ wsConnectTimeoutMs: 100,
892
+ helpers: {
893
+ probeDevToolsJsonVersion: async () => {
894
+ throw new DevToolsDiscoveryError(
895
+ "invalid_response",
896
+ "DevTools /json/version returned HTTP 404.",
897
+ );
898
+ },
899
+ listDevToolsTargets: async () => {
900
+ throw new Error("listDevToolsTargets should not be called");
901
+ },
902
+ connectCdpWsTransport: async () => {
903
+ connectCount += 1;
904
+ return createFakeTransport({
905
+ onSend: async (method) => {
906
+ if (method === "Target.attachToTarget") {
907
+ return { sessionId: "ws-session" };
908
+ }
909
+ return { ok: true };
910
+ },
911
+ trackSends: sends,
912
+ });
913
+ },
914
+ discoverTargetsViaWs: async () => {
915
+ discoverViaWsCalls += 1;
916
+ return [
917
+ {
918
+ id: "ws-target-1",
919
+ type: "page",
920
+ title: "WS Page",
921
+ url: "https://example.com/",
922
+ webSocketDebuggerUrl:
923
+ "ws://127.0.0.1:9222/devtools/page/ws-target-1",
924
+ },
925
+ ];
926
+ },
927
+ buildBrowserWsUrl: (host: string, port: number) =>
928
+ `ws://${host}:${port}/devtools/browser`,
929
+ },
930
+ });
931
+
932
+ const result = await client.send<{ ok: boolean }>("Browser.getVersion");
933
+ expect(result).toEqual({ ok: true });
934
+ expect(connectCount).toBe(1);
935
+ expect(discoverViaWsCalls).toBe(1);
936
+ // attach + forwarded call
937
+ expect(sends).toEqual([
938
+ {
939
+ method: "Target.attachToTarget",
940
+ params: { targetId: "ws-target-1", flatten: true },
941
+ sessionId: undefined,
942
+ },
943
+ {
944
+ method: "Browser.getVersion",
945
+ params: undefined,
946
+ sessionId: "ws-session",
947
+ },
948
+ ]);
949
+ });
950
+
951
+ test("falls back to WS when HTTP discovery is unreachable", async () => {
952
+ let discoverViaWsCalls = 0;
953
+
954
+ const client = createCdpInspectClient("conv-ws-unreachable", {
955
+ host: "127.0.0.1",
956
+ port: 9222,
957
+ discoveryTimeoutMs: 100,
958
+ helpers: {
959
+ probeDevToolsJsonVersion: async () => {
960
+ throw new DevToolsDiscoveryError(
961
+ "unreachable",
962
+ "Failed to reach DevTools endpoint: ECONNREFUSED",
963
+ );
964
+ },
965
+ listDevToolsTargets: async () => {
966
+ throw new Error("should not be called");
967
+ },
968
+ connectCdpWsTransport: async () =>
969
+ createFakeTransport({
970
+ onSend: async (method) => {
971
+ if (method === "Target.attachToTarget") {
972
+ return { sessionId: "ws-session" };
973
+ }
974
+ return { ok: true };
975
+ },
976
+ }),
977
+ discoverTargetsViaWs: async () => {
978
+ discoverViaWsCalls += 1;
979
+ return [
980
+ {
981
+ id: "ws-target-1",
982
+ type: "page",
983
+ title: "Page",
984
+ url: "https://example.com/",
985
+ webSocketDebuggerUrl:
986
+ "ws://127.0.0.1:9222/devtools/page/ws-target-1",
987
+ },
988
+ ];
989
+ },
990
+ buildBrowserWsUrl: (host: string, port: number) =>
991
+ `ws://${host}:${port}/devtools/browser`,
992
+ },
993
+ });
994
+
995
+ const result = await client.send<{ ok: boolean }>("Runtime.enable");
996
+ expect(result).toEqual({ ok: true });
997
+ expect(discoverViaWsCalls).toBe(1);
998
+ });
999
+
1000
+ test("does NOT fall back to WS on non_loopback error (safety constraint)", async () => {
1001
+ let connectCalled = false;
1002
+
1003
+ const client = createCdpInspectClient("conv-no-fallback-loopback", {
1004
+ host: "127.0.0.1",
1005
+ port: 9222,
1006
+ discoveryTimeoutMs: 100,
1007
+ helpers: {
1008
+ probeDevToolsJsonVersion: async () => {
1009
+ throw new DevToolsDiscoveryError(
1010
+ "non_loopback",
1011
+ "Refusing to probe non-loopback host",
1012
+ );
1013
+ },
1014
+ listDevToolsTargets: async () => {
1015
+ throw new Error("should not be called");
1016
+ },
1017
+ connectCdpWsTransport: async () => {
1018
+ connectCalled = true;
1019
+ return createFakeTransport({});
1020
+ },
1021
+ discoverTargetsViaWs: async () => {
1022
+ throw new Error("should not be called");
1023
+ },
1024
+ buildBrowserWsUrl: () => "ws://127.0.0.1:9222/devtools/browser",
1025
+ },
1026
+ });
1027
+
1028
+ let caught: unknown;
1029
+ try {
1030
+ await client.send("Browser.getVersion");
1031
+ } catch (err) {
1032
+ caught = err;
1033
+ }
1034
+
1035
+ expect(caught).toBeInstanceOf(CdpError);
1036
+ const cdpErr = caught as InstanceType<typeof CdpError>;
1037
+ expect(cdpErr.code).toBe("transport_error");
1038
+ expect(cdpErr.message).toContain("non-loopback");
1039
+ expect(connectCalled).toBe(false);
1040
+ });
1041
+
1042
+ test("does NOT fall back to WS on non_chrome error", async () => {
1043
+ let connectCalled = false;
1044
+
1045
+ const client = createCdpInspectClient("conv-no-fallback-chrome", {
1046
+ host: "127.0.0.1",
1047
+ port: 9222,
1048
+ discoveryTimeoutMs: 100,
1049
+ helpers: {
1050
+ probeDevToolsJsonVersion: async () => {
1051
+ throw new DevToolsDiscoveryError(
1052
+ "non_chrome",
1053
+ "Not Chrome: Firefox/115.0",
1054
+ );
1055
+ },
1056
+ listDevToolsTargets: async () => {
1057
+ throw new Error("should not be called");
1058
+ },
1059
+ connectCdpWsTransport: async () => {
1060
+ connectCalled = true;
1061
+ return createFakeTransport({});
1062
+ },
1063
+ discoverTargetsViaWs: async () => {
1064
+ throw new Error("should not be called");
1065
+ },
1066
+ buildBrowserWsUrl: () => "ws://127.0.0.1:9222/devtools/browser",
1067
+ },
1068
+ });
1069
+
1070
+ let caught: unknown;
1071
+ try {
1072
+ await client.send("Browser.getVersion");
1073
+ } catch (err) {
1074
+ caught = err;
1075
+ }
1076
+
1077
+ expect(caught).toBeInstanceOf(CdpError);
1078
+ const cdpErr = caught as InstanceType<typeof CdpError>;
1079
+ expect(cdpErr.code).toBe("transport_error");
1080
+ expect(connectCalled).toBe(false);
1081
+ });
1082
+
1083
+ test("does NOT fall back to WS on timeout error", async () => {
1084
+ let connectCalled = false;
1085
+
1086
+ const client = createCdpInspectClient("conv-no-fallback-timeout", {
1087
+ host: "127.0.0.1",
1088
+ port: 9222,
1089
+ discoveryTimeoutMs: 100,
1090
+ helpers: {
1091
+ probeDevToolsJsonVersion: async () => {
1092
+ throw new DevToolsDiscoveryError(
1093
+ "timeout",
1094
+ "Timed out waiting for DevTools HTTP response.",
1095
+ );
1096
+ },
1097
+ listDevToolsTargets: async () => {
1098
+ throw new Error("should not be called");
1099
+ },
1100
+ connectCdpWsTransport: async () => {
1101
+ connectCalled = true;
1102
+ return createFakeTransport({});
1103
+ },
1104
+ discoverTargetsViaWs: async () => {
1105
+ throw new Error("should not be called");
1106
+ },
1107
+ buildBrowserWsUrl: () => "ws://127.0.0.1:9222/devtools/browser",
1108
+ },
1109
+ });
1110
+
1111
+ let caught: unknown;
1112
+ try {
1113
+ await client.send("Browser.getVersion");
1114
+ } catch (err) {
1115
+ caught = err;
1116
+ }
1117
+
1118
+ expect(caught).toBeInstanceOf(CdpError);
1119
+ const cdpErr = caught as InstanceType<typeof CdpError>;
1120
+ expect(cdpErr.code).toBe("transport_error");
1121
+ expect(connectCalled).toBe(false);
1122
+ });
1123
+
1124
+ test("full fallback failure: HTTP invalid_response + WS connect fails", async () => {
1125
+ const client = createCdpInspectClient("conv-full-fail", {
1126
+ host: "127.0.0.1",
1127
+ port: 9222,
1128
+ discoveryTimeoutMs: 100,
1129
+ helpers: {
1130
+ probeDevToolsJsonVersion: async () => {
1131
+ throw new DevToolsDiscoveryError(
1132
+ "invalid_response",
1133
+ "DevTools /json/version returned HTTP 404.",
1134
+ );
1135
+ },
1136
+ listDevToolsTargets: async () => {
1137
+ throw new Error("should not be called");
1138
+ },
1139
+ connectCdpWsTransport: async () => {
1140
+ throw new CdpWsTransportError(
1141
+ "transport_error",
1142
+ "websocket closed before open",
1143
+ );
1144
+ },
1145
+ discoverTargetsViaWs: async () => {
1146
+ throw new Error("should not be called");
1147
+ },
1148
+ buildBrowserWsUrl: (host: string, port: number) =>
1149
+ `ws://${host}:${port}/devtools/browser`,
1150
+ },
1151
+ });
1152
+
1153
+ let caught: unknown;
1154
+ try {
1155
+ await client.send("Browser.getVersion");
1156
+ } catch (err) {
1157
+ caught = err;
1158
+ }
1159
+
1160
+ expect(caught).toBeInstanceOf(CdpError);
1161
+ const cdpErr = caught as InstanceType<typeof CdpError>;
1162
+ expect(cdpErr.code).toBe("transport_error");
1163
+ // Error message should explain both HTTP and WS failures
1164
+ expect(cdpErr.message).toContain("HTTP discovery failed");
1165
+ expect(cdpErr.message).toContain("WS-only fallback also failed");
1166
+ expect(cdpErr.message).toContain("127.0.0.1:9222");
1167
+ });
1168
+
1169
+ test("WS fallback: no page targets yields stable error", async () => {
1170
+ const client = createCdpInspectClient("conv-ws-no-targets", {
1171
+ host: "127.0.0.1",
1172
+ port: 9222,
1173
+ discoveryTimeoutMs: 100,
1174
+ helpers: {
1175
+ probeDevToolsJsonVersion: async () => {
1176
+ throw new DevToolsDiscoveryError("unreachable", "ECONNREFUSED");
1177
+ },
1178
+ listDevToolsTargets: async () => {
1179
+ throw new Error("should not be called");
1180
+ },
1181
+ connectCdpWsTransport: async () =>
1182
+ createFakeTransport({
1183
+ onSend: async () => ({ ok: true }),
1184
+ }),
1185
+ discoverTargetsViaWs: async () => {
1186
+ throw new DevToolsDiscoveryError(
1187
+ "no_targets",
1188
+ "No usable page targets returned by CDP Target.getTargets.",
1189
+ );
1190
+ },
1191
+ buildBrowserWsUrl: (host: string, port: number) =>
1192
+ `ws://${host}:${port}/devtools/browser`,
1193
+ },
1194
+ });
1195
+
1196
+ let caught: unknown;
1197
+ try {
1198
+ await client.send("Browser.getVersion");
1199
+ } catch (err) {
1200
+ caught = err;
1201
+ }
1202
+
1203
+ expect(caught).toBeInstanceOf(CdpError);
1204
+ const cdpErr = caught as InstanceType<typeof CdpError>;
1205
+ expect(cdpErr.code).toBe("transport_error");
1206
+ expect(cdpErr.message).toContain("No usable page targets");
1207
+ });
1208
+
1209
+ test("WS fallback caches session for subsequent calls", async () => {
1210
+ let connectCount = 0;
1211
+ let discoverViaWsCalls = 0;
1212
+
1213
+ const client = createCdpInspectClient("conv-ws-cache", {
1214
+ host: "127.0.0.1",
1215
+ port: 9222,
1216
+ discoveryTimeoutMs: 100,
1217
+ helpers: {
1218
+ probeDevToolsJsonVersion: async () => {
1219
+ throw new DevToolsDiscoveryError(
1220
+ "invalid_response",
1221
+ "no HTTP discovery",
1222
+ );
1223
+ },
1224
+ listDevToolsTargets: async () => {
1225
+ throw new Error("should not be called");
1226
+ },
1227
+ connectCdpWsTransport: async () => {
1228
+ connectCount += 1;
1229
+ return createFakeTransport({
1230
+ onSend: async (method) => {
1231
+ if (method === "Target.attachToTarget") {
1232
+ return { sessionId: "ws-session-cached" };
1233
+ }
1234
+ return { ok: true };
1235
+ },
1236
+ });
1237
+ },
1238
+ discoverTargetsViaWs: async () => {
1239
+ discoverViaWsCalls += 1;
1240
+ return [
1241
+ {
1242
+ id: "t1",
1243
+ type: "page",
1244
+ title: "Page",
1245
+ url: "https://example.com/",
1246
+ webSocketDebuggerUrl: "ws://127.0.0.1:9222/devtools/page/t1",
1247
+ },
1248
+ ];
1249
+ },
1250
+ buildBrowserWsUrl: (host: string, port: number) =>
1251
+ `ws://${host}:${port}/devtools/browser`,
1252
+ },
1253
+ });
1254
+
1255
+ await client.send("Runtime.enable");
1256
+ await client.send("Page.enable");
1257
+ await client.send("DOM.enable");
1258
+
1259
+ // Only one WS fallback discovery + one connect + one attach
1260
+ expect(connectCount).toBe(1);
1261
+ expect(discoverViaWsCalls).toBe(1);
1262
+ });
1263
+ });
@@ -185,6 +185,35 @@ describe("ExtensionCdpClient", () => {
185
185
  expect(err.underlying).toBeInstanceOf(Error);
186
186
  });
187
187
 
188
+ test("result.isError with non-JSON content throws CdpError('cdp_error')", async () => {
189
+ const { proxy } = fakeProxy(async () => ({
190
+ content:
191
+ "Host browser proxy timed out waiting for extension response (check callback failures).",
192
+ isError: true,
193
+ }));
194
+
195
+ const client = createExtensionCdpClient(proxy, "conv-6b");
196
+
197
+ let caught: unknown;
198
+ try {
199
+ await client.send("Runtime.evaluate", { expression: "document.title" });
200
+ } catch (err) {
201
+ caught = err;
202
+ }
203
+
204
+ expect(caught).toBeInstanceOf(CdpError);
205
+ const err = caught as CdpError;
206
+ expect(err.code).toBe("cdp_error");
207
+ expect(err.message).toContain(
208
+ "Host browser proxy timed out waiting for extension response",
209
+ );
210
+ expect(err.cdpMethod).toBe("Runtime.evaluate");
211
+ expect(err.cdpParams).toEqual({ expression: "document.title" });
212
+ expect(err.underlying).toBe(
213
+ "Host browser proxy timed out waiting for extension response (check callback failures).",
214
+ );
215
+ });
216
+
188
217
  test("result.content === 'Aborted' throws CdpError('aborted')", async () => {
189
218
  const { proxy, request } = fakeProxy(async () => ({
190
219
  content: "Aborted",