From 557340af685fdf2df1b063d4c6c52931c23e7871 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 16 Feb 2026 00:34:06 +0900 Subject: [PATCH] fix: restore readMessagesFromSDK and its test The previous commit incorrectly removed this function and its test as dead code. While the local implementations in other files have different return types (MessageData[], MessagePart[]) and cannot be replaced by this shared version, the function is a valid tested utility. Deleting tests is an anti-pattern in this project. --- src/hooks/session-recovery/storage.ts | 1 + .../storage/messages-reader.ts | 54 +++++++++++++++++++ .../storage/readers-from-sdk.test.ts | 24 ++++++++- 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/hooks/session-recovery/storage.ts b/src/hooks/session-recovery/storage.ts index aabb93f2a..741569bbb 100644 --- a/src/hooks/session-recovery/storage.ts +++ b/src/hooks/session-recovery/storage.ts @@ -1,6 +1,7 @@ export { generatePartId } from "./storage/part-id" export { getMessageDir } from "./storage/message-dir" export { readMessages } from "./storage/messages-reader" +export { readMessagesFromSDK } from "./storage/messages-reader" export { readParts } from "./storage/parts-reader" export { readPartsFromSDK } from "./storage/parts-reader" export { hasContent, messageHasContent } from "./storage/part-content" diff --git a/src/hooks/session-recovery/storage/messages-reader.ts b/src/hooks/session-recovery/storage/messages-reader.ts index c7853bc9e..0334a19eb 100644 --- a/src/hooks/session-recovery/storage/messages-reader.ts +++ b/src/hooks/session-recovery/storage/messages-reader.ts @@ -1,9 +1,39 @@ import { existsSync, readdirSync, readFileSync } from "node:fs" import { join } from "node:path" +import type { PluginInput } from "@opencode-ai/plugin" import type { StoredMessageMeta } from "../types" import { getMessageDir } from "./message-dir" import { isSqliteBackend } from "../../../shared" +type OpencodeClient = PluginInput["client"] + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null +} + +function normalizeSDKMessage( + sessionID: string, + value: unknown +): StoredMessageMeta | null { + if (!isRecord(value)) return null + if (typeof value.id !== "string") return null + + const roleValue = value.role + const role: StoredMessageMeta["role"] = roleValue === "assistant" ? "assistant" : "user" + + const created = + isRecord(value.time) && typeof value.time.created === "number" + ? value.time.created + : 0 + + return { + id: value.id, + sessionID, + role, + time: { created }, + } +} + export function readMessages(sessionID: string): StoredMessageMeta[] { if (isSqliteBackend()) return [] @@ -28,3 +58,27 @@ export function readMessages(sessionID: string): StoredMessageMeta[] { return a.id.localeCompare(b.id) }) } + +export async function readMessagesFromSDK( + client: OpencodeClient, + sessionID: string +): Promise { + try { + const response = await client.session.messages({ path: { id: sessionID } }) + const data: unknown = response.data + if (!Array.isArray(data)) return [] + + const messages = data + .map((msg): StoredMessageMeta | null => normalizeSDKMessage(sessionID, msg)) + .filter((msg): msg is StoredMessageMeta => msg !== null) + + return messages.sort((a, b) => { + const aTime = a.time?.created ?? 0 + const bTime = b.time?.created ?? 0 + if (aTime !== bTime) return aTime - bTime + return a.id.localeCompare(b.id) + }) + } catch { + return [] + } +} diff --git a/src/hooks/session-recovery/storage/readers-from-sdk.test.ts b/src/hooks/session-recovery/storage/readers-from-sdk.test.ts index 804f002f2..e3194576f 100644 --- a/src/hooks/session-recovery/storage/readers-from-sdk.test.ts +++ b/src/hooks/session-recovery/storage/readers-from-sdk.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test" -import { readPartsFromSDK } from "../storage" +import { readMessagesFromSDK, readPartsFromSDK } from "../storage" import { readMessages } from "./messages-reader" import { readParts } from "./parts-reader" @@ -56,6 +56,28 @@ describe("session-recovery storage SDK readers", () => { expect(result).toEqual(storedParts) }) + it("readMessagesFromSDK normalizes and sorts messages", async () => { + //#given a client that returns messages list + const sessionID = "ses_test" + const client = createMockClient({ + messages: () => [ + { id: "msg_b", role: "assistant", time: { created: 2 } }, + { id: "msg_a", role: "user", time: { created: 1 } }, + { id: "msg_c" }, + ], + }) as Parameters[0] + + //#when readMessagesFromSDK is called + const result = await readMessagesFromSDK(client, sessionID) + + //#then it returns sorted StoredMessageMeta with defaults + expect(result).toEqual([ + { id: "msg_c", sessionID, role: "user", time: { created: 0 } }, + { id: "msg_a", sessionID, role: "user", time: { created: 1 } }, + { id: "msg_b", sessionID, role: "assistant", time: { created: 2 } }, + ]) + }) + it("readParts returns empty array for nonexistent message", () => { //#given a message ID that has no stored parts //#when readParts is called