@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.
- package/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docker-entrypoint.sh +12 -2
- package/docs/architecture/memory.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +184 -69
- package/package.json +41 -41
- package/scripts/generate-openapi.ts +1 -2
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/browser-fill-credential.test.ts +229 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/catalog-files.test.ts +862 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/checker.test.ts +104 -170
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +125 -48
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/context-overflow-approval.test.ts +21 -6
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +1 -1
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-queue.test.ts +45 -2
- package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -4
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +2 -2
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/gemini-provider.test.ts +2 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +707 -371
- package/src/__tests__/headless-browser-navigate.test.ts +389 -47
- package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
- package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
- package/src/__tests__/host-bash-proxy.test.ts +150 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
- package/src/__tests__/host-cu-proxy.test.ts +171 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/log-export-workspace.test.ts +190 -0
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +61 -2
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +101 -1
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/oauth-apps-routes.test.ts +17 -12
- package/src/__tests__/oauth-cli.test.ts +707 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +50 -14
- package/src/__tests__/oauth-store.test.ts +1386 -182
- package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
- package/src/__tests__/onboarding-template-contract.test.ts +74 -55
- package/src/__tests__/openai-provider.test.ts +2 -2
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/require-fresh-approval.test.ts +40 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
- package/src/__tests__/slack-channel-config.test.ts +12 -15
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +72 -1
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +11 -5
- package/src/__tests__/test-preload.ts +14 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +62 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -35
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +55 -0
- package/src/cli/__tests__/run-assistant-command.ts +34 -7
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +18 -13
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +16 -2
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +200 -36
- package/src/cli/commands/oauth/shared.ts +5 -5
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +10 -3
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/contacts/SKILL.md +3 -0
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +12 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/env-registry.ts +14 -0
- package/src/config/env.ts +21 -0
- package/src/config/feature-flag-registry.json +46 -7
- package/src/config/loader.ts +56 -1
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +46 -5
- package/src/config/schemas/host-browser.ts +66 -0
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +16 -0
- package/src/config/types.ts +0 -1
- package/src/context/post-turn-tool-result-truncation.ts +176 -0
- package/src/context/window-manager.ts +19 -1
- package/src/credential-execution/approval-bridge.ts +49 -16
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +6 -2
- package/src/daemon/context-overflow-approval.ts +5 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +74 -19
- package/src/daemon/conversation-attachments.ts +40 -1
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +66 -3
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +159 -20
- package/src/daemon/conversation-surfaces.ts +78 -12
- package/src/daemon/conversation-tool-setup.ts +74 -11
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +227 -11
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -139
- package/src/daemon/handlers/shared.ts +65 -0
- package/src/daemon/handlers/skills.ts +232 -37
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +191 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +86 -12
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +59 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -6
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/settings.ts +12 -0
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +112 -35
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +14 -0
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +38 -10
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +65 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +177 -18
- package/src/memory/graph/capability-seed.ts +3 -5
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/retriever.ts +40 -22
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/llm-usage-store.ts +45 -4
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +1 -0
- package/src/memory/schema/oauth.ts +18 -13
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +8 -8
- package/src/oauth/byo-connection.ts +7 -7
- package/src/oauth/connect-orchestrator.ts +23 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +16 -16
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +214 -100
- package/src/oauth/platform-connection.test.ts +5 -5
- package/src/oauth/platform-connection.ts +4 -4
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +127 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -96
- package/src/prompts/templates/SOUL.md +11 -11
- package/src/providers/anthropic/client.ts +1 -0
- package/src/providers/types.ts +1 -1
- package/src/runtime/AGENTS.md +23 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/assistant-event-hub.ts +24 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +6 -7
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/chrome-extension-registry.ts +332 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +425 -70
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
- package/src/runtime/migrations/migration-transport.ts +6 -0
- package/src/runtime/migrations/migration-wizard.ts +22 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
- package/src/runtime/migrations/vbundle-importer.ts +55 -5
- package/src/runtime/pending-interactions.ts +29 -13
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/browser-cdp-routes.ts +229 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
- package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +308 -28
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/host-browser-routes.ts +279 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-routes.ts +259 -16
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +60 -25
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +87 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +9 -97
- package/src/runtime/routes/skills-routes.ts +52 -2
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/usage-routes.ts +8 -7
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/schedule/scheduler.ts +7 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +42 -25
- package/src/security/secure-keys.ts +118 -25
- package/src/security/token-manager.ts +23 -10
- package/src/skills/catalog-files.ts +492 -0
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/subagent/manager.ts +131 -26
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +645 -340
- package/src/tools/browser/browser-manager.ts +36 -12
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
- package/src/tools/browser/cdp-client/errors.ts +34 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
- package/src/tools/browser/cdp-client/factory.ts +204 -0
- package/src/tools/browser/cdp-client/index.ts +14 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +52 -0
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/permission-checker.ts +77 -100
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -1
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +24 -21
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -3
- package/src/util/platform.ts +14 -19
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/029-seed-pkb.ts +1 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/top-level-renderer.ts +19 -1
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/tools/browser/chrome-cdp.ts +0 -239
- 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
|
|
62
|
+
let snapshotBackendNodeMaps: Map<string, Map<string, number>>;
|
|
25
63
|
|
|
26
64
|
mock.module("../tools/browser/browser-manager.js", () => {
|
|
27
|
-
|
|
65
|
+
snapshotBackendNodeMaps = new Map();
|
|
28
66
|
return {
|
|
29
67
|
browserManager: {
|
|
30
68
|
getOrCreateSessionPage: async () => mockPage,
|
|
31
69
|
closeSessionPage: async () => {},
|
|
32
70
|
closeAllPages: async () => {},
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
storeSnapshotBackendNodeMap: (
|
|
72
|
+
conversationId: string,
|
|
73
|
+
map: Map<string, number>,
|
|
74
|
+
) => {
|
|
75
|
+
snapshotBackendNodeMaps.set(conversationId, map);
|
|
35
76
|
},
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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.
|
|
171
|
-
expect(browserManager.
|
|
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(
|
|
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.
|
|
202
|
-
browserManager.
|
|
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(
|
|
207
|
-
|
|
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
|
|
219
|
+
describe("snapshot backendNodeId map", () => {
|
|
220
|
+
test("stores and resolves element backendNodeIds", () => {
|
|
215
221
|
const map = new Map([
|
|
216
|
-
["e1",
|
|
217
|
-
["e2",
|
|
222
|
+
["e1", 101],
|
|
223
|
+
["e2", 202],
|
|
218
224
|
]);
|
|
219
|
-
browserManager.
|
|
225
|
+
browserManager.storeSnapshotBackendNodeMap("s1", map);
|
|
220
226
|
|
|
221
|
-
expect(browserManager.
|
|
222
|
-
|
|
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.
|
|
231
|
-
expect(
|
|
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.
|
|
240
|
+
browserManager.resolveSnapshotBackendNodeId("unknown", "e1"),
|
|
237
241
|
).toBeNull();
|
|
238
242
|
});
|
|
239
243
|
|
|
240
244
|
test("overwrites previous snapshot map for same session", () => {
|
|
241
|
-
browserManager.
|
|
242
|
-
browserManager.
|
|
243
|
-
expect(browserManager.
|
|
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
|
});
|