@vellumai/assistant 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +48 -50
- 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/memory.md +5 -2
- 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 +52 -5
- 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/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- 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 +1020 -40
- 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 +384 -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 +157 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -7
- package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
- package/src/__tests__/background-shell-host-bash.test.ts +14 -15
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- 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-conversation-messages.test.ts +8 -2
- package/src/__tests__/call-domain.test.ts +0 -2
- package/src/__tests__/call-routes-http.test.ts +0 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +62 -2
- package/src/__tests__/checker.test.ts +3 -4
- package/src/__tests__/config-loader-backfill.test.ts +461 -147
- 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-schema.test.ts +1 -0
- package/src/__tests__/config-set-platform-guard.test.ts +48 -4
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +20 -11
- package/src/__tests__/config-watcher.test.ts +142 -71
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +3 -7
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop.test.ts +454 -5
- 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-error.test.ts +150 -3
- 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-process-callsite.test.ts +43 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -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-runtime-assembly.test.ts +65 -0
- package/src/__tests__/conversation-slash-commands.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +328 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -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 +12 -12
- package/src/__tests__/cu-unified-flow.test.ts +351 -23
- package/src/__tests__/daemon-credential-client.test.ts +101 -19
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +3 -4
- 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-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +968 -2
- package/src/__tests__/helpers/call-route-handler.ts +7 -1
- package/src/__tests__/host-app-control-proxy.test.ts +772 -0
- package/src/__tests__/host-app-control-routes.test.ts +263 -0
- package/src/__tests__/host-bash-proxy.test.ts +439 -47
- package/src/__tests__/host-bash-routes.test.ts +459 -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 +248 -52
- package/src/__tests__/host-cu-routes-targeted.test.ts +429 -0
- package/src/__tests__/host-file-edit-tool.test.ts +47 -1
- package/src/__tests__/host-file-proxy-targeted.test.ts +378 -0
- package/src/__tests__/host-file-proxy.test.ts +301 -45
- package/src/__tests__/host-file-read-tool.test.ts +17 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +420 -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 +932 -0
- package/src/__tests__/host-transfer-proxy.test.ts +121 -22
- package/src/__tests__/host-transfer-routes-targeted.test.ts +662 -0
- package/src/__tests__/http-user-message-parity.test.ts +108 -1
- 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__/injector-chain.test.ts +18 -6
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- 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-profile-guard.test.ts +18 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/mcp-auth-routes.test.ts +197 -0
- package/src/__tests__/mcp-cli.test.ts +338 -2
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/migration-import-commit-http.test.ts +108 -2
- package/src/__tests__/mock-gateway-ipc.ts +1 -0
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/oauth-cli.test.ts +0 -2
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -25
- 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__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/public-ingress-urls.test.ts +97 -0
- package/src/__tests__/relay-server.test.ts +15 -4
- 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 +2 -2
- package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
- package/src/__tests__/schedule-retry.test.ts +715 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- 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__/suggestion-routes.test.ts +46 -0
- 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-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- 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-safe-storage-limits-release.test.ts +90 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
- package/src/approvals/guardian-decision-primitive.ts +13 -0
- package/src/approvals/guardian-request-resolvers.ts +16 -17
- 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/backup/snapshot-lock.ts +2 -27
- package/src/bundler/app-bundler.ts +51 -3
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-conversation-messages.ts +46 -10
- 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 -5
- package/src/cli/commands/backup.ts +6 -331
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/clients.ts +36 -37
- package/src/cli/commands/contacts.ts +137 -25
- package/src/cli/commands/conversations.ts +2 -5
- package/src/cli/commands/credentials.ts +71 -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 +303 -7
- package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
- package/src/cli/commands/oauth/connect.ts +127 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -4
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -3
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -3
- package/src/cli/commands/platform/__tests__/status.test.ts +116 -21
- package/src/cli/commands/platform/disconnect.ts +5 -4
- package/src/cli/commands/platform/index.ts +16 -25
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/lib/daemon-credential-client.ts +110 -28
- package/src/cli/program.ts +6 -2
- package/src/config/assistant-feature-flags.ts +79 -12
- 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/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +25 -4
- package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- 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 -20
- package/src/config/feature-flag-registry.json +47 -135
- package/src/config/loader.ts +197 -104
- 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 +17 -9
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/calls.ts +0 -9
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +64 -1
- package/src/config/schemas/ingress.ts +10 -6
- package/src/config/schemas/llm.ts +7 -10
- package/src/config/schemas/memory-lifecycle.ts +90 -24
- package/src/config/schemas/memory-v2.ts +121 -13
- package/src/config/schemas/platform.ts +49 -3
- package/src/config/schemas/services.ts +29 -15
- package/src/config/schemas/skills.ts +0 -6
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -55
- 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 +187 -5
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
- package/src/daemon/config-watcher.ts +89 -60
- package/src/daemon/conversation-agent-loop-handlers.ts +27 -3
- package/src/daemon/conversation-agent-loop.ts +202 -61
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +9 -4
- package/src/daemon/conversation-process.ts +24 -11
- package/src/daemon/conversation-runtime-assembly.ts +28 -2
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +305 -4
- package/src/daemon/conversation-tool-setup.ts +66 -62
- package/src/daemon/conversation.ts +38 -24
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/shared.ts +4 -2
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +389 -0
- package/src/daemon/host-bash-proxy.ts +117 -82
- package/src/daemon/host-browser-proxy.ts +67 -82
- package/src/daemon/host-cu-proxy.ts +127 -86
- package/src/daemon/host-file-proxy.ts +129 -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 +338 -129
- package/src/daemon/lifecycle.ts +194 -145
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +14 -4
- package/src/daemon/message-protocol.ts +6 -8
- package/src/daemon/message-types/contacts.ts +23 -1
- package/src/daemon/message-types/conversations.ts +15 -8
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- 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/messages.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/profiler-run-store.ts +5 -5
- 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/documents/document-store.ts +85 -0
- package/src/events/tool-audit-listener.ts +2 -1
- package/src/filing/filing-service.ts +30 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +24 -23
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +252 -0
- package/src/heartbeat/heartbeat-run-store.ts +249 -0
- package/src/heartbeat/heartbeat-service.ts +459 -54
- 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/feed-scheduler.ts +18 -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/platform-callback-registration.ts +8 -15
- package/src/inbound/public-ingress-urls.ts +32 -34
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
- package/src/ipc/assistant-server.ts +70 -3
- package/src/ipc/cli-client.ts +32 -1
- package/src/ipc/gateway-client.ts +37 -3
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/live-voice-metrics.ts +10 -10
- package/src/live-voice/protocol.ts +5 -7
- 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/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -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/admin.ts +5 -9
- 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 +51 -9
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +22 -9
- package/src/memory/context-search/sources/memory.ts +0 -1
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +5 -13
- package/src/memory/conversation-key-store.ts +2 -15
- package/src/memory/db-init.ts +6 -0
- package/src/memory/embedding-backend.ts +9 -21
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +81 -25
- package/src/memory/graph/conversation-graph-memory.ts +43 -78
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +10 -67
- package/src/memory/graph/graph-search.ts +9 -20
- package/src/memory/graph/retriever.test.ts +6 -0
- package/src/memory/graph/retriever.ts +34 -10
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/indexer.ts +54 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- 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 +114 -22
- package/src/memory/jobs-worker.ts +193 -106
- package/src/memory/memory-v2-activation-log-store.ts +33 -15
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- 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/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/pkb/pkb-search.test.ts +6 -0
- package/src/memory/pkb/pkb-search.ts +7 -0
- package/src/memory/qdrant-client.ts +49 -32
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/schema/infrastructure.ts +15 -0
- package/src/memory/search/semantic.ts +13 -67
- package/src/memory/sparse-tokenize.ts +49 -0
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +387 -344
- package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
- package/src/memory/v2/__tests__/injection.test.ts +181 -169
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +154 -188
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
- package/src/memory/v2/__tests__/static-context.test.ts +76 -2
- package/src/memory/v2/activation.ts +213 -239
- package/src/memory/v2/consolidation-job.ts +65 -17
- package/src/memory/v2/constants.ts +7 -0
- package/src/memory/v2/injection.ts +123 -103
- package/src/memory/v2/prompts/consolidation.ts +348 -92
- package/src/memory/v2/qdrant.ts +198 -1
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +113 -77
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +91 -53
- package/src/memory/v2/sparse-bm25.ts +245 -0
- package/src/memory/v2/static-context.ts +28 -5
- package/src/memory/v2/types.ts +10 -10
- 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/copy-composer.ts +13 -0
- package/src/notifications/guardian-question-mode.ts +5 -5
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +6 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/credential-token-resolver.ts +1 -3
- package/src/oauth/manual-token-connection.ts +0 -4
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- 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/plugins/defaults/injectors.ts +35 -2
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +867 -0
- package/src/proactive-artifact/job.ts +352 -0
- package/src/proactive-artifact/message-copy.ts +41 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/bootstrap-cleanup.ts +27 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +23 -24
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/speech-to-text/provider-catalog.ts +7 -8
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +151 -99
- package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/middleware.ts +0 -96
- package/src/runtime/auth/route-policy.ts +32 -0
- package/src/runtime/auth/same-actor.ts +216 -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/channel-retry-sweep.ts +65 -1
- package/src/runtime/guardian-reply-router.ts +10 -0
- package/src/runtime/http-server.ts +3 -329
- package/src/runtime/http-types.ts +0 -5
- package/src/runtime/local-actor-identity.ts +52 -11
- 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 +43 -9
- package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -5
- 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 +21 -2
- package/src/runtime/routes/contact-prompt-routes.ts +183 -0
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-query-routes.ts +36 -1
- package/src/runtime/routes/conversation-routes.ts +65 -39
- package/src/runtime/routes/debug-bash-routes.ts +163 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +169 -0
- package/src/runtime/routes/documents-routes.ts +32 -75
- package/src/runtime/routes/errors.ts +19 -4
- package/src/runtime/routes/events-routes.ts +38 -0
- 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 +56 -6
- package/src/runtime/routes/host-browser-routes.ts +108 -13
- package/src/runtime/routes/host-cu-routes.ts +66 -9
- package/src/runtime/routes/host-file-routes.ts +54 -5
- package/src/runtime/routes/host-transfer-routes.ts +122 -19
- 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 +21 -180
- package/src/runtime/routes/inbound-message-handler.ts +78 -21
- 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/guardian-reply-intercept.ts +3 -0
- 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 +14 -0
- package/src/runtime/routes/mcp-auth-routes.ts +132 -0
- package/src/runtime/routes/memory-item-routes.test.ts +41 -15
- package/src/runtime/routes/memory-item-routes.ts +10 -12
- package/src/runtime/routes/memory-v2-routes.ts +474 -1
- package/src/runtime/routes/migration-routes.ts +96 -0
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/routes/schedule-routes.ts +7 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- 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/run-script.ts +37 -5
- 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 +83 -39
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/oauth-callback-registry.ts +8 -0
- package/src/security/secure-keys.ts +55 -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 +10 -16
- 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/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- 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/background-tool-registry.ts +17 -3
- 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.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +68 -0
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +68 -0
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +78 -3
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +68 -0
- package/src/tools/host-terminal/host-shell.ts +66 -1
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +12 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- 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/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +32 -11
- package/src/tools/types.ts +28 -2
- package/src/tts/provider-catalog.ts +3 -5
- package/src/usage/pricing.ts +1 -1
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/hatched-date.ts +86 -0
- package/src/workspace/heartbeat-service.ts +19 -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/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/utils.ts +21 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- 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/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -395
- 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/bash.ts +0 -198
- package/src/signals/mcp-reload.ts +0 -18
|
@@ -60,7 +60,6 @@ const state = {
|
|
|
60
60
|
embedCalls: [] as Array<{ inputs: unknown[] }>,
|
|
61
61
|
sparseCalls: [] as string[],
|
|
62
62
|
embedReturn: [[0.1, 0.2, 0.3]] as number[][],
|
|
63
|
-
sparseReturn: { indices: [1, 2, 3], values: [0.5, 0.5, 0.5] },
|
|
64
63
|
// Programmable Qdrant query response — one entry per `using` channel,
|
|
65
64
|
// shifted in order so each test can stage dense + sparse results.
|
|
66
65
|
queryResponses: {
|
|
@@ -71,16 +70,6 @@ const state = {
|
|
|
71
70
|
points: Array<{ score?: number; payload: Record<string, unknown> }>;
|
|
72
71
|
}>,
|
|
73
72
|
},
|
|
74
|
-
// Separate response queue for the dedicated skills collection so a test
|
|
75
|
-
// querying both concept pages and skills doesn't have to interleave.
|
|
76
|
-
skillQueryResponses: {
|
|
77
|
-
dense: [] as Array<{
|
|
78
|
-
points: Array<{ score?: number; payload: Record<string, unknown> }>;
|
|
79
|
-
}>,
|
|
80
|
-
sparse: [] as Array<{
|
|
81
|
-
points: Array<{ score?: number; payload: Record<string, unknown> }>;
|
|
82
|
-
}>,
|
|
83
|
-
},
|
|
84
73
|
queryCalls: [] as Array<{
|
|
85
74
|
collection: string;
|
|
86
75
|
using: string;
|
|
@@ -90,7 +79,7 @@ const state = {
|
|
|
90
79
|
};
|
|
91
80
|
|
|
92
81
|
// Re-export every real symbol from the embedding-backend module, overriding
|
|
93
|
-
// only the
|
|
82
|
+
// only the one we control. Bun's `mock.module` replacement is process-wide,
|
|
94
83
|
// so a partial mock here would break sibling test files that import other
|
|
95
84
|
// exports from the same module (`selectEmbeddingBackend`, etc.).
|
|
96
85
|
const realEmbeddingBackend = await import("../../embedding-backend.js");
|
|
@@ -104,9 +93,23 @@ mock.module("../../embedding-backend.js", () => ({
|
|
|
104
93
|
vectors: state.embedReturn,
|
|
105
94
|
};
|
|
106
95
|
},
|
|
107
|
-
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
// `sim.ts` builds the query-side sparse vector via BM25's
|
|
99
|
+
// `generateBm25QueryEmbedding`. Wrap it to record the call text, then
|
|
100
|
+
// delegate to the real implementation so the resulting sparse vector is
|
|
101
|
+
// well-formed. Capture the function reference *before* registering the
|
|
102
|
+
// mock — ESM live bindings resolve through the namespace at call time, so
|
|
103
|
+
// `realSparseBm25.fn(...)` after `mock.module` would route into the
|
|
104
|
+
// mocked version and recurse.
|
|
105
|
+
const realSparseBm25 = await import("../sparse-bm25.js");
|
|
106
|
+
const realGenerateBm25QueryEmbedding =
|
|
107
|
+
realSparseBm25.generateBm25QueryEmbedding;
|
|
108
|
+
mock.module("../sparse-bm25.js", () => ({
|
|
109
|
+
...realSparseBm25,
|
|
110
|
+
generateBm25QueryEmbedding: (text: string) => {
|
|
108
111
|
state.sparseCalls.push(text);
|
|
109
|
-
return
|
|
112
|
+
return realGenerateBm25QueryEmbedding(text);
|
|
110
113
|
},
|
|
111
114
|
}));
|
|
112
115
|
|
|
@@ -134,11 +137,7 @@ class MockQdrantClient {
|
|
|
134
137
|
filter: params.filter,
|
|
135
138
|
});
|
|
136
139
|
const channel = params.using as "dense" | "sparse";
|
|
137
|
-
|
|
138
|
-
name === "memory_v2_skills"
|
|
139
|
-
? state.skillQueryResponses[channel]
|
|
140
|
-
: state.queryResponses[channel];
|
|
141
|
-
return queue.shift() ?? { points: [] };
|
|
140
|
+
return state.queryResponses[channel].shift() ?? { points: [] };
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
143
|
|
|
@@ -146,9 +145,7 @@ mock.module("@qdrant/js-client-rest", () => ({
|
|
|
146
145
|
QdrantClient: MockQdrantClient,
|
|
147
146
|
}));
|
|
148
147
|
|
|
149
|
-
const { simBatch,
|
|
150
|
-
const { _resetMemoryV2SkillQdrantForTests } =
|
|
151
|
-
await import("../skill-qdrant.js");
|
|
148
|
+
const { simBatch, clamp01, effectiveWeights } = await import("../sim.js");
|
|
152
149
|
const { _resetMemoryV2QdrantForTests } = await import("../qdrant.js");
|
|
153
150
|
|
|
154
151
|
// ---------------------------------------------------------------------------
|
|
@@ -159,18 +156,14 @@ function resetState(): void {
|
|
|
159
156
|
state.embedCalls.length = 0;
|
|
160
157
|
state.sparseCalls.length = 0;
|
|
161
158
|
state.embedReturn = [[0.1, 0.2, 0.3]];
|
|
162
|
-
state.sparseReturn = { indices: [1, 2, 3], values: [0.5, 0.5, 0.5] };
|
|
163
159
|
state.queryResponses.dense.length = 0;
|
|
164
160
|
state.queryResponses.sparse.length = 0;
|
|
165
|
-
state.skillQueryResponses.dense.length = 0;
|
|
166
|
-
state.skillQueryResponses.sparse.length = 0;
|
|
167
161
|
state.queryCalls.length = 0;
|
|
168
162
|
// Bun's `mock.module` persists across files in the same process, so the
|
|
169
|
-
// qdrant
|
|
170
|
-
// from a sibling test file. Reset
|
|
171
|
-
//
|
|
163
|
+
// qdrant module's singleton may already hold a MockQdrantClient instance
|
|
164
|
+
// from a sibling test file. Reset readiness so each test in this file
|
|
165
|
+
// gets a fresh `new QdrantClient()` resolved against our mock.
|
|
172
166
|
_resetMemoryV2QdrantForTests();
|
|
173
|
-
_resetMemoryV2SkillQdrantForTests();
|
|
174
167
|
}
|
|
175
168
|
|
|
176
169
|
function configWithWeights(
|
|
@@ -216,6 +209,120 @@ afterEach(resetState);
|
|
|
216
209
|
// clamp01
|
|
217
210
|
// ---------------------------------------------------------------------------
|
|
218
211
|
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// effectiveWeights — adaptive sparse weighting
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
describe("effectiveWeights", () => {
|
|
217
|
+
// The helper takes a generic config, but only reads
|
|
218
|
+
// `memory.v2.min_sparse_spread` / `full_sparse_spread`. Build a minimal
|
|
219
|
+
// shape so test cases can opt into custom thresholds vs the built-in
|
|
220
|
+
// defaults (0.2 / 0.5).
|
|
221
|
+
function configWithSpreadOverrides(
|
|
222
|
+
min?: number,
|
|
223
|
+
full?: number,
|
|
224
|
+
): AssistantConfig {
|
|
225
|
+
return {
|
|
226
|
+
memory: { v2: { min_sparse_spread: min, full_sparse_spread: full } },
|
|
227
|
+
} as unknown as AssistantConfig;
|
|
228
|
+
}
|
|
229
|
+
const baseConfig = configWithSpreadOverrides();
|
|
230
|
+
|
|
231
|
+
test("returns base weights when sparse weight is zero", () => {
|
|
232
|
+
const result = effectiveWeights(
|
|
233
|
+
[{ sparseScore: 1 }, { sparseScore: 2 }],
|
|
234
|
+
2,
|
|
235
|
+
1.0,
|
|
236
|
+
0.0,
|
|
237
|
+
baseConfig,
|
|
238
|
+
);
|
|
239
|
+
expect(result.dense).toBe(1.0);
|
|
240
|
+
expect(result.sparse).toBe(0);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("returns base weights when fewer than 2 sparse-bearing hits", () => {
|
|
244
|
+
// Single sparse-bearing hit — spread is undefined.
|
|
245
|
+
const result = effectiveWeights(
|
|
246
|
+
[{ sparseScore: 5 }, {}],
|
|
247
|
+
5,
|
|
248
|
+
0.7,
|
|
249
|
+
0.3,
|
|
250
|
+
baseConfig,
|
|
251
|
+
);
|
|
252
|
+
expect(result.dense).toBeCloseTo(0.7);
|
|
253
|
+
expect(result.sparse).toBeCloseTo(0.3);
|
|
254
|
+
expect(result.spread).toBe(0);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("collapses sparse weight to 0 when spread is below min_sparse_spread", () => {
|
|
258
|
+
// Three hits with sparseNorm = {0.95, 0.97, 1.0} → spread 0.05 < 0.2.
|
|
259
|
+
const hits = [
|
|
260
|
+
{ sparseScore: 9.5 },
|
|
261
|
+
{ sparseScore: 9.7 },
|
|
262
|
+
{ sparseScore: 10 },
|
|
263
|
+
];
|
|
264
|
+
const result = effectiveWeights(hits, 10, 0.7, 0.3, baseConfig);
|
|
265
|
+
expect(result.spread).toBeCloseTo(0.05, 6);
|
|
266
|
+
expect(result.sparse).toBeCloseTo(0, 6);
|
|
267
|
+
// Dense compensates: gets the full sparse weight added back.
|
|
268
|
+
expect(result.dense).toBeCloseTo(1.0, 6);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("preserves base weights when spread reaches full_sparse_spread", () => {
|
|
272
|
+
// sparseNorm = {0.5, 1.0} → spread 0.5 === default full threshold.
|
|
273
|
+
const hits = [{ sparseScore: 5 }, { sparseScore: 10 }];
|
|
274
|
+
const result = effectiveWeights(hits, 10, 0.7, 0.3, baseConfig);
|
|
275
|
+
expect(result.spread).toBeCloseTo(0.5, 6);
|
|
276
|
+
expect(result.sparse).toBeCloseTo(0.3, 6);
|
|
277
|
+
expect(result.dense).toBeCloseTo(0.7, 6);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("interpolates linearly between min and full thresholds", () => {
|
|
281
|
+
// sparseNorm = {0.65, 1.0} → spread 0.35; midway between 0.2 and 0.5
|
|
282
|
+
// → factor = 0.5; effSparse = 0.5 * 0.3 = 0.15; effDense = 0.7 + 0.15.
|
|
283
|
+
const hits = [{ sparseScore: 6.5 }, { sparseScore: 10 }];
|
|
284
|
+
const result = effectiveWeights(hits, 10, 0.7, 0.3, baseConfig);
|
|
285
|
+
expect(result.spread).toBeCloseTo(0.35, 6);
|
|
286
|
+
expect(result.sparse).toBeCloseTo(0.15, 6);
|
|
287
|
+
expect(result.dense).toBeCloseTo(0.85, 6);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("config overrides min and full thresholds", () => {
|
|
291
|
+
// Custom: min=0.0, full=1.0 — spread is now in [0, 1] linearly.
|
|
292
|
+
const config = configWithSpreadOverrides(0.0, 1.0);
|
|
293
|
+
const hits = [{ sparseScore: 8 }, { sparseScore: 10 }];
|
|
294
|
+
const result = effectiveWeights(hits, 10, 0.7, 0.3, config);
|
|
295
|
+
// spread 0.2; factor = 0.2; effSparse = 0.06.
|
|
296
|
+
expect(result.spread).toBeCloseTo(0.2, 6);
|
|
297
|
+
expect(result.sparse).toBeCloseTo(0.06, 6);
|
|
298
|
+
expect(result.dense).toBeCloseTo(0.94, 6);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("falls back to base weights when full <= min (degenerate config)", () => {
|
|
302
|
+
const config = configWithSpreadOverrides(0.5, 0.3);
|
|
303
|
+
const hits = [{ sparseScore: 5 }, { sparseScore: 10 }];
|
|
304
|
+
const result = effectiveWeights(hits, 10, 0.7, 0.3, config);
|
|
305
|
+
expect(result.sparse).toBeCloseTo(0.3, 6);
|
|
306
|
+
expect(result.dense).toBeCloseTo(0.7, 6);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("dense + sparse always equals baseDense + baseSparse", () => {
|
|
310
|
+
// Property check: total weight is preserved across the spread spectrum
|
|
311
|
+
// so `fused` stays interpretable as a [0, 1] similarity regardless of
|
|
312
|
+
// how aggressively sparse is collapsed.
|
|
313
|
+
const cases = [
|
|
314
|
+
[{ sparseScore: 1 }, { sparseScore: 1.05 }], // tiny spread
|
|
315
|
+
[{ sparseScore: 1 }, { sparseScore: 5 }], // mid spread
|
|
316
|
+
[{ sparseScore: 1 }, { sparseScore: 10 }], // full spread
|
|
317
|
+
];
|
|
318
|
+
for (const hits of cases) {
|
|
319
|
+
const maxSparse = Math.max(...hits.map((h) => h.sparseScore));
|
|
320
|
+
const result = effectiveWeights(hits, maxSparse, 0.7, 0.3, baseConfig);
|
|
321
|
+
expect(result.dense + result.sparse).toBeCloseTo(1.0, 6);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
219
326
|
describe("clamp01", () => {
|
|
220
327
|
test("passes values already in [0, 1] through unchanged", () => {
|
|
221
328
|
expect(clamp01(0)).toBe(0);
|
|
@@ -247,6 +354,24 @@ describe("simBatch", () => {
|
|
|
247
354
|
expect(state.queryCalls).toHaveLength(0);
|
|
248
355
|
});
|
|
249
356
|
|
|
357
|
+
test("empty text returns empty map without touching backends", async () => {
|
|
358
|
+
// Turn 1 has no prior assistant message, so `computeOwnActivation` calls
|
|
359
|
+
// `simBatch("", slugs, config)`. Gemini rejects empty content with HTTP
|
|
360
|
+
// 400 — short-circuit here so the activation pipeline doesn't crash.
|
|
361
|
+
const config = configWithWeights(0.7, 0.3);
|
|
362
|
+
|
|
363
|
+
for (const text of ["", " ", "\n\n"]) {
|
|
364
|
+
state.embedCalls.length = 0;
|
|
365
|
+
state.sparseCalls.length = 0;
|
|
366
|
+
state.queryCalls.length = 0;
|
|
367
|
+
const out = await simBatch(text, ["alice-vscode"], config);
|
|
368
|
+
expect(out.size).toBe(0);
|
|
369
|
+
expect(state.embedCalls).toHaveLength(0);
|
|
370
|
+
expect(state.sparseCalls).toHaveLength(0);
|
|
371
|
+
expect(state.queryCalls).toHaveLength(0);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
250
375
|
test("identical text yields ~1.0 when both channels max out", async () => {
|
|
251
376
|
const config = configWithWeights(0.7, 0.3);
|
|
252
377
|
stageHybridResponse([
|
|
@@ -388,162 +513,3 @@ describe("simBatch", () => {
|
|
|
388
513
|
}
|
|
389
514
|
});
|
|
390
515
|
});
|
|
391
|
-
|
|
392
|
-
// ---------------------------------------------------------------------------
|
|
393
|
-
// simSkillBatch
|
|
394
|
-
// ---------------------------------------------------------------------------
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Stage a single hybrid response on the dedicated skills queues. Mirrors
|
|
398
|
-
* `stageHybridResponse` but uses `payload.id` (skills' Qdrant payload key)
|
|
399
|
-
* instead of `payload.slug`.
|
|
400
|
-
*/
|
|
401
|
-
function stageSkillHybridResponse(
|
|
402
|
-
hits: Array<{ id: string; denseScore?: number; sparseScore?: number }>,
|
|
403
|
-
): void {
|
|
404
|
-
state.skillQueryResponses.dense.push({
|
|
405
|
-
points: hits
|
|
406
|
-
.filter((h) => h.denseScore !== undefined)
|
|
407
|
-
.map((h) => ({ score: h.denseScore, payload: { id: h.id } })),
|
|
408
|
-
});
|
|
409
|
-
state.skillQueryResponses.sparse.push({
|
|
410
|
-
points: hits
|
|
411
|
-
.filter((h) => h.sparseScore !== undefined)
|
|
412
|
-
.map((h) => ({ score: h.sparseScore, payload: { id: h.id } })),
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
describe("simSkillBatch", () => {
|
|
417
|
-
test("empty id list returns empty map without touching backends", async () => {
|
|
418
|
-
const config = configWithWeights(0.7, 0.3);
|
|
419
|
-
|
|
420
|
-
const out = await simSkillBatch("anything", [], config);
|
|
421
|
-
|
|
422
|
-
expect(out.size).toBe(0);
|
|
423
|
-
expect(state.embedCalls).toHaveLength(0);
|
|
424
|
-
expect(state.sparseCalls).toHaveLength(0);
|
|
425
|
-
expect(state.queryCalls).toHaveLength(0);
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
test("queries the dedicated skills collection and forwards an id-IN filter", async () => {
|
|
429
|
-
const config = configWithWeights(0.7, 0.3);
|
|
430
|
-
stageSkillHybridResponse([]);
|
|
431
|
-
|
|
432
|
-
await simSkillBatch(
|
|
433
|
-
"query",
|
|
434
|
-
["example-skill-a", "example-skill-b"],
|
|
435
|
-
config,
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
expect(state.queryCalls).toHaveLength(2);
|
|
439
|
-
for (const call of state.queryCalls) {
|
|
440
|
-
expect(call.collection).toBe("memory_v2_skills");
|
|
441
|
-
// The candidate ids are forwarded as a Qdrant filter so Qdrant scores
|
|
442
|
-
// exactly the candidate set, not its global top-K. Without this,
|
|
443
|
-
// candidate ids absent from the global top-K silently score 0.
|
|
444
|
-
expect(call.filter).toEqual({
|
|
445
|
-
must: [
|
|
446
|
-
{ key: "id", match: { any: ["example-skill-a", "example-skill-b"] } },
|
|
447
|
-
],
|
|
448
|
-
});
|
|
449
|
-
// Limit equals the candidate count.
|
|
450
|
-
expect(call.limit).toBe(2);
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
test("fuses dense + sparse with the configured weight blend", async () => {
|
|
455
|
-
const config = configWithWeights(0.4, 0.6);
|
|
456
|
-
stageSkillHybridResponse([
|
|
457
|
-
{ id: "example-skill-a", denseScore: 0.5, sparseScore: 4 }, // sparse-norm 1.0
|
|
458
|
-
{ id: "example-skill-b", denseScore: 0.25, sparseScore: 2 }, // sparse-norm 0.5
|
|
459
|
-
]);
|
|
460
|
-
|
|
461
|
-
const out = await simSkillBatch(
|
|
462
|
-
"query",
|
|
463
|
-
["example-skill-a", "example-skill-b"],
|
|
464
|
-
config,
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
// example-skill-a: 0.4 * 0.5 + 0.6 * 1.0 = 0.8
|
|
468
|
-
// example-skill-b: 0.4 * 0.25 + 0.6 * 0.5 = 0.4
|
|
469
|
-
expect(out.get("example-skill-a")).toBeCloseTo(0.8, 6);
|
|
470
|
-
expect(out.get("example-skill-b")).toBeCloseTo(0.4, 6);
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
test("dense-only and sparse-only hits are handled symmetrically", async () => {
|
|
474
|
-
const config = configWithWeights(0.7, 0.3);
|
|
475
|
-
stageSkillHybridResponse([
|
|
476
|
-
{ id: "example-skill-a", denseScore: 0.5 /* sparse omitted */ },
|
|
477
|
-
{ id: "example-skill-b", sparseScore: 8 /* dense omitted */ },
|
|
478
|
-
]);
|
|
479
|
-
|
|
480
|
-
const out = await simSkillBatch(
|
|
481
|
-
"query",
|
|
482
|
-
["example-skill-a", "example-skill-b"],
|
|
483
|
-
config,
|
|
484
|
-
);
|
|
485
|
-
|
|
486
|
-
// example-skill-a: 0.7 * 0.5 + 0.3 * 0 = 0.35
|
|
487
|
-
// example-skill-b: 0.7 * 0 + 0.3 * 1.0 = 0.30 (sparse-norm = 8/8)
|
|
488
|
-
expect(out.get("example-skill-a")).toBeCloseTo(0.35, 6);
|
|
489
|
-
expect(out.get("example-skill-b")).toBeCloseTo(0.3, 6);
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
test("forwards candidate ids as the Qdrant restriction; only candidates in result", async () => {
|
|
493
|
-
// The bug we're guarding against: when the skills collection has more
|
|
494
|
-
// skills than `ids.length`, calling `hybridQuerySkills` without a filter
|
|
495
|
-
// returns Qdrant's global top-K. Candidate ids absent from that top-K
|
|
496
|
-
// would silently score 0. The fix is to forward the candidate ids as a
|
|
497
|
-
// server-side restriction so Qdrant scores exactly the candidate set.
|
|
498
|
-
const config = configWithWeights(0.7, 0.3);
|
|
499
|
-
stageSkillHybridResponse([
|
|
500
|
-
{ id: "example-skill-a", denseScore: 0.5, sparseScore: 1 },
|
|
501
|
-
// `example-skill-c` would never be returned in production once the
|
|
502
|
-
// filter is applied; the post-filter in simSkillBatch defensively
|
|
503
|
-
// drops it even if a stale payload slips through.
|
|
504
|
-
{ id: "example-skill-c", denseScore: 0.9, sparseScore: 1 },
|
|
505
|
-
]);
|
|
506
|
-
|
|
507
|
-
const out = await simSkillBatch(
|
|
508
|
-
"query",
|
|
509
|
-
["example-skill-a", "example-skill-b"],
|
|
510
|
-
config,
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
// The Qdrant filter was forwarded — both channels carry the id-IN
|
|
514
|
-
// restriction matching the caller's candidate set.
|
|
515
|
-
expect(state.queryCalls).toHaveLength(2);
|
|
516
|
-
for (const call of state.queryCalls) {
|
|
517
|
-
expect(call.filter).toEqual({
|
|
518
|
-
must: [
|
|
519
|
-
{ key: "id", match: { any: ["example-skill-a", "example-skill-b"] } },
|
|
520
|
-
],
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
// Only candidate ids appear in the result map.
|
|
524
|
-
expect(out.has("example-skill-a")).toBe(true);
|
|
525
|
-
expect(out.has("example-skill-c")).toBe(false);
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
test("returned scores are clamped into [0, 1]", async () => {
|
|
529
|
-
const config = configWithWeights(0.8, 0.5); // intentionally sums to > 1
|
|
530
|
-
stageSkillHybridResponse([
|
|
531
|
-
{ id: "example-skill-a", denseScore: 1.0, sparseScore: 1 },
|
|
532
|
-
]);
|
|
533
|
-
|
|
534
|
-
const out = await simSkillBatch("query", ["example-skill-a"], config);
|
|
535
|
-
|
|
536
|
-
expect(out.get("example-skill-a")).toBe(1);
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
test("embeds the query text exactly once via dense + sparse backends", async () => {
|
|
540
|
-
const config = configWithWeights(0.7, 0.3);
|
|
541
|
-
stageSkillHybridResponse([]);
|
|
542
|
-
|
|
543
|
-
await simSkillBatch("hello skill", ["example-skill-a"], config);
|
|
544
|
-
|
|
545
|
-
expect(state.embedCalls).toHaveLength(1);
|
|
546
|
-
expect(state.embedCalls[0].inputs).toEqual(["hello skill"]);
|
|
547
|
-
expect(state.sparseCalls).toEqual(["hello skill"]);
|
|
548
|
-
});
|
|
549
|
-
});
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for `assistant/src/memory/v2/skill-store.ts`.
|
|
3
3
|
*
|
|
4
|
-
* Coverage matrix
|
|
4
|
+
* Coverage matrix:
|
|
5
5
|
* - `seedV2SkillEntries` enumerates the catalog and calls
|
|
6
|
-
* `
|
|
6
|
+
* `upsertConceptPageEmbedding` with `slug: "skills/<id>"` for each
|
|
7
|
+
* enabled skill in the unified `memory_v2_concept_pages` collection.
|
|
7
8
|
* - It skips skills whose declared feature flag is disabled.
|
|
8
|
-
* - It calls `
|
|
9
|
-
*
|
|
9
|
+
* - It calls `pruneSlugsWithPrefixExcept("skills/", ...)` with the active
|
|
10
|
+
* id list as suffixes, so stale skill slugs in the unified collection
|
|
11
|
+
* get pruned without touching concept-page slugs.
|
|
12
|
+
* - It populates the `entries` cache so `getSkillCapability` returns each
|
|
13
|
+
* entry — accepting both bare ids (`"example-skill"`) and unified-collection
|
|
14
|
+
* slugs (`"skills/example-skill"`).
|
|
10
15
|
* - It swallows errors from the embedding backend — the function resolves
|
|
11
16
|
* and the cache is unchanged from prior state.
|
|
12
17
|
*
|
|
@@ -29,6 +34,18 @@ mock.module("../../../util/logger.js", () => ({
|
|
|
29
34
|
// Programmable test state — drives every mocked dependency below.
|
|
30
35
|
// ---------------------------------------------------------------------------
|
|
31
36
|
|
|
37
|
+
interface UpsertCall {
|
|
38
|
+
slug: string;
|
|
39
|
+
dense: number[];
|
|
40
|
+
sparse: { indices: number[]; values: number[] };
|
|
41
|
+
updatedAt: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface PruneCall {
|
|
45
|
+
prefix: string;
|
|
46
|
+
activeSuffixes: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
interface TestState {
|
|
33
50
|
catalog: SkillSummary[];
|
|
34
51
|
resolved: ResolvedSkill[];
|
|
@@ -38,14 +55,8 @@ interface TestState {
|
|
|
38
55
|
embedThrows: Error | null;
|
|
39
56
|
embedReturn: number[][];
|
|
40
57
|
sparseReturn: { indices: number[]; values: number[] };
|
|
41
|
-
upsertCalls:
|
|
42
|
-
|
|
43
|
-
content: string;
|
|
44
|
-
dense: number[];
|
|
45
|
-
sparse: { indices: number[]; values: number[] };
|
|
46
|
-
updatedAt: number;
|
|
47
|
-
}>;
|
|
48
|
-
pruneCalls: Array<readonly string[]>;
|
|
58
|
+
upsertCalls: UpsertCall[];
|
|
59
|
+
pruneCalls: PruneCall[];
|
|
49
60
|
upsertThrows: Error | null;
|
|
50
61
|
}
|
|
51
62
|
|
|
@@ -99,13 +110,16 @@ mock.module("../../embedding-backend.js", () => ({
|
|
|
99
110
|
generateSparseEmbedding: () => state.sparseReturn,
|
|
100
111
|
}));
|
|
101
112
|
|
|
102
|
-
mock.module("../
|
|
103
|
-
|
|
113
|
+
mock.module("../qdrant.js", () => ({
|
|
114
|
+
upsertConceptPageEmbedding: async (params: UpsertCall) => {
|
|
104
115
|
if (state.upsertThrows) throw state.upsertThrows;
|
|
105
116
|
state.upsertCalls.push(params);
|
|
106
117
|
},
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
pruneSlugsWithPrefixExcept: async (
|
|
119
|
+
prefix: string,
|
|
120
|
+
activeSuffixes: readonly string[],
|
|
121
|
+
) => {
|
|
122
|
+
state.pruneCalls.push({ prefix, activeSuffixes });
|
|
109
123
|
},
|
|
110
124
|
}));
|
|
111
125
|
|
|
@@ -160,7 +174,7 @@ afterEach(resetState);
|
|
|
160
174
|
// ---------------------------------------------------------------------------
|
|
161
175
|
|
|
162
176
|
describe("seedV2SkillEntries", () => {
|
|
163
|
-
test("
|
|
177
|
+
test("upserts each enabled skill into the unified collection under skills/<id>", async () => {
|
|
164
178
|
const skillA = makeSummary({
|
|
165
179
|
id: "example-skill-a",
|
|
166
180
|
displayName: "Skill A",
|
|
@@ -182,15 +196,16 @@ describe("seedV2SkillEntries", () => {
|
|
|
182
196
|
await seedV2SkillEntries();
|
|
183
197
|
|
|
184
198
|
expect(state.upsertCalls).toHaveLength(2);
|
|
185
|
-
const
|
|
186
|
-
expect(
|
|
187
|
-
|
|
188
|
-
// Each upsert carries the per-skill dense + sparse +
|
|
189
|
-
|
|
199
|
+
const slugs = state.upsertCalls.map((c) => c.slug).sort();
|
|
200
|
+
expect(slugs).toEqual(["skills/example-skill-a", "skills/example-skill-b"]);
|
|
201
|
+
|
|
202
|
+
// Each upsert carries the per-skill dense + sparse + updatedAt payload,
|
|
203
|
+
// keyed under the unified `skills/<id>` slug.
|
|
204
|
+
const callA = state.upsertCalls.find(
|
|
205
|
+
(c) => c.slug === "skills/example-skill-a",
|
|
206
|
+
)!;
|
|
190
207
|
expect(callA.dense).toEqual([0.1, 0.2, 0.3]);
|
|
191
208
|
expect(callA.sparse).toEqual(state.sparseReturn);
|
|
192
|
-
expect(callA.content).toContain("Skill A");
|
|
193
|
-
expect(callA.content).toContain("(example-skill-a)");
|
|
194
209
|
expect(callA.updatedAt).toBeGreaterThan(0);
|
|
195
210
|
});
|
|
196
211
|
|
|
@@ -207,12 +222,11 @@ describe("seedV2SkillEntries", () => {
|
|
|
207
222
|
await seedV2SkillEntries();
|
|
208
223
|
|
|
209
224
|
expect(state.upsertCalls).toHaveLength(1);
|
|
210
|
-
expect(state.upsertCalls[0].
|
|
225
|
+
expect(state.upsertCalls[0].slug).toBe("skills/example-skill-a");
|
|
211
226
|
});
|
|
212
227
|
|
|
213
228
|
test("does not re-seed an installed-but-disabled skill from the remote catalog", async () => {
|
|
214
|
-
// Regression
|
|
215
|
-
// (Codex P1): if `seenIds` is built only from enabled skills, a locally
|
|
229
|
+
// Regression: if `seenIds` is built only from enabled skills, a locally
|
|
216
230
|
// installed-but-disabled skill falls through to the catalog loop and gets
|
|
217
231
|
// embedded as if it were a discoverable uninstalled skill — contradicting
|
|
218
232
|
// the user's explicit disablement.
|
|
@@ -223,8 +237,6 @@ describe("seedV2SkillEntries", () => {
|
|
|
223
237
|
{ summary: enabledSkill, state: "enabled" },
|
|
224
238
|
{ summary: disabledSkill, state: "disabled" },
|
|
225
239
|
];
|
|
226
|
-
// The remote catalog also contains the disabled skill (same id) — the
|
|
227
|
-
// seed function must NOT pull it back in via `getCatalog()`.
|
|
228
240
|
state.fullCatalog = [
|
|
229
241
|
{
|
|
230
242
|
id: "example-skill-b",
|
|
@@ -237,7 +249,7 @@ describe("seedV2SkillEntries", () => {
|
|
|
237
249
|
await seedV2SkillEntries();
|
|
238
250
|
|
|
239
251
|
expect(state.upsertCalls).toHaveLength(1);
|
|
240
|
-
expect(state.upsertCalls[0].
|
|
252
|
+
expect(state.upsertCalls[0].slug).toBe("skills/example-skill-a");
|
|
241
253
|
});
|
|
242
254
|
|
|
243
255
|
test("seeds genuinely uninstalled catalog skills alongside enabled installed skills", async () => {
|
|
@@ -263,8 +275,11 @@ describe("seedV2SkillEntries", () => {
|
|
|
263
275
|
|
|
264
276
|
await seedV2SkillEntries();
|
|
265
277
|
|
|
266
|
-
const
|
|
267
|
-
expect(
|
|
278
|
+
const slugs = state.upsertCalls.map((c) => c.slug).sort();
|
|
279
|
+
expect(slugs).toEqual([
|
|
280
|
+
"skills/example-skill-a",
|
|
281
|
+
"skills/uninstalled-skill",
|
|
282
|
+
]);
|
|
268
283
|
});
|
|
269
284
|
|
|
270
285
|
test("skips skills whose declared feature flag is disabled", async () => {
|
|
@@ -284,10 +299,10 @@ describe("seedV2SkillEntries", () => {
|
|
|
284
299
|
await seedV2SkillEntries();
|
|
285
300
|
|
|
286
301
|
expect(state.upsertCalls).toHaveLength(1);
|
|
287
|
-
expect(state.upsertCalls[0].
|
|
302
|
+
expect(state.upsertCalls[0].slug).toBe("skills/example-skill-b");
|
|
288
303
|
});
|
|
289
304
|
|
|
290
|
-
test("calls
|
|
305
|
+
test("calls pruneSlugsWithPrefixExcept with the active id list and the skills/ prefix", async () => {
|
|
291
306
|
const skillA = makeSummary({ id: "example-skill-a" });
|
|
292
307
|
const skillB = makeSummary({ id: "example-skill-b" });
|
|
293
308
|
state.catalog = [skillA, skillB];
|
|
@@ -309,13 +324,14 @@ describe("seedV2SkillEntries", () => {
|
|
|
309
324
|
await seedV2SkillEntries();
|
|
310
325
|
|
|
311
326
|
expect(state.pruneCalls).toHaveLength(1);
|
|
312
|
-
expect(
|
|
327
|
+
expect(state.pruneCalls[0].prefix).toBe("skills/");
|
|
328
|
+
expect([...state.pruneCalls[0].activeSuffixes].sort()).toEqual([
|
|
313
329
|
"example-skill-a",
|
|
314
330
|
"example-skill-b",
|
|
315
331
|
]);
|
|
316
332
|
});
|
|
317
333
|
|
|
318
|
-
test("passes only the active (post-flag-filter) ids to
|
|
334
|
+
test("passes only the active (post-flag-filter) ids to pruneSlugsWithPrefixExcept", async () => {
|
|
319
335
|
const flagged = makeSummary({
|
|
320
336
|
id: "example-skill-a",
|
|
321
337
|
featureFlag: "off-flag",
|
|
@@ -327,8 +343,6 @@ describe("seedV2SkillEntries", () => {
|
|
|
327
343
|
{ summary: unflagged, state: "enabled" },
|
|
328
344
|
];
|
|
329
345
|
state.flagsEnabled = { "off-flag": false };
|
|
330
|
-
// Remote catalog must be non-empty so catalogAvailable is true and
|
|
331
|
-
// pruning is not skipped.
|
|
332
346
|
state.fullCatalog = [
|
|
333
347
|
{ id: "example-skill-a", name: "example-skill-a", description: "A" },
|
|
334
348
|
{ id: "example-skill-b", name: "example-skill-b", description: "B" },
|
|
@@ -338,44 +352,35 @@ describe("seedV2SkillEntries", () => {
|
|
|
338
352
|
await seedV2SkillEntries();
|
|
339
353
|
|
|
340
354
|
expect(state.pruneCalls).toHaveLength(1);
|
|
341
|
-
expect(
|
|
355
|
+
expect(state.pruneCalls[0].prefix).toBe("skills/");
|
|
356
|
+
expect([...state.pruneCalls[0].activeSuffixes]).toEqual([
|
|
357
|
+
"example-skill-b",
|
|
358
|
+
]);
|
|
342
359
|
});
|
|
343
360
|
|
|
344
|
-
test("populates the entries cache so getSkillCapability
|
|
361
|
+
test("populates the entries cache so getSkillCapability resolves both bare id and unified slug", async () => {
|
|
345
362
|
const skillA = makeSummary({
|
|
346
363
|
id: "example-skill-a",
|
|
347
364
|
displayName: "Skill A",
|
|
348
365
|
});
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
});
|
|
353
|
-
state.catalog = [skillA, skillB];
|
|
354
|
-
state.resolved = [
|
|
355
|
-
{ summary: skillA, state: "enabled" },
|
|
356
|
-
{ summary: skillB, state: "enabled" },
|
|
357
|
-
];
|
|
358
|
-
state.embedReturn = [
|
|
359
|
-
[0.1, 0.2, 0.3],
|
|
360
|
-
[0.4, 0.5, 0.6],
|
|
361
|
-
];
|
|
366
|
+
state.catalog = [skillA];
|
|
367
|
+
state.resolved = [{ summary: skillA, state: "enabled" }];
|
|
368
|
+
state.embedReturn = [[0.1, 0.2, 0.3]];
|
|
362
369
|
|
|
363
370
|
expect(getSkillCapability("example-skill-a")).toBeNull();
|
|
364
371
|
|
|
365
372
|
await seedV2SkillEntries();
|
|
366
373
|
|
|
367
|
-
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
expect(
|
|
371
|
-
expect(
|
|
372
|
-
|
|
373
|
-
expect(
|
|
374
|
-
expect(entryB?.id).toBe("example-skill-b");
|
|
375
|
-
expect(entryB?.content).toContain("Skill B");
|
|
374
|
+
// Bare id and unified-slug forms both resolve to the same entry.
|
|
375
|
+
const byId = getSkillCapability("example-skill-a");
|
|
376
|
+
const bySlug = getSkillCapability("skills/example-skill-a");
|
|
377
|
+
expect(byId).not.toBeNull();
|
|
378
|
+
expect(byId?.id).toBe("example-skill-a");
|
|
379
|
+
expect(byId?.content).toContain("Skill A");
|
|
380
|
+
expect(bySlug).toEqual(byId);
|
|
376
381
|
|
|
377
|
-
// Unknown ids return null even when the cache is populated.
|
|
378
382
|
expect(getSkillCapability("unknown-skill")).toBeNull();
|
|
383
|
+
expect(getSkillCapability("skills/unknown-skill")).toBeNull();
|
|
379
384
|
});
|
|
380
385
|
|
|
381
386
|
test("swallows errors from embedWithBackend and leaves prior cache intact", async () => {
|
|
@@ -426,9 +431,10 @@ describe("seedV2SkillEntries", () => {
|
|
|
426
431
|
await seedV2SkillEntries();
|
|
427
432
|
|
|
428
433
|
expect(state.upsertCalls).toHaveLength(1);
|
|
429
|
-
expect(state.upsertCalls[0].
|
|
434
|
+
expect(state.upsertCalls[0].slug).toBe("skills/remote-only");
|
|
430
435
|
expect(state.pruneCalls).toHaveLength(1);
|
|
431
|
-
expect(
|
|
436
|
+
expect(state.pruneCalls[0].prefix).toBe("skills/");
|
|
437
|
+
expect([...state.pruneCalls[0].activeSuffixes]).toEqual(["remote-only"]);
|
|
432
438
|
});
|
|
433
439
|
|
|
434
440
|
test("skips pruning when catalog fetch returns empty (network failure guard)", async () => {
|