@inkeep/agents-run-api 0.39.4 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/system-prompt.js +5 -0
  2. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/thinking-preparation.js +5 -0
  3. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/tool.js +5 -0
  4. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/data-component.js +5 -0
  5. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/data-components.js +5 -0
  6. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/system-prompt.js +5 -0
  7. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/shared/artifact-retrieval-guidance.js +5 -0
  8. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/shared/artifact.js +5 -0
  9. package/dist/a2a/client.d.ts +184 -0
  10. package/dist/a2a/client.js +510 -0
  11. package/dist/a2a/handlers.d.ts +7 -0
  12. package/dist/a2a/handlers.js +560 -0
  13. package/dist/a2a/transfer.d.ts +22 -0
  14. package/dist/a2a/transfer.js +46 -0
  15. package/dist/a2a/types.d.ts +79 -0
  16. package/dist/a2a/types.js +22 -0
  17. package/dist/agents/Agent.d.ts +266 -0
  18. package/dist/agents/Agent.js +1927 -0
  19. package/dist/agents/ModelFactory.d.ts +63 -0
  20. package/dist/agents/ModelFactory.js +194 -0
  21. package/dist/agents/SystemPromptBuilder.d.ts +21 -0
  22. package/dist/agents/SystemPromptBuilder.js +48 -0
  23. package/dist/agents/ToolSessionManager.d.ts +63 -0
  24. package/dist/agents/ToolSessionManager.js +146 -0
  25. package/dist/agents/generateTaskHandler.d.ts +49 -0
  26. package/dist/agents/generateTaskHandler.js +521 -0
  27. package/dist/agents/relationTools.d.ts +57 -0
  28. package/dist/agents/relationTools.js +262 -0
  29. package/dist/agents/types.d.ts +28 -0
  30. package/dist/agents/types.js +1 -0
  31. package/dist/agents/versions/v1/Phase1Config.d.ts +27 -0
  32. package/dist/agents/versions/v1/Phase1Config.js +424 -0
  33. package/dist/agents/versions/v1/Phase2Config.d.ts +31 -0
  34. package/dist/agents/versions/v1/Phase2Config.js +330 -0
  35. package/dist/constants/execution-limits/defaults.d.ts +51 -0
  36. package/dist/constants/execution-limits/defaults.js +52 -0
  37. package/dist/constants/execution-limits/index.d.ts +6 -0
  38. package/dist/constants/execution-limits/index.js +21 -0
  39. package/dist/create-app.d.ts +9 -0
  40. package/dist/create-app.js +195 -0
  41. package/dist/data/agent.d.ts +7 -0
  42. package/dist/data/agent.js +72 -0
  43. package/dist/data/agents.d.ts +34 -0
  44. package/dist/data/agents.js +139 -0
  45. package/dist/data/conversations.d.ts +128 -0
  46. package/dist/data/conversations.js +522 -0
  47. package/dist/data/db/dbClient.d.ts +6 -0
  48. package/dist/data/db/dbClient.js +17 -0
  49. package/dist/env.d.ts +57 -0
  50. package/dist/env.js +1 -2
  51. package/dist/handlers/executionHandler.d.ts +39 -0
  52. package/dist/handlers/executionHandler.js +456 -0
  53. package/dist/index.d.ts +8 -29
  54. package/dist/index.js +5 -11235
  55. package/dist/instrumentation.d.ts +1 -2
  56. package/dist/instrumentation.js +66 -3
  57. package/dist/{logger2.js → logger.d.ts} +1 -2
  58. package/dist/logger.js +1 -1
  59. package/dist/middleware/api-key-auth.d.ts +26 -0
  60. package/dist/middleware/api-key-auth.js +240 -0
  61. package/dist/middleware/index.d.ts +2 -0
  62. package/dist/middleware/index.js +3 -0
  63. package/dist/openapi.d.ts +4 -0
  64. package/dist/openapi.js +54 -0
  65. package/dist/routes/agents.d.ts +12 -0
  66. package/dist/routes/agents.js +147 -0
  67. package/dist/routes/chat.d.ts +13 -0
  68. package/dist/routes/chat.js +293 -0
  69. package/dist/routes/chatDataStream.d.ts +13 -0
  70. package/dist/routes/chatDataStream.js +352 -0
  71. package/dist/routes/mcp.d.ts +13 -0
  72. package/dist/routes/mcp.js +495 -0
  73. package/dist/services/AgentSession.d.ts +356 -0
  74. package/dist/services/AgentSession.js +1208 -0
  75. package/dist/services/ArtifactParser.d.ts +105 -0
  76. package/dist/services/ArtifactParser.js +338 -0
  77. package/dist/services/ArtifactService.d.ts +123 -0
  78. package/dist/services/ArtifactService.js +612 -0
  79. package/dist/services/BaseCompressor.d.ts +183 -0
  80. package/dist/services/BaseCompressor.js +504 -0
  81. package/dist/services/ConversationCompressor.d.ts +32 -0
  82. package/dist/services/ConversationCompressor.js +91 -0
  83. package/dist/services/IncrementalStreamParser.d.ts +98 -0
  84. package/dist/services/IncrementalStreamParser.js +327 -0
  85. package/dist/services/MidGenerationCompressor.d.ts +63 -0
  86. package/dist/services/MidGenerationCompressor.js +104 -0
  87. package/dist/services/PendingToolApprovalManager.d.ts +62 -0
  88. package/dist/services/PendingToolApprovalManager.js +133 -0
  89. package/dist/services/ResponseFormatter.d.ts +39 -0
  90. package/dist/services/ResponseFormatter.js +152 -0
  91. package/dist/tools/NativeSandboxExecutor.d.ts +38 -0
  92. package/dist/tools/NativeSandboxExecutor.js +432 -0
  93. package/dist/tools/SandboxExecutorFactory.d.ts +36 -0
  94. package/dist/tools/SandboxExecutorFactory.js +80 -0
  95. package/dist/tools/VercelSandboxExecutor.d.ts +71 -0
  96. package/dist/tools/VercelSandboxExecutor.js +340 -0
  97. package/dist/tools/distill-conversation-history-tool.d.ts +62 -0
  98. package/dist/tools/distill-conversation-history-tool.js +206 -0
  99. package/dist/tools/distill-conversation-tool.d.ts +41 -0
  100. package/dist/tools/distill-conversation-tool.js +141 -0
  101. package/dist/tools/sandbox-utils.d.ts +18 -0
  102. package/dist/tools/sandbox-utils.js +53 -0
  103. package/dist/types/chat.d.ts +27 -0
  104. package/dist/types/chat.js +1 -0
  105. package/dist/types/execution-context.d.ts +46 -0
  106. package/dist/types/execution-context.js +27 -0
  107. package/dist/types/xml.d.ts +5 -0
  108. package/dist/utils/SchemaProcessor.d.ts +52 -0
  109. package/dist/utils/SchemaProcessor.js +182 -0
  110. package/dist/utils/agent-operations.d.ts +62 -0
  111. package/dist/utils/agent-operations.js +53 -0
  112. package/dist/utils/artifact-component-schema.d.ts +42 -0
  113. package/dist/utils/artifact-component-schema.js +186 -0
  114. package/dist/utils/cleanup.d.ts +21 -0
  115. package/dist/utils/cleanup.js +59 -0
  116. package/dist/utils/data-component-schema.d.ts +2 -0
  117. package/dist/utils/data-component-schema.js +3 -0
  118. package/dist/utils/default-status-schemas.d.ts +20 -0
  119. package/dist/utils/default-status-schemas.js +24 -0
  120. package/dist/utils/json-postprocessor.d.ts +13 -0
  121. package/dist/{json-postprocessor.cjs → utils/json-postprocessor.js} +1 -2
  122. package/dist/utils/model-context-utils.d.ts +39 -0
  123. package/dist/utils/model-context-utils.js +181 -0
  124. package/dist/utils/model-resolver.d.ts +6 -0
  125. package/dist/utils/model-resolver.js +34 -0
  126. package/dist/utils/schema-validation.d.ts +44 -0
  127. package/dist/utils/schema-validation.js +97 -0
  128. package/dist/utils/stream-helpers.d.ts +197 -0
  129. package/dist/utils/stream-helpers.js +518 -0
  130. package/dist/utils/stream-registry.d.ts +22 -0
  131. package/dist/utils/stream-registry.js +34 -0
  132. package/dist/utils/token-estimator.d.ts +69 -0
  133. package/dist/utils/token-estimator.js +53 -0
  134. package/dist/utils/tracer.d.ts +7 -0
  135. package/dist/utils/tracer.js +7 -0
  136. package/package.json +5 -20
  137. package/dist/SandboxExecutorFactory.cjs +0 -895
  138. package/dist/SandboxExecutorFactory.js +0 -893
  139. package/dist/SandboxExecutorFactory.js.map +0 -1
  140. package/dist/chunk-VBDAOXYI.cjs +0 -927
  141. package/dist/chunk-VBDAOXYI.js +0 -832
  142. package/dist/chunk-VBDAOXYI.js.map +0 -1
  143. package/dist/chunk.cjs +0 -34
  144. package/dist/conversations.cjs +0 -7
  145. package/dist/conversations.js +0 -7
  146. package/dist/conversations2.cjs +0 -209
  147. package/dist/conversations2.js +0 -180
  148. package/dist/conversations2.js.map +0 -1
  149. package/dist/dbClient.cjs +0 -9676
  150. package/dist/dbClient.js +0 -9670
  151. package/dist/dbClient.js.map +0 -1
  152. package/dist/dbClient2.cjs +0 -5
  153. package/dist/dbClient2.js +0 -5
  154. package/dist/env.cjs +0 -59
  155. package/dist/env.js.map +0 -1
  156. package/dist/execution-limits.cjs +0 -260
  157. package/dist/execution-limits.js +0 -63
  158. package/dist/execution-limits.js.map +0 -1
  159. package/dist/index.cjs +0 -11260
  160. package/dist/index.d.cts +0 -36
  161. package/dist/index.d.cts.map +0 -1
  162. package/dist/index.d.ts.map +0 -1
  163. package/dist/index.js.map +0 -1
  164. package/dist/instrumentation.cjs +0 -12
  165. package/dist/instrumentation.d.cts +0 -18
  166. package/dist/instrumentation.d.cts.map +0 -1
  167. package/dist/instrumentation.d.ts.map +0 -1
  168. package/dist/instrumentation2.cjs +0 -116
  169. package/dist/instrumentation2.js +0 -69
  170. package/dist/instrumentation2.js.map +0 -1
  171. package/dist/json-postprocessor.js +0 -20
  172. package/dist/json-postprocessor.js.map +0 -1
  173. package/dist/logger.cjs +0 -5
  174. package/dist/logger2.cjs +0 -1
  175. package/dist/nodefs.cjs +0 -29
  176. package/dist/nodefs.js +0 -27
  177. package/dist/nodefs.js.map +0 -1
  178. package/dist/opfs-ahp.cjs +0 -367
  179. package/dist/opfs-ahp.js +0 -368
  180. package/dist/opfs-ahp.js.map +0 -1
@@ -0,0 +1,432 @@
1
+ import { getLogger } from "../logger.js";
2
+ import { FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS } from "../constants/execution-limits/index.js";
3
+ import { createExecutionWrapper, parseExecutionResult } from "./sandbox-utils.js";
4
+ import { spawn } from "node:child_process";
5
+ import { createHash } from "node:crypto";
6
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+
10
+ //#region src/tools/NativeSandboxExecutor.ts
11
+ /**
12
+ * NativeSandboxExecutor - Function Tool Execution Engine
13
+ * ========================================================
14
+ *
15
+ * Executes user-defined function tools in isolated sandboxes using native Node.js processes.
16
+ * The main challenge here is that we can't just eval() user code - that's a security nightmare.
17
+ * Instead, we spin up separate Node.js processes with their own dependency trees.
18
+ *
19
+ * The tricky part is making this fast. Installing deps every time would be brutal
20
+ * (2-5s per execution), so we cache sandboxes based on their dependency fingerprint.
21
+ *
22
+ * How it works:
23
+ *
24
+ * 1. User calls a function tool
25
+ * 2. We hash the dependencies (e.g., "axios@1.6.0,lodash@4.17.21")
26
+ * 3. Check if we already have a sandbox with those deps installed
27
+ * 4. If yes: reuse it. If no: create new one, install deps, cache it
28
+ * 5. Write the user's function code to a temp file
29
+ * 6. Execute it in the sandboxed process with resource limits
30
+ * 7. Return the result
31
+ *
32
+ * Sandbox lifecycle:
33
+ * - Created when first needed for a dependency set
34
+ * - Reused up to 50 times or 5 minutes, whichever comes first
35
+ * - Automatically cleaned up when expired
36
+ * - Failed sandboxes are immediately destroyed
37
+ *
38
+ * Security stuff:
39
+ * - Each execution runs in its own process (not just a function call)
40
+ * - Output limited to 1MB to prevent memory bombs
41
+ * - Timeouts with graceful SIGTERM, then SIGKILL if needed
42
+ * - Runs as non-root when possible
43
+ * - Uses OS temp directory so it gets cleaned up automatically
44
+ *
45
+ * Performance:
46
+ * - Cold start: ~100-500ms (vs 2-5s without caching)
47
+ * - Hot path: ~50-100ms (just execution, no install)
48
+ * - Memory bounded by pool size limits
49
+ *
50
+ * Deployment notes:
51
+ * - Uses /tmp on Linux/macOS, %TEMP% on Windows
52
+ * - Works in Docker, Kubernetes, serverless (Vercel, Lambda)
53
+ * - No files left in project directory (no git pollution)
54
+ *
55
+ * The singleton pattern here is important - we need one shared pool
56
+ * across all tool executions, otherwise caching doesn't work.
57
+ */
58
+ const logger = getLogger("native-sandbox-executor");
59
+ /**
60
+ * Semaphore for limiting concurrent executions based on vCPU allocation
61
+ */
62
+ var ExecutionSemaphore = class {
63
+ permits;
64
+ waitQueue = [];
65
+ maxWaitTime;
66
+ constructor(permits, maxWaitTimeMs = FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS) {
67
+ this.permits = Math.max(1, permits);
68
+ this.maxWaitTime = maxWaitTimeMs;
69
+ }
70
+ async acquire(fn) {
71
+ await new Promise((resolve, reject) => {
72
+ if (this.permits > 0) {
73
+ this.permits--;
74
+ resolve();
75
+ return;
76
+ }
77
+ const timeoutId = setTimeout(() => {
78
+ const index = this.waitQueue.findIndex((item) => item.resolve === resolve);
79
+ if (index !== -1) {
80
+ this.waitQueue.splice(index, 1);
81
+ reject(/* @__PURE__ */ new Error(`Function execution queue timeout after ${this.maxWaitTime}ms. Too many concurrent executions.`));
82
+ }
83
+ }, this.maxWaitTime);
84
+ this.waitQueue.push({
85
+ resolve: () => {
86
+ clearTimeout(timeoutId);
87
+ this.permits--;
88
+ resolve();
89
+ },
90
+ reject
91
+ });
92
+ });
93
+ try {
94
+ return await fn();
95
+ } finally {
96
+ this.permits++;
97
+ const next = this.waitQueue.shift();
98
+ if (next) next.resolve();
99
+ }
100
+ }
101
+ getAvailablePermits() {
102
+ return this.permits;
103
+ }
104
+ getQueueLength() {
105
+ return this.waitQueue.length;
106
+ }
107
+ };
108
+ var NativeSandboxExecutor = class NativeSandboxExecutor {
109
+ tempDir;
110
+ sandboxPool = {};
111
+ static instance = null;
112
+ executionSemaphores = /* @__PURE__ */ new Map();
113
+ constructor() {
114
+ this.tempDir = join(tmpdir(), "inkeep-sandboxes");
115
+ this.ensureTempDir();
116
+ this.startPoolCleanup();
117
+ }
118
+ static getInstance() {
119
+ if (!NativeSandboxExecutor.instance) NativeSandboxExecutor.instance = new NativeSandboxExecutor();
120
+ return NativeSandboxExecutor.instance;
121
+ }
122
+ getSemaphore(vcpus) {
123
+ const effectiveVcpus = Math.max(1, vcpus || 1);
124
+ if (!this.executionSemaphores.has(effectiveVcpus)) {
125
+ logger.debug({ vcpus: effectiveVcpus }, "Creating new execution semaphore");
126
+ this.executionSemaphores.set(effectiveVcpus, new ExecutionSemaphore(effectiveVcpus));
127
+ }
128
+ const semaphore = this.executionSemaphores.get(effectiveVcpus);
129
+ if (!semaphore) throw new Error(`Failed to create semaphore for ${effectiveVcpus} vCPUs`);
130
+ return semaphore;
131
+ }
132
+ getExecutionStats() {
133
+ const stats = {};
134
+ for (const [vcpus, semaphore] of this.executionSemaphores.entries()) stats[`vcpu_${vcpus}`] = {
135
+ availablePermits: semaphore.getAvailablePermits(),
136
+ queueLength: semaphore.getQueueLength()
137
+ };
138
+ return stats;
139
+ }
140
+ ensureTempDir() {
141
+ try {
142
+ mkdirSync(this.tempDir, { recursive: true });
143
+ } catch {}
144
+ }
145
+ generateDependencyHash(dependencies) {
146
+ const sortedDeps = Object.keys(dependencies).sort().map((key) => `${key}@${dependencies[key]}`).join(",");
147
+ return createHash("sha256").update(sortedDeps).digest("hex").substring(0, 16);
148
+ }
149
+ getCachedSandbox(dependencyHash) {
150
+ const poolKey = dependencyHash;
151
+ const sandbox = this.sandboxPool[poolKey];
152
+ if (sandbox && existsSync(sandbox.sandboxDir)) {
153
+ const now = Date.now();
154
+ if (now - sandbox.lastUsed < FUNCTION_TOOL_SANDBOX_POOL_TTL_MS && sandbox.useCount < FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT) {
155
+ sandbox.lastUsed = now;
156
+ sandbox.useCount++;
157
+ logger.debug({
158
+ poolKey,
159
+ useCount: sandbox.useCount,
160
+ sandboxDir: sandbox.sandboxDir,
161
+ lastUsed: new Date(sandbox.lastUsed)
162
+ }, "Reusing cached sandbox");
163
+ return sandbox.sandboxDir;
164
+ }
165
+ this.cleanupSandbox(sandbox.sandboxDir);
166
+ delete this.sandboxPool[poolKey];
167
+ }
168
+ return null;
169
+ }
170
+ addToPool(dependencyHash, sandboxDir, dependencies) {
171
+ const poolKey = dependencyHash;
172
+ if (this.sandboxPool[poolKey]) this.cleanupSandbox(this.sandboxPool[poolKey].sandboxDir);
173
+ this.sandboxPool[poolKey] = {
174
+ sandboxDir,
175
+ lastUsed: Date.now(),
176
+ useCount: 1,
177
+ dependencies
178
+ };
179
+ logger.debug({
180
+ poolKey,
181
+ sandboxDir
182
+ }, "Added sandbox to pool");
183
+ }
184
+ cleanupSandbox(sandboxDir) {
185
+ try {
186
+ rmSync(sandboxDir, {
187
+ recursive: true,
188
+ force: true
189
+ });
190
+ logger.debug({ sandboxDir }, "Cleaned up sandbox");
191
+ } catch (error) {
192
+ logger.warn({
193
+ sandboxDir,
194
+ error
195
+ }, "Failed to clean up sandbox");
196
+ }
197
+ }
198
+ startPoolCleanup() {
199
+ setInterval(() => {
200
+ const now = Date.now();
201
+ const keysToDelete = [];
202
+ for (const [key, sandbox] of Object.entries(this.sandboxPool)) if (now - sandbox.lastUsed > FUNCTION_TOOL_SANDBOX_POOL_TTL_MS || sandbox.useCount >= FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT) {
203
+ this.cleanupSandbox(sandbox.sandboxDir);
204
+ keysToDelete.push(key);
205
+ }
206
+ keysToDelete.forEach((key) => {
207
+ delete this.sandboxPool[key];
208
+ });
209
+ if (keysToDelete.length > 0) logger.debug({ cleanedCount: keysToDelete.length }, "Cleaned up expired sandboxes");
210
+ }, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS);
211
+ }
212
+ detectModuleType(executeCode, configuredRuntime) {
213
+ const esmPatterns = [
214
+ /import\s+.*\s+from\s+['"]/g,
215
+ /import\s*\(/g,
216
+ /export\s+(default|const|let|var|function|class)/g,
217
+ /export\s*\{/g
218
+ ];
219
+ const cjsPatterns = [
220
+ /require\s*\(/g,
221
+ /module\.exports/g,
222
+ /exports\./g
223
+ ];
224
+ const hasEsmSyntax = esmPatterns.some((pattern) => pattern.test(executeCode));
225
+ const hasCjsSyntax = cjsPatterns.some((pattern) => pattern.test(executeCode));
226
+ if (configuredRuntime === "typescript") return hasCjsSyntax ? "cjs" : "esm";
227
+ if (hasEsmSyntax && hasCjsSyntax) {
228
+ logger.warn({ executeCode: `${executeCode.substring(0, 100)}...` }, "Both ESM and CommonJS syntax detected, defaulting to ESM");
229
+ return "esm";
230
+ }
231
+ if (hasEsmSyntax) return "esm";
232
+ if (hasCjsSyntax) return "cjs";
233
+ return "cjs";
234
+ }
235
+ async executeFunctionTool(toolId, args, config) {
236
+ const vcpus = config.sandboxConfig?.vcpus || 1;
237
+ const semaphore = this.getSemaphore(vcpus);
238
+ logger.debug({
239
+ toolId,
240
+ vcpus,
241
+ availablePermits: semaphore.getAvailablePermits(),
242
+ queueLength: semaphore.getQueueLength(),
243
+ sandboxConfig: config.sandboxConfig,
244
+ poolSize: Object.keys(this.sandboxPool).length
245
+ }, "Acquiring execution slot for function tool");
246
+ return semaphore.acquire(async () => {
247
+ return this.executeInSandbox_Internal(toolId, args, config);
248
+ });
249
+ }
250
+ async executeInSandbox_Internal(toolId, args, config) {
251
+ const dependencies = config.dependencies || {};
252
+ const dependencyHash = this.generateDependencyHash(dependencies);
253
+ logger.debug({
254
+ toolId,
255
+ dependencies,
256
+ dependencyHash,
257
+ sandboxConfig: config.sandboxConfig,
258
+ poolSize: Object.keys(this.sandboxPool).length
259
+ }, "Executing function tool");
260
+ let sandboxDir = this.getCachedSandbox(dependencyHash);
261
+ let isNewSandbox = false;
262
+ if (!sandboxDir) {
263
+ sandboxDir = join(this.tempDir, `sandbox-${dependencyHash}-${Date.now()}`);
264
+ mkdirSync(sandboxDir, { recursive: true });
265
+ isNewSandbox = true;
266
+ logger.debug({
267
+ toolId,
268
+ dependencyHash,
269
+ sandboxDir,
270
+ dependencies
271
+ }, "Creating new sandbox");
272
+ const moduleType = this.detectModuleType(config.executeCode, config.sandboxConfig?.runtime);
273
+ const packageJson = {
274
+ name: `function-tool-${toolId}`,
275
+ version: "1.0.0",
276
+ ...moduleType === "esm" && { type: "module" },
277
+ dependencies,
278
+ scripts: { start: moduleType === "esm" ? "node index.mjs" : "node index.js" }
279
+ };
280
+ writeFileSync(join(sandboxDir, "package.json"), JSON.stringify(packageJson, null, 2), "utf8");
281
+ if (Object.keys(dependencies).length > 0) await this.installDependencies(sandboxDir);
282
+ this.addToPool(dependencyHash, sandboxDir, dependencies);
283
+ }
284
+ try {
285
+ const moduleType = this.detectModuleType(config.executeCode, config.sandboxConfig?.runtime);
286
+ const executionCode = createExecutionWrapper(config.executeCode, args);
287
+ writeFileSync(join(sandboxDir, `index.${moduleType === "esm" ? "mjs" : "js"}`), executionCode, "utf8");
288
+ return await this.executeInSandbox(sandboxDir, config.sandboxConfig?.timeout || FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, moduleType, config.sandboxConfig);
289
+ } catch (error) {
290
+ if (isNewSandbox) {
291
+ this.cleanupSandbox(sandboxDir);
292
+ const poolKey = dependencyHash;
293
+ delete this.sandboxPool[poolKey];
294
+ }
295
+ throw error;
296
+ }
297
+ }
298
+ async installDependencies(sandboxDir) {
299
+ return new Promise((resolve, reject) => {
300
+ const npm = spawn("npm", [
301
+ "install",
302
+ "--no-audit",
303
+ "--no-fund"
304
+ ], {
305
+ cwd: sandboxDir,
306
+ stdio: "pipe",
307
+ env: {
308
+ ...process.env,
309
+ npm_config_cache: join(sandboxDir, ".npm-cache"),
310
+ npm_config_logs_dir: join(sandboxDir, ".npm-logs"),
311
+ npm_config_tmp: join(sandboxDir, ".npm-tmp"),
312
+ HOME: sandboxDir,
313
+ npm_config_update_notifier: "false",
314
+ npm_config_progress: "false",
315
+ npm_config_loglevel: "error"
316
+ }
317
+ });
318
+ let stderr = "";
319
+ npm.stdout?.on("data", () => {});
320
+ npm.stderr?.on("data", (data) => {
321
+ stderr += data.toString();
322
+ });
323
+ npm.on("close", (code) => {
324
+ if (code === 0) {
325
+ logger.debug({ sandboxDir }, "Dependencies installed successfully");
326
+ resolve();
327
+ } else {
328
+ logger.error({
329
+ sandboxDir,
330
+ code,
331
+ stderr
332
+ }, "Failed to install dependencies");
333
+ reject(/* @__PURE__ */ new Error(`npm install failed with code ${code}: ${stderr}`));
334
+ }
335
+ });
336
+ npm.on("error", (err) => {
337
+ logger.error({
338
+ sandboxDir,
339
+ error: err
340
+ }, "Failed to spawn npm install");
341
+ reject(err);
342
+ });
343
+ });
344
+ }
345
+ async executeInSandbox(sandboxDir, timeout, moduleType, _sandboxConfig) {
346
+ return new Promise((resolve, reject) => {
347
+ const fileExtension = moduleType === "esm" ? "mjs" : "js";
348
+ const spawnOptions = {
349
+ cwd: sandboxDir,
350
+ stdio: "pipe",
351
+ uid: process.getuid ? process.getuid() : void 0,
352
+ gid: process.getgid ? process.getgid() : void 0
353
+ };
354
+ const node = spawn("node", [`index.${fileExtension}`], spawnOptions);
355
+ let stdout = "";
356
+ let stderr = "";
357
+ let outputSize = 0;
358
+ node.stdout?.on("data", (data) => {
359
+ const dataStr = data.toString();
360
+ outputSize += dataStr.length;
361
+ if (outputSize > FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES) {
362
+ node.kill("SIGTERM");
363
+ reject(/* @__PURE__ */ new Error(`Output size exceeded limit of ${FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES} bytes`));
364
+ return;
365
+ }
366
+ stdout += dataStr;
367
+ });
368
+ node.stderr?.on("data", (data) => {
369
+ const dataStr = data.toString();
370
+ outputSize += dataStr.length;
371
+ if (outputSize > FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES) {
372
+ node.kill("SIGTERM");
373
+ reject(/* @__PURE__ */ new Error(`Output size exceeded limit of ${FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES} bytes`));
374
+ return;
375
+ }
376
+ stderr += dataStr;
377
+ });
378
+ const timeoutId = setTimeout(() => {
379
+ logger.warn({
380
+ sandboxDir,
381
+ timeout
382
+ }, "Function execution timed out, killing process");
383
+ node.kill("SIGTERM");
384
+ const forceKillTimeout = Math.min(Math.max(timeout / 10, 2e3), 5e3);
385
+ setTimeout(() => {
386
+ try {
387
+ node.kill("SIGKILL");
388
+ } catch {}
389
+ }, forceKillTimeout);
390
+ reject(/* @__PURE__ */ new Error(`Function execution timed out after ${timeout}ms`));
391
+ }, timeout);
392
+ node.on("close", (code, signal) => {
393
+ clearTimeout(timeoutId);
394
+ if (code === 0) try {
395
+ const result = parseExecutionResult(stdout, "function", logger);
396
+ if (typeof result === "object" && result !== null && "success" in result) {
397
+ const parsed = result;
398
+ if (parsed.success) resolve(parsed.result);
399
+ else reject(new Error(parsed.error || "Function execution failed"));
400
+ } else resolve(result);
401
+ } catch (parseError) {
402
+ logger.error({
403
+ stdout,
404
+ stderr,
405
+ parseError
406
+ }, "Failed to parse function result");
407
+ reject(/* @__PURE__ */ new Error(`Invalid function result: ${stdout}`));
408
+ }
409
+ else {
410
+ const errorMsg = signal ? `Function execution killed by signal ${signal}: ${stderr}` : `Function execution failed with code ${code}: ${stderr}`;
411
+ logger.error({
412
+ code,
413
+ signal,
414
+ stderr
415
+ }, "Function execution failed");
416
+ reject(new Error(errorMsg));
417
+ }
418
+ });
419
+ node.on("error", (error) => {
420
+ clearTimeout(timeoutId);
421
+ logger.error({
422
+ sandboxDir,
423
+ error
424
+ }, "Failed to spawn node process");
425
+ reject(error);
426
+ });
427
+ });
428
+ }
429
+ };
430
+
431
+ //#endregion
432
+ export { NativeSandboxExecutor };
@@ -0,0 +1,36 @@
1
+ import { FunctionToolConfig } from "./NativeSandboxExecutor.js";
2
+
3
+ //#region src/tools/SandboxExecutorFactory.d.ts
4
+
5
+ /**
6
+ * Factory for creating and managing sandbox executors
7
+ * Routes execution to the appropriate sandbox provider (native or Vercel)
8
+ */
9
+ declare class SandboxExecutorFactory {
10
+ private static instance;
11
+ private nativeExecutor;
12
+ private vercelExecutors;
13
+ private constructor();
14
+ /**
15
+ * Get singleton instance of SandboxExecutorFactory
16
+ */
17
+ static getInstance(): SandboxExecutorFactory;
18
+ /**
19
+ * Execute a function tool using the appropriate sandbox provider
20
+ */
21
+ executeFunctionTool(functionId: string, args: Record<string, unknown>, config: FunctionToolConfig): Promise<unknown>;
22
+ /**
23
+ * Execute in native sandbox
24
+ */
25
+ private executeInNativeSandbox;
26
+ /**
27
+ * Execute in Vercel sandbox
28
+ */
29
+ private executeInVercelSandbox;
30
+ /**
31
+ * Clean up all sandbox executors
32
+ */
33
+ cleanup(): Promise<void>;
34
+ }
35
+ //#endregion
36
+ export { SandboxExecutorFactory };
@@ -0,0 +1,80 @@
1
+ import { getLogger } from "../logger.js";
2
+ import { NativeSandboxExecutor } from "./NativeSandboxExecutor.js";
3
+ import { VercelSandboxExecutor } from "./VercelSandboxExecutor.js";
4
+
5
+ //#region src/tools/SandboxExecutorFactory.ts
6
+ const logger = getLogger("SandboxExecutorFactory");
7
+ /**
8
+ * Factory for creating and managing sandbox executors
9
+ * Routes execution to the appropriate sandbox provider (native or Vercel)
10
+ */
11
+ var SandboxExecutorFactory = class SandboxExecutorFactory {
12
+ static instance;
13
+ nativeExecutor = null;
14
+ vercelExecutors = /* @__PURE__ */ new Map();
15
+ constructor() {
16
+ logger.info({}, "SandboxExecutorFactory initialized");
17
+ }
18
+ /**
19
+ * Get singleton instance of SandboxExecutorFactory
20
+ */
21
+ static getInstance() {
22
+ if (!SandboxExecutorFactory.instance) SandboxExecutorFactory.instance = new SandboxExecutorFactory();
23
+ return SandboxExecutorFactory.instance;
24
+ }
25
+ /**
26
+ * Execute a function tool using the appropriate sandbox provider
27
+ */
28
+ async executeFunctionTool(functionId, args, config) {
29
+ const sandboxConfig = config.sandboxConfig;
30
+ if (!sandboxConfig) throw new Error("Sandbox configuration is required for function tool execution");
31
+ if (sandboxConfig.provider === "native") return this.executeInNativeSandbox(functionId, args, config);
32
+ if (sandboxConfig.provider === "vercel") return this.executeInVercelSandbox(functionId, args, config);
33
+ throw new Error(`Unknown sandbox provider: ${sandboxConfig.provider}`);
34
+ }
35
+ /**
36
+ * Execute in native sandbox
37
+ */
38
+ async executeInNativeSandbox(functionId, args, config) {
39
+ if (!this.nativeExecutor) {
40
+ this.nativeExecutor = NativeSandboxExecutor.getInstance();
41
+ logger.info({}, "Native sandbox executor created");
42
+ }
43
+ return this.nativeExecutor.executeFunctionTool(functionId, args, config);
44
+ }
45
+ /**
46
+ * Execute in Vercel sandbox
47
+ */
48
+ async executeInVercelSandbox(functionId, args, config) {
49
+ const vercelConfig = config.sandboxConfig;
50
+ const configKey = `${vercelConfig.teamId}:${vercelConfig.projectId}`;
51
+ if (!this.vercelExecutors.has(configKey)) {
52
+ const executor$1 = VercelSandboxExecutor.getInstance(vercelConfig);
53
+ this.vercelExecutors.set(configKey, executor$1);
54
+ logger.info({
55
+ teamId: vercelConfig.teamId,
56
+ projectId: vercelConfig.projectId
57
+ }, "Vercel sandbox executor created");
58
+ }
59
+ const executor = this.vercelExecutors.get(configKey);
60
+ if (!executor) throw new Error(`Failed to get Vercel executor for config: ${configKey}`);
61
+ const result = await executor.executeFunctionTool(functionId, args, config);
62
+ if (!result.success) throw new Error(result.error || "Vercel sandbox execution failed");
63
+ return result.result;
64
+ }
65
+ /**
66
+ * Clean up all sandbox executors
67
+ */
68
+ async cleanup() {
69
+ logger.info({}, "Cleaning up sandbox executors");
70
+ this.nativeExecutor = null;
71
+ for (const [key, executor] of this.vercelExecutors.entries()) {
72
+ await executor.cleanup();
73
+ this.vercelExecutors.delete(key);
74
+ }
75
+ logger.info({}, "Sandbox executor cleanup completed");
76
+ }
77
+ };
78
+
79
+ //#endregion
80
+ export { SandboxExecutorFactory };
@@ -0,0 +1,71 @@
1
+ import { VercelSandboxConfig } from "../types/execution-context.js";
2
+ import { FunctionToolConfig } from "./NativeSandboxExecutor.js";
3
+
4
+ //#region src/tools/VercelSandboxExecutor.d.ts
5
+ interface ExecutionResult {
6
+ success: boolean;
7
+ result?: unknown;
8
+ error?: string;
9
+ logs?: string[];
10
+ executionTime?: number;
11
+ }
12
+ /**
13
+ * Vercel Sandbox Executor with pooling/reuse
14
+ * Executes function tools in isolated Vercel Sandbox MicroVMs
15
+ * Caches and reuses sandboxes based on dependencies to improve performance
16
+ */
17
+ declare class VercelSandboxExecutor {
18
+ private static instance;
19
+ private config;
20
+ private sandboxPool;
21
+ private cleanupInterval;
22
+ private constructor();
23
+ /**
24
+ * Get singleton instance of VercelSandboxExecutor
25
+ */
26
+ static getInstance(config: VercelSandboxConfig): VercelSandboxExecutor;
27
+ /**
28
+ * Generate a hash for dependencies to use as cache key
29
+ */
30
+ private generateDependencyHash;
31
+ /**
32
+ * Get a cached sandbox if available and still valid
33
+ */
34
+ private getCachedSandbox;
35
+ /**
36
+ * Add sandbox to pool
37
+ */
38
+ private addToPool;
39
+ /**
40
+ * Increment use count for a sandbox
41
+ */
42
+ private incrementUseCount;
43
+ /**
44
+ * Remove and clean up a sandbox
45
+ */
46
+ private removeSandbox;
47
+ /**
48
+ * Start periodic cleanup of expired sandboxes
49
+ */
50
+ private startPoolCleanup;
51
+ /**
52
+ * Cleanup all sandboxes and stop cleanup interval
53
+ */
54
+ cleanup(): Promise<void>;
55
+ /**
56
+ * Extract environment variable names from code
57
+ * Matches patterns like process.env.VAR_NAME or process.env['VAR_NAME']
58
+ */
59
+ private extractEnvVars;
60
+ /**
61
+ * Create .env file content from environment variables
62
+ * Note: Currently creates empty placeholders. Values will be populated in the future.
63
+ */
64
+ private createEnvFileContent;
65
+ /**
66
+ * Execute a function tool in Vercel Sandbox with pooling
67
+ */
68
+ executeFunctionTool(functionId: string, args: Record<string, unknown>, toolConfig: FunctionToolConfig): Promise<ExecutionResult>;
69
+ }
70
+ //#endregion
71
+ export { ExecutionResult, VercelSandboxExecutor };