@vellumai/assistant 0.7.1 → 0.7.2
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 +32 -49
- package/Dockerfile +1 -0
- package/README.md +1 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
- package/bun.lock +26 -26
- package/docs/architecture/security.md +20 -0
- package/docs/plugins.md +7 -9
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
- package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
- package/node_modules/@vellumai/twilio-client/package.json +18 -0
- package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
- package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
- package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
- package/openapi.yaml +565 -12
- package/package.json +6 -3
- package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
- package/src/__tests__/app-bundler.test.ts +170 -1
- package/src/__tests__/app-control-flow.test.ts +374 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
- package/src/__tests__/app-executors.test.ts +30 -43
- package/src/__tests__/approval-routes-http.test.ts +23 -6
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
- package/src/__tests__/assistant-event-hub.test.ts +109 -2
- package/src/__tests__/assistant-event.test.ts +10 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
- package/src/__tests__/background-shell-host-bash.test.ts +14 -15
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
- package/src/__tests__/btw-routes.test.ts +13 -4
- package/src/__tests__/call-controller.test.ts +49 -1
- package/src/__tests__/call-domain.test.ts +0 -2
- package/src/__tests__/call-routes-http.test.ts +0 -2
- package/src/__tests__/channel-readiness-service.test.ts +59 -1
- package/src/__tests__/checker.test.ts +3 -4
- package/src/__tests__/config-loader-backfill.test.ts +90 -155
- package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-set-platform-guard.test.ts +48 -4
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-lifecycle.test.ts +36 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-slash-commands.test.ts +0 -4
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
- package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
- package/src/__tests__/credentials-cli.test.ts +5 -12
- package/src/__tests__/cu-unified-flow.test.ts +185 -23
- package/src/__tests__/daemon-credential-client.test.ts +101 -19
- package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
- package/src/__tests__/heartbeat-service.test.ts +718 -1
- package/src/__tests__/helpers/call-route-handler.ts +7 -1
- package/src/__tests__/host-app-control-proxy.test.ts +602 -0
- package/src/__tests__/host-app-control-routes.test.ts +263 -0
- package/src/__tests__/host-bash-proxy.test.ts +246 -47
- package/src/__tests__/host-bash-routes.test.ts +294 -0
- package/src/__tests__/host-browser-proxy.test.ts +24 -22
- package/src/__tests__/host-browser-routes.test.ts +39 -13
- package/src/__tests__/host-cu-proxy.test.ts +41 -52
- package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
- package/src/__tests__/host-file-edit-tool.test.ts +47 -1
- package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
- package/src/__tests__/host-file-proxy.test.ts +37 -43
- package/src/__tests__/host-file-read-tool.test.ts +17 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
- package/src/__tests__/host-file-write-tool.test.ts +42 -1
- package/src/__tests__/host-proxy-base.test.ts +312 -0
- package/src/__tests__/host-shell-tool.test.ts +22 -4
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
- package/src/__tests__/host-transfer-proxy.test.ts +121 -22
- package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +29 -0
- package/src/__tests__/identity-routes.test.ts +103 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
- package/src/__tests__/inline-command-runner.test.ts +0 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
- package/src/__tests__/integration-status.test.ts +85 -5
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/mcp-auth-routes.test.ts +197 -0
- package/src/__tests__/mcp-cli.test.ts +338 -2
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
- package/src/__tests__/migration-import-commit-http.test.ts +108 -2
- package/src/__tests__/mock-gateway-ipc.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +0 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
- package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +97 -0
- package/src/__tests__/require-fresh-approval.test.ts +0 -1
- package/src/__tests__/retry-backoff.test.ts +87 -0
- package/src/__tests__/runtime-events-sse.test.ts +10 -6
- package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
- package/src/__tests__/schedule-retry.test.ts +715 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-feature-flags.test.ts +43 -41
- package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
- package/src/__tests__/skill-load-inline-command.test.ts +0 -51
- package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +9 -14
- package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-config.test.ts +0 -1
- package/src/__tests__/test-preload.ts +8 -0
- package/src/__tests__/tool-approval-handler.test.ts +3 -4
- package/src/__tests__/tool-audit-listener.test.ts +48 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +3 -16
- package/src/__tests__/twilio-routes.test.ts +3 -5
- package/src/__tests__/twilio-validation.test.ts +93 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
- package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
- package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
- package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
- package/src/backup/__tests__/paths.test.ts +0 -22
- package/src/backup/__tests__/restore.test.ts +51 -151
- package/src/backup/paths.ts +2 -18
- package/src/backup/restore.ts +107 -231
- package/src/bundler/app-bundler.ts +51 -3
- package/src/calls/relay-server.ts +4 -44
- package/src/calls/twilio-config.ts +2 -17
- package/src/calls/twilio-rest.ts +33 -105
- package/src/calls/twilio-routes.ts +11 -12
- package/src/channels/types.ts +8 -7
- package/src/cli/commands/__tests__/backup.test.ts +6 -277
- package/src/cli/commands/__tests__/gateway.test.ts +288 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
- package/src/cli/commands/backup.ts +6 -331
- package/src/cli/commands/clients.ts +36 -37
- package/src/cli/commands/contacts.ts +73 -0
- package/src/cli/commands/conversations.ts +2 -5
- package/src/cli/commands/credentials.ts +15 -7
- package/src/cli/commands/domain.ts +66 -15
- package/src/cli/commands/gateway.ts +183 -0
- package/src/cli/commands/keys.ts +9 -6
- package/src/cli/commands/mcp.ts +116 -156
- package/src/cli/commands/memory-v2.ts +296 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
- package/src/cli/commands/platform/disconnect.ts +5 -4
- package/src/cli/commands/platform/index.ts +0 -18
- package/src/cli/lib/daemon-credential-client.ts +110 -28
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +67 -10
- package/src/config/bundled-skills/acp/SKILL.md +6 -0
- package/src/config/bundled-skills/acp/TOOLS.json +1 -22
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
- package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
- package/src/config/bundled-skills/app-control/SKILL.md +75 -0
- package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
- package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
- package/src/config/bundled-skills/document/TOOLS.json +0 -8
- package/src/config/bundled-skills/followups/TOOLS.json +0 -12
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
- package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
- package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
- package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
- package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
- package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
- package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
- package/src/config/bundled-skills/settings/SKILL.md +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +0 -12
- package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
- package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
- package/src/config/bundled-skills/subagent/SKILL.md +6 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
- package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
- package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
- package/src/config/bundled-tool-registry.ts +21 -0
- package/src/config/env-registry.ts +0 -2
- package/src/config/env.ts +19 -12
- package/src/config/feature-flag-registry.json +21 -133
- package/src/config/loader.ts +73 -99
- package/src/config/sanitize-for-transfer.ts +2 -0
- package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
- package/src/config/schemas/calls.ts +0 -9
- package/src/config/schemas/heartbeat.ts +63 -0
- package/src/config/schemas/ingress.ts +10 -6
- package/src/config/schemas/llm.ts +5 -10
- package/src/config/schemas/memory-lifecycle.ts +77 -24
- package/src/config/schemas/memory-v2.ts +48 -4
- package/src/config/schemas/platform.ts +6 -0
- package/src/config/schemas/services.ts +1 -15
- package/src/config/schemas/skills.ts +0 -6
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/contacts/contact-store.ts +0 -30
- package/src/contacts/contacts-write.ts +0 -27
- package/src/context/window-manager.ts +1 -2
- package/src/credential-execution/feature-gates.ts +10 -10
- package/src/credential-execution/process-manager.ts +12 -41
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
- package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
- package/src/daemon/config-watcher.ts +4 -3
- package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
- package/src/daemon/conversation-agent-loop.ts +32 -28
- package/src/daemon/conversation-lifecycle.ts +8 -1
- package/src/daemon/conversation-process.ts +16 -11
- package/src/daemon/conversation-runtime-assembly.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +125 -4
- package/src/daemon/conversation-tool-setup.ts +16 -55
- package/src/daemon/conversation.ts +21 -2
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/shared.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +293 -0
- package/src/daemon/host-bash-proxy.ts +84 -74
- package/src/daemon/host-browser-proxy.ts +67 -82
- package/src/daemon/host-cu-proxy.ts +81 -86
- package/src/daemon/host-file-proxy.ts +93 -69
- package/src/daemon/host-proxy-base.ts +294 -0
- package/src/daemon/host-proxy-preactivation.ts +82 -0
- package/src/daemon/host-transfer-proxy.ts +247 -129
- package/src/daemon/lifecycle.ts +115 -117
- package/src/daemon/message-protocol.ts +3 -8
- package/src/daemon/message-types/contacts.ts +23 -1
- package/src/daemon/message-types/conversations.ts +11 -8
- package/src/daemon/message-types/host-app-control.ts +150 -0
- package/src/daemon/message-types/host-bash.ts +4 -0
- package/src/daemon/message-types/host-cu.ts +2 -0
- package/src/daemon/message-types/host-file.ts +4 -0
- package/src/daemon/message-types/host-transfer.ts +3 -0
- package/src/daemon/message-types/schedules.ts +8 -3
- package/src/daemon/message-types/skills.ts +2 -2
- package/src/daemon/process-message.ts +18 -1
- package/src/daemon/shutdown-handlers.ts +0 -3
- package/src/daemon/tool-setup-types.ts +51 -0
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
- package/src/heartbeat/heartbeat-run-store.ts +236 -0
- package/src/heartbeat/heartbeat-service.ts +280 -49
- package/src/home/__tests__/post-connect-feed.test.ts +99 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
- package/src/home/__tests__/suggested-prompts.test.ts +89 -0
- package/src/home/post-connect-feed.ts +68 -0
- package/src/home/relationship-state-writer.ts +17 -92
- package/src/home/suggested-prompts.ts +46 -10
- package/src/inbound/public-ingress-urls.ts +32 -34
- package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
- package/src/ipc/assistant-server.ts +14 -1
- package/src/ipc/cli-client.ts +32 -1
- package/src/live-voice/live-voice-metrics.ts +10 -10
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
- package/src/mcp/mcp-auth-orchestrator.ts +213 -0
- package/src/mcp/mcp-auth-state.ts +133 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
- package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
- package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
- package/src/memory/anisotropy.test.ts +247 -0
- package/src/memory/anisotropy.ts +443 -0
- package/src/memory/auto-analysis-constants.ts +17 -0
- package/src/memory/auto-analysis-guard.ts +5 -15
- package/src/memory/canonical-guardian-store.ts +7 -7
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
- package/src/memory/context-search/agent-protocol.ts +6 -6
- package/src/memory/context-search/agent-runner.ts +32 -7
- package/src/memory/context-search/sources/memory-v2.ts +17 -5
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-key-store.ts +2 -15
- package/src/memory/db-init.ts +4 -0
- package/src/memory/embedding-backend.ts +9 -21
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
- package/src/memory/graph/conversation-graph-memory.ts +1 -24
- package/src/memory/graph/graph-search.ts +8 -0
- package/src/memory/graph/retriever.ts +28 -0
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
- package/src/memory/jobs/embed-concept-page.ts +28 -2
- package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
- package/src/memory/jobs-store.ts +66 -22
- package/src/memory/jobs-worker.ts +112 -63
- package/src/memory/memory-v2-activation-log-store.ts +1 -1
- package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
- package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/pkb/pkb-search.ts +7 -0
- package/src/memory/qdrant-client.ts +50 -20
- package/src/memory/schema/infrastructure.ts +15 -0
- package/src/memory/search/semantic.ts +7 -0
- package/src/memory/sparse-tokenize.ts +49 -0
- package/src/memory/v2/__tests__/activation.test.ts +77 -95
- package/src/memory/v2/__tests__/injection.test.ts +43 -21
- package/src/memory/v2/__tests__/sim.test.ts +166 -6
- package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
- package/src/memory/v2/__tests__/static-context.test.ts +0 -1
- package/src/memory/v2/activation.ts +69 -88
- package/src/memory/v2/consolidation-job.ts +3 -5
- package/src/memory/v2/constants.ts +7 -0
- package/src/memory/v2/injection.ts +86 -53
- package/src/memory/v2/prompts/consolidation.ts +312 -91
- package/src/memory/v2/qdrant.ts +99 -1
- package/src/memory/v2/sim.ts +126 -16
- package/src/memory/v2/skill-qdrant.ts +12 -3
- package/src/memory/v2/skill-store.ts +16 -1
- package/src/memory/v2/sparse-bm25.ts +245 -0
- package/src/memory/v2/static-context.ts +6 -5
- package/src/messaging/providers/gmail/types.ts +0 -49
- package/src/messaging/providers/slack/adapter.ts +1 -31
- package/src/messaging/providers/slack/types.ts +0 -32
- package/src/notifications/README.md +10 -10
- package/src/notifications/broadcaster.ts +1 -1
- package/src/notifications/guardian-question-mode.ts +5 -5
- package/src/oauth/connect-orchestrator.ts +4 -0
- package/src/oauth/credential-token-resolver.ts +1 -3
- package/src/oauth/manual-token-connection.ts +0 -4
- package/src/outbound-proxy/index.ts +1 -37
- package/src/outbound-proxy/logging.ts +1 -1
- package/src/outbound-proxy/policy.ts +6 -5
- package/src/outbound-proxy/router.ts +2 -1
- package/src/permissions/approval-policy.test.ts +6 -275
- package/src/permissions/approval-policy.ts +0 -51
- package/src/permissions/checker.test.ts +0 -1
- package/src/permissions/checker.ts +3 -17
- package/src/permissions/gateway-threshold-reader.ts +2 -0
- package/src/permissions/prompter.ts +34 -1
- package/src/permissions/secret-prompter.ts +6 -2
- package/src/prompts/bootstrap-cleanup.ts +27 -0
- package/src/prompts/system-prompt.ts +3 -18
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/providers/speech-to-text/provider-catalog.ts +7 -8
- package/src/runtime/assistant-event-hub.ts +118 -96
- package/src/runtime/assistant-event.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
- package/src/runtime/auth/middleware.ts +0 -96
- package/src/runtime/auth/route-policy.ts +19 -0
- package/src/runtime/btw-sidechain.ts +2 -3
- package/src/runtime/channel-invite-transport.ts +2 -48
- package/src/runtime/channel-invite-transports/email.ts +1 -1
- package/src/runtime/channel-invite-transports/slack.ts +1 -1
- package/src/runtime/channel-invite-transports/telegram.ts +1 -1
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
- package/src/runtime/channel-invite-types.ts +54 -0
- package/src/runtime/channel-readiness-service.ts +32 -13
- package/src/runtime/http-server.ts +3 -329
- package/src/runtime/http-types.ts +0 -5
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
- package/src/runtime/migrations/migration-transport.ts +7 -7
- package/src/runtime/migrations/vbundle-builder.ts +327 -60
- package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
- package/src/runtime/migrations/vbundle-importer.ts +245 -68
- package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
- package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
- package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
- package/src/runtime/migrations/vbundle-validator.ts +114 -0
- package/src/runtime/pending-interactions.ts +35 -9
- package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
- package/src/runtime/routes/approval-interception-types.ts +13 -0
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
- package/src/runtime/routes/backup-routes.ts +15 -38
- package/src/runtime/routes/btw-routes.ts +14 -37
- package/src/runtime/routes/client-routes.ts +1 -0
- package/src/runtime/routes/contact-prompt-routes.ts +183 -0
- package/src/runtime/routes/conversation-query-routes.ts +36 -1
- package/src/runtime/routes/conversation-routes.ts +30 -13
- package/src/runtime/routes/document-pdf-renderer.ts +165 -0
- package/src/runtime/routes/documents-routes.ts +30 -0
- package/src/runtime/routes/errors.ts +19 -4
- package/src/runtime/routes/events-routes.ts +12 -6
- package/src/runtime/routes/gateway-log-routes.ts +79 -0
- package/src/runtime/routes/guardian-approval-interception.ts +2 -8
- package/src/runtime/routes/heartbeat-routes.ts +103 -38
- package/src/runtime/routes/host-app-control-routes.ts +134 -0
- package/src/runtime/routes/host-bash-routes.ts +36 -6
- package/src/runtime/routes/host-browser-routes.ts +108 -13
- package/src/runtime/routes/host-cu-routes.ts +44 -14
- package/src/runtime/routes/host-file-routes.ts +33 -10
- package/src/runtime/routes/host-transfer-routes.ts +64 -24
- package/src/runtime/routes/http-adapter.ts +1 -0
- package/src/runtime/routes/identity-intro-cache.ts +30 -0
- package/src/runtime/routes/identity-routes.ts +15 -43
- package/src/runtime/routes/inbound-message-handler.ts +1 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/mcp-auth-routes.ts +132 -0
- package/src/runtime/routes/memory-item-routes.ts +10 -12
- package/src/runtime/routes/memory-v2-routes.ts +441 -1
- package/src/runtime/routes/migration-routes.ts +96 -0
- package/src/runtime/routes/schedule-routes.ts +7 -0
- package/src/runtime/verification-templates.ts +4 -7
- package/src/schedule/integration-status.ts +66 -2
- package/src/schedule/recurrence-engine.ts +4 -1
- package/src/schedule/retry-backoff.ts +18 -0
- package/src/schedule/retry-policy.ts +82 -0
- package/src/schedule/schedule-recovery.ts +64 -0
- package/src/schedule/schedule-store.ts +106 -2
- package/src/schedule/scheduler-types.ts +25 -0
- package/src/schedule/scheduler.ts +63 -38
- package/src/security/oauth-callback-registry.ts +8 -0
- package/src/sequence/analytics.ts +5 -5
- package/src/sequence/engine.ts +1 -1
- package/src/skills/catalog-files.ts +2 -8
- package/src/skills/include-graph.ts +5 -5
- package/src/skills/remote-skill-policy.ts +5 -5
- package/src/skills/skill-file-provider.ts +1 -1
- package/src/skills/skill-file-types.ts +13 -0
- package/src/skills/skillssh-audit-types.ts +28 -0
- package/src/skills/skillssh-registry.ts +8 -21
- package/src/telemetry/types.ts +2 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
- package/src/tools/apps/executors.ts +56 -69
- package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
- package/src/tools/browser/browser-execution.ts +2 -2
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
- package/src/tools/browser/cdp-client/factory.ts +23 -24
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/computer-use/definitions.ts +42 -20
- package/src/tools/executor.ts +2 -0
- package/src/tools/host-filesystem/edit.ts +26 -0
- package/src/tools/host-filesystem/read.ts +26 -0
- package/src/tools/host-filesystem/transfer.ts +31 -1
- package/src/tools/host-filesystem/write.ts +26 -0
- package/src/tools/host-terminal/host-shell.ts +58 -0
- package/src/tools/schedule/create.ts +6 -0
- package/src/tools/schedule/list.ts +2 -0
- package/src/tools/schedule/update.ts +10 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
- package/src/tools/shared/filesystem/path-policy.ts +25 -1
- package/src/tools/skills/load.ts +0 -32
- package/src/tools/tool-approval-handler.ts +1 -5
- package/src/tools/types.ts +4 -0
- package/src/usage/pricing.ts +1 -1
- package/src/workspace/hatched-date.ts +86 -0
- package/src/workspace/migrations/003-seed-device-id.ts +1 -1
- package/src/workspace/migrations/006-services-config.ts +8 -5
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
- package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
- package/src/workspace/migrations/utils.ts +21 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
- package/src/__tests__/twilio-rest.test.ts +0 -34
- package/src/backup/__tests__/backup-key.test.ts +0 -152
- package/src/backup/__tests__/backup-worker.test.ts +0 -782
- package/src/backup/__tests__/offsite-writer.test.ts +0 -641
- package/src/backup/__tests__/stream-crypt.test.ts +0 -228
- package/src/backup/backup-key.ts +0 -137
- package/src/backup/backup-worker.ts +0 -472
- package/src/backup/offsite-writer.ts +0 -222
- package/src/backup/stream-crypt.ts +0 -263
- package/src/daemon/message-types/pairing.ts +0 -58
- package/src/outbound-proxy/config.ts +0 -20
- package/src/outbound-proxy/health.ts +0 -18
- package/src/outbound-proxy/types.ts +0 -150
- package/src/runtime/capability-tokens.ts +0 -190
- package/src/signals/mcp-reload.ts +0 -18
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory MCP OAuth flow status map.
|
|
3
|
+
*
|
|
4
|
+
* Tracks the current state of daemon-owned MCP OAuth flows so the CLI can
|
|
5
|
+
* poll for completion via the IPC route.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sibling: `assistant/src/security/oauth-callback-registry.ts`. The two look
|
|
10
|
+
* similar (in-memory map, supersede semantics, ~5 min TTL) but live at
|
|
11
|
+
* different layers. The callback registry stores the deferred resolve/reject
|
|
12
|
+
* pair for a single OAuth code arrival, keyed by OAuth `state`. This file
|
|
13
|
+
* stores observable status (pending / complete / error) keyed by serverId so
|
|
14
|
+
* the polling CLI can render progress without holding a long-lived IPC
|
|
15
|
+
* connection.
|
|
16
|
+
*/
|
|
17
|
+
type McpAuthState =
|
|
18
|
+
| { status: "pending"; authUrl: string; attemptId: string; expiresAt: number }
|
|
19
|
+
| {
|
|
20
|
+
status: "complete";
|
|
21
|
+
serverId: string;
|
|
22
|
+
attemptId: string;
|
|
23
|
+
completedAt: number;
|
|
24
|
+
}
|
|
25
|
+
| { status: "error"; error: string; attemptId: string; failedAt: number };
|
|
26
|
+
|
|
27
|
+
const activeMcpAuthFlows = new Map<string, McpAuthState>();
|
|
28
|
+
|
|
29
|
+
const PENDING_TTL_MS = 5 * 60 * 1000; // 5 min — matches oauth-callback-registry.ts
|
|
30
|
+
const COMPLETION_GRACE_MS = 60 * 1000; // 60s so the polling CLI gets one final read
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Record that an OAuth flow is pending authorization.
|
|
34
|
+
* Overwrites any prior state for the same serverId (supersede semantics
|
|
35
|
+
* matching registerPendingCallback). The caller must pass an `attemptId`
|
|
36
|
+
* (a unique token per attempt) so that fire-and-forget completion writes
|
|
37
|
+
* can verify they still own the slot before mutating shared state — see
|
|
38
|
+
* `setMcpAuthComplete` / `setMcpAuthError`.
|
|
39
|
+
*/
|
|
40
|
+
export function setMcpAuthPending(
|
|
41
|
+
serverId: string,
|
|
42
|
+
authUrl: string,
|
|
43
|
+
attemptId: string,
|
|
44
|
+
): void {
|
|
45
|
+
activeMcpAuthFlows.set(serverId, {
|
|
46
|
+
status: "pending",
|
|
47
|
+
authUrl,
|
|
48
|
+
attemptId,
|
|
49
|
+
expiresAt: Date.now() + PENDING_TTL_MS,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Record that an OAuth flow completed successfully. Returns true if the
|
|
55
|
+
* write was applied; false if the attempt has been superseded by a newer
|
|
56
|
+
* one (in which case the caller's tail should silently exit without
|
|
57
|
+
* touching state).
|
|
58
|
+
*/
|
|
59
|
+
export function setMcpAuthComplete(
|
|
60
|
+
serverId: string,
|
|
61
|
+
attemptId: string,
|
|
62
|
+
): boolean {
|
|
63
|
+
const current = activeMcpAuthFlows.get(serverId);
|
|
64
|
+
if (current && current.attemptId !== attemptId) {
|
|
65
|
+
return false; // superseded
|
|
66
|
+
}
|
|
67
|
+
activeMcpAuthFlows.set(serverId, {
|
|
68
|
+
status: "complete",
|
|
69
|
+
serverId,
|
|
70
|
+
attemptId,
|
|
71
|
+
completedAt: Date.now(),
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Record that an OAuth flow failed. Returns true if the write was applied;
|
|
78
|
+
* false if the attempt has been superseded.
|
|
79
|
+
*/
|
|
80
|
+
export function setMcpAuthError(
|
|
81
|
+
serverId: string,
|
|
82
|
+
error: string,
|
|
83
|
+
attemptId: string,
|
|
84
|
+
): boolean {
|
|
85
|
+
const current = activeMcpAuthFlows.get(serverId);
|
|
86
|
+
if (current && current.attemptId !== attemptId) {
|
|
87
|
+
return false; // superseded
|
|
88
|
+
}
|
|
89
|
+
activeMcpAuthFlows.set(serverId, {
|
|
90
|
+
status: "error",
|
|
91
|
+
error,
|
|
92
|
+
attemptId,
|
|
93
|
+
failedAt: Date.now(),
|
|
94
|
+
});
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get the current state of an OAuth flow, or null if none exists.
|
|
100
|
+
*/
|
|
101
|
+
export function getMcpAuthState(serverId: string): McpAuthState | null {
|
|
102
|
+
clearExpiredMcpAuthStates();
|
|
103
|
+
return activeMcpAuthFlows.get(serverId) ?? null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Remove expired entries from the auth flow map.
|
|
108
|
+
*/
|
|
109
|
+
export function clearExpiredMcpAuthStates(): void {
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
for (const [serverId, state] of activeMcpAuthFlows) {
|
|
112
|
+
if (state.status === "pending" && now > state.expiresAt) {
|
|
113
|
+
activeMcpAuthFlows.delete(serverId);
|
|
114
|
+
} else if (
|
|
115
|
+
state.status === "complete" &&
|
|
116
|
+
now > state.completedAt + COMPLETION_GRACE_MS
|
|
117
|
+
) {
|
|
118
|
+
activeMcpAuthFlows.delete(serverId);
|
|
119
|
+
} else if (
|
|
120
|
+
state.status === "error" &&
|
|
121
|
+
now > state.failedAt + COMPLETION_GRACE_MS
|
|
122
|
+
) {
|
|
123
|
+
activeMcpAuthFlows.delete(serverId);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Test-only helper — clears all auth flow state for test isolation.
|
|
130
|
+
*/
|
|
131
|
+
export function _clearAllMcpAuthStates(): void {
|
|
132
|
+
activeMcpAuthFlows.clear();
|
|
133
|
+
}
|
|
@@ -61,6 +61,16 @@ export interface McpOAuthCallbackResult {
|
|
|
61
61
|
/** Which callback transport to use for receiving the OAuth redirect. */
|
|
62
62
|
export type McpOAuthCallbackTransport = "loopback" | "gateway";
|
|
63
63
|
|
|
64
|
+
export interface McpOAuthProviderOptions {
|
|
65
|
+
/**
|
|
66
|
+
* If provided, called with the authorization URL during
|
|
67
|
+
* redirectToAuthorization() instead of opening a browser.
|
|
68
|
+
* Used by the daemon-side orchestrator so it can return the
|
|
69
|
+
* URL to the IPC caller (CLI / web client).
|
|
70
|
+
*/
|
|
71
|
+
onAuthorizationUrl?: (url: string) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
export class McpOAuthProvider implements OAuthClientProvider {
|
|
65
75
|
private readonly serverId: string;
|
|
66
76
|
private readonly serverUrl: string;
|
|
@@ -75,6 +85,7 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
75
85
|
/** Deferred resolver/rejector for the gateway code promise. */
|
|
76
86
|
private _gatewayCodeResolve: ((code: string) => void) | undefined;
|
|
77
87
|
private _gatewayCodeReject: ((err: Error) => void) | undefined;
|
|
88
|
+
private readonly _onAuthorizationUrl: ((url: string) => void) | undefined;
|
|
78
89
|
|
|
79
90
|
/**
|
|
80
91
|
* @param interactive When true (e.g. `mcp auth` CLI), opens browser for OAuth.
|
|
@@ -82,17 +93,20 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
82
93
|
* @param callbackTransport Which transport to use for the OAuth redirect.
|
|
83
94
|
* - `"loopback"` (default): localhost HTTP server — for desktop clients.
|
|
84
95
|
* - `"gateway"`: platform ingress + callback registry — for Docker/platform.
|
|
96
|
+
* @param options Additional options for the provider.
|
|
85
97
|
*/
|
|
86
98
|
constructor(
|
|
87
99
|
serverId: string,
|
|
88
100
|
serverUrl: string,
|
|
89
101
|
interactive = false,
|
|
90
102
|
callbackTransport: McpOAuthCallbackTransport = "loopback",
|
|
103
|
+
options: McpOAuthProviderOptions = {},
|
|
91
104
|
) {
|
|
92
105
|
this.serverId = serverId;
|
|
93
106
|
this.serverUrl = serverUrl;
|
|
94
107
|
this.interactive = interactive;
|
|
95
108
|
this.callbackTransport = callbackTransport;
|
|
109
|
+
this._onAuthorizationUrl = options.onAuthorizationUrl;
|
|
96
110
|
}
|
|
97
111
|
|
|
98
112
|
// --- redirectUrl ---
|
|
@@ -269,6 +283,11 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
269
283
|
}
|
|
270
284
|
}
|
|
271
285
|
|
|
286
|
+
if (this._onAuthorizationUrl) {
|
|
287
|
+
this._onAuthorizationUrl(url);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
272
291
|
if (!this.interactive) {
|
|
273
292
|
// Daemon mode — don't open browser, just log guidance
|
|
274
293
|
log.info(
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { EMBED_JOB_TYPES, SLOW_LLM_JOB_TYPES } from "../jobs-store.js";
|
|
4
|
+
|
|
5
|
+
describe("memory job classes", () => {
|
|
6
|
+
test("EMBED_JOB_TYPES and SLOW_LLM_JOB_TYPES are disjoint", () => {
|
|
7
|
+
const embedSet = new Set<string>(EMBED_JOB_TYPES);
|
|
8
|
+
const overlap = SLOW_LLM_JOB_TYPES.filter((t) => embedSet.has(t));
|
|
9
|
+
expect(overlap).toEqual([]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("SLOW_LLM_JOB_TYPES entries are non-empty strings", () => {
|
|
13
|
+
expect(SLOW_LLM_JOB_TYPES.length).toBeGreaterThan(0);
|
|
14
|
+
for (const t of SLOW_LLM_JOB_TYPES) {
|
|
15
|
+
expect(typeof t).toBe("string");
|
|
16
|
+
expect(t.length).toBeGreaterThan(0);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("SLOW_LLM_JOB_TYPES has no duplicate entries", () => {
|
|
21
|
+
const set = new Set(SLOW_LLM_JOB_TYPES);
|
|
22
|
+
expect(set.size).toBe(SLOW_LLM_JOB_TYPES.length);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { stripLegacySparseSuffix } from "../qdrant-client.js";
|
|
4
|
+
|
|
5
|
+
describe("stripLegacySparseSuffix", () => {
|
|
6
|
+
test("strips a trailing :sparse-v<digit> suffix", () => {
|
|
7
|
+
expect(
|
|
8
|
+
stripLegacySparseSuffix("gemini:gemini-embedding-2-preview:sparse-v3"),
|
|
9
|
+
).toBe("gemini:gemini-embedding-2-preview");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("strips multi-digit version suffixes", () => {
|
|
13
|
+
expect(stripLegacySparseSuffix("openai:text-embed-3:sparse-v42")).toBe(
|
|
14
|
+
"openai:text-embed-3",
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("returns the input unchanged when no suffix is present", () => {
|
|
19
|
+
expect(stripLegacySparseSuffix("gemini:gemini-embedding-2-preview")).toBe(
|
|
20
|
+
"gemini:gemini-embedding-2-preview",
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("does not strip a non-trailing :sparse-v segment", () => {
|
|
25
|
+
expect(stripLegacySparseSuffix("foo:sparse-v3:bar")).toBe(
|
|
26
|
+
"foo:sparse-v3:bar",
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("does not strip when the version part is missing", () => {
|
|
31
|
+
expect(stripLegacySparseSuffix("provider:model:sparse-v")).toBe(
|
|
32
|
+
"provider:model:sparse-v",
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("normalizes legacy and current sentinels to the same value", () => {
|
|
37
|
+
const legacy = "gemini:gemini-embedding-2-preview:sparse-v2";
|
|
38
|
+
const current = "gemini:gemini-embedding-2-preview";
|
|
39
|
+
expect(stripLegacySparseSuffix(legacy)).toBe(
|
|
40
|
+
stripLegacySparseSuffix(current),
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("differentiates sentinels that diverge on the dense identity", () => {
|
|
45
|
+
const a = "gemini:gemini-embedding-2-preview:sparse-v2";
|
|
46
|
+
const b = "openai:text-embedding-3-small:sparse-v2";
|
|
47
|
+
expect(stripLegacySparseSuffix(a)).not.toBe(stripLegacySparseSuffix(b));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { tokenize, tokenizeStemmed } from "../sparse-tokenize.js";
|
|
4
|
+
|
|
5
|
+
describe("tokenize", () => {
|
|
6
|
+
test("lowercases and splits on non-alphanumeric runs", () => {
|
|
7
|
+
expect(tokenize("Hello, World!")).toEqual(["hello", "world"]);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("preserves alphanumeric runs as-is (no stemming)", () => {
|
|
11
|
+
expect(tokenize("running supplements taking")).toEqual([
|
|
12
|
+
"running",
|
|
13
|
+
"supplements",
|
|
14
|
+
"taking",
|
|
15
|
+
]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("handles Unicode letters and digits", () => {
|
|
19
|
+
expect(tokenize("café-99 naïve")).toEqual(["café", "99", "naïve"]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("returns empty array for empty / whitespace input", () => {
|
|
23
|
+
expect(tokenize("")).toEqual([]);
|
|
24
|
+
expect(tokenize(" \n\t ")).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("tokenizeStemmed", () => {
|
|
29
|
+
test("collapses plurals to their singular stem", () => {
|
|
30
|
+
expect(tokenizeStemmed("supplements")).toEqual(["supplement"]);
|
|
31
|
+
expect(tokenizeStemmed("tests")).toEqual(["test"]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("collapses verb tenses and gerunds to a shared stem", () => {
|
|
35
|
+
expect(tokenizeStemmed("taking")).toEqual(["take"]);
|
|
36
|
+
expect(tokenizeStemmed("running")).toEqual(["run"]);
|
|
37
|
+
expect(tokenizeStemmed("testing")).toEqual(["test"]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("singular forms map to themselves (idempotent on stems)", () => {
|
|
41
|
+
expect(tokenizeStemmed("supplement")).toEqual(["supplement"]);
|
|
42
|
+
expect(tokenizeStemmed("run")).toEqual(["run"]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("query and document forms collapse to identical buckets", () => {
|
|
46
|
+
// Plural query terms must land on the same stem as singular doc terms
|
|
47
|
+
// (and vice versa). This is the property the BM25 sparse channel
|
|
48
|
+
// relies on so doc-side weights and query-side occurrences match.
|
|
49
|
+
const queryTokens = tokenizeStemmed("filing reports");
|
|
50
|
+
const docTokens = tokenizeStemmed("filed a report");
|
|
51
|
+
expect(queryTokens).toContain("file");
|
|
52
|
+
expect(queryTokens).toContain("report");
|
|
53
|
+
expect(docTokens).toContain("file");
|
|
54
|
+
expect(docTokens).toContain("report");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("returns empty array for empty input", () => {
|
|
58
|
+
expect(tokenizeStemmed("")).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("preserves token order and length (one stem per token)", () => {
|
|
62
|
+
const original = tokenize("the quick brown foxes were jumping");
|
|
63
|
+
const stemmed = tokenizeStemmed("the quick brown foxes were jumping");
|
|
64
|
+
expect(stemmed.length).toBe(original.length);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type AnisotropyCalibration,
|
|
5
|
+
applyAnisotropyCorrection,
|
|
6
|
+
explainedVarianceRatio,
|
|
7
|
+
fitAnisotropyCalibration,
|
|
8
|
+
} from "./anisotropy.js";
|
|
9
|
+
|
|
10
|
+
const META = { provider: "gemini" as const, model: "test-model" };
|
|
11
|
+
|
|
12
|
+
function dot(a: readonly number[], b: readonly number[]): number {
|
|
13
|
+
let s = 0;
|
|
14
|
+
for (let i = 0; i < a.length; i++) s += a[i] * b[i];
|
|
15
|
+
return s;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function l2Norm(v: readonly number[]): number {
|
|
19
|
+
return Math.sqrt(dot(v, v));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function l2Normalize(v: readonly number[]): number[] {
|
|
23
|
+
const n = l2Norm(v);
|
|
24
|
+
if (n === 0) return [...v];
|
|
25
|
+
return v.map((x) => x / n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build a synthetic anisotropic corpus: every sample lives close to a fixed
|
|
30
|
+
* direction `axis`, with small Gaussian-ish noise injected in the orthogonal
|
|
31
|
+
* subspace. This gives us a known top-1 PC the fit must recover.
|
|
32
|
+
*/
|
|
33
|
+
function buildAnisotropicCorpus(
|
|
34
|
+
n: number,
|
|
35
|
+
dim: number,
|
|
36
|
+
axis: readonly number[],
|
|
37
|
+
noiseScale: number,
|
|
38
|
+
): number[][] {
|
|
39
|
+
const ax = l2Normalize(axis);
|
|
40
|
+
const out: number[][] = [];
|
|
41
|
+
let seed = 42;
|
|
42
|
+
const rand = () => {
|
|
43
|
+
// Tiny deterministic LCG so tests don't depend on Math.random.
|
|
44
|
+
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
|
|
45
|
+
return seed / 0x7fffffff;
|
|
46
|
+
};
|
|
47
|
+
for (let i = 0; i < n; i++) {
|
|
48
|
+
const v = new Array<number>(dim);
|
|
49
|
+
// Strong common direction component, slightly varied per sample.
|
|
50
|
+
const along = 1 + (rand() - 0.5) * 0.1;
|
|
51
|
+
for (let j = 0; j < dim; j++) v[j] = along * ax[j];
|
|
52
|
+
// Orthogonal noise.
|
|
53
|
+
for (let j = 0; j < dim; j++) v[j] += (rand() - 0.5) * noiseScale;
|
|
54
|
+
out.push(v);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe("fitAnisotropyCalibration", () => {
|
|
60
|
+
test("recovers the dominant direction of an anisotropic corpus", () => {
|
|
61
|
+
const dim = 16;
|
|
62
|
+
const axis = new Array<number>(dim)
|
|
63
|
+
.fill(0)
|
|
64
|
+
.map((_, i) => (i === 0 ? 1 : 0));
|
|
65
|
+
const corpus = buildAnisotropicCorpus(200, dim, axis, 0.05);
|
|
66
|
+
|
|
67
|
+
const calib = fitAnisotropyCalibration(corpus, 1, META);
|
|
68
|
+
|
|
69
|
+
expect(calib.components.length).toBe(1);
|
|
70
|
+
expect(calib.components[0].length).toBe(dim);
|
|
71
|
+
// PC1 should align with the planted axis (sign-agnostic).
|
|
72
|
+
const alignment = Math.abs(dot(calib.components[0], axis));
|
|
73
|
+
expect(alignment).toBeGreaterThan(0.95);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("captures most variance in PC1 for a strongly anisotropic corpus", () => {
|
|
77
|
+
const dim = 16;
|
|
78
|
+
const axis = new Array<number>(dim)
|
|
79
|
+
.fill(0)
|
|
80
|
+
.map((_, i) => (i === 0 ? 1 : 0));
|
|
81
|
+
const corpus = buildAnisotropicCorpus(200, dim, axis, 0.02);
|
|
82
|
+
|
|
83
|
+
const calib = fitAnisotropyCalibration(corpus, 3, META);
|
|
84
|
+
const ratios = explainedVarianceRatio(calib);
|
|
85
|
+
|
|
86
|
+
// Spectrum is monotonically non-increasing.
|
|
87
|
+
expect(ratios[0]).toBeGreaterThanOrEqual(ratios[1]);
|
|
88
|
+
expect(ratios[1]).toBeGreaterThanOrEqual(ratios[2]);
|
|
89
|
+
// PC1 should dwarf PC2 by an order of magnitude when noise is small.
|
|
90
|
+
expect(ratios[0]).toBeGreaterThan(ratios[1] * 10);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("returns mean and dim metadata", () => {
|
|
94
|
+
const dim = 8;
|
|
95
|
+
const corpus = [
|
|
96
|
+
[1, 1, 0, 0, 0, 0, 0, 0],
|
|
97
|
+
[3, 3, 0, 0, 0, 0, 0, 0],
|
|
98
|
+
];
|
|
99
|
+
const calib = fitAnisotropyCalibration(corpus, 1, META);
|
|
100
|
+
expect(calib.dim).toBe(dim);
|
|
101
|
+
expect(calib.mean[0]).toBeCloseTo(2);
|
|
102
|
+
expect(calib.mean[1]).toBeCloseTo(2);
|
|
103
|
+
expect(calib.mean[2]).toBeCloseTo(0);
|
|
104
|
+
expect(calib.sampleCount).toBe(2);
|
|
105
|
+
expect(calib.provider).toBe("gemini");
|
|
106
|
+
expect(calib.model).toBe("test-model");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("rejects empty input, bad k, and ragged rows", () => {
|
|
110
|
+
expect(() => fitAnisotropyCalibration([], 1, META)).toThrow(/no vectors/);
|
|
111
|
+
expect(() => fitAnisotropyCalibration([[1, 2, 3]], 0, META)).toThrow(
|
|
112
|
+
/positive integer/,
|
|
113
|
+
);
|
|
114
|
+
expect(() =>
|
|
115
|
+
fitAnisotropyCalibration(
|
|
116
|
+
[
|
|
117
|
+
[1, 2],
|
|
118
|
+
[3, 4, 5],
|
|
119
|
+
],
|
|
120
|
+
1,
|
|
121
|
+
META,
|
|
122
|
+
),
|
|
123
|
+
).toThrow(/dim/);
|
|
124
|
+
expect(() => fitAnisotropyCalibration([[1, 2]], 5, META)).toThrow(
|
|
125
|
+
/exceeds/,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("applyAnisotropyCorrection", () => {
|
|
131
|
+
test("output has unit L2 norm", () => {
|
|
132
|
+
const corpus = buildAnisotropicCorpus(
|
|
133
|
+
100,
|
|
134
|
+
8,
|
|
135
|
+
[1, 0, 0, 0, 0, 0, 0, 0],
|
|
136
|
+
0.05,
|
|
137
|
+
);
|
|
138
|
+
const calib = fitAnisotropyCalibration(corpus, 1, META);
|
|
139
|
+
|
|
140
|
+
const corrected = applyAnisotropyCorrection(corpus[0], calib);
|
|
141
|
+
expect(l2Norm(corrected)).toBeCloseTo(1, 6);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("removes projection onto the dominant direction", () => {
|
|
145
|
+
const dim = 8;
|
|
146
|
+
const axis = [1, 0, 0, 0, 0, 0, 0, 0];
|
|
147
|
+
const corpus = buildAnisotropicCorpus(200, dim, axis, 0.02);
|
|
148
|
+
const calib = fitAnisotropyCalibration(corpus, 1, META);
|
|
149
|
+
|
|
150
|
+
// Pick a vector that points strongly along the planted axis.
|
|
151
|
+
const sample = [...axis];
|
|
152
|
+
const corrected = applyAnisotropyCorrection(sample, calib);
|
|
153
|
+
|
|
154
|
+
// After removing PC1 (which is ~axis), the projection back onto axis
|
|
155
|
+
// should be nearly zero — the sample is in the deflated subspace.
|
|
156
|
+
expect(Math.abs(dot(corrected, axis))).toBeLessThan(0.05);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("spreads cosine similarities for vectors in the cone", () => {
|
|
160
|
+
const dim = 16;
|
|
161
|
+
const axis = new Array<number>(dim)
|
|
162
|
+
.fill(0)
|
|
163
|
+
.map((_, i) => (i === 0 ? 1 : 0));
|
|
164
|
+
const corpus = buildAnisotropicCorpus(300, dim, axis, 0.1);
|
|
165
|
+
const calib = fitAnisotropyCalibration(corpus, 1, META);
|
|
166
|
+
|
|
167
|
+
// Compute pairwise cosines on raw vs corrected vectors. The corrected
|
|
168
|
+
// distribution should have noticeably larger spread (max - min) — the
|
|
169
|
+
// whole point of the correction.
|
|
170
|
+
function spread(vectors: number[][]): number {
|
|
171
|
+
const sims: number[] = [];
|
|
172
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
173
|
+
const a = l2Normalize(vectors[i]);
|
|
174
|
+
for (let j = i + 1; j < vectors.length; j++) {
|
|
175
|
+
const b = l2Normalize(vectors[j]);
|
|
176
|
+
sims.push(dot(a, b));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
let min = Infinity;
|
|
180
|
+
let max = -Infinity;
|
|
181
|
+
for (const s of sims) {
|
|
182
|
+
if (s < min) min = s;
|
|
183
|
+
if (s > max) max = s;
|
|
184
|
+
}
|
|
185
|
+
return max - min;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const rawSpread = spread(corpus.slice(0, 30));
|
|
189
|
+
const correctedSpread = spread(
|
|
190
|
+
corpus.slice(0, 30).map((v) => applyAnisotropyCorrection(v, calib)),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
expect(correctedSpread).toBeGreaterThan(rawSpread * 2);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("rejects mismatched dim", () => {
|
|
197
|
+
const calib: AnisotropyCalibration = {
|
|
198
|
+
provider: "gemini",
|
|
199
|
+
model: "x",
|
|
200
|
+
dim: 4,
|
|
201
|
+
mean: [0, 0, 0, 0],
|
|
202
|
+
components: [[1, 0, 0, 0]],
|
|
203
|
+
componentVariance: [1],
|
|
204
|
+
totalVariance: 1,
|
|
205
|
+
sampleCount: 1,
|
|
206
|
+
fitAt: 0,
|
|
207
|
+
};
|
|
208
|
+
expect(() => applyAnisotropyCorrection([1, 2, 3], calib)).toThrow(/dim/);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe("explainedVarianceRatio", () => {
|
|
213
|
+
test("returns zeros when totalVariance is zero", () => {
|
|
214
|
+
const calib: AnisotropyCalibration = {
|
|
215
|
+
provider: "gemini",
|
|
216
|
+
model: "x",
|
|
217
|
+
dim: 2,
|
|
218
|
+
mean: [0, 0],
|
|
219
|
+
components: [[1, 0]],
|
|
220
|
+
componentVariance: [0],
|
|
221
|
+
totalVariance: 0,
|
|
222
|
+
sampleCount: 1,
|
|
223
|
+
fitAt: 0,
|
|
224
|
+
};
|
|
225
|
+
expect(explainedVarianceRatio(calib)).toEqual([0]);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("each ratio is variance/total", () => {
|
|
229
|
+
const calib: AnisotropyCalibration = {
|
|
230
|
+
provider: "gemini",
|
|
231
|
+
model: "x",
|
|
232
|
+
dim: 2,
|
|
233
|
+
mean: [0, 0],
|
|
234
|
+
components: [
|
|
235
|
+
[1, 0],
|
|
236
|
+
[0, 1],
|
|
237
|
+
],
|
|
238
|
+
componentVariance: [3, 1],
|
|
239
|
+
totalVariance: 4,
|
|
240
|
+
sampleCount: 100,
|
|
241
|
+
fitAt: 0,
|
|
242
|
+
};
|
|
243
|
+
const ratios = explainedVarianceRatio(calib);
|
|
244
|
+
expect(ratios[0]).toBeCloseTo(0.75);
|
|
245
|
+
expect(ratios[1]).toBeCloseTo(0.25);
|
|
246
|
+
});
|
|
247
|
+
});
|