@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
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/** Local cross-encoder rerank backend — drives the rerank-worker subprocess. */
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
import type { RerankDtype } from "../config/schemas/memory-v2.js";
|
|
5
|
+
import { getLogger } from "../util/logger.js";
|
|
6
|
+
import { getEmbeddingModelsDir } from "../util/platform.js";
|
|
7
|
+
import { PromiseGuard } from "../util/promise-guard.js";
|
|
8
|
+
import { EmbeddingRuntimeManager } from "./embedding-runtime-manager.js";
|
|
9
|
+
|
|
10
|
+
const log = getLogger("memory-rerank-local");
|
|
11
|
+
|
|
12
|
+
interface WorkerResponse {
|
|
13
|
+
id?: number;
|
|
14
|
+
type?: string;
|
|
15
|
+
scores?: number[];
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class LocalRerankBackend {
|
|
20
|
+
readonly model: string;
|
|
21
|
+
readonly dtype: RerankDtype;
|
|
22
|
+
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
private workerProc: any = null;
|
|
25
|
+
private stdoutBuffer = "";
|
|
26
|
+
private requestCounter = 0;
|
|
27
|
+
private pendingRequests = new Map<
|
|
28
|
+
number,
|
|
29
|
+
{ resolve: (response: WorkerResponse) => void }
|
|
30
|
+
>();
|
|
31
|
+
private stdoutReaderActive = false;
|
|
32
|
+
private activeRequests = 0;
|
|
33
|
+
private disposeRequested = false;
|
|
34
|
+
|
|
35
|
+
private readyResolve: (() => void) | null = null;
|
|
36
|
+
private readyReject: ((err: Error) => void) | null = null;
|
|
37
|
+
|
|
38
|
+
private readonly initGuard = new PromiseGuard<void>();
|
|
39
|
+
|
|
40
|
+
constructor(model: string, dtype: RerankDtype) {
|
|
41
|
+
this.model = model;
|
|
42
|
+
this.dtype = dtype;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Score paired `(queries[i], passages[i])` tuples in one batched ONNX
|
|
47
|
+
* inference call. Multiple distinct queries can ride in a single batch
|
|
48
|
+
* so callers can score the user-channel and assistant-channel queries
|
|
49
|
+
* against a shared candidate set in one tokenizer + forward pass.
|
|
50
|
+
*/
|
|
51
|
+
async score(queries: string[], passages: string[]): Promise<number[]> {
|
|
52
|
+
if (this.disposeRequested) {
|
|
53
|
+
throw new Error("Local rerank backend is shutting down");
|
|
54
|
+
}
|
|
55
|
+
if (passages.length === 0) return [];
|
|
56
|
+
if (queries.length !== passages.length) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Rerank backend got ${queries.length} queries for ${passages.length} passages`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.activeRequests++;
|
|
63
|
+
try {
|
|
64
|
+
await this.ensureInitialized();
|
|
65
|
+
const response = await this.sendRequest({ queries, passages });
|
|
66
|
+
if (response.error) {
|
|
67
|
+
throw new Error(`Rerank worker error: ${response.error}`);
|
|
68
|
+
}
|
|
69
|
+
if (!response.scores) {
|
|
70
|
+
throw new Error("Rerank worker returned no scores");
|
|
71
|
+
}
|
|
72
|
+
if (response.scores.length !== passages.length) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Rerank worker returned ${response.scores.length} scores for ${passages.length} passages`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return response.scores;
|
|
78
|
+
} finally {
|
|
79
|
+
this.activeRequests--;
|
|
80
|
+
this.disposeIfIdle();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
dispose(): void {
|
|
85
|
+
this.disposeRequested = true;
|
|
86
|
+
this.disposeIfIdle();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private sendRequest(payload: {
|
|
90
|
+
queries: string[];
|
|
91
|
+
passages: string[];
|
|
92
|
+
}): Promise<WorkerResponse> {
|
|
93
|
+
const id = ++this.requestCounter;
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
if (!this.workerProc) {
|
|
96
|
+
resolve({ error: "Worker not initialized" });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.pendingRequests.set(id, { resolve });
|
|
100
|
+
this.workerProc.stdin.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
101
|
+
try {
|
|
102
|
+
this.workerProc.stdin.flush();
|
|
103
|
+
} catch {
|
|
104
|
+
// Worker may have exited — stdout reader cleanup resolves pending requests.
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private async ensureInitialized(): Promise<void> {
|
|
110
|
+
if (this.workerProc) return;
|
|
111
|
+
await this.initGuard.run(() => this.initialize());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private async initialize(): Promise<void> {
|
|
115
|
+
log.info({ model: this.model }, "Initializing local rerank backend");
|
|
116
|
+
|
|
117
|
+
const runtimeManager = new EmbeddingRuntimeManager();
|
|
118
|
+
if (!runtimeManager.isReady()) {
|
|
119
|
+
log.info("Embedding runtime not yet available, waiting for download...");
|
|
120
|
+
await runtimeManager.ensureInstalled();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const bunPath = runtimeManager.getBunPath();
|
|
124
|
+
const workerPath = runtimeManager.getRerankWorkerPath();
|
|
125
|
+
|
|
126
|
+
if (!bunPath) {
|
|
127
|
+
throw new Error("Local rerank backend unavailable: no bun binary found");
|
|
128
|
+
}
|
|
129
|
+
if (!existsSync(workerPath)) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Local rerank backend unavailable: worker script not found at ${workerPath}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
await this.startWorker(bunPath, workerPath);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private async startWorker(
|
|
139
|
+
bunPath: string,
|
|
140
|
+
workerPath: string,
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
const embeddingModelsDir = getEmbeddingModelsDir();
|
|
143
|
+
const modelCacheDir = `${embeddingModelsDir}/model-cache`;
|
|
144
|
+
|
|
145
|
+
log.info(
|
|
146
|
+
{ bunPath, workerPath, model: this.model, dtype: this.dtype },
|
|
147
|
+
"Spawning rerank worker process",
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const proc = Bun.spawn({
|
|
151
|
+
cmd: [
|
|
152
|
+
bunPath,
|
|
153
|
+
"--smol",
|
|
154
|
+
workerPath,
|
|
155
|
+
this.model,
|
|
156
|
+
modelCacheDir,
|
|
157
|
+
this.dtype,
|
|
158
|
+
],
|
|
159
|
+
stdin: "pipe",
|
|
160
|
+
stdout: "pipe",
|
|
161
|
+
stderr: "pipe",
|
|
162
|
+
cwd: embeddingModelsDir,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
this.workerProc = proc;
|
|
166
|
+
this.startStdoutReader();
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
await this.waitForReady();
|
|
170
|
+
} catch (err) {
|
|
171
|
+
this.workerProc = null;
|
|
172
|
+
this.stdoutReaderActive = false;
|
|
173
|
+
try {
|
|
174
|
+
proc.kill();
|
|
175
|
+
} catch {
|
|
176
|
+
/* may already be dead */
|
|
177
|
+
}
|
|
178
|
+
const exitCode = await proc.exited.catch(() => undefined);
|
|
179
|
+
const stderr = await new Response(proc.stderr).text().catch(() => "");
|
|
180
|
+
if (stderr.trim()) {
|
|
181
|
+
log.warn({ stderr: stderr.trim(), exitCode }, "Rerank worker stderr");
|
|
182
|
+
}
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Rerank worker exited (code ${exitCode ?? "unknown"}): ${
|
|
185
|
+
stderr.trim() || (err instanceof Error ? err.message : String(err))
|
|
186
|
+
}`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.drainStderr(proc.stderr);
|
|
191
|
+
log.info(
|
|
192
|
+
{ pid: proc.pid, model: this.model },
|
|
193
|
+
"Rerank worker process started",
|
|
194
|
+
);
|
|
195
|
+
this.disposeIfIdle();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private drainStderr(stderr: ReadableStream<Uint8Array>): void {
|
|
199
|
+
const reader = stderr.getReader();
|
|
200
|
+
const decoder = new TextDecoder();
|
|
201
|
+
(async () => {
|
|
202
|
+
try {
|
|
203
|
+
while (true) {
|
|
204
|
+
const { done, value } = await reader.read();
|
|
205
|
+
if (done) break;
|
|
206
|
+
const text = decoder.decode(value, { stream: true }).trim();
|
|
207
|
+
if (text) log.debug({ workerStderr: text }, "Rerank worker stderr");
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
/* expected on shutdown */
|
|
211
|
+
}
|
|
212
|
+
})();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private startStdoutReader(): void {
|
|
216
|
+
if (this.stdoutReaderActive || !this.workerProc) return;
|
|
217
|
+
this.stdoutReaderActive = true;
|
|
218
|
+
|
|
219
|
+
const proc = this.workerProc;
|
|
220
|
+
const reader = proc.stdout.getReader();
|
|
221
|
+
const decoder = new TextDecoder();
|
|
222
|
+
|
|
223
|
+
(async () => {
|
|
224
|
+
try {
|
|
225
|
+
while (true) {
|
|
226
|
+
const { done, value } = await reader.read();
|
|
227
|
+
if (done) break;
|
|
228
|
+
this.stdoutBuffer += decoder.decode(value, { stream: true });
|
|
229
|
+
this.processStdoutBuffer();
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
/* reader cancelled or stream errored */
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (this.workerProc === proc) {
|
|
236
|
+
for (const pending of this.pendingRequests.values()) {
|
|
237
|
+
pending.resolve({
|
|
238
|
+
error: "Rerank worker process exited unexpectedly",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
this.pendingRequests.clear();
|
|
242
|
+
this.workerProc = null;
|
|
243
|
+
this.stdoutReaderActive = false;
|
|
244
|
+
this.stdoutBuffer = "";
|
|
245
|
+
this.initGuard.reset();
|
|
246
|
+
}
|
|
247
|
+
})();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private processStdoutBuffer(): void {
|
|
251
|
+
let idx: number;
|
|
252
|
+
while ((idx = this.stdoutBuffer.indexOf("\n")) !== -1) {
|
|
253
|
+
const line = this.stdoutBuffer.slice(0, idx);
|
|
254
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(idx + 1);
|
|
255
|
+
if (!line.trim()) continue;
|
|
256
|
+
|
|
257
|
+
let msg: WorkerResponse;
|
|
258
|
+
try {
|
|
259
|
+
msg = JSON.parse(line);
|
|
260
|
+
} catch {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (msg.type === "ready") {
|
|
265
|
+
this.readyResolve?.();
|
|
266
|
+
this.readyResolve = null;
|
|
267
|
+
this.readyReject = null;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (msg.type === "error" && this.readyReject) {
|
|
271
|
+
this.readyReject(
|
|
272
|
+
new Error(msg.error ?? "Worker initialization failed"),
|
|
273
|
+
);
|
|
274
|
+
this.readyResolve = null;
|
|
275
|
+
this.readyReject = null;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (msg.id !== undefined) {
|
|
280
|
+
const pending = this.pendingRequests.get(msg.id);
|
|
281
|
+
if (pending) {
|
|
282
|
+
this.pendingRequests.delete(msg.id);
|
|
283
|
+
pending.resolve(msg);
|
|
284
|
+
this.disposeIfIdle();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private waitForReady(): Promise<void> {
|
|
291
|
+
return new Promise<void>((resolve, reject) => {
|
|
292
|
+
// First-call timeout. Generous because the first run downloads the
|
|
293
|
+
// ONNX weights (~280 MB to ~1 GB depending on model) before loading.
|
|
294
|
+
const timeout = setTimeout(() => {
|
|
295
|
+
this.readyResolve = null;
|
|
296
|
+
this.readyReject = null;
|
|
297
|
+
reject(new Error("Rerank worker timed out waiting for model to load"));
|
|
298
|
+
}, 120_000);
|
|
299
|
+
|
|
300
|
+
this.readyResolve = () => {
|
|
301
|
+
clearTimeout(timeout);
|
|
302
|
+
resolve();
|
|
303
|
+
};
|
|
304
|
+
this.readyReject = (err: Error) => {
|
|
305
|
+
clearTimeout(timeout);
|
|
306
|
+
reject(err);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
this.workerProc?.exited.then(() => {
|
|
310
|
+
if (this.readyResolve) {
|
|
311
|
+
clearTimeout(timeout);
|
|
312
|
+
this.readyResolve = null;
|
|
313
|
+
this.readyReject = null;
|
|
314
|
+
reject(
|
|
315
|
+
new Error("Rerank worker process exited before becoming ready"),
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private disposeIfIdle(): void {
|
|
323
|
+
if (!this.disposeRequested) return;
|
|
324
|
+
if (this.activeRequests > 0) return;
|
|
325
|
+
if (this.pendingRequests.size > 0) return;
|
|
326
|
+
if (this.readyResolve || this.readyReject) return;
|
|
327
|
+
|
|
328
|
+
const proc = this.workerProc;
|
|
329
|
+
this.workerProc = null;
|
|
330
|
+
this.stdoutReaderActive = false;
|
|
331
|
+
this.stdoutBuffer = "";
|
|
332
|
+
this.initGuard.reset();
|
|
333
|
+
|
|
334
|
+
if (!proc) return;
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
proc.kill();
|
|
338
|
+
} catch {
|
|
339
|
+
/* may already be exiting */
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ── Module-level singleton management ─────────────────────────────────
|
|
345
|
+
|
|
346
|
+
let _backend: LocalRerankBackend | null = null;
|
|
347
|
+
|
|
348
|
+
export function getOrCreateRerankBackend(
|
|
349
|
+
model: string,
|
|
350
|
+
dtype: RerankDtype,
|
|
351
|
+
): LocalRerankBackend {
|
|
352
|
+
if (_backend?.model === model && _backend.dtype === dtype) return _backend;
|
|
353
|
+
if (_backend) {
|
|
354
|
+
try {
|
|
355
|
+
_backend.dispose();
|
|
356
|
+
} catch {
|
|
357
|
+
/* best effort */
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
_backend = new LocalRerankBackend(model, dtype);
|
|
361
|
+
return _backend;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** @internal Test-only: reset the cached backend. */
|
|
365
|
+
export function _resetRerankBackendForTests(): void {
|
|
366
|
+
if (_backend) {
|
|
367
|
+
try {
|
|
368
|
+
_backend.dispose();
|
|
369
|
+
} catch {
|
|
370
|
+
/* best effort */
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
_backend = null;
|
|
374
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { inArray } from "drizzle-orm";
|
|
2
2
|
|
|
3
3
|
import { getConfig } from "../../config/loader.js";
|
|
4
|
-
import { isMemoryV2ReadActive } from "../context-search/sources/memory-v2.js";
|
|
5
4
|
import { getDb } from "../db-connection.js";
|
|
6
5
|
import { withQdrantBreaker } from "../qdrant-circuit-breaker.js";
|
|
7
6
|
import type {
|
|
@@ -9,7 +8,7 @@ import type {
|
|
|
9
8
|
QdrantSparseVector,
|
|
10
9
|
} from "../qdrant-client.js";
|
|
11
10
|
import { getQdrantClient } from "../qdrant-client.js";
|
|
12
|
-
import {
|
|
11
|
+
import { memorySegments, memorySummaries } from "../schema.js";
|
|
13
12
|
// ── Types (inlined from deleted types.ts) ──────────────────────────
|
|
14
13
|
|
|
15
14
|
type CandidateType = "segment" | "item" | "summary" | "media";
|
|
@@ -52,15 +51,14 @@ export async function semanticSearch(
|
|
|
52
51
|
_model: string,
|
|
53
52
|
limit: number,
|
|
54
53
|
excludedMessageIds: string[] = [],
|
|
55
|
-
scopeIds?: string[],
|
|
56
54
|
sparseVector?: QdrantSparseVector,
|
|
57
55
|
): Promise<Candidate[]> {
|
|
58
56
|
if (limit <= 0) return [];
|
|
59
57
|
|
|
60
|
-
// v2 owns the read path when
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
if (
|
|
58
|
+
// v2 owns the read path when enabled; the v1 `memory` collection is in
|
|
59
|
+
// active retirement, and routing semantic recall there would re-enter the
|
|
60
|
+
// same corrupted sparse segments that can OOM-crash Qdrant.
|
|
61
|
+
if (getConfig().memory.v2.enabled) return [];
|
|
64
62
|
|
|
65
63
|
const qdrant = getQdrantClient();
|
|
66
64
|
|
|
@@ -75,7 +73,7 @@ export async function semanticSearch(
|
|
|
75
73
|
let isHybrid = false;
|
|
76
74
|
if (sparseVector && sparseVector.indices.length > 0) {
|
|
77
75
|
isHybrid = true;
|
|
78
|
-
const filter = buildHybridFilter(excludedMessageIds
|
|
76
|
+
const filter = buildHybridFilter(excludedMessageIds);
|
|
79
77
|
results = await withQdrantBreaker(() =>
|
|
80
78
|
qdrant.hybridSearch({
|
|
81
79
|
denseVector: queryVector,
|
|
@@ -92,7 +90,6 @@ export async function semanticSearch(
|
|
|
92
90
|
fetchLimit,
|
|
93
91
|
["summary", "segment", "media"],
|
|
94
92
|
excludedMessageIds,
|
|
95
|
-
scopeIds,
|
|
96
93
|
),
|
|
97
94
|
);
|
|
98
95
|
}
|
|
@@ -102,18 +99,15 @@ export async function semanticSearch(
|
|
|
102
99
|
// Batch-fetch all backing records upfront to avoid N+1 queries per result
|
|
103
100
|
const summaryTargetIds: string[] = [];
|
|
104
101
|
const segmentTargetIds: string[] = [];
|
|
105
|
-
const mediaConversationIds: string[] = [];
|
|
106
102
|
for (const r of results) {
|
|
107
103
|
if (r.payload.target_type === "summary")
|
|
108
104
|
summaryTargetIds.push(r.payload.target_id);
|
|
109
105
|
else if (r.payload.target_type === "segment")
|
|
110
106
|
segmentTargetIds.push(r.payload.target_id);
|
|
111
|
-
else if (r.payload.target_type === "media" && r.payload.conversation_id)
|
|
112
|
-
mediaConversationIds.push(r.payload.conversation_id);
|
|
113
107
|
}
|
|
114
108
|
|
|
115
109
|
const summariesMap = new Map<string, typeof memorySummaries.$inferSelect>();
|
|
116
|
-
if (
|
|
110
|
+
if (summaryTargetIds.length > 0) {
|
|
117
111
|
const allSummaries = db
|
|
118
112
|
.select()
|
|
119
113
|
.from(memorySummaries)
|
|
@@ -123,7 +117,7 @@ export async function semanticSearch(
|
|
|
123
117
|
}
|
|
124
118
|
|
|
125
119
|
const segmentsMap = new Map<string, typeof memorySegments.$inferSelect>();
|
|
126
|
-
if (
|
|
120
|
+
if (segmentTargetIds.length > 0) {
|
|
127
121
|
const allSegments = db
|
|
128
122
|
.select()
|
|
129
123
|
.from(memorySegments)
|
|
@@ -132,23 +126,6 @@ export async function semanticSearch(
|
|
|
132
126
|
for (const seg of allSegments) segmentsMap.set(seg.id, seg);
|
|
133
127
|
}
|
|
134
128
|
|
|
135
|
-
// Batch-fetch conversation scope IDs for media results to avoid N+1 queries.
|
|
136
|
-
// When a conversation is not found (deleted), its media is excluded rather than
|
|
137
|
-
// falling back to "default" scope, which would leak private media.
|
|
138
|
-
const mediaScopeMap = new Map<string, string>();
|
|
139
|
-
if (scopeIds && mediaConversationIds.length > 0) {
|
|
140
|
-
const unique = [...new Set(mediaConversationIds)];
|
|
141
|
-
const rows = db
|
|
142
|
-
.select({
|
|
143
|
-
id: conversations.id,
|
|
144
|
-
memoryScopeId: conversations.memoryScopeId,
|
|
145
|
-
})
|
|
146
|
-
.from(conversations)
|
|
147
|
-
.where(inArray(conversations.id, unique))
|
|
148
|
-
.all();
|
|
149
|
-
for (const row of rows) mediaScopeMap.set(row.id, row.memoryScopeId);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
129
|
const candidates: Candidate[] = [];
|
|
153
130
|
for (const result of results) {
|
|
154
131
|
const { payload, score } = result;
|
|
@@ -160,10 +137,7 @@ export async function semanticSearch(
|
|
|
160
137
|
// Legacy item vectors — skip (table dropped, Qdrant cleanup pending)
|
|
161
138
|
continue;
|
|
162
139
|
} else if (payload.target_type === "summary") {
|
|
163
|
-
if (
|
|
164
|
-
const summary = summariesMap.get(payload.target_id);
|
|
165
|
-
if (!summary || !scopeIds.includes(summary.scopeId)) continue;
|
|
166
|
-
}
|
|
140
|
+
if (!summariesMap.has(payload.target_id)) continue;
|
|
167
141
|
candidates.push({
|
|
168
142
|
key: `summary:${payload.target_id}`,
|
|
169
143
|
type: "summary",
|
|
@@ -180,22 +154,6 @@ export async function semanticSearch(
|
|
|
180
154
|
finalScore: 0,
|
|
181
155
|
});
|
|
182
156
|
} else if (payload.target_type === "media") {
|
|
183
|
-
// Use stored memory_scope_id when available; fall back to deriving
|
|
184
|
-
// scope from conversation_id for legacy media points.
|
|
185
|
-
// If the conversation was deleted, skip the media to avoid leaking
|
|
186
|
-
// private media into the default scope.
|
|
187
|
-
if (scopeIds) {
|
|
188
|
-
let mediaScopeId: string | undefined;
|
|
189
|
-
if (payload.memory_scope_id) {
|
|
190
|
-
mediaScopeId = payload.memory_scope_id;
|
|
191
|
-
} else if (payload.conversation_id) {
|
|
192
|
-
mediaScopeId = mediaScopeMap.get(payload.conversation_id);
|
|
193
|
-
if (!mediaScopeId) continue; // conversation deleted — skip
|
|
194
|
-
} else {
|
|
195
|
-
mediaScopeId = "default";
|
|
196
|
-
}
|
|
197
|
-
if (!scopeIds.includes(mediaScopeId)) continue;
|
|
198
|
-
}
|
|
199
157
|
candidates.push({
|
|
200
158
|
key: `media:${payload.target_id}`,
|
|
201
159
|
type: "media",
|
|
@@ -212,10 +170,7 @@ export async function semanticSearch(
|
|
|
212
170
|
finalScore: 0,
|
|
213
171
|
});
|
|
214
172
|
} else {
|
|
215
|
-
if (
|
|
216
|
-
const segment = segmentsMap.get(payload.target_id);
|
|
217
|
-
if (!segment || !scopeIds.includes(segment.scopeId)) continue;
|
|
218
|
-
}
|
|
173
|
+
if (!segmentsMap.has(payload.target_id)) continue;
|
|
219
174
|
candidates.push({
|
|
220
175
|
key: `segment:${payload.target_id}`,
|
|
221
176
|
type: "segment",
|
|
@@ -254,14 +209,9 @@ export async function semanticSearch(
|
|
|
254
209
|
/**
|
|
255
210
|
* Build a Qdrant filter for hybrid search. Mirrors the logic in
|
|
256
211
|
* `searchWithFilter` but as a standalone object for the query API.
|
|
257
|
-
*
|
|
258
|
-
* Scope filtering: points with a `memory_scope_id` payload field are
|
|
259
|
-
* filtered at the Qdrant level. Legacy points without the field pass
|
|
260
|
-
* through and are caught by post-query DB filtering.
|
|
261
212
|
*/
|
|
262
213
|
function buildHybridFilter(
|
|
263
214
|
excludeMessageIds: string[],
|
|
264
|
-
scopeIds?: string[],
|
|
265
215
|
): Record<string, unknown> {
|
|
266
216
|
const mustConditions: Array<Record<string, unknown>> = [
|
|
267
217
|
{
|
|
@@ -270,18 +220,6 @@ function buildHybridFilter(
|
|
|
270
220
|
},
|
|
271
221
|
];
|
|
272
222
|
|
|
273
|
-
// Scope filtering: accept points whose memory_scope_id matches one of the
|
|
274
|
-
// allowed scopes, OR points that lack the field entirely (legacy data).
|
|
275
|
-
// Post-query DB filtering remains as defense-in-depth for legacy points.
|
|
276
|
-
if (scopeIds && scopeIds.length > 0) {
|
|
277
|
-
mustConditions.push({
|
|
278
|
-
should: [
|
|
279
|
-
{ key: "memory_scope_id", match: { any: scopeIds } },
|
|
280
|
-
{ is_empty: { key: "memory_scope_id" } },
|
|
281
|
-
],
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
223
|
const mustNotConditions: Array<Record<string, unknown>> = [
|
|
286
224
|
{ key: "_meta", match: { value: true } },
|
|
287
225
|
];
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { and, asc, eq, gt,
|
|
1
|
+
import { and, asc, eq, gt, sql } from "drizzle-orm";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
TraceEvent,
|
|
5
5
|
TraceEventKind,
|
|
6
6
|
} from "../daemon/message-types/messages.js";
|
|
7
7
|
import { getDb } from "./db-connection.js";
|
|
8
|
-
import { rawChanges } from "./raw-query.js";
|
|
9
8
|
import { traceEvents } from "./schema.js";
|
|
10
9
|
|
|
11
10
|
// ---------------------------------------------------------------------------
|
|
@@ -115,21 +114,6 @@ export function getTraceEvents(
|
|
|
115
114
|
return rows.map(rowToTraceEventRow);
|
|
116
115
|
}
|
|
117
116
|
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// Cleanup
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Delete trace events older than `maxAgeDays` based on `created_at`.
|
|
124
|
-
* Returns the count of deleted rows.
|
|
125
|
-
*/
|
|
126
|
-
export function deleteOldTraceEvents(maxAgeDays: number): number {
|
|
127
|
-
const db = getDb();
|
|
128
|
-
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
|
|
129
|
-
db.delete(traceEvents).where(lt(traceEvents.createdAt, cutoff)).run();
|
|
130
|
-
return rawChanges();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
117
|
// ---------------------------------------------------------------------------
|
|
134
118
|
// Sequence
|
|
135
119
|
// ---------------------------------------------------------------------------
|