Skip to content

Commit 37a98b3

Browse files
fix: fall back to inline image data for internal URIs (#228)
* fix: fall back to inline image data for internal URIs * Harden image URI fallback handling --------- Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
1 parent b93225f commit 37a98b3

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

src/CodexAcpClient.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ function buildPromptItems(prompt: acp.ContentBlock[]): UserInput[] {
703703
case "text":
704704
return {type: "text", text: block.text, text_elements: []};
705705
case "image": {
706-
const url = block.uri ?? `data:${block.mimeType};base64,${block.data}`;
706+
const url = isSupportedImageUrl(block.uri) ? block.uri : imageDataUrl(block);
707707
return {type: "image", url};
708708
}
709709
case "resource_link":
@@ -729,10 +729,26 @@ function buildPromptItems(prompt: acp.ContentBlock[]): UserInput[] {
729729
}).filter((block): block is UserInput => block !== null);
730730
}
731731

732+
function imageDataUrl(block: acp.ContentBlock & { type: "image" }): string {
733+
return `data:${block.mimeType};base64,${block.data}`;
734+
}
735+
732736
function isImageMimeType(mimeType: string | null | undefined): mimeType is string {
733737
return mimeType?.startsWith("image/") ?? false;
734738
}
735739

740+
function isSupportedImageUrl(uri: string | null | undefined): uri is string {
741+
if (!uri) {
742+
return false;
743+
}
744+
try {
745+
const protocol = new URL(uri).protocol;
746+
return protocol === "http:" || protocol === "https:" || protocol === "data:";
747+
} catch {
748+
return false;
749+
}
750+
}
751+
736752
function formatUriAsLink(name: string | null | undefined, uri: string): string {
737753
if (name && name.length > 0) {
738754
return `[@${name}](${uri})`;

src/__tests__/CodexACPAgent/CodexAcpClient.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,62 @@ describe('ACP server test', { timeout: 40_000 }, () => {
14121412
}));
14131413
});
14141414

1415+
it ('should use inline image data for internal image URIs', async () => {
1416+
const { mockFixture, turnStartSpy } = setupPromptFixture({
1417+
supportedInputModalities: ["text", "image"],
1418+
});
1419+
1420+
const prompt: acp.ContentBlock[] = [
1421+
{ type: "text", text: "Hello" },
1422+
{ type: "image", mimeType: "image/png", data: "abc123", uri: "zed:///agent/pasted-image?name=Image" },
1423+
];
1424+
1425+
await mockFixture.getCodexAcpAgent().prompt({ sessionId: "id", prompt });
1426+
1427+
expect(turnStartSpy).toHaveBeenCalledWith(expect.objectContaining({
1428+
input: [
1429+
{ type: "text", text: "Hello", text_elements: [] },
1430+
{ type: "image", url: "data:image/png;base64,abc123" },
1431+
]
1432+
}));
1433+
});
1434+
1435+
it ('should use inline image data for local file image URIs', async () => {
1436+
const { mockFixture, turnStartSpy } = setupPromptFixture({
1437+
supportedInputModalities: ["text", "image"],
1438+
});
1439+
1440+
const prompt: acp.ContentBlock[] = [
1441+
{ type: "image", mimeType: "image/png", data: "abc123", uri: "file:///Users/test/Desktop/Screenshot%201.png" },
1442+
];
1443+
1444+
await mockFixture.getCodexAcpAgent().prompt({ sessionId: "id", prompt });
1445+
1446+
expect(turnStartSpy).toHaveBeenCalledWith(expect.objectContaining({
1447+
input: [
1448+
{ type: "image", url: "data:image/png;base64,abc123" },
1449+
]
1450+
}));
1451+
});
1452+
1453+
it ('should preserve data image URLs', async () => {
1454+
const { mockFixture, turnStartSpy } = setupPromptFixture({
1455+
supportedInputModalities: ["text", "image"],
1456+
});
1457+
1458+
const prompt: acp.ContentBlock[] = [
1459+
{ type: "image", mimeType: "image/png", data: "fallback", uri: "data:image/png;base64,abc123" },
1460+
];
1461+
1462+
await mockFixture.getCodexAcpAgent().prompt({ sessionId: "id", prompt });
1463+
1464+
expect(turnStartSpy).toHaveBeenCalledWith(expect.objectContaining({
1465+
input: [
1466+
{ type: "image", url: "data:image/png;base64,abc123" },
1467+
]
1468+
}));
1469+
});
1470+
14151471
it ('should show rate limits from multiple sources in status', async () => {
14161472
const rateLimits: RateLimitsMap = new Map();
14171473
rateLimits.set("limit-1", {

0 commit comments

Comments
 (0)