1+ import crypto from "node:crypto" ;
12import fs from "node:fs/promises" ;
23import os from "node:os" ;
34import path from "node:path" ;
45import { DatabaseSync } from "node:sqlite" ;
5- import crypto from "node:crypto" ;
66import type { OpenClawConfig } from "openclaw/plugin-sdk/memory-core-host-engine-foundation" ;
77import { afterAll , afterEach , beforeAll , beforeEach , describe , expect , it , vi } from "vitest" ;
88import { closeAllMemorySearchManagers , getMemorySearchManager } from "./index.js" ;
@@ -16,11 +16,35 @@ const MOCK_CACHE_KEY_DATA = {
1616 dimensions : 1024 ,
1717} ;
1818
19+ const MOCK_CACHE_KEY_DATA_DIFFERENT_DIMS = {
20+ provider : "openai-compatible" ,
21+ baseUrl : "https://dashscope.example.com/v1" ,
22+ model : "text-embedding-v4" ,
23+ dimensions : 3072 ,
24+ } ;
25+
26+ const MOCK_CACHE_KEY_DATA_DIFFERENT_BASE_URL = {
27+ provider : "openai-compatible" ,
28+ baseUrl : "https://different-endpoint.example.com/v1" ,
29+ model : "text-embedding-v4" ,
30+ dimensions : 1024 ,
31+ } ;
32+
1933const STALE_PROVIDER_KEY = crypto
2034 . createHash ( "sha256" )
2135 . update ( JSON . stringify ( { provider : "none" , model : "fts-only" } ) )
2236 . digest ( "hex" ) ;
2337
38+ const DIFFERENT_DIMS_PROVIDER_KEY = crypto
39+ . createHash ( "sha256" )
40+ . update ( JSON . stringify ( MOCK_CACHE_KEY_DATA_DIFFERENT_DIMS ) )
41+ . digest ( "hex" ) ;
42+
43+ const DIFFERENT_BASE_URL_PROVIDER_KEY = crypto
44+ . createHash ( "sha256" )
45+ . update ( JSON . stringify ( MOCK_CACHE_KEY_DATA_DIFFERENT_BASE_URL ) )
46+ . digest ( "hex" ) ;
47+
2448const createEmbeddingProviderMock = vi . hoisted ( ( ) =>
2549 vi . fn ( async ( ) => ( {
2650 requestedProvider : "openai-compatible" ,
@@ -43,9 +67,10 @@ vi.mock("./embeddings.js", () => ({
4367 resolveEmbeddingProviderAdapterTransport : ( providerId : string ) =>
4468 providerId === "local" ? "local" : "remote" ,
4569 resolveEmbeddingProviderFallbackModel : ( ) => "fts-only" ,
70+ resolveEmbeddingProviderIndexIdentity : ( ) => undefined ,
4671} ) ) ;
4772
48- describe ( "memory manager auto-fixes providerKey mismatch from CLI index --force" , ( ) => {
73+ describe ( "memory manager auto-fixes stale FTS-only providerKey from CLI index --force" , ( ) => {
4974 let fixtureRoot = "" ;
5075 let caseId = 0 ;
5176 let workspaceDir = "" ;
@@ -113,9 +138,9 @@ describe("memory manager auto-fixes providerKey mismatch from CLI index --force"
113138
114139 function overwriteProviderKeyInMeta ( providerKey : string ) : void {
115140 const db = new DatabaseSync ( indexPath ) ;
116- const row = db
117- . prepare ( `SELECT value FROM meta WHERE key = ?` )
118- . get ( "memory_index_meta_v1" ) as { value : string } | undefined ;
141+ const row = db . prepare ( `SELECT value FROM meta WHERE key = ?` ) . get ( "memory_index_meta_v1" ) as
142+ | { value : string }
143+ | undefined ;
119144 if ( ! row ?. value ) {
120145 db . close ( ) ;
121146 throw new Error ( "no meta row found" ) ;
@@ -129,7 +154,7 @@ describe("memory manager auto-fixes providerKey mismatch from CLI index --force"
129154 db . close ( ) ;
130155 }
131156
132- it ( "auto-fixes stale providerKey and allows search after provider init" , async ( ) => {
157+ it ( "auto-fixes stale FTS-only providerKey written by CLI before provider init" , async ( ) => {
133158 const firstManager = await createManager ( ) ;
134159 await firstManager . sync ( { reason : "cli" , force : true } ) ;
135160 expect ( indexIdentityStatus ( firstManager ) ) . toBe ( "valid" ) ;
@@ -142,15 +167,13 @@ describe("memory manager auto-fixes providerKey mismatch from CLI index --force"
142167
143168 const reopenedManager = await createManager ( ) ;
144169
145- expect ( indexIdentityStatus ( reopenedManager ) ) . toBe ( "valid" ) ;
146-
147170 const results = await reopenedManager . search ( "Test topic" ) ;
148171
149172 expect ( indexIdentityStatus ( reopenedManager ) ) . toBe ( "valid" ) ;
150173 expect ( results . length ) . toBeGreaterThan ( 0 ) ;
151174 } ) ;
152175
153- it ( "does not auto-fix providerKey mismatch when provider id also differs" , async ( ) => {
176+ it ( "does not auto-fix when provider id also differs" , async ( ) => {
154177 const firstManager = await createManager ( ) ;
155178 await firstManager . sync ( { reason : "cli" , force : true } ) ;
156179
@@ -159,9 +182,9 @@ describe("memory manager auto-fixes providerKey mismatch from CLI index --force"
159182 await closeAllMemorySearchManagers ( ) ;
160183
161184 const db = new DatabaseSync ( indexPath ) ;
162- const row = db
163- . prepare ( `SELECT value FROM meta WHERE key = ?` )
164- . get ( "memory_index_meta_v1" ) as { value : string } | undefined ;
185+ const row = db . prepare ( `SELECT value FROM meta WHERE key = ?` ) . get ( "memory_index_meta_v1" ) as
186+ | { value : string }
187+ | undefined ;
165188 if ( ! row ?. value ) {
166189 db . close ( ) ;
167190 throw new Error ( "no meta row found" ) ;
@@ -182,4 +205,40 @@ describe("memory manager auto-fixes providerKey mismatch from CLI index --force"
182205 expect ( indexIdentityStatus ( reopenedManager ) ) . toBe ( "mismatched" ) ;
183206 expect ( results . length ) . toBe ( 0 ) ;
184207 } ) ;
208+
209+ it ( "does not auto-fix when runtime dimensions differ from index" , async ( ) => {
210+ const firstManager = await createManager ( ) ;
211+ await firstManager . sync ( { reason : "cli" , force : true } ) ;
212+
213+ await manager ! . close ( ) ;
214+ manager = null ;
215+ await closeAllMemorySearchManagers ( ) ;
216+
217+ overwriteProviderKeyInMeta ( DIFFERENT_DIMS_PROVIDER_KEY ) ;
218+
219+ const reopenedManager = await createManager ( ) ;
220+
221+ const results = await reopenedManager . search ( "Test topic" ) ;
222+
223+ expect ( indexIdentityStatus ( reopenedManager ) ) . toBe ( "mismatched" ) ;
224+ expect ( results . length ) . toBe ( 0 ) ;
225+ } ) ;
226+
227+ it ( "does not auto-fix when runtime baseUrl differs from index" , async ( ) => {
228+ const firstManager = await createManager ( ) ;
229+ await firstManager . sync ( { reason : "cli" , force : true } ) ;
230+
231+ await manager ! . close ( ) ;
232+ manager = null ;
233+ await closeAllMemorySearchManagers ( ) ;
234+
235+ overwriteProviderKeyInMeta ( DIFFERENT_BASE_URL_PROVIDER_KEY ) ;
236+
237+ const reopenedManager = await createManager ( ) ;
238+
239+ const results = await reopenedManager . search ( "Test topic" ) ;
240+
241+ expect ( indexIdentityStatus ( reopenedManager ) ) . toBe ( "mismatched" ) ;
242+ expect ( results . length ) . toBe ( 0 ) ;
243+ } ) ;
185244} ) ;
0 commit comments