@vellumai/assistant 0.7.2 → 0.8.0
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 +45 -29
- package/Dockerfile +1 -0
- package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
- package/bun.lock +3 -0
- package/docs/architecture/memory.md +5 -2
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
- package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
- package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- package/openapi.yaml +470 -25
- package/package.json +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +291 -0
- package/src/__tests__/app-control-flow.test.ts +21 -11
- package/src/__tests__/approval-cascade.test.ts +8 -16
- package/src/__tests__/approval-routes-http.test.ts +6 -0
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/assistant-event.test.ts +0 -10
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- package/src/__tests__/call-constants.test.ts +10 -1
- package/src/__tests__/call-controller.test.ts +127 -0
- package/src/__tests__/call-conversation-messages.test.ts +8 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +4 -2
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
- package/src/__tests__/config-loader-backfill.test.ts +379 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
- package/src/__tests__/config-watcher.test.ts +140 -69
- 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 +6 -33
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +12 -7
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +457 -8
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
- package/src/__tests__/conversation-error.test.ts +150 -3
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
- package/src/__tests__/conversation-process-callsite.test.ts +38 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -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 +170 -9
- package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credentials-cli.test.ts +7 -0
- package/src/__tests__/cu-unified-flow.test.ts +176 -10
- package/src/__tests__/date-context.test.ts +164 -2
- 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__/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 +2 -20
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
- package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +260 -11
- package/src/__tests__/host-app-control-proxy.test.ts +195 -25
- package/src/__tests__/host-bash-proxy.test.ts +227 -34
- package/src/__tests__/host-bash-routes.test.ts +178 -13
- package/src/__tests__/host-cu-proxy.test.ts +210 -3
- package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
- package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
- package/src/__tests__/host-file-proxy.test.ts +268 -6
- package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
- package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
- package/src/__tests__/http-user-message-parity.test.ts +107 -1
- package/src/__tests__/injector-chain.test.ts +36 -16
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
- package/src/__tests__/managed-profile-guard.test.ts +18 -0
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/notification-decision-fallback.test.ts +91 -0
- package/src/__tests__/notification-decision-strategy.test.ts +22 -0
- package/src/__tests__/oauth-cli.test.ts +121 -0
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- 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__/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 -27
- package/src/__tests__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/relay-server.test.ts +60 -5
- package/src/__tests__/runtime-events-sse.test.ts +4 -8
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -1
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
- package/src/__tests__/secret-response-routing.test.ts +7 -5
- package/src/__tests__/server-history-render.test.ts +82 -0
- package/src/__tests__/skill-include-graph.test.ts +31 -0
- package/src/__tests__/skill-load-tool.test.ts +44 -16
- package/src/__tests__/skills.test.ts +39 -0
- package/src/__tests__/suggestion-routes.test.ts +46 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
- package/src/__tests__/tool-executor.test.ts +155 -0
- package/src/__tests__/twilio-validation.test.ts +2 -2
- package/src/__tests__/voice-session-bridge.test.ts +3 -0
- 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-069-seed-onboarding-threads.test.ts +120 -0
- package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
- package/src/agent/loop.ts +11 -0
- package/src/approvals/guardian-request-resolvers.ts +3 -32
- package/src/backup/snapshot-lock.ts +2 -27
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-constants.ts +5 -8
- package/src/calls/call-controller.ts +130 -67
- package/src/calls/call-conversation-messages.ts +46 -10
- package/src/calls/relay-server.ts +7 -1
- package/src/calls/voice-session-bridge.ts +1 -1
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/contacts.ts +64 -25
- package/src/cli/commands/credentials.ts +56 -0
- package/src/cli/commands/memory-v2.ts +11 -10
- package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
- package/src/cli/commands/oauth/connect.ts +124 -40
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
- package/src/cli/commands/platform/index.ts +16 -7
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/program.ts +4 -2
- package/src/config/assistant-feature-flags.ts +13 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
- 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/env.ts +0 -8
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +199 -27
- package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +13 -0
- package/src/config/schemas/memory-v2.ts +76 -12
- package/src/config/schemas/platform.ts +43 -3
- package/src/config/schemas/services.ts +28 -0
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -25
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/config-watcher.ts +85 -57
- package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
- package/src/daemon/conversation-agent-loop.ts +183 -43
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +22 -10
- package/src/daemon/conversation-process.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +26 -0
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +211 -29
- package/src/daemon/conversation-tool-setup.ts +66 -19
- package/src/daemon/conversation.ts +18 -23
- 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/handlers/shared.ts +26 -1
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +137 -41
- package/src/daemon/host-bash-proxy.ts +47 -22
- package/src/daemon/host-browser-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +50 -4
- package/src/daemon/host-file-proxy.ts +44 -8
- package/src/daemon/host-transfer-proxy.ts +97 -6
- package/src/daemon/lifecycle.ts +167 -101
- 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 +66 -15
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +4 -0
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- package/src/daemon/message-types/messages.ts +22 -1
- package/src/daemon/profiler-run-store.ts +5 -5
- package/src/daemon/tool-setup-types.ts +2 -2
- package/src/documents/document-store.ts +119 -0
- package/src/filing/filing-service.ts +29 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
- package/src/heartbeat/heartbeat-run-store.ts +13 -0
- package/src/heartbeat/heartbeat-service.ts +205 -31
- package/src/home/feed-scheduler.ts +18 -0
- package/src/inbound/platform-callback-registration.ts +8 -15
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/assistant-server.ts +149 -38
- package/src/ipc/gateway-client.ts +37 -3
- package/src/ipc/skill-server.ts +99 -42
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/protocol.ts +5 -7
- 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-worker-v2-schedule.test.ts +34 -51
- 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/admin.ts +5 -9
- package/src/memory/context-search/agent-runner.ts +19 -2
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +1 -16
- package/src/memory/context-search/sources/memory.ts +2 -3
- package/src/memory/context-search/sources/pkb.ts +2 -3
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +4 -12
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
- package/src/memory/graph/conversation-graph-memory.ts +72 -61
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +11 -67
- package/src/memory/graph/graph-search.ts +4 -24
- package/src/memory/graph/retriever.test.ts +12 -1
- package/src/memory/graph/retriever.ts +10 -15
- package/src/memory/graph/tool-handlers.ts +3 -4
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/indexer.ts +53 -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 +116 -0
- package/src/memory/jobs/embed-concept-page.ts +223 -87
- package/src/memory/jobs-store.ts +48 -0
- package/src/memory/jobs-worker.ts +85 -43
- package/src/memory/memory-v2-activation-log-store.ts +32 -14
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +7 -0
- package/src/memory/pkb/pkb-search.ts +4 -5
- package/src/memory/qdrant-client.ts +3 -13
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/search/semantic.ts +10 -72
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +346 -255
- package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
- package/src/memory/v2/__tests__/injection.test.ts +297 -190
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +113 -196
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/static-context.test.ts +77 -14
- package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
- package/src/memory/v2/activation.ts +149 -156
- package/src/memory/v2/consolidation-job.ts +69 -20
- package/src/memory/v2/injection.ts +75 -68
- package/src/memory/v2/page-store.ts +39 -0
- package/src/memory/v2/prompts/consolidation.ts +41 -1
- package/src/memory/v2/qdrant.ts +306 -46
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +77 -110
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +82 -59
- package/src/memory/v2/static-context.ts +26 -8
- package/src/memory/v2/sweep-job.ts +5 -6
- package/src/memory/v2/types.ts +17 -10
- package/src/notifications/copy-composer.ts +47 -0
- package/src/notifications/decision-engine.ts +46 -0
- 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 +2 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- package/src/permissions/gateway-threshold-reader.ts +116 -8
- package/src/permissions/prompter.ts +86 -96
- package/src/permissions/secret-prompter.ts +31 -31
- package/src/plugins/defaults/injectors.ts +36 -4
- 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 +914 -0
- package/src/proactive-artifact/job.ts +366 -0
- package/src/proactive-artifact/message-copy.ts +58 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +21 -7
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/SOUL.md +13 -28
- 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/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 +36 -6
- package/src/runtime/assistant-event.ts +0 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/same-actor.ts +216 -0
- package/src/runtime/channel-approvals.ts +3 -2
- package/src/runtime/channel-retry-sweep.ts +65 -1
- package/src/runtime/local-actor-identity.ts +52 -11
- package/src/runtime/pending-interactions.ts +27 -15
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
- package/src/runtime/routes/approval-routes.ts +7 -3
- package/src/runtime/routes/client-routes.ts +20 -2
- package/src/runtime/routes/consolidation-routes.ts +8 -9
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-query-routes.ts +44 -1
- package/src/runtime/routes/conversation-routes.ts +35 -26
- package/src/runtime/routes/debug-bash-routes.ts +165 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +6 -2
- package/src/runtime/routes/documents-routes.ts +2 -75
- package/src/runtime/routes/events-routes.ts +41 -9
- package/src/runtime/routes/filing-routes.ts +2 -3
- package/src/runtime/routes/host-bash-routes.ts +23 -3
- package/src/runtime/routes/host-cu-routes.ts +33 -6
- package/src/runtime/routes/host-file-routes.ts +32 -6
- package/src/runtime/routes/host-transfer-routes.ts +79 -16
- package/src/runtime/routes/identity-routes.ts +7 -138
- package/src/runtime/routes/inbound-message-handler.ts +77 -12
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/memory-item-routes.test.ts +37 -17
- package/src/runtime/routes/memory-item-routes.ts +5 -6
- package/src/runtime/routes/memory-v2-routes.ts +136 -17
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/run-script.ts +37 -5
- package/src/schedule/scheduler.ts +20 -1
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/secure-keys.ts +55 -0
- package/src/skills/include-graph.ts +35 -13
- package/src/skills/remote-skill-policy.ts +4 -10
- 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/tools/background-tool-registry.ts +17 -3
- package/src/tools/document/document-tool.ts +20 -0
- package/src/tools/executor.ts +18 -2
- package/src/tools/host-filesystem/edit.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +43 -1
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +43 -1
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +56 -11
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +43 -1
- package/src/tools/host-terminal/host-shell.ts +13 -6
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +14 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/permission-checker.ts +15 -0
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- package/src/tools/skills/load.ts +24 -20
- package/src/tools/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +31 -6
- package/src/tools/tool-name-aliases.ts +19 -0
- package/src/tools/types.ts +43 -3
- package/src/tts/provider-catalog.ts +3 -5
- 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/heartbeat-service.ts +19 -0
- 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 +14 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
- package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
- package/src/workspace/migrations/registry.ts +14 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -404
- package/src/signals/bash.ts +0 -198
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for `readMemoryV2StaticContent` — the loader that powers the
|
|
3
|
-
* `memory-v2-static` user-message auto-injection.
|
|
4
|
-
* lived in the deprecated `system-prompt-memory-v2.test.ts`:
|
|
5
|
-
* - Returns null when the v2 flag is off.
|
|
3
|
+
* `memory-v2-static` user-message auto-injection.
|
|
6
4
|
* - Returns null when `config.memory.v2.enabled` is off.
|
|
7
5
|
* - Reads the four files in canonical order and joins them under headings.
|
|
8
6
|
* - Skips empty / missing files.
|
|
@@ -47,9 +45,8 @@ mock.module("../../../config/loader.js", () => ({
|
|
|
47
45
|
setNestedValue: () => {},
|
|
48
46
|
}));
|
|
49
47
|
|
|
50
|
-
const {
|
|
51
|
-
await import("
|
|
52
|
-
const { readMemoryV2StaticContent } = await import("../static-context.js");
|
|
48
|
+
const { readMemoryV2StaticContent, shouldLoadMemoryV2Static } =
|
|
49
|
+
await import("../static-context.js");
|
|
53
50
|
|
|
54
51
|
const MEMORY_FILES = [
|
|
55
52
|
"essentials.md",
|
|
@@ -74,18 +71,10 @@ describe("readMemoryV2StaticContent", () => {
|
|
|
74
71
|
beforeEach(() => {
|
|
75
72
|
mkdirSync(TEST_DIR, { recursive: true });
|
|
76
73
|
configMemoryV2Enabled = true;
|
|
77
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
78
74
|
});
|
|
79
75
|
|
|
80
76
|
afterEach(() => {
|
|
81
77
|
cleanupMemoryDir();
|
|
82
|
-
_setOverridesForTesting({});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("returns null when the feature flag is off", () => {
|
|
86
|
-
_setOverridesForTesting({ "memory-v2-enabled": false });
|
|
87
|
-
for (const file of MEMORY_FILES) writeMemoryFile(file, `Content ${file}`);
|
|
88
|
-
expect(readMemoryV2StaticContent()).toBeNull();
|
|
89
78
|
});
|
|
90
79
|
|
|
91
80
|
test("returns null when config.memory.v2.enabled is off", () => {
|
|
@@ -150,3 +139,77 @@ describe("readMemoryV2StaticContent", () => {
|
|
|
150
139
|
expect(readMemoryV2StaticContent()).toBeNull();
|
|
151
140
|
});
|
|
152
141
|
});
|
|
142
|
+
|
|
143
|
+
describe("shouldLoadMemoryV2Static", () => {
|
|
144
|
+
test("blocks all turns until the cadence gate fires", () => {
|
|
145
|
+
expect(
|
|
146
|
+
shouldLoadMemoryV2Static({
|
|
147
|
+
shouldInjectNowAndPkb: false,
|
|
148
|
+
sourceChannel: "vellum",
|
|
149
|
+
isTrustedActor: true,
|
|
150
|
+
}),
|
|
151
|
+
).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("allows guardian-trusted local conversations", () => {
|
|
155
|
+
expect(
|
|
156
|
+
shouldLoadMemoryV2Static({
|
|
157
|
+
shouldInjectNowAndPkb: true,
|
|
158
|
+
sourceChannel: "vellum",
|
|
159
|
+
isTrustedActor: true,
|
|
160
|
+
}),
|
|
161
|
+
).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("allows local-channel conversations even when trust class is unknown (analyze runs, dev)", () => {
|
|
165
|
+
expect(
|
|
166
|
+
shouldLoadMemoryV2Static({
|
|
167
|
+
shouldInjectNowAndPkb: true,
|
|
168
|
+
sourceChannel: "vellum",
|
|
169
|
+
isTrustedActor: false,
|
|
170
|
+
}),
|
|
171
|
+
).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("allows turns with no trust context (work-item task runs, internal background)", () => {
|
|
175
|
+
expect(
|
|
176
|
+
shouldLoadMemoryV2Static({
|
|
177
|
+
shouldInjectNowAndPkb: true,
|
|
178
|
+
sourceChannel: undefined,
|
|
179
|
+
isTrustedActor: false,
|
|
180
|
+
}),
|
|
181
|
+
).toBe(true);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const REMOTE_CHANNELS = [
|
|
185
|
+
"phone",
|
|
186
|
+
"slack",
|
|
187
|
+
"telegram",
|
|
188
|
+
"whatsapp",
|
|
189
|
+
"email",
|
|
190
|
+
] as const;
|
|
191
|
+
|
|
192
|
+
test("allows guardian-trusted remote channels (user's own phone/Slack)", () => {
|
|
193
|
+
for (const channel of REMOTE_CHANNELS) {
|
|
194
|
+
expect(
|
|
195
|
+
shouldLoadMemoryV2Static({
|
|
196
|
+
shouldInjectNowAndPkb: true,
|
|
197
|
+
sourceChannel: channel,
|
|
198
|
+
isTrustedActor: true,
|
|
199
|
+
}),
|
|
200
|
+
).toBe(true);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("blocks non-guardian remote-channel actors (the leak this gate exists to prevent)", () => {
|
|
205
|
+
for (const channel of REMOTE_CHANNELS) {
|
|
206
|
+
expect(
|
|
207
|
+
shouldLoadMemoryV2Static({
|
|
208
|
+
shouldInjectNowAndPkb: true,
|
|
209
|
+
sourceChannel: channel,
|
|
210
|
+
isTrustedActor: false,
|
|
211
|
+
}),
|
|
212
|
+
).toBe(false);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for `assistant/src/memory/v2/sweep-job.ts`.
|
|
3
3
|
*
|
|
4
|
-
* Coverage matrix
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
4
|
+
* Coverage matrix:
|
|
5
|
+
* - v2 disabled in config → no provider/DB calls, returns 0 (early bail).
|
|
6
|
+
* - sweep_enabled off → no provider call, returns 0.
|
|
7
|
+
* - v2 on + no recent messages → no provider call, returns 0.
|
|
8
|
+
* - v2 on + recent messages → provider invoked with rendered prompt;
|
|
8
9
|
* each entry is appended to `memory/buffer.md` AND today's archive.
|
|
9
10
|
* - Tool-call response shape mismatch → returns 0 without writes.
|
|
10
11
|
* - Empty entries are skipped (the model can't pad the buffer).
|
|
@@ -24,7 +25,6 @@ import { tmpdir } from "node:os";
|
|
|
24
25
|
import { join } from "node:path";
|
|
25
26
|
import {
|
|
26
27
|
afterAll,
|
|
27
|
-
afterEach,
|
|
28
28
|
beforeAll,
|
|
29
29
|
beforeEach,
|
|
30
30
|
describe,
|
|
@@ -85,23 +85,20 @@ afterAll(() => {
|
|
|
85
85
|
const { resetDb, getDb } = await import("../../db-connection.js");
|
|
86
86
|
const { initializeDb } = await import("../../db-init.js");
|
|
87
87
|
const { messages, conversations } = await import("../../schema.js");
|
|
88
|
-
const { _setOverridesForTesting } =
|
|
89
|
-
await import("../../../config/assistant-feature-flags.js");
|
|
90
88
|
const { memoryV2SweepJob } = await import("../sweep-job.js");
|
|
91
89
|
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// the handler a minimal stand-in instead of materializing the full default
|
|
90
|
+
// The handler reads `config.memory.v2.enabled` and `sweep_enabled`, so we
|
|
91
|
+
// hand it a minimal stand-in instead of materializing the full default
|
|
95
92
|
// config — which would otherwise pull in heavy schemas this test doesn't
|
|
96
|
-
// exercise.
|
|
97
|
-
// `flag on` cases need the field set; the `flag off` case bails before
|
|
98
|
-
// the check and uses the bare empty stand-in.
|
|
93
|
+
// exercise.
|
|
99
94
|
const CONFIG = {
|
|
100
|
-
memory: { v2: { sweep_enabled: true } },
|
|
95
|
+
memory: { v2: { enabled: true, sweep_enabled: true } },
|
|
96
|
+
} as Parameters<typeof memoryV2SweepJob>[1];
|
|
97
|
+
const CONFIG_V2_OFF = {
|
|
98
|
+
memory: { v2: { enabled: false, sweep_enabled: true } },
|
|
101
99
|
} as Parameters<typeof memoryV2SweepJob>[1];
|
|
102
|
-
const CONFIG_FLAG_OFF = {} as Parameters<typeof memoryV2SweepJob>[1];
|
|
103
100
|
const CONFIG_SWEEP_OFF = {
|
|
104
|
-
memory: { v2: { sweep_enabled: false } },
|
|
101
|
+
memory: { v2: { enabled: true, sweep_enabled: false } },
|
|
105
102
|
} as Parameters<typeof memoryV2SweepJob>[1];
|
|
106
103
|
|
|
107
104
|
function makeJob(): Parameters<typeof memoryV2SweepJob>[0] {
|
|
@@ -205,18 +202,13 @@ beforeEach(() => {
|
|
|
205
202
|
providerStub = null;
|
|
206
203
|
});
|
|
207
204
|
|
|
208
|
-
afterEach(() => {
|
|
209
|
-
_setOverridesForTesting({});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
205
|
// ---------------------------------------------------------------------------
|
|
213
206
|
|
|
214
|
-
describe("memoryV2SweepJob —
|
|
215
|
-
test("returns 0 without invoking the provider when
|
|
216
|
-
_setOverridesForTesting({ "memory-v2-enabled": false });
|
|
207
|
+
describe("memoryV2SweepJob — v2 disabled", () => {
|
|
208
|
+
test("returns 0 without invoking the provider when memory.v2.enabled is false", async () => {
|
|
217
209
|
providerStub = makeEntriesProvider(["should-not-be-written"]);
|
|
218
210
|
|
|
219
|
-
const written = await memoryV2SweepJob(makeJob(),
|
|
211
|
+
const written = await memoryV2SweepJob(makeJob(), CONFIG_V2_OFF);
|
|
220
212
|
|
|
221
213
|
expect(written).toBe(0);
|
|
222
214
|
expect(providerCalls).toHaveLength(0);
|
|
@@ -224,9 +216,8 @@ describe("memoryV2SweepJob — flag off", () => {
|
|
|
224
216
|
});
|
|
225
217
|
});
|
|
226
218
|
|
|
227
|
-
describe("memoryV2SweepJob —
|
|
219
|
+
describe("memoryV2SweepJob — sweep_enabled off", () => {
|
|
228
220
|
test("returns 0 without invoking the provider when sweep_enabled is false", async () => {
|
|
229
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
230
221
|
// No message seeding required — the sweep_enabled bail short-circuits
|
|
231
222
|
// before any DB or workspace reads.
|
|
232
223
|
providerStub = makeEntriesProvider(["should-not-be-written"]);
|
|
@@ -239,11 +230,7 @@ describe("memoryV2SweepJob — flag on, sweep_enabled off", () => {
|
|
|
239
230
|
});
|
|
240
231
|
});
|
|
241
232
|
|
|
242
|
-
describe("memoryV2SweepJob —
|
|
243
|
-
beforeEach(() => {
|
|
244
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
245
|
-
});
|
|
246
|
-
|
|
233
|
+
describe("memoryV2SweepJob — no recent messages", () => {
|
|
247
234
|
test("returns 0 when no messages exist in the recent window", async () => {
|
|
248
235
|
providerStub = makeEntriesProvider(["should-not-be-written"]);
|
|
249
236
|
|
|
@@ -273,9 +260,8 @@ describe("memoryV2SweepJob — flag on, no recent messages", () => {
|
|
|
273
260
|
// DB intact long enough for the SQL inserts here to clash.
|
|
274
261
|
let convCounter = 0;
|
|
275
262
|
|
|
276
|
-
describe("memoryV2SweepJob —
|
|
263
|
+
describe("memoryV2SweepJob — recent messages", () => {
|
|
277
264
|
beforeEach(() => {
|
|
278
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
279
265
|
seedMessages(`conv-${++convCounter}`, [
|
|
280
266
|
{
|
|
281
267
|
role: "user",
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
// Memory v2 — Per-turn activation update
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
//
|
|
5
|
-
// Implements the activation formula from §4 of the design doc
|
|
5
|
+
// Implements the activation formula from §4 of the design doc plus an
|
|
6
|
+
// additive cross-encoder rerank boost on the unified top-K-by-A_o pool:
|
|
6
7
|
//
|
|
7
8
|
// A_o(n, t+1) = d · A(n, t)
|
|
8
9
|
// + c_user · sim(User_{t+1}, n)
|
|
9
10
|
// + c_assistant · sim(Assistant_t, n)
|
|
10
11
|
// + c_now · sim(NOW.md, n)
|
|
12
|
+
// + c_user · α · r_norm(User_{t+1}, n) [n ∈ topK]
|
|
13
|
+
// + c_assistant · α · r_norm(Assistant_t, n) [n ∈ topK]
|
|
11
14
|
//
|
|
12
15
|
// A(n, t+1) = [ A_o(n)
|
|
13
16
|
// + k · Σ_{m∈in1(n)} A_o(m)
|
|
@@ -40,7 +43,8 @@ import {
|
|
|
40
43
|
import { clampUnitInterval } from "../validation.js";
|
|
41
44
|
import type { EdgeIndex } from "./edge-index.js";
|
|
42
45
|
import { hybridQueryConceptPages } from "./qdrant.js";
|
|
43
|
-
import {
|
|
46
|
+
import { rerankCandidates } from "./reranker.js";
|
|
47
|
+
import { simBatch } from "./sim.js";
|
|
44
48
|
import type { ActivationState, EverInjectedEntry } from "./types.js";
|
|
45
49
|
|
|
46
50
|
/**
|
|
@@ -77,6 +81,7 @@ interface SelectCandidatesParams {
|
|
|
77
81
|
/** NOW context string (essentials/threads/recent or NOW.md). */
|
|
78
82
|
nowText: string;
|
|
79
83
|
config: AssistantConfig;
|
|
84
|
+
signal?: AbortSignal;
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
interface SelectCandidatesResult {
|
|
@@ -104,7 +109,8 @@ interface SelectCandidatesResult {
|
|
|
104
109
|
export async function selectCandidates(
|
|
105
110
|
params: SelectCandidatesParams,
|
|
106
111
|
): Promise<SelectCandidatesResult> {
|
|
107
|
-
const { priorState, userText, assistantText, nowText, config } =
|
|
112
|
+
const { priorState, userText, assistantText, nowText, config, signal } =
|
|
113
|
+
params;
|
|
108
114
|
|
|
109
115
|
const fromPrior = new Set<string>();
|
|
110
116
|
const fromAnn = new Set<string>();
|
|
@@ -125,12 +131,16 @@ export async function selectCandidates(
|
|
|
125
131
|
.join("\n");
|
|
126
132
|
|
|
127
133
|
if (annQueryText.length > 0) {
|
|
128
|
-
|
|
134
|
+
throwIfAborted(signal);
|
|
135
|
+
const denseResult = await embedWithBackend(config, [annQueryText], {
|
|
136
|
+
signal,
|
|
137
|
+
});
|
|
129
138
|
const dense = await applyCorrectionIfCalibrated(
|
|
130
139
|
denseResult.vectors[0],
|
|
131
140
|
denseResult.provider,
|
|
132
141
|
denseResult.model,
|
|
133
142
|
);
|
|
143
|
+
throwIfAborted(signal);
|
|
134
144
|
const sparse = generateSparseEmbedding(annQueryText);
|
|
135
145
|
const limit =
|
|
136
146
|
config.memory.v2.ann_candidate_limit ?? UNLIMITED_ANN_CANDIDATE_LIMIT;
|
|
@@ -154,6 +164,7 @@ interface ComputeOwnActivationParams {
|
|
|
154
164
|
assistantText: string;
|
|
155
165
|
nowText: string;
|
|
156
166
|
config: AssistantConfig;
|
|
167
|
+
signal?: AbortSignal;
|
|
157
168
|
}
|
|
158
169
|
|
|
159
170
|
/**
|
|
@@ -164,12 +175,18 @@ interface ComputeOwnActivationParams {
|
|
|
164
175
|
interface OwnActivationBreakdown {
|
|
165
176
|
/** `d * prev(slug)` — the decayed prior-turn activation contribution. */
|
|
166
177
|
priorContribution: number;
|
|
167
|
-
/** Raw `sim(user, slug)
|
|
178
|
+
/** Raw fused `sim(user, slug)`, before `c_user` weighting. */
|
|
168
179
|
simUser: number;
|
|
169
|
-
/** Raw `sim(assistant, slug)
|
|
180
|
+
/** Raw fused `sim(assistant, slug)`, before `c_assistant` weighting. */
|
|
170
181
|
simAssistant: number;
|
|
171
|
-
/** Raw `sim(now, slug)
|
|
182
|
+
/** Raw fused `sim(now, slug)`, before `c_now` weighting. */
|
|
172
183
|
simNow: number;
|
|
184
|
+
/** Rerank delta `α · r_norm_u`; 0 outside the top-K pool. Applied to `A_o` weighted by `c_user`. */
|
|
185
|
+
simUserRerankBoost: number;
|
|
186
|
+
/** Rerank delta `α · r_norm_a`; 0 outside the top-K pool. Applied to `A_o` weighted by `c_assistant`. NOW skips rerank. */
|
|
187
|
+
simAssistantRerankBoost: number;
|
|
188
|
+
/** True when this slug was in the unified top-K rerank pool. Lets the inspector distinguish "cross-encoder normalised to 0" from "rerank skipped this slug." */
|
|
189
|
+
inRerankPool: boolean;
|
|
173
190
|
}
|
|
174
191
|
|
|
175
192
|
interface ComputeOwnActivationResult {
|
|
@@ -181,21 +198,34 @@ interface ComputeOwnActivationResult {
|
|
|
181
198
|
|
|
182
199
|
/**
|
|
183
200
|
* Apply the own-activation formula
|
|
184
|
-
* A_o(n) = d · prev(n)
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* candidate set
|
|
188
|
-
*
|
|
189
|
-
*
|
|
201
|
+
* A_o(n) = d · prev(n)
|
|
202
|
+
* + c_user · sim_u + c_assistant · sim_a + c_now · sim_n
|
|
203
|
+
* + c_user · α · r_norm_u + c_assistant · α · r_norm_a
|
|
204
|
+
* over the candidate set, where the rerank terms only fire for slugs that
|
|
205
|
+
* land in the unified top-K-by-pre-rerank-A_o window. Returns a sparse map
|
|
206
|
+
* keyed by slug; slugs whose computed value rounds to 0 are still included
|
|
207
|
+
* so callers can see the candidate set explicitly. Also returns a per-slug
|
|
208
|
+
* breakdown of the raw inputs (decayed prior + raw sims + rerank deltas) so
|
|
209
|
+
* callers can render contribution diagnostics without re-running the math.
|
|
190
210
|
*
|
|
191
211
|
* The three `simBatch` calls run concurrently — they hit independent named
|
|
192
|
-
* vectors and embed independent query texts.
|
|
212
|
+
* vectors and embed independent query texts. Cross-encoder rerank then runs
|
|
213
|
+
* once on the unified top-K (selected by pre-rerank A_o, not per-channel
|
|
214
|
+
* fused sim) so an entry strong in both channels can't double-boost itself
|
|
215
|
+
* past entries that only land in one channel.
|
|
193
216
|
*/
|
|
194
217
|
export async function computeOwnActivation(
|
|
195
218
|
params: ComputeOwnActivationParams,
|
|
196
219
|
): Promise<ComputeOwnActivationResult> {
|
|
197
|
-
const {
|
|
198
|
-
|
|
220
|
+
const {
|
|
221
|
+
candidates,
|
|
222
|
+
priorState,
|
|
223
|
+
userText,
|
|
224
|
+
assistantText,
|
|
225
|
+
nowText,
|
|
226
|
+
config,
|
|
227
|
+
signal,
|
|
228
|
+
} = params;
|
|
199
229
|
|
|
200
230
|
const activation = new Map<string, number>();
|
|
201
231
|
const breakdown = new Map<string, OwnActivationBreakdown>();
|
|
@@ -204,30 +234,122 @@ export async function computeOwnActivation(
|
|
|
204
234
|
const { d, c_user, c_assistant, c_now } = config.memory.v2;
|
|
205
235
|
const slugList = [...candidates];
|
|
206
236
|
|
|
237
|
+
// NOW context is structured (timestamps, current focus) — outside the
|
|
238
|
+
// cross-encoder's training distribution, so it never participates in rerank.
|
|
207
239
|
const [simUser, simAssistant, simNow] = await Promise.all([
|
|
208
|
-
simBatch(userText, slugList, config),
|
|
209
|
-
simBatch(assistantText, slugList, config),
|
|
210
|
-
simBatch(nowText, slugList, config),
|
|
240
|
+
simBatch(userText, slugList, config, { signal }),
|
|
241
|
+
simBatch(assistantText, slugList, config, { signal }),
|
|
242
|
+
simBatch(nowText, slugList, config, { signal }),
|
|
211
243
|
]);
|
|
212
244
|
|
|
213
|
-
|
|
245
|
+
interface SlugInputs {
|
|
246
|
+
slug: string;
|
|
247
|
+
priorContribution: number;
|
|
248
|
+
simU: number;
|
|
249
|
+
simA: number;
|
|
250
|
+
simN: number;
|
|
251
|
+
/** Pre-rerank A_o; ranking signal for the unified rerank pool. */
|
|
252
|
+
preRerank: number;
|
|
253
|
+
}
|
|
254
|
+
const inputs: SlugInputs[] = slugList.map((slug) => {
|
|
214
255
|
const prev = priorState?.state[slug] ?? 0;
|
|
215
256
|
const simU = simUser.get(slug) ?? 0;
|
|
216
257
|
const simA = simAssistant.get(slug) ?? 0;
|
|
217
258
|
const simN = simNow.get(slug) ?? 0;
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
priorContribution
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
259
|
+
const priorContribution = d * prev;
|
|
260
|
+
return {
|
|
261
|
+
slug,
|
|
262
|
+
priorContribution,
|
|
263
|
+
simU,
|
|
264
|
+
simA,
|
|
265
|
+
simN,
|
|
266
|
+
preRerank:
|
|
267
|
+
priorContribution + c_user * simU + c_assistant * simA + c_now * simN,
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Unified top-K by pre-rerank A_o. Both channels rerank against the **same**
|
|
272
|
+
// slug set, so a slug strong on user can't crowd out one strong on assistant
|
|
273
|
+
// by virtue of appearing in both per-channel top-Ks. Both channel queries
|
|
274
|
+
// ride in a single `rerankCandidates` call so the worker tokenizes and
|
|
275
|
+
// forward-passes them together — half the per-call overhead of two
|
|
276
|
+
// serialised round-trips.
|
|
277
|
+
let userRerankBoost: ReadonlyMap<string, number> = new Map();
|
|
278
|
+
let assistantRerankBoost: ReadonlyMap<string, number> = new Map();
|
|
279
|
+
let inPoolSet: ReadonlySet<string> = new Set();
|
|
280
|
+
const rerankCfg = config.memory.v2.rerank;
|
|
281
|
+
if (rerankCfg?.enabled) {
|
|
282
|
+
throwIfAborted(signal);
|
|
283
|
+
const topSlugs = inputs
|
|
284
|
+
.slice()
|
|
285
|
+
.sort((a, b) => b.preRerank - a.preRerank)
|
|
286
|
+
.slice(0, rerankCfg.top_k)
|
|
287
|
+
.map((e) => e.slug);
|
|
288
|
+
if (topSlugs.length > 0) {
|
|
289
|
+
inPoolSet = new Set(topSlugs);
|
|
290
|
+
const [userScores, assistantScores] = await rerankCandidates(
|
|
291
|
+
[userText, assistantText],
|
|
292
|
+
topSlugs,
|
|
293
|
+
config,
|
|
294
|
+
);
|
|
295
|
+
throwIfAborted(signal);
|
|
296
|
+
userRerankBoost = normalizeRerankScores(userScores, rerankCfg.alpha);
|
|
297
|
+
assistantRerankBoost = normalizeRerankScores(
|
|
298
|
+
assistantScores,
|
|
299
|
+
rerankCfg.alpha,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
for (const e of inputs) {
|
|
305
|
+
const boostU = userRerankBoost.get(e.slug) ?? 0;
|
|
306
|
+
const boostA = assistantRerankBoost.get(e.slug) ?? 0;
|
|
307
|
+
activation.set(
|
|
308
|
+
e.slug,
|
|
309
|
+
clampUnitInterval(e.preRerank + c_user * boostU + c_assistant * boostA),
|
|
310
|
+
);
|
|
311
|
+
breakdown.set(e.slug, {
|
|
312
|
+
priorContribution: e.priorContribution,
|
|
313
|
+
simUser: e.simU,
|
|
314
|
+
simAssistant: e.simA,
|
|
315
|
+
simNow: e.simN,
|
|
316
|
+
simUserRerankBoost: boostU,
|
|
317
|
+
simAssistantRerankBoost: boostA,
|
|
318
|
+
inRerankPool: inPoolSet.has(e.slug),
|
|
225
319
|
});
|
|
226
320
|
}
|
|
227
321
|
|
|
228
322
|
return { activation, breakdown };
|
|
229
323
|
}
|
|
230
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Per-batch normalisation: divide raw cross-encoder scores by the channel's
|
|
327
|
+
* own max and return `alpha · r_norm` per slug. Empty input or all-zero
|
|
328
|
+
* scores yield an empty Map so the channel contributes 0 boost.
|
|
329
|
+
*/
|
|
330
|
+
function normalizeRerankScores(
|
|
331
|
+
rawScores: ReadonlyMap<string, number>,
|
|
332
|
+
alpha: number,
|
|
333
|
+
): Map<string, number> {
|
|
334
|
+
const out = new Map<string, number>();
|
|
335
|
+
if (rawScores.size === 0) return out;
|
|
336
|
+
let maxScore = 0;
|
|
337
|
+
for (const v of rawScores.values()) {
|
|
338
|
+
if (v > maxScore) maxScore = v;
|
|
339
|
+
}
|
|
340
|
+
if (maxScore === 0) return out;
|
|
341
|
+
for (const [slug, raw] of rawScores) {
|
|
342
|
+
out.set(slug, alpha * (raw / maxScore));
|
|
343
|
+
}
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
348
|
+
if (signal?.aborted) {
|
|
349
|
+
throw new DOMException("Aborted", "AbortError");
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
231
353
|
// ---------------------------------------------------------------------------
|
|
232
354
|
// Spreading activation
|
|
233
355
|
// ---------------------------------------------------------------------------
|
|
@@ -411,132 +533,3 @@ export function selectInjections(
|
|
|
411
533
|
|
|
412
534
|
return { topNow, toInject };
|
|
413
535
|
}
|
|
414
|
-
|
|
415
|
-
// ---------------------------------------------------------------------------
|
|
416
|
-
// Skill autoinjection — candidate / activation / injection selection
|
|
417
|
-
// ---------------------------------------------------------------------------
|
|
418
|
-
//
|
|
419
|
-
// Skills are stateless: there is no decay carry-over (`d · prev`), no
|
|
420
|
-
// spreading activation, and no `everInjected` dedup. The agent re-presents
|
|
421
|
-
// the top-K active skills every turn so it can drop or pick them up freely.
|
|
422
|
-
// The pipeline therefore reduces to:
|
|
423
|
-
// 1. ANN candidate selection against the dedicated skills collection.
|
|
424
|
-
// 2. Pure similarity-only activation: A_skill = c_user·sim_u +
|
|
425
|
-
// c_assistant·sim_a + c_now·sim_n, clamped to [0, 1].
|
|
426
|
-
// 3. Top-K by activation, lexicographic tie-break, no injection delta.
|
|
427
|
-
//
|
|
428
|
-
// The activation coefficients are reused from `config.memory.v2.{c_user,
|
|
429
|
-
// c_assistant, c_now}` — the design doc (§9) deliberately shares them with
|
|
430
|
-
// concept-page activation rather than introducing parallel knobs.
|
|
431
|
-
|
|
432
|
-
interface ComputeSkillActivationParams {
|
|
433
|
-
candidates: ReadonlySet<string>;
|
|
434
|
-
userText: string;
|
|
435
|
-
assistantText: string;
|
|
436
|
-
nowText: string;
|
|
437
|
-
config: AssistantConfig;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Per-skill breakdown of the raw similarity inputs, captured before any
|
|
442
|
-
* coefficient weighting. Skills have no decay term, so the breakdown is just
|
|
443
|
-
* the three raw sims. Surfaced for telemetry / inspector views.
|
|
444
|
-
*/
|
|
445
|
-
interface SkillActivationBreakdown {
|
|
446
|
-
/** Raw `sim(user, skill)` similarity, before `c_user` weighting. */
|
|
447
|
-
simUser: number;
|
|
448
|
-
/** Raw `sim(assistant, skill)` similarity, before `c_assistant` weighting. */
|
|
449
|
-
simAssistant: number;
|
|
450
|
-
/** Raw `sim(now, skill)` similarity, before `c_now` weighting. */
|
|
451
|
-
simNow: number;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
interface ComputeSkillActivationResult {
|
|
455
|
-
/** Final clamped skill-activation value per id. */
|
|
456
|
-
activation: Map<string, number>;
|
|
457
|
-
/** Per-skill breakdown of the raw sim inputs that fed into `activation`. */
|
|
458
|
-
breakdown: Map<string, SkillActivationBreakdown>;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Apply the skill-side activation formula (no decay carry-over, no spread):
|
|
463
|
-
* A_skill(s) = clamp01(c_user · sim_u + c_assistant · sim_a + c_now · sim_n)
|
|
464
|
-
*
|
|
465
|
-
* Reuses the activation coefficients from `config.memory.v2`. The three
|
|
466
|
-
* `simSkillBatch` calls run concurrently — they hit independent named
|
|
467
|
-
* vectors and embed independent query texts. Returns a per-skill breakdown
|
|
468
|
-
* of the raw sims alongside the activation map so callers can render
|
|
469
|
-
* contribution diagnostics without re-running the math.
|
|
470
|
-
*
|
|
471
|
-
* Empty candidates short-circuits to an empty map without touching the
|
|
472
|
-
* embedding backend or Qdrant.
|
|
473
|
-
*/
|
|
474
|
-
export async function computeSkillActivation(
|
|
475
|
-
params: ComputeSkillActivationParams,
|
|
476
|
-
): Promise<ComputeSkillActivationResult> {
|
|
477
|
-
const { candidates, userText, assistantText, nowText, config } = params;
|
|
478
|
-
|
|
479
|
-
const activation = new Map<string, number>();
|
|
480
|
-
const breakdown = new Map<string, SkillActivationBreakdown>();
|
|
481
|
-
if (candidates.size === 0) return { activation, breakdown };
|
|
482
|
-
|
|
483
|
-
const { c_user, c_assistant, c_now } = config.memory.v2;
|
|
484
|
-
const idList = [...candidates];
|
|
485
|
-
|
|
486
|
-
const [simUser, simAssistant, simNow] = await Promise.all([
|
|
487
|
-
simSkillBatch(userText, idList, config),
|
|
488
|
-
simSkillBatch(assistantText, idList, config),
|
|
489
|
-
simSkillBatch(nowText, idList, config),
|
|
490
|
-
]);
|
|
491
|
-
|
|
492
|
-
for (const id of idList) {
|
|
493
|
-
const simU = simUser.get(id) ?? 0;
|
|
494
|
-
const simA = simAssistant.get(id) ?? 0;
|
|
495
|
-
const simN = simNow.get(id) ?? 0;
|
|
496
|
-
const value = c_user * simU + c_assistant * simA + c_now * simN;
|
|
497
|
-
activation.set(id, clampUnitInterval(value));
|
|
498
|
-
breakdown.set(id, { simUser: simU, simAssistant: simA, simNow: simN });
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return { activation, breakdown };
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
interface SelectSkillInjectionsParams {
|
|
505
|
-
/** Final skill activation map. */
|
|
506
|
-
A: ReadonlyMap<string, number>;
|
|
507
|
-
/** Cap on the per-turn skill slate, e.g. `config.memory.v2.skills_top_k`. */
|
|
508
|
-
topK: number;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
interface SelectSkillInjectionsResult {
|
|
512
|
-
/**
|
|
513
|
-
* Top-K skill ids by activation (descending), tie-broken lexicographically.
|
|
514
|
-
* Skills are re-presented every turn — no `toInject` delta — so the caller
|
|
515
|
-
* uses this list verbatim to render the skill slate.
|
|
516
|
-
*/
|
|
517
|
-
topNow: string[];
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Pick the top-K skill ids by activation (descending; stable on ties via id
|
|
522
|
-
* lexicographic order). Skills are stateless — there is no `everInjected`
|
|
523
|
-
* dedup, so the same id can appear on consecutive turns.
|
|
524
|
-
*
|
|
525
|
-
* Returns `{ topNow: [] }` for an empty activation map or `topK <= 0`.
|
|
526
|
-
*/
|
|
527
|
-
export function selectSkillInjections(
|
|
528
|
-
params: SelectSkillInjectionsParams,
|
|
529
|
-
): SelectSkillInjectionsResult {
|
|
530
|
-
const { A, topK } = params;
|
|
531
|
-
if (A.size === 0 || topK <= 0) {
|
|
532
|
-
return { topNow: [] };
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const ranked = [...A.entries()].sort(([idA, valA], [idB, valB]) => {
|
|
536
|
-
if (valB !== valA) return valB - valA; // higher activation first
|
|
537
|
-
return idA < idB ? -1 : idA > idB ? 1 : 0; // stable tie-break
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
const topNow = ranked.slice(0, topK).map(([id]) => id);
|
|
541
|
-
return { topNow };
|
|
542
|
-
}
|