summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-11-15 22:50:13 -0800
committerGitHub <[email protected]>2025-11-16 00:50:13 -0600
commit9b8a7da1e6bdc972b912d67502ae93a191836712 (patch)
treea8e7c9ae32c10d25b3668ba5bd1e007fa7c48302
parent61fd21182c0bec717c66c28db56066c9e60c26c1 (diff)
downloadopencode-9b8a7da1e6bdc972b912d67502ae93a191836712.tar.gz
opencode-9b8a7da1e6bdc972b912d67502ae93a191836712.zip
fix: history jsonl file corruption cases (#4364)
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx41
1 files changed, 35 insertions, 6 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx
index 4b02d558a..4fd60dd36 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx
@@ -4,7 +4,7 @@ import { onMount } from "solid-js"
import { createStore, produce } from "solid-js/store"
import { clone } from "remeda"
import { createSimpleContext } from "../../context/helper"
-import { appendFile } from "fs/promises"
+import { appendFile, writeFile } from "fs/promises"
import type { AgentPart, FilePart, TextPart } from "@opencode-ai/sdk"
export type PromptInfo = {
@@ -24,6 +24,8 @@ export type PromptInfo = {
)[]
}
+const MAX_HISTORY_ENTRIES = 50
+
export const { use: usePromptHistory, provider: PromptHistoryProvider } = createSimpleContext({
name: "PromptHistory",
init: () => {
@@ -33,8 +35,23 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create
const lines = text
.split("\n")
.filter(Boolean)
- .map((line) => JSON.parse(line))
- setStore("history", lines as PromptInfo[])
+ .map((line) => {
+ try {
+ return JSON.parse(line)
+ } catch {
+ return null
+ }
+ })
+ .filter((line): line is PromptInfo => line !== null)
+ .slice(-MAX_HISTORY_ENTRIES)
+
+ setStore("history", lines)
+
+ // Rewrite file with only valid entries to self-heal corruption
+ if (lines.length > 0) {
+ const content = lines.map((line) => JSON.stringify(line)).join("\n") + "\n"
+ writeFile(historyFile.name!, content).catch(() => {})
+ }
})
const [store, setStore] = createStore({
@@ -64,14 +81,26 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create
return store.history.at(store.index)
},
append(item: PromptInfo) {
- item = clone(item)
- appendFile(historyFile.name!, JSON.stringify(item) + "\n")
+ const entry = clone(item)
+ let trimmed = false
setStore(
produce((draft) => {
- draft.history.push(item)
+ draft.history.push(entry)
+ if (draft.history.length > MAX_HISTORY_ENTRIES) {
+ draft.history = draft.history.slice(-MAX_HISTORY_ENTRIES)
+ trimmed = true
+ }
draft.index = 0
}),
)
+
+ if (trimmed) {
+ const content = store.history.map((line) => JSON.stringify(line)).join("\n") + "\n"
+ writeFile(historyFile.name!, content).catch(() => {})
+ return
+ }
+
+ appendFile(historyFile.name!, JSON.stringify(entry) + "\n").catch(() => {})
},
}
},