@librechat/agents 3.2.39 → 3.2.42
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/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +4 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +4 -3
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +9 -1
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +4 -0
- package/dist/cjs/messages/cache.cjs +21 -0
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/tools/ReadFile.cjs +2 -2
- package/dist/cjs/tools/ReadFile.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +13 -13
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +87 -7
- package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +11 -11
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +5 -2
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +4 -3
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +10 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -3
- package/dist/esm/messages/cache.mjs +21 -1
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/tools/ReadFile.mjs +2 -2
- package/dist/esm/tools/ReadFile.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +14 -14
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +85 -8
- package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +11 -11
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
- package/dist/types/llm/bedrock/index.d.ts +7 -0
- package/dist/types/messages/cache.d.ts +17 -0
- package/dist/types/tools/ReadFile.d.ts +4 -4
- package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +44 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +2 -0
- package/src/graphs/Graph.ts +17 -5
- package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +12 -12
- package/src/hooks/createWorkspacePolicyHook.ts +7 -6
- package/src/llm/bedrock/index.ts +18 -2
- package/src/llm/bedrock/llm.spec.ts +97 -0
- package/src/messages/cache.test.ts +31 -0
- package/src/messages/cache.ts +25 -0
- package/src/tools/ReadFile.ts +2 -2
- package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +131 -0
- package/src/tools/__tests__/LocalExecutionTools.test.ts +25 -25
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +5 -5
- package/src/tools/__tests__/ReadFile.test.ts +3 -3
- package/src/tools/__tests__/ToolNode.session.test.ts +2 -2
- package/src/tools/__tests__/workspaceSeam.test.ts +2 -2
- package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +18 -13
- package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +165 -9
- package/src/tools/local/LocalCodingTools.ts +14 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloudflareSandboxExecutionEngine.mjs","names":["path"],"sources":["../../../../src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts"],"sourcesContent":["import { PassThrough } from 'stream';\nimport { posix as path } from 'path';\nimport { EventEmitter } from 'events';\nimport type { WriteFileOptions, MakeDirectoryOptions, Stats } from 'fs';\nimport type { ChildProcessWithoutNullStreams } from 'child_process';\nimport type { FileHandle } from 'fs/promises';\nimport type { WorkspaceFS, ReaddirEntry } from '@/tools/local/workspaceFS';\nimport type * as t from '@/types';\nimport {\n LOCAL_SPAWN_TIMEOUT_MS,\n validateBashCommand,\n} from '@/tools/local/LocalExecutionEngine';\n\nconst DEFAULT_WORKSPACE_ROOT = '/workspace';\nconst DEFAULT_TIMEOUT_MS = 60000;\nconst DEFAULT_MAX_OUTPUT_CHARS = 200000;\nconst PROTECTED_TARGET_ARG_RE = /^(?:\\/|~|\\$\\{?HOME\\}?|\\.)(?:\\/?\\.?\\*|\\/)?$/;\nconst DESTRUCTIVE_OP_IN_COMMAND_RE =\n /\\b(?:rm\\s+-[^\\s]*[rf]|chmod\\s+-R|chown\\s+-R)\\b/;\n\ntype SpawnResult = {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n timedOut: boolean;\n};\n\ntype RuntimeCommand = {\n fileName: string;\n source?: string;\n command: string;\n};\n\ntype SandboxRuntimeContext = {\n sandbox: t.CloudflareSandboxRuntime;\n workspaceRoot: string;\n env?: Record<string, string | undefined>;\n timeoutMs: number;\n maxOutputChars: number;\n shell: string;\n};\n\nconst sandboxFactoryCache = new WeakMap<\n t.CloudflareSandboxExecutionConfig,\n Promise<t.CloudflareSandboxRuntime>\n>();\n\nfunction normalizeWorkspaceRoot(workspaceRoot: string): string {\n const normalized = path.normalize(workspaceRoot);\n return normalized === '/' ? normalized : normalized.replace(/\\/+$/, '');\n}\n\nexport function getCloudflareWorkspaceRoot(\n config?: t.CloudflareSandboxExecutionConfig\n): string {\n return normalizeWorkspaceRoot(\n config?.workspaceRoot ?? DEFAULT_WORKSPACE_ROOT\n );\n}\n\nexport async function resolveCloudflareSandbox(\n config: t.CloudflareSandboxExecutionConfig\n): Promise<t.CloudflareSandboxRuntime> {\n const sandbox = config.sandbox;\n if (typeof sandbox !== 'function') {\n return sandbox;\n }\n let cached = sandboxFactoryCache.get(config);\n if (cached == null) {\n cached = Promise.resolve()\n .then(() => sandbox())\n .catch((error: unknown) => {\n sandboxFactoryCache.delete(config);\n throw error;\n });\n sandboxFactoryCache.set(config, cached);\n }\n return cached;\n}\n\nasync function getRuntimeContext(\n config: t.CloudflareSandboxExecutionConfig\n): Promise<SandboxRuntimeContext> {\n return {\n sandbox: await resolveCloudflareSandbox(config),\n workspaceRoot: getCloudflareWorkspaceRoot(config),\n env: config.env,\n timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n maxOutputChars: config.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS,\n shell: config.shell ?? 'bash',\n };\n}\n\nfunction toSandboxPath(filePath: string, workspaceRoot: string): string {\n const raw = filePath === '' ? '.' : filePath;\n const root = normalizeWorkspaceRoot(workspaceRoot);\n const resolved = raw.startsWith('/')\n ? path.normalize(raw)\n : path.resolve(root, raw);\n if (root === '/') {\n return resolved;\n }\n if (resolved === root || resolved.startsWith(`${root}/`)) {\n return resolved;\n }\n throw new Error(\n `Path is outside the Cloudflare sandbox workspace: ${filePath}`\n );\n}\n\nfunction quote(value: string): string {\n if (value === '') {\n return '\\'\\'';\n }\n if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n return value;\n }\n return `'${value.replace(/'/g, '\\'\\\\\\'\\'')}'`;\n}\n\nfunction withInSandboxTimeout(command: string, timeoutMs: number): string {\n const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));\n return `timeout -k 2s ${timeoutSeconds}s ${command}`;\n}\n\nfunction outerTimeoutMs(timeoutMs: number): number {\n return timeoutMs + 5000;\n}\n\nfunction isInSandboxTimeoutExit(exitCode: number | null): boolean {\n return exitCode === 124 || exitCode === 137;\n}\n\nfunction truncateOutput(value: string, maxChars: number): string {\n if (maxChars <= 0 || value.length <= maxChars) {\n return value;\n }\n const head = Math.max(Math.floor(maxChars / 2), 0);\n const tail = Math.max(maxChars - head, 0);\n return `${value.slice(0, head)}\\n...[truncated ${value.length - maxChars} chars]...\\n${value.slice(value.length - tail)}`;\n}\n\nasync function readStream(stream: ReadableStream<Uint8Array>): Promise<Buffer> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));\n}\n\nasync function normalizeReadFileContent(\n result: t.CloudflareSandboxReadFileResult\n): Promise<Buffer> {\n if (typeof result === 'string') {\n return Buffer.from(result, 'utf8');\n }\n if (Buffer.isBuffer(result)) {\n return result;\n }\n if (result instanceof Uint8Array) {\n return Buffer.from(result);\n }\n const content = result.content;\n if (typeof content === 'string') {\n if (result.encoding === 'base64') {\n return Buffer.from(content, 'base64');\n }\n return Buffer.from(content, 'utf8');\n }\n if (Buffer.isBuffer(content)) {\n return content;\n }\n if (content instanceof Uint8Array) {\n return Buffer.from(content);\n }\n return readStream(content);\n}\n\nfunction bytesToStream(bytes: Uint8Array): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller): void {\n controller.enqueue(bytes);\n controller.close();\n },\n });\n}\n\nfunction normalizeWriteFileContent(content: string | Buffer | Uint8Array): {\n content: string | ReadableStream<Uint8Array>;\n options?: { encoding?: string };\n} {\n if (typeof content === 'string') {\n return { content, options: { encoding: 'utf8' } };\n }\n return { content: bytesToStream(content) };\n}\n\nfunction createStats(info: {\n size?: number;\n type?: t.CloudflareSandboxFileInfo['type'];\n}): Stats {\n const type = info.type ?? 'file';\n const now = new Date();\n return {\n size: info.size ?? 0,\n isFile: () => type === 'file',\n isDirectory: () => type === 'directory',\n isSymbolicLink: () => type === 'symlink',\n isBlockDevice: () => false,\n isCharacterDevice: () => false,\n isFIFO: () => false,\n isSocket: () => false,\n dev: 0,\n ino: 0,\n mode: 0,\n nlink: 1,\n uid: 0,\n gid: 0,\n rdev: 0,\n blksize: 0,\n blocks: 0,\n atimeMs: now.getTime(),\n mtimeMs: now.getTime(),\n ctimeMs: now.getTime(),\n birthtimeMs: now.getTime(),\n atime: now,\n mtime: now,\n ctime: now,\n birthtime: now,\n } as Stats;\n}\n\nfunction normalizeFileList(\n result: t.CloudflareSandboxListFilesResult\n): t.CloudflareSandboxFileInfo[] {\n return Array.isArray(result) ? result : result.files;\n}\n\nfunction entryNameFor(\n info: t.CloudflareSandboxFileInfo,\n parentPath: string\n): string {\n if (info.name !== '') {\n return info.name.includes('/') ? path.basename(info.name) : info.name;\n }\n if (info.absolutePath != null && info.absolutePath !== '') {\n return path.basename(info.absolutePath);\n }\n if (info.relativePath != null && info.relativePath !== '') {\n return path.basename(info.relativePath);\n }\n return path.basename(parentPath);\n}\n\nfunction entryAbsolutePath(\n info: t.CloudflareSandboxFileInfo,\n parentPath: string\n): string {\n if (info.absolutePath != null && info.absolutePath !== '') {\n return path.normalize(info.absolutePath);\n }\n if (info.relativePath != null && info.relativePath !== '') {\n return path.resolve(parentPath, info.relativePath);\n }\n return path.resolve(parentPath, info.name);\n}\n\nfunction createDirent(info: t.CloudflareSandboxFileInfo): ReaddirEntry {\n return {\n name: entryNameFor(info, ''),\n isFile: () => (info.type ?? 'file') === 'file',\n isDirectory: () => info.type === 'directory',\n isSymbolicLink: () => info.type === 'symlink',\n };\n}\n\nasync function findChildInfo(\n sandbox: t.CloudflareSandboxRuntime,\n filePath: string\n): Promise<t.CloudflareSandboxFileInfo | undefined> {\n const parent = path.dirname(filePath);\n const basename = path.basename(filePath);\n const entries = normalizeFileList(\n await sandbox.listFiles(parent, { includeHidden: true })\n );\n return entries.find((entry) => {\n const absolute = entryAbsolutePath(entry, parent);\n return absolute === filePath || entryNameFor(entry, parent) === basename;\n });\n}\n\nexport function createCloudflareWorkspaceFS(\n config: t.CloudflareSandboxExecutionConfig\n): WorkspaceFS {\n const workspaceRoot = getCloudflareWorkspaceRoot(config);\n\n const fs: WorkspaceFS = {\n readFile: (async (filePath: string, encoding?: 'utf8') => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const buffer = await normalizeReadFileContent(\n await sandbox.readFile(resolved, encoding ? { encoding } : undefined)\n );\n return encoding != null ? buffer.toString(encoding) : buffer;\n }) as WorkspaceFS['readFile'],\n writeFile: async (\n filePath: string,\n content: string | Buffer,\n _options?: WriteFileOptions\n ) => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const normalized = normalizeWriteFileContent(content);\n await sandbox.writeFile(resolved, normalized.content, normalized.options);\n },\n stat: async (filePath: string) => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n if (resolved === workspaceRoot) {\n const entries = normalizeFileList(\n await sandbox.listFiles(resolved, { includeHidden: true })\n );\n return createStats({ size: entries.length, type: 'directory' });\n }\n const info = await findChildInfo(sandbox, resolved);\n if (info != null) {\n return createStats({ size: info.size, type: info.type });\n }\n try {\n const entries = normalizeFileList(\n await sandbox.listFiles(resolved, { includeHidden: true })\n );\n return createStats({ size: entries.length, type: 'directory' });\n } catch {\n const buffer = await normalizeReadFileContent(\n await sandbox.readFile(resolved)\n );\n return createStats({ size: buffer.length, type: 'file' });\n }\n },\n readdir: (async (filePath: string, options?: { withFileTypes: true }) => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const entries = normalizeFileList(\n await sandbox.listFiles(resolved, { includeHidden: true })\n );\n if (options?.withFileTypes === true) {\n return entries.map(createDirent);\n }\n return entries.map((entry) => entryNameFor(entry, resolved));\n }) as WorkspaceFS['readdir'],\n mkdir: async (filePath: string, options?: MakeDirectoryOptions) => {\n const sandbox = await resolveCloudflareSandbox(config);\n await sandbox.mkdir(toSandboxPath(filePath, workspaceRoot), {\n recursive: options?.recursive,\n });\n },\n realpath: async (filePath: string) =>\n toSandboxPath(filePath, workspaceRoot),\n unlink: async (filePath: string) => {\n const sandbox = await resolveCloudflareSandbox(config);\n await sandbox.deleteFile(toSandboxPath(filePath, workspaceRoot));\n },\n open: async (filePath: string, _flags: 'r') => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const buffer = await normalizeReadFileContent(\n await sandbox.readFile(resolved)\n );\n return {\n read: async (\n target: Buffer,\n offset: number,\n length: number,\n position: number\n ) => {\n const start = Math.max(position, 0);\n const slice = buffer.subarray(start, start + length);\n slice.copy(target, offset);\n return { bytesRead: slice.length, buffer: target };\n },\n close: async () => undefined,\n } as unknown as FileHandle;\n },\n };\n\n return fs;\n}\n\nfunction createCloudflareSpawn(\n config: t.CloudflareSandboxExecutionConfig\n): t.LocalSpawn {\n return (command, args, options) => {\n const stdout = new PassThrough();\n const stderr = new PassThrough();\n const child = new EventEmitter() as ChildProcessWithoutNullStreams;\n const abortController = new AbortController();\n const state = { closed: false };\n /** Read through a function so concurrent `closeOnce` mutation (via\n * `kill()`/abort during an `await`) isn't statically narrowed away. */\n const isClosed = (): boolean => state.closed;\n const closeOnce = (\n exitCode: number | null,\n signal: NodeJS.Signals | null\n ): void => {\n if (state.closed) {\n return;\n }\n state.closed = true;\n stdout.end();\n stderr.end();\n Object.assign(child, {\n exitCode,\n signalCode: signal,\n });\n child.emit('close', exitCode, signal);\n };\n Object.assign(child, {\n stdout,\n stderr,\n stdin: new PassThrough(),\n stdio: [null, stdout, stderr],\n killed: false,\n exitCode: null,\n signalCode: null,\n pid: undefined,\n kill: (signal: NodeJS.Signals = 'SIGTERM') => {\n Object.assign(child, { killed: true, signalCode: signal });\n abortController.abort();\n closeOnce(null, signal);\n return true;\n },\n });\n\n void (async (): Promise<void> => {\n const ctx = await getRuntimeContext(config);\n const rendered = [command, ...args].map(quote).join(' ');\n const spawnTimeoutMs = (\n options as {\n [LOCAL_SPAWN_TIMEOUT_MS]?: number;\n }\n )[LOCAL_SPAWN_TIMEOUT_MS];\n const timeoutMs =\n typeof spawnTimeoutMs === 'number' && Number.isFinite(spawnTimeoutMs)\n ? spawnTimeoutMs\n : ctx.timeoutMs;\n const timedCommand = withInSandboxTimeout(rendered, timeoutMs);\n const cwd =\n options.cwd == null ? ctx.workspaceRoot : options.cwd.toString();\n if (isClosed()) {\n return;\n }\n const execOptions: t.CloudflareSandboxExecOptions = {\n cwd,\n env: ctx.env,\n timeout: outerTimeoutMs(timeoutMs),\n };\n if (ctx.sandbox.supportsExecSignal === true) {\n execOptions.signal = abortController.signal;\n }\n try {\n const result = await ctx.sandbox.exec(timedCommand, execOptions);\n if (isClosed()) {\n return;\n }\n if (result.stdout) stdout.write(result.stdout);\n if (result.stderr) stderr.write(result.stderr);\n closeOnce(result.exitCode, null);\n } catch (error) {\n if (isClosed()) {\n return;\n }\n stderr.write((error as Error).message);\n closeOnce(1, null);\n }\n })();\n\n return child;\n };\n}\n\nexport function createCloudflareLocalExecutionConfig(\n config: t.CloudflareSandboxExecutionConfig\n): t.LocalExecutionConfig {\n const workspaceRoot = getCloudflareWorkspaceRoot(config);\n return {\n cwd: workspaceRoot,\n workspace: { root: workspaceRoot },\n exec: {\n spawn: createCloudflareSpawn(config),\n fs: createCloudflareWorkspaceFS(config),\n sandboxed: true,\n },\n shell: config.shell ?? 'bash',\n timeoutMs: config.timeoutMs,\n maxOutputChars: config.maxOutputChars,\n env: config.env,\n includeCodingTools: config.includeCodingTools,\n compileCheck: config.compileCheck,\n readOnly: config.readOnly,\n allowDangerousCommands: config.allowDangerousCommands,\n bashAst: config.bashAst,\n fileCheckpointing: config.fileCheckpointing,\n maxReadBytes: config.maxReadBytes,\n attachReadAttachments: config.attachReadAttachments,\n maxAttachmentBytes: config.maxAttachmentBytes,\n postEditSyntaxCheck: config.postEditSyntaxCheck,\n };\n}\n\nexport async function validateCloudflareBashCommand(\n command: string,\n args: readonly string[],\n config: t.CloudflareSandboxExecutionConfig\n): Promise<void> {\n const localConfig = createCloudflareLocalExecutionConfig(config);\n const validation = await validateBashCommand(command, localConfig);\n if (!validation.valid) {\n throw new Error(validation.errors.join('\\n'));\n }\n\n if (\n args.length > 0 &&\n config.allowDangerousCommands !== true &&\n DESTRUCTIVE_OP_IN_COMMAND_RE.test(command)\n ) {\n const offending = args.find((arg) => PROTECTED_TARGET_ARG_RE.test(arg));\n if (offending !== undefined) {\n throw new Error(\n `Command matches a destructive command pattern (protected target \"${offending}\" passed via positional arg).`\n );\n }\n }\n}\n\nexport async function executeCloudflareBash(\n command: string,\n config: t.CloudflareSandboxExecutionConfig,\n args: readonly string[] = []\n): Promise<SpawnResult> {\n await validateCloudflareBashCommand(command, args, config);\n const ctx = await getRuntimeContext(config);\n const shellCommand =\n args.length > 0\n ? `${ctx.shell} -lc ${quote(command)} -- ${args.map(quote).join(' ')}`\n : `${ctx.shell} -lc ${quote(command)}`;\n const result = await ctx.sandbox.exec(\n withInSandboxTimeout(shellCommand, ctx.timeoutMs),\n {\n cwd: ctx.workspaceRoot,\n env: ctx.env,\n timeout: outerTimeoutMs(ctx.timeoutMs),\n }\n );\n return {\n stdout: truncateOutput(result.stdout, ctx.maxOutputChars),\n stderr: truncateOutput(result.stderr, ctx.maxOutputChars),\n exitCode: result.exitCode,\n timedOut: isInSandboxTimeoutExit(result.exitCode),\n };\n}\n\nfunction runtimeForCode(\n lang: string,\n tempDir: string,\n code: string,\n args: string[] = [],\n shell = 'bash'\n): RuntimeCommand {\n const fileFor = (name: string): string => path.join(tempDir, name);\n const argText = args.map(quote).join(' ');\n switch (lang) {\n case 'py':\n case 'python':\n return {\n fileName: 'main.py',\n source: code,\n command: `python3 ${quote(fileFor('main.py'))} ${argText}`,\n };\n case 'js':\n case 'javascript':\n return {\n fileName: 'main.js',\n source: code,\n command: `node ${quote(fileFor('main.js'))} ${argText}`,\n };\n case 'ts':\n case 'typescript':\n return {\n fileName: 'main.ts',\n source: code,\n command: `npx --no-install tsx ${quote(fileFor('main.ts'))} ${argText}`,\n };\n case 'php':\n return {\n fileName: 'main.php',\n source: code,\n command: `php ${quote(fileFor('main.php'))} ${argText}`,\n };\n case 'go':\n return {\n fileName: 'main.go',\n source: code,\n command: `go run ${quote(fileFor('main.go'))} ${argText}`,\n };\n case 'rs':\n return {\n fileName: 'main.rs',\n source: code,\n command: `${shell} -lc ${quote(\n `rustc ${quote(fileFor('main.rs'))} -o ${quote(fileFor('main-rs'))} && ${quote(fileFor('main-rs'))} ${argText}`\n )}`,\n };\n case 'c':\n return {\n fileName: 'main.c',\n source: code,\n command: `${shell} -lc ${quote(\n `cc ${quote(fileFor('main.c'))} -o ${quote(fileFor('main-c'))} && ${quote(fileFor('main-c'))} ${argText}`\n )}`,\n };\n case 'cpp':\n return {\n fileName: 'main.cpp',\n source: code,\n command: `${shell} -lc ${quote(\n `c++ ${quote(fileFor('main.cpp'))} -o ${quote(fileFor('main-cpp'))} && ${quote(fileFor('main-cpp'))} ${argText}`\n )}`,\n };\n case 'java':\n return {\n fileName: 'Main.java',\n source: code,\n command: `${shell} -lc ${quote(\n `javac ${quote(fileFor('Main.java'))} && java -cp ${quote(tempDir)} Main ${argText}`\n )}`,\n };\n case 'r':\n return {\n fileName: 'main.R',\n source: code,\n command: `Rscript ${quote(fileFor('main.R'))} ${argText}`,\n };\n case 'd':\n return {\n fileName: 'main.d',\n source: code,\n command: `${shell} -lc ${quote(\n `dmd ${quote(fileFor('main.d'))} -of=${quote(fileFor('main-d'))} && ${quote(fileFor('main-d'))} ${argText}`\n )}`,\n };\n case 'f90':\n return {\n fileName: 'main.f90',\n source: code,\n command: `${shell} -lc ${quote(\n `gfortran ${quote(fileFor('main.f90'))} -o ${quote(fileFor('main-f90'))} && ${quote(fileFor('main-f90'))} ${argText}`\n )}`,\n };\n case 'bash':\n case 'sh':\n return {\n fileName: 'main.sh',\n source: code,\n command: `${shell} -lc ${quote(code)} -- ${argText}`,\n };\n default:\n throw new Error(`Unsupported Cloudflare sandbox runtime: ${lang}`);\n }\n}\n\nexport async function executeCloudflareCode(\n input: { lang: string; code: string; args?: string[] },\n config: t.CloudflareSandboxExecutionConfig\n): Promise<SpawnResult> {\n if (input.lang === 'bash' || input.lang === 'sh') {\n return executeCloudflareBash(input.code, config, input.args ?? []);\n }\n const ctx = await getRuntimeContext(config);\n const id = globalThis.crypto.randomUUID();\n const tempDir = path.join(ctx.workspaceRoot, '.lc-exec', id);\n const runtime = runtimeForCode(\n input.lang,\n tempDir,\n input.code,\n input.args,\n ctx.shell\n );\n await ctx.sandbox.mkdir(tempDir, { recursive: true });\n if (runtime.source != null) {\n await ctx.sandbox.writeFile(\n path.join(tempDir, runtime.fileName),\n runtime.source,\n {\n encoding: 'utf8',\n }\n );\n }\n try {\n const result = await ctx.sandbox.exec(\n withInSandboxTimeout(runtime.command, ctx.timeoutMs),\n {\n cwd: ctx.workspaceRoot,\n env: ctx.env,\n timeout: outerTimeoutMs(ctx.timeoutMs),\n }\n );\n return {\n stdout: truncateOutput(result.stdout, ctx.maxOutputChars),\n stderr: truncateOutput(result.stderr, ctx.maxOutputChars),\n exitCode: result.exitCode,\n timedOut: isInSandboxTimeoutExit(result.exitCode),\n };\n } finally {\n await ctx.sandbox\n .exec(`rm -rf ${quote(tempDir)}`, {\n cwd: ctx.workspaceRoot,\n env: ctx.env,\n timeout: 10000,\n })\n .catch(() => undefined);\n }\n}\n\nexport function formatCloudflareOutput(\n result: SpawnResult,\n cwd: string\n): string {\n let formatted = '';\n if (result.stdout !== '') {\n formatted += `stdout:\\n${result.stdout}\\n`;\n } else {\n formatted += 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n }\n if (result.stderr !== '') {\n formatted += `stderr:\\n${result.stderr}\\n`;\n }\n if (result.exitCode != null && result.exitCode !== 0) {\n formatted += `exit_code: ${result.exitCode}\\n`;\n }\n if (result.timedOut) {\n formatted += 'timed_out: true\\n';\n }\n formatted += `working_directory: ${cwd}`;\n return formatted.trim();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CloudflareSandboxExecutionEngine.mjs","names":["path"],"sources":["../../../../src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts"],"sourcesContent":["import { PassThrough } from 'stream';\nimport { posix as path } from 'path';\nimport { EventEmitter } from 'events';\nimport type { WriteFileOptions, MakeDirectoryOptions, Stats } from 'fs';\nimport type { ChildProcessWithoutNullStreams } from 'child_process';\nimport type { FileHandle } from 'fs/promises';\nimport type { WorkspaceFS, ReaddirEntry } from '@/tools/local/workspaceFS';\nimport type * as t from '@/types';\nimport {\n LOCAL_SPAWN_TIMEOUT_MS,\n validateBashCommand,\n} from '@/tools/local/LocalExecutionEngine';\n\nconst DEFAULT_WORKSPACE_ROOT = '/workspace';\nconst DEFAULT_TIMEOUT_MS = 60000;\nconst DEFAULT_MAX_OUTPUT_CHARS = 200000;\nconst PROTECTED_TARGET_ARG_RE = /^(?:\\/|~|\\$\\{?HOME\\}?|\\.)(?:\\/?\\.?\\*|\\/)?$/;\nconst DESTRUCTIVE_OP_IN_COMMAND_RE =\n /\\b(?:rm\\s+-[^\\s]*[rf]|chmod\\s+-R|chown\\s+-R)\\b/;\n\ntype SpawnResult = {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n timedOut: boolean;\n};\n\ntype RuntimeCommand = {\n fileName: string;\n source?: string;\n command: string;\n};\n\ntype SandboxRuntimeContext = {\n sandbox: t.CloudflareSandboxRuntime;\n workspaceRoot: string;\n env?: Record<string, string | undefined>;\n timeoutMs: number;\n maxOutputChars: number;\n shell: string;\n};\n\nconst sandboxFactoryCache = new WeakMap<\n t.CloudflareSandboxExecutionConfig,\n Promise<t.CloudflareSandboxRuntime>\n>();\n\nfunction normalizeWorkspaceRoot(workspaceRoot: string): string {\n const normalized = path.normalize(workspaceRoot);\n return normalized === '/' ? normalized : normalized.replace(/\\/+$/, '');\n}\n\nexport function getCloudflareWorkspaceRoot(\n config?: t.CloudflareSandboxExecutionConfig\n): string {\n return normalizeWorkspaceRoot(\n config?.workspaceRoot ?? DEFAULT_WORKSPACE_ROOT\n );\n}\n\nexport async function resolveCloudflareSandbox(\n config: t.CloudflareSandboxExecutionConfig\n): Promise<t.CloudflareSandboxRuntime> {\n const sandbox = config.sandbox;\n if (typeof sandbox !== 'function') {\n return sandbox;\n }\n let cached = sandboxFactoryCache.get(config);\n if (cached == null) {\n cached = Promise.resolve()\n .then(() => sandbox())\n .catch((error: unknown) => {\n sandboxFactoryCache.delete(config);\n throw error;\n });\n sandboxFactoryCache.set(config, cached);\n }\n return cached;\n}\n\nasync function getRuntimeContext(\n config: t.CloudflareSandboxExecutionConfig\n): Promise<SandboxRuntimeContext> {\n return {\n sandbox: await resolveCloudflareSandbox(config),\n workspaceRoot: getCloudflareWorkspaceRoot(config),\n env: config.env,\n timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n maxOutputChars: config.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS,\n shell: config.shell ?? 'bash',\n };\n}\n\nfunction toSandboxPath(filePath: string, workspaceRoot: string): string {\n const raw = filePath === '' ? '.' : filePath;\n const root = normalizeWorkspaceRoot(workspaceRoot);\n const resolved = raw.startsWith('/')\n ? path.normalize(raw)\n : path.resolve(root, raw);\n if (root === '/') {\n return resolved;\n }\n if (resolved === root || resolved.startsWith(`${root}/`)) {\n return resolved;\n }\n throw new Error(\n `Path is outside the Cloudflare sandbox workspace: ${filePath}`\n );\n}\n\nfunction quote(value: string): string {\n if (value === '') {\n return '\\'\\'';\n }\n if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n return value;\n }\n return `'${value.replace(/'/g, '\\'\\\\\\'\\'')}'`;\n}\n\nfunction withInSandboxTimeout(command: string, timeoutMs: number): string {\n const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));\n return `timeout -k 2s ${timeoutSeconds}s ${command}`;\n}\n\nfunction outerTimeoutMs(timeoutMs: number): number {\n return timeoutMs + 5000;\n}\n\nfunction isInSandboxTimeoutExit(exitCode: number | null): boolean {\n return exitCode === 124 || exitCode === 137;\n}\n\n/**\n * Client-side backstop timeout for a `sandbox.exec()` await: a few seconds beyond\n * the exec's own `timeout` option, so a stalled exec that never honors `timeout`\n * still can't outlast this.\n */\nexport function clientExecTimeoutMs(timeoutMs: number): number {\n return outerTimeoutMs(timeoutMs) + 5000;\n}\n\n/**\n * Bound a `sandbox.exec()` await with a CLIENT-SIDE timeout.\n *\n * The native Cloudflare Sandbox Durable Object `exec()` is effectively\n * uncancellable from the host: `ExecOptions` has no `signal` (so\n * `supportsExecSignal` is false for the native transport), and its `timeout`\n * option is not reliably enforced when the container/RPC itself stalls — while\n * the in-sandbox `timeout(1)` wrapper only bounds a command that is actually\n * running. So a stalled exec (an unresponsive/cold container) otherwise hangs\n * until the host's run-level abort, burning the whole run budget on one tool\n * call. This race guarantees the host await settles within `timeoutMs`\n * regardless of the transport.\n *\n * On timeout the underlying `exec` promise may keep running in the DO (a\n * native-DO exec cannot be truly cancelled), so its late settlement is swallowed\n * to avoid an unhandled rejection.\n */\nexport async function withClientTimeout<T>(\n exec: Promise<T>,\n timeoutMs: number,\n label: string,\n options: {\n /**\n * Detach the backstop timer from the event loop. Use ONLY when something else\n * already settles the caller (e.g. the spawn path, where spawnLocalProcess's\n * own timer resolves the child). The awaited direct-exec paths must leave it\n * REF'd so the timeout is guaranteed to fire even if nothing else is pending.\n */\n unref?: boolean;\n /** Invoked when the client timeout fires — e.g. abort a signal-aware exec. */\n onTimeout?: () => void;\n } = {}\n): Promise<T> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n return exec;\n }\n // Swallow a late rejection from the losing promise after the race settles.\n exec.catch(() => undefined);\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n exec,\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n // Reject FIRST so this client-timeout message reliably wins the race;\n // only then abort a signal-aware exec, whose resulting AbortError must\n // not surface to the caller instead of the timeout.\n reject(\n new Error(\n `${label} exceeded ${timeoutMs}ms client-side timeout (sandbox exec did not return)`\n )\n );\n options.onTimeout?.();\n }, timeoutMs);\n if (options.unref === true) {\n (timer as { unref?: () => void } | undefined)?.unref?.();\n }\n }),\n ]);\n } finally {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Run `sandbox.exec()` bounded by a client-side timeout, and — for signal-aware\n * transports (e.g. the HTTP bridge, `supportsExecSignal === true`) — abort the\n * underlying exec when the timeout fires instead of merely abandoning it. The\n * native DO transport ignores `signal`, so it only gets the timeout. Leaves the\n * backstop timer ref'd (this is an awaited direct-exec path).\n */\nexport async function execWithClientTimeout(\n sandbox: t.CloudflareSandboxRuntime,\n command: string,\n options: t.CloudflareSandboxExecOptions,\n timeoutMs: number,\n label: string,\n runOptions: { unref?: boolean } = {}\n): Promise<t.CloudflareSandboxExecResult> {\n const controller = new AbortController();\n const execOptions: t.CloudflareSandboxExecOptions = { ...options };\n const callerSignal = options.signal;\n let onCallerAbort: (() => void) | undefined;\n if (sandbox.supportsExecSignal === true) {\n // Compose the caller's signal (e.g. run/user cancellation) with our timeout\n // controller so EITHER source cancels the exec — don't clobber the caller's.\n if (callerSignal != null) {\n if (callerSignal.aborted) {\n controller.abort();\n } else {\n onCallerAbort = (): void => controller.abort();\n callerSignal.addEventListener('abort', onCallerAbort, { once: true });\n }\n }\n execOptions.signal = controller.signal;\n } else if ('signal' in execOptions) {\n // Native DO RPC cannot consume an AbortSignal (and would fail to clone it).\n // Strip any caller-provided one so the spread above can't reintroduce it.\n delete execOptions.signal;\n }\n try {\n return await withClientTimeout(\n sandbox.exec(command, execOptions),\n timeoutMs,\n label,\n {\n unref: runOptions.unref,\n onTimeout: () => controller.abort(),\n }\n );\n } finally {\n // Don't leave a listener attached to a long-lived/shared caller signal.\n if (onCallerAbort != null && callerSignal != null) {\n callerSignal.removeEventListener('abort', onCallerAbort);\n }\n }\n}\n\nfunction truncateOutput(value: string, maxChars: number): string {\n if (maxChars <= 0 || value.length <= maxChars) {\n return value;\n }\n const head = Math.max(Math.floor(maxChars / 2), 0);\n const tail = Math.max(maxChars - head, 0);\n return `${value.slice(0, head)}\\n...[truncated ${value.length - maxChars} chars]...\\n${value.slice(value.length - tail)}`;\n}\n\nasync function readStream(stream: ReadableStream<Uint8Array>): Promise<Buffer> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));\n}\n\nasync function normalizeReadFileContent(\n result: t.CloudflareSandboxReadFileResult\n): Promise<Buffer> {\n if (typeof result === 'string') {\n return Buffer.from(result, 'utf8');\n }\n if (Buffer.isBuffer(result)) {\n return result;\n }\n if (result instanceof Uint8Array) {\n return Buffer.from(result);\n }\n const content = result.content;\n if (typeof content === 'string') {\n if (result.encoding === 'base64') {\n return Buffer.from(content, 'base64');\n }\n return Buffer.from(content, 'utf8');\n }\n if (Buffer.isBuffer(content)) {\n return content;\n }\n if (content instanceof Uint8Array) {\n return Buffer.from(content);\n }\n return readStream(content);\n}\n\nfunction bytesToStream(bytes: Uint8Array): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller): void {\n controller.enqueue(bytes);\n controller.close();\n },\n });\n}\n\nfunction normalizeWriteFileContent(content: string | Buffer | Uint8Array): {\n content: string | ReadableStream<Uint8Array>;\n options?: { encoding?: string };\n} {\n if (typeof content === 'string') {\n return { content, options: { encoding: 'utf8' } };\n }\n return { content: bytesToStream(content) };\n}\n\nfunction createStats(info: {\n size?: number;\n type?: t.CloudflareSandboxFileInfo['type'];\n}): Stats {\n const type = info.type ?? 'file';\n const now = new Date();\n return {\n size: info.size ?? 0,\n isFile: () => type === 'file',\n isDirectory: () => type === 'directory',\n isSymbolicLink: () => type === 'symlink',\n isBlockDevice: () => false,\n isCharacterDevice: () => false,\n isFIFO: () => false,\n isSocket: () => false,\n dev: 0,\n ino: 0,\n mode: 0,\n nlink: 1,\n uid: 0,\n gid: 0,\n rdev: 0,\n blksize: 0,\n blocks: 0,\n atimeMs: now.getTime(),\n mtimeMs: now.getTime(),\n ctimeMs: now.getTime(),\n birthtimeMs: now.getTime(),\n atime: now,\n mtime: now,\n ctime: now,\n birthtime: now,\n } as Stats;\n}\n\nfunction normalizeFileList(\n result: t.CloudflareSandboxListFilesResult\n): t.CloudflareSandboxFileInfo[] {\n return Array.isArray(result) ? result : result.files;\n}\n\nfunction entryNameFor(\n info: t.CloudflareSandboxFileInfo,\n parentPath: string\n): string {\n if (info.name !== '') {\n return info.name.includes('/') ? path.basename(info.name) : info.name;\n }\n if (info.absolutePath != null && info.absolutePath !== '') {\n return path.basename(info.absolutePath);\n }\n if (info.relativePath != null && info.relativePath !== '') {\n return path.basename(info.relativePath);\n }\n return path.basename(parentPath);\n}\n\nfunction entryAbsolutePath(\n info: t.CloudflareSandboxFileInfo,\n parentPath: string\n): string {\n if (info.absolutePath != null && info.absolutePath !== '') {\n return path.normalize(info.absolutePath);\n }\n if (info.relativePath != null && info.relativePath !== '') {\n return path.resolve(parentPath, info.relativePath);\n }\n return path.resolve(parentPath, info.name);\n}\n\nfunction createDirent(info: t.CloudflareSandboxFileInfo): ReaddirEntry {\n return {\n name: entryNameFor(info, ''),\n isFile: () => (info.type ?? 'file') === 'file',\n isDirectory: () => info.type === 'directory',\n isSymbolicLink: () => info.type === 'symlink',\n };\n}\n\nasync function findChildInfo(\n sandbox: t.CloudflareSandboxRuntime,\n filePath: string\n): Promise<t.CloudflareSandboxFileInfo | undefined> {\n const parent = path.dirname(filePath);\n const basename = path.basename(filePath);\n const entries = normalizeFileList(\n await sandbox.listFiles(parent, { includeHidden: true })\n );\n return entries.find((entry) => {\n const absolute = entryAbsolutePath(entry, parent);\n return absolute === filePath || entryNameFor(entry, parent) === basename;\n });\n}\n\nexport function createCloudflareWorkspaceFS(\n config: t.CloudflareSandboxExecutionConfig\n): WorkspaceFS {\n const workspaceRoot = getCloudflareWorkspaceRoot(config);\n\n const fs: WorkspaceFS = {\n readFile: (async (filePath: string, encoding?: 'utf8') => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const buffer = await normalizeReadFileContent(\n await sandbox.readFile(resolved, encoding ? { encoding } : undefined)\n );\n return encoding != null ? buffer.toString(encoding) : buffer;\n }) as WorkspaceFS['readFile'],\n writeFile: async (\n filePath: string,\n content: string | Buffer,\n _options?: WriteFileOptions\n ) => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const normalized = normalizeWriteFileContent(content);\n await sandbox.writeFile(resolved, normalized.content, normalized.options);\n },\n stat: async (filePath: string) => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n if (resolved === workspaceRoot) {\n const entries = normalizeFileList(\n await sandbox.listFiles(resolved, { includeHidden: true })\n );\n return createStats({ size: entries.length, type: 'directory' });\n }\n const info = await findChildInfo(sandbox, resolved);\n if (info != null) {\n return createStats({ size: info.size, type: info.type });\n }\n try {\n const entries = normalizeFileList(\n await sandbox.listFiles(resolved, { includeHidden: true })\n );\n return createStats({ size: entries.length, type: 'directory' });\n } catch {\n const buffer = await normalizeReadFileContent(\n await sandbox.readFile(resolved)\n );\n return createStats({ size: buffer.length, type: 'file' });\n }\n },\n readdir: (async (filePath: string, options?: { withFileTypes: true }) => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const entries = normalizeFileList(\n await sandbox.listFiles(resolved, { includeHidden: true })\n );\n if (options?.withFileTypes === true) {\n return entries.map(createDirent);\n }\n return entries.map((entry) => entryNameFor(entry, resolved));\n }) as WorkspaceFS['readdir'],\n mkdir: async (filePath: string, options?: MakeDirectoryOptions) => {\n const sandbox = await resolveCloudflareSandbox(config);\n await sandbox.mkdir(toSandboxPath(filePath, workspaceRoot), {\n recursive: options?.recursive,\n });\n },\n realpath: async (filePath: string) =>\n toSandboxPath(filePath, workspaceRoot),\n unlink: async (filePath: string) => {\n const sandbox = await resolveCloudflareSandbox(config);\n await sandbox.deleteFile(toSandboxPath(filePath, workspaceRoot));\n },\n open: async (filePath: string, _flags: 'r') => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const buffer = await normalizeReadFileContent(\n await sandbox.readFile(resolved)\n );\n return {\n read: async (\n target: Buffer,\n offset: number,\n length: number,\n position: number\n ) => {\n const start = Math.max(position, 0);\n const slice = buffer.subarray(start, start + length);\n slice.copy(target, offset);\n return { bytesRead: slice.length, buffer: target };\n },\n close: async () => undefined,\n } as unknown as FileHandle;\n },\n };\n\n return fs;\n}\n\nfunction createCloudflareSpawn(\n config: t.CloudflareSandboxExecutionConfig\n): t.LocalSpawn {\n return (command, args, options) => {\n const stdout = new PassThrough();\n const stderr = new PassThrough();\n const child = new EventEmitter() as ChildProcessWithoutNullStreams;\n const abortController = new AbortController();\n const state = { closed: false };\n /** Read through a function so concurrent `closeOnce` mutation (via\n * `kill()`/abort during an `await`) isn't statically narrowed away. */\n const isClosed = (): boolean => state.closed;\n const closeOnce = (\n exitCode: number | null,\n signal: NodeJS.Signals | null\n ): void => {\n if (state.closed) {\n return;\n }\n state.closed = true;\n stdout.end();\n stderr.end();\n Object.assign(child, {\n exitCode,\n signalCode: signal,\n });\n child.emit('close', exitCode, signal);\n };\n Object.assign(child, {\n stdout,\n stderr,\n stdin: new PassThrough(),\n stdio: [null, stdout, stderr],\n killed: false,\n exitCode: null,\n signalCode: null,\n pid: undefined,\n kill: (signal: NodeJS.Signals = 'SIGTERM') => {\n Object.assign(child, { killed: true, signalCode: signal });\n abortController.abort();\n closeOnce(null, signal);\n return true;\n },\n });\n\n void (async (): Promise<void> => {\n const ctx = await getRuntimeContext(config);\n const rendered = [command, ...args].map(quote).join(' ');\n const spawnTimeoutMs = (\n options as {\n [LOCAL_SPAWN_TIMEOUT_MS]?: number;\n }\n )[LOCAL_SPAWN_TIMEOUT_MS];\n const timeoutMs =\n typeof spawnTimeoutMs === 'number' && Number.isFinite(spawnTimeoutMs)\n ? spawnTimeoutMs\n : ctx.timeoutMs;\n const timedCommand = withInSandboxTimeout(rendered, timeoutMs);\n const cwd =\n options.cwd == null ? ctx.workspaceRoot : options.cwd.toString();\n if (isClosed()) {\n return;\n }\n const execOptions: t.CloudflareSandboxExecOptions = {\n cwd,\n env: ctx.env,\n timeout: outerTimeoutMs(timeoutMs),\n };\n if (ctx.sandbox.supportsExecSignal === true) {\n execOptions.signal = abortController.signal;\n }\n try {\n const result = await withClientTimeout(\n ctx.sandbox.exec(timedCommand, execOptions),\n clientExecTimeoutMs(timeoutMs),\n 'cloudflare sandbox exec',\n // spawnLocalProcess's own timer already resolves the child, so this\n // backstop may safely detach; abort the (signal-aware) exec on timeout.\n { unref: true, onTimeout: () => abortController.abort() }\n );\n if (isClosed()) {\n return;\n }\n if (result.stdout) stdout.write(result.stdout);\n if (result.stderr) stderr.write(result.stderr);\n closeOnce(result.exitCode, null);\n } catch (error) {\n if (isClosed()) {\n return;\n }\n stderr.write((error as Error).message);\n closeOnce(1, null);\n }\n })();\n\n return child;\n };\n}\n\nexport function createCloudflareLocalExecutionConfig(\n config: t.CloudflareSandboxExecutionConfig\n): t.LocalExecutionConfig {\n const workspaceRoot = getCloudflareWorkspaceRoot(config);\n return {\n cwd: workspaceRoot,\n workspace: { root: workspaceRoot },\n exec: {\n spawn: createCloudflareSpawn(config),\n fs: createCloudflareWorkspaceFS(config),\n sandboxed: true,\n },\n shell: config.shell ?? 'bash',\n timeoutMs: config.timeoutMs,\n maxOutputChars: config.maxOutputChars,\n env: config.env,\n includeCodingTools: config.includeCodingTools,\n compileCheck: config.compileCheck,\n readOnly: config.readOnly,\n allowDangerousCommands: config.allowDangerousCommands,\n bashAst: config.bashAst,\n fileCheckpointing: config.fileCheckpointing,\n maxReadBytes: config.maxReadBytes,\n attachReadAttachments: config.attachReadAttachments,\n maxAttachmentBytes: config.maxAttachmentBytes,\n postEditSyntaxCheck: config.postEditSyntaxCheck,\n };\n}\n\nexport async function validateCloudflareBashCommand(\n command: string,\n args: readonly string[],\n config: t.CloudflareSandboxExecutionConfig\n): Promise<void> {\n const localConfig = createCloudflareLocalExecutionConfig(config);\n const validation = await validateBashCommand(command, localConfig);\n if (!validation.valid) {\n throw new Error(validation.errors.join('\\n'));\n }\n\n if (\n args.length > 0 &&\n config.allowDangerousCommands !== true &&\n DESTRUCTIVE_OP_IN_COMMAND_RE.test(command)\n ) {\n const offending = args.find((arg) => PROTECTED_TARGET_ARG_RE.test(arg));\n if (offending !== undefined) {\n throw new Error(\n `Command matches a destructive command pattern (protected target \"${offending}\" passed via positional arg).`\n );\n }\n }\n}\n\nexport async function executeCloudflareBash(\n command: string,\n config: t.CloudflareSandboxExecutionConfig,\n args: readonly string[] = []\n): Promise<SpawnResult> {\n await validateCloudflareBashCommand(command, args, config);\n const ctx = await getRuntimeContext(config);\n const shellCommand =\n args.length > 0\n ? `${ctx.shell} -lc ${quote(command)} -- ${args.map(quote).join(' ')}`\n : `${ctx.shell} -lc ${quote(command)}`;\n const result = await execWithClientTimeout(\n ctx.sandbox,\n withInSandboxTimeout(shellCommand, ctx.timeoutMs),\n {\n cwd: ctx.workspaceRoot,\n env: ctx.env,\n timeout: outerTimeoutMs(ctx.timeoutMs),\n },\n clientExecTimeoutMs(ctx.timeoutMs),\n 'cloudflare sandbox bash exec'\n );\n return {\n stdout: truncateOutput(result.stdout, ctx.maxOutputChars),\n stderr: truncateOutput(result.stderr, ctx.maxOutputChars),\n exitCode: result.exitCode,\n timedOut: isInSandboxTimeoutExit(result.exitCode),\n };\n}\n\nfunction runtimeForCode(\n lang: string,\n tempDir: string,\n code: string,\n args: string[] = [],\n shell = 'bash'\n): RuntimeCommand {\n const fileFor = (name: string): string => path.join(tempDir, name);\n const argText = args.map(quote).join(' ');\n switch (lang) {\n case 'py':\n case 'python':\n return {\n fileName: 'main.py',\n source: code,\n command: `python3 ${quote(fileFor('main.py'))} ${argText}`,\n };\n case 'js':\n case 'javascript':\n return {\n fileName: 'main.js',\n source: code,\n command: `node ${quote(fileFor('main.js'))} ${argText}`,\n };\n case 'ts':\n case 'typescript':\n return {\n fileName: 'main.ts',\n source: code,\n command: `npx --no-install tsx ${quote(fileFor('main.ts'))} ${argText}`,\n };\n case 'php':\n return {\n fileName: 'main.php',\n source: code,\n command: `php ${quote(fileFor('main.php'))} ${argText}`,\n };\n case 'go':\n return {\n fileName: 'main.go',\n source: code,\n command: `go run ${quote(fileFor('main.go'))} ${argText}`,\n };\n case 'rs':\n return {\n fileName: 'main.rs',\n source: code,\n command: `${shell} -lc ${quote(\n `rustc ${quote(fileFor('main.rs'))} -o ${quote(fileFor('main-rs'))} && ${quote(fileFor('main-rs'))} ${argText}`\n )}`,\n };\n case 'c':\n return {\n fileName: 'main.c',\n source: code,\n command: `${shell} -lc ${quote(\n `cc ${quote(fileFor('main.c'))} -o ${quote(fileFor('main-c'))} && ${quote(fileFor('main-c'))} ${argText}`\n )}`,\n };\n case 'cpp':\n return {\n fileName: 'main.cpp',\n source: code,\n command: `${shell} -lc ${quote(\n `c++ ${quote(fileFor('main.cpp'))} -o ${quote(fileFor('main-cpp'))} && ${quote(fileFor('main-cpp'))} ${argText}`\n )}`,\n };\n case 'java':\n return {\n fileName: 'Main.java',\n source: code,\n command: `${shell} -lc ${quote(\n `javac ${quote(fileFor('Main.java'))} && java -cp ${quote(tempDir)} Main ${argText}`\n )}`,\n };\n case 'r':\n return {\n fileName: 'main.R',\n source: code,\n command: `Rscript ${quote(fileFor('main.R'))} ${argText}`,\n };\n case 'd':\n return {\n fileName: 'main.d',\n source: code,\n command: `${shell} -lc ${quote(\n `dmd ${quote(fileFor('main.d'))} -of=${quote(fileFor('main-d'))} && ${quote(fileFor('main-d'))} ${argText}`\n )}`,\n };\n case 'f90':\n return {\n fileName: 'main.f90',\n source: code,\n command: `${shell} -lc ${quote(\n `gfortran ${quote(fileFor('main.f90'))} -o ${quote(fileFor('main-f90'))} && ${quote(fileFor('main-f90'))} ${argText}`\n )}`,\n };\n case 'bash':\n case 'sh':\n return {\n fileName: 'main.sh',\n source: code,\n command: `${shell} -lc ${quote(code)} -- ${argText}`,\n };\n default:\n throw new Error(`Unsupported Cloudflare sandbox runtime: ${lang}`);\n }\n}\n\nexport async function executeCloudflareCode(\n input: { lang: string; code: string; args?: string[] },\n config: t.CloudflareSandboxExecutionConfig\n): Promise<SpawnResult> {\n if (input.lang === 'bash' || input.lang === 'sh') {\n return executeCloudflareBash(input.code, config, input.args ?? []);\n }\n const ctx = await getRuntimeContext(config);\n const id = globalThis.crypto.randomUUID();\n const tempDir = path.join(ctx.workspaceRoot, '.lc-exec', id);\n const runtime = runtimeForCode(\n input.lang,\n tempDir,\n input.code,\n input.args,\n ctx.shell\n );\n await ctx.sandbox.mkdir(tempDir, { recursive: true });\n if (runtime.source != null) {\n await ctx.sandbox.writeFile(\n path.join(tempDir, runtime.fileName),\n runtime.source,\n {\n encoding: 'utf8',\n }\n );\n }\n let execSucceeded = false;\n try {\n const result = await execWithClientTimeout(\n ctx.sandbox,\n withInSandboxTimeout(runtime.command, ctx.timeoutMs),\n {\n cwd: ctx.workspaceRoot,\n env: ctx.env,\n timeout: outerTimeoutMs(ctx.timeoutMs),\n },\n clientExecTimeoutMs(ctx.timeoutMs),\n 'cloudflare sandbox code-exec'\n );\n execSucceeded = true;\n return {\n stdout: truncateOutput(result.stdout, ctx.maxOutputChars),\n stderr: truncateOutput(result.stderr, ctx.maxOutputChars),\n exitCode: result.exitCode,\n timedOut: isInSandboxTimeoutExit(result.exitCode),\n };\n } finally {\n // After a normal run, AWAIT cleanup so the temp dir is gone before returning.\n // After a stalled/failed run, detach it (unref'd) so we don't pile a second\n // client timeout onto the caller's latency; cleanup still runs best-effort.\n const detach = !execSucceeded;\n const cleanup = execWithClientTimeout(\n ctx.sandbox,\n `rm -rf ${quote(tempDir)}`,\n {\n cwd: ctx.workspaceRoot,\n env: ctx.env,\n timeout: 10000,\n },\n clientExecTimeoutMs(10000),\n 'cloudflare sandbox cleanup',\n { unref: detach }\n ).catch(() => undefined);\n if (!detach) {\n await cleanup;\n }\n }\n}\n\nexport function formatCloudflareOutput(\n result: SpawnResult,\n cwd: string\n): string {\n let formatted = '';\n if (result.stdout !== '') {\n formatted += `stdout:\\n${result.stdout}\\n`;\n } else {\n formatted += 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n }\n if (result.stderr !== '') {\n formatted += `stderr:\\n${result.stderr}\\n`;\n }\n if (result.exitCode != null && result.exitCode !== 0) {\n formatted += `exit_code: ${result.exitCode}\\n`;\n }\n if (result.timedOut) {\n formatted += 'timed_out: true\\n';\n }\n formatted += `working_directory: ${cwd}`;\n return formatted.trim();\n}\n"],"mappings":"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"}
|
|
@@ -31,7 +31,7 @@ const LocalListDirectoryToolName = "list_directory";
|
|
|
31
31
|
const LocalReadFileToolSchema = {
|
|
32
32
|
type: "object",
|
|
33
33
|
properties: {
|
|
34
|
-
|
|
34
|
+
path: {
|
|
35
35
|
type: "string",
|
|
36
36
|
description: "Path to a local file, relative to the configured cwd unless absolute paths are allowed."
|
|
37
37
|
},
|
|
@@ -44,12 +44,12 @@ const LocalReadFileToolSchema = {
|
|
|
44
44
|
description: "Optional maximum number of lines to return."
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
|
-
required: ["
|
|
47
|
+
required: ["path"]
|
|
48
48
|
};
|
|
49
49
|
const LocalWriteFileToolSchema = {
|
|
50
50
|
type: "object",
|
|
51
51
|
properties: {
|
|
52
|
-
|
|
52
|
+
path: {
|
|
53
53
|
type: "string",
|
|
54
54
|
description: "Path to write, relative to the configured cwd unless absolute paths are allowed."
|
|
55
55
|
},
|
|
@@ -58,12 +58,12 @@ const LocalWriteFileToolSchema = {
|
|
|
58
58
|
description: "Complete file contents to write."
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
|
-
required: ["
|
|
61
|
+
required: ["path", "content"]
|
|
62
62
|
};
|
|
63
63
|
const LocalEditFileToolSchema = {
|
|
64
64
|
type: "object",
|
|
65
65
|
properties: {
|
|
66
|
-
|
|
66
|
+
path: {
|
|
67
67
|
type: "string",
|
|
68
68
|
description: "Path to edit, relative to the configured cwd unless absolute paths are allowed."
|
|
69
69
|
},
|
|
@@ -88,7 +88,7 @@ const LocalEditFileToolSchema = {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
},
|
|
91
|
-
required: ["
|
|
91
|
+
required: ["path"]
|
|
92
92
|
};
|
|
93
93
|
const LocalGrepSearchToolSchema = {
|
|
94
94
|
type: "object",
|
|
@@ -258,9 +258,9 @@ function createLocalReadFileTool(config = {}) {
|
|
|
258
258
|
const fs = getWorkspaceFS(config);
|
|
259
259
|
return tool(async (rawInput) => {
|
|
260
260
|
const input = rawInput;
|
|
261
|
-
const path = await resolveWorkspacePathSafe(input.
|
|
261
|
+
const path = await resolveWorkspacePathSafe(input.path, config, "read");
|
|
262
262
|
const fileStat = await fs.stat(path);
|
|
263
|
-
if (!fileStat.isFile()) throw new Error(`Path is not a file: ${input.
|
|
263
|
+
if (!fileStat.isFile()) throw new Error(`Path is not a file: ${input.path}`);
|
|
264
264
|
const maxBytes = Math.max(config.maxReadBytes ?? DEFAULT_MAX_READ_BYTES, 1);
|
|
265
265
|
if (fileStat.size > maxBytes) return [`File is ${fileStat.size} bytes, exceeds the ${maxBytes}-byte read cap. Read a slice via bash (e.g. head/sed) or raise local.maxReadBytes.`, {
|
|
266
266
|
path,
|
|
@@ -330,7 +330,7 @@ function createLocalWriteFileTool(config = {}, checkpointer) {
|
|
|
330
330
|
return tool(async (rawInput) => {
|
|
331
331
|
const input = rawInput;
|
|
332
332
|
if (config.readOnly === true) throw new Error("write_file is blocked in read-only local mode.");
|
|
333
|
-
const path = await resolveWorkspacePathSafe(input.
|
|
333
|
+
const path = await resolveWorkspacePathSafe(input.path, config, "write");
|
|
334
334
|
if (checkpointer != null) await checkpointer.captureBeforeWrite(path);
|
|
335
335
|
let before = "";
|
|
336
336
|
let encoding = {
|
|
@@ -379,7 +379,7 @@ function createLocalEditFileTool(config = {}, checkpointer) {
|
|
|
379
379
|
if (config.readOnly === true) throw new Error("edit_file is blocked in read-only local mode.");
|
|
380
380
|
const edits = normalizeEdits(input);
|
|
381
381
|
if (edits.length === 0) throw new Error("edit_file requires old_text/new_text or edits[].");
|
|
382
|
-
const path = await resolveWorkspacePathSafe(input.
|
|
382
|
+
const path = await resolveWorkspacePathSafe(input.path, config, "write");
|
|
383
383
|
const encoding = decodeFile(await fs.readFile(path, "utf8"));
|
|
384
384
|
const original = encoding.text;
|
|
385
385
|
let next = original;
|
|
@@ -387,7 +387,7 @@ function createLocalEditFileTool(config = {}, checkpointer) {
|
|
|
387
387
|
for (let i = 0; i < edits.length; i++) {
|
|
388
388
|
const edit = edits[i];
|
|
389
389
|
const match = locateEdit(next, edit.oldText);
|
|
390
|
-
if (match == null) throw new Error(`Edit ${i + 1}/${edits.length}: could not locate old_text in ${input.
|
|
390
|
+
if (match == null) throw new Error(`Edit ${i + 1}/${edits.length}: could not locate old_text in ${input.path}. Tried exact, line-trimmed, whitespace-normalized, and indentation-flexible matching. Re-read the file and copy the literal lines.`);
|
|
391
391
|
strategiesUsed.push(match.strategy);
|
|
392
392
|
next = applyEdit(next, match, edit.newText);
|
|
393
393
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalCodingTools.mjs","names":["input"],"sources":["../../../../src/tools/local/LocalCodingTools.ts"],"sourcesContent":["import { basename, dirname } from 'path';\nimport { createTwoFilesPatch } from 'diff';\nimport { tool } from '@langchain/core/tools';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport {\n createLocalBashProgrammaticToolCallingTool,\n createLocalProgrammaticToolCallingTool,\n} from './LocalProgrammaticToolCalling';\nimport {\n getSpawn,\n getWorkspaceFS,\n resolveWorkspacePathSafe,\n spawnLocalProcess,\n truncateLocalOutput,\n} from './LocalExecutionEngine';\nimport {\n createLocalBashExecutionTool,\n createLocalCodeExecutionTool,\n} from './LocalExecutionTools';\nimport {\n createCompileCheckTool,\n createCompileCheckToolDefinition,\n} from './CompileCheckTool';\nimport { classifyAttachment, imageAttachmentContent } from './attachments';\nimport { createLocalFileCheckpointer } from './FileCheckpointer';\nimport { applyEdit, locateEdit } from './editStrategies';\nimport { decodeFile, encodeFile } from './textEncoding';\nimport { runPostEditSyntaxCheck } from './syntaxCheck';\nimport { Constants } from '@/common';\n\nconst MAX_READ_CHARS = 256000;\nconst DEFAULT_MAX_RESULTS = 200;\nconst DEFAULT_MAX_READ_BYTES = 10 * 1024 * 1024;\nconst BINARY_DETECTION_BYTES = 8000;\n\n/**\n * Tool name aliases retained for back-compat with consumers that imported\n * the per-file `Local*ToolName` constants. The canonical names live on\n * `Constants.*` (see `src/common/enum.ts`); these aliases just point at\n * them so a typo upstream gets caught at the type level.\n */\nexport const LocalWriteFileToolName = Constants.WRITE_FILE;\nexport const LocalEditFileToolName = Constants.EDIT_FILE;\nexport const LocalGrepSearchToolName = Constants.GREP_SEARCH;\nexport const LocalGlobSearchToolName = Constants.GLOB_SEARCH;\nexport const LocalListDirectoryToolName = Constants.LIST_DIRECTORY;\n\nexport const LocalReadFileToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description:\n 'Path to a local file, relative to the configured cwd unless absolute paths are allowed.',\n },\n offset: {\n type: 'integer',\n description: 'Optional 1-indexed line offset for large files.',\n },\n limit: {\n type: 'integer',\n description: 'Optional maximum number of lines to return.',\n },\n },\n required: ['file_path'],\n};\n\nexport const LocalWriteFileToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description:\n 'Path to write, relative to the configured cwd unless absolute paths are allowed.',\n },\n content: {\n type: 'string',\n description: 'Complete file contents to write.',\n },\n },\n required: ['file_path', 'content'],\n};\n\nexport const LocalEditFileToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description:\n 'Path to edit, relative to the configured cwd unless absolute paths are allowed.',\n },\n old_text: {\n type: 'string',\n description: 'Exact text to replace. Must appear exactly once.',\n },\n new_text: {\n type: 'string',\n description: 'Replacement text.',\n },\n edits: {\n type: 'array',\n description:\n 'Optional batch of exact replacements. Each old_text must appear exactly once in the original file.',\n items: {\n type: 'object',\n properties: {\n old_text: { type: 'string' },\n new_text: { type: 'string' },\n },\n required: ['old_text', 'new_text'],\n },\n },\n },\n required: ['file_path'],\n};\n\nexport const LocalGrepSearchToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Regex pattern to search for.',\n },\n path: {\n type: 'string',\n description: 'Directory or file to search. Defaults to cwd.',\n },\n glob: {\n type: 'string',\n description: 'Optional file glob passed to rg -g.',\n },\n max_results: {\n type: 'integer',\n description: 'Maximum matching lines to return.',\n },\n },\n required: ['pattern'],\n};\n\nexport const LocalGlobSearchToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'File glob pattern, for example \"src/**/*.ts\".',\n },\n path: {\n type: 'string',\n description: 'Directory to search. Defaults to cwd.',\n },\n max_results: {\n type: 'integer',\n description: 'Maximum file paths to return.',\n },\n },\n required: ['pattern'],\n};\n\nexport const LocalListDirectoryToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Directory to list. Defaults to cwd.',\n },\n },\n};\n\nfunction lineWindow(\n content: string,\n offset?: number,\n limit?: number\n): { text: string; truncated: boolean } {\n const start = Math.max((offset ?? 1) - 1, 0);\n // Avoid splitting the whole file when the caller asked for a small\n // window. For a 10 MB file with `offset: 1, limit: 10`, the prior\n // `content.split('\\n')` allocated millions of strings to throw all\n // but 10 away. We walk newline indices directly: O(start + limit)\n // instead of O(file). When `limit` is omitted, fall back to the\n // simple split — it's the same amount of work either way.\n if (limit == null || limit <= 0) {\n const lines = content.split('\\n');\n const selected = lines.slice(start);\n const numbered = selected\n .map(\n (line, index) =>\n `${String(start + index + 1).padStart(6, ' ')}\\t${line}`\n )\n .join('\\n');\n return {\n text: truncateLocalOutput(numbered, MAX_READ_CHARS),\n truncated: numbered.length > MAX_READ_CHARS,\n };\n }\n // Walk to the start line by counting newlines.\n let cursor = 0;\n for (let i = 0; i < start; i++) {\n const next = content.indexOf('\\n', cursor);\n if (next === -1) {\n // File has fewer lines than `offset` — return empty window.\n return { text: '', truncated: false };\n }\n cursor = next + 1;\n }\n // Collect up to `limit` lines from `cursor`.\n const out: string[] = [];\n let pos = cursor;\n let exhausted = true;\n for (let k = 0; k < limit; k++) {\n const next = content.indexOf('\\n', pos);\n if (next === -1) {\n out.push(content.slice(pos));\n break;\n }\n out.push(content.slice(pos, next));\n pos = next + 1;\n if (k === limit - 1 && pos < content.length) {\n exhausted = false;\n }\n }\n const numbered = out\n .map(\n (text, index) => `${String(start + index + 1).padStart(6, ' ')}\\t${text}`\n )\n .join('\\n');\n return {\n text: truncateLocalOutput(numbered, MAX_READ_CHARS),\n truncated: !exhausted || numbered.length > MAX_READ_CHARS,\n };\n}\n\nconst MAX_DIFF_CHARS = 4000;\n\ntype SyntaxRun =\n | {\n mode: 'auto' | 'strict';\n outcome: import('./syntaxCheck').SyntaxCheckOutcome;\n }\n | undefined;\n\nasync function maybeRunSyntaxCheck(\n path: string,\n config: t.LocalExecutionConfig\n): Promise<SyntaxRun> {\n const mode = config.postEditSyntaxCheck ?? 'off';\n if (mode === 'off') return undefined;\n const outcome = await runPostEditSyntaxCheck(path, config);\n if (outcome == null) return undefined;\n return { mode, outcome };\n}\n\nfunction appendSyntaxCheckSummary(base: string, run: SyntaxRun): string {\n if (run == null) return base;\n if (run.outcome.ok) return base;\n const banner =\n run.mode === 'strict'\n ? `\\n\\n[syntax-check FAILED via ${run.outcome.checker}]\\n`\n : `\\n\\n[syntax-check warning via ${run.outcome.checker}]\\n`;\n return `${base}${banner}${run.outcome.output}`;\n}\n\n/**\n * Revert a write_file/edit_file mutation in `postEditSyntaxCheck:\n * 'strict'` mode after the post-write syntax check failed. Strict\n * mode advertises a safety gate, so leaving the corrupted file on\n * disk + throwing is a half-broken contract — the model \"reacts\" to\n * the error but the next call sees broken on-disk state. Codex P2\n * [49]. Best-effort: a swallowed error here means the workspace is\n * still in the bad post-write state, but we still throw the\n * original syntax-check error so the caller knows.\n *\n * - If the file existed pre-write: restore the previous bytes with\n * the original encoding.\n * - If the file is brand-new: unlink it.\n */\nasync function revertStrictWrite(\n fs: import('./workspaceFS').WorkspaceFS,\n path: string,\n existed: boolean,\n before: string,\n encoding: { text: string; hasBom: boolean; newline: '\\n' | '\\r\\n' }\n): Promise<void> {\n try {\n if (existed) {\n // encodeFile uses encoding.{hasBom,newline} to restore the\n // on-disk shape; the `text` field is overridden by the\n // explicit `before` arg we pass in.\n await fs.writeFile(\n path,\n encodeFile(before, { ...encoding, text: before }),\n 'utf8'\n );\n } else {\n await fs.unlink(path);\n }\n } catch {\n /* best-effort: caller still sees the original syntax error */\n }\n}\n\nfunction summariseDiff(\n filePath: string,\n before: string,\n after: string\n): string {\n if (before === after) {\n return '(no textual changes)';\n }\n const name = basename(filePath);\n const patch = createTwoFilesPatch(name, name, before, after, '', '', {\n context: 3,\n });\n if (patch.length <= MAX_DIFF_CHARS) {\n return patch;\n }\n return (\n patch.slice(0, MAX_DIFF_CHARS) +\n `\\n[... diff truncated, ${patch.length - MAX_DIFF_CHARS} more chars ...]`\n );\n}\n\nfunction normalizeEdits(input: {\n old_text?: string;\n new_text?: string;\n edits?: Array<{ old_text?: string; new_text?: string }>;\n}): Array<{ oldText: string; newText: string }> {\n const edits = Array.isArray(input.edits)\n ? input.edits.map((edit) => ({\n oldText: edit.old_text ?? '',\n newText: edit.new_text ?? '',\n }))\n : [];\n\n if (input.old_text != null || input.new_text != null) {\n edits.push({\n oldText: input.old_text ?? '',\n newText: input.new_text ?? '',\n });\n }\n\n return edits;\n}\n\nfunction toolDefinition(\n name: string,\n description: string,\n parameters: t.JsonSchemaType\n): t.LCTool {\n return {\n name,\n description,\n parameters,\n allowed_callers: ['direct', 'code_execution'],\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n toolType: 'builtin',\n };\n}\n\nasync function looksBinary(\n path: string,\n fs: import('./workspaceFS').WorkspaceFS\n): Promise<boolean> {\n let handle;\n try {\n handle = await fs.open(path, 'r');\n const sample = Buffer.alloc(BINARY_DETECTION_BYTES);\n const { bytesRead } = await handle.read(\n sample,\n 0,\n BINARY_DETECTION_BYTES,\n 0\n );\n for (let i = 0; i < bytesRead; i++) {\n if (sample[i] === 0) {\n return true;\n }\n }\n return false;\n } finally {\n await handle?.close();\n }\n}\n\nconst DEFAULT_MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;\n\nexport function createLocalReadFileTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n file_path: string;\n offset?: number;\n limit?: number;\n };\n const path = await resolveWorkspacePathSafe(\n input.file_path,\n config,\n 'read'\n );\n const fileStat = await fs.stat(path);\n if (!fileStat.isFile()) {\n throw new Error(`Path is not a file: ${input.file_path}`);\n }\n const maxBytes = Math.max(\n config.maxReadBytes ?? DEFAULT_MAX_READ_BYTES,\n 1\n );\n if (fileStat.size > maxBytes) {\n const stub = `File is ${fileStat.size} bytes, exceeds the ${maxBytes}-byte read cap. Read a slice via bash (e.g. head/sed) or raise local.maxReadBytes.`;\n return [stub, { path, bytes: fileStat.size, truncated: true }];\n }\n\n if (await looksBinary(path, fs)) {\n const attachmentMode = config.attachReadAttachments ?? 'off';\n if (attachmentMode !== 'off') {\n const attachment = await classifyAttachment({\n path,\n bytes: fileStat.size,\n mode: attachmentMode,\n maxBytes: config.maxAttachmentBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES,\n // Route through the configured WorkspaceFS so a custom\n // engine sees the same path semantics as `read_file`\n // itself (manual review finding F).\n fs,\n });\n if (attachment.kind === 'image') {\n return [\n imageAttachmentContent(path, attachment),\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n attachment: 'image',\n },\n ];\n }\n if (attachment.kind === 'pdf') {\n return [\n [\n {\n type: 'text',\n text: `Read ${path} (application/pdf, ${fileStat.size} bytes). PDF attached as base64 data URL; vision-capable models that accept PDF will render it.`,\n },\n {\n type: 'image_url',\n image_url: { url: attachment.dataUrl },\n },\n ],\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n attachment: 'pdf',\n },\n ];\n }\n if (attachment.kind === 'oversize') {\n return [\n `Refusing to embed ${attachment.mime} attachment (${attachment.bytes} bytes exceeds ${attachment.maxBytes}-byte cap).`,\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n attachment: 'oversize',\n },\n ];\n }\n if (attachment.kind === 'binary') {\n return [\n `Refusing to read binary file (${fileStat.size} bytes, ${attachment.mime}): ${path}`,\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n binary: true,\n },\n ];\n }\n // text-or-unknown falls through to the text-read path below.\n } else {\n return [\n `Refusing to read binary file (${fileStat.size} bytes): ${path}`,\n { path, bytes: fileStat.size, binary: true },\n ];\n }\n }\n\n const content = await fs.readFile(path, 'utf8');\n const result = lineWindow(content, input.offset, input.limit);\n return [\n result.truncated ? `${result.text}\\n[truncated]` : result.text,\n { path, bytes: fileStat.size },\n ];\n },\n {\n name: Constants.READ_FILE,\n description:\n 'Read a local text file from the configured working directory with line numbers. ' +\n 'When `attachReadAttachments` is enabled (e.g. images-only), reading an image returns an ' +\n '`image_url` content block so vision-capable models can see the file directly.',\n schema: LocalReadFileToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalWriteFileTool(\n config: t.LocalExecutionConfig = {},\n checkpointer?: t.LocalFileCheckpointer\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as { file_path: string; content: string };\n if (config.readOnly === true) {\n throw new Error('write_file is blocked in read-only local mode.');\n }\n const path = await resolveWorkspacePathSafe(\n input.file_path,\n config,\n 'write'\n );\n if (checkpointer != null) {\n await checkpointer.captureBeforeWrite(path);\n }\n\n let before = '';\n let encoding = { text: '', hasBom: false, newline: '\\n' as const } as\n | ReturnType<typeof decodeFile>\n | { text: string; hasBom: false; newline: '\\n' };\n let existed = false;\n try {\n const raw = await fs.readFile(path, 'utf8');\n const decoded = decodeFile(raw);\n before = decoded.text;\n encoding = decoded;\n existed = true;\n } catch {\n existed = false;\n }\n\n await fs.mkdir(dirname(path), { recursive: true });\n const finalText = encodeFile(input.content, encoding);\n await fs.writeFile(path, finalText, 'utf8');\n\n const syntax = await maybeRunSyntaxCheck(path, config);\n\n const diff = existed\n ? summariseDiff(path, before, input.content)\n : `(new file, ${input.content.length} chars)`;\n const baseSummary = existed\n ? `Overwrote ${path} (${input.content.length} chars). Diff:\\n${diff}`\n : `Created ${path} (${input.content.length} chars).`;\n const summary = appendSyntaxCheckSummary(baseSummary, syntax);\n if (syntax?.outcome.ok === false && syntax.mode === 'strict') {\n // Roll back the write so strict mode is an actual gate, not\n // \"fail the call AND leave the corrupted file on disk\".\n // Codex P2 [49].\n await revertStrictWrite(fs, path, existed, before, encoding);\n throw new Error(\n `write_file syntax check failed (${syntax.outcome.checker}); reverted to pre-write state.\\n${syntax.outcome.output}`\n );\n }\n return [\n summary,\n {\n path,\n bytes: finalText.length,\n new_file: !existed,\n newline: encoding.newline === '\\r\\n' ? 'CRLF' : 'LF',\n had_bom: encoding.hasBom,\n ...(syntax != null && syntax.outcome.ok === false\n ? { syntax_error: syntax.outcome.checker }\n : {}),\n },\n ];\n },\n {\n name: LocalWriteFileToolName,\n description:\n 'Create or overwrite a local text file in the configured working directory. ' +\n 'Preserves the existing BOM and line endings when overwriting; defaults to LF without BOM for new files. ' +\n 'Returns a unified diff of the changes when overwriting.',\n schema: LocalWriteFileToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalEditFileTool(\n config: t.LocalExecutionConfig = {},\n checkpointer?: t.LocalFileCheckpointer\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n file_path: string;\n old_text?: string;\n new_text?: string;\n edits?: Array<{ old_text?: string; new_text?: string }>;\n };\n if (config.readOnly === true) {\n throw new Error('edit_file is blocked in read-only local mode.');\n }\n const edits = normalizeEdits(input);\n if (edits.length === 0) {\n throw new Error('edit_file requires old_text/new_text or edits[].');\n }\n\n const path = await resolveWorkspacePathSafe(\n input.file_path,\n config,\n 'write'\n );\n const raw = await fs.readFile(path, 'utf8');\n const encoding = decodeFile(raw);\n const original = encoding.text;\n\n let next = original;\n const strategiesUsed: string[] = [];\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i];\n const match = locateEdit(next, edit.oldText);\n if (match == null) {\n throw new Error(\n `Edit ${i + 1}/${edits.length}: could not locate old_text in ${input.file_path}. ` +\n 'Tried exact, line-trimmed, whitespace-normalized, and indentation-flexible matching. ' +\n 'Re-read the file and copy the literal lines.'\n );\n }\n strategiesUsed.push(match.strategy);\n next = applyEdit(next, match, edit.newText);\n }\n\n if (checkpointer != null) {\n await checkpointer.captureBeforeWrite(path);\n }\n const finalText = encodeFile(next, encoding);\n await fs.writeFile(path, finalText, 'utf8');\n\n const syntax = await maybeRunSyntaxCheck(path, config);\n\n const diff = summariseDiff(path, original, next);\n const fuzzy = strategiesUsed.some((s) => s !== 'exact');\n const baseSummary =\n `Applied ${edits.length} edit(s) to ${path}` +\n (fuzzy ? ` (strategies: ${strategiesUsed.join(', ')})` : '') +\n `. Diff:\\n${diff}`;\n const summary = appendSyntaxCheckSummary(baseSummary, syntax);\n if (syntax?.outcome.ok === false && syntax.mode === 'strict') {\n // Restore the pre-edit bytes so strict mode is an actual\n // gate (Codex P2 [49]). edit_file always operates on an\n // existing file, so `existed = true` here.\n await revertStrictWrite(fs, path, true, original, encoding);\n throw new Error(\n `edit_file syntax check failed (${syntax.outcome.checker}); reverted to pre-edit state.\\n${syntax.outcome.output}`\n );\n }\n return [\n summary,\n {\n path,\n edits: edits.length,\n strategies: strategiesUsed,\n newline: encoding.newline === '\\r\\n' ? 'CRLF' : 'LF',\n had_bom: encoding.hasBom,\n ...(syntax != null && syntax.outcome.ok === false\n ? { syntax_error: syntax.outcome.checker }\n : {}),\n },\n ];\n },\n {\n name: LocalEditFileToolName,\n description:\n 'Apply exact text replacements to a local file. The matcher tries exact, line-trimmed, whitespace-normalized, and indentation-flexible strategies in order so common LLM whitespace mistakes are recoverable. Each old_text must still match exactly one location. Returns a unified diff of the changes.',\n schema: LocalEditFileToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\n/**\n * Ripgrep availability cache, keyed on the *effective execution\n * backend* — whatever function `getSpawn(config)` returns. Without\n * the backend key, a Run that probes `rg` over Node's\n * `child_process.spawn` would poison subsequent Runs whose\n * `local.exec.spawn` routes to a remote sandbox or container that\n * doesn't have rg installed: the cached `true` would skip the probe,\n * the rg invocation would throw, and the Node fallback wouldn't be\n * reached. Per-backend caching avoids that without paying for a\n * spawn-per-search.\n */\n// Per-backend × per-env cache. Codex P1 #34 — keying by spawn\n// backend alone misses the case where two Runs share a backend but\n// vary `local.env` (especially PATH). Stale cache then claims `rg`\n// is available, the rg path runs, and the spawn fails with ENOENT\n// instead of falling back to the Node walker. The inner Map is\n// keyed by a stable JSON hash of the effective env so each unique\n// env gets its own probe.\nlet ripgrepAvailabilityByBackend = new WeakMap<\n t.LocalSpawn,\n Map<string, Promise<boolean>>\n>();\n\nfunction envCacheKey(env: NodeJS.ProcessEnv | undefined): string {\n // PATH is the only env entry that affects command lookup, but\n // hashing the whole env keeps the key correct for hosts that\n // vary anything else relevant. Stable JSON via sorted keys so\n // {A:1,B:2} and {B:2,A:1} produce the same hash.\n if (env == null) return '';\n const sorted: Record<string, string | undefined> = {};\n for (const k of Object.keys(env).sort()) {\n sorted[k] = env[k];\n }\n return JSON.stringify(sorted);\n}\n\nasync function isRipgrepAvailable(\n config: t.LocalExecutionConfig\n): Promise<boolean> {\n const backend = getSpawn(config);\n let envMap = ripgrepAvailabilityByBackend.get(backend);\n if (envMap == null) {\n envMap = new Map();\n ripgrepAvailabilityByBackend.set(backend, envMap);\n }\n const envKey = envCacheKey(config.env);\n let probePromise = envMap.get(envKey);\n if (probePromise == null) {\n probePromise = spawnLocalProcess(\n 'rg',\n ['--version'],\n { ...config, timeoutMs: 5000, sandbox: { enabled: false } },\n { internal: true }\n )\n .then((probe) => probe.exitCode === 0)\n .catch(() => false);\n envMap.set(envKey, probePromise);\n }\n return probePromise;\n}\n\n/**\n * Test-only reset hook. Clears the ripgrep-availability cache so\n * tests can swap in mocked spawn backends and reprobe deterministically.\n *\n * @internal Not part of the public SDK surface; the leading underscore\n * and `@internal` tag together signal that consumers should not call\n * this. Tests import it via the module path directly.\n */\nexport function _resetRipgrepCacheForTests(): void {\n ripgrepAvailabilityByBackend = new WeakMap();\n}\n\n// Skipped by the Node-fallback walker (used when ripgrep is\n// unavailable). Covers common build outputs, virtualenvs, and\n// caches so a `grep_search`/`glob_search` on a large monorepo or a\n// Python project with `.venv/` doesn't read every file under those\n// trees. ripgrep itself respects .gitignore so it doesn't need this\n// list. Audit follow-up from the comprehensive review (finding #3).\nconst SKIP_DIRS = new Set([\n '.git',\n '.svn',\n '.hg',\n 'node_modules',\n '.next',\n '.nuxt',\n '.cache',\n '.parcel-cache',\n '.turbo',\n 'dist',\n 'build',\n 'out',\n 'target',\n 'vendor',\n 'coverage',\n '.nyc_output',\n '__pycache__',\n '.venv',\n 'venv',\n 'env',\n '.tox',\n '.mypy_cache',\n '.pytest_cache',\n '.ruff_cache',\n]);\n\nfunction globToRegExp(pattern: string): RegExp {\n let result = '^';\n for (let i = 0; i < pattern.length; i++) {\n const c = pattern[i];\n if (c === '*') {\n if (pattern[i + 1] === '*') {\n result += '.*';\n i += 1;\n if (pattern[i + 1] === '/') {\n i += 1;\n }\n } else {\n result += '[^/]*';\n }\n } else if (c === '?') {\n result += '[^/]';\n } else if ('.+^$|(){}[]\\\\'.includes(c)) {\n result += '\\\\' + c;\n } else {\n result += c;\n }\n }\n result += '$';\n return new RegExp(result);\n}\n\nasync function* walkFiles(\n root: string,\n fs: import('./workspaceFS').WorkspaceFS\n): AsyncGenerator<string> {\n const stack: string[] = [root];\n while (stack.length > 0) {\n const dir = stack.pop() as string;\n let entries;\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry.name.startsWith('.git') || SKIP_DIRS.has(entry.name)) {\n continue;\n }\n const full = `${dir}/${entry.name}`;\n if (entry.isDirectory()) {\n stack.push(full);\n } else if (entry.isFile()) {\n yield full;\n }\n }\n }\n}\n\n/**\n * Catastrophic-backtracking guardrails for the fallback grep path.\n *\n * Without ripgrep we run the model-supplied pattern through Node's\n * `RegExp` engine, which uses a backtracking implementation. Patterns\n * with nested unbounded quantifiers (`(a+)+`, `(.*)*`, etc.) can\n * monopolise the event loop for arbitrary wall-clock time on\n * pathological input, and `setTimeout` cannot interrupt a synchronous\n * `RegExp.exec`. Manual review (finding D) flagged this as a real DoS.\n *\n * Mitigations applied here, in order of severity:\n * 1. Cap pattern length so an obviously oversize regex is rejected\n * before compile.\n * 2. Reject patterns that contain a nested unbounded quantifier of\n * the form `(...+|*)([+*]|{n,})` — the standard pathological\n * shape. Still a heuristic (not a full safety proof), but blocks\n * every common DoS construction we've seen in coding-agent logs.\n * 3. Wall-clock budget for the overall search: each file's regex\n * pass is checked against a deadline; once exceeded the search\n * bails with a partial result. Doesn't interrupt a stuck\n * `exec()` call, but stops a slow pattern from making the whole\n * Run hang once the first hung file finishes.\n *\n * Hosts that need bulletproof regex safety should install `rg` —\n * ripgrep uses RE2 internally and has no backtracking.\n */\nconst MAX_FALLBACK_PATTERN_LENGTH = 1024;\nconst FALLBACK_GREP_BUDGET_MS = 5000;\n// Per-file byte cap. Codex P2 #41 — without it, the whole-file\n// `readFile` + `split('\\n')` for a multi-GB log is an unbounded\n// allocation that the wall-clock budget (checked between files)\n// can't interrupt. Hosts that need to grep large files should\n// install ripgrep.\nconst FALLBACK_GREP_MAX_FILE_BYTES = 5 * 1024 * 1024;\n\n/**\n * Heuristic: walks `pattern` to find any `(<contents>)<quant>` where\n * `<contents>` itself has an unbounded quantifier. Catches the\n * classic `(a+)+` form AND the double-nested `((a+)+)` form (which a\n * single-pass regex misses because `[^)]*` stops at the first inner\n * close-paren). Misses sufficiently obfuscated cases — bulletproof\n * ReDoS detection requires a real parser. The 5 s wall-clock budget\n * is the hard backstop for anything this slip past.\n */\nfunction hasNestedUnboundedQuantifier(pattern: string): boolean {\n for (let i = 1; i < pattern.length - 1; i++) {\n if (pattern[i] !== ')') continue;\n if (pattern[i - 1] === '\\\\') continue;\n const next = pattern[i + 1];\n if (next !== '+' && next !== '*' && next !== '{') continue;\n // Walk back to find the matching opening paren (respecting depth\n // and `\\(` escapes).\n let depth = 1;\n let j = i - 1;\n while (j >= 0) {\n const c = pattern[j];\n const escaped = j > 0 && pattern[j - 1] === '\\\\';\n if (!escaped) {\n if (c === ')') depth++;\n else if (c === '(') {\n depth--;\n if (depth === 0) break;\n }\n }\n j--;\n }\n if (j < 0) continue;\n const inner = pattern.slice(j + 1, i);\n if (/(?<!\\\\)[+*]/.test(inner)) return true;\n }\n return false;\n}\n\nclass FallbackGrepError extends Error {\n readonly kind: 'pattern-too-long' | 'unsafe-pattern' | 'invalid-pattern';\n constructor(\n kind: 'pattern-too-long' | 'unsafe-pattern' | 'invalid-pattern',\n message: string\n ) {\n super(message);\n this.kind = kind;\n }\n}\n\nfunction compileFallbackRegex(pattern: string): RegExp {\n if (pattern.length > MAX_FALLBACK_PATTERN_LENGTH) {\n throw new FallbackGrepError(\n 'pattern-too-long',\n `Pattern exceeds ${MAX_FALLBACK_PATTERN_LENGTH}-char fallback cap (install ripgrep for unbounded patterns).`\n );\n }\n if (hasNestedUnboundedQuantifier(pattern)) {\n throw new FallbackGrepError(\n 'unsafe-pattern',\n 'Pattern contains a nested unbounded quantifier (e.g. `(a+)+` or `((a+)+)`) which can cause catastrophic backtracking in the Node fallback. Install ripgrep for RE2-safe matching.'\n );\n }\n try {\n return new RegExp(pattern);\n } catch (e) {\n throw new FallbackGrepError(\n 'invalid-pattern',\n `Invalid regex: ${(e as Error).message}`\n );\n }\n}\n\n/** Structured return so callers can count matches separately from\n * diagnostic skip-sentinels (Codex P2 [43]). */\ntype FallbackGrepResult = { matches: string[]; skipped: string[] };\n\n/** Renders fallback-grep output: real matches first, skip diagnostics appended. */\nfunction formatFallbackGrepDisplay(result: FallbackGrepResult): string {\n if (result.matches.length > 0) {\n return [...result.matches, ...result.skipped].join('\\n');\n }\n if (result.skipped.length > 0) {\n return result.skipped.join('\\n');\n }\n return 'No matches found.';\n}\n\nasync function fallbackGrep(\n root: string,\n pattern: string,\n globFilter: string | undefined,\n maxResults: number,\n fs: import('./workspaceFS').WorkspaceFS\n): Promise<FallbackGrepResult> {\n const rx = compileFallbackRegex(pattern);\n const deadline = Date.now() + FALLBACK_GREP_BUDGET_MS;\n const globRx =\n globFilter != null && globFilter !== ''\n ? globToRegExp(globFilter)\n : undefined;\n const matches: string[] = [];\n // Track skipped (oversize) files separately so they don't consume\n // the maxResults budget. Codex P2 [43]: round 14's fix pushed skip\n // sentinels into `matches`, so a directory of one oversize non-\n // matching file falsely reported `matches: 1`, and enough\n // oversize files could fill the budget before any real match was\n // scanned. Now diagnostics are appended after real matches and\n // independent of the budget.\n const skippedDiagnostics: string[] = [];\n for await (const file of walkFiles(root, fs)) {\n if (Date.now() > deadline) {\n // Wall-clock budget exceeded — return partial results rather\n // than letting a slow pattern hang the Run.\n return { matches, skipped: skippedDiagnostics };\n }\n if (globRx != null) {\n const rel = file.startsWith(root + '/')\n ? file.slice(root.length + 1)\n : file;\n if (!globRx.test(rel)) {\n continue;\n }\n }\n // Skip files larger than the per-file cap and remember them as\n // diagnostics (NOT as matches). Codex P2 [41]: pre-fix\n // `fs.readFile` then `.split('\\n')` allocated the whole file +\n // an array of every line, which a single multi-GB log could\n // turn into an OOM even after the regex DoS guards.\n let stat;\n try {\n stat = await fs.stat(file);\n } catch {\n continue;\n }\n if (stat.size > FALLBACK_GREP_MAX_FILE_BYTES) {\n skippedDiagnostics.push(\n `${file}:0:[skipped: file > ${FALLBACK_GREP_MAX_FILE_BYTES} bytes; install ripgrep for unbounded grep]`\n );\n continue;\n }\n let content;\n try {\n content = await fs.readFile(file, 'utf8');\n } catch {\n continue;\n }\n if (content.includes('\\0')) {\n continue;\n }\n // Re-check the deadline AFTER the read — a slow disk on one\n // file can blow the budget without us noticing.\n if (Date.now() > deadline) {\n return { matches, skipped: skippedDiagnostics };\n }\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n if (rx.test(lines[i])) {\n matches.push(`${file}:${i + 1}:${lines[i]}`);\n if (matches.length >= maxResults) {\n return { matches, skipped: skippedDiagnostics };\n }\n }\n }\n }\n return { matches, skipped: skippedDiagnostics };\n}\n\nasync function fallbackGlob(\n root: string,\n pattern: string,\n maxResults: number,\n fs: import('./workspaceFS').WorkspaceFS\n): Promise<string[]> {\n const rx = globToRegExp(pattern);\n const out: string[] = [];\n for await (const file of walkFiles(root, fs)) {\n const rel = file.startsWith(root + '/')\n ? file.slice(root.length + 1)\n : file;\n if (rx.test(rel)) {\n out.push(file);\n if (out.length >= maxResults) {\n break;\n }\n }\n }\n return out;\n}\n\nexport function createLocalGrepSearchTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n pattern: string;\n path?: string;\n glob?: string;\n max_results?: number;\n };\n const target = await resolveWorkspacePathSafe(\n input.path ?? '.',\n config,\n 'read'\n );\n const maxResults = Math.max(input.max_results ?? DEFAULT_MAX_RESULTS, 1);\n\n if (await isRipgrepAvailable(config)) {\n // Pass the pattern through `-e` so dash-prefixed patterns\n // like `-foo` are treated as the search regex, not as a\n // (probably-unknown) flag. `rg --help` explicitly requires\n // `-e/--regexp` (or `--`) for that case. Same trick avoids\n // any future flag-conflict if a user query happens to look\n // like an rg long option.\n const args = [\n '--line-number',\n '--column',\n '--hidden',\n '--glob',\n '!.git/**',\n ...(input.glob != null && input.glob !== ''\n ? ['--glob', input.glob]\n : []),\n '-e',\n input.pattern,\n target,\n ];\n const result = await spawnLocalProcess('rg', args, {\n ...config,\n timeoutMs: config.timeoutMs ?? 30000,\n });\n // ripgrep exit codes:\n // 0 → at least one match\n // 1 → no matches (clean — \"No matches found.\")\n // 2 → real error (bad regex, unreadable target, etc.)\n // Without this branch (Codex P2 #23 — same fix shape glob_search\n // got from P2 #13), exit-2 errors silently mapped to\n // `matches: 0`, so the agent treated tooling failures as a\n // genuine absence of matches.\n if (\n result.timedOut ||\n (result.exitCode != null && result.exitCode > 1)\n ) {\n const detail = result.stderr.trim() || `rg exited ${result.exitCode}`;\n return [\n `grep_search failed: ${detail}`,\n {\n matches: 0,\n engine: 'ripgrep',\n error: detail,\n exitCode: result.exitCode,\n },\n ];\n }\n const lines = result.stdout\n .split('\\n')\n .filter(Boolean)\n .slice(0, maxResults);\n const output =\n lines.length > 0\n ? lines.join('\\n')\n : result.stderr.trim() || 'No matches found.';\n return [output, { matches: lines.length, engine: 'ripgrep' }];\n }\n\n try {\n const { matches, skipped } = await fallbackGrep(\n target,\n input.pattern,\n input.glob,\n maxResults,\n fs\n );\n // Artifact count: ONLY real matches (Codex P2 [43] —\n // skip sentinels used to inflate the count and the budget).\n const display = formatFallbackGrepDisplay({ matches, skipped });\n return [\n display,\n {\n matches: matches.length,\n skipped: skipped.length,\n engine: 'node-fallback',\n },\n ];\n } catch (e) {\n if (e instanceof FallbackGrepError) {\n return [\n `grep_search refused the pattern: ${e.message}`,\n {\n matches: 0,\n engine: 'node-fallback',\n error: e.message,\n kind: e.kind,\n },\n ];\n }\n throw e;\n }\n },\n {\n name: LocalGrepSearchToolName,\n description:\n 'Search local files for a regex pattern (ripgrep when available, Node fallback otherwise).',\n schema: LocalGrepSearchToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalGlobSearchTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n pattern: string;\n path?: string;\n max_results?: number;\n };\n const target = await resolveWorkspacePathSafe(\n input.path ?? '.',\n config,\n 'read'\n );\n const maxResults = Math.max(input.max_results ?? DEFAULT_MAX_RESULTS, 1);\n\n if (await isRipgrepAvailable(config)) {\n const result = await spawnLocalProcess(\n 'rg',\n [\n '--files',\n '--hidden',\n '--glob',\n '!.git/**',\n '--glob',\n input.pattern,\n target,\n ],\n { ...config, timeoutMs: config.timeoutMs ?? 30000 }\n );\n // rg --files exit codes:\n // 0 → at least one file matched\n // 1 → no files matched (clean — \"No files found.\")\n // 2 → real error (bad glob, unreadable target, etc.)\n // Without this branch, exit-2 errors used to silently map to\n // \"No files found.\" — the agent then treats a tooling failure\n // as a real absence of matches.\n if (\n result.timedOut ||\n (result.exitCode != null && result.exitCode > 1)\n ) {\n const detail = result.stderr.trim() || `rg exited ${result.exitCode}`;\n return [\n `glob_search failed: ${detail}`,\n {\n files: [],\n engine: 'ripgrep',\n error: detail,\n exitCode: result.exitCode,\n },\n ];\n }\n const lines = result.stdout\n .split('\\n')\n .filter(Boolean)\n .slice(0, maxResults);\n return [\n lines.length > 0 ? lines.join('\\n') : 'No files found.',\n { files: lines, engine: 'ripgrep' },\n ];\n }\n\n const files = await fallbackGlob(target, input.pattern, maxResults, fs);\n return [\n files.length > 0 ? files.join('\\n') : 'No files found.',\n { files, engine: 'node-fallback' },\n ];\n },\n {\n name: LocalGlobSearchToolName,\n description:\n 'Find local files matching a glob pattern (ripgrep when available, Node fallback otherwise).',\n schema: LocalGlobSearchToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalListDirectoryTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as { path?: string };\n const path = await resolveWorkspacePathSafe(\n input.path ?? '.',\n config,\n 'read'\n );\n const entries = await fs.readdir(path, { withFileTypes: true });\n const output = entries\n .map(\n (entry) => `${entry.isDirectory() ? 'dir ' : 'file'}\\t${entry.name}`\n )\n .join('\\n');\n return [output || 'Directory is empty.', { path, count: entries.length }];\n },\n {\n name: LocalListDirectoryToolName,\n description: 'List files and directories in a local directory.',\n schema: LocalListDirectoryToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport type LocalCodingToolBundle = {\n tools: DynamicStructuredTool[];\n /**\n * Present when `config.fileCheckpointing === true` or a `checkpointer`\n * was passed in. Callers can call `rewind()` to restore captured\n * pre-write contents.\n */\n checkpointer?: t.LocalFileCheckpointer;\n};\n\nexport function createLocalCodingTools(\n config: t.LocalExecutionConfig = {},\n options: { checkpointer?: t.LocalFileCheckpointer } = {}\n): DynamicStructuredTool[] {\n const checkpointer =\n options.checkpointer ??\n (config.fileCheckpointing === true\n ? createLocalFileCheckpointer({ fs: config.exec?.fs })\n : undefined);\n return [\n createLocalReadFileTool(config),\n createLocalWriteFileTool(config, checkpointer),\n createLocalEditFileTool(config, checkpointer),\n createLocalGrepSearchTool(config),\n createLocalGlobSearchTool(config),\n createLocalListDirectoryTool(config),\n createCompileCheckTool(config),\n createLocalBashExecutionTool({ config }),\n createLocalCodeExecutionTool(config),\n createLocalProgrammaticToolCallingTool(config),\n createLocalBashProgrammaticToolCallingTool(config),\n ];\n}\n\n/**\n * Variant of `createLocalCodingTools` that returns the bundle alongside\n * the file checkpointer so callers can later call\n * `bundle.checkpointer?.rewind()`.\n */\nexport function createLocalCodingToolBundle(\n config: t.LocalExecutionConfig = {},\n options: { checkpointer?: t.LocalFileCheckpointer } = {}\n): LocalCodingToolBundle {\n const checkpointer =\n options.checkpointer ??\n (config.fileCheckpointing === true\n ? createLocalFileCheckpointer({ fs: config.exec?.fs })\n : undefined);\n return {\n tools: createLocalCodingTools(config, { checkpointer }),\n checkpointer,\n };\n}\n\nexport function createLocalCodingToolDefinitions(): t.LCTool[] {\n return [\n toolDefinition(\n Constants.READ_FILE,\n 'Read a local text file from the configured working directory with line numbers.',\n LocalReadFileToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalWriteFileToolName,\n 'Create or overwrite a local text file in the configured working directory.',\n LocalWriteFileToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalEditFileToolName,\n 'Apply exact text replacements to a local file.',\n LocalEditFileToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalGrepSearchToolName,\n 'Search local files with ripgrep and return matching lines.',\n LocalGrepSearchToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalGlobSearchToolName,\n 'Find local files matching a glob pattern.',\n LocalGlobSearchToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalListDirectoryToolName,\n 'List files and directories in a local directory.',\n LocalListDirectoryToolSchema as t.JsonSchemaType\n ),\n createCompileCheckToolDefinition(),\n ];\n}\n\nexport function createLocalCodingToolRegistry(): t.LCToolRegistry {\n return new Map(\n createLocalCodingToolDefinitions().map((definition) => [\n definition.name,\n definition,\n ])\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LocalCodingTools.mjs","names":["input"],"sources":["../../../../src/tools/local/LocalCodingTools.ts"],"sourcesContent":["import { basename, dirname } from 'path';\nimport { createTwoFilesPatch } from 'diff';\nimport { tool } from '@langchain/core/tools';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport {\n createLocalBashProgrammaticToolCallingTool,\n createLocalProgrammaticToolCallingTool,\n} from './LocalProgrammaticToolCalling';\nimport {\n getSpawn,\n getWorkspaceFS,\n resolveWorkspacePathSafe,\n spawnLocalProcess,\n truncateLocalOutput,\n} from './LocalExecutionEngine';\nimport {\n createLocalBashExecutionTool,\n createLocalCodeExecutionTool,\n} from './LocalExecutionTools';\nimport {\n createCompileCheckTool,\n createCompileCheckToolDefinition,\n} from './CompileCheckTool';\nimport { classifyAttachment, imageAttachmentContent } from './attachments';\nimport { createLocalFileCheckpointer } from './FileCheckpointer';\nimport { applyEdit, locateEdit } from './editStrategies';\nimport { decodeFile, encodeFile } from './textEncoding';\nimport { runPostEditSyntaxCheck } from './syntaxCheck';\nimport { Constants } from '@/common';\n\nconst MAX_READ_CHARS = 256000;\nconst DEFAULT_MAX_RESULTS = 200;\nconst DEFAULT_MAX_READ_BYTES = 10 * 1024 * 1024;\nconst BINARY_DETECTION_BYTES = 8000;\n\n/**\n * Tool name aliases retained for back-compat with consumers that imported\n * the per-file `Local*ToolName` constants. The canonical names live on\n * `Constants.*` (see `src/common/enum.ts`); these aliases just point at\n * them so a typo upstream gets caught at the type level.\n */\nexport const LocalWriteFileToolName = Constants.WRITE_FILE;\nexport const LocalEditFileToolName = Constants.EDIT_FILE;\nexport const LocalGrepSearchToolName = Constants.GREP_SEARCH;\nexport const LocalGlobSearchToolName = Constants.GLOB_SEARCH;\nexport const LocalListDirectoryToolName = Constants.LIST_DIRECTORY;\n\nexport const LocalReadFileToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description:\n 'Path to a local file, relative to the configured cwd unless absolute paths are allowed.',\n },\n offset: {\n type: 'integer',\n description: 'Optional 1-indexed line offset for large files.',\n },\n limit: {\n type: 'integer',\n description: 'Optional maximum number of lines to return.',\n },\n },\n required: ['path'],\n};\n\nexport const LocalWriteFileToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description:\n 'Path to write, relative to the configured cwd unless absolute paths are allowed.',\n },\n content: {\n type: 'string',\n description: 'Complete file contents to write.',\n },\n },\n required: ['path', 'content'],\n};\n\nexport const LocalEditFileToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description:\n 'Path to edit, relative to the configured cwd unless absolute paths are allowed.',\n },\n old_text: {\n type: 'string',\n description: 'Exact text to replace. Must appear exactly once.',\n },\n new_text: {\n type: 'string',\n description: 'Replacement text.',\n },\n edits: {\n type: 'array',\n description:\n 'Optional batch of exact replacements. Each old_text must appear exactly once in the original file.',\n items: {\n type: 'object',\n properties: {\n old_text: { type: 'string' },\n new_text: { type: 'string' },\n },\n required: ['old_text', 'new_text'],\n },\n },\n },\n required: ['path'],\n};\n\nexport const LocalGrepSearchToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Regex pattern to search for.',\n },\n path: {\n type: 'string',\n description: 'Directory or file to search. Defaults to cwd.',\n },\n glob: {\n type: 'string',\n description: 'Optional file glob passed to rg -g.',\n },\n max_results: {\n type: 'integer',\n description: 'Maximum matching lines to return.',\n },\n },\n required: ['pattern'],\n};\n\nexport const LocalGlobSearchToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'File glob pattern, for example \"src/**/*.ts\".',\n },\n path: {\n type: 'string',\n description: 'Directory to search. Defaults to cwd.',\n },\n max_results: {\n type: 'integer',\n description: 'Maximum file paths to return.',\n },\n },\n required: ['pattern'],\n};\n\nexport const LocalListDirectoryToolSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Directory to list. Defaults to cwd.',\n },\n },\n};\n\nfunction lineWindow(\n content: string,\n offset?: number,\n limit?: number\n): { text: string; truncated: boolean } {\n const start = Math.max((offset ?? 1) - 1, 0);\n // Avoid splitting the whole file when the caller asked for a small\n // window. For a 10 MB file with `offset: 1, limit: 10`, the prior\n // `content.split('\\n')` allocated millions of strings to throw all\n // but 10 away. We walk newline indices directly: O(start + limit)\n // instead of O(file). When `limit` is omitted, fall back to the\n // simple split — it's the same amount of work either way.\n if (limit == null || limit <= 0) {\n const lines = content.split('\\n');\n const selected = lines.slice(start);\n const numbered = selected\n .map(\n (line, index) =>\n `${String(start + index + 1).padStart(6, ' ')}\\t${line}`\n )\n .join('\\n');\n return {\n text: truncateLocalOutput(numbered, MAX_READ_CHARS),\n truncated: numbered.length > MAX_READ_CHARS,\n };\n }\n // Walk to the start line by counting newlines.\n let cursor = 0;\n for (let i = 0; i < start; i++) {\n const next = content.indexOf('\\n', cursor);\n if (next === -1) {\n // File has fewer lines than `offset` — return empty window.\n return { text: '', truncated: false };\n }\n cursor = next + 1;\n }\n // Collect up to `limit` lines from `cursor`.\n const out: string[] = [];\n let pos = cursor;\n let exhausted = true;\n for (let k = 0; k < limit; k++) {\n const next = content.indexOf('\\n', pos);\n if (next === -1) {\n out.push(content.slice(pos));\n break;\n }\n out.push(content.slice(pos, next));\n pos = next + 1;\n if (k === limit - 1 && pos < content.length) {\n exhausted = false;\n }\n }\n const numbered = out\n .map(\n (text, index) => `${String(start + index + 1).padStart(6, ' ')}\\t${text}`\n )\n .join('\\n');\n return {\n text: truncateLocalOutput(numbered, MAX_READ_CHARS),\n truncated: !exhausted || numbered.length > MAX_READ_CHARS,\n };\n}\n\nconst MAX_DIFF_CHARS = 4000;\n\ntype SyntaxRun =\n | {\n mode: 'auto' | 'strict';\n outcome: import('./syntaxCheck').SyntaxCheckOutcome;\n }\n | undefined;\n\nasync function maybeRunSyntaxCheck(\n path: string,\n config: t.LocalExecutionConfig\n): Promise<SyntaxRun> {\n const mode = config.postEditSyntaxCheck ?? 'off';\n if (mode === 'off') return undefined;\n const outcome = await runPostEditSyntaxCheck(path, config);\n if (outcome == null) return undefined;\n return { mode, outcome };\n}\n\nfunction appendSyntaxCheckSummary(base: string, run: SyntaxRun): string {\n if (run == null) return base;\n if (run.outcome.ok) return base;\n const banner =\n run.mode === 'strict'\n ? `\\n\\n[syntax-check FAILED via ${run.outcome.checker}]\\n`\n : `\\n\\n[syntax-check warning via ${run.outcome.checker}]\\n`;\n return `${base}${banner}${run.outcome.output}`;\n}\n\n/**\n * Revert a write_file/edit_file mutation in `postEditSyntaxCheck:\n * 'strict'` mode after the post-write syntax check failed. Strict\n * mode advertises a safety gate, so leaving the corrupted file on\n * disk + throwing is a half-broken contract — the model \"reacts\" to\n * the error but the next call sees broken on-disk state. Codex P2\n * [49]. Best-effort: a swallowed error here means the workspace is\n * still in the bad post-write state, but we still throw the\n * original syntax-check error so the caller knows.\n *\n * - If the file existed pre-write: restore the previous bytes with\n * the original encoding.\n * - If the file is brand-new: unlink it.\n */\nasync function revertStrictWrite(\n fs: import('./workspaceFS').WorkspaceFS,\n path: string,\n existed: boolean,\n before: string,\n encoding: { text: string; hasBom: boolean; newline: '\\n' | '\\r\\n' }\n): Promise<void> {\n try {\n if (existed) {\n // encodeFile uses encoding.{hasBom,newline} to restore the\n // on-disk shape; the `text` field is overridden by the\n // explicit `before` arg we pass in.\n await fs.writeFile(\n path,\n encodeFile(before, { ...encoding, text: before }),\n 'utf8'\n );\n } else {\n await fs.unlink(path);\n }\n } catch {\n /* best-effort: caller still sees the original syntax error */\n }\n}\n\nfunction summariseDiff(\n filePath: string,\n before: string,\n after: string\n): string {\n if (before === after) {\n return '(no textual changes)';\n }\n const name = basename(filePath);\n const patch = createTwoFilesPatch(name, name, before, after, '', '', {\n context: 3,\n });\n if (patch.length <= MAX_DIFF_CHARS) {\n return patch;\n }\n return (\n patch.slice(0, MAX_DIFF_CHARS) +\n `\\n[... diff truncated, ${patch.length - MAX_DIFF_CHARS} more chars ...]`\n );\n}\n\nfunction normalizeEdits(input: {\n old_text?: string;\n new_text?: string;\n edits?: Array<{ old_text?: string; new_text?: string }>;\n}): Array<{ oldText: string; newText: string }> {\n const edits = Array.isArray(input.edits)\n ? input.edits.map((edit) => ({\n oldText: edit.old_text ?? '',\n newText: edit.new_text ?? '',\n }))\n : [];\n\n if (input.old_text != null || input.new_text != null) {\n edits.push({\n oldText: input.old_text ?? '',\n newText: input.new_text ?? '',\n });\n }\n\n return edits;\n}\n\nfunction toolDefinition(\n name: string,\n description: string,\n parameters: t.JsonSchemaType\n): t.LCTool {\n return {\n name,\n description,\n parameters,\n allowed_callers: ['direct', 'code_execution'],\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n toolType: 'builtin',\n };\n}\n\nasync function looksBinary(\n path: string,\n fs: import('./workspaceFS').WorkspaceFS\n): Promise<boolean> {\n let handle;\n try {\n handle = await fs.open(path, 'r');\n const sample = Buffer.alloc(BINARY_DETECTION_BYTES);\n const { bytesRead } = await handle.read(\n sample,\n 0,\n BINARY_DETECTION_BYTES,\n 0\n );\n for (let i = 0; i < bytesRead; i++) {\n if (sample[i] === 0) {\n return true;\n }\n }\n return false;\n } finally {\n await handle?.close();\n }\n}\n\nconst DEFAULT_MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;\n\nexport function createLocalReadFileTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n path: string;\n offset?: number;\n limit?: number;\n };\n const path = await resolveWorkspacePathSafe(\n input.path,\n config,\n 'read'\n );\n const fileStat = await fs.stat(path);\n if (!fileStat.isFile()) {\n throw new Error(`Path is not a file: ${input.path}`);\n }\n const maxBytes = Math.max(\n config.maxReadBytes ?? DEFAULT_MAX_READ_BYTES,\n 1\n );\n if (fileStat.size > maxBytes) {\n const stub = `File is ${fileStat.size} bytes, exceeds the ${maxBytes}-byte read cap. Read a slice via bash (e.g. head/sed) or raise local.maxReadBytes.`;\n return [stub, { path, bytes: fileStat.size, truncated: true }];\n }\n\n if (await looksBinary(path, fs)) {\n const attachmentMode = config.attachReadAttachments ?? 'off';\n if (attachmentMode !== 'off') {\n const attachment = await classifyAttachment({\n path,\n bytes: fileStat.size,\n mode: attachmentMode,\n maxBytes: config.maxAttachmentBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES,\n // Route through the configured WorkspaceFS so a custom\n // engine sees the same path semantics as `read_file`\n // itself (manual review finding F).\n fs,\n });\n if (attachment.kind === 'image') {\n return [\n imageAttachmentContent(path, attachment),\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n attachment: 'image',\n },\n ];\n }\n if (attachment.kind === 'pdf') {\n return [\n [\n {\n type: 'text',\n text: `Read ${path} (application/pdf, ${fileStat.size} bytes). PDF attached as base64 data URL; vision-capable models that accept PDF will render it.`,\n },\n {\n type: 'image_url',\n image_url: { url: attachment.dataUrl },\n },\n ],\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n attachment: 'pdf',\n },\n ];\n }\n if (attachment.kind === 'oversize') {\n return [\n `Refusing to embed ${attachment.mime} attachment (${attachment.bytes} bytes exceeds ${attachment.maxBytes}-byte cap).`,\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n attachment: 'oversize',\n },\n ];\n }\n if (attachment.kind === 'binary') {\n return [\n `Refusing to read binary file (${fileStat.size} bytes, ${attachment.mime}): ${path}`,\n {\n path,\n bytes: fileStat.size,\n mime: attachment.mime,\n binary: true,\n },\n ];\n }\n // text-or-unknown falls through to the text-read path below.\n } else {\n return [\n `Refusing to read binary file (${fileStat.size} bytes): ${path}`,\n { path, bytes: fileStat.size, binary: true },\n ];\n }\n }\n\n const content = await fs.readFile(path, 'utf8');\n const result = lineWindow(content, input.offset, input.limit);\n return [\n result.truncated ? `${result.text}\\n[truncated]` : result.text,\n { path, bytes: fileStat.size },\n ];\n },\n {\n name: Constants.READ_FILE,\n description:\n 'Read a local text file from the configured working directory with line numbers. ' +\n 'When `attachReadAttachments` is enabled (e.g. images-only), reading an image returns an ' +\n '`image_url` content block so vision-capable models can see the file directly.',\n schema: LocalReadFileToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalWriteFileTool(\n config: t.LocalExecutionConfig = {},\n checkpointer?: t.LocalFileCheckpointer\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as { path: string; content: string };\n if (config.readOnly === true) {\n throw new Error('write_file is blocked in read-only local mode.');\n }\n const path = await resolveWorkspacePathSafe(\n input.path,\n config,\n 'write'\n );\n if (checkpointer != null) {\n await checkpointer.captureBeforeWrite(path);\n }\n\n let before = '';\n let encoding = { text: '', hasBom: false, newline: '\\n' as const } as\n | ReturnType<typeof decodeFile>\n | { text: string; hasBom: false; newline: '\\n' };\n let existed = false;\n try {\n const raw = await fs.readFile(path, 'utf8');\n const decoded = decodeFile(raw);\n before = decoded.text;\n encoding = decoded;\n existed = true;\n } catch {\n existed = false;\n }\n\n await fs.mkdir(dirname(path), { recursive: true });\n const finalText = encodeFile(input.content, encoding);\n await fs.writeFile(path, finalText, 'utf8');\n\n const syntax = await maybeRunSyntaxCheck(path, config);\n\n const diff = existed\n ? summariseDiff(path, before, input.content)\n : `(new file, ${input.content.length} chars)`;\n const baseSummary = existed\n ? `Overwrote ${path} (${input.content.length} chars). Diff:\\n${diff}`\n : `Created ${path} (${input.content.length} chars).`;\n const summary = appendSyntaxCheckSummary(baseSummary, syntax);\n if (syntax?.outcome.ok === false && syntax.mode === 'strict') {\n // Roll back the write so strict mode is an actual gate, not\n // \"fail the call AND leave the corrupted file on disk\".\n // Codex P2 [49].\n await revertStrictWrite(fs, path, existed, before, encoding);\n throw new Error(\n `write_file syntax check failed (${syntax.outcome.checker}); reverted to pre-write state.\\n${syntax.outcome.output}`\n );\n }\n return [\n summary,\n {\n path,\n bytes: finalText.length,\n new_file: !existed,\n newline: encoding.newline === '\\r\\n' ? 'CRLF' : 'LF',\n had_bom: encoding.hasBom,\n ...(syntax != null && syntax.outcome.ok === false\n ? { syntax_error: syntax.outcome.checker }\n : {}),\n },\n ];\n },\n {\n name: LocalWriteFileToolName,\n description:\n 'Create or overwrite a local text file in the configured working directory. ' +\n 'Preserves the existing BOM and line endings when overwriting; defaults to LF without BOM for new files. ' +\n 'Returns a unified diff of the changes when overwriting.',\n schema: LocalWriteFileToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalEditFileTool(\n config: t.LocalExecutionConfig = {},\n checkpointer?: t.LocalFileCheckpointer\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n path: string;\n old_text?: string;\n new_text?: string;\n edits?: Array<{ old_text?: string; new_text?: string }>;\n };\n if (config.readOnly === true) {\n throw new Error('edit_file is blocked in read-only local mode.');\n }\n const edits = normalizeEdits(input);\n if (edits.length === 0) {\n throw new Error('edit_file requires old_text/new_text or edits[].');\n }\n\n const path = await resolveWorkspacePathSafe(\n input.path,\n config,\n 'write'\n );\n const raw = await fs.readFile(path, 'utf8');\n const encoding = decodeFile(raw);\n const original = encoding.text;\n\n let next = original;\n const strategiesUsed: string[] = [];\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i];\n const match = locateEdit(next, edit.oldText);\n if (match == null) {\n throw new Error(\n `Edit ${i + 1}/${edits.length}: could not locate old_text in ${input.path}. ` +\n 'Tried exact, line-trimmed, whitespace-normalized, and indentation-flexible matching. ' +\n 'Re-read the file and copy the literal lines.'\n );\n }\n strategiesUsed.push(match.strategy);\n next = applyEdit(next, match, edit.newText);\n }\n\n if (checkpointer != null) {\n await checkpointer.captureBeforeWrite(path);\n }\n const finalText = encodeFile(next, encoding);\n await fs.writeFile(path, finalText, 'utf8');\n\n const syntax = await maybeRunSyntaxCheck(path, config);\n\n const diff = summariseDiff(path, original, next);\n const fuzzy = strategiesUsed.some((s) => s !== 'exact');\n const baseSummary =\n `Applied ${edits.length} edit(s) to ${path}` +\n (fuzzy ? ` (strategies: ${strategiesUsed.join(', ')})` : '') +\n `. Diff:\\n${diff}`;\n const summary = appendSyntaxCheckSummary(baseSummary, syntax);\n if (syntax?.outcome.ok === false && syntax.mode === 'strict') {\n // Restore the pre-edit bytes so strict mode is an actual\n // gate (Codex P2 [49]). edit_file always operates on an\n // existing file, so `existed = true` here.\n await revertStrictWrite(fs, path, true, original, encoding);\n throw new Error(\n `edit_file syntax check failed (${syntax.outcome.checker}); reverted to pre-edit state.\\n${syntax.outcome.output}`\n );\n }\n return [\n summary,\n {\n path,\n edits: edits.length,\n strategies: strategiesUsed,\n newline: encoding.newline === '\\r\\n' ? 'CRLF' : 'LF',\n had_bom: encoding.hasBom,\n ...(syntax != null && syntax.outcome.ok === false\n ? { syntax_error: syntax.outcome.checker }\n : {}),\n },\n ];\n },\n {\n name: LocalEditFileToolName,\n description:\n 'Apply exact text replacements to a local file. The matcher tries exact, line-trimmed, whitespace-normalized, and indentation-flexible strategies in order so common LLM whitespace mistakes are recoverable. Each old_text must still match exactly one location. Returns a unified diff of the changes.',\n schema: LocalEditFileToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\n/**\n * Ripgrep availability cache, keyed on the *effective execution\n * backend* — whatever function `getSpawn(config)` returns. Without\n * the backend key, a Run that probes `rg` over Node's\n * `child_process.spawn` would poison subsequent Runs whose\n * `local.exec.spawn` routes to a remote sandbox or container that\n * doesn't have rg installed: the cached `true` would skip the probe,\n * the rg invocation would throw, and the Node fallback wouldn't be\n * reached. Per-backend caching avoids that without paying for a\n * spawn-per-search.\n */\n// Per-backend × per-env cache. Codex P1 #34 — keying by spawn\n// backend alone misses the case where two Runs share a backend but\n// vary `local.env` (especially PATH). Stale cache then claims `rg`\n// is available, the rg path runs, and the spawn fails with ENOENT\n// instead of falling back to the Node walker. The inner Map is\n// keyed by a stable JSON hash of the effective env so each unique\n// env gets its own probe.\nlet ripgrepAvailabilityByBackend = new WeakMap<\n t.LocalSpawn,\n Map<string, Promise<boolean>>\n>();\n\nfunction envCacheKey(env: NodeJS.ProcessEnv | undefined): string {\n // PATH is the only env entry that affects command lookup, but\n // hashing the whole env keeps the key correct for hosts that\n // vary anything else relevant. Stable JSON via sorted keys so\n // {A:1,B:2} and {B:2,A:1} produce the same hash.\n if (env == null) return '';\n const sorted: Record<string, string | undefined> = {};\n for (const k of Object.keys(env).sort()) {\n sorted[k] = env[k];\n }\n return JSON.stringify(sorted);\n}\n\nasync function isRipgrepAvailable(\n config: t.LocalExecutionConfig\n): Promise<boolean> {\n const backend = getSpawn(config);\n let envMap = ripgrepAvailabilityByBackend.get(backend);\n if (envMap == null) {\n envMap = new Map();\n ripgrepAvailabilityByBackend.set(backend, envMap);\n }\n const envKey = envCacheKey(config.env);\n let probePromise = envMap.get(envKey);\n if (probePromise == null) {\n probePromise = spawnLocalProcess(\n 'rg',\n ['--version'],\n { ...config, timeoutMs: 5000, sandbox: { enabled: false } },\n { internal: true }\n )\n .then((probe) => probe.exitCode === 0)\n .catch(() => false);\n envMap.set(envKey, probePromise);\n }\n return probePromise;\n}\n\n/**\n * Test-only reset hook. Clears the ripgrep-availability cache so\n * tests can swap in mocked spawn backends and reprobe deterministically.\n *\n * @internal Not part of the public SDK surface; the leading underscore\n * and `@internal` tag together signal that consumers should not call\n * this. Tests import it via the module path directly.\n */\nexport function _resetRipgrepCacheForTests(): void {\n ripgrepAvailabilityByBackend = new WeakMap();\n}\n\n// Skipped by the Node-fallback walker (used when ripgrep is\n// unavailable). Covers common build outputs, virtualenvs, and\n// caches so a `grep_search`/`glob_search` on a large monorepo or a\n// Python project with `.venv/` doesn't read every file under those\n// trees. ripgrep itself respects .gitignore so it doesn't need this\n// list. Audit follow-up from the comprehensive review (finding #3).\nconst SKIP_DIRS = new Set([\n '.git',\n '.svn',\n '.hg',\n 'node_modules',\n '.next',\n '.nuxt',\n '.cache',\n '.parcel-cache',\n '.turbo',\n 'dist',\n 'build',\n 'out',\n 'target',\n 'vendor',\n 'coverage',\n '.nyc_output',\n '__pycache__',\n '.venv',\n 'venv',\n 'env',\n '.tox',\n '.mypy_cache',\n '.pytest_cache',\n '.ruff_cache',\n]);\n\nfunction globToRegExp(pattern: string): RegExp {\n let result = '^';\n for (let i = 0; i < pattern.length; i++) {\n const c = pattern[i];\n if (c === '*') {\n if (pattern[i + 1] === '*') {\n result += '.*';\n i += 1;\n if (pattern[i + 1] === '/') {\n i += 1;\n }\n } else {\n result += '[^/]*';\n }\n } else if (c === '?') {\n result += '[^/]';\n } else if ('.+^$|(){}[]\\\\'.includes(c)) {\n result += '\\\\' + c;\n } else {\n result += c;\n }\n }\n result += '$';\n return new RegExp(result);\n}\n\nasync function* walkFiles(\n root: string,\n fs: import('./workspaceFS').WorkspaceFS\n): AsyncGenerator<string> {\n const stack: string[] = [root];\n while (stack.length > 0) {\n const dir = stack.pop() as string;\n let entries;\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry.name.startsWith('.git') || SKIP_DIRS.has(entry.name)) {\n continue;\n }\n const full = `${dir}/${entry.name}`;\n if (entry.isDirectory()) {\n stack.push(full);\n } else if (entry.isFile()) {\n yield full;\n }\n }\n }\n}\n\n/**\n * Catastrophic-backtracking guardrails for the fallback grep path.\n *\n * Without ripgrep we run the model-supplied pattern through Node's\n * `RegExp` engine, which uses a backtracking implementation. Patterns\n * with nested unbounded quantifiers (`(a+)+`, `(.*)*`, etc.) can\n * monopolise the event loop for arbitrary wall-clock time on\n * pathological input, and `setTimeout` cannot interrupt a synchronous\n * `RegExp.exec`. Manual review (finding D) flagged this as a real DoS.\n *\n * Mitigations applied here, in order of severity:\n * 1. Cap pattern length so an obviously oversize regex is rejected\n * before compile.\n * 2. Reject patterns that contain a nested unbounded quantifier of\n * the form `(...+|*)([+*]|{n,})` — the standard pathological\n * shape. Still a heuristic (not a full safety proof), but blocks\n * every common DoS construction we've seen in coding-agent logs.\n * 3. Wall-clock budget for the overall search: each file's regex\n * pass is checked against a deadline; once exceeded the search\n * bails with a partial result. Doesn't interrupt a stuck\n * `exec()` call, but stops a slow pattern from making the whole\n * Run hang once the first hung file finishes.\n *\n * Hosts that need bulletproof regex safety should install `rg` —\n * ripgrep uses RE2 internally and has no backtracking.\n */\nconst MAX_FALLBACK_PATTERN_LENGTH = 1024;\nconst FALLBACK_GREP_BUDGET_MS = 5000;\n// Per-file byte cap. Codex P2 #41 — without it, the whole-file\n// `readFile` + `split('\\n')` for a multi-GB log is an unbounded\n// allocation that the wall-clock budget (checked between files)\n// can't interrupt. Hosts that need to grep large files should\n// install ripgrep.\nconst FALLBACK_GREP_MAX_FILE_BYTES = 5 * 1024 * 1024;\n\n/**\n * Heuristic: walks `pattern` to find any `(<contents>)<quant>` where\n * `<contents>` itself has an unbounded quantifier. Catches the\n * classic `(a+)+` form AND the double-nested `((a+)+)` form (which a\n * single-pass regex misses because `[^)]*` stops at the first inner\n * close-paren). Misses sufficiently obfuscated cases — bulletproof\n * ReDoS detection requires a real parser. The 5 s wall-clock budget\n * is the hard backstop for anything this slip past.\n */\nfunction hasNestedUnboundedQuantifier(pattern: string): boolean {\n for (let i = 1; i < pattern.length - 1; i++) {\n if (pattern[i] !== ')') continue;\n if (pattern[i - 1] === '\\\\') continue;\n const next = pattern[i + 1];\n if (next !== '+' && next !== '*' && next !== '{') continue;\n // Walk back to find the matching opening paren (respecting depth\n // and `\\(` escapes).\n let depth = 1;\n let j = i - 1;\n while (j >= 0) {\n const c = pattern[j];\n const escaped = j > 0 && pattern[j - 1] === '\\\\';\n if (!escaped) {\n if (c === ')') depth++;\n else if (c === '(') {\n depth--;\n if (depth === 0) break;\n }\n }\n j--;\n }\n if (j < 0) continue;\n const inner = pattern.slice(j + 1, i);\n if (/(?<!\\\\)[+*]/.test(inner)) return true;\n }\n return false;\n}\n\nclass FallbackGrepError extends Error {\n readonly kind: 'pattern-too-long' | 'unsafe-pattern' | 'invalid-pattern';\n constructor(\n kind: 'pattern-too-long' | 'unsafe-pattern' | 'invalid-pattern',\n message: string\n ) {\n super(message);\n this.kind = kind;\n }\n}\n\nfunction compileFallbackRegex(pattern: string): RegExp {\n if (pattern.length > MAX_FALLBACK_PATTERN_LENGTH) {\n throw new FallbackGrepError(\n 'pattern-too-long',\n `Pattern exceeds ${MAX_FALLBACK_PATTERN_LENGTH}-char fallback cap (install ripgrep for unbounded patterns).`\n );\n }\n if (hasNestedUnboundedQuantifier(pattern)) {\n throw new FallbackGrepError(\n 'unsafe-pattern',\n 'Pattern contains a nested unbounded quantifier (e.g. `(a+)+` or `((a+)+)`) which can cause catastrophic backtracking in the Node fallback. Install ripgrep for RE2-safe matching.'\n );\n }\n try {\n return new RegExp(pattern);\n } catch (e) {\n throw new FallbackGrepError(\n 'invalid-pattern',\n `Invalid regex: ${(e as Error).message}`\n );\n }\n}\n\n/** Structured return so callers can count matches separately from\n * diagnostic skip-sentinels (Codex P2 [43]). */\ntype FallbackGrepResult = { matches: string[]; skipped: string[] };\n\n/** Renders fallback-grep output: real matches first, skip diagnostics appended. */\nfunction formatFallbackGrepDisplay(result: FallbackGrepResult): string {\n if (result.matches.length > 0) {\n return [...result.matches, ...result.skipped].join('\\n');\n }\n if (result.skipped.length > 0) {\n return result.skipped.join('\\n');\n }\n return 'No matches found.';\n}\n\nasync function fallbackGrep(\n root: string,\n pattern: string,\n globFilter: string | undefined,\n maxResults: number,\n fs: import('./workspaceFS').WorkspaceFS\n): Promise<FallbackGrepResult> {\n const rx = compileFallbackRegex(pattern);\n const deadline = Date.now() + FALLBACK_GREP_BUDGET_MS;\n const globRx =\n globFilter != null && globFilter !== ''\n ? globToRegExp(globFilter)\n : undefined;\n const matches: string[] = [];\n // Track skipped (oversize) files separately so they don't consume\n // the maxResults budget. Codex P2 [43]: round 14's fix pushed skip\n // sentinels into `matches`, so a directory of one oversize non-\n // matching file falsely reported `matches: 1`, and enough\n // oversize files could fill the budget before any real match was\n // scanned. Now diagnostics are appended after real matches and\n // independent of the budget.\n const skippedDiagnostics: string[] = [];\n for await (const file of walkFiles(root, fs)) {\n if (Date.now() > deadline) {\n // Wall-clock budget exceeded — return partial results rather\n // than letting a slow pattern hang the Run.\n return { matches, skipped: skippedDiagnostics };\n }\n if (globRx != null) {\n const rel = file.startsWith(root + '/')\n ? file.slice(root.length + 1)\n : file;\n if (!globRx.test(rel)) {\n continue;\n }\n }\n // Skip files larger than the per-file cap and remember them as\n // diagnostics (NOT as matches). Codex P2 [41]: pre-fix\n // `fs.readFile` then `.split('\\n')` allocated the whole file +\n // an array of every line, which a single multi-GB log could\n // turn into an OOM even after the regex DoS guards.\n let stat;\n try {\n stat = await fs.stat(file);\n } catch {\n continue;\n }\n if (stat.size > FALLBACK_GREP_MAX_FILE_BYTES) {\n skippedDiagnostics.push(\n `${file}:0:[skipped: file > ${FALLBACK_GREP_MAX_FILE_BYTES} bytes; install ripgrep for unbounded grep]`\n );\n continue;\n }\n let content;\n try {\n content = await fs.readFile(file, 'utf8');\n } catch {\n continue;\n }\n if (content.includes('\\0')) {\n continue;\n }\n // Re-check the deadline AFTER the read — a slow disk on one\n // file can blow the budget without us noticing.\n if (Date.now() > deadline) {\n return { matches, skipped: skippedDiagnostics };\n }\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n if (rx.test(lines[i])) {\n matches.push(`${file}:${i + 1}:${lines[i]}`);\n if (matches.length >= maxResults) {\n return { matches, skipped: skippedDiagnostics };\n }\n }\n }\n }\n return { matches, skipped: skippedDiagnostics };\n}\n\nasync function fallbackGlob(\n root: string,\n pattern: string,\n maxResults: number,\n fs: import('./workspaceFS').WorkspaceFS\n): Promise<string[]> {\n const rx = globToRegExp(pattern);\n const out: string[] = [];\n for await (const file of walkFiles(root, fs)) {\n const rel = file.startsWith(root + '/')\n ? file.slice(root.length + 1)\n : file;\n if (rx.test(rel)) {\n out.push(file);\n if (out.length >= maxResults) {\n break;\n }\n }\n }\n return out;\n}\n\nexport function createLocalGrepSearchTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n pattern: string;\n path?: string;\n glob?: string;\n max_results?: number;\n };\n const target = await resolveWorkspacePathSafe(\n input.path ?? '.',\n config,\n 'read'\n );\n const maxResults = Math.max(input.max_results ?? DEFAULT_MAX_RESULTS, 1);\n\n if (await isRipgrepAvailable(config)) {\n // Pass the pattern through `-e` so dash-prefixed patterns\n // like `-foo` are treated as the search regex, not as a\n // (probably-unknown) flag. `rg --help` explicitly requires\n // `-e/--regexp` (or `--`) for that case. Same trick avoids\n // any future flag-conflict if a user query happens to look\n // like an rg long option.\n const args = [\n '--line-number',\n '--column',\n '--hidden',\n '--glob',\n '!.git/**',\n ...(input.glob != null && input.glob !== ''\n ? ['--glob', input.glob]\n : []),\n '-e',\n input.pattern,\n target,\n ];\n const result = await spawnLocalProcess('rg', args, {\n ...config,\n timeoutMs: config.timeoutMs ?? 30000,\n });\n // ripgrep exit codes:\n // 0 → at least one match\n // 1 → no matches (clean — \"No matches found.\")\n // 2 → real error (bad regex, unreadable target, etc.)\n // Without this branch (Codex P2 #23 — same fix shape glob_search\n // got from P2 #13), exit-2 errors silently mapped to\n // `matches: 0`, so the agent treated tooling failures as a\n // genuine absence of matches.\n if (\n result.timedOut ||\n (result.exitCode != null && result.exitCode > 1)\n ) {\n const detail = result.stderr.trim() || `rg exited ${result.exitCode}`;\n return [\n `grep_search failed: ${detail}`,\n {\n matches: 0,\n engine: 'ripgrep',\n error: detail,\n exitCode: result.exitCode,\n },\n ];\n }\n const lines = result.stdout\n .split('\\n')\n .filter(Boolean)\n .slice(0, maxResults);\n const output =\n lines.length > 0\n ? lines.join('\\n')\n : result.stderr.trim() || 'No matches found.';\n return [output, { matches: lines.length, engine: 'ripgrep' }];\n }\n\n try {\n const { matches, skipped } = await fallbackGrep(\n target,\n input.pattern,\n input.glob,\n maxResults,\n fs\n );\n // Artifact count: ONLY real matches (Codex P2 [43] —\n // skip sentinels used to inflate the count and the budget).\n const display = formatFallbackGrepDisplay({ matches, skipped });\n return [\n display,\n {\n matches: matches.length,\n skipped: skipped.length,\n engine: 'node-fallback',\n },\n ];\n } catch (e) {\n if (e instanceof FallbackGrepError) {\n return [\n `grep_search refused the pattern: ${e.message}`,\n {\n matches: 0,\n engine: 'node-fallback',\n error: e.message,\n kind: e.kind,\n },\n ];\n }\n throw e;\n }\n },\n {\n name: LocalGrepSearchToolName,\n description:\n 'Search local files for a regex pattern (ripgrep when available, Node fallback otherwise).',\n schema: LocalGrepSearchToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalGlobSearchTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n pattern: string;\n path?: string;\n max_results?: number;\n };\n const target = await resolveWorkspacePathSafe(\n input.path ?? '.',\n config,\n 'read'\n );\n const maxResults = Math.max(input.max_results ?? DEFAULT_MAX_RESULTS, 1);\n\n if (await isRipgrepAvailable(config)) {\n const result = await spawnLocalProcess(\n 'rg',\n [\n '--files',\n '--hidden',\n '--glob',\n '!.git/**',\n '--glob',\n input.pattern,\n target,\n ],\n { ...config, timeoutMs: config.timeoutMs ?? 30000 }\n );\n // rg --files exit codes:\n // 0 → at least one file matched\n // 1 → no files matched (clean — \"No files found.\")\n // 2 → real error (bad glob, unreadable target, etc.)\n // Without this branch, exit-2 errors used to silently map to\n // \"No files found.\" — the agent then treats a tooling failure\n // as a real absence of matches.\n if (\n result.timedOut ||\n (result.exitCode != null && result.exitCode > 1)\n ) {\n const detail = result.stderr.trim() || `rg exited ${result.exitCode}`;\n return [\n `glob_search failed: ${detail}`,\n {\n files: [],\n engine: 'ripgrep',\n error: detail,\n exitCode: result.exitCode,\n },\n ];\n }\n const lines = result.stdout\n .split('\\n')\n .filter(Boolean)\n .slice(0, maxResults);\n return [\n lines.length > 0 ? lines.join('\\n') : 'No files found.',\n { files: lines, engine: 'ripgrep' },\n ];\n }\n\n const files = await fallbackGlob(target, input.pattern, maxResults, fs);\n return [\n files.length > 0 ? files.join('\\n') : 'No files found.',\n { files, engine: 'node-fallback' },\n ];\n },\n {\n name: LocalGlobSearchToolName,\n description:\n 'Find local files matching a glob pattern (ripgrep when available, Node fallback otherwise).',\n schema: LocalGlobSearchToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createLocalListDirectoryTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n const fs = getWorkspaceFS(config);\n return tool(\n async (rawInput) => {\n const input = rawInput as { path?: string };\n const path = await resolveWorkspacePathSafe(\n input.path ?? '.',\n config,\n 'read'\n );\n const entries = await fs.readdir(path, { withFileTypes: true });\n const output = entries\n .map(\n (entry) => `${entry.isDirectory() ? 'dir ' : 'file'}\\t${entry.name}`\n )\n .join('\\n');\n return [output || 'Directory is empty.', { path, count: entries.length }];\n },\n {\n name: LocalListDirectoryToolName,\n description: 'List files and directories in a local directory.',\n schema: LocalListDirectoryToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport type LocalCodingToolBundle = {\n tools: DynamicStructuredTool[];\n /**\n * Present when `config.fileCheckpointing === true` or a `checkpointer`\n * was passed in. Callers can call `rewind()` to restore captured\n * pre-write contents.\n */\n checkpointer?: t.LocalFileCheckpointer;\n};\n\nexport function createLocalCodingTools(\n config: t.LocalExecutionConfig = {},\n options: { checkpointer?: t.LocalFileCheckpointer } = {}\n): DynamicStructuredTool[] {\n const checkpointer =\n options.checkpointer ??\n (config.fileCheckpointing === true\n ? createLocalFileCheckpointer({ fs: config.exec?.fs })\n : undefined);\n return [\n createLocalReadFileTool(config),\n createLocalWriteFileTool(config, checkpointer),\n createLocalEditFileTool(config, checkpointer),\n createLocalGrepSearchTool(config),\n createLocalGlobSearchTool(config),\n createLocalListDirectoryTool(config),\n createCompileCheckTool(config),\n createLocalBashExecutionTool({ config }),\n createLocalCodeExecutionTool(config),\n createLocalProgrammaticToolCallingTool(config),\n createLocalBashProgrammaticToolCallingTool(config),\n ];\n}\n\n/**\n * Variant of `createLocalCodingTools` that returns the bundle alongside\n * the file checkpointer so callers can later call\n * `bundle.checkpointer?.rewind()`.\n */\nexport function createLocalCodingToolBundle(\n config: t.LocalExecutionConfig = {},\n options: { checkpointer?: t.LocalFileCheckpointer } = {}\n): LocalCodingToolBundle {\n const checkpointer =\n options.checkpointer ??\n (config.fileCheckpointing === true\n ? createLocalFileCheckpointer({ fs: config.exec?.fs })\n : undefined);\n return {\n tools: createLocalCodingTools(config, { checkpointer }),\n checkpointer,\n };\n}\n\nexport function createLocalCodingToolDefinitions(): t.LCTool[] {\n return [\n toolDefinition(\n Constants.READ_FILE,\n 'Read a local text file from the configured working directory with line numbers.',\n LocalReadFileToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalWriteFileToolName,\n 'Create or overwrite a local text file in the configured working directory.',\n LocalWriteFileToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalEditFileToolName,\n 'Apply exact text replacements to a local file.',\n LocalEditFileToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalGrepSearchToolName,\n 'Search local files with ripgrep and return matching lines.',\n LocalGrepSearchToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalGlobSearchToolName,\n 'Find local files matching a glob pattern.',\n LocalGlobSearchToolSchema as t.JsonSchemaType\n ),\n toolDefinition(\n LocalListDirectoryToolName,\n 'List files and directories in a local directory.',\n LocalListDirectoryToolSchema as t.JsonSchemaType\n ),\n createCompileCheckToolDefinition(),\n ];\n}\n\nexport function createLocalCodingToolRegistry(): t.LCToolRegistry {\n return new Map(\n createLocalCodingToolDefinitions().map((definition) => [\n definition.name,\n definition,\n ])\n );\n}\n"],"mappings":"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"}
|
|
@@ -105,6 +105,13 @@ export declare class CustomChatBedrockConverse extends ChatBedrockConverse {
|
|
|
105
105
|
* Service tier for model invocation.
|
|
106
106
|
*/
|
|
107
107
|
serviceTier?: ServiceTierType;
|
|
108
|
+
/**
|
|
109
|
+
* The configured model id, captured at construction so it survives the
|
|
110
|
+
* temporary `this.model` swap to an application-inference-profile ARN during
|
|
111
|
+
* generation. Used to gate the Bedrock tool cache point to Claude models
|
|
112
|
+
* (see {@link supportsBedrockToolCache}).
|
|
113
|
+
*/
|
|
114
|
+
private readonly cacheModelId;
|
|
108
115
|
constructor(fields?: CustomChatBedrockConverseInput);
|
|
109
116
|
static lc_name(): string;
|
|
110
117
|
/**
|