From f69820e76e79e55e88a678818d35d57c2ea537bc Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 15 Feb 2026 14:54:58 +0900 Subject: [PATCH] feat: implement SQLite backend for prependThinkingPart via HTTP PATCH --- .../storage/thinking-prepend.ts | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/src/hooks/session-recovery/storage/thinking-prepend.ts b/src/hooks/session-recovery/storage/thinking-prepend.ts index 6ddffb06..c63a57fb 100644 --- a/src/hooks/session-recovery/storage/thinking-prepend.ts +++ b/src/hooks/session-recovery/storage/thinking-prepend.ts @@ -1,9 +1,13 @@ import { existsSync, mkdirSync, writeFileSync } from "node:fs" import { join } from "node:path" +import type { PluginInput } from "@opencode-ai/plugin" import { PART_STORAGE, THINKING_TYPES } from "../constants" +import type { MessageData } from "../types" import { readMessages } from "./messages-reader" import { readParts } from "./parts-reader" -import { log, isSqliteBackend } from "../../../shared" +import { log, isSqliteBackend, patchPart } from "../../../shared" + +type OpencodeClient = PluginInput["client"] function findLastThinkingContent(sessionID: string, beforeMessageID: string): string { const messages = readMessages(sessionID) @@ -33,7 +37,7 @@ function findLastThinkingContent(sessionID: string, beforeMessageID: string): st export function prependThinkingPart(sessionID: string, messageID: string): boolean { if (isSqliteBackend()) { - log("[session-recovery] Disabled on SQLite backend: prependThinkingPart") + log("[session-recovery] Disabled on SQLite backend: prependThinkingPart (use async variant)") return false } @@ -62,3 +66,58 @@ export function prependThinkingPart(sessionID: string, messageID: string): boole return false } } + +async function findLastThinkingContentFromSDK( + client: OpencodeClient, + sessionID: string, + beforeMessageID: string +): Promise { + try { + const response = await client.session.messages({ path: { id: sessionID } }) + const messages = (response.data ?? []) as MessageData[] + + const currentIndex = messages.findIndex((m) => m.info?.id === beforeMessageID) + if (currentIndex === -1) return "" + + for (let i = currentIndex - 1; i >= 0; i--) { + const msg = messages[i] + if (msg.info?.role !== "assistant") continue + if (!msg.parts) continue + + for (const part of msg.parts) { + if (part.type && THINKING_TYPES.has(part.type)) { + const content = part.thinking || part.text + if (content && content.trim().length > 0) return content + } + } + } + } catch { + return "" + } + return "" +} + +export async function prependThinkingPartAsync( + client: OpencodeClient, + sessionID: string, + messageID: string +): Promise { + const previousThinking = await findLastThinkingContentFromSDK(client, sessionID, messageID) + + const partId = "prt_0000000000_thinking" + const part: Record = { + id: partId, + sessionID, + messageID, + type: "thinking", + thinking: previousThinking || "[Continuing from previous reasoning]", + synthetic: true, + } + + try { + return await patchPart(client, sessionID, messageID, partId, part) + } catch (error) { + log("[session-recovery] prependThinkingPartAsync failed", { error: String(error) }) + return false + } +}