@vellumai/assistant 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docs/architecture/memory.md +1 -1
- 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__/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__/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 +16 -1
- 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 +2 -2
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -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 -1
- 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__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- 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__/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 +75 -57
- 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__/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 -1
- 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-tools.test.ts +9 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- 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 +14 -29
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -6
- 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 +53 -3
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- 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 +44 -44
- 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 +6 -3
- 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/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 +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
- 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 +1 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- 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 +44 -5
- 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 +8 -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 -15
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/context-overflow-approval.ts +5 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +58 -24
- package/src/daemon/conversation-attachments.ts +40 -0
- package/src/daemon/conversation-process.ts +48 -1
- package/src/daemon/conversation-runtime-assembly.ts +118 -36
- package/src/daemon/conversation-surfaces.ts +37 -36
- package/src/daemon/conversation-tool-setup.ts +74 -8
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +226 -8
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -140
- package/src/daemon/handlers/shared.ts +58 -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 +65 -11
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +55 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -5
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +92 -12
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +5 -24
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +23 -0
- 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 +176 -17
- 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/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/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 +3 -3
- 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 +126 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -1
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -105
- 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 +2 -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/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 +2 -1
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +301 -27
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- 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-routes.ts +42 -22
- 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/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 -82
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -0
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- 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/shell.ts +21 -16
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -0
- package/src/util/platform.ts +14 -19
- 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,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level event bus for out-of-band browser-session signals.
|
|
3
|
+
*
|
|
4
|
+
* The `host_browser_event` and `host_browser_session_invalidated`
|
|
5
|
+
* envelopes (see `assistant/src/daemon/message-types/host-browser.ts`)
|
|
6
|
+
* carry unsolicited CDP events and detach notifications from the
|
|
7
|
+
* chrome extension to the daemon. Unlike `host_browser_result`, these
|
|
8
|
+
* frames are not tied to a specific in-flight request and cannot be
|
|
9
|
+
* routed through `pending-interactions`. Instead they publish into
|
|
10
|
+
* this bus, where tool-side consumers subscribe to react to the signal.
|
|
11
|
+
*
|
|
12
|
+
* Two distinct surfaces:
|
|
13
|
+
*
|
|
14
|
+
* 1. **CDP event listeners** — free-form subscribers that observe
|
|
15
|
+
* every incoming `host_browser_event`. Primarily a seam for
|
|
16
|
+
* future work (event-driven session tracking, lifecycle
|
|
17
|
+
* instrumentation, tool-side reactive hooks) and for tests that
|
|
18
|
+
* need to assert that events were routed.
|
|
19
|
+
*
|
|
20
|
+
* 2. **Invalidated target registry** — a short-lived set of target
|
|
21
|
+
* ids that the extension has reported as detached. The
|
|
22
|
+
* `BrowserSessionManager` checks this set on its next `send()`
|
|
23
|
+
* and evicts any matching session before dispatch so the
|
|
24
|
+
* extension dispatcher can re-attach fresh. Entries are
|
|
25
|
+
* consumed on first lookup to keep the set from growing
|
|
26
|
+
* unbounded across long-running processes.
|
|
27
|
+
*
|
|
28
|
+
* Both surfaces are intentionally transport-agnostic — the WS and
|
|
29
|
+
* HTTP paths both publish through the same module so the routing
|
|
30
|
+
* semantics stay in lockstep.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { getLogger } from "../util/logger.js";
|
|
34
|
+
|
|
35
|
+
const log = getLogger("browser-session-events");
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// CDP event listener surface
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A forwarded CDP event as consumed by runtime-side subscribers. The
|
|
43
|
+
* wire shape comes from `HostBrowserEvent` in
|
|
44
|
+
* `assistant/src/daemon/message-types/host-browser.ts` and is stripped
|
|
45
|
+
* of its `type` discriminator here for ergonomic subscriber code.
|
|
46
|
+
*/
|
|
47
|
+
export interface ForwardedCdpEvent {
|
|
48
|
+
/** CDP event method name, e.g. "Page.frameNavigated". */
|
|
49
|
+
method: string;
|
|
50
|
+
/** CDP event params forwarded verbatim. Opaque to the bus. */
|
|
51
|
+
params?: unknown;
|
|
52
|
+
/**
|
|
53
|
+
* Optional CDP session id — present for flat child sessions routed
|
|
54
|
+
* through `Target.attachToTarget` with `flatten: true`.
|
|
55
|
+
*/
|
|
56
|
+
cdpSessionId?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type CdpEventListener = (event: ForwardedCdpEvent) => void;
|
|
60
|
+
|
|
61
|
+
const cdpEventListeners = new Set<CdpEventListener>();
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Subscribe to forwarded CDP events. Returns an unsubscribe function;
|
|
65
|
+
* callers MUST invoke it at end-of-lifecycle to avoid leaking closures
|
|
66
|
+
* into the module-level set. Listener errors are caught and logged so
|
|
67
|
+
* a broken subscriber cannot take down the WS dispatch path.
|
|
68
|
+
*/
|
|
69
|
+
export function onCdpEvent(listener: CdpEventListener): () => void {
|
|
70
|
+
cdpEventListeners.add(listener);
|
|
71
|
+
return () => {
|
|
72
|
+
cdpEventListeners.delete(listener);
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Fan an incoming CDP event out to all registered listeners. Called
|
|
78
|
+
* by the WS frame dispatcher in
|
|
79
|
+
* `assistant/src/runtime/routes/host-browser-routes.ts` after a
|
|
80
|
+
* `host_browser_event` envelope has been validated.
|
|
81
|
+
*/
|
|
82
|
+
export function publishCdpEvent(event: ForwardedCdpEvent): void {
|
|
83
|
+
for (const listener of cdpEventListeners) {
|
|
84
|
+
try {
|
|
85
|
+
listener(event);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
log.warn(
|
|
88
|
+
{ err, method: event.method },
|
|
89
|
+
"CDP event listener threw — suppressing to protect dispatcher",
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Invalidated target registry
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Short-lived set of target ids the chrome extension has reported as
|
|
101
|
+
* detached since the last `consumeInvalidatedTargetId` lookup. The
|
|
102
|
+
* `BrowserSessionManager.invalidateByTargetId` path reads entries
|
|
103
|
+
* out of this set on each `send()` and evicts any matching session
|
|
104
|
+
* before dispatch.
|
|
105
|
+
*
|
|
106
|
+
* Entries are consumed on first read so the set never grows
|
|
107
|
+
* unbounded. If a future consumer needs multiple reads of the same
|
|
108
|
+
* invalidation, a separate long-lived registry should be introduced
|
|
109
|
+
* — this set is scoped to the minimum viable behaviour for
|
|
110
|
+
* "next command forces reattach".
|
|
111
|
+
*/
|
|
112
|
+
const invalidatedTargetIds = new Set<string>();
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Record that the chrome extension has reported a target as
|
|
116
|
+
* detached. Idempotent — re-marking a target is a no-op. Logs at
|
|
117
|
+
* debug because the signal is benign (just a lifecycle notification)
|
|
118
|
+
* and noisy in high-churn workloads.
|
|
119
|
+
*/
|
|
120
|
+
export function markTargetInvalidated(targetId: string, reason?: string): void {
|
|
121
|
+
invalidatedTargetIds.add(targetId);
|
|
122
|
+
log.debug({ targetId, reason }, "browser-session target invalidated");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Peek at whether a given target id is currently marked invalidated
|
|
127
|
+
* without consuming the entry. Primarily used by tests — production
|
|
128
|
+
* consumers should call {@link consumeInvalidatedTargetId} so the
|
|
129
|
+
* set stays bounded.
|
|
130
|
+
*/
|
|
131
|
+
export function isTargetInvalidated(targetId: string): boolean {
|
|
132
|
+
return invalidatedTargetIds.has(targetId);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Test-only helper: snapshot the current invalidated set without
|
|
137
|
+
* draining it. Exported so unit tests can assert exact set contents
|
|
138
|
+
* across multiple frames; production code must not rely on this.
|
|
139
|
+
*/
|
|
140
|
+
export function __peekInvalidatedTargetIdsForTests(): string[] {
|
|
141
|
+
return Array.from(invalidatedTargetIds);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Atomically remove and return a target id from the invalidated set.
|
|
146
|
+
* Returns `true` when the id was present (and has now been removed),
|
|
147
|
+
* `false` otherwise. Designed to be called by
|
|
148
|
+
* `BrowserSessionManager.send()` so the first dispatch after a
|
|
149
|
+
* detach forces a reattach and subsequent dispatches proceed normally.
|
|
150
|
+
*/
|
|
151
|
+
export function consumeInvalidatedTargetId(targetId: string): boolean {
|
|
152
|
+
return invalidatedTargetIds.delete(targetId);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Test-only helper: clear the entire invalidated set. Not exported
|
|
157
|
+
* from any public index — used by unit tests that need a clean slate
|
|
158
|
+
* between cases. Not safe to call from production code because it
|
|
159
|
+
* would race against concurrent invalidations.
|
|
160
|
+
*/
|
|
161
|
+
export function __resetBrowserSessionEventsForTests(): void {
|
|
162
|
+
cdpEventListeners.clear();
|
|
163
|
+
invalidatedTargetIds.clear();
|
|
164
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrowserSessionManager — multi-backend session router for host_browser.
|
|
3
|
+
*
|
|
4
|
+
* This module is the single CDP backend selector for browser tools. The
|
|
5
|
+
* `cdp-client` factory (`assistant/src/tools/browser/cdp-client/factory.ts`)
|
|
6
|
+
* constructs a BrowserSessionManager per tool invocation, registers the
|
|
7
|
+
* appropriate backend from a three-way selection:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Extension** — selected when `hostBrowserProxy` is present (macOS
|
|
10
|
+
* desktop / cloud-hosted with a chrome-extension bound to the
|
|
11
|
+
* conversation).
|
|
12
|
+
* 2. **cdp-inspect** — selected when the extension is absent and
|
|
13
|
+
* `hostBrowser.cdpInspect.enabled` is `true` in config. Attaches to
|
|
14
|
+
* an already-running Chrome via `--remote-debugging-port`.
|
|
15
|
+
* 3. **Local** — default when neither of the above applies.
|
|
16
|
+
* Drives a Playwright-backed sacrificial-profile Chromium.
|
|
17
|
+
*
|
|
18
|
+
* The factory exposes a `ScopedCdpClient` that routes `send()` through
|
|
19
|
+
* the manager. This gives every call site a single choke point for
|
|
20
|
+
* session invalidation and future multi-tab routing.
|
|
21
|
+
*/
|
|
22
|
+
export * from "./backends/cdp-inspect.js";
|
|
23
|
+
export * from "./backends/extension.js";
|
|
24
|
+
export * from "./backends/local.js";
|
|
25
|
+
export * from "./events.js";
|
|
26
|
+
export * from "./manager.js";
|
|
27
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { v4 as uuid } from "uuid";
|
|
2
|
+
|
|
3
|
+
import { consumeInvalidatedTargetId } from "./events.js";
|
|
4
|
+
import type {
|
|
5
|
+
BrowserBackend,
|
|
6
|
+
BrowserSession,
|
|
7
|
+
CdpCommand,
|
|
8
|
+
CdpResult,
|
|
9
|
+
} from "./types.js";
|
|
10
|
+
|
|
11
|
+
export interface BrowserSessionManagerOptions {
|
|
12
|
+
/** Ordered list of backends to try; first available wins. */
|
|
13
|
+
backends: BrowserBackend[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class BrowserSessionManager {
|
|
17
|
+
private backends: BrowserBackend[];
|
|
18
|
+
private sessions = new Map<string, BrowserSession>();
|
|
19
|
+
|
|
20
|
+
constructor(opts: BrowserSessionManagerOptions) {
|
|
21
|
+
this.backends = opts.backends;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Pick an available backend or throw. */
|
|
25
|
+
selectBackend(): BrowserBackend {
|
|
26
|
+
const b = this.backends.find((x) => x.isAvailable());
|
|
27
|
+
if (!b) throw new Error("No available browser backend");
|
|
28
|
+
return b;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
createSession(): BrowserSession {
|
|
32
|
+
const backend = this.selectBackend();
|
|
33
|
+
const session: BrowserSession = { id: uuid(), backendKind: backend.kind };
|
|
34
|
+
this.sessions.set(session.id, session);
|
|
35
|
+
return session;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getSession(id: string): BrowserSession | undefined {
|
|
39
|
+
return this.sessions.get(id);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Dispatch a CDP command.
|
|
44
|
+
*
|
|
45
|
+
* - If `sessionId` is provided, the session must exist in the manager; otherwise this throws.
|
|
46
|
+
* The command is routed through the backend whose `kind` matches the session's `backendKind`,
|
|
47
|
+
* ensuring per-session backend isolation and making `disposeSession()` an actual enforcement
|
|
48
|
+
* boundary against stale ids. If the session has an opaque `targetId` and the command does
|
|
49
|
+
* not already carry its own CDP `sessionId`, the manager injects the session's `targetId`
|
|
50
|
+
* as the CDP `sessionId` so backends can multiplex commands across multiple tabs/targets.
|
|
51
|
+
* - If `sessionId` is `undefined`, the first available backend is selected for one-off
|
|
52
|
+
* commands without a session handle (e.g. transport health probes).
|
|
53
|
+
*/
|
|
54
|
+
async send(
|
|
55
|
+
sessionId: string | undefined,
|
|
56
|
+
command: CdpCommand,
|
|
57
|
+
signal?: AbortSignal,
|
|
58
|
+
): Promise<CdpResult> {
|
|
59
|
+
let backend: BrowserBackend;
|
|
60
|
+
let outgoing = command;
|
|
61
|
+
if (sessionId !== undefined) {
|
|
62
|
+
const session = this.sessions.get(sessionId);
|
|
63
|
+
if (!session) {
|
|
64
|
+
throw new Error(`Unknown browser session: ${sessionId}`);
|
|
65
|
+
}
|
|
66
|
+
// If the chrome extension has reported this session's target
|
|
67
|
+
// as detached since the last dispatch, evict the session and
|
|
68
|
+
// throw so the caller can create a fresh one. Reading (and
|
|
69
|
+
// consuming) the invalidation flag here keeps the "next
|
|
70
|
+
// command forces reattach" semantics in lockstep with the
|
|
71
|
+
// host_browser_session_invalidated envelope handler — without
|
|
72
|
+
// this check the manager would happily forward a CDP command
|
|
73
|
+
// against a torn-down target and hit a permanent failure.
|
|
74
|
+
if (
|
|
75
|
+
session.targetId !== undefined &&
|
|
76
|
+
consumeInvalidatedTargetId(session.targetId)
|
|
77
|
+
) {
|
|
78
|
+
this.sessions.delete(sessionId);
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Browser session ${sessionId} was invalidated (target ${session.targetId} detached)`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const matched = this.backends.find((b) => b.kind === session.backendKind);
|
|
84
|
+
if (!matched) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`No backend available for session kind: ${session.backendKind}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
backend = matched;
|
|
90
|
+
// If the session has an opaque targetId and the command does not
|
|
91
|
+
// carry its own CDP sessionId, inject the session's targetId as
|
|
92
|
+
// the CDP sessionId. Backends that support multi-target routing
|
|
93
|
+
// will forward it; backends that ignore it will treat the call
|
|
94
|
+
// as "most-recent-tab" as before.
|
|
95
|
+
if (session.targetId !== undefined && command.sessionId === undefined) {
|
|
96
|
+
outgoing = { ...command, sessionId: session.targetId };
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
backend = this.selectBackend();
|
|
100
|
+
}
|
|
101
|
+
return backend.send(outgoing, signal);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
disposeSession(id: string): void {
|
|
105
|
+
this.sessions.delete(id);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Evict a session that the backend has informed us is no longer
|
|
110
|
+
* valid — e.g. the chrome extension dispatched a
|
|
111
|
+
* `host_browser_session_invalidated` envelope after Chrome detached
|
|
112
|
+
* the debugger from the underlying tab/target.
|
|
113
|
+
*
|
|
114
|
+
* Functionally equivalent to {@link disposeSession} today (both
|
|
115
|
+
* remove the session from the manager's map so a subsequent
|
|
116
|
+
* `send()` throws "Unknown browser session") but preserved as a
|
|
117
|
+
* distinct method so call sites can stay explicit about intent.
|
|
118
|
+
* Callers that receive a detach/invalidated signal should use this
|
|
119
|
+
* method; callers that are cleaning up at end-of-lifecycle should
|
|
120
|
+
* use {@link disposeSession}.
|
|
121
|
+
*
|
|
122
|
+
* Returns `true` when a session was actually removed, `false` when
|
|
123
|
+
* no session with that id was tracked. Returning a boolean lets
|
|
124
|
+
* transport-level dispatchers (see
|
|
125
|
+
* `resolveHostBrowserSessionInvalidated`) log at the right level
|
|
126
|
+
* based on whether the invalidation had any effect.
|
|
127
|
+
*/
|
|
128
|
+
invalidateSession(id: string): boolean {
|
|
129
|
+
return this.sessions.delete(id);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Evict every session whose opaque `targetId` matches the supplied
|
|
134
|
+
* id. Used by the WS dispatcher when a `host_browser_session_invalidated`
|
|
135
|
+
* envelope arrives without a manager-level session id: the
|
|
136
|
+
* extension-side dispatcher only knows its own `tabId` / `targetId`
|
|
137
|
+
* and does not carry our uuid session handle.
|
|
138
|
+
*
|
|
139
|
+
* Returns the number of sessions removed. A zero return does not
|
|
140
|
+
* necessarily indicate an error — the target may not have a
|
|
141
|
+
* runtime-side session attached to it yet, or the session may
|
|
142
|
+
* already have been disposed by its owning tool.
|
|
143
|
+
*/
|
|
144
|
+
invalidateByTargetId(targetId: string): number {
|
|
145
|
+
let removed = 0;
|
|
146
|
+
for (const [id, session] of this.sessions) {
|
|
147
|
+
if (session.targetId === targetId) {
|
|
148
|
+
this.sessions.delete(id);
|
|
149
|
+
removed += 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return removed;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
disposeAll(): void {
|
|
156
|
+
for (const b of this.backends) b.dispose();
|
|
157
|
+
this.sessions.clear();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type BrowserBackendKind = "extension" | "local" | "cdp-inspect";
|
|
2
|
+
|
|
3
|
+
export interface CdpCommand {
|
|
4
|
+
method: string;
|
|
5
|
+
params?: Record<string, unknown>;
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface CdpResult {
|
|
10
|
+
/** Raw CDP result object; opaque to the manager. */
|
|
11
|
+
result?: unknown;
|
|
12
|
+
/** CDP error envelope if the command failed. */
|
|
13
|
+
error?: { code: number; message: string; data?: unknown };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BrowserSession {
|
|
17
|
+
id: string;
|
|
18
|
+
backendKind: BrowserBackendKind;
|
|
19
|
+
/** Opaque target/sessionId from the backend. Omitted for "most-recent-tab" commands. */
|
|
20
|
+
targetId?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface BrowserBackend {
|
|
24
|
+
kind: BrowserBackendKind;
|
|
25
|
+
isAvailable(): boolean;
|
|
26
|
+
send(command: CdpCommand, signal?: AbortSignal): Promise<CdpResult>;
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
INTERACTIVE_INTERFACES,
|
|
5
|
+
INTERFACE_IDS,
|
|
6
|
+
isInterfaceId,
|
|
7
|
+
supportsHostProxy,
|
|
8
|
+
} from "../types.js";
|
|
9
|
+
|
|
10
|
+
describe("INTERFACE_IDS", () => {
|
|
11
|
+
test("includes chrome-extension", () => {
|
|
12
|
+
expect(
|
|
13
|
+
(INTERFACE_IDS as readonly string[]).includes("chrome-extension"),
|
|
14
|
+
).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("still includes macos and other existing interfaces", () => {
|
|
18
|
+
for (const id of [
|
|
19
|
+
"macos",
|
|
20
|
+
"ios",
|
|
21
|
+
"cli",
|
|
22
|
+
"telegram",
|
|
23
|
+
"phone",
|
|
24
|
+
"vellum",
|
|
25
|
+
"whatsapp",
|
|
26
|
+
"slack",
|
|
27
|
+
"email",
|
|
28
|
+
]) {
|
|
29
|
+
expect((INTERFACE_IDS as readonly string[]).includes(id)).toBe(true);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("INTERACTIVE_INTERFACES", () => {
|
|
35
|
+
test("does NOT include chrome-extension", () => {
|
|
36
|
+
// Chrome extensions don't render SSE-backed prompter UI, so they must
|
|
37
|
+
// stay out of the interactive set even though they have an InterfaceId.
|
|
38
|
+
expect(INTERACTIVE_INTERFACES.has("chrome-extension" as never)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("still includes macos", () => {
|
|
42
|
+
expect(INTERACTIVE_INTERFACES.has("macos")).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("isInterfaceId", () => {
|
|
47
|
+
test("returns true for chrome-extension", () => {
|
|
48
|
+
expect(isInterfaceId("chrome-extension")).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("returns true for macos", () => {
|
|
52
|
+
expect(isInterfaceId("macos")).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("returns false for unknown interface", () => {
|
|
56
|
+
expect(isInterfaceId("safari-extension")).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("supportsHostProxy", () => {
|
|
61
|
+
// ── macOS: supports host_bash / host_file / host_cu, but NOT host_browser. ──
|
|
62
|
+
test("macos returns true (no capability)", () => {
|
|
63
|
+
expect(supportsHostProxy("macos")).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("macos returns true for host_bash", () => {
|
|
67
|
+
expect(supportsHostProxy("macos", "host_bash")).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("macos returns true for host_file", () => {
|
|
71
|
+
expect(supportsHostProxy("macos", "host_file")).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("macos returns true for host_cu", () => {
|
|
75
|
+
expect(supportsHostProxy("macos", "host_cu")).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("macos returns false for host_browser", () => {
|
|
79
|
+
expect(supportsHostProxy("macos", "host_browser")).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ── chrome-extension: only host_browser. ──
|
|
83
|
+
test("chrome-extension returns false (no capability)", () => {
|
|
84
|
+
// Chrome extension does not support "any host proxy at all" — it only
|
|
85
|
+
// supports host_browser, so the no-arg form must return false to keep
|
|
86
|
+
// existing call sites that guard desktop-only behavior unchanged.
|
|
87
|
+
expect(supportsHostProxy("chrome-extension")).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("chrome-extension returns true for host_browser", () => {
|
|
91
|
+
expect(supportsHostProxy("chrome-extension", "host_browser")).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("chrome-extension returns false for host_bash", () => {
|
|
95
|
+
expect(supportsHostProxy("chrome-extension", "host_bash")).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("chrome-extension returns false for host_file", () => {
|
|
99
|
+
expect(supportsHostProxy("chrome-extension", "host_file")).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("chrome-extension returns false for host_cu", () => {
|
|
103
|
+
expect(supportsHostProxy("chrome-extension", "host_cu")).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ── Non-supporting interfaces: false in all forms. ──
|
|
107
|
+
test("cli returns false (no capability)", () => {
|
|
108
|
+
expect(supportsHostProxy("cli")).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("cli returns false for host_bash", () => {
|
|
112
|
+
expect(supportsHostProxy("cli", "host_bash")).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("cli returns false for host_browser", () => {
|
|
116
|
+
expect(supportsHostProxy("cli", "host_browser")).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("telegram returns false (no capability)", () => {
|
|
120
|
+
expect(supportsHostProxy("telegram")).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("telegram returns false for host_browser", () => {
|
|
124
|
+
expect(supportsHostProxy("telegram", "host_browser")).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("vellum returns false (no capability)", () => {
|
|
128
|
+
expect(supportsHostProxy("vellum")).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("email returns false for host_browser", () => {
|
|
132
|
+
expect(supportsHostProxy("email", "host_browser")).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
});
|
package/src/channels/types.ts
CHANGED
|
@@ -48,6 +48,7 @@ export const INTERFACE_IDS = [
|
|
|
48
48
|
"whatsapp",
|
|
49
49
|
"slack",
|
|
50
50
|
"email",
|
|
51
|
+
"chrome-extension",
|
|
51
52
|
] as const;
|
|
52
53
|
|
|
53
54
|
export type InterfaceId = (typeof INTERFACE_IDS)[number];
|
|
@@ -90,9 +91,58 @@ export function isInteractiveInterface(id: InterfaceId): boolean {
|
|
|
90
91
|
return INTERACTIVE_INTERFACES.has(id);
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Host proxy capabilities that an interface can support. The macOS client
|
|
96
|
+
* supports all four; the chrome-extension interface only supports
|
|
97
|
+
* host_browser (via the Chrome DevTools Protocol proxy).
|
|
98
|
+
*/
|
|
99
|
+
export type HostProxyCapability =
|
|
100
|
+
| "host_bash"
|
|
101
|
+
| "host_file"
|
|
102
|
+
| "host_cu"
|
|
103
|
+
| "host_browser";
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Interfaces that support the full desktop host-proxy set (all four
|
|
107
|
+
* `HostProxyCapability` values). This is the capability-level identity used
|
|
108
|
+
* by the discriminated transport metadata union and by the
|
|
109
|
+
* `supportsHostProxy(id)` type predicate.
|
|
110
|
+
*
|
|
111
|
+
* Extend this literal type AND the `supportsHostProxy` implementation
|
|
112
|
+
* below in lock-step when adding a new host-capable client (e.g. a native
|
|
113
|
+
* Linux or Windows desktop).
|
|
114
|
+
*/
|
|
115
|
+
export type HostProxyInterfaceId = "macos";
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Whether the interface supports a host proxy capability.
|
|
119
|
+
*
|
|
120
|
+
* The no-arg form `supportsHostProxy(id)` asks "is this interface a desktop
|
|
121
|
+
* host-proxy client?" — it returns `true` only for macOS and is the type
|
|
122
|
+
* predicate that narrows `InterfaceId` to `HostProxyInterfaceId`. It returns
|
|
123
|
+
* `false` for chrome-extension because chrome-extension only supports
|
|
124
|
+
* `host_browser`, and the no-arg form is the gate that legacy desktop-only
|
|
125
|
+
* call sites use (e.g. preactivating computer-use, restoring host proxies
|
|
126
|
+
* in the drain queue). Callers that want to check a single capability —
|
|
127
|
+
* for example, to decide whether to keep `hostBrowserProxy` available for
|
|
128
|
+
* chrome-extension — should pass the capability explicitly:
|
|
129
|
+
* `supportsHostProxy(id, "host_browser")`.
|
|
130
|
+
*/
|
|
131
|
+
export function supportsHostProxy(id: InterfaceId): id is HostProxyInterfaceId;
|
|
132
|
+
export function supportsHostProxy(
|
|
133
|
+
id: InterfaceId,
|
|
134
|
+
capability: HostProxyCapability,
|
|
135
|
+
): boolean;
|
|
136
|
+
export function supportsHostProxy(
|
|
137
|
+
id: InterfaceId,
|
|
138
|
+
capability?: HostProxyCapability,
|
|
139
|
+
): boolean {
|
|
140
|
+
// host_browser is excluded for macos because the proxy path requires a
|
|
141
|
+
// Chrome extension that isn't guaranteed to be attached; browser tools
|
|
142
|
+
// fall back to the local Playwright Chromium instead.
|
|
143
|
+
if (id === "macos") return capability !== "host_browser";
|
|
144
|
+
if (id === "chrome-extension" && capability === "host_browser") return true;
|
|
145
|
+
return false;
|
|
96
146
|
}
|
|
97
147
|
|
|
98
148
|
export interface TurnInterfaceContext {
|