summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-03-19 12:00:32 -0400
committerGitHub <[email protected]>2026-03-19 12:00:32 -0400
commit0d7e62a532ba9f2163f872414c52a213c517936a (patch)
tree2133df08b5b2cd2ffbd6d230348233553adf2cd8
parent41aa254db4b748b200861ee5e832b6fa3e47701e (diff)
downloadopencode-0d7e62a532ba9f2163f872414c52a213c517936a.tar.gz
opencode-0d7e62a532ba9f2163f872414c52a213c517936a.zip
fix forked prompt attachments losing file parts (#17815)
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx6
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/part.ts16
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx3
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx3
-rw-r--r--packages/opencode/test/cli/cmd/tui/prompt-part.test.ts47
5 files changed, 69 insertions, 6 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
index c85426cc2..249a48d35 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
@@ -13,6 +13,7 @@ import { MessageID, PartID } from "@/session/schema"
import { createStore, produce } from "solid-js/store"
import { useKeybind } from "@tui/context/keybind"
import { usePromptHistory, type PromptInfo } from "./history"
+import { assign } from "./part"
import { usePromptStash } from "./stash"
import { DialogStash } from "../dialog-stash"
import { type AutocompleteRef, Autocomplete } from "./autocomplete"
@@ -643,10 +644,7 @@ export function Prompt(props: PromptProps) {
type: "text",
text: inputText,
},
- ...nonTextParts.map((x) => ({
- id: PartID.ascending(),
- ...x,
- })),
+ ...nonTextParts.map(assign),
],
})
.catch(() => {})
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/part.ts b/packages/opencode/src/cli/cmd/tui/component/prompt/part.ts
new file mode 100644
index 000000000..8cdcef606
--- /dev/null
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/part.ts
@@ -0,0 +1,16 @@
+import { PartID } from "@/session/schema"
+import type { PromptInfo } from "./history"
+
+type Item = PromptInfo["parts"][number]
+
+export function strip(part: Item & { id: string; messageID: string; sessionID: string }): Item {
+ const { id: _id, messageID: _messageID, sessionID: _sessionID, ...rest } = part
+ return rest
+}
+
+export function assign(part: Item): Item & { id: PartID } {
+ return {
+ ...part,
+ id: PartID.ascending(),
+ }
+}
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx
index 62154cce5..742d51be2 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx
@@ -7,6 +7,7 @@ import { useSDK } from "@tui/context/sdk"
import { useRoute } from "@tui/context/route"
import { useDialog } from "../../ui/dialog"
import type { PromptInfo } from "@tui/component/prompt/history"
+import { strip } from "@tui/component/prompt/part"
export function DialogForkFromTimeline(props: { sessionID: string; onMove: (messageID: string) => void }) {
const sync = useSync()
@@ -42,7 +43,7 @@ export function DialogForkFromTimeline(props: { sessionID: string; onMove: (mess
if (part.type === "text") {
if (!part.synthetic) agg.input += part.text
}
- if (part.type === "file") agg.parts.push(part)
+ if (part.type === "file") agg.parts.push(strip(part))
return agg
},
{ input: "", parts: [] as PromptInfo["parts"] },
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx
index ff17b5567..a51a6cfe5 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx
@@ -5,6 +5,7 @@ import { useSDK } from "@tui/context/sdk"
import { useRoute } from "@tui/context/route"
import { Clipboard } from "@tui/util/clipboard"
import type { PromptInfo } from "@tui/component/prompt/history"
+import { strip } from "@tui/component/prompt/part"
export function DialogMessage(props: {
messageID: string
@@ -40,7 +41,7 @@ export function DialogMessage(props: {
if (part.type === "text") {
if (!part.synthetic) agg.input += part.text
}
- if (part.type === "file") agg.parts.push(part)
+ if (part.type === "file") agg.parts.push(strip(part))
return agg
},
{ input: "", parts: [] as PromptInfo["parts"] },
diff --git a/packages/opencode/test/cli/cmd/tui/prompt-part.test.ts b/packages/opencode/test/cli/cmd/tui/prompt-part.test.ts
new file mode 100644
index 000000000..326d3e624
--- /dev/null
+++ b/packages/opencode/test/cli/cmd/tui/prompt-part.test.ts
@@ -0,0 +1,47 @@
+import { describe, expect, test } from "bun:test"
+import type { PromptInfo } from "../../../../src/cli/cmd/tui/component/prompt/history"
+import { assign, strip } from "../../../../src/cli/cmd/tui/component/prompt/part"
+
+describe("prompt part", () => {
+ test("strip removes persisted ids from reused file parts", () => {
+ const part = {
+ id: "prt_old",
+ sessionID: "ses_old",
+ messageID: "msg_old",
+ type: "file" as const,
+ mime: "image/png",
+ filename: "tiny.png",
+ url: "data:image/png;base64,abc",
+ }
+
+ expect(strip(part)).toEqual({
+ type: "file",
+ mime: "image/png",
+ filename: "tiny.png",
+ url: "data:image/png;base64,abc",
+ })
+ })
+
+ test("assign overwrites stale runtime ids", () => {
+ const part = {
+ id: "prt_old",
+ sessionID: "ses_old",
+ messageID: "msg_old",
+ type: "file" as const,
+ mime: "image/png",
+ filename: "tiny.png",
+ url: "data:image/png;base64,abc",
+ } as PromptInfo["parts"][number]
+
+ const next = assign(part)
+
+ expect(next.id).not.toBe("prt_old")
+ expect(next.id.startsWith("prt_")).toBe(true)
+ expect(next).toMatchObject({
+ type: "file",
+ mime: "image/png",
+ filename: "tiny.png",
+ url: "data:image/png;base64,abc",
+ })
+ })
+})