@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
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
// Memory Tool handlers
|
|
3
3
|
//
|
|
4
4
|
// remember: save facts to the PKB (buffer.md + daily archive) under the v1
|
|
5
|
-
// path, or to memory/buffer.md + memory/archive/<today>.md when
|
|
6
|
-
//
|
|
5
|
+
// path, or to memory/buffer.md + memory/archive/<today>.md when memory v2 is
|
|
6
|
+
// active.
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
|
|
12
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
13
12
|
import type { AssistantConfig } from "../../config/types.js";
|
|
14
13
|
import { getLogger } from "../../util/logger.js";
|
|
15
14
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
@@ -46,7 +45,7 @@ export function handleRemember(
|
|
|
46
45
|
const now = new Date();
|
|
47
46
|
const entry = formatRememberEntry(input.content.trim(), now);
|
|
48
47
|
|
|
49
|
-
if (
|
|
48
|
+
if (config.memory.v2.enabled) {
|
|
50
49
|
appendBufferAndArchive({
|
|
51
50
|
rootDir: join(workspaceDir, "memory"),
|
|
52
51
|
entry,
|
|
@@ -56,10 +56,10 @@ export const graphRecallDefinition: ToolDefinition = {
|
|
|
56
56
|
/**
|
|
57
57
|
* Save a fact to the assistant's knowledge base. The fact is appended to
|
|
58
58
|
* `buffer.md` (immediately available in the next conversation) and the daily
|
|
59
|
-
* archive (permanent date-indexed record).
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
59
|
+
* archive (permanent date-indexed record). When `memory.v2.enabled` is true,
|
|
60
|
+
* writes go under `memory/`; otherwise they go under `pkb/`. Consolidation
|
|
61
|
+
* of the buffer into longer-form storage runs as a separate periodic job in
|
|
62
|
+
* both modes.
|
|
63
63
|
*/
|
|
64
64
|
export const graphRememberDefinition: ToolDefinition = {
|
|
65
65
|
name: "remember",
|
package/src/memory/indexer.ts
CHANGED
|
@@ -175,39 +175,9 @@ export async function indexMessageNow(
|
|
|
175
175
|
// Summaries still run — they feed the graph retrieval pipeline and
|
|
176
176
|
// are not recursion-prone.
|
|
177
177
|
if (!isAutoAnalysisSource) {
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const graphPendingCount =
|
|
182
|
-
(graphCurrentVal ? parseInt(graphCurrentVal, 10) : 0) + 1;
|
|
183
|
-
setMemoryCheckpoint(graphPendingKey, String(graphPendingCount));
|
|
184
|
-
|
|
185
|
-
const graphBatchFired = graphPendingCount >= batchSize;
|
|
186
|
-
if (graphBatchFired) {
|
|
187
|
-
setMemoryCheckpoint(graphPendingKey, "0");
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Single pending `graph_extract` row per conversation. If the
|
|
191
|
-
// batch threshold just fired, pull `runAfter` back to now so the
|
|
192
|
-
// job runs immediately; otherwise debounce by the idle timeout.
|
|
193
|
-
// Routing both paths through `upsertDebouncedJob` ensures the
|
|
194
|
-
// row's `runAfter` reflects whichever trigger ran last, so a
|
|
195
|
-
// batch crossing always takes effect immediately.
|
|
196
|
-
const extractRunAfter = graphBatchFired
|
|
197
|
-
? Date.now()
|
|
198
|
-
: Date.now() + idleTimeoutMs;
|
|
199
|
-
upsertDebouncedJob(
|
|
200
|
-
"graph_extract",
|
|
201
|
-
{
|
|
202
|
-
conversationId: input.conversationId,
|
|
203
|
-
scopeId: input.scopeId ?? "default",
|
|
204
|
-
},
|
|
205
|
-
extractRunAfter,
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
// Reading config here is best-effort: feature-gated triggers below
|
|
209
|
-
// (memory v2 sweep, auto-analyze batch) skip when it fails — the
|
|
210
|
-
// idle-debounced enqueues above are unaffected.
|
|
178
|
+
// Reading config here is best-effort: when it fails we treat v2 as
|
|
179
|
+
// inactive (failing-open to v1) so a config error never silently
|
|
180
|
+
// drops both extraction paths.
|
|
211
181
|
let triggerConfig: ReturnType<typeof getConfig> | null = null;
|
|
212
182
|
try {
|
|
213
183
|
triggerConfig = getConfig();
|
|
@@ -218,20 +188,58 @@ export async function indexMessageNow(
|
|
|
218
188
|
);
|
|
219
189
|
}
|
|
220
190
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
//
|
|
191
|
+
const v2Config =
|
|
192
|
+
triggerConfig != null && triggerConfig.memory.v2.enabled
|
|
193
|
+
? triggerConfig
|
|
194
|
+
: null;
|
|
195
|
+
|
|
196
|
+
// ── Graph extraction (v1) ───────────────────────────────────────
|
|
197
|
+
// Suppressed when v2 is active — v2 reads memory from buffer.md
|
|
198
|
+
// and concept pages, so the v1 graph would be stale data nobody
|
|
199
|
+
// consumes. Pending-count tracking is suppressed too; otherwise a
|
|
200
|
+
// flag flip back to v1 would fire an immediate batch from counts
|
|
201
|
+
// accumulated during the v2 window.
|
|
202
|
+
let extractRunAfter: number;
|
|
203
|
+
if (v2Config == null) {
|
|
204
|
+
const graphPendingKey = `graph_extract:${input.conversationId}:pending_count`;
|
|
205
|
+
const graphCurrentVal = getMemoryCheckpoint(graphPendingKey);
|
|
206
|
+
const graphPendingCount =
|
|
207
|
+
(graphCurrentVal ? parseInt(graphCurrentVal, 10) : 0) + 1;
|
|
208
|
+
setMemoryCheckpoint(graphPendingKey, String(graphPendingCount));
|
|
209
|
+
|
|
210
|
+
const graphBatchFired = graphPendingCount >= batchSize;
|
|
211
|
+
if (graphBatchFired) {
|
|
212
|
+
setMemoryCheckpoint(graphPendingKey, "0");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Single pending `graph_extract` row per conversation. If the
|
|
216
|
+
// batch threshold just fired, pull `runAfter` back to now so the
|
|
217
|
+
// job runs immediately; otherwise debounce by the idle timeout.
|
|
218
|
+
// Routing both paths through `upsertDebouncedJob` ensures the
|
|
219
|
+
// row's `runAfter` reflects whichever trigger ran last, so a
|
|
220
|
+
// batch crossing always takes effect immediately.
|
|
221
|
+
extractRunAfter = graphBatchFired
|
|
222
|
+
? Date.now()
|
|
223
|
+
: Date.now() + idleTimeoutMs;
|
|
224
|
+
upsertDebouncedJob(
|
|
225
|
+
"graph_extract",
|
|
226
|
+
{
|
|
227
|
+
conversationId: input.conversationId,
|
|
228
|
+
scopeId: input.scopeId ?? "default",
|
|
229
|
+
},
|
|
230
|
+
extractRunAfter,
|
|
231
|
+
);
|
|
232
|
+
} else {
|
|
233
|
+
extractRunAfter = Date.now() + idleTimeoutMs;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Memory v2 sweep: when v2 is on AND `sweep_enabled` is set, every
|
|
237
|
+
// extraction trigger also enqueues a sweep. The sweep itself reads
|
|
238
|
+
// recent messages globally, so the `conversationId` here is just
|
|
239
|
+
// the dedup key — one pending row per active conversation.
|
|
227
240
|
// `sweep_enabled` defaults to false because `remember()` is the
|
|
228
241
|
// primary capture path; the sweep is opt-in.
|
|
229
|
-
if (
|
|
230
|
-
triggerConfig != null &&
|
|
231
|
-
isAssistantFeatureFlagEnabled("memory-v2-enabled", triggerConfig) &&
|
|
232
|
-
triggerConfig.memory.v2.enabled &&
|
|
233
|
-
triggerConfig.memory.v2.sweep_enabled
|
|
234
|
-
) {
|
|
242
|
+
if (v2Config != null && v2Config.memory.v2.sweep_enabled) {
|
|
235
243
|
upsertDebouncedJob(
|
|
236
244
|
"memory_v2_sweep",
|
|
237
245
|
{ conversationId: input.conversationId },
|
|
@@ -7,10 +7,7 @@ import {
|
|
|
7
7
|
resetMessageCursorCheckpoint,
|
|
8
8
|
writeMessageCursorCheckpoint,
|
|
9
9
|
} from "../checkpoints.js";
|
|
10
|
-
import {
|
|
11
|
-
getConversationMemoryScopeId,
|
|
12
|
-
messageMetadataSchema,
|
|
13
|
-
} from "../conversation-crud.js";
|
|
10
|
+
import { messageMetadataSchema } from "../conversation-crud.js";
|
|
14
11
|
import { getDb } from "../db-connection.js";
|
|
15
12
|
import { indexMessageNow } from "../indexer.js";
|
|
16
13
|
import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
|
|
@@ -72,13 +69,7 @@ export async function backfillJob(
|
|
|
72
69
|
.all();
|
|
73
70
|
|
|
74
71
|
if (batch.length > 0) {
|
|
75
|
-
const scopeCache = new Map<string, string>();
|
|
76
72
|
for (const message of batch) {
|
|
77
|
-
let scopeId = scopeCache.get(message.conversationId);
|
|
78
|
-
if (scopeId === undefined) {
|
|
79
|
-
scopeId = getConversationMemoryScopeId(message.conversationId);
|
|
80
|
-
scopeCache.set(message.conversationId, scopeId);
|
|
81
|
-
}
|
|
82
73
|
const { provenanceTrustClass, automated } = parseMessageMetadata(
|
|
83
74
|
message.metadata ?? null,
|
|
84
75
|
);
|
|
@@ -89,7 +80,7 @@ export async function backfillJob(
|
|
|
89
80
|
role: message.role,
|
|
90
81
|
content: message.content,
|
|
91
82
|
createdAt: message.createdAt,
|
|
92
|
-
scopeId,
|
|
83
|
+
scopeId: "default",
|
|
93
84
|
provenanceTrustClass,
|
|
94
85
|
automated,
|
|
95
86
|
},
|
|
@@ -54,6 +54,49 @@ export function pruneOldLlmRequestLogsJob(
|
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Delete trace events older than the configured retention period.
|
|
59
|
+
* Processes in batches to avoid long DB locks and excessive WAL growth.
|
|
60
|
+
* Re-enqueues itself if more rows remain.
|
|
61
|
+
*/
|
|
62
|
+
export function pruneOldTraceEventsJob(
|
|
63
|
+
job: MemoryJob,
|
|
64
|
+
config: AssistantConfig,
|
|
65
|
+
): void {
|
|
66
|
+
const rawRetention = job.payload.retentionDays;
|
|
67
|
+
const retentionDays =
|
|
68
|
+
typeof rawRetention === "number" &&
|
|
69
|
+
Number.isFinite(rawRetention) &&
|
|
70
|
+
rawRetention >= 0
|
|
71
|
+
? rawRetention
|
|
72
|
+
: config.memory.cleanup.traceEventRetentionDays;
|
|
73
|
+
|
|
74
|
+
// 0 means disabled
|
|
75
|
+
if (retentionDays === 0) return;
|
|
76
|
+
|
|
77
|
+
const cutoffMs = Date.now() - retentionDays * 86_400_000;
|
|
78
|
+
|
|
79
|
+
rawRun(
|
|
80
|
+
`DELETE FROM trace_events WHERE rowid IN (SELECT rowid FROM trace_events WHERE created_at < ? LIMIT ?)`,
|
|
81
|
+
cutoffMs,
|
|
82
|
+
PRUNE_LOG_BATCH_LIMIT,
|
|
83
|
+
);
|
|
84
|
+
const deleted = rawChanges();
|
|
85
|
+
|
|
86
|
+
if (deleted >= PRUNE_LOG_BATCH_LIMIT) {
|
|
87
|
+
enqueueMemoryJob("prune_old_trace_events", { retentionDays });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
log.info(
|
|
91
|
+
{
|
|
92
|
+
deleted,
|
|
93
|
+
retentionDays,
|
|
94
|
+
cutoffMs,
|
|
95
|
+
},
|
|
96
|
+
"Pruned old trace events",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
57
100
|
/**
|
|
58
101
|
* Delete conversations that have had no activity (updatedAt) for longer than
|
|
59
102
|
* the configured retention period. Processes in batches so a single job doesn't
|
|
@@ -3,7 +3,6 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import { eq } from "drizzle-orm";
|
|
4
4
|
|
|
5
5
|
import type { AssistantConfig } from "../../config/types.js";
|
|
6
|
-
import { getConversationMemoryScopeId } from "../conversation-crud.js";
|
|
7
6
|
import { getDb } from "../db-connection.js";
|
|
8
7
|
import type { EmbeddingInput } from "../embedding-types.js";
|
|
9
8
|
import { asString, embedAndUpsert } from "../job-utils.js";
|
|
@@ -18,7 +17,7 @@ import {
|
|
|
18
17
|
|
|
19
18
|
export async function embedSegmentJob(
|
|
20
19
|
job: MemoryJob,
|
|
21
|
-
config: AssistantConfig
|
|
20
|
+
config: AssistantConfig,
|
|
22
21
|
): Promise<void> {
|
|
23
22
|
const segmentId = asString(job.payload.segmentId);
|
|
24
23
|
if (!segmentId) return;
|
|
@@ -39,7 +38,7 @@ export async function embedSegmentJob(
|
|
|
39
38
|
|
|
40
39
|
export async function embedSummaryJob(
|
|
41
40
|
job: MemoryJob,
|
|
42
|
-
config: AssistantConfig
|
|
41
|
+
config: AssistantConfig,
|
|
43
42
|
): Promise<void> {
|
|
44
43
|
const summaryId = asString(job.payload.summaryId);
|
|
45
44
|
if (!summaryId) return;
|
|
@@ -60,13 +59,13 @@ export async function embedSummaryJob(
|
|
|
60
59
|
created_at: summary.startAt,
|
|
61
60
|
last_seen_at: summary.endAt,
|
|
62
61
|
memory_scope_id: summary.scopeId,
|
|
63
|
-
}
|
|
62
|
+
},
|
|
64
63
|
);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
export async function embedMediaJob(
|
|
68
67
|
job: MemoryJob,
|
|
69
|
-
config: AssistantConfig
|
|
68
|
+
config: AssistantConfig,
|
|
70
69
|
): Promise<void> {
|
|
71
70
|
const assetId = asString(job.payload.assetId);
|
|
72
71
|
if (!assetId) return;
|
|
@@ -99,7 +98,7 @@ export async function embedMediaJob(
|
|
|
99
98
|
|
|
100
99
|
export async function embedAttachmentJob(
|
|
101
100
|
job: MemoryJob,
|
|
102
|
-
config: AssistantConfig
|
|
101
|
+
config: AssistantConfig,
|
|
103
102
|
): Promise<void> {
|
|
104
103
|
const messageId = asString(job.payload.messageId);
|
|
105
104
|
const blockIndex = job.payload.blockIndex as number;
|
|
@@ -125,11 +124,10 @@ export async function embedAttachmentJob(
|
|
|
125
124
|
|
|
126
125
|
// Use messageId + blockIndex as targetId for uniqueness
|
|
127
126
|
const targetId = `${messageId}:${blockIndex}`;
|
|
128
|
-
const memoryScopeId = getConversationMemoryScopeId(message.conversationId);
|
|
129
127
|
await embedAndUpsert(config, "media", targetId, input, {
|
|
130
128
|
created_at: message.createdAt,
|
|
131
129
|
message_id: messageId,
|
|
132
130
|
conversation_id: message.conversationId,
|
|
133
|
-
memory_scope_id:
|
|
131
|
+
memory_scope_id: "default",
|
|
134
132
|
});
|
|
135
133
|
}
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
userMessage,
|
|
11
11
|
} from "../../providers/provider-send-message.js";
|
|
12
12
|
import { getLogger } from "../../util/logger.js";
|
|
13
|
-
import { getConversationMemoryScopeId } from "../conversation-crud.js";
|
|
14
13
|
import { getDb } from "../db-connection.js";
|
|
15
14
|
import { asString, truncate } from "../job-utils.js";
|
|
16
15
|
import { enqueueMemoryJob, type MemoryJob } from "../jobs-store.js";
|
|
@@ -91,10 +90,6 @@ export async function buildConversationSummaryJob(
|
|
|
91
90
|
"conversation",
|
|
92
91
|
);
|
|
93
92
|
|
|
94
|
-
// Inherit the conversation's memory scope so summaries stay aligned with
|
|
95
|
-
// the retrieval scope used by the source conversation.
|
|
96
|
-
const scopeId = getConversationMemoryScopeId(conversationId);
|
|
97
|
-
|
|
98
93
|
const now = Date.now();
|
|
99
94
|
const summaryId = existing?.id ?? uuid();
|
|
100
95
|
const nextVersion = (existing?.version ?? 0) + 1;
|
|
@@ -108,7 +103,7 @@ export async function buildConversationSummaryJob(
|
|
|
108
103
|
id: summaryId,
|
|
109
104
|
scope: "conversation",
|
|
110
105
|
scopeKey: conversationId,
|
|
111
|
-
scopeId,
|
|
106
|
+
scopeId: "default",
|
|
112
107
|
summary: summaryText,
|
|
113
108
|
tokenEstimate: estimateTextTokens(summaryText),
|
|
114
109
|
version: nextVersion,
|
|
@@ -123,7 +118,7 @@ export async function buildConversationSummaryJob(
|
|
|
123
118
|
summary: summaryText,
|
|
124
119
|
tokenEstimate: estimateTextTokens(summaryText),
|
|
125
120
|
version: sql`${memorySummaries.version} + 1`,
|
|
126
|
-
scopeId,
|
|
121
|
+
scopeId: "default",
|
|
127
122
|
startAt: earliestCovered,
|
|
128
123
|
endAt: latestCovered,
|
|
129
124
|
updatedAt: now,
|
|
@@ -86,6 +86,10 @@ const upsertCalls: Array<{
|
|
|
86
86
|
slug: string;
|
|
87
87
|
dense: number[];
|
|
88
88
|
sparse: { indices: number[]; values: number[] };
|
|
89
|
+
summary?: {
|
|
90
|
+
dense: number[];
|
|
91
|
+
sparse: { indices: number[]; values: number[] };
|
|
92
|
+
};
|
|
89
93
|
updatedAt: number;
|
|
90
94
|
}> = [];
|
|
91
95
|
|
|
@@ -96,6 +100,10 @@ mock.module("../../v2/qdrant.js", () => ({
|
|
|
96
100
|
slug: string;
|
|
97
101
|
dense: number[];
|
|
98
102
|
sparse: { indices: number[]; values: number[] };
|
|
103
|
+
summary?: {
|
|
104
|
+
dense: number[];
|
|
105
|
+
sparse: { indices: number[]; values: number[] };
|
|
106
|
+
};
|
|
99
107
|
updatedAt: number;
|
|
100
108
|
}) => {
|
|
101
109
|
upsertCalls.push(params);
|
|
@@ -242,6 +250,114 @@ describe("embedConceptPageJob — happy path", () => {
|
|
|
242
250
|
});
|
|
243
251
|
});
|
|
244
252
|
|
|
253
|
+
describe("embedConceptPageJob — summary embedding", () => {
|
|
254
|
+
test("embeds the summary when present and forwards summary vectors to upsert", async () => {
|
|
255
|
+
await writePage(tmpWorkspace, {
|
|
256
|
+
slug: "summarized-page",
|
|
257
|
+
frontmatter: {
|
|
258
|
+
edges: [],
|
|
259
|
+
ref_files: [],
|
|
260
|
+
summary: "A short prose summary that retrieval indexes separately.",
|
|
261
|
+
},
|
|
262
|
+
body: "Long-form body content.\n",
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
await embedConceptPageJob(
|
|
266
|
+
makeJob({ slug: "summarized-page" }),
|
|
267
|
+
TEST_CONFIG,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// Body and summary are batched into one backend call (saves a round-trip).
|
|
271
|
+
expect(embedWithBackendCalls).toHaveLength(1);
|
|
272
|
+
expect(embedWithBackendCalls[0].inputs).toHaveLength(2);
|
|
273
|
+
expect(upsertCalls).toHaveLength(1);
|
|
274
|
+
const call = upsertCalls[0];
|
|
275
|
+
expect(call.slug).toBe("summarized-page");
|
|
276
|
+
expect(call.dense).toEqual([0.1, 0.2, 0.3, 0.4]);
|
|
277
|
+
expect(call.sparse).toBeDefined();
|
|
278
|
+
expect(call.summary?.dense).toEqual([0.1, 0.2, 0.3, 0.4]);
|
|
279
|
+
expect(call.summary?.sparse).toBeDefined();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("skips summary embedding when the page has no summary in frontmatter", async () => {
|
|
283
|
+
await writePage(tmpWorkspace, {
|
|
284
|
+
slug: "legacy-page",
|
|
285
|
+
frontmatter: { edges: [], ref_files: [] },
|
|
286
|
+
body: "Body only — no summary in frontmatter.\n",
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
await embedConceptPageJob(makeJob({ slug: "legacy-page" }), TEST_CONFIG);
|
|
290
|
+
|
|
291
|
+
// Only the body was embedded.
|
|
292
|
+
expect(embedWithBackendCalls).toHaveLength(1);
|
|
293
|
+
expect(upsertCalls).toHaveLength(1);
|
|
294
|
+
const call = upsertCalls[0];
|
|
295
|
+
expect(call.summary).toBeUndefined();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("skips summary embedding when the summary is whitespace-only", async () => {
|
|
299
|
+
// Whitespace-only summaries (` `, `\n`) are equivalent to absent — the
|
|
300
|
+
// embedding backend would reject the empty input downstream anyway.
|
|
301
|
+
await writePage(tmpWorkspace, {
|
|
302
|
+
slug: "whitespace-summary",
|
|
303
|
+
frontmatter: {
|
|
304
|
+
edges: [],
|
|
305
|
+
ref_files: [],
|
|
306
|
+
summary: " ",
|
|
307
|
+
},
|
|
308
|
+
body: "Body content.\n",
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
await embedConceptPageJob(
|
|
312
|
+
makeJob({ slug: "whitespace-summary" }),
|
|
313
|
+
TEST_CONFIG,
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
expect(embedWithBackendCalls).toHaveLength(1);
|
|
317
|
+
expect(upsertCalls[0].summary).toBeUndefined();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("body and summary cache rows are independent (summary edit doesn't invalidate body)", async () => {
|
|
321
|
+
// Write a page with a summary, run the job to prime caches.
|
|
322
|
+
await writePage(tmpWorkspace, {
|
|
323
|
+
slug: "cached-summary",
|
|
324
|
+
frontmatter: {
|
|
325
|
+
edges: [],
|
|
326
|
+
ref_files: [],
|
|
327
|
+
summary: "First version of the summary.",
|
|
328
|
+
},
|
|
329
|
+
body: "Stable body that never changes.\n",
|
|
330
|
+
});
|
|
331
|
+
await embedConceptPageJob(
|
|
332
|
+
makeJob({ slug: "cached-summary" }),
|
|
333
|
+
TEST_CONFIG,
|
|
334
|
+
);
|
|
335
|
+
// Body + summary batched into a single backend call on first run.
|
|
336
|
+
expect(embedWithBackendCalls).toHaveLength(1);
|
|
337
|
+
expect(embedWithBackendCalls[0].inputs).toHaveLength(2);
|
|
338
|
+
|
|
339
|
+
// Edit only the summary — body stays identical, only the summary text
|
|
340
|
+
// changes. Re-running the job should hit the body cache (no re-embed)
|
|
341
|
+
// but recompute the summary embedding.
|
|
342
|
+
await writePage(tmpWorkspace, {
|
|
343
|
+
slug: "cached-summary",
|
|
344
|
+
frontmatter: {
|
|
345
|
+
edges: [],
|
|
346
|
+
ref_files: [],
|
|
347
|
+
summary: "Second version of the summary, different wording.",
|
|
348
|
+
},
|
|
349
|
+
body: "Stable body that never changes.\n",
|
|
350
|
+
});
|
|
351
|
+
await embedConceptPageJob(
|
|
352
|
+
makeJob({ slug: "cached-summary" }),
|
|
353
|
+
TEST_CONFIG,
|
|
354
|
+
);
|
|
355
|
+
// One additional backend call with only the summary text — body hit the cache.
|
|
356
|
+
expect(embedWithBackendCalls).toHaveLength(2);
|
|
357
|
+
expect(embedWithBackendCalls[1].inputs).toHaveLength(1);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
245
361
|
describe("embedConceptPageJob — cache hit", () => {
|
|
246
362
|
test("reuses the cached dense vector when content hash matches", async () => {
|
|
247
363
|
await writePage(tmpWorkspace, {
|