|
|
@@ -1,5 +1,5 @@
|
|
|
import { beforeEach, describe, expect } from "bun:test"
|
|
|
-import { Effect, Exit, Layer } from "effect"
|
|
|
+import { Effect, Exit, Layer, PlatformError } from "effect"
|
|
|
import { Config } from "@opencode-ai/core/config"
|
|
|
import { ConfigAttachments } from "@opencode-ai/core/config/attachments"
|
|
|
import { FileSystem } from "@opencode-ai/core/filesystem"
|
|
|
@@ -18,6 +18,8 @@ import { testEffect } from "./lib/effect"
|
|
|
import { toolIdentity, executeTool, settleTool, toolDefinitions } from "./lib/tool"
|
|
|
|
|
|
const assertions: PermissionV2.AssertInput[] = []
|
|
|
+const missingPath = "__missing_read_target__.txt"
|
|
|
+const missingAbsolutePath = `${process.cwd()}/${missingPath}`
|
|
|
const readCalls: {
|
|
|
input: AbsolutePath
|
|
|
page: ReadToolFileSystem.PageInput
|
|
|
@@ -70,7 +72,24 @@ const config = Layer.succeed(Config.Service, Config.Service.of({ entries: () =>
|
|
|
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) }))),
|
|
|
+ FSUtil.Service.use((fs) =>
|
|
|
+ Effect.succeed(
|
|
|
+ FSUtil.Service.of({
|
|
|
+ ...fs,
|
|
|
+ realPath: (path) =>
|
|
|
+ path === missingAbsolutePath
|
|
|
+ ? Effect.fail(
|
|
|
+ PlatformError.systemError({
|
|
|
+ _tag: "NotFound",
|
|
|
+ module: "FileSystem",
|
|
|
+ method: "realPath",
|
|
|
+ pathOrDescriptor: path,
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ : Effect.succeed(path),
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
).pipe(Layer.provide(FSUtil.defaultLayer))
|
|
|
const infrastructure = Layer.mergeAll(
|
|
|
testFileSystem,
|
|
|
@@ -453,6 +472,22 @@ describe("ReadTool", () => {
|
|
|
}),
|
|
|
)
|
|
|
|
|
|
+ it.effect("returns missing paths as model-visible tool failures", () =>
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const registry = yield* ToolRegistry.Service
|
|
|
+
|
|
|
+ expect(
|
|
|
+ yield* executeTool(registry, {
|
|
|
+ sessionID,
|
|
|
+ ...toolIdentity,
|
|
|
+ call: { type: "tool-call", id: "call-missing-path", name: "read", input: { path: missingPath } },
|
|
|
+ }),
|
|
|
+ ).toEqual({ type: "error", value: `Unable to read ${missingPath}` })
|
|
|
+ expect(assertions).toEqual([])
|
|
|
+ expect(readCalls).toEqual([])
|
|
|
+ }),
|
|
|
+ )
|
|
|
+
|
|
|
it.effect("lists a bounded directory page through read", () =>
|
|
|
Effect.gen(function* () {
|
|
|
resolvedType = "directory"
|