@librechat/agents 3.2.41 → 3.2.43

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.
Files changed (75) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +4 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +5 -2
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +1 -1
  6. package/dist/cjs/llm/bedrock/index.cjs +9 -1
  7. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  8. package/dist/cjs/main.cjs +6 -0
  9. package/dist/cjs/messages/cache.cjs +43 -0
  10. package/dist/cjs/messages/cache.cjs.map +1 -1
  11. package/dist/cjs/session/JsonlSessionStore.cjs +1 -1
  12. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +2 -2
  13. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
  14. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +118 -22
  15. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
  16. package/dist/cjs/tools/local/CompileCheckTool.cjs +11 -3
  17. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
  18. package/dist/cjs/tools/local/FileCheckpointer.cjs +8 -3
  19. package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -1
  20. package/dist/cjs/tools/local/LocalCodingTools.cjs +26 -7
  21. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  22. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +2 -2
  23. package/dist/cjs/tools/local/syntaxCheck.cjs +6 -2
  24. package/dist/cjs/tools/local/syntaxCheck.cjs.map +1 -1
  25. package/dist/cjs/tools/local/workspaceFS.cjs +20 -0
  26. package/dist/cjs/tools/local/workspaceFS.cjs.map +1 -1
  27. package/dist/esm/agents/AgentContext.mjs +5 -4
  28. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  29. package/dist/esm/graphs/Graph.mjs +6 -3
  30. package/dist/esm/graphs/Graph.mjs.map +1 -1
  31. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +1 -1
  32. package/dist/esm/llm/bedrock/index.mjs +10 -2
  33. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  34. package/dist/esm/main.mjs +3 -3
  35. package/dist/esm/messages/cache.mjs +42 -1
  36. package/dist/esm/messages/cache.mjs.map +1 -1
  37. package/dist/esm/session/JsonlSessionStore.mjs +1 -1
  38. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +3 -3
  39. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
  40. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +115 -23
  41. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
  42. package/dist/esm/tools/local/CompileCheckTool.mjs +11 -3
  43. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
  44. package/dist/esm/tools/local/FileCheckpointer.mjs +9 -4
  45. package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -1
  46. package/dist/esm/tools/local/LocalCodingTools.mjs +26 -7
  47. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  48. package/dist/esm/tools/local/LocalExecutionEngine.mjs +2 -2
  49. package/dist/esm/tools/local/syntaxCheck.mjs +6 -2
  50. package/dist/esm/tools/local/syntaxCheck.mjs.map +1 -1
  51. package/dist/esm/tools/local/workspaceFS.mjs +19 -1
  52. package/dist/esm/tools/local/workspaceFS.mjs.map +1 -1
  53. package/dist/types/agents/AgentContext.d.ts +3 -2
  54. package/dist/types/llm/bedrock/index.d.ts +7 -0
  55. package/dist/types/messages/cache.d.ts +27 -0
  56. package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +53 -0
  57. package/dist/types/tools/local/workspaceFS.d.ts +13 -0
  58. package/package.json +1 -1
  59. package/src/agents/AgentContext.ts +10 -3
  60. package/src/graphs/Graph.ts +24 -6
  61. package/src/llm/bedrock/index.ts +19 -3
  62. package/src/llm/bedrock/llm.spec.ts +97 -0
  63. package/src/messages/cache.test.ts +63 -0
  64. package/src/messages/cache.ts +51 -0
  65. package/src/specs/vllm-reasoning-toolcalls.test.ts +340 -0
  66. package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +323 -0
  67. package/src/tools/__tests__/LocalExecutionTools.test.ts +54 -0
  68. package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +7 -2
  69. package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +269 -37
  70. package/src/tools/local/CompileCheckTool.ts +19 -3
  71. package/src/tools/local/FileCheckpointer.ts +20 -4
  72. package/src/tools/local/LocalCodingTools.ts +61 -8
  73. package/src/tools/local/__tests__/FileCheckpointer.test.ts +42 -0
  74. package/src/tools/local/syntaxCheck.ts +14 -2
  75. package/src/tools/local/workspaceFS.ts +27 -0
@@ -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 {\n WorkspaceClientTimeoutError,\n isWorkspaceClientTimeoutError,\n} 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 * Client-side backstop timeout for a native-DO sandbox FILE-IO RPC\n * (`readFile`/`writeFile`/`listFiles`/`mkdir`/`deleteFile`). Unlike `exec()`\n * there is no in-sandbox `timeout(1)` layer for these to honor, so this is just a\n * few seconds of headroom over the configured tool timeout — enough that a normal\n * (even large, byte-capped) read completes, while a stalled/cold container can't\n * outlast it. See `withClientTimeout` for why the native DO RPC needs this.\n */\nexport function clientFsTimeoutMs(timeoutMs: number): number {\n return 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 WorkspaceClientTimeoutError(\n `${label} exceeded ${timeoutMs}ms client-side timeout (sandbox RPC 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 timeoutMs: number\n): Promise<t.CloudflareSandboxFileInfo | undefined> {\n const parent = path.dirname(filePath);\n const basename = path.basename(filePath);\n const entries = normalizeFileList(\n await withClientTimeout(\n sandbox.listFiles(parent, { includeHidden: true }),\n timeoutMs,\n 'cloudflare sandbox listFiles'\n )\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 // Native-DO file-IO RPCs have the SAME stall hazard as exec() (PR #252): no\n // `signal`, no reliably-enforced timeout, so a cold/unresponsive container\n // hangs the host await until the run-level abort — burning the whole budget on\n // one read (observed: a `read_file` that stalled ~552s before the wall-clock\n // budget killed it). Bound every native FS RPC with the same client-side\n // backstop the exec sites use.\n const fsTimeoutMs = clientFsTimeoutMs(config.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n const bound = <T>(op: Promise<T>, label: string): Promise<T> =>\n withClientTimeout(op, fsTimeoutMs, `cloudflare sandbox ${label}`);\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 // Wrap the stream drain (normalizeReadFileContent) inside the backstop too:\n // a sandbox.readFile that resolves to a { content: ReadableStream } can\n // still stall mid-drain after the RPC promise settled.\n const buffer = await bound(\n (async (): Promise<Buffer> =>\n normalizeReadFileContent(\n await sandbox.readFile(resolved, encoding ? { encoding } : undefined)\n ))(),\n 'readFile'\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 bound(\n sandbox.writeFile(resolved, normalized.content, normalized.options),\n 'writeFile'\n );\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 bound(\n sandbox.listFiles(resolved, { includeHidden: true }),\n 'listFiles'\n )\n );\n return createStats({ size: entries.length, type: 'directory' });\n }\n const info = await findChildInfo(sandbox, resolved, fsTimeoutMs);\n if (info != null) {\n return createStats({ size: info.size, type: info.type });\n }\n try {\n const entries = normalizeFileList(\n await bound(\n sandbox.listFiles(resolved, { includeHidden: true }),\n 'listFiles'\n )\n );\n return createStats({ size: entries.length, type: 'directory' });\n } catch (error) {\n // A directory-probe timeout is a stalled container, not \"not a directory\".\n // Don't fall through to the readFile branch — that would wait through a\n // SECOND full backstop (~2x the timeout) before surfacing.\n if (isWorkspaceClientTimeoutError(error)) {\n throw error;\n }\n const buffer = await bound(\n (async (): Promise<Buffer> =>\n normalizeReadFileContent(await sandbox.readFile(resolved)))(),\n 'readFile'\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 bound(\n sandbox.listFiles(resolved, { includeHidden: true }),\n 'listFiles'\n )\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 bound(\n sandbox.mkdir(toSandboxPath(filePath, workspaceRoot), {\n recursive: options?.recursive,\n }),\n 'mkdir'\n );\n },\n realpath: async (filePath: string) =>\n toSandboxPath(filePath, workspaceRoot),\n unlink: async (filePath: string) => {\n const sandbox = await resolveCloudflareSandbox(config);\n await bound(\n sandbox.deleteFile(toSandboxPath(filePath, workspaceRoot)),\n 'deleteFile'\n );\n },\n open: async (filePath: string, _flags: 'r') => {\n const sandbox = await resolveCloudflareSandbox(config);\n const resolved = toSandboxPath(filePath, workspaceRoot);\n const buffer = await bound(\n (async (): Promise<Buffer> =>\n normalizeReadFileContent(await sandbox.readFile(resolved)))(),\n 'readFile'\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 let execSucceeded = false;\n try {\n // Bound the temp-dir setup RPCs (they run BEFORE the bounded exec): a\n // native-DO stall here would hang the host on a single mkdir/writeFile and\n // burn the run budget. Keep them INSIDE the try so the finally cleanup still\n // removes .lc-exec/<uuid> if setup throws — the uncancellable write can land\n // late on a cold container, so an orphaned dir would otherwise accumulate.\n await withClientTimeout(\n ctx.sandbox.mkdir(tempDir, { recursive: true }),\n clientFsTimeoutMs(ctx.timeoutMs),\n 'cloudflare sandbox mkdir'\n );\n if (runtime.source != null) {\n await withClientTimeout(\n ctx.sandbox.writeFile(\n path.join(tempDir, runtime.fileName),\n runtime.source,\n { encoding: 'utf8' }\n ),\n clientFsTimeoutMs(ctx.timeoutMs),\n 'cloudflare sandbox writeFile'\n );\n }\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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"}
@@ -1,5 +1,6 @@
1
1
  import "../../common/enum.mjs";
2
2
  import "../../common/index.mjs";
3
+ import { isWorkspaceClientTimeoutError } from "./workspaceFS.mjs";
3
4
  import { getLocalCwd, getWorkspaceFS, spawnLocalProcess, truncateLocalOutput, validateBashCommand } from "./LocalExecutionEngine.mjs";
4
5
  import { tool } from "@langchain/core/tools";
5
6
  import { resolve } from "path";
@@ -47,7 +48,8 @@ async function pathExists(fs, p) {
47
48
  try {
48
49
  await fs.stat(p);
49
50
  return true;
50
- } catch {
51
+ } catch (error) {
52
+ if (isWorkspaceClientTimeoutError(error)) throw error;
51
53
  return false;
52
54
  }
53
55
  }
@@ -58,7 +60,10 @@ async function detect(cwd, fs) {
58
60
  reason: "tsconfig.json present"
59
61
  };
60
62
  if (await pathExists(fs, resolve(cwd, "package.json"))) {
61
- if ((await fs.readFile(resolve(cwd, "package.json"), "utf8").catch(() => "")).includes("\"typescript\"")) return {
63
+ if ((await fs.readFile(resolve(cwd, "package.json"), "utf8").catch((error) => {
64
+ if (isWorkspaceClientTimeoutError(error)) throw error;
65
+ return "";
66
+ })).includes("\"typescript\"")) return {
62
67
  kind: "typescript",
63
68
  command: "npx --no-install tsc --noEmit",
64
69
  reason: "package.json declares typescript"
@@ -75,7 +80,10 @@ async function detect(cwd, fs) {
75
80
  reason: "go.mod present"
76
81
  };
77
82
  if (await pathExists(fs, resolve(cwd, "pyproject.toml")) || await pathExists(fs, resolve(cwd, "setup.py")) || await pathExists(fs, resolve(cwd, "setup.cfg"))) {
78
- if ((await fs.readFile(resolve(cwd, "pyproject.toml"), "utf8").catch(() => "")).includes("mypy")) return {
83
+ if ((await fs.readFile(resolve(cwd, "pyproject.toml"), "utf8").catch((error) => {
84
+ if (isWorkspaceClientTimeoutError(error)) throw error;
85
+ return "";
86
+ })).includes("mypy")) return {
79
87
  kind: "python-mypy",
80
88
  command: "python3 -m mypy .",
81
89
  reason: "pyproject.toml declares mypy"
@@ -1 +1 @@
1
- {"version":3,"file":"CompileCheckTool.mjs","names":[],"sources":["../../../../src/tools/local/CompileCheckTool.ts"],"sourcesContent":["/**\n * `compile_check` — a thin LLM-callable wrapper around the project's\n * standard typecheck/lint command. Lets the agent answer \"did my\n * change break anything?\" without us shipping a real LSP client.\n *\n * Auto-detection priority (first hit wins):\n *\n * 1. `local.compileCheck.command` — explicit override\n * 2. `tsconfig.json` → `npx --no-install tsc --noEmit`\n * 3. `package.json` with a typescript dep → same as 2\n * 4. `pyproject.toml` or `setup.py` / `setup.cfg`\n * with a dev dep on mypy → `python3 -m mypy .`\n * else → `python3 -m py_compile <every .py>`\n * (bounded by find-walk so node_modules\n * and `.venv` don't blow up)\n * 5. `Cargo.toml` → `cargo check --message-format=short`\n * 6. `go.mod` → `go vet ./...`\n * 7. otherwise → tells the agent there's\n * no detected toolchain.\n *\n * Output is the spawn process's stdout/stderr passed through\n * `truncateLocalOutput` so a 10MB tsc dump can't blow context. The\n * exit code is reported.\n */\n\nimport { resolve } from 'path';\nimport { tool } from '@langchain/core/tools';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type { WorkspaceFS } from './workspaceFS';\nimport type * as t from '@/types';\nimport {\n getLocalCwd,\n getWorkspaceFS,\n spawnLocalProcess,\n truncateLocalOutput,\n validateBashCommand,\n} from './LocalExecutionEngine';\nimport { Constants } from '@/common';\n\n/** Back-compat alias; canonical name lives on `Constants.COMPILE_CHECK`. */\nexport const CompileCheckToolName = Constants.COMPILE_CHECK;\n\nconst CompileCheckSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description:\n 'Optional explicit command to run instead of the auto-detected one. Runs verbatim from the local engine cwd; honours the standard sandbox/AST gate.',\n },\n timeout_ms: {\n type: 'integer',\n description:\n 'Optional timeout in milliseconds. Defaults to 120000 (2 min).',\n },\n },\n};\n\ntype DetectedKind =\n | 'typescript'\n | 'python-mypy'\n | 'python-compile'\n | 'rust'\n | 'go'\n | 'unknown';\n\ntype Detection = {\n kind: DetectedKind;\n command: string;\n reason: string;\n};\n\nasync function pathExists(fs: WorkspaceFS, p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\n// Probes for project markers via the configured WorkspaceFS so a Run\n// with `local.exec.fs` (in-memory or remote engine) detects the right\n// toolchain against the actual workspace — not the host filesystem.\n// Codex P1 #25.\nasync function detect(cwd: string, fs: WorkspaceFS): Promise<Detection> {\n if (await pathExists(fs, resolve(cwd, 'tsconfig.json'))) {\n return {\n kind: 'typescript',\n command: 'npx --no-install tsc --noEmit',\n reason: 'tsconfig.json present',\n };\n }\n if (await pathExists(fs, resolve(cwd, 'package.json'))) {\n const pkgRaw = await fs\n .readFile(resolve(cwd, 'package.json'), 'utf8')\n .catch(() => '');\n if (pkgRaw.includes('\"typescript\"')) {\n return {\n kind: 'typescript',\n command: 'npx --no-install tsc --noEmit',\n reason: 'package.json declares typescript',\n };\n }\n }\n if (await pathExists(fs, resolve(cwd, 'Cargo.toml'))) {\n return {\n kind: 'rust',\n command: 'cargo check --message-format=short',\n reason: 'Cargo.toml present',\n };\n }\n if (await pathExists(fs, resolve(cwd, 'go.mod'))) {\n return {\n kind: 'go',\n command: 'go vet ./...',\n reason: 'go.mod present',\n };\n }\n if (\n (await pathExists(fs, resolve(cwd, 'pyproject.toml'))) ||\n (await pathExists(fs, resolve(cwd, 'setup.py'))) ||\n (await pathExists(fs, resolve(cwd, 'setup.cfg')))\n ) {\n const pyToml = await fs\n .readFile(resolve(cwd, 'pyproject.toml'), 'utf8')\n .catch(() => '');\n if (pyToml.includes('mypy')) {\n return {\n kind: 'python-mypy',\n command: 'python3 -m mypy .',\n reason: 'pyproject.toml declares mypy',\n };\n }\n return {\n kind: 'python-compile',\n command:\n 'python3 -c \"import compileall, sys; sys.exit(0 if compileall.compile_dir(\\'.\\', quiet=1, rx=__import__(\\'re\\').compile(r\\'(node_modules|\\\\.venv|\\\\.git|build|dist)\\')) else 1)\"',\n reason: 'Python project (no mypy detected)',\n };\n }\n return {\n kind: 'unknown',\n command: '',\n reason:\n 'no recognised project marker (tsconfig.json, package.json[typescript], Cargo.toml, go.mod, pyproject.toml, setup.py)',\n };\n}\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\nexport function createCompileCheckTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n command?: string;\n timeout_ms?: number;\n };\n const cwd = getLocalCwd(config);\n const fs = getWorkspaceFS(config);\n const overrideCommand = input.command ?? config.compileCheck?.command;\n let detection: Detection;\n if (overrideCommand != null && overrideCommand.trim() !== '') {\n detection = {\n kind: 'unknown',\n command: overrideCommand,\n reason: 'explicit override',\n };\n } else {\n detection = await detect(cwd, fs);\n }\n\n if (detection.command === '') {\n const explainer = `compile_check: ${detection.reason}. Pass an explicit \\`command\\` (e.g. \\`npm run typecheck\\`) to override.`;\n return [\n explainer,\n {\n kind: detection.kind,\n ran: false,\n reason: detection.reason,\n cwd,\n },\n ];\n }\n\n // Codex P1 #21: route the resolved command through the same\n // safety gates the rest of the local engine uses. Without this\n // a host with `readOnly: true` (or relying on the destructive-\n // command guard) could be bypassed by passing a `command`\n // override to compile_check that performs writes/deletes.\n // Auto-detected commands (tsc/cargo/etc.) pass these gates\n // unchanged — the validation is only blocking for genuinely\n // mutating overrides.\n const validation = await validateBashCommand(detection.command, config);\n if (!validation.valid) {\n const explainer = `compile_check refused to run \\`${detection.command}\\`: ${validation.errors.join('; ')}`;\n return [\n explainer,\n {\n kind: detection.kind,\n ran: false,\n reason: validation.errors.join('; '),\n cwd,\n },\n ];\n }\n\n const timeoutMs =\n input.timeout_ms ??\n config.compileCheck?.timeoutMs ??\n DEFAULT_TIMEOUT_MS;\n const result = await spawnLocalProcess(\n config.shell ?? (process.platform === 'win32' ? 'bash.exe' : 'bash'),\n ['-lc', detection.command],\n {\n ...config,\n timeoutMs,\n maxOutputChars: config.maxOutputChars ?? 8000,\n }\n );\n\n const passed = result.exitCode === 0 && !result.timedOut;\n const headline = passed\n ? `compile_check (${detection.kind}) PASSED via \\`${detection.command}\\``\n : `compile_check (${detection.kind}) FAILED via \\`${detection.command}\\` ` +\n `(exit=${result.exitCode ?? 'unknown'}${result.timedOut ? ', timed_out=true' : ''})`;\n\n let body = '';\n if (result.stdout !== '') {\n body += `\\n\\nstdout:\\n${truncateLocalOutput(result.stdout, 4000)}`;\n }\n if (result.stderr !== '') {\n body += `\\n\\nstderr:\\n${truncateLocalOutput(result.stderr, 4000)}`;\n }\n if (result.fullOutputPath != null) {\n body += `\\n\\nfull_output_path: ${result.fullOutputPath}`;\n }\n const summary = `${headline}${body}\\n\\nworking_directory: ${cwd}\\nreason: ${detection.reason}`;\n\n return [\n summary,\n {\n kind: detection.kind,\n ran: true,\n passed,\n exit_code: result.exitCode,\n timed_out: result.timedOut,\n command: detection.command,\n cwd,\n },\n ];\n },\n {\n name: CompileCheckToolName,\n description:\n 'Run the project\\'s standard typecheck or lint pass and return its output. Auto-detects from project markers (tsconfig.json/package.json -> tsc; Cargo.toml -> cargo check; go.mod -> go vet; pyproject.toml -> mypy or py_compile). Pass `command` to override.',\n schema: CompileCheckSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createCompileCheckToolDefinition(): t.LCTool {\n return {\n name: CompileCheckToolName,\n description:\n 'Run the project\\'s standard typecheck or lint pass and return its output.',\n parameters: CompileCheckSchema,\n allowed_callers: ['direct', 'code_execution'],\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n toolType: 'builtin',\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CompileCheckTool.mjs","names":[],"sources":["../../../../src/tools/local/CompileCheckTool.ts"],"sourcesContent":["/**\n * `compile_check` — a thin LLM-callable wrapper around the project's\n * standard typecheck/lint command. Lets the agent answer \"did my\n * change break anything?\" without us shipping a real LSP client.\n *\n * Auto-detection priority (first hit wins):\n *\n * 1. `local.compileCheck.command` — explicit override\n * 2. `tsconfig.json` → `npx --no-install tsc --noEmit`\n * 3. `package.json` with a typescript dep → same as 2\n * 4. `pyproject.toml` or `setup.py` / `setup.cfg`\n * with a dev dep on mypy → `python3 -m mypy .`\n * else → `python3 -m py_compile <every .py>`\n * (bounded by find-walk so node_modules\n * and `.venv` don't blow up)\n * 5. `Cargo.toml` → `cargo check --message-format=short`\n * 6. `go.mod` → `go vet ./...`\n * 7. otherwise → tells the agent there's\n * no detected toolchain.\n *\n * Output is the spawn process's stdout/stderr passed through\n * `truncateLocalOutput` so a 10MB tsc dump can't blow context. The\n * exit code is reported.\n */\n\nimport { resolve } from 'path';\nimport { tool } from '@langchain/core/tools';\nimport type { DynamicStructuredTool } from '@langchain/core/tools';\nimport type { WorkspaceFS } from './workspaceFS';\nimport { isWorkspaceClientTimeoutError } from './workspaceFS';\nimport type * as t from '@/types';\nimport {\n getLocalCwd,\n getWorkspaceFS,\n spawnLocalProcess,\n truncateLocalOutput,\n validateBashCommand,\n} from './LocalExecutionEngine';\nimport { Constants } from '@/common';\n\n/** Back-compat alias; canonical name lives on `Constants.COMPILE_CHECK`. */\nexport const CompileCheckToolName = Constants.COMPILE_CHECK;\n\nconst CompileCheckSchema: t.JsonSchemaType = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description:\n 'Optional explicit command to run instead of the auto-detected one. Runs verbatim from the local engine cwd; honours the standard sandbox/AST gate.',\n },\n timeout_ms: {\n type: 'integer',\n description:\n 'Optional timeout in milliseconds. Defaults to 120000 (2 min).',\n },\n },\n};\n\ntype DetectedKind =\n | 'typescript'\n | 'python-mypy'\n | 'python-compile'\n | 'rust'\n | 'go'\n | 'unknown';\n\ntype Detection = {\n kind: DetectedKind;\n command: string;\n reason: string;\n};\n\nasync function pathExists(fs: WorkspaceFS, p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch (error) {\n // A stalled-RPC timeout is not \"absent\" — don't let it pick a weaker/wrong\n // toolchain after waiting through the backstop; surface it.\n if (isWorkspaceClientTimeoutError(error)) {\n throw error;\n }\n return false;\n }\n}\n\n// Probes for project markers via the configured WorkspaceFS so a Run\n// with `local.exec.fs` (in-memory or remote engine) detects the right\n// toolchain against the actual workspace — not the host filesystem.\n// Codex P1 #25.\nasync function detect(cwd: string, fs: WorkspaceFS): Promise<Detection> {\n if (await pathExists(fs, resolve(cwd, 'tsconfig.json'))) {\n return {\n kind: 'typescript',\n command: 'npx --no-install tsc --noEmit',\n reason: 'tsconfig.json present',\n };\n }\n if (await pathExists(fs, resolve(cwd, 'package.json'))) {\n const pkgRaw = await fs\n .readFile(resolve(cwd, 'package.json'), 'utf8')\n .catch((error) => {\n if (isWorkspaceClientTimeoutError(error)) {\n throw error;\n }\n return '';\n });\n if (pkgRaw.includes('\"typescript\"')) {\n return {\n kind: 'typescript',\n command: 'npx --no-install tsc --noEmit',\n reason: 'package.json declares typescript',\n };\n }\n }\n if (await pathExists(fs, resolve(cwd, 'Cargo.toml'))) {\n return {\n kind: 'rust',\n command: 'cargo check --message-format=short',\n reason: 'Cargo.toml present',\n };\n }\n if (await pathExists(fs, resolve(cwd, 'go.mod'))) {\n return {\n kind: 'go',\n command: 'go vet ./...',\n reason: 'go.mod present',\n };\n }\n if (\n (await pathExists(fs, resolve(cwd, 'pyproject.toml'))) ||\n (await pathExists(fs, resolve(cwd, 'setup.py'))) ||\n (await pathExists(fs, resolve(cwd, 'setup.cfg')))\n ) {\n const pyToml = await fs\n .readFile(resolve(cwd, 'pyproject.toml'), 'utf8')\n .catch((error) => {\n if (isWorkspaceClientTimeoutError(error)) {\n throw error;\n }\n return '';\n });\n if (pyToml.includes('mypy')) {\n return {\n kind: 'python-mypy',\n command: 'python3 -m mypy .',\n reason: 'pyproject.toml declares mypy',\n };\n }\n return {\n kind: 'python-compile',\n command:\n 'python3 -c \"import compileall, sys; sys.exit(0 if compileall.compile_dir(\\'.\\', quiet=1, rx=__import__(\\'re\\').compile(r\\'(node_modules|\\\\.venv|\\\\.git|build|dist)\\')) else 1)\"',\n reason: 'Python project (no mypy detected)',\n };\n }\n return {\n kind: 'unknown',\n command: '',\n reason:\n 'no recognised project marker (tsconfig.json, package.json[typescript], Cargo.toml, go.mod, pyproject.toml, setup.py)',\n };\n}\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\nexport function createCompileCheckTool(\n config: t.LocalExecutionConfig = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput) => {\n const input = rawInput as {\n command?: string;\n timeout_ms?: number;\n };\n const cwd = getLocalCwd(config);\n const fs = getWorkspaceFS(config);\n const overrideCommand = input.command ?? config.compileCheck?.command;\n let detection: Detection;\n if (overrideCommand != null && overrideCommand.trim() !== '') {\n detection = {\n kind: 'unknown',\n command: overrideCommand,\n reason: 'explicit override',\n };\n } else {\n detection = await detect(cwd, fs);\n }\n\n if (detection.command === '') {\n const explainer = `compile_check: ${detection.reason}. Pass an explicit \\`command\\` (e.g. \\`npm run typecheck\\`) to override.`;\n return [\n explainer,\n {\n kind: detection.kind,\n ran: false,\n reason: detection.reason,\n cwd,\n },\n ];\n }\n\n // Codex P1 #21: route the resolved command through the same\n // safety gates the rest of the local engine uses. Without this\n // a host with `readOnly: true` (or relying on the destructive-\n // command guard) could be bypassed by passing a `command`\n // override to compile_check that performs writes/deletes.\n // Auto-detected commands (tsc/cargo/etc.) pass these gates\n // unchanged — the validation is only blocking for genuinely\n // mutating overrides.\n const validation = await validateBashCommand(detection.command, config);\n if (!validation.valid) {\n const explainer = `compile_check refused to run \\`${detection.command}\\`: ${validation.errors.join('; ')}`;\n return [\n explainer,\n {\n kind: detection.kind,\n ran: false,\n reason: validation.errors.join('; '),\n cwd,\n },\n ];\n }\n\n const timeoutMs =\n input.timeout_ms ??\n config.compileCheck?.timeoutMs ??\n DEFAULT_TIMEOUT_MS;\n const result = await spawnLocalProcess(\n config.shell ?? (process.platform === 'win32' ? 'bash.exe' : 'bash'),\n ['-lc', detection.command],\n {\n ...config,\n timeoutMs,\n maxOutputChars: config.maxOutputChars ?? 8000,\n }\n );\n\n const passed = result.exitCode === 0 && !result.timedOut;\n const headline = passed\n ? `compile_check (${detection.kind}) PASSED via \\`${detection.command}\\``\n : `compile_check (${detection.kind}) FAILED via \\`${detection.command}\\` ` +\n `(exit=${result.exitCode ?? 'unknown'}${result.timedOut ? ', timed_out=true' : ''})`;\n\n let body = '';\n if (result.stdout !== '') {\n body += `\\n\\nstdout:\\n${truncateLocalOutput(result.stdout, 4000)}`;\n }\n if (result.stderr !== '') {\n body += `\\n\\nstderr:\\n${truncateLocalOutput(result.stderr, 4000)}`;\n }\n if (result.fullOutputPath != null) {\n body += `\\n\\nfull_output_path: ${result.fullOutputPath}`;\n }\n const summary = `${headline}${body}\\n\\nworking_directory: ${cwd}\\nreason: ${detection.reason}`;\n\n return [\n summary,\n {\n kind: detection.kind,\n ran: true,\n passed,\n exit_code: result.exitCode,\n timed_out: result.timedOut,\n command: detection.command,\n cwd,\n },\n ];\n },\n {\n name: CompileCheckToolName,\n description:\n 'Run the project\\'s standard typecheck or lint pass and return its output. Auto-detects from project markers (tsconfig.json/package.json -> tsc; Cargo.toml -> cargo check; go.mod -> go vet; pyproject.toml -> mypy or py_compile). Pass `command` to override.',\n schema: CompileCheckSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport function createCompileCheckToolDefinition(): t.LCTool {\n return {\n name: CompileCheckToolName,\n description:\n 'Run the project\\'s standard typecheck or lint pass and return its output.',\n parameters: CompileCheckSchema,\n allowed_callers: ['direct', 'code_execution'],\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n toolType: 'builtin',\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { nodeWorkspaceFS } from "./workspaceFS.mjs";
1
+ import { isWorkspaceClientTimeoutError, nodeWorkspaceFS } from "./workspaceFS.mjs";
2
2
  import { dirname } from "path";
3
3
  //#region src/tools/local/FileCheckpointer.ts
4
4
  /**
@@ -32,7 +32,8 @@ var LocalFileCheckpointerImpl = class {
32
32
  let info;
33
33
  try {
34
34
  info = await this.fs.stat(absolutePath);
35
- } catch {
35
+ } catch (error) {
36
+ if (isWorkspaceClientTimeoutError(error)) throw error;
36
37
  this.snapshots.set(absolutePath, { kind: "absent" });
37
38
  return;
38
39
  }
@@ -51,7 +52,9 @@ var LocalFileCheckpointerImpl = class {
51
52
  let restored = 0;
52
53
  for (const [path, snapshot] of this.snapshots.entries()) {
53
54
  if (snapshot.kind === "absent") {
54
- await this.fs.unlink(path).catch(() => void 0);
55
+ await this.fs.unlink(path).catch((error) => {
56
+ if (isWorkspaceClientTimeoutError(error)) throw error;
57
+ });
55
58
  restored++;
56
59
  continue;
57
60
  }
@@ -59,7 +62,9 @@ var LocalFileCheckpointerImpl = class {
59
62
  await this.fs.mkdir(dirname(path), { recursive: true });
60
63
  await this.fs.writeFile(path, snapshot.content);
61
64
  restored++;
62
- } catch {}
65
+ } catch (error) {
66
+ if (isWorkspaceClientTimeoutError(error)) throw error;
67
+ }
63
68
  }
64
69
  return restored;
65
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FileCheckpointer.mjs","names":[],"sources":["../../../../src/tools/local/FileCheckpointer.ts"],"sourcesContent":["import { dirname } from 'path';\nimport type { WorkspaceFS } from './workspaceFS';\nimport type * as t from '@/types';\nimport { nodeWorkspaceFS } from './workspaceFS';\n\ntype Snapshot = { kind: 'absent' } | { kind: 'present'; content: Buffer };\n\n/**\n * Per-Run snapshot store for write_file / edit_file. Captures the\n * pre-write byte content of every path the local engine is about to\n * mutate so a later `rewind()` can restore the working tree to its\n * original state. Notes:\n *\n * - Idempotent per path: subsequent captures preserve the first\n * snapshot (so rewind always restores the *original* content).\n * - Captures missing files as `{ kind: 'absent' }`; rewind deletes\n * those paths so created files are removed.\n * - In-memory: snapshots live for the lifetime of this instance and\n * are not persisted across processes. Tie the lifetime to a Run.\n * - Bounded by `maxBytesPerFile` (default 32 MiB) to bound memory.\n * A file larger than the cap is recorded but not snapshotted; the\n * rewind of that path is best-effort and the caller is told via\n * the result count not to trust it.\n */\nexport class LocalFileCheckpointerImpl implements t.LocalFileCheckpointer {\n private snapshots = new Map<string, Snapshot>();\n private oversizePaths = new Set<string>();\n\n constructor(\n private readonly maxBytesPerFile: number = 32 * 1024 * 1024,\n private readonly fs: WorkspaceFS = nodeWorkspaceFS\n ) {}\n\n async captureBeforeWrite(absolutePath: string): Promise<void> {\n if (\n this.snapshots.has(absolutePath) ||\n this.oversizePaths.has(absolutePath)\n ) {\n return;\n }\n let info;\n try {\n info = await this.fs.stat(absolutePath);\n } catch {\n this.snapshots.set(absolutePath, { kind: 'absent' });\n return;\n }\n if (!info.isFile()) {\n return;\n }\n if (info.size > this.maxBytesPerFile) {\n this.oversizePaths.add(absolutePath);\n return;\n }\n const content = (await this.fs.readFile(absolutePath)) as Buffer;\n this.snapshots.set(absolutePath, { kind: 'present', content });\n }\n\n async rewind(): Promise<number> {\n let restored = 0;\n for (const [path, snapshot] of this.snapshots.entries()) {\n if (snapshot.kind === 'absent') {\n await this.fs.unlink(path).catch(() => undefined);\n restored++;\n continue;\n }\n try {\n await this.fs.mkdir(dirname(path), { recursive: true });\n await this.fs.writeFile(path, snapshot.content);\n restored++;\n } catch {\n // Best-effort: ignore individual restore failures so the rest\n // of the rewind continues.\n }\n }\n return restored;\n }\n\n capturedPaths(): string[] {\n return [...this.snapshots.keys(), ...this.oversizePaths];\n }\n}\n\n/**\n * Convenience factory so callers don't have to reach for the impl\n * class directly. Accepts an optional `WorkspaceFS` so a host using a\n * non-default engine (remote sandbox, in-memory test FS, etc.) can\n * route the checkpointer through the same I/O.\n */\nexport function createLocalFileCheckpointer(\n options: { maxBytesPerFile?: number; fs?: WorkspaceFS } = {}\n): t.LocalFileCheckpointer {\n return new LocalFileCheckpointerImpl(options.maxBytesPerFile, options.fs);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,4BAAb,MAA0E;CAKrD;CACA;CALnB,4BAAoB,IAAI,IAAsB;CAC9C,gCAAwB,IAAI,IAAY;CAExC,YACE,kBAA2C,KAAK,OAAO,MACvD,KAAmC,iBACnC;EAFiB,KAAA,kBAAA;EACA,KAAA,KAAA;CAChB;CAEH,MAAM,mBAAmB,cAAqC;EAC5D,IACE,KAAK,UAAU,IAAI,YAAY,KAC/B,KAAK,cAAc,IAAI,YAAY,GAEnC;EAEF,IAAI;EACJ,IAAI;GACF,OAAO,MAAM,KAAK,GAAG,KAAK,YAAY;EACxC,QAAQ;GACN,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,SAAS,CAAC;GACnD;EACF;EACA,IAAI,CAAC,KAAK,OAAO,GACf;EAEF,IAAI,KAAK,OAAO,KAAK,iBAAiB;GACpC,KAAK,cAAc,IAAI,YAAY;GACnC;EACF;EACA,MAAM,UAAW,MAAM,KAAK,GAAG,SAAS,YAAY;EACpD,KAAK,UAAU,IAAI,cAAc;GAAE,MAAM;GAAW;EAAQ,CAAC;CAC/D;CAEA,MAAM,SAA0B;EAC9B,IAAI,WAAW;EACf,KAAK,MAAM,CAAC,MAAM,aAAa,KAAK,UAAU,QAAQ,GAAG;GACvD,IAAI,SAAS,SAAS,UAAU;IAC9B,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,CAAC,YAAY,KAAA,CAAS;IAChD;IACA;GACF;GACA,IAAI;IACF,MAAM,KAAK,GAAG,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;IACtD,MAAM,KAAK,GAAG,UAAU,MAAM,SAAS,OAAO;IAC9C;GACF,QAAQ,CAGR;EACF;EACA,OAAO;CACT;CAEA,gBAA0B;EACxB,OAAO,CAAC,GAAG,KAAK,UAAU,KAAK,GAAG,GAAG,KAAK,aAAa;CACzD;AACF;;;;;;;AAQA,SAAgB,4BACd,UAA0D,CAAC,GAClC;CACzB,OAAO,IAAI,0BAA0B,QAAQ,iBAAiB,QAAQ,EAAE;AAC1E"}
1
+ {"version":3,"file":"FileCheckpointer.mjs","names":[],"sources":["../../../../src/tools/local/FileCheckpointer.ts"],"sourcesContent":["import { dirname } from 'path';\nimport type { WorkspaceFS } from './workspaceFS';\nimport type * as t from '@/types';\nimport { isWorkspaceClientTimeoutError, nodeWorkspaceFS } from './workspaceFS';\n\ntype Snapshot = { kind: 'absent' } | { kind: 'present'; content: Buffer };\n\n/**\n * Per-Run snapshot store for write_file / edit_file. Captures the\n * pre-write byte content of every path the local engine is about to\n * mutate so a later `rewind()` can restore the working tree to its\n * original state. Notes:\n *\n * - Idempotent per path: subsequent captures preserve the first\n * snapshot (so rewind always restores the *original* content).\n * - Captures missing files as `{ kind: 'absent' }`; rewind deletes\n * those paths so created files are removed.\n * - In-memory: snapshots live for the lifetime of this instance and\n * are not persisted across processes. Tie the lifetime to a Run.\n * - Bounded by `maxBytesPerFile` (default 32 MiB) to bound memory.\n * A file larger than the cap is recorded but not snapshotted; the\n * rewind of that path is best-effort and the caller is told via\n * the result count not to trust it.\n */\nexport class LocalFileCheckpointerImpl implements t.LocalFileCheckpointer {\n private snapshots = new Map<string, Snapshot>();\n private oversizePaths = new Set<string>();\n\n constructor(\n private readonly maxBytesPerFile: number = 32 * 1024 * 1024,\n private readonly fs: WorkspaceFS = nodeWorkspaceFS\n ) {}\n\n async captureBeforeWrite(absolutePath: string): Promise<void> {\n if (\n this.snapshots.has(absolutePath) ||\n this.oversizePaths.has(absolutePath)\n ) {\n return;\n }\n let info;\n try {\n info = await this.fs.stat(absolutePath);\n } catch (error) {\n // A stalled-RPC timeout is NOT \"file absent\" — snapshotting it as absent\n // would delete an existing file on revert. Surface it instead.\n if (isWorkspaceClientTimeoutError(error)) {\n throw error;\n }\n this.snapshots.set(absolutePath, { kind: 'absent' });\n return;\n }\n if (!info.isFile()) {\n return;\n }\n if (info.size > this.maxBytesPerFile) {\n this.oversizePaths.add(absolutePath);\n return;\n }\n const content = (await this.fs.readFile(absolutePath)) as Buffer;\n this.snapshots.set(absolutePath, { kind: 'present', content });\n }\n\n async rewind(): Promise<number> {\n let restored = 0;\n for (const [path, snapshot] of this.snapshots.entries()) {\n if (snapshot.kind === 'absent') {\n await this.fs.unlink(path).catch((error) => {\n // A timed-out delete did NOT happen — surface it rather than counting\n // the path as restored (which would falsely claim the workspace is\n // back to its pre-write state).\n if (isWorkspaceClientTimeoutError(error)) {\n throw error;\n }\n });\n restored++;\n continue;\n }\n try {\n await this.fs.mkdir(dirname(path), { recursive: true });\n await this.fs.writeFile(path, snapshot.content);\n restored++;\n } catch (error) {\n // A timed-out restore left the bad write in place — surface it.\n if (isWorkspaceClientTimeoutError(error)) {\n throw error;\n }\n // Best-effort: ignore individual restore failures so the rest\n // of the rewind continues.\n }\n }\n return restored;\n }\n\n capturedPaths(): string[] {\n return [...this.snapshots.keys(), ...this.oversizePaths];\n }\n}\n\n/**\n * Convenience factory so callers don't have to reach for the impl\n * class directly. Accepts an optional `WorkspaceFS` so a host using a\n * non-default engine (remote sandbox, in-memory test FS, etc.) can\n * route the checkpointer through the same I/O.\n */\nexport function createLocalFileCheckpointer(\n options: { maxBytesPerFile?: number; fs?: WorkspaceFS } = {}\n): t.LocalFileCheckpointer {\n return new LocalFileCheckpointerImpl(options.maxBytesPerFile, options.fs);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,4BAAb,MAA0E;CAKrD;CACA;CALnB,4BAAoB,IAAI,IAAsB;CAC9C,gCAAwB,IAAI,IAAY;CAExC,YACE,kBAA2C,KAAK,OAAO,MACvD,KAAmC,iBACnC;EAFiB,KAAA,kBAAA;EACA,KAAA,KAAA;CAChB;CAEH,MAAM,mBAAmB,cAAqC;EAC5D,IACE,KAAK,UAAU,IAAI,YAAY,KAC/B,KAAK,cAAc,IAAI,YAAY,GAEnC;EAEF,IAAI;EACJ,IAAI;GACF,OAAO,MAAM,KAAK,GAAG,KAAK,YAAY;EACxC,SAAS,OAAO;GAGd,IAAI,8BAA8B,KAAK,GACrC,MAAM;GAER,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,SAAS,CAAC;GACnD;EACF;EACA,IAAI,CAAC,KAAK,OAAO,GACf;EAEF,IAAI,KAAK,OAAO,KAAK,iBAAiB;GACpC,KAAK,cAAc,IAAI,YAAY;GACnC;EACF;EACA,MAAM,UAAW,MAAM,KAAK,GAAG,SAAS,YAAY;EACpD,KAAK,UAAU,IAAI,cAAc;GAAE,MAAM;GAAW;EAAQ,CAAC;CAC/D;CAEA,MAAM,SAA0B;EAC9B,IAAI,WAAW;EACf,KAAK,MAAM,CAAC,MAAM,aAAa,KAAK,UAAU,QAAQ,GAAG;GACvD,IAAI,SAAS,SAAS,UAAU;IAC9B,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,CAAC,OAAO,UAAU;KAI1C,IAAI,8BAA8B,KAAK,GACrC,MAAM;IAEV,CAAC;IACD;IACA;GACF;GACA,IAAI;IACF,MAAM,KAAK,GAAG,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;IACtD,MAAM,KAAK,GAAG,UAAU,MAAM,SAAS,OAAO;IAC9C;GACF,SAAS,OAAO;IAEd,IAAI,8BAA8B,KAAK,GACrC,MAAM;GAIV;EACF;EACA,OAAO;CACT;CAEA,gBAA0B;EACxB,OAAO,CAAC,GAAG,KAAK,UAAU,KAAK,GAAG,GAAG,KAAK,aAAa;CACzD;AACF;;;;;;;AAQA,SAAgB,4BACd,UAA0D,CAAC,GAClC;CACzB,OAAO,IAAI,0BAA0B,QAAQ,iBAAiB,QAAQ,EAAE;AAC1E"}
@@ -1,5 +1,6 @@
1
1
  import "../../common/enum.mjs";
2
2
  import "../../common/index.mjs";
3
+ import { isWorkspaceClientTimeoutError } from "./workspaceFS.mjs";
3
4
  import { getSpawn, getWorkspaceFS, resolveWorkspacePathSafe, spawnLocalProcess, truncateLocalOutput } from "./LocalExecutionEngine.mjs";
4
5
  import { createCompileCheckTool, createCompileCheckToolDefinition } from "./CompileCheckTool.mjs";
5
6
  import { createLocalFileCheckpointer } from "./FileCheckpointer.mjs";
@@ -211,7 +212,9 @@ async function revertStrictWrite(fs, path, existed, before, encoding) {
211
212
  text: before
212
213
  }), "utf8");
213
214
  else await fs.unlink(path);
214
- } catch {}
215
+ } catch (error) {
216
+ if (isWorkspaceClientTimeoutError(error)) throw error;
217
+ }
215
218
  }
216
219
  function summariseDiff(filePath, before, after) {
217
220
  if (before === after) return "(no textual changes)";
@@ -344,13 +347,20 @@ function createLocalWriteFileTool(config = {}, checkpointer) {
344
347
  before = decoded.text;
345
348
  encoding = decoded;
346
349
  existed = true;
347
- } catch {
350
+ } catch (error) {
351
+ if (isWorkspaceClientTimeoutError(error)) throw error;
348
352
  existed = false;
349
353
  }
350
354
  await fs.mkdir(dirname(path), { recursive: true });
351
355
  const finalText = encodeFile(input.content, encoding);
352
356
  await fs.writeFile(path, finalText, "utf8");
353
- const syntax = await maybeRunSyntaxCheck(path, config);
357
+ let syntax;
358
+ try {
359
+ syntax = await maybeRunSyntaxCheck(path, config);
360
+ } catch (error) {
361
+ if (isWorkspaceClientTimeoutError(error) && config.postEditSyntaxCheck === "strict") await revertStrictWrite(fs, path, existed, before, encoding);
362
+ throw error;
363
+ }
354
364
  const diff = existed ? summariseDiff(path, before, input.content) : `(new file, ${input.content.length} chars)`;
355
365
  const summary = appendSyntaxCheckSummary(existed ? `Overwrote ${path} (${input.content.length} chars). Diff:\n${diff}` : `Created ${path} (${input.content.length} chars).`, syntax);
356
366
  if (syntax?.outcome.ok === false && syntax.mode === "strict") {
@@ -394,7 +404,13 @@ function createLocalEditFileTool(config = {}, checkpointer) {
394
404
  if (checkpointer != null) await checkpointer.captureBeforeWrite(path);
395
405
  const finalText = encodeFile(next, encoding);
396
406
  await fs.writeFile(path, finalText, "utf8");
397
- const syntax = await maybeRunSyntaxCheck(path, config);
407
+ let syntax;
408
+ try {
409
+ syntax = await maybeRunSyntaxCheck(path, config);
410
+ } catch (error) {
411
+ if (isWorkspaceClientTimeoutError(error) && config.postEditSyntaxCheck === "strict") await revertStrictWrite(fs, path, true, original, encoding);
412
+ throw error;
413
+ }
398
414
  const diff = summariseDiff(path, original, next);
399
415
  const fuzzy = strategiesUsed.some((s) => s !== "exact");
400
416
  const summary = appendSyntaxCheckSummary(`Applied ${edits.length} edit(s) to ${path}` + (fuzzy ? ` (strategies: ${strategiesUsed.join(", ")})` : "") + `. Diff:\n${diff}`, syntax);
@@ -514,7 +530,8 @@ async function* walkFiles(root, fs) {
514
530
  let entries;
515
531
  try {
516
532
  entries = await fs.readdir(dir, { withFileTypes: true });
517
- } catch {
533
+ } catch (error) {
534
+ if (isWorkspaceClientTimeoutError(error)) throw error;
518
535
  continue;
519
536
  }
520
537
  for (const entry of entries) {
@@ -628,7 +645,8 @@ async function fallbackGrep(root, pattern, globFilter, maxResults, fs) {
628
645
  let stat;
629
646
  try {
630
647
  stat = await fs.stat(file);
631
- } catch {
648
+ } catch (error) {
649
+ if (isWorkspaceClientTimeoutError(error)) throw error;
632
650
  continue;
633
651
  }
634
652
  if (stat.size > FALLBACK_GREP_MAX_FILE_BYTES) {
@@ -638,7 +656,8 @@ async function fallbackGrep(root, pattern, globFilter, maxResults, fs) {
638
656
  let content;
639
657
  try {
640
658
  content = await fs.readFile(file, "utf8");
641
- } catch {
659
+ } catch (error) {
660
+ if (isWorkspaceClientTimeoutError(error)) throw error;
642
661
  continue;
643
662
  }
644
663
  if (content.includes("\0")) continue;