Buckets:
| import { beforeEach, describe, expect } from "bun:test" | |
| import { Effect, Exit, Layer } from "effect" | |
| import { Config } from "@opencode-ai/core/config" | |
| import { ConfigAttachments } from "@opencode-ai/core/config/attachments" | |
| import { FileSystem } from "@opencode-ai/core/filesystem" | |
| import { FSUtil } from "@opencode-ai/core/fs-util" | |
| import { Location } from "@opencode-ai/core/location" | |
| import { Image } from "@opencode-ai/core/image" | |
| import { PermissionV2 } from "@opencode-ai/core/permission" | |
| import { SessionV2 } from "@opencode-ai/core/session" | |
| import { AbsolutePath } from "@opencode-ai/core/schema" | |
| import { Global } from "@opencode-ai/core/global" | |
| import { location } from "./fixture/location" | |
| import { ToolRegistry } from "@opencode-ai/core/tool/registry" | |
| import { ReadTool } from "@opencode-ai/core/tool/read" | |
| import { ReadToolFileSystem } from "@opencode-ai/core/tool/read-filesystem" | |
| import { testEffect } from "./lib/effect" | |
| import { toolIdentity, executeTool, settleTool, toolDefinitions } from "./lib/tool" | |
| const assertions: PermissionV2.AssertInput[] = [] | |
| const readCalls: { | |
| input: AbsolutePath | |
| page: ReadToolFileSystem.PageInput | |
| }[] = [] | |
| const listCalls: ReadToolFileSystem.PageInput[] = [] | |
| let resolvedType: "file" | "directory" = "file" | |
| let resolveFailure: unknown | |
| let readResult: FileSystem.Content | ReadToolFileSystem.TextPage = { | |
| uri: "file:///README.md", | |
| name: "README.md", | |
| content: "hello", | |
| encoding: "utf8", | |
| mime: "text/plain", | |
| } | |
| let readFailure: unknown | |
| let configEntries: Config.Entry[] = [] | |
| const reader = Layer.succeed( | |
| ReadToolFileSystem.Service, | |
| ReadToolFileSystem.Service.of({ | |
| inspect: () => (resolveFailure === undefined ? Effect.succeed(resolvedType) : Effect.die(resolveFailure)), | |
| read: (input, _resource, page = {}) => { | |
| readCalls.push({ input, page }) | |
| if (readFailure !== undefined) return Effect.die(readFailure) | |
| return Effect.succeed(readResult) | |
| }, | |
| list: (_path, input = {}) => | |
| Effect.sync(() => { | |
| listCalls.push(input) | |
| return new ReadToolFileSystem.ListPage({ entries: [], truncated: false }) | |
| }), | |
| }), | |
| ) | |
| let allow = true | |
| const permission = Layer.succeed( | |
| PermissionV2.Service, | |
| PermissionV2.Service.of({ | |
| assert: (input) => | |
| Effect.sync(() => { | |
| assertions.push(input) | |
| }).pipe(Effect.andThen(allow ? Effect.void : Effect.fail(new PermissionV2.DeniedError({ rules: [] })))), | |
| ask: () => Effect.die("unused"), | |
| reply: () => Effect.die("unused"), | |
| get: () => Effect.die("unused"), | |
| forSession: () => Effect.die("unused"), | |
| list: () => Effect.die("unused"), | |
| }), | |
| ) | |
| const registry = ToolRegistry.defaultLayer.pipe(Layer.provide(permission)) | |
| const config = Layer.succeed(Config.Service, Config.Service.of({ entries: () => Effect.succeed(configEntries) })) | |
| const image = Image.layer.pipe(Layer.provide(config)) | |
| const testFileSystem = Layer.effect( | |
| FSUtil.Service, | |
| FSUtil.Service.use((fs) => Effect.succeed(FSUtil.Service.of({ ...fs, realPath: (path) => Effect.succeed(path) }))), | |
| ).pipe(Layer.provide(FSUtil.defaultLayer)) | |
| const infrastructure = Layer.mergeAll( | |
| testFileSystem, | |
| Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make(process.cwd()) }))), | |
| Global.layerWith({ data: Global.Path.data }), | |
| ) | |
| const unavailableImage = Layer.succeed( | |
| Image.Service, | |
| Image.Service.of({ normalize: () => Effect.fail(new Image.ResizerUnavailableError()) }), | |
| ) | |
| const read = ReadTool.layer.pipe( | |
| Layer.provide(registry), | |
| Layer.provide(reader), | |
| Layer.provide(permission), | |
| Layer.provide(config), | |
| Layer.provide(image), | |
| Layer.provide(infrastructure), | |
| ) | |
| const it = testEffect(Layer.mergeAll(registry, reader, permission, config, image, infrastructure, read)) | |
| const unavailableRead = ReadTool.layer.pipe( | |
| Layer.provide(registry), | |
| Layer.provide(reader), | |
| Layer.provide(permission), | |
| Layer.provide(config), | |
| Layer.provide(unavailableImage), | |
| Layer.provide(infrastructure), | |
| ) | |
| const itWithoutResizer = testEffect( | |
| Layer.mergeAll(registry, reader, permission, config, unavailableImage, infrastructure, unavailableRead), | |
| ) | |
| const sessionID = SessionV2.ID.make("ses_read_tool_test") | |
| describe("ReadTool", () => { | |
| beforeEach(() => { | |
| assertions.length = 0 | |
| readCalls.length = 0 | |
| listCalls.length = 0 | |
| allow = true | |
| resolvedType = "file" | |
| resolveFailure = undefined | |
| readResult = { | |
| uri: "file:///README.md", | |
| name: "README.md", | |
| content: "hello", | |
| encoding: "utf8", | |
| mime: "text/plain", | |
| } | |
| readFailure = undefined | |
| configEntries = [] | |
| }) | |
| it.effect("registers, authorizes, and reads through the location filesystem", () => | |
| Effect.gen(function* () { | |
| const registry = yield* ToolRegistry.Service | |
| expect(yield* toolDefinitions(registry)).toMatchObject([{ name: "read" }]) | |
| expect(yield* toolDefinitions(registry, [{ action: "read", resource: "*", effect: "deny" }])).toEqual([]) | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-read", name: "read", input: { path: "README.md" } }, | |
| }), | |
| ).toEqual({ | |
| type: "json", | |
| value: { | |
| uri: "file:///README.md", | |
| name: "README.md", | |
| content: "hello", | |
| encoding: "utf8", | |
| mime: "text/plain", | |
| }, | |
| }) | |
| expect(assertions).toMatchObject([{ sessionID, action: "read", resources: ["README.md"], save: ["*"] }]) | |
| expect(readCalls).toEqual([{ input: AbsolutePath.make(`${process.cwd()}/README.md`), page: {} }]) | |
| }), | |
| ) | |
| it.effect("returns a small PNG as native media instead of durable base64 text", () => | |
| Effect.gen(function* () { | |
| const png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" | |
| readResult = { | |
| uri: "file:///pixel.png", | |
| name: "pixel.png", | |
| content: png, | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-image", name: "read", input: { path: "pixel.png" } }, | |
| }), | |
| ).toEqual({ | |
| type: "content", | |
| value: [ | |
| { type: "text", text: "Image read successfully" }, | |
| { type: "file", uri: `data:image/png;base64,${png}`, mime: "image/png", name: "pixel.png" }, | |
| ], | |
| }) | |
| expect(readCalls).toEqual([{ input: AbsolutePath.make(`${process.cwd()}/pixel.png`), page: {} }]) | |
| const settled = yield* settleTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-image-settle", name: "read", input: { path: "pixel.png" } }, | |
| }) | |
| expect(settled.output?.structured).toMatchObject({ | |
| uri: "file:///pixel.png", | |
| name: "pixel.png", | |
| mime: "image/png", | |
| encoding: "base64", | |
| }) | |
| expect(settled.output?.content).toMatchObject([ | |
| { type: "text", text: "Image read successfully" }, | |
| { type: "file", mime: "image/png", uri: `data:image/png;base64,${png}` }, | |
| ]) | |
| }), | |
| ) | |
| it.effect("preserves a PNG above the generic text limit as native media", () => | |
| Effect.gen(function* () { | |
| const photon = yield* Effect.promise(() => import("@silvia-odwyer/photon-node")) | |
| const pixels = Uint8Array.from({ length: 256 * 256 * 4 }, (_, index) => (index * 73 + (index >> 3)) % 256) | |
| const source = new photon.PhotonImage(pixels, 256, 256) | |
| const png = Buffer.from(source.get_bytes()).toString("base64") | |
| source.free() | |
| expect(Buffer.byteLength(png)).toBeGreaterThan(50 * 1024) | |
| readResult = { | |
| uri: "file:///large.png", | |
| name: "large.png", | |
| content: png, | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| const registry = yield* ToolRegistry.Service | |
| const settled = yield* settleTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-large-image", name: "read", input: { path: "large.png" } }, | |
| }) | |
| expect(settled.outputPaths).toBeUndefined() | |
| expect(settled.output?.structured).toMatchObject({ | |
| uri: "file:///large.png", | |
| name: "large.png", | |
| mime: "image/png", | |
| encoding: "base64", | |
| }) | |
| expect(settled.result).toEqual({ | |
| type: "content", | |
| value: [ | |
| { type: "text", text: "Image read successfully" }, | |
| { type: "file", uri: `data:image/png;base64,${png}`, mime: "image/png", name: "large.png" }, | |
| ], | |
| }) | |
| }), | |
| ) | |
| itWithoutResizer.effect("returns the original image when the resizer is unavailable", () => | |
| Effect.gen(function* () { | |
| const png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" | |
| readResult = { | |
| uri: "file:///pixel.png", | |
| name: "pixel.png", | |
| content: png, | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-image-fallback", name: "read", input: { path: "pixel.png" } }, | |
| }), | |
| ).toMatchObject({ | |
| type: "content", | |
| value: [{ type: "text" }, { type: "file", uri: `data:image/png;base64,${png}`, mime: "image/png" }], | |
| }) | |
| }), | |
| ) | |
| it.effect("rejects invalid image data returned by the filesystem", () => | |
| Effect.gen(function* () { | |
| readResult = { | |
| uri: "file:///truncated.png", | |
| name: "truncated.png", | |
| content: "iVBORw0KGgo=", | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-truncated-image", name: "read", input: { path: "truncated.png" } }, | |
| }), | |
| ).toEqual({ type: "error", value: "Image could not be decoded: truncated.png" }) | |
| }), | |
| ) | |
| it.effect("rejects oversized images when resizing is disabled", () => | |
| Effect.gen(function* () { | |
| const photon = yield* Effect.promise(() => import("@silvia-odwyer/photon-node")) | |
| const source = new photon.PhotonImage(new Uint8Array(Array.from({ length: 16 * 4 }, () => 255)), 16, 1) | |
| const base64 = Buffer.from(source.get_bytes()).toString("base64") | |
| source.free() | |
| readResult = { | |
| uri: "file:///wide.png", | |
| name: "wide.png", | |
| content: base64, | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| configEntries = [ | |
| new Config.Document({ | |
| type: "document", | |
| info: new Config.Info({ | |
| attachments: new ConfigAttachments.Info({ | |
| image: new ConfigAttachments.Image({ auto_resize: false, max_width: 4 }), | |
| }), | |
| }), | |
| }), | |
| ] | |
| const registry = yield* ToolRegistry.Service | |
| const result = yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-wide-image", name: "read", input: { path: "wide.png" } }, | |
| }) | |
| expect(result.type).toBe("error") | |
| if (result.type === "error") expect(result.value).toContain("exceeding configured limits 4x2000") | |
| }), | |
| ) | |
| it.effect("resizes images to configured dimensions before returning media", () => | |
| Effect.gen(function* () { | |
| const photon = yield* Effect.promise(() => import("@silvia-odwyer/photon-node")) | |
| const source = new photon.PhotonImage(new Uint8Array(Array.from({ length: 16 * 4 }, () => 255)), 16, 1) | |
| const base64 = Buffer.from(source.get_bytes()).toString("base64") | |
| source.free() | |
| readResult = { | |
| uri: "file:///wide.png", | |
| name: "wide.png", | |
| content: base64, | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| configEntries = [ | |
| new Config.Document({ | |
| type: "document", | |
| info: new Config.Info({ | |
| attachments: new ConfigAttachments.Info({ image: new ConfigAttachments.Image({ max_width: 4 }) }), | |
| }), | |
| }), | |
| ] | |
| const registry = yield* ToolRegistry.Service | |
| const result = yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-resize-image", name: "read", input: { path: "wide.png" } }, | |
| }) | |
| expect(result.type).toBe("content") | |
| if (result.type !== "content") return | |
| const media = result.value[1] | |
| expect(media?.type).toBe("file") | |
| if (media?.type !== "file") return | |
| const resized = photon.PhotonImage.new_from_byteslice(Buffer.from(media.uri.split(",")[1] ?? "", "base64")) | |
| expect(resized.get_width()).toBeLessThanOrEqual(4) | |
| expect(resized.get_height()).toBeLessThanOrEqual(2_000) | |
| resized.free() | |
| }), | |
| ) | |
| it.effect("enforces max base64 bytes after resize attempts", () => | |
| Effect.gen(function* () { | |
| const png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" | |
| readResult = { | |
| uri: "file:///pixel.png", | |
| name: "pixel.png", | |
| content: png, | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| configEntries = [ | |
| new Config.Document({ | |
| type: "document", | |
| info: new Config.Info({ | |
| attachments: new ConfigAttachments.Info({ | |
| image: new ConfigAttachments.Image({ max_base64_bytes: 1 }), | |
| }), | |
| }), | |
| }), | |
| ] | |
| const registry = yield* ToolRegistry.Service | |
| const result = yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-max-bytes", name: "read", input: { path: "pixel.png" } }, | |
| }) | |
| expect(result.type).toBe("error") | |
| if (result.type === "error") expect(result.value).toContain("/1 bytes") | |
| }), | |
| ) | |
| it.effect("returns supported image contents despite a misleading binary extension", () => | |
| Effect.gen(function* () { | |
| const png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" | |
| readResult = { | |
| uri: "file:///pixel.bin", | |
| name: "pixel.bin", | |
| content: png, | |
| encoding: "base64", | |
| mime: "image/png", | |
| } | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-disguised-image", name: "read", input: { path: "pixel.bin" } }, | |
| }), | |
| ).toMatchObject({ | |
| type: "content", | |
| value: [{ type: "text" }, { type: "file", mime: "image/png", name: "pixel.bin" }], | |
| }) | |
| }), | |
| ) | |
| it.effect("preserves unexpected filesystem defects", () => | |
| Effect.gen(function* () { | |
| readFailure = new ReadToolFileSystem.BinaryFileError("archive.dat") | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| Exit.isFailure( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { | |
| type: "tool-call", | |
| id: "call-binary", | |
| name: "read", | |
| input: { path: "archive.dat", offset: 2, limit: 1 }, | |
| }, | |
| }).pipe(Effect.exit), | |
| ), | |
| ).toBe(true) | |
| expect(readCalls).toEqual([ | |
| { input: AbsolutePath.make(`${process.cwd()}/archive.dat`), page: { offset: 2, limit: 1 } }, | |
| ]) | |
| }), | |
| ) | |
| it.effect("does not read when permission is denied", () => | |
| Effect.gen(function* () { | |
| allow = false | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-read", name: "read", input: { path: "README.md" } }, | |
| }), | |
| ).toEqual({ type: "error", value: "Unable to read README.md" }) | |
| expect(readCalls).toEqual([]) | |
| }), | |
| ) | |
| it.effect("lists a bounded directory page through read", () => | |
| Effect.gen(function* () { | |
| resolvedType = "directory" | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { | |
| type: "tool-call", | |
| id: "call-read-directory", | |
| name: "read", | |
| input: { path: "src", offset: 2, limit: 10 }, | |
| }, | |
| }), | |
| ).toEqual({ type: "json", value: { entries: [], truncated: false } }) | |
| expect(assertions).toMatchObject([{ sessionID, action: "read", resources: ["src"], save: ["*"] }]) | |
| expect(listCalls).toEqual([{ offset: 2, limit: 10 }]) | |
| }), | |
| ) | |
| it.effect("does not list a directory when permission is denied", () => | |
| Effect.gen(function* () { | |
| allow = false | |
| resolvedType = "directory" | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-read-directory-denied", name: "read", input: { path: "src" } }, | |
| }), | |
| ).toEqual({ type: "error", value: "Unable to read src" }) | |
| expect(listCalls).toEqual([]) | |
| }), | |
| ) | |
| it.effect("preserves unexpected resolution defects", () => | |
| Effect.gen(function* () { | |
| const registry = yield* ToolRegistry.Service | |
| resolveFailure = new Error("missing") | |
| expect( | |
| Exit.isFailure( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-missing", name: "read", input: { path: "missing.txt" } }, | |
| }).pipe(Effect.exit), | |
| ), | |
| ).toBe(true) | |
| expect(readCalls).toEqual([]) | |
| }), | |
| ) | |
| it.effect("forwards pagination and returns bounded text pages with continuation", () => | |
| Effect.gen(function* () { | |
| readResult = new ReadToolFileSystem.TextPage({ | |
| type: "text-page", | |
| content: "hello", | |
| mime: "text/plain", | |
| offset: 2, | |
| truncated: true, | |
| next: 3, | |
| }) | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { | |
| type: "tool-call", | |
| id: "call-large", | |
| name: "read", | |
| input: { path: "large.txt", offset: 2, limit: 1 }, | |
| }, | |
| }), | |
| ).toEqual({ | |
| type: "json", | |
| value: { type: "text-page", content: "hello", mime: "text/plain", offset: 2, truncated: true, next: 3 }, | |
| }) | |
| expect(readCalls).toEqual([ | |
| { input: AbsolutePath.make(`${process.cwd()}/large.txt`), page: { offset: 2, limit: 1 } }, | |
| ]) | |
| }), | |
| ) | |
| it.effect("rejects unsupported binary discovered by a direct read", () => | |
| Effect.gen(function* () { | |
| readResult = { | |
| uri: "file:///late-binary", | |
| name: "late-binary", | |
| content: "AAECAw==", | |
| encoding: "base64", | |
| mime: "application/octet-stream", | |
| } | |
| const registry = yield* ToolRegistry.Service | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-direct-binary", name: "read", input: { path: "late-binary" } }, | |
| }), | |
| ).toEqual({ type: "error", value: "Cannot read binary file: late-binary" }) | |
| }), | |
| ) | |
| }) | |
Xet Storage Details
- Size:
- 19.7 kB
- Xet hash:
- 5ddb4375bc21370c0192459eebc1ad06d9a08afa021c0694482c14398924bea0
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.