@frontmcp/plugin-codecall 1.8.3 → 1.8.4

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.
@@ -62,6 +62,9 @@ export interface CodeCallVmEnvironment {
62
62
  * @param options - Optional behavior configuration
63
63
  * @param options.throwOnError - When true (default), throws on error.
64
64
  * When false, returns { success, data, error }.
65
+ * The sandbox calls it without options: it applies a script's
66
+ * `{ throwOnError: false }` itself, for `callTool()` and
67
+ * namespace methods alike, so it needs the throw.
65
68
  *
66
69
  * SECURITY NOTES:
67
70
  * - Cannot call 'codecall:*' tools (self-reference blocked)
@@ -83,12 +86,11 @@ export interface CodeCallVmEnvironment {
83
86
  /**
84
87
  * Optional dotted-tool-name namespaces, as data: `{ acme: { getUser: 'acme.getUser' } }`.
85
88
  *
86
- * The enclave service writes them into the script as AgentScript objects whose methods
87
- * call `callTool('acme.getUser', input)` inside the sandbox, so AgentScript can call
88
- * `await acme.getUser({...})` and every such call still passes the sandbox's tool-call
89
- * cap, rate limit and suspicious-sequence checks (they are never host functions). Tools
90
- * whose names cannot be expressed as `{validIdent}.{validIdent}` remain reachable only
91
- * via `callTool`.
89
+ * The enclave service hands them to the sandbox as its `toolNamespaces`, which builds the
90
+ * objects itself, so AgentScript can call `await acme.getUser({...})` and every such call is a
91
+ * `callTool('acme.getUser', …)` inside the sandbox: it passes the tool-call cap, rate limit
92
+ * and suspicious-sequence checks (they are never host functions). Tools whose names the
93
+ * sandbox can't bind remain reachable only via `callTool`.
92
94
  */
93
95
  toolNamespaces?: Record<string, Record<string, string>>;
94
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"codecall.symbol.d.ts","sourceRoot":"","sources":["../src/codecall.symbol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codecall.symbol.d.ts","sourceRoot":"","sources":["../src/codecall.symbol.ts"],"names":[],"mappings":"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"}
package/esm/index.mjs CHANGED
@@ -1733,7 +1733,9 @@ var ToolSearchService = class _ToolSearchService {
1733
1733
  };
1734
1734
 
1735
1735
  // plugins/plugin-codecall/src/services/enclave.service.ts
1736
- import { Enclave } from "@enclave-vm/core";
1736
+ import {
1737
+ Enclave
1738
+ } from "@enclave-vm/core";
1737
1739
  import { Provider as Provider3, ProviderScope as ProviderScope3 } from "@frontmcp/sdk";
1738
1740
 
1739
1741
  // plugins/plugin-codecall/src/utils/build-tool-namespaces.ts
@@ -1784,9 +1786,29 @@ var UNBINDABLE_NAMESPACES = new Set(
1784
1786
  "localStorage sessionStorage indexedDB crypto performance structuredClone AbortController AbortSignal",
1785
1787
  "MessageChannel MessagePort BroadcastChannel TextEncoder TextDecoder Intl setTimeout setInterval",
1786
1788
  "setImmediate clearTimeout clearInterval clearImmediate queueMicrotask WebAssembly Worker SharedWorker",
1787
- "ServiceWorker WeakRef FinalizationRegistry BigInt Atomics SharedArrayBuffer ArrayBuffer DataView"
1789
+ "ServiceWorker WeakRef FinalizationRegistry BigInt Atomics SharedArrayBuffer ArrayBuffer DataView",
1790
+ "importScripts ShadowRealm Iterator AsyncIterator"
1791
+ ].join(" ").split(" ")
1792
+ );
1793
+ var UNCALLABLE_METHODS = new Set(
1794
+ [
1795
+ "process require module exports Buffer eval Function AsyncFunction GeneratorFunction arguments RegExp",
1796
+ "Promise Symbol Proxy Reflect Error TypeError ReferenceError SyntaxError RangeError URIError EvalError",
1797
+ "AggregateError fetch XMLHttpRequest WebSocket localStorage sessionStorage indexedDB crypto performance",
1798
+ "structuredClone AbortController AbortSignal MessageChannel MessagePort BroadcastChannel TextEncoder",
1799
+ "TextDecoder Intl setTimeout setInterval setImmediate clearTimeout clearInterval clearImmediate",
1800
+ "queueMicrotask WebAssembly Worker SharedWorker ServiceWorker WeakMap WeakSet WeakRef FinalizationRegistry",
1801
+ "Map Set Atomics SharedArrayBuffer importScripts ShadowRealm Iterator AsyncIterator"
1788
1802
  ].join(" ").split(" ")
1789
1803
  );
1804
+ var SANDBOX_TOOL_NAME_RE = /^[a-zA-Z][a-zA-Z0-9:._-]*$/;
1805
+ var MAX_SANDBOX_TOOL_NAME_LENGTH = 256;
1806
+ function isBindableNamespace(ns, reservedGlobals) {
1807
+ return IDENT_RE.test(ns) && !PROTOTYPE_KEYS.has(ns) && !ns.startsWith("__") && !RESERVED_NAMESPACES.has(ns) && !UNBINDABLE_NAMESPACES.has(ns) && !reservedGlobals.has(ns);
1808
+ }
1809
+ function isBindableMethod(ns, method, toolName) {
1810
+ return IDENT_RE.test(method) && !PROTOTYPE_KEYS.has(method) && !method.startsWith("__") && !UNCALLABLE_METHODS.has(method) && toolName === `${ns}.${method}` && toolName.length <= MAX_SANDBOX_TOOL_NAME_LENGTH && SANDBOX_TOOL_NAME_RE.test(toolName);
1811
+ }
1790
1812
  function buildToolNamespaces(tools) {
1791
1813
  const namespaces = /* @__PURE__ */ Object.create(null);
1792
1814
  const skipped = [];
@@ -1812,6 +1834,10 @@ function buildToolNamespaces(tools) {
1812
1834
  skipped.push({ name, reason: "reserved-namespace" });
1813
1835
  continue;
1814
1836
  }
1837
+ if (!isBindableMethod(ns, method, name)) {
1838
+ skipped.push({ name, reason: "unsupported-name" });
1839
+ continue;
1840
+ }
1815
1841
  const bucket = namespaces[ns] ?? (namespaces[ns] = /* @__PURE__ */ Object.create(null));
1816
1842
  if (Object.prototype.hasOwnProperty.call(bucket, method)) {
1817
1843
  skipped.push({ name, reason: "duplicate-method" });
@@ -1821,49 +1847,49 @@ function buildToolNamespaces(tools) {
1821
1847
  }
1822
1848
  return { namespaces, skipped };
1823
1849
  }
1824
- var NAMESPACE_CALL_HELPER = "__codecallNamespaceCall";
1825
- var NAMESPACE_CALL_HELPER_SOURCE = `const ${NAMESPACE_CALL_HELPER} = async (toolName, run, options) => { if (options && options.throwOnError === false) { try { return { success: true, data: await run() }; } catch (error) { return { success: false, error: { message: error && error.message, toolName: toolName } }; } } return await run(); };`;
1826
- function mentionsIdentifier(script, identifier) {
1827
- let from = 0;
1828
- for (; ; ) {
1829
- const at = script.indexOf(identifier, from);
1830
- if (at === -1) return false;
1831
- from = at + 1;
1832
- const before = at > 0 ? script[at - 1] : "";
1833
- const after = script[at + identifier.length] ?? "";
1834
- if (/[A-Za-z0-9_$'"`]/.test(before) || /[A-Za-z0-9_$]/.test(after)) continue;
1835
- if (before === "." && script.slice(Math.max(0, at - 3), at) !== "...") continue;
1836
- return true;
1837
- }
1838
- }
1839
- function wrapScriptWithToolNamespaces(script, namespaces) {
1840
- if (!namespaces) return script;
1841
- const declarations = [];
1850
+ function toSandboxToolNamespaces(namespaces, reservedGlobals = []) {
1851
+ if (!namespaces) return void 0;
1852
+ const reserved = new Set(reservedGlobals);
1853
+ const bindable = /* @__PURE__ */ Object.create(null);
1854
+ let count = 0;
1842
1855
  for (const ns of Object.keys(namespaces)) {
1843
- if (!IDENT_RE.test(ns) || RESERVED_NAMESPACES.has(ns) || UNBINDABLE_NAMESPACES.has(ns) || ns.startsWith("__")) {
1844
- continue;
1845
- }
1846
- if (PROTOTYPE_KEYS.has(ns) || !mentionsIdentifier(script, ns)) continue;
1856
+ if (!isBindableNamespace(ns, reserved)) continue;
1847
1857
  const methods = namespaces[ns];
1848
- const entries = [];
1858
+ const bucket = /* @__PURE__ */ Object.create(null);
1859
+ let methodCount = 0;
1849
1860
  for (const method of Object.keys(methods)) {
1850
1861
  const toolName = methods[method];
1851
- if (!IDENT_RE.test(method) || PROTOTYPE_KEYS.has(method) || toolName !== `${ns}.${method}`) continue;
1852
- const literal = JSON.stringify(toolName);
1853
- entries.push(
1854
- `${JSON.stringify(method)}: (input, options) => ${NAMESPACE_CALL_HELPER}(${literal}, () => callTool(${literal}, input === undefined ? {} : input), options)`
1855
- );
1862
+ if (typeof toolName !== "string" || !isBindableMethod(ns, method, toolName)) continue;
1863
+ bucket[method] = toolName;
1864
+ methodCount += 1;
1856
1865
  }
1857
- if (entries.length > 0) {
1858
- declarations.push(`const ${ns} = { ${entries.join(", ")} };`);
1866
+ if (methodCount > 0) {
1867
+ bindable[ns] = bucket;
1868
+ count += 1;
1859
1869
  }
1860
1870
  }
1861
- if (declarations.length === 0) return script;
1862
- return `${NAMESPACE_CALL_HELPER_SOURCE} ${declarations.join(" ")} return await (async () => {${script}
1863
- })();`;
1871
+ return count > 0 ? bindable : void 0;
1864
1872
  }
1865
1873
 
1866
1874
  // plugins/plugin-codecall/src/services/enclave.service.ts
1875
+ function toolFailureMessage(error, toolName) {
1876
+ if (typeof error === "string" && error) return error;
1877
+ const message = error?.message;
1878
+ return typeof message === "string" && message ? message : `Tool call failed: ${toolName}`;
1879
+ }
1880
+ function toolFailureName(error) {
1881
+ const name = error && typeof error === "object" ? error.name : void 0;
1882
+ return typeof name === "string" && name ? name.slice(0, 128) : "ToolError";
1883
+ }
1884
+ var PARSE_FAILURE_PREFIX = "Failed to parse AgentScript code: ";
1885
+ var PARSE_POSITION_RE = /\((\d+):(\d+)\)$/;
1886
+ function parseFailureOf(message) {
1887
+ const match = PARSE_POSITION_RE.exec(message);
1888
+ const line = match ? Number(match[1]) - 1 : 0;
1889
+ if (!match || line < 1) return { message };
1890
+ const column = Number(match[2]);
1891
+ return { message: `${message.slice(0, match.index)}(${line}:${column})`, location: { line, column } };
1892
+ }
1867
1893
  var ScriptTooLargeError = class extends Error {
1868
1894
  code = "SCRIPT_TOO_LARGE";
1869
1895
  scriptLength;
@@ -1901,8 +1927,20 @@ var EnclaveService = class {
1901
1927
  throw new ScriptTooLargeError(code.length, maxLength);
1902
1928
  }
1903
1929
  }
1930
+ const toolFailures = [];
1904
1931
  const toolHandler = async (toolName, args) => {
1905
- return environment.callTool(toolName, args);
1932
+ try {
1933
+ return await environment.callTool(toolName, args);
1934
+ } catch (error) {
1935
+ const code2 = error?.code;
1936
+ toolFailures.push({
1937
+ toolName,
1938
+ name: toolFailureName(error),
1939
+ message: toolFailureMessage(error, toolName),
1940
+ ...typeof code2 === "string" ? { code: code2 } : {}
1941
+ });
1942
+ throw error;
1943
+ }
1906
1944
  };
1907
1945
  const sidecar = this.sidecarOptions.enabled ? {
1908
1946
  enabled: true,
@@ -1912,8 +1950,29 @@ var EnclaveService = class {
1912
1950
  maxResolvedSize: this.sidecarOptions.maxResolvedSize,
1913
1951
  allowComposites: this.sidecarOptions.allowComposites
1914
1952
  } : void 0;
1915
- const source = wrapScriptWithToolNamespaces(code, environment.toolNamespaces);
1916
- const enclave = new Enclave({
1953
+ const { mcpLog, mcpNotify } = environment;
1954
+ const globals = {
1955
+ // Provide getTool as a custom global
1956
+ getTool: environment.getTool,
1957
+ // Provide logging functions if available
1958
+ ...mcpLog ? {
1959
+ mcpLog: (level, message, metadata) => {
1960
+ mcpLog(level, message, metadata);
1961
+ logs.push(`[mcp:${level}] ${message}`);
1962
+ }
1963
+ } : {},
1964
+ ...mcpNotify ? {
1965
+ mcpNotify: (event, payload) => {
1966
+ mcpNotify(event, payload);
1967
+ logs.push(`[notify] ${event}`);
1968
+ }
1969
+ } : {}
1970
+ // Note: enclave-vm v2.0.0+ provides its own __safe_console internally with rate limiting
1971
+ // and output size limits. Passing console in globals causes "Cannot redefine property"
1972
+ // errors due to Double VM architecture. Console output from user scripts goes to stdout
1973
+ // via enclave's internal console, not to this logs array. Only mcpLog/mcpNotify are captured.
1974
+ };
1975
+ const options = {
1917
1976
  timeout: this.vmOptions.timeoutMs,
1918
1977
  maxToolCalls: this.vmOptions.maxSteps || 100,
1919
1978
  maxIterations: 1e4,
@@ -1923,33 +1982,20 @@ var EnclaveService = class {
1923
1982
  validate: true,
1924
1983
  transform: true,
1925
1984
  sidecar,
1985
+ // Error stacks name no host file, whatever stage raised them.
1986
+ sanitizeStackTraces: true,
1926
1987
  // Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
1927
1988
  allowFunctionsInGlobals: true,
1928
- globals: {
1929
- // Provide getTool as a custom global
1930
- getTool: environment.getTool,
1931
- // Provide logging functions if available
1932
- ...environment.mcpLog ? {
1933
- mcpLog: (level, message, metadata) => {
1934
- environment.mcpLog(level, message, metadata);
1935
- logs.push(`[mcp:${level}] ${message}`);
1936
- }
1937
- } : {},
1938
- ...environment.mcpNotify ? {
1939
- mcpNotify: (event, payload) => {
1940
- environment.mcpNotify(event, payload);
1941
- logs.push(`[notify] ${event}`);
1942
- }
1943
- } : {}
1944
- // Note: enclave-vm v2.0.0+ provides its own __safe_console internally with rate limiting
1945
- // and output size limits. Passing console in globals causes "Cannot redefine property"
1946
- // errors due to Double VM architecture. Console output from user scripts goes to stdout
1947
- // via enclave's internal console, not to this logs array. Only mcpLog/mcpNotify are captured.
1948
- }
1949
- });
1989
+ globals,
1990
+ // Tool namespaces (`acme.getUser()` for a tool named `acme.getUser`) are built by the sandbox
1991
+ // itself: a method call is exactly `callTool('acme.getUser', args, options)`, with the
1992
+ // sandbox's tool-call cap, rate limit, suspicious-sequence checks and `throwOnError`.
1993
+ toolNamespaces: toSandboxToolNamespaces(environment.toolNamespaces, Object.keys(globals))
1994
+ };
1995
+ const enclave = createEnclave(options);
1950
1996
  try {
1951
- const result = await enclave.run(source);
1952
- return this.mapEnclaveResult(result, logs);
1997
+ const result = await enclave.run(code);
1998
+ return this.mapEnclaveResult(result, logs, toolFailures);
1953
1999
  } finally {
1954
2000
  enclave.dispose();
1955
2001
  }
@@ -1957,58 +2003,56 @@ var EnclaveService = class {
1957
2003
  /**
1958
2004
  * Map Enclave ExecutionResult to EnclaveExecutionResult
1959
2005
  */
1960
- mapEnclaveResult(result, logs) {
2006
+ mapEnclaveResult(result, logs, toolFailures = []) {
2007
+ const stats = {
2008
+ duration: result.stats.duration,
2009
+ toolCallCount: result.stats.toolCallCount,
2010
+ iterationCount: result.stats.iterationCount
2011
+ };
1961
2012
  if (result.success) {
2013
+ return { success: true, result: result.value, logs, timedOut: false, stats };
2014
+ }
2015
+ const error = result.error ?? { name: "Error", message: "Script execution failed" };
2016
+ if (error.code === "ENCLAVE_ERROR" && error.message?.startsWith(PARSE_FAILURE_PREFIX)) {
2017
+ const { message, location } = parseFailureOf(error.message);
1962
2018
  return {
1963
- success: true,
1964
- result: result.value,
2019
+ success: false,
2020
+ error: { message, name: "SyntaxError", code: "SYNTAX_ERROR", ...location ? { location } : {} },
1965
2021
  logs,
1966
2022
  timedOut: false,
1967
- stats: {
1968
- duration: result.stats.duration,
1969
- toolCallCount: result.stats.toolCallCount,
1970
- iterationCount: result.stats.iterationCount
1971
- }
2023
+ stats
1972
2024
  };
1973
2025
  }
1974
- const error = result.error;
1975
- const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
1976
- if (error.code === "VALIDATION_ERROR") {
1977
- const issues = error.data?.issues ?? [];
1978
- const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
2026
+ const toolFailure = [...toolFailures].reverse().find((failure) => failure.message === error.message && failure.name === error.name);
2027
+ if (toolFailure) {
1979
2028
  return {
1980
2029
  success: false,
1981
2030
  error: {
1982
2031
  message: error.message,
1983
- name: "ValidationError",
1984
- code: error.code,
1985
- ...blockedPatterns.length > 0 ? { blockedPatterns } : {}
2032
+ name: error.name,
2033
+ ...toolFailure.code ? { code: toolFailure.code } : {},
2034
+ toolName: toolFailure.toolName
1986
2035
  },
1987
2036
  logs,
1988
2037
  timedOut: false,
1989
- stats: {
1990
- duration: result.stats.duration,
1991
- toolCallCount: result.stats.toolCallCount,
1992
- iterationCount: result.stats.iterationCount
1993
- }
2038
+ stats
1994
2039
  };
1995
2040
  }
1996
- const errorData = error.data;
1997
- const toolName = errorData?.["toolName"];
1998
- if (toolName) {
2041
+ const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
2042
+ if (error.code === "VALIDATION_ERROR") {
2043
+ const issues = error.data?.issues ?? [];
2044
+ const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
1999
2045
  return {
2000
2046
  success: false,
2001
2047
  error: {
2002
2048
  message: error.message,
2003
- name: error.name,
2004
- stack: error.stack,
2049
+ name: "ValidationError",
2005
2050
  code: error.code,
2006
- toolName,
2007
- toolInput: errorData?.["toolInput"],
2008
- details: errorData?.["details"]
2051
+ ...blockedPatterns.length > 0 ? { blockedPatterns } : {}
2009
2052
  },
2010
2053
  logs,
2011
- timedOut
2054
+ timedOut: false,
2055
+ stats
2012
2056
  };
2013
2057
  }
2014
2058
  return {
@@ -2021,11 +2065,7 @@ var EnclaveService = class {
2021
2065
  },
2022
2066
  logs,
2023
2067
  timedOut,
2024
- stats: {
2025
- duration: result.stats.duration,
2026
- toolCallCount: result.stats.toolCallCount,
2027
- iterationCount: result.stats.iterationCount
2028
- }
2068
+ stats
2029
2069
  };
2030
2070
  }
2031
2071
  };
@@ -2036,6 +2076,16 @@ EnclaveService = __decorateClass([
2036
2076
  scope: ProviderScope3.GLOBAL
2037
2077
  })
2038
2078
  ], EnclaveService);
2079
+ function createEnclave(options) {
2080
+ try {
2081
+ return new Enclave(options);
2082
+ } catch (error) {
2083
+ if (!options.toolNamespaces || !(error instanceof TypeError) || !error.message.startsWith("Invalid toolNamespaces")) {
2084
+ throw error;
2085
+ }
2086
+ return new Enclave({ ...options, toolNamespaces: void 0 });
2087
+ }
2088
+ }
2039
2089
 
2040
2090
  // plugins/plugin-codecall/src/tools/search.tool.ts
2041
2091
  import { getCallSurface, Tool, ToolContext } from "@frontmcp/sdk";
@@ -3355,6 +3405,16 @@ var ExecuteTool = class extends ToolContext5 {
3355
3405
  }
3356
3406
  if (!executionResult.success) {
3357
3407
  const error = executionResult.error;
3408
+ if (error.code === "SYNTAX_ERROR") {
3409
+ audit?.logExecutionFailure(executionId, script, durationMs, error.message);
3410
+ return {
3411
+ status: "syntax_error",
3412
+ error: {
3413
+ message: toClientErrorMessage(error.message) || "Syntax error in script",
3414
+ ...error.location ? { location: error.location } : {}
3415
+ }
3416
+ };
3417
+ }
3358
3418
  if (error.code === "VALIDATION_ERROR" || error.name === "ValidationError") {
3359
3419
  audit?.logSecurityAstBlocked(executionId, error.blockedPatterns?.join(", ") ?? "UNKNOWN", error.message);
3360
3420
  audit?.logExecutionFailure(executionId, script, durationMs, error.message);
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-codecall",
3
- "version": "1.8.3",
3
+ "version": "1.8.4",
4
4
  "description": "CodeCall plugin for FrontMCP - AgentScript-based meta-tools for orchestrating MCP tools",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -49,13 +49,13 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@enclave-vm/core": "^2.15.3",
52
- "@frontmcp/protocol": "1.8.3",
53
- "@frontmcp/sdk": "1.8.3",
52
+ "@frontmcp/protocol": "1.8.4",
53
+ "@frontmcp/sdk": "1.8.4",
54
54
  "vectoriadb": "^2.3.2"
55
55
  },
56
56
  "peerDependencies": {
57
- "@frontmcp/lazy-zod": "1.8.3",
58
- "@frontmcp/plugin-cache": "1.8.3"
57
+ "@frontmcp/lazy-zod": "1.8.4",
58
+ "@frontmcp/plugin-cache": "1.8.4"
59
59
  },
60
60
  "devDependencies": {
61
61
  "reflect-metadata": "^0.2.2"
package/index.js CHANGED
@@ -1823,9 +1823,29 @@ var UNBINDABLE_NAMESPACES = new Set(
1823
1823
  "localStorage sessionStorage indexedDB crypto performance structuredClone AbortController AbortSignal",
1824
1824
  "MessageChannel MessagePort BroadcastChannel TextEncoder TextDecoder Intl setTimeout setInterval",
1825
1825
  "setImmediate clearTimeout clearInterval clearImmediate queueMicrotask WebAssembly Worker SharedWorker",
1826
- "ServiceWorker WeakRef FinalizationRegistry BigInt Atomics SharedArrayBuffer ArrayBuffer DataView"
1826
+ "ServiceWorker WeakRef FinalizationRegistry BigInt Atomics SharedArrayBuffer ArrayBuffer DataView",
1827
+ "importScripts ShadowRealm Iterator AsyncIterator"
1827
1828
  ].join(" ").split(" ")
1828
1829
  );
1830
+ var UNCALLABLE_METHODS = new Set(
1831
+ [
1832
+ "process require module exports Buffer eval Function AsyncFunction GeneratorFunction arguments RegExp",
1833
+ "Promise Symbol Proxy Reflect Error TypeError ReferenceError SyntaxError RangeError URIError EvalError",
1834
+ "AggregateError fetch XMLHttpRequest WebSocket localStorage sessionStorage indexedDB crypto performance",
1835
+ "structuredClone AbortController AbortSignal MessageChannel MessagePort BroadcastChannel TextEncoder",
1836
+ "TextDecoder Intl setTimeout setInterval setImmediate clearTimeout clearInterval clearImmediate",
1837
+ "queueMicrotask WebAssembly Worker SharedWorker ServiceWorker WeakMap WeakSet WeakRef FinalizationRegistry",
1838
+ "Map Set Atomics SharedArrayBuffer importScripts ShadowRealm Iterator AsyncIterator"
1839
+ ].join(" ").split(" ")
1840
+ );
1841
+ var SANDBOX_TOOL_NAME_RE = /^[a-zA-Z][a-zA-Z0-9:._-]*$/;
1842
+ var MAX_SANDBOX_TOOL_NAME_LENGTH = 256;
1843
+ function isBindableNamespace(ns, reservedGlobals) {
1844
+ return IDENT_RE.test(ns) && !PROTOTYPE_KEYS.has(ns) && !ns.startsWith("__") && !RESERVED_NAMESPACES.has(ns) && !UNBINDABLE_NAMESPACES.has(ns) && !reservedGlobals.has(ns);
1845
+ }
1846
+ function isBindableMethod(ns, method, toolName) {
1847
+ return IDENT_RE.test(method) && !PROTOTYPE_KEYS.has(method) && !method.startsWith("__") && !UNCALLABLE_METHODS.has(method) && toolName === `${ns}.${method}` && toolName.length <= MAX_SANDBOX_TOOL_NAME_LENGTH && SANDBOX_TOOL_NAME_RE.test(toolName);
1848
+ }
1829
1849
  function buildToolNamespaces(tools) {
1830
1850
  const namespaces = /* @__PURE__ */ Object.create(null);
1831
1851
  const skipped = [];
@@ -1851,6 +1871,10 @@ function buildToolNamespaces(tools) {
1851
1871
  skipped.push({ name, reason: "reserved-namespace" });
1852
1872
  continue;
1853
1873
  }
1874
+ if (!isBindableMethod(ns, method, name)) {
1875
+ skipped.push({ name, reason: "unsupported-name" });
1876
+ continue;
1877
+ }
1854
1878
  const bucket = namespaces[ns] ?? (namespaces[ns] = /* @__PURE__ */ Object.create(null));
1855
1879
  if (Object.prototype.hasOwnProperty.call(bucket, method)) {
1856
1880
  skipped.push({ name, reason: "duplicate-method" });
@@ -1860,49 +1884,49 @@ function buildToolNamespaces(tools) {
1860
1884
  }
1861
1885
  return { namespaces, skipped };
1862
1886
  }
1863
- var NAMESPACE_CALL_HELPER = "__codecallNamespaceCall";
1864
- var NAMESPACE_CALL_HELPER_SOURCE = `const ${NAMESPACE_CALL_HELPER} = async (toolName, run, options) => { if (options && options.throwOnError === false) { try { return { success: true, data: await run() }; } catch (error) { return { success: false, error: { message: error && error.message, toolName: toolName } }; } } return await run(); };`;
1865
- function mentionsIdentifier(script, identifier) {
1866
- let from = 0;
1867
- for (; ; ) {
1868
- const at = script.indexOf(identifier, from);
1869
- if (at === -1) return false;
1870
- from = at + 1;
1871
- const before = at > 0 ? script[at - 1] : "";
1872
- const after = script[at + identifier.length] ?? "";
1873
- if (/[A-Za-z0-9_$'"`]/.test(before) || /[A-Za-z0-9_$]/.test(after)) continue;
1874
- if (before === "." && script.slice(Math.max(0, at - 3), at) !== "...") continue;
1875
- return true;
1876
- }
1877
- }
1878
- function wrapScriptWithToolNamespaces(script, namespaces) {
1879
- if (!namespaces) return script;
1880
- const declarations = [];
1887
+ function toSandboxToolNamespaces(namespaces, reservedGlobals = []) {
1888
+ if (!namespaces) return void 0;
1889
+ const reserved = new Set(reservedGlobals);
1890
+ const bindable = /* @__PURE__ */ Object.create(null);
1891
+ let count = 0;
1881
1892
  for (const ns of Object.keys(namespaces)) {
1882
- if (!IDENT_RE.test(ns) || RESERVED_NAMESPACES.has(ns) || UNBINDABLE_NAMESPACES.has(ns) || ns.startsWith("__")) {
1883
- continue;
1884
- }
1885
- if (PROTOTYPE_KEYS.has(ns) || !mentionsIdentifier(script, ns)) continue;
1893
+ if (!isBindableNamespace(ns, reserved)) continue;
1886
1894
  const methods = namespaces[ns];
1887
- const entries = [];
1895
+ const bucket = /* @__PURE__ */ Object.create(null);
1896
+ let methodCount = 0;
1888
1897
  for (const method of Object.keys(methods)) {
1889
1898
  const toolName = methods[method];
1890
- if (!IDENT_RE.test(method) || PROTOTYPE_KEYS.has(method) || toolName !== `${ns}.${method}`) continue;
1891
- const literal = JSON.stringify(toolName);
1892
- entries.push(
1893
- `${JSON.stringify(method)}: (input, options) => ${NAMESPACE_CALL_HELPER}(${literal}, () => callTool(${literal}, input === undefined ? {} : input), options)`
1894
- );
1899
+ if (typeof toolName !== "string" || !isBindableMethod(ns, method, toolName)) continue;
1900
+ bucket[method] = toolName;
1901
+ methodCount += 1;
1895
1902
  }
1896
- if (entries.length > 0) {
1897
- declarations.push(`const ${ns} = { ${entries.join(", ")} };`);
1903
+ if (methodCount > 0) {
1904
+ bindable[ns] = bucket;
1905
+ count += 1;
1898
1906
  }
1899
1907
  }
1900
- if (declarations.length === 0) return script;
1901
- return `${NAMESPACE_CALL_HELPER_SOURCE} ${declarations.join(" ")} return await (async () => {${script}
1902
- })();`;
1908
+ return count > 0 ? bindable : void 0;
1903
1909
  }
1904
1910
 
1905
1911
  // plugins/plugin-codecall/src/services/enclave.service.ts
1912
+ function toolFailureMessage(error, toolName) {
1913
+ if (typeof error === "string" && error) return error;
1914
+ const message = error?.message;
1915
+ return typeof message === "string" && message ? message : `Tool call failed: ${toolName}`;
1916
+ }
1917
+ function toolFailureName(error) {
1918
+ const name = error && typeof error === "object" ? error.name : void 0;
1919
+ return typeof name === "string" && name ? name.slice(0, 128) : "ToolError";
1920
+ }
1921
+ var PARSE_FAILURE_PREFIX = "Failed to parse AgentScript code: ";
1922
+ var PARSE_POSITION_RE = /\((\d+):(\d+)\)$/;
1923
+ function parseFailureOf(message) {
1924
+ const match = PARSE_POSITION_RE.exec(message);
1925
+ const line = match ? Number(match[1]) - 1 : 0;
1926
+ if (!match || line < 1) return { message };
1927
+ const column = Number(match[2]);
1928
+ return { message: `${message.slice(0, match.index)}(${line}:${column})`, location: { line, column } };
1929
+ }
1906
1930
  var ScriptTooLargeError = class extends Error {
1907
1931
  code = "SCRIPT_TOO_LARGE";
1908
1932
  scriptLength;
@@ -1940,8 +1964,20 @@ var EnclaveService = class {
1940
1964
  throw new ScriptTooLargeError(code.length, maxLength);
1941
1965
  }
1942
1966
  }
1967
+ const toolFailures = [];
1943
1968
  const toolHandler = async (toolName, args) => {
1944
- return environment.callTool(toolName, args);
1969
+ try {
1970
+ return await environment.callTool(toolName, args);
1971
+ } catch (error) {
1972
+ const code2 = error?.code;
1973
+ toolFailures.push({
1974
+ toolName,
1975
+ name: toolFailureName(error),
1976
+ message: toolFailureMessage(error, toolName),
1977
+ ...typeof code2 === "string" ? { code: code2 } : {}
1978
+ });
1979
+ throw error;
1980
+ }
1945
1981
  };
1946
1982
  const sidecar = this.sidecarOptions.enabled ? {
1947
1983
  enabled: true,
@@ -1951,8 +1987,29 @@ var EnclaveService = class {
1951
1987
  maxResolvedSize: this.sidecarOptions.maxResolvedSize,
1952
1988
  allowComposites: this.sidecarOptions.allowComposites
1953
1989
  } : void 0;
1954
- const source = wrapScriptWithToolNamespaces(code, environment.toolNamespaces);
1955
- const enclave = new import_core.Enclave({
1990
+ const { mcpLog, mcpNotify } = environment;
1991
+ const globals = {
1992
+ // Provide getTool as a custom global
1993
+ getTool: environment.getTool,
1994
+ // Provide logging functions if available
1995
+ ...mcpLog ? {
1996
+ mcpLog: (level, message, metadata) => {
1997
+ mcpLog(level, message, metadata);
1998
+ logs.push(`[mcp:${level}] ${message}`);
1999
+ }
2000
+ } : {},
2001
+ ...mcpNotify ? {
2002
+ mcpNotify: (event, payload) => {
2003
+ mcpNotify(event, payload);
2004
+ logs.push(`[notify] ${event}`);
2005
+ }
2006
+ } : {}
2007
+ // Note: enclave-vm v2.0.0+ provides its own __safe_console internally with rate limiting
2008
+ // and output size limits. Passing console in globals causes "Cannot redefine property"
2009
+ // errors due to Double VM architecture. Console output from user scripts goes to stdout
2010
+ // via enclave's internal console, not to this logs array. Only mcpLog/mcpNotify are captured.
2011
+ };
2012
+ const options = {
1956
2013
  timeout: this.vmOptions.timeoutMs,
1957
2014
  maxToolCalls: this.vmOptions.maxSteps || 100,
1958
2015
  maxIterations: 1e4,
@@ -1962,33 +2019,20 @@ var EnclaveService = class {
1962
2019
  validate: true,
1963
2020
  transform: true,
1964
2021
  sidecar,
2022
+ // Error stacks name no host file, whatever stage raised them.
2023
+ sanitizeStackTraces: true,
1965
2024
  // Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
1966
2025
  allowFunctionsInGlobals: true,
1967
- globals: {
1968
- // Provide getTool as a custom global
1969
- getTool: environment.getTool,
1970
- // Provide logging functions if available
1971
- ...environment.mcpLog ? {
1972
- mcpLog: (level, message, metadata) => {
1973
- environment.mcpLog(level, message, metadata);
1974
- logs.push(`[mcp:${level}] ${message}`);
1975
- }
1976
- } : {},
1977
- ...environment.mcpNotify ? {
1978
- mcpNotify: (event, payload) => {
1979
- environment.mcpNotify(event, payload);
1980
- logs.push(`[notify] ${event}`);
1981
- }
1982
- } : {}
1983
- // Note: enclave-vm v2.0.0+ provides its own __safe_console internally with rate limiting
1984
- // and output size limits. Passing console in globals causes "Cannot redefine property"
1985
- // errors due to Double VM architecture. Console output from user scripts goes to stdout
1986
- // via enclave's internal console, not to this logs array. Only mcpLog/mcpNotify are captured.
1987
- }
1988
- });
2026
+ globals,
2027
+ // Tool namespaces (`acme.getUser()` for a tool named `acme.getUser`) are built by the sandbox
2028
+ // itself: a method call is exactly `callTool('acme.getUser', args, options)`, with the
2029
+ // sandbox's tool-call cap, rate limit, suspicious-sequence checks and `throwOnError`.
2030
+ toolNamespaces: toSandboxToolNamespaces(environment.toolNamespaces, Object.keys(globals))
2031
+ };
2032
+ const enclave = createEnclave(options);
1989
2033
  try {
1990
- const result = await enclave.run(source);
1991
- return this.mapEnclaveResult(result, logs);
2034
+ const result = await enclave.run(code);
2035
+ return this.mapEnclaveResult(result, logs, toolFailures);
1992
2036
  } finally {
1993
2037
  enclave.dispose();
1994
2038
  }
@@ -1996,58 +2040,56 @@ var EnclaveService = class {
1996
2040
  /**
1997
2041
  * Map Enclave ExecutionResult to EnclaveExecutionResult
1998
2042
  */
1999
- mapEnclaveResult(result, logs) {
2043
+ mapEnclaveResult(result, logs, toolFailures = []) {
2044
+ const stats = {
2045
+ duration: result.stats.duration,
2046
+ toolCallCount: result.stats.toolCallCount,
2047
+ iterationCount: result.stats.iterationCount
2048
+ };
2000
2049
  if (result.success) {
2050
+ return { success: true, result: result.value, logs, timedOut: false, stats };
2051
+ }
2052
+ const error = result.error ?? { name: "Error", message: "Script execution failed" };
2053
+ if (error.code === "ENCLAVE_ERROR" && error.message?.startsWith(PARSE_FAILURE_PREFIX)) {
2054
+ const { message, location } = parseFailureOf(error.message);
2001
2055
  return {
2002
- success: true,
2003
- result: result.value,
2056
+ success: false,
2057
+ error: { message, name: "SyntaxError", code: "SYNTAX_ERROR", ...location ? { location } : {} },
2004
2058
  logs,
2005
2059
  timedOut: false,
2006
- stats: {
2007
- duration: result.stats.duration,
2008
- toolCallCount: result.stats.toolCallCount,
2009
- iterationCount: result.stats.iterationCount
2010
- }
2060
+ stats
2011
2061
  };
2012
2062
  }
2013
- const error = result.error;
2014
- const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
2015
- if (error.code === "VALIDATION_ERROR") {
2016
- const issues = error.data?.issues ?? [];
2017
- const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
2063
+ const toolFailure = [...toolFailures].reverse().find((failure) => failure.message === error.message && failure.name === error.name);
2064
+ if (toolFailure) {
2018
2065
  return {
2019
2066
  success: false,
2020
2067
  error: {
2021
2068
  message: error.message,
2022
- name: "ValidationError",
2023
- code: error.code,
2024
- ...blockedPatterns.length > 0 ? { blockedPatterns } : {}
2069
+ name: error.name,
2070
+ ...toolFailure.code ? { code: toolFailure.code } : {},
2071
+ toolName: toolFailure.toolName
2025
2072
  },
2026
2073
  logs,
2027
2074
  timedOut: false,
2028
- stats: {
2029
- duration: result.stats.duration,
2030
- toolCallCount: result.stats.toolCallCount,
2031
- iterationCount: result.stats.iterationCount
2032
- }
2075
+ stats
2033
2076
  };
2034
2077
  }
2035
- const errorData = error.data;
2036
- const toolName = errorData?.["toolName"];
2037
- if (toolName) {
2078
+ const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
2079
+ if (error.code === "VALIDATION_ERROR") {
2080
+ const issues = error.data?.issues ?? [];
2081
+ const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
2038
2082
  return {
2039
2083
  success: false,
2040
2084
  error: {
2041
2085
  message: error.message,
2042
- name: error.name,
2043
- stack: error.stack,
2086
+ name: "ValidationError",
2044
2087
  code: error.code,
2045
- toolName,
2046
- toolInput: errorData?.["toolInput"],
2047
- details: errorData?.["details"]
2088
+ ...blockedPatterns.length > 0 ? { blockedPatterns } : {}
2048
2089
  },
2049
2090
  logs,
2050
- timedOut
2091
+ timedOut: false,
2092
+ stats
2051
2093
  };
2052
2094
  }
2053
2095
  return {
@@ -2060,11 +2102,7 @@ var EnclaveService = class {
2060
2102
  },
2061
2103
  logs,
2062
2104
  timedOut,
2063
- stats: {
2064
- duration: result.stats.duration,
2065
- toolCallCount: result.stats.toolCallCount,
2066
- iterationCount: result.stats.iterationCount
2067
- }
2105
+ stats
2068
2106
  };
2069
2107
  }
2070
2108
  };
@@ -2075,6 +2113,16 @@ EnclaveService = __decorateClass([
2075
2113
  scope: import_sdk3.ProviderScope.GLOBAL
2076
2114
  })
2077
2115
  ], EnclaveService);
2116
+ function createEnclave(options) {
2117
+ try {
2118
+ return new import_core.Enclave(options);
2119
+ } catch (error) {
2120
+ if (!options.toolNamespaces || !(error instanceof TypeError) || !error.message.startsWith("Invalid toolNamespaces")) {
2121
+ throw error;
2122
+ }
2123
+ return new import_core.Enclave({ ...options, toolNamespaces: void 0 });
2124
+ }
2125
+ }
2078
2126
 
2079
2127
  // plugins/plugin-codecall/src/tools/search.tool.ts
2080
2128
  var import_sdk4 = require("@frontmcp/sdk");
@@ -3394,6 +3442,16 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
3394
3442
  }
3395
3443
  if (!executionResult.success) {
3396
3444
  const error = executionResult.error;
3445
+ if (error.code === "SYNTAX_ERROR") {
3446
+ audit?.logExecutionFailure(executionId, script, durationMs, error.message);
3447
+ return {
3448
+ status: "syntax_error",
3449
+ error: {
3450
+ message: toClientErrorMessage(error.message) || "Syntax error in script",
3451
+ ...error.location ? { location: error.location } : {}
3452
+ }
3453
+ };
3454
+ }
3397
3455
  if (error.code === "VALIDATION_ERROR" || error.name === "ValidationError") {
3398
3456
  audit?.logSecurityAstBlocked(executionId, error.blockedPatterns?.join(", ") ?? "UNKNOWN", error.message);
3399
3457
  audit?.logExecutionFailure(executionId, script, durationMs, error.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-codecall",
3
- "version": "1.8.3",
3
+ "version": "1.8.4",
4
4
  "description": "CodeCall plugin for FrontMCP - AgentScript-based meta-tools for orchestrating MCP tools",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -49,13 +49,13 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@enclave-vm/core": "^2.15.3",
52
- "@frontmcp/protocol": "1.8.3",
53
- "@frontmcp/sdk": "1.8.3",
52
+ "@frontmcp/protocol": "1.8.4",
53
+ "@frontmcp/sdk": "1.8.4",
54
54
  "vectoriadb": "^2.3.2"
55
55
  },
56
56
  "peerDependencies": {
57
- "@frontmcp/lazy-zod": "1.8.3",
58
- "@frontmcp/plugin-cache": "1.8.3"
57
+ "@frontmcp/lazy-zod": "1.8.4",
58
+ "@frontmcp/plugin-cache": "1.8.4"
59
59
  },
60
60
  "devDependencies": {
61
61
  "reflect-metadata": "^0.2.2"
@@ -14,6 +14,11 @@ export interface EnclaveExecutionResult {
14
14
  toolName?: string;
15
15
  toolInput?: unknown;
16
16
  details?: unknown;
17
+ /** For a script that doesn't parse (`code: 'SYNTAX_ERROR'`): where, in the script's own lines. */
18
+ location?: {
19
+ line: number;
20
+ column: number;
21
+ };
17
22
  /**
18
23
  * Distinct AST-validation issue codes, for the `codecall:security:ast-blocked` audit event.
19
24
  *
@@ -1 +1 @@
1
- {"version":3,"file":"enclave.service.d.ts","sourceRoot":"","sources":["../../src/services/enclave.service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAA6B,MAAM,oBAAoB,CAAC;AAE3F,OAAO,KAAK,cAAc,MAAM,+BAA+B,CAAC;AAGhE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB;;;;;WAKG;QACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,sBAAsB;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CASpD;AAOD,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,MAAM,EAAE,cAAc;IAOlC;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAsFhG;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAgFzB"}
1
+ {"version":3,"file":"enclave.service.d.ts","sourceRoot":"","sources":["../../src/services/enclave.service.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,qBAAqB,EAA6B,MAAM,oBAAoB,CAAC;AAE3F,OAAO,KAAK,cAAc,MAAM,+BAA+B,CAAC;AAGhE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,kGAAkG;QAClG,QAAQ,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5C;;;;;WAKG;QACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAkDD;;;;;;;;GAQG;AACH;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,sBAAsB;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CASpD;AAOD,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,MAAM,EAAE,cAAc;IAOlC;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAwGhG;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAsFzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"execute.tool.d.ts","sourceRoot":"","sources":["../../src/tools/execute.tool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,WAAW,EAAkB,MAAM,eAAe,CAAC;AAwBlF,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAyC1B,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAIjB,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAgVvE"}
1
+ {"version":3,"file":"execute.tool.d.ts","sourceRoot":"","sources":["../../src/tools/execute.tool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,WAAW,EAAkB,MAAM,eAAe,CAAC;AAwBlF,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAyC1B,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAIjB,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CA4VvE"}
@@ -13,9 +13,9 @@ export type ToolNamespaces = Record<string, ToolNamespace>;
13
13
  * Reasons a tool may be skipped during namespace generation. Returned so the
14
14
  * caller can surface diagnostics without re-deriving the rules.
15
15
  */
16
- export type NamespaceSkipReason = 'no-namespace-prefix' | 'invalid-identifier' | 'reserved-namespace' | 'prototype-key' | 'duplicate-method';
16
+ export type NamespaceSkipReason = 'no-namespace-prefix' | 'invalid-identifier' | 'reserved-namespace' | 'prototype-key' | 'unsupported-name' | 'duplicate-method';
17
17
  export interface BuildToolNamespacesResult {
18
- /** The namespaces a script may use, as plain data. Render them with `wrapScriptWithToolNamespaces`. */
18
+ /** The namespaces a script may use, as plain data (the sandbox's `toolNamespaces`). */
19
19
  namespaces: ToolNamespaces;
20
20
  /** Tools that were skipped, with the reason. Useful for logs / lint output. */
21
21
  skipped: Array<{
@@ -32,25 +32,22 @@ export interface BuildToolNamespacesResult {
32
32
  * - Tools whose prefix collides with a reserved global or word are skipped.
33
33
  * - Tools whose prefix or suffix is a prototype key (`__proto__`, `constructor`,
34
34
  * `prototype`) are skipped.
35
+ * - Methods AgentScript refuses as property names (`fetch`, `Map`, `__x`), and tool names the
36
+ * sandbox can't carry (outside letters, digits, `:`, `.`, `_` and `-`, or not starting with a
37
+ * letter), are skipped.
35
38
  * - First registration wins on duplicate `{ns}.{method}` (subsequent are reported as skipped).
36
39
  *
37
- * The result is plain data (method → tool name). The namespace functions themselves are
38
- * written in AgentScript by `wrapScriptWithToolNamespaces`, so every call they make is a
39
- * `callTool()` inside the sandbox and goes through the sandbox's tool-call cap, rate limit
40
- * and suspicious-sequence checks, exactly like a direct `callTool()`.
40
+ * The result is plain data (method → tool name), handed to the sandbox as its `toolNamespaces`
41
+ * (see `toSandboxToolNamespaces`): the sandbox builds the namespace objects itself, and a method
42
+ * call `ns.method(args, options)` is exactly `callTool('ns.method', args, options)`, with the
43
+ * sandbox's tool-call cap, rate limit, suspicious-sequence checks and `throwOnError` handling.
41
44
  */
42
45
  export declare function buildToolNamespaces(tools: ReadonlyArray<NamespaceableTool>): BuildToolNamespacesResult;
43
46
  /**
44
- * Make the namespaces available to `script`, as AgentScript.
45
- *
46
- * Each namespace becomes a `const` object whose methods call `callTool('<ns>.<method>', input)`
47
- * inside the sandbox, so the enclave counts, rate-limits and pattern-checks them like any
48
- * other `callTool()`. (1.8.2 injected them as host functions, which reached the tool
49
- * pipeline without passing any of those checks.) The user's script runs in a nested async
50
- * function, so it can still declare its own variables with the same names.
51
- *
52
- * Only namespaces the script mentions are emitted; a script that uses none is returned as is.
53
- * Everything is emitted on the script's first line, so line numbers in parse errors are kept.
47
+ * The namespaces to hand the sandbox as its `toolNamespaces`: every namespace and method it can
48
+ * bind, next to custom globals named `reservedGlobals` (`getTool`, `mcpLog`, ...). Rechecks what
49
+ * `buildToolNamespaces` guarantees, since the sandbox refuses the whole configuration, and so fails
50
+ * every script, over a single name it can't bind. Returns undefined when nothing is left.
54
51
  */
55
- export declare function wrapScriptWithToolNamespaces(script: string, namespaces: ToolNamespaces | undefined): string;
52
+ export declare function toSandboxToolNamespaces(namespaces: ToolNamespaces | undefined, reservedGlobals?: Iterable<string>): ToolNamespaces | undefined;
56
53
  //# sourceMappingURL=build-tool-namespaces.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-tool-namespaces.d.ts","sourceRoot":"","sources":["../../src/utils/build-tool-namespaces.ts"],"names":[],"mappings":"AA2DA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,qGAAqG;AACrG,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnD,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,WAAW,yBAAyB;IACxC,uGAAuG;IACvG,UAAU,EAAE,cAAc,CAAC;IAC3B,+EAA+E;IAC/E,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;CAC/D;AA4BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,yBAAyB,CAiDtG;AAgCD;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,GAAG,SAAS,GAAG,MAAM,CA0B3G"}
1
+ {"version":3,"file":"build-tool-namespaces.d.ts","sourceRoot":"","sources":["../../src/utils/build-tool-namespaces.ts"],"names":[],"mappings":"AA2DA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,qGAAqG;AACrG,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnD,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,eAAe,GACf,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,yBAAyB;IACxC,uFAAuF;IACvF,UAAU,EAAE,cAAc,CAAC;IAC3B,+EAA+E;IAC/E,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;CAC/D;AAiFD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,yBAAyB,CAsDtG;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,cAAc,GAAG,SAAS,EACtC,eAAe,GAAE,QAAQ,CAAC,MAAM,CAAM,GACrC,cAAc,GAAG,SAAS,CAsB5B"}