@di-code/coding-agent 0.1.2 → 0.1.4
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 +499 -143
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/clipboard-image.d.ts +13 -0
- package/dist/core/clipboard-image.d.ts.map +1 -0
- package/dist/core/clipboard-image.js +99 -0
- package/dist/core/clipboard-image.js.map +1 -0
- package/dist/core/context-builder.d.ts.map +1 -1
- package/dist/core/context-builder.js +6 -2
- package/dist/core/context-builder.js.map +1 -1
- package/dist/core/image-input.d.ts +13 -0
- package/dist/core/image-input.d.ts.map +1 -0
- package/dist/core/image-input.js +147 -0
- package/dist/core/image-input.js.map +1 -0
- package/dist/core/session/session-storage.d.ts.map +1 -1
- package/dist/core/session/session-storage.js +1 -0
- package/dist/core/session/session-storage.js.map +1 -1
- package/dist/core/session.d.ts +19 -2
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +101 -4
- package/dist/core/session.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -2
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +25 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +136 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +13 -4
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +64 -30
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/read.d.ts +2 -2
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +2 -2
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/entry.js +9 -2
- package/dist/entry.js.map +1 -1
- package/dist/main.d.ts +3 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +42 -8
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive-components.d.ts +3 -0
- package/dist/modes/interactive-components.d.ts.map +1 -1
- package/dist/modes/interactive-components.js +76 -25
- package/dist/modes/interactive-components.js.map +1 -1
- package/dist/modes/interactive-diff.d.ts +8 -0
- package/dist/modes/interactive-diff.d.ts.map +1 -0
- package/dist/modes/interactive-diff.js +78 -0
- package/dist/modes/interactive-diff.js.map +1 -0
- package/dist/modes/interactive-layout.d.ts +6 -1
- package/dist/modes/interactive-layout.d.ts.map +1 -1
- package/dist/modes/interactive-layout.js +21 -1
- package/dist/modes/interactive-layout.js.map +1 -1
- package/dist/modes/interactive-state.d.ts +17 -2
- package/dist/modes/interactive-state.d.ts.map +1 -1
- package/dist/modes/interactive-state.js +103 -3
- package/dist/modes/interactive-state.js.map +1 -1
- package/dist/modes/interactive.d.ts +18 -0
- package/dist/modes/interactive.d.ts.map +1 -1
- package/dist/modes/interactive.js +289 -24
- package/dist/modes/interactive.js.map +1 -1
- package/dist/provider-onboarding.d.ts +8 -1
- package/dist/provider-onboarding.d.ts.map +1 -1
- package/dist/provider-onboarding.js +53 -23
- package/dist/provider-onboarding.js.map +1 -1
- package/dist/rpc-entry.js +1 -1
- package/dist/rpc-entry.js.map +1 -1
- package/dist/startup.d.ts +11 -3
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +240 -84
- package/dist/startup.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +2 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/utils/syntax-highlight.js +55 -0
- package/dist/utils/syntax-highlight.js.map +1 -0
- package/package.json +9 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { realpath } from \"node:fs/promises\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\n\nexport const DEFAULT_BASH_TIMEOUT_MS = 30_000;\nexport const MAX_BASH_TIMEOUT_MS = 300_000;\nexport const DEFAULT_BASH_MAX_OUTPUT_BYTES = 50 * 1024;\n\nexport const bashParameters = Type.Object({\n\tcommand: Type.String({ minLength: 1 }),\n\ttimeoutMs: Type.Optional(Type.Integer({ minimum: 1, maximum: MAX_BASH_TIMEOUT_MS })),\n});\n\nexport type BashParameters = Static<typeof bashParameters>;\n\nexport interface BashRunOptions {\n\treadonly signal?: AbortSignal;\n\treadonly timeoutMs: number;\n\treadonly onStdout: (chunk: Buffer) => void;\n\treadonly onStderr: (chunk: Buffer) => void;\n}\n\nexport interface BashExecution {\n\treadonly exitCode: number | null;\n\treadonly timedOut: boolean;\n\treadonly aborted: boolean;\n}\n\nexport interface BashOperations {\n\trun(command: string, cwd: string, options: BashRunOptions): Promise<BashExecution>;\n}\n\nexport interface BashToolOptions {\n\treadonly operations?: BashOperations;\n\treadonly maxOutputBytes?: number;\n}\n\nexport type BashTool = AgentTool<typeof bashParameters>;\n\nexport interface ShellRuntime {\n\treadonly executable: string;\n\treadonly detached: boolean;\n\treadonly description: string;\n\targs(command: string): readonly string[];\n}\n\ninterface OutputSnapshot {\n\treadonly text: string;\n\treadonly truncated: boolean;\n}\n\ninterface BashStatus {\n\treadonly exitCode: number | null;\n\treadonly timedOut: boolean;\n\treadonly aborted: boolean;\n\treadonly stdoutTruncated: boolean;\n\treadonly stderrTruncated: boolean;\n}\n\nfunction assertTimeout(timeoutMs: number): void {\n\tif (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_BASH_TIMEOUT_MS) {\n\t\tthrow new Error(`timeoutMs must be an integer between 1 and ${MAX_BASH_TIMEOUT_MS}`);\n\t}\n}\n\nfunction assertMaxOutputBytes(maxOutputBytes: number): void {\n\tif (!Number.isInteger(maxOutputBytes) || maxOutputBytes < 1) {\n\t\tthrow new Error(\"maxOutputBytes must be a positive integer\");\n\t}\n}\n\nfunction createOutputCapture(maxBytes: number): {\n\tappend(chunk: Buffer): void;\n\tsnapshot(): OutputSnapshot;\n} {\n\tconst chunks: Buffer[] = [];\n\tlet bytes = 0;\n\tlet truncated = false;\n\n\treturn {\n\t\tappend(chunk) {\n\t\t\tconst remaining = maxBytes - bytes;\n\t\t\tif (remaining > 0) {\n\t\t\t\tconst kept = chunk.subarray(0, remaining);\n\t\t\t\tchunks.push(Buffer.from(kept));\n\t\t\t\tbytes += kept.length;\n\t\t\t}\n\t\t\tif (chunk.length > remaining) truncated = true;\n\t\t},\n\t\tsnapshot() {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\treturn { text: decoder.write(Buffer.concat(chunks, bytes)), truncated };\n\t\t},\n\t};\n}\n\nfunction createPowerShellScript(command: string): string {\n\treturn [\n\t\t\"$OutputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)\",\n\t\t\"$script:diCodeSucceeded = $false\",\n\t\t\"$script:diCodeExitCode = 0\",\n\t\t\"& {\",\n\t\tcommand,\n\t\t\"$script:diCodeSucceeded = $?\",\n\t\t\"$script:diCodeExitCode = $LASTEXITCODE\",\n\t\t\"} | Out-Default\",\n\t\t\"if ($script:diCodeSucceeded) { exit 0 }\",\n\t\t\"if ($script:diCodeExitCode -is [int] -and $script:diCodeExitCode -ne 0) { exit $script:diCodeExitCode }\",\n\t\t\"exit 1\",\n\t].join(\"\\n\");\n}\n\nexport function resolveShellRuntime(platform: NodeJS.Platform = process.platform): ShellRuntime {\n\tif (platform === \"win32\") {\n\t\treturn {\n\t\t\texecutable: \"powershell.exe\",\n\t\t\tdetached: false,\n\t\t\tdescription:\n\t\t\t\t\"Execute a Windows PowerShell command in the allowed root with timeout and bounded output. Use PowerShell syntax such as Get-ChildItem and Get-Content; do not use Bash syntax.\",\n\t\t\targs: (command) => [\"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\", createPowerShellScript(command)],\n\t\t};\n\t}\n\n\treturn {\n\t\texecutable: \"/bin/sh\",\n\t\tdetached: true,\n\t\tdescription:\n\t\t\t\"Execute a POSIX shell command using /bin/sh in the allowed root with timeout and bounded output. Use portable POSIX syntax; Bash-only extensions may not be available.\",\n\t\targs: (command) => [\"-c\", command],\n\t};\n}\n\nfunction waitForProcess(command: string, cwd: string, options: BashRunOptions): Promise<BashExecution> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (options.signal?.aborted) {\n\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst runtime = resolveShellRuntime();\n\t\tconst child = spawn(runtime.executable, [...runtime.args(command)], {\n\t\t\tcwd,\n\t\t\tdetached: runtime.detached,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\tlet timedOut = false;\n\t\tlet aborted = false;\n\t\tlet settled = false;\n\t\tlet termination: Promise<void> | undefined;\n\n\t\tconst requestTermination = (): void => {\n\t\t\tif (child.pid !== undefined) termination ??= killProcessTree(child.pid, child);\n\t\t};\n\t\tconst onAbort = (): void => {\n\t\t\taborted = true;\n\t\t\trequestTermination();\n\t\t};\n\t\tconst timeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\trequestTermination();\n\t\t}, options.timeoutMs);\n\n\t\toptions.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tif (options.signal?.aborted) onAbort();\n\t\tchild.stdout.on(\"data\", options.onStdout);\n\t\tchild.stderr.on(\"data\", options.onStderr);\n\n\t\tconst cleanup = (): void => {\n\t\t\tclearTimeout(timeout);\n\t\t\toptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t};\n\t\tchild.once(\"error\", (error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(error);\n\t\t});\n\t\tchild.once(\"close\", (exitCode) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tvoid (termination ?? Promise.resolve()).then(\n\t\t\t\t() => resolve({ exitCode, timedOut, aborted }),\n\t\t\t\t(error: unknown) => reject(error),\n\t\t\t);\n\t\t});\n\t});\n}\n\nfunction waitForChild(child: ReturnType<typeof spawn>): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tchild.once(\"error\", () => resolve());\n\t\tchild.once(\"close\", () => resolve());\n\t});\n}\n\nasync function killProcessTree(pid: number, child: ReturnType<typeof spawn>): Promise<void> {\n\tif (process.platform === \"win32\") {\n\t\tconst killer = spawn(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\tstdio: \"ignore\",\n\t\t\twindowsHide: true,\n\t\t});\n\t\tawait waitForChild(killer);\n\t\treturn;\n\t}\n\n\ttry {\n\t\tprocess.kill(-pid, \"SIGKILL\");\n\t} catch {\n\t\ttry {\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t} catch {\n\t\t\t// The process already exited between the timeout and termination request.\n\t\t}\n\t}\n}\n\nexport function createLocalBashOperations(): BashOperations {\n\treturn { run: waitForProcess };\n}\n\nfunction formatResult(stdout: OutputSnapshot, stderr: OutputSnapshot, execution: BashExecution): string {\n\tconst status: BashStatus = {\n\t\texitCode: execution.exitCode,\n\t\ttimedOut: execution.timedOut,\n\t\taborted: execution.aborted,\n\t\tstdoutTruncated: stdout.truncated,\n\t\tstderrTruncated: stderr.truncated,\n\t};\n\treturn `stdout:\\n${stdout.text || \"(empty)\"}\\n\\nstderr:\\n${stderr.text || \"(empty)\"}\\n\\nstatus: ${JSON.stringify(status)}`;\n}\n\nexport function createBashTool(allowedRoot: string, options: BashToolOptions = {}): BashTool {\n\tconst operations = options.operations ?? createLocalBashOperations();\n\tconst maxOutputBytes = options.maxOutputBytes ?? DEFAULT_BASH_MAX_OUTPUT_BYTES;\n\tconst runtime = resolveShellRuntime();\n\tassertMaxOutputBytes(maxOutputBytes);\n\n\treturn {\n\t\tname: \"bash\",\n\t\tdescription: runtime.description,\n\t\tparameters: bashParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.command.length === 0) throw new Error(\"command must not be empty\");\n\t\t\tconst timeoutMs = parameters.timeoutMs ?? DEFAULT_BASH_TIMEOUT_MS;\n\t\t\tassertTimeout(timeoutMs);\n\t\t\tconst cwd = await realpath(allowedRoot);\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\tconst stdout = createOutputCapture(maxOutputBytes);\n\t\t\tconst stderr = createOutputCapture(maxOutputBytes);\n\t\t\tconst executionResult = await operations.run(parameters.command, cwd, {\n\t\t\t\tsignal,\n\t\t\t\ttimeoutMs,\n\t\t\t\tonStdout: (chunk) => stdout.append(chunk),\n\t\t\t\tonStderr: (chunk) => stderr.append(chunk),\n\t\t\t});\n\t\t\tconst execution = signal?.aborted ? { ...executionResult, aborted: true } : executionResult;\n\t\t\tconst output = formatResult(stdout.snapshot(), stderr.snapshot(), execution);\n\n\t\t\tif (execution.aborted) throw new Error(`Command aborted\\n\\n${output}`);\n\t\t\tif (execution.timedOut) throw new Error(`Command timed out after ${timeoutMs} ms\\n\\n${output}`);\n\t\t\tif (execution.exitCode !== 0) {\n\t\t\t\tthrow new Error(`Command exited with code ${execution.exitCode ?? \"unknown\"}\\n\\n${output}`);\n\t\t\t}\n\t\t\treturn [{ type: \"text\", text: output }];\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { realpath } from \"node:fs/promises\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\n\nexport const DEFAULT_BASH_TIMEOUT_MS = 30_000;\nexport const MAX_BASH_TIMEOUT_MS = 300_000;\nexport const DEFAULT_BASH_MAX_OUTPUT_BYTES = 50 * 1024;\n\nexport const bashParameters = Type.Object({\n\tcommand: Type.String({ minLength: 1 }),\n\ttimeoutMs: Type.Optional(Type.Integer({ minimum: 1, maximum: MAX_BASH_TIMEOUT_MS })),\n});\n\nexport type BashParameters = Static<typeof bashParameters>;\n\nexport interface BashRunOptions {\n\treadonly signal?: AbortSignal;\n\treadonly timeoutMs: number;\n\treadonly onStdout: (chunk: Buffer) => void;\n\treadonly onStderr: (chunk: Buffer) => void;\n}\n\nexport interface BashExecution {\n\treadonly exitCode: number | null;\n\treadonly timedOut: boolean;\n\treadonly aborted: boolean;\n}\n\nexport interface BashOperations {\n\trun(command: string, cwd: string, options: BashRunOptions): Promise<BashExecution>;\n}\n\nexport interface BashToolOptions {\n\treadonly operations?: BashOperations;\n\treadonly maxOutputBytes?: number;\n}\n\nexport type BashTool = AgentTool<typeof bashParameters, ToolResultContent[]>;\n\nexport interface ShellRuntime {\n\treadonly executable: string;\n\treadonly detached: boolean;\n\treadonly description: string;\n\targs(command: string): readonly string[];\n}\n\ninterface OutputSnapshot {\n\treadonly text: string;\n\treadonly truncated: boolean;\n}\n\ninterface BashStatus {\n\treadonly exitCode: number | null;\n\treadonly timedOut: boolean;\n\treadonly aborted: boolean;\n\treadonly stdoutTruncated: boolean;\n\treadonly stderrTruncated: boolean;\n}\n\nfunction assertTimeout(timeoutMs: number): void {\n\tif (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_BASH_TIMEOUT_MS) {\n\t\tthrow new Error(`timeoutMs must be an integer between 1 and ${MAX_BASH_TIMEOUT_MS}`);\n\t}\n}\n\nfunction assertMaxOutputBytes(maxOutputBytes: number): void {\n\tif (!Number.isInteger(maxOutputBytes) || maxOutputBytes < 1) {\n\t\tthrow new Error(\"maxOutputBytes must be a positive integer\");\n\t}\n}\n\nfunction createOutputCapture(maxBytes: number): {\n\tappend(chunk: Buffer): void;\n\tsnapshot(): OutputSnapshot;\n} {\n\tconst chunks: Buffer[] = [];\n\tlet bytes = 0;\n\tlet truncated = false;\n\n\treturn {\n\t\tappend(chunk) {\n\t\t\tconst remaining = maxBytes - bytes;\n\t\t\tif (remaining > 0) {\n\t\t\t\tconst kept = chunk.subarray(0, remaining);\n\t\t\t\tchunks.push(Buffer.from(kept));\n\t\t\t\tbytes += kept.length;\n\t\t\t}\n\t\t\tif (chunk.length > remaining) truncated = true;\n\t\t},\n\t\tsnapshot() {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\treturn { text: decoder.write(Buffer.concat(chunks, bytes)), truncated };\n\t\t},\n\t};\n}\n\nfunction createPowerShellScript(command: string): string {\n\treturn [\n\t\t\"$OutputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)\",\n\t\t\"$script:diCodeSucceeded = $false\",\n\t\t\"$script:diCodeExitCode = 0\",\n\t\t\"& {\",\n\t\tcommand,\n\t\t\"$script:diCodeSucceeded = $?\",\n\t\t\"$script:diCodeExitCode = $LASTEXITCODE\",\n\t\t\"} | Out-Default\",\n\t\t\"if ($script:diCodeSucceeded) { exit 0 }\",\n\t\t\"if ($script:diCodeExitCode -is [int] -and $script:diCodeExitCode -ne 0) { exit $script:diCodeExitCode }\",\n\t\t\"exit 1\",\n\t].join(\"\\n\");\n}\n\nexport function resolveShellRuntime(platform: NodeJS.Platform = process.platform): ShellRuntime {\n\tif (platform === \"win32\") {\n\t\treturn {\n\t\t\texecutable: \"powershell.exe\",\n\t\t\tdetached: false,\n\t\t\tdescription:\n\t\t\t\t\"Execute a Windows PowerShell command in the allowed root with timeout and bounded output. Use PowerShell syntax such as Get-ChildItem and Get-Content; do not use Bash syntax.\",\n\t\t\targs: (command) => [\"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\", createPowerShellScript(command)],\n\t\t};\n\t}\n\n\treturn {\n\t\texecutable: \"/bin/sh\",\n\t\tdetached: true,\n\t\tdescription:\n\t\t\t\"Execute a POSIX shell command using /bin/sh in the allowed root with timeout and bounded output. Use portable POSIX syntax; Bash-only extensions may not be available.\",\n\t\targs: (command) => [\"-c\", command],\n\t};\n}\n\nfunction waitForProcess(command: string, cwd: string, options: BashRunOptions): Promise<BashExecution> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (options.signal?.aborted) {\n\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst runtime = resolveShellRuntime();\n\t\tconst child = spawn(runtime.executable, [...runtime.args(command)], {\n\t\t\tcwd,\n\t\t\tdetached: runtime.detached,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\tlet timedOut = false;\n\t\tlet aborted = false;\n\t\tlet settled = false;\n\t\tlet termination: Promise<void> | undefined;\n\n\t\tconst requestTermination = (): void => {\n\t\t\tif (child.pid !== undefined) termination ??= killProcessTree(child.pid, child);\n\t\t};\n\t\tconst onAbort = (): void => {\n\t\t\taborted = true;\n\t\t\trequestTermination();\n\t\t};\n\t\tconst timeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\trequestTermination();\n\t\t}, options.timeoutMs);\n\n\t\toptions.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tif (options.signal?.aborted) onAbort();\n\t\tchild.stdout.on(\"data\", options.onStdout);\n\t\tchild.stderr.on(\"data\", options.onStderr);\n\n\t\tconst cleanup = (): void => {\n\t\t\tclearTimeout(timeout);\n\t\t\toptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t};\n\t\tchild.once(\"error\", (error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(error);\n\t\t});\n\t\tchild.once(\"close\", (exitCode) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tvoid (termination ?? Promise.resolve()).then(\n\t\t\t\t() => resolve({ exitCode, timedOut, aborted }),\n\t\t\t\t(error: unknown) => reject(error),\n\t\t\t);\n\t\t});\n\t});\n}\n\nfunction waitForChild(child: ReturnType<typeof spawn>): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tchild.once(\"error\", () => resolve());\n\t\tchild.once(\"close\", () => resolve());\n\t});\n}\n\nasync function killProcessTree(pid: number, child: ReturnType<typeof spawn>): Promise<void> {\n\tif (process.platform === \"win32\") {\n\t\tconst killer = spawn(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\tstdio: \"ignore\",\n\t\t\twindowsHide: true,\n\t\t});\n\t\tawait waitForChild(killer);\n\t\treturn;\n\t}\n\n\ttry {\n\t\tprocess.kill(-pid, \"SIGKILL\");\n\t} catch {\n\t\ttry {\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t} catch {\n\t\t\t// The process already exited between the timeout and termination request.\n\t\t}\n\t}\n}\n\nexport function createLocalBashOperations(): BashOperations {\n\treturn { run: waitForProcess };\n}\n\nfunction formatResult(stdout: OutputSnapshot, stderr: OutputSnapshot, execution: BashExecution): string {\n\tconst status: BashStatus = {\n\t\texitCode: execution.exitCode,\n\t\ttimedOut: execution.timedOut,\n\t\taborted: execution.aborted,\n\t\tstdoutTruncated: stdout.truncated,\n\t\tstderrTruncated: stderr.truncated,\n\t};\n\treturn `stdout:\\n${stdout.text || \"(empty)\"}\\n\\nstderr:\\n${stderr.text || \"(empty)\"}\\n\\nstatus: ${JSON.stringify(status)}`;\n}\n\nexport function createBashTool(allowedRoot: string, options: BashToolOptions = {}): BashTool {\n\tconst operations = options.operations ?? createLocalBashOperations();\n\tconst maxOutputBytes = options.maxOutputBytes ?? DEFAULT_BASH_MAX_OUTPUT_BYTES;\n\tconst runtime = resolveShellRuntime();\n\tassertMaxOutputBytes(maxOutputBytes);\n\n\treturn {\n\t\tname: \"bash\",\n\t\tdescription: runtime.description,\n\t\tparameters: bashParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.command.length === 0) throw new Error(\"command must not be empty\");\n\t\t\tconst timeoutMs = parameters.timeoutMs ?? DEFAULT_BASH_TIMEOUT_MS;\n\t\t\tassertTimeout(timeoutMs);\n\t\t\tconst cwd = await realpath(allowedRoot);\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\tconst stdout = createOutputCapture(maxOutputBytes);\n\t\t\tconst stderr = createOutputCapture(maxOutputBytes);\n\t\t\tconst executionResult = await operations.run(parameters.command, cwd, {\n\t\t\t\tsignal,\n\t\t\t\ttimeoutMs,\n\t\t\t\tonStdout: (chunk) => stdout.append(chunk),\n\t\t\t\tonStderr: (chunk) => stderr.append(chunk),\n\t\t\t});\n\t\t\tconst execution = signal?.aborted ? { ...executionResult, aborted: true } : executionResult;\n\t\t\tconst output = formatResult(stdout.snapshot(), stderr.snapshot(), execution);\n\n\t\t\tif (execution.aborted) throw new Error(`Command aborted\\n\\n${output}`);\n\t\t\tif (execution.timedOut) throw new Error(`Command timed out after ${timeoutMs} ms\\n\\n${output}`);\n\t\t\tif (execution.exitCode !== 0) {\n\t\t\t\tthrow new Error(`Command exited with code ${execution.exitCode ?? \"unknown\"}\\n\\n${output}`);\n\t\t\t}\n\t\t\treturn [{ type: \"text\", text: output }];\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface Edit {
|
|
2
|
+
readonly oldText: string;
|
|
3
|
+
readonly newText: string;
|
|
4
|
+
}
|
|
5
|
+
export interface EditDiffResult {
|
|
6
|
+
readonly diff: string;
|
|
7
|
+
readonly patch: string;
|
|
8
|
+
readonly firstChangedLine?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface EditDiffError {
|
|
11
|
+
readonly error: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function applyEditsToContent(content: string, edits: readonly Edit[], path: string): {
|
|
14
|
+
baseContent: string;
|
|
15
|
+
newContent: string;
|
|
16
|
+
};
|
|
17
|
+
/** Generate a standard unified patch for storage/export. */
|
|
18
|
+
export declare function generateUnifiedPatch(path: string, oldContent: string, newContent: string, contextLines?: number): string;
|
|
19
|
+
/** Generate the line-numbered display diff consumed by the interactive renderer. */
|
|
20
|
+
export declare function generateDiffString(oldContent: string, newContent: string, contextLines?: number): {
|
|
21
|
+
diff: string;
|
|
22
|
+
firstChangedLine: number | undefined;
|
|
23
|
+
};
|
|
24
|
+
export declare function computeEditsDiff(path: string, edits: readonly Edit[], cwd: string): Promise<EditDiffResult | EditDiffError>;
|
|
25
|
+
//# sourceMappingURL=edit-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-diff.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit-diff.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAqDD,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,IAAI,EAAE,MAAM,GACV;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAQ7C;AAED,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,MAAM,CAKnH;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CACjC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,SAAI,GACd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAuDxD;AAED,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,CAmBzC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { access, constants, readFile } from "node:fs/promises";
|
|
2
|
+
import * as Diff from "diff";
|
|
3
|
+
import { resolveAllowedFilePath } from "./path-boundary.js";
|
|
4
|
+
function normalizeToLF(text) {
|
|
5
|
+
return text.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
|
6
|
+
}
|
|
7
|
+
function stripBom(text) {
|
|
8
|
+
return text.startsWith("\uFEFF") ? text.slice(1) : text;
|
|
9
|
+
}
|
|
10
|
+
function findMatches(content, search) {
|
|
11
|
+
const matches = [];
|
|
12
|
+
let offset = 0;
|
|
13
|
+
while (offset <= content.length - search.length) {
|
|
14
|
+
const index = content.indexOf(search, offset);
|
|
15
|
+
if (index < 0)
|
|
16
|
+
break;
|
|
17
|
+
matches.push(index);
|
|
18
|
+
offset = index + 1;
|
|
19
|
+
}
|
|
20
|
+
return matches;
|
|
21
|
+
}
|
|
22
|
+
function matchEdits(content, edits, path) {
|
|
23
|
+
const matches = edits.map((edit, index) => {
|
|
24
|
+
if (edit.oldText.length === 0)
|
|
25
|
+
throw new Error(`oldText must not be empty in ${path}`);
|
|
26
|
+
const indexes = findMatches(content, edit.oldText);
|
|
27
|
+
if (indexes.length === 0) {
|
|
28
|
+
throw new Error(`Could not find edits[${index}] in ${path}. The oldText must match exactly.`);
|
|
29
|
+
}
|
|
30
|
+
if (indexes.length > 1) {
|
|
31
|
+
throw new Error(`Found ${indexes.length} occurrences of edits[${index}] in ${path}. Each oldText must be unique.`);
|
|
32
|
+
}
|
|
33
|
+
return { ...edit, matchIndex: indexes[0], matchLength: edit.oldText.length };
|
|
34
|
+
});
|
|
35
|
+
const ordered = [...matches].sort((left, right) => left.matchIndex - right.matchIndex);
|
|
36
|
+
for (let index = 1; index < ordered.length; index++) {
|
|
37
|
+
const previous = ordered[index - 1];
|
|
38
|
+
const current = ordered[index];
|
|
39
|
+
if (current.matchIndex < previous.matchIndex + previous.matchLength) {
|
|
40
|
+
throw new Error(`Edits overlap in ${path}. Merge overlapping regions into one edit.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return matches;
|
|
44
|
+
}
|
|
45
|
+
export function applyEditsToContent(content, edits, path) {
|
|
46
|
+
if (edits.length === 0)
|
|
47
|
+
throw new Error(`At least one edit is required for ${path}`);
|
|
48
|
+
const matched = matchEdits(content, edits, path).sort((left, right) => right.matchIndex - left.matchIndex);
|
|
49
|
+
let result = content;
|
|
50
|
+
for (const edit of matched) {
|
|
51
|
+
result = `${result.slice(0, edit.matchIndex)}${edit.newText}${result.slice(edit.matchIndex + edit.matchLength)}`;
|
|
52
|
+
}
|
|
53
|
+
return { baseContent: content, newContent: result };
|
|
54
|
+
}
|
|
55
|
+
/** Generate a standard unified patch for storage/export. */
|
|
56
|
+
export function generateUnifiedPatch(path, oldContent, newContent, contextLines = 4) {
|
|
57
|
+
return Diff.createTwoFilesPatch(path, path, oldContent, newContent, undefined, undefined, {
|
|
58
|
+
context: contextLines,
|
|
59
|
+
headerOptions: Diff.FILE_HEADERS_ONLY,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Generate the line-numbered display diff consumed by the interactive renderer. */
|
|
63
|
+
export function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
64
|
+
const parts = Diff.diffLines(oldContent, newContent, { newlineIsToken: false });
|
|
65
|
+
const oldLines = oldContent.split("\n");
|
|
66
|
+
const newLines = newContent.split("\n");
|
|
67
|
+
const lineNumWidth = String(Math.max(oldLines.length, newLines.length)).length;
|
|
68
|
+
const output = [];
|
|
69
|
+
let oldLine = 1;
|
|
70
|
+
let newLine = 1;
|
|
71
|
+
let firstChangedLine;
|
|
72
|
+
let changedSeen = false;
|
|
73
|
+
for (let partIndex = 0; partIndex < parts.length; partIndex++) {
|
|
74
|
+
const part = parts[partIndex];
|
|
75
|
+
const lines = part.value.split("\n");
|
|
76
|
+
if (lines.at(-1) === "")
|
|
77
|
+
lines.pop();
|
|
78
|
+
if (part.removed) {
|
|
79
|
+
if (firstChangedLine === undefined && lines.length > 0)
|
|
80
|
+
firstChangedLine = newLine;
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
output.push(`-${String(oldLine).padStart(lineNumWidth, " ")} ${line}`);
|
|
83
|
+
oldLine += 1;
|
|
84
|
+
}
|
|
85
|
+
changedSeen = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (part.added) {
|
|
89
|
+
if (firstChangedLine === undefined && lines.length > 0)
|
|
90
|
+
firstChangedLine = newLine;
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
output.push(`+${String(newLine).padStart(lineNumWidth, " ")} ${line}`);
|
|
93
|
+
newLine += 1;
|
|
94
|
+
}
|
|
95
|
+
changedSeen = true;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const changedLater = parts.slice(partIndex + 1).some((candidate) => candidate.added || candidate.removed);
|
|
99
|
+
const visibleIndexes = !changedSeen && !changedLater
|
|
100
|
+
? lines.map((_line, index) => index)
|
|
101
|
+
: !changedSeen && changedLater
|
|
102
|
+
? lines.map((_line, index) => index).slice(-contextLines)
|
|
103
|
+
: changedSeen && changedLater
|
|
104
|
+
? lines.length <= contextLines * 2
|
|
105
|
+
? lines.map((_line, index) => index)
|
|
106
|
+
: [
|
|
107
|
+
...lines.map((_line, index) => index).slice(0, contextLines),
|
|
108
|
+
...lines.map((_line, index) => index).slice(-contextLines),
|
|
109
|
+
]
|
|
110
|
+
: lines.map((_line, index) => index).slice(0, contextLines);
|
|
111
|
+
for (const lineIndex of visibleIndexes) {
|
|
112
|
+
output.push(` ${String(newLine + lineIndex).padStart(lineNumWidth, " ")} ${lines[lineIndex]}`);
|
|
113
|
+
}
|
|
114
|
+
oldLine += lines.length;
|
|
115
|
+
newLine += lines.length;
|
|
116
|
+
}
|
|
117
|
+
return { diff: output.join("\n"), firstChangedLine };
|
|
118
|
+
}
|
|
119
|
+
export async function computeEditsDiff(path, edits, cwd) {
|
|
120
|
+
try {
|
|
121
|
+
const absolutePath = await resolveAllowedFilePath(path, cwd);
|
|
122
|
+
await access(absolutePath, constants.R_OK);
|
|
123
|
+
const rawContent = stripBom(await readFile(absolutePath, "utf8"));
|
|
124
|
+
const baseContent = normalizeToLF(rawContent);
|
|
125
|
+
const applied = applyEditsToContent(baseContent, edits.map((edit) => ({ oldText: normalizeToLF(edit.oldText), newText: normalizeToLF(edit.newText) })), path);
|
|
126
|
+
const display = generateDiffString(applied.baseContent, applied.newContent);
|
|
127
|
+
return {
|
|
128
|
+
...display,
|
|
129
|
+
patch: generateUnifiedPatch(path, applied.baseContent, applied.newContent),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (cause) {
|
|
133
|
+
return { error: cause instanceof Error ? cause.message : String(cause) };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=edit-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-diff.js","sourceRoot":"","sources":["../../../src/core/tools/edit-diff.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, constants, readFile } from \"node:fs/promises\";\nimport * as Diff from \"diff\";\nimport { resolveAllowedFilePath } from \"./path-boundary.ts\";\n\nexport interface Edit {\n\treadonly oldText: string;\n\treadonly newText: string;\n}\n\nexport interface EditDiffResult {\n\treadonly diff: string;\n\treadonly patch: string;\n\treadonly firstChangedLine?: number;\n}\n\nexport interface EditDiffError {\n\treadonly error: string;\n}\n\ninterface MatchedEdit extends Edit {\n\treadonly matchIndex: number;\n\treadonly matchLength: number;\n}\n\nfunction normalizeToLF(text: string): string {\n\treturn text.replaceAll(\"\\r\\n\", \"\\n\").replaceAll(\"\\r\", \"\\n\");\n}\n\nfunction stripBom(text: string): string {\n\treturn text.startsWith(\"\\uFEFF\") ? text.slice(1) : text;\n}\n\nfunction findMatches(content: string, search: string): number[] {\n\tconst matches: number[] = [];\n\tlet offset = 0;\n\twhile (offset <= content.length - search.length) {\n\t\tconst index = content.indexOf(search, offset);\n\t\tif (index < 0) break;\n\t\tmatches.push(index);\n\t\toffset = index + 1;\n\t}\n\treturn matches;\n}\n\nfunction matchEdits(content: string, edits: readonly Edit[], path: string): MatchedEdit[] {\n\tconst matches = edits.map((edit, index) => {\n\t\tif (edit.oldText.length === 0) throw new Error(`oldText must not be empty in ${path}`);\n\t\tconst indexes = findMatches(content, edit.oldText);\n\t\tif (indexes.length === 0) {\n\t\t\tthrow new Error(`Could not find edits[${index}] in ${path}. The oldText must match exactly.`);\n\t\t}\n\t\tif (indexes.length > 1) {\n\t\t\tthrow new Error(\n\t\t\t\t`Found ${indexes.length} occurrences of edits[${index}] in ${path}. Each oldText must be unique.`,\n\t\t\t);\n\t\t}\n\t\treturn { ...edit, matchIndex: indexes[0], matchLength: edit.oldText.length };\n\t});\n\n\tconst ordered = [...matches].sort((left, right) => left.matchIndex - right.matchIndex);\n\tfor (let index = 1; index < ordered.length; index++) {\n\t\tconst previous = ordered[index - 1];\n\t\tconst current = ordered[index];\n\t\tif (current.matchIndex < previous.matchIndex + previous.matchLength) {\n\t\t\tthrow new Error(`Edits overlap in ${path}. Merge overlapping regions into one edit.`);\n\t\t}\n\t}\n\treturn matches;\n}\n\nexport function applyEditsToContent(\n\tcontent: string,\n\tedits: readonly Edit[],\n\tpath: string,\n): { baseContent: string; newContent: string } {\n\tif (edits.length === 0) throw new Error(`At least one edit is required for ${path}`);\n\tconst matched = matchEdits(content, edits, path).sort((left, right) => right.matchIndex - left.matchIndex);\n\tlet result = content;\n\tfor (const edit of matched) {\n\t\tresult = `${result.slice(0, edit.matchIndex)}${edit.newText}${result.slice(edit.matchIndex + edit.matchLength)}`;\n\t}\n\treturn { baseContent: content, newContent: result };\n}\n\n/** Generate a standard unified patch for storage/export. */\nexport function generateUnifiedPatch(path: string, oldContent: string, newContent: string, contextLines = 4): string {\n\treturn Diff.createTwoFilesPatch(path, path, oldContent, newContent, undefined, undefined, {\n\t\tcontext: contextLines,\n\t\theaderOptions: Diff.FILE_HEADERS_ONLY,\n\t});\n}\n\n/** Generate the line-numbered display diff consumed by the interactive renderer. */\nexport function generateDiffString(\n\toldContent: string,\n\tnewContent: string,\n\tcontextLines = 4,\n): { diff: string; firstChangedLine: number | undefined } {\n\tconst parts = Diff.diffLines(oldContent, newContent, { newlineIsToken: false });\n\tconst oldLines = oldContent.split(\"\\n\");\n\tconst newLines = newContent.split(\"\\n\");\n\tconst lineNumWidth = String(Math.max(oldLines.length, newLines.length)).length;\n\tconst output: string[] = [];\n\tlet oldLine = 1;\n\tlet newLine = 1;\n\tlet firstChangedLine: number | undefined;\n\tlet changedSeen = false;\n\tfor (let partIndex = 0; partIndex < parts.length; partIndex++) {\n\t\tconst part = parts[partIndex];\n\t\tconst lines = part.value.split(\"\\n\");\n\t\tif (lines.at(-1) === \"\") lines.pop();\n\t\tif (part.removed) {\n\t\t\tif (firstChangedLine === undefined && lines.length > 0) firstChangedLine = newLine;\n\t\t\tfor (const line of lines) {\n\t\t\t\toutput.push(`-${String(oldLine).padStart(lineNumWidth, \" \")} ${line}`);\n\t\t\t\toldLine += 1;\n\t\t\t}\n\t\t\tchangedSeen = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (part.added) {\n\t\t\tif (firstChangedLine === undefined && lines.length > 0) firstChangedLine = newLine;\n\t\t\tfor (const line of lines) {\n\t\t\t\toutput.push(`+${String(newLine).padStart(lineNumWidth, \" \")} ${line}`);\n\t\t\t\tnewLine += 1;\n\t\t\t}\n\t\t\tchangedSeen = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst changedLater = parts.slice(partIndex + 1).some((candidate) => candidate.added || candidate.removed);\n\t\tconst visibleIndexes =\n\t\t\t!changedSeen && !changedLater\n\t\t\t\t? lines.map((_line, index) => index)\n\t\t\t\t: !changedSeen && changedLater\n\t\t\t\t\t? lines.map((_line, index) => index).slice(-contextLines)\n\t\t\t\t\t: changedSeen && changedLater\n\t\t\t\t\t\t? lines.length <= contextLines * 2\n\t\t\t\t\t\t\t? lines.map((_line, index) => index)\n\t\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\t\t...lines.map((_line, index) => index).slice(0, contextLines),\n\t\t\t\t\t\t\t\t\t...lines.map((_line, index) => index).slice(-contextLines),\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: lines.map((_line, index) => index).slice(0, contextLines);\n\t\tfor (const lineIndex of visibleIndexes) {\n\t\t\toutput.push(` ${String(newLine + lineIndex).padStart(lineNumWidth, \" \")} ${lines[lineIndex]}`);\n\t\t}\n\t\toldLine += lines.length;\n\t\tnewLine += lines.length;\n\t}\n\n\treturn { diff: output.join(\"\\n\"), firstChangedLine };\n}\n\nexport async function computeEditsDiff(\n\tpath: string,\n\tedits: readonly Edit[],\n\tcwd: string,\n): Promise<EditDiffResult | EditDiffError> {\n\ttry {\n\t\tconst absolutePath = await resolveAllowedFilePath(path, cwd);\n\t\tawait access(absolutePath, constants.R_OK);\n\t\tconst rawContent = stripBom(await readFile(absolutePath, \"utf8\"));\n\t\tconst baseContent = normalizeToLF(rawContent);\n\t\tconst applied = applyEditsToContent(\n\t\t\tbaseContent,\n\t\t\tedits.map((edit) => ({ oldText: normalizeToLF(edit.oldText), newText: normalizeToLF(edit.newText) })),\n\t\t\tpath,\n\t\t);\n\t\tconst display = generateDiffString(applied.baseContent, applied.newContent);\n\t\treturn {\n\t\t\t...display,\n\t\t\tpatch: generateUnifiedPatch(path, applied.baseContent, applied.newContent),\n\t\t};\n\t} catch (cause) {\n\t\treturn { error: cause instanceof Error ? cause.message : String(cause) };\n\t}\n}\n"]}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import type { AgentTool } from "@di-code/agent";
|
|
1
|
+
import type { AgentTool, ToolExecutionResult } from "@di-code/agent";
|
|
2
2
|
import { type Static, Type } from "@di-code/ai";
|
|
3
3
|
export declare const editParameters: Type.TObject<{
|
|
4
4
|
path: Type.TString;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
edits: Type.TOptional<Type.TArray<Type.TObject<{
|
|
6
|
+
oldText: Type.TString;
|
|
7
|
+
newText: Type.TString;
|
|
8
|
+
}>>>;
|
|
9
|
+
oldText: Type.TOptional<Type.TString>;
|
|
10
|
+
newText: Type.TOptional<Type.TString>;
|
|
7
11
|
}>;
|
|
8
12
|
export type EditParameters = Static<typeof editParameters>;
|
|
9
13
|
export interface EditOperations {
|
|
@@ -13,6 +17,11 @@ export interface EditOperations {
|
|
|
13
17
|
export interface EditToolOptions {
|
|
14
18
|
readonly operations?: EditOperations;
|
|
15
19
|
}
|
|
16
|
-
export
|
|
20
|
+
export interface EditToolDetails {
|
|
21
|
+
readonly diff: string;
|
|
22
|
+
readonly patch: string;
|
|
23
|
+
readonly firstChangedLine?: number;
|
|
24
|
+
}
|
|
25
|
+
export type EditTool = AgentTool<typeof editParameters, ToolExecutionResult<EditToolDetails>>;
|
|
17
26
|
export declare function createEditTool(allowedRoot: string, options?: EditToolOptions): EditTool;
|
|
18
27
|
//# sourceMappingURL=edit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,KAAK,MAAM,EAA0B,IAAI,EAAE,MAAM,aAAa,CAAC;AAUxE,eAAO,MAAM,cAAc;;;;;;;;EAMzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC;CACrC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,cAAc,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC,CAAC;AAmE9F,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CA8D3F"}
|
package/dist/core/tools/edit.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { readFile as fsReadFile, writeFile as fsWriteFile } from "node:fs/promises";
|
|
2
2
|
import { Type } from "@di-code/ai";
|
|
3
|
+
import { applyEditsToContent, generateDiffString, generateUnifiedPatch } from "./edit-diff.js";
|
|
3
4
|
import { withFileMutationQueue } from "./file-mutation-queue.js";
|
|
4
5
|
import { resolveAllowedFilePath } from "./path-boundary.js";
|
|
6
|
+
const editItemParameters = Type.Object({
|
|
7
|
+
oldText: Type.String(),
|
|
8
|
+
newText: Type.String(),
|
|
9
|
+
});
|
|
5
10
|
export const editParameters = Type.Object({
|
|
6
11
|
path: Type.String({ minLength: 1 }),
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
edits: Type.Optional(Type.Array(editItemParameters, { minItems: 1 })),
|
|
13
|
+
// Compatibility with the original di-code single-edit contract.
|
|
14
|
+
oldText: Type.Optional(Type.String()),
|
|
15
|
+
newText: Type.Optional(Type.String()),
|
|
9
16
|
});
|
|
10
17
|
const defaultEditOperations = {
|
|
11
18
|
async readFile(filePath) {
|
|
@@ -28,21 +35,6 @@ function restoreLineEndings(text, ending) {
|
|
|
28
35
|
function detectLineEnding(text) {
|
|
29
36
|
return text.includes("\r\n") ? "\r\n" : "\n";
|
|
30
37
|
}
|
|
31
|
-
function countMatches(text, search) {
|
|
32
|
-
let count = 0;
|
|
33
|
-
let offset = 0;
|
|
34
|
-
while (true) {
|
|
35
|
-
const index = text.indexOf(search, offset);
|
|
36
|
-
if (index === -1)
|
|
37
|
-
return count;
|
|
38
|
-
count += 1;
|
|
39
|
-
offset = index + 1;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function replaceOnce(text, oldText, newText) {
|
|
43
|
-
const index = text.indexOf(oldText);
|
|
44
|
-
return `${text.slice(0, index)}${newText}${text.slice(index + oldText.length)}`;
|
|
45
|
-
}
|
|
46
38
|
function hasUtf8Bom(bytes) {
|
|
47
39
|
return bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf;
|
|
48
40
|
}
|
|
@@ -60,18 +52,42 @@ function decodeUtf8(bytes, path) {
|
|
|
60
52
|
throw new Error(`File is not valid UTF-8: ${path}`);
|
|
61
53
|
return text;
|
|
62
54
|
}
|
|
55
|
+
function normalizeEdits(parameters, path) {
|
|
56
|
+
if (parameters.edits) {
|
|
57
|
+
return parameters.edits.map((edit) => ({ oldText: edit.oldText, newText: edit.newText }));
|
|
58
|
+
}
|
|
59
|
+
if (typeof parameters.oldText === "string" && typeof parameters.newText === "string") {
|
|
60
|
+
return [{ oldText: parameters.oldText, newText: parameters.newText }];
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`edit requires edits[] or oldText/newText in ${path}`);
|
|
63
|
+
}
|
|
64
|
+
function legacyError(cause, path, legacy) {
|
|
65
|
+
if (!legacy || !(cause instanceof Error))
|
|
66
|
+
throw cause;
|
|
67
|
+
if (cause.message.includes("oldText must not be empty"))
|
|
68
|
+
throw cause;
|
|
69
|
+
if (cause.message.includes("Could not find edits[0]")) {
|
|
70
|
+
throw new Error(`Text to replace was not found in ${path}`);
|
|
71
|
+
}
|
|
72
|
+
if (cause.message.includes("Found ") && cause.message.includes("occurrences of edits[0]")) {
|
|
73
|
+
throw new Error(`Text to replace is ambiguous in ${path}`);
|
|
74
|
+
}
|
|
75
|
+
throw cause;
|
|
76
|
+
}
|
|
63
77
|
export function createEditTool(allowedRoot, options = {}) {
|
|
64
78
|
const operations = options.operations ?? defaultEditOperations;
|
|
65
79
|
return {
|
|
66
80
|
name: "edit",
|
|
67
|
-
description: "
|
|
81
|
+
description: "Edit one UTF-8 text file using one or more unique exact replacements in edits[]. Legacy oldText/newText is also accepted.",
|
|
68
82
|
parameters: editParameters,
|
|
69
83
|
async execute(_toolCallId, parameters, signal) {
|
|
70
84
|
throwIfAborted(signal);
|
|
71
85
|
if (parameters.path.length === 0)
|
|
72
86
|
throw new Error("path must not be empty");
|
|
73
|
-
if (parameters.oldText
|
|
87
|
+
if (parameters.edits === undefined && parameters.oldText === "")
|
|
74
88
|
throw new Error("oldText must not be empty");
|
|
89
|
+
const edits = normalizeEdits(parameters, parameters.path);
|
|
90
|
+
const legacy = parameters.edits === undefined;
|
|
75
91
|
const absolutePath = await resolveAllowedFilePath(parameters.path, allowedRoot);
|
|
76
92
|
return withFileMutationQueue(absolutePath, async () => {
|
|
77
93
|
throwIfAborted(signal);
|
|
@@ -82,24 +98,42 @@ export function createEditTool(allowedRoot, options = {}) {
|
|
|
82
98
|
const hasBom = hasUtf8Bom(originalBytes);
|
|
83
99
|
const originalText = decodeUtf8(originalBytes, parameters.path).replace(/^\uFEFF/, "");
|
|
84
100
|
const ending = detectLineEnding(originalText);
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
const baseContent = normalizeLineEndings(originalText);
|
|
102
|
+
const normalizedEdits = edits.map((edit) => ({
|
|
103
|
+
oldText: normalizeLineEndings(edit.oldText),
|
|
104
|
+
newText: normalizeLineEndings(edit.newText),
|
|
105
|
+
}));
|
|
106
|
+
let newContent;
|
|
107
|
+
try {
|
|
108
|
+
newContent = applyEditsToContent(baseContent, normalizedEdits, parameters.path).newContent;
|
|
109
|
+
}
|
|
110
|
+
catch (cause) {
|
|
111
|
+
legacyError(cause, parameters.path, legacy);
|
|
112
|
+
}
|
|
94
113
|
throwIfAborted(signal);
|
|
95
114
|
const currentBytes = await operations.readFile(absolutePath);
|
|
96
115
|
throwIfAborted(signal);
|
|
97
116
|
if (!currentBytes.equals(originalBytes)) {
|
|
98
117
|
throw new Error(`File changed during edit: ${parameters.path}`);
|
|
99
118
|
}
|
|
100
|
-
await operations.writeFile(absolutePath, `${hasBom ? "\uFEFF" : ""}${
|
|
119
|
+
await operations.writeFile(absolutePath, `${hasBom ? "\uFEFF" : ""}${restoreLineEndings(newContent, ending)}`);
|
|
101
120
|
throwIfAborted(signal);
|
|
102
|
-
|
|
121
|
+
const display = generateDiffString(baseContent, newContent);
|
|
122
|
+
return {
|
|
123
|
+
content: [
|
|
124
|
+
{
|
|
125
|
+
type: "text",
|
|
126
|
+
text: legacy
|
|
127
|
+
? `Successfully replaced text in ${parameters.path}`
|
|
128
|
+
: `Successfully replaced ${normalizedEdits.length} block(s) in ${parameters.path}.`,
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
details: {
|
|
132
|
+
diff: display.diff,
|
|
133
|
+
patch: generateUnifiedPatch(parameters.path, baseContent, newContent),
|
|
134
|
+
...(display.firstChangedLine === undefined ? {} : { firstChangedLine: display.firstChangedLine }),
|
|
135
|
+
},
|
|
136
|
+
};
|
|
103
137
|
});
|
|
104
138
|
},
|
|
105
139
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile as fsReadFile, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveAllowedFilePath } from \"./path-boundary.ts\";\n\nexport const editParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\toldText: Type.String({ minLength: 1 }),\n\tnewText: Type.String(),\n});\n\nexport type EditParameters = Static<typeof editParameters>;\n\nexport interface EditOperations {\n\treadFile(filePath: string): Promise<Buffer>;\n\twriteFile(filePath: string, content: string): Promise<void>;\n}\n\nexport interface EditToolOptions {\n\treadonly operations?: EditOperations;\n}\n\nexport type EditTool = AgentTool<typeof editParameters>;\n\nconst defaultEditOperations: EditOperations = {\n\tasync readFile(filePath) {\n\t\treturn fsReadFile(filePath);\n\t},\n\tasync writeFile(filePath, content) {\n\t\tawait fsWriteFile(filePath, content, \"utf8\");\n\t},\n};\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n}\n\nfunction normalizeLineEndings(text: string): string {\n\treturn text.replaceAll(\"\\r\\n\", \"\\n\").replaceAll(\"\\r\", \"\\n\");\n}\n\nfunction restoreLineEndings(text: string, ending: \"\\n\" | \"\\r\\n\"): string {\n\treturn ending === \"\\n\" ? text : text.replaceAll(\"\\n\", \"\\r\\n\");\n}\n\nfunction detectLineEnding(text: string): \"\\n\" | \"\\r\\n\" {\n\treturn text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n}\n\nfunction countMatches(text: string, search: string): number {\n\tlet count = 0;\n\tlet offset = 0;\n\twhile (true) {\n\t\tconst index = text.indexOf(search, offset);\n\t\tif (index === -1) return count;\n\t\tcount += 1;\n\t\toffset = index + 1;\n\t}\n}\n\nfunction replaceOnce(text: string, oldText: string, newText: string): string {\n\tconst index = text.indexOf(oldText);\n\treturn `${text.slice(0, index)}${newText}${text.slice(index + oldText.length)}`;\n}\n\nfunction hasUtf8Bom(bytes: Buffer): boolean {\n\treturn bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf;\n}\n\nfunction containsNulByte(bytes: Buffer): boolean {\n\tconst sampleLength = Math.min(bytes.length, 8 * 1024);\n\tfor (let index = 0; index < sampleLength; index++) {\n\t\tif (bytes[index] === 0) return true;\n\t}\n\treturn false;\n}\n\nfunction decodeUtf8(bytes: Buffer, path: string): string {\n\tconst text = bytes.toString(\"utf8\");\n\tif (!Buffer.from(text, \"utf8\").equals(bytes)) throw new Error(`File is not valid UTF-8: ${path}`);\n\treturn text;\n}\n\nexport function createEditTool(allowedRoot: string, options: EditToolOptions = {}): EditTool {\n\tconst operations = options.operations ?? defaultEditOperations;\n\n\treturn {\n\t\tname: \"edit\",\n\t\tdescription: \"Replace one unique exact text block in a UTF-8 text file inside the allowed root.\",\n\t\tparameters: editParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tif (parameters.oldText.length === 0) throw new Error(\"oldText must not be empty\");\n\t\t\tconst absolutePath = await resolveAllowedFilePath(parameters.path, allowedRoot);\n\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tconst originalBytes = await operations.readFile(absolutePath);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tif (containsNulByte(originalBytes)) throw new Error(\"Binary files are not supported by edit\");\n\t\t\t\tconst hasBom = hasUtf8Bom(originalBytes);\n\t\t\t\tconst originalText = decodeUtf8(originalBytes, parameters.path).replace(/^\\uFEFF/, \"\");\n\t\t\t\tconst ending = detectLineEnding(originalText);\n\t\t\t\tconst content = normalizeLineEndings(originalText);\n\t\t\t\tconst oldText = normalizeLineEndings(parameters.oldText);\n\t\t\t\tconst newText = normalizeLineEndings(parameters.newText);\n\t\t\t\tconst matches = countMatches(content, oldText);\n\t\t\t\tif (matches === 0) throw new Error(`Text to replace was not found in ${parameters.path}`);\n\t\t\t\tif (matches > 1) throw new Error(`Text to replace is ambiguous in ${parameters.path}`);\n\n\t\t\t\tconst updatedText = restoreLineEndings(replaceOnce(content, oldText, newText), ending);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tconst currentBytes = await operations.readFile(absolutePath);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tif (!currentBytes.equals(originalBytes)) {\n\t\t\t\t\tthrow new Error(`File changed during edit: ${parameters.path}`);\n\t\t\t\t}\n\t\t\t\tawait operations.writeFile(absolutePath, `${hasBom ? \"\\uFEFF\" : \"\"}${updatedText}`);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\treturn [{ type: \"text\", text: `Successfully replaced text in ${parameters.path}` }];\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile as fsReadFile, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport type { AgentTool, ToolExecutionResult } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { applyEditsToContent, type Edit, generateDiffString, generateUnifiedPatch } from \"./edit-diff.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveAllowedFilePath } from \"./path-boundary.ts\";\n\nconst editItemParameters = Type.Object({\n\toldText: Type.String(),\n\tnewText: Type.String(),\n});\n\nexport const editParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\tedits: Type.Optional(Type.Array(editItemParameters, { minItems: 1 })),\n\t// Compatibility with the original di-code single-edit contract.\n\toldText: Type.Optional(Type.String()),\n\tnewText: Type.Optional(Type.String()),\n});\n\nexport type EditParameters = Static<typeof editParameters>;\n\nexport interface EditOperations {\n\treadFile(filePath: string): Promise<Buffer>;\n\twriteFile(filePath: string, content: string): Promise<void>;\n}\n\nexport interface EditToolOptions {\n\treadonly operations?: EditOperations;\n}\n\nexport interface EditToolDetails {\n\treadonly diff: string;\n\treadonly patch: string;\n\treadonly firstChangedLine?: number;\n}\n\nexport type EditTool = AgentTool<typeof editParameters, ToolExecutionResult<EditToolDetails>>;\n\nconst defaultEditOperations: EditOperations = {\n\tasync readFile(filePath) {\n\t\treturn fsReadFile(filePath);\n\t},\n\tasync writeFile(filePath, content) {\n\t\tawait fsWriteFile(filePath, content, \"utf8\");\n\t},\n};\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n}\n\nfunction normalizeLineEndings(text: string): string {\n\treturn text.replaceAll(\"\\r\\n\", \"\\n\").replaceAll(\"\\r\", \"\\n\");\n}\n\nfunction restoreLineEndings(text: string, ending: \"\\n\" | \"\\r\\n\"): string {\n\treturn ending === \"\\n\" ? text : text.replaceAll(\"\\n\", \"\\r\\n\");\n}\n\nfunction detectLineEnding(text: string): \"\\n\" | \"\\r\\n\" {\n\treturn text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n}\n\nfunction hasUtf8Bom(bytes: Buffer): boolean {\n\treturn bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf;\n}\n\nfunction containsNulByte(bytes: Buffer): boolean {\n\tconst sampleLength = Math.min(bytes.length, 8 * 1024);\n\tfor (let index = 0; index < sampleLength; index++) {\n\t\tif (bytes[index] === 0) return true;\n\t}\n\treturn false;\n}\n\nfunction decodeUtf8(bytes: Buffer, path: string): string {\n\tconst text = bytes.toString(\"utf8\");\n\tif (!Buffer.from(text, \"utf8\").equals(bytes)) throw new Error(`File is not valid UTF-8: ${path}`);\n\treturn text;\n}\n\nfunction normalizeEdits(parameters: EditParameters, path: string): Edit[] {\n\tif (parameters.edits) {\n\t\treturn parameters.edits.map((edit) => ({ oldText: edit.oldText, newText: edit.newText }));\n\t}\n\tif (typeof parameters.oldText === \"string\" && typeof parameters.newText === \"string\") {\n\t\treturn [{ oldText: parameters.oldText, newText: parameters.newText }];\n\t}\n\tthrow new Error(`edit requires edits[] or oldText/newText in ${path}`);\n}\n\nfunction legacyError(cause: unknown, path: string, legacy: boolean): never {\n\tif (!legacy || !(cause instanceof Error)) throw cause;\n\tif (cause.message.includes(\"oldText must not be empty\")) throw cause;\n\tif (cause.message.includes(\"Could not find edits[0]\")) {\n\t\tthrow new Error(`Text to replace was not found in ${path}`);\n\t}\n\tif (cause.message.includes(\"Found \") && cause.message.includes(\"occurrences of edits[0]\")) {\n\t\tthrow new Error(`Text to replace is ambiguous in ${path}`);\n\t}\n\tthrow cause;\n}\n\nexport function createEditTool(allowedRoot: string, options: EditToolOptions = {}): EditTool {\n\tconst operations = options.operations ?? defaultEditOperations;\n\n\treturn {\n\t\tname: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit one UTF-8 text file using one or more unique exact replacements in edits[]. Legacy oldText/newText is also accepted.\",\n\t\tparameters: editParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolExecutionResult<EditToolDetails>> {\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tif (parameters.edits === undefined && parameters.oldText === \"\") throw new Error(\"oldText must not be empty\");\n\t\t\tconst edits = normalizeEdits(parameters, parameters.path);\n\t\t\tconst legacy = parameters.edits === undefined;\n\t\t\tconst absolutePath = await resolveAllowedFilePath(parameters.path, allowedRoot);\n\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tconst originalBytes = await operations.readFile(absolutePath);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tif (containsNulByte(originalBytes)) throw new Error(\"Binary files are not supported by edit\");\n\t\t\t\tconst hasBom = hasUtf8Bom(originalBytes);\n\t\t\t\tconst originalText = decodeUtf8(originalBytes, parameters.path).replace(/^\\uFEFF/, \"\");\n\t\t\t\tconst ending = detectLineEnding(originalText);\n\t\t\t\tconst baseContent = normalizeLineEndings(originalText);\n\t\t\t\tconst normalizedEdits = edits.map((edit) => ({\n\t\t\t\t\toldText: normalizeLineEndings(edit.oldText),\n\t\t\t\t\tnewText: normalizeLineEndings(edit.newText),\n\t\t\t\t}));\n\t\t\t\tlet newContent: string;\n\t\t\t\ttry {\n\t\t\t\t\tnewContent = applyEditsToContent(baseContent, normalizedEdits, parameters.path).newContent;\n\t\t\t\t} catch (cause) {\n\t\t\t\t\tlegacyError(cause, parameters.path, legacy);\n\t\t\t\t}\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tconst currentBytes = await operations.readFile(absolutePath);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tif (!currentBytes.equals(originalBytes)) {\n\t\t\t\t\tthrow new Error(`File changed during edit: ${parameters.path}`);\n\t\t\t\t}\n\t\t\t\tawait operations.writeFile(absolutePath, `${hasBom ? \"\\uFEFF\" : \"\"}${restoreLineEndings(newContent, ending)}`);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tconst display = generateDiffString(baseContent, newContent);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: legacy\n\t\t\t\t\t\t\t\t? `Successfully replaced text in ${parameters.path}`\n\t\t\t\t\t\t\t\t: `Successfully replaced ${normalizedEdits.length} block(s) in ${parameters.path}.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t] satisfies ToolResultContent[],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tdiff: display.diff,\n\t\t\t\t\t\tpatch: generateUnifiedPatch(parameters.path, baseContent, newContent),\n\t\t\t\t\t\t...(display.firstChangedLine === undefined ? {} : { firstChangedLine: display.firstChangedLine }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTool } from "@di-code/agent";
|
|
2
|
-
import { type Static, Type } from "@di-code/ai";
|
|
2
|
+
import { type Static, type ToolResultContent, Type } from "@di-code/ai";
|
|
3
3
|
export declare const DEFAULT_READ_MAX_LINES = 2000;
|
|
4
4
|
export declare const DEFAULT_READ_MAX_BYTES: number;
|
|
5
5
|
export declare const readParameters: Type.TObject<{
|
|
@@ -12,6 +12,6 @@ export interface ReadToolOptions {
|
|
|
12
12
|
readonly maxLines?: number;
|
|
13
13
|
readonly maxBytes?: number;
|
|
14
14
|
}
|
|
15
|
-
export type ReadTool = AgentTool<typeof readParameters>;
|
|
15
|
+
export type ReadTool = AgentTool<typeof readParameters, ToolResultContent[]>;
|
|
16
16
|
export declare function createReadTool(allowedRoot: string, options?: ReadToolOptions): ReadTool;
|
|
17
17
|
//# sourceMappingURL=read.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxE,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAC5C,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD,eAAO,MAAM,cAAc;;;;EAIzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,cAAc,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAuG7E,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CA8B3F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAuC,IAAI,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAC5C,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACnC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACnD,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;CAClD,CAAC,CAAC;AAmBH,SAAS,qBAAqB,CAAC,IAAY,EAAE,KAAyB;IACrE,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6BAA6B,CAAC,CAAC;IACvD,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IACrC,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAwB,EAAE,MAAc,EAAE,KAAyB;IAC5F,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,IAAI,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,wCAAwC,CAAC,CAAC;QAC1F,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACpF,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,CAAC;IAC9B,IAAI,UAAU,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,2BAA2B,KAAK,CAAC,MAAM,eAAe,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7C,OAAO;QACN,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,SAAS,EAAE,MAAM;QACjB,OAAO;QACP,UAAU,EAAE,KAAK,CAAC,MAAM;QACxB,WAAW,EAAE,KAAK,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;KACvF,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAkB,EAAE,QAAgB,EAAE,QAAgB;IAChF,IAAI,MAAM,CAAC,OAAO,KAAK,EAAE;QAAE,OAAO,MAAM,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,WAAW,GAA6B,IAAI,CAAC;IAEjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,QAAQ,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;YACjC,WAAW,GAAG,OAAO,CAAC;YACtB,MAAM;QACP,CAAC;QACD,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,KAAK,GAAG,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3E,IAAI,SAAS,GAAG,QAAQ,EAAE,CAAC;YAC1B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YACxF,WAAW,GAAG,OAAO,CAAC;YACtB,MAAM;QACP,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,KAAK,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,WAAW,KAAK,IAAI,IAAI,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;QAAE,WAAW,GAAG,OAAO,CAAC;IAClF,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IACxC,OAAO;QACN,GAAG,MAAM;QACT,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC/C,WAAW;KACX,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,QAAgB;IACxC,IAAI,QAAQ,GAAG,IAAI,KAAK,CAAC;QAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,MAAM,CAAC;IAC3D,OAAO,GAAG,QAAQ,QAAQ,CAAC;AAC5B,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAkB,EAAE,QAAgB;IAC/D,IAAI,MAAM,CAAC,WAAW,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IACvD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;QACrD,OAAO,GAAG,MAAM,CAAC,OAAO,QAAQ,SAAS,mCAAmC,UAAU,gBAAgB,CAAC;IACxG,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7F,OAAO,GAAG,MAAM,CAAC,OAAO,sBAAsB,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,OAAO,MAAM,CAAC,UAAU,GAAG,MAAM,gBAAgB,UAAU,gBAAgB,CAAC;AAC7J,CAAC;AAED,SAAS,eAAe,CAAC,MAAc;IACtC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACvD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY,EAAE,KAAK,EAAE,EAAE,CAAC;QACnD,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,UAA2B,EAAE;IAChF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,sBAAsB,CAAC;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,sBAAsB,CAAC;IAC5D,qBAAqB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC5C,qBAAqB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAE5C,OAAO;QACN,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,uFAAuF;QACpG,UAAU,EAAE,cAAc;QAC1B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM;YAC5C,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC1D,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5E,qBAAqB,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YACnD,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;YACjD,MAAM,YAAY,GAAG,MAAM,sBAAsB,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAChF,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC5C,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC3D,CAAC;YACD,MAAM,UAAU,GAAG,gBAAgB,CAClC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EACnC,UAAU,CAAC,MAAM,IAAI,CAAC,EACtB,UAAU,CAAC,KAAK,CAChB,CAAC;YACF,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACxE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { resolveAllowedFilePath } from \"./path-boundary.ts\";\n\nexport const DEFAULT_READ_MAX_LINES = 2_000;\nexport const DEFAULT_READ_MAX_BYTES = 50 * 1024;\n\nexport const readParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\toffset: Type.Optional(Type.Integer({ minimum: 1 })),\n\tlimit: Type.Optional(Type.Integer({ minimum: 1 })),\n});\n\nexport type ReadParameters = Static<typeof readParameters>;\n\nexport interface ReadToolOptions {\n\treadonly maxLines?: number;\n\treadonly maxBytes?: number;\n}\n\nexport type ReadTool = AgentTool<typeof readParameters>;\n\ninterface TextWindow {\n\treadonly content: string;\n\treadonly startLine: number;\n\treadonly endLine: number;\n\treadonly totalLines: number;\n\treadonly truncatedBy: \"limit\" | \"lines\" | \"bytes\" | null;\n}\n\nfunction assertPositiveInteger(name: string, value: number | undefined): void {\n\tif (value !== undefined && (!Number.isInteger(value) || value < 1)) {\n\t\tthrow new Error(`${name} must be a positive integer`);\n\t}\n}\n\nfunction splitLines(text: string): string[] {\n\tif (text.length === 0) return [];\n\tconst lines = text.split(\"\\n\");\n\tif (text.endsWith(\"\\n\")) lines.pop();\n\treturn lines;\n}\n\nfunction selectUserWindow(lines: readonly string[], offset: number, limit: number | undefined): TextWindow {\n\tif (lines.length === 0) {\n\t\tif (offset > 1) throw new Error(`Offset ${offset} is beyond end of file (0 lines total)`);\n\t\treturn { content: \"\", startLine: 1, endLine: 0, totalLines: 0, truncatedBy: null };\n\t}\n\n\tconst startIndex = offset - 1;\n\tif (startIndex >= lines.length) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${lines.length} lines total)`);\n\t}\n\n\tconst available = lines.slice(startIndex);\n\tconst selected = limit === undefined ? available : available.slice(0, limit);\n\tconst endLine = offset + selected.length - 1;\n\treturn {\n\t\tcontent: selected.join(\"\\n\"),\n\t\tstartLine: offset,\n\t\tendLine,\n\t\ttotalLines: lines.length,\n\t\ttruncatedBy: limit !== undefined && selected.length < available.length ? \"limit\" : null,\n\t};\n}\n\nfunction applyOutputLimits(window: TextWindow, maxLines: number, maxBytes: number): TextWindow {\n\tif (window.content === \"\") return window;\n\tconst lines = window.content.split(\"\\n\");\n\tconst selected: string[] = [];\n\tlet bytes = 0;\n\tlet truncatedBy: \"lines\" | \"bytes\" | null = null;\n\n\tfor (const line of lines) {\n\t\tif (selected.length >= maxLines) {\n\t\t\ttruncatedBy = \"lines\";\n\t\t\tbreak;\n\t\t}\n\t\tconst separatorBytes = selected.length === 0 ? 0 : 1;\n\t\tconst nextBytes = bytes + separatorBytes + Buffer.byteLength(line, \"utf8\");\n\t\tif (nextBytes > maxBytes) {\n\t\t\tif (selected.length === 0) throw new Error(\"A single line exceeds the read byte limit\");\n\t\t\ttruncatedBy = \"bytes\";\n\t\t\tbreak;\n\t\t}\n\t\tselected.push(line);\n\t\tbytes = nextBytes;\n\t}\n\n\tif (truncatedBy === null && selected.length < lines.length) truncatedBy = \"lines\";\n\tif (truncatedBy === null) return window;\n\treturn {\n\t\t...window,\n\t\tcontent: selected.join(\"\\n\"),\n\t\tendLine: window.startLine + selected.length - 1,\n\t\ttruncatedBy,\n\t};\n}\n\nfunction formatByteLimit(maxBytes: number): string {\n\tif (maxBytes % 1024 === 0) return `${maxBytes / 1024} KiB`;\n\treturn `${maxBytes} bytes`;\n}\n\nfunction appendContinuation(window: TextWindow, maxBytes: number): string {\n\tif (window.truncatedBy === null) return window.content;\n\tconst nextOffset = window.endLine + 1;\n\tif (window.truncatedBy === \"limit\") {\n\t\tconst remaining = window.totalLines - window.endLine;\n\t\treturn `${window.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t}\n\tconst reason = window.truncatedBy === \"bytes\" ? ` (${formatByteLimit(maxBytes)} limit)` : \"\";\n\treturn `${window.content}\\n\\n[Showing lines ${window.startLine}-${window.endLine} of ${window.totalLines}${reason}. Use offset=${nextOffset} to continue.]`;\n}\n\nfunction containsNulByte(buffer: Buffer): boolean {\n\tconst sampleLength = Math.min(buffer.length, 8 * 1024);\n\tfor (let index = 0; index < sampleLength; index++) {\n\t\tif (buffer[index] === 0) return true;\n\t}\n\treturn false;\n}\n\nexport function createReadTool(allowedRoot: string, options: ReadToolOptions = {}): ReadTool {\n\tconst maxLines = options.maxLines ?? DEFAULT_READ_MAX_LINES;\n\tconst maxBytes = options.maxBytes ?? DEFAULT_READ_MAX_BYTES;\n\tassertPositiveInteger(\"maxLines\", maxLines);\n\tassertPositiveInteger(\"maxBytes\", maxBytes);\n\n\treturn {\n\t\tname: \"read\",\n\t\tdescription: \"Read a UTF-8 text file inside the allowed root. Use offset and limit for large files.\",\n\t\tparameters: readParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tassertPositiveInteger(\"offset\", parameters.offset);\n\t\t\tassertPositiveInteger(\"limit\", parameters.limit);\n\t\t\tconst absolutePath = await resolveAllowedFilePath(parameters.path, allowedRoot);\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tconst buffer = await readFile(absolutePath);\n\t\t\tif (containsNulByte(buffer)) {\n\t\t\t\tthrow new Error(\"Binary files are not supported by read\");\n\t\t\t}\n\t\t\tconst userWindow = selectUserWindow(\n\t\t\t\tsplitLines(buffer.toString(\"utf8\")),\n\t\t\t\tparameters.offset ?? 1,\n\t\t\t\tparameters.limit,\n\t\t\t);\n\t\t\tconst boundedWindow = applyOutputLimits(userWindow, maxLines, maxBytes);\n\t\t\treturn [{ type: \"text\", text: appendContinuation(boundedWindow, maxBytes) }];\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { resolveAllowedFilePath } from \"./path-boundary.ts\";\n\nexport const DEFAULT_READ_MAX_LINES = 2_000;\nexport const DEFAULT_READ_MAX_BYTES = 50 * 1024;\n\nexport const readParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\toffset: Type.Optional(Type.Integer({ minimum: 1 })),\n\tlimit: Type.Optional(Type.Integer({ minimum: 1 })),\n});\n\nexport type ReadParameters = Static<typeof readParameters>;\n\nexport interface ReadToolOptions {\n\treadonly maxLines?: number;\n\treadonly maxBytes?: number;\n}\n\nexport type ReadTool = AgentTool<typeof readParameters, ToolResultContent[]>;\n\ninterface TextWindow {\n\treadonly content: string;\n\treadonly startLine: number;\n\treadonly endLine: number;\n\treadonly totalLines: number;\n\treadonly truncatedBy: \"limit\" | \"lines\" | \"bytes\" | null;\n}\n\nfunction assertPositiveInteger(name: string, value: number | undefined): void {\n\tif (value !== undefined && (!Number.isInteger(value) || value < 1)) {\n\t\tthrow new Error(`${name} must be a positive integer`);\n\t}\n}\n\nfunction splitLines(text: string): string[] {\n\tif (text.length === 0) return [];\n\tconst lines = text.split(\"\\n\");\n\tif (text.endsWith(\"\\n\")) lines.pop();\n\treturn lines;\n}\n\nfunction selectUserWindow(lines: readonly string[], offset: number, limit: number | undefined): TextWindow {\n\tif (lines.length === 0) {\n\t\tif (offset > 1) throw new Error(`Offset ${offset} is beyond end of file (0 lines total)`);\n\t\treturn { content: \"\", startLine: 1, endLine: 0, totalLines: 0, truncatedBy: null };\n\t}\n\n\tconst startIndex = offset - 1;\n\tif (startIndex >= lines.length) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${lines.length} lines total)`);\n\t}\n\n\tconst available = lines.slice(startIndex);\n\tconst selected = limit === undefined ? available : available.slice(0, limit);\n\tconst endLine = offset + selected.length - 1;\n\treturn {\n\t\tcontent: selected.join(\"\\n\"),\n\t\tstartLine: offset,\n\t\tendLine,\n\t\ttotalLines: lines.length,\n\t\ttruncatedBy: limit !== undefined && selected.length < available.length ? \"limit\" : null,\n\t};\n}\n\nfunction applyOutputLimits(window: TextWindow, maxLines: number, maxBytes: number): TextWindow {\n\tif (window.content === \"\") return window;\n\tconst lines = window.content.split(\"\\n\");\n\tconst selected: string[] = [];\n\tlet bytes = 0;\n\tlet truncatedBy: \"lines\" | \"bytes\" | null = null;\n\n\tfor (const line of lines) {\n\t\tif (selected.length >= maxLines) {\n\t\t\ttruncatedBy = \"lines\";\n\t\t\tbreak;\n\t\t}\n\t\tconst separatorBytes = selected.length === 0 ? 0 : 1;\n\t\tconst nextBytes = bytes + separatorBytes + Buffer.byteLength(line, \"utf8\");\n\t\tif (nextBytes > maxBytes) {\n\t\t\tif (selected.length === 0) throw new Error(\"A single line exceeds the read byte limit\");\n\t\t\ttruncatedBy = \"bytes\";\n\t\t\tbreak;\n\t\t}\n\t\tselected.push(line);\n\t\tbytes = nextBytes;\n\t}\n\n\tif (truncatedBy === null && selected.length < lines.length) truncatedBy = \"lines\";\n\tif (truncatedBy === null) return window;\n\treturn {\n\t\t...window,\n\t\tcontent: selected.join(\"\\n\"),\n\t\tendLine: window.startLine + selected.length - 1,\n\t\ttruncatedBy,\n\t};\n}\n\nfunction formatByteLimit(maxBytes: number): string {\n\tif (maxBytes % 1024 === 0) return `${maxBytes / 1024} KiB`;\n\treturn `${maxBytes} bytes`;\n}\n\nfunction appendContinuation(window: TextWindow, maxBytes: number): string {\n\tif (window.truncatedBy === null) return window.content;\n\tconst nextOffset = window.endLine + 1;\n\tif (window.truncatedBy === \"limit\") {\n\t\tconst remaining = window.totalLines - window.endLine;\n\t\treturn `${window.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t}\n\tconst reason = window.truncatedBy === \"bytes\" ? ` (${formatByteLimit(maxBytes)} limit)` : \"\";\n\treturn `${window.content}\\n\\n[Showing lines ${window.startLine}-${window.endLine} of ${window.totalLines}${reason}. Use offset=${nextOffset} to continue.]`;\n}\n\nfunction containsNulByte(buffer: Buffer): boolean {\n\tconst sampleLength = Math.min(buffer.length, 8 * 1024);\n\tfor (let index = 0; index < sampleLength; index++) {\n\t\tif (buffer[index] === 0) return true;\n\t}\n\treturn false;\n}\n\nexport function createReadTool(allowedRoot: string, options: ReadToolOptions = {}): ReadTool {\n\tconst maxLines = options.maxLines ?? DEFAULT_READ_MAX_LINES;\n\tconst maxBytes = options.maxBytes ?? DEFAULT_READ_MAX_BYTES;\n\tassertPositiveInteger(\"maxLines\", maxLines);\n\tassertPositiveInteger(\"maxBytes\", maxBytes);\n\n\treturn {\n\t\tname: \"read\",\n\t\tdescription: \"Read a UTF-8 text file inside the allowed root. Use offset and limit for large files.\",\n\t\tparameters: readParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tassertPositiveInteger(\"offset\", parameters.offset);\n\t\t\tassertPositiveInteger(\"limit\", parameters.limit);\n\t\t\tconst absolutePath = await resolveAllowedFilePath(parameters.path, allowedRoot);\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tconst buffer = await readFile(absolutePath);\n\t\t\tif (containsNulByte(buffer)) {\n\t\t\t\tthrow new Error(\"Binary files are not supported by read\");\n\t\t\t}\n\t\t\tconst userWindow = selectUserWindow(\n\t\t\t\tsplitLines(buffer.toString(\"utf8\")),\n\t\t\t\tparameters.offset ?? 1,\n\t\t\t\tparameters.limit,\n\t\t\t);\n\t\t\tconst boundedWindow = applyOutputLimits(userWindow, maxLines, maxBytes);\n\t\t\treturn [{ type: \"text\", text: appendContinuation(boundedWindow, maxBytes) }];\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTool } from "@di-code/agent";
|
|
2
|
-
import { type Static, Type } from "@di-code/ai";
|
|
2
|
+
import { type Static, type ToolResultContent, Type } from "@di-code/ai";
|
|
3
3
|
export declare const writeParameters: Type.TObject<{
|
|
4
4
|
path: Type.TString;
|
|
5
5
|
content: Type.TString;
|
|
@@ -12,6 +12,6 @@ export interface WriteOperations {
|
|
|
12
12
|
export interface WriteToolOptions {
|
|
13
13
|
readonly operations?: WriteOperations;
|
|
14
14
|
}
|
|
15
|
-
export type WriteTool = AgentTool<typeof writeParameters>;
|
|
15
|
+
export type WriteTool = AgentTool<typeof writeParameters, ToolResultContent[]>;
|
|
16
16
|
export declare function createWriteTool(allowedRoot: string, options?: WriteToolOptions): WriteTool;
|
|
17
17
|
//# sourceMappingURL=write.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAIxE,eAAO,MAAM,eAAe;;;EAG1B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,eAAe,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAe/E,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAwB9F"}
|