@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
@@ -1,63 +0,0 @@
1
- /**
2
- * Protocol types for the Chrome extension relay bridge.
3
- *
4
- * Messages flow:
5
- * Assistant → ExtensionRelayServer → WebSocket → Chrome Extension → Tab (JS eval)
6
- */
7
-
8
- export interface CookieSpec {
9
- url: string;
10
- name: string;
11
- value: string;
12
- domain?: string;
13
- path?: string;
14
- secure?: boolean;
15
- httpOnly?: boolean;
16
- expirationDate?: number;
17
- }
18
-
19
- /**
20
- * Command sent from the server to the extension over WebSocket.
21
- */
22
- export interface ExtensionCommand {
23
- id: string; // UUID
24
- action:
25
- | "evaluate"
26
- | "navigate"
27
- | "get_cookies"
28
- | "set_cookie"
29
- | "screenshot"
30
- | "find_tab"
31
- | "new_tab";
32
- tabId?: number;
33
- code?: string; // for evaluate
34
- url?: string; // for navigate / find_tab / new_tab
35
- domain?: string; // for get_cookies
36
- cookie?: CookieSpec;
37
- timeoutMs?: number;
38
- }
39
-
40
- /**
41
- * Response sent from the extension back to the server.
42
- */
43
- export interface ExtensionResponse {
44
- id: string;
45
- success: boolean;
46
- result?: unknown;
47
- error?: string;
48
- tabId?: number;
49
- }
50
-
51
- /**
52
- * Periodic heartbeat from the extension to the server.
53
- */
54
- export interface ExtensionHeartbeat {
55
- type: "heartbeat";
56
- extensionVersion: string;
57
- connectedTabs: number;
58
- }
59
-
60
- /**
61
- * Any message received from the extension (heartbeat or command response).
62
- */
63
- export type ExtensionInboundMessage = ExtensionHeartbeat | ExtensionResponse;
@@ -1,203 +0,0 @@
1
- /**
2
- * WebSocket server for the Chrome extension relay bridge.
3
- *
4
- * Holds a single active extension connection. Commands are sent as JSON
5
- * and matched to pending Promises by UUID.
6
- */
7
-
8
- import type { ServerWebSocket } from "bun";
9
-
10
- import { getLogger } from "../util/logger.js";
11
- import type {
12
- ExtensionCommand,
13
- ExtensionHeartbeat,
14
- ExtensionInboundMessage,
15
- ExtensionResponse,
16
- } from "./protocol.js";
17
-
18
- const log = getLogger("browser-extension-relay");
19
-
20
- const DEFAULT_COMMAND_TIMEOUT_MS = 30_000;
21
-
22
- interface PendingCommand {
23
- resolve: (value: ExtensionResponse) => void;
24
- reject: (reason: Error) => void;
25
- timer: ReturnType<typeof setTimeout>;
26
- }
27
-
28
- export interface BrowserRelayWebSocketData {
29
- wsType: "browser-relay";
30
- connectionId: string;
31
- }
32
-
33
- export interface ExtensionRelayStatus {
34
- connected: boolean;
35
- connectionId: string | null;
36
- lastHeartbeatAt: number | null;
37
- pendingCommandCount: number;
38
- }
39
-
40
- /**
41
- * Manages the single active Chrome extension WebSocket connection and
42
- * dispatches commands to it.
43
- */
44
- export class ExtensionRelayServer {
45
- private ws: ServerWebSocket<BrowserRelayWebSocketData> | null = null;
46
- private connectionId: string | null = null;
47
- private lastHeartbeatAt: number | null = null;
48
- private pendingCommands = new Map<string, PendingCommand>();
49
-
50
- // ── WebSocket lifecycle ────────────────────────────────────────────
51
-
52
- handleOpen(ws: ServerWebSocket<BrowserRelayWebSocketData>): void {
53
- const newId = ws.data.connectionId;
54
-
55
- if (this.ws) {
56
- log.warn(
57
- { oldConnectionId: this.connectionId, newConnectionId: newId },
58
- "Browser extension relay: new connection displaced an existing one",
59
- );
60
- try {
61
- this.ws.close(1001, "Displaced by new connection");
62
- } catch {
63
- // best-effort
64
- }
65
- this.rejectAllPending(
66
- new Error("Extension reconnected — previous commands cancelled"),
67
- );
68
- }
69
-
70
- this.ws = ws;
71
- this.connectionId = newId;
72
- log.info({ connectionId: newId }, "Browser extension relay connected");
73
- }
74
-
75
- handleMessage(
76
- ws: ServerWebSocket<BrowserRelayWebSocketData>,
77
- raw: string,
78
- ): void {
79
- let msg: ExtensionInboundMessage;
80
- try {
81
- msg = JSON.parse(raw) as ExtensionInboundMessage;
82
- } catch {
83
- log.warn(
84
- { connectionId: ws.data.connectionId },
85
- "Browser extension relay: failed to parse message",
86
- );
87
- return;
88
- }
89
-
90
- if ("type" in msg && msg.type === "heartbeat") {
91
- this.handleHeartbeat(msg);
92
- return;
93
- }
94
-
95
- // Otherwise it's a command response
96
- const response = msg as ExtensionResponse;
97
- const pending = this.pendingCommands.get(response.id);
98
- if (!pending) {
99
- log.warn(
100
- { id: response.id },
101
- "Browser extension relay: received response for unknown command id",
102
- );
103
- return;
104
- }
105
-
106
- clearTimeout(pending.timer);
107
- this.pendingCommands.delete(response.id);
108
- pending.resolve(response);
109
- }
110
-
111
- handleClose(
112
- ws: ServerWebSocket<BrowserRelayWebSocketData>,
113
- code: number,
114
- reason?: string,
115
- ): void {
116
- const closedId = ws.data.connectionId;
117
- if (this.connectionId !== closedId) {
118
- // Stale close for a displaced connection — ignore
119
- return;
120
- }
121
-
122
- log.info(
123
- { connectionId: closedId, code, reason },
124
- "Browser extension relay disconnected",
125
- );
126
- this.ws = null;
127
- this.connectionId = null;
128
- this.rejectAllPending(new Error(`Extension disconnected (code=${code})`));
129
- }
130
-
131
- // ── Command dispatch ───────────────────────────────────────────────
132
-
133
- /**
134
- * Send a command to the extension and wait for its response.
135
- */
136
- sendCommand(
137
- command: Omit<ExtensionCommand, "id">,
138
- timeoutMs: number = DEFAULT_COMMAND_TIMEOUT_MS,
139
- ): Promise<ExtensionResponse> {
140
- if (!this.ws) {
141
- return Promise.reject(new Error("Browser extension is not connected"));
142
- }
143
-
144
- const id = crypto.randomUUID();
145
- const fullCommand: ExtensionCommand = { ...command, id };
146
-
147
- return new Promise<ExtensionResponse>((resolve, reject) => {
148
- const timer = setTimeout(() => {
149
- this.pendingCommands.delete(id);
150
- reject(
151
- new Error(
152
- `Browser extension command timed out after ${timeoutMs}ms (action=${command.action})`,
153
- ),
154
- );
155
- }, timeoutMs);
156
-
157
- this.pendingCommands.set(id, { resolve, reject, timer });
158
-
159
- try {
160
- this.ws!.send(JSON.stringify(fullCommand));
161
- } catch (err) {
162
- clearTimeout(timer);
163
- this.pendingCommands.delete(id);
164
- reject(err instanceof Error ? err : new Error(String(err)));
165
- }
166
- });
167
- }
168
-
169
- // ── Status ─────────────────────────────────────────────────────────
170
-
171
- getStatus(): ExtensionRelayStatus {
172
- return {
173
- connected: !!this.ws,
174
- connectionId: this.connectionId,
175
- lastHeartbeatAt: this.lastHeartbeatAt,
176
- pendingCommandCount: this.pendingCommands.size,
177
- };
178
- }
179
-
180
- // ── Private helpers ────────────────────────────────────────────────
181
-
182
- private handleHeartbeat(msg: ExtensionHeartbeat): void {
183
- this.lastHeartbeatAt = Date.now();
184
- log.debug(
185
- {
186
- extensionVersion: msg.extensionVersion,
187
- connectedTabs: msg.connectedTabs,
188
- },
189
- "Browser extension heartbeat received",
190
- );
191
- }
192
-
193
- private rejectAllPending(err: Error): void {
194
- for (const [id, pending] of this.pendingCommands) {
195
- clearTimeout(pending.timer);
196
- pending.reject(err);
197
- this.pendingCommands.delete(id);
198
- }
199
- }
200
- }
201
-
202
- /** Module-level singleton — imported by http-server and client. */
203
- export const extensionRelayServer = new ExtensionRelayServer();
@@ -1,14 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const SandboxConfigSchema = z
4
- .object({
5
- enabled: z
6
- .boolean({ error: "sandbox.enabled must be a boolean" })
7
- .default(false)
8
- .describe(
9
- "Whether to run tool executions in a sandboxed environment for safety",
10
- ),
11
- })
12
- .describe("Sandbox configuration for isolating tool executions");
13
-
14
- export type SandboxConfig = z.infer<typeof SandboxConfigSchema>;
@@ -1,180 +0,0 @@
1
- /**
2
- * Singleton runtime store for the two-axis permission mode.
3
- *
4
- * Reads initial state from the `permissions` section of config.json on
5
- * initialization and persists mutations back to the config file via the
6
- * raw-config read/write helpers so env-var–derived keys are never leaked
7
- * to disk.
8
- *
9
- * Downstream consumers (e.g. SSE broadcast) register change listeners
10
- * via `onModeChanged()`.
11
- */
12
-
13
- import {
14
- invalidateConfigCache,
15
- loadConfig,
16
- loadRawConfig,
17
- saveRawConfig,
18
- } from "../config/loader.js";
19
- import { getLogger } from "../util/logger.js";
20
- import type { PermissionMode } from "./permission-mode.js";
21
- import { DEFAULT_PERMISSION_MODE } from "./permission-mode.js";
22
-
23
- const log = getLogger("permission-mode-store");
24
-
25
- // ---------------------------------------------------------------------------
26
- // Types
27
- // ---------------------------------------------------------------------------
28
-
29
- export type ModeChangeListener = (mode: PermissionMode) => void;
30
-
31
- // ---------------------------------------------------------------------------
32
- // Module-level state
33
- // ---------------------------------------------------------------------------
34
-
35
- let currentMode: PermissionMode = { ...DEFAULT_PERMISSION_MODE };
36
- let initialized = false;
37
- const listeners: ModeChangeListener[] = [];
38
-
39
- // ---------------------------------------------------------------------------
40
- // Internal helpers
41
- // ---------------------------------------------------------------------------
42
-
43
- function notifyListeners(): void {
44
- const snapshot = { ...currentMode };
45
- for (const listener of listeners) {
46
- try {
47
- listener(snapshot);
48
- } catch (err) {
49
- log.error({ err }, "Error in permission mode change listener");
50
- }
51
- }
52
- }
53
-
54
- /**
55
- * Persist the current in-memory permission mode to config.json.
56
- *
57
- * Uses the raw-config pattern (loadRawConfig → mutate → saveRawConfig) so
58
- * that env-var–derived fields (API keys, dataDir) are never written to disk.
59
- */
60
- function persistToConfig(): void {
61
- try {
62
- const raw = loadRawConfig();
63
-
64
- // Ensure the permissions object exists
65
- if (
66
- raw.permissions == null ||
67
- typeof raw.permissions !== "object" ||
68
- Array.isArray(raw.permissions)
69
- ) {
70
- raw.permissions = {};
71
- }
72
-
73
- const permissions = raw.permissions as Record<string, unknown>;
74
- permissions.askBeforeActing = currentMode.askBeforeActing;
75
- permissions.hostAccess = currentMode.hostAccess;
76
-
77
- saveRawConfig(raw);
78
-
79
- // Invalidate the cached config so the next loadConfig() picks up the
80
- // persisted values rather than returning stale in-memory state.
81
- invalidateConfigCache();
82
- } catch (err) {
83
- log.error({ err }, "Failed to persist permission mode to config");
84
- }
85
- }
86
-
87
- // ---------------------------------------------------------------------------
88
- // Public API
89
- // ---------------------------------------------------------------------------
90
-
91
- /**
92
- * Initialize the store from the current config. Safe to call multiple times;
93
- * subsequent calls are no-ops unless `resetForTesting()` has been called.
94
- */
95
- export function initPermissionModeStore(): void {
96
- if (initialized) return;
97
-
98
- try {
99
- const config = loadConfig();
100
- currentMode = {
101
- askBeforeActing: config.permissions.askBeforeActing,
102
- hostAccess: config.permissions.hostAccess,
103
- };
104
- } catch (err) {
105
- log.warn(
106
- { err },
107
- "Failed to load permission mode from config; using defaults",
108
- );
109
- currentMode = { ...DEFAULT_PERMISSION_MODE };
110
- }
111
-
112
- initialized = true;
113
- }
114
-
115
- /**
116
- * Return the current permission mode. Initializes from config on first call
117
- * if `initPermissionModeStore()` hasn't been called yet.
118
- */
119
- export function getMode(): PermissionMode {
120
- if (!initialized) {
121
- initPermissionModeStore();
122
- }
123
- return { ...currentMode };
124
- }
125
-
126
- /**
127
- * Update the `askBeforeActing` axis. Persists to config.json and notifies
128
- * change listeners.
129
- */
130
- export function setAskBeforeActing(value: boolean): void {
131
- if (!initialized) {
132
- initPermissionModeStore();
133
- }
134
-
135
- if (currentMode.askBeforeActing === value) return;
136
-
137
- currentMode.askBeforeActing = value;
138
- persistToConfig();
139
- notifyListeners();
140
- }
141
-
142
- /**
143
- * Update the `hostAccess` axis. Persists to config.json and notifies
144
- * change listeners.
145
- */
146
- export function setHostAccess(value: boolean): void {
147
- if (!initialized) {
148
- initPermissionModeStore();
149
- }
150
-
151
- if (currentMode.hostAccess === value) return;
152
-
153
- currentMode.hostAccess = value;
154
- persistToConfig();
155
- notifyListeners();
156
- }
157
-
158
- /**
159
- * Register a callback that fires whenever the permission mode changes.
160
- * Returns an unsubscribe function.
161
- */
162
- export function onModeChanged(callback: ModeChangeListener): () => void {
163
- listeners.push(callback);
164
- return () => {
165
- const idx = listeners.indexOf(callback);
166
- if (idx >= 0) {
167
- listeners.splice(idx, 1);
168
- }
169
- };
170
- }
171
-
172
- /**
173
- * Reset the store to uninitialized state. **Test-only** — production code
174
- * should never call this.
175
- */
176
- export function resetForTesting(): void {
177
- currentMode = { ...DEFAULT_PERMISSION_MODE };
178
- initialized = false;
179
- listeners.length = 0;
180
- }
@@ -1,239 +0,0 @@
1
- /**
2
- * Shared Chrome CDP session management.
3
- *
4
- * Consolidates the duplicated launch / readiness / window-management logic
5
- * that was previously copy-pasted across the Amazon and DoorDash CLIs.
6
- * Callers get back a {@link CdpSession} with structured metadata so they can
7
- * make cleanup decisions (e.g. only kill Chrome if *we* launched it).
8
- */
9
-
10
- import { execSync, spawn as spawnChild } from "node:child_process";
11
- import { homedir } from "node:os";
12
- import { join as pathJoin } from "node:path";
13
-
14
- // ---------------------------------------------------------------------------
15
- // Constants
16
- // ---------------------------------------------------------------------------
17
-
18
- const DEFAULT_CDP_PORT = 9222;
19
- const DEFAULT_CDP_BASE = `http://localhost:${DEFAULT_CDP_PORT}`;
20
- const DEFAULT_USER_DATA_DIR = pathJoin(
21
- homedir(),
22
- "Library/Application Support/Google/Chrome-CDP",
23
- );
24
- const CHROME_APP_PATH =
25
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
26
-
27
- // ---------------------------------------------------------------------------
28
- // Types
29
- // ---------------------------------------------------------------------------
30
-
31
- export interface CdpSession {
32
- /** Base URL for the CDP HTTP endpoints (e.g. `http://localhost:9222`). */
33
- baseUrl: string;
34
- /** Whether this helper launched Chrome (true) or it was already running (false). */
35
- launchedByUs: boolean;
36
- /** The `--user-data-dir` used for the Chrome instance. */
37
- userDataDir: string;
38
- }
39
-
40
- export interface EnsureChromeOptions {
41
- /** CDP port. Defaults to `9222`. */
42
- port?: number;
43
- /** User data directory for Chrome. Defaults to `~/Library/Application Support/Google/Chrome-CDP`. */
44
- userDataDir?: string;
45
- /** Initial URL to open when launching Chrome. */
46
- startUrl?: string;
47
- }
48
-
49
- // ---------------------------------------------------------------------------
50
- // Readiness check
51
- // ---------------------------------------------------------------------------
52
-
53
- /**
54
- * Returns `true` when a CDP endpoint is responding at the given base URL
55
- * and has at least one open page tab. A CDP endpoint with zero tabs is
56
- * stale and unusable - callers should treat it as not ready.
57
- */
58
- export async function isCdpReady(
59
- cdpBase: string = DEFAULT_CDP_BASE,
60
- ): Promise<boolean> {
61
- try {
62
- const res = await fetch(`${cdpBase}/json/version`);
63
- if (!res.ok) return false;
64
-
65
- // Verify there's at least one page tab - a CDP endpoint with no tabs
66
- // is a stale Chrome process that should be relaunched.
67
- const listRes = await fetch(`${cdpBase}/json/list`);
68
- if (!listRes.ok) return false;
69
- const targets = (await listRes.json()) as Array<{ type: string }>;
70
- return targets.some((t) => t.type === "page");
71
- } catch {
72
- return false;
73
- }
74
- }
75
-
76
- // ---------------------------------------------------------------------------
77
- // Launch / ensure
78
- // ---------------------------------------------------------------------------
79
-
80
- /**
81
- * Ensure a Chrome instance with CDP is available. If one is already listening
82
- * on the target port, returns immediately. Otherwise spawns a new detached
83
- * Chrome process and waits for the CDP endpoint to become ready.
84
- *
85
- * Returns a {@link CdpSession} with metadata about the running instance.
86
- */
87
- export async function ensureChromeWithCdp(
88
- options: EnsureChromeOptions = {},
89
- ): Promise<CdpSession> {
90
- const port = options.port ?? DEFAULT_CDP_PORT;
91
- const baseUrl = `http://localhost:${port}`;
92
- const userDataDir = options.userDataDir ?? DEFAULT_USER_DATA_DIR;
93
-
94
- if (await isCdpReady(baseUrl)) {
95
- return { baseUrl, launchedByUs: false, userDataDir };
96
- }
97
-
98
- // If CDP is responding but has no tabs (stale), kill the process holding the port.
99
- try {
100
- const versionRes = await fetch(`${baseUrl}/json/version`);
101
- if (versionRes.ok) {
102
- // Stale Chrome - CDP up but no tabs. Kill it so we can relaunch.
103
- try {
104
- execSync(`lsof -ti :${port} | xargs kill -9 2>/dev/null`, {
105
- stdio: "ignore",
106
- });
107
- } catch {
108
- // Ignore - process may have already exited.
109
- }
110
- // Brief wait for port to clear.
111
- await new Promise((r) => setTimeout(r, 500));
112
- }
113
- } catch {
114
- // CDP not responding at all - port is free, proceed to launch.
115
- }
116
-
117
- const args = [
118
- `--remote-debugging-port=${port}`,
119
- `--force-renderer-accessibility`,
120
- `--user-data-dir=${userDataDir}`,
121
- ];
122
- if (options.startUrl) {
123
- args.push(options.startUrl);
124
- }
125
-
126
- spawnChild(CHROME_APP_PATH, args, {
127
- detached: true,
128
- stdio: "ignore",
129
- }).unref();
130
-
131
- // Poll until CDP responds (up to 15 s)
132
- for (let i = 0; i < 30; i++) {
133
- await new Promise((r) => setTimeout(r, 500));
134
- if (await isCdpReady(baseUrl)) {
135
- return { baseUrl, launchedByUs: true, userDataDir };
136
- }
137
- }
138
-
139
- throw new Error("Chrome started but CDP endpoint not responding after 15s");
140
- }
141
-
142
- // ---------------------------------------------------------------------------
143
- // Window management helpers
144
- // ---------------------------------------------------------------------------
145
-
146
- /**
147
- * Look up the first page target and return its WebSocket debugger URL.
148
- */
149
- async function findPageTarget(cdpBase: string): Promise<string | null> {
150
- const res = await fetch(`${cdpBase}/json/list`);
151
- const targets = (await res.json()) as Array<{
152
- type: string;
153
- webSocketDebuggerUrl: string;
154
- }>;
155
- const page = targets.find((t) => t.type === "page");
156
- return page?.webSocketDebuggerUrl ?? null;
157
- }
158
-
159
- /**
160
- * Set the window state of the Chrome window owning the first page target.
161
- * Used by both minimize and restore.
162
- */
163
- async function setWindowState(
164
- cdpBase: string,
165
- windowState: "minimized" | "normal",
166
- ): Promise<void> {
167
- const wsUrl = await findPageTarget(cdpBase);
168
- if (!wsUrl) return;
169
-
170
- const ws = new WebSocket(wsUrl);
171
-
172
- await new Promise<void>((resolve, reject) => {
173
- const timeout = setTimeout(() => {
174
- ws.close();
175
- reject(new Error(`CDP ${windowState} timed out`));
176
- }, 5000);
177
-
178
- ws.addEventListener("open", () => {
179
- ws.send(JSON.stringify({ id: 1, method: "Browser.getWindowForTarget" }));
180
- });
181
-
182
- ws.addEventListener("message", (event) => {
183
- const msg = JSON.parse(String(event.data)) as {
184
- id: number;
185
- result?: { windowId: number };
186
- error?: { message: string };
187
- };
188
- if (msg.id === 1 && msg.result) {
189
- ws.send(
190
- JSON.stringify({
191
- id: 2,
192
- method: "Browser.setWindowBounds",
193
- params: {
194
- windowId: msg.result.windowId,
195
- bounds: { windowState },
196
- },
197
- }),
198
- );
199
- } else if (msg.id === 1) {
200
- clearTimeout(timeout);
201
- ws.close();
202
- reject(new Error("Browser.getWindowForTarget failed"));
203
- } else if (msg.id === 2) {
204
- clearTimeout(timeout);
205
- ws.close();
206
- if (msg.error) {
207
- reject(
208
- new Error(`Browser.setWindowBounds failed: ${msg.error.message}`),
209
- );
210
- } else {
211
- resolve();
212
- }
213
- }
214
- });
215
-
216
- ws.addEventListener("error", (err) => {
217
- clearTimeout(timeout);
218
- reject(err);
219
- });
220
- });
221
- }
222
-
223
- /**
224
- * Minimize the Chrome window associated with the CDP session.
225
- */
226
- export async function minimizeChromeWindow(
227
- cdpBase: string = DEFAULT_CDP_BASE,
228
- ): Promise<void> {
229
- await setWindowState(cdpBase, "minimized");
230
- }
231
-
232
- /**
233
- * Restore (un-minimize) the Chrome window associated with the CDP session.
234
- */
235
- export async function restoreChromeWindow(
236
- cdpBase: string = DEFAULT_CDP_BASE,
237
- ): Promise<void> {
238
- await setWindowState(cdpBase, "normal");
239
- }