@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
@@ -203,7 +203,13 @@ class BrowserManager {
203
203
  private pages = new Map<string, Page>();
204
204
  private rawPages = new Map<string, unknown>();
205
205
  private cdpSessions = new Map<string, CDPSession>();
206
- private snapshotMaps = new Map<string, Map<string, string>>();
206
+ /**
207
+ * CDP backendNodeId lookup per conversation, populated by the
208
+ * AX-tree-based `executeBrowserSnapshot`. Click/type/hover/etc. tools
209
+ * resolve an element_id against this map and talk to CDP with the
210
+ * resulting backendNodeId.
211
+ */
212
+ private snapshotBackendNodeMaps = new Map<string, Map<string, number>>();
207
213
  private browserCdpSession: CDPSession | null = null;
208
214
  private browserWindowId: number | null = null;
209
215
  private interactiveModeSessions = new Set<string>();
@@ -358,7 +364,7 @@ class BrowserManager {
358
364
  this.rawPages.clear();
359
365
  this.cdpSessions.clear();
360
366
 
361
- this.snapshotMaps.clear();
367
+ this.snapshotBackendNodeMaps.clear();
362
368
  this.downloads.clear();
363
369
  for (const pending of this.pendingDownloads.values()) {
364
370
  for (const waiter of pending)
@@ -384,7 +390,7 @@ class BrowserManager {
384
390
  }
385
391
 
386
392
  // Clear stale snapshot mappings and CDP state when replacing a closed page
387
- this.snapshotMaps.delete(conversationId);
393
+ this.snapshotBackendNodeMaps.delete(conversationId);
388
394
  await this.stopScreencast(conversationId);
389
395
 
390
396
  const page = await context.newPage();
@@ -437,7 +443,7 @@ class BrowserManager {
437
443
  }
438
444
  this.pages.delete(conversationId);
439
445
  this.rawPages.delete(conversationId);
440
- this.snapshotMaps.delete(conversationId);
446
+ this.snapshotBackendNodeMaps.delete(conversationId);
441
447
  this.downloads.delete(conversationId);
442
448
  // Reject any pending download waiters
443
449
  const pending = this.pendingDownloads.get(conversationId);
@@ -470,7 +476,7 @@ class BrowserManager {
470
476
  }
471
477
  this.pages.clear();
472
478
  this.rawPages.clear();
473
- this.snapshotMaps.clear();
479
+ this.snapshotBackendNodeMaps.clear();
474
480
  this.downloads.clear();
475
481
  for (const pending of this.pendingDownloads.values()) {
476
482
  for (const waiter of pending) waiter.reject(new Error("Browser closed"));
@@ -526,19 +532,37 @@ class BrowserManager {
526
532
  }
527
533
  }
528
534
 
529
- storeSnapshotMap(conversationId: string, map: Map<string, string>): void {
530
- this.snapshotMaps.set(conversationId, map);
535
+ /**
536
+ * Store the backendNodeId lookup produced by the AX-tree-based
537
+ * `executeBrowserSnapshot`. Callers overwrite any prior map for the
538
+ * conversation so each snapshot fully supersedes the previous one.
539
+ */
540
+ storeSnapshotBackendNodeMap(
541
+ conversationId: string,
542
+ map: Map<string, number>,
543
+ ): void {
544
+ this.snapshotBackendNodeMaps.set(conversationId, map);
531
545
  }
532
546
 
533
- clearSnapshotMap(conversationId: string): void {
534
- this.snapshotMaps.delete(conversationId);
547
+ /**
548
+ * Drop the backendNodeId lookup for a conversation so stale element
549
+ * IDs from a previous snapshot cannot resolve after a navigation or
550
+ * page close.
551
+ */
552
+ clearSnapshotBackendNodeMap(conversationId: string): void {
553
+ this.snapshotBackendNodeMaps.delete(conversationId);
535
554
  }
536
555
 
537
- resolveSnapshotSelector(
556
+ /**
557
+ * Look up the CDP backendNodeId for an element id produced by the
558
+ * most recent AX-tree snapshot. Returns `null` if no snapshot has
559
+ * been taken or the id is unknown.
560
+ */
561
+ resolveSnapshotBackendNodeId(
538
562
  conversationId: string,
539
563
  elementId: string,
540
- ): string | null {
541
- const map = this.snapshotMaps.get(conversationId);
564
+ ): number | null {
565
+ const map = this.snapshotBackendNodeMaps.get(conversationId);
542
566
  if (!map) return null;
543
567
  return map.get(elementId) ?? null;
544
568
  }
@@ -0,0 +1,318 @@
1
+ import { describe, expect, it } from "bun:test";
2
+
3
+ import {
4
+ type AxSnapshotElement,
5
+ type AxSnapshotResult,
6
+ formatAxSnapshot,
7
+ transformAxTree,
8
+ } from "../accessibility-snapshot.js";
9
+ import nestedFramesFixture from "./fixtures/ax-tree-nested-frames.json" with { type: "json" };
10
+ import simpleFixture from "./fixtures/ax-tree-simple.json" with { type: "json" };
11
+
12
+ // ── transformAxTree ──────────────────────────────────────────────────
13
+
14
+ describe("transformAxTree", () => {
15
+ it("happy path: simple fixture returns exactly 3 elements with stable eids", () => {
16
+ const result = transformAxTree(simpleFixture);
17
+
18
+ expect(result.elements).toHaveLength(3);
19
+
20
+ const [e1, e2, e3] = result.elements as [
21
+ AxSnapshotElement,
22
+ AxSnapshotElement,
23
+ AxSnapshotElement,
24
+ ];
25
+
26
+ // e1: the button (trimmed + focusable prop not surfaced as attr).
27
+ expect(e1.eid).toBe("e1");
28
+ expect(e1.role).toBe("button");
29
+ expect(e1.name).toBe("Submit Form");
30
+ expect(e1.backendNodeId).toBe(101);
31
+ expect(e1.attrs).toEqual({ disabled: "false" });
32
+ expect(e1.value).toBeUndefined();
33
+
34
+ // e2: the link carries its url property in attrs.
35
+ expect(e2.eid).toBe("e2");
36
+ expect(e2.role).toBe("link");
37
+ expect(e2.name).toBe("About Us");
38
+ expect(e2.backendNodeId).toBe(102);
39
+ expect(e2.attrs).toEqual({ url: "https://example.com/about" });
40
+
41
+ // e3: the textbox carries placeholder + required + a `value` field.
42
+ expect(e3.eid).toBe("e3");
43
+ expect(e3.role).toBe("textbox");
44
+ expect(e3.name).toBe("Email address");
45
+ expect(e3.backendNodeId).toBe(103);
46
+ expect(e3.value).toBe("user@example.com");
47
+ expect(e3.attrs).toEqual({
48
+ placeholder: "you@example.com",
49
+ required: "true",
50
+ });
51
+ });
52
+
53
+ it("filters out ignored nodes", () => {
54
+ const result = transformAxTree(simpleFixture);
55
+ for (const el of result.elements) {
56
+ expect(el.name).not.toBe("Hidden Action");
57
+ }
58
+ });
59
+
60
+ it("filters out nodes without backendDOMNodeId", () => {
61
+ const result = transformAxTree(simpleFixture);
62
+ for (const el of result.elements) {
63
+ expect(el.name).not.toBe("Orphan Button");
64
+ }
65
+ });
66
+
67
+ it("truncates to opts.maxElements", () => {
68
+ const result = transformAxTree(simpleFixture, { maxElements: 2 });
69
+ expect(result.elements).toHaveLength(2);
70
+ expect(result.elements[0]?.eid).toBe("e1");
71
+ expect(result.elements[1]?.eid).toBe("e2");
72
+ // selectorMap only contains the kept elements.
73
+ expect(result.selectorMap.size).toBe(2);
74
+ expect(result.selectorMap.get("e1")).toBe(101);
75
+ expect(result.selectorMap.get("e2")).toBe(102);
76
+ expect(result.selectorMap.has("e3")).toBe(false);
77
+ });
78
+
79
+ it("selectorMap.get() returns the backendNodeId for each eid", () => {
80
+ const result = transformAxTree(simpleFixture);
81
+ expect(result.selectorMap.size).toBe(3);
82
+ expect(result.selectorMap.get("e1")).toBe(101);
83
+ expect(result.selectorMap.get("e2")).toBe(102);
84
+ expect(result.selectorMap.get("e3")).toBe(103);
85
+ });
86
+
87
+ it("filters out non-interactive roles (StaticText, generic, RootWebArea)", () => {
88
+ const fixture = {
89
+ nodes: [
90
+ {
91
+ nodeId: "1",
92
+ role: { value: "RootWebArea" },
93
+ name: { value: "Root" },
94
+ backendDOMNodeId: 1,
95
+ childIds: ["2", "3", "4"],
96
+ ignored: false,
97
+ },
98
+ {
99
+ nodeId: "2",
100
+ role: { value: "StaticText" },
101
+ name: { value: "Just some text" },
102
+ backendDOMNodeId: 2,
103
+ childIds: [],
104
+ ignored: false,
105
+ },
106
+ {
107
+ nodeId: "3",
108
+ role: { value: "generic" },
109
+ name: { value: "A div" },
110
+ backendDOMNodeId: 3,
111
+ childIds: [],
112
+ ignored: false,
113
+ },
114
+ {
115
+ nodeId: "4",
116
+ role: { value: "button" },
117
+ name: { value: "Real Button" },
118
+ backendDOMNodeId: 4,
119
+ childIds: [],
120
+ ignored: false,
121
+ },
122
+ ],
123
+ };
124
+
125
+ const result = transformAxTree(fixture);
126
+ expect(result.elements).toHaveLength(1);
127
+ expect(result.elements[0]?.name).toBe("Real Button");
128
+ expect(result.elements[0]?.role).toBe("button");
129
+ });
130
+
131
+ it("includes focusable nodes even when the role is not interactive", () => {
132
+ // Regression test for contenteditable divs and custom widgets: a
133
+ // generic-role node with `focusable: true` must still surface.
134
+ const fixture = {
135
+ nodes: [
136
+ {
137
+ nodeId: "1",
138
+ role: { value: "RootWebArea" },
139
+ name: { value: "Root" },
140
+ backendDOMNodeId: 1,
141
+ childIds: ["2"],
142
+ ignored: false,
143
+ },
144
+ {
145
+ nodeId: "2",
146
+ role: { value: "generic" },
147
+ name: { value: "Contenteditable field" },
148
+ properties: [
149
+ { name: "focusable", value: { type: "boolean", value: true } },
150
+ ],
151
+ backendDOMNodeId: 42,
152
+ childIds: [],
153
+ ignored: false,
154
+ },
155
+ ],
156
+ };
157
+
158
+ const result = transformAxTree(fixture);
159
+ expect(result.elements).toHaveLength(1);
160
+ const [el] = result.elements;
161
+ expect(el?.role).toBe("generic");
162
+ expect(el?.backendNodeId).toBe(42);
163
+ expect(el?.name).toBe("Contenteditable field");
164
+ });
165
+
166
+ it("traverses nested RootWebArea (iframe) children in document order", () => {
167
+ const result = transformAxTree(nestedFramesFixture);
168
+
169
+ // Expect 4 kept elements across parent + inner frame, in doc order.
170
+ expect(result.elements).toHaveLength(4);
171
+ expect(result.elements.map((el) => el.name)).toEqual([
172
+ "Parent Button",
173
+ "Inner Link",
174
+ "Inner Search",
175
+ "Subscribe",
176
+ ]);
177
+ expect(result.elements.map((el) => el.backendNodeId)).toEqual([
178
+ 201, 203, 204, 205,
179
+ ]);
180
+ expect(result.elements.map((el) => el.eid)).toEqual([
181
+ "e1",
182
+ "e2",
183
+ "e3",
184
+ "e4",
185
+ ]);
186
+ });
187
+
188
+ it("truncates names longer than 80 chars", () => {
189
+ const longName = "x".repeat(200);
190
+ const fixture = {
191
+ nodes: [
192
+ {
193
+ nodeId: "1",
194
+ role: { value: "button" },
195
+ name: { value: longName },
196
+ backendDOMNodeId: 1,
197
+ childIds: [],
198
+ ignored: false,
199
+ },
200
+ ],
201
+ };
202
+ const result = transformAxTree(fixture);
203
+ expect(result.elements).toHaveLength(1);
204
+ expect(result.elements[0]?.name.length).toBe(80);
205
+ });
206
+
207
+ it("returns an empty result for malformed input", () => {
208
+ expect(transformAxTree(null)).toEqual({
209
+ elements: [],
210
+ selectorMap: new Map(),
211
+ });
212
+ expect(transformAxTree(undefined)).toEqual({
213
+ elements: [],
214
+ selectorMap: new Map(),
215
+ });
216
+ expect(transformAxTree({})).toEqual({
217
+ elements: [],
218
+ selectorMap: new Map(),
219
+ });
220
+ expect(transformAxTree({ nodes: [] })).toEqual({
221
+ elements: [],
222
+ selectorMap: new Map(),
223
+ });
224
+ });
225
+ });
226
+
227
+ // ── formatAxSnapshot ──────────────────────────────────────────────────
228
+
229
+ describe("formatAxSnapshot", () => {
230
+ it("byte-matches the legacy executeBrowserSnapshot format for the simple fixture", () => {
231
+ const result = transformAxTree(simpleFixture);
232
+ const formatted = formatAxSnapshot(result, {
233
+ url: "https://example.com/",
234
+ title: "Example",
235
+ });
236
+
237
+ const expected = [
238
+ "URL: https://example.com/",
239
+ "Title: Example",
240
+ "",
241
+ `[e1] <button disabled="false"> Submit Form`,
242
+ `[e2] <link url="https://example.com/about"> About Us`,
243
+ `[e3] <textbox placeholder="you@example.com" required="true" value="user@example.com"> Email address`,
244
+ "",
245
+ "3 interactive elements found.",
246
+ ].join("\n");
247
+
248
+ expect(formatted).toBe(expected);
249
+ });
250
+
251
+ it("uses '(none)' when title is empty", () => {
252
+ const result: AxSnapshotResult = {
253
+ elements: [],
254
+ selectorMap: new Map(),
255
+ };
256
+ const formatted = formatAxSnapshot(result, {
257
+ url: "about:blank",
258
+ title: "",
259
+ });
260
+ expect(formatted).toBe(
261
+ [
262
+ "URL: about:blank",
263
+ "Title: (none)",
264
+ "",
265
+ "(no interactive elements found)",
266
+ ].join("\n"),
267
+ );
268
+ });
269
+
270
+ it("renders 'interactive element' (singular) for a single-element result", () => {
271
+ const result: AxSnapshotResult = {
272
+ elements: [
273
+ {
274
+ eid: "e1",
275
+ role: "button",
276
+ name: "Click",
277
+ attrs: {},
278
+ backendNodeId: 1,
279
+ },
280
+ ],
281
+ selectorMap: new Map([["e1", 1]]),
282
+ };
283
+ const formatted = formatAxSnapshot(result, {
284
+ url: "https://example.com/",
285
+ title: "T",
286
+ });
287
+ expect(formatted).toBe(
288
+ [
289
+ "URL: https://example.com/",
290
+ "Title: T",
291
+ "",
292
+ "[e1] <button> Click",
293
+ "",
294
+ "1 interactive element found.",
295
+ ].join("\n"),
296
+ );
297
+ });
298
+
299
+ it("omits the trailing ' name' segment when name is empty", () => {
300
+ const result: AxSnapshotResult = {
301
+ elements: [
302
+ {
303
+ eid: "e1",
304
+ role: "button",
305
+ name: "",
306
+ attrs: {},
307
+ backendNodeId: 1,
308
+ },
309
+ ],
310
+ selectorMap: new Map([["e1", 1]]),
311
+ };
312
+ const formatted = formatAxSnapshot(result, {
313
+ url: "https://example.com/",
314
+ title: "T",
315
+ });
316
+ expect(formatted.split("\n")[3]).toBe("[e1] <button>");
317
+ });
318
+ });