@inkeep/agents-run-api 0.18.0 → 0.18.1

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.
@@ -7,6 +7,58 @@ import { tmpdir } from 'os';
7
7
  import { join } from 'path';
8
8
 
9
9
  var logger = getLogger("local-sandbox-executor");
10
+ var ExecutionSemaphore = class {
11
+ constructor(permits, maxWaitTimeMs = 3e4) {
12
+ __publicField(this, "permits");
13
+ __publicField(this, "waitQueue", []);
14
+ __publicField(this, "maxWaitTime");
15
+ this.permits = Math.max(1, permits);
16
+ this.maxWaitTime = maxWaitTimeMs;
17
+ }
18
+ async acquire(fn) {
19
+ await new Promise((resolve, reject) => {
20
+ if (this.permits > 0) {
21
+ this.permits--;
22
+ resolve();
23
+ return;
24
+ }
25
+ const timeoutId = setTimeout(() => {
26
+ const index = this.waitQueue.findIndex((item) => item.resolve === resolve);
27
+ if (index !== -1) {
28
+ this.waitQueue.splice(index, 1);
29
+ reject(
30
+ new Error(
31
+ `Function execution queue timeout after ${this.maxWaitTime}ms. Too many concurrent executions.`
32
+ )
33
+ );
34
+ }
35
+ }, this.maxWaitTime);
36
+ this.waitQueue.push({
37
+ resolve: () => {
38
+ clearTimeout(timeoutId);
39
+ this.permits--;
40
+ resolve();
41
+ },
42
+ reject
43
+ });
44
+ });
45
+ try {
46
+ return await fn();
47
+ } finally {
48
+ this.permits++;
49
+ const next = this.waitQueue.shift();
50
+ if (next) {
51
+ next.resolve();
52
+ }
53
+ }
54
+ }
55
+ getAvailablePermits() {
56
+ return this.permits;
57
+ }
58
+ getQueueLength() {
59
+ return this.waitQueue.length;
60
+ }
61
+ };
10
62
  var _LocalSandboxExecutor = class _LocalSandboxExecutor {
11
63
  constructor() {
12
64
  __publicField(this, "tempDir");
@@ -14,6 +66,7 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
14
66
  __publicField(this, "POOL_TTL", 5 * 60 * 1e3);
15
67
  // 5 minutes
16
68
  __publicField(this, "MAX_USE_COUNT", 50);
69
+ __publicField(this, "executionSemaphores", /* @__PURE__ */ new Map());
17
70
  this.tempDir = join(tmpdir(), "inkeep-sandboxes");
18
71
  this.ensureTempDir();
19
72
  this.startPoolCleanup();
@@ -24,6 +77,34 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
24
77
  }
25
78
  return _LocalSandboxExecutor.instance;
26
79
  }
80
+ /**
81
+ * Get or create a semaphore for the specified vCPU limit
82
+ */
83
+ getSemaphore(vcpus) {
84
+ const effectiveVcpus = Math.max(1, vcpus || 1);
85
+ if (!this.executionSemaphores.has(effectiveVcpus)) {
86
+ logger.debug({ vcpus: effectiveVcpus }, "Creating new execution semaphore");
87
+ this.executionSemaphores.set(effectiveVcpus, new ExecutionSemaphore(effectiveVcpus));
88
+ }
89
+ const semaphore = this.executionSemaphores.get(effectiveVcpus);
90
+ if (!semaphore) {
91
+ throw new Error(`Failed to create semaphore for ${effectiveVcpus} vCPUs`);
92
+ }
93
+ return semaphore;
94
+ }
95
+ /**
96
+ * Get execution statistics for monitoring and debugging
97
+ */
98
+ getExecutionStats() {
99
+ const stats = {};
100
+ for (const [vcpus, semaphore] of this.executionSemaphores.entries()) {
101
+ stats[`vcpu_${vcpus}`] = {
102
+ availablePermits: semaphore.getAvailablePermits(),
103
+ queueLength: semaphore.getQueueLength()
104
+ };
105
+ }
106
+ return stats;
107
+ }
27
108
  ensureTempDir() {
28
109
  try {
29
110
  mkdirSync(this.tempDir, { recursive: true });
@@ -98,7 +179,7 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
98
179
  }
99
180
  }, 6e4);
100
181
  }
101
- detectModuleType(executeCode) {
182
+ detectModuleType(executeCode, configuredRuntime) {
102
183
  const esmPatterns = [
103
184
  /import\s+.*\s+from\s+['"]/g,
104
185
  // import ... from '...'
@@ -119,6 +200,9 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
119
200
  ];
120
201
  const hasEsmSyntax = esmPatterns.some((pattern) => pattern.test(executeCode));
121
202
  const hasCjsSyntax = cjsPatterns.some((pattern) => pattern.test(executeCode));
203
+ if (configuredRuntime === "typescript") {
204
+ return hasCjsSyntax ? "cjs" : "esm";
205
+ }
122
206
  if (hasEsmSyntax && hasCjsSyntax) {
123
207
  logger.warn(
124
208
  { executeCode: `${executeCode.substring(0, 100)}...` },
@@ -135,6 +219,24 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
135
219
  return "cjs";
136
220
  }
137
221
  async executeFunctionTool(toolId, args, config) {
222
+ const vcpus = config.sandboxConfig?.vcpus || 1;
223
+ const semaphore = this.getSemaphore(vcpus);
224
+ logger.debug(
225
+ {
226
+ toolId,
227
+ vcpus,
228
+ availablePermits: semaphore.getAvailablePermits(),
229
+ queueLength: semaphore.getQueueLength(),
230
+ sandboxConfig: config.sandboxConfig,
231
+ poolSize: Object.keys(this.sandboxPool).length
232
+ },
233
+ "Acquiring execution slot for function tool"
234
+ );
235
+ return semaphore.acquire(async () => {
236
+ return this.executeInSandbox_Internal(toolId, args, config);
237
+ });
238
+ }
239
+ async executeInSandbox_Internal(toolId, args, config) {
138
240
  const dependencies = config.dependencies || {};
139
241
  const dependencyHash = this.generateDependencyHash(dependencies);
140
242
  logger.debug(
@@ -142,6 +244,7 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
142
244
  toolId,
143
245
  dependencies,
144
246
  dependencyHash,
247
+ sandboxConfig: config.sandboxConfig,
145
248
  poolSize: Object.keys(this.sandboxPool).length
146
249
  },
147
250
  "Executing function tool"
@@ -161,7 +264,7 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
161
264
  },
162
265
  "Creating new sandbox"
163
266
  );
164
- const moduleType = this.detectModuleType(config.executeCode);
267
+ const moduleType = this.detectModuleType(config.executeCode, config.sandboxConfig?.runtime);
165
268
  const packageJson = {
166
269
  name: `function-tool-${toolId}`,
167
270
  version: "1.0.0",
@@ -178,14 +281,15 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
178
281
  this.addToPool(dependencyHash, sandboxDir, dependencies);
179
282
  }
180
283
  try {
181
- const moduleType = this.detectModuleType(config.executeCode);
284
+ const moduleType = this.detectModuleType(config.executeCode, config.sandboxConfig?.runtime);
182
285
  const executionCode = this.wrapFunctionCode(config.executeCode, args);
183
286
  const fileExtension = moduleType === "esm" ? "mjs" : "js";
184
287
  writeFileSync(join(sandboxDir, `index.${fileExtension}`), executionCode, "utf8");
185
288
  const result = await this.executeInSandbox(
186
289
  sandboxDir,
187
290
  config.sandboxConfig?.timeout || 3e4,
188
- moduleType
291
+ moduleType,
292
+ config.sandboxConfig
189
293
  );
190
294
  return result;
191
295
  } catch (error) {
@@ -224,7 +328,7 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
224
328
  });
225
329
  });
226
330
  }
227
- async executeInSandbox(sandboxDir, timeout, moduleType) {
331
+ async executeInSandbox(sandboxDir, timeout, moduleType, _sandboxConfig) {
228
332
  return new Promise((resolve, reject) => {
229
333
  const fileExtension = moduleType === "esm" ? "mjs" : "js";
230
334
  const spawnOptions = {
@@ -262,12 +366,13 @@ var _LocalSandboxExecutor = class _LocalSandboxExecutor {
262
366
  const timeoutId = setTimeout(() => {
263
367
  logger.warn({ sandboxDir, timeout }, "Function execution timed out, killing process");
264
368
  node.kill("SIGTERM");
369
+ const forceKillTimeout = Math.min(Math.max(timeout / 10, 2e3), 5e3);
265
370
  setTimeout(() => {
266
371
  try {
267
372
  node.kill("SIGKILL");
268
373
  } catch {
269
374
  }
270
- }, 5e3);
375
+ }, forceKillTimeout);
271
376
  reject(new Error(`Function execution timed out after ${timeout}ms`));
272
377
  }, timeout);
273
378
  node.on("close", (code, signal) => {
package/dist/index.cjs CHANGED
@@ -389,10 +389,62 @@ var LocalSandboxExecutor_exports = {};
389
389
  __export(LocalSandboxExecutor_exports, {
390
390
  LocalSandboxExecutor: () => LocalSandboxExecutor
391
391
  });
392
- var logger17, _LocalSandboxExecutor, LocalSandboxExecutor;
392
+ var logger17, ExecutionSemaphore, _LocalSandboxExecutor, LocalSandboxExecutor;
393
393
  var init_LocalSandboxExecutor = __esm({
394
394
  "src/tools/LocalSandboxExecutor.ts"() {
395
395
  logger17 = agentsCore.getLogger("local-sandbox-executor");
396
+ ExecutionSemaphore = class {
397
+ constructor(permits, maxWaitTimeMs = 3e4) {
398
+ __publicField(this, "permits");
399
+ __publicField(this, "waitQueue", []);
400
+ __publicField(this, "maxWaitTime");
401
+ this.permits = Math.max(1, permits);
402
+ this.maxWaitTime = maxWaitTimeMs;
403
+ }
404
+ async acquire(fn) {
405
+ await new Promise((resolve, reject) => {
406
+ if (this.permits > 0) {
407
+ this.permits--;
408
+ resolve();
409
+ return;
410
+ }
411
+ const timeoutId = setTimeout(() => {
412
+ const index = this.waitQueue.findIndex((item) => item.resolve === resolve);
413
+ if (index !== -1) {
414
+ this.waitQueue.splice(index, 1);
415
+ reject(
416
+ new Error(
417
+ `Function execution queue timeout after ${this.maxWaitTime}ms. Too many concurrent executions.`
418
+ )
419
+ );
420
+ }
421
+ }, this.maxWaitTime);
422
+ this.waitQueue.push({
423
+ resolve: () => {
424
+ clearTimeout(timeoutId);
425
+ this.permits--;
426
+ resolve();
427
+ },
428
+ reject
429
+ });
430
+ });
431
+ try {
432
+ return await fn();
433
+ } finally {
434
+ this.permits++;
435
+ const next = this.waitQueue.shift();
436
+ if (next) {
437
+ next.resolve();
438
+ }
439
+ }
440
+ }
441
+ getAvailablePermits() {
442
+ return this.permits;
443
+ }
444
+ getQueueLength() {
445
+ return this.waitQueue.length;
446
+ }
447
+ };
396
448
  _LocalSandboxExecutor = class _LocalSandboxExecutor {
397
449
  constructor() {
398
450
  __publicField(this, "tempDir");
@@ -400,6 +452,7 @@ var init_LocalSandboxExecutor = __esm({
400
452
  __publicField(this, "POOL_TTL", 5 * 60 * 1e3);
401
453
  // 5 minutes
402
454
  __publicField(this, "MAX_USE_COUNT", 50);
455
+ __publicField(this, "executionSemaphores", /* @__PURE__ */ new Map());
403
456
  this.tempDir = path.join(os.tmpdir(), "inkeep-sandboxes");
404
457
  this.ensureTempDir();
405
458
  this.startPoolCleanup();
@@ -410,6 +463,34 @@ var init_LocalSandboxExecutor = __esm({
410
463
  }
411
464
  return _LocalSandboxExecutor.instance;
412
465
  }
466
+ /**
467
+ * Get or create a semaphore for the specified vCPU limit
468
+ */
469
+ getSemaphore(vcpus) {
470
+ const effectiveVcpus = Math.max(1, vcpus || 1);
471
+ if (!this.executionSemaphores.has(effectiveVcpus)) {
472
+ logger17.debug({ vcpus: effectiveVcpus }, "Creating new execution semaphore");
473
+ this.executionSemaphores.set(effectiveVcpus, new ExecutionSemaphore(effectiveVcpus));
474
+ }
475
+ const semaphore = this.executionSemaphores.get(effectiveVcpus);
476
+ if (!semaphore) {
477
+ throw new Error(`Failed to create semaphore for ${effectiveVcpus} vCPUs`);
478
+ }
479
+ return semaphore;
480
+ }
481
+ /**
482
+ * Get execution statistics for monitoring and debugging
483
+ */
484
+ getExecutionStats() {
485
+ const stats = {};
486
+ for (const [vcpus, semaphore] of this.executionSemaphores.entries()) {
487
+ stats[`vcpu_${vcpus}`] = {
488
+ availablePermits: semaphore.getAvailablePermits(),
489
+ queueLength: semaphore.getQueueLength()
490
+ };
491
+ }
492
+ return stats;
493
+ }
413
494
  ensureTempDir() {
414
495
  try {
415
496
  fs.mkdirSync(this.tempDir, { recursive: true });
@@ -484,7 +565,7 @@ var init_LocalSandboxExecutor = __esm({
484
565
  }
485
566
  }, 6e4);
486
567
  }
487
- detectModuleType(executeCode) {
568
+ detectModuleType(executeCode, configuredRuntime) {
488
569
  const esmPatterns = [
489
570
  /import\s+.*\s+from\s+['"]/g,
490
571
  // import ... from '...'
@@ -505,6 +586,9 @@ var init_LocalSandboxExecutor = __esm({
505
586
  ];
506
587
  const hasEsmSyntax = esmPatterns.some((pattern) => pattern.test(executeCode));
507
588
  const hasCjsSyntax = cjsPatterns.some((pattern) => pattern.test(executeCode));
589
+ if (configuredRuntime === "typescript") {
590
+ return hasCjsSyntax ? "cjs" : "esm";
591
+ }
508
592
  if (hasEsmSyntax && hasCjsSyntax) {
509
593
  logger17.warn(
510
594
  { executeCode: `${executeCode.substring(0, 100)}...` },
@@ -521,6 +605,24 @@ var init_LocalSandboxExecutor = __esm({
521
605
  return "cjs";
522
606
  }
523
607
  async executeFunctionTool(toolId, args, config) {
608
+ const vcpus = config.sandboxConfig?.vcpus || 1;
609
+ const semaphore = this.getSemaphore(vcpus);
610
+ logger17.debug(
611
+ {
612
+ toolId,
613
+ vcpus,
614
+ availablePermits: semaphore.getAvailablePermits(),
615
+ queueLength: semaphore.getQueueLength(),
616
+ sandboxConfig: config.sandboxConfig,
617
+ poolSize: Object.keys(this.sandboxPool).length
618
+ },
619
+ "Acquiring execution slot for function tool"
620
+ );
621
+ return semaphore.acquire(async () => {
622
+ return this.executeInSandbox_Internal(toolId, args, config);
623
+ });
624
+ }
625
+ async executeInSandbox_Internal(toolId, args, config) {
524
626
  const dependencies = config.dependencies || {};
525
627
  const dependencyHash = this.generateDependencyHash(dependencies);
526
628
  logger17.debug(
@@ -528,6 +630,7 @@ var init_LocalSandboxExecutor = __esm({
528
630
  toolId,
529
631
  dependencies,
530
632
  dependencyHash,
633
+ sandboxConfig: config.sandboxConfig,
531
634
  poolSize: Object.keys(this.sandboxPool).length
532
635
  },
533
636
  "Executing function tool"
@@ -547,7 +650,7 @@ var init_LocalSandboxExecutor = __esm({
547
650
  },
548
651
  "Creating new sandbox"
549
652
  );
550
- const moduleType = this.detectModuleType(config.executeCode);
653
+ const moduleType = this.detectModuleType(config.executeCode, config.sandboxConfig?.runtime);
551
654
  const packageJson = {
552
655
  name: `function-tool-${toolId}`,
553
656
  version: "1.0.0",
@@ -564,14 +667,15 @@ var init_LocalSandboxExecutor = __esm({
564
667
  this.addToPool(dependencyHash, sandboxDir, dependencies);
565
668
  }
566
669
  try {
567
- const moduleType = this.detectModuleType(config.executeCode);
670
+ const moduleType = this.detectModuleType(config.executeCode, config.sandboxConfig?.runtime);
568
671
  const executionCode = this.wrapFunctionCode(config.executeCode, args);
569
672
  const fileExtension = moduleType === "esm" ? "mjs" : "js";
570
673
  fs.writeFileSync(path.join(sandboxDir, `index.${fileExtension}`), executionCode, "utf8");
571
674
  const result = await this.executeInSandbox(
572
675
  sandboxDir,
573
676
  config.sandboxConfig?.timeout || 3e4,
574
- moduleType
677
+ moduleType,
678
+ config.sandboxConfig
575
679
  );
576
680
  return result;
577
681
  } catch (error) {
@@ -610,7 +714,7 @@ var init_LocalSandboxExecutor = __esm({
610
714
  });
611
715
  });
612
716
  }
613
- async executeInSandbox(sandboxDir, timeout, moduleType) {
717
+ async executeInSandbox(sandboxDir, timeout, moduleType, _sandboxConfig) {
614
718
  return new Promise((resolve, reject) => {
615
719
  const fileExtension = moduleType === "esm" ? "mjs" : "js";
616
720
  const spawnOptions = {
@@ -648,12 +752,13 @@ var init_LocalSandboxExecutor = __esm({
648
752
  const timeoutId = setTimeout(() => {
649
753
  logger17.warn({ sandboxDir, timeout }, "Function execution timed out, killing process");
650
754
  node.kill("SIGTERM");
755
+ const forceKillTimeout = Math.min(Math.max(timeout / 10, 2e3), 5e3);
651
756
  setTimeout(() => {
652
757
  try {
653
758
  node.kill("SIGKILL");
654
759
  } catch {
655
760
  }
656
- }, 5e3);
761
+ }, forceKillTimeout);
657
762
  reject(new Error(`Function execution timed out after ${timeout}ms`));
658
763
  }, timeout);
659
764
  node.on("close", (code, signal) => {
@@ -8001,11 +8106,21 @@ var Agent = class {
8001
8106
  "Function Tool Called"
8002
8107
  );
8003
8108
  try {
8109
+ const project = await agentsCore.getProject(dbClient_default)({
8110
+ scopes: { tenantId: this.config.tenantId, projectId: this.config.projectId }
8111
+ });
8112
+ const defaultSandboxConfig = {
8113
+ provider: "local",
8114
+ runtime: "node22",
8115
+ timeout: 3e4,
8116
+ vcpus: 1
8117
+ };
8004
8118
  const result = await sandboxExecutor.executeFunctionTool(functionToolDef.id, args, {
8005
8119
  description: functionToolDef.description || functionToolDef.name,
8006
8120
  inputSchema: functionData.inputSchema || {},
8007
8121
  executeCode: functionData.executeCode,
8008
- dependencies: functionData.dependencies || {}
8122
+ dependencies: functionData.dependencies || {},
8123
+ sandboxConfig: project?.sandboxConfig || defaultSandboxConfig
8009
8124
  });
8010
8125
  toolSessionManager.recordToolResult(sessionId || "", {
8011
8126
  toolCallId,
package/dist/index.js CHANGED
@@ -7129,7 +7129,7 @@ var Agent = class {
7129
7129
  if (functionToolsData.length === 0) {
7130
7130
  return functionTools;
7131
7131
  }
7132
- const { LocalSandboxExecutor } = await import('./LocalSandboxExecutor-PQIRECLQ.js');
7132
+ const { LocalSandboxExecutor } = await import('./LocalSandboxExecutor-2UQ32ZZH.js');
7133
7133
  const sandboxExecutor = LocalSandboxExecutor.getInstance();
7134
7134
  for (const functionToolDef of functionToolsData) {
7135
7135
  const functionId = functionToolDef.functionId;
@@ -7164,11 +7164,21 @@ var Agent = class {
7164
7164
  "Function Tool Called"
7165
7165
  );
7166
7166
  try {
7167
+ const project = await getProject(dbClient_default)({
7168
+ scopes: { tenantId: this.config.tenantId, projectId: this.config.projectId }
7169
+ });
7170
+ const defaultSandboxConfig = {
7171
+ provider: "local",
7172
+ runtime: "node22",
7173
+ timeout: 3e4,
7174
+ vcpus: 1
7175
+ };
7167
7176
  const result = await sandboxExecutor.executeFunctionTool(functionToolDef.id, args, {
7168
7177
  description: functionToolDef.description || functionToolDef.name,
7169
7178
  inputSchema: functionData.inputSchema || {},
7170
7179
  executeCode: functionData.executeCode,
7171
- dependencies: functionData.dependencies || {}
7180
+ dependencies: functionData.dependencies || {},
7181
+ sandboxConfig: project?.sandboxConfig || defaultSandboxConfig
7172
7182
  });
7173
7183
  toolSessionManager.recordToolResult(sessionId || "", {
7174
7184
  toolCallId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-run-api",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "Agents Run API for Inkeep Agent Framework - handles chat, agent execution, and streaming",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -51,7 +51,7 @@
51
51
  "traverse": "^0.6.11",
52
52
  "ts-pattern": "^5.7.1",
53
53
  "zod": "^4.1.11",
54
- "@inkeep/agents-core": "^0.18.0"
54
+ "@inkeep/agents-core": "^0.18.1"
55
55
  },
56
56
  "optionalDependencies": {
57
57
  "keytar": "^7.9.0"