mirror of
https://github.com/Heretek-AI/opencode.git
synced 2026-11-10 10:21:31 -10:00
272 lines
8.4 KiB
TypeScript
272 lines
8.4 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { pathToFileURL } from "node:url"
|
|
import { contentBlockToParts, partsToContentChunks, promptContentToParts } from "../../src/acp/content"
|
|
|
|
describe("acp content conversion", () => {
|
|
test("plain text block becomes a text part", () => {
|
|
expect(contentBlockToParts({ type: "text", text: "hello" })).toEqual([{ type: "text", text: "hello" }])
|
|
})
|
|
|
|
test("assistant-only text audience becomes synthetic", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "text",
|
|
text: "internal",
|
|
annotations: { audience: ["assistant"] },
|
|
}),
|
|
).toEqual([{ type: "text", text: "internal", synthetic: true }])
|
|
})
|
|
|
|
test("user-only text audience becomes ignored", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "text",
|
|
text: "visible to user",
|
|
annotations: { audience: ["user"] },
|
|
}),
|
|
).toEqual([{ type: "text", text: "visible to user", ignored: true }])
|
|
})
|
|
|
|
test("image block with base64 data becomes a data URL file part", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "image",
|
|
data: "AAAA",
|
|
mimeType: "image/png",
|
|
uri: "file:///tmp/screenshot.png",
|
|
}),
|
|
).toEqual([
|
|
{
|
|
type: "file",
|
|
url: "data:image/png;base64,AAAA",
|
|
filename: "screenshot.png",
|
|
mime: "image/png",
|
|
},
|
|
])
|
|
})
|
|
|
|
test("image block with only a data URL URI becomes a file part", () => {
|
|
expect(
|
|
contentBlockToParts({ type: "image", data: "", mimeType: "image/png", uri: "data:image/png;base64,AAAA" }),
|
|
).toEqual([{ type: "file", url: "data:image/png;base64,AAAA", filename: "image", mime: "image/png" }])
|
|
})
|
|
|
|
test("image block with only an http URI becomes a markdown link", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "image",
|
|
data: "",
|
|
mimeType: "image/jpeg",
|
|
uri: "https://example.com/assets/photo.jpg",
|
|
}),
|
|
).toEqual([{ type: "text", text: "[photo.jpg](https://example.com/assets/photo.jpg)" }])
|
|
expect(
|
|
contentBlockToParts({ type: "image", data: "", mimeType: "image/png", uri: "https://example.com/" }),
|
|
).toEqual([{ type: "text", text: "[image](https://example.com/)" }])
|
|
expect(
|
|
contentBlockToParts({ type: "image", data: "", mimeType: "image/png", uri: "http://example.com/a.png" }),
|
|
).toEqual([{ type: "text", text: "[a.png](http://example.com/a.png)" }])
|
|
})
|
|
|
|
test("image block with only a URI of another scheme or a bare path becomes a markdown link", () => {
|
|
expect(contentBlockToParts({ type: "image", data: "", mimeType: "image/png", uri: "ftp://host/a.png" })).toEqual([
|
|
{ type: "text", text: "[a.png](ftp://host/a.png)" },
|
|
])
|
|
expect(contentBlockToParts({ type: "image", data: "", mimeType: "image/png", uri: "assets/b.png" })).toEqual([
|
|
{ type: "text", text: "[b.png](assets/b.png)" },
|
|
])
|
|
})
|
|
|
|
test("image block with only a file URI becomes a file part", () => {
|
|
expect(
|
|
contentBlockToParts({ type: "image", data: "", mimeType: "image/png", uri: "file:///tmp/screenshot.png" }),
|
|
).toEqual([{ type: "file", url: "file:///tmp/screenshot.png", filename: "screenshot.png", mime: "image/png" }])
|
|
})
|
|
|
|
test("image block with only a zed path becomes a file part named after the path", () => {
|
|
expect(
|
|
contentBlockToParts({ type: "image", data: "", mimeType: "image/png", uri: "zed://file?path=/tmp/shot.png" }),
|
|
).toEqual([{ type: "file", url: pathToFileURL("/tmp/shot.png").href, filename: "shot.png", mime: "image/png" }])
|
|
})
|
|
|
|
test("resource_link file URL becomes a file part with name and fallback mime", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "resource_link",
|
|
uri: "file:///tmp/notes.txt",
|
|
name: "client-notes.txt",
|
|
}),
|
|
).toEqual([
|
|
{
|
|
type: "file",
|
|
url: "file:///tmp/notes.txt",
|
|
filename: "client-notes.txt",
|
|
mime: "text/plain",
|
|
},
|
|
])
|
|
})
|
|
|
|
test("resource_link to another scheme becomes a markdown link", () => {
|
|
expect(contentBlockToParts({ type: "resource_link", uri: "https://example.com/spec", name: "spec" })).toEqual([
|
|
{ type: "text", text: "[spec](https://example.com/spec)" },
|
|
])
|
|
})
|
|
|
|
test("resource_link zed path becomes a file URL part", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "resource_link",
|
|
uri: "zed://workspace?path=/tmp/project/src/app.ts",
|
|
name: "app.ts",
|
|
mimeType: "text/typescript",
|
|
}),
|
|
).toEqual([
|
|
{
|
|
type: "file",
|
|
url: pathToFileURL("/tmp/project/src/app.ts").href,
|
|
filename: "app.ts",
|
|
mime: "text/typescript",
|
|
},
|
|
])
|
|
})
|
|
|
|
test("resource with text becomes a sourced text part", () => {
|
|
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", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "resource",
|
|
resource: {
|
|
uri: "mcp://server/context",
|
|
text: "context",
|
|
},
|
|
}),
|
|
).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({
|
|
type: "resource",
|
|
resource: {
|
|
uri: "file:///tmp/report.pdf",
|
|
mimeType: "application/pdf",
|
|
blob: "JVBERg==",
|
|
},
|
|
}),
|
|
).toEqual([
|
|
{
|
|
type: "file",
|
|
url: "data:application/pdf;base64,JVBERg==",
|
|
filename: "report.pdf",
|
|
mime: "application/pdf",
|
|
},
|
|
])
|
|
})
|
|
|
|
test("data URL resource is preserved as a file part", () => {
|
|
expect(
|
|
contentBlockToParts({
|
|
type: "resource",
|
|
resource: {
|
|
uri: "data:text/plain;base64,aGVsbG8=",
|
|
mimeType: "text/plain",
|
|
blob: "ignored",
|
|
},
|
|
}),
|
|
).toEqual([
|
|
{
|
|
type: "file",
|
|
url: "data:text/plain;base64,aGVsbG8=",
|
|
filename: "file",
|
|
mime: "text/plain",
|
|
},
|
|
])
|
|
})
|
|
|
|
test("unsupported blocks are ignored", () => {
|
|
expect(promptContentToParts([{ type: "audio", data: "AAAA", mimeType: "audio/wav" }])).toEqual([])
|
|
expect(
|
|
promptContentToParts([
|
|
// @ts-expect-error Exercise forward compatibility with an unknown ACP content block.
|
|
{ type: "unknown", text: "skip" },
|
|
]),
|
|
).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe("acp replay conversion", () => {
|
|
test("replays text audience annotations", () => {
|
|
expect(partsToContentChunks([{ type: "text", text: "cached", synthetic: true }])).toEqual([
|
|
{
|
|
content: {
|
|
type: "text",
|
|
text: "cached",
|
|
annotations: { audience: ["assistant"] },
|
|
},
|
|
},
|
|
])
|
|
})
|
|
|
|
test("replays file and data URL parts as ACP content", () => {
|
|
expect(
|
|
partsToContentChunks([
|
|
{ type: "file", url: "file:///tmp/readme.md", filename: "readme.md", mime: "text/markdown" },
|
|
{ type: "file", url: "data:text/plain;base64,aGVsbG8=", filename: "note.txt", mime: "text/plain" },
|
|
]),
|
|
).toEqual([
|
|
{
|
|
content: {
|
|
type: "resource_link",
|
|
uri: "file:///tmp/readme.md",
|
|
name: "readme.md",
|
|
mimeType: "text/markdown",
|
|
},
|
|
},
|
|
{
|
|
content: {
|
|
type: "resource",
|
|
resource: {
|
|
uri: pathToFileURL("note.txt").href,
|
|
mimeType: "text/plain",
|
|
text: "hello",
|
|
},
|
|
},
|
|
},
|
|
])
|
|
})
|
|
})
|