@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,23 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for
|
|
2
|
+
* Tests for v1/v2 mutual exclusion in `maybeEnqueueGraphMaintenanceJobs`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - Flag on + config on, no prior checkpoint → row enqueued, checkpoint
|
|
9
|
-
* stamped to `nowMs`.
|
|
10
|
-
* - Flag on + config on, recent checkpoint → no row enqueued (interval
|
|
11
|
-
* not yet elapsed).
|
|
12
|
-
* - Flag on + config on, stale checkpoint → row enqueued, checkpoint
|
|
13
|
-
* refreshed.
|
|
14
|
-
* - The v1 maintenance entries (decay, consolidate, pattern_scan,
|
|
15
|
-
* narrative) still fire under the v2 path — adding the v2 entry must
|
|
16
|
-
* not regress v1 scheduling.
|
|
4
|
+
* The schedule is mutually exclusive: when `memory.v2.enabled` is true,
|
|
5
|
+
* only `memory_v2_consolidate` is scheduled; otherwise the four v1
|
|
6
|
+
* entries (decay, consolidate, pattern_scan, narrative) fire and the v2
|
|
7
|
+
* entry does not.
|
|
17
8
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
9
|
+
* Coverage:
|
|
10
|
+
* - Config off → only v1 entries fire (no `memory_v2_consolidate`).
|
|
11
|
+
* - Config on, no prior checkpoint → only the v2 entry fires.
|
|
12
|
+
* - Config on, recent checkpoint → no v2 row (interval not yet elapsed).
|
|
13
|
+
* - Config on, stale checkpoint → v2 row enqueued, checkpoint refreshed.
|
|
14
|
+
*
|
|
15
|
+
* The sweep job is intentionally NOT scheduled here: it is wired into the
|
|
16
|
+
* `graph_extract` debounce in `indexer.ts`. Those triggers are covered by
|
|
17
|
+
* the separate trigger-path tests; this file owns only the cron entries.
|
|
21
18
|
*
|
|
22
19
|
* Tests use a temp workspace pinned via `VELLUM_WORKSPACE_DIR` so the DB
|
|
23
20
|
* lives under `tmpdir()` and `~/.vellum/` is never touched.
|
|
@@ -27,7 +24,6 @@ import { tmpdir } from "node:os";
|
|
|
27
24
|
import { join } from "node:path";
|
|
28
25
|
import {
|
|
29
26
|
afterAll,
|
|
30
|
-
afterEach,
|
|
31
27
|
beforeAll,
|
|
32
28
|
beforeEach,
|
|
33
29
|
describe,
|
|
@@ -69,8 +65,6 @@ const { getDb } = await import("../db-connection.js");
|
|
|
69
65
|
const { initializeDb } = await import("../db-init.js");
|
|
70
66
|
const { resetTestTables } = await import("../raw-query.js");
|
|
71
67
|
const { memoryJobs } = await import("../schema.js");
|
|
72
|
-
const { _setOverridesForTesting } =
|
|
73
|
-
await import("../../config/assistant-feature-flags.js");
|
|
74
68
|
const { applyNestedDefaults } = await import("../../config/loader.js");
|
|
75
69
|
const { setMemoryCheckpoint, deleteMemoryCheckpoint } =
|
|
76
70
|
await import("../checkpoints.js");
|
|
@@ -111,26 +105,10 @@ beforeEach(() => {
|
|
|
111
105
|
resetTestTables("memory_jobs", "memory_checkpoints");
|
|
112
106
|
});
|
|
113
107
|
|
|
114
|
-
afterEach(() => {
|
|
115
|
-
_setOverridesForTesting({});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
108
|
// ---------------------------------------------------------------------------
|
|
119
109
|
|
|
120
110
|
describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
121
|
-
test("does not enqueue consolidate when memory-v2-enabled flag is off", () => {
|
|
122
|
-
_setOverridesForTesting({ "memory-v2-enabled": false });
|
|
123
|
-
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
124
|
-
|
|
125
|
-
// Force the interval to look elapsed. If the gate failed open, this
|
|
126
|
-
// would be enough to enqueue a job.
|
|
127
|
-
maybeEnqueueGraphMaintenanceJobs(config, Date.now());
|
|
128
|
-
|
|
129
|
-
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
111
|
test("does not enqueue consolidate when config.memory.v2.enabled is off", () => {
|
|
133
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
134
112
|
const config = buildConfig({ v2Enabled: false, intervalHours: 1 });
|
|
135
113
|
|
|
136
114
|
maybeEnqueueGraphMaintenanceJobs(config, Date.now());
|
|
@@ -138,17 +116,20 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
138
116
|
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
139
117
|
});
|
|
140
118
|
|
|
141
|
-
test("enqueues consolidate when
|
|
142
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
119
|
+
test("enqueues consolidate when v2 is on and no checkpoint exists", () => {
|
|
143
120
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
144
121
|
|
|
145
122
|
maybeEnqueueGraphMaintenanceJobs(config, Date.now());
|
|
146
123
|
|
|
147
124
|
expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
|
|
125
|
+
// v1 entries are suppressed when v2 is active.
|
|
126
|
+
expect(countPendingJobs("graph_decay")).toBe(0);
|
|
127
|
+
expect(countPendingJobs("graph_consolidate")).toBe(0);
|
|
128
|
+
expect(countPendingJobs("graph_pattern_scan")).toBe(0);
|
|
129
|
+
expect(countPendingJobs("graph_narrative_refine")).toBe(0);
|
|
148
130
|
});
|
|
149
131
|
|
|
150
132
|
test("does not enqueue consolidate before the interval has elapsed", () => {
|
|
151
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
152
133
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
153
134
|
|
|
154
135
|
const now = Date.now();
|
|
@@ -161,7 +142,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
161
142
|
});
|
|
162
143
|
|
|
163
144
|
test("enqueues consolidate again once the interval elapses", () => {
|
|
164
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
165
145
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
166
146
|
|
|
167
147
|
const now = Date.now();
|
|
@@ -177,7 +157,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
177
157
|
});
|
|
178
158
|
|
|
179
159
|
test("respects a custom consolidation_interval_hours value", () => {
|
|
180
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
181
160
|
const config = buildConfig({ v2Enabled: true, intervalHours: 6 });
|
|
182
161
|
|
|
183
162
|
const now = Date.now();
|
|
@@ -200,11 +179,10 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
200
179
|
expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
|
|
201
180
|
});
|
|
202
181
|
|
|
203
|
-
test("v1
|
|
204
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
182
|
+
test("v1 maintenance entries are suppressed when v2 is active", () => {
|
|
205
183
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
206
184
|
|
|
207
|
-
// No checkpoints set — every entry
|
|
185
|
+
// No checkpoints set — every entry would be due if it were scheduled.
|
|
208
186
|
deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
|
|
209
187
|
deleteMemoryCheckpoint("graph_maintenance:consolidate:last_run");
|
|
210
188
|
deleteMemoryCheckpoint("graph_maintenance:pattern_scan:last_run");
|
|
@@ -213,23 +191,28 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
213
191
|
|
|
214
192
|
maybeEnqueueGraphMaintenanceJobs(config, Date.now());
|
|
215
193
|
|
|
216
|
-
expect(countPendingJobs("graph_decay")).toBe(
|
|
217
|
-
expect(countPendingJobs("graph_consolidate")).toBe(
|
|
218
|
-
expect(countPendingJobs("graph_pattern_scan")).toBe(
|
|
219
|
-
expect(countPendingJobs("graph_narrative_refine")).toBe(
|
|
194
|
+
expect(countPendingJobs("graph_decay")).toBe(0);
|
|
195
|
+
expect(countPendingJobs("graph_consolidate")).toBe(0);
|
|
196
|
+
expect(countPendingJobs("graph_pattern_scan")).toBe(0);
|
|
197
|
+
expect(countPendingJobs("graph_narrative_refine")).toBe(0);
|
|
220
198
|
expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
|
|
221
199
|
});
|
|
222
200
|
|
|
223
|
-
test("
|
|
224
|
-
|
|
225
|
-
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
201
|
+
test("v2-off path fires v1 entries and does not enqueue v2", () => {
|
|
202
|
+
const config = buildConfig({ v2Enabled: false, intervalHours: 1 });
|
|
226
203
|
|
|
227
204
|
deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
|
|
205
|
+
deleteMemoryCheckpoint("graph_maintenance:consolidate:last_run");
|
|
206
|
+
deleteMemoryCheckpoint("graph_maintenance:pattern_scan:last_run");
|
|
207
|
+
deleteMemoryCheckpoint("graph_maintenance:narrative:last_run");
|
|
228
208
|
deleteMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY);
|
|
229
209
|
|
|
230
210
|
maybeEnqueueGraphMaintenanceJobs(config, Date.now());
|
|
231
211
|
|
|
232
212
|
expect(countPendingJobs("graph_decay")).toBe(1);
|
|
213
|
+
expect(countPendingJobs("graph_consolidate")).toBe(1);
|
|
214
|
+
expect(countPendingJobs("graph_pattern_scan")).toBe(1);
|
|
215
|
+
expect(countPendingJobs("graph_narrative_refine")).toBe(1);
|
|
233
216
|
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
234
217
|
});
|
|
235
218
|
});
|
|
@@ -29,7 +29,6 @@ import { memoryV2ActivationLogs } from "../schema.js";
|
|
|
29
29
|
import {
|
|
30
30
|
sampleConcepts,
|
|
31
31
|
sampleConfig,
|
|
32
|
-
sampleSkills,
|
|
33
32
|
} from "./fixtures/memory-v2-activation-fixtures.js";
|
|
34
33
|
|
|
35
34
|
initializeDb();
|
|
@@ -53,7 +52,6 @@ describe("memory-v2-activation-log-store", () => {
|
|
|
53
52
|
turn: 3,
|
|
54
53
|
mode: "per-turn",
|
|
55
54
|
concepts: sampleConcepts,
|
|
56
|
-
skills: sampleSkills,
|
|
57
55
|
config: sampleConfig,
|
|
58
56
|
});
|
|
59
57
|
|
|
@@ -65,7 +63,6 @@ describe("memory-v2-activation-log-store", () => {
|
|
|
65
63
|
expect(result!.turn).toBe(3);
|
|
66
64
|
expect(result!.mode).toBe("per-turn");
|
|
67
65
|
expect(result!.concepts).toEqual(sampleConcepts);
|
|
68
|
-
expect(result!.skills).toEqual(sampleSkills);
|
|
69
66
|
expect(result!.config).toEqual(sampleConfig);
|
|
70
67
|
});
|
|
71
68
|
|
|
@@ -82,7 +79,6 @@ describe("memory-v2-activation-log-store", () => {
|
|
|
82
79
|
turn: 1,
|
|
83
80
|
mode: "context-load",
|
|
84
81
|
concepts: sampleConcepts,
|
|
85
|
-
skills: sampleSkills,
|
|
86
82
|
config: sampleConfig,
|
|
87
83
|
});
|
|
88
84
|
recordMemoryV2ActivationLog({
|
|
@@ -90,7 +86,6 @@ describe("memory-v2-activation-log-store", () => {
|
|
|
90
86
|
turn: 2,
|
|
91
87
|
mode: "per-turn",
|
|
92
88
|
concepts: sampleConcepts,
|
|
93
|
-
skills: sampleSkills,
|
|
94
89
|
config: sampleConfig,
|
|
95
90
|
});
|
|
96
91
|
|
|
@@ -110,7 +105,6 @@ describe("memory-v2-activation-log-store", () => {
|
|
|
110
105
|
turn: 3,
|
|
111
106
|
mode: "per-turn",
|
|
112
107
|
concepts: sampleConcepts,
|
|
113
|
-
skills: sampleSkills,
|
|
114
108
|
config: sampleConfig,
|
|
115
109
|
});
|
|
116
110
|
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
mock.module("../../util/logger.js", () => ({
|
|
4
|
+
getLogger: () =>
|
|
5
|
+
new Proxy({} as Record<string, unknown>, {
|
|
6
|
+
get: () => () => {},
|
|
7
|
+
}),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
mock.module("../../config/loader.js", () => ({
|
|
11
|
+
getConfig: () => ({
|
|
12
|
+
ui: {},
|
|
13
|
+
model: "test",
|
|
14
|
+
provider: "test",
|
|
15
|
+
memory: { enabled: false },
|
|
16
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
17
|
+
secretDetection: { enabled: false },
|
|
18
|
+
}),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
let listPagesImpl: (workspaceDir: string) => Promise<string[]> = async () => [];
|
|
22
|
+
|
|
23
|
+
mock.module("../v2/page-store.js", () => ({
|
|
24
|
+
listPages: (workspaceDir: string) => listPagesImpl(workspaceDir),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
import { getDb } from "../db-connection.js";
|
|
28
|
+
import { initializeDb } from "../db-init.js";
|
|
29
|
+
import {
|
|
30
|
+
type MemoryV2ConceptRowRecord,
|
|
31
|
+
recordMemoryV2ActivationLog,
|
|
32
|
+
} from "../memory-v2-activation-log-store.js";
|
|
33
|
+
import { getConceptFrequencySummary } from "../memory-v2-concept-frequency.js";
|
|
34
|
+
import { memoryV2ActivationLogs } from "../schema.js";
|
|
35
|
+
import { sampleConfig } from "./fixtures/memory-v2-activation-fixtures.js";
|
|
36
|
+
|
|
37
|
+
initializeDb();
|
|
38
|
+
|
|
39
|
+
const WORKSPACE = "/tmp/memory-v2-concept-frequency-test";
|
|
40
|
+
|
|
41
|
+
function makeConcept(
|
|
42
|
+
slug: string,
|
|
43
|
+
status: MemoryV2ConceptRowRecord["status"],
|
|
44
|
+
): MemoryV2ConceptRowRecord {
|
|
45
|
+
return {
|
|
46
|
+
slug,
|
|
47
|
+
finalActivation: 0.5,
|
|
48
|
+
ownActivation: 0.4,
|
|
49
|
+
priorActivation: 0.1,
|
|
50
|
+
simUser: 0.3,
|
|
51
|
+
simAssistant: 0.2,
|
|
52
|
+
simNow: 0.1,
|
|
53
|
+
simUserRerankBoost: 0,
|
|
54
|
+
simAssistantRerankBoost: 0,
|
|
55
|
+
inRerankPool: false,
|
|
56
|
+
spreadContribution: 0.1,
|
|
57
|
+
source: "ann_top50",
|
|
58
|
+
status,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resetTables(): void {
|
|
63
|
+
getDb().delete(memoryV2ActivationLogs).run();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("memory-v2-concept-frequency", () => {
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
resetTables();
|
|
69
|
+
listPagesImpl = async () => [];
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("aggregates per-status counts across multiple turns", async () => {
|
|
73
|
+
const conv = "conv-1";
|
|
74
|
+
recordMemoryV2ActivationLog({
|
|
75
|
+
conversationId: conv,
|
|
76
|
+
turn: 1,
|
|
77
|
+
mode: "context-load",
|
|
78
|
+
concepts: [
|
|
79
|
+
makeConcept("alice", "injected"),
|
|
80
|
+
makeConcept("bob", "not_injected"),
|
|
81
|
+
],
|
|
82
|
+
config: sampleConfig,
|
|
83
|
+
});
|
|
84
|
+
recordMemoryV2ActivationLog({
|
|
85
|
+
conversationId: conv,
|
|
86
|
+
turn: 2,
|
|
87
|
+
mode: "per-turn",
|
|
88
|
+
concepts: [
|
|
89
|
+
makeConcept("alice", "in_context"),
|
|
90
|
+
makeConcept("bob", "injected"),
|
|
91
|
+
],
|
|
92
|
+
config: sampleConfig,
|
|
93
|
+
});
|
|
94
|
+
recordMemoryV2ActivationLog({
|
|
95
|
+
conversationId: conv,
|
|
96
|
+
turn: 3,
|
|
97
|
+
mode: "per-turn",
|
|
98
|
+
concepts: [
|
|
99
|
+
makeConcept("alice", "injected"),
|
|
100
|
+
makeConcept("charlie", "page_missing"),
|
|
101
|
+
],
|
|
102
|
+
config: sampleConfig,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
listPagesImpl = async () => ["alice", "bob", "delta"];
|
|
106
|
+
|
|
107
|
+
const result = await getConceptFrequencySummary(WORKSPACE, {});
|
|
108
|
+
|
|
109
|
+
expect(result.totals.logCount).toBe(3);
|
|
110
|
+
expect(result.totals.conceptOccurrences).toBe(6);
|
|
111
|
+
expect(result.filters).toEqual({ conversationId: null, sinceMs: null });
|
|
112
|
+
|
|
113
|
+
const bySlug = new Map(result.concepts.map((c) => [c.slug, c]));
|
|
114
|
+
|
|
115
|
+
expect(bySlug.get("alice")!.counts).toEqual({
|
|
116
|
+
injected: 2,
|
|
117
|
+
in_context: 1,
|
|
118
|
+
not_injected: 0,
|
|
119
|
+
page_missing: 0,
|
|
120
|
+
});
|
|
121
|
+
expect(bySlug.get("alice")!.totalEvaluations).toBe(3);
|
|
122
|
+
expect(bySlug.get("alice")!.onDisk).toBe(true);
|
|
123
|
+
expect(bySlug.get("alice")!.lastInjectedAt).not.toBeNull();
|
|
124
|
+
|
|
125
|
+
expect(bySlug.get("bob")!.counts).toEqual({
|
|
126
|
+
injected: 1,
|
|
127
|
+
in_context: 0,
|
|
128
|
+
not_injected: 1,
|
|
129
|
+
page_missing: 0,
|
|
130
|
+
});
|
|
131
|
+
expect(bySlug.get("bob")!.totalEvaluations).toBe(2);
|
|
132
|
+
expect(bySlug.get("bob")!.onDisk).toBe(true);
|
|
133
|
+
|
|
134
|
+
expect(bySlug.get("charlie")!.counts).toEqual({
|
|
135
|
+
injected: 0,
|
|
136
|
+
in_context: 0,
|
|
137
|
+
not_injected: 0,
|
|
138
|
+
page_missing: 1,
|
|
139
|
+
});
|
|
140
|
+
expect(bySlug.get("charlie")!.onDisk).toBe(false);
|
|
141
|
+
expect(bySlug.get("charlie")!.lastInjectedAt).toBeNull();
|
|
142
|
+
|
|
143
|
+
// Sorted by totalEvaluations desc — alice (3) before bob (2) before charlie (1).
|
|
144
|
+
expect(result.concepts.map((c) => c.slug)).toEqual([
|
|
145
|
+
"alice",
|
|
146
|
+
"bob",
|
|
147
|
+
"charlie",
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
// delta is on disk but never appeared in any log row.
|
|
151
|
+
expect(result.neverEvaluatedSlugs).toEqual(["delta"]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("conversationId filter narrows aggregation", async () => {
|
|
155
|
+
recordMemoryV2ActivationLog({
|
|
156
|
+
conversationId: "conv-a",
|
|
157
|
+
turn: 1,
|
|
158
|
+
mode: "per-turn",
|
|
159
|
+
concepts: [makeConcept("alice", "injected")],
|
|
160
|
+
config: sampleConfig,
|
|
161
|
+
});
|
|
162
|
+
recordMemoryV2ActivationLog({
|
|
163
|
+
conversationId: "conv-b",
|
|
164
|
+
turn: 1,
|
|
165
|
+
mode: "per-turn",
|
|
166
|
+
concepts: [
|
|
167
|
+
makeConcept("alice", "injected"),
|
|
168
|
+
makeConcept("alice", "injected"),
|
|
169
|
+
],
|
|
170
|
+
config: sampleConfig,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
listPagesImpl = async () => ["alice"];
|
|
174
|
+
|
|
175
|
+
const all = await getConceptFrequencySummary(WORKSPACE, {});
|
|
176
|
+
expect(all.totals.logCount).toBe(2);
|
|
177
|
+
expect(all.concepts[0]!.counts.injected).toBe(3);
|
|
178
|
+
expect(all.filters.conversationId).toBeNull();
|
|
179
|
+
|
|
180
|
+
const onlyA = await getConceptFrequencySummary(WORKSPACE, {
|
|
181
|
+
conversationId: "conv-a",
|
|
182
|
+
});
|
|
183
|
+
expect(onlyA.totals.logCount).toBe(1);
|
|
184
|
+
expect(onlyA.concepts[0]!.counts.injected).toBe(1);
|
|
185
|
+
expect(onlyA.filters.conversationId).toBe("conv-a");
|
|
186
|
+
|
|
187
|
+
const onlyB = await getConceptFrequencySummary(WORKSPACE, {
|
|
188
|
+
conversationId: "conv-b",
|
|
189
|
+
});
|
|
190
|
+
expect(onlyB.totals.logCount).toBe(1);
|
|
191
|
+
expect(onlyB.concepts[0]!.counts.injected).toBe(2);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("sinceMs filter excludes older log rows", async () => {
|
|
195
|
+
recordMemoryV2ActivationLog({
|
|
196
|
+
conversationId: "conv-1",
|
|
197
|
+
turn: 1,
|
|
198
|
+
mode: "per-turn",
|
|
199
|
+
concepts: [makeConcept("alice", "injected")],
|
|
200
|
+
config: sampleConfig,
|
|
201
|
+
});
|
|
202
|
+
// Backdate the just-written row — recordMemoryV2ActivationLog uses Date.now().
|
|
203
|
+
getDb().update(memoryV2ActivationLogs).set({ createdAt: 1_000 }).run();
|
|
204
|
+
|
|
205
|
+
recordMemoryV2ActivationLog({
|
|
206
|
+
conversationId: "conv-1",
|
|
207
|
+
turn: 2,
|
|
208
|
+
mode: "per-turn",
|
|
209
|
+
concepts: [makeConcept("alice", "injected")],
|
|
210
|
+
config: sampleConfig,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
listPagesImpl = async () => ["alice"];
|
|
214
|
+
|
|
215
|
+
const all = await getConceptFrequencySummary(WORKSPACE, {});
|
|
216
|
+
expect(all.totals.logCount).toBe(2);
|
|
217
|
+
expect(all.concepts[0]!.counts.injected).toBe(2);
|
|
218
|
+
|
|
219
|
+
const recent = await getConceptFrequencySummary(WORKSPACE, {
|
|
220
|
+
sinceMs: 10_000,
|
|
221
|
+
});
|
|
222
|
+
expect(recent.totals.logCount).toBe(1);
|
|
223
|
+
expect(recent.concepts[0]!.counts.injected).toBe(1);
|
|
224
|
+
expect(recent.filters.sinceMs).toBe(10_000);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("never-evaluated list excludes slugs that appeared in any status", async () => {
|
|
228
|
+
recordMemoryV2ActivationLog({
|
|
229
|
+
conversationId: "conv-1",
|
|
230
|
+
turn: 1,
|
|
231
|
+
mode: "per-turn",
|
|
232
|
+
concepts: [
|
|
233
|
+
makeConcept("alice", "injected"),
|
|
234
|
+
makeConcept("bob", "not_injected"),
|
|
235
|
+
makeConcept("charlie", "page_missing"),
|
|
236
|
+
],
|
|
237
|
+
config: sampleConfig,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
listPagesImpl = async () => ["alice", "bob", "delta", "echo"];
|
|
241
|
+
|
|
242
|
+
const result = await getConceptFrequencySummary(WORKSPACE, {});
|
|
243
|
+
// bob was scored but rejected — still excluded from neverEvaluated.
|
|
244
|
+
expect(result.neverEvaluatedSlugs).toEqual(["delta", "echo"]);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("returns empty result when no logs exist", async () => {
|
|
248
|
+
listPagesImpl = async () => ["alice", "bob"];
|
|
249
|
+
|
|
250
|
+
const result = await getConceptFrequencySummary(WORKSPACE, {});
|
|
251
|
+
expect(result.totals).toEqual({ logCount: 0, conceptOccurrences: 0 });
|
|
252
|
+
expect(result.concepts).toEqual([]);
|
|
253
|
+
expect(result.neverEvaluatedSlugs).toEqual(["alice", "bob"]);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("flags slugs that appear in logs but no longer have a page on disk", async () => {
|
|
257
|
+
recordMemoryV2ActivationLog({
|
|
258
|
+
conversationId: "conv-1",
|
|
259
|
+
turn: 1,
|
|
260
|
+
mode: "per-turn",
|
|
261
|
+
concepts: [makeConcept("ghost", "injected")],
|
|
262
|
+
config: sampleConfig,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
listPagesImpl = async () => ["alice"];
|
|
266
|
+
|
|
267
|
+
const result = await getConceptFrequencySummary(WORKSPACE, {});
|
|
268
|
+
const ghost = result.concepts.find((c) => c.slug === "ghost")!;
|
|
269
|
+
expect(ghost.onDisk).toBe(false);
|
|
270
|
+
expect(ghost.counts.injected).toBe(1);
|
|
271
|
+
});
|
|
272
|
+
});
|
package/src/memory/admin.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { getWorkspaceDir } from "../util/platform.js";
|
|
|
6
6
|
import { deleteMemoryCheckpoint } from "./checkpoints.js";
|
|
7
7
|
import { runDeterministicRecallSearch } from "./context-search/search.js";
|
|
8
8
|
import type { RecallEvidence } from "./context-search/types.js";
|
|
9
|
-
import { getConversationMemoryScopeId } from "./conversation-crud.js";
|
|
10
9
|
import { getDb } from "./db-connection.js";
|
|
11
10
|
import { getMemoryBackendStatus } from "./embedding-backend.js";
|
|
12
11
|
import {
|
|
@@ -32,7 +31,7 @@ import {
|
|
|
32
31
|
|
|
33
32
|
const log = getLogger("memory-admin");
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
interface MemorySystemStatus {
|
|
36
35
|
enabled: boolean;
|
|
37
36
|
degraded: boolean;
|
|
38
37
|
reason: string | null;
|
|
@@ -47,7 +46,7 @@ export interface MemorySystemStatus {
|
|
|
47
46
|
jobs: Record<string, number>;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
interface AdminMemoryQueryResult {
|
|
51
50
|
results: Array<{
|
|
52
51
|
id: string;
|
|
53
52
|
content: string;
|
|
@@ -106,7 +105,6 @@ export async function queryMemory(
|
|
|
106
105
|
{ query, sources: ["memory", "conversations", "pkb"] },
|
|
107
106
|
{
|
|
108
107
|
workingDir: getWorkspaceDir(),
|
|
109
|
-
memoryScopeId: getConversationMemoryScopeId(conversationId) ?? "default",
|
|
110
108
|
conversationId,
|
|
111
109
|
config,
|
|
112
110
|
},
|
|
@@ -148,7 +146,7 @@ function readNumericMetadata(
|
|
|
148
146
|
|
|
149
147
|
// ── Short segment cleanup ─────────────────────────────────────────────
|
|
150
148
|
|
|
151
|
-
|
|
149
|
+
interface CleanupShortSegmentsResult {
|
|
152
150
|
removed: number;
|
|
153
151
|
failed: number;
|
|
154
152
|
dryRunCount?: number;
|
|
@@ -216,7 +214,7 @@ export async function compactLongMemoryNodes(
|
|
|
216
214
|
|
|
217
215
|
// ── Re-extraction ──────────────────────────────────────────────────────
|
|
218
216
|
|
|
219
|
-
|
|
217
|
+
interface ReextractTarget {
|
|
220
218
|
conversationId: string;
|
|
221
219
|
title: string | null;
|
|
222
220
|
messageCount: number;
|
|
@@ -312,11 +310,9 @@ export function requestReextract(targets: ReextractTarget[]): {
|
|
|
312
310
|
)
|
|
313
311
|
.run();
|
|
314
312
|
|
|
315
|
-
// Resolve scope and enqueue re-extraction
|
|
316
|
-
const scopeId = getConversationMemoryScopeId(conversationId);
|
|
317
313
|
const jobId = enqueueMemoryJob("graph_extract", {
|
|
318
314
|
conversationId,
|
|
319
|
-
scopeId,
|
|
315
|
+
scopeId: "default",
|
|
320
316
|
});
|
|
321
317
|
jobIds.push(jobId);
|
|
322
318
|
|
|
@@ -306,7 +306,17 @@ export async function runAgenticRecall(
|
|
|
306
306
|
[...RECALL_AGENT_TOOL_DEFINITIONS],
|
|
307
307
|
undefined,
|
|
308
308
|
{
|
|
309
|
-
|
|
309
|
+
// `thinking: disabled` is required because we set `temperature: 0`
|
|
310
|
+
// explicitly. Anthropic 400s on `temperature` ≠ 1 whenever thinking
|
|
311
|
+
// is enabled or in adaptive mode; without this, profiles that
|
|
312
|
+
// resolve thinking-enabled (Opus 4.x at `effort: high|xhigh`, etc.)
|
|
313
|
+
// would fail. Recall is tool-call-heavy reasoning where determinism
|
|
314
|
+
// matters more than extended chain-of-thought.
|
|
315
|
+
config: {
|
|
316
|
+
callSite: "recall",
|
|
317
|
+
temperature: 0,
|
|
318
|
+
thinking: { type: "disabled" },
|
|
319
|
+
},
|
|
310
320
|
signal: context.signal,
|
|
311
321
|
},
|
|
312
322
|
);
|
|
@@ -597,7 +607,14 @@ async function tryFinalFinishRecall(options: {
|
|
|
597
607
|
[FINISH_RECALL_TOOL_DEFINITION],
|
|
598
608
|
undefined,
|
|
599
609
|
{
|
|
600
|
-
|
|
610
|
+
// `thinking: disabled` required for the same reason as the agent
|
|
611
|
+
// round above — Anthropic 400s on `temperature` ≠ 1 whenever
|
|
612
|
+
// thinking is enabled or in adaptive mode.
|
|
613
|
+
config: {
|
|
614
|
+
callSite: "recall",
|
|
615
|
+
temperature: 0,
|
|
616
|
+
thinking: { type: "disabled" },
|
|
617
|
+
},
|
|
601
618
|
signal: options.context.signal,
|
|
602
619
|
},
|
|
603
620
|
);
|
|
@@ -88,10 +88,7 @@ export async function searchConversationSource(
|
|
|
88
88
|
|
|
89
89
|
for (const ftsMatch of ftsMatches) {
|
|
90
90
|
try {
|
|
91
|
-
rows = mergeConversationRows(
|
|
92
|
-
rows,
|
|
93
|
-
searchWithFts(ftsMatch, context.memoryScopeId, queryLimit),
|
|
94
|
-
);
|
|
91
|
+
rows = mergeConversationRows(rows, searchWithFts(ftsMatch, queryLimit));
|
|
95
92
|
} catch {
|
|
96
93
|
// Try the next, broader query shape.
|
|
97
94
|
}
|
|
@@ -100,7 +97,7 @@ export async function searchConversationSource(
|
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
if (rows.length === 0) {
|
|
103
|
-
rows = searchWithLike(trimmedQuery,
|
|
100
|
+
rows = searchWithLike(trimmedQuery, queryLimit);
|
|
104
101
|
}
|
|
105
102
|
|
|
106
103
|
const sortedRows = rows
|
|
@@ -130,7 +127,6 @@ export async function searchConversationSource(
|
|
|
130
127
|
|
|
131
128
|
function searchWithFts(
|
|
132
129
|
ftsMatch: string,
|
|
133
|
-
memoryScopeId: string,
|
|
134
130
|
limit: number,
|
|
135
131
|
): ConversationEvidenceRow[] {
|
|
136
132
|
return rawAll<ConversationEvidenceRow>(
|
|
@@ -146,14 +142,12 @@ function searchWithFts(
|
|
|
146
142
|
JOIN messages m ON m.id = messages_fts.message_id
|
|
147
143
|
JOIN conversations c ON c.id = m.conversation_id
|
|
148
144
|
WHERE messages_fts MATCH ?
|
|
149
|
-
AND c.memory_scope_id = ?
|
|
150
145
|
AND (c.conversation_type IS NULL OR c.conversation_type != 'private')
|
|
151
146
|
AND (c.source IS NULL OR c.source NOT IN (?, ?))
|
|
152
147
|
ORDER BY bm25(messages_fts), m.created_at DESC
|
|
153
148
|
LIMIT ?
|
|
154
149
|
`,
|
|
155
150
|
ftsMatch,
|
|
156
|
-
memoryScopeId,
|
|
157
151
|
SUBAGENT_SOURCE,
|
|
158
152
|
AUTO_ANALYSIS_SOURCE,
|
|
159
153
|
limit,
|
|
@@ -162,7 +156,6 @@ function searchWithFts(
|
|
|
162
156
|
|
|
163
157
|
function searchWithLike(
|
|
164
158
|
query: string,
|
|
165
|
-
memoryScopeId: string,
|
|
166
159
|
limit: number,
|
|
167
160
|
): ConversationEvidenceRow[] {
|
|
168
161
|
return rawAll<ConversationEvidenceRow>(
|
|
@@ -177,14 +170,12 @@ function searchWithLike(
|
|
|
177
170
|
FROM messages m
|
|
178
171
|
JOIN conversations c ON c.id = m.conversation_id
|
|
179
172
|
WHERE m.content LIKE ? ESCAPE '\\'
|
|
180
|
-
AND c.memory_scope_id = ?
|
|
181
173
|
AND (c.conversation_type IS NULL OR c.conversation_type != 'private')
|
|
182
174
|
AND (c.source IS NULL OR c.source NOT IN (?, ?))
|
|
183
175
|
ORDER BY m.created_at DESC
|
|
184
176
|
LIMIT ?
|
|
185
177
|
`,
|
|
186
178
|
buildLikePattern(query),
|
|
187
|
-
memoryScopeId,
|
|
188
179
|
SUBAGENT_SOURCE,
|
|
189
180
|
AUTO_ANALYSIS_SOURCE,
|
|
190
181
|
limit,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Memory v2 — `recall` adapter for the `memory` source
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
//
|
|
5
|
-
// When
|
|
5
|
+
// When v2 is enabled, the `memory` recall source reads from the v2
|
|
6
6
|
// concept-page subsystem (under `<workspace>/memory/concepts/`) instead of
|
|
7
7
|
// the legacy graph. Two retrieval paths run in parallel and merge:
|
|
8
8
|
//
|
|
@@ -26,8 +26,6 @@
|
|
|
26
26
|
import { readdir, readFile, realpath, stat } from "node:fs/promises";
|
|
27
27
|
import { extname, isAbsolute, join, relative } from "node:path";
|
|
28
28
|
|
|
29
|
-
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
30
|
-
import type { AssistantConfig } from "../../../config/schema.js";
|
|
31
29
|
import { getLogger } from "../../../util/logger.js";
|
|
32
30
|
import { embedWithRetry } from "../../embed.js";
|
|
33
31
|
import { generateSparseEmbedding } from "../../embedding-backend.js";
|
|
@@ -46,19 +44,6 @@ import type {
|
|
|
46
44
|
RecallSearchResult,
|
|
47
45
|
} from "../types.js";
|
|
48
46
|
|
|
49
|
-
/**
|
|
50
|
-
* True when both v2 gates are on. Single source of truth shared by the recall
|
|
51
|
-
* `memory` source (which delegates to v2), the `pkb` source (which
|
|
52
|
-
* short-circuits because v2 absorbs PKB as a read source), and the per-turn
|
|
53
|
-
* PKB injectors (which go silent so v2 owns the read path end-to-end).
|
|
54
|
-
*/
|
|
55
|
-
export function isMemoryV2ReadActive(config: AssistantConfig): boolean {
|
|
56
|
-
return (
|
|
57
|
-
isAssistantFeatureFlagEnabled("memory-v2-enabled", config) &&
|
|
58
|
-
config.memory.v2.enabled
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
47
|
const log = getLogger("context-search-memory-v2-source");
|
|
63
48
|
|
|
64
49
|
/**
|