@vellumai/assistant 0.6.2 → 0.6.4
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/ARCHITECTURE.md +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +41 -49
- package/bunfig.toml +3 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +1111 -86
- package/package.json +40 -42
- package/scripts/generate-openapi.ts +0 -2
- package/scripts/test.sh +73 -18
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- 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__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +240 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +1000 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -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 +1248 -224
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-overflow-approval.test.ts +16 -1
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
- package/src/__tests__/conversation-agent-loop.test.ts +99 -3
- 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 +290 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +12 -4
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +946 -62
- package/src/__tests__/conversation-routes-disk-view.test.ts +275 -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 +324 -46
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- 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-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +6 -3
- package/src/__tests__/credential-vault-unit.test.ts +383 -7
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +42 -18
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +2 -0
- package/src/__tests__/gemini-provider.test.ts +66 -2
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +738 -359
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +528 -49
- package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
- package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +145 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -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 +423 -0
- package/src/__tests__/host-cu-proxy.test.ts +166 -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__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +67 -8
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +109 -7
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/oauth-apps-routes.test.ts +18 -12
- package/src/__tests__/oauth-cli.test.ts +709 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +52 -14
- package/src/__tests__/oauth-store.test.ts +1465 -176
- package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
- package/src/__tests__/onboarding-template-contract.test.ts +81 -70
- package/src/__tests__/openai-provider.test.ts +178 -2
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- 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 +32 -3
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- 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__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +8 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +576 -16
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- 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 +184 -27
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +25 -5
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -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__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +55 -9
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- 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/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +69 -3
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +3 -4
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +273 -16
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -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 +32 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -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/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +215 -36
- package/src/cli/commands/oauth/shared.ts +7 -6
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -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 +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -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/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +5 -2
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +54 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +9 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -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/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +38 -0
- package/src/config/env.ts +49 -4
- package/src/config/feature-flag-registry.json +85 -14
- package/src/config/loader.ts +82 -13
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +81 -15
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +112 -0
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +52 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -1
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +177 -0
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +61 -10
- package/src/credential-execution/approval-bridge.ts +49 -15
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/context-overflow-approval.ts +5 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
- package/src/daemon/conversation-agent-loop.ts +153 -42
- package/src/daemon/conversation-attachments.ts +40 -0
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +622 -13
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +128 -36
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +131 -40
- package/src/daemon/conversation-tool-setup.ts +99 -8
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +292 -16
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +13 -141
- package/src/daemon/handlers/shared.ts +80 -0
- package/src/daemon/handlers/skills.ts +483 -44
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +192 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +179 -28
- package/src/daemon/message-protocol.ts +13 -0
- package/src/daemon/message-types/conversations.ts +89 -14
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +19 -5
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +26 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +439 -14
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/transport-hints.ts +5 -24
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +30 -20
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/mcp/client.ts +59 -24
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +31 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +122 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +18 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -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/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +67 -40
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +137 -60
- 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/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/oauth.ts +21 -13
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +26 -9
- package/src/oauth/byo-connection.ts +10 -8
- package/src/oauth/connect-orchestrator.ts +25 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +22 -18
- package/src/oauth/connection.ts +3 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +223 -100
- package/src/oauth/platform-connection.test.ts +101 -3
- package/src/oauth/platform-connection.ts +56 -35
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +133 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +76 -38
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -105
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -60
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +10 -1
- package/src/runtime/AGENTS.md +65 -0
- package/src/runtime/__tests__/agent-wake.test.ts +831 -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/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -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 +48 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +33 -9
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +368 -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 +815 -75
- package/src/runtime/http-types.ts +6 -2
- 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 +198 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +7 -0
- package/src/runtime/migrations/migration-wizard.ts +23 -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 +96 -1
- package/src/runtime/migrations/vbundle-importer.ts +89 -5
- package/src/runtime/pending-interactions.ts +18 -13
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
- package/src/runtime/routes/conversation-management-routes.ts +223 -0
- package/src/runtime/routes/conversation-routes.ts +598 -103
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +268 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +262 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/log-export-routes.ts +42 -22
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +122 -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 +31 -102
- package/src/runtime/routes/skills-routes.ts +128 -9
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +38 -9
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- 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/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +57 -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 +68 -29
- package/src/security/secure-keys.ts +143 -27
- package/src/security/token-manager.ts +31 -10
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +554 -0
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +169 -40
- 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/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +1787 -342
- package/src/tools/browser/browser-manager.ts +81 -12
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- 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 +1263 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -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 +1007 -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 +744 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
- package/src/tools/browser/cdp-client/errors.ts +49 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
- package/src/tools/browser/cdp-client/factory.ts +914 -0
- package/src/tools/browser/cdp-client/index.ts +28 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +120 -0
- package/src/tools/credentials/vault.ts +35 -6
- 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/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- 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/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- 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 +15 -0
- package/src/tools/terminal/shell.ts +36 -20
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +19 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +63 -24
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +31 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- 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/cli/commands/browser-relay.ts +0 -536
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the `_action: "launch_conversation"` dispatch branch in
|
|
3
|
+
* `handleSurfaceAction` AND the `launchConversation` helper it calls into.
|
|
4
|
+
*
|
|
5
|
+
* The real `launchConversation` is exercised end-to-end: its DB-hitting
|
|
6
|
+
* dependencies (`conversation-key-store`, `conversation-crud`) and its
|
|
7
|
+
* registered daemon deps are stubbed so the helper runs without a DB.
|
|
8
|
+
* This lets the tests assert the full invariant set in one place:
|
|
9
|
+
*
|
|
10
|
+
* - `handleSurfaceAction` does NOT publish `open_conversation` itself —
|
|
11
|
+
* `launchConversation` is the sole emitter of that event.
|
|
12
|
+
* - Exactly one `open_conversation` is published per launch, carrying
|
|
13
|
+
* the caller-supplied `focus` value (false for fan-out launchers).
|
|
14
|
+
* - The handler returns promptly — the seed turn
|
|
15
|
+
* (`persistAndProcessMessage`) is fire-and-forget.
|
|
16
|
+
* - `originTrustContext` is forwarded to the spawned conversation.
|
|
17
|
+
*
|
|
18
|
+
* These tests guard the single-emitter invariant: exactly one
|
|
19
|
+
* `open_conversation` event is published per launch, with the
|
|
20
|
+
* caller-supplied `focus` value preserved so fan-out launchers do not
|
|
21
|
+
* steal focus from the origin.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
25
|
+
|
|
26
|
+
// ── Module-level mocks ─────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
// Hub publish capture — used by the single-emit assertions. We spread
|
|
29
|
+
// the real module into each override so unrelated exports (e.g.
|
|
30
|
+
// `formatSseFrame` on assistant-event) stay accessible to other
|
|
31
|
+
// importers loaded indirectly through `conversation-surfaces.ts`.
|
|
32
|
+
const publishCalls: Array<unknown> = [];
|
|
33
|
+
const realHub = await import("../../runtime/assistant-event-hub.js");
|
|
34
|
+
const realEvent = await import("../../runtime/assistant-event.js");
|
|
35
|
+
mock.module("../../runtime/assistant-event-hub.js", () => ({
|
|
36
|
+
...realHub,
|
|
37
|
+
assistantEventHub: {
|
|
38
|
+
publish: async (event: unknown) => {
|
|
39
|
+
publishCalls.push(event);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
43
|
+
mock.module("../../runtime/assistant-event.js", () => ({
|
|
44
|
+
...realEvent,
|
|
45
|
+
// Pass-through so `focus` / `conversationId` can be asserted directly
|
|
46
|
+
// on the captured event's `message` payload.
|
|
47
|
+
buildAssistantEvent: (
|
|
48
|
+
assistantId: string,
|
|
49
|
+
message: unknown,
|
|
50
|
+
conversationId?: string,
|
|
51
|
+
) => ({ assistantId, message, conversationId }),
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
// Stub DB helpers so the real `launchConversation` can run without a DB.
|
|
55
|
+
// We spread the real module and override only the specific functions the
|
|
56
|
+
// helper uses — other importers (e.g. conversation-surfaces itself)
|
|
57
|
+
// continue to see `getMessages`, `updateMessageContent`, etc.
|
|
58
|
+
let nextKeyStoreResult: { conversationId: string } = {
|
|
59
|
+
conversationId: "conv-new",
|
|
60
|
+
};
|
|
61
|
+
const updateTitleCalls: Array<{ conversationId: string; title: string }> = [];
|
|
62
|
+
const realKeyStore = await import("../../memory/conversation-key-store.js");
|
|
63
|
+
const realCrud = await import("../../memory/conversation-crud.js");
|
|
64
|
+
mock.module("../../memory/conversation-key-store.js", () => ({
|
|
65
|
+
...realKeyStore,
|
|
66
|
+
getOrCreateConversation: (_key: string) => nextKeyStoreResult,
|
|
67
|
+
}));
|
|
68
|
+
mock.module("../../memory/conversation-crud.js", () => ({
|
|
69
|
+
...realCrud,
|
|
70
|
+
updateConversationTitle: (
|
|
71
|
+
conversationId: string,
|
|
72
|
+
title: string,
|
|
73
|
+
_priority: number,
|
|
74
|
+
) => {
|
|
75
|
+
updateTitleCalls.push({ conversationId, title });
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
// Dynamic imports after mock.module calls so the stubs take effect
|
|
80
|
+
// before the modules under test are loaded.
|
|
81
|
+
const { createSurfaceMutex, handleSurfaceAction } =
|
|
82
|
+
await import("../conversation-surfaces.js");
|
|
83
|
+
const { registerLaunchConversationDeps, resetLaunchConversationDeps } =
|
|
84
|
+
await import("../conversation-launch.js");
|
|
85
|
+
type SurfaceConversationContext =
|
|
86
|
+
import("../conversation-surfaces.js").SurfaceConversationContext;
|
|
87
|
+
type TrustContext = import("../conversation-runtime-assembly.js").TrustContext;
|
|
88
|
+
type ServerMessage = import("../message-protocol.js").ServerMessage;
|
|
89
|
+
type SurfaceData = import("../message-protocol.js").SurfaceData;
|
|
90
|
+
type SurfaceType = import("../message-protocol.js").SurfaceType;
|
|
91
|
+
|
|
92
|
+
// ── launchConversation deps harness ────────────────────────────────
|
|
93
|
+
|
|
94
|
+
interface DepsHarness {
|
|
95
|
+
getOrCreateCalls: Array<string>;
|
|
96
|
+
processCalls: Array<{ conversationId: string; content: string }>;
|
|
97
|
+
lastTrustContext(): unknown | null;
|
|
98
|
+
resolveProcess: () => void;
|
|
99
|
+
rejectProcess: (err: Error) => void;
|
|
100
|
+
/** Resolves once `persistAndProcessMessage` has actually been invoked. */
|
|
101
|
+
processStarted: Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function setupLaunchDeps(): DepsHarness {
|
|
105
|
+
const getOrCreateCalls: Array<string> = [];
|
|
106
|
+
const processCalls: Array<{ conversationId: string; content: string }> = [];
|
|
107
|
+
let trustContextOnConversation: unknown | null = null;
|
|
108
|
+
let resolveProcess = () => {};
|
|
109
|
+
let rejectProcess: (err: Error) => void = () => {};
|
|
110
|
+
let markProcessStarted = () => {};
|
|
111
|
+
const processStarted = new Promise<void>((resolve) => {
|
|
112
|
+
markProcessStarted = resolve;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const fakeConversation = {
|
|
116
|
+
setTrustContext: (c: unknown) => {
|
|
117
|
+
trustContextOnConversation = c;
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
registerLaunchConversationDeps({
|
|
122
|
+
getOrCreateConversation: async (id: string) => {
|
|
123
|
+
getOrCreateCalls.push(id);
|
|
124
|
+
return fakeConversation as never;
|
|
125
|
+
},
|
|
126
|
+
persistAndProcessMessage: (conversationId: string, content: string) => {
|
|
127
|
+
processCalls.push({ conversationId, content });
|
|
128
|
+
markProcessStarted();
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
resolveProcess = () => resolve({ messageId: "msg-1" });
|
|
131
|
+
rejectProcess = (err) => reject(err);
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
publishAssistantEvent: () => {},
|
|
135
|
+
getAssistantId: () => "assistant-test-id",
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
getOrCreateCalls,
|
|
140
|
+
processCalls,
|
|
141
|
+
lastTrustContext: () => trustContextOnConversation,
|
|
142
|
+
resolveProcess: () => resolveProcess(),
|
|
143
|
+
rejectProcess: (err: Error) => rejectProcess(err),
|
|
144
|
+
processStarted,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ── Surface-context harness ────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
interface HarnessContext extends SurfaceConversationContext {
|
|
151
|
+
sent: ServerMessage[];
|
|
152
|
+
enqueueCalls: Array<{ content: string }>;
|
|
153
|
+
processCalls: Array<{ content: string }>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function makeContext(
|
|
157
|
+
overrides?: Partial<SurfaceConversationContext>,
|
|
158
|
+
): HarnessContext {
|
|
159
|
+
const sent: ServerMessage[] = [];
|
|
160
|
+
const enqueueCalls: Array<{ content: string }> = [];
|
|
161
|
+
const processCalls: Array<{ content: string }> = [];
|
|
162
|
+
|
|
163
|
+
const base: SurfaceConversationContext = {
|
|
164
|
+
conversationId: "origin-conv-id",
|
|
165
|
+
traceEmitter: { emit: () => {} },
|
|
166
|
+
sendToClient: (msg) => sent.push(msg),
|
|
167
|
+
pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
|
|
168
|
+
lastSurfaceAction: new Map<
|
|
169
|
+
string,
|
|
170
|
+
{ actionId: string; data?: Record<string, unknown> }
|
|
171
|
+
>(),
|
|
172
|
+
surfaceState: new Map<
|
|
173
|
+
string,
|
|
174
|
+
{ surfaceType: SurfaceType; data: SurfaceData; title?: string }
|
|
175
|
+
>(),
|
|
176
|
+
surfaceUndoStacks: new Map<string, string[]>(),
|
|
177
|
+
accumulatedSurfaceState: new Map<string, Record<string, unknown>>(),
|
|
178
|
+
surfaceActionRequestIds: new Set<string>(),
|
|
179
|
+
currentTurnSurfaces: [],
|
|
180
|
+
isProcessing: () => false,
|
|
181
|
+
enqueueMessage: (content: string) => {
|
|
182
|
+
enqueueCalls.push({ content });
|
|
183
|
+
return { queued: false, requestId: "enq-req" };
|
|
184
|
+
},
|
|
185
|
+
getQueueDepth: () => 0,
|
|
186
|
+
processMessage: async (content: string) => {
|
|
187
|
+
processCalls.push({ content });
|
|
188
|
+
return "ok";
|
|
189
|
+
},
|
|
190
|
+
withSurface: createSurfaceMutex(),
|
|
191
|
+
...overrides,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
return Object.assign(base, {
|
|
195
|
+
sent,
|
|
196
|
+
enqueueCalls,
|
|
197
|
+
processCalls,
|
|
198
|
+
}) as HarnessContext;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Register a surface on `ctx`. Launcher cards arrive as history-restored
|
|
203
|
+
* surfaces (no `pendingSurfaceActions` entry) — matching how the card
|
|
204
|
+
* actually reaches `handleSurfaceAction` after reconstruction.
|
|
205
|
+
*/
|
|
206
|
+
function registerCardSurface(
|
|
207
|
+
ctx: SurfaceConversationContext,
|
|
208
|
+
surfaceId: string,
|
|
209
|
+
): void {
|
|
210
|
+
ctx.surfaceState.set(surfaceId, {
|
|
211
|
+
surfaceType: "card",
|
|
212
|
+
data: { title: "Launch" } as unknown as SurfaceData,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Helper: filter captured publish calls down to `open_conversation`
|
|
217
|
+
// events. Typed so assertions can reach the inner `message` payload.
|
|
218
|
+
function openConversationEvents(): Array<{
|
|
219
|
+
assistantId: string;
|
|
220
|
+
conversationId?: string;
|
|
221
|
+
message: {
|
|
222
|
+
type: "open_conversation";
|
|
223
|
+
conversationId: string;
|
|
224
|
+
title?: string;
|
|
225
|
+
anchorMessageId?: string;
|
|
226
|
+
focus?: boolean;
|
|
227
|
+
};
|
|
228
|
+
}> {
|
|
229
|
+
return publishCalls
|
|
230
|
+
.filter((e): e is { message: { type: "open_conversation" } } => {
|
|
231
|
+
const ev = e as { message?: { type?: string } };
|
|
232
|
+
return ev.message?.type === "open_conversation";
|
|
233
|
+
})
|
|
234
|
+
.map(
|
|
235
|
+
(e) =>
|
|
236
|
+
e as unknown as {
|
|
237
|
+
assistantId: string;
|
|
238
|
+
conversationId?: string;
|
|
239
|
+
message: {
|
|
240
|
+
type: "open_conversation";
|
|
241
|
+
conversationId: string;
|
|
242
|
+
title?: string;
|
|
243
|
+
anchorMessageId?: string;
|
|
244
|
+
focus?: boolean;
|
|
245
|
+
};
|
|
246
|
+
},
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Tests ──────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
describe("handleSurfaceAction — launch_conversation dispatch", () => {
|
|
253
|
+
beforeEach(() => {
|
|
254
|
+
publishCalls.length = 0;
|
|
255
|
+
updateTitleCalls.length = 0;
|
|
256
|
+
nextKeyStoreResult = { conversationId: "conv-new" };
|
|
257
|
+
// Reset module-level `_deps` so a test that forgets to call
|
|
258
|
+
// `setupLaunchDeps()` cannot accidentally piggy-back on deps left
|
|
259
|
+
// registered by a previous test. Each test that exercises the launch
|
|
260
|
+
// helper must call `setupLaunchDeps()` explicitly.
|
|
261
|
+
resetLaunchConversationDeps();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("launches new conversation with inherited trust context and no chat message", async () => {
|
|
265
|
+
nextKeyStoreResult = { conversationId: "conv-launched-1" };
|
|
266
|
+
const harness = setupLaunchDeps();
|
|
267
|
+
const originTrustContext: TrustContext = {
|
|
268
|
+
sourceChannel: "vellum",
|
|
269
|
+
trustClass: "guardian",
|
|
270
|
+
guardianChatId: "chat-guardian",
|
|
271
|
+
guardianPrincipalId: "principal-guardian",
|
|
272
|
+
};
|
|
273
|
+
const ctx = makeContext({ trustContext: originTrustContext });
|
|
274
|
+
registerCardSurface(ctx, "surface-1");
|
|
275
|
+
|
|
276
|
+
const result = await handleSurfaceAction(ctx, "surface-1", "launch", {
|
|
277
|
+
_action: "launch_conversation",
|
|
278
|
+
title: "New Thread",
|
|
279
|
+
seedPrompt: "S",
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// 1. Response shape.
|
|
283
|
+
expect(result).toEqual({
|
|
284
|
+
accepted: true,
|
|
285
|
+
conversationId: "conv-launched-1",
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// 2. Exactly ONE `open_conversation` event was published for the new
|
|
289
|
+
// id, with focus: false. `launchConversation` is the sole emitter;
|
|
290
|
+
// `handleSurfaceAction` delegates entirely to it.
|
|
291
|
+
const openEvents = openConversationEvents();
|
|
292
|
+
expect(openEvents).toHaveLength(1);
|
|
293
|
+
expect(openEvents[0].message.conversationId).toBe("conv-launched-1");
|
|
294
|
+
expect(openEvents[0].message.focus).toBe(false);
|
|
295
|
+
expect(openEvents[0].message.title).toBe("New Thread");
|
|
296
|
+
|
|
297
|
+
// 3. The spawned conversation inherited the origin's trust context.
|
|
298
|
+
expect(harness.lastTrustContext()).toEqual(originTrustContext);
|
|
299
|
+
|
|
300
|
+
// 4. Seed turn was kicked off fire-and-forget — resolve it to clean
|
|
301
|
+
// up the pending promise the harness stubbed.
|
|
302
|
+
await harness.processStarted;
|
|
303
|
+
expect(harness.processCalls).toHaveLength(1);
|
|
304
|
+
expect(harness.processCalls[0].content).toBe("S");
|
|
305
|
+
harness.resolveProcess();
|
|
306
|
+
|
|
307
|
+
// 5. No chat message side effect on the origin conversation — neither
|
|
308
|
+
// the LLM pipeline nor the `[User action on app: ...]` text echo.
|
|
309
|
+
expect(ctx.enqueueCalls).toHaveLength(0);
|
|
310
|
+
expect(ctx.processCalls).toHaveLength(0);
|
|
311
|
+
const anyUserActionEcho = ctx.sent.some(
|
|
312
|
+
(msg) =>
|
|
313
|
+
"text" in msg &&
|
|
314
|
+
typeof msg.text === "string" &&
|
|
315
|
+
msg.text.includes("[User action on app:"),
|
|
316
|
+
);
|
|
317
|
+
expect(anyUserActionEcho).toBe(false);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("returns error when title or seedPrompt is missing", async () => {
|
|
321
|
+
const ctx = makeContext();
|
|
322
|
+
registerCardSurface(ctx, "surface-2");
|
|
323
|
+
|
|
324
|
+
// Missing seedPrompt.
|
|
325
|
+
const missingSeed = await handleSurfaceAction(ctx, "surface-2", "launch", {
|
|
326
|
+
_action: "launch_conversation",
|
|
327
|
+
title: "T",
|
|
328
|
+
});
|
|
329
|
+
expect(missingSeed).toEqual({
|
|
330
|
+
accepted: false,
|
|
331
|
+
error: "missing_title_or_seedPrompt",
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Missing title.
|
|
335
|
+
const missingTitle = await handleSurfaceAction(ctx, "surface-2", "launch", {
|
|
336
|
+
_action: "launch_conversation",
|
|
337
|
+
seedPrompt: "S",
|
|
338
|
+
});
|
|
339
|
+
expect(missingTitle).toEqual({
|
|
340
|
+
accepted: false,
|
|
341
|
+
error: "missing_title_or_seedPrompt",
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Neither field: still the same validation error.
|
|
345
|
+
const missingBoth = await handleSurfaceAction(ctx, "surface-2", "launch", {
|
|
346
|
+
_action: "launch_conversation",
|
|
347
|
+
});
|
|
348
|
+
expect(missingBoth).toEqual({
|
|
349
|
+
accepted: false,
|
|
350
|
+
error: "missing_title_or_seedPrompt",
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// No launch-side effects in any of the failed validations — no events,
|
|
354
|
+
// no queued origin-conversation messages.
|
|
355
|
+
expect(publishCalls).toHaveLength(0);
|
|
356
|
+
expect(ctx.enqueueCalls).toHaveLength(0);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("omits originTrustContext when origin conversation has none", async () => {
|
|
360
|
+
nextKeyStoreResult = { conversationId: "conv-launched-3" };
|
|
361
|
+
const harness = setupLaunchDeps();
|
|
362
|
+
// No `trustContext` on the origin context — simulating the
|
|
363
|
+
// no-inherited-guardian path.
|
|
364
|
+
const ctx = makeContext();
|
|
365
|
+
registerCardSurface(ctx, "surface-3");
|
|
366
|
+
|
|
367
|
+
const result = await handleSurfaceAction(ctx, "surface-3", "launch", {
|
|
368
|
+
_action: "launch_conversation",
|
|
369
|
+
title: "T",
|
|
370
|
+
seedPrompt: "S",
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(result).toEqual({
|
|
374
|
+
accepted: true,
|
|
375
|
+
conversationId: "conv-launched-3",
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Trust context was never applied to the spawned conversation.
|
|
379
|
+
expect(harness.lastTrustContext()).toBeNull();
|
|
380
|
+
|
|
381
|
+
// Still exactly one open_conversation event with focus: false.
|
|
382
|
+
const openEvents = openConversationEvents();
|
|
383
|
+
expect(openEvents).toHaveLength(1);
|
|
384
|
+
expect(openEvents[0].message.focus).toBe(false);
|
|
385
|
+
|
|
386
|
+
await harness.processStarted;
|
|
387
|
+
harness.resolveProcess();
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("handler returns before the seed turn resolves (fire-and-forget)", async () => {
|
|
391
|
+
nextKeyStoreResult = { conversationId: "conv-nonblocking" };
|
|
392
|
+
const harness = setupLaunchDeps();
|
|
393
|
+
const ctx = makeContext();
|
|
394
|
+
registerCardSurface(ctx, "surface-4");
|
|
395
|
+
|
|
396
|
+
// The harness's `persistAndProcessMessage` returns a pending Promise
|
|
397
|
+
// that only resolves when we call `resolveProcess()`. If the helper
|
|
398
|
+
// (or handler) awaited it, `await handleSurfaceAction(...)` below
|
|
399
|
+
// would hang. The fact that it resolves while the seed turn is still
|
|
400
|
+
// pending proves the fire-and-forget behavior that the HTTP route
|
|
401
|
+
// relies on for the fan-out multi-launch UX.
|
|
402
|
+
const result = await handleSurfaceAction(ctx, "surface-4", "launch", {
|
|
403
|
+
_action: "launch_conversation",
|
|
404
|
+
title: "T",
|
|
405
|
+
seedPrompt: "S",
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
expect(result).toEqual({
|
|
409
|
+
accepted: true,
|
|
410
|
+
conversationId: "conv-nonblocking",
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Seed turn is in-flight but not yet resolved. Prove the helper
|
|
414
|
+
// actually invoked it (so we know fire-and-forget is wired), then
|
|
415
|
+
// resolve it to clean up.
|
|
416
|
+
await harness.processStarted;
|
|
417
|
+
expect(harness.processCalls).toHaveLength(1);
|
|
418
|
+
harness.resolveProcess();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
test("seed turn rejection is swallowed by the helper's .catch()", async () => {
|
|
422
|
+
nextKeyStoreResult = { conversationId: "conv-seed-fails" };
|
|
423
|
+
const harness = setupLaunchDeps();
|
|
424
|
+
const ctx = makeContext();
|
|
425
|
+
registerCardSurface(ctx, "surface-5");
|
|
426
|
+
|
|
427
|
+
const result = await handleSurfaceAction(ctx, "surface-5", "launch", {
|
|
428
|
+
_action: "launch_conversation",
|
|
429
|
+
title: "T",
|
|
430
|
+
seedPrompt: "boom",
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
expect(result).toEqual({
|
|
434
|
+
accepted: true,
|
|
435
|
+
conversationId: "conv-seed-fails",
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// Reject the pending seed turn — the helper's `.catch()` handler
|
|
439
|
+
// must swallow it. If it didn't, Bun would surface the unhandled
|
|
440
|
+
// rejection at test-end and this test would fail.
|
|
441
|
+
await harness.processStarted;
|
|
442
|
+
harness.rejectProcess(new Error("seed-turn-failed"));
|
|
443
|
+
// Give the microtask queue a tick so the `.catch()` runs before
|
|
444
|
+
// the test completes.
|
|
445
|
+
await Promise.resolve();
|
|
446
|
+
await Promise.resolve();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("dispatches launch even when pendingSurfaceActions has an entry for the surface (first-click case)", async () => {
|
|
450
|
+
// Regression for the gap that left the launch branch unreachable on the
|
|
451
|
+
// FIRST click of a freshly-rendered persistent launcher card. `ui_show`
|
|
452
|
+
// unconditionally sets `pendingSurfaceActions` for any interactive card
|
|
453
|
+
// (regardless of `persistent`), so without this fix `handleSurfaceAction`
|
|
454
|
+
// saw `pending` truthy, skipped the launch dispatch, and fell through to
|
|
455
|
+
// the pending path — emitting the `[User action on card surface: ...]`
|
|
456
|
+
// message and triggering a full LLM round-trip on every click. The plan
|
|
457
|
+
// claimed to eliminate that round-trip; this test enforces it.
|
|
458
|
+
nextKeyStoreResult = { conversationId: "conv-pending-set" };
|
|
459
|
+
const harness = setupLaunchDeps();
|
|
460
|
+
const ctx = makeContext();
|
|
461
|
+
registerCardSurface(ctx, "surface-pending");
|
|
462
|
+
// Simulate `ui_show` having stamped a pending entry for this surface
|
|
463
|
+
// (which it does for any interactive card, including persistent ones).
|
|
464
|
+
ctx.pendingSurfaceActions.set("surface-pending", { surfaceType: "card" });
|
|
465
|
+
|
|
466
|
+
const result = await handleSurfaceAction(ctx, "surface-pending", "launch", {
|
|
467
|
+
_action: "launch_conversation",
|
|
468
|
+
title: "T",
|
|
469
|
+
seedPrompt: "S",
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
expect(result).toEqual({
|
|
473
|
+
accepted: true,
|
|
474
|
+
conversationId: "conv-pending-set",
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// Exactly one open_conversation event with focus: false — the launch
|
|
478
|
+
// branch ran, not the pending fallthrough.
|
|
479
|
+
const openEvents = openConversationEvents();
|
|
480
|
+
expect(openEvents).toHaveLength(1);
|
|
481
|
+
expect(openEvents[0].message.conversationId).toBe("conv-pending-set");
|
|
482
|
+
expect(openEvents[0].message.focus).toBe(false);
|
|
483
|
+
|
|
484
|
+
// Critical: NO message was enqueued onto the origin conversation. If the
|
|
485
|
+
// launch dispatch had fallen through to the pending path, the
|
|
486
|
+
// `[User action on card surface: ...]` text would have been enqueued and
|
|
487
|
+
// an LLM turn would have started.
|
|
488
|
+
expect(ctx.enqueueCalls).toHaveLength(0);
|
|
489
|
+
|
|
490
|
+
// Pending entry was deleted so subsequent sibling clicks on the same
|
|
491
|
+
// persistent card aren't blocked behind a stale "owes-an-answer" flag.
|
|
492
|
+
expect(ctx.pendingSurfaceActions.has("surface-pending")).toBe(false);
|
|
493
|
+
|
|
494
|
+
await harness.processStarted;
|
|
495
|
+
harness.resolveProcess();
|
|
496
|
+
});
|
|
497
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `isToolActiveForContext` host-tool capability gating.
|
|
3
|
+
*
|
|
4
|
+
* Scenarios verified:
|
|
5
|
+
* - chrome-extension is its own executor and is exempt from the hasNoClient
|
|
6
|
+
* gate (the extension's own popup UI gates commands; there is no SSE
|
|
7
|
+
* interactive approval channel, and chrome-extension turns intentionally
|
|
8
|
+
* run with `hasNoClient: true` because chrome-extension is not in
|
|
9
|
+
* `INTERACTIVE_INTERFACES`).
|
|
10
|
+
* - macos requires a connected SSE client for host tools that flow through
|
|
11
|
+
* the proxy (e.g. host_bash, host_file_*), so `hasNoClient: true` denies
|
|
12
|
+
* those on macos.
|
|
13
|
+
* - host_browser is NOT in the macos capability set because the proxy path
|
|
14
|
+
* requires a Chrome extension that isn't guaranteed to be attached; macos
|
|
15
|
+
* browser tools fall back to local Playwright Chromium.
|
|
16
|
+
*
|
|
17
|
+
* The per-capability check (`supportsHostProxy(transport, capability)`) runs
|
|
18
|
+
* first and is authoritative for structural support, so host_bash and
|
|
19
|
+
* host_file_* are filtered out for chrome-extension regardless of the
|
|
20
|
+
* hasNoClient flag, and host_browser is filtered out for macos.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { describe, expect, test } from "bun:test";
|
|
24
|
+
|
|
25
|
+
import type { SkillProjectionCache } from "../conversation-skill-tools.js";
|
|
26
|
+
import {
|
|
27
|
+
HOST_TOOL_NAMES,
|
|
28
|
+
HOST_TOOL_TO_CAPABILITY,
|
|
29
|
+
isToolActiveForContext,
|
|
30
|
+
type SkillProjectionContext,
|
|
31
|
+
} from "../conversation-tool-setup.js";
|
|
32
|
+
|
|
33
|
+
function makeCtx(
|
|
34
|
+
overrides: Partial<SkillProjectionContext> = {},
|
|
35
|
+
): SkillProjectionContext {
|
|
36
|
+
return {
|
|
37
|
+
skillProjectionState: new Map(),
|
|
38
|
+
skillProjectionCache: {} as SkillProjectionCache,
|
|
39
|
+
coreToolNames: new Set<string>(),
|
|
40
|
+
toolsDisabledDepth: 0,
|
|
41
|
+
...overrides,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("isToolActiveForContext — host tool capability gating", () => {
|
|
46
|
+
// macOS transport: SSE-based interactive approval required.
|
|
47
|
+
test("host_bash is active for macOS with a connected client", () => {
|
|
48
|
+
expect(
|
|
49
|
+
isToolActiveForContext(
|
|
50
|
+
"host_bash",
|
|
51
|
+
makeCtx({ hasNoClient: false, transportInterface: "macos" }),
|
|
52
|
+
),
|
|
53
|
+
).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("host_bash is NOT active for macOS when hasNoClient is true (security invariant)", () => {
|
|
57
|
+
// macOS uses an SSE-based interactive approval channel. Without a
|
|
58
|
+
// connected client the guardian auto-approve path could execute host
|
|
59
|
+
// commands unattended, so host tools must be denied.
|
|
60
|
+
expect(
|
|
61
|
+
isToolActiveForContext(
|
|
62
|
+
"host_bash",
|
|
63
|
+
makeCtx({ hasNoClient: true, transportInterface: "macos" }),
|
|
64
|
+
),
|
|
65
|
+
).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("host_file_read is NOT active for macOS when hasNoClient is true", () => {
|
|
69
|
+
expect(
|
|
70
|
+
isToolActiveForContext(
|
|
71
|
+
"host_file_read",
|
|
72
|
+
makeCtx({ hasNoClient: true, transportInterface: "macos" }),
|
|
73
|
+
),
|
|
74
|
+
).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("host_browser is NOT active for macOS (uses local Playwright)", () => {
|
|
78
|
+
// host_browser is not in the macos capability set because the proxy path
|
|
79
|
+
// requires a Chrome extension that isn't guaranteed to be attached; macos
|
|
80
|
+
// browser tools fall back to local Playwright Chromium instead. The
|
|
81
|
+
// per-capability check is authoritative, so host_browser is filtered out
|
|
82
|
+
// for macos regardless of client connection state.
|
|
83
|
+
expect(
|
|
84
|
+
isToolActiveForContext(
|
|
85
|
+
"host_browser",
|
|
86
|
+
makeCtx({ hasNoClient: false, transportInterface: "macos" }),
|
|
87
|
+
),
|
|
88
|
+
).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("host_browser is NOT active for macOS when hasNoClient is true", () => {
|
|
92
|
+
// Same capability gate as above: host_browser is unsupported on macos
|
|
93
|
+
// regardless of connection state.
|
|
94
|
+
expect(
|
|
95
|
+
isToolActiveForContext(
|
|
96
|
+
"host_browser",
|
|
97
|
+
makeCtx({ hasNoClient: true, transportInterface: "macos" }),
|
|
98
|
+
),
|
|
99
|
+
).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// chrome-extension transport: the extension is its own executor.
|
|
103
|
+
test("host_browser is active for chrome-extension even when hasNoClient is true", () => {
|
|
104
|
+
// chrome-extension turns run with `hasNoClient: true` by design because
|
|
105
|
+
// chrome-extension is not in `INTERACTIVE_INTERFACES` — it is not an
|
|
106
|
+
// SSE interactive channel. The extension gates host_browser commands
|
|
107
|
+
// via its own popup UI, so the hasNoClient gate must not filter
|
|
108
|
+
// host_browser out for chrome-extension transports.
|
|
109
|
+
expect(
|
|
110
|
+
isToolActiveForContext(
|
|
111
|
+
"host_browser",
|
|
112
|
+
makeCtx({
|
|
113
|
+
hasNoClient: true,
|
|
114
|
+
transportInterface: "chrome-extension",
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("host_browser is active for chrome-extension when hasNoClient is false", () => {
|
|
121
|
+
expect(
|
|
122
|
+
isToolActiveForContext(
|
|
123
|
+
"host_browser",
|
|
124
|
+
makeCtx({
|
|
125
|
+
hasNoClient: false,
|
|
126
|
+
transportInterface: "chrome-extension",
|
|
127
|
+
}),
|
|
128
|
+
),
|
|
129
|
+
).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("host_bash is NOT active for chrome-extension even when hasNoClient is true", () => {
|
|
133
|
+
// The per-capability check runs first and is authoritative: chrome-extension
|
|
134
|
+
// only supports `host_browser`, so `host_bash` must be filtered out.
|
|
135
|
+
expect(
|
|
136
|
+
isToolActiveForContext(
|
|
137
|
+
"host_bash",
|
|
138
|
+
makeCtx({
|
|
139
|
+
hasNoClient: true,
|
|
140
|
+
transportInterface: "chrome-extension",
|
|
141
|
+
}),
|
|
142
|
+
),
|
|
143
|
+
).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("host_file_read is NOT active for chrome-extension when hasNoClient is true", () => {
|
|
147
|
+
expect(
|
|
148
|
+
isToolActiveForContext(
|
|
149
|
+
"host_file_read",
|
|
150
|
+
makeCtx({
|
|
151
|
+
hasNoClient: true,
|
|
152
|
+
transportInterface: "chrome-extension",
|
|
153
|
+
}),
|
|
154
|
+
),
|
|
155
|
+
).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Backwards-compat fallback: no transport plumbed through.
|
|
159
|
+
test("host_bash falls back to hasNoClient gate when transport is undefined (client connected)", () => {
|
|
160
|
+
// Without a transport interface we cannot run the per-capability check,
|
|
161
|
+
// so we fall back to the coarse-grained `hasNoClient` behavior.
|
|
162
|
+
expect(
|
|
163
|
+
isToolActiveForContext(
|
|
164
|
+
"host_bash",
|
|
165
|
+
makeCtx({ hasNoClient: false, transportInterface: undefined }),
|
|
166
|
+
),
|
|
167
|
+
).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("host_bash falls back to hasNoClient gate when transport is undefined (no client)", () => {
|
|
171
|
+
expect(
|
|
172
|
+
isToolActiveForContext(
|
|
173
|
+
"host_bash",
|
|
174
|
+
makeCtx({ hasNoClient: true, transportInterface: undefined }),
|
|
175
|
+
),
|
|
176
|
+
).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("HOST_TOOL_NAMES derivation", () => {
|
|
181
|
+
test("HOST_TOOL_NAMES is derived from HOST_TOOL_TO_CAPABILITY", () => {
|
|
182
|
+
// Sanity check: every tool in the names set has a capability mapping.
|
|
183
|
+
// This is structurally enforced by the code (HOST_TOOL_NAMES is built
|
|
184
|
+
// from HOST_TOOL_TO_CAPABILITY.keys()), but we test it to make the
|
|
185
|
+
// invariant visible to readers and to catch any regression that
|
|
186
|
+
// splits the two collections back apart.
|
|
187
|
+
for (const name of HOST_TOOL_NAMES) {
|
|
188
|
+
expect(HOST_TOOL_TO_CAPABILITY.has(name)).toBe(true);
|
|
189
|
+
}
|
|
190
|
+
// Cardinality check: the two collections must have the same size so a
|
|
191
|
+
// future addition to HOST_TOOL_NAMES without a matching capability entry
|
|
192
|
+
// (or vice versa) would fail.
|
|
193
|
+
expect(HOST_TOOL_NAMES.size).toBe(HOST_TOOL_TO_CAPABILITY.size);
|
|
194
|
+
});
|
|
195
|
+
});
|