@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
package/src/memory/v2/qdrant.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface ConceptPageQueryResult {
|
|
|
62
62
|
|
|
63
63
|
let _client: QdrantRestClient | null = null;
|
|
64
64
|
let _collectionReady = false;
|
|
65
|
+
let _collectionReadyPromise: Promise<void> | null = null;
|
|
65
66
|
|
|
66
67
|
/** Lazily create a Qdrant REST client bound to the resolved URL. */
|
|
67
68
|
function getClient(): QdrantRestClient {
|
|
@@ -85,7 +86,15 @@ function getClient(): QdrantRestClient {
|
|
|
85
86
|
*/
|
|
86
87
|
export async function ensureConceptPageCollection(): Promise<void> {
|
|
87
88
|
if (_collectionReady) return;
|
|
89
|
+
if (_collectionReadyPromise) return _collectionReadyPromise;
|
|
88
90
|
|
|
91
|
+
_collectionReadyPromise = ensureConceptPageCollectionOnce().finally(() => {
|
|
92
|
+
_collectionReadyPromise = null;
|
|
93
|
+
});
|
|
94
|
+
return _collectionReadyPromise;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function ensureConceptPageCollectionOnce(): Promise<void> {
|
|
89
98
|
const client = getClient();
|
|
90
99
|
const config = getConfig();
|
|
91
100
|
const vectorSize = config.memory.qdrant.vectorSize;
|
|
@@ -215,6 +224,95 @@ export async function deleteConceptPageEmbedding(slug: string): Promise<void> {
|
|
|
215
224
|
}
|
|
216
225
|
}
|
|
217
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Remove every point whose slug starts with the given prefix and whose
|
|
229
|
+
* remaining suffix is not in `activeSuffixes`. Used by the skill-seed flow to
|
|
230
|
+
* drop stale `skills/<id>` slugs after a skill is uninstalled or disabled,
|
|
231
|
+
* since skills now share the concept-page collection rather than living in a
|
|
232
|
+
* dedicated one.
|
|
233
|
+
*
|
|
234
|
+
* Idempotent: when the live `<prefix>*` slugs already match `activeSuffixes`,
|
|
235
|
+
* the function performs a single scroll and no deletes.
|
|
236
|
+
*/
|
|
237
|
+
export async function pruneSlugsWithPrefixExcept(
|
|
238
|
+
prefix: string,
|
|
239
|
+
activeSuffixes: readonly string[],
|
|
240
|
+
): Promise<void> {
|
|
241
|
+
await ensureConceptPageCollection();
|
|
242
|
+
|
|
243
|
+
const client = getClient();
|
|
244
|
+
const activeSet = new Set(activeSuffixes);
|
|
245
|
+
|
|
246
|
+
const doPrune = async (): Promise<void> => {
|
|
247
|
+
const stalePointIds: Array<string | number> = [];
|
|
248
|
+
let offset: string | number | undefined = undefined;
|
|
249
|
+
const maxIterations = 10_000;
|
|
250
|
+
const batchSize = 256;
|
|
251
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
252
|
+
const result = await client.scroll(MEMORY_V2_COLLECTION, {
|
|
253
|
+
limit: batchSize,
|
|
254
|
+
with_payload: true,
|
|
255
|
+
with_vector: false,
|
|
256
|
+
...(offset !== undefined ? { offset } : {}),
|
|
257
|
+
});
|
|
258
|
+
for (const point of result.points) {
|
|
259
|
+
const slug = (point.payload as { slug?: unknown } | null)?.slug;
|
|
260
|
+
if (typeof slug !== "string") continue;
|
|
261
|
+
if (!slug.startsWith(prefix)) continue;
|
|
262
|
+
const suffix = slug.slice(prefix.length);
|
|
263
|
+
if (!activeSet.has(suffix)) {
|
|
264
|
+
stalePointIds.push(point.id);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const next = result.next_page_offset;
|
|
268
|
+
if (next == null) break;
|
|
269
|
+
offset = typeof next === "string" ? next : (next as number);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (stalePointIds.length === 0) return;
|
|
273
|
+
|
|
274
|
+
await client.delete(MEMORY_V2_COLLECTION, {
|
|
275
|
+
wait: true,
|
|
276
|
+
points: stalePointIds,
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
await doPrune();
|
|
282
|
+
} catch (err) {
|
|
283
|
+
if (isCollectionMissing(err)) {
|
|
284
|
+
_collectionReady = false;
|
|
285
|
+
await ensureConceptPageCollection();
|
|
286
|
+
await doPrune();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
throw err;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Best-effort delete of the legacy `memory_v2_skills` Qdrant collection. Skill
|
|
295
|
+
* embeddings now live alongside concept pages in `memory_v2_concept_pages`
|
|
296
|
+
* under the `skills/<id>` slug prefix, so the dedicated collection is dead
|
|
297
|
+
* weight on installs upgraded from the split-collection era. Fire-and-forget:
|
|
298
|
+
* on a fresh install (collection never existed) or a transient Qdrant
|
|
299
|
+
* unavailable, we log and move on.
|
|
300
|
+
*/
|
|
301
|
+
export async function dropLegacySkillsCollection(): Promise<void> {
|
|
302
|
+
try {
|
|
303
|
+
const client = getClient();
|
|
304
|
+
const exists = await client.collectionExists("memory_v2_skills");
|
|
305
|
+
if (!exists.exists) return;
|
|
306
|
+
await client.deleteCollection("memory_v2_skills");
|
|
307
|
+
log.info("Deleted legacy memory_v2_skills Qdrant collection");
|
|
308
|
+
} catch (err) {
|
|
309
|
+
log.warn(
|
|
310
|
+
{ err },
|
|
311
|
+
"Failed to drop legacy memory_v2_skills collection — non-fatal",
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
218
316
|
/**
|
|
219
317
|
* Run separate dense and sparse queries against the concept-page collection
|
|
220
318
|
* and return per-channel scores per slug. Callers fuse these — typically via
|
|
@@ -236,6 +334,7 @@ export async function hybridQueryConceptPages(
|
|
|
236
334
|
sparse: SparseEmbedding,
|
|
237
335
|
limit: number,
|
|
238
336
|
restrictToSlugs?: readonly string[],
|
|
337
|
+
options?: { skipSparse?: boolean },
|
|
239
338
|
): Promise<ConceptPageQueryResult[]> {
|
|
240
339
|
if (restrictToSlugs && restrictToSlugs.length === 0) {
|
|
241
340
|
// An empty restriction means "no candidates"; skip the round-trip.
|
|
@@ -249,6 +348,13 @@ export async function hybridQueryConceptPages(
|
|
|
249
348
|
? { must: [{ key: "slug", match: { any: [...restrictToSlugs] } }] }
|
|
250
349
|
: undefined;
|
|
251
350
|
|
|
351
|
+
// When the caller weighted sparse to zero, skip the round-trip entirely.
|
|
352
|
+
// The downstream fuser (`fuseHit` in `sim.ts`) already treats a missing
|
|
353
|
+
// sparse score as a 0 contribution, so omitting the query is a pure
|
|
354
|
+
// optimization — and it's also the kill switch operators use to dodge a
|
|
355
|
+
// Qdrant 1.13.x sparse-index crash that we've reproduced in the wild.
|
|
356
|
+
const skipSparse = options?.skipSparse ?? false;
|
|
357
|
+
|
|
252
358
|
const denseQuery = () =>
|
|
253
359
|
client.query(MEMORY_V2_COLLECTION, {
|
|
254
360
|
query: dense,
|
|
@@ -267,7 +373,14 @@ export async function hybridQueryConceptPages(
|
|
|
267
373
|
});
|
|
268
374
|
|
|
269
375
|
// Run both queries concurrently — they hit independent named vectors.
|
|
270
|
-
|
|
376
|
+
// When sparse is gated off we still resolve a Promise so the destructuring
|
|
377
|
+
// below stays uniform; the empty `points: []` matches the shape of a
|
|
378
|
+
// no-hit Qdrant response.
|
|
379
|
+
const emptyResult = {
|
|
380
|
+
points: [] as Array<{ payload?: unknown; score?: number }>,
|
|
381
|
+
};
|
|
382
|
+
const runQueries = async () =>
|
|
383
|
+
Promise.all([denseQuery(), skipSparse ? emptyResult : sparseQuery()]);
|
|
271
384
|
|
|
272
385
|
let denseResults;
|
|
273
386
|
let sparseResults;
|
|
@@ -305,6 +418,89 @@ export async function hybridQueryConceptPages(
|
|
|
305
418
|
return Array.from(merged.values());
|
|
306
419
|
}
|
|
307
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Page through the v2 concept-page collection and return up to `maxSamples`
|
|
423
|
+
* stored dense vectors. Used by the anisotropy-fit pipeline to compute a
|
|
424
|
+
* corpus mean + top-k principal components without re-embedding every page.
|
|
425
|
+
*
|
|
426
|
+
* Sparse vectors are skipped — anisotropy is a dense-embedding phenomenon, and
|
|
427
|
+
* pulling the sparse side would just inflate the response. Payload is also
|
|
428
|
+
* skipped because the fit doesn't need slug identity.
|
|
429
|
+
*
|
|
430
|
+
* Returns an empty array when the collection is empty or missing. Caller
|
|
431
|
+
* decides what to do (typically: surface a "no vectors to fit" error).
|
|
432
|
+
*/
|
|
433
|
+
export async function sampleConceptPageDenseVectors(
|
|
434
|
+
maxSamples: number,
|
|
435
|
+
): Promise<number[][]> {
|
|
436
|
+
if (maxSamples <= 0) return [];
|
|
437
|
+
await ensureConceptPageCollection();
|
|
438
|
+
|
|
439
|
+
const client = getClient();
|
|
440
|
+
const out: number[][] = [];
|
|
441
|
+
let offset: string | number | undefined = undefined;
|
|
442
|
+
// Same pagination guard pattern as the rest of the file — bounds the loop
|
|
443
|
+
// even if Qdrant somehow keeps handing back a non-null offset.
|
|
444
|
+
const maxIterations = 10_000;
|
|
445
|
+
const batchSize = Math.min(256, maxSamples);
|
|
446
|
+
|
|
447
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
448
|
+
if (out.length >= maxSamples) break;
|
|
449
|
+
const remaining = maxSamples - out.length;
|
|
450
|
+
let result;
|
|
451
|
+
try {
|
|
452
|
+
result = await client.scroll(MEMORY_V2_COLLECTION, {
|
|
453
|
+
limit: Math.min(batchSize, remaining),
|
|
454
|
+
with_payload: false,
|
|
455
|
+
// Fetch only the dense named vector — sparse is irrelevant for
|
|
456
|
+
// anisotropy correction.
|
|
457
|
+
with_vector: ["dense"],
|
|
458
|
+
...(offset !== undefined ? { offset } : {}),
|
|
459
|
+
});
|
|
460
|
+
} catch (err) {
|
|
461
|
+
if (isCollectionMissing(err)) {
|
|
462
|
+
_collectionReady = false;
|
|
463
|
+
return out;
|
|
464
|
+
}
|
|
465
|
+
throw err;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
for (const point of result.points) {
|
|
469
|
+
const v = extractDenseVector(point.vector);
|
|
470
|
+
if (v) out.push(v);
|
|
471
|
+
if (out.length >= maxSamples) break;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const next = result.next_page_offset;
|
|
475
|
+
if (next == null) break;
|
|
476
|
+
offset = typeof next === "string" ? next : (next as number);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return out;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Pull the `dense` named-vector payload out of a Qdrant point. Defensively
|
|
484
|
+
* handles both the named-vector shape (`{ dense: [...] }`) and the legacy
|
|
485
|
+
* unnamed-vector shape (`number[]`) so older collection layouts don't trip
|
|
486
|
+
* the sampler. Returns `null` for shapes we don't recognise.
|
|
487
|
+
*/
|
|
488
|
+
function extractDenseVector(vector: unknown): number[] | null {
|
|
489
|
+
if (Array.isArray(vector)) {
|
|
490
|
+
if (vector.every((n) => typeof n === "number")) {
|
|
491
|
+
return vector as number[];
|
|
492
|
+
}
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
if (vector && typeof vector === "object") {
|
|
496
|
+
const dense = (vector as { dense?: unknown }).dense;
|
|
497
|
+
if (Array.isArray(dense) && dense.every((n) => typeof n === "number")) {
|
|
498
|
+
return dense as number[];
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
|
|
308
504
|
/**
|
|
309
505
|
* Detect "collection not found" errors so callers can reset readiness and
|
|
310
506
|
* retry after an external deletion (e.g. workspace reset).
|
|
@@ -339,4 +535,5 @@ function pointIdForSlug(slug: string): string {
|
|
|
339
535
|
export function _resetMemoryV2QdrantForTests(): void {
|
|
340
536
|
_client = null;
|
|
341
537
|
_collectionReady = false;
|
|
538
|
+
_collectionReadyPromise = null;
|
|
342
539
|
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/** Memory v2 cross-encoder rerank — `(query, page-preview)` pairs scored by a local model. */
|
|
2
|
+
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
|
|
5
|
+
import type { AssistantConfig } from "../../config/types.js";
|
|
6
|
+
import { getLogger } from "../../util/logger.js";
|
|
7
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
8
|
+
import { getOrCreateRerankBackend } from "../rerank-local.js";
|
|
9
|
+
import { readPage } from "./page-store.js";
|
|
10
|
+
|
|
11
|
+
const log = getLogger("memory-v2-reranker");
|
|
12
|
+
|
|
13
|
+
// ~512-token model context for bge-reranker-base; cap input to bound payload.
|
|
14
|
+
const PASSAGE_CHAR_CAP = 240;
|
|
15
|
+
|
|
16
|
+
interface CacheEntry {
|
|
17
|
+
scores: Map<string, number>;
|
|
18
|
+
ts: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const CACHE_TTL_MS = 2 * 60 * 1000;
|
|
22
|
+
const CACHE_MAX_ENTRIES = 64;
|
|
23
|
+
const cache = new Map<string, CacheEntry>();
|
|
24
|
+
|
|
25
|
+
function cacheKey(query: string, slugs: readonly string[]): string {
|
|
26
|
+
const sorted = [...slugs].sort().join("\0");
|
|
27
|
+
return createHash("sha256").update(`${query}\0${sorted}`).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function evictExpired(now: number): void {
|
|
31
|
+
for (const [k, v] of cache) {
|
|
32
|
+
if (now - v.ts > CACHE_TTL_MS) cache.delete(k);
|
|
33
|
+
}
|
|
34
|
+
if (cache.size > CACHE_MAX_ENTRIES) {
|
|
35
|
+
const toDrop = cache.size - CACHE_MAX_ENTRIES;
|
|
36
|
+
let i = 0;
|
|
37
|
+
for (const k of cache.keys()) {
|
|
38
|
+
if (i++ >= toDrop) break;
|
|
39
|
+
cache.delete(k);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildPassage(slug: string, body: string): string {
|
|
45
|
+
const trimmed = body.replace(/^\s+/, "");
|
|
46
|
+
const blank = trimmed.search(/\n\s*\n/);
|
|
47
|
+
const para = blank === -1 ? trimmed : trimmed.slice(0, blank);
|
|
48
|
+
const stripped = para.replace(/^#+\s.*\n/, "").trim();
|
|
49
|
+
const compact = stripped.replace(/\s+/g, " ").slice(0, PASSAGE_CHAR_CAP);
|
|
50
|
+
return `${slug}\n${compact}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Run the cross-encoder over each candidate's first-paragraph preview for
|
|
55
|
+
* one or more queries against the same candidate set. Returns one
|
|
56
|
+
* `Map<slug, score>` per query, in the same order as the `queries` array.
|
|
57
|
+
*
|
|
58
|
+
* Multi-query batching: the user-channel and assistant-channel queries share
|
|
59
|
+
* a candidate set per turn, so scoring them in a single tokenizer +
|
|
60
|
+
* forward-pass call avoids the ONNX-invocation overhead of two serialised
|
|
61
|
+
* worker round-trips. Cache hits short-circuit per-query independently —
|
|
62
|
+
* a whitespace-only query yields an empty Map without hitting the backend.
|
|
63
|
+
*
|
|
64
|
+
* Failures (worker down, page read errors) yield empty Maps so callers can
|
|
65
|
+
* fall back to pure fused scores. Per-batch normalisation and boost math
|
|
66
|
+
* live in `computeOwnActivation`.
|
|
67
|
+
*/
|
|
68
|
+
export async function rerankCandidates(
|
|
69
|
+
queries: readonly string[],
|
|
70
|
+
candidates: readonly string[],
|
|
71
|
+
config: AssistantConfig,
|
|
72
|
+
): Promise<Array<Map<string, number>>> {
|
|
73
|
+
if (queries.length === 0) return [];
|
|
74
|
+
if (candidates.length === 0) return queries.map(() => new Map());
|
|
75
|
+
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
evictExpired(now);
|
|
78
|
+
|
|
79
|
+
const results: Array<Map<string, number> | null> = queries.map(() => null);
|
|
80
|
+
const uncachedIndices: number[] = [];
|
|
81
|
+
for (let i = 0; i < queries.length; i++) {
|
|
82
|
+
const q = queries[i];
|
|
83
|
+
if (q.trim().length === 0) {
|
|
84
|
+
results[i] = new Map();
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const key = cacheKey(q, candidates);
|
|
88
|
+
const cached = cache.get(key);
|
|
89
|
+
if (cached) {
|
|
90
|
+
// Refresh insertion order so frequently-hit entries survive eviction.
|
|
91
|
+
cache.delete(key);
|
|
92
|
+
cache.set(key, { ...cached, ts: now });
|
|
93
|
+
results[i] = new Map(cached.scores);
|
|
94
|
+
} else {
|
|
95
|
+
uncachedIndices.push(i);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const finalize = (): Array<Map<string, number>> =>
|
|
100
|
+
results.map((r) => r ?? new Map());
|
|
101
|
+
|
|
102
|
+
if (uncachedIndices.length === 0) return finalize();
|
|
103
|
+
|
|
104
|
+
const workspaceDir = getWorkspaceDir();
|
|
105
|
+
const pages = await Promise.all(
|
|
106
|
+
candidates.map((slug) =>
|
|
107
|
+
readPage(workspaceDir, slug).catch((err) => {
|
|
108
|
+
log.debug({ err, slug }, "Reranker skipping page that failed to load");
|
|
109
|
+
return null;
|
|
110
|
+
}),
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
const passages: string[] = [];
|
|
114
|
+
const slugsForPassages: string[] = [];
|
|
115
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
116
|
+
const page = pages[i];
|
|
117
|
+
if (!page) continue;
|
|
118
|
+
passages.push(buildPassage(candidates[i], page.body));
|
|
119
|
+
slugsForPassages.push(candidates[i]);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (passages.length === 0) {
|
|
123
|
+
for (const i of uncachedIndices) results[i] = new Map();
|
|
124
|
+
return finalize();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// One tokenizer + ONNX forward pass over every uncached query × passage
|
|
128
|
+
// pair. Pairs are laid out query-major: queries[uncached[0]] × passages,
|
|
129
|
+
// then queries[uncached[1]] × passages, etc.
|
|
130
|
+
const batchQueries: string[] = [];
|
|
131
|
+
const batchPassages: string[] = [];
|
|
132
|
+
for (const qi of uncachedIndices) {
|
|
133
|
+
const q = queries[qi];
|
|
134
|
+
for (const p of passages) {
|
|
135
|
+
batchQueries.push(q);
|
|
136
|
+
batchPassages.push(p);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const { model, dtype } = config.memory.v2.rerank;
|
|
141
|
+
let scores: number[];
|
|
142
|
+
try {
|
|
143
|
+
const backend = getOrCreateRerankBackend(model, dtype);
|
|
144
|
+
scores = await backend.score(batchQueries, batchPassages);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
log.warn(
|
|
147
|
+
{ err, model, n: batchPassages.length },
|
|
148
|
+
"Rerank backend failed; falling back to pure fused scores",
|
|
149
|
+
);
|
|
150
|
+
for (const i of uncachedIndices) results[i] = new Map();
|
|
151
|
+
return finalize();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (let j = 0; j < uncachedIndices.length; j++) {
|
|
155
|
+
const qi = uncachedIndices[j];
|
|
156
|
+
const offset = j * passages.length;
|
|
157
|
+
const result = new Map<string, number>();
|
|
158
|
+
for (let i = 0; i < slugsForPassages.length; i++) {
|
|
159
|
+
const s = scores[offset + i];
|
|
160
|
+
if (typeof s !== "number" || Number.isNaN(s)) continue;
|
|
161
|
+
// sigmoid output should already be in [0, 1]; clamp defensively.
|
|
162
|
+
result.set(slugsForPassages[i], Math.max(0, Math.min(1, s)));
|
|
163
|
+
}
|
|
164
|
+
results[qi] = result;
|
|
165
|
+
cache.set(cacheKey(queries[qi], candidates), {
|
|
166
|
+
scores: new Map(result),
|
|
167
|
+
ts: now,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return finalize();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** @internal Test-only: clear the LRU cache. */
|
|
175
|
+
export function _resetRerankCacheForTests(): void {
|
|
176
|
+
cache.clear();
|
|
177
|
+
}
|
package/src/memory/v2/sim.ts
CHANGED
|
@@ -26,13 +26,11 @@
|
|
|
26
26
|
// only as a per-turn ordering signal, not compared across turns.
|
|
27
27
|
|
|
28
28
|
import type { AssistantConfig } from "../../config/types.js";
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
generateSparseEmbedding,
|
|
32
|
-
} from "../embedding-backend.js";
|
|
29
|
+
import { applyCorrectionIfCalibrated } from "../anisotropy.js";
|
|
30
|
+
import { embedWithBackend } from "../embedding-backend.js";
|
|
33
31
|
import { clampUnitInterval } from "../validation.js";
|
|
34
32
|
import { hybridQueryConceptPages } from "./qdrant.js";
|
|
35
|
-
import {
|
|
33
|
+
import { generateBm25QueryEmbedding } from "./sparse-bm25.js";
|
|
36
34
|
|
|
37
35
|
/**
|
|
38
36
|
* Clamp a value into the closed unit interval [0, 1]. Re-exported under the
|
|
@@ -40,6 +38,79 @@ import { hybridQuerySkills } from "./skill-qdrant.js";
|
|
|
40
38
|
*/
|
|
41
39
|
export const clamp01 = clampUnitInterval;
|
|
42
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Built-in defaults for adaptive sparse weighting. Live here (not in the
|
|
43
|
+
* config schema) so operators don't see two new knobs in their config until
|
|
44
|
+
* they actually want to tune them.
|
|
45
|
+
*
|
|
46
|
+
* Below `MIN_SPREAD`, the sparse channel is treated as no-signal (its scores
|
|
47
|
+
* are uniform across the candidate set, so it can't rank anything) and the
|
|
48
|
+
* sparse weight collapses to 0. At or above `FULL_SPREAD`, sparse weight
|
|
49
|
+
* stays at its configured value. Linear interpolation between.
|
|
50
|
+
*/
|
|
51
|
+
const ADAPTIVE_SPARSE_MIN_SPREAD = 0.2;
|
|
52
|
+
const ADAPTIVE_SPARSE_FULL_SPREAD = 0.5;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Per-query effective dense + sparse weights, derived from the configured
|
|
56
|
+
* base weights and the spread of normalized sparse scores across the hit
|
|
57
|
+
* set. When the sparse channel can't discriminate (low spread or fewer
|
|
58
|
+
* than two sparse-bearing candidates), its weight collapses and dense
|
|
59
|
+
* weight is boosted to compensate so `dense + sparse` still equals
|
|
60
|
+
* `baseDense + baseSparse` and `fused` stays interpretable as a [0, 1]
|
|
61
|
+
* similarity.
|
|
62
|
+
*
|
|
63
|
+
* Pure function — exported so the diagnostic surface in
|
|
64
|
+
* `memory-v2-routes.explain-similarity` can show the effective weights and
|
|
65
|
+
* the measured spread alongside per-channel score statistics.
|
|
66
|
+
*/
|
|
67
|
+
export function effectiveWeights(
|
|
68
|
+
hits: ReadonlyArray<{ sparseScore?: number }>,
|
|
69
|
+
maxSparse: number,
|
|
70
|
+
baseDense: number,
|
|
71
|
+
baseSparse: number,
|
|
72
|
+
config: AssistantConfig,
|
|
73
|
+
): { dense: number; sparse: number; spread: number } {
|
|
74
|
+
// Short-circuit when the channel is already disabled or unscored. Returning
|
|
75
|
+
// base weights here keeps `fused` numerically identical to today's output
|
|
76
|
+
// for the no-sparse-signal cases the existing tests assume.
|
|
77
|
+
if (baseSparse === 0 || maxSparse === 0) {
|
|
78
|
+
return { dense: baseDense, sparse: baseSparse, spread: 0 };
|
|
79
|
+
}
|
|
80
|
+
let min = Infinity;
|
|
81
|
+
let max = -Infinity;
|
|
82
|
+
let count = 0;
|
|
83
|
+
for (const h of hits) {
|
|
84
|
+
if (h.sparseScore === undefined) continue;
|
|
85
|
+
const norm = h.sparseScore / maxSparse;
|
|
86
|
+
if (norm < min) min = norm;
|
|
87
|
+
if (norm > max) max = norm;
|
|
88
|
+
count++;
|
|
89
|
+
}
|
|
90
|
+
// With < 2 sparse-bearing hits the spread is undefined — fall back to base
|
|
91
|
+
// weights so single-hit retrievals still surface their sparse contribution
|
|
92
|
+
// (and the existing fusion-math tests stay green).
|
|
93
|
+
if (count < 2) {
|
|
94
|
+
return { dense: baseDense, sparse: baseSparse, spread: 0 };
|
|
95
|
+
}
|
|
96
|
+
const spread = max - min;
|
|
97
|
+
|
|
98
|
+
const minSpread =
|
|
99
|
+
config.memory.v2.min_sparse_spread ?? ADAPTIVE_SPARSE_MIN_SPREAD;
|
|
100
|
+
const fullSpread =
|
|
101
|
+
config.memory.v2.full_sparse_spread ?? ADAPTIVE_SPARSE_FULL_SPREAD;
|
|
102
|
+
// Degenerate config (full <= min): no interpolation range. Don't try to
|
|
103
|
+
// adapt; trust the operator's base weights and report the measured spread
|
|
104
|
+
// for diagnostics.
|
|
105
|
+
if (fullSpread <= minSpread) {
|
|
106
|
+
return { dense: baseDense, sparse: baseSparse, spread };
|
|
107
|
+
}
|
|
108
|
+
const factor = clamp01((spread - minSpread) / (fullSpread - minSpread));
|
|
109
|
+
const sparse = baseSparse * factor;
|
|
110
|
+
const dense = baseDense + (baseSparse - sparse);
|
|
111
|
+
return { dense, sparse, spread };
|
|
112
|
+
}
|
|
113
|
+
|
|
43
114
|
/**
|
|
44
115
|
* Compute hybrid (dense + sparse) similarity scores between a query text and
|
|
45
116
|
* a fixed set of candidate concept-page slugs.
|
|
@@ -63,23 +134,41 @@ export const clamp01 = clampUnitInterval;
|
|
|
63
134
|
* Edge cases:
|
|
64
135
|
* - Empty `candidateSlugs` → returns an empty map without touching Qdrant
|
|
65
136
|
* or the embedding backend.
|
|
66
|
-
* - Empty
|
|
67
|
-
*
|
|
137
|
+
* - Empty / whitespace-only `text` → returns an empty map without touching
|
|
138
|
+
* Qdrant or the embedding backend. The Gemini embedding API rejects empty
|
|
139
|
+
* content with HTTP 400, and short-circuiting here prevents the failure
|
|
140
|
+
* from cascading through `Promise.all` in `computeOwnActivation` (e.g.
|
|
141
|
+
* turn 1 has no prior assistant message, so its `simBatch` channel is
|
|
142
|
+
* called with `""`). Treating the channel's contribution as 0 is the
|
|
143
|
+
* same outcome a no-hit query would produce.
|
|
68
144
|
*/
|
|
69
145
|
export async function simBatch(
|
|
70
146
|
text: string,
|
|
71
147
|
candidateSlugs: readonly string[],
|
|
72
148
|
config: AssistantConfig,
|
|
149
|
+
options?: { signal?: AbortSignal },
|
|
73
150
|
): Promise<Map<string, number>> {
|
|
74
151
|
if (candidateSlugs.length === 0) {
|
|
75
152
|
return new Map();
|
|
76
153
|
}
|
|
154
|
+
if (text.trim().length === 0) {
|
|
155
|
+
return new Map();
|
|
156
|
+
}
|
|
77
157
|
|
|
78
|
-
// Sparse uses the
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
158
|
+
// Sparse uses BM25: the query side encodes binary occurrences per token,
|
|
159
|
+
// and the stored doc vectors carry the IDF · TF-saturated weights — Qdrant
|
|
160
|
+
// dot product then yields the BM25 score directly.
|
|
161
|
+
throwIfAborted(options?.signal);
|
|
162
|
+
const denseResult = await embedWithBackend(config, [text], {
|
|
163
|
+
signal: options?.signal,
|
|
164
|
+
});
|
|
165
|
+
const denseVector = await applyCorrectionIfCalibrated(
|
|
166
|
+
denseResult.vectors[0],
|
|
167
|
+
denseResult.provider,
|
|
168
|
+
denseResult.model,
|
|
169
|
+
);
|
|
170
|
+
throwIfAborted(options?.signal);
|
|
171
|
+
const sparseVector = generateBm25QueryEmbedding(text);
|
|
83
172
|
|
|
84
173
|
const hits = await hybridQueryConceptPages(
|
|
85
174
|
denseVector,
|
|
@@ -93,81 +182,28 @@ export async function simBatch(
|
|
|
93
182
|
}
|
|
94
183
|
|
|
95
184
|
const maxSparse = computeMaxSparse(hits);
|
|
96
|
-
const { dense_weight:
|
|
185
|
+
const { dense_weight: baseDense, sparse_weight: baseSparse } =
|
|
97
186
|
config.memory.v2;
|
|
187
|
+
const { dense: denseWeight, sparse: sparseWeight } = effectiveWeights(
|
|
188
|
+
hits,
|
|
189
|
+
maxSparse,
|
|
190
|
+
baseDense,
|
|
191
|
+
baseSparse,
|
|
192
|
+
config,
|
|
193
|
+
);
|
|
98
194
|
|
|
99
195
|
const scores = new Map<string, number>();
|
|
100
196
|
for (const hit of hits) {
|
|
101
197
|
scores.set(hit.slug, fuseHit(hit, maxSparse, denseWeight, sparseWeight));
|
|
102
198
|
}
|
|
199
|
+
|
|
103
200
|
return scores;
|
|
104
201
|
}
|
|
105
202
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
* dedicated `memory_v2_skills` Qdrant collection via `hybridQuerySkills`.
|
|
110
|
-
*
|
|
111
|
-
* Differences from `simBatch`:
|
|
112
|
-
* - Keys are skill `id` values (not concept-page slugs).
|
|
113
|
-
* - Restricts the query to the caller's candidate ids server-side via
|
|
114
|
-
* `hybridQuerySkills`'s `restrictToIds` parameter. Without this, when the
|
|
115
|
-
* skills collection has more skills than `ids.length`, Qdrant would
|
|
116
|
-
* return its global top-K and candidate ids absent from that top-K would
|
|
117
|
-
* silently score 0 — corrupting the activation calculation.
|
|
118
|
-
*
|
|
119
|
-
* Returns a `Map<id, score>` of fused scores in [0, 1]. Ids that did not hit
|
|
120
|
-
* either channel are absent from the map.
|
|
121
|
-
*
|
|
122
|
-
* Edge cases:
|
|
123
|
-
* - Empty `ids` → returns an empty map without touching Qdrant or the
|
|
124
|
-
* embedding backend.
|
|
125
|
-
* - Empty query text → still queries (dense may still hit), and the sparse
|
|
126
|
-
* contribution is zero.
|
|
127
|
-
*/
|
|
128
|
-
export async function simSkillBatch(
|
|
129
|
-
text: string,
|
|
130
|
-
ids: readonly string[],
|
|
131
|
-
config: AssistantConfig,
|
|
132
|
-
): Promise<Map<string, number>> {
|
|
133
|
-
if (ids.length === 0) {
|
|
134
|
-
return new Map();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const denseResult = await embedWithBackend(config, [text]);
|
|
138
|
-
const denseVector = denseResult.vectors[0];
|
|
139
|
-
const sparseVector = generateSparseEmbedding(text);
|
|
140
|
-
|
|
141
|
-
const hits = await hybridQuerySkills(
|
|
142
|
-
denseVector,
|
|
143
|
-
sparseVector,
|
|
144
|
-
ids.length,
|
|
145
|
-
ids,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
if (hits.length === 0) {
|
|
149
|
-
return new Map();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Defensive post-filter — `hybridQuerySkills` restricts server-side, so
|
|
153
|
-
// every hit should already be in `ids`, but keep this guard so a buggy
|
|
154
|
-
// payload (e.g. a missing/typoed id index) can't silently inject
|
|
155
|
-
// out-of-set ids into the score map.
|
|
156
|
-
const idSet = new Set(ids);
|
|
157
|
-
const filtered = hits.filter((h) => idSet.has(h.id));
|
|
158
|
-
if (filtered.length === 0) {
|
|
159
|
-
return new Map();
|
|
203
|
+
function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
204
|
+
if (signal?.aborted) {
|
|
205
|
+
throw new DOMException("Aborted", "AbortError");
|
|
160
206
|
}
|
|
161
|
-
|
|
162
|
-
const maxSparse = computeMaxSparse(filtered);
|
|
163
|
-
const { dense_weight: denseWeight, sparse_weight: sparseWeight } =
|
|
164
|
-
config.memory.v2;
|
|
165
|
-
|
|
166
|
-
const scores = new Map<string, number>();
|
|
167
|
-
for (const hit of filtered) {
|
|
168
|
-
scores.set(hit.id, fuseHit(hit, maxSparse, denseWeight, sparseWeight));
|
|
169
|
-
}
|
|
170
|
-
return scores;
|
|
171
207
|
}
|
|
172
208
|
|
|
173
209
|
/**
|
|
@@ -2,9 +2,10 @@ import { getConfig } from "../../config/loader.js";
|
|
|
2
2
|
import type { SkillCapabilityInput } from "../../skills/skill-memory.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Render the prose-style capability statement embedded into the
|
|
6
|
-
* `
|
|
7
|
-
* `### Skills You Can Use`. Capped at 500 chars to
|
|
5
|
+
* Render the prose-style capability statement embedded into the unified
|
|
6
|
+
* `memory_v2_concept_pages` Qdrant collection (under the `skills/<id>` slug
|
|
7
|
+
* prefix) and rendered in `### Skills You Can Use`. Capped at 500 chars to
|
|
8
|
+
* match v1's behavior.
|
|
8
9
|
*/
|
|
9
10
|
export function buildSkillContent(input: SkillCapabilityInput): string {
|
|
10
11
|
let content = `The "${input.displayName}" skill (${input.id}) is available. ${input.description}.`;
|