@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
|
@@ -98,52 +98,93 @@ export async function embedConceptPageJob(
|
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
const contentHash = embeddingInputContentHash({ type: "text", text });
|
|
102
101
|
const expectedDim = config.memory.qdrant.vectorSize;
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
// The status provider is the cache lookup key for any prior row; the
|
|
103
|
+
// *actual* provider/model come back on the embedded result. They usually
|
|
104
|
+
// match, but a backend swap mid-run would surface here — body and summary
|
|
105
|
+
// are then re-embedded together so both rows write under the same identity.
|
|
106
|
+
const cacheProvider = status.provider;
|
|
107
|
+
const cacheModel = status.model!;
|
|
108
|
+
|
|
109
|
+
const db = getDb();
|
|
105
110
|
|
|
106
111
|
// Cache lookup: same (targetType, targetId, provider, model) row gets
|
|
107
112
|
// reused across runs as long as `contentHash` matches. The dim mismatch
|
|
108
113
|
// check guards against a config change (vectorSize bumped) since the last
|
|
109
|
-
// write — in that case we treat the row as stale and re-embed.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
114
|
+
// write — in that case we treat the row as stale and re-embed. The body
|
|
115
|
+
// and (optional) summary share the same provider/model — but each gets
|
|
116
|
+
// its own cache row keyed by a distinct targetId so summary edits don't
|
|
117
|
+
// invalidate the body cache and vice versa.
|
|
118
|
+
const bodyContentHash = embeddingInputContentHash({ type: "text", text });
|
|
119
|
+
const bodyCache = readEmbeddingCache(
|
|
120
|
+
db,
|
|
121
|
+
slug,
|
|
122
|
+
cacheProvider,
|
|
123
|
+
cacheModel,
|
|
124
|
+
expectedDim,
|
|
125
|
+
);
|
|
126
|
+
const bodyCacheHit = bodyCache?.contentHash === bodyContentHash;
|
|
127
|
+
|
|
128
|
+
// Optional summary embedding — only when the page has a `summary` in its
|
|
129
|
+
// frontmatter. Pages without one fall back to body-only retrieval at
|
|
130
|
+
// query time (the activation pipeline reads the summary score as
|
|
131
|
+
// undefined and uses the body score directly).
|
|
132
|
+
const summaryText = page.frontmatter.summary?.trim() ?? "";
|
|
133
|
+
const hasSummary = summaryText.length > 0;
|
|
134
|
+
const summaryCacheId = `${slug}#summary`;
|
|
135
|
+
const summaryContentHash = hasSummary
|
|
136
|
+
? embeddingInputContentHash({ type: "text", text: summaryText })
|
|
137
|
+
: undefined;
|
|
138
|
+
const summaryCache = hasSummary
|
|
139
|
+
? readEmbeddingCache(
|
|
140
|
+
db,
|
|
141
|
+
summaryCacheId,
|
|
142
|
+
cacheProvider,
|
|
143
|
+
cacheModel,
|
|
144
|
+
expectedDim,
|
|
145
|
+
)
|
|
146
|
+
: null;
|
|
147
|
+
const summaryCacheHit =
|
|
148
|
+
hasSummary && summaryCache?.contentHash === summaryContentHash;
|
|
149
|
+
|
|
150
|
+
// Batch all cache misses into one `embedWithBackend` call. Each backend
|
|
151
|
+
// round-trip is the dominant cost — fresh body + fresh summary in a
|
|
152
|
+
// single batch saves a round-trip vs serial calls and gives both vectors
|
|
153
|
+
// the same provider/model regardless of any backend rotation mid-run.
|
|
154
|
+
type Slot = "body" | "summary";
|
|
155
|
+
const toEmbed: Array<{ type: "text"; text: string }> = [];
|
|
156
|
+
const slots: Slot[] = [];
|
|
157
|
+
if (!bodyCacheHit) {
|
|
158
|
+
toEmbed.push({ type: "text", text });
|
|
159
|
+
slots.push("body");
|
|
160
|
+
}
|
|
161
|
+
if (hasSummary && !summaryCacheHit) {
|
|
162
|
+
toEmbed.push({ type: "text", text: summaryText });
|
|
163
|
+
slots.push("summary");
|
|
145
164
|
}
|
|
146
165
|
|
|
166
|
+
let bodyDense: number[] | undefined = bodyCacheHit ? bodyCache!.dense : undefined;
|
|
167
|
+
let summaryDense: number[] | undefined = summaryCacheHit
|
|
168
|
+
? summaryCache!.dense
|
|
169
|
+
: undefined;
|
|
170
|
+
let writeProvider = cacheProvider;
|
|
171
|
+
let writeModel = cacheModel;
|
|
172
|
+
if (toEmbed.length > 0) {
|
|
173
|
+
const embedded = await embedWithBackend(config, toEmbed);
|
|
174
|
+
writeProvider = embedded.provider;
|
|
175
|
+
writeModel = embedded.model;
|
|
176
|
+
for (let i = 0; i < slots.length; i++) {
|
|
177
|
+
const vector = embedded.vectors[i];
|
|
178
|
+
if (!vector) continue;
|
|
179
|
+
if (slots[i] === "body") bodyDense = vector;
|
|
180
|
+
else summaryDense = vector;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Body embedding is the ground truth — without it the page can't surface.
|
|
184
|
+
// (Cache hit paths populate `bodyDense` above; a fresh embed that returned
|
|
185
|
+
// no vectors short-circuits here too.)
|
|
186
|
+
if (!bodyDense) return;
|
|
187
|
+
|
|
147
188
|
// Sparse is cheap (in-process tokenization) and changes any time the body
|
|
148
189
|
// changes, so we always recompute it rather than caching alongside dense.
|
|
149
190
|
// BM25 weights live on the doc side; queries embed binary occurrence in
|
|
@@ -151,57 +192,42 @@ export async function embedConceptPageJob(
|
|
|
151
192
|
// corpus for the first time), fall back to the legacy TF-only encoding —
|
|
152
193
|
// the next reembed pass overwrites the page once stats are available.
|
|
153
194
|
const corpusStats = getConceptPageCorpusStats();
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
195
|
+
const encodeSparse = (input: string) =>
|
|
196
|
+
corpusStats
|
|
197
|
+
? generateBm25DocEmbedding(input, corpusStats, {
|
|
198
|
+
k1: config.memory.v2.bm25_k1,
|
|
199
|
+
b: config.memory.v2.bm25_b,
|
|
200
|
+
})
|
|
201
|
+
: generateSparseEmbedding(input);
|
|
202
|
+
const sparse = encodeSparse(text);
|
|
203
|
+
const summarySparse = hasSummary ? encodeSparse(summaryText) : undefined;
|
|
160
204
|
|
|
161
205
|
const now = Date.now();
|
|
162
206
|
// Persist freshly embedded vectors for cross-restart reuse. On cache hit
|
|
163
207
|
// the existing row already has identical content + hash, so the write
|
|
164
208
|
// would be a no-op — skip it. Best-effort: write failure is not fatal,
|
|
165
209
|
// we still want the Qdrant upsert below to fire.
|
|
166
|
-
if (!
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
memoryEmbeddings.provider,
|
|
188
|
-
memoryEmbeddings.model,
|
|
189
|
-
],
|
|
190
|
-
set: {
|
|
191
|
-
vectorBlob: blobValue,
|
|
192
|
-
vectorJson: null,
|
|
193
|
-
dimensions: dense.length,
|
|
194
|
-
contentHash,
|
|
195
|
-
updatedAt: now,
|
|
196
|
-
},
|
|
197
|
-
})
|
|
198
|
-
.run();
|
|
199
|
-
} catch (err) {
|
|
200
|
-
log.warn(
|
|
201
|
-
{ err, slug },
|
|
202
|
-
"Failed to write concept-page embedding cache row",
|
|
203
|
-
);
|
|
204
|
-
}
|
|
210
|
+
if (!bodyCacheHit) {
|
|
211
|
+
writeEmbeddingCache(db, {
|
|
212
|
+
slug,
|
|
213
|
+
cacheId: slug,
|
|
214
|
+
dense: bodyDense,
|
|
215
|
+
contentHash: bodyContentHash,
|
|
216
|
+
provider: writeProvider,
|
|
217
|
+
model: writeModel,
|
|
218
|
+
now,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (hasSummary && !summaryCacheHit && summaryDense && summaryContentHash) {
|
|
222
|
+
writeEmbeddingCache(db, {
|
|
223
|
+
slug,
|
|
224
|
+
cacheId: summaryCacheId,
|
|
225
|
+
dense: summaryDense,
|
|
226
|
+
contentHash: summaryContentHash,
|
|
227
|
+
provider: writeProvider,
|
|
228
|
+
model: writeModel,
|
|
229
|
+
now,
|
|
230
|
+
});
|
|
205
231
|
}
|
|
206
232
|
|
|
207
233
|
// Apply anisotropy correction at the boundary between the (raw) cached
|
|
@@ -210,19 +236,129 @@ export async function embedConceptPageJob(
|
|
|
210
236
|
// the cache survives and the (cheap) correction math reruns over each
|
|
211
237
|
// cached vector. Pass-through when no calibration is fit yet.
|
|
212
238
|
const correctedDense = await applyCorrectionIfCalibrated(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
239
|
+
bodyDense,
|
|
240
|
+
writeProvider,
|
|
241
|
+
writeModel,
|
|
216
242
|
);
|
|
243
|
+
const correctedSummaryDense = summaryDense
|
|
244
|
+
? await applyCorrectionIfCalibrated(summaryDense, writeProvider, writeModel)
|
|
245
|
+
: undefined;
|
|
217
246
|
|
|
218
247
|
await upsertConceptPageEmbedding({
|
|
219
248
|
slug,
|
|
220
249
|
dense: correctedDense,
|
|
221
250
|
sparse,
|
|
251
|
+
summary:
|
|
252
|
+
correctedSummaryDense && summarySparse
|
|
253
|
+
? { dense: correctedSummaryDense, sparse: summarySparse }
|
|
254
|
+
: undefined,
|
|
222
255
|
updatedAt: now,
|
|
223
256
|
});
|
|
224
257
|
}
|
|
225
258
|
|
|
259
|
+
/** SQLite cache row shape returned by `readEmbeddingCache`. */
|
|
260
|
+
interface EmbeddingCacheEntry {
|
|
261
|
+
dense: number[];
|
|
262
|
+
contentHash: string;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Look up a cached dense vector keyed on `(targetType, targetId, provider,
|
|
267
|
+
* model)`. Returns the row only when the persisted dimensions match the
|
|
268
|
+
* configured expectation — a stale row from a previous `vectorSize` is
|
|
269
|
+
* treated as a cache miss so the caller re-embeds.
|
|
270
|
+
*/
|
|
271
|
+
function readEmbeddingCache(
|
|
272
|
+
db: ReturnType<typeof getDb>,
|
|
273
|
+
cacheId: string,
|
|
274
|
+
provider: string,
|
|
275
|
+
model: string,
|
|
276
|
+
expectedDim: number,
|
|
277
|
+
): EmbeddingCacheEntry | null {
|
|
278
|
+
const row = db
|
|
279
|
+
.select({
|
|
280
|
+
vectorBlob: memoryEmbeddings.vectorBlob,
|
|
281
|
+
vectorJson: memoryEmbeddings.vectorJson,
|
|
282
|
+
dimensions: memoryEmbeddings.dimensions,
|
|
283
|
+
contentHash: memoryEmbeddings.contentHash,
|
|
284
|
+
})
|
|
285
|
+
.from(memoryEmbeddings)
|
|
286
|
+
.where(
|
|
287
|
+
and(
|
|
288
|
+
eq(memoryEmbeddings.targetType, CONCEPT_PAGE_TARGET_TYPE),
|
|
289
|
+
eq(memoryEmbeddings.targetId, cacheId),
|
|
290
|
+
eq(memoryEmbeddings.provider, provider),
|
|
291
|
+
eq(memoryEmbeddings.model, model),
|
|
292
|
+
),
|
|
293
|
+
)
|
|
294
|
+
.get();
|
|
295
|
+
if (!row || row.dimensions !== expectedDim) return null;
|
|
296
|
+
// A row without a contentHash is a legacy/corrupt entry — treat as a miss
|
|
297
|
+
// and force a re-embed rather than misalign the cache key.
|
|
298
|
+
if (row.contentHash === null) return null;
|
|
299
|
+
const dense = row.vectorBlob
|
|
300
|
+
? blobToVector(row.vectorBlob as Buffer)
|
|
301
|
+
: (JSON.parse(row.vectorJson!) as number[]);
|
|
302
|
+
return { dense, contentHash: row.contentHash };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Persist a freshly embedded dense vector in the SQLite cache. Best-effort:
|
|
307
|
+
* a write failure is logged and swallowed so the Qdrant upsert still runs.
|
|
308
|
+
*/
|
|
309
|
+
function writeEmbeddingCache(
|
|
310
|
+
db: ReturnType<typeof getDb>,
|
|
311
|
+
params: {
|
|
312
|
+
slug: string;
|
|
313
|
+
cacheId: string;
|
|
314
|
+
dense: number[];
|
|
315
|
+
contentHash: string;
|
|
316
|
+
provider: string;
|
|
317
|
+
model: string;
|
|
318
|
+
now: number;
|
|
319
|
+
},
|
|
320
|
+
): void {
|
|
321
|
+
const { slug, cacheId, dense, contentHash, provider, model, now } = params;
|
|
322
|
+
try {
|
|
323
|
+
const blobValue = vectorToBlob(dense);
|
|
324
|
+
db.insert(memoryEmbeddings)
|
|
325
|
+
.values({
|
|
326
|
+
id: randomUUID(),
|
|
327
|
+
targetType: CONCEPT_PAGE_TARGET_TYPE,
|
|
328
|
+
targetId: cacheId,
|
|
329
|
+
provider,
|
|
330
|
+
model,
|
|
331
|
+
dimensions: dense.length,
|
|
332
|
+
vectorBlob: blobValue,
|
|
333
|
+
vectorJson: null,
|
|
334
|
+
contentHash,
|
|
335
|
+
createdAt: now,
|
|
336
|
+
updatedAt: now,
|
|
337
|
+
})
|
|
338
|
+
.onConflictDoUpdate({
|
|
339
|
+
target: [
|
|
340
|
+
memoryEmbeddings.targetType,
|
|
341
|
+
memoryEmbeddings.targetId,
|
|
342
|
+
memoryEmbeddings.provider,
|
|
343
|
+
memoryEmbeddings.model,
|
|
344
|
+
],
|
|
345
|
+
set: {
|
|
346
|
+
vectorBlob: blobValue,
|
|
347
|
+
vectorJson: null,
|
|
348
|
+
dimensions: dense.length,
|
|
349
|
+
contentHash,
|
|
350
|
+
updatedAt: now,
|
|
351
|
+
},
|
|
352
|
+
})
|
|
353
|
+
.run();
|
|
354
|
+
} catch (err) {
|
|
355
|
+
log.warn(
|
|
356
|
+
{ err, slug, cacheId },
|
|
357
|
+
"Failed to write concept-page embedding cache row",
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
226
362
|
/**
|
|
227
363
|
* Enqueue an `embed_concept_page` job (async, fire-and-forget). Modeled on
|
|
228
364
|
* `enqueuePkbIndexJob` — callers that want a slug re-embedded after a write
|
package/src/memory/jobs-store.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type MemoryJobType =
|
|
|
18
18
|
| "embed_summary"
|
|
19
19
|
| "prune_old_conversations"
|
|
20
20
|
| "prune_old_llm_request_logs"
|
|
21
|
+
| "prune_old_trace_events"
|
|
21
22
|
| "build_conversation_summary"
|
|
22
23
|
| "conversation_analyze"
|
|
23
24
|
| "backfill"
|
|
@@ -366,6 +367,53 @@ export function enqueuePruneOldConversationsJob(
|
|
|
366
367
|
return enqueueMemoryJob("prune_old_conversations", payload);
|
|
367
368
|
}
|
|
368
369
|
|
|
370
|
+
export function enqueuePruneOldTraceEventsJob(retentionDays?: number): string {
|
|
371
|
+
const db = getDb();
|
|
372
|
+
const existing = db
|
|
373
|
+
.select()
|
|
374
|
+
.from(memoryJobs)
|
|
375
|
+
.where(
|
|
376
|
+
and(
|
|
377
|
+
eq(memoryJobs.type, "prune_old_trace_events"),
|
|
378
|
+
inArray(memoryJobs.status, ["pending", "running"]),
|
|
379
|
+
),
|
|
380
|
+
)
|
|
381
|
+
.orderBy(asc(memoryJobs.createdAt))
|
|
382
|
+
.get();
|
|
383
|
+
if (existing) {
|
|
384
|
+
if (
|
|
385
|
+
existing.status === "pending" &&
|
|
386
|
+
typeof retentionDays === "number" &&
|
|
387
|
+
Number.isFinite(retentionDays) &&
|
|
388
|
+
retentionDays >= 0
|
|
389
|
+
) {
|
|
390
|
+
let payload: Record<string, unknown> = {};
|
|
391
|
+
try {
|
|
392
|
+
payload = JSON.parse(existing.payload) as Record<string, unknown>;
|
|
393
|
+
} catch {
|
|
394
|
+
payload = {};
|
|
395
|
+
}
|
|
396
|
+
if (payload.retentionDays !== retentionDays) {
|
|
397
|
+
db.update(memoryJobs)
|
|
398
|
+
.set({
|
|
399
|
+
payload: JSON.stringify({ ...payload, retentionDays }),
|
|
400
|
+
updatedAt: Date.now(),
|
|
401
|
+
})
|
|
402
|
+
.where(eq(memoryJobs.id, existing.id))
|
|
403
|
+
.run();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return existing.id;
|
|
407
|
+
}
|
|
408
|
+
const payload =
|
|
409
|
+
typeof retentionDays === "number" &&
|
|
410
|
+
Number.isFinite(retentionDays) &&
|
|
411
|
+
retentionDays >= 0
|
|
412
|
+
? { retentionDays }
|
|
413
|
+
: {};
|
|
414
|
+
return enqueueMemoryJob("prune_old_trace_events", payload);
|
|
415
|
+
}
|
|
416
|
+
|
|
369
417
|
export interface LaneBudgets {
|
|
370
418
|
slowLlm: number;
|
|
371
419
|
fast: number;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
2
1
|
import { getConfig } from "../config/loader.js";
|
|
3
2
|
import type { AssistantConfig } from "../config/types.js";
|
|
3
|
+
import {
|
|
4
|
+
checkDiskPressureBackgroundGate,
|
|
5
|
+
diskPressureBackgroundSkipLogFields,
|
|
6
|
+
shouldLogDiskPressureBackgroundSkip,
|
|
7
|
+
} from "../daemon/disk-pressure-background-gate.js";
|
|
4
8
|
import { getLogger } from "../util/logger.js";
|
|
5
9
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
|
|
6
10
|
import {
|
|
@@ -23,6 +27,7 @@ import { backfillJob } from "./job-handlers/backfill.js";
|
|
|
23
27
|
import {
|
|
24
28
|
pruneOldConversationsJob,
|
|
25
29
|
pruneOldLlmRequestLogsJob,
|
|
30
|
+
pruneOldTraceEventsJob,
|
|
26
31
|
} from "./job-handlers/cleanup.js";
|
|
27
32
|
import { generateConversationStartersJob } from "./job-handlers/conversation-starters.js";
|
|
28
33
|
// ── Per-job-type handlers ──────────────────────────────────────────
|
|
@@ -54,6 +59,7 @@ import {
|
|
|
54
59
|
enqueueMemoryJob,
|
|
55
60
|
enqueuePruneOldConversationsJob,
|
|
56
61
|
enqueuePruneOldLlmRequestLogsJob,
|
|
62
|
+
enqueuePruneOldTraceEventsJob,
|
|
57
63
|
failMemoryJob,
|
|
58
64
|
failStalledJobs,
|
|
59
65
|
type MemoryJob,
|
|
@@ -167,6 +173,20 @@ export async function runMemoryJobsOnce(
|
|
|
167
173
|
if (!config.memory.enabled) return 0;
|
|
168
174
|
const enableScheduledCleanup = options.enableScheduledCleanup === true;
|
|
169
175
|
|
|
176
|
+
const diskPressureGate = checkDiskPressureBackgroundGate("background-work");
|
|
177
|
+
if (diskPressureGate.action === "skip") {
|
|
178
|
+
if (shouldLogDiskPressureBackgroundSkip("memory-jobs-worker")) {
|
|
179
|
+
log.warn(
|
|
180
|
+
{
|
|
181
|
+
source: "memory",
|
|
182
|
+
...diskPressureBackgroundSkipLogFields(diskPressureGate),
|
|
183
|
+
},
|
|
184
|
+
"Memory jobs worker skipped during disk pressure cleanup mode",
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
170
190
|
// Fail jobs that have been running longer than the configured timeout
|
|
171
191
|
const timedOut = failStalledJobs(config.memory.jobs.stalledJobTimeoutMs);
|
|
172
192
|
if (timedOut > 0) {
|
|
@@ -455,6 +475,9 @@ async function processJob(
|
|
|
455
475
|
case "prune_old_llm_request_logs":
|
|
456
476
|
pruneOldLlmRequestLogsJob(job, config);
|
|
457
477
|
return;
|
|
478
|
+
case "prune_old_trace_events":
|
|
479
|
+
pruneOldTraceEventsJob(job, config);
|
|
480
|
+
return;
|
|
458
481
|
case "build_conversation_summary":
|
|
459
482
|
await buildConversationSummaryJob(job, config);
|
|
460
483
|
return;
|
|
@@ -486,6 +509,11 @@ async function processJob(
|
|
|
486
509
|
await embedGraphTriggerJob(job, config);
|
|
487
510
|
return;
|
|
488
511
|
case "graph_extract":
|
|
512
|
+
// Stale rows enqueued before v2 was enabled (or by any unguarded v1
|
|
513
|
+
// path) must not consume embedding/extraction budget when v2 is on.
|
|
514
|
+
if (config.memory.v2.enabled) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
489
517
|
await graphExtractJob(job, config);
|
|
490
518
|
return;
|
|
491
519
|
case "conversation_analyze":
|
|
@@ -560,14 +588,20 @@ function maybeEnqueueScheduledCleanupJobs(
|
|
|
560
588
|
cleanup.llmRequestLogRetentionMs !== null
|
|
561
589
|
? enqueuePruneOldLlmRequestLogsJob(cleanup.llmRequestLogRetentionMs)
|
|
562
590
|
: null;
|
|
591
|
+
const pruneTraceEventsJobId =
|
|
592
|
+
cleanup.traceEventRetentionDays > 0
|
|
593
|
+
? enqueuePruneOldTraceEventsJob(cleanup.traceEventRetentionDays)
|
|
594
|
+
: null;
|
|
563
595
|
markScheduledCleanupEnqueued(nowMs);
|
|
564
596
|
log.debug(
|
|
565
597
|
{
|
|
566
598
|
pruneConversationsJobId,
|
|
567
599
|
pruneLlmRequestLogsJobId,
|
|
600
|
+
pruneTraceEventsJobId,
|
|
568
601
|
enqueueIntervalMs: cleanup.enqueueIntervalMs,
|
|
569
602
|
conversationRetentionDays: cleanup.conversationRetentionDays,
|
|
570
603
|
llmRequestLogRetentionMs: cleanup.llmRequestLogRetentionMs,
|
|
604
|
+
traceEventRetentionDays: cleanup.traceEventRetentionDays,
|
|
571
605
|
},
|
|
572
606
|
"Enqueued scheduled memory cleanup jobs",
|
|
573
607
|
);
|
|
@@ -590,58 +624,66 @@ export const GRAPH_MAINTENANCE_CHECKPOINTS = {
|
|
|
590
624
|
} as const;
|
|
591
625
|
|
|
592
626
|
/**
|
|
593
|
-
* Enqueue periodic graph maintenance jobs
|
|
594
|
-
* Uses durable checkpoints so intervals survive daemon restarts — jobs only fire
|
|
595
|
-
* when the actual elapsed time since last run exceeds the interval.
|
|
627
|
+
* Enqueue periodic graph maintenance jobs.
|
|
596
628
|
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
* the
|
|
601
|
-
*
|
|
629
|
+
* Mutually exclusive between v1 and v2:
|
|
630
|
+
* - v2 active (`memory.v2.enabled` on) → only `memory_v2_consolidate` is
|
|
631
|
+
* scheduled.
|
|
632
|
+
* - v2 inactive → the four v1 entries (decay, consolidate, pattern_scan,
|
|
633
|
+
* narrative) are scheduled instead.
|
|
634
|
+
*
|
|
635
|
+
* Read/write paths route to v2 when the flag is on, so v1 graph data goes
|
|
636
|
+
* unread; running v1 maintenance alongside v2 is wasted compute and LLM
|
|
637
|
+
* spend. The v1 code path remains live so flipping the flag back to off
|
|
638
|
+
* fully re-engages v1.
|
|
639
|
+
*
|
|
640
|
+
* Uses durable checkpoints so intervals survive daemon restarts — jobs only
|
|
641
|
+
* fire when the actual elapsed time since last run exceeds the interval.
|
|
642
|
+
* Sweep is intentionally not on this schedule: it is debounced from the
|
|
643
|
+
* live `graph_extract` trigger path (see `indexMessageNow` in `indexer.ts`)
|
|
644
|
+
* so it runs on the same idle/message-count cadence.
|
|
602
645
|
*/
|
|
603
646
|
export function maybeEnqueueGraphMaintenanceJobs(
|
|
604
647
|
config: AssistantConfig,
|
|
605
648
|
nowMs = Date.now(),
|
|
606
649
|
): void {
|
|
650
|
+
const v2Active = config.memory.v2.enabled;
|
|
651
|
+
|
|
607
652
|
const schedule: Array<{
|
|
608
653
|
key: string;
|
|
609
654
|
intervalMs: number;
|
|
610
655
|
jobType: MemoryJobType;
|
|
611
|
-
}> =
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
jobType: "memory_v2_consolidate",
|
|
643
|
-
});
|
|
644
|
-
}
|
|
656
|
+
}> = v2Active
|
|
657
|
+
? [
|
|
658
|
+
{
|
|
659
|
+
key: GRAPH_MAINTENANCE_CHECKPOINTS.memoryV2Consolidate,
|
|
660
|
+
intervalMs:
|
|
661
|
+
config.memory.v2.consolidation_interval_hours * 60 * 60 * 1000,
|
|
662
|
+
jobType: "memory_v2_consolidate",
|
|
663
|
+
},
|
|
664
|
+
]
|
|
665
|
+
: [
|
|
666
|
+
{
|
|
667
|
+
key: GRAPH_MAINTENANCE_CHECKPOINTS.decay,
|
|
668
|
+
intervalMs: GRAPH_DECAY_INTERVAL_MS,
|
|
669
|
+
jobType: "graph_decay",
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
key: GRAPH_MAINTENANCE_CHECKPOINTS.consolidate,
|
|
673
|
+
intervalMs: GRAPH_CONSOLIDATE_INTERVAL_MS,
|
|
674
|
+
jobType: "graph_consolidate",
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
key: GRAPH_MAINTENANCE_CHECKPOINTS.patternScan,
|
|
678
|
+
intervalMs: GRAPH_PATTERN_SCAN_INTERVAL_MS,
|
|
679
|
+
jobType: "graph_pattern_scan",
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
key: GRAPH_MAINTENANCE_CHECKPOINTS.narrative,
|
|
683
|
+
intervalMs: GRAPH_NARRATIVE_INTERVAL_MS,
|
|
684
|
+
jobType: "graph_narrative_refine",
|
|
685
|
+
},
|
|
686
|
+
];
|
|
645
687
|
|
|
646
688
|
for (const { key, intervalMs, jobType } of schedule) {
|
|
647
689
|
const lastRun = parseInt(getMemoryCheckpoint(key) ?? "0", 10);
|