@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
|
@@ -68,17 +68,6 @@ const state = {
|
|
|
68
68
|
points: Array<{ score?: number; payload: Record<string, unknown> }>;
|
|
69
69
|
}>,
|
|
70
70
|
},
|
|
71
|
-
// Separate response queue for the dedicated `memory_v2_skills` collection
|
|
72
|
-
// so a test asserting on skill activation does not have to interleave
|
|
73
|
-
// responses with concept-page queries.
|
|
74
|
-
skillQueryResponses: {
|
|
75
|
-
dense: [] as Array<{
|
|
76
|
-
points: Array<{ score?: number; payload: Record<string, unknown> }>;
|
|
77
|
-
}>,
|
|
78
|
-
sparse: [] as Array<{
|
|
79
|
-
points: Array<{ score?: number; payload: Record<string, unknown> }>;
|
|
80
|
-
}>,
|
|
81
|
-
},
|
|
82
71
|
queryCalls: [] as Array<{
|
|
83
72
|
collection: string;
|
|
84
73
|
using: string;
|
|
@@ -126,11 +115,7 @@ class MockQdrantClient {
|
|
|
126
115
|
filter: params.filter,
|
|
127
116
|
});
|
|
128
117
|
const channel = params.using as "dense" | "sparse";
|
|
129
|
-
|
|
130
|
-
name === "memory_v2_skills"
|
|
131
|
-
? state.skillQueryResponses[channel]
|
|
132
|
-
: state.queryResponses[channel];
|
|
133
|
-
return queue.shift() ?? { points: [] };
|
|
118
|
+
return state.queryResponses[channel].shift() ?? { points: [] };
|
|
134
119
|
}
|
|
135
120
|
}
|
|
136
121
|
|
|
@@ -138,6 +123,37 @@ mock.module("@qdrant/js-client-rest", () => ({
|
|
|
138
123
|
QdrantClient: MockQdrantClient,
|
|
139
124
|
}));
|
|
140
125
|
|
|
126
|
+
// Reranker mock — keeps the activation tests hermetic when rerank.enabled is
|
|
127
|
+
// flipped on by an integration case. Tests stage `rerankState.scores` to
|
|
128
|
+
// program the boost outcome. The activation pipeline now passes both the
|
|
129
|
+
// user-channel and assistant-channel queries into a single rerank call, so
|
|
130
|
+
// `rerankState.calls` records the full `queries` array per invocation.
|
|
131
|
+
const rerankState = {
|
|
132
|
+
scores: null as Map<string, number> | null,
|
|
133
|
+
calls: [] as Array<{ queries: string[]; candidates: string[] }>,
|
|
134
|
+
};
|
|
135
|
+
mock.module("../reranker.js", () => ({
|
|
136
|
+
rerankCandidates: async (
|
|
137
|
+
queries: readonly string[],
|
|
138
|
+
candidates: readonly string[],
|
|
139
|
+
): Promise<Array<Map<string, number>>> => {
|
|
140
|
+
rerankState.calls.push({
|
|
141
|
+
queries: [...queries],
|
|
142
|
+
candidates: [...candidates],
|
|
143
|
+
});
|
|
144
|
+
return queries.map(() => {
|
|
145
|
+
if (rerankState.scores === null) return new Map();
|
|
146
|
+
const out = new Map<string, number>();
|
|
147
|
+
for (const slug of candidates) {
|
|
148
|
+
const v = rerankState.scores.get(slug);
|
|
149
|
+
if (v !== undefined) out.set(slug, v);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
_resetRerankCacheForTests: () => {},
|
|
155
|
+
}));
|
|
156
|
+
|
|
141
157
|
// Static `import type` is fine — types erase, so they don't run module-init
|
|
142
158
|
// code that would race the mocks above.
|
|
143
159
|
import type { EdgeIndex } from "../edge-index.js";
|
|
@@ -145,16 +161,11 @@ import type { ActivationState } from "../types.js";
|
|
|
145
161
|
|
|
146
162
|
const {
|
|
147
163
|
computeOwnActivation,
|
|
148
|
-
computeSkillActivation,
|
|
149
164
|
selectCandidates,
|
|
150
165
|
selectInjections,
|
|
151
|
-
selectSkillCandidates,
|
|
152
|
-
selectSkillInjections,
|
|
153
166
|
spreadActivation,
|
|
154
167
|
} = await import("../activation.js");
|
|
155
168
|
const { _resetMemoryV2QdrantForTests } = await import("../qdrant.js");
|
|
156
|
-
const { _resetMemoryV2SkillQdrantForTests } =
|
|
157
|
-
await import("../skill-qdrant.js");
|
|
158
169
|
|
|
159
170
|
// ---------------------------------------------------------------------------
|
|
160
171
|
// Helpers
|
|
@@ -167,16 +178,15 @@ function resetState(): void {
|
|
|
167
178
|
state.sparseReturn = { indices: [1, 2, 3], values: [0.5, 0.5, 0.5] };
|
|
168
179
|
state.queryResponses.dense.length = 0;
|
|
169
180
|
state.queryResponses.sparse.length = 0;
|
|
170
|
-
state.skillQueryResponses.dense.length = 0;
|
|
171
|
-
state.skillQueryResponses.sparse.length = 0;
|
|
172
181
|
state.queryCalls.length = 0;
|
|
182
|
+
rerankState.scores = null;
|
|
183
|
+
rerankState.calls.length = 0;
|
|
173
184
|
// Bun's `mock.module` persists across files in the same process, so the
|
|
174
|
-
// qdrant
|
|
175
|
-
// instance from a sibling test file (e.g. sim.test.ts). Resetting
|
|
185
|
+
// qdrant module's `_client` singleton may already hold a MockQdrantClient
|
|
186
|
+
// instance from a sibling test file (e.g. sim.test.ts). Resetting the
|
|
176
187
|
// cache AND any latched readiness forces a fresh `new QdrantClient()` —
|
|
177
188
|
// which under our mock above resolves to *this* file's MockQdrantClient.
|
|
178
189
|
_resetMemoryV2QdrantForTests();
|
|
179
|
-
_resetMemoryV2SkillQdrantForTests();
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
/**
|
|
@@ -193,6 +203,7 @@ function makeConfig(
|
|
|
193
203
|
epsilon: number;
|
|
194
204
|
dense_weight: number;
|
|
195
205
|
sparse_weight: number;
|
|
206
|
+
ann_candidate_limit: number | null;
|
|
196
207
|
}> = {},
|
|
197
208
|
): AssistantConfig {
|
|
198
209
|
return {
|
|
@@ -205,6 +216,7 @@ function makeConfig(
|
|
|
205
216
|
epsilon: 0.01,
|
|
206
217
|
dense_weight: 1.0,
|
|
207
218
|
sparse_weight: 0.0,
|
|
219
|
+
ann_candidate_limit: null,
|
|
208
220
|
...overrides,
|
|
209
221
|
},
|
|
210
222
|
},
|
|
@@ -342,7 +354,13 @@ describe("selectCandidates", () => {
|
|
|
342
354
|
expect(out.fromAnn).toEqual(new Set(["alice-vscode", "delta-recipe"]));
|
|
343
355
|
});
|
|
344
356
|
|
|
345
|
-
test("ANN
|
|
357
|
+
test("ANN candidate query honors `config.memory.v2.ann_candidate_limit` and runs without slug restriction", async () => {
|
|
358
|
+
// Default `ann_candidate_limit: null` → unlimited, so the unlimited
|
|
359
|
+
// sentinel (1_000_000 — a Qdrant-safe stand-in for "every page") is
|
|
360
|
+
// passed to both channels. We don't pin to `MAX_SAFE_INTEGER` here:
|
|
361
|
+
// Qdrant's sparse `SearchContext` pre-allocates `limit * 16` bytes,
|
|
362
|
+
// and `MAX_SAFE_INTEGER` triggers a ~144 PB alloc that SIGABRTs the
|
|
363
|
+
// Qdrant process — so the constant deliberately undercuts it.
|
|
346
364
|
stageHybridResponse([{ slug: "alpha", denseScore: 0.5, sparseScore: 1 }]);
|
|
347
365
|
await selectCandidates({
|
|
348
366
|
priorState: null,
|
|
@@ -351,10 +369,25 @@ describe("selectCandidates", () => {
|
|
|
351
369
|
nowText: "",
|
|
352
370
|
config: makeConfig(),
|
|
353
371
|
});
|
|
354
|
-
// Both channels (dense + sparse) ran with limit=50 and no filter.
|
|
355
372
|
expect(state.queryCalls).toHaveLength(2);
|
|
356
373
|
for (const call of state.queryCalls) {
|
|
357
|
-
expect(call.limit).toBe(
|
|
374
|
+
expect(call.limit).toBe(1_000_000);
|
|
375
|
+
expect(call.filter).toBeUndefined();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Explicit override flows through both channels verbatim.
|
|
379
|
+
state.queryCalls.length = 0;
|
|
380
|
+
stageHybridResponse([{ slug: "beta", denseScore: 0.5, sparseScore: 1 }]);
|
|
381
|
+
await selectCandidates({
|
|
382
|
+
priorState: null,
|
|
383
|
+
userText: "hello",
|
|
384
|
+
assistantText: "",
|
|
385
|
+
nowText: "",
|
|
386
|
+
config: makeConfig({ ann_candidate_limit: 25 }),
|
|
387
|
+
});
|
|
388
|
+
expect(state.queryCalls).toHaveLength(2);
|
|
389
|
+
for (const call of state.queryCalls) {
|
|
390
|
+
expect(call.limit).toBe(25);
|
|
358
391
|
expect(call.filter).toBeUndefined();
|
|
359
392
|
}
|
|
360
393
|
});
|
|
@@ -532,6 +565,257 @@ describe("computeOwnActivation", () => {
|
|
|
532
565
|
// No prior state → prev=0 → priorContribution=0 regardless of `d`.
|
|
533
566
|
expect(out.breakdown.get("fresh")?.priorContribution).toBe(0);
|
|
534
567
|
});
|
|
568
|
+
|
|
569
|
+
test("rerank boost on user/assistant flips top-1 when fused had it second", async () => {
|
|
570
|
+
// Three Qdrant queries fire in parallel inside computeOwnActivation:
|
|
571
|
+
// user, assistant, now. Stage identical hits for each so the only signal
|
|
572
|
+
// separating slugs is the rerank boost on the user + assistant channels.
|
|
573
|
+
const stagedHits = [
|
|
574
|
+
{ slug: "lexical", denseScore: 0.6, sparseScore: 0 },
|
|
575
|
+
{ slug: "semantic", denseScore: 0.5, sparseScore: 0 },
|
|
576
|
+
];
|
|
577
|
+
stageHybridResponse(stagedHits); // user channel
|
|
578
|
+
stageHybridResponse(stagedHits); // assistant channel
|
|
579
|
+
stageHybridResponse(stagedHits); // now channel
|
|
580
|
+
rerankState.scores = new Map([
|
|
581
|
+
["lexical", 0.05],
|
|
582
|
+
["semantic", 0.95],
|
|
583
|
+
]);
|
|
584
|
+
|
|
585
|
+
const config = {
|
|
586
|
+
memory: {
|
|
587
|
+
v2: {
|
|
588
|
+
d: 0.0,
|
|
589
|
+
c_user: 0.5,
|
|
590
|
+
c_assistant: 0.5,
|
|
591
|
+
c_now: 0.0,
|
|
592
|
+
dense_weight: 1.0,
|
|
593
|
+
sparse_weight: 0.0,
|
|
594
|
+
rerank: {
|
|
595
|
+
enabled: true,
|
|
596
|
+
top_k: 50,
|
|
597
|
+
alpha: 0.5,
|
|
598
|
+
model: "test-model",
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
} as unknown as AssistantConfig;
|
|
603
|
+
|
|
604
|
+
const out = await computeOwnActivation({
|
|
605
|
+
candidates: new Set(["lexical", "semantic"]),
|
|
606
|
+
priorState: null,
|
|
607
|
+
userText: "u",
|
|
608
|
+
assistantText: "a",
|
|
609
|
+
nowText: "n",
|
|
610
|
+
config,
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// Without rerank: lexical (0.6) would beat semantic (0.5) on both
|
|
614
|
+
// user and assistant channels.
|
|
615
|
+
// With rerank (alpha=0.5):
|
|
616
|
+
// lexical: 0.6 + 0.5 · (0.05/0.95) ≈ 0.626
|
|
617
|
+
// semantic: 0.5 + 0.5 · 1.0 = 1.0
|
|
618
|
+
// The semantic candidate now wins on both rerank-boosted channels.
|
|
619
|
+
expect(out.activation.get("semantic")!).toBeGreaterThan(
|
|
620
|
+
out.activation.get("lexical")!,
|
|
621
|
+
);
|
|
622
|
+
// Both rerank-enabled channels ride in a single batched rerank call.
|
|
623
|
+
expect(rerankState.calls).toHaveLength(1);
|
|
624
|
+
expect(rerankState.calls[0].queries).toEqual(["u", "a"]);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
test("rerank pool is the unified top-K by pre-rerank A_o, not per-channel fused", async () => {
|
|
628
|
+
// Three candidates. The per-channel fused-sim top-2s would have picked
|
|
629
|
+
// different sets:
|
|
630
|
+
// user channel: a=0.9, b=0.5, c=0.4 → per-channel top-2 = [a, b]
|
|
631
|
+
// assistant channel: a=0.5, b=0.4, c=0.9 → per-channel top-2 = [c, a]
|
|
632
|
+
// But pre-rerank A_o (c_user=c_assistant=0.5) is:
|
|
633
|
+
// a = 0.5·0.9 + 0.5·0.5 = 0.70
|
|
634
|
+
// b = 0.5·0.5 + 0.5·0.4 = 0.45
|
|
635
|
+
// c = 0.5·0.4 + 0.5·0.9 = 0.65
|
|
636
|
+
// → unified top-2 = [a, c]. b drops out, even though it would have made
|
|
637
|
+
// the user-channel pool under the old per-channel selection.
|
|
638
|
+
stageHybridResponse([
|
|
639
|
+
{ slug: "a", denseScore: 0.9 },
|
|
640
|
+
{ slug: "b", denseScore: 0.5 },
|
|
641
|
+
{ slug: "c", denseScore: 0.4 },
|
|
642
|
+
]); // user
|
|
643
|
+
stageHybridResponse([
|
|
644
|
+
{ slug: "a", denseScore: 0.5 },
|
|
645
|
+
{ slug: "b", denseScore: 0.4 },
|
|
646
|
+
{ slug: "c", denseScore: 0.9 },
|
|
647
|
+
]); // assistant
|
|
648
|
+
stageHybridResponse([]); // now (no signal)
|
|
649
|
+
rerankState.scores = new Map([
|
|
650
|
+
["a", 0.5],
|
|
651
|
+
["b", 0.5],
|
|
652
|
+
["c", 0.5],
|
|
653
|
+
]);
|
|
654
|
+
|
|
655
|
+
const config = {
|
|
656
|
+
memory: {
|
|
657
|
+
v2: {
|
|
658
|
+
d: 0.0,
|
|
659
|
+
c_user: 0.5,
|
|
660
|
+
c_assistant: 0.5,
|
|
661
|
+
c_now: 0.0,
|
|
662
|
+
dense_weight: 1.0,
|
|
663
|
+
sparse_weight: 0.0,
|
|
664
|
+
rerank: {
|
|
665
|
+
enabled: true,
|
|
666
|
+
top_k: 2,
|
|
667
|
+
alpha: 0.3,
|
|
668
|
+
model: "test-model",
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
} as unknown as AssistantConfig;
|
|
673
|
+
|
|
674
|
+
await computeOwnActivation({
|
|
675
|
+
candidates: new Set(["a", "b", "c"]),
|
|
676
|
+
priorState: null,
|
|
677
|
+
userText: "u",
|
|
678
|
+
assistantText: "a",
|
|
679
|
+
nowText: "",
|
|
680
|
+
config,
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
// Single batched rerank call carrying both channel queries against the
|
|
684
|
+
// unified slug set, sorted by pre-rerank A_o descending.
|
|
685
|
+
expect(rerankState.calls).toHaveLength(1);
|
|
686
|
+
expect(rerankState.calls[0].queries).toEqual(["u", "a"]);
|
|
687
|
+
expect(rerankState.calls[0].candidates).toEqual(["a", "c"]);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
test("rerank-disabled candidates outside the unified pool get zero boost", async () => {
|
|
691
|
+
// Two candidates, top_k=1. The lower pre-rerank A_o slug must end up
|
|
692
|
+
// with simUserRerankBoost=0 / simAssistantRerankBoost=0 in the breakdown.
|
|
693
|
+
stageHybridResponse([
|
|
694
|
+
{ slug: "winner", denseScore: 0.9 },
|
|
695
|
+
{ slug: "loser", denseScore: 0.2 },
|
|
696
|
+
]); // user
|
|
697
|
+
stageHybridResponse([
|
|
698
|
+
{ slug: "winner", denseScore: 0.9 },
|
|
699
|
+
{ slug: "loser", denseScore: 0.2 },
|
|
700
|
+
]); // assistant
|
|
701
|
+
stageHybridResponse([]); // now
|
|
702
|
+
// The mocked reranker hands back scores for whatever slugs it's
|
|
703
|
+
// called with. Stage scores for both; the assertion below is that
|
|
704
|
+
// the loser still receives 0 because it's never sent to the
|
|
705
|
+
// reranker — top_k=1 cuts it off.
|
|
706
|
+
rerankState.scores = new Map([
|
|
707
|
+
["winner", 0.5],
|
|
708
|
+
["loser", 0.5],
|
|
709
|
+
]);
|
|
710
|
+
|
|
711
|
+
const config = {
|
|
712
|
+
memory: {
|
|
713
|
+
v2: {
|
|
714
|
+
d: 0.0,
|
|
715
|
+
c_user: 0.5,
|
|
716
|
+
c_assistant: 0.5,
|
|
717
|
+
c_now: 0.0,
|
|
718
|
+
dense_weight: 1.0,
|
|
719
|
+
sparse_weight: 0.0,
|
|
720
|
+
rerank: {
|
|
721
|
+
enabled: true,
|
|
722
|
+
top_k: 1,
|
|
723
|
+
alpha: 0.3,
|
|
724
|
+
model: "test-model",
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
} as unknown as AssistantConfig;
|
|
729
|
+
|
|
730
|
+
const out = await computeOwnActivation({
|
|
731
|
+
candidates: new Set(["winner", "loser"]),
|
|
732
|
+
priorState: null,
|
|
733
|
+
userText: "u",
|
|
734
|
+
assistantText: "a",
|
|
735
|
+
nowText: "",
|
|
736
|
+
config,
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
expect(out.breakdown.get("loser")?.simUserRerankBoost).toBe(0);
|
|
740
|
+
expect(out.breakdown.get("loser")?.simAssistantRerankBoost).toBe(0);
|
|
741
|
+
expect(out.breakdown.get("winner")?.simUserRerankBoost).toBeGreaterThan(0);
|
|
742
|
+
expect(
|
|
743
|
+
out.breakdown.get("winner")?.simAssistantRerankBoost,
|
|
744
|
+
).toBeGreaterThan(0);
|
|
745
|
+
// inRerankPool tags pool membership independently of the boost value, so
|
|
746
|
+
// the inspector can keep the rerank rows visible even when the channel
|
|
747
|
+
// max happened to normalise to 0.
|
|
748
|
+
expect(out.breakdown.get("winner")?.inRerankPool).toBe(true);
|
|
749
|
+
expect(out.breakdown.get("loser")?.inRerankPool).toBe(false);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
test("inRerankPool is false for every slug when rerank is disabled", async () => {
|
|
753
|
+
stageHybridResponse([{ slug: "alice", denseScore: 0.5 }]);
|
|
754
|
+
stageHybridResponse([{ slug: "alice", denseScore: 0.4 }]);
|
|
755
|
+
stageHybridResponse([{ slug: "alice", denseScore: 0.2 }]);
|
|
756
|
+
|
|
757
|
+
// No `rerank` block at all → rerankCfg is undefined and the rerank
|
|
758
|
+
// branch never runs, so no slug is in the pool.
|
|
759
|
+
const out = await computeOwnActivation({
|
|
760
|
+
candidates: new Set(["alice"]),
|
|
761
|
+
priorState: null,
|
|
762
|
+
userText: "u",
|
|
763
|
+
assistantText: "a",
|
|
764
|
+
nowText: "n",
|
|
765
|
+
config: makeConfig(),
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
expect(out.breakdown.get("alice")?.inRerankPool).toBe(false);
|
|
769
|
+
expect(out.breakdown.get("alice")?.simUserRerankBoost).toBe(0);
|
|
770
|
+
expect(out.breakdown.get("alice")?.simAssistantRerankBoost).toBe(0);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
test("rerank boost is additive on A_o and leaves raw simUser / simAssistant untouched", async () => {
|
|
774
|
+
stageHybridResponse([{ slug: "a", denseScore: 0.5 }]); // user
|
|
775
|
+
stageHybridResponse([{ slug: "a", denseScore: 0.4 }]); // assistant
|
|
776
|
+
stageHybridResponse([]); // now
|
|
777
|
+
rerankState.scores = new Map([["a", 0.8]]);
|
|
778
|
+
|
|
779
|
+
const config = {
|
|
780
|
+
memory: {
|
|
781
|
+
v2: {
|
|
782
|
+
d: 0.0,
|
|
783
|
+
c_user: 0.5,
|
|
784
|
+
c_assistant: 0.5,
|
|
785
|
+
c_now: 0.0,
|
|
786
|
+
dense_weight: 1.0,
|
|
787
|
+
sparse_weight: 0.0,
|
|
788
|
+
rerank: {
|
|
789
|
+
enabled: true,
|
|
790
|
+
top_k: 50,
|
|
791
|
+
alpha: 0.4,
|
|
792
|
+
model: "test-model",
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
} as unknown as AssistantConfig;
|
|
797
|
+
|
|
798
|
+
const out = await computeOwnActivation({
|
|
799
|
+
candidates: new Set(["a"]),
|
|
800
|
+
priorState: null,
|
|
801
|
+
userText: "u",
|
|
802
|
+
assistantText: "a",
|
|
803
|
+
nowText: "",
|
|
804
|
+
config,
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
const breakdown = out.breakdown.get("a");
|
|
808
|
+
// Raw fused similarities are reported untouched by rerank.
|
|
809
|
+
expect(breakdown?.simUser).toBeCloseTo(0.5, 6);
|
|
810
|
+
expect(breakdown?.simAssistant).toBeCloseTo(0.4, 6);
|
|
811
|
+
// Both rerank deltas are alpha · r_norm = 0.4 · 1.0 = 0.4 (single
|
|
812
|
+
// candidate normalises to 1.0 in each channel).
|
|
813
|
+
expect(breakdown?.simUserRerankBoost).toBeCloseTo(0.4, 6);
|
|
814
|
+
expect(breakdown?.simAssistantRerankBoost).toBeCloseTo(0.4, 6);
|
|
815
|
+
// Final A_o = c_user·simU + c_assistant·simA + c_user·boostU + c_assistant·boostA
|
|
816
|
+
// = 0.5·0.5 + 0.5·0.4 + 0.5·0.4 + 0.5·0.4 = 0.25+0.20+0.20+0.20 = 0.85
|
|
817
|
+
expect(out.activation.get("a")).toBeCloseTo(0.85, 6);
|
|
818
|
+
});
|
|
535
819
|
});
|
|
536
820
|
|
|
537
821
|
// ---------------------------------------------------------------------------
|
|
@@ -682,15 +966,59 @@ describe("spreadActivation", () => {
|
|
|
682
966
|
expect(out.final.get("bob")).toBeCloseTo(0.9, 6);
|
|
683
967
|
});
|
|
684
968
|
|
|
685
|
-
test("
|
|
686
|
-
// Edge alice→bob: bob has predecessor alice
|
|
687
|
-
// `ownActivation
|
|
688
|
-
//
|
|
969
|
+
test("predecessors not in the candidate set are dropped from both numerator and denominator", () => {
|
|
970
|
+
// Edge alice→bob: bob has structural predecessor alice, but alice is not
|
|
971
|
+
// in `ownActivation`. With the new formula she contributes nothing —
|
|
972
|
+
// hop1 has no active predecessors so the whole hop drops out of both
|
|
973
|
+
// sides of the ratio. Bob therefore stays at his own activation.
|
|
689
974
|
const edges = buildEdgeIndex([["alice", "bob"]]);
|
|
690
975
|
const own = new Map([["bob", 0.6]]);
|
|
691
976
|
const out = spreadActivation(own, edges, 0.5, 2);
|
|
692
|
-
|
|
693
|
-
|
|
977
|
+
expect(out.final.get("bob")).toBeCloseTo(0.6, 6);
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
test("L_2 norm over multiple active predecessors rewards strong outliers more than avg would", () => {
|
|
981
|
+
// bob has 4 predecessors in the candidate set: one strong, three weak.
|
|
982
|
+
// L_2 = √((0.8² + 0.1² + 0.1² + 0.1²) / 4) = √(0.1675) ≈ 0.40927
|
|
983
|
+
// Plain avg of the same set = 0.275, so L_2 lifts bob more than avg
|
|
984
|
+
// would — the design goal of preferring quality over quantity.
|
|
985
|
+
const edges = buildEdgeIndex([
|
|
986
|
+
["a1", "bob"],
|
|
987
|
+
["a2", "bob"],
|
|
988
|
+
["a3", "bob"],
|
|
989
|
+
["a4", "bob"],
|
|
990
|
+
]);
|
|
991
|
+
const own = new Map([
|
|
992
|
+
["a1", 0.8],
|
|
993
|
+
["a2", 0.1],
|
|
994
|
+
["a3", 0.1],
|
|
995
|
+
["a4", 0.1],
|
|
996
|
+
["bob", 0.0],
|
|
997
|
+
]);
|
|
998
|
+
const out = spreadActivation(own, edges, 0.5, 2);
|
|
999
|
+
const rms = Math.sqrt((0.8 * 0.8 + 3 * 0.1 * 0.1) / 4);
|
|
1000
|
+
// numerator = 0 + 0.5 · rms
|
|
1001
|
+
// denominator = 1 + 0.5
|
|
1002
|
+
expect(out.final.get("bob")).toBeCloseTo((0.5 * rms) / 1.5, 6);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
test("high-in-degree hub with mostly-inactive predecessors stays near A_o", () => {
|
|
1006
|
+
// 100 structural predecessors point at hub; only one (`pred0`) is in
|
|
1007
|
+
// the candidate set. The old formula would crush hub by the structural
|
|
1008
|
+
// count (denominator ≈ 51); the new formula folds the empty bulk out
|
|
1009
|
+
// and the L_2 averages over the single active predecessor only.
|
|
1010
|
+
const rawEdges: Array<[string, string]> = [];
|
|
1011
|
+
for (let i = 0; i < 100; i++) rawEdges.push([`pred${i}`, "hub"]);
|
|
1012
|
+
const edges = buildEdgeIndex(rawEdges);
|
|
1013
|
+
const own = new Map([
|
|
1014
|
+
["hub", 0.6],
|
|
1015
|
+
["pred0", 0.5],
|
|
1016
|
+
]);
|
|
1017
|
+
const out = spreadActivation(own, edges, 0.5, 2);
|
|
1018
|
+
// hop1 active = {pred0}, L_2([0.5]) = 0.5.
|
|
1019
|
+
// numerator = 0.6 + 0.5 · 0.5 = 0.85
|
|
1020
|
+
// denominator = 1 + 0.5 = 1.5
|
|
1021
|
+
expect(out.final.get("hub")).toBeCloseTo(0.85 / 1.5, 6);
|
|
694
1022
|
});
|
|
695
1023
|
|
|
696
1024
|
test("empty own-activation map returns empty result", () => {
|
|
@@ -829,132 +1157,26 @@ describe("selectInjections", () => {
|
|
|
829
1157
|
});
|
|
830
1158
|
|
|
831
1159
|
// ---------------------------------------------------------------------------
|
|
832
|
-
//
|
|
833
|
-
// ---------------------------------------------------------------------------
|
|
834
|
-
|
|
835
|
-
/** Stage a single hybrid response on the skills queues (payload key = `id`). */
|
|
836
|
-
function stageSkillHybridResponse(
|
|
837
|
-
hits: Array<{ id: string; denseScore?: number; sparseScore?: number }>,
|
|
838
|
-
): void {
|
|
839
|
-
state.skillQueryResponses.dense.push({
|
|
840
|
-
points: hits
|
|
841
|
-
.filter((h) => h.denseScore !== undefined)
|
|
842
|
-
.map((h) => ({ score: h.denseScore, payload: { id: h.id } })),
|
|
843
|
-
});
|
|
844
|
-
state.skillQueryResponses.sparse.push({
|
|
845
|
-
points: hits
|
|
846
|
-
.filter((h) => h.sparseScore !== undefined)
|
|
847
|
-
.map((h) => ({ score: h.sparseScore, payload: { id: h.id } })),
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
describe("selectSkillCandidates", () => {
|
|
852
|
-
test("returns hit ids from the skills collection", async () => {
|
|
853
|
-
stageSkillHybridResponse([
|
|
854
|
-
{ id: "example-skill-a", denseScore: 0.5, sparseScore: 1 },
|
|
855
|
-
{ id: "example-skill-b", denseScore: 0.3, sparseScore: 1 },
|
|
856
|
-
]);
|
|
857
|
-
const out = await selectSkillCandidates({
|
|
858
|
-
userText: "user said hello",
|
|
859
|
-
assistantText: "",
|
|
860
|
-
nowText: "",
|
|
861
|
-
config: makeConfig(),
|
|
862
|
-
topK: 10,
|
|
863
|
-
});
|
|
864
|
-
expect(out).toEqual(new Set(["example-skill-a", "example-skill-b"]));
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
test("empty turn text short-circuits without backend calls", async () => {
|
|
868
|
-
const out = await selectSkillCandidates({
|
|
869
|
-
userText: "",
|
|
870
|
-
assistantText: "",
|
|
871
|
-
nowText: "",
|
|
872
|
-
config: makeConfig(),
|
|
873
|
-
topK: 10,
|
|
874
|
-
});
|
|
875
|
-
expect(out.size).toBe(0);
|
|
876
|
-
expect(state.embedCalls).toHaveLength(0);
|
|
877
|
-
expect(state.queryCalls).toHaveLength(0);
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
test("topK=0 short-circuits without backend calls", async () => {
|
|
881
|
-
const out = await selectSkillCandidates({
|
|
882
|
-
userText: "anything",
|
|
883
|
-
assistantText: "anything",
|
|
884
|
-
nowText: "anything",
|
|
885
|
-
config: makeConfig(),
|
|
886
|
-
topK: 0,
|
|
887
|
-
});
|
|
888
|
-
expect(out.size).toBe(0);
|
|
889
|
-
expect(state.embedCalls).toHaveLength(0);
|
|
890
|
-
expect(state.queryCalls).toHaveLength(0);
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
test("forwards topK and queries the skills collection unrestricted", async () => {
|
|
894
|
-
stageSkillHybridResponse([
|
|
895
|
-
{ id: "example-skill-a", denseScore: 0.5, sparseScore: 1 },
|
|
896
|
-
]);
|
|
897
|
-
await selectSkillCandidates({
|
|
898
|
-
userText: "hello",
|
|
899
|
-
assistantText: "",
|
|
900
|
-
nowText: "",
|
|
901
|
-
config: makeConfig(),
|
|
902
|
-
topK: 7,
|
|
903
|
-
});
|
|
904
|
-
// Both channels (dense + sparse) ran with limit=7 and no slug/id filter,
|
|
905
|
-
// against the dedicated skills collection.
|
|
906
|
-
expect(state.queryCalls).toHaveLength(2);
|
|
907
|
-
for (const call of state.queryCalls) {
|
|
908
|
-
expect(call.collection).toBe("memory_v2_skills");
|
|
909
|
-
expect(call.limit).toBe(7);
|
|
910
|
-
expect(call.filter).toBeUndefined();
|
|
911
|
-
}
|
|
912
|
-
});
|
|
913
|
-
|
|
914
|
-
test("embeds concatenated turn text exactly once", async () => {
|
|
915
|
-
stageSkillHybridResponse([]);
|
|
916
|
-
await selectSkillCandidates({
|
|
917
|
-
userText: "user line",
|
|
918
|
-
assistantText: "assistant line",
|
|
919
|
-
nowText: "now line",
|
|
920
|
-
config: makeConfig(),
|
|
921
|
-
topK: 5,
|
|
922
|
-
});
|
|
923
|
-
expect(state.embedCalls).toHaveLength(1);
|
|
924
|
-
expect(state.embedCalls[0].inputs).toEqual([
|
|
925
|
-
"user line\nassistant line\nnow line",
|
|
926
|
-
]);
|
|
927
|
-
expect(state.sparseCalls).toEqual(["user line\nassistant line\nnow line"]);
|
|
928
|
-
});
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
// ---------------------------------------------------------------------------
|
|
932
|
-
// computeSkillActivation
|
|
1160
|
+
// Skills as concept slugs — the unified pool
|
|
933
1161
|
// ---------------------------------------------------------------------------
|
|
1162
|
+
//
|
|
1163
|
+
// Skills participate in the concept-page pipeline under the slug prefix
|
|
1164
|
+
// `skills/<id>`. There is no longer a dedicated skill activation function;
|
|
1165
|
+
// the only post-unification behavioral assertion worth preserving here is
|
|
1166
|
+
// that a `skills/<id>` slug flows through `computeOwnActivation` exactly
|
|
1167
|
+
// like a concept slug — same formula, same clamp, same breakdown shape.
|
|
1168
|
+
|
|
1169
|
+
describe("skills participate in the unified pipeline", () => {
|
|
1170
|
+
test("computeOwnActivation scores a `skills/<id>` slug like any concept slug", async () => {
|
|
1171
|
+
// Three simBatch responses, one per channel (user/assistant/now), with
|
|
1172
|
+
// a single skill-prefixed slug as the only candidate.
|
|
1173
|
+
stageHybridResponse([{ slug: "skills/example-skill-a", denseScore: 0.5 }]);
|
|
1174
|
+
stageHybridResponse([{ slug: "skills/example-skill-a", denseScore: 0.4 }]);
|
|
1175
|
+
stageHybridResponse([{ slug: "skills/example-skill-a", denseScore: 0.2 }]);
|
|
934
1176
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
candidates: new Set(),
|
|
939
|
-
userText: "u",
|
|
940
|
-
assistantText: "a",
|
|
941
|
-
nowText: "n",
|
|
942
|
-
config: makeConfig(),
|
|
943
|
-
});
|
|
944
|
-
expect(out.activation.size).toBe(0);
|
|
945
|
-
expect(out.breakdown.size).toBe(0);
|
|
946
|
-
expect(state.embedCalls).toHaveLength(0);
|
|
947
|
-
expect(state.queryCalls).toHaveLength(0);
|
|
948
|
-
});
|
|
949
|
-
|
|
950
|
-
test("applies similarity-only formula with no decay term", async () => {
|
|
951
|
-
// Stage three skill responses — one per `simSkillBatch` call.
|
|
952
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.5 }]); // simU
|
|
953
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.4 }]); // simA
|
|
954
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.2 }]); // simN
|
|
955
|
-
|
|
956
|
-
const out = await computeSkillActivation({
|
|
957
|
-
candidates: new Set(["example-skill-a"]),
|
|
1177
|
+
const out = await computeOwnActivation({
|
|
1178
|
+
candidates: new Set(["skills/example-skill-a"]),
|
|
1179
|
+
priorState: null,
|
|
958
1180
|
userText: "u",
|
|
959
1181
|
assistantText: "a",
|
|
960
1182
|
nowText: "n",
|
|
@@ -965,191 +1187,12 @@ describe("computeSkillActivation", () => {
|
|
|
965
1187
|
c_now: 0.2,
|
|
966
1188
|
}),
|
|
967
1189
|
});
|
|
968
|
-
// No `d · prev` term: 0.3*0.5 + 0.2*0.4 + 0.2*0.2 = 0.15 + 0.08 + 0.04 = 0.27
|
|
969
|
-
expect(out.activation.get("example-skill-a")).toBeCloseTo(0.27, 6);
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
test("output excludes any decay term — d coefficient is unused", async () => {
|
|
973
|
-
// The skill activation formula is `c_user·simU + c_assistant·simA +
|
|
974
|
-
// c_now·simN`. Run with d=0.9 and d=0.0 — if the implementation
|
|
975
|
-
// accidentally included a `d · prev` term, the two would diverge. The
|
|
976
|
-
// function has no priorState parameter, so prev=0; both runs must equal
|
|
977
|
-
// the d-free formula exactly. Stage three sim responses per run.
|
|
978
|
-
const stage = () => {
|
|
979
|
-
stageSkillHybridResponse([{ id: "alpha", denseScore: 0.4 }]);
|
|
980
|
-
stageSkillHybridResponse([{ id: "alpha", denseScore: 0.4 }]);
|
|
981
|
-
stageSkillHybridResponse([{ id: "alpha", denseScore: 0.4 }]);
|
|
982
|
-
};
|
|
983
|
-
const baseConfig = { c_user: 0.3, c_assistant: 0.2, c_now: 0.2 };
|
|
984
|
-
|
|
985
|
-
stage();
|
|
986
|
-
const withHighD = await computeSkillActivation({
|
|
987
|
-
candidates: new Set(["alpha"]),
|
|
988
|
-
userText: "u",
|
|
989
|
-
assistantText: "a",
|
|
990
|
-
nowText: "n",
|
|
991
|
-
config: makeConfig({ ...baseConfig, d: 0.9 }),
|
|
992
|
-
});
|
|
993
|
-
stage();
|
|
994
|
-
const withZeroD = await computeSkillActivation({
|
|
995
|
-
candidates: new Set(["alpha"]),
|
|
996
|
-
userText: "u",
|
|
997
|
-
assistantText: "a",
|
|
998
|
-
nowText: "n",
|
|
999
|
-
config: makeConfig({ ...baseConfig, d: 0.0 }),
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
// Both equal `0.3*0.4 + 0.2*0.4 + 0.2*0.4 = 0.28` — d is ignored.
|
|
1003
|
-
expect(withHighD.activation.get("alpha")).toBeCloseTo(0.28, 6);
|
|
1004
|
-
expect(withZeroD.activation.get("alpha")).toBeCloseTo(0.28, 6);
|
|
1005
|
-
});
|
|
1006
|
-
|
|
1007
|
-
test("clamps over-1.0 results down to [0, 1]", async () => {
|
|
1008
|
-
stageSkillHybridResponse([{ id: "loud-skill", denseScore: 1.0 }]); // simU
|
|
1009
|
-
stageSkillHybridResponse([{ id: "loud-skill", denseScore: 1.0 }]); // simA
|
|
1010
|
-
stageSkillHybridResponse([{ id: "loud-skill", denseScore: 1.0 }]); // simN
|
|
1011
|
-
|
|
1012
|
-
// Coefficients intentionally sum to > 1 so the unclamped result
|
|
1013
|
-
// overshoots — the implementation must still produce <= 1.0.
|
|
1014
|
-
const out = await computeSkillActivation({
|
|
1015
|
-
candidates: new Set(["loud-skill"]),
|
|
1016
|
-
userText: "u",
|
|
1017
|
-
assistantText: "a",
|
|
1018
|
-
nowText: "n",
|
|
1019
|
-
config: makeConfig({
|
|
1020
|
-
c_user: 0.5,
|
|
1021
|
-
c_assistant: 0.5,
|
|
1022
|
-
c_now: 0.5,
|
|
1023
|
-
}),
|
|
1024
|
-
});
|
|
1025
|
-
expect(out.activation.get("loud-skill")).toBe(1);
|
|
1026
|
-
});
|
|
1027
1190
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
candidates: new Set(["ghost-skill"]),
|
|
1035
|
-
userText: "u",
|
|
1036
|
-
assistantText: "a",
|
|
1037
|
-
nowText: "n",
|
|
1038
|
-
config: makeConfig(),
|
|
1039
|
-
});
|
|
1040
|
-
expect(out.activation.get("ghost-skill")).toBe(0);
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
test("breakdown captures the raw sims for each candidate", async () => {
|
|
1044
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.5 }]); // simU
|
|
1045
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.4 }]); // simA
|
|
1046
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.2 }]); // simN
|
|
1047
|
-
|
|
1048
|
-
const out = await computeSkillActivation({
|
|
1049
|
-
candidates: new Set(["example-skill-a"]),
|
|
1050
|
-
userText: "u",
|
|
1051
|
-
assistantText: "a",
|
|
1052
|
-
nowText: "n",
|
|
1053
|
-
config: makeConfig({
|
|
1054
|
-
c_user: 0.3,
|
|
1055
|
-
c_assistant: 0.2,
|
|
1056
|
-
c_now: 0.2,
|
|
1057
|
-
}),
|
|
1058
|
-
});
|
|
1059
|
-
const breakdown = out.breakdown.get("example-skill-a");
|
|
1060
|
-
expect(breakdown).toBeDefined();
|
|
1061
|
-
expect(breakdown?.simUser).toBeCloseTo(0.5, 6);
|
|
1062
|
-
expect(breakdown?.simAssistant).toBeCloseTo(0.4, 6);
|
|
1063
|
-
expect(breakdown?.simNow).toBeCloseTo(0.2, 6);
|
|
1064
|
-
});
|
|
1065
|
-
|
|
1066
|
-
test("uses the dedicated skills collection and never queries concept pages", async () => {
|
|
1067
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.5 }]);
|
|
1068
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.5 }]);
|
|
1069
|
-
stageSkillHybridResponse([{ id: "example-skill-a", denseScore: 0.5 }]);
|
|
1070
|
-
|
|
1071
|
-
await computeSkillActivation({
|
|
1072
|
-
candidates: new Set(["example-skill-a"]),
|
|
1073
|
-
userText: "u",
|
|
1074
|
-
assistantText: "a",
|
|
1075
|
-
nowText: "n",
|
|
1076
|
-
config: makeConfig(),
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
// Three simSkillBatch calls × 2 channels = 6 total queries, all against
|
|
1080
|
-
// the skills collection. No spread → no extra calls beyond these.
|
|
1081
|
-
expect(state.queryCalls).toHaveLength(6);
|
|
1082
|
-
for (const call of state.queryCalls) {
|
|
1083
|
-
expect(call.collection).toBe("memory_v2_skills");
|
|
1084
|
-
}
|
|
1085
|
-
});
|
|
1086
|
-
});
|
|
1087
|
-
|
|
1088
|
-
// ---------------------------------------------------------------------------
|
|
1089
|
-
// selectSkillInjections
|
|
1090
|
-
// ---------------------------------------------------------------------------
|
|
1091
|
-
|
|
1092
|
-
describe("selectSkillInjections", () => {
|
|
1093
|
-
test("returns empty when activation is empty", () => {
|
|
1094
|
-
const out = selectSkillInjections({ A: new Map(), topK: 5 });
|
|
1095
|
-
expect(out).toEqual({ topNow: [] });
|
|
1096
|
-
});
|
|
1097
|
-
|
|
1098
|
-
test("returns empty when topK is 0", () => {
|
|
1099
|
-
const out = selectSkillInjections({
|
|
1100
|
-
A: new Map([
|
|
1101
|
-
["example-skill-a", 0.5],
|
|
1102
|
-
["example-skill-b", 0.4],
|
|
1103
|
-
]),
|
|
1104
|
-
topK: 0,
|
|
1105
|
-
});
|
|
1106
|
-
expect(out).toEqual({ topNow: [] });
|
|
1107
|
-
});
|
|
1108
|
-
|
|
1109
|
-
test("ranks by activation descending and trims to topK", () => {
|
|
1110
|
-
const out = selectSkillInjections({
|
|
1111
|
-
A: new Map([
|
|
1112
|
-
["example-skill-a", 0.1],
|
|
1113
|
-
["example-skill-b", 0.9],
|
|
1114
|
-
["example-skill-c", 0.5],
|
|
1115
|
-
["example-skill-d", 0.3],
|
|
1116
|
-
]),
|
|
1117
|
-
topK: 2,
|
|
1118
|
-
});
|
|
1119
|
-
expect(out.topNow).toEqual(["example-skill-b", "example-skill-c"]);
|
|
1120
|
-
});
|
|
1121
|
-
|
|
1122
|
-
test("skills are stateless: the same id may be returned on consecutive turns", () => {
|
|
1123
|
-
// No `everInjected` parameter exists — selectSkillInjections takes only
|
|
1124
|
-
// the activation map and topK. So calling it twice with the same A map
|
|
1125
|
-
// returns the same result; there is no dedup against prior turns.
|
|
1126
|
-
const A = new Map([
|
|
1127
|
-
["example-skill-a", 0.9],
|
|
1128
|
-
["example-skill-b", 0.5],
|
|
1129
|
-
]);
|
|
1130
|
-
const turn1 = selectSkillInjections({ A, topK: 5 });
|
|
1131
|
-
const turn2 = selectSkillInjections({ A, topK: 5 });
|
|
1132
|
-
expect(turn1.topNow).toEqual(["example-skill-a", "example-skill-b"]);
|
|
1133
|
-
expect(turn2.topNow).toEqual(turn1.topNow);
|
|
1134
|
-
});
|
|
1135
|
-
|
|
1136
|
-
test("breaks ties by id ascending for deterministic output", () => {
|
|
1137
|
-
const out = selectSkillInjections({
|
|
1138
|
-
A: new Map([
|
|
1139
|
-
["zeta-skill", 0.5],
|
|
1140
|
-
["example-skill-a", 0.5],
|
|
1141
|
-
["mike-skill", 0.5],
|
|
1142
|
-
]),
|
|
1143
|
-
topK: 5,
|
|
1144
|
-
});
|
|
1145
|
-
expect(out.topNow).toEqual(["example-skill-a", "mike-skill", "zeta-skill"]);
|
|
1146
|
-
});
|
|
1147
|
-
|
|
1148
|
-
test("topK clamps to the available activation entries", () => {
|
|
1149
|
-
const out = selectSkillInjections({
|
|
1150
|
-
A: new Map([["only-skill", 0.7]]),
|
|
1151
|
-
topK: 100,
|
|
1152
|
-
});
|
|
1153
|
-
expect(out.topNow).toEqual(["only-skill"]);
|
|
1191
|
+
// No prior state → priorContribution = 0.
|
|
1192
|
+
// 0.3*0.5 + 0.2*0.4 + 0.2*0.2 = 0.15 + 0.08 + 0.04 = 0.27
|
|
1193
|
+
expect(out.activation.get("skills/example-skill-a")).toBeCloseTo(0.27, 6);
|
|
1194
|
+
expect(out.breakdown.get("skills/example-skill-a")?.priorContribution).toBe(
|
|
1195
|
+
0,
|
|
1196
|
+
);
|
|
1154
1197
|
});
|
|
1155
1198
|
});
|