@vellumai/assistant 0.6.1 → 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 (463) hide show
  1. package/bun.lock +40 -40
  2. package/bunfig.toml +3 -0
  3. package/docker-entrypoint.sh +12 -2
  4. package/docs/architecture/memory.md +1 -1
  5. package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
  6. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  7. package/openapi.yaml +184 -69
  8. package/package.json +41 -41
  9. package/scripts/generate-openapi.ts +1 -2
  10. package/src/__tests__/acp-session.test.ts +43 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  12. package/src/__tests__/app-executors.test.ts +1 -0
  13. package/src/__tests__/app-source-watcher.test.ts +37 -11
  14. package/src/__tests__/approval-routes-http.test.ts +178 -1
  15. package/src/__tests__/assistant-event-hub.test.ts +30 -0
  16. package/src/__tests__/browser-fill-credential.test.ts +229 -94
  17. package/src/__tests__/browser-manager.test.ts +40 -27
  18. package/src/__tests__/catalog-files.test.ts +862 -0
  19. package/src/__tests__/channel-approvals.test.ts +53 -0
  20. package/src/__tests__/checker.test.ts +104 -170
  21. package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
  22. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  23. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  24. package/src/__tests__/config-schema.test.ts +125 -48
  25. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  26. package/src/__tests__/context-overflow-approval.test.ts +21 -6
  27. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  28. package/src/__tests__/conversation-agent-loop.test.ts +1 -1
  29. package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
  30. package/src/__tests__/conversation-attachments.test.ts +80 -4
  31. package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
  32. package/src/__tests__/conversation-directories-parse.test.ts +105 -0
  33. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  34. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  35. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  36. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  37. package/src/__tests__/conversation-queue.test.ts +45 -2
  38. package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
  39. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  40. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  41. package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
  42. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  43. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  44. package/src/__tests__/conversation-store.test.ts +195 -0
  45. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  46. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
  47. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  48. package/src/__tests__/credential-vault-unit.test.ts +4 -4
  49. package/src/__tests__/credential-vault.test.ts +152 -13
  50. package/src/__tests__/credentials-cli.test.ts +2 -2
  51. package/src/__tests__/date-context.test.ts +4 -4
  52. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  53. package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
  54. package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
  55. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  56. package/src/__tests__/gemini-provider.test.ts +2 -2
  57. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  58. package/src/__tests__/headless-browser-interactions.test.ts +707 -371
  59. package/src/__tests__/headless-browser-navigate.test.ts +389 -47
  60. package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
  61. package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
  62. package/src/__tests__/host-bash-proxy.test.ts +150 -1
  63. package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
  64. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  65. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  66. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  67. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  68. package/src/__tests__/host-browser-routes.test.ts +198 -0
  69. package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
  70. package/src/__tests__/host-cu-proxy.test.ts +171 -1
  71. package/src/__tests__/host-file-proxy.test.ts +185 -1
  72. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  73. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  74. package/src/__tests__/host-shell-tool.test.ts +1 -11
  75. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  76. package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
  77. package/src/__tests__/inline-command-runner.test.ts +7 -5
  78. package/src/__tests__/integration-status.test.ts +6 -7
  79. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  80. package/src/__tests__/log-export-workspace.test.ts +190 -0
  81. package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
  82. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  83. package/src/__tests__/mcp-health-check.test.ts +10 -3
  84. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  85. package/src/__tests__/migration-export-http.test.ts +61 -2
  86. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  87. package/src/__tests__/migration-import-commit-http.test.ts +101 -1
  88. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  89. package/src/__tests__/navigate-settings-tab.test.ts +14 -1
  90. package/src/__tests__/notification-broadcaster.test.ts +65 -0
  91. package/src/__tests__/oauth-apps-routes.test.ts +17 -12
  92. package/src/__tests__/oauth-cli.test.ts +707 -60
  93. package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
  94. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  95. package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
  96. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  97. package/src/__tests__/oauth-providers-routes.test.ts +50 -14
  98. package/src/__tests__/oauth-store.test.ts +1386 -182
  99. package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
  100. package/src/__tests__/onboarding-template-contract.test.ts +74 -55
  101. package/src/__tests__/openai-provider.test.ts +2 -2
  102. package/src/__tests__/outlook-categories.test.ts +1 -1
  103. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  104. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  105. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  106. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  107. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  108. package/src/__tests__/outlook-trash.test.ts +1 -1
  109. package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
  110. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  111. package/src/__tests__/permission-mode.test.ts +28 -56
  112. package/src/__tests__/pkb-autoinject.test.ts +96 -0
  113. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  114. package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
  115. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  116. package/src/__tests__/require-fresh-approval.test.ts +40 -3
  117. package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
  118. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  119. package/src/__tests__/schedule-routes.test.ts +162 -0
  120. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  121. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  123. package/src/__tests__/set-permission-mode.test.ts +13 -250
  124. package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
  125. package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
  126. package/src/__tests__/slack-channel-config.test.ts +12 -15
  127. package/src/__tests__/subagent-detail.test.ts +44 -2
  128. package/src/__tests__/subagent-disposal.test.ts +1 -0
  129. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  130. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  131. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  132. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  134. package/src/__tests__/subagent-tools.test.ts +1 -0
  135. package/src/__tests__/subagent-types.test.ts +1 -0
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  137. package/src/__tests__/system-prompt.test.ts +72 -1
  138. package/src/__tests__/task-scheduler.test.ts +32 -6
  139. package/src/__tests__/telegram-config.test.ts +10 -13
  140. package/src/__tests__/terminal-sandbox.test.ts +1 -1
  141. package/src/__tests__/terminal-tools.test.ts +11 -5
  142. package/src/__tests__/test-preload.ts +14 -0
  143. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  144. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
  145. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
  146. package/src/__tests__/tool-executor.test.ts +0 -1
  147. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  148. package/src/__tests__/top-level-renderer.test.ts +73 -1
  149. package/src/__tests__/transport-hints-queue.test.ts +62 -0
  150. package/src/__tests__/trust-store.test.ts +4 -4
  151. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  152. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  153. package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
  154. package/src/__tests__/workspace-policy.test.ts +2 -7
  155. package/src/acp/client-handler.ts +30 -4
  156. package/src/agent/loop.ts +12 -35
  157. package/src/approvals/guardian-request-resolvers.ts +21 -15
  158. package/src/browser-session/__tests__/manager.test.ts +297 -0
  159. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  160. package/src/browser-session/backends/extension.ts +26 -0
  161. package/src/browser-session/backends/local.ts +24 -0
  162. package/src/browser-session/events.ts +164 -0
  163. package/src/browser-session/index.ts +27 -0
  164. package/src/browser-session/manager.ts +159 -0
  165. package/src/browser-session/types.ts +28 -0
  166. package/src/channels/__tests__/types.test.ts +134 -0
  167. package/src/channels/types.ts +55 -0
  168. package/src/cli/__tests__/run-assistant-command.ts +34 -7
  169. package/src/cli/__tests__/unknown-command.test.ts +33 -0
  170. package/src/cli/commands/browser-relay.ts +339 -409
  171. package/src/cli/commands/credentials.ts +3 -3
  172. package/src/cli/commands/default-action.ts +68 -1
  173. package/src/cli/commands/email.ts +18 -13
  174. package/src/cli/commands/mcp.ts +16 -4
  175. package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
  176. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  177. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  178. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  179. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
  180. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
  181. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
  182. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  183. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  184. package/src/cli/commands/oauth/apps.ts +7 -4
  185. package/src/cli/commands/oauth/connect.ts +16 -2
  186. package/src/cli/commands/oauth/disconnect.ts +1 -1
  187. package/src/cli/commands/oauth/providers.ts +200 -36
  188. package/src/cli/commands/oauth/shared.ts +5 -5
  189. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
  190. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  191. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  192. package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
  193. package/src/cli/commands/platform/index.ts +107 -10
  194. package/src/cli/commands/usage.ts +10 -9
  195. package/src/cli/lib/daemon-credential-client.ts +4 -0
  196. package/src/cli/program.ts +10 -3
  197. package/src/config/assistant-feature-flags.ts +59 -55
  198. package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
  199. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
  200. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  201. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  202. package/src/config/bundled-skills/contacts/SKILL.md +3 -0
  203. package/src/config/bundled-skills/document/SKILL.md +4 -0
  204. package/src/config/bundled-skills/gmail/SKILL.md +12 -7
  205. package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
  206. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
  207. package/src/config/bundled-skills/outlook/SKILL.md +7 -0
  208. package/src/config/bundled-skills/settings/TOOLS.json +1 -1
  209. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
  210. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  211. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  212. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  213. package/src/config/env-registry.ts +14 -0
  214. package/src/config/env.ts +21 -0
  215. package/src/config/feature-flag-registry.json +46 -7
  216. package/src/config/loader.ts +56 -1
  217. package/src/config/sanitize-for-transfer.ts +47 -0
  218. package/src/config/schema.ts +46 -5
  219. package/src/config/schemas/host-browser.ts +66 -0
  220. package/src/config/schemas/memory-lifecycle.ts +1 -1
  221. package/src/config/schemas/memory-retrieval.ts +103 -0
  222. package/src/config/schemas/security.ts +0 -6
  223. package/src/config/schemas/services.ts +16 -0
  224. package/src/config/types.ts +0 -1
  225. package/src/context/post-turn-tool-result-truncation.ts +176 -0
  226. package/src/context/window-manager.ts +19 -1
  227. package/src/credential-execution/approval-bridge.ts +49 -16
  228. package/src/credential-execution/managed-catalog.ts +3 -7
  229. package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
  230. package/src/daemon/app-source-watcher.ts +35 -0
  231. package/src/daemon/config-watcher.ts +6 -2
  232. package/src/daemon/context-overflow-approval.ts +5 -1
  233. package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
  234. package/src/daemon/conversation-agent-loop.ts +74 -19
  235. package/src/daemon/conversation-attachments.ts +40 -1
  236. package/src/daemon/conversation-messaging.ts +3 -0
  237. package/src/daemon/conversation-process.ts +66 -3
  238. package/src/daemon/conversation-queue-manager.ts +8 -0
  239. package/src/daemon/conversation-runtime-assembly.ts +159 -20
  240. package/src/daemon/conversation-surfaces.ts +78 -12
  241. package/src/daemon/conversation-tool-setup.ts +74 -11
  242. package/src/daemon/conversation-workspace.ts +12 -0
  243. package/src/daemon/conversation.ts +227 -11
  244. package/src/daemon/date-context.ts +10 -10
  245. package/src/daemon/first-greeting.ts +3 -2
  246. package/src/daemon/handlers/conversations.ts +9 -139
  247. package/src/daemon/handlers/shared.ts +65 -0
  248. package/src/daemon/handlers/skills.ts +232 -37
  249. package/src/daemon/host-bash-proxy.ts +48 -13
  250. package/src/daemon/host-browser-proxy.ts +191 -0
  251. package/src/daemon/host-cu-proxy.ts +36 -11
  252. package/src/daemon/host-file-proxy.ts +57 -9
  253. package/src/daemon/lifecycle.ts +86 -12
  254. package/src/daemon/message-protocol.ts +7 -0
  255. package/src/daemon/message-types/conversations.ts +59 -13
  256. package/src/daemon/message-types/host-browser.ts +100 -0
  257. package/src/daemon/message-types/messages.ts +5 -6
  258. package/src/daemon/message-types/notifications.ts +12 -0
  259. package/src/daemon/message-types/settings.ts +12 -0
  260. package/src/daemon/message-types/skills.ts +10 -0
  261. package/src/daemon/message-types/subagents.ts +2 -0
  262. package/src/daemon/server.ts +112 -35
  263. package/src/daemon/tool-side-effects.ts +6 -0
  264. package/src/daemon/transport-hints.ts +14 -0
  265. package/src/inbound/platform-callback-registration.ts +18 -17
  266. package/src/index.ts +1 -1
  267. package/src/mcp/client.ts +59 -24
  268. package/src/memory/app-store.ts +31 -1
  269. package/src/memory/conversation-crud.ts +38 -10
  270. package/src/memory/conversation-directories.ts +39 -0
  271. package/src/memory/conversation-group-migration.ts +65 -5
  272. package/src/memory/conversation-starters-cadence.ts +76 -0
  273. package/src/memory/conversation-title-service.ts +5 -2
  274. package/src/memory/db-init.ts +12 -0
  275. package/src/memory/embedding-backend.test.ts +75 -0
  276. package/src/memory/embedding-backend.ts +131 -5
  277. package/src/memory/embedding-gemini.test.ts +54 -0
  278. package/src/memory/embedding-gemini.ts +20 -9
  279. package/src/memory/embedding-local.ts +177 -18
  280. package/src/memory/graph/capability-seed.ts +3 -5
  281. package/src/memory/graph/consolidation.ts +10 -23
  282. package/src/memory/graph/extraction-job.ts +15 -0
  283. package/src/memory/graph/retriever.ts +40 -22
  284. package/src/memory/graph/store.test.ts +7 -3
  285. package/src/memory/graph/store.ts +47 -12
  286. package/src/memory/group-crud.ts +25 -9
  287. package/src/memory/llm-usage-store.ts +45 -4
  288. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  289. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  290. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  291. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  292. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  293. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  294. package/src/memory/migrations/index.ts +6 -0
  295. package/src/memory/migrations/registry.ts +8 -0
  296. package/src/memory/schema/conversations.ts +1 -0
  297. package/src/memory/schema/oauth.ts +18 -13
  298. package/src/messaging/provider.ts +1 -1
  299. package/src/notifications/broadcaster.ts +6 -0
  300. package/src/notifications/conversation-pairing.ts +12 -4
  301. package/src/notifications/emit-signal.ts +14 -0
  302. package/src/notifications/signal.ts +11 -0
  303. package/src/oauth/AGENTS.md +76 -0
  304. package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
  305. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  306. package/src/oauth/byo-connection.test.ts +8 -8
  307. package/src/oauth/byo-connection.ts +7 -7
  308. package/src/oauth/connect-orchestrator.ts +23 -21
  309. package/src/oauth/connect-types.ts +3 -3
  310. package/src/oauth/connection-resolver.test.ts +17 -4
  311. package/src/oauth/connection-resolver.ts +16 -16
  312. package/src/oauth/connection.ts +1 -1
  313. package/src/oauth/manual-token-connection.ts +13 -13
  314. package/src/oauth/oauth-store.ts +214 -100
  315. package/src/oauth/platform-connection.test.ts +5 -5
  316. package/src/oauth/platform-connection.ts +4 -4
  317. package/src/oauth/provider-serializer.ts +31 -5
  318. package/src/oauth/revoke.ts +76 -0
  319. package/src/oauth/seed-providers.ts +127 -87
  320. package/src/oauth/token-persistence.ts +1 -1
  321. package/src/permissions/checker.ts +3 -3
  322. package/src/permissions/defaults.ts +7 -8
  323. package/src/permissions/permission-mode.ts +4 -11
  324. package/src/permissions/prompter.ts +13 -3
  325. package/src/permissions/v2-consent-policy.ts +87 -0
  326. package/src/platform/client.ts +1 -1
  327. package/src/prompts/system-prompt.ts +18 -21
  328. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  329. package/src/prompts/templates/BOOTSTRAP.md +59 -96
  330. package/src/prompts/templates/SOUL.md +11 -11
  331. package/src/providers/anthropic/client.ts +1 -0
  332. package/src/providers/types.ts +1 -1
  333. package/src/runtime/AGENTS.md +23 -0
  334. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  335. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  336. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  337. package/src/runtime/assistant-event-hub.ts +24 -2
  338. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  339. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  340. package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
  341. package/src/runtime/auth/middleware.ts +98 -0
  342. package/src/runtime/auth/route-policy.ts +6 -7
  343. package/src/runtime/auth/token-service.ts +8 -0
  344. package/src/runtime/capability-tokens.ts +414 -0
  345. package/src/runtime/channel-approvals.ts +18 -5
  346. package/src/runtime/chrome-extension-registry.ts +332 -0
  347. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  348. package/src/runtime/guardian-decision-types.ts +7 -0
  349. package/src/runtime/http-server.ts +425 -70
  350. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  351. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  352. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
  353. package/src/runtime/migrations/migration-transport.ts +6 -0
  354. package/src/runtime/migrations/migration-wizard.ts +22 -2
  355. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  356. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  357. package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
  358. package/src/runtime/migrations/vbundle-importer.ts +55 -5
  359. package/src/runtime/pending-interactions.ts +29 -13
  360. package/src/runtime/routes/approval-routes.ts +90 -16
  361. package/src/runtime/routes/browser-cdp-routes.ts +229 -0
  362. package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
  363. package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
  364. package/src/runtime/routes/conversation-management-routes.ts +108 -0
  365. package/src/runtime/routes/conversation-routes.ts +308 -28
  366. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  367. package/src/runtime/routes/group-routes.ts +22 -8
  368. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  369. package/src/runtime/routes/host-browser-routes.ts +279 -0
  370. package/src/runtime/routes/host-file-routes.ts +9 -1
  371. package/src/runtime/routes/identity-routes.ts +259 -16
  372. package/src/runtime/routes/log-export/AGENTS.md +104 -0
  373. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
  374. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
  375. package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
  376. package/src/runtime/routes/log-export-routes.ts +60 -25
  377. package/src/runtime/routes/memory-item-routes.ts +1 -7
  378. package/src/runtime/routes/migration-routes.ts +87 -2
  379. package/src/runtime/routes/oauth-apps.ts +15 -17
  380. package/src/runtime/routes/oauth-providers.ts +4 -0
  381. package/src/runtime/routes/schedule-routes.ts +24 -11
  382. package/src/runtime/routes/settings-routes.ts +9 -97
  383. package/src/runtime/routes/skills-routes.ts +52 -2
  384. package/src/runtime/routes/subagents-routes.ts +14 -10
  385. package/src/runtime/routes/usage-routes.ts +8 -7
  386. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  387. package/src/runtime/routes/workspace-routes.ts +8 -1
  388. package/src/runtime/routes/workspace-utils.ts +2 -0
  389. package/src/schedule/scheduler.ts +7 -5
  390. package/src/security/ces-credential-client.ts +20 -0
  391. package/src/security/ces-rpc-credential-backend.ts +17 -0
  392. package/src/security/credential-backend.ts +5 -0
  393. package/src/security/oauth2.ts +42 -25
  394. package/src/security/secure-keys.ts +118 -25
  395. package/src/security/token-manager.ts +23 -10
  396. package/src/skills/catalog-files.ts +492 -0
  397. package/src/skills/inline-command-runner.ts +12 -14
  398. package/src/subagent/manager.ts +131 -26
  399. package/src/subagent/types.ts +19 -0
  400. package/src/tools/apps/executors.ts +11 -2
  401. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  402. package/src/tools/browser/auth-detector.ts +43 -12
  403. package/src/tools/browser/browser-execution.ts +645 -340
  404. package/src/tools/browser/browser-manager.ts +36 -12
  405. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  406. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  407. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
  408. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
  409. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
  410. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  411. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  412. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  413. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  414. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  415. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  416. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
  417. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  418. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
  419. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  420. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
  421. package/src/tools/browser/cdp-client/errors.ts +34 -0
  422. package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
  423. package/src/tools/browser/cdp-client/factory.ts +204 -0
  424. package/src/tools/browser/cdp-client/index.ts +14 -0
  425. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  426. package/src/tools/browser/cdp-client/types.ts +52 -0
  427. package/src/tools/filesystem/edit.ts +1 -1
  428. package/src/tools/filesystem/list.ts +1 -1
  429. package/src/tools/filesystem/read.ts +1 -1
  430. package/src/tools/filesystem/write.ts +2 -1
  431. package/src/tools/host-filesystem/edit.ts +1 -1
  432. package/src/tools/host-filesystem/read.ts +12 -15
  433. package/src/tools/host-filesystem/write.ts +1 -1
  434. package/src/tools/host-terminal/host-shell.ts +21 -16
  435. package/src/tools/permission-checker.ts +77 -100
  436. package/src/tools/registry.ts +0 -2
  437. package/src/tools/secret-detection-handler.ts +34 -1
  438. package/src/tools/shared/filesystem/image-read.ts +61 -40
  439. package/src/tools/skills/sandbox-runner.ts +3 -6
  440. package/src/tools/subagent/spawn.ts +47 -3
  441. package/src/tools/subagent/status.ts +2 -0
  442. package/src/tools/system/register.ts +2 -16
  443. package/src/tools/terminal/safe-env.ts +7 -0
  444. package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
  445. package/src/tools/terminal/sandbox.ts +4 -1
  446. package/src/tools/terminal/shell.ts +24 -21
  447. package/src/tools/tool-approval-handler.ts +48 -2
  448. package/src/tools/types.ts +2 -3
  449. package/src/util/platform.ts +14 -19
  450. package/src/watcher/provider-types.ts +1 -1
  451. package/src/workspace/migrations/029-seed-pkb.ts +1 -0
  452. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
  453. package/src/workspace/migrations/registry.ts +2 -0
  454. package/src/workspace/top-level-renderer.ts +19 -1
  455. package/src/__tests__/chrome-cdp.test.ts +0 -419
  456. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  457. package/src/__tests__/permission-mode-store.test.ts +0 -277
  458. package/src/browser-extension-relay/protocol.ts +0 -63
  459. package/src/browser-extension-relay/server.ts +0 -203
  460. package/src/config/schemas/sandbox.ts +0 -14
  461. package/src/permissions/permission-mode-store.ts +0 -180
  462. package/src/tools/browser/chrome-cdp.ts +0 -239
  463. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -0,0 +1,635 @@
1
+ import { getLogger } from "../../../util/logger.js";
2
+ import {
3
+ type DevToolsTarget,
4
+ type DevToolsVersionInfo,
5
+ listDevToolsTargets,
6
+ pickDefaultTarget,
7
+ probeDevToolsJsonVersion,
8
+ } from "./cdp-inspect/discovery.js";
9
+ import {
10
+ type CdpWsTransport,
11
+ CdpWsTransportError,
12
+ connectCdpWsTransport,
13
+ } from "./cdp-inspect/ws-transport.js";
14
+ import { CdpError } from "./errors.js";
15
+ import type { CdpClientKind, ScopedCdpClient } from "./types.js";
16
+
17
+ const log = getLogger("cdp-inspect-client");
18
+
19
+ /**
20
+ * Default timeout (ms) for each discovery HTTP probe. Kept short so a
21
+ * user who has no chrome running on the configured port fails fast
22
+ * instead of blocking the entire tool invocation. The ws-transport
23
+ * has its own, separate connect timeout.
24
+ */
25
+ const DEFAULT_DISCOVERY_TIMEOUT_MS = 2_000;
26
+
27
+ /**
28
+ * Subset of DevTools endpoint config the CdpInspectClient needs. The
29
+ * higher-level factory is responsible for feeding these values
30
+ * from the user's settings. Everything else — connect timeouts, ws
31
+ * retries, abort plumbing — is controlled locally here so we don't
32
+ * leak transport knobs into tool call sites.
33
+ */
34
+ export interface CdpInspectClientOptions {
35
+ /** Loopback host — enforced by discovery helpers before any I/O. */
36
+ host: string;
37
+ /** Port the user's Chrome is listening on for DevTools HTTP. */
38
+ port: number;
39
+ /** Optional per-attach discovery probe timeout. */
40
+ discoveryTimeoutMs?: number;
41
+ /**
42
+ * Optional per-attach ws connect timeout. Forwarded verbatim to
43
+ * {@link connectCdpWsTransport}.
44
+ */
45
+ wsConnectTimeoutMs?: number;
46
+ /**
47
+ * Test seam: override the discovery / transport helpers so unit
48
+ * tests don't need a real Chrome or a Bun.serve-backed fake peer.
49
+ * The factory does not use this path.
50
+ */
51
+ helpers?: CdpInspectHelpers;
52
+ }
53
+
54
+ /**
55
+ * Override shape used by tests. Each field defaults to the real
56
+ * implementation imported at the top of this module when omitted.
57
+ */
58
+ export interface CdpInspectHelpers {
59
+ probeDevToolsJsonVersion?: typeof probeDevToolsJsonVersion;
60
+ listDevToolsTargets?: typeof listDevToolsTargets;
61
+ pickDefaultTarget?: typeof pickDefaultTarget;
62
+ connectCdpWsTransport?: typeof connectCdpWsTransport;
63
+ }
64
+
65
+ interface AttachedSession {
66
+ transport: CdpWsTransport;
67
+ sessionId: string;
68
+ target: DevToolsTarget;
69
+ version: DevToolsVersionInfo;
70
+ }
71
+
72
+ /**
73
+ * In-flight attach handle. Wraps the shared attach promise with a
74
+ * dedicated {@link AbortController} and a ref-count of live callers
75
+ * waiting on the attach. When every caller has raced its own signal
76
+ * and given up, the ref-count drops to zero and the shared controller
77
+ * aborts so the underlying discovery / ws / `Target.attachToTarget`
78
+ * work stops promptly instead of wedging the socket.
79
+ */
80
+ interface PendingAttach {
81
+ /** Shared attach promise — resolved exactly once per attach attempt. */
82
+ promise: Promise<AttachedSession>;
83
+ /** Controller wired through probe / list / connect / attach. */
84
+ controller: AbortController;
85
+ /** Number of live callers still awaiting this attach. */
86
+ waiters: number;
87
+ }
88
+
89
+ /**
90
+ * CdpClient backed by the DevTools JSON protocol over a raw
91
+ * WebSocket (the `cdp-inspect` transport). Composes the discovery
92
+ * helpers (`probeDevToolsJsonVersion` + `listDevToolsTargets` +
93
+ * `pickDefaultTarget`) with the shared `connectCdpWsTransport` to
94
+ * reach an already-running Chrome instance the user has launched
95
+ * with `--remote-debugging-port`.
96
+ *
97
+ * Lifetime mirrors {@link import("./local-cdp-client.js").LocalCdpClient}:
98
+ *
99
+ * - Lazy one-time attach: the first `send()` performs version probe
100
+ * + target discovery + ws connect + `Target.attachToTarget`, then
101
+ * caches the session for every subsequent call.
102
+ * - Concurrent callers share a single in-flight attach promise so
103
+ * `Target.attachToTarget` runs exactly once per client instance.
104
+ * - Each `send(..., signal)` caller can race its own AbortSignal
105
+ * against the shared attach and cut through promptly. When every
106
+ * concurrent caller has aborted, the shared attach work is also
107
+ * cancelled so we don't leak discovery fetches or a half-open ws.
108
+ * - If the attach promise rejects, the cached promise is cleared so
109
+ * the next `send()` retries from scratch instead of replaying the
110
+ * same failure forever.
111
+ * - `dispose()` is idempotent and tears down the ws transport if an
112
+ * attach ever resolved.
113
+ */
114
+ export class CdpInspectClient implements ScopedCdpClient {
115
+ readonly kind: CdpClientKind = "cdp-inspect";
116
+
117
+ private pending: PendingAttach | null = null;
118
+ private session: AttachedSession | null = null;
119
+ private disposed = false;
120
+ private readonly helpers: Required<CdpInspectHelpers>;
121
+
122
+ constructor(
123
+ public readonly conversationId: string,
124
+ private readonly options: CdpInspectClientOptions,
125
+ ) {
126
+ this.helpers = {
127
+ probeDevToolsJsonVersion:
128
+ options.helpers?.probeDevToolsJsonVersion ?? probeDevToolsJsonVersion,
129
+ listDevToolsTargets:
130
+ options.helpers?.listDevToolsTargets ?? listDevToolsTargets,
131
+ pickDefaultTarget:
132
+ options.helpers?.pickDefaultTarget ?? pickDefaultTarget,
133
+ connectCdpWsTransport:
134
+ options.helpers?.connectCdpWsTransport ?? connectCdpWsTransport,
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Lazily attach (and cache) a CDP session against the configured
140
+ * DevTools endpoint. Each caller races its own `signal` against the
141
+ * shared attach so an individual abort always wins promptly; when
142
+ * every waiter has aborted, the shared attach work is cancelled
143
+ * too via the pending attach's internal controller. See class-level
144
+ * docs for the resilience contract — in particular, transient
145
+ * attach failures must NOT poison the cached promise for subsequent
146
+ * calls.
147
+ */
148
+ private async ensureSession(signal?: AbortSignal): Promise<AttachedSession> {
149
+ if (this.disposed) {
150
+ throw new CdpError("disposed", "CdpInspectClient already disposed");
151
+ }
152
+ if (this.session) return this.session;
153
+
154
+ const pending = this.pending ?? this.startAttach();
155
+ pending.waiters += 1;
156
+
157
+ // `onAbort` fires exactly once if this caller's signal wins the
158
+ // race. It (a) drops this caller's waiter slot and (b) aborts
159
+ // the shared controller iff no other caller is still listening,
160
+ // so the underlying discovery / ws / attach work is cancelled
161
+ // promptly instead of leaking into the background.
162
+ let released = false;
163
+ const onAbort = () => {
164
+ if (released) return;
165
+ released = true;
166
+ pending.waiters -= 1;
167
+ if (pending.waiters <= 0 && this.pending === pending) {
168
+ // Clear the cached pending attach synchronously BEFORE
169
+ // firing the shared controller's abort. Otherwise a new
170
+ // `send()` that enters `ensureSession` between this abort
171
+ // and the async `.catch()` handler in `startAttach()` would
172
+ // reuse this already-aborted attach and immediately fail
173
+ // with an `aborted` error even though the new caller never
174
+ // aborted its own signal.
175
+ this.pending = null;
176
+ try {
177
+ pending.controller.abort();
178
+ } catch {
179
+ // best effort
180
+ }
181
+ }
182
+ };
183
+
184
+ try {
185
+ return await raceAbort(pending.promise, signal, onAbort);
186
+ } finally {
187
+ // Inner attach resolved or rejected before this caller's
188
+ // signal fired — drop the waiter slot without touching the
189
+ // shared controller (it's already settled). If `onAbort` ran,
190
+ // `released` is already true and this is a no-op.
191
+ if (!released) {
192
+ released = true;
193
+ pending.waiters -= 1;
194
+ }
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Kick off a fresh shared attach. The returned {@link PendingAttach}
200
+ * is cached on `this.pending` until it either resolves (in which
201
+ * case the session is stashed on `this.session`) or rejects (in
202
+ * which case the cache is cleared so the next caller retries from
203
+ * scratch).
204
+ */
205
+ private startAttach(): PendingAttach {
206
+ const controller = new AbortController();
207
+ const pending: PendingAttach = {
208
+ controller,
209
+ waiters: 0,
210
+ promise: this.attach(controller.signal),
211
+ };
212
+ this.pending = pending;
213
+
214
+ pending.promise
215
+ .then((session) => {
216
+ // Another concurrent attach may have won the race (e.g. after
217
+ // a dispose + retry). Only cache the result if we're still
218
+ // the current attempt.
219
+ if (this.pending === pending) {
220
+ this.pending = null;
221
+ if (this.disposed) {
222
+ // Dispose landed before we could publish the session —
223
+ // tear down the transport immediately so we don't leak.
224
+ try {
225
+ session.transport.dispose();
226
+ } catch {
227
+ // best effort
228
+ }
229
+ return;
230
+ }
231
+ this.session = session;
232
+ } else {
233
+ // A stale attempt — drop the transport so we don't leak.
234
+ try {
235
+ session.transport.dispose();
236
+ } catch {
237
+ // best effort
238
+ }
239
+ }
240
+ })
241
+ .catch(() => {
242
+ // Clear the cached pending attach on rejection so the next
243
+ // call retries from scratch instead of replaying the same
244
+ // failure forever. Only clear if we're still the current
245
+ // attempt — a concurrent dispose may have already nulled it.
246
+ if (this.pending === pending) {
247
+ this.pending = null;
248
+ }
249
+ });
250
+
251
+ return pending;
252
+ }
253
+
254
+ /**
255
+ * Perform the actual discovery + ws-connect + attach sequence. All
256
+ * underlying errors are rethrown unchanged so the `send()` wrapper
257
+ * can map them to stable `CdpError` codes without double-wrapping
258
+ * the already-typed discovery / ws-transport errors.
259
+ *
260
+ * The `signal` here is the shared, internal {@link PendingAttach}
261
+ * signal — NOT the per-caller signal. It is aborted when the last
262
+ * caller interested in this attach has given up, or when `dispose()`
263
+ * races an in-flight attach.
264
+ */
265
+ private async attach(signal: AbortSignal): Promise<AttachedSession> {
266
+ const discoveryTimeoutMs =
267
+ this.options.discoveryTimeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS;
268
+ const { host, port } = this.options;
269
+
270
+ const version = await this.helpers.probeDevToolsJsonVersion({
271
+ host,
272
+ port,
273
+ timeoutMs: discoveryTimeoutMs,
274
+ signal,
275
+ });
276
+ if (signal.aborted) {
277
+ throw new CdpError("aborted", "CdpInspectClient attach aborted");
278
+ }
279
+ const targets = await this.helpers.listDevToolsTargets({
280
+ host,
281
+ port,
282
+ timeoutMs: discoveryTimeoutMs,
283
+ signal,
284
+ });
285
+ if (signal.aborted) {
286
+ throw new CdpError("aborted", "CdpInspectClient attach aborted");
287
+ }
288
+ const target = this.helpers.pickDefaultTarget(targets);
289
+
290
+ // Prefer the browser-level ws URL from the version probe because
291
+ // it lets us multiplex multiple attached targets through a single
292
+ // transport. Fall back to the target-specific URL if (for some
293
+ // reason) the version probe omitted it.
294
+ const wsUrl = version.webSocketDebuggerUrl || target.webSocketDebuggerUrl;
295
+ const transport = await this.helpers.connectCdpWsTransport(wsUrl, {
296
+ connectTimeoutMs: this.options.wsConnectTimeoutMs,
297
+ signal,
298
+ });
299
+
300
+ // If dispose() landed while connect was in flight, tear down the
301
+ // transport we just opened and surface a "disposed" CdpError to
302
+ // the caller so we don't leak a half-attached session.
303
+ if (this.disposed) {
304
+ try {
305
+ transport.dispose();
306
+ } catch {
307
+ // best effort
308
+ }
309
+ throw new CdpError("disposed", "CdpInspectClient disposed during attach");
310
+ }
311
+ if (signal.aborted) {
312
+ try {
313
+ transport.dispose();
314
+ } catch {
315
+ // best effort
316
+ }
317
+ throw new CdpError(
318
+ "aborted",
319
+ "CdpInspectClient attach aborted after ws connect",
320
+ );
321
+ }
322
+
323
+ let attachResult: unknown;
324
+ try {
325
+ attachResult = await transport.send<unknown>(
326
+ "Target.attachToTarget",
327
+ {
328
+ targetId: target.id,
329
+ flatten: true,
330
+ },
331
+ { signal },
332
+ );
333
+ } catch (err) {
334
+ // Attach failed — drop the transport we just opened so we don't
335
+ // leak the socket on retry.
336
+ try {
337
+ transport.dispose();
338
+ } catch {
339
+ // best effort
340
+ }
341
+ throw err;
342
+ }
343
+
344
+ const sessionId = extractSessionId(attachResult);
345
+ if (!sessionId) {
346
+ try {
347
+ transport.dispose();
348
+ } catch {
349
+ // best effort
350
+ }
351
+ throw new CdpError(
352
+ "cdp_error",
353
+ "Target.attachToTarget did not return a sessionId",
354
+ { cdpMethod: "Target.attachToTarget" },
355
+ );
356
+ }
357
+
358
+ log.debug(
359
+ {
360
+ conversationId: this.conversationId,
361
+ targetId: target.id,
362
+ sessionId,
363
+ },
364
+ "Attached CdpInspectClient session",
365
+ );
366
+
367
+ return { transport, sessionId, target, version };
368
+ }
369
+
370
+ async send<T = unknown>(
371
+ method: string,
372
+ params?: Record<string, unknown>,
373
+ signal?: AbortSignal,
374
+ ): Promise<T> {
375
+ if (this.disposed) {
376
+ throw new CdpError("disposed", "CdpInspectClient already disposed", {
377
+ cdpMethod: method,
378
+ cdpParams: params,
379
+ });
380
+ }
381
+ if (signal?.aborted) {
382
+ throw new CdpError("aborted", "Aborted before send", {
383
+ cdpMethod: method,
384
+ cdpParams: params,
385
+ });
386
+ }
387
+
388
+ let attached: AttachedSession;
389
+ try {
390
+ attached = await this.ensureSession(signal);
391
+ } catch (err) {
392
+ if (signal?.aborted) {
393
+ throw new CdpError("aborted", "Aborted during send", {
394
+ cdpMethod: method,
395
+ cdpParams: params,
396
+ underlying: err,
397
+ });
398
+ }
399
+ // If a concurrent dispose() aborted the shared attach under us,
400
+ // surface a stable "disposed" error instead of the incidental
401
+ // discovery / transport rejection that the aborted work threw.
402
+ if (this.disposed) {
403
+ throw new CdpError("disposed", "CdpInspectClient already disposed", {
404
+ cdpMethod: method,
405
+ cdpParams: params,
406
+ underlying: err,
407
+ });
408
+ }
409
+ throw mapEnsureSessionError(err, method, params);
410
+ }
411
+
412
+ // A late dispose may have landed while ensureSession was in
413
+ // flight — surface a "disposed" error instead of sending into a
414
+ // torn-down transport.
415
+ if (this.disposed) {
416
+ throw new CdpError("disposed", "CdpInspectClient already disposed", {
417
+ cdpMethod: method,
418
+ cdpParams: params,
419
+ });
420
+ }
421
+
422
+ try {
423
+ return (await attached.transport.send<T>(method, params, {
424
+ sessionId: attached.sessionId,
425
+ signal,
426
+ })) as T;
427
+ } catch (err) {
428
+ if (signal?.aborted) {
429
+ throw new CdpError("aborted", "Aborted during send", {
430
+ cdpMethod: method,
431
+ cdpParams: params,
432
+ underlying: err,
433
+ });
434
+ }
435
+ if (err instanceof CdpWsTransportError) {
436
+ if (err.code === "aborted") {
437
+ throw new CdpError("aborted", err.message, {
438
+ cdpMethod: method,
439
+ cdpParams: params,
440
+ underlying: err,
441
+ });
442
+ }
443
+ if (err.code === "cdp_error") {
444
+ throw new CdpError("cdp_error", err.cdpMessage ?? err.message, {
445
+ cdpMethod: method,
446
+ cdpParams: params,
447
+ underlying: err,
448
+ });
449
+ }
450
+ // closed / timeout / transport_error all map onto
451
+ // transport_error in the shared CdpClient taxonomy.
452
+ throw new CdpError("transport_error", err.message, {
453
+ cdpMethod: method,
454
+ cdpParams: params,
455
+ underlying: err,
456
+ });
457
+ }
458
+ if (err instanceof CdpError) {
459
+ throw err;
460
+ }
461
+ const msg = err instanceof Error ? err.message : String(err);
462
+ throw new CdpError("cdp_error", msg, {
463
+ cdpMethod: method,
464
+ cdpParams: params,
465
+ underlying: err,
466
+ });
467
+ }
468
+ }
469
+
470
+ dispose(): void {
471
+ if (this.disposed) return;
472
+ this.disposed = true;
473
+
474
+ // Cancel any in-flight attach so discovery / ws / Target.attach
475
+ // stop promptly. The `.then()` handler in startAttach() will
476
+ // tear down any transport that managed to open before dispose
477
+ // landed.
478
+ const pending = this.pending;
479
+ this.pending = null;
480
+ if (pending) {
481
+ try {
482
+ pending.controller.abort();
483
+ } catch {
484
+ // best effort
485
+ }
486
+ }
487
+
488
+ const session = this.session;
489
+ this.session = null;
490
+ if (session) {
491
+ try {
492
+ session.transport.dispose();
493
+ } catch (err) {
494
+ log.debug(
495
+ { err },
496
+ "CdpInspectClient: transport.dispose threw (ignored)",
497
+ );
498
+ }
499
+ }
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Classify an `ensureSession()` rejection into a stable CdpError
505
+ * code. Discovery + ws-transport failures become `transport_error`,
506
+ * while CDP-level errors returned by `Target.attachToTarget` become
507
+ * `cdp_error`. Already-typed CdpErrors (e.g. a missing-sessionId
508
+ * attach response or a concurrent dispose) are rewritten so that
509
+ * the internal `cdpMethod` (`"Target.attachToTarget"`) is replaced
510
+ * with the caller's method, while preserving the underlying error
511
+ * shape.
512
+ */
513
+ function mapEnsureSessionError(
514
+ err: unknown,
515
+ method: string,
516
+ params?: Record<string, unknown>,
517
+ ): CdpError {
518
+ if (err instanceof CdpError) {
519
+ // Rewrite cdpMethod to the caller's method so attach-stage
520
+ // metadata (e.g. "Target.attachToTarget") doesn't leak into the
521
+ // caller-visible error. Preserve code, message, and the original
522
+ // underlying error so logging / upstream mapping can still
523
+ // introspect the real cause.
524
+ return new CdpError(err.code, err.message, {
525
+ cdpMethod: method,
526
+ cdpParams: params,
527
+ underlying: err.underlying ?? err,
528
+ });
529
+ }
530
+ if (err instanceof CdpWsTransportError) {
531
+ if (err.code === "cdp_error") {
532
+ return new CdpError("cdp_error", err.cdpMessage ?? err.message, {
533
+ cdpMethod: method,
534
+ cdpParams: params,
535
+ underlying: err,
536
+ });
537
+ }
538
+ return new CdpError("transport_error", err.message, {
539
+ cdpMethod: method,
540
+ cdpParams: params,
541
+ underlying: err,
542
+ });
543
+ }
544
+ // DevToolsDiscoveryError (and any other non-CDP rejection) is
545
+ // treated as a transport-level failure.
546
+ const msg = err instanceof Error ? err.message : String(err);
547
+ return new CdpError("transport_error", msg, {
548
+ cdpMethod: method,
549
+ cdpParams: params,
550
+ underlying: err,
551
+ });
552
+ }
553
+
554
+ /**
555
+ * Race a long-running shared promise against a per-caller
556
+ * {@link AbortSignal}. When the signal fires first, the returned
557
+ * promise rejects with a synthetic `abort` error and the optional
558
+ * `onAbort` hook is invoked exactly once so callers can decrement
559
+ * ref-counts, release locks, etc. The underlying `inner` promise is
560
+ * intentionally NOT cancelled here — shared in-flight work is
561
+ * cancelled separately via the owning {@link PendingAttach}
562
+ * controller once every waiter has given up.
563
+ */
564
+ function raceAbort<T>(
565
+ inner: Promise<T>,
566
+ signal: AbortSignal | undefined,
567
+ onAbort: () => void,
568
+ ): Promise<T> {
569
+ if (!signal) return inner;
570
+ if (signal.aborted) {
571
+ try {
572
+ onAbort();
573
+ } catch {
574
+ // best effort
575
+ }
576
+ return Promise.reject(new Error("aborted"));
577
+ }
578
+ return new Promise<T>((resolve, reject) => {
579
+ let settled = false;
580
+ const handleAbort = () => {
581
+ if (settled) return;
582
+ settled = true;
583
+ try {
584
+ onAbort();
585
+ } catch {
586
+ // best effort
587
+ }
588
+ reject(new Error("aborted"));
589
+ };
590
+ signal.addEventListener("abort", handleAbort, { once: true });
591
+ inner.then(
592
+ (value) => {
593
+ if (settled) return;
594
+ settled = true;
595
+ signal.removeEventListener("abort", handleAbort);
596
+ resolve(value);
597
+ },
598
+ (err) => {
599
+ if (settled) return;
600
+ settled = true;
601
+ signal.removeEventListener("abort", handleAbort);
602
+ reject(err);
603
+ },
604
+ );
605
+ });
606
+ }
607
+
608
+ /**
609
+ * Pull the `sessionId` field out of a `Target.attachToTarget` CDP
610
+ * result. CDP returns an object shaped `{ sessionId: string }`; we
611
+ * guard defensively against malformed replies so a broken Chrome
612
+ * fork cannot silently send us into an un-typed send loop.
613
+ */
614
+ function extractSessionId(result: unknown): string | null {
615
+ if (!result || typeof result !== "object") return null;
616
+ const record = result as Record<string, unknown>;
617
+ const sessionId = record.sessionId;
618
+ if (typeof sessionId === "string" && sessionId.length > 0) {
619
+ return sessionId;
620
+ }
621
+ return null;
622
+ }
623
+
624
+ /**
625
+ * Factory for a fresh {@link CdpInspectClient} bound to a
626
+ * conversation. Keeping the constructor + factory split lets the
627
+ * cdp-client factory wires this up alongside local / extension
628
+ * without exposing the class directly to callers.
629
+ */
630
+ export function createCdpInspectClient(
631
+ conversationId: string,
632
+ options: CdpInspectClientOptions,
633
+ ): CdpInspectClient {
634
+ return new CdpInspectClient(conversationId, options);
635
+ }
@@ -0,0 +1,34 @@
1
+ export type CdpErrorCode =
2
+ | "cdp_error" // JSON-RPC error returned by CDP
3
+ | "transport_error" // underlying transport failed (socket closed, timeout)
4
+ | "aborted" // caller-provided AbortSignal fired
5
+ | "disposed"; // client.dispose() already called
6
+
7
+ /**
8
+ * Single error type thrown by all CdpClient implementations. Carries
9
+ * the offending CDP method + params for logging and a stable code so
10
+ * callers can branch without string-sniffing.
11
+ */
12
+ export class CdpError extends Error {
13
+ readonly code: CdpErrorCode;
14
+ readonly cdpMethod?: string;
15
+ readonly cdpParams?: Record<string, unknown>;
16
+ readonly underlying?: unknown;
17
+
18
+ constructor(
19
+ code: CdpErrorCode,
20
+ message: string,
21
+ details?: {
22
+ cdpMethod?: string;
23
+ cdpParams?: Record<string, unknown>;
24
+ underlying?: unknown;
25
+ },
26
+ ) {
27
+ super(message);
28
+ this.name = "CdpError";
29
+ this.code = code;
30
+ this.cdpMethod = details?.cdpMethod;
31
+ this.cdpParams = details?.cdpParams;
32
+ this.underlying = details?.underlying;
33
+ }
34
+ }