diff --git a/src/tools/session-manager/storage.test.ts b/src/tools/session-manager/storage.test.ts index df111dad6..1fbdb4e37 100644 --- a/src/tools/session-manager/storage.test.ts +++ b/src/tools/session-manager/storage.test.ts @@ -10,6 +10,7 @@ const TEST_PART_STORAGE = join(TEST_DIR, "part") const TEST_SESSION_STORAGE = join(TEST_DIR, "session") const TEST_TODO_DIR = join(TEST_DIR, "todos") const TEST_TRANSCRIPT_DIR = join(TEST_DIR, "transcripts") +let sqliteBackend = false mock.module("./constants", () => ({ OPENCODE_STORAGE: TEST_DIR, @@ -27,7 +28,7 @@ mock.module("./constants", () => ({ })) mock.module("../../shared/opencode-storage-detection", () => ({ - isSqliteBackend: () => false, + isSqliteBackend: () => sqliteBackend, resetSqliteBackendCache: () => {}, })) @@ -69,6 +70,7 @@ const storage = await import("./storage") describe("session-manager storage", () => { beforeEach(() => { + sqliteBackend = false if (existsSync(TEST_DIR)) { rmSync(TEST_DIR, { recursive: true, force: true }) } @@ -81,6 +83,8 @@ describe("session-manager storage", () => { }) afterEach(() => { + sqliteBackend = false + storage.resetStorageClient() if (existsSync(TEST_DIR)) { rmSync(TEST_DIR, { recursive: true, force: true }) } @@ -235,6 +239,47 @@ describe("session-manager storage", () => { expect(info?.agents_used).toContain("build") expect(info?.agents_used).toContain("oracle") }) + + test("getSessionInfo uses SDK session messages on sqlite backend", async () => { + sqliteBackend = true + const now = Date.now() + + storage.setStorageClient({ + session: { + messages: async () => ({ + data: [ + { + info: { + id: "msg_sqlite_1", + role: "user", + agent: "atlas", + time: { created: now - 5000, updated: now - 5000 }, + }, + parts: [], + }, + { + info: { + id: "msg_sqlite_2", + role: "assistant", + agent: "prometheus", + time: { created: now, updated: now }, + }, + parts: [], + }, + ], + }), + todo: async () => ({ data: [] }), + }, + } as never) + + const info = await getSessionInfo("ses_sqlite") + + expect(info).not.toBeNull() + expect(info?.id).toBe("ses_sqlite") + expect(info?.message_count).toBe(2) + expect(info?.agents_used).toContain("atlas") + expect(info?.agents_used).toContain("prometheus") + }) }) describe("session-manager storage - getMainSessions", () => { diff --git a/src/tools/session-manager/storage.ts b/src/tools/session-manager/storage.ts index 46a00daa8..87f52cf97 100644 --- a/src/tools/session-manager/storage.ts +++ b/src/tools/session-manager/storage.ts @@ -119,5 +119,43 @@ export async function readSessionTranscript(sessionID: string): Promise } export async function getSessionInfo(sessionID: string): Promise { + if (isSqliteBackend() && sdkClient) { + try { + const sdkMessages = await getSdkSessionMessages(sdkClient, sessionID) + if (sdkMessages.length > 0) { + const agentsUsed = new Set() + let firstMessage: Date | undefined + let lastMessage: Date | undefined + + for (const msg of sdkMessages) { + if (msg.agent) agentsUsed.add(msg.agent) + if (msg.time?.created) { + const date = new Date(msg.time.created) + if (!firstMessage || date < firstMessage) firstMessage = date + if (!lastMessage || date > lastMessage) lastMessage = date + } + } + + const todos = await readSessionTodos(sessionID) + const transcriptEntries = await readSessionTranscript(sessionID) + + return { + id: sessionID, + message_count: sdkMessages.length, + first_message: firstMessage, + last_message: lastMessage, + agents_used: Array.from(agentsUsed), + has_todos: todos.length > 0, + has_transcript: transcriptEntries > 0, + todos, + transcript_entries: transcriptEntries, + } + } + } catch (error) { + if (!shouldFallbackFromSdkError(error)) throw error + log("[session-manager] falling back to file session info after SDK unavailable error", { error: String(error), sessionID }) + } + } + return getFileSessionInfo(sessionID) }