@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
@@ -9,35 +9,82 @@ mock.module("../util/logger.js", () => ({
9
9
  }),
10
10
  }));
11
11
 
12
+ /**
13
+ * Fake CDP session driven by the real `LocalCdpClient` via the mocked
14
+ * `browserManager.getOrCreateSessionPage` below. `sendCalls` records
15
+ * every `session.send(method, params)` so tests can assert the exact
16
+ * sequence of CDP commands issued by `executeBrowserFillCredential`.
17
+ * `sendHandler` is replaced per test to shape responses or throw.
18
+ */
19
+ interface SendCall {
20
+ method: string;
21
+ params: Record<string, unknown> | undefined;
22
+ }
23
+
24
+ let sendCalls: SendCall[];
25
+ let sendHandler: (
26
+ method: string,
27
+ params: Record<string, unknown> | undefined,
28
+ ) => unknown;
29
+ let detachCalls: number;
30
+
31
+ function resetCdpMock() {
32
+ sendCalls = [];
33
+ detachCalls = 0;
34
+ sendHandler = defaultCdpHandler;
35
+ }
36
+
37
+ const fakeCdpSession = {
38
+ send: async (method: string, params?: Record<string, unknown>) => {
39
+ sendCalls.push({ method, params });
40
+ const value = sendHandler(method, params);
41
+ if (value instanceof Error) throw value;
42
+ return value;
43
+ },
44
+ detach: async () => {
45
+ detachCalls += 1;
46
+ },
47
+ };
48
+
49
+ /**
50
+ * Fake Playwright page that LocalCdpClient drives. Only the
51
+ * `context().newCDPSession()` surface is needed — all credential-fill
52
+ * work now flows through CDP.
53
+ */
12
54
  let mockPage: {
13
- click: ReturnType<typeof mock>;
14
- fill: ReturnType<typeof mock>;
15
- press: ReturnType<typeof mock>;
16
- evaluate: ReturnType<typeof mock>;
17
- title: ReturnType<typeof mock>;
18
- url: ReturnType<typeof mock>;
19
- goto: ReturnType<typeof mock>;
20
55
  close: () => Promise<void>;
21
56
  isClosed: () => boolean;
57
+ context: () => {
58
+ newCDPSession: (page: unknown) => Promise<typeof fakeCdpSession>;
59
+ };
22
60
  };
23
61
 
24
- let snapshotMaps: Map<string, Map<string, string>>;
62
+ let snapshotBackendNodeMaps: Map<string, Map<string, number>>;
25
63
 
26
64
  mock.module("../tools/browser/browser-manager.js", () => {
27
- snapshotMaps = new Map();
65
+ snapshotBackendNodeMaps = new Map();
28
66
  return {
29
67
  browserManager: {
30
68
  getOrCreateSessionPage: async () => mockPage,
31
69
  closeSessionPage: async () => {},
32
70
  closeAllPages: async () => {},
33
- storeSnapshotMap: (conversationId: string, map: Map<string, string>) => {
34
- snapshotMaps.set(conversationId, map);
71
+ storeSnapshotBackendNodeMap: (
72
+ conversationId: string,
73
+ map: Map<string, number>,
74
+ ) => {
75
+ snapshotBackendNodeMaps.set(conversationId, map);
35
76
  },
36
- resolveSnapshotSelector: (conversationId: string, elementId: string) => {
37
- const map = snapshotMaps.get(conversationId);
77
+ resolveSnapshotBackendNodeId: (
78
+ conversationId: string,
79
+ elementId: string,
80
+ ) => {
81
+ const map = snapshotBackendNodeMaps.get(conversationId);
38
82
  if (!map) return null;
39
83
  return map.get(elementId) ?? null;
40
84
  },
85
+ clearSnapshotBackendNodeMap: (conversationId: string) => {
86
+ snapshotBackendNodeMaps.delete(conversationId);
87
+ },
41
88
  },
42
89
  };
43
90
  });
@@ -82,21 +129,50 @@ const ctx: ToolContext = {
82
129
 
83
130
  function resetMockPage() {
84
131
  mockPage = {
85
- click: mock(async () => {}),
86
- fill: mock(async () => {}),
87
- press: mock(async () => {}),
88
- evaluate: mock(async () => ""),
89
- title: mock(async () => "Test Page"),
90
- url: mock(() => "https://example.com/"),
91
- goto: mock(async () => ({
92
- status: () => 200,
93
- url: () => "https://example.com/",
94
- })),
95
132
  close: async () => {},
96
133
  isClosed: () => false,
134
+ context: () => ({
135
+ newCDPSession: async (_page: unknown) => fakeCdpSession,
136
+ }),
97
137
  };
98
138
  }
99
139
 
140
+ /**
141
+ * Default CDP handler used by every test unless overridden. Returns
142
+ * the minimum plumbing needed for:
143
+ * - `getCurrentUrl` via Runtime.evaluate (for the broker domain check)
144
+ * - `querySelectorBackendNodeId` via DOM.getDocument / querySelector / describeNode
145
+ * - `focusElement` via DOM.focus
146
+ * - `clearAndInsertText` via DOM.resolveNode + Runtime.callFunctionOn + Input.insertText
147
+ * - `dispatchKeyPress` via Input.dispatchKeyEvent
148
+ */
149
+ function defaultCdpHandler(
150
+ method: string,
151
+ _params: Record<string, unknown> | undefined,
152
+ ): unknown {
153
+ switch (method) {
154
+ case "Runtime.evaluate":
155
+ // getCurrentUrl() reads document.location.href via Runtime.evaluate
156
+ return { result: { value: "https://example.com/" } };
157
+ case "DOM.getDocument":
158
+ return { root: { nodeId: 1 } };
159
+ case "DOM.querySelector":
160
+ return { nodeId: 42 };
161
+ case "DOM.describeNode":
162
+ return { node: { backendNodeId: 100 } };
163
+ case "DOM.resolveNode":
164
+ // Used by clearAndInsertText to obtain a remote object on the
165
+ // target element so it can run the value-clearing function.
166
+ return { object: { objectId: "obj-1" } };
167
+ case "Runtime.callFunctionOn":
168
+ // The clear-helper function returns undefined; tests don't
169
+ // depend on the return value.
170
+ return { result: { value: undefined } };
171
+ default:
172
+ return {};
173
+ }
174
+ }
175
+
100
176
  function defaultMetadata(service: string, field: string) {
101
177
  return {
102
178
  credentialId: `${service}:${field}`,
@@ -114,7 +190,8 @@ function defaultMetadata(service: string, field: string) {
114
190
  describe("executeBrowserFillCredential", () => {
115
191
  beforeEach(() => {
116
192
  resetMockPage();
117
- snapshotMaps.clear();
193
+ resetCdpMock();
194
+ snapshotBackendNodeMaps.clear();
118
195
  mockGetSecureKey = mock(() => "super-secret-password");
119
196
  mockGetCredentialMetadata = mock((service: string, field: string) =>
120
197
  defaultMetadata(service, field),
@@ -122,23 +199,79 @@ describe("executeBrowserFillCredential", () => {
122
199
  });
123
200
 
124
201
  test("fills credential into element by element_id", async () => {
125
- snapshotMaps.set(
126
- "test-conversation",
127
- new Map([["e1", '[data-vellum-eid="e1"]']]),
128
- );
202
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
129
203
  const result = await executeBrowserFillCredential(
130
204
  { service: "gmail", field: "password", element_id: "e1" },
131
205
  ctx,
132
206
  );
133
207
  expect(result.isError).toBe(false);
134
208
  expect(result.content).toContain("Filled password for gmail");
135
- expect(mockPage.fill).toHaveBeenCalledWith(
136
- '[data-vellum-eid="e1"]',
137
- "super-secret-password",
138
- );
209
+
210
+ // Backend path now goes through clearAndInsertText:
211
+ // Runtime.evaluate (getCurrentUrl)
212
+ // → DOM.focus
213
+ // → DOM.resolveNode
214
+ // → Runtime.callFunctionOn (clear)
215
+ // → DOM.focus (re-focus)
216
+ // → Input.insertText
217
+ const methods = sendCalls.map((c) => c.method);
218
+ expect(methods).toContain("DOM.focus");
219
+ expect(methods).toContain("DOM.resolveNode");
220
+ expect(methods).toContain("Runtime.callFunctionOn");
221
+ expect(methods).toContain("Input.insertText");
222
+ expect(methods).not.toContain("DOM.querySelector");
223
+
224
+ const focusCall = sendCalls.find((c) => c.method === "DOM.focus")!;
225
+ expect(focusCall.params).toEqual({ backendNodeId: 555 });
226
+
227
+ const insertCall = sendCalls.find((c) => c.method === "Input.insertText")!;
228
+ expect(insertCall.params).toEqual({ text: "super-secret-password" });
229
+
139
230
  expect(mockGetSecureKey).toHaveBeenCalledWith(
140
231
  credentialKey("gmail", "password"),
141
232
  );
233
+
234
+ // CdpClient disposed in finally → session.detach called.
235
+ await new Promise((resolve) => setTimeout(resolve, 0));
236
+ expect(detachCalls).toBe(1);
237
+ });
238
+
239
+ test("clears pre-populated field BEFORE inserting credential text", async () => {
240
+ // Regression: previously, fillCredential called focus + insertText
241
+ // directly, which APPENDED the credential to any existing value
242
+ // (autofill, prior typing, etc.) — corrupting the password and
243
+ // leaking partial state. The fix routes through the shared
244
+ // clearAndInsertText helper.
245
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
246
+ await executeBrowserFillCredential(
247
+ { service: "gmail", field: "password", element_id: "e1" },
248
+ ctx,
249
+ );
250
+
251
+ // The clear must happen BEFORE the insertText. Specifically, the
252
+ // Runtime.callFunctionOn that runs the clearing function declaration
253
+ // must precede Input.insertText.
254
+ const methodSeq = sendCalls.map((c) => c.method);
255
+ const clearIdx = methodSeq.indexOf("Runtime.callFunctionOn");
256
+ const insertIdx = methodSeq.indexOf("Input.insertText");
257
+ expect(clearIdx).toBeGreaterThanOrEqual(0);
258
+ expect(insertIdx).toBeGreaterThan(clearIdx);
259
+
260
+ // The clearing function must reset both `value` and
261
+ // `textContent` so input/textarea AND contenteditable targets
262
+ // are handled.
263
+ const clearCall = sendCalls[clearIdx]!;
264
+ const fnDecl = (clearCall.params as { functionDeclaration: string })
265
+ .functionDeclaration;
266
+ expect(fnDecl).toContain('this.value = ""');
267
+ expect(fnDecl).toContain("this.textContent");
268
+ expect(fnDecl).toContain('new Event("input"');
269
+
270
+ // After the clear, the helper re-focuses the element (some sites
271
+ // blur on programmatic value reset) before inserting text — so we
272
+ // expect at least 2 DOM.focus calls in total.
273
+ const focusCount = methodSeq.filter((m) => m === "DOM.focus").length;
274
+ expect(focusCount).toBeGreaterThanOrEqual(2);
142
275
  });
143
276
 
144
277
  test("fills credential by CSS selector", async () => {
@@ -148,18 +281,27 @@ describe("executeBrowserFillCredential", () => {
148
281
  );
149
282
  expect(result.isError).toBe(false);
150
283
  expect(result.content).toContain("Filled token for github");
151
- expect(mockPage.fill).toHaveBeenCalledWith(
152
- 'input[name="password"]',
153
- "super-secret-password",
154
- );
284
+
285
+ // Selector path must resolve the backendNodeId via DOM.querySelector
286
+ // before focusing + inserting text.
287
+ const methods = sendCalls.map((c) => c.method);
288
+ expect(methods).toContain("DOM.getDocument");
289
+ expect(methods).toContain("DOM.querySelector");
290
+ expect(methods).toContain("DOM.describeNode");
291
+ expect(methods).toContain("DOM.focus");
292
+ expect(methods).toContain("Input.insertText");
293
+
294
+ // DOM.focus uses the backendNodeId (100) returned by DOM.describeNode
295
+ const focusCall = sendCalls.find((c) => c.method === "DOM.focus")!;
296
+ expect(focusCall.params).toEqual({ backendNodeId: 100 });
297
+
298
+ const insertCall = sendCalls.find((c) => c.method === "Input.insertText")!;
299
+ expect(insertCall.params).toEqual({ text: "super-secret-password" });
155
300
  });
156
301
 
157
302
  test("returns error when credential not found", async () => {
158
303
  mockGetCredentialMetadata = mock(() => undefined);
159
- snapshotMaps.set(
160
- "test-conversation",
161
- new Map([["e1", '[data-vellum-eid="e1"]']]),
162
- );
304
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
163
305
  const result = await executeBrowserFillCredential(
164
306
  { service: "slack", field: "api_key", element_id: "e1" },
165
307
  ctx,
@@ -167,15 +309,15 @@ describe("executeBrowserFillCredential", () => {
167
309
  expect(result.isError).toBe(true);
168
310
  expect(result.content).toContain("No credential stored for slack/api_key");
169
311
  expect(result.content).toContain("credential_store");
170
- expect(mockPage.fill).not.toHaveBeenCalled();
312
+ // The broker short-circuits before DOM.focus is dispatched.
313
+ const methods = sendCalls.map((c) => c.method);
314
+ expect(methods).not.toContain("DOM.focus");
315
+ expect(methods).not.toContain("Input.insertText");
171
316
  });
172
317
 
173
318
  test("returns error when metadata exists but no stored value", async () => {
174
319
  mockGetSecureKey = mock(() => undefined);
175
- snapshotMaps.set(
176
- "test-conversation",
177
- new Map([["e1", '[data-vellum-eid="e1"]']]),
178
- );
320
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
179
321
  const result = await executeBrowserFillCredential(
180
322
  { service: "slack", field: "api_key", element_id: "e1" },
181
323
  ctx,
@@ -183,7 +325,8 @@ describe("executeBrowserFillCredential", () => {
183
325
  expect(result.isError).toBe(true);
184
326
  expect(result.content).toContain("No credential stored for slack/api_key");
185
327
  expect(result.content).toContain("credential_store");
186
- expect(mockPage.fill).not.toHaveBeenCalled();
328
+ const methods = sendCalls.map((c) => c.method);
329
+ expect(methods).not.toContain("Input.insertText");
187
330
  });
188
331
 
189
332
  test("returns error when element not found", async () => {
@@ -194,13 +337,13 @@ describe("executeBrowserFillCredential", () => {
194
337
  expect(result.isError).toBe(true);
195
338
  expect(result.content).toContain('element_id "e99" not found');
196
339
  expect(result.content).toContain("browser_snapshot");
340
+ // Element resolution fails before any CDP session is opened.
341
+ expect(sendCalls).toHaveLength(0);
342
+ expect(detachCalls).toBe(0);
197
343
  });
198
344
 
199
345
  test("presses Enter after fill when press_enter is true", async () => {
200
- snapshotMaps.set(
201
- "test-conversation",
202
- new Map([["e2", '[data-vellum-eid="e2"]']]),
203
- );
346
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e2", 222]]));
204
347
  const result = await executeBrowserFillCredential(
205
348
  {
206
349
  service: "gmail",
@@ -211,21 +354,34 @@ describe("executeBrowserFillCredential", () => {
211
354
  ctx,
212
355
  );
213
356
  expect(result.isError).toBe(false);
214
- expect(mockPage.fill).toHaveBeenCalledWith(
215
- '[data-vellum-eid="e2"]',
216
- "super-secret-password",
357
+ const methods = sendCalls.map((c) => c.method);
358
+ expect(methods).toContain("Input.insertText");
359
+ // dispatchKeyPress for "Enter" dispatches keyDown + char + keyUp
360
+ // (Enter has text "\r" so it now produces a char event too).
361
+ const keyEvents = sendCalls.filter(
362
+ (c) => c.method === "Input.dispatchKeyEvent",
363
+ );
364
+ expect(keyEvents).toHaveLength(3);
365
+ expect((keyEvents[0]!.params as { type: string; key: string }).type).toBe(
366
+ "keyDown",
217
367
  );
218
- expect(mockPage.press).toHaveBeenCalledWith(
219
- '[data-vellum-eid="e2"]',
368
+ expect((keyEvents[0]!.params as { type: string; key: string }).key).toBe(
220
369
  "Enter",
221
370
  );
371
+ expect((keyEvents[1]!.params as { type: string }).type).toBe("char");
372
+ expect((keyEvents[2]!.params as { type: string }).type).toBe("keyUp");
373
+ // Enter must come AFTER Input.insertText.
374
+ const insertIdx = sendCalls.findIndex(
375
+ (c) => c.method === "Input.insertText",
376
+ );
377
+ const firstKeyIdx = sendCalls.findIndex(
378
+ (c) => c.method === "Input.dispatchKeyEvent",
379
+ );
380
+ expect(firstKeyIdx).toBeGreaterThan(insertIdx);
222
381
  });
223
382
 
224
383
  test("credential value NEVER appears in result content", async () => {
225
- snapshotMaps.set(
226
- "test-conversation",
227
- new Map([["e1", '[data-vellum-eid="e1"]']]),
228
- );
384
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
229
385
  const result = await executeBrowserFillCredential(
230
386
  { service: "gmail", field: "password", element_id: "e1" },
231
387
  ctx,
@@ -235,10 +391,7 @@ describe("executeBrowserFillCredential", () => {
235
391
  });
236
392
 
237
393
  test("returns error when service is missing", async () => {
238
- snapshotMaps.set(
239
- "test-conversation",
240
- new Map([["e1", '[data-vellum-eid="e1"]']]),
241
- );
394
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
242
395
  const result = await executeBrowserFillCredential(
243
396
  { field: "password", element_id: "e1" },
244
397
  ctx,
@@ -248,10 +401,7 @@ describe("executeBrowserFillCredential", () => {
248
401
  });
249
402
 
250
403
  test("returns error when field is missing", async () => {
251
- snapshotMaps.set(
252
- "test-conversation",
253
- new Map([["e1", '[data-vellum-eid="e1"]']]),
254
- );
404
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
255
405
  const result = await executeBrowserFillCredential(
256
406
  { service: "gmail", element_id: "e1" },
257
407
  ctx,
@@ -265,10 +415,7 @@ describe("executeBrowserFillCredential", () => {
265
415
  // -----------------------------------------------------------------------
266
416
  describe("broker integration", () => {
267
417
  test("fill succeeds with no domain or tool-policy checks", async () => {
268
- snapshotMaps.set(
269
- "test-conversation",
270
- new Map([["e1", '[data-vellum-eid="e1"]']]),
271
- );
418
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
272
419
  const result = await executeBrowserFillCredential(
273
420
  { service: "gmail", field: "password", element_id: "e1" },
274
421
  ctx,
@@ -279,10 +426,7 @@ describe("executeBrowserFillCredential", () => {
279
426
  });
280
427
 
281
428
  test("credential access goes through broker (metadata + value checked)", async () => {
282
- snapshotMaps.set(
283
- "test-conversation",
284
- new Map([["e1", '[data-vellum-eid="e1"]']]),
285
- );
429
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
286
430
  await executeBrowserFillCredential(
287
431
  { service: "gmail", field: "password", element_id: "e1" },
288
432
  ctx,
@@ -302,10 +446,7 @@ describe("executeBrowserFillCredential", () => {
302
446
  ...defaultMetadata(service, field),
303
447
  allowedTools: ["some_other_tool"],
304
448
  }));
305
- snapshotMaps.set(
306
- "test-conversation",
307
- new Map([["e1", '[data-vellum-eid="e1"]']]),
308
- );
449
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
309
450
  const result = await executeBrowserFillCredential(
310
451
  { service: "gmail", field: "password", element_id: "e1" },
311
452
  ctx,
@@ -314,7 +455,9 @@ describe("executeBrowserFillCredential", () => {
314
455
  expect(result.content).toContain("Policy denied");
315
456
  expect(result.content).toContain("not allowed to use credential");
316
457
  expect(result.content).toContain("credential_store");
317
- expect(mockPage.fill).not.toHaveBeenCalled();
458
+ // The broker short-circuits before Input.insertText fires.
459
+ const methods = sendCalls.map((c) => c.method);
460
+ expect(methods).not.toContain("Input.insertText");
318
461
  });
319
462
 
320
463
  test("returns domain policy denial with actionable message", async () => {
@@ -322,10 +465,7 @@ describe("executeBrowserFillCredential", () => {
322
465
  ...defaultMetadata(service, field),
323
466
  allowedDomains: ["other-site.com"],
324
467
  }));
325
- snapshotMaps.set(
326
- "test-conversation",
327
- new Map([["e1", '[data-vellum-eid="e1"]']]),
328
- );
468
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
329
469
  const result = await executeBrowserFillCredential(
330
470
  { service: "gmail", field: "password", element_id: "e1" },
331
471
  ctx,
@@ -333,7 +473,8 @@ describe("executeBrowserFillCredential", () => {
333
473
  expect(result.isError).toBe(true);
334
474
  expect(result.content).toContain("Domain policy denied");
335
475
  expect(result.content).toContain("Navigate to an allowed domain");
336
- expect(mockPage.fill).not.toHaveBeenCalled();
476
+ const methods = sendCalls.map((c) => c.method);
477
+ expect(methods).not.toContain("Input.insertText");
337
478
  });
338
479
 
339
480
  test("passes current page domain to broker", async () => {
@@ -341,15 +482,12 @@ describe("executeBrowserFillCredential", () => {
341
482
  ...defaultMetadata(service, field),
342
483
  allowedDomains: ["example.com"],
343
484
  }));
344
- snapshotMaps.set(
345
- "test-conversation",
346
- new Map([["e1", '[data-vellum-eid="e1"]']]),
347
- );
485
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
348
486
  const result = await executeBrowserFillCredential(
349
487
  { service: "gmail", field: "password", element_id: "e1" },
350
488
  ctx,
351
489
  );
352
- // Page URL is https://example.com/ which matches allowedDomains
490
+ // Default handler returns https://example.com/ matches allowedDomains
353
491
  expect(result.isError).toBe(false);
354
492
  expect(result.content).toContain("Filled password for gmail");
355
493
  });
@@ -359,10 +497,7 @@ describe("executeBrowserFillCredential", () => {
359
497
  ...defaultMetadata(service, field),
360
498
  allowedTools: ["other_tool"],
361
499
  }));
362
- snapshotMaps.set(
363
- "test-conversation",
364
- new Map([["e1", '[data-vellum-eid="e1"]']]),
365
- );
500
+ snapshotBackendNodeMaps.set("test-conversation", new Map([["e1", 555]]));
366
501
  const result = await executeBrowserFillCredential(
367
502
  { service: "gmail", field: "password", element_id: "e1" },
368
503
  ctx,
@@ -165,13 +165,15 @@ describe("BrowserManager", () => {
165
165
  // Should not throw
166
166
  });
167
167
 
168
- test("clears snapshot map for the session", async () => {
168
+ test("clears snapshot backendNodeId map for the session", async () => {
169
169
  await browserManager.getOrCreateSessionPage("s1");
170
- browserManager.storeSnapshotMap("s1", new Map([["e1", "#btn"]]));
171
- expect(browserManager.resolveSnapshotSelector("s1", "e1")).toBe("#btn");
170
+ browserManager.storeSnapshotBackendNodeMap("s1", new Map([["e1", 42]]));
171
+ expect(browserManager.resolveSnapshotBackendNodeId("s1", "e1")).toBe(42);
172
172
 
173
173
  await browserManager.closeSessionPage("s1");
174
- expect(browserManager.resolveSnapshotSelector("s1", "e1")).toBeNull();
174
+ expect(
175
+ browserManager.resolveSnapshotBackendNodeId("s1", "e1"),
176
+ ).toBeNull();
175
177
  });
176
178
  });
177
179
 
@@ -195,52 +197,63 @@ describe("BrowserManager", () => {
195
197
  // Should not throw
196
198
  });
197
199
 
198
- test("clears all snapshot maps", async () => {
200
+ test("clears all snapshot backendNodeId maps", async () => {
199
201
  await browserManager.getOrCreateSessionPage("s1");
200
202
  await browserManager.getOrCreateSessionPage("s2");
201
- browserManager.storeSnapshotMap("s1", new Map([["e1", "#a"]]));
202
- browserManager.storeSnapshotMap("s2", new Map([["e2", "#b"]]));
203
+ browserManager.storeSnapshotBackendNodeMap("s1", new Map([["e1", 11]]));
204
+ browserManager.storeSnapshotBackendNodeMap("s2", new Map([["e2", 22]]));
203
205
 
204
206
  await browserManager.closeAllPages();
205
207
 
206
- expect(browserManager.resolveSnapshotSelector("s1", "e1")).toBeNull();
207
- expect(browserManager.resolveSnapshotSelector("s2", "e2")).toBeNull();
208
+ expect(
209
+ browserManager.resolveSnapshotBackendNodeId("s1", "e1"),
210
+ ).toBeNull();
211
+ expect(
212
+ browserManager.resolveSnapshotBackendNodeId("s2", "e2"),
213
+ ).toBeNull();
208
214
  });
209
215
  });
210
216
 
211
- // ── snapshot map ─────────────────────────────────────────────
217
+ // ── snapshot backendNodeId map ───────────────────────────────
212
218
 
213
- describe("snapshot map", () => {
214
- test("stores and resolves element selectors", () => {
219
+ describe("snapshot backendNodeId map", () => {
220
+ test("stores and resolves element backendNodeIds", () => {
215
221
  const map = new Map([
216
- ["e1", "#submit-btn"],
217
- ["e2", 'input[name="email"]'],
222
+ ["e1", 101],
223
+ ["e2", 202],
218
224
  ]);
219
- browserManager.storeSnapshotMap("s1", map);
225
+ browserManager.storeSnapshotBackendNodeMap("s1", map);
220
226
 
221
- expect(browserManager.resolveSnapshotSelector("s1", "e1")).toBe(
222
- "#submit-btn",
223
- );
224
- expect(browserManager.resolveSnapshotSelector("s1", "e2")).toBe(
225
- 'input[name="email"]',
226
- );
227
+ expect(browserManager.resolveSnapshotBackendNodeId("s1", "e1")).toBe(101);
228
+ expect(browserManager.resolveSnapshotBackendNodeId("s1", "e2")).toBe(202);
227
229
  });
228
230
 
229
231
  test("returns null for unknown element id", () => {
230
- browserManager.storeSnapshotMap("s1", new Map([["e1", "#btn"]]));
231
- expect(browserManager.resolveSnapshotSelector("s1", "e999")).toBeNull();
232
+ browserManager.storeSnapshotBackendNodeMap("s1", new Map([["e1", 42]]));
233
+ expect(
234
+ browserManager.resolveSnapshotBackendNodeId("s1", "e999"),
235
+ ).toBeNull();
232
236
  });
233
237
 
234
238
  test("returns null for unknown session", () => {
235
239
  expect(
236
- browserManager.resolveSnapshotSelector("unknown", "e1"),
240
+ browserManager.resolveSnapshotBackendNodeId("unknown", "e1"),
237
241
  ).toBeNull();
238
242
  });
239
243
 
240
244
  test("overwrites previous snapshot map for same session", () => {
241
- browserManager.storeSnapshotMap("s1", new Map([["e1", "#old"]]));
242
- browserManager.storeSnapshotMap("s1", new Map([["e1", "#new"]]));
243
- expect(browserManager.resolveSnapshotSelector("s1", "e1")).toBe("#new");
245
+ browserManager.storeSnapshotBackendNodeMap("s1", new Map([["e1", 1]]));
246
+ browserManager.storeSnapshotBackendNodeMap("s1", new Map([["e1", 999]]));
247
+ expect(browserManager.resolveSnapshotBackendNodeId("s1", "e1")).toBe(999);
248
+ });
249
+
250
+ test("clearSnapshotBackendNodeMap drops the map for a session", () => {
251
+ browserManager.storeSnapshotBackendNodeMap("s1", new Map([["e1", 42]]));
252
+ expect(browserManager.resolveSnapshotBackendNodeId("s1", "e1")).toBe(42);
253
+ browserManager.clearSnapshotBackendNodeMap("s1");
254
+ expect(
255
+ browserManager.resolveSnapshotBackendNodeId("s1", "e1"),
256
+ ).toBeNull();
244
257
  });
245
258
  });
246
259
  });