@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
|
@@ -3,12 +3,16 @@ import { beforeEach, describe, expect, jest, mock, test } from "bun:test";
|
|
|
3
3
|
const mockConnect = jest.fn();
|
|
4
4
|
const mockDisconnect = jest.fn();
|
|
5
5
|
let mockIsConnected = true;
|
|
6
|
+
let mockLastError: Error | null = null;
|
|
6
7
|
|
|
7
8
|
mock.module("../mcp/client.js", () => ({
|
|
8
9
|
McpClient: class {
|
|
9
10
|
get isConnected() {
|
|
10
11
|
return mockIsConnected;
|
|
11
12
|
}
|
|
13
|
+
get lastError() {
|
|
14
|
+
return mockLastError;
|
|
15
|
+
}
|
|
12
16
|
connect = mockConnect;
|
|
13
17
|
disconnect = mockDisconnect;
|
|
14
18
|
},
|
|
@@ -32,6 +36,7 @@ describe("checkServerHealth", () => {
|
|
|
32
36
|
mockConnect.mockReset();
|
|
33
37
|
mockDisconnect.mockReset();
|
|
34
38
|
mockIsConnected = true;
|
|
39
|
+
mockLastError = null;
|
|
35
40
|
});
|
|
36
41
|
|
|
37
42
|
test("returns Connected when server connects successfully", async () => {
|
|
@@ -43,7 +48,7 @@ describe("checkServerHealth", () => {
|
|
|
43
48
|
expect(mockDisconnect).toHaveBeenCalled();
|
|
44
49
|
});
|
|
45
50
|
|
|
46
|
-
test("returns Needs authentication when isConnected is false", async () => {
|
|
51
|
+
test("returns Needs authentication when isConnected is false and no lastError", async () => {
|
|
47
52
|
mockConnect.mockResolvedValue(undefined);
|
|
48
53
|
mockIsConnected = false;
|
|
49
54
|
|
|
@@ -51,8 +56,10 @@ describe("checkServerHealth", () => {
|
|
|
51
56
|
expect(result).toContain("Needs authentication");
|
|
52
57
|
});
|
|
53
58
|
|
|
54
|
-
test("returns Error when connect
|
|
55
|
-
mockConnect.
|
|
59
|
+
test("returns Error when connect fails with lastError", async () => {
|
|
60
|
+
mockConnect.mockResolvedValue(undefined);
|
|
61
|
+
mockIsConnected = false;
|
|
62
|
+
mockLastError = new Error("Connection refused");
|
|
56
63
|
mockDisconnect.mockResolvedValue(undefined);
|
|
57
64
|
|
|
58
65
|
const result = await checkServerHealth("test", serverConfig());
|
|
@@ -834,7 +834,9 @@ describe("round-trip: export -> validate -> preflight -> import", () => {
|
|
|
834
834
|
expect(sha256Hex(writtenDb)).toBe(sha256Hex(dbData));
|
|
835
835
|
|
|
836
836
|
const writtenConfig = readFileSync(testConfigPath, "utf8");
|
|
837
|
-
expect(writtenConfig).toBe(
|
|
837
|
+
expect(writtenConfig).toBe(
|
|
838
|
+
JSON.stringify({ model: "test-round-trip" }, null, 2) + "\n",
|
|
839
|
+
);
|
|
838
840
|
}
|
|
839
841
|
});
|
|
840
842
|
|
|
@@ -75,7 +75,7 @@ beforeAll(() => {
|
|
|
75
75
|
// Write test fixture files so the export reads real data
|
|
76
76
|
mkdirSync(testDbDir, { recursive: true });
|
|
77
77
|
writeFileSync(testDbPath, SQLITE_HEADER);
|
|
78
|
-
writeFileSync(testConfigPath, JSON.stringify(TEST_CONFIG, null, 2));
|
|
78
|
+
writeFileSync(testConfigPath, JSON.stringify(TEST_CONFIG, null, 2) + "\n");
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
// ---------------------------------------------------------------------------
|
|
@@ -323,7 +323,7 @@ describe("export data population", () => {
|
|
|
323
323
|
);
|
|
324
324
|
expect(configFile).toBeDefined();
|
|
325
325
|
const expectedConfigSize = Buffer.byteLength(
|
|
326
|
-
JSON.stringify(TEST_CONFIG, null, 2),
|
|
326
|
+
JSON.stringify(TEST_CONFIG, null, 2) + "\n",
|
|
327
327
|
);
|
|
328
328
|
expect(configFile!.size).toBe(expectedConfigSize);
|
|
329
329
|
});
|
|
@@ -428,6 +428,65 @@ describe("export graceful fallback", () => {
|
|
|
428
428
|
});
|
|
429
429
|
});
|
|
430
430
|
|
|
431
|
+
// ---------------------------------------------------------------------------
|
|
432
|
+
// Config sanitization tests
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
|
|
435
|
+
describe("export config sanitization", () => {
|
|
436
|
+
test("exported config.json has environment-specific fields stripped", async () => {
|
|
437
|
+
// Write a config with environment-specific fields that should be stripped
|
|
438
|
+
const configWithEnvFields = {
|
|
439
|
+
provider: "anthropic",
|
|
440
|
+
model: "test-model",
|
|
441
|
+
ingress: {
|
|
442
|
+
publicBaseUrl: "https://my-tunnel.example.com",
|
|
443
|
+
enabled: true,
|
|
444
|
+
port: 8080,
|
|
445
|
+
},
|
|
446
|
+
daemon: {
|
|
447
|
+
autoStart: true,
|
|
448
|
+
logLevel: "debug",
|
|
449
|
+
},
|
|
450
|
+
skills: {
|
|
451
|
+
load: {
|
|
452
|
+
extraDirs: ["/home/user/custom-skills", "/opt/skills"],
|
|
453
|
+
autoReload: true,
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
memory: { enabled: true },
|
|
457
|
+
};
|
|
458
|
+
writeFileSync(testConfigPath, JSON.stringify(configWithEnvFields, null, 2));
|
|
459
|
+
|
|
460
|
+
const req = new Request("http://localhost/v1/migrations/export", {
|
|
461
|
+
method: "POST",
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
const res = await handleMigrationExport(req);
|
|
465
|
+
const archiveData = new Uint8Array(await res.arrayBuffer());
|
|
466
|
+
const entries = parseTarEntries(archiveData);
|
|
467
|
+
|
|
468
|
+
const configEntry = entries.find((e) => e.name === "workspace/config.json");
|
|
469
|
+
expect(configEntry).toBeDefined();
|
|
470
|
+
|
|
471
|
+
const parsedConfig = JSON.parse(
|
|
472
|
+
new TextDecoder().decode(configEntry!.data),
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
// Environment-specific fields should be stripped/reset
|
|
476
|
+
expect(parsedConfig.ingress.publicBaseUrl).toBe("");
|
|
477
|
+
expect(parsedConfig.ingress.enabled).toBeUndefined();
|
|
478
|
+
expect(parsedConfig.daemon).toBeUndefined();
|
|
479
|
+
expect(parsedConfig.skills.load.extraDirs).toEqual([]);
|
|
480
|
+
|
|
481
|
+
// Non-environment-specific fields should be preserved
|
|
482
|
+
expect(parsedConfig.provider).toBe("anthropic");
|
|
483
|
+
expect(parsedConfig.model).toBe("test-model");
|
|
484
|
+
expect(parsedConfig.ingress.port).toBe(8080);
|
|
485
|
+
expect(parsedConfig.skills.load.autoReload).toBe(true);
|
|
486
|
+
expect(parsedConfig.memory.enabled).toBe(true);
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
431
490
|
// ---------------------------------------------------------------------------
|
|
432
491
|
// Auth policy registration tests
|
|
433
492
|
// ---------------------------------------------------------------------------
|
|
@@ -285,6 +285,72 @@ describe("streamExportVBundle round-trip", () => {
|
|
|
285
285
|
});
|
|
286
286
|
});
|
|
287
287
|
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
// Streaming config sanitization test
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
describe("streamExportVBundle config sanitization", () => {
|
|
293
|
+
test("exported config.json has environment-specific fields stripped", async () => {
|
|
294
|
+
// Write a config with environment-specific fields that should be stripped
|
|
295
|
+
const configWithEnvFields = {
|
|
296
|
+
provider: "anthropic",
|
|
297
|
+
model: "test-model",
|
|
298
|
+
ingress: {
|
|
299
|
+
publicBaseUrl: "https://my-tunnel.example.com",
|
|
300
|
+
enabled: true,
|
|
301
|
+
port: 8080,
|
|
302
|
+
},
|
|
303
|
+
daemon: {
|
|
304
|
+
autoStart: true,
|
|
305
|
+
logLevel: "debug",
|
|
306
|
+
},
|
|
307
|
+
skills: {
|
|
308
|
+
load: {
|
|
309
|
+
extraDirs: ["/home/user/custom-skills", "/opt/skills"],
|
|
310
|
+
autoReload: true,
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
memory: { enabled: true },
|
|
314
|
+
};
|
|
315
|
+
writeFileSync(testConfigPath, JSON.stringify(configWithEnvFields, null, 2));
|
|
316
|
+
|
|
317
|
+
const result = await streamExportVBundle({
|
|
318
|
+
workspaceDir: testDir,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const archiveData = new Uint8Array(readFileSync(result.tempPath));
|
|
323
|
+
const entries = parseTarEntries(archiveData);
|
|
324
|
+
|
|
325
|
+
const configEntry = entries.find(
|
|
326
|
+
(e) => e.name === "workspace/config.json",
|
|
327
|
+
);
|
|
328
|
+
expect(configEntry).toBeDefined();
|
|
329
|
+
|
|
330
|
+
const parsedConfig = JSON.parse(
|
|
331
|
+
new TextDecoder().decode(configEntry!.data),
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
// Environment-specific fields should be stripped/reset
|
|
335
|
+
expect(parsedConfig.ingress.publicBaseUrl).toBe("");
|
|
336
|
+
expect(parsedConfig.ingress.enabled).toBeUndefined();
|
|
337
|
+
expect(parsedConfig.daemon).toBeUndefined();
|
|
338
|
+
expect(parsedConfig.skills.load.extraDirs).toEqual([]);
|
|
339
|
+
|
|
340
|
+
// Non-environment-specific fields should be preserved
|
|
341
|
+
expect(parsedConfig.provider).toBe("anthropic");
|
|
342
|
+
expect(parsedConfig.model).toBe("test-model");
|
|
343
|
+
expect(parsedConfig.ingress.port).toBe(8080);
|
|
344
|
+
expect(parsedConfig.skills.load.autoReload).toBe(true);
|
|
345
|
+
expect(parsedConfig.memory.enabled).toBe(true);
|
|
346
|
+
} finally {
|
|
347
|
+
// Restore original config for subsequent tests
|
|
348
|
+
writeFileSync(testConfigPath, JSON.stringify(TEST_CONFIG, null, 2));
|
|
349
|
+
await result.cleanup();
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
288
354
|
// ---------------------------------------------------------------------------
|
|
289
355
|
// Cleanup idempotency test
|
|
290
356
|
// ---------------------------------------------------------------------------
|
|
@@ -731,7 +731,9 @@ describe("commitImport", () => {
|
|
|
731
731
|
expect(writtenDb).toEqual(newDbData);
|
|
732
732
|
|
|
733
733
|
const writtenConfig = readFileSync(testConfigPath, "utf8");
|
|
734
|
-
expect(writtenConfig).toBe(
|
|
734
|
+
expect(writtenConfig).toBe(
|
|
735
|
+
JSON.stringify({ model: "claude" }, null, 2) + "\n",
|
|
736
|
+
);
|
|
735
737
|
}
|
|
736
738
|
});
|
|
737
739
|
});
|
|
@@ -872,6 +874,104 @@ describe("commitImport — workspace clearing", () => {
|
|
|
872
874
|
});
|
|
873
875
|
});
|
|
874
876
|
|
|
877
|
+
// ---------------------------------------------------------------------------
|
|
878
|
+
// Config sanitization tests
|
|
879
|
+
// ---------------------------------------------------------------------------
|
|
880
|
+
|
|
881
|
+
describe("commitImport — config sanitization", () => {
|
|
882
|
+
test("imported workspace/config.json has environment-specific fields stripped", () => {
|
|
883
|
+
const configWithEnvFields = {
|
|
884
|
+
provider: "anthropic",
|
|
885
|
+
model: "test-model",
|
|
886
|
+
ingress: {
|
|
887
|
+
publicBaseUrl: "https://my-tunnel.example.com",
|
|
888
|
+
enabled: true,
|
|
889
|
+
port: 8080,
|
|
890
|
+
},
|
|
891
|
+
daemon: {
|
|
892
|
+
autoStart: true,
|
|
893
|
+
logLevel: "debug",
|
|
894
|
+
},
|
|
895
|
+
skills: {
|
|
896
|
+
load: {
|
|
897
|
+
extraDirs: ["/home/user/custom-skills"],
|
|
898
|
+
autoReload: true,
|
|
899
|
+
},
|
|
900
|
+
},
|
|
901
|
+
memory: { enabled: true },
|
|
902
|
+
};
|
|
903
|
+
const configData = new TextEncoder().encode(
|
|
904
|
+
JSON.stringify(configWithEnvFields, null, 2),
|
|
905
|
+
);
|
|
906
|
+
|
|
907
|
+
const vbundle = createValidVBundle([
|
|
908
|
+
{ path: "workspace/config.json", data: configData },
|
|
909
|
+
]);
|
|
910
|
+
|
|
911
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
912
|
+
const result = commitImport({
|
|
913
|
+
archiveData: vbundle,
|
|
914
|
+
pathResolver: resolver,
|
|
915
|
+
workspaceDir: testDir,
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
expect(result.ok).toBe(true);
|
|
919
|
+
if (!result.ok) return;
|
|
920
|
+
|
|
921
|
+
// Read the written config from disk and verify sanitization
|
|
922
|
+
const writtenConfig = JSON.parse(readFileSync(testConfigPath, "utf8"));
|
|
923
|
+
|
|
924
|
+
// Environment-specific fields should be stripped/reset
|
|
925
|
+
expect(writtenConfig.ingress.publicBaseUrl).toBe("");
|
|
926
|
+
expect(writtenConfig.ingress.enabled).toBeUndefined();
|
|
927
|
+
expect(writtenConfig.daemon).toBeUndefined();
|
|
928
|
+
expect(writtenConfig.skills.load.extraDirs).toEqual([]);
|
|
929
|
+
|
|
930
|
+
// Non-environment-specific fields should be preserved
|
|
931
|
+
expect(writtenConfig.provider).toBe("anthropic");
|
|
932
|
+
expect(writtenConfig.model).toBe("test-model");
|
|
933
|
+
expect(writtenConfig.ingress.port).toBe(8080);
|
|
934
|
+
expect(writtenConfig.skills.load.autoReload).toBe(true);
|
|
935
|
+
expect(writtenConfig.memory.enabled).toBe(true);
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
test("imported config/settings.json (legacy) has environment-specific fields stripped", () => {
|
|
939
|
+
const configWithEnvFields = {
|
|
940
|
+
provider: "openai",
|
|
941
|
+
daemon: { autoStart: false },
|
|
942
|
+
ingress: {
|
|
943
|
+
publicBaseUrl: "https://old-tunnel.example.com",
|
|
944
|
+
enabled: false,
|
|
945
|
+
},
|
|
946
|
+
skills: { load: { extraDirs: ["/legacy/skills"] } },
|
|
947
|
+
};
|
|
948
|
+
const configData = new TextEncoder().encode(
|
|
949
|
+
JSON.stringify(configWithEnvFields, null, 2),
|
|
950
|
+
);
|
|
951
|
+
|
|
952
|
+
const vbundle = createValidVBundle([
|
|
953
|
+
{ path: "config/settings.json", data: configData },
|
|
954
|
+
]);
|
|
955
|
+
|
|
956
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
957
|
+
const result = commitImport({
|
|
958
|
+
archiveData: vbundle,
|
|
959
|
+
pathResolver: resolver,
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
expect(result.ok).toBe(true);
|
|
963
|
+
if (!result.ok) return;
|
|
964
|
+
|
|
965
|
+
const writtenConfig = JSON.parse(readFileSync(testConfigPath, "utf8"));
|
|
966
|
+
|
|
967
|
+
expect(writtenConfig.ingress.publicBaseUrl).toBe("");
|
|
968
|
+
expect(writtenConfig.ingress.enabled).toBeUndefined();
|
|
969
|
+
expect(writtenConfig.daemon).toBeUndefined();
|
|
970
|
+
expect(writtenConfig.skills.load.extraDirs).toEqual([]);
|
|
971
|
+
expect(writtenConfig.provider).toBe("openai");
|
|
972
|
+
});
|
|
973
|
+
});
|
|
974
|
+
|
|
875
975
|
// ---------------------------------------------------------------------------
|
|
876
976
|
// Auth policy registration tests
|
|
877
977
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync guard: the `NATIVE_HOST_MARKER_HEADER` and
|
|
3
|
+
* `NATIVE_HOST_MARKER_VALUE` constants are referenced from two coupled
|
|
4
|
+
* files that MUST stay in lockstep:
|
|
5
|
+
*
|
|
6
|
+
* - assistant/src/runtime/routes/browser-extension-pair-routes.ts
|
|
7
|
+
* (the runtime side that rejects unmarked pair requests with 403)
|
|
8
|
+
* - clients/chrome-extension/native-host/src/index.ts
|
|
9
|
+
* (the native messaging helper that stamps the marker on every
|
|
10
|
+
* pair POST before forwarding to the assistant)
|
|
11
|
+
*
|
|
12
|
+
* If either side drifts (typo in the header name, or a different
|
|
13
|
+
* value), the pair flow silently breaks end-to-end: the native host
|
|
14
|
+
* sends an unrecognizable header, the runtime rejects the request as
|
|
15
|
+
* if it came from a drive-by webpage, and the extension never gets a
|
|
16
|
+
* token.
|
|
17
|
+
*
|
|
18
|
+
* This guard reads the raw source text of both files at test time and
|
|
19
|
+
* uses regexes to extract the literal string values of each constant.
|
|
20
|
+
* It deliberately does NOT `import` the constants — the whole point is
|
|
21
|
+
* to catch the physical file divergence that an import-based test
|
|
22
|
+
* would paper over (a typo'd export still type-checks if both files
|
|
23
|
+
* are updated independently but inconsistently).
|
|
24
|
+
*
|
|
25
|
+
* Modeled on `extension-id-sync-guard.test.ts`.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { readFileSync } from "node:fs";
|
|
29
|
+
import { join, resolve } from "node:path";
|
|
30
|
+
import { describe, expect, test } from "bun:test";
|
|
31
|
+
|
|
32
|
+
const repoRoot = resolve(__dirname, "..", "..", "..");
|
|
33
|
+
|
|
34
|
+
const ASSISTANT_PAIR_ROUTE_PATH =
|
|
35
|
+
"assistant/src/runtime/routes/browser-extension-pair-routes.ts";
|
|
36
|
+
const NATIVE_HOST_INDEX_PATH =
|
|
37
|
+
"clients/chrome-extension/native-host/src/index.ts";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extract the string literal value of an exported const from raw
|
|
41
|
+
* TypeScript source text. Accepts both single- and double-quoted
|
|
42
|
+
* forms and is permissive about whitespace so minor formatter changes
|
|
43
|
+
* don't break the guard.
|
|
44
|
+
*
|
|
45
|
+
* Returns the literal string (without quotes) or `null` if the export
|
|
46
|
+
* cannot be found.
|
|
47
|
+
*/
|
|
48
|
+
function extractExportedConstString(
|
|
49
|
+
source: string,
|
|
50
|
+
constName: string,
|
|
51
|
+
): string | null {
|
|
52
|
+
// Matches: export const FOO = "bar"; (also ' and whitespace
|
|
53
|
+
// variations). The value group stops at the next matching quote.
|
|
54
|
+
const re = new RegExp(
|
|
55
|
+
`export\\s+const\\s+${constName}\\s*=\\s*(['"])([^'"]*)\\1`,
|
|
56
|
+
);
|
|
57
|
+
const match = source.match(re);
|
|
58
|
+
if (!match) return null;
|
|
59
|
+
return match[2] ?? null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe("native-host marker sync guard", () => {
|
|
63
|
+
test("NATIVE_HOST_MARKER_HEADER matches across runtime route and native host", () => {
|
|
64
|
+
const assistantSource = readFileSync(
|
|
65
|
+
join(repoRoot, ASSISTANT_PAIR_ROUTE_PATH),
|
|
66
|
+
"utf8",
|
|
67
|
+
);
|
|
68
|
+
const nativeHostSource = readFileSync(
|
|
69
|
+
join(repoRoot, NATIVE_HOST_INDEX_PATH),
|
|
70
|
+
"utf8",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const assistantHeader = extractExportedConstString(
|
|
74
|
+
assistantSource,
|
|
75
|
+
"NATIVE_HOST_MARKER_HEADER",
|
|
76
|
+
);
|
|
77
|
+
const nativeHostHeader = extractExportedConstString(
|
|
78
|
+
nativeHostSource,
|
|
79
|
+
"NATIVE_HOST_MARKER_HEADER",
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(assistantHeader).not.toBeNull();
|
|
83
|
+
expect(nativeHostHeader).not.toBeNull();
|
|
84
|
+
// Also require a non-empty value so an accidental `""` in either
|
|
85
|
+
// file trips the guard.
|
|
86
|
+
expect(assistantHeader!.length).toBeGreaterThan(0);
|
|
87
|
+
expect(nativeHostHeader!.length).toBeGreaterThan(0);
|
|
88
|
+
|
|
89
|
+
if (assistantHeader !== nativeHostHeader) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`NATIVE_HOST_MARKER_HEADER drift detected:\n` +
|
|
92
|
+
` ${ASSISTANT_PAIR_ROUTE_PATH}: ${JSON.stringify(
|
|
93
|
+
assistantHeader,
|
|
94
|
+
)}\n` +
|
|
95
|
+
` ${NATIVE_HOST_INDEX_PATH}: ${JSON.stringify(nativeHostHeader)}\n\n` +
|
|
96
|
+
`Both files must declare the same header name or pairing ` +
|
|
97
|
+
`silently breaks (the runtime rejects the native host's ` +
|
|
98
|
+
`requests as unmarked drive-by browser fetches).`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("NATIVE_HOST_MARKER_VALUE matches across runtime route and native host", () => {
|
|
104
|
+
const assistantSource = readFileSync(
|
|
105
|
+
join(repoRoot, ASSISTANT_PAIR_ROUTE_PATH),
|
|
106
|
+
"utf8",
|
|
107
|
+
);
|
|
108
|
+
const nativeHostSource = readFileSync(
|
|
109
|
+
join(repoRoot, NATIVE_HOST_INDEX_PATH),
|
|
110
|
+
"utf8",
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const assistantValue = extractExportedConstString(
|
|
114
|
+
assistantSource,
|
|
115
|
+
"NATIVE_HOST_MARKER_VALUE",
|
|
116
|
+
);
|
|
117
|
+
const nativeHostValue = extractExportedConstString(
|
|
118
|
+
nativeHostSource,
|
|
119
|
+
"NATIVE_HOST_MARKER_VALUE",
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(assistantValue).not.toBeNull();
|
|
123
|
+
expect(nativeHostValue).not.toBeNull();
|
|
124
|
+
expect(assistantValue!.length).toBeGreaterThan(0);
|
|
125
|
+
expect(nativeHostValue!.length).toBeGreaterThan(0);
|
|
126
|
+
|
|
127
|
+
if (assistantValue !== nativeHostValue) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`NATIVE_HOST_MARKER_VALUE drift detected:\n` +
|
|
130
|
+
` ${ASSISTANT_PAIR_ROUTE_PATH}: ${JSON.stringify(
|
|
131
|
+
assistantValue,
|
|
132
|
+
)}\n` +
|
|
133
|
+
` ${NATIVE_HOST_INDEX_PATH}: ${JSON.stringify(nativeHostValue)}\n\n` +
|
|
134
|
+
`Both files must declare the same marker value or pairing ` +
|
|
135
|
+
`silently breaks (the runtime rejects the native host's ` +
|
|
136
|
+
`requests as unmarked drive-by browser fetches).`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("extractExportedConstString helper handles both quote styles", () => {
|
|
142
|
+
// Smoke-test the parser helper itself so a regression in the
|
|
143
|
+
// regex can't silently mask a real drift in the coupled files.
|
|
144
|
+
expect(
|
|
145
|
+
extractExportedConstString(`export const FOO = "hello";`, "FOO"),
|
|
146
|
+
).toBe("hello");
|
|
147
|
+
expect(
|
|
148
|
+
extractExportedConstString(`export const FOO = 'hello';`, "FOO"),
|
|
149
|
+
).toBe("hello");
|
|
150
|
+
expect(
|
|
151
|
+
extractExportedConstString(`export const FOO = "spaced";`, "FOO"),
|
|
152
|
+
).toBe("spaced");
|
|
153
|
+
expect(
|
|
154
|
+
extractExportedConstString(`export const OTHER = "x";`, "FOO"),
|
|
155
|
+
).toBeNull();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -2,14 +2,14 @@ import { describe, expect, mock, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
const mockGetApp = mock((_appId: string) => ({
|
|
4
4
|
id: "app-1",
|
|
5
|
-
|
|
5
|
+
provider: "google",
|
|
6
6
|
clientId: "client-1",
|
|
7
7
|
}));
|
|
8
8
|
|
|
9
9
|
const mockListConnections = mock(() => [
|
|
10
10
|
{
|
|
11
11
|
id: "conn-1",
|
|
12
|
-
|
|
12
|
+
provider: "google",
|
|
13
13
|
accountInfo: '{"email":"alice@example.com"}',
|
|
14
14
|
grantedScopes: '["email","profile"]',
|
|
15
15
|
status: "active",
|
|
@@ -20,7 +20,7 @@ const mockListConnections = mock(() => [
|
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
id: "conn-2",
|
|
23
|
-
|
|
23
|
+
provider: "google",
|
|
24
24
|
accountInfo: null,
|
|
25
25
|
grantedScopes: [],
|
|
26
26
|
status: "active",
|
|
@@ -37,28 +37,33 @@ mock.module("../oauth/oauth-store.js", () => ({
|
|
|
37
37
|
getApp: mockGetApp,
|
|
38
38
|
getAppClientSecret: mock(() => Promise.resolve(undefined)),
|
|
39
39
|
getConnection: mock(() => undefined),
|
|
40
|
-
getProvider: mock((
|
|
41
|
-
|
|
40
|
+
getProvider: mock((provider: string) =>
|
|
41
|
+
provider === "google"
|
|
42
42
|
? {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
provider: "google",
|
|
44
|
+
displayLabel: "Google",
|
|
45
45
|
description: "Google OAuth provider",
|
|
46
46
|
dashboardUrl: "https://console.cloud.google.com/apis/credentials",
|
|
47
|
+
logoUrl: null,
|
|
47
48
|
clientIdPlaceholder: null,
|
|
48
49
|
requiresClientSecret: 1,
|
|
49
50
|
managedServiceConfigKey: "google-oauth",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
52
|
+
tokenExchangeUrl: "https://oauth2.googleapis.com/token",
|
|
53
|
+
refreshUrl: null,
|
|
54
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
53
55
|
userinfoUrl: null,
|
|
54
56
|
baseUrl: null,
|
|
55
57
|
defaultScopes: "[]",
|
|
56
58
|
scopePolicy: "[]",
|
|
57
|
-
|
|
59
|
+
scopeSeparator: null,
|
|
60
|
+
authorizeParams: null,
|
|
58
61
|
pingUrl: null,
|
|
59
62
|
pingMethod: null,
|
|
60
63
|
pingHeaders: null,
|
|
61
64
|
pingBody: null,
|
|
65
|
+
revokeUrl: null,
|
|
66
|
+
revokeBodyTemplate: null,
|
|
62
67
|
loopbackPort: null,
|
|
63
68
|
injectionTemplates: null,
|
|
64
69
|
appType: null,
|
|
@@ -80,7 +85,7 @@ mock.module("../oauth/oauth-store.js", () => ({
|
|
|
80
85
|
upsertApp: mock(() =>
|
|
81
86
|
Promise.resolve({
|
|
82
87
|
id: "app-1",
|
|
83
|
-
|
|
88
|
+
provider: "google",
|
|
84
89
|
clientId: "client-1",
|
|
85
90
|
createdAt: 1735689500000,
|
|
86
91
|
updatedAt: 1735689550000,
|