fix(memory): detect shipped status indexes

This commit is contained in:
Vincent Koc
2026-06-18 11:55:46 +02:00
parent 39c9fd8592
commit d074c0d970
2 changed files with 92 additions and 16 deletions
+60
View File
@@ -573,6 +573,66 @@ describe("resolveSharedMemoryStatusSnapshot", () => {
expect(getMemorySearchManager).not.toHaveBeenCalled();
});
it("recognizes shipped memory tables before the manager migrates them", async () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-status-memory-"));
const databasePath = path.join(tempDir, "openclaw-agent.sqlite");
const db = new DatabaseSync(databasePath);
db.exec(`
CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
CREATE TABLE files (
path TEXT PRIMARY KEY,
source TEXT NOT NULL DEFAULT 'memory',
hash TEXT NOT NULL,
mtime INTEGER NOT NULL,
size INTEGER NOT NULL
);
CREATE TABLE chunks (
id TEXT PRIMARY KEY,
path TEXT NOT NULL,
source TEXT NOT NULL DEFAULT 'memory',
start_line INTEGER NOT NULL,
end_line INTEGER NOT NULL,
hash TEXT NOT NULL,
model TEXT NOT NULL,
text TEXT NOT NULL,
embedding TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
INSERT INTO files VALUES ('MEMORY.md', 'memory', 'file-hash', 10, 20);
`);
db.close();
const manager = {
probeVectorStoreAvailability: vi.fn(async () => true),
probeVectorAvailability: vi.fn(async () => true),
status: vi.fn(() => ({
backend: "builtin" as const,
provider: "openai",
files: 1,
chunks: 0,
vector: { enabled: true, available: true },
fts: { enabled: true, available: true },
})),
close: vi.fn(async () => {}),
};
const getMemorySearchManager = vi.fn(async () => ({ manager }));
try {
const result = await resolveSharedMemoryStatusSnapshot({
cfg: {},
agentStatus: { defaultId: "main" },
memoryPlugin: { enabled: true, slot: "memory-core" },
resolveMemoryConfig: vi.fn(() => ({ store: { databasePath } })),
getMemorySearchManager,
requireDefaultDatabasePath: () => databasePath,
});
expect(getMemorySearchManager).toHaveBeenCalledOnce();
expect(result?.files).toBe(1);
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
it("does not initialize memory status for an agent database owned by another feature", async () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-status-memory-"));
const databasePath = path.join(tempDir, "openclaw-agent.sqlite");
+32 -16
View File
@@ -56,35 +56,51 @@ function hasBuiltInMemoryState(databasePath: string): boolean {
let db: DatabaseSync | undefined;
try {
db = new DatabaseSync(databasePath, { readOnly: true });
const builtInMemoryTables = [
MEMORY_INDEX_META_TABLE,
MEMORY_INDEX_SOURCES_TABLE,
MEMORY_INDEX_CHUNKS_TABLE,
const builtInMemoryTableSets = [
{
meta: MEMORY_INDEX_META_TABLE,
sources: MEMORY_INDEX_SOURCES_TABLE,
chunks: MEMORY_INDEX_CHUNKS_TABLE,
},
{
meta: "meta",
sources: "files",
chunks: "chunks",
},
] as const;
const builtInMemoryTables = builtInMemoryTableSets.flatMap(({ meta, sources, chunks }) => [
meta,
sources,
chunks,
]);
const tableNames = new Set(
(
db
.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (?, ?, ?)`)
.prepare(
`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${builtInMemoryTables.map(() => "?").join(", ")})`,
)
.all(...builtInMemoryTables) as Array<{ name?: unknown }>
)
.map((row) => row.name)
.filter((name): name is string => typeof name === "string"),
);
if (
tableNames.has(MEMORY_INDEX_META_TABLE) &&
db
.prepare(`SELECT 1 AS ok FROM ${MEMORY_INDEX_META_TABLE} WHERE key = ? LIMIT 1`)
.get(MEMORY_INDEX_META_KEY)
) {
return true;
}
for (const tableName of [MEMORY_INDEX_SOURCES_TABLE, MEMORY_INDEX_CHUNKS_TABLE] as const) {
for (const tables of builtInMemoryTableSets) {
if (
tableNames.has(tableName) &&
db.prepare(`SELECT 1 AS ok FROM ${tableName} LIMIT 1`).get()
tableNames.has(tables.meta) &&
db
.prepare(`SELECT 1 AS ok FROM ${tables.meta} WHERE key = ? LIMIT 1`)
.get(MEMORY_INDEX_META_KEY)
) {
return true;
}
for (const tableName of [tables.sources, tables.chunks]) {
if (
tableNames.has(tableName) &&
db.prepare(`SELECT 1 AS ok FROM ${tableName} LIMIT 1`).get()
) {
return true;
}
}
}
return false;
} catch {