@vellumai/assistant 0.4.17 → 0.4.18
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/eslint.config.mjs +2 -2
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +128 -120
- package/src/__tests__/account-registry.test.ts +121 -110
- package/src/__tests__/active-skill-tools.test.ts +200 -172
- package/src/__tests__/actor-token-service.test.ts +341 -274
- package/src/__tests__/agent-loop-thinking.test.ts +28 -19
- package/src/__tests__/agent-loop.test.ts +798 -378
- package/src/__tests__/anthropic-provider.test.ts +405 -247
- package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
- package/src/__tests__/app-bundler.test.ts +112 -79
- package/src/__tests__/app-executors.test.ts +205 -178
- package/src/__tests__/app-git-history.test.ts +90 -73
- package/src/__tests__/app-git-service.test.ts +67 -53
- package/src/__tests__/app-open-proxy.test.ts +29 -25
- package/src/__tests__/approval-conversation-turn.test.ts +100 -81
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
- package/src/__tests__/approval-message-composer.test.ts +119 -119
- package/src/__tests__/approval-primitive.test.ts +264 -233
- package/src/__tests__/approval-routes-http.test.ts +4 -3
- package/src/__tests__/asset-materialize-tool.test.ts +250 -178
- package/src/__tests__/asset-search-tool.test.ts +251 -191
- package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
- package/src/__tests__/assistant-attachments.test.ts +254 -186
- package/src/__tests__/assistant-event-hub.test.ts +105 -63
- package/src/__tests__/assistant-event.test.ts +66 -58
- package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
- package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
- package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
- package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
- package/src/__tests__/attachments-store.test.ts +240 -183
- package/src/__tests__/attachments.test.ts +70 -62
- package/src/__tests__/audit-log-rotation.test.ts +50 -35
- package/src/__tests__/browser-fill-credential.test.ts +169 -101
- package/src/__tests__/browser-manager.test.ts +97 -75
- package/src/__tests__/browser-runtime-check.test.ts +16 -15
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
- package/src/__tests__/browser-skill-endstate.test.ts +97 -72
- package/src/__tests__/bundle-scanner.test.ts +47 -22
- package/src/__tests__/bundled-asset.test.ts +74 -47
- package/src/__tests__/call-constants.test.ts +19 -19
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-conversation-messages.test.ts +90 -65
- package/src/__tests__/call-domain.test.ts +149 -121
- package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
- package/src/__tests__/call-pointer-messages.test.ts +213 -154
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
- package/src/__tests__/call-recovery.test.ts +232 -212
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
- package/src/__tests__/call-state-machine.test.ts +62 -51
- package/src/__tests__/call-state.test.ts +89 -75
- package/src/__tests__/call-store.test.ts +387 -316
- package/src/__tests__/callback-handoff-copy.test.ts +84 -82
- package/src/__tests__/canonical-guardian-store.test.ts +331 -280
- package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
- package/src/__tests__/channel-approval.test.ts +139 -137
- package/src/__tests__/channel-approvals.test.ts +0 -1
- package/src/__tests__/channel-delivery-store.test.ts +232 -194
- package/src/__tests__/channel-guardian.test.ts +5 -3
- package/src/__tests__/channel-invite-transport.test.ts +107 -92
- package/src/__tests__/channel-policy.test.ts +42 -38
- package/src/__tests__/channel-readiness-service.test.ts +119 -102
- package/src/__tests__/channel-reply-delivery.test.ts +147 -118
- package/src/__tests__/channel-retry-sweep.test.ts +153 -110
- package/src/__tests__/checker.test.ts +3309 -1850
- package/src/__tests__/clarification-resolver.test.ts +91 -79
- package/src/__tests__/classifier.test.ts +64 -54
- package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
- package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
- package/src/__tests__/clawhub.test.ts +92 -82
- package/src/__tests__/cli.test.ts +30 -30
- package/src/__tests__/clipboard.test.ts +53 -46
- package/src/__tests__/commit-guarantee.test.ts +59 -52
- package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
- package/src/__tests__/compaction.benchmark.test.ts +33 -31
- package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
- package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
- package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
- package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
- package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
- package/src/__tests__/computer-use-tools.test.ts +121 -98
- package/src/__tests__/config-schema.test.ts +443 -347
- package/src/__tests__/config-watcher.test.ts +96 -81
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
- package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
- package/src/__tests__/conflict-policy.test.ts +151 -80
- package/src/__tests__/conflict-store.test.ts +203 -157
- package/src/__tests__/connection-policy.test.ts +89 -59
- package/src/__tests__/contacts-tools.test.ts +247 -178
- package/src/__tests__/context-memory-e2e.test.ts +306 -214
- package/src/__tests__/context-token-estimator.test.ts +114 -74
- package/src/__tests__/context-window-manager.test.ts +269 -167
- package/src/__tests__/contradiction-checker.test.ts +161 -135
- package/src/__tests__/conversation-attention-store.test.ts +350 -290
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-pairing.test.ts +220 -113
- package/src/__tests__/conversation-store.test.ts +390 -235
- package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
- package/src/__tests__/credential-broker-server-use.test.ts +283 -243
- package/src/__tests__/credential-broker.test.ts +128 -74
- package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
- package/src/__tests__/credential-metadata-store.test.ts +360 -311
- package/src/__tests__/credential-policy-validate.test.ts +81 -65
- package/src/__tests__/credential-resolve.test.ts +212 -145
- package/src/__tests__/credential-security-e2e.test.ts +144 -103
- package/src/__tests__/credential-security-invariants.test.ts +253 -208
- package/src/__tests__/credential-selection.test.ts +254 -146
- package/src/__tests__/credential-vault-unit.test.ts +531 -341
- package/src/__tests__/credential-vault.test.ts +761 -484
- package/src/__tests__/daemon-assistant-events.test.ts +91 -66
- package/src/__tests__/daemon-lifecycle.test.ts +258 -190
- package/src/__tests__/daemon-server-session-init.test.ts +0 -1
- package/src/__tests__/date-context.test.ts +314 -249
- package/src/__tests__/db-migration-rollback.test.ts +259 -130
- package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
- package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/dictation-mode-detection.test.ts +77 -55
- package/src/__tests__/dictation-profile-store.test.ts +70 -56
- package/src/__tests__/dictation-text-processing.test.ts +53 -35
- package/src/__tests__/diff.test.ts +102 -98
- package/src/__tests__/domain-normalize.test.ts +54 -54
- package/src/__tests__/domain-policy.test.ts +71 -55
- package/src/__tests__/dynamic-page-surface.test.ts +31 -33
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
- package/src/__tests__/edit-engine.test.ts +56 -56
- package/src/__tests__/elevenlabs-client.test.ts +117 -91
- package/src/__tests__/elevenlabs-config.test.ts +32 -31
- package/src/__tests__/email-classifier.test.ts +15 -12
- package/src/__tests__/email-cli.test.ts +121 -108
- package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
- package/src/__tests__/encrypted-store.test.ts +180 -154
- package/src/__tests__/entity-extractor.test.ts +108 -87
- package/src/__tests__/entity-search.test.ts +664 -258
- package/src/__tests__/ephemeral-permissions.test.ts +224 -188
- package/src/__tests__/event-bus.test.ts +81 -77
- package/src/__tests__/extract-email.test.ts +29 -20
- package/src/__tests__/file-edit-tool.test.ts +62 -44
- package/src/__tests__/file-ops-service.test.ts +131 -114
- package/src/__tests__/file-read-tool.test.ts +48 -31
- package/src/__tests__/file-write-tool.test.ts +43 -37
- package/src/__tests__/filesystem-tools.test.ts +238 -209
- package/src/__tests__/followup-tools.test.ts +237 -162
- package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
- package/src/__tests__/frontmatter.test.ts +96 -81
- package/src/__tests__/fuzzy-match-property.test.ts +75 -81
- package/src/__tests__/fuzzy-match.test.ts +71 -65
- package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-image-service.test.ts +113 -100
- package/src/__tests__/gemini-provider.test.ts +297 -220
- package/src/__tests__/get-weather.test.ts +188 -114
- package/src/__tests__/gmail-integration.test.ts +0 -1
- package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
- package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
- package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
- package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
- package/src/__tests__/guardian-action-store.test.ts +158 -109
- package/src/__tests__/guardian-action-sweep.test.ts +114 -100
- package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
- package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
- package/src/__tests__/guardian-dispatch.test.ts +316 -256
- package/src/__tests__/guardian-grant-minting.test.ts +247 -178
- package/src/__tests__/guardian-outbound-http.test.ts +5 -3
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
- package/src/__tests__/guardian-question-copy.test.ts +17 -17
- package/src/__tests__/guardian-question-mode.test.ts +134 -100
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
- package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
- package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
- package/src/__tests__/handlers-slack-config.test.ts +63 -54
- package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
- package/src/__tests__/handlers-telegram-config.test.ts +662 -329
- package/src/__tests__/handlers-twitter-config.test.ts +525 -298
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
- package/src/__tests__/headless-browser-interactions.test.ts +444 -280
- package/src/__tests__/headless-browser-navigate.test.ts +116 -79
- package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
- package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
- package/src/__tests__/heartbeat-service.test.ts +76 -58
- package/src/__tests__/history-repair-observability.test.ts +14 -14
- package/src/__tests__/history-repair.test.ts +171 -167
- package/src/__tests__/home-base-bootstrap.test.ts +30 -27
- package/src/__tests__/hooks-blocking.test.ts +86 -37
- package/src/__tests__/hooks-cli.test.ts +104 -68
- package/src/__tests__/hooks-config.test.ts +81 -43
- package/src/__tests__/hooks-discovery.test.ts +106 -96
- package/src/__tests__/hooks-integration.test.ts +78 -72
- package/src/__tests__/hooks-manager.test.ts +99 -61
- package/src/__tests__/hooks-runner.test.ts +94 -71
- package/src/__tests__/hooks-settings.test.ts +69 -64
- package/src/__tests__/hooks-templates.test.ts +85 -54
- package/src/__tests__/hooks-ts-runner.test.ts +82 -45
- package/src/__tests__/hooks-watch.test.ts +32 -22
- package/src/__tests__/host-file-edit-tool.test.ts +190 -148
- package/src/__tests__/host-file-read-tool.test.ts +86 -63
- package/src/__tests__/host-file-write-tool.test.ts +98 -64
- package/src/__tests__/host-shell-tool.test.ts +342 -233
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-member-store.test.ts +163 -159
- package/src/__tests__/ingress-reconcile.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +441 -356
- package/src/__tests__/ingress-url-consistency.test.ts +125 -64
- package/src/__tests__/integration-status.test.ts +93 -73
- package/src/__tests__/intent-routing.test.ts +148 -118
- package/src/__tests__/invite-redemption-service.test.ts +163 -121
- package/src/__tests__/ipc-blob-store.test.ts +104 -91
- package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
- package/src/__tests__/ipc-contract.test.ts +24 -23
- package/src/__tests__/ipc-protocol.test.ts +52 -46
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
- package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
- package/src/__tests__/ipc-validate.test.ts +240 -179
- package/src/__tests__/key-migration.test.ts +123 -90
- package/src/__tests__/keychain.test.ts +150 -123
- package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
- package/src/__tests__/llm-usage-store.test.ts +112 -87
- package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
- package/src/__tests__/managed-store.test.ts +411 -360
- package/src/__tests__/mcp-cli.test.ts +189 -123
- package/src/__tests__/mcp-health-check.test.ts +26 -21
- package/src/__tests__/media-generate-image.test.ts +122 -99
- package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
- package/src/__tests__/media-visibility-policy.test.ts +86 -38
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
- package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
- package/src/__tests__/memory-query-builder.test.ts +32 -33
- package/src/__tests__/memory-recall-quality.test.ts +761 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
- package/src/__tests__/memory-regressions.test.ts +3725 -2642
- package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
- package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
- package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
- package/src/__tests__/messaging-send-tool.test.ts +36 -29
- package/src/__tests__/migration-cli-flows.test.ts +69 -53
- package/src/__tests__/migration-ordering.test.ts +103 -86
- package/src/__tests__/mime-builder.test.ts +55 -32
- package/src/__tests__/mock-signup-server.test.ts +384 -246
- package/src/__tests__/model-intents.test.ts +61 -37
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
- package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
- package/src/__tests__/non-member-access-request.test.ts +3 -2
- package/src/__tests__/notification-broadcaster.test.ts +99 -81
- package/src/__tests__/notification-decision-fallback.test.ts +223 -178
- package/src/__tests__/notification-decision-strategy.test.ts +375 -337
- package/src/__tests__/notification-deep-link.test.ts +67 -61
- package/src/__tests__/notification-guardian-path.test.ts +248 -206
- package/src/__tests__/notification-routing-intent.test.ts +166 -93
- package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
- package/src/__tests__/notification-thread-candidates.test.ts +64 -61
- package/src/__tests__/oauth-callback-registry.test.ts +40 -30
- package/src/__tests__/oauth-connect-handler.test.ts +109 -89
- package/src/__tests__/oauth-scope-policy.test.ts +63 -55
- package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
- package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
- package/src/__tests__/onboarding-template-contract.test.ts +93 -94
- package/src/__tests__/openai-provider.test.ts +366 -274
- package/src/__tests__/pairing-concurrent.test.ts +18 -12
- package/src/__tests__/pairing-routes.test.ts +45 -41
- package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
- package/src/__tests__/parser.test.ts +316 -226
- package/src/__tests__/path-classifier.test.ts +24 -25
- package/src/__tests__/path-policy.test.ts +187 -147
- package/src/__tests__/phone.test.ts +36 -36
- package/src/__tests__/platform-move-helper.test.ts +48 -40
- package/src/__tests__/platform-socket-path.test.ts +23 -24
- package/src/__tests__/platform-workspace-migration.test.ts +464 -414
- package/src/__tests__/platform.test.ts +61 -53
- package/src/__tests__/playbook-execution.test.ts +397 -265
- package/src/__tests__/playbook-tools.test.ts +267 -196
- package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
- package/src/__tests__/pricing.test.ts +316 -136
- package/src/__tests__/profile-compiler.test.ts +206 -188
- package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
- package/src/__tests__/provider-error-scenarios.test.ts +212 -158
- package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
- package/src/__tests__/provider-registry-ollama.test.ts +13 -9
- package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
- package/src/__tests__/proxy-approval-callback.test.ts +180 -119
- package/src/__tests__/public-ingress-urls.test.ts +112 -94
- package/src/__tests__/qdrant-manager.test.ts +147 -98
- package/src/__tests__/ratelimit.test.ts +152 -82
- package/src/__tests__/recording-handler.test.ts +273 -151
- package/src/__tests__/recording-intent-fallback.test.ts +94 -75
- package/src/__tests__/recording-intent-handler.test.ts +0 -1
- package/src/__tests__/recording-intent.test.ts +578 -379
- package/src/__tests__/recording-state-machine.test.ts +530 -316
- package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
- package/src/__tests__/recurrence-engine.test.ts +81 -41
- package/src/__tests__/recurrence-types.test.ts +63 -44
- package/src/__tests__/relay-server.test.ts +2131 -1602
- package/src/__tests__/reminder-store.test.ts +158 -80
- package/src/__tests__/reminder.test.ts +113 -109
- package/src/__tests__/remote-skill-policy.test.ts +96 -72
- package/src/__tests__/request-file-tool.test.ts +74 -67
- package/src/__tests__/response-tier.test.ts +131 -74
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
- package/src/__tests__/sandbox-host-parity.test.ts +377 -301
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
- package/src/__tests__/schedule-store.test.ts +268 -205
- package/src/__tests__/schedule-tools.test.ts +702 -524
- package/src/__tests__/scheduler-recurrence.test.ts +240 -130
- package/src/__tests__/scoped-approval-grants.test.ts +258 -168
- package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
- package/src/__tests__/script-proxy-certs.test.ts +38 -35
- package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
- package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
- package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
- package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
- package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
- package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
- package/src/__tests__/script-proxy-policy.test.ts +308 -153
- package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
- package/src/__tests__/script-proxy-router.test.ts +111 -77
- package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
- package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
- package/src/__tests__/secret-allowlist.test.ts +105 -90
- package/src/__tests__/secret-ingress-handler.test.ts +41 -30
- package/src/__tests__/secret-onetime-send.test.ts +67 -50
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
- package/src/__tests__/secret-response-routing.test.ts +50 -41
- package/src/__tests__/secret-scanner-executor.test.ts +152 -111
- package/src/__tests__/secret-scanner.test.ts +495 -413
- package/src/__tests__/secure-keys.test.ts +132 -121
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +43 -42
- package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
- package/src/__tests__/sequence-store.test.ts +335 -167
- package/src/__tests__/server-history-render.test.ts +341 -202
- package/src/__tests__/session-abort-tool-results.test.ts +133 -70
- package/src/__tests__/session-confirmation-signals.test.ts +252 -160
- package/src/__tests__/session-conflict-gate.test.ts +775 -585
- package/src/__tests__/session-error.test.ts +222 -191
- package/src/__tests__/session-evictor.test.ts +79 -62
- package/src/__tests__/session-init.benchmark.test.ts +170 -108
- package/src/__tests__/session-load-history-repair.test.ts +273 -139
- package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
- package/src/__tests__/session-pre-run-repair.test.ts +106 -59
- package/src/__tests__/session-profile-injection.test.ts +198 -130
- package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
- package/src/__tests__/session-queue.test.ts +624 -321
- package/src/__tests__/session-runtime-assembly.test.ts +425 -329
- package/src/__tests__/session-runtime-workspace.test.ts +69 -61
- package/src/__tests__/session-skill-tools.test.ts +973 -678
- package/src/__tests__/session-slash-known.test.ts +185 -133
- package/src/__tests__/session-slash-queue.test.ts +147 -81
- package/src/__tests__/session-slash-unknown.test.ts +135 -90
- package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
- package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
- package/src/__tests__/session-undo.test.ts +43 -30
- package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
- package/src/__tests__/session-workspace-injection.test.ts +245 -117
- package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
- package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
- package/src/__tests__/shell-credential-ref.test.ts +126 -90
- package/src/__tests__/shell-identity.test.ts +134 -111
- package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
- package/src/__tests__/shell-parser-property.test.ts +435 -288
- package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
- package/src/__tests__/size-guard.test.ts +42 -44
- package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
- package/src/__tests__/skill-feature-flags.test.ts +75 -47
- package/src/__tests__/skill-include-graph.test.ts +143 -148
- package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
- package/src/__tests__/skill-load-tool.test.ts +371 -199
- package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
- package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
- package/src/__tests__/skill-script-runner-host.test.ts +460 -250
- package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
- package/src/__tests__/skill-script-runner.test.ts +115 -74
- package/src/__tests__/skill-tool-factory.test.ts +140 -96
- package/src/__tests__/skill-tool-manifest.test.ts +306 -210
- package/src/__tests__/skill-version-hash.test.ts +70 -56
- package/src/__tests__/skills.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +127 -84
- package/src/__tests__/slack-skill.test.ts +60 -47
- package/src/__tests__/slash-commands-catalog.test.ts +37 -31
- package/src/__tests__/slash-commands-parser.test.ts +71 -64
- package/src/__tests__/slash-commands-resolver.test.ts +143 -107
- package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
- package/src/__tests__/speaker-identification.test.ts +28 -25
- package/src/__tests__/starter-bundle.test.ts +27 -23
- package/src/__tests__/starter-task-flow.test.ts +67 -52
- package/src/__tests__/subagent-manager-notify.test.ts +154 -108
- package/src/__tests__/subagent-tools.test.ts +311 -270
- package/src/__tests__/subagent-types.test.ts +40 -40
- package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
- package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
- package/src/__tests__/swarm-orchestrator.test.ts +135 -101
- package/src/__tests__/swarm-plan-validator.test.ts +125 -73
- package/src/__tests__/swarm-recursion.test.ts +58 -46
- package/src/__tests__/swarm-router-planner.test.ts +99 -74
- package/src/__tests__/swarm-session-integration.test.ts +148 -91
- package/src/__tests__/swarm-tool.test.ts +65 -45
- package/src/__tests__/swarm-worker-backend.test.ts +59 -45
- package/src/__tests__/swarm-worker-runner.test.ts +133 -118
- package/src/__tests__/system-prompt.test.ts +290 -256
- package/src/__tests__/task-compiler.test.ts +176 -120
- package/src/__tests__/task-management-tools.test.ts +561 -456
- package/src/__tests__/task-memory-cleanup.test.ts +627 -362
- package/src/__tests__/task-runner.test.ts +117 -94
- package/src/__tests__/task-scheduler.test.ts +113 -84
- package/src/__tests__/task-tools.test.ts +349 -264
- package/src/__tests__/terminal-sandbox.test.ts +138 -108
- package/src/__tests__/terminal-tools.test.ts +350 -305
- package/src/__tests__/thread-seed-composer.test.ts +307 -180
- package/src/__tests__/tool-approval-handler.test.ts +238 -137
- package/src/__tests__/tool-audit-listener.test.ts +69 -69
- package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
- package/src/__tests__/tool-executor-redaction.test.ts +112 -109
- package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
- package/src/__tests__/tool-executor.test.ts +1274 -674
- package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
- package/src/__tests__/tool-metrics-listener.test.ts +97 -85
- package/src/__tests__/tool-notification-listener.test.ts +42 -25
- package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
- package/src/__tests__/tool-policy.test.ts +44 -25
- package/src/__tests__/tool-profiling-listener.test.ts +99 -93
- package/src/__tests__/tool-result-truncation.test.ts +5 -4
- package/src/__tests__/tool-trace-listener.test.ts +131 -111
- package/src/__tests__/top-level-renderer.test.ts +62 -58
- package/src/__tests__/top-level-scanner.test.ts +68 -64
- package/src/__tests__/trace-emitter.test.ts +56 -56
- package/src/__tests__/trust-context-guards.test.ts +65 -65
- package/src/__tests__/trust-store.test.ts +1239 -806
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
- package/src/__tests__/trusted-contact-verification.test.ts +251 -231
- package/src/__tests__/turn-commit.test.ts +259 -200
- package/src/__tests__/twilio-provider.test.ts +140 -126
- package/src/__tests__/twilio-rest.test.ts +22 -18
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
- package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/twitter-auth-handler.test.ts +184 -139
- package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
- package/src/__tests__/twitter-cli-routing.test.ts +146 -99
- package/src/__tests__/twitter-oauth-client.test.ts +82 -65
- package/src/__tests__/update-bulletin-format.test.ts +69 -66
- package/src/__tests__/update-bulletin-state.test.ts +66 -60
- package/src/__tests__/update-bulletin.test.ts +150 -114
- package/src/__tests__/update-template-contract.test.ts +15 -10
- package/src/__tests__/url-safety.test.ts +288 -265
- package/src/__tests__/user-reference.test.ts +32 -32
- package/src/__tests__/view-image-tool.test.ts +118 -96
- package/src/__tests__/voice-invite-redemption.test.ts +111 -106
- package/src/__tests__/voice-quality.test.ts +117 -102
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
- package/src/__tests__/voice-session-bridge.test.ts +351 -216
- package/src/__tests__/weather-skill-regression.test.ts +170 -120
- package/src/__tests__/web-fetch.test.ts +664 -526
- package/src/__tests__/web-search.test.ts +379 -213
- package/src/__tests__/work-item-output.test.ts +90 -53
- package/src/__tests__/workspace-git-service.test.ts +437 -356
- package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
- package/src/__tests__/workspace-lifecycle.test.ts +98 -64
- package/src/__tests__/workspace-policy.test.ts +139 -71
- package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +4 -3
- package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
- package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/env.ts +3 -4
- package/src/memory/db-connection.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +10 -3
- package/src/messaging/providers/gmail/client.ts +280 -72
- package/src/runtime/auth/__tests__/context.test.ts +75 -65
- package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
- package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
- package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
- package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
- package/src/runtime/auth/__tests__/policy.test.ts +9 -9
- package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
- package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
- package/src/runtime/auth/__tests__/subject.test.ts +54 -54
- package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
- package/src/runtime/auth/scopes.ts +3 -0
- package/src/runtime/auth/token-service.ts +4 -1
- package/src/runtime/auth/types.ts +2 -1
- package/src/runtime/http-server.ts +2 -1
- package/src/security/secure-keys.ts +103 -53
- package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
- package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
- package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
* migration system detects and handles it gracefully.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { Database } from
|
|
14
|
-
import { describe, expect,test } from
|
|
15
|
-
import { drizzle } from 'drizzle-orm/bun-sqlite';
|
|
13
|
+
import { Database } from "bun:sqlite";
|
|
14
|
+
import { describe, expect, test } from "bun:test";
|
|
16
15
|
|
|
17
|
-
import {
|
|
18
|
-
|
|
16
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
17
|
+
|
|
18
|
+
import { getSqliteFrom } from "../memory/db-connection.js";
|
|
19
|
+
import * as schema from "../memory/schema.js";
|
|
19
20
|
import {
|
|
20
21
|
migrateJobDeferrals,
|
|
21
22
|
migrateMemoryEntityRelationDedup,
|
|
@@ -23,16 +24,16 @@ import {
|
|
|
23
24
|
MIGRATION_REGISTRY,
|
|
24
25
|
type MigrationValidationResult,
|
|
25
26
|
validateMigrationState,
|
|
26
|
-
} from
|
|
27
|
+
} from "../memory/schema-migration.js";
|
|
27
28
|
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
// Helpers
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
31
32
|
|
|
32
33
|
function createTestDb() {
|
|
33
|
-
const sqlite = new Database(
|
|
34
|
-
sqlite.exec(
|
|
35
|
-
sqlite.exec(
|
|
34
|
+
const sqlite = new Database(":memory:");
|
|
35
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
36
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
36
37
|
return drizzle(sqlite, { schema });
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -112,8 +113,8 @@ function bootstrapEntityRelationsTable(raw: Database): void {
|
|
|
112
113
|
// 1. Crash-between-migrations
|
|
113
114
|
// ---------------------------------------------------------------------------
|
|
114
115
|
|
|
115
|
-
describe(
|
|
116
|
-
test(
|
|
116
|
+
describe("crash-between-migrations: consistent state on re-run", () => {
|
|
117
|
+
test("migrateJobDeferrals: crashed migration (started but not completed) re-runs successfully", () => {
|
|
117
118
|
// Simulate a crash scenario: the checkpoint key 'migration_job_deferrals'
|
|
118
119
|
// is present with value 'started' (as if a crash marker was set before the
|
|
119
120
|
// real completion INSERT). The actual migration logic uses BEGIN/COMMIT, so
|
|
@@ -144,15 +145,20 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
144
145
|
// Note: the current migrateJobDeferrals uses a simple presence check (SELECT 1), so
|
|
145
146
|
// inserting any value for the key marks it as "done" from the guard's perspective.
|
|
146
147
|
// This test documents the actual behavior: the guard sees the key and skips.
|
|
147
|
-
raw.exec(
|
|
148
|
+
raw.exec(
|
|
149
|
+
`INSERT INTO memory_checkpoints (key, value, updated_at) VALUES ('migration_job_deferrals', 'started', ${now})`,
|
|
150
|
+
);
|
|
148
151
|
|
|
149
152
|
// Run migration — guard will see the 'started' checkpoint and skip.
|
|
150
153
|
migrateJobDeferrals(db);
|
|
151
154
|
|
|
152
155
|
// Since the checkpoint exists (even as 'started'), the migration was skipped.
|
|
153
156
|
// The job's deferrals column should still be 0 (migration didn't run).
|
|
154
|
-
const job = raw
|
|
155
|
-
|
|
157
|
+
const job = raw
|
|
158
|
+
.query(`SELECT * FROM memory_jobs WHERE id = 'job-1'`)
|
|
159
|
+
.get() as {
|
|
160
|
+
attempts: number;
|
|
161
|
+
deferrals: number;
|
|
156
162
|
} | null;
|
|
157
163
|
expect(job).toBeTruthy();
|
|
158
164
|
// Migration was skipped because the checkpoint key exists.
|
|
@@ -160,7 +166,7 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
160
166
|
expect(job!.attempts).toBe(5);
|
|
161
167
|
});
|
|
162
168
|
|
|
163
|
-
test(
|
|
169
|
+
test("migrateJobDeferrals: no checkpoint means migration runs and reconciles data", () => {
|
|
164
170
|
// Clean start: no checkpoint written. The migration should run, move the
|
|
165
171
|
// attempts count into deferrals, and write the completion checkpoint.
|
|
166
172
|
const db = createTestDb();
|
|
@@ -186,8 +192,12 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
186
192
|
migrateJobDeferrals(db);
|
|
187
193
|
|
|
188
194
|
// Legacy embed_segment job should have deferrals = 3, attempts = 0.
|
|
189
|
-
const legacyJob = raw
|
|
190
|
-
|
|
195
|
+
const legacyJob = raw
|
|
196
|
+
.query(`SELECT * FROM memory_jobs WHERE id = 'job-legacy'`)
|
|
197
|
+
.get() as {
|
|
198
|
+
attempts: number;
|
|
199
|
+
deferrals: number;
|
|
200
|
+
last_error: string | null;
|
|
191
201
|
} | null;
|
|
192
202
|
expect(legacyJob).toBeTruthy();
|
|
193
203
|
expect(legacyJob!.deferrals).toBe(3);
|
|
@@ -200,12 +210,16 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
200
210
|
// AND type IN ('embed_segment', 'embed_item', 'embed_summary') — it does include embed_item.
|
|
201
211
|
// The last_error check: the migration doesn't filter by last_error, so embed_item also moves.
|
|
202
212
|
// Verify completion checkpoint is written.
|
|
203
|
-
const checkpoint = raw
|
|
213
|
+
const checkpoint = raw
|
|
214
|
+
.query(
|
|
215
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_job_deferrals'`,
|
|
216
|
+
)
|
|
217
|
+
.get() as { value: string } | null;
|
|
204
218
|
expect(checkpoint).toBeTruthy();
|
|
205
|
-
expect(checkpoint!.value).toBe(
|
|
219
|
+
expect(checkpoint!.value).toBe("1");
|
|
206
220
|
});
|
|
207
221
|
|
|
208
|
-
test(
|
|
222
|
+
test("migrateJobDeferrals: migration is idempotent — second call is a no-op", () => {
|
|
209
223
|
const db = createTestDb();
|
|
210
224
|
const raw = getRaw(db);
|
|
211
225
|
|
|
@@ -222,15 +236,25 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
222
236
|
migrateJobDeferrals(db);
|
|
223
237
|
|
|
224
238
|
// Snapshot state after first run.
|
|
225
|
-
const after1 = raw
|
|
226
|
-
|
|
239
|
+
const after1 = raw
|
|
240
|
+
.query(
|
|
241
|
+
`SELECT attempts, deferrals FROM memory_jobs WHERE id = 'job-idem'`,
|
|
242
|
+
)
|
|
243
|
+
.get() as {
|
|
244
|
+
attempts: number;
|
|
245
|
+
deferrals: number;
|
|
227
246
|
};
|
|
228
247
|
|
|
229
248
|
// Second run — should be a no-op (checkpoint already written).
|
|
230
249
|
migrateJobDeferrals(db);
|
|
231
250
|
|
|
232
|
-
const after2 = raw
|
|
233
|
-
|
|
251
|
+
const after2 = raw
|
|
252
|
+
.query(
|
|
253
|
+
`SELECT attempts, deferrals FROM memory_jobs WHERE id = 'job-idem'`,
|
|
254
|
+
)
|
|
255
|
+
.get() as {
|
|
256
|
+
attempts: number;
|
|
257
|
+
deferrals: number;
|
|
234
258
|
};
|
|
235
259
|
|
|
236
260
|
expect(after1.deferrals).toBe(4);
|
|
@@ -240,7 +264,7 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
240
264
|
expect(after2.attempts).toBe(after1.attempts);
|
|
241
265
|
});
|
|
242
266
|
|
|
243
|
-
test(
|
|
267
|
+
test("crash in migrateMemoryEntityRelationDedup: temp table left behind is cleaned up on retry", () => {
|
|
244
268
|
// Simulate a crash mid-migration that left the temp staging table behind.
|
|
245
269
|
// On retry the migration should clean up the temp table, then succeed.
|
|
246
270
|
const db = createTestDb();
|
|
@@ -252,8 +276,12 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
252
276
|
const now = Date.now();
|
|
253
277
|
|
|
254
278
|
// Insert duplicate entity relations that need deduplication.
|
|
255
|
-
raw.exec(
|
|
256
|
-
|
|
279
|
+
raw.exec(
|
|
280
|
+
`INSERT INTO memory_entity_relations VALUES ('r1', 'e1', 'e2', 'knows', NULL, ${now - 2000}, ${now - 1000})`,
|
|
281
|
+
);
|
|
282
|
+
raw.exec(
|
|
283
|
+
`INSERT INTO memory_entity_relations VALUES ('r2', 'e1', 'e2', 'knows', 'some evidence', ${now - 3000}, ${now})`,
|
|
284
|
+
);
|
|
257
285
|
|
|
258
286
|
// Simulate a crash: manually create the temp staging table (as if the migration
|
|
259
287
|
// started creating it but crashed before finishing). The migration's DROP TABLE IF EXISTS
|
|
@@ -266,44 +294,57 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
266
294
|
`);
|
|
267
295
|
|
|
268
296
|
// Verify stale temp table exists before migration retry.
|
|
269
|
-
const tempBefore = raw
|
|
270
|
-
|
|
271
|
-
|
|
297
|
+
const tempBefore = raw
|
|
298
|
+
.query(
|
|
299
|
+
`SELECT name FROM sqlite_temp_master WHERE type = 'table' AND name = 'memory_entity_relation_merge'`,
|
|
300
|
+
)
|
|
301
|
+
.get();
|
|
272
302
|
expect(tempBefore).toBeTruthy();
|
|
273
303
|
|
|
274
304
|
// Run the migration — it should drop the stale temp table and proceed correctly.
|
|
275
305
|
migrateMemoryEntityRelationDedup(db);
|
|
276
306
|
|
|
277
307
|
// After migration: temp table should be gone.
|
|
278
|
-
const tempAfter = raw
|
|
279
|
-
|
|
280
|
-
|
|
308
|
+
const tempAfter = raw
|
|
309
|
+
.query(
|
|
310
|
+
`SELECT name FROM sqlite_temp_master WHERE type = 'table' AND name = 'memory_entity_relation_merge'`,
|
|
311
|
+
)
|
|
312
|
+
.get();
|
|
281
313
|
expect(tempAfter).toBeNull();
|
|
282
314
|
|
|
283
315
|
// Duplicates should have been merged into a single row.
|
|
284
|
-
const relations = raw
|
|
285
|
-
|
|
286
|
-
|
|
316
|
+
const relations = raw
|
|
317
|
+
.query(`SELECT * FROM memory_entity_relations ORDER BY id`)
|
|
318
|
+
.all() as Array<{
|
|
319
|
+
id: string;
|
|
320
|
+
source_entity_id: string;
|
|
321
|
+
target_entity_id: string;
|
|
322
|
+
relation: string;
|
|
323
|
+
first_seen_at: number;
|
|
324
|
+
last_seen_at: number;
|
|
325
|
+
evidence: string | null;
|
|
287
326
|
}>;
|
|
288
327
|
expect(relations).toHaveLength(1);
|
|
289
|
-
expect(relations[0].source_entity_id).toBe(
|
|
290
|
-
expect(relations[0].target_entity_id).toBe(
|
|
291
|
-
expect(relations[0].relation).toBe(
|
|
328
|
+
expect(relations[0].source_entity_id).toBe("e1");
|
|
329
|
+
expect(relations[0].target_entity_id).toBe("e2");
|
|
330
|
+
expect(relations[0].relation).toBe("knows");
|
|
292
331
|
// Merged: MIN(first_seen_at), MAX(last_seen_at).
|
|
293
332
|
expect(relations[0].first_seen_at).toBe(now - 3000);
|
|
294
333
|
expect(relations[0].last_seen_at).toBe(now);
|
|
295
334
|
// Evidence from latest row (rank_latest = 1).
|
|
296
|
-
expect(relations[0].evidence).toBe(
|
|
335
|
+
expect(relations[0].evidence).toBe("some evidence");
|
|
297
336
|
|
|
298
337
|
// Completion checkpoint must be written.
|
|
299
|
-
const cp = raw
|
|
300
|
-
|
|
301
|
-
|
|
338
|
+
const cp = raw
|
|
339
|
+
.query(
|
|
340
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_memory_entity_relations_dedup_v1'`,
|
|
341
|
+
)
|
|
342
|
+
.get() as { value: string } | null;
|
|
302
343
|
expect(cp).toBeTruthy();
|
|
303
|
-
expect(cp!.value).toBe(
|
|
344
|
+
expect(cp!.value).toBe("1");
|
|
304
345
|
});
|
|
305
346
|
|
|
306
|
-
test(
|
|
347
|
+
test("crash in transaction: rolled-back migration leaves DB in pre-migration state", () => {
|
|
307
348
|
// Verify that when migrateMemoryEntityRelationDedup fails mid-transaction, it
|
|
308
349
|
// rolls back cleanly — the DB remains in the pre-migration state and the
|
|
309
350
|
// checkpoint is NOT written.
|
|
@@ -318,10 +359,18 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
318
359
|
bootstrapEntityRelationsTable(raw);
|
|
319
360
|
|
|
320
361
|
const now = Date.now();
|
|
321
|
-
raw.exec(
|
|
322
|
-
|
|
362
|
+
raw.exec(
|
|
363
|
+
`INSERT INTO memory_entity_relations VALUES ('r1', 'e1', 'e2', 'knows', NULL, ${now}, ${now})`,
|
|
364
|
+
);
|
|
365
|
+
raw.exec(
|
|
366
|
+
`INSERT INTO memory_entity_relations VALUES ('r2', 'e1', 'e2', 'knows', 'evidence', ${now - 1000}, ${now})`,
|
|
367
|
+
);
|
|
323
368
|
|
|
324
|
-
const countBefore = (
|
|
369
|
+
const countBefore = (
|
|
370
|
+
raw.query(`SELECT COUNT(*) AS c FROM memory_entity_relations`).get() as {
|
|
371
|
+
c: number;
|
|
372
|
+
}
|
|
373
|
+
).c;
|
|
325
374
|
expect(countBefore).toBe(2);
|
|
326
375
|
|
|
327
376
|
// Install a trigger that raises an error on the first INSERT, causing the
|
|
@@ -346,17 +395,23 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
346
395
|
raw.exec(`DROP TRIGGER IF EXISTS fail_on_insert`);
|
|
347
396
|
|
|
348
397
|
// After rollback: row count must be unchanged (DELETE was rolled back).
|
|
349
|
-
const countAfter = (
|
|
398
|
+
const countAfter = (
|
|
399
|
+
raw.query(`SELECT COUNT(*) AS c FROM memory_entity_relations`).get() as {
|
|
400
|
+
c: number;
|
|
401
|
+
}
|
|
402
|
+
).c;
|
|
350
403
|
expect(countAfter).toBe(2);
|
|
351
404
|
|
|
352
405
|
// No checkpoint should have been written (COMMIT never executed).
|
|
353
|
-
const cp = raw
|
|
354
|
-
|
|
355
|
-
|
|
406
|
+
const cp = raw
|
|
407
|
+
.query(
|
|
408
|
+
`SELECT 1 FROM memory_checkpoints WHERE key = 'migration_memory_entity_relations_dedup_v1'`,
|
|
409
|
+
)
|
|
410
|
+
.get();
|
|
356
411
|
expect(cp).toBeNull();
|
|
357
412
|
});
|
|
358
413
|
|
|
359
|
-
test(
|
|
414
|
+
test("multiple migrations: crash after first completes leaves second un-checkpointed", () => {
|
|
360
415
|
// Simulates: migration_job_deferrals completed (checkpoint = '1'),
|
|
361
416
|
// but a second migration (memory_entity_relations_dedup) never ran.
|
|
362
417
|
// On next startup, the first skips (checkpoint found), the second runs fresh.
|
|
@@ -370,29 +425,41 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
370
425
|
const now = Date.now();
|
|
371
426
|
|
|
372
427
|
// Manually set first migration as complete.
|
|
373
|
-
raw.exec(
|
|
428
|
+
raw.exec(
|
|
429
|
+
`INSERT INTO memory_checkpoints (key, value, updated_at) VALUES ('migration_job_deferrals', '1', ${now})`,
|
|
430
|
+
);
|
|
374
431
|
|
|
375
432
|
// Insert duplicate relations that need migration.
|
|
376
|
-
raw.exec(
|
|
377
|
-
|
|
433
|
+
raw.exec(
|
|
434
|
+
`INSERT INTO memory_entity_relations VALUES ('r1', 'e1', 'e2', 'friends', NULL, ${now - 1000}, ${now - 500})`,
|
|
435
|
+
);
|
|
436
|
+
raw.exec(
|
|
437
|
+
`INSERT INTO memory_entity_relations VALUES ('r2', 'e1', 'e2', 'friends', 'evidence', ${now - 2000}, ${now})`,
|
|
438
|
+
);
|
|
378
439
|
|
|
379
440
|
// Run second migration from clean state (no checkpoint for it).
|
|
380
441
|
migrateMemoryEntityRelationDedup(db);
|
|
381
442
|
|
|
382
443
|
// Second migration should have run and deduplicated.
|
|
383
|
-
const relations = raw
|
|
444
|
+
const relations = raw
|
|
445
|
+
.query(`SELECT COUNT(*) AS c FROM memory_entity_relations`)
|
|
446
|
+
.all() as Array<{ c: number }>;
|
|
384
447
|
expect(relations[0].c).toBe(1);
|
|
385
448
|
|
|
386
449
|
// Both checkpoints should now exist.
|
|
387
|
-
const cp1 = raw
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
450
|
+
const cp1 = raw
|
|
451
|
+
.query(
|
|
452
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_job_deferrals'`,
|
|
453
|
+
)
|
|
454
|
+
.get() as { value: string } | null;
|
|
455
|
+
const cp2 = raw
|
|
456
|
+
.query(
|
|
457
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_memory_entity_relations_dedup_v1'`,
|
|
458
|
+
)
|
|
459
|
+
.get() as { value: string } | null;
|
|
460
|
+
|
|
461
|
+
expect(cp1!.value).toBe("1");
|
|
462
|
+
expect(cp2!.value).toBe("1");
|
|
396
463
|
});
|
|
397
464
|
});
|
|
398
465
|
|
|
@@ -400,7 +467,7 @@ describe('crash-between-migrations: consistent state on re-run', () => {
|
|
|
400
467
|
// 2. Schema-drift recovery
|
|
401
468
|
// ---------------------------------------------------------------------------
|
|
402
469
|
|
|
403
|
-
describe(
|
|
470
|
+
describe("schema-drift recovery: migration handles unexpected schema state", () => {
|
|
404
471
|
test('validateMigrationState: detects crashed migration with "started" value', () => {
|
|
405
472
|
// Simulate a scenario where a checkpoint value is 'started' — meaning the
|
|
406
473
|
// migration wrote a start marker (via UPSERT) but never wrote the completion '1'.
|
|
@@ -414,20 +481,26 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
414
481
|
const now = Date.now();
|
|
415
482
|
|
|
416
483
|
// Insert a "started" checkpoint — simulates mid-migration crash.
|
|
417
|
-
raw.exec(
|
|
484
|
+
raw.exec(
|
|
485
|
+
`INSERT INTO memory_checkpoints (key, value, updated_at) VALUES ('migration_job_deferrals', 'started', ${now})`,
|
|
486
|
+
);
|
|
418
487
|
// A completed checkpoint should not be flagged.
|
|
419
|
-
raw.exec(
|
|
488
|
+
raw.exec(
|
|
489
|
+
`INSERT INTO memory_checkpoints (key, value, updated_at) VALUES ('migration_memory_entity_relations_dedup_v1', '1', ${now})`,
|
|
490
|
+
);
|
|
420
491
|
|
|
421
492
|
// validateMigrationState logs warnings for crashed migrations and returns
|
|
422
493
|
// structured diagnostic data. Assert directly on the returned result rather
|
|
423
494
|
// than re-deriving the crashed list from the raw DB — this verifies the
|
|
424
495
|
// function itself detects the crash, not just that the data is present.
|
|
425
496
|
const result: MigrationValidationResult = validateMigrationState(db);
|
|
426
|
-
expect(result.crashed).toContain(
|
|
427
|
-
expect(result.crashed).not.toContain(
|
|
497
|
+
expect(result.crashed).toContain("migration_job_deferrals");
|
|
498
|
+
expect(result.crashed).not.toContain(
|
|
499
|
+
"migration_memory_entity_relations_dedup_v1",
|
|
500
|
+
);
|
|
428
501
|
});
|
|
429
502
|
|
|
430
|
-
test(
|
|
503
|
+
test("validateMigrationState: detects dependency violation (child complete, parent missing)", () => {
|
|
431
504
|
// Simulates schema drift: a dependent migration ran (checkpoint written) but
|
|
432
505
|
// its declared prerequisite migration has no checkpoint. This indicates the
|
|
433
506
|
// migrations were applied out of order — a schema consistency violation.
|
|
@@ -447,19 +520,25 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
447
520
|
|
|
448
521
|
// validateMigrationState throws an IntegrityError on dependency violations
|
|
449
522
|
// to block daemon startup with an inconsistent schema.
|
|
450
|
-
expect(() => validateMigrationState(db)).toThrow(
|
|
451
|
-
|
|
523
|
+
expect(() => validateMigrationState(db)).toThrow(
|
|
524
|
+
"Migration dependency violations detected",
|
|
525
|
+
);
|
|
526
|
+
expect(() => validateMigrationState(db)).toThrow(
|
|
527
|
+
"migration_memory_items_fingerprint_scope_unique_v1",
|
|
528
|
+
);
|
|
452
529
|
|
|
453
530
|
// Sanity-check: confirm the registry also declares this dependency, so the
|
|
454
531
|
// violation detection is grounded in real schema intent.
|
|
455
532
|
const saltedEntry = MIGRATION_REGISTRY.find(
|
|
456
|
-
(e) => e.key ===
|
|
533
|
+
(e) => e.key === "migration_memory_items_scope_salted_fingerprints_v1",
|
|
457
534
|
);
|
|
458
535
|
expect(saltedEntry).toBeTruthy();
|
|
459
|
-
expect(saltedEntry!.dependsOn).toContain(
|
|
536
|
+
expect(saltedEntry!.dependsOn).toContain(
|
|
537
|
+
"migration_memory_items_fingerprint_scope_unique_v1",
|
|
538
|
+
);
|
|
460
539
|
});
|
|
461
540
|
|
|
462
|
-
test(
|
|
541
|
+
test("validateMigrationState: no checkpoints table is handled gracefully", () => {
|
|
463
542
|
// On a very old database, memory_checkpoints may not exist at all.
|
|
464
543
|
// validateMigrationState should catch the error and return without crashing.
|
|
465
544
|
const db = createTestDb();
|
|
@@ -468,7 +547,7 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
468
547
|
expect(() => validateMigrationState(db)).not.toThrow();
|
|
469
548
|
});
|
|
470
549
|
|
|
471
|
-
test(
|
|
550
|
+
test("migrateMemoryItemsFingerprintScopeUnique: old schema with UNIQUE on fingerprint is migrated", () => {
|
|
472
551
|
// Schema drift: the DB has the old column-level UNIQUE constraint on fingerprint.
|
|
473
552
|
// The migration should detect this, rebuild the table without the constraint,
|
|
474
553
|
// and write the completion checkpoint.
|
|
@@ -495,37 +574,53 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
495
574
|
`);
|
|
496
575
|
|
|
497
576
|
// Verify old schema has column-level UNIQUE.
|
|
498
|
-
const ddlBefore =
|
|
499
|
-
|
|
500
|
-
|
|
577
|
+
const ddlBefore =
|
|
578
|
+
(
|
|
579
|
+
raw
|
|
580
|
+
.query(
|
|
581
|
+
`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_items'`,
|
|
582
|
+
)
|
|
583
|
+
.get() as { sql: string } | null
|
|
584
|
+
)?.sql ?? "";
|
|
501
585
|
expect(ddlBefore).toMatch(/fingerprint\s+TEXT\s+NOT\s+NULL\s+UNIQUE/i);
|
|
502
586
|
|
|
503
587
|
// Run migration.
|
|
504
588
|
migrateMemoryItemsFingerprintScopeUnique(db);
|
|
505
589
|
|
|
506
590
|
// Checkpoint should be written.
|
|
507
|
-
const cp = raw
|
|
508
|
-
|
|
509
|
-
|
|
591
|
+
const cp = raw
|
|
592
|
+
.query(
|
|
593
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_memory_items_fingerprint_scope_unique_v1'`,
|
|
594
|
+
)
|
|
595
|
+
.get() as { value: string } | null;
|
|
510
596
|
expect(cp).toBeTruthy();
|
|
511
|
-
expect(cp!.value).toBe(
|
|
597
|
+
expect(cp!.value).toBe("1");
|
|
512
598
|
|
|
513
599
|
// The new DDL should NOT have column-level UNIQUE on fingerprint.
|
|
514
|
-
const ddlAfter =
|
|
515
|
-
|
|
516
|
-
|
|
600
|
+
const ddlAfter =
|
|
601
|
+
(
|
|
602
|
+
raw
|
|
603
|
+
.query(
|
|
604
|
+
`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_items'`,
|
|
605
|
+
)
|
|
606
|
+
.get() as { sql: string } | null
|
|
607
|
+
)?.sql ?? "";
|
|
517
608
|
expect(ddlAfter).not.toMatch(/fingerprint\s+TEXT\s+NOT\s+NULL\s+UNIQUE/i);
|
|
518
609
|
|
|
519
610
|
// Existing rows should still be present and readable.
|
|
520
|
-
const items = raw
|
|
521
|
-
id
|
|
611
|
+
const items = raw
|
|
612
|
+
.query(`SELECT id, fingerprint, scope_id FROM memory_items ORDER BY id`)
|
|
613
|
+
.all() as Array<{
|
|
614
|
+
id: string;
|
|
615
|
+
fingerprint: string;
|
|
616
|
+
scope_id: string;
|
|
522
617
|
}>;
|
|
523
618
|
expect(items).toHaveLength(2);
|
|
524
|
-
expect(items[0].id).toBe(
|
|
525
|
-
expect(items[1].id).toBe(
|
|
619
|
+
expect(items[0].id).toBe("item-1");
|
|
620
|
+
expect(items[1].id).toBe("item-2");
|
|
526
621
|
});
|
|
527
622
|
|
|
528
|
-
test(
|
|
623
|
+
test("migrateMemoryItemsFingerprintScopeUnique: fresh DB (no column UNIQUE) is handled without rebuilding", () => {
|
|
529
624
|
// On a fresh install, the table was created without the column-level UNIQUE.
|
|
530
625
|
// The migration should detect this and just write the checkpoint without
|
|
531
626
|
// doing any table rebuild.
|
|
@@ -561,18 +656,22 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
561
656
|
migrateMemoryItemsFingerprintScopeUnique(db);
|
|
562
657
|
|
|
563
658
|
// Checkpoint should be written (short-circuit path).
|
|
564
|
-
const cp = raw
|
|
565
|
-
|
|
566
|
-
|
|
659
|
+
const cp = raw
|
|
660
|
+
.query(
|
|
661
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_memory_items_fingerprint_scope_unique_v1'`,
|
|
662
|
+
)
|
|
663
|
+
.get() as { value: string } | null;
|
|
567
664
|
expect(cp).toBeTruthy();
|
|
568
|
-
expect(cp!.value).toBe(
|
|
665
|
+
expect(cp!.value).toBe("1");
|
|
569
666
|
|
|
570
667
|
// Row should still be there.
|
|
571
|
-
const item = raw
|
|
668
|
+
const item = raw
|
|
669
|
+
.query(`SELECT id FROM memory_items WHERE id = 'item-modern'`)
|
|
670
|
+
.get();
|
|
572
671
|
expect(item).toBeTruthy();
|
|
573
672
|
});
|
|
574
673
|
|
|
575
|
-
test(
|
|
674
|
+
test("migrateMemoryItemsFingerprintScopeUnique: already-migrated DB is idempotent", () => {
|
|
576
675
|
// If the migration has already completed (checkpoint exists), a second run
|
|
577
676
|
// must not modify the schema or data.
|
|
578
677
|
const db = createTestDb();
|
|
@@ -604,17 +703,21 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
604
703
|
|
|
605
704
|
// First run.
|
|
606
705
|
migrateMemoryItemsFingerprintScopeUnique(db);
|
|
607
|
-
const countAfter1 = (
|
|
706
|
+
const countAfter1 = (
|
|
707
|
+
raw.query(`SELECT COUNT(*) AS c FROM memory_items`).get() as { c: number }
|
|
708
|
+
).c;
|
|
608
709
|
|
|
609
710
|
// Second run — must be idempotent.
|
|
610
711
|
migrateMemoryItemsFingerprintScopeUnique(db);
|
|
611
|
-
const countAfter2 = (
|
|
712
|
+
const countAfter2 = (
|
|
713
|
+
raw.query(`SELECT COUNT(*) AS c FROM memory_items`).get() as { c: number }
|
|
714
|
+
).c;
|
|
612
715
|
|
|
613
716
|
expect(countAfter1).toBe(1);
|
|
614
717
|
expect(countAfter2).toBe(1);
|
|
615
718
|
});
|
|
616
719
|
|
|
617
|
-
test(
|
|
720
|
+
test("schema-drift: partial migration left _new table behind — next run handles it", () => {
|
|
618
721
|
// Simulate schema drift where a previous migration run created a *_new table
|
|
619
722
|
// (e.g., memory_items_new) but crashed before the DROP + RENAME step.
|
|
620
723
|
// The next migration run on the same migration will fail because memory_items_new
|
|
@@ -644,9 +747,11 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
644
747
|
`);
|
|
645
748
|
|
|
646
749
|
// The stale _new table exists.
|
|
647
|
-
const newTableBefore = raw
|
|
648
|
-
|
|
649
|
-
|
|
750
|
+
const newTableBefore = raw
|
|
751
|
+
.query(
|
|
752
|
+
`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'memory_items_new'`,
|
|
753
|
+
)
|
|
754
|
+
.get();
|
|
650
755
|
expect(newTableBefore).toBeTruthy();
|
|
651
756
|
|
|
652
757
|
// Running the migration now will fail because memory_items_new already exists.
|
|
@@ -660,15 +765,19 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
660
765
|
|
|
661
766
|
if (threwError) {
|
|
662
767
|
// The migration failed — checkpoint should NOT have been written.
|
|
663
|
-
const cpAfterFail = raw
|
|
664
|
-
|
|
665
|
-
|
|
768
|
+
const cpAfterFail = raw
|
|
769
|
+
.query(
|
|
770
|
+
`SELECT 1 FROM memory_checkpoints WHERE key = 'migration_memory_items_fingerprint_scope_unique_v1'`,
|
|
771
|
+
)
|
|
772
|
+
.get();
|
|
666
773
|
expect(cpAfterFail).toBeNull();
|
|
667
774
|
|
|
668
775
|
// Original table must still be intact.
|
|
669
|
-
const originalTableStillExists = raw
|
|
670
|
-
|
|
671
|
-
|
|
776
|
+
const originalTableStillExists = raw
|
|
777
|
+
.query(
|
|
778
|
+
`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'memory_items'`,
|
|
779
|
+
)
|
|
780
|
+
.get();
|
|
672
781
|
expect(originalTableStillExists).toBeTruthy();
|
|
673
782
|
|
|
674
783
|
// Recovery: drop the stale _new table, then re-run the migration.
|
|
@@ -676,22 +785,26 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
676
785
|
migrateMemoryItemsFingerprintScopeUnique(db);
|
|
677
786
|
|
|
678
787
|
// After recovery: checkpoint should be written and original table migrated.
|
|
679
|
-
const cpAfterRecovery = raw
|
|
680
|
-
|
|
681
|
-
|
|
788
|
+
const cpAfterRecovery = raw
|
|
789
|
+
.query(
|
|
790
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_memory_items_fingerprint_scope_unique_v1'`,
|
|
791
|
+
)
|
|
792
|
+
.get() as { value: string } | null;
|
|
682
793
|
expect(cpAfterRecovery).toBeTruthy();
|
|
683
|
-
expect(cpAfterRecovery!.value).toBe(
|
|
794
|
+
expect(cpAfterRecovery!.value).toBe("1");
|
|
684
795
|
} else {
|
|
685
796
|
// If the migration succeeded despite the stale table (e.g., CREATE TABLE IF NOT EXISTS
|
|
686
797
|
// silently skipped), the checkpoint should be written.
|
|
687
|
-
const cp = raw
|
|
688
|
-
|
|
689
|
-
|
|
798
|
+
const cp = raw
|
|
799
|
+
.query(
|
|
800
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_memory_items_fingerprint_scope_unique_v1'`,
|
|
801
|
+
)
|
|
802
|
+
.get() as { value: string } | null;
|
|
690
803
|
expect(cp).toBeTruthy();
|
|
691
804
|
}
|
|
692
805
|
});
|
|
693
806
|
|
|
694
|
-
test(
|
|
807
|
+
test("MIGRATION_REGISTRY: version numbers are strictly monotonically increasing", () => {
|
|
695
808
|
// Registry ordering invariant: each entry's version must be strictly greater
|
|
696
809
|
// than the previous one. A violation here would mean the ordering guarantees
|
|
697
810
|
// documented in the migration comments cannot be relied upon.
|
|
@@ -702,7 +815,7 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
702
815
|
}
|
|
703
816
|
});
|
|
704
817
|
|
|
705
|
-
test(
|
|
818
|
+
test("MIGRATION_REGISTRY: all dependsOn references point to existing registry keys", () => {
|
|
706
819
|
// Schema drift guard: if a migration declares a dependency on a key that
|
|
707
820
|
// doesn't exist in the registry, the dependency check in validateMigrationState
|
|
708
821
|
// can never be satisfied. This test ensures all declared dependencies are valid.
|
|
@@ -715,7 +828,7 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
715
828
|
}
|
|
716
829
|
});
|
|
717
830
|
|
|
718
|
-
test(
|
|
831
|
+
test("migrateMemoryEntityRelationDedup: idempotent on already-deduplicated table", () => {
|
|
719
832
|
// If no duplicates exist, the migration should run without errors, write
|
|
720
833
|
// the checkpoint, and leave the data unchanged.
|
|
721
834
|
const db = createTestDb();
|
|
@@ -727,24 +840,40 @@ describe('schema-drift recovery: migration handles unexpected schema state', ()
|
|
|
727
840
|
const now = Date.now();
|
|
728
841
|
|
|
729
842
|
// Insert distinct relations (no duplicates).
|
|
730
|
-
raw.exec(
|
|
731
|
-
|
|
732
|
-
|
|
843
|
+
raw.exec(
|
|
844
|
+
`INSERT INTO memory_entity_relations VALUES ('r1', 'e1', 'e2', 'knows', NULL, ${now}, ${now})`,
|
|
845
|
+
);
|
|
846
|
+
raw.exec(
|
|
847
|
+
`INSERT INTO memory_entity_relations VALUES ('r2', 'e1', 'e3', 'knows', NULL, ${now}, ${now})`,
|
|
848
|
+
);
|
|
849
|
+
raw.exec(
|
|
850
|
+
`INSERT INTO memory_entity_relations VALUES ('r3', 'e2', 'e3', 'friends', 'evidence', ${now}, ${now})`,
|
|
851
|
+
);
|
|
733
852
|
|
|
734
853
|
migrateMemoryEntityRelationDedup(db);
|
|
735
854
|
|
|
736
|
-
const count = (
|
|
855
|
+
const count = (
|
|
856
|
+
raw.query(`SELECT COUNT(*) AS c FROM memory_entity_relations`).get() as {
|
|
857
|
+
c: number;
|
|
858
|
+
}
|
|
859
|
+
).c;
|
|
737
860
|
// All 3 rows are distinct and should survive the dedup.
|
|
738
861
|
expect(count).toBe(3);
|
|
739
862
|
|
|
740
|
-
const cp = raw
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
863
|
+
const cp = raw
|
|
864
|
+
.query(
|
|
865
|
+
`SELECT value FROM memory_checkpoints WHERE key = 'migration_memory_entity_relations_dedup_v1'`,
|
|
866
|
+
)
|
|
867
|
+
.get() as { value: string } | null;
|
|
868
|
+
expect(cp!.value).toBe("1");
|
|
744
869
|
|
|
745
870
|
// Second run — must be a no-op (checkpoint exists).
|
|
746
871
|
migrateMemoryEntityRelationDedup(db);
|
|
747
|
-
const countAfter2 = (
|
|
872
|
+
const countAfter2 = (
|
|
873
|
+
raw.query(`SELECT COUNT(*) AS c FROM memory_entity_relations`).get() as {
|
|
874
|
+
c: number;
|
|
875
|
+
}
|
|
876
|
+
).c;
|
|
748
877
|
expect(countAfter2).toBe(3);
|
|
749
878
|
});
|
|
750
879
|
});
|