refactor(core): unify v2 tool architecture (#31168)
This commit is contained in:
parent
effd27b239
commit
660a00d317
46 changed files with 2297 additions and 2041 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Deferred, Effect, Fiber, Layer } from "effect"
|
||||
import { Deferred, Effect, Exit, Fiber, Layer } from "effect"
|
||||
import { FileMutation } from "@opencode-ai/core/file-mutation"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
|
|
@ -14,6 +14,7 @@ import { ApplyPatchTool } from "@opencode-ai/core/tool/apply-patch"
|
|||
import { location } from "./fixture/location"
|
||||
import { tmpdir } from "./fixture/tmpdir"
|
||||
import { testEffect } from "./lib/effect"
|
||||
import { toolIdentity, executeTool, settleTool, toolDefinitions } from "./lib/tool"
|
||||
|
||||
const sessionID = SessionV2.ID.make("ses_apply_patch_tool_test")
|
||||
const assertions: PermissionV2.AssertInput[] = []
|
||||
|
|
@ -92,6 +93,7 @@ const withTool = <A, E, R>(directory: string, body: (registry: ToolRegistry.Inte
|
|||
const registry = ToolRegistry.defaultLayer.pipe(Layer.provide(permission))
|
||||
const patch = ApplyPatchTool.layer.pipe(
|
||||
Layer.provide(registry),
|
||||
Layer.provide(permission),
|
||||
Layer.provide(resolution),
|
||||
Layer.provide(mutation),
|
||||
Layer.provide(filesystem),
|
||||
|
|
@ -103,6 +105,7 @@ const withTool = <A, E, R>(directory: string, body: (registry: ToolRegistry.Inte
|
|||
|
||||
const call = (patchText: string, id = "call-apply-patch") => ({
|
||||
sessionID,
|
||||
...toolIdentity,
|
||||
call: { type: "tool-call" as const, id, name: "apply_patch", input: { patchText } },
|
||||
})
|
||||
|
||||
|
|
@ -129,8 +132,9 @@ describe("ApplyPatchTool", () => {
|
|||
Effect.andThen(
|
||||
withTool(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect((yield* registry.definitions()).map((tool) => tool.name)).toEqual(["apply_patch"])
|
||||
const settled = yield* registry.settle(
|
||||
expect((yield* toolDefinitions(registry)).map((tool) => tool.name)).toEqual(["apply_patch"])
|
||||
const settled = yield* settleTool(
|
||||
registry,
|
||||
call(
|
||||
"*** Begin Patch\n*** Add File: nested/new.txt\n+created\n*** Update File: update.txt\n@@\n-before\n+after\n*** Delete File: remove.txt\n*** End Patch",
|
||||
),
|
||||
|
|
@ -146,7 +150,7 @@ describe("ApplyPatchTool", () => {
|
|||
{ type: "delete", resource: "remove.txt" },
|
||||
],
|
||||
})
|
||||
expect(assertions).toEqual([
|
||||
expect(assertions).toMatchObject([
|
||||
{ sessionID, action: "edit", resources: ["nested/new.txt", "update.txt", "remove.txt"], save: ["*"] },
|
||||
])
|
||||
expect(readsBeforeEditApproval).toBe(0)
|
||||
|
|
@ -175,7 +179,8 @@ describe("ApplyPatchTool", () => {
|
|||
withTool(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* registry.execute(
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call(
|
||||
"*** Begin Patch\n*** Add File: created.txt\n+created\n*** Update File: old.txt\n*** Move to: moved.txt\n@@\n-before\n+after\n*** End Patch",
|
||||
),
|
||||
|
|
@ -203,7 +208,8 @@ describe("ApplyPatchTool", () => {
|
|||
withTool(active.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* registry.execute(
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call(`*** Begin Patch\n*** Update File: ${target}\n@@\n-before\n+after\n*** End Patch`),
|
||||
),
|
||||
).toMatchObject({ type: "text" })
|
||||
|
|
@ -236,7 +242,8 @@ describe("ApplyPatchTool", () => {
|
|||
withTool(active.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* registry.execute(
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call(
|
||||
`*** Begin Patch\n*** Update File: ${first}\n@@\n-before\n+after\n*** Update File: ${second}\n@@\n-before\n+after\n*** End Patch`,
|
||||
),
|
||||
|
|
@ -266,7 +273,8 @@ describe("ApplyPatchTool", () => {
|
|||
return withTool(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* registry.execute(
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call(
|
||||
"*** Begin Patch\n*** Add File: created.txt\n+created\n*** Update File: missing.txt\n@@\n-before\n+after\n*** End Patch",
|
||||
),
|
||||
|
|
@ -291,7 +299,8 @@ describe("ApplyPatchTool", () => {
|
|||
withTool(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* registry.execute(
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call("*** Begin Patch\n*** Add File: existing.txt\n+replacement\n*** End Patch"),
|
||||
),
|
||||
).toEqual({ type: "error", value: "Unable to apply patch at existing.txt" })
|
||||
|
|
@ -315,7 +324,10 @@ describe("ApplyPatchTool", () => {
|
|||
return withTool(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* registry.execute(call("*** Begin Patch\n*** Add File: appeared.txt\n+replacement\n*** End Patch")),
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call("*** Begin Patch\n*** Add File: appeared.txt\n+replacement\n*** End Patch"),
|
||||
),
|
||||
).toEqual({ type: "error", value: "Unable to apply patch at appeared.txt" })
|
||||
expect(yield* Effect.promise(() => fs.readFile(target, "utf8"))).toBe("winner\n")
|
||||
}),
|
||||
|
|
@ -325,7 +337,7 @@ describe("ApplyPatchTool", () => {
|
|||
),
|
||||
)
|
||||
|
||||
it.live("reports earlier sequential applications when a later commit fails", () =>
|
||||
it.live("preserves a later commit defect after earlier sequential applications", () =>
|
||||
Effect.acquireUseRelease(
|
||||
Effect.promise(() => tmpdir()),
|
||||
(tmp) => {
|
||||
|
|
@ -338,13 +350,13 @@ describe("ApplyPatchTool", () => {
|
|||
withTool(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* registry.execute(
|
||||
call("*** Begin Patch\n*** Delete File: first.txt\n*** Delete File: second.txt\n*** End Patch"),
|
||||
Exit.isFailure(
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call("*** Begin Patch\n*** Delete File: first.txt\n*** Delete File: second.txt\n*** End Patch"),
|
||||
).pipe(Effect.exit),
|
||||
),
|
||||
).toEqual({
|
||||
type: "error",
|
||||
value: "Patch partially applied before failing at second.txt. Applied: first.txt",
|
||||
})
|
||||
).toBe(true)
|
||||
expect(yield* exists(first)).toBe(false)
|
||||
expect(yield* exists(second)).toBe(true)
|
||||
}),
|
||||
|
|
@ -370,11 +382,10 @@ describe("ApplyPatchTool", () => {
|
|||
yield* Effect.promise(() => Promise.all([fs.writeFile(first, "first"), fs.writeFile(second, "second")]))
|
||||
yield* withTool(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
const run = yield* registry
|
||||
.execute(
|
||||
call("*** Begin Patch\n*** Delete File: first.txt\n*** Delete File: second.txt\n*** End Patch"),
|
||||
)
|
||||
.pipe(Effect.forkChild)
|
||||
const run = yield* executeTool(
|
||||
registry,
|
||||
call("*** Begin Patch\n*** Delete File: first.txt\n*** Delete File: second.txt\n*** End Patch"),
|
||||
).pipe(Effect.forkChild)
|
||||
yield* Deferred.await(removeStarted!)
|
||||
const interrupt = yield* Fiber.interrupt(run).pipe(Effect.forkChild)
|
||||
yield* Deferred.succeed(releaseRemove!, undefined)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue