Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "opencode-mem",
"version": "2.34.0",
"version": "2.35.0",
"description": "OpenCode plugin that gives coding agents persistent memory using local Turso/libSQL vector search",
"type": "module",
"main": "dist/plugin.js",
Expand Down
1 change: 1 addition & 0 deletions tests/add-memory-phase-logging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mock.module(${JSON.stringify(configUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: (message, data) => logs.push({ message, data }),
safeLog: (message, data) => logs.push({ message, data }),
serializeError: (error) => ({
error: error instanceof Error ? error.message : String(error),
name: error instanceof Error ? error.name : undefined,
Expand Down
2 changes: 1 addition & 1 deletion tests/ai-cleanup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ mock.module(${JSON.stringify(configUrl)}, () => ({
},
}));

mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));

mock.module(${JSON.stringify(opencodeProviderLoaderUrl)}, () => ({
loadOpencodeProvider: async () => ({
Expand Down
1 change: 1 addition & 0 deletions tests/api-handlers-container-tag-traversal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ mock.module(${JSON.stringify(userPromptManagerUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
1 change: 1 addition & 0 deletions tests/api-handlers-profile-strip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const profileWithEmbeddings = ${JSON.stringify(profileWithEmbeddings)};

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
2 changes: 1 addition & 1 deletion tests/auto-capture-concurrency.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ mock.module(${JSON.stringify(u("src/services/context.js"))}, () => ({ formatCont
mock.module(${JSON.stringify(u("src/services/privacy.js"))}, () => ({
stripPrivateContent: (v) => v, isFullyPrivate: () => false,
}));
mock.module(${JSON.stringify(u("src/services/logger.js"))}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(u("src/services/logger.js"))}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(u("src/services/language-detector.js"))}, () => ({
detectLanguage: () => "en", getLanguageName: () => "English",
}));
Expand Down
1 change: 1 addition & 0 deletions tests/auto-capture-config-toast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ mock.module(${JSON.stringify(configUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: (message) => logs.push(message),
safeLog: () => {},
}));

const { notifyAutoCaptureProviderStatus } = await import(${JSON.stringify(statusUrl)});
Expand Down
1 change: 1 addition & 0 deletions tests/auto-capture-provider-order.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ mock.module(${JSON.stringify(promptManagerUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: (message, data) => logs.push({ message, data }),
safeLog: (message, data) => logs.push({ message, data }),
serializeError: (error) => ({
error: error instanceof Error ? error.message : String(error),
name: error instanceof Error ? error.name : undefined,
Expand Down
6 changes: 3 additions & 3 deletions tests/auto-capture.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ mock.module(${JSON.stringify(promptManagerUrl)}, () => ({
},
}));

mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(languageUrl)}, () => ({
detectLanguage: () => "en",
getLanguageName: () => "English",
Expand Down Expand Up @@ -279,7 +279,7 @@ mock.module(${JSON.stringify(promptManagerUrl)}, () => ({
},
}));

mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(languageUrl)}, () => ({
detectLanguage: () => "en",
getLanguageName: () => "English",
Expand Down Expand Up @@ -430,7 +430,7 @@ mock.module(${JSON.stringify(promptManagerUrl)}, () => ({
},
}));

mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(languageUrl)}, () => ({
detectLanguage: () => "en",
getLanguageName: () => "English",
Expand Down
1 change: 1 addition & 0 deletions tests/cleanup-ai-sessions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mock.module(${JSON.stringify(configUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: String(error) }),
}));

Expand Down
1 change: 1 addition & 0 deletions tests/client-scope-traversal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ mock.module(${JSON.stringify(configUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
2 changes: 1 addition & 1 deletion tests/compaction-agent-preservation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ mock.module(${JSON.stringify(webServerUrl)}, () => ({
startWebServer: async () => null,
WebServer: class {},
}));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(languageUrl)}, () => ({ getLanguageName: () => "English" }));

const mockClient = {
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/profile-learning-lock-worker.mts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ mock.module(configUrl, () => ({
initConfig: () => {},
isConfigured: () => true,
}));
mock.module(loggerUrl, () => ({ log: () => {} }));
mock.module(loggerUrl, () => ({ log: () => {}, safeLog: () => {} }));

// current-boot-invalid: the LOCAL machine's boot identity reads as garbage.
// The implementation must treat the local boot id as "unknown" and never use
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/profile-learning-memory-writer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ mock.module(configUrl, () => ({
const loggerUrl = new URL("../../src/services/logger.js", import.meta.url).href;
mock.module(loggerUrl, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/profile-learning-service-worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ mock.module(tagsUrl, () => ({
// off the lock rather than silently doing nothing.
mock.module(loggerUrl, () => ({
log: (label, data) => event("log", { label, data }),
safeLog: (label, data) => event("log", { label, data }),
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/profile-learning-storage-error-worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ mock.module(tagsUrl, () => ({

mock.module(loggerUrl, () => ({
log: (label, data) => event("log", { label, data }),
safeLog: (label, data) => event("log", { label, data }),
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/v2-provider-refresh.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ mock.module(moduleUrl("services/user-prompt/user-prompt-manager"), () => ({
mock.module(moduleUrl("services/logger"), () => ({
log: (...args) =>
logs.push(args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" ")),
safeLog: (...args) =>
logs.push(args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" ")),
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
2 changes: 1 addition & 1 deletion tests/memory-portability-tool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ mock.module(${JSON.stringify(webServerUrl)}, () => ({
startWebServer: async () => null,
WebServer: class {},
}));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(languageUrl)}, () => ({ getLanguageName: () => "English" }));

const { OpenCodeMemPlugin } = await import(${JSON.stringify(indexUrl)});
Expand Down
2 changes: 1 addition & 1 deletion tests/profile-learning-idle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ mock.module(${JSON.stringify(privacyUrl)}, () => ({
isFullyPrivate: () => false,
}));
mock.module(${JSON.stringify(promptManagerUrl)}, () => ({ userPromptManager: { savePrompt() {} } }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(languageUrl)}, () => ({ getLanguageName: () => "English" }));
// The web server only starts once the Turso readiness gate passes, and that
// gate is what decides whether an owner exists at all.
Expand Down
1 change: 1 addition & 0 deletions tests/tag-migration-opencode-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ mock.module(${JSON.stringify(userPromptManagerUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
1 change: 1 addition & 0 deletions tests/tag-migration-skip-tagged.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ mock.module(${JSON.stringify(userPromptManagerUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
1 change: 1 addition & 0 deletions tests/tag-migration-soft-failure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ mock.module(${JSON.stringify(userPromptManagerUrl)}, () => ({

mock.module(${JSON.stringify(loggerUrl)}, () => ({
log: () => {},
safeLog: () => {},
serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }),
}));

Expand Down
2 changes: 1 addition & 1 deletion tests/tool-scope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ mock.module(${JSON.stringify(webServerUrl)}, () => ({
startWebServer: async () => null,
WebServer: class {},
}));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(languageUrl)}, () => ({ getLanguageName: () => "English" }));

const { OpenCodeMemPlugin } = await import(${JSON.stringify(indexUrl)});
Expand Down
39 changes: 21 additions & 18 deletions tests/turso-wal-index-recovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,27 +162,30 @@ describe("turso WAL index recovery", () => {
try {
const db = await manager.getConnection(dbPath);
values = (await db.all<{ v: string }>("SELECT v FROM t ORDER BY id")).map((row) => row.v);

// Assert while the connection is still open: closeAll runs
// PRAGMA wal_checkpoint(TRUNCATE) and empties the -wal afterward.
expect(values).toEqual(["checkpointed", "wal-only"]);
expect(existsSync(walPath)).toBe(true);
expect(statSync(walPath).size).toBeGreaterThan(0);
// Same inode: the file was kept, not moved and recreated.
expect(statSync(walPath).ino).toBe(walInode);

const [quarantined] = quarantineDirs(baseDir);
const quarantinedFiles = readdirSync(
join(baseDir, "recovery", quarantined as string)
).sort();
expect(quarantinedFiles).toContain("memories.db-tshm");
expect(quarantinedFiles.every((name) => /-(shm|tshm)$/.test(name))).toBe(true);
// Engine evidence, not an assumption: the old -tshm is in quarantine *and*
// a fresh, non-empty index exists beside the WAL again, rebuilt on reopen.
// The seeded WAL has no torn trailing frame; a torn WAL is the
// "retry fails, original error surfaces, WAL untouched" case.
expect(existsSync(`${dbPath}-tshm`)).toBe(true);
expect(statSync(`${dbPath}-tshm`).size).toBeGreaterThan(0);
} finally {
await manager.closeAll();
}

// The WAL was not discarded, and its committed row survived the recovery.
expect(values).toEqual(["checkpointed", "wal-only"]);
expect(existsSync(walPath)).toBe(true);
expect(statSync(walPath).size).toBeGreaterThan(0);
// Same inode: the file was kept, not moved and recreated.
expect(statSync(walPath).ino).toBe(walInode);

const [quarantined] = quarantineDirs(baseDir);
const quarantinedFiles = readdirSync(join(baseDir, "recovery", quarantined as string)).sort();
expect(quarantinedFiles).toContain("memories.db-tshm");
expect(quarantinedFiles.every((name) => /-(shm|tshm)$/.test(name))).toBe(true);
// Engine evidence, not an assumption: the old -tshm is in quarantine *and*
// a fresh, non-empty index exists beside the WAL again, rebuilt on reopen.
// The seeded WAL has no torn trailing frame; a torn WAL is the
// "retry fails, original error surfaces, WAL untouched" case.
expect(existsSync(`${dbPath}-tshm`)).toBe(true);
expect(statSync(`${dbPath}-tshm`).size).toBeGreaterThan(0);
}
);

Expand Down
2 changes: 1 addition & 1 deletion tests/user-profile-learning-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ mock.module(${JSON.stringify(profileManagerUrl)}, () => ({
},
}));

mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));

mock.module(${JSON.stringify(opencodeProviderLoaderUrl)}, () => ({
loadOpencodeProvider: async () => ({
Expand Down
2 changes: 1 addition & 1 deletion tests/user-profile-learning-language.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ mock.module(${JSON.stringify(profileManagerUrl)}, () => ({
},
}));

mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));

let capturedSystemPrompt = null;
let capturedUserPrompt = null;
Expand Down
2 changes: 1 addition & 1 deletion tests/user-profile-learning-prompt-mode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ mock.module(${JSON.stringify(profileManagerUrl)}, () => ({
},
}));

mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));
mock.module(${JSON.stringify(loggerUrl)}, () => ({ log: () => {}, safeLog: () => {}, serializeError: (error) => ({ error: error instanceof Error ? error.message : String(error) }) }));

let structuredSystemPrompt = null;
let externalSystemPrompt = null;
Expand Down
Loading