diff --git a/packages/core/src/discovery/cache/__tests__/sessions.test.ts b/packages/core/src/discovery/cache/__tests__/sessions.test.ts index b6f046e..0200aa2 100644 --- a/packages/core/src/discovery/cache/__tests__/sessions.test.ts +++ b/packages/core/src/discovery/cache/__tests__/sessions.test.ts @@ -2,6 +2,7 @@ import { mkdtempSync, rmSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { setCoreDiagnostics } from "../../../utils/diagnostics.js"; import { clearCache, getCacheInfo, @@ -22,6 +23,7 @@ vi.mock("node:os", async (importOriginal) => { }); afterEach(() => { + setCoreDiagnostics(null); rmSync(join(testHomeDir, ".cache"), { recursive: true, force: true }); setSchemaEnsuredPath(null); }); @@ -50,6 +52,43 @@ describe("cached sessions", () => { ]); }); + it("preserves and reports metadata omitted from an incremental change", () => { + const events: Array<{ event: string; detail?: Record }> = []; + setCoreDiagnostics({ warn: (event, detail) => events.push({ event, detail }) }); + saveCachedSessions("codex", [makeSessionHead("one")], { + one: { id: "one", sourcePath: "/transcripts/one.jsonl" }, + }); + + saveCachedSessionChanges( + "codex", + [{ session: makeSessionHead("one", { title: "Updated" }), sortIndex: 0 }], + [], + ); + + const cached = loadCachedSessions("codex"); + expect(cached?.sessions[0]?.title).toBe("Updated"); + expect(cached?.meta.one).toEqual({ + id: "one", + sourcePath: "/transcripts/one.jsonl", + }); + expect( + withCacheDb( + (db) => + ( + db + .prepare("SELECT source_path FROM sessions WHERE agent_name = ? AND session_id = ?") + .get("codex", "one") as { source_path?: string } + ).source_path, + ), + ).toBe("/transcripts/one.jsonl"); + expect(events).toEqual([ + { + event: "cache.session_meta_missing", + detail: { agent: "codex", session_id: "one" }, + }, + ]); + }); + it("round-trips parent references through the structured cache", () => { const child = makeSessionHead("child", { parent_reference: { agentName: "codex", sessionId: "parent" }, diff --git a/packages/core/src/discovery/cache/messages.ts b/packages/core/src/discovery/cache/messages.ts index 7294d01..6a5bc60 100644 --- a/packages/core/src/discovery/cache/messages.ts +++ b/packages/core/src/discovery/cache/messages.ts @@ -151,7 +151,10 @@ export function prepareUpsertSession(db: SQLiteDatabase): SQLiteStatement { sort_index = excluded.sort_index, slug = excluded.slug, title = excluded.title, - source_path = excluded.source_path, + source_path = CASE + WHEN excluded.meta_json IS NULL THEN sessions.source_path + ELSE excluded.source_path + END, directory = excluded.directory, parent_agent_name = excluded.parent_agent_name, parent_session_id = excluded.parent_session_id, @@ -175,7 +178,7 @@ export function prepareUpsertSession(db: SQLiteDatabase): SQLiteStatement { smart_tags_json = excluded.smart_tags_json, smart_tags_source_updated_at = excluded.smart_tags_source_updated_at, smart_tags_classifier_revision = excluded.smart_tags_classifier_revision, - meta_json = excluded.meta_json, + meta_json = COALESCE(excluded.meta_json, sessions.meta_json), publication_id = excluded.publication_id `); } diff --git a/packages/core/src/discovery/cache/sessions.ts b/packages/core/src/discovery/cache/sessions.ts index 5ebf930..2c065ba 100644 --- a/packages/core/src/discovery/cache/sessions.ts +++ b/packages/core/src/discovery/cache/sessions.ts @@ -6,6 +6,7 @@ import type { SessionCacheMeta } from "../../agents/base.js"; import type { ReferencedSessionHead, SessionReference } from "../../contract/index.js"; import { formatSessionReference, normalizeSessionReference } from "../../contract/index.js"; import type { SessionDetail, SessionHead } from "../../types/index.js"; +import { getCoreDiagnostics } from "../../utils/diagnostics.js"; import { tableExists, type SQLiteDatabase } from "../../utils/sqlite.js"; import { getCachePath, @@ -604,6 +605,12 @@ export function writeCachedSessionChanges( for (const { session, sortIndex } of changes) { const sessionMeta = meta[session.id]; + if (!sessionMeta) { + getCoreDiagnostics()?.warn("cache.session_meta_missing", { + agent: agentName, + session_id: session.id, + }); + } const metaJson = sessionMeta ? JSON.stringify(sessionMeta) : null; upsertSessionRow( upsertSession,