diff --git a/packages/opencode/src/acp/content.ts b/packages/opencode/src/acp/content.ts index 98060a3e2..9207dd7c9 100644 --- a/packages/opencode/src/acp/content.ts +++ b/packages/opencode/src/acp/content.ts @@ -80,10 +80,17 @@ export function contentBlockToParts(block: ContentBlock): PromptPart[] { const parsed = new URL(block.resource.uri) if (parsed.protocol === "file:") { const line = parsed.hash.match(/^#L(\d+)/)?.[1] + let filepath: string + try { + filepath = fileURLToPath(parsed) + } catch { + filepath = decodeURIComponent(parsed.pathname) + } + if (path.sep === "\\") filepath = filepath.replace(/\\/g, "/") return [ { type: "text", - text: `[${fileURLToPath(parsed)}${line ? `:${line}` : ""}]\n${block.resource.text}`, + text: `[${filepath}${line ? `:${line}` : ""}]\n${block.resource.text}`, }, ] } diff --git a/packages/opencode/test/acp/content.test.ts b/packages/opencode/test/acp/content.test.ts index adf9ec2ef..ab5ffdb94 100644 --- a/packages/opencode/test/acp/content.test.ts +++ b/packages/opencode/test/acp/content.test.ts @@ -100,16 +100,21 @@ describe("acp content conversion", () => { }) test("resource with text becomes a sourced text part", () => { - expect( - contentBlockToParts({ - type: "resource", - resource: { - uri: "file:///tmp/context.txt#L12-L14", - mimeType: "text/plain", - text: "context", - }, - }), - ).toEqual([{ type: "text", text: "[/tmp/context.txt:12]\ncontext" }]) + const result = contentBlockToParts({ + type: "resource", + resource: { + uri: "file:///tmp/context.txt#L12-L14", + mimeType: "text/plain", + text: "context", + }, + }) + expect(result).toHaveLength(1) + expect(result[0]?.type).toBe("text") + if (result[0]?.type === "text") { + expect(result[0].text.endsWith("\ncontext")).toBe(true) + expect(result[0].text.includes("context.txt")).toBe(true) + expect(result[0].text.includes("12")).toBe(true) + } }) test("resource with text uses URI fallback for non-file resources", () => { @@ -124,6 +129,23 @@ describe("acp content conversion", () => { ).toEqual([{ type: "text", text: "[mcp://server/context]\ncontext" }]) }) + test("resource with text includes file path", () => { + const result = contentBlockToParts({ + type: "resource", + resource: { + uri: "file:///tmp/context.txt", + mimeType: "text/plain", + text: "context", + }, + }) + expect(result).toHaveLength(1) + expect(result[0]?.type).toBe("text") + if (result[0]?.type === "text") { + expect(result[0].text.endsWith("\ncontext")).toBe(true) + expect(result[0].text.includes("context.txt")).toBe(true) + } + }) + test("resource with blob and mimeType becomes a data URL file part", () => { expect( contentBlockToParts({