refactor(tool): migrate tool framework + all 18 built-in tools to Effect Schema (#23244)

This commit is contained in:
Kit Langton
2026-04-23 16:09:34 -04:00
committed by GitHub
parent fdb22d0577
commit 9d583dc856
25 changed files with 1035 additions and 205 deletions
+17 -9
View File
@@ -1,5 +1,4 @@
import z from "zod"
import { Effect, Option, Scope } from "effect"
import { Effect, Option, Schema, Scope } from "effect"
import { createReadStream } from "fs"
import * as path from "path"
import { createInterface } from "readline"
@@ -19,10 +18,19 @@ const MAX_BYTES = 50 * 1024
const MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`
const SAMPLE_BYTES = 4096
const parameters = z.object({
filePath: z.string().describe("The absolute path to the file or directory to read"),
offset: z.coerce.number().describe("The line number to start reading from (1-indexed)").optional(),
limit: z.coerce.number().describe("The maximum number of lines to read (defaults to 2000)").optional(),
// `offset` and `limit` were originally `z.coerce.number()` — the runtime
// coercion was useful when the tool was called from a shell but serves no
// purpose in the LLM tool-call path (the model emits typed JSON). The JSON
// Schema output is identical (`type: "number"`), so the LLM view is
// unchanged; purely CLI-facing uses must now send numbers rather than strings.
export const Parameters = Schema.Struct({
filePath: Schema.String.annotate({ description: "The absolute path to the file or directory to read" }),
offset: Schema.optional(Schema.Number).annotate({
description: "The line number to start reading from (1-indexed)",
}),
limit: Schema.optional(Schema.Number).annotate({
description: "The maximum number of lines to read (defaults to 2000)",
}),
})
export const ReadTool = Tool.define(
@@ -140,7 +148,7 @@ export const ReadTool = Tool.define(
return nonPrintableCount / bytes.length > 0.3
}
const run = Effect.fn("ReadTool.execute")(function* (params: z.infer<typeof parameters>, ctx: Tool.Context) {
const run = Effect.fn("ReadTool.execute")(function* (params: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context) {
if (params.offset !== undefined && params.offset < 1) {
return yield* Effect.fail(new Error("offset must be greater than or equal to 1"))
}
@@ -275,8 +283,8 @@ export const ReadTool = Tool.define(
return {
description: DESCRIPTION,
parameters,
execute: (params: z.infer<typeof parameters>, ctx: Tool.Context) => run(params, ctx).pipe(Effect.orDie),
parameters: Parameters,
execute: (params: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context) => run(params, ctx).pipe(Effect.orDie),
}
}),
)