@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
|
@@ -9,6 +9,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
9
9
|
}),
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
|
+
import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
|
|
12
13
|
import type { Conversation } from "../daemon/conversation.js";
|
|
13
14
|
import * as trustStore from "../permissions/trust-store.js";
|
|
14
15
|
import type {
|
|
@@ -81,6 +82,7 @@ function registerPendingConfirmation(
|
|
|
81
82
|
|
|
82
83
|
describe("getChannelApprovalPrompt", () => {
|
|
83
84
|
beforeEach(() => {
|
|
85
|
+
_setOverridesForTesting({});
|
|
84
86
|
pendingInteractions.clear();
|
|
85
87
|
});
|
|
86
88
|
|
|
@@ -169,6 +171,20 @@ describe("getChannelApprovalPrompt", () => {
|
|
|
169
171
|
const result = getChannelApprovalPrompt("conv-2");
|
|
170
172
|
expect(result).toBeNull();
|
|
171
173
|
});
|
|
174
|
+
|
|
175
|
+
test("returns approve_once + reject only under v2", () => {
|
|
176
|
+
_setOverridesForTesting({ "permission-controls-v2": true });
|
|
177
|
+
registerPendingConfirmation("req-v2", "conv-1", "shell");
|
|
178
|
+
|
|
179
|
+
const result = getChannelApprovalPrompt("conv-1");
|
|
180
|
+
expect(result).not.toBeNull();
|
|
181
|
+
expect(result!.actions.map((a) => a.id)).toEqual([
|
|
182
|
+
"approve_once",
|
|
183
|
+
"reject",
|
|
184
|
+
]);
|
|
185
|
+
expect(result!.plainTextFallback).not.toContain("10 minutes");
|
|
186
|
+
expect(result!.plainTextFallback).not.toContain("always");
|
|
187
|
+
});
|
|
172
188
|
});
|
|
173
189
|
|
|
174
190
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -241,6 +257,7 @@ describe("buildApprovalUIMetadata", () => {
|
|
|
241
257
|
|
|
242
258
|
describe("handleChannelDecision", () => {
|
|
243
259
|
beforeEach(() => {
|
|
260
|
+
_setOverridesForTesting({});
|
|
244
261
|
pendingInteractions.clear();
|
|
245
262
|
});
|
|
246
263
|
|
|
@@ -414,6 +431,24 @@ describe("handleChannelDecision", () => {
|
|
|
414
431
|
|
|
415
432
|
addRuleSpy.mockRestore();
|
|
416
433
|
});
|
|
434
|
+
|
|
435
|
+
test("v2 collapses legacy approval actions to one-time allow without persisting rules", () => {
|
|
436
|
+
_setOverridesForTesting({ "permission-controls-v2": true });
|
|
437
|
+
registerPendingConfirmation("req-v2", "conv-1", "shell");
|
|
438
|
+
|
|
439
|
+
const addRuleSpy = spyOn(trustStore, "addRule");
|
|
440
|
+
const result = handleChannelDecision("conv-1", {
|
|
441
|
+
action: "approve_always",
|
|
442
|
+
requestId: "req-v2",
|
|
443
|
+
source: "plain_text",
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
expect(result).toEqual({
|
|
447
|
+
applied: true,
|
|
448
|
+
requestId: "req-v2",
|
|
449
|
+
});
|
|
450
|
+
expect(addRuleSpy).not.toHaveBeenCalled();
|
|
451
|
+
});
|
|
417
452
|
});
|
|
418
453
|
|
|
419
454
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -421,6 +456,10 @@ describe("handleChannelDecision", () => {
|
|
|
421
456
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
422
457
|
|
|
423
458
|
describe("buildGuardianApprovalPrompt", () => {
|
|
459
|
+
beforeEach(() => {
|
|
460
|
+
_setOverridesForTesting({});
|
|
461
|
+
});
|
|
462
|
+
|
|
424
463
|
test("prompt includes requester identifier and tool name", () => {
|
|
425
464
|
const approvalInfo: PendingApprovalInfo = {
|
|
426
465
|
requestId: "req-g1",
|
|
@@ -457,6 +496,20 @@ describe("buildGuardianApprovalPrompt", () => {
|
|
|
457
496
|
expect(prompt.plainTextFallback).toContain("yes");
|
|
458
497
|
expect(prompt.plainTextFallback).toContain("no");
|
|
459
498
|
});
|
|
499
|
+
|
|
500
|
+
test("uses approve_once + reject only under v2", () => {
|
|
501
|
+
_setOverridesForTesting({ "permission-controls-v2": true });
|
|
502
|
+
const approvalInfo: PendingApprovalInfo = {
|
|
503
|
+
requestId: "req-g4",
|
|
504
|
+
toolName: "shell",
|
|
505
|
+
input: {},
|
|
506
|
+
riskLevel: "medium",
|
|
507
|
+
persistentDecisionsAllowed: true,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const prompt = buildGuardianApprovalPrompt(approvalInfo, "dana");
|
|
511
|
+
expect(prompt.actions.map((a) => a.id)).toEqual(["approve_once", "reject"]);
|
|
512
|
+
});
|
|
460
513
|
});
|
|
461
514
|
|
|
462
515
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import {
|
|
10
|
+
afterAll,
|
|
11
|
+
afterEach,
|
|
12
|
+
beforeEach,
|
|
13
|
+
describe,
|
|
14
|
+
expect,
|
|
15
|
+
mock,
|
|
16
|
+
test,
|
|
17
|
+
} from "bun:test";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Mocks — declared before imports that depend on platform/logger
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
const WORKSPACE_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
24
|
+
const CONFIG_PATH = join(WORKSPACE_DIR, "config.json");
|
|
25
|
+
|
|
26
|
+
function ensureTestDir(): void {
|
|
27
|
+
const dirs = [
|
|
28
|
+
WORKSPACE_DIR,
|
|
29
|
+
join(WORKSPACE_DIR, "data"),
|
|
30
|
+
join(WORKSPACE_DIR, "data", "memory"),
|
|
31
|
+
join(WORKSPACE_DIR, "data", "memory", "knowledge"),
|
|
32
|
+
join(WORKSPACE_DIR, "data", "logs"),
|
|
33
|
+
];
|
|
34
|
+
for (const dir of dirs) {
|
|
35
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function makeLoggerStub(): Record<string, unknown> {
|
|
40
|
+
const stub: Record<string, unknown> = {};
|
|
41
|
+
for (const m of [
|
|
42
|
+
"info",
|
|
43
|
+
"warn",
|
|
44
|
+
"error",
|
|
45
|
+
"debug",
|
|
46
|
+
"trace",
|
|
47
|
+
"fatal",
|
|
48
|
+
"silent",
|
|
49
|
+
"child",
|
|
50
|
+
]) {
|
|
51
|
+
stub[m] = m === "child" ? () => makeLoggerStub() : () => {};
|
|
52
|
+
}
|
|
53
|
+
return stub;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
mock.module("../util/logger.js", () => ({
|
|
57
|
+
getLogger: () => makeLoggerStub(),
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Feature flag mock — controls whether managed-gemini-embeddings-enabled is on
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
let featureFlagEnabled = false;
|
|
65
|
+
|
|
66
|
+
mock.module("../config/assistant-feature-flags.js", () => ({
|
|
67
|
+
isAssistantFeatureFlagEnabled: (key: string) => {
|
|
68
|
+
if (key === "managed-gemini-embeddings-enabled") return featureFlagEnabled;
|
|
69
|
+
return true;
|
|
70
|
+
},
|
|
71
|
+
_setOverridesForTesting: () => {},
|
|
72
|
+
clearFeatureFlagOverridesCache: () => {},
|
|
73
|
+
initFeatureFlagOverrides: async () => {},
|
|
74
|
+
getAssistantFeatureFlagDefaults: () => ({}),
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
// Restore all mocked modules after this file's tests complete to prevent
|
|
78
|
+
// cross-test contamination when running grouped with other test files.
|
|
79
|
+
afterAll(() => {
|
|
80
|
+
mock.restore();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
import { invalidateConfigCache, loadConfig } from "../config/loader.js";
|
|
84
|
+
import { _setStorePath } from "../security/encrypted-store.js";
|
|
85
|
+
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Helpers
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
function writeConfig(obj: unknown): void {
|
|
91
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readConfig(): Record<string, unknown> {
|
|
95
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Stash and restore IS_PLATFORM across each test. */
|
|
99
|
+
let originalIsPlatform: string | undefined;
|
|
100
|
+
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Tests
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
describe("managed Gemini embedding defaults (via loadConfig)", () => {
|
|
106
|
+
beforeEach(() => {
|
|
107
|
+
ensureTestDir();
|
|
108
|
+
const resetPaths = [
|
|
109
|
+
CONFIG_PATH,
|
|
110
|
+
join(WORKSPACE_DIR, "keys.enc"),
|
|
111
|
+
join(WORKSPACE_DIR, "data"),
|
|
112
|
+
join(WORKSPACE_DIR, "data", "memory"),
|
|
113
|
+
];
|
|
114
|
+
for (const path of resetPaths) {
|
|
115
|
+
if (existsSync(path)) {
|
|
116
|
+
rmSync(path, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
ensureTestDir();
|
|
120
|
+
_setStorePath(join(WORKSPACE_DIR, "keys.enc"));
|
|
121
|
+
invalidateConfigCache();
|
|
122
|
+
|
|
123
|
+
// Reset mock state
|
|
124
|
+
featureFlagEnabled = false;
|
|
125
|
+
originalIsPlatform = process.env.IS_PLATFORM;
|
|
126
|
+
delete process.env.IS_PLATFORM;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
afterEach(() => {
|
|
130
|
+
_setStorePath(null);
|
|
131
|
+
invalidateConfigCache();
|
|
132
|
+
|
|
133
|
+
// Restore IS_PLATFORM
|
|
134
|
+
if (originalIsPlatform !== undefined) {
|
|
135
|
+
process.env.IS_PLATFORM = originalIsPlatform;
|
|
136
|
+
} else {
|
|
137
|
+
delete process.env.IS_PLATFORM;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("applies managed Gemini defaults when FF on + IS_PLATFORM + provider auto", () => {
|
|
142
|
+
writeConfig({});
|
|
143
|
+
|
|
144
|
+
featureFlagEnabled = true;
|
|
145
|
+
process.env.IS_PLATFORM = "true";
|
|
146
|
+
|
|
147
|
+
const config = loadConfig();
|
|
148
|
+
|
|
149
|
+
// In-memory config should have managed Gemini defaults
|
|
150
|
+
expect(config.memory.embeddings.provider).toBe("gemini");
|
|
151
|
+
expect(config.memory.embeddings.geminiModel).toBe(
|
|
152
|
+
"gemini-embedding-2-preview",
|
|
153
|
+
);
|
|
154
|
+
expect(config.memory.embeddings.geminiDimensions).toBe(3072);
|
|
155
|
+
expect(config.memory.qdrant.vectorSize).toBe(3072);
|
|
156
|
+
|
|
157
|
+
// Config file on disk should also be updated
|
|
158
|
+
const raw = readConfig();
|
|
159
|
+
const memoryRaw = raw.memory as Record<string, unknown>;
|
|
160
|
+
const embeddingsRaw = memoryRaw.embeddings as Record<string, unknown>;
|
|
161
|
+
const qdrantRaw = memoryRaw.qdrant as Record<string, unknown>;
|
|
162
|
+
expect(embeddingsRaw.provider).toBe("gemini");
|
|
163
|
+
expect(embeddingsRaw.geminiModel).toBe("gemini-embedding-2-preview");
|
|
164
|
+
expect(embeddingsRaw.geminiDimensions).toBe(3072);
|
|
165
|
+
expect(qdrantRaw.vectorSize).toBe(3072);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("does NOT apply when feature flag is OFF", () => {
|
|
169
|
+
writeConfig({});
|
|
170
|
+
|
|
171
|
+
featureFlagEnabled = false;
|
|
172
|
+
process.env.IS_PLATFORM = "true";
|
|
173
|
+
|
|
174
|
+
const config = loadConfig();
|
|
175
|
+
|
|
176
|
+
expect(config.memory.embeddings.provider).toBe("auto");
|
|
177
|
+
expect(config.memory.qdrant.vectorSize).toBe(384);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("does NOT apply when IS_PLATFORM is not set", () => {
|
|
181
|
+
writeConfig({});
|
|
182
|
+
|
|
183
|
+
featureFlagEnabled = true;
|
|
184
|
+
delete process.env.IS_PLATFORM;
|
|
185
|
+
|
|
186
|
+
const config = loadConfig();
|
|
187
|
+
|
|
188
|
+
expect(config.memory.embeddings.provider).toBe("auto");
|
|
189
|
+
expect(config.memory.qdrant.vectorSize).toBe(384);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("does NOT apply when provider is explicitly set to local", () => {
|
|
193
|
+
writeConfig({
|
|
194
|
+
memory: { embeddings: { provider: "local" } },
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
featureFlagEnabled = true;
|
|
198
|
+
process.env.IS_PLATFORM = "true";
|
|
199
|
+
|
|
200
|
+
const config = loadConfig();
|
|
201
|
+
expect(config.memory.embeddings.provider).toBe("local");
|
|
202
|
+
expect(config.memory.qdrant.vectorSize).toBe(384);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("does NOT apply when provider is explicitly set to openai", () => {
|
|
206
|
+
writeConfig({
|
|
207
|
+
memory: { embeddings: { provider: "openai" } },
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
featureFlagEnabled = true;
|
|
211
|
+
process.env.IS_PLATFORM = "true";
|
|
212
|
+
|
|
213
|
+
const config = loadConfig();
|
|
214
|
+
expect(config.memory.embeddings.provider).toBe("openai");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("does NOT apply when provider is explicitly set to gemini", () => {
|
|
218
|
+
writeConfig({
|
|
219
|
+
memory: {
|
|
220
|
+
embeddings: { provider: "gemini", geminiDimensions: 768 },
|
|
221
|
+
qdrant: { vectorSize: 768 },
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
featureFlagEnabled = true;
|
|
226
|
+
process.env.IS_PLATFORM = "true";
|
|
227
|
+
|
|
228
|
+
const config = loadConfig();
|
|
229
|
+
|
|
230
|
+
// Already gemini — should not overwrite user's custom dimensions
|
|
231
|
+
expect(config.memory.embeddings.provider).toBe("gemini");
|
|
232
|
+
expect(config.memory.embeddings.geminiDimensions).toBe(768);
|
|
233
|
+
expect(config.memory.qdrant.vectorSize).toBe(768);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("does NOT apply when provider is explicitly set to ollama", () => {
|
|
237
|
+
writeConfig({
|
|
238
|
+
memory: { embeddings: { provider: "ollama" } },
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
featureFlagEnabled = true;
|
|
242
|
+
process.env.IS_PLATFORM = "true";
|
|
243
|
+
|
|
244
|
+
const config = loadConfig();
|
|
245
|
+
expect(config.memory.embeddings.provider).toBe("ollama");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("is idempotent — second loadConfig is a no-op after migration", () => {
|
|
249
|
+
writeConfig({});
|
|
250
|
+
|
|
251
|
+
featureFlagEnabled = true;
|
|
252
|
+
process.env.IS_PLATFORM = "true";
|
|
253
|
+
|
|
254
|
+
const config = loadConfig();
|
|
255
|
+
expect(config.memory.embeddings.provider).toBe("gemini");
|
|
256
|
+
|
|
257
|
+
// Read file content after first migration
|
|
258
|
+
const contentAfterFirst = readFileSync(CONFIG_PATH, "utf-8");
|
|
259
|
+
|
|
260
|
+
// Second call — provider is now "gemini", not "auto", so migration skipped
|
|
261
|
+
invalidateConfigCache();
|
|
262
|
+
const config2 = loadConfig();
|
|
263
|
+
expect(config2.memory.embeddings.provider).toBe("gemini");
|
|
264
|
+
|
|
265
|
+
// File on disk should not have changed
|
|
266
|
+
const contentAfterSecond = readFileSync(CONFIG_PATH, "utf-8");
|
|
267
|
+
expect(contentAfterSecond).toBe(contentAfterFirst);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("preserves existing config values while setting managed defaults", () => {
|
|
271
|
+
writeConfig({
|
|
272
|
+
provider: "anthropic",
|
|
273
|
+
model: "claude-opus-4-6",
|
|
274
|
+
memory: {
|
|
275
|
+
enabled: true,
|
|
276
|
+
qdrant: { collection: "my-collection", onDisk: false },
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
featureFlagEnabled = true;
|
|
281
|
+
process.env.IS_PLATFORM = "true";
|
|
282
|
+
|
|
283
|
+
const config = loadConfig();
|
|
284
|
+
|
|
285
|
+
// Managed defaults applied
|
|
286
|
+
expect(config.memory.embeddings.provider).toBe("gemini");
|
|
287
|
+
expect(config.memory.embeddings.geminiModel).toBe(
|
|
288
|
+
"gemini-embedding-2-preview",
|
|
289
|
+
);
|
|
290
|
+
expect(config.memory.qdrant.vectorSize).toBe(3072);
|
|
291
|
+
|
|
292
|
+
// Existing values preserved
|
|
293
|
+
const raw = readConfig();
|
|
294
|
+
expect(raw.provider).toBe("anthropic");
|
|
295
|
+
expect(raw.model).toBe("claude-opus-4-6");
|
|
296
|
+
const memoryRaw = raw.memory as Record<string, unknown>;
|
|
297
|
+
expect(memoryRaw.enabled).toBe(true);
|
|
298
|
+
const qdrantRaw = memoryRaw.qdrant as Record<string, unknown>;
|
|
299
|
+
expect(qdrantRaw.collection).toBe("my-collection");
|
|
300
|
+
expect(qdrantRaw.onDisk).toBe(false);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("does NOT apply when both FF off and IS_PLATFORM not set", () => {
|
|
304
|
+
writeConfig({});
|
|
305
|
+
|
|
306
|
+
featureFlagEnabled = false;
|
|
307
|
+
delete process.env.IS_PLATFORM;
|
|
308
|
+
|
|
309
|
+
const config = loadConfig();
|
|
310
|
+
|
|
311
|
+
expect(config.memory.embeddings.provider).toBe("auto");
|
|
312
|
+
expect(config.memory.qdrant.vectorSize).toBe(384);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("applies when IS_PLATFORM is '1'", () => {
|
|
316
|
+
writeConfig({});
|
|
317
|
+
|
|
318
|
+
featureFlagEnabled = true;
|
|
319
|
+
process.env.IS_PLATFORM = "1";
|
|
320
|
+
|
|
321
|
+
const config = loadConfig();
|
|
322
|
+
|
|
323
|
+
expect(config.memory.embeddings.provider).toBe("gemini");
|
|
324
|
+
expect(config.memory.embeddings.geminiDimensions).toBe(3072);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
@@ -87,7 +87,7 @@ describe("getSchemaAtPath", () => {
|
|
|
87
87
|
expect(result).not.toBeNull();
|
|
88
88
|
// maxTokens has a default, so it should be parseable
|
|
89
89
|
const parsed = (result as z.ZodType).parse(undefined);
|
|
90
|
-
expect(parsed).toBe(
|
|
90
|
+
expect(parsed).toBe(64000);
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
test("navigates nested paths (memory.segmentation → object schema)", () => {
|
|
@@ -188,7 +188,7 @@ describe("z.toJSONSchema integration", () => {
|
|
|
188
188
|
expect(properties.calls).toBeDefined();
|
|
189
189
|
expect(properties.memory).toBeDefined();
|
|
190
190
|
expect(properties.timeouts).toBeDefined();
|
|
191
|
-
expect(properties.
|
|
191
|
+
expect(properties.permissions).toBeDefined();
|
|
192
192
|
});
|
|
193
193
|
|
|
194
194
|
test("full schema emits real properties for transformed fields (ingress)", () => {
|
|
@@ -81,7 +81,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
81
81
|
"inference-provider-native",
|
|
82
82
|
);
|
|
83
83
|
expect(result.services["web-search"].mode).toBe("your-own");
|
|
84
|
-
expect(result.maxTokens).toBe(
|
|
84
|
+
expect(result.maxTokens).toBe(64000);
|
|
85
85
|
expect(result.thinking).toEqual({
|
|
86
86
|
enabled: true,
|
|
87
87
|
streamThinking: true,
|
|
@@ -107,9 +107,6 @@ describe("AssistantConfigSchema", () => {
|
|
|
107
107
|
toolExecutionTimeoutSec: 120,
|
|
108
108
|
providerStreamTimeoutSec: 1800,
|
|
109
109
|
});
|
|
110
|
-
expect(result.sandbox).toEqual({
|
|
111
|
-
enabled: false,
|
|
112
|
-
});
|
|
113
110
|
expect(result.rateLimit).toEqual({
|
|
114
111
|
maxRequestsPerMinute: 0,
|
|
115
112
|
});
|
|
@@ -169,7 +166,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
169
166
|
enqueueIntervalMs: 6 * 60 * 60 * 1000,
|
|
170
167
|
supersededItemRetentionMs: 30 * 24 * 60 * 60 * 1000,
|
|
171
168
|
conversationRetentionDays: 0,
|
|
172
|
-
llmRequestLogRetentionMs:
|
|
169
|
+
llmRequestLogRetentionMs: 1 * 24 * 60 * 60 * 1000,
|
|
173
170
|
});
|
|
174
171
|
});
|
|
175
172
|
|
|
@@ -400,29 +397,10 @@ describe("AssistantConfigSchema", () => {
|
|
|
400
397
|
}
|
|
401
398
|
});
|
|
402
399
|
|
|
403
|
-
test("sandbox with only enabled still parses", () => {
|
|
404
|
-
const result = AssistantConfigSchema.parse({ sandbox: { enabled: false } });
|
|
405
|
-
expect(result.sandbox.enabled).toBe(false);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
test("rejects unknown sandbox fields", () => {
|
|
409
|
-
const result = AssistantConfigSchema.safeParse({
|
|
410
|
-
sandbox: { backend: "docker" },
|
|
411
|
-
});
|
|
412
|
-
// Unknown keys are stripped by Zod passthrough/strip, so parse should still succeed
|
|
413
|
-
// but the unknown field should not appear in the output
|
|
414
|
-
if (result.success) {
|
|
415
|
-
expect(
|
|
416
|
-
(result.data.sandbox as Record<string, unknown>)["backend"],
|
|
417
|
-
).toBeUndefined();
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
|
|
421
400
|
test("defaults permissions.mode to workspace", () => {
|
|
422
401
|
const result = AssistantConfigSchema.parse({});
|
|
423
402
|
expect(result.permissions).toEqual({
|
|
424
403
|
mode: "workspace",
|
|
425
|
-
askBeforeActing: true,
|
|
426
404
|
hostAccess: false,
|
|
427
405
|
});
|
|
428
406
|
});
|
|
@@ -861,6 +839,125 @@ describe("AssistantConfigSchema", () => {
|
|
|
861
839
|
});
|
|
862
840
|
expect(result.calls.callerIdentity.allowPerCallOverride).toBe(true);
|
|
863
841
|
});
|
|
842
|
+
|
|
843
|
+
// ── hostBrowser.cdpInspect config ─────────────────────────────────
|
|
844
|
+
|
|
845
|
+
test("applies hostBrowser.cdpInspect defaults", () => {
|
|
846
|
+
const result = AssistantConfigSchema.parse({});
|
|
847
|
+
expect(result.hostBrowser).toEqual({
|
|
848
|
+
cdpInspect: {
|
|
849
|
+
enabled: false,
|
|
850
|
+
host: "localhost",
|
|
851
|
+
port: 9222,
|
|
852
|
+
probeTimeoutMs: 500,
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
test("accepts hostBrowser.cdpInspect enabled with custom host/port", () => {
|
|
858
|
+
const result = AssistantConfigSchema.parse({
|
|
859
|
+
hostBrowser: {
|
|
860
|
+
cdpInspect: {
|
|
861
|
+
enabled: true,
|
|
862
|
+
host: "127.0.0.1",
|
|
863
|
+
port: 9333,
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
});
|
|
867
|
+
expect(result.hostBrowser.cdpInspect.enabled).toBe(true);
|
|
868
|
+
expect(result.hostBrowser.cdpInspect.host).toBe("127.0.0.1");
|
|
869
|
+
expect(result.hostBrowser.cdpInspect.port).toBe(9333);
|
|
870
|
+
// Unset field should still receive its default.
|
|
871
|
+
expect(result.hostBrowser.cdpInspect.probeTimeoutMs).toBe(500);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
test("accepts hostBrowser.cdpInspect custom probeTimeoutMs", () => {
|
|
875
|
+
const result = AssistantConfigSchema.parse({
|
|
876
|
+
hostBrowser: { cdpInspect: { probeTimeoutMs: 1000 } },
|
|
877
|
+
});
|
|
878
|
+
expect(result.hostBrowser.cdpInspect.probeTimeoutMs).toBe(1000);
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
test("rejects hostBrowser.cdpInspect.port below 1", () => {
|
|
882
|
+
const result = AssistantConfigSchema.safeParse({
|
|
883
|
+
hostBrowser: { cdpInspect: { port: 0 } },
|
|
884
|
+
});
|
|
885
|
+
expect(result.success).toBe(false);
|
|
886
|
+
if (!result.success) {
|
|
887
|
+
expect(
|
|
888
|
+
result.error.issues.some((issue) =>
|
|
889
|
+
issue.path.join(".").includes("hostBrowser.cdpInspect.port"),
|
|
890
|
+
),
|
|
891
|
+
).toBe(true);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
test("rejects hostBrowser.cdpInspect.port above 65535", () => {
|
|
896
|
+
const result = AssistantConfigSchema.safeParse({
|
|
897
|
+
hostBrowser: { cdpInspect: { port: 70000 } },
|
|
898
|
+
});
|
|
899
|
+
expect(result.success).toBe(false);
|
|
900
|
+
if (!result.success) {
|
|
901
|
+
expect(
|
|
902
|
+
result.error.issues.some((issue) =>
|
|
903
|
+
issue.path.join(".").includes("hostBrowser.cdpInspect.port"),
|
|
904
|
+
),
|
|
905
|
+
).toBe(true);
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
test("rejects non-integer hostBrowser.cdpInspect.port", () => {
|
|
910
|
+
const result = AssistantConfigSchema.safeParse({
|
|
911
|
+
hostBrowser: { cdpInspect: { port: 9222.5 } },
|
|
912
|
+
});
|
|
913
|
+
expect(result.success).toBe(false);
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
test("rejects hostBrowser.cdpInspect.probeTimeoutMs below 50", () => {
|
|
917
|
+
const result = AssistantConfigSchema.safeParse({
|
|
918
|
+
hostBrowser: { cdpInspect: { probeTimeoutMs: 10 } },
|
|
919
|
+
});
|
|
920
|
+
expect(result.success).toBe(false);
|
|
921
|
+
if (!result.success) {
|
|
922
|
+
expect(
|
|
923
|
+
result.error.issues.some((issue) =>
|
|
924
|
+
issue.path
|
|
925
|
+
.join(".")
|
|
926
|
+
.includes("hostBrowser.cdpInspect.probeTimeoutMs"),
|
|
927
|
+
),
|
|
928
|
+
).toBe(true);
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
test("rejects hostBrowser.cdpInspect.probeTimeoutMs above 5000", () => {
|
|
933
|
+
const result = AssistantConfigSchema.safeParse({
|
|
934
|
+
hostBrowser: { cdpInspect: { probeTimeoutMs: 10000 } },
|
|
935
|
+
});
|
|
936
|
+
expect(result.success).toBe(false);
|
|
937
|
+
if (!result.success) {
|
|
938
|
+
expect(
|
|
939
|
+
result.error.issues.some((issue) =>
|
|
940
|
+
issue.path
|
|
941
|
+
.join(".")
|
|
942
|
+
.includes("hostBrowser.cdpInspect.probeTimeoutMs"),
|
|
943
|
+
),
|
|
944
|
+
).toBe(true);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
test("rejects non-integer hostBrowser.cdpInspect.probeTimeoutMs", () => {
|
|
949
|
+
const result = AssistantConfigSchema.safeParse({
|
|
950
|
+
hostBrowser: { cdpInspect: { probeTimeoutMs: 500.5 } },
|
|
951
|
+
});
|
|
952
|
+
expect(result.success).toBe(false);
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
test("rejects non-boolean hostBrowser.cdpInspect.enabled", () => {
|
|
956
|
+
const result = AssistantConfigSchema.safeParse({
|
|
957
|
+
hostBrowser: { cdpInspect: { enabled: "yes" } },
|
|
958
|
+
});
|
|
959
|
+
expect(result.success).toBe(false);
|
|
960
|
+
});
|
|
864
961
|
});
|
|
865
962
|
|
|
866
963
|
// ---------------------------------------------------------------------------
|
|
@@ -1002,7 +1099,7 @@ describe("loadConfig with schema validation", () => {
|
|
|
1002
1099
|
const config = loadConfig();
|
|
1003
1100
|
expect(config.services.inference.provider).toBe("anthropic");
|
|
1004
1101
|
expect(config.services.inference.model).toBe("claude-opus-4-6");
|
|
1005
|
-
expect(config.maxTokens).toBe(
|
|
1102
|
+
expect(config.maxTokens).toBe(64000);
|
|
1006
1103
|
expect(config.thinking).toEqual({
|
|
1007
1104
|
enabled: true,
|
|
1008
1105
|
streamThinking: true,
|
|
@@ -1034,7 +1131,7 @@ describe("loadConfig with schema validation", () => {
|
|
|
1034
1131
|
test("falls back to default for invalid maxTokens", () => {
|
|
1035
1132
|
writeConfig({ maxTokens: -100 });
|
|
1036
1133
|
const config = loadConfig();
|
|
1037
|
-
expect(config.maxTokens).toBe(
|
|
1134
|
+
expect(config.maxTokens).toBe(64000);
|
|
1038
1135
|
});
|
|
1039
1136
|
|
|
1040
1137
|
test("falls back to defaults for invalid nested values", () => {
|
|
@@ -1059,13 +1156,13 @@ describe("loadConfig with schema validation", () => {
|
|
|
1059
1156
|
expect(config.services.inference.provider).toBe("openai");
|
|
1060
1157
|
expect(config.services.inference.model).toBe("gpt-4");
|
|
1061
1158
|
expect(config.thinking.enabled).toBe(true);
|
|
1062
|
-
expect(config.maxTokens).toBe(
|
|
1159
|
+
expect(config.maxTokens).toBe(64000);
|
|
1063
1160
|
});
|
|
1064
1161
|
|
|
1065
1162
|
test("handles no config file", () => {
|
|
1066
1163
|
const config = loadConfig();
|
|
1067
1164
|
expect(config.services.inference.provider).toBe("anthropic");
|
|
1068
|
-
expect(config.maxTokens).toBe(
|
|
1165
|
+
expect(config.maxTokens).toBe(64000);
|
|
1069
1166
|
});
|
|
1070
1167
|
|
|
1071
1168
|
test("partial nested objects get defaults for missing fields", () => {
|
|
@@ -1084,25 +1181,6 @@ describe("loadConfig with schema validation", () => {
|
|
|
1084
1181
|
expect(config.secretDetection.action).toBe("redact");
|
|
1085
1182
|
});
|
|
1086
1183
|
|
|
1087
|
-
test("falls back for invalid sandbox.enabled", () => {
|
|
1088
|
-
writeConfig({ sandbox: { enabled: "yes" } });
|
|
1089
|
-
const config = loadConfig();
|
|
1090
|
-
expect(config.sandbox.enabled).toBe(false);
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
test("loads sandbox with only enabled field", () => {
|
|
1094
|
-
writeConfig({ sandbox: { enabled: false } });
|
|
1095
|
-
const config = loadConfig();
|
|
1096
|
-
expect(config.sandbox.enabled).toBe(false);
|
|
1097
|
-
});
|
|
1098
|
-
|
|
1099
|
-
test("strips unknown sandbox fields", () => {
|
|
1100
|
-
writeConfig({ sandbox: { enabled: true, backend: "docker" } });
|
|
1101
|
-
const config = loadConfig();
|
|
1102
|
-
expect(config.sandbox.enabled).toBe(true);
|
|
1103
|
-
expect("backend" in config.sandbox).toBe(false);
|
|
1104
|
-
});
|
|
1105
|
-
|
|
1106
1184
|
test("falls back for invalid contextWindow relationship", () => {
|
|
1107
1185
|
writeConfig({
|
|
1108
1186
|
contextWindow: { targetBudgetRatio: 0.8, compactThreshold: 0.8 },
|
|
@@ -1131,7 +1209,6 @@ describe("loadConfig with schema validation", () => {
|
|
|
1131
1209
|
const config = loadConfig();
|
|
1132
1210
|
expect(config.permissions).toEqual({
|
|
1133
1211
|
mode: "workspace",
|
|
1134
|
-
askBeforeActing: true,
|
|
1135
1212
|
hostAccess: false,
|
|
1136
1213
|
});
|
|
1137
1214
|
});
|