@vellumai/assistant 0.6.2 → 0.6.3

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 (396) hide show
  1. package/bun.lock +40 -40
  2. package/bunfig.toml +3 -0
  3. package/docs/architecture/memory.md +1 -1
  4. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  5. package/openapi.yaml +184 -69
  6. package/package.json +41 -41
  7. package/scripts/generate-openapi.ts +1 -2
  8. package/src/__tests__/acp-session.test.ts +43 -0
  9. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  10. package/src/__tests__/app-executors.test.ts +1 -0
  11. package/src/__tests__/app-source-watcher.test.ts +37 -11
  12. package/src/__tests__/approval-routes-http.test.ts +178 -1
  13. package/src/__tests__/browser-fill-credential.test.ts +229 -94
  14. package/src/__tests__/browser-manager.test.ts +40 -27
  15. package/src/__tests__/catalog-files.test.ts +862 -0
  16. package/src/__tests__/channel-approvals.test.ts +53 -0
  17. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  18. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  19. package/src/__tests__/config-schema.test.ts +125 -48
  20. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  21. package/src/__tests__/context-overflow-approval.test.ts +16 -1
  22. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  23. package/src/__tests__/conversation-agent-loop.test.ts +1 -1
  24. package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
  25. package/src/__tests__/conversation-attachments.test.ts +80 -4
  26. package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
  27. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  28. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  29. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  30. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  31. package/src/__tests__/conversation-queue.test.ts +45 -2
  32. package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
  33. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  34. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  35. package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
  36. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  37. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  38. package/src/__tests__/conversation-store.test.ts +195 -0
  39. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  40. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
  41. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  42. package/src/__tests__/credential-vault-unit.test.ts +4 -4
  43. package/src/__tests__/credential-vault.test.ts +152 -13
  44. package/src/__tests__/credentials-cli.test.ts +2 -2
  45. package/src/__tests__/date-context.test.ts +4 -4
  46. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  47. package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
  48. package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
  49. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  50. package/src/__tests__/gemini-provider.test.ts +2 -2
  51. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  52. package/src/__tests__/headless-browser-interactions.test.ts +707 -371
  53. package/src/__tests__/headless-browser-navigate.test.ts +389 -47
  54. package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
  55. package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
  56. package/src/__tests__/host-bash-proxy.test.ts +150 -1
  57. package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
  58. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  59. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  60. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  61. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  62. package/src/__tests__/host-browser-routes.test.ts +198 -0
  63. package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
  64. package/src/__tests__/host-cu-proxy.test.ts +171 -1
  65. package/src/__tests__/host-file-proxy.test.ts +185 -1
  66. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  67. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  68. package/src/__tests__/host-shell-tool.test.ts +1 -11
  69. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  70. package/src/__tests__/integration-status.test.ts +6 -7
  71. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  72. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  73. package/src/__tests__/mcp-health-check.test.ts +10 -3
  74. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  75. package/src/__tests__/migration-export-http.test.ts +61 -2
  76. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  77. package/src/__tests__/migration-import-commit-http.test.ts +101 -1
  78. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  79. package/src/__tests__/oauth-apps-routes.test.ts +17 -12
  80. package/src/__tests__/oauth-cli.test.ts +707 -60
  81. package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
  82. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  83. package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
  84. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  85. package/src/__tests__/oauth-providers-routes.test.ts +50 -14
  86. package/src/__tests__/oauth-store.test.ts +1386 -182
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
  88. package/src/__tests__/onboarding-template-contract.test.ts +75 -57
  89. package/src/__tests__/openai-provider.test.ts +2 -2
  90. package/src/__tests__/outlook-categories.test.ts +1 -1
  91. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  92. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  93. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  94. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  95. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  96. package/src/__tests__/outlook-trash.test.ts +1 -1
  97. package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
  98. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  99. package/src/__tests__/permission-mode.test.ts +28 -56
  100. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  101. package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
  102. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  103. package/src/__tests__/require-fresh-approval.test.ts +40 -1
  104. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  105. package/src/__tests__/schedule-routes.test.ts +162 -0
  106. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  107. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  108. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  109. package/src/__tests__/set-permission-mode.test.ts +13 -250
  110. package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
  111. package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
  112. package/src/__tests__/slack-channel-config.test.ts +12 -15
  113. package/src/__tests__/subagent-detail.test.ts +44 -2
  114. package/src/__tests__/subagent-disposal.test.ts +1 -0
  115. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  116. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  117. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  118. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  119. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  120. package/src/__tests__/subagent-tools.test.ts +1 -0
  121. package/src/__tests__/subagent-types.test.ts +1 -0
  122. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  123. package/src/__tests__/system-prompt.test.ts +72 -1
  124. package/src/__tests__/task-scheduler.test.ts +32 -6
  125. package/src/__tests__/telegram-config.test.ts +10 -13
  126. package/src/__tests__/terminal-tools.test.ts +9 -0
  127. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  128. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  129. package/src/__tests__/top-level-renderer.test.ts +73 -1
  130. package/src/__tests__/transport-hints-queue.test.ts +14 -29
  131. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  132. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  133. package/src/acp/client-handler.ts +30 -4
  134. package/src/agent/loop.ts +12 -6
  135. package/src/approvals/guardian-request-resolvers.ts +21 -15
  136. package/src/browser-session/__tests__/manager.test.ts +297 -0
  137. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  138. package/src/browser-session/backends/extension.ts +26 -0
  139. package/src/browser-session/backends/local.ts +24 -0
  140. package/src/browser-session/events.ts +164 -0
  141. package/src/browser-session/index.ts +27 -0
  142. package/src/browser-session/manager.ts +159 -0
  143. package/src/browser-session/types.ts +28 -0
  144. package/src/channels/__tests__/types.test.ts +134 -0
  145. package/src/channels/types.ts +53 -3
  146. package/src/cli/commands/browser-relay.ts +339 -409
  147. package/src/cli/commands/credentials.ts +3 -3
  148. package/src/cli/commands/email.ts +18 -13
  149. package/src/cli/commands/mcp.ts +16 -4
  150. package/src/cli/commands/oauth/__tests__/connect.test.ts +44 -44
  151. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  152. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  153. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  154. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
  155. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
  156. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
  157. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  158. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  159. package/src/cli/commands/oauth/apps.ts +7 -4
  160. package/src/cli/commands/oauth/connect.ts +6 -3
  161. package/src/cli/commands/oauth/disconnect.ts +1 -1
  162. package/src/cli/commands/oauth/providers.ts +200 -36
  163. package/src/cli/commands/oauth/shared.ts +5 -5
  164. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
  165. package/src/cli/commands/platform/index.ts +107 -10
  166. package/src/cli/commands/usage.ts +10 -9
  167. package/src/cli/lib/daemon-credential-client.ts +4 -0
  168. package/src/cli/program.ts +1 -1
  169. package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
  170. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
  171. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  172. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  173. package/src/config/bundled-skills/contacts/SKILL.md +3 -0
  174. package/src/config/bundled-skills/document/SKILL.md +4 -0
  175. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  176. package/src/config/bundled-skills/outlook/SKILL.md +7 -0
  177. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  178. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  179. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  180. package/src/config/env-registry.ts +14 -0
  181. package/src/config/env.ts +21 -0
  182. package/src/config/feature-flag-registry.json +44 -5
  183. package/src/config/loader.ts +56 -1
  184. package/src/config/sanitize-for-transfer.ts +47 -0
  185. package/src/config/schema.ts +46 -5
  186. package/src/config/schemas/host-browser.ts +66 -0
  187. package/src/config/schemas/memory-lifecycle.ts +1 -1
  188. package/src/config/schemas/memory-retrieval.ts +103 -0
  189. package/src/config/schemas/security.ts +0 -6
  190. package/src/config/schemas/services.ts +8 -0
  191. package/src/config/types.ts +0 -1
  192. package/src/context/post-turn-tool-result-truncation.ts +176 -0
  193. package/src/context/window-manager.ts +19 -1
  194. package/src/credential-execution/approval-bridge.ts +49 -15
  195. package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
  196. package/src/daemon/app-source-watcher.ts +35 -0
  197. package/src/daemon/context-overflow-approval.ts +5 -0
  198. package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
  199. package/src/daemon/conversation-agent-loop.ts +58 -24
  200. package/src/daemon/conversation-attachments.ts +40 -0
  201. package/src/daemon/conversation-process.ts +48 -1
  202. package/src/daemon/conversation-runtime-assembly.ts +118 -36
  203. package/src/daemon/conversation-surfaces.ts +37 -36
  204. package/src/daemon/conversation-tool-setup.ts +74 -8
  205. package/src/daemon/conversation-workspace.ts +12 -0
  206. package/src/daemon/conversation.ts +226 -8
  207. package/src/daemon/date-context.ts +10 -10
  208. package/src/daemon/first-greeting.ts +3 -2
  209. package/src/daemon/handlers/conversations.ts +9 -140
  210. package/src/daemon/handlers/shared.ts +58 -0
  211. package/src/daemon/handlers/skills.ts +232 -37
  212. package/src/daemon/host-bash-proxy.ts +48 -13
  213. package/src/daemon/host-browser-proxy.ts +191 -0
  214. package/src/daemon/host-cu-proxy.ts +36 -11
  215. package/src/daemon/host-file-proxy.ts +57 -9
  216. package/src/daemon/lifecycle.ts +65 -11
  217. package/src/daemon/message-protocol.ts +7 -0
  218. package/src/daemon/message-types/conversations.ts +55 -13
  219. package/src/daemon/message-types/host-browser.ts +100 -0
  220. package/src/daemon/message-types/messages.ts +5 -5
  221. package/src/daemon/message-types/skills.ts +10 -0
  222. package/src/daemon/message-types/subagents.ts +2 -0
  223. package/src/daemon/server.ts +92 -12
  224. package/src/daemon/tool-side-effects.ts +6 -0
  225. package/src/daemon/transport-hints.ts +5 -24
  226. package/src/inbound/platform-callback-registration.ts +18 -17
  227. package/src/mcp/client.ts +59 -24
  228. package/src/memory/app-store.ts +31 -1
  229. package/src/memory/conversation-crud.ts +23 -0
  230. package/src/memory/conversation-starters-cadence.ts +76 -0
  231. package/src/memory/conversation-title-service.ts +5 -2
  232. package/src/memory/db-init.ts +12 -0
  233. package/src/memory/embedding-backend.test.ts +75 -0
  234. package/src/memory/embedding-backend.ts +131 -5
  235. package/src/memory/embedding-gemini.test.ts +54 -0
  236. package/src/memory/embedding-gemini.ts +20 -9
  237. package/src/memory/embedding-local.ts +176 -17
  238. package/src/memory/graph/consolidation.ts +10 -23
  239. package/src/memory/graph/extraction-job.ts +15 -0
  240. package/src/memory/graph/retriever.ts +40 -22
  241. package/src/memory/graph/store.test.ts +7 -3
  242. package/src/memory/graph/store.ts +47 -12
  243. package/src/memory/llm-usage-store.ts +45 -4
  244. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  245. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  246. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  247. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  248. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  249. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  250. package/src/memory/migrations/index.ts +6 -0
  251. package/src/memory/migrations/registry.ts +8 -0
  252. package/src/memory/schema/conversations.ts +1 -0
  253. package/src/memory/schema/oauth.ts +18 -13
  254. package/src/oauth/AGENTS.md +76 -0
  255. package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
  256. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  257. package/src/oauth/byo-connection.test.ts +8 -8
  258. package/src/oauth/byo-connection.ts +7 -7
  259. package/src/oauth/connect-orchestrator.ts +23 -21
  260. package/src/oauth/connect-types.ts +3 -3
  261. package/src/oauth/connection-resolver.test.ts +17 -4
  262. package/src/oauth/connection-resolver.ts +16 -16
  263. package/src/oauth/connection.ts +1 -1
  264. package/src/oauth/manual-token-connection.ts +13 -13
  265. package/src/oauth/oauth-store.ts +214 -100
  266. package/src/oauth/platform-connection.test.ts +3 -3
  267. package/src/oauth/platform-connection.ts +4 -4
  268. package/src/oauth/provider-serializer.ts +31 -5
  269. package/src/oauth/revoke.ts +76 -0
  270. package/src/oauth/seed-providers.ts +126 -87
  271. package/src/oauth/token-persistence.ts +1 -1
  272. package/src/permissions/permission-mode.ts +4 -11
  273. package/src/permissions/prompter.ts +13 -1
  274. package/src/permissions/v2-consent-policy.ts +87 -0
  275. package/src/prompts/system-prompt.ts +18 -21
  276. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  277. package/src/prompts/templates/BOOTSTRAP.md +59 -105
  278. package/src/providers/anthropic/client.ts +1 -0
  279. package/src/providers/types.ts +1 -1
  280. package/src/runtime/AGENTS.md +23 -0
  281. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  282. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  283. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  284. package/src/runtime/assistant-event-hub.ts +2 -2
  285. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  286. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  287. package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
  288. package/src/runtime/auth/middleware.ts +98 -0
  289. package/src/runtime/auth/route-policy.ts +6 -7
  290. package/src/runtime/capability-tokens.ts +414 -0
  291. package/src/runtime/channel-approvals.ts +18 -5
  292. package/src/runtime/chrome-extension-registry.ts +332 -0
  293. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  294. package/src/runtime/guardian-decision-types.ts +7 -0
  295. package/src/runtime/http-server.ts +425 -70
  296. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  297. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  298. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
  299. package/src/runtime/migrations/migration-transport.ts +6 -0
  300. package/src/runtime/migrations/migration-wizard.ts +22 -2
  301. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  302. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  303. package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
  304. package/src/runtime/migrations/vbundle-importer.ts +55 -5
  305. package/src/runtime/pending-interactions.ts +29 -13
  306. package/src/runtime/routes/approval-routes.ts +90 -16
  307. package/src/runtime/routes/browser-cdp-routes.ts +229 -0
  308. package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
  309. package/src/runtime/routes/conversation-analysis-routes.ts +2 -1
  310. package/src/runtime/routes/conversation-management-routes.ts +108 -0
  311. package/src/runtime/routes/conversation-routes.ts +301 -27
  312. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  313. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  314. package/src/runtime/routes/host-browser-routes.ts +279 -0
  315. package/src/runtime/routes/host-file-routes.ts +9 -1
  316. package/src/runtime/routes/identity-routes.ts +259 -16
  317. package/src/runtime/routes/log-export-routes.ts +42 -22
  318. package/src/runtime/routes/memory-item-routes.ts +1 -7
  319. package/src/runtime/routes/migration-routes.ts +87 -2
  320. package/src/runtime/routes/oauth-apps.ts +15 -17
  321. package/src/runtime/routes/oauth-providers.ts +4 -0
  322. package/src/runtime/routes/schedule-routes.ts +24 -11
  323. package/src/runtime/routes/settings-routes.ts +9 -97
  324. package/src/runtime/routes/skills-routes.ts +52 -2
  325. package/src/runtime/routes/subagents-routes.ts +14 -10
  326. package/src/runtime/routes/usage-routes.ts +8 -7
  327. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  328. package/src/runtime/routes/workspace-routes.ts +8 -1
  329. package/src/runtime/routes/workspace-utils.ts +2 -0
  330. package/src/schedule/scheduler.ts +7 -5
  331. package/src/security/ces-credential-client.ts +20 -0
  332. package/src/security/ces-rpc-credential-backend.ts +17 -0
  333. package/src/security/credential-backend.ts +5 -0
  334. package/src/security/oauth2.ts +42 -25
  335. package/src/security/secure-keys.ts +118 -25
  336. package/src/security/token-manager.ts +23 -10
  337. package/src/skills/catalog-files.ts +492 -0
  338. package/src/subagent/manager.ts +131 -26
  339. package/src/subagent/types.ts +19 -0
  340. package/src/tools/apps/executors.ts +11 -2
  341. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  342. package/src/tools/browser/auth-detector.ts +43 -12
  343. package/src/tools/browser/browser-execution.ts +645 -340
  344. package/src/tools/browser/browser-manager.ts +36 -12
  345. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  346. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  347. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
  348. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
  349. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
  350. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  351. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  352. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  353. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  354. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  355. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  356. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
  357. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  358. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
  359. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  360. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
  361. package/src/tools/browser/cdp-client/errors.ts +34 -0
  362. package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
  363. package/src/tools/browser/cdp-client/factory.ts +204 -0
  364. package/src/tools/browser/cdp-client/index.ts +14 -0
  365. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  366. package/src/tools/browser/cdp-client/types.ts +52 -0
  367. package/src/tools/filesystem/edit.ts +1 -1
  368. package/src/tools/filesystem/list.ts +1 -1
  369. package/src/tools/filesystem/read.ts +1 -1
  370. package/src/tools/filesystem/write.ts +2 -1
  371. package/src/tools/host-filesystem/edit.ts +1 -1
  372. package/src/tools/host-filesystem/read.ts +12 -15
  373. package/src/tools/host-filesystem/write.ts +1 -1
  374. package/src/tools/host-terminal/host-shell.ts +21 -16
  375. package/src/tools/permission-checker.ts +77 -82
  376. package/src/tools/registry.ts +0 -2
  377. package/src/tools/secret-detection-handler.ts +34 -0
  378. package/src/tools/shared/filesystem/image-read.ts +61 -40
  379. package/src/tools/subagent/spawn.ts +47 -3
  380. package/src/tools/subagent/status.ts +2 -0
  381. package/src/tools/system/register.ts +2 -16
  382. package/src/tools/terminal/safe-env.ts +7 -0
  383. package/src/tools/terminal/shell.ts +21 -16
  384. package/src/tools/tool-approval-handler.ts +48 -2
  385. package/src/tools/types.ts +2 -0
  386. package/src/util/platform.ts +14 -19
  387. package/src/workspace/top-level-renderer.ts +19 -1
  388. package/src/__tests__/chrome-cdp.test.ts +0 -419
  389. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  390. package/src/__tests__/permission-mode-store.test.ts +0 -277
  391. package/src/browser-extension-relay/protocol.ts +0 -63
  392. package/src/browser-extension-relay/server.ts +0 -203
  393. package/src/config/schemas/sandbox.ts +0 -14
  394. package/src/permissions/permission-mode-store.ts +0 -180
  395. package/src/tools/browser/chrome-cdp.ts +0 -239
  396. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -0,0 +1,64 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "nodeId": "1",
5
+ "role": { "type": "internalRole", "value": "RootWebArea" },
6
+ "name": { "type": "computedString", "value": "Parent" },
7
+ "ignored": false,
8
+ "backendDOMNodeId": 200,
9
+ "childIds": ["2", "3", "6"]
10
+ },
11
+ {
12
+ "nodeId": "2",
13
+ "role": { "type": "role", "value": "button" },
14
+ "name": { "type": "computedString", "value": "Parent Button" },
15
+ "backendDOMNodeId": 201,
16
+ "ignored": false,
17
+ "childIds": []
18
+ },
19
+ {
20
+ "nodeId": "3",
21
+ "role": { "type": "internalRole", "value": "RootWebArea" },
22
+ "name": { "type": "computedString", "value": "Inner Frame" },
23
+ "backendDOMNodeId": 202,
24
+ "ignored": false,
25
+ "childIds": ["4", "5"]
26
+ },
27
+ {
28
+ "nodeId": "4",
29
+ "role": { "type": "role", "value": "link" },
30
+ "name": { "type": "computedString", "value": "Inner Link" },
31
+ "properties": [
32
+ {
33
+ "name": "url",
34
+ "value": {
35
+ "type": "string",
36
+ "value": "https://example.com/inner"
37
+ }
38
+ }
39
+ ],
40
+ "backendDOMNodeId": 203,
41
+ "ignored": false,
42
+ "childIds": []
43
+ },
44
+ {
45
+ "nodeId": "5",
46
+ "role": { "type": "role", "value": "textbox" },
47
+ "name": { "type": "computedString", "value": "Inner Search" },
48
+ "backendDOMNodeId": 204,
49
+ "ignored": false,
50
+ "childIds": []
51
+ },
52
+ {
53
+ "nodeId": "6",
54
+ "role": { "type": "role", "value": "checkbox" },
55
+ "name": { "type": "computedString", "value": "Subscribe" },
56
+ "properties": [
57
+ { "name": "checked", "value": { "type": "boolean", "value": true } }
58
+ ],
59
+ "backendDOMNodeId": 205,
60
+ "ignored": false,
61
+ "childIds": []
62
+ }
63
+ ]
64
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "nodeId": "1",
5
+ "role": { "type": "internalRole", "value": "RootWebArea" },
6
+ "name": { "type": "computedString", "value": "Example" },
7
+ "ignored": false,
8
+ "backendDOMNodeId": 100,
9
+ "childIds": ["2", "3", "4", "5", "6"]
10
+ },
11
+ {
12
+ "nodeId": "2",
13
+ "role": { "type": "role", "value": "button" },
14
+ "name": { "type": "computedString", "value": " Submit Form " },
15
+ "properties": [
16
+ { "name": "disabled", "value": { "type": "boolean", "value": false } },
17
+ { "name": "focusable", "value": { "type": "boolean", "value": true } }
18
+ ],
19
+ "backendDOMNodeId": 101,
20
+ "ignored": false,
21
+ "childIds": []
22
+ },
23
+ {
24
+ "nodeId": "3",
25
+ "role": { "type": "role", "value": "link" },
26
+ "name": { "type": "computedString", "value": "About Us" },
27
+ "properties": [
28
+ {
29
+ "name": "url",
30
+ "value": { "type": "string", "value": "https://example.com/about" }
31
+ }
32
+ ],
33
+ "backendDOMNodeId": 102,
34
+ "ignored": false,
35
+ "childIds": []
36
+ },
37
+ {
38
+ "nodeId": "4",
39
+ "role": { "type": "role", "value": "textbox" },
40
+ "name": { "type": "computedString", "value": "Email address" },
41
+ "value": { "type": "string", "value": "user@example.com" },
42
+ "properties": [
43
+ {
44
+ "name": "placeholder",
45
+ "value": { "type": "string", "value": "you@example.com" }
46
+ },
47
+ { "name": "required", "value": { "type": "boolean", "value": true } }
48
+ ],
49
+ "backendDOMNodeId": 103,
50
+ "ignored": false,
51
+ "childIds": []
52
+ },
53
+ {
54
+ "nodeId": "5",
55
+ "role": { "type": "role", "value": "button" },
56
+ "name": { "type": "computedString", "value": "Hidden Action" },
57
+ "backendDOMNodeId": 104,
58
+ "ignored": true,
59
+ "childIds": []
60
+ },
61
+ {
62
+ "nodeId": "6",
63
+ "role": { "type": "role", "value": "button" },
64
+ "name": { "type": "computedString", "value": "Orphan Button" },
65
+ "ignored": false,
66
+ "childIds": []
67
+ }
68
+ ]
69
+ }
@@ -0,0 +1,310 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ // Silence the logger from local-cdp-client.
4
+ mock.module("../../../../util/logger.js", () => ({
5
+ getLogger: () => ({
6
+ info: () => {},
7
+ debug: () => {},
8
+ warn: () => {},
9
+ error: () => {},
10
+ }),
11
+ }));
12
+
13
+ let fakeSessionSendCalls: Array<{ method: string; params?: unknown }> = [];
14
+ let fakeSessionDetachCalls = 0;
15
+ let newCdpSessionCalls = 0;
16
+ let getOrCreateSessionPageCalls = 0;
17
+ let fakeSessionHandler: (method: string, params?: unknown) => unknown = () =>
18
+ undefined;
19
+ // When set, the next call to `browserManager.getOrCreateSessionPage`
20
+ // throws this error then clears it (so subsequent calls succeed). Used
21
+ // by tests that exercise the cache-clear-on-rejection retry path.
22
+ let getOrCreateSessionPageError: Error | null = null;
23
+ // When non-null, every call to `browserManager.getOrCreateSessionPage`
24
+ // throws this error indefinitely.
25
+ let getOrCreateSessionPagePersistentError: Error | null = null;
26
+
27
+ function resetFakes() {
28
+ fakeSessionSendCalls = [];
29
+ fakeSessionDetachCalls = 0;
30
+ newCdpSessionCalls = 0;
31
+ getOrCreateSessionPageCalls = 0;
32
+ fakeSessionHandler = () => undefined;
33
+ getOrCreateSessionPageError = null;
34
+ getOrCreateSessionPagePersistentError = null;
35
+ }
36
+
37
+ mock.module("../../browser-manager.js", () => {
38
+ const fakeSession = {
39
+ send: async (method: string, params?: unknown) => {
40
+ fakeSessionSendCalls.push({ method, params });
41
+ return fakeSessionHandler(method, params);
42
+ },
43
+ detach: async () => {
44
+ fakeSessionDetachCalls += 1;
45
+ },
46
+ };
47
+ const fakePage = {
48
+ context: () => ({
49
+ newCDPSession: async () => {
50
+ newCdpSessionCalls += 1;
51
+ return fakeSession;
52
+ },
53
+ }),
54
+ };
55
+ return {
56
+ browserManager: {
57
+ getOrCreateSessionPage: async (_conversationId: string) => {
58
+ getOrCreateSessionPageCalls += 1;
59
+ if (getOrCreateSessionPagePersistentError) {
60
+ throw getOrCreateSessionPagePersistentError;
61
+ }
62
+ if (getOrCreateSessionPageError) {
63
+ const err = getOrCreateSessionPageError;
64
+ getOrCreateSessionPageError = null;
65
+ throw err;
66
+ }
67
+ return fakePage;
68
+ },
69
+ },
70
+ };
71
+ });
72
+
73
+ // Import under test AFTER mock.module calls so that the module's
74
+ // top-level imports resolve to our fakes.
75
+ const { createLocalCdpClient, LocalCdpClient } =
76
+ await import("../local-cdp-client.js");
77
+ const { CdpError } = await import("../errors.js");
78
+
79
+ describe("LocalCdpClient", () => {
80
+ beforeEach(() => {
81
+ resetFakes();
82
+ });
83
+
84
+ test("kind is 'local' and exposes conversationId", () => {
85
+ const client = createLocalCdpClient("conv-kind");
86
+ expect(client).toBeInstanceOf(LocalCdpClient);
87
+ expect(client.kind).toBe("local");
88
+ expect(client.conversationId).toBe("conv-kind");
89
+ });
90
+
91
+ test("send() returns the underlying session's response", async () => {
92
+ fakeSessionHandler = (method) => {
93
+ if (method === "Browser.getVersion") {
94
+ return { product: "HeadlessChrome/120.0.0.0" };
95
+ }
96
+ return undefined;
97
+ };
98
+ const client = createLocalCdpClient("conv-happy");
99
+ const result = await client.send<{ product: string }>("Browser.getVersion");
100
+ expect(result).toEqual({ product: "HeadlessChrome/120.0.0.0" });
101
+ expect(fakeSessionSendCalls).toEqual([
102
+ { method: "Browser.getVersion", params: undefined },
103
+ ]);
104
+ expect(getOrCreateSessionPageCalls).toBe(1);
105
+ expect(newCdpSessionCalls).toBe(1);
106
+ });
107
+
108
+ test("send() forwards params to the underlying session", async () => {
109
+ fakeSessionHandler = () => ({ ok: true });
110
+ const client = createLocalCdpClient("conv-params");
111
+ await client.send("Page.navigate", { url: "https://example.com/" });
112
+ expect(fakeSessionSendCalls).toEqual([
113
+ {
114
+ method: "Page.navigate",
115
+ params: { url: "https://example.com/" },
116
+ },
117
+ ]);
118
+ });
119
+
120
+ test("multiple send() calls share a single CDP session", async () => {
121
+ fakeSessionHandler = () => ({ ok: true });
122
+ const client = createLocalCdpClient("conv-reuse");
123
+ await client.send("Browser.getVersion");
124
+ await client.send("Runtime.enable");
125
+ await client.send("Page.enable");
126
+ expect(newCdpSessionCalls).toBe(1);
127
+ expect(getOrCreateSessionPageCalls).toBe(1);
128
+ expect(fakeSessionSendCalls.map((c) => c.method)).toEqual([
129
+ "Browser.getVersion",
130
+ "Runtime.enable",
131
+ "Page.enable",
132
+ ]);
133
+ });
134
+
135
+ test("concurrent send() calls share a single in-flight session", async () => {
136
+ fakeSessionHandler = () => ({ ok: true });
137
+ const client = createLocalCdpClient("conv-concurrent");
138
+ await Promise.all([
139
+ client.send("Browser.getVersion"),
140
+ client.send("Runtime.enable"),
141
+ client.send("Page.enable"),
142
+ ]);
143
+ expect(newCdpSessionCalls).toBe(1);
144
+ expect(getOrCreateSessionPageCalls).toBe(1);
145
+ expect(fakeSessionSendCalls.length).toBe(3);
146
+ });
147
+
148
+ test("dispose() detaches the CDP session exactly once", async () => {
149
+ fakeSessionHandler = () => ({ ok: true });
150
+ const client = createLocalCdpClient("conv-dispose");
151
+ await client.send("Browser.getVersion");
152
+ client.dispose();
153
+ // dispose schedules detach asynchronously; flush microtasks.
154
+ await new Promise((resolve) => setTimeout(resolve, 0));
155
+ expect(fakeSessionDetachCalls).toBe(1);
156
+
157
+ // Idempotent: a second dispose is a no-op.
158
+ client.dispose();
159
+ await new Promise((resolve) => setTimeout(resolve, 0));
160
+ expect(fakeSessionDetachCalls).toBe(1);
161
+ });
162
+
163
+ test("dispose() without any sends does not call detach", async () => {
164
+ const client = createLocalCdpClient("conv-dispose-empty");
165
+ client.dispose();
166
+ await new Promise((resolve) => setTimeout(resolve, 0));
167
+ expect(fakeSessionDetachCalls).toBe(0);
168
+ expect(newCdpSessionCalls).toBe(0);
169
+ });
170
+
171
+ test("send() after dispose throws CdpError with code 'disposed'", async () => {
172
+ const client = createLocalCdpClient("conv-after-dispose");
173
+ client.dispose();
174
+ let caught: unknown;
175
+ try {
176
+ await client.send("Browser.getVersion");
177
+ } catch (err) {
178
+ caught = err;
179
+ }
180
+ expect(caught).toBeInstanceOf(CdpError);
181
+ const cdpErr = caught as InstanceType<typeof CdpError>;
182
+ expect(cdpErr.code).toBe("disposed");
183
+ expect(cdpErr.cdpMethod).toBe("Browser.getVersion");
184
+ // No session was ever created, so no calls to the fake page.
185
+ expect(newCdpSessionCalls).toBe(0);
186
+ expect(fakeSessionSendCalls.length).toBe(0);
187
+ });
188
+
189
+ test("send() with an already-aborted signal throws CdpError 'aborted' without touching the session", async () => {
190
+ const client = createLocalCdpClient("conv-pre-aborted");
191
+ const controller = new AbortController();
192
+ controller.abort();
193
+ let caught: unknown;
194
+ try {
195
+ await client.send("Browser.getVersion", undefined, controller.signal);
196
+ } catch (err) {
197
+ caught = err;
198
+ }
199
+ expect(caught).toBeInstanceOf(CdpError);
200
+ const cdpErr = caught as InstanceType<typeof CdpError>;
201
+ expect(cdpErr.code).toBe("aborted");
202
+ expect(cdpErr.cdpMethod).toBe("Browser.getVersion");
203
+ expect(fakeSessionSendCalls.length).toBe(0);
204
+ expect(newCdpSessionCalls).toBe(0);
205
+ expect(getOrCreateSessionPageCalls).toBe(0);
206
+ });
207
+
208
+ test("send() wraps underlying session errors as CdpError 'cdp_error'", async () => {
209
+ const underlying = new Error("target closed");
210
+ fakeSessionHandler = () => {
211
+ throw underlying;
212
+ };
213
+ const client = createLocalCdpClient("conv-throw");
214
+ let caught: unknown;
215
+ try {
216
+ await client.send("Runtime.evaluate", { expression: "1+1" });
217
+ } catch (err) {
218
+ caught = err;
219
+ }
220
+ expect(caught).toBeInstanceOf(CdpError);
221
+ const cdpErr = caught as InstanceType<typeof CdpError>;
222
+ expect(cdpErr.code).toBe("cdp_error");
223
+ expect(cdpErr.message).toBe("target closed");
224
+ expect(cdpErr.cdpMethod).toBe("Runtime.evaluate");
225
+ expect(cdpErr.cdpParams).toEqual({ expression: "1+1" });
226
+ expect(cdpErr.underlying).toBe(underlying);
227
+ });
228
+
229
+ test("send() classifies as 'aborted' when the signal fires during the underlying call", async () => {
230
+ const controller = new AbortController();
231
+ fakeSessionHandler = () => {
232
+ controller.abort();
233
+ throw new Error("target closed");
234
+ };
235
+ const client = createLocalCdpClient("conv-abort-mid");
236
+ let caught: unknown;
237
+ try {
238
+ await client.send(
239
+ "Page.navigate",
240
+ { url: "about:blank" },
241
+ controller.signal,
242
+ );
243
+ } catch (err) {
244
+ caught = err;
245
+ }
246
+ expect(caught).toBeInstanceOf(CdpError);
247
+ const cdpErr = caught as InstanceType<typeof CdpError>;
248
+ expect(cdpErr.code).toBe("aborted");
249
+ expect(cdpErr.cdpMethod).toBe("Page.navigate");
250
+ expect(cdpErr.underlying).toBeInstanceOf(Error);
251
+ });
252
+
253
+ test("send() wraps ensureSession failures as CdpError 'transport_error'", async () => {
254
+ getOrCreateSessionPagePersistentError = new Error(
255
+ "Failed to launch chromium",
256
+ );
257
+ const client = createLocalCdpClient("conv-launch-fail");
258
+ let caught: unknown;
259
+ try {
260
+ await client.send("Browser.getVersion");
261
+ } catch (err) {
262
+ caught = err;
263
+ }
264
+ expect(caught).toBeInstanceOf(CdpError);
265
+ const cdpErr = caught as InstanceType<typeof CdpError>;
266
+ expect(cdpErr.code).toBe("transport_error");
267
+ expect(cdpErr.message).toBe("Failed to launch chromium");
268
+ expect(cdpErr.cdpMethod).toBe("Browser.getVersion");
269
+ expect(cdpErr.underlying).toBeInstanceOf(Error);
270
+ // Underlying session was never created.
271
+ expect(newCdpSessionCalls).toBe(0);
272
+ });
273
+
274
+ test("send() retries ensureSession after a transient failure", async () => {
275
+ // First call: ensureSession rejects (transient browser launch error).
276
+ // Second call: ensureSession succeeds and the underlying CDP method
277
+ // resolves. The cached promise from the first failure must have
278
+ // been cleared so the second call actually retries.
279
+ getOrCreateSessionPageError = new Error("transient launch failure");
280
+ fakeSessionHandler = (method) => {
281
+ if (method === "Browser.getVersion") {
282
+ return { product: "HeadlessChrome/120.0.0.0" };
283
+ }
284
+ return undefined;
285
+ };
286
+ const client = createLocalCdpClient("conv-retry");
287
+
288
+ // First call → CdpError("transport_error") from ensureSession.
289
+ let firstErr: unknown;
290
+ try {
291
+ await client.send("Browser.getVersion");
292
+ } catch (err) {
293
+ firstErr = err;
294
+ }
295
+ expect(firstErr).toBeInstanceOf(CdpError);
296
+ expect((firstErr as InstanceType<typeof CdpError>).code).toBe(
297
+ "transport_error",
298
+ );
299
+ expect(getOrCreateSessionPageCalls).toBe(1);
300
+ expect(newCdpSessionCalls).toBe(0);
301
+
302
+ // Second call → cached promise was cleared, so ensureSession is
303
+ // re-invoked, getOrCreateSessionPage runs again, and the call
304
+ // succeeds against the freshly created session.
305
+ const result = await client.send<{ product: string }>("Browser.getVersion");
306
+ expect(result).toEqual({ product: "HeadlessChrome/120.0.0.0" });
307
+ expect(getOrCreateSessionPageCalls).toBe(2);
308
+ expect(newCdpSessionCalls).toBe(1);
309
+ });
310
+ });
@@ -0,0 +1,96 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ type CdpClient,
5
+ type CdpClientKind,
6
+ CdpError,
7
+ type CdpErrorCode,
8
+ type ScopedCdpClient,
9
+ } from "../index.js";
10
+
11
+ describe("CdpError", () => {
12
+ test("subclasses Error", () => {
13
+ const err = new CdpError("cdp_error", "boom");
14
+ expect(err).toBeInstanceOf(Error);
15
+ expect(err).toBeInstanceOf(CdpError);
16
+ });
17
+
18
+ test("sets name to CdpError", () => {
19
+ const err = new CdpError("aborted", "test");
20
+ expect(err.name).toBe("CdpError");
21
+ });
22
+
23
+ test("captures code, message, and default details", () => {
24
+ const err = new CdpError("aborted", "caller aborted");
25
+ expect(err.code).toBe("aborted");
26
+ expect(err.message).toBe("caller aborted");
27
+ expect(err.cdpMethod).toBeUndefined();
28
+ expect(err.cdpParams).toBeUndefined();
29
+ expect(err.underlying).toBeUndefined();
30
+ });
31
+
32
+ test("captures cdpMethod, cdpParams, and underlying when provided", () => {
33
+ const cause = new Error("socket closed");
34
+ const err = new CdpError("transport_error", "send failed", {
35
+ cdpMethod: "Page.navigate",
36
+ cdpParams: { url: "https://example.com/" },
37
+ underlying: cause,
38
+ });
39
+ expect(err.code).toBe("transport_error");
40
+ expect(err.cdpMethod).toBe("Page.navigate");
41
+ expect(err.cdpParams).toEqual({ url: "https://example.com/" });
42
+ expect(err.underlying).toBe(cause);
43
+ });
44
+
45
+ test("supports all documented CdpErrorCode values", () => {
46
+ const codes: CdpErrorCode[] = [
47
+ "cdp_error",
48
+ "transport_error",
49
+ "aborted",
50
+ "disposed",
51
+ ];
52
+ for (const code of codes) {
53
+ const err = new CdpError(code, `code ${code}`);
54
+ expect(err.code).toBe(code);
55
+ }
56
+ });
57
+ });
58
+
59
+ describe("cdp-client re-exports", () => {
60
+ test("CdpError is reachable from index", () => {
61
+ expect(CdpError).toBeDefined();
62
+ expect(typeof CdpError).toBe("function");
63
+ });
64
+
65
+ test("CdpClient interface is structurally assignable", () => {
66
+ // Compile-time: ensure CdpClient is importable and can be implemented.
67
+ const stub: CdpClient = {
68
+ send: async <T = unknown>(
69
+ _method: string,
70
+ _params?: Record<string, unknown>,
71
+ _signal?: AbortSignal,
72
+ ): Promise<T> => {
73
+ throw new CdpError("disposed", "stub");
74
+ },
75
+ dispose: () => {},
76
+ };
77
+ expect(typeof stub.send).toBe("function");
78
+ expect(typeof stub.dispose).toBe("function");
79
+ });
80
+
81
+ test("ScopedCdpClient exposes kind and conversationId", () => {
82
+ const kinds: CdpClientKind[] = ["local", "extension", "cdp-inspect"];
83
+ for (const kind of kinds) {
84
+ const scoped: ScopedCdpClient = {
85
+ kind,
86
+ conversationId: "conv-123",
87
+ send: async <T = unknown>(): Promise<T> => {
88
+ throw new CdpError("disposed", "stub");
89
+ },
90
+ dispose: () => {},
91
+ };
92
+ expect(scoped.kind).toBe(kind);
93
+ expect(scoped.conversationId).toBe("conv-123");
94
+ }
95
+ });
96
+ });