@mikode13/harness 5.0.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -2
- package/dist/agent/domain/agent.d.ts +63 -1
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/engines/domain/model/llmAgent.d.ts +2 -0
- package/dist/engines/domain/model/llmAgent.js +101 -26
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/engines/domain/model/stepContext.d.ts +16 -0
- package/dist/engines/domain/model/stepContext.js +60 -0
- package/dist/engines/domain/model/stepContext.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/llm/domain/llm.d.ts +7 -0
- package/dist/llm/domain/llm.js.map +1 -1
- package/dist/llm/infrastructure/claudeLLMClient.d.ts +2 -1
- package/dist/llm/infrastructure/claudeLLMClient.js +1 -0
- package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
- package/dist/llm/infrastructure/openAILLMClient.d.ts +2 -1
- package/dist/llm/infrastructure/openAILLMClient.js +2 -1
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +10 -5
- package/dist/orchestration/domain/model/orchestratorAgent.js +24 -6
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/tools/domain/preparedCall.d.ts +14 -1
- package/dist/tools/domain/preparedCall.js +12 -0
- package/dist/tools/domain/preparedCall.js.map +1 -1
- package/dist/tools/domain/tool.d.ts +14 -0
- package/dist/tools/domain/tool.js +7 -1
- package/dist/tools/domain/tool.js.map +1 -1
- package/dist/tools/infrastructure/commandTool.d.ts +1 -1
- package/dist/tools/infrastructure/commandTool.js +32 -1
- package/dist/tools/infrastructure/commandTool.js.map +1 -1
- package/dist/tools/infrastructure/defineTool.d.ts +5 -3
- package/dist/tools/infrastructure/defineTool.js +14 -2
- package/dist/tools/infrastructure/defineTool.js.map +1 -1
- package/dist/tools/infrastructure/editFormats.js +26 -1
- package/dist/tools/infrastructure/editFormats.js.map +1 -1
- package/dist/tools/infrastructure/showChangesTool.js +1 -0
- package/dist/tools/infrastructure/showChangesTool.js.map +1 -1
- package/dist/tools/infrastructure/trackedReadFile.js +2 -1
- package/dist/tools/infrastructure/trackedReadFile.js.map +1 -1
- package/dist/tools/infrastructure/validatedSummarizer.d.ts +7 -0
- package/dist/tools/infrastructure/validatedSummarizer.js +15 -0
- package/dist/tools/infrastructure/validatedSummarizer.js.map +1 -0
- package/dist/tools/infrastructure/workspaceTools.d.ts +6 -0
- package/dist/tools/infrastructure/workspaceTools.js +16 -0
- package/dist/tools/infrastructure/workspaceTools.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commandTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/commandTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpath } from 'node:fs/promises';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { z } from 'zod';\nimport type { CommandLine } from '#src/agent/domain/approval';\nimport type { RunContext } from '#src/agent/domain/runContext';\nimport { displayPath } from '#src/recovery/domain/runChanges';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport type { AccessPolicy } from '../domain/accessPolicy.ts';\nimport {\n\tbuildArguments,\n\ttype CommandDefinition,\n\ttype CommandParameter,\n\ttype Commands,\n\tdefaultCommandOutputBytes,\n\tdefaultCommandTimeoutMs,\n\ttype ParameterValue,\n\tparametersOf,\n\tvalidateCommands,\n} from '../domain/commands.ts';\nimport { EditRefusedError, EditUnconfirmedError } from '../domain/fileEdits.ts';\nimport type { PreparingTool } from '../domain/preparedCall.ts';\nimport { CommandCheckpoint, type CoveredFiles } from './commandCheckpoint.ts';\nimport { type CommandProcessResult, runCommandProcess } from './commandProcess.ts';\nimport { definePreparingTool } from './defineTool.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\n\n// Only what a toolchain needs to find itself and its locale. Everything else, the provider\n// keys included, stays in this process.\nconst inheritedVariables = ['PATH', 'HOME', 'TMPDIR', 'USER', 'SHELL', 'LANG'];\n// Long enough for a slow disk, short enough not to hold up building an agent.\nconst cwdCheckMs = 10_000;\n\n/** A command ready to run: its definition, and where it runs as a real path. */\ninterface ResolvedCommand {\n\tdefinition: CommandDefinition;\n\tcwd: string;\n}\n\n/**\n * `runCommand`, which runs one of the commands the consumer configured, by name, with the\n * parameters each one declares. Every call asks the approver, showing the program exactly as\n * it will run; `autoApprove` skips that.\n *\n * Around each command, the covered files are captured before and compared after, so what it\n * changed is in the run's history and can be undone: see `CommandCheckpoint`. If the capture\n * fails, the command does not run. A timeout or a cancellation stops its whole process group,\n * and is never reported as a completed command. If the group cannot be confirmed stopped, what\n * changed so far is recorded, the observation stays open, and writing stops for the run.\n *\n * This is not a sandbox: a command runs the repository's code, with everything this user can\n * reach. See \"Configured commands are trusted, not sandboxed\" in decisions.md.\n *\n * @throws {InvalidAgentConfigError} for an invalid configuration, a working directory outside\n * every `write` root, or on Windows, where a process group cannot be stopped.\n */\nexport async function createCommandTool({\n\tcommands,\n\tpolicy,\n\twrites,\n\tcovered,\n\tbase,\n\tplatform = process.platform,\n\trunProcess = runCommandProcess,\n}: {\n\tcommands: Commands;\n\tpolicy: AccessPolicy;\n\twrites: WorkspaceWrites;\n\tcovered: CoveredFiles;\n\t/** The real path of the first root, where a relative `cwd` starts. */\n\tbase: string;\n\tplatform?: NodeJS.Platform;\n\t/** Runs the program. Replaced in tests, to reach what a real process rarely does. */\n\trunProcess?: typeof runCommandProcess;\n}): Promise<PreparingTool> {\n\tif (platform === 'win32') {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t'Configured commands are not supported on Windows yet: the harness cannot stop a command and everything it started there',\n\t\t);\n\t}\n\tvalidateCommands(commands);\n\tconst resolved = new Map<string, ResolvedCommand>();\n\tfor (const [name, definition] of Object.entries(commands)) {\n\t\tresolved.set(name, { definition, cwd: await resolveCwd(name, definition, policy, base) });\n\t}\n\n\treturn definePreparingTool({\n\t\tname: 'runCommand',\n\t\tdescription: describeTool(commands),\n\t\tinput: inputSchema(commands),\n\t\tprepare: async ({ command: { name, ...values } }, signal, { run }) => {\n\t\t\tconst command = resolved.get(name);\n\t\t\t// The schema allows only the configured names.\n\t\t\tif (!command) throw new Error(`There is no command named \"${name}\"`);\n\t\t\tconst paramValues: Record<string, ParameterValue> = {};\n\t\t\tfor (const parameter of parametersOf(command.definition)) {\n\t\t\t\tconst value =\n\t\t\t\t\t(values as Record<string, ParameterValue | undefined>)[parameter.name] ?? null;\n\t\t\t\tparamValues[parameter.name] =\n\t\t\t\t\tparameter.type === 'paths' && Array.isArray(value)\n\t\t\t\t\t\t? await resolvePaths(value, command.cwd, policy, signal)\n\t\t\t\t\t\t: value;\n\t\t\t}\n\t\t\tconst line: CommandLine = Object.freeze({\n\t\t\t\texecutable: command.definition.executable,\n\t\t\t\targs: Object.freeze(buildArguments(command.definition, paramValues)) as string[],\n\t\t\t\tcwd: command.cwd,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\t// A command can change anything its user can: it is always asked about.\n\t\t\t\trisk: 'destructive',\n\t\t\t\tcommand: line,\n\t\t\t\trun: runSignal =>\n\t\t\t\t\trunCommand({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tline,\n\t\t\t\t\t\tdefinition: command.definition,\n\t\t\t\t\t\tcontext: run,\n\t\t\t\t\t\tsignal: runSignal,\n\t\t\t\t\t}),\n\t\t\t};\n\t\t},\n\t});\n\n\tasync function runCommand({\n\t\tname,\n\t\tline,\n\t\tdefinition,\n\t\tcontext,\n\t\tsignal,\n\t}: {\n\t\tname: string;\n\t\tline: CommandLine;\n\t\tdefinition: CommandDefinition;\n\t\tcontext: RunContext;\n\t\tsignal: AbortSignal;\n\t}): Promise<string> {\n\t\tsignal.throwIfAborted();\n\t\tconst session = writes.sessionFor(context);\n\t\tconst journal = await session.journalForChange();\n\t\tlet checkpoint: CommandCheckpoint;\n\t\ttry {\n\t\t\tcheckpoint = await CommandCheckpoint.begin({ journal, covered, by: name, signal });\n\t\t} catch (error) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`The harness could not capture the files before \"${name}\", so it did not run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tlet result: CommandProcessResult | undefined;\n\t\tlet startFailure: unknown;\n\t\ttry {\n\t\t\tresult = await runProcess(line.executable, line.args, {\n\t\t\t\tcwd: line.cwd,\n\t\t\t\tenv: environmentFor(definition),\n\t\t\t\ttimeoutMs: definition.timeoutMs ?? defaultCommandTimeoutMs,\n\t\t\t\toutputBytes: definition.outputBytes ?? defaultCommandOutputBytes,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tstartFailure = error;\n\t\t}\n\n\t\tconst complete = result?.stopped ?? true;\n\t\tlet observed: Awaited<ReturnType<CommandCheckpoint['end']>>;\n\t\ttry {\n\t\t\tobserved = await checkpoint.end({ complete });\n\t\t} catch (error) {\n\t\t\tsession.stop('Writing stopped: the harness could not record what a command changed');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`\"${name}\" ran, but the harness could not record what it changed; writing has stopped for this run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tif (!result) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(`\"${name}\" could not be started`, { cause: startFailure });\n\t\t}\n\t\tconst report = describeRun(name, definition, result, observed);\n\t\tif (!complete) {\n\t\t\tsession.stop('Writing stopped: a command could not be stopped');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`${report}\\n\"${name}\" could not be stopped: something it started may still be changing files, and the history may not hold all of it. Writing has stopped for this run.`,\n\t\t\t);\n\t\t}\n\t\t// After its changes are recorded: a cancelled command is never reported as finished.\n\t\tif (result.outcome === 'cancelled') signal.throwIfAborted();\n\t\tif (result.outcome !== 'exited') throw new Error(report);\n\t\treturn report;\n\t}\n\n\tfunction describeRun(\n\t\tname: string,\n\t\tdefinition: CommandDefinition,\n\t\tresult: CommandProcessResult,\n\t\t{ changes, unrecorded }: Awaited<ReturnType<CommandCheckpoint['end']>>,\n\t): string {\n\t\tconst seconds = (result.durationMs / 1000).toFixed(1);\n\t\tconst lines: string[] = [];\n\t\tif (result.outcome === 'timedOut') {\n\t\t\tconst limit = (definition.timeoutMs ?? defaultCommandTimeoutMs) / 1000;\n\t\t\tlines.push(`\"${name}\" ran past its limit of ${String(limit)} s and was stopped.`);\n\t\t} else if (result.outcome === 'cancelled') {\n\t\t\tlines.push(`\"${name}\" was stopped: the run was cancelled.`);\n\t\t} else if (result.exitCode !== null) {\n\t\t\tlines.push(`\"${name}\" exited with code ${String(result.exitCode)} after ${seconds} s.`);\n\t\t} else {\n\t\t\tlines.push(`\"${name}\" was killed by ${result.signal ?? 'a signal'} after ${seconds} s.`);\n\t\t}\n\n\t\tconst shown = (path: string) => displayPath(base, path);\n\t\tlines.push(\n\t\t\tchanges.length === 0\n\t\t\t\t? 'It changed no file the history covers.'\n\t\t\t\t: `Files it changed, recorded in the history: ${changes.map(change => shown(change.path)).join(', ')}.`,\n\t\t);\n\t\tif (unrecorded.length > 0) {\n\t\t\tlines.push(\n\t\t\t\t`Not recorded, because the history cannot copy them (a folder, a link, or a file over 64 MB): ${unrecorded.map(shown).join(', ')}.`,\n\t\t\t);\n\t\t}\n\n\t\tconst limit = definition.outputBytes ?? defaultCommandOutputBytes;\n\t\tif (result.printedBytes === 0) {\n\t\t\tlines.push('It printed nothing.');\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\tresult.printedBytes > limit\n\t\t\t\t\t? `Output, the last ${kilobytes(limit)} of ${kilobytes(result.printedBytes)}:`\n\t\t\t\t\t: 'Output:',\n\t\t\t\tresult.output,\n\t\t\t);\n\t\t}\n\t\treturn lines.join('\\n');\n\t}\n}\n\nfunction kilobytes(bytes: number): string {\n\treturn `${String(Math.ceil(bytes / 1024))} KB`;\n}\n\n/** Its working directory, as a real path inside a `write` root. */\nasync function resolveCwd(\n\tname: string,\n\tdefinition: CommandDefinition,\n\tpolicy: AccessPolicy,\n\tbase: string,\n): Promise<string> {\n\tconst named = resolve(base, definition.cwd ?? '.');\n\tlet real: string;\n\ttry {\n\t\treal = await realpath(named);\n\t\tawait policy.check(real, 'write', AbortSignal.timeout(cwdCheckMs));\n\t} catch (error) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`Command \"${name}\" needs a cwd inside a write root of the workspace`,\n\t\t\t{ cause: error },\n\t\t);\n\t}\n\treturn real;\n}\n\n/**\n * Each path, once the policy lets the agent read it, relative to where the command runs. One\n * that would read as a flag is given as `./` and its name.\n */\nasync function resolvePaths(\n\tpaths: string[],\n\tcwd: string,\n\tpolicy: AccessPolicy,\n\tsignal: AbortSignal,\n): Promise<string[]> {\n\tconst resolved: string[] = [];\n\tfor (const path of paths) {\n\t\tconst { absolute } = await policy.check(path, 'read', signal);\n\t\tconst fromCwd = relative(cwd, absolute) || '.';\n\t\tresolved.push(fromCwd.startsWith('-') && !isAbsolute(fromCwd) ? `./${fromCwd}` : fromCwd);\n\t}\n\treturn resolved;\n}\n\n/** A small environment of its own: the inherited few, then what the consumer set. */\nexport function environmentFor(\n\tdefinition: CommandDefinition,\n\tenv: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [variable, value] of Object.entries(env)) {\n\t\tif (value === undefined) continue;\n\t\tif (inheritedVariables.includes(variable) || variable.startsWith('LC_')) {\n\t\t\tinherited[variable] = value;\n\t\t}\n\t}\n\treturn { ...inherited, ...definition.env };\n}\n\nfunction describeTool(commands: Commands): string {\n\tconst list = Object.entries(commands)\n\t\t.map(([name, { description }]) => `- ${name}: ${description}`)\n\t\t.join('\\n');\n\treturn [\n\t\t\"Runs one of the project's configured commands, by name, such as its tests or its formatter.\",\n\t\t'Files it changes are recorded in the history, so they can be undone. Its output is cut to its end.',\n\t\t'Commands:',\n\t\tlist,\n\t].join('\\n');\n}\n\n/**\n * One object per command, told apart by `name`, each with the parameters that command takes.\n * Strict mode needs every key present, so a parameter that may be left out takes `null`.\n */\nfunction inputSchema(commands: Commands) {\n\tconst variants = Object.entries(commands).map(([name, definition]) =>\n\t\tz.object({\n\t\t\tname: z.enum([name]),\n\t\t\t...Object.fromEntries(\n\t\t\t\tparametersOf(definition).map(parameter => [parameter.name, parameterSchema(parameter)]),\n\t\t\t),\n\t\t}),\n\t);\n\tconst [first, ...rest] = variants;\n\t// `validateCommands` refuses an empty configuration.\n\tif (!first) throw new InvalidAgentConfigError('No command is configured');\n\treturn z.object({\n\t\tcommand: (rest.length === 0 ? first : z.union([first, ...rest])).describe(\n\t\t\t'The command to run, and its parameters.',\n\t\t),\n\t});\n}\n\nfunction parameterSchema(parameter: CommandParameter): z.ZodType {\n\tswitch (parameter.type) {\n\t\tcase 'paths':\n\t\t\treturn z\n\t\t\t\t.array(z.string())\n\t\t\t\t.describe(`${parameter.description} Paths inside the workspace; [] for none.`);\n\t\tcase 'choice': {\n\t\t\tconst values = z.enum(parameter.values as [string, ...string[]]);\n\t\t\treturn parameter.required === true\n\t\t\t\t? values.describe(parameter.description)\n\t\t\t\t: values.nullable().describe(`${parameter.description} null to leave it out.`);\n\t\t}\n\t\tcase 'text': {\n\t\t\tconst text = z.string();\n\t\t\tconst description = `${parameter.description} Must match the regular expression ${parameter.pattern}, and not start with \"-\".`;\n\t\t\treturn parameter.required === true\n\t\t\t\t? text.describe(description)\n\t\t\t\t: text.nullable().describe(`${description} null to leave it out.`);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"commandTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/commandTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpath } from 'node:fs/promises';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { z } from 'zod';\nimport type { CommandLine } from '#src/agent/domain/approval';\nimport type { RunContext } from '#src/agent/domain/runContext';\nimport { displayPath } from '#src/recovery/domain/runChanges';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport type { AccessPolicy } from '../domain/accessPolicy.ts';\nimport {\n\tbuildArguments,\n\ttype CommandDefinition,\n\ttype CommandParameter,\n\ttype Commands,\n\tdefaultCommandOutputBytes,\n\tdefaultCommandTimeoutMs,\n\ttype ParameterValue,\n\tparametersOf,\n\tvalidateCommands,\n} from '../domain/commands.ts';\nimport { EditRefusedError, EditUnconfirmedError } from '../domain/fileEdits.ts';\nimport { ToolExecutionError, type PreparingTool } from '../domain/preparedCall.ts';\nimport { CommandCheckpoint, type CoveredFiles } from './commandCheckpoint.ts';\nimport { type CommandProcessResult, runCommandProcess } from './commandProcess.ts';\nimport { definePreparingTool } from './defineTool.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\n\n// Only what a toolchain needs to find itself and its locale. Everything else, the provider\n// keys included, stays in this process.\nconst inheritedVariables = ['PATH', 'HOME', 'TMPDIR', 'USER', 'SHELL', 'LANG'];\n// Long enough for a slow disk, short enough not to hold up building an agent.\nconst cwdCheckMs = 10_000;\n\n/** A command ready to run: its definition, and where it runs as a real path. */\ninterface ResolvedCommand {\n\tdefinition: CommandDefinition;\n\tcwd: string;\n}\n\n/**\n * `runCommand`, which runs one of the commands the consumer configured, by name, with the\n * parameters each one declares. Every call asks the approver, showing the program exactly as\n * it will run; `autoApprove` skips that.\n *\n * Around each command, the covered files are captured before and compared after, so what it\n * changed is in the run's history and can be undone: see `CommandCheckpoint`. If the capture\n * fails, the command does not run. A timeout or a cancellation stops its whole process group,\n * and is never reported as a completed command. If the group cannot be confirmed stopped, what\n * changed so far is recorded, the observation stays open, and writing stops for the run.\n *\n * This is not a sandbox: a command runs the repository's code, with everything this user can\n * reach. See \"Configured commands are trusted, not sandboxed\" in decisions.md.\n *\n * @throws {InvalidAgentConfigError} for an invalid configuration, a working directory outside\n * every `write` root, or on Windows, where a process group cannot be stopped.\n */\nexport async function createCommandTool({\n\tcommands,\n\tpolicy,\n\twrites,\n\tcovered,\n\tbase,\n\tplatform = process.platform,\n\trunProcess = runCommandProcess,\n}: {\n\tcommands: Commands;\n\tpolicy: AccessPolicy;\n\twrites: WorkspaceWrites;\n\tcovered: CoveredFiles;\n\t/** The real path of the first root, where a relative `cwd` starts. */\n\tbase: string;\n\tplatform?: NodeJS.Platform;\n\t/** Runs the program. Replaced in tests, to reach what a real process rarely does. */\n\trunProcess?: typeof runCommandProcess;\n}): Promise<PreparingTool> {\n\tif (platform === 'win32') {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t'Configured commands are not supported on Windows yet: the harness cannot stop a command and everything it started there',\n\t\t);\n\t}\n\tvalidateCommands(commands);\n\tconst resolved = new Map<string, ResolvedCommand>();\n\tfor (const [name, definition] of Object.entries(commands)) {\n\t\tresolved.set(name, { definition, cwd: await resolveCwd(name, definition, policy, base) });\n\t}\n\n\treturn definePreparingTool({\n\t\tname: 'runCommand',\n\t\tdescription: describeTool(commands),\n\t\tinput: inputSchema(commands),\n\t\tsummarize: ({ command: { name, ...values } }) => {\n\t\t\tconst definition = commands[name];\n\t\t\t// The schema only permits configured names, but keep this defensive for callers that\n\t\t\t// use a tool outside LLMAgent. This description never uses the resolved cwd.\n\t\t\treturn definition ? visibleCommand(definition, values) : name;\n\t\t},\n\t\tprepare: async ({ command: { name, ...values } }, signal, { run }) => {\n\t\t\tconst command = resolved.get(name);\n\t\t\t// The schema allows only the configured names.\n\t\t\tif (!command) throw new Error(`There is no command named \"${name}\"`);\n\t\t\tconst paramValues: Record<string, ParameterValue> = {};\n\t\t\tfor (const parameter of parametersOf(command.definition)) {\n\t\t\t\tconst value =\n\t\t\t\t\t(values as Record<string, ParameterValue | undefined>)[parameter.name] ?? null;\n\t\t\t\tparamValues[parameter.name] =\n\t\t\t\t\tparameter.type === 'paths' && Array.isArray(value)\n\t\t\t\t\t\t? await resolvePaths(value, command.cwd, policy, signal)\n\t\t\t\t\t\t: value;\n\t\t\t}\n\t\t\tconst line: CommandLine = Object.freeze({\n\t\t\t\texecutable: command.definition.executable,\n\t\t\t\targs: Object.freeze(buildArguments(command.definition, paramValues)) as string[],\n\t\t\t\tcwd: command.cwd,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\t// A command can change anything its user can: it is always asked about.\n\t\t\t\trisk: 'destructive',\n\t\t\t\tcommand: line,\n\t\t\t\trun: runSignal =>\n\t\t\t\t\trunCommand({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tline,\n\t\t\t\t\t\tdefinition: command.definition,\n\t\t\t\t\t\tcontext: run,\n\t\t\t\t\t\tsignal: runSignal,\n\t\t\t\t\t}),\n\t\t\t};\n\t\t},\n\t});\n\n\tasync function runCommand({\n\t\tname,\n\t\tline,\n\t\tdefinition,\n\t\tcontext,\n\t\tsignal,\n\t}: {\n\t\tname: string;\n\t\tline: CommandLine;\n\t\tdefinition: CommandDefinition;\n\t\tcontext: RunContext;\n\t\tsignal: AbortSignal;\n\t}): Promise<string> {\n\t\tsignal.throwIfAborted();\n\t\tconst session = writes.sessionFor(context);\n\t\tconst journal = await session.journalForChange();\n\t\tlet checkpoint: CommandCheckpoint;\n\t\ttry {\n\t\t\tcheckpoint = await CommandCheckpoint.begin({ journal, covered, by: name, signal });\n\t\t} catch (error) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`The harness could not capture the files before \"${name}\", so it did not run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tlet result: CommandProcessResult | undefined;\n\t\tlet startFailure: unknown;\n\t\ttry {\n\t\t\tresult = await runProcess(line.executable, line.args, {\n\t\t\t\tcwd: line.cwd,\n\t\t\t\tenv: environmentFor(definition),\n\t\t\t\ttimeoutMs: definition.timeoutMs ?? defaultCommandTimeoutMs,\n\t\t\t\toutputBytes: definition.outputBytes ?? defaultCommandOutputBytes,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tstartFailure = error;\n\t\t}\n\n\t\tconst complete = result?.stopped ?? true;\n\t\tlet observed: Awaited<ReturnType<CommandCheckpoint['end']>>;\n\t\ttry {\n\t\t\tobserved = await checkpoint.end({ complete });\n\t\t} catch (error) {\n\t\t\tsession.stop('Writing stopped: the harness could not record what a command changed');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`\"${name}\" ran, but the harness could not record what it changed; writing has stopped for this run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tif (!result) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(`\"${name}\" could not be started`, { cause: startFailure });\n\t\t}\n\t\tconst report = describeRun(name, definition, result, observed);\n\t\tif (!complete) {\n\t\t\tsession.stop('Writing stopped: a command could not be stopped');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`${report}\\n\"${name}\" could not be stopped: something it started may still be changing files, and the history may not hold all of it. Writing has stopped for this run.`,\n\t\t\t);\n\t\t}\n\t\t// After its changes are recorded: a cancelled command is never reported as finished.\n\t\tif (result.outcome === 'cancelled') signal.throwIfAborted();\n\t\tif (result.outcome !== 'exited') throw new ToolExecutionError(report);\n\t\tif (result.exitCode !== 0) {\n\t\t\tthrow new ToolExecutionError(report, {\n\t\t\t\t...(result.exitCode === null ? {} : { exitCode: result.exitCode }),\n\t\t\t});\n\t\t}\n\t\treturn report;\n\t}\n\n\tfunction describeRun(\n\t\tname: string,\n\t\tdefinition: CommandDefinition,\n\t\tresult: CommandProcessResult,\n\t\t{ changes, unrecorded }: Awaited<ReturnType<CommandCheckpoint['end']>>,\n\t): string {\n\t\tconst seconds = (result.durationMs / 1000).toFixed(1);\n\t\tconst lines: string[] = [];\n\t\tif (result.outcome === 'timedOut') {\n\t\t\tconst limit = (definition.timeoutMs ?? defaultCommandTimeoutMs) / 1000;\n\t\t\tlines.push(`\"${name}\" ran past its limit of ${String(limit)} s and was stopped.`);\n\t\t} else if (result.outcome === 'cancelled') {\n\t\t\tlines.push(`\"${name}\" was stopped: the run was cancelled.`);\n\t\t} else if (result.exitCode !== null) {\n\t\t\tlines.push(`\"${name}\" exited with code ${String(result.exitCode)} after ${seconds} s.`);\n\t\t} else {\n\t\t\tlines.push(`\"${name}\" was killed by ${result.signal ?? 'a signal'} after ${seconds} s.`);\n\t\t}\n\n\t\tconst shown = (path: string) => displayPath(base, path);\n\t\tlines.push(\n\t\t\tchanges.length === 0\n\t\t\t\t? 'It changed no file the history covers.'\n\t\t\t\t: `Files it changed, recorded in the history: ${changes.map(change => shown(change.path)).join(', ')}.`,\n\t\t);\n\t\tif (unrecorded.length > 0) {\n\t\t\tlines.push(\n\t\t\t\t`Not recorded, because the history cannot copy them (a folder, a link, or a file over 64 MB): ${unrecorded.map(shown).join(', ')}.`,\n\t\t\t);\n\t\t}\n\n\t\tconst limit = definition.outputBytes ?? defaultCommandOutputBytes;\n\t\tif (result.printedBytes === 0) {\n\t\t\tlines.push('It printed nothing.');\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\tresult.printedBytes > limit\n\t\t\t\t\t? `Output, the last ${kilobytes(limit)} of ${kilobytes(result.printedBytes)}:`\n\t\t\t\t\t: 'Output:',\n\t\t\t\tresult.output,\n\t\t\t);\n\t\t}\n\t\treturn lines.join('\\n');\n\t}\n}\n\nfunction kilobytes(bytes: number): string {\n\treturn `${String(Math.ceil(bytes / 1024))} KB`;\n}\n\n/** The command line the model can see, before paths are resolved against the host workspace. */\nfunction visibleCommand(\n\tdefinition: CommandDefinition,\n\tvalues: Record<string, ParameterValue | undefined>,\n): string {\n\tconst args: string[] = [];\n\tfor (const argument of definition.args ?? []) {\n\t\tif (typeof argument === 'string') {\n\t\t\targs.push(argument);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst value = values[argument.name] ?? null;\n\t\tif (argument.type === 'paths') {\n\t\t\tif (Array.isArray(value)) args.push(...value);\n\t\t} else if (value !== null && typeof value === 'string') {\n\t\t\targs.push(value);\n\t\t}\n\t}\n\n\treturn [definition.executable, ...args].join(' ');\n}\n\n/** Its working directory, as a real path inside a `write` root. */\nasync function resolveCwd(\n\tname: string,\n\tdefinition: CommandDefinition,\n\tpolicy: AccessPolicy,\n\tbase: string,\n): Promise<string> {\n\tconst named = resolve(base, definition.cwd ?? '.');\n\tlet real: string;\n\ttry {\n\t\treal = await realpath(named);\n\t\tawait policy.check(real, 'write', AbortSignal.timeout(cwdCheckMs));\n\t} catch (error) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`Command \"${name}\" needs a cwd inside a write root of the workspace`,\n\t\t\t{ cause: error },\n\t\t);\n\t}\n\treturn real;\n}\n\n/**\n * Each path, once the policy lets the agent read it, relative to where the command runs. One\n * that would read as a flag is given as `./` and its name.\n */\nasync function resolvePaths(\n\tpaths: string[],\n\tcwd: string,\n\tpolicy: AccessPolicy,\n\tsignal: AbortSignal,\n): Promise<string[]> {\n\tconst resolved: string[] = [];\n\tfor (const path of paths) {\n\t\tconst { absolute } = await policy.check(path, 'read', signal);\n\t\tconst fromCwd = relative(cwd, absolute) || '.';\n\t\tresolved.push(fromCwd.startsWith('-') && !isAbsolute(fromCwd) ? `./${fromCwd}` : fromCwd);\n\t}\n\treturn resolved;\n}\n\n/** A small environment of its own: the inherited few, then what the consumer set. */\nexport function environmentFor(\n\tdefinition: CommandDefinition,\n\tenv: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [variable, value] of Object.entries(env)) {\n\t\tif (value === undefined) continue;\n\t\tif (inheritedVariables.includes(variable) || variable.startsWith('LC_')) {\n\t\t\tinherited[variable] = value;\n\t\t}\n\t}\n\treturn { ...inherited, ...definition.env };\n}\n\nfunction describeTool(commands: Commands): string {\n\tconst list = Object.entries(commands)\n\t\t.map(([name, { description }]) => `- ${name}: ${description}`)\n\t\t.join('\\n');\n\treturn [\n\t\t\"Runs one of the project's configured commands, by name, such as its tests or its formatter.\",\n\t\t'Files it changes are recorded in the history, so they can be undone. Its output is cut to its end.',\n\t\t'Commands:',\n\t\tlist,\n\t].join('\\n');\n}\n\n/**\n * One object per command, told apart by `name`, each with the parameters that command takes.\n * Strict mode needs every key present, so a parameter that may be left out takes `null`.\n */\nfunction inputSchema(commands: Commands) {\n\tconst variants = Object.entries(commands).map(([name, definition]) =>\n\t\tz.object({\n\t\t\tname: z.enum([name]),\n\t\t\t...Object.fromEntries(\n\t\t\t\tparametersOf(definition).map(parameter => [parameter.name, parameterSchema(parameter)]),\n\t\t\t),\n\t\t}),\n\t);\n\tconst [first, ...rest] = variants;\n\t// `validateCommands` refuses an empty configuration.\n\tif (!first) throw new InvalidAgentConfigError('No command is configured');\n\treturn z.object({\n\t\tcommand: (rest.length === 0 ? first : z.union([first, ...rest])).describe(\n\t\t\t'The command to run, and its parameters.',\n\t\t),\n\t});\n}\n\nfunction parameterSchema(parameter: CommandParameter): z.ZodType {\n\tswitch (parameter.type) {\n\t\tcase 'paths':\n\t\t\treturn z\n\t\t\t\t.array(z.string())\n\t\t\t\t.describe(`${parameter.description} Paths inside the workspace; [] for none.`);\n\t\tcase 'choice': {\n\t\t\tconst values = z.enum(parameter.values as [string, ...string[]]);\n\t\t\treturn parameter.required === true\n\t\t\t\t? values.describe(parameter.description)\n\t\t\t\t: values.nullable().describe(`${parameter.description} null to leave it out.`);\n\t\t}\n\t\tcase 'text': {\n\t\t\tconst text = z.string();\n\t\t\tconst description = `${parameter.description} Must match the regular expression ${parameter.pattern}, and not start with \"-\".`;\n\t\t\treturn parameter.required === true\n\t\t\t\t? text.describe(description)\n\t\t\t\t: text.nullable().describe(`${description} null to leave it out.`);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import type { ToolRisk } from '#src/agent/domain/approval';
|
|
3
3
|
import type { PreparedCall, PreparingTool } from '../domain/preparedCall.ts';
|
|
4
4
|
import type { ToolCallContext } from '../domain/readRegistry.ts';
|
|
5
|
-
import type
|
|
5
|
+
import { type Tool } from '../domain/tool.ts';
|
|
6
6
|
/**
|
|
7
7
|
* Builds a `Tool` from one Zod schema, which gives the model its JSON Schema, validates what the
|
|
8
8
|
* model sends, and types the input `execute` receives. Invalid input is rejected with a message
|
|
@@ -14,11 +14,12 @@ import type { Tool } from '../domain/tool.ts';
|
|
|
14
14
|
*
|
|
15
15
|
* @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.
|
|
16
16
|
*/
|
|
17
|
-
export declare function defineTool<Schema extends z.ZodObject>({ name, description, input, risk, execute, }: {
|
|
17
|
+
export declare function defineTool<Schema extends z.ZodObject>({ name, description, input, risk, summarize, execute, }: {
|
|
18
18
|
name: string;
|
|
19
19
|
description: string;
|
|
20
20
|
input: Schema;
|
|
21
21
|
risk: ToolRisk | ((input: z.infer<Schema>) => ToolRisk);
|
|
22
|
+
summarize?: (input: z.infer<Schema>) => string;
|
|
22
23
|
execute: (input: z.infer<Schema>, signal: AbortSignal) => Promise<string>;
|
|
23
24
|
}): Tool;
|
|
24
25
|
/**
|
|
@@ -27,9 +28,10 @@ export declare function defineTool<Schema extends z.ZodObject>({ name, descripti
|
|
|
27
28
|
*
|
|
28
29
|
* @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.
|
|
29
30
|
*/
|
|
30
|
-
export declare function definePreparingTool<Schema extends z.ZodObject>({ name, description, input, prepare, }: {
|
|
31
|
+
export declare function definePreparingTool<Schema extends z.ZodObject>({ name, description, input, summarize, prepare, }: {
|
|
31
32
|
name: string;
|
|
32
33
|
description: string;
|
|
33
34
|
input: Schema;
|
|
35
|
+
summarize?: (input: z.infer<Schema>) => string;
|
|
34
36
|
prepare: (input: z.infer<Schema>, signal: AbortSignal, call: ToolCallContext) => Promise<PreparedCall>;
|
|
35
37
|
}): PreparingTool;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { InvalidAgentConfigError } from '#src/shared/domain/errors';
|
|
3
|
+
import {} from "../domain/tool.js";
|
|
4
|
+
import { validatedSummarizer } from "./validatedSummarizer.js";
|
|
3
5
|
// Both providers take tools in strict mode, which supports only part of JSON Schema. Claude
|
|
4
6
|
// rejects these bounds; a tool that needs one checks it in its own code instead.
|
|
5
7
|
const unsupportedKeywords = [
|
|
@@ -70,11 +72,16 @@ function assertStrict(schema, toolName) {
|
|
|
70
72
|
*
|
|
71
73
|
* @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.
|
|
72
74
|
*/
|
|
73
|
-
export function defineTool({ name, description, input, risk, execute, }) {
|
|
75
|
+
export function defineTool({ name, description, input, risk, summarize, execute, }) {
|
|
74
76
|
return {
|
|
75
77
|
name,
|
|
76
78
|
description,
|
|
77
79
|
inputSchema: strictSchema(input, name),
|
|
80
|
+
...(summarize
|
|
81
|
+
? {
|
|
82
|
+
summarize: validatedSummarizer(input, summarize),
|
|
83
|
+
}
|
|
84
|
+
: {}),
|
|
78
85
|
risk: raw => {
|
|
79
86
|
if (typeof risk === 'string')
|
|
80
87
|
return risk;
|
|
@@ -104,11 +111,16 @@ function strictSchema(input, toolName) {
|
|
|
104
111
|
*
|
|
105
112
|
* @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.
|
|
106
113
|
*/
|
|
107
|
-
export function definePreparingTool({ name, description, input, prepare, }) {
|
|
114
|
+
export function definePreparingTool({ name, description, input, summarize, prepare, }) {
|
|
108
115
|
return {
|
|
109
116
|
name,
|
|
110
117
|
description,
|
|
111
118
|
inputSchema: strictSchema(input, name),
|
|
119
|
+
...(summarize
|
|
120
|
+
? {
|
|
121
|
+
summarize: validatedSummarizer(input, summarize),
|
|
122
|
+
}
|
|
123
|
+
: {}),
|
|
112
124
|
prepare: (raw, signal, call) => {
|
|
113
125
|
const parsed = input.safeParse(raw);
|
|
114
126
|
if (!parsed.success)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/defineTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport type { ToolRisk } from '#src/agent/domain/approval';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport type { JSONSchema } from '#src/llm/domain/tool';\nimport type { PreparedCall, PreparingTool } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport type { Tool } from '../domain/tool.ts';\n\n// Both providers take tools in strict mode, which supports only part of JSON Schema. Claude\n// rejects these bounds; a tool that needs one checks it in its own code instead.\nconst unsupportedKeywords = [\n\t'minimum',\n\t'maximum',\n\t'exclusiveMinimum',\n\t'exclusiveMaximum',\n\t'multipleOf',\n\t'minLength',\n\t'maxLength',\n];\n\n// Keywords whose value is one schema, or a list or map of them. Anything else, such as the\n// keys of `properties`, is a name or a value, never a schema to check.\nconst schemaKeywords = ['items', 'not', 'additionalProperties'];\nconst schemaListKeywords = ['anyOf', 'oneOf', 'allOf', 'prefixItems'];\nconst schemaMapKeywords = ['properties', '$defs', 'definitions'];\n\nfunction isSchema(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Walks the whole schema, nested objects included, and visits only schemas: a property named\n * `maxLength` is a name, not the keyword. A schema the providers reject would fail every\n * request with a 400, so it fails here, when the tool is defined.\n */\nfunction assertStrict(schema: unknown, toolName: string): void {\n\tif (!isSchema(schema)) {\n\t\treturn;\n\t}\n\n\tconst keyword = unsupportedKeywords.find(candidate => candidate in schema);\n\tif (keyword) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`Tool \"${toolName}\" uses \"${keyword}\", which strict mode does not support; check it in the tool's code`,\n\t\t);\n\t}\n\n\tif (schema.type === 'object') {\n\t\t// A record or a loose object takes keys strict mode cannot know in advance.\n\t\tif (schema.additionalProperties !== false) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`Tool \"${toolName}\" has an object open to any key; strict mode needs every key declared`,\n\t\t\t);\n\t\t}\n\n\t\tconst required = (schema.required ?? []) as string[];\n\t\t// OpenAI requires every property: an optional one is a union with `null`.\n\t\tconst optional = Object.keys(schema.properties ?? {}).filter(\n\t\t\tproperty => !required.includes(property),\n\t\t);\n\t\tif (optional.length > 0) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`Tool \"${toolName}\" has optional fields (${optional.join(', ')}); make them .nullable() instead`,\n\t\t\t);\n\t\t}\n\t}\n\n\tfor (const key of schemaKeywords) assertStrict(schema[key], toolName);\n\tfor (const key of schemaListKeywords) {\n\t\tconst list = schema[key];\n\t\tif (Array.isArray(list)) for (const item of list) assertStrict(item, toolName);\n\t}\n\tfor (const key of schemaMapKeywords) {\n\t\tconst map = schema[key];\n\t\tif (isSchema(map)) for (const item of Object.values(map)) assertStrict(item, toolName);\n\t}\n}\n\n/**\n * Builds a `Tool` from one Zod schema, which gives the model its JSON Schema, validates what the\n * model sends, and types the input `execute` receives. Invalid input is rejected with a message\n * naming what is wrong, which the agent hands back to the model as an error result.\n *\n * `risk` is one level for every call, or a function that judges each call from its validated\n * input, such as `destructive` only when a file already exists. Input that fails validation\n * counts as `safe`, because `execute` rejects it before doing anything.\n *\n * @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.\n */\nexport function defineTool<Schema extends z.ZodObject>({\n\tname,\n\tdescription,\n\tinput,\n\trisk,\n\texecute,\n}: {\n\tname: string;\n\tdescription: string;\n\tinput: Schema;\n\trisk: ToolRisk | ((input: z.infer<Schema>) => ToolRisk);\n\texecute: (input: z.infer<Schema>, signal: AbortSignal) => Promise<string>;\n}): Tool {\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tinputSchema: strictSchema(input, name),\n\t\trisk: raw => {\n\t\t\tif (typeof risk === 'string') return risk;\n\t\t\tconst parsed = input.safeParse(raw);\n\t\t\treturn parsed.success ? risk(parsed.data) : 'safe';\n\t\t},\n\t\texecute: (raw, signal) => {\n\t\t\tconst parsed = input.safeParse(raw);\n\t\t\tif (!parsed.success) {\n\t\t\t\treturn Promise.reject(new Error(z.prettifyError(parsed.error)));\n\t\t\t}\n\n\t\t\treturn execute(parsed.data, signal);\n\t\t},\n\t};\n}\n\n/** The JSON schema of `input`, checked against what strict mode supports. */\nfunction strictSchema(input: z.ZodObject, toolName: string): JSONSchema {\n\tconst inputSchema: Record<string, unknown> = { ...z.toJSONSchema(input) };\n\t// The dialect marker is noise to both APIs.\n\tdelete inputSchema.$schema;\n\tassertStrict(inputSchema, toolName);\n\treturn inputSchema as JSONSchema;\n}\n\n/**\n * Builds a harness tool that prepares each call, from a Zod schema, as `defineTool` builds a\n * consumer's: input that fails validation is refused before anything is prepared. Internal.\n *\n * @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.\n */\nexport function definePreparingTool<Schema extends z.ZodObject>({\n\tname,\n\tdescription,\n\tinput,\n\tprepare,\n}: {\n\tname: string;\n\tdescription: string;\n\tinput: Schema;\n\tprepare: (\n\t\tinput: z.infer<Schema>,\n\t\tsignal: AbortSignal,\n\t\tcall: ToolCallContext,\n\t) => Promise<PreparedCall>;\n}): PreparingTool {\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tinputSchema: strictSchema(input, name),\n\t\tprepare: (raw, signal, call) => {\n\t\t\tconst parsed = input.safeParse(raw);\n\t\t\tif (!parsed.success) return Promise.reject(new Error(z.prettifyError(parsed.error)));\n\t\t\treturn prepare(parsed.data, signal, call);\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"defineTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/defineTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport type { ToolRisk } from '#src/agent/domain/approval';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport type { JSONSchema } from '#src/llm/domain/tool';\nimport type { PreparedCall, PreparingTool } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { type Tool } from '../domain/tool.ts';\nimport { validatedSummarizer } from './validatedSummarizer.ts';\n\n// Both providers take tools in strict mode, which supports only part of JSON Schema. Claude\n// rejects these bounds; a tool that needs one checks it in its own code instead.\nconst unsupportedKeywords = [\n\t'minimum',\n\t'maximum',\n\t'exclusiveMinimum',\n\t'exclusiveMaximum',\n\t'multipleOf',\n\t'minLength',\n\t'maxLength',\n];\n\n// Keywords whose value is one schema, or a list or map of them. Anything else, such as the\n// keys of `properties`, is a name or a value, never a schema to check.\nconst schemaKeywords = ['items', 'not', 'additionalProperties'];\nconst schemaListKeywords = ['anyOf', 'oneOf', 'allOf', 'prefixItems'];\nconst schemaMapKeywords = ['properties', '$defs', 'definitions'];\n\nfunction isSchema(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Walks the whole schema, nested objects included, and visits only schemas: a property named\n * `maxLength` is a name, not the keyword. A schema the providers reject would fail every\n * request with a 400, so it fails here, when the tool is defined.\n */\nfunction assertStrict(schema: unknown, toolName: string): void {\n\tif (!isSchema(schema)) {\n\t\treturn;\n\t}\n\n\tconst keyword = unsupportedKeywords.find(candidate => candidate in schema);\n\tif (keyword) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`Tool \"${toolName}\" uses \"${keyword}\", which strict mode does not support; check it in the tool's code`,\n\t\t);\n\t}\n\n\tif (schema.type === 'object') {\n\t\t// A record or a loose object takes keys strict mode cannot know in advance.\n\t\tif (schema.additionalProperties !== false) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`Tool \"${toolName}\" has an object open to any key; strict mode needs every key declared`,\n\t\t\t);\n\t\t}\n\n\t\tconst required = (schema.required ?? []) as string[];\n\t\t// OpenAI requires every property: an optional one is a union with `null`.\n\t\tconst optional = Object.keys(schema.properties ?? {}).filter(\n\t\t\tproperty => !required.includes(property),\n\t\t);\n\t\tif (optional.length > 0) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`Tool \"${toolName}\" has optional fields (${optional.join(', ')}); make them .nullable() instead`,\n\t\t\t);\n\t\t}\n\t}\n\n\tfor (const key of schemaKeywords) assertStrict(schema[key], toolName);\n\tfor (const key of schemaListKeywords) {\n\t\tconst list = schema[key];\n\t\tif (Array.isArray(list)) for (const item of list) assertStrict(item, toolName);\n\t}\n\tfor (const key of schemaMapKeywords) {\n\t\tconst map = schema[key];\n\t\tif (isSchema(map)) for (const item of Object.values(map)) assertStrict(item, toolName);\n\t}\n}\n\n/**\n * Builds a `Tool` from one Zod schema, which gives the model its JSON Schema, validates what the\n * model sends, and types the input `execute` receives. Invalid input is rejected with a message\n * naming what is wrong, which the agent hands back to the model as an error result.\n *\n * `risk` is one level for every call, or a function that judges each call from its validated\n * input, such as `destructive` only when a file already exists. Input that fails validation\n * counts as `safe`, because `execute` rejects it before doing anything.\n *\n * @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.\n */\nexport function defineTool<Schema extends z.ZodObject>({\n\tname,\n\tdescription,\n\tinput,\n\trisk,\n\tsummarize,\n\texecute,\n}: {\n\tname: string;\n\tdescription: string;\n\tinput: Schema;\n\trisk: ToolRisk | ((input: z.infer<Schema>) => ToolRisk);\n\tsummarize?: (input: z.infer<Schema>) => string;\n\texecute: (input: z.infer<Schema>, signal: AbortSignal) => Promise<string>;\n}): Tool {\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tinputSchema: strictSchema(input, name),\n\t\t...(summarize\n\t\t\t? {\n\t\t\t\t\tsummarize: validatedSummarizer(input, summarize),\n\t\t\t\t}\n\t\t\t: {}),\n\t\trisk: raw => {\n\t\t\tif (typeof risk === 'string') return risk;\n\t\t\tconst parsed = input.safeParse(raw);\n\t\t\treturn parsed.success ? risk(parsed.data) : 'safe';\n\t\t},\n\t\texecute: (raw, signal) => {\n\t\t\tconst parsed = input.safeParse(raw);\n\t\t\tif (!parsed.success) {\n\t\t\t\treturn Promise.reject(new Error(z.prettifyError(parsed.error)));\n\t\t\t}\n\n\t\t\treturn execute(parsed.data, signal);\n\t\t},\n\t};\n}\n\n/** The JSON schema of `input`, checked against what strict mode supports. */\nfunction strictSchema(input: z.ZodObject, toolName: string): JSONSchema {\n\tconst inputSchema: Record<string, unknown> = { ...z.toJSONSchema(input) };\n\t// The dialect marker is noise to both APIs.\n\tdelete inputSchema.$schema;\n\tassertStrict(inputSchema, toolName);\n\treturn inputSchema as JSONSchema;\n}\n\n/**\n * Builds a harness tool that prepares each call, from a Zod schema, as `defineTool` builds a\n * consumer's: input that fails validation is refused before anything is prepared. Internal.\n *\n * @throws {InvalidAgentConfigError} when the schema falls outside what strict mode supports.\n */\nexport function definePreparingTool<Schema extends z.ZodObject>({\n\tname,\n\tdescription,\n\tinput,\n\tsummarize,\n\tprepare,\n}: {\n\tname: string;\n\tdescription: string;\n\tinput: Schema;\n\tsummarize?: (input: z.infer<Schema>) => string;\n\tprepare: (\n\t\tinput: z.infer<Schema>,\n\t\tsignal: AbortSignal,\n\t\tcall: ToolCallContext,\n\t) => Promise<PreparedCall>;\n}): PreparingTool {\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tinputSchema: strictSchema(input, name),\n\t\t...(summarize\n\t\t\t? {\n\t\t\t\t\tsummarize: validatedSummarizer(input, summarize),\n\t\t\t\t}\n\t\t\t: {}),\n\t\tprepare: (raw, signal, call) => {\n\t\t\tconst parsed = input.safeParse(raw);\n\t\t\tif (!parsed.success) return Promise.reject(new Error(z.prettifyError(parsed.error)));\n\t\t\treturn prepare(parsed.data, signal, call);\n\t\t},\n\t};\n}\n"]}
|
|
@@ -7,6 +7,7 @@ import { applyUpdate, createdContent } from "../domain/v4aPatch.js";
|
|
|
7
7
|
import { definePreparingTool } from "./defineTool.js";
|
|
8
8
|
import { prepareTrackedEdit } from "./trackedEdits.js";
|
|
9
9
|
import { readTracked } from "./trackedReadFile.js";
|
|
10
|
+
import { validatedSummarizer } from "./validatedSummarizer.js";
|
|
10
11
|
import { readRange } from "./workspaceTools.js";
|
|
11
12
|
// A folder the editor views is listed, as its reference implementation does, up to this many.
|
|
12
13
|
const maxListedFiles = 200;
|
|
@@ -14,11 +15,16 @@ const maxListedFiles = 200;
|
|
|
14
15
|
* A native tool: its provider gives the model the schema, so the one here only says what the
|
|
15
16
|
* calls hold, and each call is checked against `input` before anything is prepared.
|
|
16
17
|
*/
|
|
17
|
-
function nativeTool({ name, description, native, input, prepare, }) {
|
|
18
|
+
function nativeTool({ name, description, native, input, summarize, prepare, }) {
|
|
18
19
|
return asNative({
|
|
19
20
|
name,
|
|
20
21
|
description,
|
|
21
22
|
inputSchema: { type: 'object' },
|
|
23
|
+
...(summarize
|
|
24
|
+
? {
|
|
25
|
+
summarize: validatedSummarizer(input, summarize),
|
|
26
|
+
}
|
|
27
|
+
: {}),
|
|
22
28
|
prepare: (raw, signal, call) => {
|
|
23
29
|
const parsed = input.safeParse(raw);
|
|
24
30
|
if (!parsed.success)
|
|
@@ -42,6 +48,7 @@ export function createApplyPatchTool(writes) {
|
|
|
42
48
|
description: 'Creates, changes or deletes a file with a V4A patch.',
|
|
43
49
|
native: 'applyPatch',
|
|
44
50
|
input: patchOperation,
|
|
51
|
+
summarize: operation => `${operation.type.replace('_file', '').replace('_', ' ')} ${operation.path}`,
|
|
45
52
|
prepare: (operation, signal, call) => {
|
|
46
53
|
const { path } = operation;
|
|
47
54
|
switch (operation.type) {
|
|
@@ -157,6 +164,7 @@ export function createTextEditorTool({ writes, policy, workspace, }) {
|
|
|
157
164
|
description: 'Views, creates and changes files.',
|
|
158
165
|
native: 'textEditor',
|
|
159
166
|
input: editorCommand,
|
|
167
|
+
summarize: editSummary,
|
|
160
168
|
prepare: async (command, signal, call) => {
|
|
161
169
|
const { path } = command;
|
|
162
170
|
switch (command.command) {
|
|
@@ -205,7 +213,24 @@ export function createDeleteFileTool(writes) {
|
|
|
205
213
|
name: 'delete_file',
|
|
206
214
|
description: 'Deletes a file. Read it first.',
|
|
207
215
|
input: z.object({ path: z.string().describe('The file, relative to the root.') }),
|
|
216
|
+
summarize: ({ path }) => `delete ${path}`,
|
|
208
217
|
prepare: ({ path }, signal, call) => prepareTrackedEdit(writes, { kind: 'delete', path }, signal, call),
|
|
209
218
|
});
|
|
210
219
|
}
|
|
220
|
+
function editSummary(command) {
|
|
221
|
+
switch (command.command) {
|
|
222
|
+
case 'view': {
|
|
223
|
+
const range = command.view_range
|
|
224
|
+
? `${String(command.view_range[0])}-${command.view_range[1] === -1 ? 'end' : String(command.view_range[1])}`
|
|
225
|
+
: undefined;
|
|
226
|
+
return `view ${command.path}${range ? `:${range}` : ''}`;
|
|
227
|
+
}
|
|
228
|
+
case 'str_replace':
|
|
229
|
+
return `replace ${command.path}`;
|
|
230
|
+
case 'create':
|
|
231
|
+
return `create ${command.path}`;
|
|
232
|
+
case 'insert':
|
|
233
|
+
return `insert ${command.path} after ${String(command.insert_line)}`;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
211
236
|
//# sourceMappingURL=editFormats.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editFormats.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/editFormats.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat } from 'node:fs/promises';\nimport { z } from 'zod';\nimport { asNative, type NativeTool } from '#src/llm/domain/tool';\nimport type { AccessPolicy } from '../domain/accessPolicy.ts';\nimport { EditRefusedError } from '../domain/fileEdits.ts';\nimport type { PreparedCall, PreparingTool } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { addedLine, joinLines, type Line, splitLines, textsOf } from '../domain/textLines.ts';\nimport { applyUpdate, createdContent } from '../domain/v4aPatch.ts';\nimport type { Workspace } from '../domain/workspace.ts';\nimport { definePreparingTool } from './defineTool.ts';\nimport { prepareTrackedEdit } from './trackedEdits.ts';\nimport { readTracked } from './trackedReadFile.ts';\nimport { readRange } from './workspaceTools.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\n\n// A folder the editor views is listed, as its reference implementation does, up to this many.\nconst maxListedFiles = 200;\n\n/**\n * A native tool: its provider gives the model the schema, so the one here only says what the\n * calls hold, and each call is checked against `input` before anything is prepared.\n */\nfunction nativeTool<Schema extends z.ZodType>({\n\tname,\n\tdescription,\n\tnative,\n\tinput,\n\tprepare,\n}: {\n\tname: string;\n\tdescription: string;\n\tnative: NativeTool;\n\tinput: Schema;\n\tprepare: (\n\t\tinput: z.infer<Schema>,\n\t\tsignal: AbortSignal,\n\t\tcall: ToolCallContext,\n\t) => Promise<PreparedCall>;\n}): PreparingTool {\n\treturn asNative(\n\t\t{\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tinputSchema: { type: 'object' },\n\t\t\tprepare: (raw: unknown, signal: AbortSignal, call: ToolCallContext) => {\n\t\t\t\tconst parsed = input.safeParse(raw);\n\t\t\t\tif (!parsed.success) return Promise.reject(new Error(z.prettifyError(parsed.error)));\n\t\t\t\treturn prepare(parsed.data, signal, call);\n\t\t\t},\n\t\t},\n\t\tnative,\n\t);\n}\n\nconst patchOperation = z.discriminatedUnion('type', [\n\tz.object({ type: z.literal('create_file'), path: z.string(), diff: z.string() }),\n\tz.object({ type: z.literal('update_file'), path: z.string(), diff: z.string() }),\n\tz.object({ type: z.literal('delete_file'), path: z.string() }),\n]);\n\n/**\n * OpenAI's native `apply_patch`: each call is one operation on one file, `create_file` and\n * `update_file` with a V4A diff. It cannot read, so the agent also has `readFile`.\n */\nexport function createApplyPatchTool(writes: WorkspaceWrites): PreparingTool {\n\treturn nativeTool({\n\t\tname: 'apply_patch',\n\t\tdescription: 'Creates, changes or deletes a file with a V4A patch.',\n\t\tnative: 'applyPatch',\n\t\tinput: patchOperation,\n\t\tprepare: (operation, signal, call) => {\n\t\t\tconst { path } = operation;\n\t\t\tswitch (operation.type) {\n\t\t\t\tcase 'create_file':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{ kind: 'create', path, content: Buffer.from(createdContent(operation.diff, path)) },\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'update_file':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: 'modify',\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tchange: current =>\n\t\t\t\t\t\t\t\tBuffer.from(applyUpdate(current.toString('utf8'), operation.diff, path)),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'delete_file':\n\t\t\t\t\treturn prepareTrackedEdit(writes, { kind: 'delete', path }, signal, call);\n\t\t\t}\n\t\t},\n\t});\n}\n\nconst editorCommand = z.discriminatedUnion('command', [\n\tz.object({\n\t\tcommand: z.literal('view'),\n\t\tpath: z.string(),\n\t\tview_range: z.tuple([z.number().int(), z.number().int()]).optional(),\n\t}),\n\tz.object({\n\t\tcommand: z.literal('str_replace'),\n\t\tpath: z.string(),\n\t\told_str: z.string(),\n\t\tnew_str: z.string().optional(),\n\t}),\n\tz.object({ command: z.literal('create'), path: z.string(), file_text: z.string() }),\n\tz.object({\n\t\tcommand: z.literal('insert'),\n\t\tpath: z.string(),\n\t\tinsert_line: z.number().int(),\n\t\tinsert_text: z.string(),\n\t}),\n]);\n\n/** How many times `text` appears in `source`, without overlaps. */\nfunction occurrences(source: string, text: string): number {\n\treturn source.split(text).length - 1;\n}\n\n/**\n * Replaces the one place `oldText` appears. The model writes `\\n`: when the text as sent is not\n * in the file, it is tried with CRLF too, so a CRLF file can be edited, and written with CRLF.\n */\nfunction replaceOnce(source: string, oldText: string, newText: string, path: string): string {\n\tif (oldText === '') {\n\t\tthrow new EditRefusedError(`old_str is empty; copy the text to replace from \"${path}\"`);\n\t}\n\tlet [from, to] = [oldText, newText];\n\tlet count = occurrences(source, from);\n\tif (count === 0 && !oldText.includes('\\r\\n') && oldText.includes('\\n')) {\n\t\t[from, to] = [oldText.replaceAll('\\n', '\\r\\n'), newText.replaceAll('\\n', '\\r\\n')];\n\t\tcount = occurrences(source, from);\n\t}\n\tif (count === 0) {\n\t\tthrow new EditRefusedError(\n\t\t\t`old_str is not in \"${path}\"; view the file and copy the text exactly, whitespace included`,\n\t\t);\n\t}\n\tif (count > 1) {\n\t\tthrow new EditRefusedError(\n\t\t\t`old_str appears ${String(count)} times in \"${path}\"; include more of the lines around it so it appears once`,\n\t\t);\n\t}\n\tconst at = source.indexOf(from);\n\t// New lines end like the line where the text sits, so a CRLF line gains no bare `\\n`.\n\t// When the text ends with its line's newline, that newline says how the line ends.\n\tconst ending = endingAt(source, from.endsWith('\\n') ? at + from.length - 1 : at + from.length);\n\tconst written = to.replace(/(?<!\\r)\\n/g, ending);\n\treturn source.slice(0, at) + written + source.slice(at + from.length);\n}\n\n/** The ending of the line holding `index`, or the text's usual one for a last line without. */\nfunction endingAt(source: string, index: number): '\\n' | '\\r\\n' {\n\tconst newline = source.indexOf('\\n', index);\n\tif (newline === -1) return splitLines(source).eol;\n\treturn source[newline - 1] === '\\r' ? '\\r\\n' : '\\n';\n}\n\n/**\n * Puts `text` after line `after` of `source`, 0 being before the first. The lines inserted end\n * like the line before them, and every other line is written back as it was.\n */\nfunction insertAfter(source: string, after: number, text: string, path: string): string {\n\tconst file = splitLines(source);\n\tif (after < 0 || after > file.lines.length) {\n\t\tthrow new EditRefusedError(\n\t\t\t`insert_line must be between 0 and ${String(file.lines.length)}, the number of lines in \"${path}\"`,\n\t\t);\n\t}\n\tconst texts = textsOf(splitLines(text).lines);\n\tconst inserted: Line[] = [];\n\tfor (const line of texts.length > 0 ? texts : ['']) {\n\t\tinserted.push(addedLine(line, inserted.at(-1) ?? file.lines[after - 1], file.eol));\n\t}\n\tconst lines = [...file.lines];\n\tlines.splice(after, 0, ...inserted);\n\treturn joinLines({ ...file, lines, finalNewline: file.finalNewline || file.lines.length === 0 });\n}\n\n/** The files under a folder, as the editor shows a folder it views. */\nasync function listFolder(\n\tworkspace: Workspace,\n\tpath: string,\n\tsignal: AbortSignal,\n): Promise<string> {\n\tconst { files, total, truncated } = await workspace.listFiles(\n\t\t{ path, limit: maxListedFiles },\n\t\tsignal,\n\t);\n\tif (files.length === 0) return 'No files.';\n\tconst notice = truncated\n\t\t? `\\n[Showing ${String(files.length)} of ${String(total)} files. View a folder inside it to see the rest.]`\n\t\t: '';\n\treturn files.join('\\n') + notice;\n}\n\n/**\n * Claude's native text editor, `str_replace_based_edit_tool`. `view` reads through the access\n * policy and counts as a read, so it replaces `readFile`; on a folder it lists the files in it.\n * `create` makes only a file that does not exist, and the editor has no command to delete:\n * Claude gets `delete_file` for that.\n */\nexport function createTextEditorTool({\n\twrites,\n\tpolicy,\n\tworkspace,\n}: {\n\twrites: WorkspaceWrites;\n\tpolicy: AccessPolicy;\n\tworkspace: Workspace;\n}): PreparingTool {\n\treturn nativeTool({\n\t\tname: 'str_replace_based_edit_tool',\n\t\tdescription: 'Views, creates and changes files.',\n\t\tnative: 'textEditor',\n\t\tinput: editorCommand,\n\t\tprepare: async (command, signal, call) => {\n\t\t\tconst { path } = command;\n\t\t\tswitch (command.command) {\n\t\t\t\tcase 'view': {\n\t\t\t\t\tconst [start, end] = command.view_range ?? [1, -1];\n\t\t\t\t\tif (start < 1) {\n\t\t\t\t\t\tthrow new Error(`view_range starts at line ${String(start)}; lines count from 1`);\n\t\t\t\t\t}\n\t\t\t\t\tif (end !== -1 && end < start) {\n\t\t\t\t\t\tthrow new Error(`view_range ends before it starts: [${String(start)}, ${String(end)}]`);\n\t\t\t\t\t}\n\t\t\t\t\tconst { from, count } = readRange({\n\t\t\t\t\t\tfromLine: start,\n\t\t\t\t\t\tlineCount: end === -1 ? null : end - start + 1,\n\t\t\t\t\t});\n\t\t\t\t\tconst target = await policy.check(path, 'read', signal);\n\t\t\t\t\tconst isFolder = await lstat(target.absolute).then(\n\t\t\t\t\t\tstats => stats.isDirectory(),\n\t\t\t\t\t\t() => false,\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\trisk: 'safe',\n\t\t\t\t\t\trun: runSignal =>\n\t\t\t\t\t\t\tisFolder\n\t\t\t\t\t\t\t\t? listFolder(workspace, path, runSignal)\n\t\t\t\t\t\t\t\t: readTracked({ target, path, from, count, reads: call.reads, signal: runSignal }),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tcase 'str_replace':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: 'modify',\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tchange: current =>\n\t\t\t\t\t\t\t\tBuffer.from(\n\t\t\t\t\t\t\t\t\treplaceOnce(\n\t\t\t\t\t\t\t\t\t\tcurrent.toString('utf8'),\n\t\t\t\t\t\t\t\t\t\tcommand.old_str,\n\t\t\t\t\t\t\t\t\t\tcommand.new_str ?? '',\n\t\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'create':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{ kind: 'create', path, content: Buffer.from(command.file_text) },\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'insert':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: 'modify',\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tchange: current =>\n\t\t\t\t\t\t\t\tBuffer.from(\n\t\t\t\t\t\t\t\t\tinsertAfter(\n\t\t\t\t\t\t\t\t\t\tcurrent.toString('utf8'),\n\t\t\t\t\t\t\t\t\t\tcommand.insert_line,\n\t\t\t\t\t\t\t\t\t\tcommand.insert_text,\n\t\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/** The delete Claude's text editor has no command for. */\nexport function createDeleteFileTool(writes: WorkspaceWrites): PreparingTool {\n\treturn definePreparingTool({\n\t\tname: 'delete_file',\n\t\tdescription: 'Deletes a file. Read it first.',\n\t\tinput: z.object({ path: z.string().describe('The file, relative to the root.') }),\n\t\tprepare: ({ path }, signal, call) =>\n\t\t\tprepareTrackedEdit(writes, { kind: 'delete', path }, signal, call),\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"editFormats.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/editFormats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAmB,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAa,UAAU,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,8FAA8F;AAC9F,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B;;;GAGG;AACH,SAAS,UAAU,CAA2B,EAC7C,IAAI,EACJ,WAAW,EACX,MAAM,EACN,KAAK,EACL,SAAS,EACT,OAAO,GAYP;IACA,OAAO,QAAQ,CACd;QACC,IAAI;QACJ,WAAW;QACX,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC/B,GAAG,CAAC,SAAS;YACZ,CAAC,CAAC;gBACA,SAAS,EAAE,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC;aAChD;YACF,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,EAAE,CAAC,GAAY,EAAE,MAAmB,EAAE,IAAqB,EAAE,EAAE;YACrE,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrF,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC;KACD,EACD,MAAM,CACN,CAAC;AACH,CAAC;AAED,MAAM,cAAc,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IACnD,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAChF,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAChF,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;CAC9D,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC3D,OAAO,UAAU,CAAC;QACjB,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,sDAAsD;QACnE,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,cAAc;QACrB,SAAS,EAAE,SAAS,CAAC,EAAE,CACtB,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE;QAC7E,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;YACpC,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;YAC3B,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;gBACxB,KAAK,aAAa;oBACjB,OAAO,kBAAkB,CACxB,MAAM,EACN,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,EACpF,MAAM,EACN,IAAI,CACJ,CAAC;gBACH,KAAK,aAAa;oBACjB,OAAO,kBAAkB,CACxB,MAAM,EACN;wBACC,IAAI,EAAE,QAAQ;wBACd,IAAI;wBACJ,MAAM,EAAE,OAAO,CAAC,EAAE,CACjB,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;qBACzE,EACD,MAAM,EACN,IAAI,CACJ,CAAC;gBACH,KAAK,aAAa;oBACjB,OAAO,kBAAkB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YAC5E,CAAC;QACF,CAAC;KACD,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE;IACrD,CAAC,CAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;KACpE,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;QACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,CAAC;IACF,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACnF,CAAC,CAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC5B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;QAC7B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;KACvB,CAAC;CACF,CAAC,CAAC;AAEH,mEAAmE;AACnE,SAAS,WAAW,CAAC,MAAc,EAAE,IAAY;IAChD,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,MAAc,EAAE,OAAe,EAAE,OAAe,EAAE,IAAY;IAClF,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,gBAAgB,CAAC,oDAAoD,IAAI,GAAG,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACpC,IAAI,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAClF,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACzB,sBAAsB,IAAI,iEAAiE,CAC3F,CAAC;IACH,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,gBAAgB,CACzB,mBAAmB,MAAM,CAAC,KAAK,CAAC,cAAc,IAAI,2DAA2D,CAC7G,CAAC;IACH,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,sFAAsF;IACtF,mFAAmF;IACnF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/F,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;AACvE,CAAC;AAED,+FAA+F;AAC/F,SAAS,QAAQ,CAAC,MAAc,EAAE,KAAa;IAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,OAAO,KAAK,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC;IAClD,OAAO,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,MAAc,EAAE,KAAa,EAAE,IAAY,EAAE,IAAY;IAC7E,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,gBAAgB,CACzB,qCAAqC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,6BAA6B,IAAI,GAAG,CAClG,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAW,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC;IACpC,OAAO,SAAS,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;AAClG,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,UAAU,CACxB,SAAoB,EACpB,IAAY,EACZ,MAAmB;IAEnB,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,SAAS,CAAC,SAAS,CAC5D,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,EAC/B,MAAM,CACN,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IAC3C,MAAM,MAAM,GAAG,SAAS;QACvB,CAAC,CAAC,cAAc,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,mDAAmD;QAC3G,CAAC,CAAC,EAAE,CAAC;IACN,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;AAClC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,EACpC,MAAM,EACN,MAAM,EACN,SAAS,GAKT;IACA,OAAO,UAAU,CAAC;QACjB,IAAI,EAAE,6BAA6B;QACnC,WAAW,EAAE,mCAAmC;QAChD,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,aAAa;QACpB,SAAS,EAAE,WAAW;QACtB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;YACxC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;YACzB,QAAQ,OAAO,CAAC,OAAO,EAAE,CAAC;gBACzB,KAAK,MAAM,CAAC,CAAC,CAAC;oBACb,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;oBACnD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;wBACf,MAAM,IAAI,KAAK,CAAC,6BAA6B,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;oBACnF,CAAC;oBACD,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;wBAC/B,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACzF,CAAC;oBACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;wBACjC,QAAQ,EAAE,KAAK;wBACf,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,GAAG,CAAC;qBAC9C,CAAC,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;oBACxD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CACjD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,EAC5B,GAAG,EAAE,CAAC,KAAK,CACX,CAAC;oBACF,OAAO;wBACN,IAAI,EAAE,MAAM;wBACZ,GAAG,EAAE,SAAS,CAAC,EAAE,CAChB,QAAQ;4BACP,CAAC,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC;4BACxC,CAAC,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;qBACpF,CAAC;gBACH,CAAC;gBACD,KAAK,aAAa;oBACjB,OAAO,kBAAkB,CACxB,MAAM,EACN;wBACC,IAAI,EAAE,QAAQ;wBACd,IAAI;wBACJ,MAAM,EAAE,OAAO,CAAC,EAAE,CACjB,MAAM,CAAC,IAAI,CACV,WAAW,CACV,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EACxB,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,IAAI,EAAE,EACrB,IAAI,CACJ,CACD;qBACF,EACD,MAAM,EACN,IAAI,CACJ,CAAC;gBACH,KAAK,QAAQ;oBACZ,OAAO,kBAAkB,CACxB,MAAM,EACN,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EACjE,MAAM,EACN,IAAI,CACJ,CAAC;gBACH,KAAK,QAAQ;oBACZ,OAAO,kBAAkB,CACxB,MAAM,EACN;wBACC,IAAI,EAAE,QAAQ;wBACd,IAAI;wBACJ,MAAM,EAAE,OAAO,CAAC,EAAE,CACjB,MAAM,CAAC,IAAI,CACV,WAAW,CACV,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EACxB,OAAO,CAAC,WAAW,EACnB,OAAO,CAAC,WAAW,EACnB,IAAI,CACJ,CACD;qBACF,EACD,MAAM,EACN,IAAI,CACJ,CAAC;YACJ,CAAC;QACF,CAAC;KACD,CAAC,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC3D,OAAO,mBAAmB,CAAC;QAC1B,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,gCAAgC;QAC7C,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC,EAAE,CAAC;QACjF,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,UAAU,IAAI,EAAE;QACzC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CACnC,kBAAkB,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC;KACnE,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CACnB,OAIgF;IAEhF,QAAQ,OAAO,CAAC,OAAO,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,CAAC,CAAC;YACb,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU;gBAC/B,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC5G,CAAC,CAAC,SAAS,CAAC;YACb,OAAO,QAAQ,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC1D,CAAC;QACD,KAAK,aAAa;YACjB,OAAO,WAAW,OAAO,CAAC,IAAI,EAAE,CAAC;QAClC,KAAK,QAAQ;YACZ,OAAO,UAAU,OAAO,CAAC,IAAI,EAAE,CAAC;QACjC,KAAK,QAAQ;YACZ,OAAO,UAAU,OAAO,CAAC,IAAI,UAAU,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;IACvE,CAAC;AACF,CAAC","sourcesContent":["import { lstat } from 'node:fs/promises';\nimport { z } from 'zod';\nimport { asNative, type NativeTool } from '#src/llm/domain/tool';\nimport type { AccessPolicy } from '../domain/accessPolicy.ts';\nimport { EditRefusedError } from '../domain/fileEdits.ts';\nimport type { PreparedCall, PreparingTool } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { addedLine, joinLines, type Line, splitLines, textsOf } from '../domain/textLines.ts';\nimport { applyUpdate, createdContent } from '../domain/v4aPatch.ts';\nimport type { Workspace } from '../domain/workspace.ts';\nimport { definePreparingTool } from './defineTool.ts';\nimport { prepareTrackedEdit } from './trackedEdits.ts';\nimport { readTracked } from './trackedReadFile.ts';\nimport { validatedSummarizer } from './validatedSummarizer.ts';\nimport { readRange } from './workspaceTools.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\n\n// A folder the editor views is listed, as its reference implementation does, up to this many.\nconst maxListedFiles = 200;\n\n/**\n * A native tool: its provider gives the model the schema, so the one here only says what the\n * calls hold, and each call is checked against `input` before anything is prepared.\n */\nfunction nativeTool<Schema extends z.ZodType>({\n\tname,\n\tdescription,\n\tnative,\n\tinput,\n\tsummarize,\n\tprepare,\n}: {\n\tname: string;\n\tdescription: string;\n\tnative: NativeTool;\n\tinput: Schema;\n\tsummarize?: (input: z.infer<Schema>) => string;\n\tprepare: (\n\t\tinput: z.infer<Schema>,\n\t\tsignal: AbortSignal,\n\t\tcall: ToolCallContext,\n\t) => Promise<PreparedCall>;\n}): PreparingTool {\n\treturn asNative(\n\t\t{\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tinputSchema: { type: 'object' },\n\t\t\t...(summarize\n\t\t\t\t? {\n\t\t\t\t\t\tsummarize: validatedSummarizer(input, summarize),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\tprepare: (raw: unknown, signal: AbortSignal, call: ToolCallContext) => {\n\t\t\t\tconst parsed = input.safeParse(raw);\n\t\t\t\tif (!parsed.success) return Promise.reject(new Error(z.prettifyError(parsed.error)));\n\t\t\t\treturn prepare(parsed.data, signal, call);\n\t\t\t},\n\t\t},\n\t\tnative,\n\t);\n}\n\nconst patchOperation = z.discriminatedUnion('type', [\n\tz.object({ type: z.literal('create_file'), path: z.string(), diff: z.string() }),\n\tz.object({ type: z.literal('update_file'), path: z.string(), diff: z.string() }),\n\tz.object({ type: z.literal('delete_file'), path: z.string() }),\n]);\n\n/**\n * OpenAI's native `apply_patch`: each call is one operation on one file, `create_file` and\n * `update_file` with a V4A diff. It cannot read, so the agent also has `readFile`.\n */\nexport function createApplyPatchTool(writes: WorkspaceWrites): PreparingTool {\n\treturn nativeTool({\n\t\tname: 'apply_patch',\n\t\tdescription: 'Creates, changes or deletes a file with a V4A patch.',\n\t\tnative: 'applyPatch',\n\t\tinput: patchOperation,\n\t\tsummarize: operation =>\n\t\t\t`${operation.type.replace('_file', '').replace('_', ' ')} ${operation.path}`,\n\t\tprepare: (operation, signal, call) => {\n\t\t\tconst { path } = operation;\n\t\t\tswitch (operation.type) {\n\t\t\t\tcase 'create_file':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{ kind: 'create', path, content: Buffer.from(createdContent(operation.diff, path)) },\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'update_file':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: 'modify',\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tchange: current =>\n\t\t\t\t\t\t\t\tBuffer.from(applyUpdate(current.toString('utf8'), operation.diff, path)),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'delete_file':\n\t\t\t\t\treturn prepareTrackedEdit(writes, { kind: 'delete', path }, signal, call);\n\t\t\t}\n\t\t},\n\t});\n}\n\nconst editorCommand = z.discriminatedUnion('command', [\n\tz.object({\n\t\tcommand: z.literal('view'),\n\t\tpath: z.string(),\n\t\tview_range: z.tuple([z.number().int(), z.number().int()]).optional(),\n\t}),\n\tz.object({\n\t\tcommand: z.literal('str_replace'),\n\t\tpath: z.string(),\n\t\told_str: z.string(),\n\t\tnew_str: z.string().optional(),\n\t}),\n\tz.object({ command: z.literal('create'), path: z.string(), file_text: z.string() }),\n\tz.object({\n\t\tcommand: z.literal('insert'),\n\t\tpath: z.string(),\n\t\tinsert_line: z.number().int(),\n\t\tinsert_text: z.string(),\n\t}),\n]);\n\n/** How many times `text` appears in `source`, without overlaps. */\nfunction occurrences(source: string, text: string): number {\n\treturn source.split(text).length - 1;\n}\n\n/**\n * Replaces the one place `oldText` appears. The model writes `\\n`: when the text as sent is not\n * in the file, it is tried with CRLF too, so a CRLF file can be edited, and written with CRLF.\n */\nfunction replaceOnce(source: string, oldText: string, newText: string, path: string): string {\n\tif (oldText === '') {\n\t\tthrow new EditRefusedError(`old_str is empty; copy the text to replace from \"${path}\"`);\n\t}\n\tlet [from, to] = [oldText, newText];\n\tlet count = occurrences(source, from);\n\tif (count === 0 && !oldText.includes('\\r\\n') && oldText.includes('\\n')) {\n\t\t[from, to] = [oldText.replaceAll('\\n', '\\r\\n'), newText.replaceAll('\\n', '\\r\\n')];\n\t\tcount = occurrences(source, from);\n\t}\n\tif (count === 0) {\n\t\tthrow new EditRefusedError(\n\t\t\t`old_str is not in \"${path}\"; view the file and copy the text exactly, whitespace included`,\n\t\t);\n\t}\n\tif (count > 1) {\n\t\tthrow new EditRefusedError(\n\t\t\t`old_str appears ${String(count)} times in \"${path}\"; include more of the lines around it so it appears once`,\n\t\t);\n\t}\n\tconst at = source.indexOf(from);\n\t// New lines end like the line where the text sits, so a CRLF line gains no bare `\\n`.\n\t// When the text ends with its line's newline, that newline says how the line ends.\n\tconst ending = endingAt(source, from.endsWith('\\n') ? at + from.length - 1 : at + from.length);\n\tconst written = to.replace(/(?<!\\r)\\n/g, ending);\n\treturn source.slice(0, at) + written + source.slice(at + from.length);\n}\n\n/** The ending of the line holding `index`, or the text's usual one for a last line without. */\nfunction endingAt(source: string, index: number): '\\n' | '\\r\\n' {\n\tconst newline = source.indexOf('\\n', index);\n\tif (newline === -1) return splitLines(source).eol;\n\treturn source[newline - 1] === '\\r' ? '\\r\\n' : '\\n';\n}\n\n/**\n * Puts `text` after line `after` of `source`, 0 being before the first. The lines inserted end\n * like the line before them, and every other line is written back as it was.\n */\nfunction insertAfter(source: string, after: number, text: string, path: string): string {\n\tconst file = splitLines(source);\n\tif (after < 0 || after > file.lines.length) {\n\t\tthrow new EditRefusedError(\n\t\t\t`insert_line must be between 0 and ${String(file.lines.length)}, the number of lines in \"${path}\"`,\n\t\t);\n\t}\n\tconst texts = textsOf(splitLines(text).lines);\n\tconst inserted: Line[] = [];\n\tfor (const line of texts.length > 0 ? texts : ['']) {\n\t\tinserted.push(addedLine(line, inserted.at(-1) ?? file.lines[after - 1], file.eol));\n\t}\n\tconst lines = [...file.lines];\n\tlines.splice(after, 0, ...inserted);\n\treturn joinLines({ ...file, lines, finalNewline: file.finalNewline || file.lines.length === 0 });\n}\n\n/** The files under a folder, as the editor shows a folder it views. */\nasync function listFolder(\n\tworkspace: Workspace,\n\tpath: string,\n\tsignal: AbortSignal,\n): Promise<string> {\n\tconst { files, total, truncated } = await workspace.listFiles(\n\t\t{ path, limit: maxListedFiles },\n\t\tsignal,\n\t);\n\tif (files.length === 0) return 'No files.';\n\tconst notice = truncated\n\t\t? `\\n[Showing ${String(files.length)} of ${String(total)} files. View a folder inside it to see the rest.]`\n\t\t: '';\n\treturn files.join('\\n') + notice;\n}\n\n/**\n * Claude's native text editor, `str_replace_based_edit_tool`. `view` reads through the access\n * policy and counts as a read, so it replaces `readFile`; on a folder it lists the files in it.\n * `create` makes only a file that does not exist, and the editor has no command to delete:\n * Claude gets `delete_file` for that.\n */\nexport function createTextEditorTool({\n\twrites,\n\tpolicy,\n\tworkspace,\n}: {\n\twrites: WorkspaceWrites;\n\tpolicy: AccessPolicy;\n\tworkspace: Workspace;\n}): PreparingTool {\n\treturn nativeTool({\n\t\tname: 'str_replace_based_edit_tool',\n\t\tdescription: 'Views, creates and changes files.',\n\t\tnative: 'textEditor',\n\t\tinput: editorCommand,\n\t\tsummarize: editSummary,\n\t\tprepare: async (command, signal, call) => {\n\t\t\tconst { path } = command;\n\t\t\tswitch (command.command) {\n\t\t\t\tcase 'view': {\n\t\t\t\t\tconst [start, end] = command.view_range ?? [1, -1];\n\t\t\t\t\tif (start < 1) {\n\t\t\t\t\t\tthrow new Error(`view_range starts at line ${String(start)}; lines count from 1`);\n\t\t\t\t\t}\n\t\t\t\t\tif (end !== -1 && end < start) {\n\t\t\t\t\t\tthrow new Error(`view_range ends before it starts: [${String(start)}, ${String(end)}]`);\n\t\t\t\t\t}\n\t\t\t\t\tconst { from, count } = readRange({\n\t\t\t\t\t\tfromLine: start,\n\t\t\t\t\t\tlineCount: end === -1 ? null : end - start + 1,\n\t\t\t\t\t});\n\t\t\t\t\tconst target = await policy.check(path, 'read', signal);\n\t\t\t\t\tconst isFolder = await lstat(target.absolute).then(\n\t\t\t\t\t\tstats => stats.isDirectory(),\n\t\t\t\t\t\t() => false,\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\trisk: 'safe',\n\t\t\t\t\t\trun: runSignal =>\n\t\t\t\t\t\t\tisFolder\n\t\t\t\t\t\t\t\t? listFolder(workspace, path, runSignal)\n\t\t\t\t\t\t\t\t: readTracked({ target, path, from, count, reads: call.reads, signal: runSignal }),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tcase 'str_replace':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: 'modify',\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tchange: current =>\n\t\t\t\t\t\t\t\tBuffer.from(\n\t\t\t\t\t\t\t\t\treplaceOnce(\n\t\t\t\t\t\t\t\t\t\tcurrent.toString('utf8'),\n\t\t\t\t\t\t\t\t\t\tcommand.old_str,\n\t\t\t\t\t\t\t\t\t\tcommand.new_str ?? '',\n\t\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'create':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{ kind: 'create', path, content: Buffer.from(command.file_text) },\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t\tcase 'insert':\n\t\t\t\t\treturn prepareTrackedEdit(\n\t\t\t\t\t\twrites,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: 'modify',\n\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\tchange: current =>\n\t\t\t\t\t\t\t\tBuffer.from(\n\t\t\t\t\t\t\t\t\tinsertAfter(\n\t\t\t\t\t\t\t\t\t\tcurrent.toString('utf8'),\n\t\t\t\t\t\t\t\t\t\tcommand.insert_line,\n\t\t\t\t\t\t\t\t\t\tcommand.insert_text,\n\t\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tcall,\n\t\t\t\t\t);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/** The delete Claude's text editor has no command for. */\nexport function createDeleteFileTool(writes: WorkspaceWrites): PreparingTool {\n\treturn definePreparingTool({\n\t\tname: 'delete_file',\n\t\tdescription: 'Deletes a file. Read it first.',\n\t\tinput: z.object({ path: z.string().describe('The file, relative to the root.') }),\n\t\tsummarize: ({ path }) => `delete ${path}`,\n\t\tprepare: ({ path }, signal, call) =>\n\t\t\tprepareTrackedEdit(writes, { kind: 'delete', path }, signal, call),\n\t});\n}\n\nfunction editSummary(\n\tcommand:\n\t\t| { command: 'view'; path: string; view_range?: [number, number] }\n\t\t| { command: 'str_replace'; path: string; old_str: string; new_str?: string }\n\t\t| { command: 'create'; path: string; file_text: string }\n\t\t| { command: 'insert'; path: string; insert_line: number; insert_text: string },\n): string {\n\tswitch (command.command) {\n\t\tcase 'view': {\n\t\t\tconst range = command.view_range\n\t\t\t\t? `${String(command.view_range[0])}-${command.view_range[1] === -1 ? 'end' : String(command.view_range[1])}`\n\t\t\t\t: undefined;\n\t\t\treturn `view ${command.path}${range ? `:${range}` : ''}`;\n\t\t}\n\t\tcase 'str_replace':\n\t\t\treturn `replace ${command.path}`;\n\t\tcase 'create':\n\t\t\treturn `create ${command.path}`;\n\t\tcase 'insert':\n\t\t\treturn `insert ${command.path} after ${String(command.insert_line)}`;\n\t}\n}\n"]}
|
|
@@ -15,6 +15,7 @@ export function createShowChangesTool({ store, policy, }) {
|
|
|
15
15
|
name: 'showChanges',
|
|
16
16
|
description: 'Shows what this task has changed in the repository so far, as a diff per file, in the order each file was first changed.',
|
|
17
17
|
input: z.object({}),
|
|
18
|
+
summarize: () => 'changes in this task',
|
|
18
19
|
prepare: (_, __, { run }) => Promise.resolve({
|
|
19
20
|
risk: 'safe',
|
|
20
21
|
run: async (signal) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"showChangesTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/showChangesTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAqB,MAAM,2BAA2B,CAAC;AAEjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErB,MAAM,UAAU,GAAG,4CAA4C,CAAC;AAEhE;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,EACrC,KAAK,EACL,MAAM,GAIN;IACA,OAAO,mBAAmB,CAAC;QAC1B,IAAI,EAAE,aAAa;QACnB,WAAW,EACV,0HAA0H;QAC3H,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAC3B,OAAO,CAAC,OAAO,CAAC;YACf,IAAI,EAAE,MAAM;YACZ,GAAG,EAAE,KAAK,EAAC,MAAM,EAAC,EAAE;gBACnB,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;gBACjC,IAAI,KAAK,KAAK,SAAS;oBAAE,OAAO,UAAU,CAAC;gBAC3C,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE;oBAC5C,QAAQ;oBACR,OAAO,EAAE,KAAK,EAAC,IAAI,EAAC,EAAE;wBACrB,IAAI,CAAC;4BACJ,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;4BACzC,OAAO,IAAI,CAAC;wBACb,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BAChB,IAAI,KAAK,YAAY,iBAAiB;gCAAE,OAAO,KAAK,CAAC;4BACrD,MAAM,KAAK,CAAC;wBACb,CAAC;oBACF,CAAC;iBACD,CAAC,CAAC;gBACH,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;YACxC,CAAC;SACD,CAAC;KACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { z } from 'zod';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport { showChanges } from '#src/recovery/domain/runChanges';\nimport { AccessDeniedError, type AccessPolicy } from '../domain/accessPolicy.ts';\nimport type { PreparingTool } from '../domain/preparedCall.ts';\nimport { definePreparingTool } from './defineTool.ts';\n\n// A reviewer reads a change whole, but not a rewrite of the whole repository.\nconst maxLines = 600;\n\nconst nothingYet = 'Nothing has been changed in this task yet.';\n\n/**\n * `showChanges` for the planner and the reviewer: what the current task has changed so far, as a\n * diff per file, from the run's own record. It shows only what the role may read: a secret the\n * executor was allowed to write is counted, never shown.\n */\nexport function createShowChangesTool({\n\tstore,\n\tpolicy,\n}: {\n\tstore: RecoveryStore;\n\tpolicy: AccessPolicy;\n}): PreparingTool {\n\treturn definePreparingTool({\n\t\tname: 'showChanges',\n\t\tdescription:\n\t\t\t'Shows what this task has changed in the repository so far, as a diff per file, in the order each file was first changed.',\n\t\tinput: z.object({}),\n\t\tprepare: (_, __, { run }) =>\n\t\t\tPromise.resolve({\n\t\t\t\trisk: 'safe',\n\t\t\t\trun: async signal => {\n\t\t\t\t\tconst { runId } = run.historyRun;\n\t\t\t\t\tif (runId === undefined) return nothingYet;\n\t\t\t\t\tconst diff = await showChanges(store, runId, {\n\t\t\t\t\t\tmaxLines,\n\t\t\t\t\t\tvisible: async path => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait policy.check(path, 'read', signal);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (error instanceof AccessDeniedError) return false;\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\treturn diff === '' ? nothingYet : diff;\n\t\t\t\t},\n\t\t\t}),\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"showChangesTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/showChangesTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAqB,MAAM,2BAA2B,CAAC;AAEjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErB,MAAM,UAAU,GAAG,4CAA4C,CAAC;AAEhE;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,EACrC,KAAK,EACL,MAAM,GAIN;IACA,OAAO,mBAAmB,CAAC;QAC1B,IAAI,EAAE,aAAa;QACnB,WAAW,EACV,0HAA0H;QAC3H,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACnB,SAAS,EAAE,GAAG,EAAE,CAAC,sBAAsB;QACvC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAC3B,OAAO,CAAC,OAAO,CAAC;YACf,IAAI,EAAE,MAAM;YACZ,GAAG,EAAE,KAAK,EAAC,MAAM,EAAC,EAAE;gBACnB,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;gBACjC,IAAI,KAAK,KAAK,SAAS;oBAAE,OAAO,UAAU,CAAC;gBAC3C,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE;oBAC5C,QAAQ;oBACR,OAAO,EAAE,KAAK,EAAC,IAAI,EAAC,EAAE;wBACrB,IAAI,CAAC;4BACJ,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;4BACzC,OAAO,IAAI,CAAC;wBACb,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BAChB,IAAI,KAAK,YAAY,iBAAiB;gCAAE,OAAO,KAAK,CAAC;4BACrD,MAAM,KAAK,CAAC;wBACb,CAAC;oBACF,CAAC;iBACD,CAAC,CAAC;gBACH,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;YACxC,CAAC;SACD,CAAC;KACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { z } from 'zod';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport { showChanges } from '#src/recovery/domain/runChanges';\nimport { AccessDeniedError, type AccessPolicy } from '../domain/accessPolicy.ts';\nimport type { PreparingTool } from '../domain/preparedCall.ts';\nimport { definePreparingTool } from './defineTool.ts';\n\n// A reviewer reads a change whole, but not a rewrite of the whole repository.\nconst maxLines = 600;\n\nconst nothingYet = 'Nothing has been changed in this task yet.';\n\n/**\n * `showChanges` for the planner and the reviewer: what the current task has changed so far, as a\n * diff per file, from the run's own record. It shows only what the role may read: a secret the\n * executor was allowed to write is counted, never shown.\n */\nexport function createShowChangesTool({\n\tstore,\n\tpolicy,\n}: {\n\tstore: RecoveryStore;\n\tpolicy: AccessPolicy;\n}): PreparingTool {\n\treturn definePreparingTool({\n\t\tname: 'showChanges',\n\t\tdescription:\n\t\t\t'Shows what this task has changed in the repository so far, as a diff per file, in the order each file was first changed.',\n\t\tinput: z.object({}),\n\t\tsummarize: () => 'changes in this task',\n\t\tprepare: (_, __, { run }) =>\n\t\t\tPromise.resolve({\n\t\t\t\trisk: 'safe',\n\t\t\t\trun: async signal => {\n\t\t\t\t\tconst { runId } = run.historyRun;\n\t\t\t\t\tif (runId === undefined) return nothingYet;\n\t\t\t\t\tconst diff = await showChanges(store, runId, {\n\t\t\t\t\t\tmaxLines,\n\t\t\t\t\t\tvisible: async path => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait policy.check(path, 'read', signal);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (error instanceof AccessDeniedError) return false;\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\treturn diff === '' ? nothingYet : diff;\n\t\t\t\t},\n\t\t\t}),\n\t});\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { lstat, readFile } from 'node:fs/promises';
|
|
3
3
|
import { isText } from '#src/recovery/domain/runChanges';
|
|
4
4
|
import { definePreparingTool } from "./defineTool.js";
|
|
5
|
-
import { readFileDescription, readFileInput, readRange, renderLines } from "./workspaceTools.js";
|
|
5
|
+
import { readFileDescription, readFileInput, readFileSummary, readRange, renderLines, } from "./workspaceTools.js";
|
|
6
6
|
// The same bound as an edit: a file too large to change need not be read whole to track it.
|
|
7
7
|
const maxFileBytes = 5 * 1024 * 1024;
|
|
8
8
|
/**
|
|
@@ -65,6 +65,7 @@ export function createTrackedReadFile(policy) {
|
|
|
65
65
|
name: 'readFile',
|
|
66
66
|
description: readFileDescription,
|
|
67
67
|
input: readFileInput,
|
|
68
|
+
summarize: readFileSummary,
|
|
68
69
|
prepare: async ({ path, fromLine, lineCount }, signal, { reads }) => {
|
|
69
70
|
const { from, count } = readRange({ fromLine, lineCount });
|
|
70
71
|
const target = await policy.check(path, 'read', signal);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trackedReadFile.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/trackedReadFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAIzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,
|
|
1
|
+
{"version":3,"file":"trackedReadFile.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/trackedReadFile.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { lstat, readFile } from 'node:fs/promises';\nimport { isText } from '#src/recovery/domain/runChanges';\nimport type { AccessPolicy, AllowedPath } from '../domain/accessPolicy.ts';\nimport type { PreparingTool } from '../domain/preparedCall.ts';\nimport type { RunReads } from '../domain/readRegistry.ts';\nimport { definePreparingTool } from './defineTool.ts';\nimport {\n\treadFileDescription,\n\treadFileInput,\n\treadFileSummary,\n\treadRange,\n\trenderLines,\n} from './workspaceTools.ts';\n\n// The same bound as an edit: a file too large to change need not be read whole to track it.\nconst maxFileBytes = 5 * 1024 * 1024;\n\n/**\n * The lines of a text, split as the diff an edit returns splits them, so their numbers agree: on\n * `\\n`, with a `\\r` before it dropped. A final newline ends the last line; it starts no other.\n */\nfunction lines(text: string): string[] {\n\tif (text === '') return [];\n\tconst all = text.split('\\n').map(line => (line.endsWith('\\r') ? line.slice(0, -1) : line));\n\tif (all.at(-1) === '') all.pop();\n\treturn all;\n}\n\nfunction code(error: unknown): string {\n\treturn (error as NodeJS.ErrnoException).code ?? 'an unknown error';\n}\n\n/**\n * Reads lines `from` to `from + count - 1` of a file the policy allowed, and records the version\n * of the whole file the model saw. The file is read once, so the version recorded is the content\n * shown. A partial read counts: the model saw this version, and can read the rest.\n */\nexport async function readTracked({\n\ttarget,\n\tpath,\n\tfrom,\n\tcount,\n\treads,\n\tsignal,\n}: {\n\ttarget: AllowedPath;\n\tpath: string;\n\tfrom: number;\n\tcount: number;\n\treads: RunReads;\n\tsignal: AbortSignal;\n}): Promise<string> {\n\tsignal.throwIfAborted();\n\tconst stats = await lstat(target.absolute).catch((error: unknown) => {\n\t\tif (code(error) === 'ENOENT') return undefined;\n\t\t// Its own message would name the host path.\n\t\tthrow new Error(`Could not read ${path} (${code(error)})`);\n\t});\n\t// One answer for every file it cannot show, as the read tools give.\n\tif (!stats?.isFile()) throw new Error(`No such file: ${path}`);\n\tif (stats.size > maxFileBytes) {\n\t\tthrow new Error(\n\t\t\t`${path} is larger than ${String(maxFileBytes / 1024 / 1024)} MB, too large to read`,\n\t\t);\n\t}\n\tconst content = await readFile(target.absolute, { signal }).catch((error: unknown) => {\n\t\tsignal.throwIfAborted();\n\t\tthrow new Error(`Could not read ${path} (${code(error)})`);\n\t});\n\tif (!isText(content)) throw new Error(`Not a text file: ${path}`);\n\n\tconst all = lines(content.toString('utf8'));\n\tconst shown = all.slice(from - 1, from - 1 + count);\n\treads.record(target.absolute, createHash('sha256').update(content).digest('hex'));\n\treturn renderLines({\n\t\tpath,\n\t\tlines: shown,\n\t\tfrom,\n\t\ttotalLines: all.length,\n\t\ttruncated: from - 1 + shown.length < all.length,\n\t});\n}\n\n/**\n * `readFile` for an agent that edits: the same tool for the model, through the access policy,\n * and it records the version of the file the model saw, so an edit starts only from it.\n */\nexport function createTrackedReadFile(policy: AccessPolicy): PreparingTool {\n\treturn definePreparingTool({\n\t\tname: 'readFile',\n\t\tdescription: readFileDescription,\n\t\tinput: readFileInput,\n\t\tsummarize: readFileSummary,\n\t\tprepare: async ({ path, fromLine, lineCount }, signal, { reads }) => {\n\t\t\tconst { from, count } = readRange({ fromLine, lineCount });\n\t\t\tconst target = await policy.check(path, 'read', signal);\n\t\t\treturn {\n\t\t\t\trisk: 'safe',\n\t\t\t\trun: runSignal => readTracked({ target, path, from, count, reads, signal: runSignal }),\n\t\t\t};\n\t\t},\n\t});\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type ToolSummarizer } from '../domain/tool.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Keeps tool narration on the same validated-input boundary as execution. A provider can send
|
|
5
|
+
* anything to a tool, so a summarizer must never receive the unvalidated value it describes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function validatedSummarizer<Schema extends z.ZodType>(schema: Schema, summarize: (input: z.infer<Schema>) => string): ToolSummarizer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { InvalidToolInputError } from "../domain/tool.js";
|
|
3
|
+
/**
|
|
4
|
+
* Keeps tool narration on the same validated-input boundary as execution. A provider can send
|
|
5
|
+
* anything to a tool, so a summarizer must never receive the unvalidated value it describes.
|
|
6
|
+
*/
|
|
7
|
+
export function validatedSummarizer(schema, summarize) {
|
|
8
|
+
return (raw) => {
|
|
9
|
+
const parsed = schema.safeParse(raw);
|
|
10
|
+
if (!parsed.success)
|
|
11
|
+
throw new InvalidToolInputError(z.prettifyError(parsed.error));
|
|
12
|
+
return summarize(parsed.data);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=validatedSummarizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validatedSummarizer.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/validatedSummarizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAuB,MAAM,mBAAmB,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAClC,MAAc,EACd,SAA6C;IAE7C,OAAO,CAAC,GAAY,EAAE,EAAE;QACvB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,IAAI,qBAAqB,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACpF,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC;AACH,CAAC","sourcesContent":["import { z } from 'zod';\nimport { InvalidToolInputError, type ToolSummarizer } from '../domain/tool.ts';\n\n/**\n * Keeps tool narration on the same validated-input boundary as execution. A provider can send\n * anything to a tool, so a summarizer must never receive the unvalidated value it describes.\n */\nexport function validatedSummarizer<Schema extends z.ZodType>(\n\tschema: Schema,\n\tsummarize: (input: z.infer<Schema>) => string,\n): ToolSummarizer {\n\treturn (raw: unknown) => {\n\t\tconst parsed = schema.safeParse(raw);\n\t\tif (!parsed.success) throw new InvalidToolInputError(z.prettifyError(parsed.error));\n\t\treturn summarize(parsed.data);\n\t};\n}\n"]}
|
|
@@ -28,5 +28,11 @@ export declare function readRange({ fromLine, lineCount, }: {
|
|
|
28
28
|
from: number;
|
|
29
29
|
count: number;
|
|
30
30
|
};
|
|
31
|
+
/** A stable description of a read request, using only the range the model supplied. */
|
|
32
|
+
export declare function readFileSummary({ path, fromLine, lineCount, }: {
|
|
33
|
+
path: string;
|
|
34
|
+
fromLine: number | null;
|
|
35
|
+
lineCount: number | null;
|
|
36
|
+
}): string;
|
|
31
37
|
/** The three read-only tools an agent explores a repository with. */
|
|
32
38
|
export declare function createWorkspaceTools(workspace: Workspace): Tool[];
|
|
@@ -56,6 +56,19 @@ export function readRange({ fromLine, lineCount, }) {
|
|
|
56
56
|
assertPositiveInteger('lineCount', requested);
|
|
57
57
|
return { from, count: Math.min(requested, maxLines) };
|
|
58
58
|
}
|
|
59
|
+
/** A stable description of a read request, using only the range the model supplied. */
|
|
60
|
+
export function readFileSummary({ path, fromLine, lineCount, }) {
|
|
61
|
+
const suppliedFrom = fromLine ?? 1;
|
|
62
|
+
const from = Number.isInteger(suppliedFrom) && suppliedFrom > 0 ? suppliedFrom : 1;
|
|
63
|
+
const suppliedCount = lineCount ?? maxLines;
|
|
64
|
+
const requested = Number.isInteger(suppliedCount) && suppliedCount > 0 ? suppliedCount : maxLines;
|
|
65
|
+
const count = Math.min(requested, maxLines);
|
|
66
|
+
return `${path}:${String(from)}-${String(from + count - 1)}`;
|
|
67
|
+
}
|
|
68
|
+
function scopeSummary(path, glob) {
|
|
69
|
+
const scopePath = path ?? '.';
|
|
70
|
+
return glob ? `${scopePath === '.' ? '' : `${scopePath}/`}${glob}` : scopePath;
|
|
71
|
+
}
|
|
59
72
|
function assertPositiveInteger(name, value) {
|
|
60
73
|
if (!Number.isInteger(value) || value < 1) {
|
|
61
74
|
throw new Error(`${name} must be a whole number of at least 1; got ${String(value)}`);
|
|
@@ -69,6 +82,7 @@ export function createWorkspaceTools(workspace) {
|
|
|
69
82
|
description: 'Lists the files of the repository, sorted. Ignored files such as dependencies are left out.',
|
|
70
83
|
risk: 'safe',
|
|
71
84
|
input: z.object(scope),
|
|
85
|
+
summarize: ({ path, glob }) => scopeSummary(path, glob),
|
|
72
86
|
execute: async ({ path, glob }, signal) => {
|
|
73
87
|
// The port takes "no scope" as a missing field.
|
|
74
88
|
const { files, total, truncated } = await workspace.listFiles({ path: path ?? undefined, glob: glob ?? undefined, limit: maxFiles }, signal);
|
|
@@ -91,6 +105,7 @@ export function createWorkspaceTools(workspace) {
|
|
|
91
105
|
ignoreCase: z.boolean().nullable().describe('Match regardless of case. Defaults to false.'),
|
|
92
106
|
...scope,
|
|
93
107
|
}),
|
|
108
|
+
summarize: ({ path, glob, pattern }) => `"${pattern}" in ${scopeSummary(path, glob)}`,
|
|
94
109
|
execute: async ({ path, glob, ignoreCase, pattern }, signal) => {
|
|
95
110
|
const { matches, total, truncated } = await workspace.searchText({
|
|
96
111
|
path: path ?? undefined,
|
|
@@ -114,6 +129,7 @@ export function createWorkspaceTools(workspace) {
|
|
|
114
129
|
description: readFileDescription,
|
|
115
130
|
risk: 'safe',
|
|
116
131
|
input: readFileInput,
|
|
132
|
+
summarize: readFileSummary,
|
|
117
133
|
execute: async ({ fromLine, lineCount, path }, signal) => {
|
|
118
134
|
// Checked before reading.
|
|
119
135
|
const { from, count } = readRange({ fromLine, lineCount });
|