@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
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw CDP JSON-RPC WebSocket transport used by the `cdp-inspect`
|
|
3
|
+
* backend. This module is intentionally backend-agnostic: it has no
|
|
4
|
+
* dependency on the browser-session manager, the cdp-inspect client,
|
|
5
|
+
* or any feature-flag / config plumbing. It simply adapts a CDP
|
|
6
|
+
* WebSocket URL (as returned from DevTools `/json/version` or
|
|
7
|
+
* `/json/list`) into an asynchronous request/response + event
|
|
8
|
+
* interface.
|
|
9
|
+
*
|
|
10
|
+
* The transport is deliberately minimal:
|
|
11
|
+
* - `send(method, params, opts?)` writes a JSON-RPC 2.0 request
|
|
12
|
+
* frame with a monotonic id, registers a pending entry in the
|
|
13
|
+
* correlation map, and resolves/rejects when the matching
|
|
14
|
+
* response arrives (or the socket dies / the caller aborts).
|
|
15
|
+
* - `addEventListener(listener)` subscribes to every inbound frame
|
|
16
|
+
* that carries no `id` — these are CDP domain events fanned out
|
|
17
|
+
* verbatim to listeners. Listeners do not affect request/response
|
|
18
|
+
* correlation.
|
|
19
|
+
* - `dispose()` proactively closes the socket and rejects every
|
|
20
|
+
* still-pending request exactly once with `CdpWsTransportError(
|
|
21
|
+
* "closed")`. It is idempotent.
|
|
22
|
+
*
|
|
23
|
+
* Failure modes map 1:1 onto {@link CdpWsTransportError} codes:
|
|
24
|
+
* - `closed` — the socket was closed (remote close,
|
|
25
|
+
* `dispose()`, or a pending send racing an
|
|
26
|
+
* already-closed transport).
|
|
27
|
+
* - `aborted` — the per-request `AbortSignal` fired. Any
|
|
28
|
+
* subsequent CDP response for that id is
|
|
29
|
+
* silently dropped.
|
|
30
|
+
* - `timeout` — the connect timeout expired before the
|
|
31
|
+
* socket reached `OPEN`.
|
|
32
|
+
* - `transport_error`— a WebSocket `error` event fired, or a send
|
|
33
|
+
* failed (e.g. serialization failure).
|
|
34
|
+
* - `cdp_error` — the peer returned a JSON-RPC error envelope
|
|
35
|
+
* (`{id, error: {code, message}}`).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
export type CdpWsTransportErrorCode =
|
|
39
|
+
| "closed"
|
|
40
|
+
| "aborted"
|
|
41
|
+
| "timeout"
|
|
42
|
+
| "transport_error"
|
|
43
|
+
| "cdp_error";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Error thrown (or used to reject) by {@link CdpWsTransport} and
|
|
47
|
+
* {@link connectCdpWsTransport}. The `code` discriminates the
|
|
48
|
+
* category of failure so callers can branch without string-sniffing
|
|
49
|
+
* the message. For `cdp_error`, the CDP JSON-RPC error envelope
|
|
50
|
+
* fields are copied through verbatim for logging and upstream
|
|
51
|
+
* error mapping.
|
|
52
|
+
*/
|
|
53
|
+
export class CdpWsTransportError extends Error {
|
|
54
|
+
readonly code: CdpWsTransportErrorCode;
|
|
55
|
+
readonly cdpMethod?: string;
|
|
56
|
+
readonly cdpCode?: number;
|
|
57
|
+
readonly cdpMessage?: string;
|
|
58
|
+
readonly cdpData?: unknown;
|
|
59
|
+
readonly underlying?: unknown;
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
code: CdpWsTransportErrorCode,
|
|
63
|
+
message?: string,
|
|
64
|
+
details?: {
|
|
65
|
+
cdpMethod?: string;
|
|
66
|
+
cdpCode?: number;
|
|
67
|
+
cdpMessage?: string;
|
|
68
|
+
cdpData?: unknown;
|
|
69
|
+
underlying?: unknown;
|
|
70
|
+
},
|
|
71
|
+
) {
|
|
72
|
+
super(message ?? code);
|
|
73
|
+
this.name = "CdpWsTransportError";
|
|
74
|
+
this.code = code;
|
|
75
|
+
this.cdpMethod = details?.cdpMethod;
|
|
76
|
+
this.cdpCode = details?.cdpCode;
|
|
77
|
+
this.cdpMessage = details?.cdpMessage;
|
|
78
|
+
this.cdpData = details?.cdpData;
|
|
79
|
+
this.underlying = details?.underlying;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Payload handed to event listeners registered via
|
|
85
|
+
* {@link CdpWsTransport.addEventListener}. Mirrors the wire-level
|
|
86
|
+
* shape of a CDP JSON-RPC notification minus the `id` field
|
|
87
|
+
* (notifications, by definition, carry no id).
|
|
88
|
+
*/
|
|
89
|
+
export interface CdpTransportEvent {
|
|
90
|
+
method: string;
|
|
91
|
+
params?: unknown;
|
|
92
|
+
sessionId?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Public interface exposed by this transport. Deliberately narrower
|
|
97
|
+
* than the higher-level `CdpClient` type used by tool code — this
|
|
98
|
+
* layer does not know about conversations, backend selection, or
|
|
99
|
+
* CDP error mapping to the shared `CdpError` taxonomy.
|
|
100
|
+
*/
|
|
101
|
+
export interface CdpWsTransport {
|
|
102
|
+
/**
|
|
103
|
+
* Send a CDP method call over the socket and await its response.
|
|
104
|
+
*
|
|
105
|
+
* - `method` / `params` are serialized verbatim into a JSON-RPC
|
|
106
|
+
* 2.0 request envelope.
|
|
107
|
+
* - `opts.sessionId`, if provided, is forwarded on the wire as a
|
|
108
|
+
* top-level `sessionId` field — required for CDP "flattened"
|
|
109
|
+
* session attach mode.
|
|
110
|
+
* - `opts.signal` cancels the pending request: the returned
|
|
111
|
+
* promise rejects with `CdpWsTransportError("aborted")` and any
|
|
112
|
+
* subsequent response carrying the matching id is dropped.
|
|
113
|
+
*
|
|
114
|
+
* Failure modes:
|
|
115
|
+
* - resolves with the `result` field on success.
|
|
116
|
+
* - rejects with `cdp_error` if the peer returns a
|
|
117
|
+
* `{id, error}` envelope.
|
|
118
|
+
* - rejects with `closed` if the socket closes (or is disposed)
|
|
119
|
+
* before a response arrives.
|
|
120
|
+
* - rejects with `aborted` if the caller cancels.
|
|
121
|
+
* - rejects with `transport_error` if the send itself fails
|
|
122
|
+
* (e.g. the socket is already in a non-OPEN state and we race
|
|
123
|
+
* a close, or JSON serialization fails).
|
|
124
|
+
*/
|
|
125
|
+
send<T = unknown>(
|
|
126
|
+
method: string,
|
|
127
|
+
params?: Record<string, unknown>,
|
|
128
|
+
opts?: { sessionId?: string; signal?: AbortSignal },
|
|
129
|
+
): Promise<T>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Register a listener for every inbound JSON-RPC notification
|
|
133
|
+
* (i.e. any frame whose `id` is missing). Returns an unsubscribe
|
|
134
|
+
* function. Listener errors are swallowed so one bad consumer
|
|
135
|
+
* cannot tear down the transport.
|
|
136
|
+
*/
|
|
137
|
+
addEventListener(listener: (event: CdpTransportEvent) => void): () => void;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Close the underlying socket and reject every still-pending
|
|
141
|
+
* request with `CdpWsTransportError("closed")`. Idempotent —
|
|
142
|
+
* calling `dispose()` twice does nothing on the second call. A
|
|
143
|
+
* `dispose()` after a remote close is still safe.
|
|
144
|
+
*/
|
|
145
|
+
dispose(): void;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface PendingRequest {
|
|
149
|
+
resolve: (value: unknown) => void;
|
|
150
|
+
reject: (err: CdpWsTransportError) => void;
|
|
151
|
+
method: string;
|
|
152
|
+
// Listener cleanup for the per-request abort signal. May be null if
|
|
153
|
+
// the caller did not provide a signal.
|
|
154
|
+
cleanupAbort: (() => void) | null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Minimal structural shape of the WebSocket we depend on. Using a
|
|
159
|
+
* local interface (instead of the DOM / bun-types `WebSocket`
|
|
160
|
+
* global's static constants) lets us stay compatible with either
|
|
161
|
+
* runtime and keeps the tests free of DOM typing hassles.
|
|
162
|
+
*/
|
|
163
|
+
interface WsLike {
|
|
164
|
+
readyState: number;
|
|
165
|
+
send(data: string): void;
|
|
166
|
+
close(): void;
|
|
167
|
+
addEventListener(type: "open", listener: () => void): void;
|
|
168
|
+
addEventListener(type: "close", listener: () => void): void;
|
|
169
|
+
addEventListener(type: "error", listener: (ev: unknown) => void): void;
|
|
170
|
+
addEventListener(
|
|
171
|
+
type: "message",
|
|
172
|
+
listener: (ev: { data: unknown }) => void,
|
|
173
|
+
): void;
|
|
174
|
+
removeEventListener?: (type: string, listener: unknown) => void;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// WebSocket.readyState constants. We avoid depending on the global
|
|
178
|
+
// WebSocket static (e.g. `WebSocket.OPEN`) because test fakes may not
|
|
179
|
+
// expose the static properties.
|
|
180
|
+
const WS_OPEN = 1;
|
|
181
|
+
|
|
182
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 5_000;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Open a raw CDP WebSocket transport against `url`. Resolves only
|
|
186
|
+
* after the socket has reached `OPEN`; rejects with
|
|
187
|
+
* `CdpWsTransportError("timeout")` if the connect-timeout expires,
|
|
188
|
+
* `CdpWsTransportError("aborted")` if `opts.signal` fires, or
|
|
189
|
+
* `transport_error` if the socket errors or closes before opening.
|
|
190
|
+
*/
|
|
191
|
+
export async function connectCdpWsTransport(
|
|
192
|
+
url: string,
|
|
193
|
+
opts?: { connectTimeoutMs?: number; signal?: AbortSignal },
|
|
194
|
+
): Promise<CdpWsTransport> {
|
|
195
|
+
const connectTimeoutMs = opts?.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
196
|
+
const callerSignal = opts?.signal;
|
|
197
|
+
|
|
198
|
+
if (callerSignal?.aborted) {
|
|
199
|
+
throw new CdpWsTransportError("aborted", "aborted before connect");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// bun's global `WebSocket` is API-compatible with the browser one.
|
|
203
|
+
const WebSocketCtor: new (url: string) => WsLike = (
|
|
204
|
+
globalThis as unknown as {
|
|
205
|
+
WebSocket: new (url: string) => WsLike;
|
|
206
|
+
}
|
|
207
|
+
).WebSocket;
|
|
208
|
+
if (typeof WebSocketCtor !== "function") {
|
|
209
|
+
throw new CdpWsTransportError(
|
|
210
|
+
"transport_error",
|
|
211
|
+
"global WebSocket is not available in this runtime",
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let ws: WsLike;
|
|
216
|
+
try {
|
|
217
|
+
ws = new WebSocketCtor(url);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
throw new CdpWsTransportError(
|
|
220
|
+
"transport_error",
|
|
221
|
+
err instanceof Error ? err.message : String(err),
|
|
222
|
+
{ underlying: err },
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await new Promise<void>((resolve, reject) => {
|
|
227
|
+
let settled = false;
|
|
228
|
+
const timer = setTimeout(() => {
|
|
229
|
+
if (settled) return;
|
|
230
|
+
settled = true;
|
|
231
|
+
cleanupAbort();
|
|
232
|
+
try {
|
|
233
|
+
ws.close();
|
|
234
|
+
} catch {
|
|
235
|
+
// best effort
|
|
236
|
+
}
|
|
237
|
+
reject(new CdpWsTransportError("timeout", "connect timeout"));
|
|
238
|
+
}, connectTimeoutMs);
|
|
239
|
+
|
|
240
|
+
const onOpen = () => {
|
|
241
|
+
if (settled) return;
|
|
242
|
+
settled = true;
|
|
243
|
+
clearTimeout(timer);
|
|
244
|
+
cleanupAbort();
|
|
245
|
+
resolve();
|
|
246
|
+
};
|
|
247
|
+
const onError = (ev: unknown) => {
|
|
248
|
+
if (settled) return;
|
|
249
|
+
settled = true;
|
|
250
|
+
clearTimeout(timer);
|
|
251
|
+
cleanupAbort();
|
|
252
|
+
try {
|
|
253
|
+
ws.close();
|
|
254
|
+
} catch {
|
|
255
|
+
// best effort
|
|
256
|
+
}
|
|
257
|
+
reject(
|
|
258
|
+
new CdpWsTransportError(
|
|
259
|
+
"transport_error",
|
|
260
|
+
"websocket error during connect",
|
|
261
|
+
{ underlying: ev },
|
|
262
|
+
),
|
|
263
|
+
);
|
|
264
|
+
};
|
|
265
|
+
const onCloseBeforeOpen = () => {
|
|
266
|
+
if (settled) return;
|
|
267
|
+
settled = true;
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
cleanupAbort();
|
|
270
|
+
reject(
|
|
271
|
+
new CdpWsTransportError(
|
|
272
|
+
"transport_error",
|
|
273
|
+
"websocket closed before open",
|
|
274
|
+
),
|
|
275
|
+
);
|
|
276
|
+
};
|
|
277
|
+
const onCallerAbort = () => {
|
|
278
|
+
if (settled) return;
|
|
279
|
+
settled = true;
|
|
280
|
+
clearTimeout(timer);
|
|
281
|
+
cleanupAbort();
|
|
282
|
+
try {
|
|
283
|
+
ws.close();
|
|
284
|
+
} catch {
|
|
285
|
+
// best effort
|
|
286
|
+
}
|
|
287
|
+
reject(new CdpWsTransportError("aborted", "aborted during connect"));
|
|
288
|
+
};
|
|
289
|
+
const cleanupAbort = () => {
|
|
290
|
+
if (callerSignal) {
|
|
291
|
+
callerSignal.removeEventListener("abort", onCallerAbort);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
ws.addEventListener("open", onOpen);
|
|
296
|
+
ws.addEventListener("error", onError);
|
|
297
|
+
ws.addEventListener("close", onCloseBeforeOpen);
|
|
298
|
+
if (callerSignal) {
|
|
299
|
+
callerSignal.addEventListener("abort", onCallerAbort, { once: true });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
return createTransport(ws);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function createTransport(ws: WsLike): CdpWsTransport {
|
|
307
|
+
const pending = new Map<number, PendingRequest>();
|
|
308
|
+
const listeners = new Set<(event: CdpTransportEvent) => void>();
|
|
309
|
+
let nextId = 1;
|
|
310
|
+
let disposed = false;
|
|
311
|
+
let closed = false;
|
|
312
|
+
|
|
313
|
+
const rejectAllPending = (code: CdpWsTransportErrorCode, message: string) => {
|
|
314
|
+
if (pending.size === 0) return;
|
|
315
|
+
// Snapshot entries so that caller `.catch()` handlers invoked
|
|
316
|
+
// synchronously via `reject` cannot mutate the map we are iterating.
|
|
317
|
+
const entries = Array.from(pending.entries());
|
|
318
|
+
pending.clear();
|
|
319
|
+
for (const [, entry] of entries) {
|
|
320
|
+
entry.cleanupAbort?.();
|
|
321
|
+
entry.reject(
|
|
322
|
+
new CdpWsTransportError(code, message, { cdpMethod: entry.method }),
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const handleMessage = (ev: { data: unknown }) => {
|
|
328
|
+
if (disposed) return;
|
|
329
|
+
let raw: string;
|
|
330
|
+
if (typeof ev.data === "string") {
|
|
331
|
+
raw = ev.data;
|
|
332
|
+
} else if (ev.data instanceof ArrayBuffer) {
|
|
333
|
+
raw = new TextDecoder().decode(ev.data);
|
|
334
|
+
} else if (
|
|
335
|
+
typeof (ev.data as { toString?: () => string })?.toString === "function"
|
|
336
|
+
) {
|
|
337
|
+
raw = String(ev.data);
|
|
338
|
+
} else {
|
|
339
|
+
// Unknown binary payload — CDP is always JSON text, so drop it.
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
let frame: unknown;
|
|
343
|
+
try {
|
|
344
|
+
frame = JSON.parse(raw);
|
|
345
|
+
} catch {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (!frame || typeof frame !== "object") return;
|
|
349
|
+
const obj = frame as {
|
|
350
|
+
id?: unknown;
|
|
351
|
+
result?: unknown;
|
|
352
|
+
error?: { code?: unknown; message?: unknown; data?: unknown };
|
|
353
|
+
method?: unknown;
|
|
354
|
+
params?: unknown;
|
|
355
|
+
sessionId?: unknown;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
if (typeof obj.id === "number") {
|
|
359
|
+
const entry = pending.get(obj.id);
|
|
360
|
+
if (!entry) {
|
|
361
|
+
// Either an unknown id (protocol violation) or an aborted
|
|
362
|
+
// request whose entry we already removed — drop silently.
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
pending.delete(obj.id);
|
|
366
|
+
entry.cleanupAbort?.();
|
|
367
|
+
if (obj.error && typeof obj.error === "object") {
|
|
368
|
+
const cdpCode =
|
|
369
|
+
typeof obj.error.code === "number" ? obj.error.code : undefined;
|
|
370
|
+
const cdpMessage =
|
|
371
|
+
typeof obj.error.message === "string" ? obj.error.message : undefined;
|
|
372
|
+
entry.reject(
|
|
373
|
+
new CdpWsTransportError(
|
|
374
|
+
"cdp_error",
|
|
375
|
+
cdpMessage ?? `cdp error for ${entry.method}`,
|
|
376
|
+
{
|
|
377
|
+
cdpMethod: entry.method,
|
|
378
|
+
cdpCode,
|
|
379
|
+
cdpMessage,
|
|
380
|
+
cdpData: obj.error.data,
|
|
381
|
+
},
|
|
382
|
+
),
|
|
383
|
+
);
|
|
384
|
+
} else {
|
|
385
|
+
entry.resolve(obj.result);
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// No id → CDP domain event. Fan out to listeners, swallowing
|
|
391
|
+
// any listener throws.
|
|
392
|
+
if (typeof obj.method === "string") {
|
|
393
|
+
const event: CdpTransportEvent = {
|
|
394
|
+
method: obj.method,
|
|
395
|
+
params: obj.params,
|
|
396
|
+
sessionId:
|
|
397
|
+
typeof obj.sessionId === "string" ? obj.sessionId : undefined,
|
|
398
|
+
};
|
|
399
|
+
for (const listener of listeners) {
|
|
400
|
+
try {
|
|
401
|
+
listener(event);
|
|
402
|
+
} catch {
|
|
403
|
+
// listener errors are swallowed to keep the transport alive
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const handleClose = () => {
|
|
410
|
+
if (closed) return;
|
|
411
|
+
closed = true;
|
|
412
|
+
rejectAllPending("closed", "websocket closed");
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const handleError = (ev: unknown) => {
|
|
416
|
+
if (closed) return;
|
|
417
|
+
closed = true;
|
|
418
|
+
// Best-effort close after an error so we don't leak a half-open
|
|
419
|
+
// socket. Do not throw on already-closed sockets.
|
|
420
|
+
try {
|
|
421
|
+
ws.close();
|
|
422
|
+
} catch {
|
|
423
|
+
// ignored
|
|
424
|
+
}
|
|
425
|
+
// Reject pending as transport_error so callers can distinguish
|
|
426
|
+
// a protocol-level peer close from an explicit socket error.
|
|
427
|
+
if (pending.size > 0) {
|
|
428
|
+
const entries = Array.from(pending.entries());
|
|
429
|
+
pending.clear();
|
|
430
|
+
for (const [, entry] of entries) {
|
|
431
|
+
entry.cleanupAbort?.();
|
|
432
|
+
entry.reject(
|
|
433
|
+
new CdpWsTransportError("transport_error", "websocket error", {
|
|
434
|
+
cdpMethod: entry.method,
|
|
435
|
+
underlying: ev,
|
|
436
|
+
}),
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
ws.addEventListener("message", handleMessage);
|
|
443
|
+
ws.addEventListener("close", handleClose);
|
|
444
|
+
ws.addEventListener("error", handleError);
|
|
445
|
+
|
|
446
|
+
const transport: CdpWsTransport = {
|
|
447
|
+
send<T = unknown>(
|
|
448
|
+
method: string,
|
|
449
|
+
params?: Record<string, unknown>,
|
|
450
|
+
opts?: { sessionId?: string; signal?: AbortSignal },
|
|
451
|
+
): Promise<T> {
|
|
452
|
+
if (disposed || closed) {
|
|
453
|
+
return Promise.reject(
|
|
454
|
+
new CdpWsTransportError("closed", "transport already closed", {
|
|
455
|
+
cdpMethod: method,
|
|
456
|
+
}),
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
const signal = opts?.signal;
|
|
460
|
+
if (signal?.aborted) {
|
|
461
|
+
return Promise.reject(
|
|
462
|
+
new CdpWsTransportError("aborted", "aborted before send", {
|
|
463
|
+
cdpMethod: method,
|
|
464
|
+
}),
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const id = nextId++;
|
|
469
|
+
const frame: Record<string, unknown> = { id, method };
|
|
470
|
+
if (params !== undefined) frame.params = params;
|
|
471
|
+
if (opts?.sessionId !== undefined) frame.sessionId = opts.sessionId;
|
|
472
|
+
|
|
473
|
+
let serialized: string;
|
|
474
|
+
try {
|
|
475
|
+
serialized = JSON.stringify(frame);
|
|
476
|
+
} catch (err) {
|
|
477
|
+
return Promise.reject(
|
|
478
|
+
new CdpWsTransportError(
|
|
479
|
+
"transport_error",
|
|
480
|
+
err instanceof Error ? err.message : String(err),
|
|
481
|
+
{ cdpMethod: method, underlying: err },
|
|
482
|
+
),
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Guard against sending on a non-OPEN socket. By construction
|
|
487
|
+
// the socket is OPEN at the time we hand the transport to
|
|
488
|
+
// callers (connectCdpWsTransport waits for the `open` event),
|
|
489
|
+
// so any other readyState means the socket has since moved
|
|
490
|
+
// past OPEN — treat it as closed so callers can't observe
|
|
491
|
+
// silently dropped frames.
|
|
492
|
+
if (ws.readyState !== WS_OPEN) {
|
|
493
|
+
return Promise.reject(
|
|
494
|
+
new CdpWsTransportError("closed", "socket not open", {
|
|
495
|
+
cdpMethod: method,
|
|
496
|
+
}),
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return new Promise<T>((resolve, reject) => {
|
|
501
|
+
// Register the pending entry FIRST so that an abort or
|
|
502
|
+
// inbound response racing the rest of this function body
|
|
503
|
+
// always has a live entry to act on. Without this ordering
|
|
504
|
+
// a synchronous abort registered below could fire before
|
|
505
|
+
// the entry exists, silently dropping the cancellation.
|
|
506
|
+
pending.set(id, {
|
|
507
|
+
resolve: (value: unknown) => resolve(value as T),
|
|
508
|
+
reject,
|
|
509
|
+
method,
|
|
510
|
+
cleanupAbort: null,
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
if (signal) {
|
|
514
|
+
const onAbort = () => {
|
|
515
|
+
const entry = pending.get(id);
|
|
516
|
+
if (!entry) return;
|
|
517
|
+
pending.delete(id);
|
|
518
|
+
entry.cleanupAbort?.();
|
|
519
|
+
entry.reject(
|
|
520
|
+
new CdpWsTransportError("aborted", "aborted during send", {
|
|
521
|
+
cdpMethod: method,
|
|
522
|
+
}),
|
|
523
|
+
);
|
|
524
|
+
};
|
|
525
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
526
|
+
const entry = pending.get(id);
|
|
527
|
+
if (entry) {
|
|
528
|
+
entry.cleanupAbort = () => {
|
|
529
|
+
signal.removeEventListener("abort", onAbort);
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
try {
|
|
535
|
+
ws.send(serialized);
|
|
536
|
+
} catch (err) {
|
|
537
|
+
const entry = pending.get(id);
|
|
538
|
+
if (entry) {
|
|
539
|
+
pending.delete(id);
|
|
540
|
+
entry.cleanupAbort?.();
|
|
541
|
+
}
|
|
542
|
+
reject(
|
|
543
|
+
new CdpWsTransportError(
|
|
544
|
+
"transport_error",
|
|
545
|
+
err instanceof Error ? err.message : String(err),
|
|
546
|
+
{ cdpMethod: method, underlying: err },
|
|
547
|
+
),
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
},
|
|
552
|
+
|
|
553
|
+
addEventListener(listener) {
|
|
554
|
+
listeners.add(listener);
|
|
555
|
+
return () => {
|
|
556
|
+
listeners.delete(listener);
|
|
557
|
+
};
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
dispose() {
|
|
561
|
+
if (disposed) return;
|
|
562
|
+
disposed = true;
|
|
563
|
+
// Reject pending requests BEFORE calling close() so that
|
|
564
|
+
// callers observe the explicit "disposed" signal even if the
|
|
565
|
+
// underlying `close()` fires a synchronous `close` event.
|
|
566
|
+
rejectAllPending("closed", "transport disposed");
|
|
567
|
+
if (!closed) {
|
|
568
|
+
closed = true;
|
|
569
|
+
try {
|
|
570
|
+
ws.close();
|
|
571
|
+
} catch {
|
|
572
|
+
// ignored — already-closed sockets may throw
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
return transport;
|
|
579
|
+
}
|