@illuma-ai/agents 1.0.93 → 1.0.94
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/tools/CodeExecutor.cjs +14 -1
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +14 -1
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/types/tools/CodeExecutor.d.ts +26 -2
- package/package.json +1 -1
- package/src/tools/CodeExecutor.ts +20 -1
- package/src/tools/__tests__/CodeExecutor.test.ts +76 -0
|
@@ -58,8 +58,20 @@ const CodeExecutionToolSchema = {
|
|
|
58
58
|
items: { type: 'string' },
|
|
59
59
|
description: 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',
|
|
60
60
|
},
|
|
61
|
+
code_id: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
description: 'ID of previously stored code (returned by a prior execute_code call). Use with old_str/new_str to edit stored code instead of rewriting it.',
|
|
64
|
+
},
|
|
65
|
+
old_str: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: 'Exact string to find in stored code (requires code_id). Copy verbatim from the code you wrote.',
|
|
68
|
+
},
|
|
69
|
+
new_str: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
description: 'Replacement string (requires code_id and old_str). The matched old_str will be replaced with this value.',
|
|
72
|
+
},
|
|
61
73
|
},
|
|
62
|
-
required: ['lang'
|
|
74
|
+
required: ['lang'],
|
|
63
75
|
};
|
|
64
76
|
const CodeExecutionToolDescription = `
|
|
65
77
|
Runs code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.
|
|
@@ -68,6 +80,7 @@ Usage:
|
|
|
68
80
|
- No network access available.
|
|
69
81
|
- Generated files are automatically delivered; **DO NOT** provide download links.
|
|
70
82
|
- NEVER use this tool to execute malicious code.
|
|
83
|
+
- When a code_id is returned in output, you can edit that code using code_id + old_str + new_str instead of rewriting the entire code block.
|
|
71
84
|
`.trim();
|
|
72
85
|
const CodeExecutionToolName = _enum.Constants.EXECUTE_CODE;
|
|
73
86
|
const CodeExecutionToolDefinition = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeExecutor.cjs","sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport type * as t from '@/types';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\nexport const imageExtRegex = /\\.(jpg|jpeg|png|gif|webp)$/i;\nexport const getCodeBaseURL = (): string =>\n getEnvironmentVariable(EnvVar.CODE_BASEURL) ??\n Constants.OFFICIAL_CODE_BASEURL;\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n \"stdout: Empty. Ensure you're writing output explicitly.\\n\";\n\nconst SUPPORTED_LANGUAGES = [\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n 'bash',\n] as const;\n\nexport const CodeExecutionToolSchema = {\n type: 'object',\n properties: {\n lang: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES,\n description:\n 'The programming language or runtime to execute the code in.',\n },\n code: {\n type: 'string',\n description: `The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',\n },\n },\n required: ['lang', 'code'],\n} as const;\n\n// NOTE: Resolved at call time inside the tool function, not at module load time.\n// Module-level caching caused stale URLs when env vars changed between restarts.\n\ntype SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\nexport const CodeExecutionToolDescription = `\nRuns code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious code.\n`.trim();\n\nexport const CodeExecutionToolName = Constants.EXECUTE_CODE;\n\nexport const CodeExecutionToolDefinition = {\n name: CodeExecutionToolName,\n description: CodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n} as const;\n\nfunction createCodeExecutionTool(\n params: t.CodeExecutionToolParams = {}\n): DynamicStructuredTool {\n const apiKey =\n params[EnvVar.CODE_API_KEY] ??\n params.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n if (!apiKey) {\n throw new Error('No API key provided for code execution tool.');\n }\n\n const description = `\n⛔ STOP! Before using this tool, ask: \"Does user need a DOWNLOADABLE FILE?\"\n- If NO (dashboard, chart, visualization, UI) → DO NOT USE THIS TOOL. Use content_tool write instead.\n- If YES (.pptx, .docx, .pdf, .xlsx) → Use this tool.\n\nRuns code in a stateless execution environment. Each execution is isolated.\n\n🚫 NEVER USE FOR:\n- Dashboards, charts, visualizations → Use content_tool write with React/Chart.js\n- \"Mock data\" or \"sample data\" for display → Hardcode data in content_tool write\n- UI components, HTML pages, React apps → Use content_tool write\n\n✅ ONLY USE FOR:\n- File generation: PowerPoint (.pptx), Word (.docx), PDF (.pdf), Excel (.xlsx)\n- Processing uploaded files (CSV, Excel analysis)\n- Heavy computation requiring Python\n\nRules:\n- No network access available\n- Generated files auto-delivered (no download links needed)\n`.trim();\n\n return tool(\n async (rawInput, config) => {\n // Resolve URL at call time (not module load time) to pick up env var changes\n const baseEndpoint = getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const { lang, code, ...rest } = rawInput as {\n lang: SupportedLanguage;\n code: string;\n args?: string[];\n };\n /**\n * Extract session context from config.toolCall (injected by ToolNode).\n * - session_id: For API to associate with previous session\n * - _injected_files: File refs to pass directly (avoids /files endpoint race condition)\n */\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang,\n code,\n ...rest,\n ...params,\n };\n\n /**\n * Pass session_id to /exec so code-executor reuses the existing session workspace.\n * This allows retries and follow-up executions to access previously generated files.\n */\n if (session_id != null && session_id.length > 0) {\n postData.session_id = session_id;\n }\n\n /**\n * File injection priority:\n * 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)\n * 2. Fall back to fetching from /files endpoint if session_id provided but no injected files\n */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n /** Fallback: fetch from /files endpoint (may have race condition issues) */\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n let stdoutCapped = false;\n\n // Self-healing: Cap large stdout to prevent context bloat.\n // Preserves head (8KB) + tail (4KB) so the agent sees beginning and end.\n const STDOUT_MAX_CHARS = 16384;\n const STDOUT_HEAD_CHARS = 8192;\n const STDOUT_TAIL_CHARS = 4096;\n if (result.stdout && result.stdout.length > STDOUT_MAX_CHARS) {\n const originalLen = result.stdout.length;\n const head = result.stdout.substring(0, STDOUT_HEAD_CHARS);\n const tail = result.stdout.substring(\n result.stdout.length - STDOUT_TAIL_CHARS\n );\n const omitted = originalLen - STDOUT_HEAD_CHARS - STDOUT_TAIL_CHARS;\n result.stdout = `${head}\\n\\n[...${omitted} chars omitted...]\\n\\n${tail}`;\n stdoutCapped = true;\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] stdout capped: ${originalLen} → ${result.stdout.length} chars`\n );\n }\n\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n\n // Self-healing: Detect code truncation (syntax error on long code).\n // When the agent's generated code is >1500 chars and produces a SyntaxError,\n // it's likely truncated mid-generation rather than a real bug.\n const CODE_TRUNCATION_MIN_CHARS = 1500;\n if (result.stderr && code.length > CODE_TRUNCATION_MIN_CHARS) {\n const stderrLower = result.stderr.toLowerCase();\n if (\n stderrLower.includes('syntaxerror') ||\n stderrLower.includes('unexpected end') ||\n stderrLower.includes('unexpected eof') ||\n stderrLower.includes('unterminated')\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] Code truncation detected: code=${code.length} chars, stderr contains syntax error`\n );\n formattedOutput +=\n '\\n[CODE_TRUNCATION_LIKELY] Your code appears truncated mid-generation.' +\n ' Split into multiple smaller execute_code calls (max 60 lines each).' +\n ' For documents: create+save first, then open+append+save in follow-up calls.' +\n ' Do NOT retry the same long code block.';\n }\n }\n\n // Self-healing: Advisory when stdout was capped\n if (stdoutCapped) {\n formattedOutput +=\n '\\n[OUTPUT_TOO_LARGE] stdout was capped. Use targeted print() for specific values.';\n }\n\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error (${EXEC_ENDPOINT}):\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: CodeExecutionToolName,\n description,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };\n"],"names":["config","getEnvironmentVariable","EnvVar","Constants","tool","HttpsProxyAgent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CodeExecutor.cjs","sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport type * as t from '@/types';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\nexport const imageExtRegex = /\\.(jpg|jpeg|png|gif|webp)$/i;\nexport const getCodeBaseURL = (): string =>\n getEnvironmentVariable(EnvVar.CODE_BASEURL) ??\n Constants.OFFICIAL_CODE_BASEURL;\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n \"stdout: Empty. Ensure you're writing output explicitly.\\n\";\n\nconst SUPPORTED_LANGUAGES = [\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n 'bash',\n] as const;\n\nexport const CodeExecutionToolSchema = {\n type: 'object',\n properties: {\n lang: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES,\n description:\n 'The programming language or runtime to execute the code in.',\n },\n code: {\n type: 'string',\n description: `The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',\n },\n code_id: {\n type: 'string',\n description:\n 'ID of previously stored code (returned by a prior execute_code call). Use with old_str/new_str to edit stored code instead of rewriting it.',\n },\n old_str: {\n type: 'string',\n description:\n 'Exact string to find in stored code (requires code_id). Copy verbatim from the code you wrote.',\n },\n new_str: {\n type: 'string',\n description:\n 'Replacement string (requires code_id and old_str). The matched old_str will be replaced with this value.',\n },\n },\n required: ['lang'],\n} as const;\n\n// NOTE: Resolved at call time inside the tool function, not at module load time.\n// Module-level caching caused stale URLs when env vars changed between restarts.\n\ntype SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\nexport const CodeExecutionToolDescription = `\nRuns code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious code.\n- When a code_id is returned in output, you can edit that code using code_id + old_str + new_str instead of rewriting the entire code block.\n`.trim();\n\nexport const CodeExecutionToolName = Constants.EXECUTE_CODE;\n\nexport const CodeExecutionToolDefinition = {\n name: CodeExecutionToolName,\n description: CodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n} as const;\n\nfunction createCodeExecutionTool(\n params: t.CodeExecutionToolParams = {}\n): DynamicStructuredTool {\n const apiKey =\n params[EnvVar.CODE_API_KEY] ??\n params.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n if (!apiKey) {\n throw new Error('No API key provided for code execution tool.');\n }\n\n const description = `\n⛔ STOP! Before using this tool, ask: \"Does user need a DOWNLOADABLE FILE?\"\n- If NO (dashboard, chart, visualization, UI) → DO NOT USE THIS TOOL. Use content_tool write instead.\n- If YES (.pptx, .docx, .pdf, .xlsx) → Use this tool.\n\nRuns code in a stateless execution environment. Each execution is isolated.\n\n🚫 NEVER USE FOR:\n- Dashboards, charts, visualizations → Use content_tool write with React/Chart.js\n- \"Mock data\" or \"sample data\" for display → Hardcode data in content_tool write\n- UI components, HTML pages, React apps → Use content_tool write\n\n✅ ONLY USE FOR:\n- File generation: PowerPoint (.pptx), Word (.docx), PDF (.pdf), Excel (.xlsx)\n- Processing uploaded files (CSV, Excel analysis)\n- Heavy computation requiring Python\n\nRules:\n- No network access available\n- Generated files auto-delivered (no download links needed)\n`.trim();\n\n return tool(\n async (rawInput, config) => {\n // Resolve URL at call time (not module load time) to pick up env var changes\n const baseEndpoint = getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const { lang, code, ...rest } = rawInput as {\n lang: SupportedLanguage;\n code: string;\n code_id?: string;\n old_str?: string;\n new_str?: string;\n args?: string[];\n };\n /**\n * Extract session context from config.toolCall (injected by ToolNode).\n * - session_id: For API to associate with previous session\n * - _injected_files: File refs to pass directly (avoids /files endpoint race condition)\n */\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang,\n code,\n ...rest,\n ...params,\n };\n\n /**\n * Pass session_id to /exec so code-executor reuses the existing session workspace.\n * This allows retries and follow-up executions to access previously generated files.\n */\n if (session_id != null && session_id.length > 0) {\n postData.session_id = session_id;\n }\n\n /**\n * File injection priority:\n * 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)\n * 2. Fall back to fetching from /files endpoint if session_id provided but no injected files\n */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n /** Fallback: fetch from /files endpoint (may have race condition issues) */\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n let stdoutCapped = false;\n\n // Self-healing: Cap large stdout to prevent context bloat.\n // Preserves head (8KB) + tail (4KB) so the agent sees beginning and end.\n const STDOUT_MAX_CHARS = 16384;\n const STDOUT_HEAD_CHARS = 8192;\n const STDOUT_TAIL_CHARS = 4096;\n if (result.stdout && result.stdout.length > STDOUT_MAX_CHARS) {\n const originalLen = result.stdout.length;\n const head = result.stdout.substring(0, STDOUT_HEAD_CHARS);\n const tail = result.stdout.substring(\n result.stdout.length - STDOUT_TAIL_CHARS\n );\n const omitted = originalLen - STDOUT_HEAD_CHARS - STDOUT_TAIL_CHARS;\n result.stdout = `${head}\\n\\n[...${omitted} chars omitted...]\\n\\n${tail}`;\n stdoutCapped = true;\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] stdout capped: ${originalLen} → ${result.stdout.length} chars`\n );\n }\n\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n\n // Self-healing: Detect code truncation (syntax error on long code).\n // When the agent's generated code is >1500 chars and produces a SyntaxError,\n // it's likely truncated mid-generation rather than a real bug.\n const CODE_TRUNCATION_MIN_CHARS = 1500;\n if (result.stderr && code.length > CODE_TRUNCATION_MIN_CHARS) {\n const stderrLower = result.stderr.toLowerCase();\n if (\n stderrLower.includes('syntaxerror') ||\n stderrLower.includes('unexpected end') ||\n stderrLower.includes('unexpected eof') ||\n stderrLower.includes('unterminated')\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] Code truncation detected: code=${code.length} chars, stderr contains syntax error`\n );\n formattedOutput +=\n '\\n[CODE_TRUNCATION_LIKELY] Your code appears truncated mid-generation.' +\n ' Split into multiple smaller execute_code calls (max 60 lines each).' +\n ' For documents: create+save first, then open+append+save in follow-up calls.' +\n ' Do NOT retry the same long code block.';\n }\n }\n\n // Self-healing: Advisory when stdout was capped\n if (stdoutCapped) {\n formattedOutput +=\n '\\n[OUTPUT_TOO_LARGE] stdout was capped. Use targeted print() for specific values.';\n }\n\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error (${EXEC_ENDPOINT}):\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: CodeExecutionToolName,\n description,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };\n"],"names":["config","getEnvironmentVariable","EnvVar","Constants","tool","HttpsProxyAgent"],"mappings":"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"}
|
|
@@ -56,8 +56,20 @@ const CodeExecutionToolSchema = {
|
|
|
56
56
|
items: { type: 'string' },
|
|
57
57
|
description: 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',
|
|
58
58
|
},
|
|
59
|
+
code_id: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'ID of previously stored code (returned by a prior execute_code call). Use with old_str/new_str to edit stored code instead of rewriting it.',
|
|
62
|
+
},
|
|
63
|
+
old_str: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Exact string to find in stored code (requires code_id). Copy verbatim from the code you wrote.',
|
|
66
|
+
},
|
|
67
|
+
new_str: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'Replacement string (requires code_id and old_str). The matched old_str will be replaced with this value.',
|
|
70
|
+
},
|
|
59
71
|
},
|
|
60
|
-
required: ['lang'
|
|
72
|
+
required: ['lang'],
|
|
61
73
|
};
|
|
62
74
|
const CodeExecutionToolDescription = `
|
|
63
75
|
Runs code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.
|
|
@@ -66,6 +78,7 @@ Usage:
|
|
|
66
78
|
- No network access available.
|
|
67
79
|
- Generated files are automatically delivered; **DO NOT** provide download links.
|
|
68
80
|
- NEVER use this tool to execute malicious code.
|
|
81
|
+
- When a code_id is returned in output, you can edit that code using code_id + old_str + new_str instead of rewriting the entire code block.
|
|
69
82
|
`.trim();
|
|
70
83
|
const CodeExecutionToolName = Constants.EXECUTE_CODE;
|
|
71
84
|
const CodeExecutionToolDefinition = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeExecutor.mjs","sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport type * as t from '@/types';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\nexport const imageExtRegex = /\\.(jpg|jpeg|png|gif|webp)$/i;\nexport const getCodeBaseURL = (): string =>\n getEnvironmentVariable(EnvVar.CODE_BASEURL) ??\n Constants.OFFICIAL_CODE_BASEURL;\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n \"stdout: Empty. Ensure you're writing output explicitly.\\n\";\n\nconst SUPPORTED_LANGUAGES = [\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n 'bash',\n] as const;\n\nexport const CodeExecutionToolSchema = {\n type: 'object',\n properties: {\n lang: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES,\n description:\n 'The programming language or runtime to execute the code in.',\n },\n code: {\n type: 'string',\n description: `The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',\n },\n },\n required: ['lang', 'code'],\n} as const;\n\n// NOTE: Resolved at call time inside the tool function, not at module load time.\n// Module-level caching caused stale URLs when env vars changed between restarts.\n\ntype SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\nexport const CodeExecutionToolDescription = `\nRuns code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious code.\n`.trim();\n\nexport const CodeExecutionToolName = Constants.EXECUTE_CODE;\n\nexport const CodeExecutionToolDefinition = {\n name: CodeExecutionToolName,\n description: CodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n} as const;\n\nfunction createCodeExecutionTool(\n params: t.CodeExecutionToolParams = {}\n): DynamicStructuredTool {\n const apiKey =\n params[EnvVar.CODE_API_KEY] ??\n params.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n if (!apiKey) {\n throw new Error('No API key provided for code execution tool.');\n }\n\n const description = `\n⛔ STOP! Before using this tool, ask: \"Does user need a DOWNLOADABLE FILE?\"\n- If NO (dashboard, chart, visualization, UI) → DO NOT USE THIS TOOL. Use content_tool write instead.\n- If YES (.pptx, .docx, .pdf, .xlsx) → Use this tool.\n\nRuns code in a stateless execution environment. Each execution is isolated.\n\n🚫 NEVER USE FOR:\n- Dashboards, charts, visualizations → Use content_tool write with React/Chart.js\n- \"Mock data\" or \"sample data\" for display → Hardcode data in content_tool write\n- UI components, HTML pages, React apps → Use content_tool write\n\n✅ ONLY USE FOR:\n- File generation: PowerPoint (.pptx), Word (.docx), PDF (.pdf), Excel (.xlsx)\n- Processing uploaded files (CSV, Excel analysis)\n- Heavy computation requiring Python\n\nRules:\n- No network access available\n- Generated files auto-delivered (no download links needed)\n`.trim();\n\n return tool(\n async (rawInput, config) => {\n // Resolve URL at call time (not module load time) to pick up env var changes\n const baseEndpoint = getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const { lang, code, ...rest } = rawInput as {\n lang: SupportedLanguage;\n code: string;\n args?: string[];\n };\n /**\n * Extract session context from config.toolCall (injected by ToolNode).\n * - session_id: For API to associate with previous session\n * - _injected_files: File refs to pass directly (avoids /files endpoint race condition)\n */\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang,\n code,\n ...rest,\n ...params,\n };\n\n /**\n * Pass session_id to /exec so code-executor reuses the existing session workspace.\n * This allows retries and follow-up executions to access previously generated files.\n */\n if (session_id != null && session_id.length > 0) {\n postData.session_id = session_id;\n }\n\n /**\n * File injection priority:\n * 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)\n * 2. Fall back to fetching from /files endpoint if session_id provided but no injected files\n */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n /** Fallback: fetch from /files endpoint (may have race condition issues) */\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n let stdoutCapped = false;\n\n // Self-healing: Cap large stdout to prevent context bloat.\n // Preserves head (8KB) + tail (4KB) so the agent sees beginning and end.\n const STDOUT_MAX_CHARS = 16384;\n const STDOUT_HEAD_CHARS = 8192;\n const STDOUT_TAIL_CHARS = 4096;\n if (result.stdout && result.stdout.length > STDOUT_MAX_CHARS) {\n const originalLen = result.stdout.length;\n const head = result.stdout.substring(0, STDOUT_HEAD_CHARS);\n const tail = result.stdout.substring(\n result.stdout.length - STDOUT_TAIL_CHARS\n );\n const omitted = originalLen - STDOUT_HEAD_CHARS - STDOUT_TAIL_CHARS;\n result.stdout = `${head}\\n\\n[...${omitted} chars omitted...]\\n\\n${tail}`;\n stdoutCapped = true;\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] stdout capped: ${originalLen} → ${result.stdout.length} chars`\n );\n }\n\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n\n // Self-healing: Detect code truncation (syntax error on long code).\n // When the agent's generated code is >1500 chars and produces a SyntaxError,\n // it's likely truncated mid-generation rather than a real bug.\n const CODE_TRUNCATION_MIN_CHARS = 1500;\n if (result.stderr && code.length > CODE_TRUNCATION_MIN_CHARS) {\n const stderrLower = result.stderr.toLowerCase();\n if (\n stderrLower.includes('syntaxerror') ||\n stderrLower.includes('unexpected end') ||\n stderrLower.includes('unexpected eof') ||\n stderrLower.includes('unterminated')\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] Code truncation detected: code=${code.length} chars, stderr contains syntax error`\n );\n formattedOutput +=\n '\\n[CODE_TRUNCATION_LIKELY] Your code appears truncated mid-generation.' +\n ' Split into multiple smaller execute_code calls (max 60 lines each).' +\n ' For documents: create+save first, then open+append+save in follow-up calls.' +\n ' Do NOT retry the same long code block.';\n }\n }\n\n // Self-healing: Advisory when stdout was capped\n if (stdoutCapped) {\n formattedOutput +=\n '\\n[OUTPUT_TOO_LARGE] stdout was capped. Use targeted print() for specific values.';\n }\n\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error (${EXEC_ENDPOINT}):\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: CodeExecutionToolName,\n description,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CodeExecutor.mjs","sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport type * as t from '@/types';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\nexport const imageExtRegex = /\\.(jpg|jpeg|png|gif|webp)$/i;\nexport const getCodeBaseURL = (): string =>\n getEnvironmentVariable(EnvVar.CODE_BASEURL) ??\n Constants.OFFICIAL_CODE_BASEURL;\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n \"stdout: Empty. Ensure you're writing output explicitly.\\n\";\n\nconst SUPPORTED_LANGUAGES = [\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n 'bash',\n] as const;\n\nexport const CodeExecutionToolSchema = {\n type: 'object',\n properties: {\n lang: {\n type: 'string',\n enum: SUPPORTED_LANGUAGES,\n description:\n 'The programming language or runtime to execute the code in.',\n },\n code: {\n type: 'string',\n description: `The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',\n },\n code_id: {\n type: 'string',\n description:\n 'ID of previously stored code (returned by a prior execute_code call). Use with old_str/new_str to edit stored code instead of rewriting it.',\n },\n old_str: {\n type: 'string',\n description:\n 'Exact string to find in stored code (requires code_id). Copy verbatim from the code you wrote.',\n },\n new_str: {\n type: 'string',\n description:\n 'Replacement string (requires code_id and old_str). The matched old_str will be replaced with this value.',\n },\n },\n required: ['lang'],\n} as const;\n\n// NOTE: Resolved at call time inside the tool function, not at module load time.\n// Module-level caching caused stale URLs when env vars changed between restarts.\n\ntype SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];\n\nexport const CodeExecutionToolDescription = `\nRuns code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious code.\n- When a code_id is returned in output, you can edit that code using code_id + old_str + new_str instead of rewriting the entire code block.\n`.trim();\n\nexport const CodeExecutionToolName = Constants.EXECUTE_CODE;\n\nexport const CodeExecutionToolDefinition = {\n name: CodeExecutionToolName,\n description: CodeExecutionToolDescription,\n schema: CodeExecutionToolSchema,\n} as const;\n\nfunction createCodeExecutionTool(\n params: t.CodeExecutionToolParams = {}\n): DynamicStructuredTool {\n const apiKey =\n params[EnvVar.CODE_API_KEY] ??\n params.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n if (!apiKey) {\n throw new Error('No API key provided for code execution tool.');\n }\n\n const description = `\n⛔ STOP! Before using this tool, ask: \"Does user need a DOWNLOADABLE FILE?\"\n- If NO (dashboard, chart, visualization, UI) → DO NOT USE THIS TOOL. Use content_tool write instead.\n- If YES (.pptx, .docx, .pdf, .xlsx) → Use this tool.\n\nRuns code in a stateless execution environment. Each execution is isolated.\n\n🚫 NEVER USE FOR:\n- Dashboards, charts, visualizations → Use content_tool write with React/Chart.js\n- \"Mock data\" or \"sample data\" for display → Hardcode data in content_tool write\n- UI components, HTML pages, React apps → Use content_tool write\n\n✅ ONLY USE FOR:\n- File generation: PowerPoint (.pptx), Word (.docx), PDF (.pdf), Excel (.xlsx)\n- Processing uploaded files (CSV, Excel analysis)\n- Heavy computation requiring Python\n\nRules:\n- No network access available\n- Generated files auto-delivered (no download links needed)\n`.trim();\n\n return tool(\n async (rawInput, config) => {\n // Resolve URL at call time (not module load time) to pick up env var changes\n const baseEndpoint = getCodeBaseURL();\n const EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\n const { lang, code, ...rest } = rawInput as {\n lang: SupportedLanguage;\n code: string;\n code_id?: string;\n old_str?: string;\n new_str?: string;\n args?: string[];\n };\n /**\n * Extract session context from config.toolCall (injected by ToolNode).\n * - session_id: For API to associate with previous session\n * - _injected_files: File refs to pass directly (avoids /files endpoint race condition)\n */\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang,\n code,\n ...rest,\n ...params,\n };\n\n /**\n * Pass session_id to /exec so code-executor reuses the existing session workspace.\n * This allows retries and follow-up executions to access previously generated files.\n */\n if (session_id != null && session_id.length > 0) {\n postData.session_id = session_id;\n }\n\n /**\n * File injection priority:\n * 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)\n * 2. Fall back to fetching from /files endpoint if session_id provided but no injected files\n */\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n /** Fallback: fetch from /files endpoint (may have race condition issues) */\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'Illuma/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n let stdoutCapped = false;\n\n // Self-healing: Cap large stdout to prevent context bloat.\n // Preserves head (8KB) + tail (4KB) so the agent sees beginning and end.\n const STDOUT_MAX_CHARS = 16384;\n const STDOUT_HEAD_CHARS = 8192;\n const STDOUT_TAIL_CHARS = 4096;\n if (result.stdout && result.stdout.length > STDOUT_MAX_CHARS) {\n const originalLen = result.stdout.length;\n const head = result.stdout.substring(0, STDOUT_HEAD_CHARS);\n const tail = result.stdout.substring(\n result.stdout.length - STDOUT_TAIL_CHARS\n );\n const omitted = originalLen - STDOUT_HEAD_CHARS - STDOUT_TAIL_CHARS;\n result.stdout = `${head}\\n\\n[...${omitted} chars omitted...]\\n\\n${tail}`;\n stdoutCapped = true;\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] stdout capped: ${originalLen} → ${result.stdout.length} chars`\n );\n }\n\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n\n // Self-healing: Detect code truncation (syntax error on long code).\n // When the agent's generated code is >1500 chars and produces a SyntaxError,\n // it's likely truncated mid-generation rather than a real bug.\n const CODE_TRUNCATION_MIN_CHARS = 1500;\n if (result.stderr && code.length > CODE_TRUNCATION_MIN_CHARS) {\n const stderrLower = result.stderr.toLowerCase();\n if (\n stderrLower.includes('syntaxerror') ||\n stderrLower.includes('unexpected end') ||\n stderrLower.includes('unexpected eof') ||\n stderrLower.includes('unterminated')\n ) {\n // eslint-disable-next-line no-console\n console.debug(\n `[CodeExecutor] Code truncation detected: code=${code.length} chars, stderr contains syntax error`\n );\n formattedOutput +=\n '\\n[CODE_TRUNCATION_LIKELY] Your code appears truncated mid-generation.' +\n ' Split into multiple smaller execute_code calls (max 60 lines each).' +\n ' For documents: create+save first, then open+append+save in follow-up calls.' +\n ' Do NOT retry the same long code block.';\n }\n }\n\n // Self-healing: Advisory when stdout was capped\n if (stdoutCapped) {\n formattedOutput +=\n '\\n[OUTPUT_TOO_LARGE] stdout was capped. Use targeted print() for specific values.';\n }\n\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error (${EXEC_ENDPOINT}):\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: CodeExecutionToolName,\n description,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };\n"],"names":[],"mappings":"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"}
|
|
@@ -22,8 +22,20 @@ export declare const CodeExecutionToolSchema: {
|
|
|
22
22
|
};
|
|
23
23
|
readonly description: "Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.";
|
|
24
24
|
};
|
|
25
|
+
readonly code_id: {
|
|
26
|
+
readonly type: "string";
|
|
27
|
+
readonly description: "ID of previously stored code (returned by a prior execute_code call). Use with old_str/new_str to edit stored code instead of rewriting it.";
|
|
28
|
+
};
|
|
29
|
+
readonly old_str: {
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
readonly description: "Exact string to find in stored code (requires code_id). Copy verbatim from the code you wrote.";
|
|
32
|
+
};
|
|
33
|
+
readonly new_str: {
|
|
34
|
+
readonly type: "string";
|
|
35
|
+
readonly description: "Replacement string (requires code_id and old_str). The matched old_str will be replaced with this value.";
|
|
36
|
+
};
|
|
25
37
|
};
|
|
26
|
-
readonly required: readonly ["lang"
|
|
38
|
+
readonly required: readonly ["lang"];
|
|
27
39
|
};
|
|
28
40
|
export declare const CodeExecutionToolDescription: string;
|
|
29
41
|
export declare const CodeExecutionToolName = Constants.EXECUTE_CODE;
|
|
@@ -49,8 +61,20 @@ export declare const CodeExecutionToolDefinition: {
|
|
|
49
61
|
};
|
|
50
62
|
readonly description: "Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.";
|
|
51
63
|
};
|
|
64
|
+
readonly code_id: {
|
|
65
|
+
readonly type: "string";
|
|
66
|
+
readonly description: "ID of previously stored code (returned by a prior execute_code call). Use with old_str/new_str to edit stored code instead of rewriting it.";
|
|
67
|
+
};
|
|
68
|
+
readonly old_str: {
|
|
69
|
+
readonly type: "string";
|
|
70
|
+
readonly description: "Exact string to find in stored code (requires code_id). Copy verbatim from the code you wrote.";
|
|
71
|
+
};
|
|
72
|
+
readonly new_str: {
|
|
73
|
+
readonly type: "string";
|
|
74
|
+
readonly description: "Replacement string (requires code_id and old_str). The matched old_str will be replaced with this value.";
|
|
75
|
+
};
|
|
52
76
|
};
|
|
53
|
-
readonly required: readonly ["lang"
|
|
77
|
+
readonly required: readonly ["lang"];
|
|
54
78
|
};
|
|
55
79
|
};
|
|
56
80
|
declare function createCodeExecutionTool(params?: t.CodeExecutionToolParams): DynamicStructuredTool;
|
package/package.json
CHANGED
|
@@ -66,8 +66,23 @@ export const CodeExecutionToolSchema = {
|
|
|
66
66
|
description:
|
|
67
67
|
'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.',
|
|
68
68
|
},
|
|
69
|
+
code_id: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
description:
|
|
72
|
+
'ID of previously stored code (returned by a prior execute_code call). Use with old_str/new_str to edit stored code instead of rewriting it.',
|
|
73
|
+
},
|
|
74
|
+
old_str: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
description:
|
|
77
|
+
'Exact string to find in stored code (requires code_id). Copy verbatim from the code you wrote.',
|
|
78
|
+
},
|
|
79
|
+
new_str: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
description:
|
|
82
|
+
'Replacement string (requires code_id and old_str). The matched old_str will be replaced with this value.',
|
|
83
|
+
},
|
|
69
84
|
},
|
|
70
|
-
required: ['lang'
|
|
85
|
+
required: ['lang'],
|
|
71
86
|
} as const;
|
|
72
87
|
|
|
73
88
|
// NOTE: Resolved at call time inside the tool function, not at module load time.
|
|
@@ -82,6 +97,7 @@ Usage:
|
|
|
82
97
|
- No network access available.
|
|
83
98
|
- Generated files are automatically delivered; **DO NOT** provide download links.
|
|
84
99
|
- NEVER use this tool to execute malicious code.
|
|
100
|
+
- When a code_id is returned in output, you can edit that code using code_id + old_str + new_str instead of rewriting the entire code block.
|
|
85
101
|
`.trim();
|
|
86
102
|
|
|
87
103
|
export const CodeExecutionToolName = Constants.EXECUTE_CODE;
|
|
@@ -135,6 +151,9 @@ Rules:
|
|
|
135
151
|
const { lang, code, ...rest } = rawInput as {
|
|
136
152
|
lang: SupportedLanguage;
|
|
137
153
|
code: string;
|
|
154
|
+
code_id?: string;
|
|
155
|
+
old_str?: string;
|
|
156
|
+
new_str?: string;
|
|
138
157
|
args?: string[];
|
|
139
158
|
};
|
|
140
159
|
/**
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for CodeExecutor schema and tool definition.
|
|
3
|
+
*
|
|
4
|
+
* Validates the execute_code tool schema accepts the new optional
|
|
5
|
+
* code_id/old_str/new_str fields for code editing, and that
|
|
6
|
+
* 'code' is no longer required (only 'lang' is).
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
CodeExecutionToolSchema,
|
|
10
|
+
CodeExecutionToolDescription,
|
|
11
|
+
CodeExecutionToolName,
|
|
12
|
+
CodeExecutionToolDefinition,
|
|
13
|
+
} from '../CodeExecutor';
|
|
14
|
+
|
|
15
|
+
describe('CodeExecutionToolSchema', () => {
|
|
16
|
+
it('should require only "lang" in required array', () => {
|
|
17
|
+
expect(CodeExecutionToolSchema.required).toEqual(['lang']);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should have "code" as an optional property', () => {
|
|
21
|
+
expect(CodeExecutionToolSchema.properties.code).toBeDefined();
|
|
22
|
+
expect(CodeExecutionToolSchema.properties.code.type).toBe('string');
|
|
23
|
+
// code is not in required
|
|
24
|
+
expect(CodeExecutionToolSchema.required).not.toContain('code');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should have optional code_id property', () => {
|
|
28
|
+
expect(CodeExecutionToolSchema.properties.code_id).toBeDefined();
|
|
29
|
+
expect(CodeExecutionToolSchema.properties.code_id.type).toBe('string');
|
|
30
|
+
expect(CodeExecutionToolSchema.required).not.toContain('code_id');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should have optional old_str property', () => {
|
|
34
|
+
expect(CodeExecutionToolSchema.properties.old_str).toBeDefined();
|
|
35
|
+
expect(CodeExecutionToolSchema.properties.old_str.type).toBe('string');
|
|
36
|
+
expect(CodeExecutionToolSchema.required).not.toContain('old_str');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should have optional new_str property', () => {
|
|
40
|
+
expect(CodeExecutionToolSchema.properties.new_str).toBeDefined();
|
|
41
|
+
expect(CodeExecutionToolSchema.properties.new_str.type).toBe('string');
|
|
42
|
+
expect(CodeExecutionToolSchema.required).not.toContain('new_str');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should still have lang with enum of supported languages', () => {
|
|
46
|
+
expect(CodeExecutionToolSchema.properties.lang.type).toBe('string');
|
|
47
|
+
expect(CodeExecutionToolSchema.properties.lang.enum).toContain('py');
|
|
48
|
+
expect(CodeExecutionToolSchema.properties.lang.enum).toContain('js');
|
|
49
|
+
expect(CodeExecutionToolSchema.properties.lang.enum).toContain('ts');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should still have args as optional array', () => {
|
|
53
|
+
expect(CodeExecutionToolSchema.properties.args).toBeDefined();
|
|
54
|
+
expect(CodeExecutionToolSchema.properties.args.type).toBe('array');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('CodeExecutionToolDescription', () => {
|
|
59
|
+
it('should mention code_id editing capability', () => {
|
|
60
|
+
expect(CodeExecutionToolDescription).toContain('code_id');
|
|
61
|
+
expect(CodeExecutionToolDescription).toContain('old_str');
|
|
62
|
+
expect(CodeExecutionToolDescription).toContain('new_str');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('CodeExecutionToolDefinition', () => {
|
|
67
|
+
it('should export correct name', () => {
|
|
68
|
+
expect(CodeExecutionToolName).toBe('execute_code');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should have schema with new fields', () => {
|
|
72
|
+
expect(CodeExecutionToolDefinition.schema.properties.code_id).toBeDefined();
|
|
73
|
+
expect(CodeExecutionToolDefinition.schema.properties.old_str).toBeDefined();
|
|
74
|
+
expect(CodeExecutionToolDefinition.schema.properties.new_str).toBeDefined();
|
|
75
|
+
});
|
|
76
|
+
});
|