@vellumai/assistant 0.8.3 → 0.8.4
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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +25 -7
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +135 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +151 -56
- package/src/runtime/auth/route-policy.ts +7 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `assistant/src/memory/v2/injection-events.ts` and its sibling
|
|
3
|
+
* migration `256-memory-v2-injection-events.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Coverage matrix:
|
|
6
|
+
* - Migration creates the table + both indexes; safe to re-run.
|
|
7
|
+
* - Backfill replays router-sourced concepts from memory_v2_activation_logs
|
|
8
|
+
* and is idempotent on a forced re-run with cleared checkpoint.
|
|
9
|
+
* - Backfill is a no-op when the activation-logs table doesn't exist
|
|
10
|
+
* (pre-234 DB).
|
|
11
|
+
* - recordInjectionEvents appends one row per slug per call; empty list
|
|
12
|
+
* is a no-op.
|
|
13
|
+
* - computeInjectionScore matches the closed-form decay at known deltas
|
|
14
|
+
* (0d ≈ 1, 3d ≈ 0.5, 6d ≈ 0.25) and sums multiple events linearly.
|
|
15
|
+
* - computeInjectionScores returns the same per-slug values in batch and
|
|
16
|
+
* omits slugs with no events.
|
|
17
|
+
*
|
|
18
|
+
* Uses an in-memory bun:sqlite database — no real workspace DB.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Database } from "bun:sqlite";
|
|
22
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
23
|
+
|
|
24
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
25
|
+
|
|
26
|
+
import { makeMockLogger } from "../../../__tests__/helpers/mock-logger.js";
|
|
27
|
+
|
|
28
|
+
mock.module("../../../util/logger.js", () => ({
|
|
29
|
+
getLogger: () => makeMockLogger(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
import type { DrizzleDb } from "../../db-connection.js";
|
|
33
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
34
|
+
import { migrateMemoryV2ActivationLogs } from "../../migrations/234-memory-v2-activation-logs.js";
|
|
35
|
+
import {
|
|
36
|
+
downMemoryV2InjectionEvents,
|
|
37
|
+
migrateMemoryV2InjectionEvents,
|
|
38
|
+
} from "../../migrations/256-memory-v2-injection-events.js";
|
|
39
|
+
import * as schema from "../../schema.js";
|
|
40
|
+
import {
|
|
41
|
+
computeInjectionScore,
|
|
42
|
+
computeInjectionScores,
|
|
43
|
+
INJECTION_SCORE_HALF_LIFE_MS,
|
|
44
|
+
recordInjectionEvents,
|
|
45
|
+
} from "../injection-events.js";
|
|
46
|
+
|
|
47
|
+
// memory_checkpoints is required by withCrashRecovery and is normally
|
|
48
|
+
// created by an early core migration. Stand it up by hand so we can run
|
|
49
|
+
// the v2 migrations in isolation against a fresh in-memory DB.
|
|
50
|
+
const CHECKPOINTS_DDL = /*sql*/ `
|
|
51
|
+
CREATE TABLE memory_checkpoints (
|
|
52
|
+
key TEXT PRIMARY KEY,
|
|
53
|
+
value TEXT NOT NULL,
|
|
54
|
+
updated_at INTEGER NOT NULL
|
|
55
|
+
)
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
let sqlite: Database;
|
|
59
|
+
let database: DrizzleDb;
|
|
60
|
+
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
sqlite = new Database(":memory:");
|
|
63
|
+
database = drizzle(sqlite, { schema });
|
|
64
|
+
getSqliteFrom(database).exec(CHECKPOINTS_DDL);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
sqlite.close();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
function insertActivationLog(
|
|
72
|
+
rawDb: Database,
|
|
73
|
+
args: {
|
|
74
|
+
id: string;
|
|
75
|
+
concepts: Array<{ slug: string; source: string; status?: string }>;
|
|
76
|
+
createdAt: number;
|
|
77
|
+
},
|
|
78
|
+
): void {
|
|
79
|
+
rawDb
|
|
80
|
+
.prepare(
|
|
81
|
+
`INSERT INTO memory_v2_activation_logs (
|
|
82
|
+
id, conversation_id, message_id, turn, mode,
|
|
83
|
+
concepts_json, skills_json, config_json, created_at
|
|
84
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
85
|
+
)
|
|
86
|
+
.run(
|
|
87
|
+
args.id,
|
|
88
|
+
"conv-1",
|
|
89
|
+
`msg-${args.id}`,
|
|
90
|
+
1,
|
|
91
|
+
"router",
|
|
92
|
+
JSON.stringify(args.concepts),
|
|
93
|
+
"[]",
|
|
94
|
+
"{}",
|
|
95
|
+
args.createdAt,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
describe("migrateMemoryV2InjectionEvents", () => {
|
|
100
|
+
test("creates table and both indexes; safe to re-run", () => {
|
|
101
|
+
migrateMemoryV2InjectionEvents(database);
|
|
102
|
+
migrateMemoryV2InjectionEvents(database);
|
|
103
|
+
|
|
104
|
+
const raw = getSqliteFrom(database);
|
|
105
|
+
const table = raw
|
|
106
|
+
.query(
|
|
107
|
+
`SELECT name FROM sqlite_master WHERE type='table' AND name='memory_v2_injection_events'`,
|
|
108
|
+
)
|
|
109
|
+
.get();
|
|
110
|
+
expect(table).toBeTruthy();
|
|
111
|
+
|
|
112
|
+
const indexNames = new Set(
|
|
113
|
+
(
|
|
114
|
+
raw
|
|
115
|
+
.query(
|
|
116
|
+
`SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='memory_v2_injection_events'`,
|
|
117
|
+
)
|
|
118
|
+
.all() as Array<{ name: string }>
|
|
119
|
+
).map((r) => r.name),
|
|
120
|
+
);
|
|
121
|
+
expect(indexNames.has("idx_memory_v2_injection_events_slug_time")).toBe(
|
|
122
|
+
true,
|
|
123
|
+
);
|
|
124
|
+
expect(indexNames.has("idx_memory_v2_injection_events_time")).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("backfill replays router-sourced concepts and ignores carry_over", () => {
|
|
128
|
+
migrateMemoryV2ActivationLogs(database);
|
|
129
|
+
const raw = getSqliteFrom(database);
|
|
130
|
+
insertActivationLog(raw, {
|
|
131
|
+
id: "log-1",
|
|
132
|
+
concepts: [
|
|
133
|
+
{ slug: "alice", source: "router", status: "injected" },
|
|
134
|
+
{ slug: "bob", source: "router", status: "in_context" },
|
|
135
|
+
{ slug: "ghost", source: "carry_over", status: "not_injected" },
|
|
136
|
+
],
|
|
137
|
+
createdAt: 1_000_000,
|
|
138
|
+
});
|
|
139
|
+
insertActivationLog(raw, {
|
|
140
|
+
id: "log-2",
|
|
141
|
+
concepts: [{ slug: "alice", source: "router", status: "injected" }],
|
|
142
|
+
createdAt: 2_000_000,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
migrateMemoryV2InjectionEvents(database);
|
|
146
|
+
|
|
147
|
+
const rows = raw
|
|
148
|
+
.query(
|
|
149
|
+
`SELECT slug, injected_at FROM memory_v2_injection_events ORDER BY injected_at, slug`,
|
|
150
|
+
)
|
|
151
|
+
.all() as Array<{ slug: string; injected_at: number }>;
|
|
152
|
+
expect(rows).toEqual([
|
|
153
|
+
{ slug: "alice", injected_at: 1_000_000 },
|
|
154
|
+
{ slug: "bob", injected_at: 1_000_000 },
|
|
155
|
+
{ slug: "alice", injected_at: 2_000_000 },
|
|
156
|
+
]);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("backfill is a no-op when memory_v2_activation_logs is absent", () => {
|
|
160
|
+
// No activation-logs migration applied first.
|
|
161
|
+
expect(() => migrateMemoryV2InjectionEvents(database)).not.toThrow();
|
|
162
|
+
const { n } = getSqliteFrom(database)
|
|
163
|
+
.query(`SELECT COUNT(*) as n FROM memory_v2_injection_events`)
|
|
164
|
+
.get() as { n: number };
|
|
165
|
+
expect(n).toBe(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("forced re-run does not double-insert existing events", () => {
|
|
169
|
+
migrateMemoryV2ActivationLogs(database);
|
|
170
|
+
const raw = getSqliteFrom(database);
|
|
171
|
+
insertActivationLog(raw, {
|
|
172
|
+
id: "log-1",
|
|
173
|
+
concepts: [{ slug: "alice", source: "router", status: "injected" }],
|
|
174
|
+
createdAt: 1_000_000,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
migrateMemoryV2InjectionEvents(database);
|
|
178
|
+
// Simulate someone manually clearing the checkpoint — the in-table
|
|
179
|
+
// guard should still prevent re-backfill.
|
|
180
|
+
raw
|
|
181
|
+
.prepare(
|
|
182
|
+
`DELETE FROM memory_checkpoints WHERE key = 'migration_memory_v2_injection_events_v1'`,
|
|
183
|
+
)
|
|
184
|
+
.run();
|
|
185
|
+
migrateMemoryV2InjectionEvents(database);
|
|
186
|
+
|
|
187
|
+
const { n } = raw
|
|
188
|
+
.query(`SELECT COUNT(*) as n FROM memory_v2_injection_events`)
|
|
189
|
+
.get() as { n: number };
|
|
190
|
+
expect(n).toBe(1);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("downMemoryV2InjectionEvents drops the table", () => {
|
|
194
|
+
migrateMemoryV2InjectionEvents(database);
|
|
195
|
+
downMemoryV2InjectionEvents(database);
|
|
196
|
+
const table = getSqliteFrom(database)
|
|
197
|
+
.query(
|
|
198
|
+
`SELECT name FROM sqlite_master WHERE type='table' AND name='memory_v2_injection_events'`,
|
|
199
|
+
)
|
|
200
|
+
.get();
|
|
201
|
+
expect(table).toBeFalsy();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("recordInjectionEvents", () => {
|
|
206
|
+
beforeEach(() => {
|
|
207
|
+
migrateMemoryV2InjectionEvents(database);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("appends one row per slug at the same timestamp", () => {
|
|
211
|
+
const t = 1_000_000;
|
|
212
|
+
recordInjectionEvents(database, ["alice", "bob", "alice"], t);
|
|
213
|
+
const rows = getSqliteFrom(database)
|
|
214
|
+
.query(
|
|
215
|
+
`SELECT slug, injected_at FROM memory_v2_injection_events ORDER BY id`,
|
|
216
|
+
)
|
|
217
|
+
.all();
|
|
218
|
+
expect(rows).toEqual([
|
|
219
|
+
{ slug: "alice", injected_at: t },
|
|
220
|
+
{ slug: "bob", injected_at: t },
|
|
221
|
+
{ slug: "alice", injected_at: t },
|
|
222
|
+
]);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("empty list is a no-op", () => {
|
|
226
|
+
recordInjectionEvents(database, [], 1_000_000);
|
|
227
|
+
const { n } = getSqliteFrom(database)
|
|
228
|
+
.query(`SELECT COUNT(*) as n FROM memory_v2_injection_events`)
|
|
229
|
+
.get() as { n: number };
|
|
230
|
+
expect(n).toBe(0);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("computeInjectionScore", () => {
|
|
235
|
+
beforeEach(() => {
|
|
236
|
+
migrateMemoryV2InjectionEvents(database);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("returns 0 for a slug with no events", () => {
|
|
240
|
+
expect(computeInjectionScore(database, "missing", Date.now())).toBe(0);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("single event 0 days ago → score ≈ 1", () => {
|
|
244
|
+
const now = 10_000_000_000;
|
|
245
|
+
recordInjectionEvents(database, ["alice"], now);
|
|
246
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(1, 5);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("single event 3 days (one half-life) ago → score ≈ 0.5", () => {
|
|
250
|
+
const now = 10_000_000_000;
|
|
251
|
+
recordInjectionEvents(
|
|
252
|
+
database,
|
|
253
|
+
["alice"],
|
|
254
|
+
now - INJECTION_SCORE_HALF_LIFE_MS,
|
|
255
|
+
);
|
|
256
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(0.5, 5);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("single event 6 days (two half-lives) ago → score ≈ 0.25", () => {
|
|
260
|
+
const now = 10_000_000_000;
|
|
261
|
+
recordInjectionEvents(
|
|
262
|
+
database,
|
|
263
|
+
["alice"],
|
|
264
|
+
now - 2 * INJECTION_SCORE_HALF_LIFE_MS,
|
|
265
|
+
);
|
|
266
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(0.25, 5);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("multiple events sum independently", () => {
|
|
270
|
+
const now = 10_000_000_000;
|
|
271
|
+
recordInjectionEvents(database, ["alice"], now);
|
|
272
|
+
recordInjectionEvents(
|
|
273
|
+
database,
|
|
274
|
+
["alice"],
|
|
275
|
+
now - INJECTION_SCORE_HALF_LIFE_MS,
|
|
276
|
+
);
|
|
277
|
+
recordInjectionEvents(
|
|
278
|
+
database,
|
|
279
|
+
["alice"],
|
|
280
|
+
now - 2 * INJECTION_SCORE_HALF_LIFE_MS,
|
|
281
|
+
);
|
|
282
|
+
expect(computeInjectionScore(database, "alice", now)).toBeCloseTo(1.75, 5);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe("computeInjectionScores", () => {
|
|
287
|
+
beforeEach(() => {
|
|
288
|
+
migrateMemoryV2InjectionEvents(database);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("returns the same per-slug values as the single-slug helper", () => {
|
|
292
|
+
const now = 10_000_000_000;
|
|
293
|
+
recordInjectionEvents(database, ["alice", "bob"], now);
|
|
294
|
+
recordInjectionEvents(
|
|
295
|
+
database,
|
|
296
|
+
["alice"],
|
|
297
|
+
now - INJECTION_SCORE_HALF_LIFE_MS,
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const scores = computeInjectionScores(
|
|
301
|
+
database,
|
|
302
|
+
["alice", "bob", "ghost"],
|
|
303
|
+
now,
|
|
304
|
+
);
|
|
305
|
+
expect(scores.get("alice")).toBeCloseTo(1.5, 5);
|
|
306
|
+
expect(scores.get("bob")).toBeCloseTo(1, 5);
|
|
307
|
+
// ghost has no events — omitted from the result, not present as 0.
|
|
308
|
+
expect(scores.has("ghost")).toBe(false);
|
|
309
|
+
expect(scores.get("alice")).toBeCloseTo(
|
|
310
|
+
computeInjectionScore(database, "alice", now),
|
|
311
|
+
5,
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("empty slug list returns empty map", () => {
|
|
316
|
+
expect(computeInjectionScores(database, [], Date.now()).size).toBe(0);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
@@ -260,6 +260,8 @@ mock.module("../page-store.js", () => ({
|
|
|
260
260
|
interface RouterResultStub {
|
|
261
261
|
selectedSlugs: string[];
|
|
262
262
|
failureReason: string | null;
|
|
263
|
+
/** Tier provenance per slug. Defaults to `tier3:0` for any selected slug. */
|
|
264
|
+
sourceBySlug?: Map<string, string>;
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
const routerState = {
|
|
@@ -270,12 +272,20 @@ const routerState = {
|
|
|
270
272
|
mock.module("../router.js", () => ({
|
|
271
273
|
runRouter: async () => {
|
|
272
274
|
routerState.callCount++;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
275
|
+
const result = routerState.nextResult ?? {
|
|
276
|
+
selectedSlugs: [],
|
|
277
|
+
failureReason: null,
|
|
278
|
+
};
|
|
279
|
+
// Synthesize a default sourceBySlug for stubs that don't set one — pre-
|
|
280
|
+
// tier-provenance tests stage `selectedSlugs` only and expect every pick
|
|
281
|
+
// to flow through as a router selection. Treating them as `tier3:0` is
|
|
282
|
+
// the closest equivalent under the new model.
|
|
283
|
+
if (!result.sourceBySlug) {
|
|
284
|
+
const map = new Map<string, string>();
|
|
285
|
+
for (const slug of result.selectedSlugs) map.set(slug, "tier3:0");
|
|
286
|
+
result.sourceBySlug = map;
|
|
287
|
+
}
|
|
288
|
+
return result;
|
|
279
289
|
},
|
|
280
290
|
}));
|
|
281
291
|
|
|
@@ -388,6 +398,8 @@ import type { SkillEntry } from "../types.js";
|
|
|
388
398
|
const { getSqliteFrom } = await import("../../db-connection.js");
|
|
389
399
|
const { migrateActivationState } =
|
|
390
400
|
await import("../../migrations/232-activation-state.js");
|
|
401
|
+
const { migrateMemoryV2InjectionEvents } =
|
|
402
|
+
await import("../../migrations/256-memory-v2-injection-events.js");
|
|
391
403
|
const schema = await import("../../schema.js");
|
|
392
404
|
const { clearEverInjected, hydrate, save } =
|
|
393
405
|
await import("../activation-store.js");
|
|
@@ -409,6 +421,7 @@ function createTestDb(): DrizzleDb {
|
|
|
409
421
|
)
|
|
410
422
|
`);
|
|
411
423
|
migrateActivationState(db);
|
|
424
|
+
migrateMemoryV2InjectionEvents(db);
|
|
412
425
|
return db;
|
|
413
426
|
}
|
|
414
427
|
|
|
@@ -743,7 +756,7 @@ describe("injectMemoryV2Block", () => {
|
|
|
743
756
|
|
|
744
757
|
expect(result.block).not.toBeNull();
|
|
745
758
|
expect(result.block).toContain(
|
|
746
|
-
"
|
|
759
|
+
'Use `file_read("memory/concepts/path/to/file.md")` to read the full pages for any of the injected memory summaries you want more information on.',
|
|
747
760
|
);
|
|
748
761
|
expect(result.block).toContain(
|
|
749
762
|
"# memory/concepts/summarized-page.md\nA short prose description",
|
|
@@ -776,11 +789,13 @@ describe("injectMemoryV2Block", () => {
|
|
|
776
789
|
});
|
|
777
790
|
|
|
778
791
|
expect(result.block).not.toBeNull();
|
|
779
|
-
//
|
|
780
|
-
const
|
|
781
|
-
result.block!.match(
|
|
792
|
+
// Header appears exactly once.
|
|
793
|
+
const headerCount = (
|
|
794
|
+
result.block!.match(
|
|
795
|
+
/Use `file_read\("memory\/concepts\/path\/to\/file\.md"\)` to read/g,
|
|
796
|
+
) ?? []
|
|
782
797
|
).length;
|
|
783
|
-
expect(
|
|
798
|
+
expect(headerCount).toBe(1);
|
|
784
799
|
// summarized-page → short form (path + summary, no body, no frontmatter).
|
|
785
800
|
expect(result.block).toContain("# memory/concepts/summarized-page.md\nA");
|
|
786
801
|
expect(result.block).not.toContain("Long-form body content");
|
|
@@ -1820,7 +1835,9 @@ describe("injectMemoryV2Block", () => {
|
|
|
1820
1835
|
expect(row.mode).toBe("router");
|
|
1821
1836
|
const aliceRow = row.concepts.find((c) => c.slug === "alice-vscode");
|
|
1822
1837
|
expect(aliceRow).toBeDefined();
|
|
1823
|
-
|
|
1838
|
+
// Default-stub provenance is `tier3:0` (single-batch path); see the
|
|
1839
|
+
// runRouter mock for the synthesis rule.
|
|
1840
|
+
expect(aliceRow!.source).toBe("tier3:0");
|
|
1824
1841
|
expect(aliceRow!.status).toBe("injected");
|
|
1825
1842
|
expect(aliceRow!.finalActivation).toBe(0);
|
|
1826
1843
|
expect(aliceRow!.ownActivation).toBe(0);
|
|
@@ -2002,7 +2019,7 @@ describe("injectMemoryV2Block", () => {
|
|
|
2002
2019
|
);
|
|
2003
2020
|
expect(phantom).toBeDefined();
|
|
2004
2021
|
expect(phantom!.status).toBe("page_missing");
|
|
2005
|
-
expect(phantom!.source).toBe("
|
|
2022
|
+
expect(phantom!.source).toBe("tier3:0");
|
|
2006
2023
|
});
|
|
2007
2024
|
|
|
2008
2025
|
test("flag-on: router re-picking a prior-everInjected slug does NOT re-render it; non-overlapping picks render and append to everInjected", async () => {
|
|
@@ -2107,7 +2124,7 @@ describe("injectMemoryV2Block", () => {
|
|
|
2107
2124
|
expect(bobRow).toBeDefined();
|
|
2108
2125
|
expect(aliceRow!.source).toBe("carry_over");
|
|
2109
2126
|
expect(aliceRow!.status).toBe("in_context");
|
|
2110
|
-
expect(bobRow!.source).toBe("
|
|
2127
|
+
expect(bobRow!.source).toBe("tier3:0");
|
|
2111
2128
|
expect(bobRow!.status).toBe("injected");
|
|
2112
2129
|
});
|
|
2113
2130
|
|