@frontmcp/plugin-codecall 1.8.3 → 1.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codecall.symbol.d.ts +8 -6
- package/codecall.symbol.d.ts.map +1 -1
- package/esm/index.mjs +282 -101
- package/esm/package.json +7 -5
- package/index.js +279 -100
- package/package.json +7 -5
- package/services/enclave.service.d.ts +5 -1
- package/services/enclave.service.d.ts.map +1 -1
- package/tools/execute.schema.d.ts +2 -2
- package/tools/execute.schema.d.ts.map +1 -1
- package/tools/execute.tool.d.ts.map +1 -1
- package/utils/build-tool-namespaces.d.ts +14 -17
- package/utils/build-tool-namespaces.d.ts.map +1 -1
- package/utils/script-lines.d.ts +14 -0
- package/utils/script-lines.d.ts.map +1 -0
package/codecall.symbol.d.ts
CHANGED
|
@@ -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
|
|
87
|
-
* call `
|
|
88
|
-
* `
|
|
89
|
-
*
|
|
90
|
-
*
|
|
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
|
}
|
package/codecall.symbol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codecall.symbol.d.ts","sourceRoot":"","sources":["../src/codecall.symbol.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,sBAAsB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,YAAY,CAAC;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;CAChE;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC
|
|
1
|
+
{"version":3,"file":"codecall.symbol.d.ts","sourceRoot":"","sources":["../src/codecall.symbol.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,sBAAsB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,YAAY,CAAC;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;CAChE;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EACxB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,KACtB,OAAO,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAEhD;;;;;;OAMG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,uBAAuB,GAAG,SAAS,CAAC;IAE/D,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAEnH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAEtE;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAEhF;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAErD;;OAEG;IACH,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExC;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B"}
|
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 {
|
|
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,164 @@ function buildToolNamespaces(tools) {
|
|
|
1821
1847
|
}
|
|
1822
1848
|
return { namespaces, skipped };
|
|
1823
1849
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
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 (!
|
|
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
|
|
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 (
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1862
|
+
if (typeof toolName !== "string" || !isBindableMethod(ns, method, toolName)) continue;
|
|
1863
|
+
bucket[method] = toolName;
|
|
1864
|
+
methodCount += 1;
|
|
1865
|
+
}
|
|
1866
|
+
if (methodCount > 0) {
|
|
1867
|
+
bindable[ns] = bucket;
|
|
1868
|
+
count += 1;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
return count > 0 ? bindable : void 0;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
// plugins/plugin-codecall/src/utils/script-lines.ts
|
|
1875
|
+
import { isWrappedInMain, transformAgentScript } from "@enclave-vm/ast";
|
|
1876
|
+
import * as acorn from "acorn";
|
|
1877
|
+
var PARSE_OPTIONS = { ecmaVersion: "latest", locations: true };
|
|
1878
|
+
var NON_CHILD_KEYS = /* @__PURE__ */ new Set(["type", "loc", "start", "end", "range"]);
|
|
1879
|
+
function isNode(value) {
|
|
1880
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1881
|
+
}
|
|
1882
|
+
function isAddedByTransform(node) {
|
|
1883
|
+
if (node.type === "VariableDeclaration") {
|
|
1884
|
+
const [declaration] = node["declarations"] ?? [];
|
|
1885
|
+
const id = declaration?.["id"];
|
|
1886
|
+
return typeof id?.["name"] === "string" && id["name"].startsWith("__iter_");
|
|
1887
|
+
}
|
|
1888
|
+
if (node.type === "IfStatement") {
|
|
1889
|
+
const right = node["test"]?.["right"];
|
|
1890
|
+
return right?.type === "Identifier" && right["name"] === "__maxIterations";
|
|
1891
|
+
}
|
|
1892
|
+
return false;
|
|
1893
|
+
}
|
|
1894
|
+
function parseScript(script) {
|
|
1895
|
+
try {
|
|
1896
|
+
return { body: parseBody(script, "script"), offset: 0 };
|
|
1897
|
+
} catch {
|
|
1898
|
+
try {
|
|
1899
|
+
return { body: parseBody(script, "module"), offset: 0 };
|
|
1900
|
+
} catch {
|
|
1901
|
+
const wrapped = parseBody(`async function __temp__() {
|
|
1902
|
+
${script}
|
|
1903
|
+
}`, "script");
|
|
1904
|
+
const fn = wrapped[0];
|
|
1905
|
+
const block = fn?.["body"];
|
|
1906
|
+
return { body: block?.["body"] ?? [], offset: 1 };
|
|
1856
1907
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
function parseBody(code, sourceType) {
|
|
1911
|
+
const program = acorn.parse(code, { ...PARSE_OPTIONS, sourceType });
|
|
1912
|
+
return program["body"] ?? [];
|
|
1913
|
+
}
|
|
1914
|
+
function treesOf(script) {
|
|
1915
|
+
try {
|
|
1916
|
+
const wrap = !isWrappedInMain(script);
|
|
1917
|
+
const printed = parseBody(transformAgentScript(script, { wrapInMain: wrap }), "script");
|
|
1918
|
+
const { body, offset } = parseScript(script);
|
|
1919
|
+
if (!wrap) return { printed, script: body, offset };
|
|
1920
|
+
const main = printed[0]?.["body"];
|
|
1921
|
+
return { printed: main?.["body"] ?? [], script: body, offset };
|
|
1922
|
+
} catch {
|
|
1923
|
+
return void 0;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
function pairLists(printed, script, depth, out, offset) {
|
|
1927
|
+
const kept = printed.filter((node) => !(isNode(node) && isAddedByTransform(node)));
|
|
1928
|
+
if (kept.length !== script.length) return;
|
|
1929
|
+
kept.forEach((node, index) => {
|
|
1930
|
+
const own = script[index];
|
|
1931
|
+
if (isNode(node) && isNode(own)) pairNodes(node, own, depth, out, offset);
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
function pairNodes(printed, script, depth, out, offset) {
|
|
1935
|
+
if (printed.type === "ReturnStatement" && script.type === "ExpressionStatement") {
|
|
1936
|
+
record(printed, script, depth, out, offset);
|
|
1937
|
+
const argument = printed["argument"];
|
|
1938
|
+
const expression = script["expression"];
|
|
1939
|
+
if (isNode(argument) && isNode(expression)) pairNodes(argument, expression, depth + 1, out, offset);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
if (printed.type !== script.type) return;
|
|
1943
|
+
record(printed, script, depth, out, offset);
|
|
1944
|
+
for (const key of Object.keys(printed)) {
|
|
1945
|
+
if (NON_CHILD_KEYS.has(key)) continue;
|
|
1946
|
+
const printedChild = printed[key];
|
|
1947
|
+
const scriptChild = script[key];
|
|
1948
|
+
if (Array.isArray(printedChild) && Array.isArray(scriptChild)) {
|
|
1949
|
+
pairLists(printedChild, scriptChild, depth + 1, out, offset);
|
|
1950
|
+
} else if (isNode(printedChild) && isNode(scriptChild)) {
|
|
1951
|
+
pairNodes(printedChild, scriptChild, depth + 1, out, offset);
|
|
1859
1952
|
}
|
|
1860
1953
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1954
|
+
}
|
|
1955
|
+
function record(printed, script, depth, out, offset) {
|
|
1956
|
+
if (!printed.loc || !script.loc) return;
|
|
1957
|
+
out.push({
|
|
1958
|
+
printedStart: printed.loc.start.line,
|
|
1959
|
+
printedEnd: printed.loc.end.line,
|
|
1960
|
+
scriptLine: script.loc.start.line - offset,
|
|
1961
|
+
depth
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
function validatedLineMapper(script) {
|
|
1965
|
+
const trees = treesOf(script);
|
|
1966
|
+
if (!trees) return () => void 0;
|
|
1967
|
+
const pairs = [];
|
|
1968
|
+
pairLists(trees.printed, trees.script, 0, pairs, trees.offset);
|
|
1969
|
+
return (printedLine) => {
|
|
1970
|
+
const starting = pairs.filter((pair) => pair.printedStart === printedLine);
|
|
1971
|
+
const candidates = starting.length > 0 ? starting : pairs.filter((pair) => pair.printedStart <= printedLine && printedLine <= pair.printedEnd);
|
|
1972
|
+
const best = candidates.reduce(
|
|
1973
|
+
(deepest, pair) => !deepest || pair.depth > deepest.depth ? pair : deepest,
|
|
1974
|
+
void 0
|
|
1975
|
+
);
|
|
1976
|
+
return best && best.scriptLine >= 1 ? best.scriptLine : void 0;
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
var ISSUE_LINE_RE = / \(line (\d+)\)/g;
|
|
1980
|
+
function withScriptLines(message, script) {
|
|
1981
|
+
if (!message.includes("(line ")) return message;
|
|
1982
|
+
const lineOf = validatedLineMapper(script);
|
|
1983
|
+
return message.replace(ISSUE_LINE_RE, (_match, printed) => {
|
|
1984
|
+
const line = lineOf(Number(printed));
|
|
1985
|
+
return line === void 0 ? "" : ` (line ${line})`;
|
|
1986
|
+
});
|
|
1864
1987
|
}
|
|
1865
1988
|
|
|
1866
1989
|
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1990
|
+
function toolFailureMessage(error, toolName) {
|
|
1991
|
+
if (typeof error === "string" && error) return error;
|
|
1992
|
+
const message = error?.message;
|
|
1993
|
+
return typeof message === "string" && message ? message : `Tool call failed: ${toolName}`;
|
|
1994
|
+
}
|
|
1995
|
+
function toolFailureName(error) {
|
|
1996
|
+
const name = error && typeof error === "object" ? error.name : void 0;
|
|
1997
|
+
return typeof name === "string" && name ? name.slice(0, 128) : "ToolError";
|
|
1998
|
+
}
|
|
1999
|
+
var PARSE_FAILURE_PREFIX = "Failed to parse AgentScript code: ";
|
|
2000
|
+
var PARSE_POSITION_RE = /\((\d+):(\d+)\)$/;
|
|
2001
|
+
function parseFailureOf(message) {
|
|
2002
|
+
const match = PARSE_POSITION_RE.exec(message);
|
|
2003
|
+
const line = match ? Number(match[1]) - 1 : 0;
|
|
2004
|
+
if (!match || line < 1) return { message };
|
|
2005
|
+
const column = Number(match[2]);
|
|
2006
|
+
return { message: `${message.slice(0, match.index)}(${line}:${column})`, location: { line, column } };
|
|
2007
|
+
}
|
|
1867
2008
|
var ScriptTooLargeError = class extends Error {
|
|
1868
2009
|
code = "SCRIPT_TOO_LARGE";
|
|
1869
2010
|
scriptLength;
|
|
@@ -1901,8 +2042,20 @@ var EnclaveService = class {
|
|
|
1901
2042
|
throw new ScriptTooLargeError(code.length, maxLength);
|
|
1902
2043
|
}
|
|
1903
2044
|
}
|
|
2045
|
+
const toolFailures = [];
|
|
1904
2046
|
const toolHandler = async (toolName, args) => {
|
|
1905
|
-
|
|
2047
|
+
try {
|
|
2048
|
+
return await environment.callTool(toolName, args);
|
|
2049
|
+
} catch (error) {
|
|
2050
|
+
const code2 = error?.code;
|
|
2051
|
+
toolFailures.push({
|
|
2052
|
+
toolName,
|
|
2053
|
+
name: toolFailureName(error),
|
|
2054
|
+
message: toolFailureMessage(error, toolName),
|
|
2055
|
+
...typeof code2 === "string" ? { code: code2 } : {}
|
|
2056
|
+
});
|
|
2057
|
+
throw error;
|
|
2058
|
+
}
|
|
1906
2059
|
};
|
|
1907
2060
|
const sidecar = this.sidecarOptions.enabled ? {
|
|
1908
2061
|
enabled: true,
|
|
@@ -1912,8 +2065,29 @@ var EnclaveService = class {
|
|
|
1912
2065
|
maxResolvedSize: this.sidecarOptions.maxResolvedSize,
|
|
1913
2066
|
allowComposites: this.sidecarOptions.allowComposites
|
|
1914
2067
|
} : void 0;
|
|
1915
|
-
const
|
|
1916
|
-
const
|
|
2068
|
+
const { mcpLog, mcpNotify } = environment;
|
|
2069
|
+
const globals = {
|
|
2070
|
+
// Provide getTool as a custom global
|
|
2071
|
+
getTool: environment.getTool,
|
|
2072
|
+
// Provide logging functions if available
|
|
2073
|
+
...mcpLog ? {
|
|
2074
|
+
mcpLog: (level, message, metadata) => {
|
|
2075
|
+
mcpLog(level, message, metadata);
|
|
2076
|
+
logs.push(`[mcp:${level}] ${message}`);
|
|
2077
|
+
}
|
|
2078
|
+
} : {},
|
|
2079
|
+
...mcpNotify ? {
|
|
2080
|
+
mcpNotify: (event, payload) => {
|
|
2081
|
+
mcpNotify(event, payload);
|
|
2082
|
+
logs.push(`[notify] ${event}`);
|
|
2083
|
+
}
|
|
2084
|
+
} : {}
|
|
2085
|
+
// Note: enclave-vm v2.0.0+ provides its own __safe_console internally with rate limiting
|
|
2086
|
+
// and output size limits. Passing console in globals causes "Cannot redefine property"
|
|
2087
|
+
// errors due to Double VM architecture. Console output from user scripts goes to stdout
|
|
2088
|
+
// via enclave's internal console, not to this logs array. Only mcpLog/mcpNotify are captured.
|
|
2089
|
+
};
|
|
2090
|
+
const options = {
|
|
1917
2091
|
timeout: this.vmOptions.timeoutMs,
|
|
1918
2092
|
maxToolCalls: this.vmOptions.maxSteps || 100,
|
|
1919
2093
|
maxIterations: 1e4,
|
|
@@ -1923,33 +2097,20 @@ var EnclaveService = class {
|
|
|
1923
2097
|
validate: true,
|
|
1924
2098
|
transform: true,
|
|
1925
2099
|
sidecar,
|
|
2100
|
+
// Error stacks name no host file, whatever stage raised them.
|
|
2101
|
+
sanitizeStackTraces: true,
|
|
1926
2102
|
// Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
|
|
1927
2103
|
allowFunctionsInGlobals: true,
|
|
1928
|
-
globals
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
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
|
-
});
|
|
2104
|
+
globals,
|
|
2105
|
+
// Tool namespaces (`acme.getUser()` for a tool named `acme.getUser`) are built by the sandbox
|
|
2106
|
+
// itself: a method call is exactly `callTool('acme.getUser', args, options)`, with the
|
|
2107
|
+
// sandbox's tool-call cap, rate limit, suspicious-sequence checks and `throwOnError`.
|
|
2108
|
+
toolNamespaces: toSandboxToolNamespaces(environment.toolNamespaces, Object.keys(globals))
|
|
2109
|
+
};
|
|
2110
|
+
const enclave = createEnclave(options);
|
|
1950
2111
|
try {
|
|
1951
|
-
const result = await enclave.run(
|
|
1952
|
-
return this.mapEnclaveResult(result, logs);
|
|
2112
|
+
const result = await enclave.run(code);
|
|
2113
|
+
return this.mapEnclaveResult(result, logs, toolFailures, code);
|
|
1953
2114
|
} finally {
|
|
1954
2115
|
enclave.dispose();
|
|
1955
2116
|
}
|
|
@@ -1957,58 +2118,58 @@ var EnclaveService = class {
|
|
|
1957
2118
|
/**
|
|
1958
2119
|
* Map Enclave ExecutionResult to EnclaveExecutionResult
|
|
1959
2120
|
*/
|
|
1960
|
-
mapEnclaveResult(result, logs) {
|
|
2121
|
+
mapEnclaveResult(result, logs, toolFailures = [], script = "") {
|
|
2122
|
+
const stats = {
|
|
2123
|
+
duration: result.stats.duration,
|
|
2124
|
+
toolCallCount: result.stats.toolCallCount,
|
|
2125
|
+
iterationCount: result.stats.iterationCount
|
|
2126
|
+
};
|
|
1961
2127
|
if (result.success) {
|
|
2128
|
+
return { success: true, result: result.value, logs, timedOut: false, stats };
|
|
2129
|
+
}
|
|
2130
|
+
const error = result.error ?? { name: "Error", message: "Script execution failed" };
|
|
2131
|
+
if (error.code === "ENCLAVE_ERROR" && error.message?.startsWith(PARSE_FAILURE_PREFIX)) {
|
|
2132
|
+
const { message, location } = parseFailureOf(error.message);
|
|
1962
2133
|
return {
|
|
1963
|
-
success:
|
|
1964
|
-
|
|
2134
|
+
success: false,
|
|
2135
|
+
error: { message, name: "SyntaxError", code: "SYNTAX_ERROR", ...location ? { location } : {} },
|
|
1965
2136
|
logs,
|
|
1966
2137
|
timedOut: false,
|
|
1967
|
-
stats
|
|
1968
|
-
duration: result.stats.duration,
|
|
1969
|
-
toolCallCount: result.stats.toolCallCount,
|
|
1970
|
-
iterationCount: result.stats.iterationCount
|
|
1971
|
-
}
|
|
2138
|
+
stats
|
|
1972
2139
|
};
|
|
1973
2140
|
}
|
|
1974
|
-
const
|
|
1975
|
-
|
|
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))];
|
|
2141
|
+
const toolFailure = [...toolFailures].reverse().find((failure) => failure.message === error.message && failure.name === error.name);
|
|
2142
|
+
if (toolFailure) {
|
|
1979
2143
|
return {
|
|
1980
2144
|
success: false,
|
|
1981
2145
|
error: {
|
|
1982
2146
|
message: error.message,
|
|
1983
|
-
name:
|
|
1984
|
-
code:
|
|
1985
|
-
|
|
2147
|
+
name: error.name,
|
|
2148
|
+
...toolFailure.code ? { code: toolFailure.code } : {},
|
|
2149
|
+
toolName: toolFailure.toolName
|
|
1986
2150
|
},
|
|
1987
2151
|
logs,
|
|
1988
2152
|
timedOut: false,
|
|
1989
|
-
stats
|
|
1990
|
-
duration: result.stats.duration,
|
|
1991
|
-
toolCallCount: result.stats.toolCallCount,
|
|
1992
|
-
iterationCount: result.stats.iterationCount
|
|
1993
|
-
}
|
|
2153
|
+
stats
|
|
1994
2154
|
};
|
|
1995
2155
|
}
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
2156
|
+
const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
|
|
2157
|
+
if (error.code === "VALIDATION_ERROR") {
|
|
2158
|
+
const issues = error.data?.issues ?? [];
|
|
2159
|
+
const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
|
|
1999
2160
|
return {
|
|
2000
2161
|
success: false,
|
|
2001
2162
|
error: {
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2163
|
+
// The enclave names lines of the code it validated (the script wrapped and printed again),
|
|
2164
|
+
// not the script's own.
|
|
2165
|
+
message: withScriptLines(error.message, script),
|
|
2166
|
+
name: "ValidationError",
|
|
2005
2167
|
code: error.code,
|
|
2006
|
-
|
|
2007
|
-
toolInput: errorData?.["toolInput"],
|
|
2008
|
-
details: errorData?.["details"]
|
|
2168
|
+
...blockedPatterns.length > 0 ? { blockedPatterns } : {}
|
|
2009
2169
|
},
|
|
2010
2170
|
logs,
|
|
2011
|
-
timedOut
|
|
2171
|
+
timedOut: false,
|
|
2172
|
+
stats
|
|
2012
2173
|
};
|
|
2013
2174
|
}
|
|
2014
2175
|
return {
|
|
@@ -2021,11 +2182,7 @@ var EnclaveService = class {
|
|
|
2021
2182
|
},
|
|
2022
2183
|
logs,
|
|
2023
2184
|
timedOut,
|
|
2024
|
-
stats
|
|
2025
|
-
duration: result.stats.duration,
|
|
2026
|
-
toolCallCount: result.stats.toolCallCount,
|
|
2027
|
-
iterationCount: result.stats.iterationCount
|
|
2028
|
-
}
|
|
2185
|
+
stats
|
|
2029
2186
|
};
|
|
2030
2187
|
}
|
|
2031
2188
|
};
|
|
@@ -2036,6 +2193,16 @@ EnclaveService = __decorateClass([
|
|
|
2036
2193
|
scope: ProviderScope3.GLOBAL
|
|
2037
2194
|
})
|
|
2038
2195
|
], EnclaveService);
|
|
2196
|
+
function createEnclave(options) {
|
|
2197
|
+
try {
|
|
2198
|
+
return new Enclave(options);
|
|
2199
|
+
} catch (error) {
|
|
2200
|
+
if (!options.toolNamespaces || !(error instanceof TypeError) || !error.message.startsWith("Invalid toolNamespaces")) {
|
|
2201
|
+
throw error;
|
|
2202
|
+
}
|
|
2203
|
+
return new Enclave({ ...options, toolNamespaces: void 0 });
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2039
2206
|
|
|
2040
2207
|
// plugins/plugin-codecall/src/tools/search.tool.ts
|
|
2041
2208
|
import { getCallSurface, Tool, ToolContext } from "@frontmcp/sdk";
|
|
@@ -3136,7 +3303,11 @@ var runtimeErrorPayloadSchema = z7.object({
|
|
|
3136
3303
|
var toolErrorPayloadSchema = z7.object({
|
|
3137
3304
|
source: z7.literal("tool"),
|
|
3138
3305
|
toolName: z7.string(),
|
|
3139
|
-
|
|
3306
|
+
/**
|
|
3307
|
+
* @deprecated Never set: the sandbox resolves sidecar references in a call's arguments before
|
|
3308
|
+
* the tool sees them, so they may hold data the script itself never had.
|
|
3309
|
+
*/
|
|
3310
|
+
toolInput: z7.unknown().optional(),
|
|
3140
3311
|
message: z7.string(),
|
|
3141
3312
|
code: z7.string().optional(),
|
|
3142
3313
|
/** @deprecated Never set: the enclave's error data is not sanitized for clients. */
|
|
@@ -3355,6 +3526,16 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3355
3526
|
}
|
|
3356
3527
|
if (!executionResult.success) {
|
|
3357
3528
|
const error = executionResult.error;
|
|
3529
|
+
if (error.code === "SYNTAX_ERROR") {
|
|
3530
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
3531
|
+
return {
|
|
3532
|
+
status: "syntax_error",
|
|
3533
|
+
error: {
|
|
3534
|
+
message: toClientErrorMessage(error.message) || "Syntax error in script",
|
|
3535
|
+
...error.location ? { location: error.location } : {}
|
|
3536
|
+
}
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3358
3539
|
if (error.code === "VALIDATION_ERROR" || error.name === "ValidationError") {
|
|
3359
3540
|
audit?.logSecurityAstBlocked(executionId, error.blockedPatterns?.join(", ") ?? "UNKNOWN", error.message);
|
|
3360
3541
|
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
@@ -3373,10 +3554,10 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3373
3554
|
error: {
|
|
3374
3555
|
source: "tool",
|
|
3375
3556
|
toolName: error.toolName,
|
|
3376
|
-
toolInput: error.toolInput,
|
|
3377
3557
|
message: toClientErrorMessage(error.message),
|
|
3378
3558
|
code: error.code
|
|
3379
|
-
// No `details`: the enclave's error data is not sanitized for clients.
|
|
3559
|
+
// No `details`: the enclave's error data is not sanitized for clients. No `toolInput`:
|
|
3560
|
+
// the arguments the tool saw may hold sidecar-resolved data the script never had.
|
|
3380
3561
|
}
|
|
3381
3562
|
};
|
|
3382
3563
|
}
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-codecall",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.5",
|
|
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",
|
|
@@ -48,14 +48,16 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"@enclave-vm/ast": "^2.15.3",
|
|
51
52
|
"@enclave-vm/core": "^2.15.3",
|
|
52
|
-
"@frontmcp/protocol": "1.8.
|
|
53
|
-
"@frontmcp/sdk": "1.8.
|
|
53
|
+
"@frontmcp/protocol": "1.8.5",
|
|
54
|
+
"@frontmcp/sdk": "1.8.5",
|
|
55
|
+
"acorn": "^8.15.0",
|
|
54
56
|
"vectoriadb": "^2.3.2"
|
|
55
57
|
},
|
|
56
58
|
"peerDependencies": {
|
|
57
|
-
"@frontmcp/lazy-zod": "1.8.
|
|
58
|
-
"@frontmcp/plugin-cache": "1.8.
|
|
59
|
+
"@frontmcp/lazy-zod": "1.8.5",
|
|
60
|
+
"@frontmcp/plugin-cache": "1.8.5"
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|
|
61
63
|
"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,164 @@ function buildToolNamespaces(tools) {
|
|
|
1860
1884
|
}
|
|
1861
1885
|
return { namespaces, skipped };
|
|
1862
1886
|
}
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
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 (!
|
|
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
|
|
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 (
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1899
|
+
if (typeof toolName !== "string" || !isBindableMethod(ns, method, toolName)) continue;
|
|
1900
|
+
bucket[method] = toolName;
|
|
1901
|
+
methodCount += 1;
|
|
1902
|
+
}
|
|
1903
|
+
if (methodCount > 0) {
|
|
1904
|
+
bindable[ns] = bucket;
|
|
1905
|
+
count += 1;
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
return count > 0 ? bindable : void 0;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
// plugins/plugin-codecall/src/utils/script-lines.ts
|
|
1912
|
+
var import_ast = require("@enclave-vm/ast");
|
|
1913
|
+
var acorn = __toESM(require("acorn"));
|
|
1914
|
+
var PARSE_OPTIONS = { ecmaVersion: "latest", locations: true };
|
|
1915
|
+
var NON_CHILD_KEYS = /* @__PURE__ */ new Set(["type", "loc", "start", "end", "range"]);
|
|
1916
|
+
function isNode(value) {
|
|
1917
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1918
|
+
}
|
|
1919
|
+
function isAddedByTransform(node) {
|
|
1920
|
+
if (node.type === "VariableDeclaration") {
|
|
1921
|
+
const [declaration] = node["declarations"] ?? [];
|
|
1922
|
+
const id = declaration?.["id"];
|
|
1923
|
+
return typeof id?.["name"] === "string" && id["name"].startsWith("__iter_");
|
|
1924
|
+
}
|
|
1925
|
+
if (node.type === "IfStatement") {
|
|
1926
|
+
const right = node["test"]?.["right"];
|
|
1927
|
+
return right?.type === "Identifier" && right["name"] === "__maxIterations";
|
|
1928
|
+
}
|
|
1929
|
+
return false;
|
|
1930
|
+
}
|
|
1931
|
+
function parseScript(script) {
|
|
1932
|
+
try {
|
|
1933
|
+
return { body: parseBody(script, "script"), offset: 0 };
|
|
1934
|
+
} catch {
|
|
1935
|
+
try {
|
|
1936
|
+
return { body: parseBody(script, "module"), offset: 0 };
|
|
1937
|
+
} catch {
|
|
1938
|
+
const wrapped = parseBody(`async function __temp__() {
|
|
1939
|
+
${script}
|
|
1940
|
+
}`, "script");
|
|
1941
|
+
const fn = wrapped[0];
|
|
1942
|
+
const block = fn?.["body"];
|
|
1943
|
+
return { body: block?.["body"] ?? [], offset: 1 };
|
|
1895
1944
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
function parseBody(code, sourceType) {
|
|
1948
|
+
const program = acorn.parse(code, { ...PARSE_OPTIONS, sourceType });
|
|
1949
|
+
return program["body"] ?? [];
|
|
1950
|
+
}
|
|
1951
|
+
function treesOf(script) {
|
|
1952
|
+
try {
|
|
1953
|
+
const wrap = !(0, import_ast.isWrappedInMain)(script);
|
|
1954
|
+
const printed = parseBody((0, import_ast.transformAgentScript)(script, { wrapInMain: wrap }), "script");
|
|
1955
|
+
const { body, offset } = parseScript(script);
|
|
1956
|
+
if (!wrap) return { printed, script: body, offset };
|
|
1957
|
+
const main = printed[0]?.["body"];
|
|
1958
|
+
return { printed: main?.["body"] ?? [], script: body, offset };
|
|
1959
|
+
} catch {
|
|
1960
|
+
return void 0;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function pairLists(printed, script, depth, out, offset) {
|
|
1964
|
+
const kept = printed.filter((node) => !(isNode(node) && isAddedByTransform(node)));
|
|
1965
|
+
if (kept.length !== script.length) return;
|
|
1966
|
+
kept.forEach((node, index) => {
|
|
1967
|
+
const own = script[index];
|
|
1968
|
+
if (isNode(node) && isNode(own)) pairNodes(node, own, depth, out, offset);
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
function pairNodes(printed, script, depth, out, offset) {
|
|
1972
|
+
if (printed.type === "ReturnStatement" && script.type === "ExpressionStatement") {
|
|
1973
|
+
record(printed, script, depth, out, offset);
|
|
1974
|
+
const argument = printed["argument"];
|
|
1975
|
+
const expression = script["expression"];
|
|
1976
|
+
if (isNode(argument) && isNode(expression)) pairNodes(argument, expression, depth + 1, out, offset);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
if (printed.type !== script.type) return;
|
|
1980
|
+
record(printed, script, depth, out, offset);
|
|
1981
|
+
for (const key of Object.keys(printed)) {
|
|
1982
|
+
if (NON_CHILD_KEYS.has(key)) continue;
|
|
1983
|
+
const printedChild = printed[key];
|
|
1984
|
+
const scriptChild = script[key];
|
|
1985
|
+
if (Array.isArray(printedChild) && Array.isArray(scriptChild)) {
|
|
1986
|
+
pairLists(printedChild, scriptChild, depth + 1, out, offset);
|
|
1987
|
+
} else if (isNode(printedChild) && isNode(scriptChild)) {
|
|
1988
|
+
pairNodes(printedChild, scriptChild, depth + 1, out, offset);
|
|
1898
1989
|
}
|
|
1899
1990
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1991
|
+
}
|
|
1992
|
+
function record(printed, script, depth, out, offset) {
|
|
1993
|
+
if (!printed.loc || !script.loc) return;
|
|
1994
|
+
out.push({
|
|
1995
|
+
printedStart: printed.loc.start.line,
|
|
1996
|
+
printedEnd: printed.loc.end.line,
|
|
1997
|
+
scriptLine: script.loc.start.line - offset,
|
|
1998
|
+
depth
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
function validatedLineMapper(script) {
|
|
2002
|
+
const trees = treesOf(script);
|
|
2003
|
+
if (!trees) return () => void 0;
|
|
2004
|
+
const pairs = [];
|
|
2005
|
+
pairLists(trees.printed, trees.script, 0, pairs, trees.offset);
|
|
2006
|
+
return (printedLine) => {
|
|
2007
|
+
const starting = pairs.filter((pair) => pair.printedStart === printedLine);
|
|
2008
|
+
const candidates = starting.length > 0 ? starting : pairs.filter((pair) => pair.printedStart <= printedLine && printedLine <= pair.printedEnd);
|
|
2009
|
+
const best = candidates.reduce(
|
|
2010
|
+
(deepest, pair) => !deepest || pair.depth > deepest.depth ? pair : deepest,
|
|
2011
|
+
void 0
|
|
2012
|
+
);
|
|
2013
|
+
return best && best.scriptLine >= 1 ? best.scriptLine : void 0;
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
var ISSUE_LINE_RE = / \(line (\d+)\)/g;
|
|
2017
|
+
function withScriptLines(message, script) {
|
|
2018
|
+
if (!message.includes("(line ")) return message;
|
|
2019
|
+
const lineOf = validatedLineMapper(script);
|
|
2020
|
+
return message.replace(ISSUE_LINE_RE, (_match, printed) => {
|
|
2021
|
+
const line = lineOf(Number(printed));
|
|
2022
|
+
return line === void 0 ? "" : ` (line ${line})`;
|
|
2023
|
+
});
|
|
1903
2024
|
}
|
|
1904
2025
|
|
|
1905
2026
|
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
2027
|
+
function toolFailureMessage(error, toolName) {
|
|
2028
|
+
if (typeof error === "string" && error) return error;
|
|
2029
|
+
const message = error?.message;
|
|
2030
|
+
return typeof message === "string" && message ? message : `Tool call failed: ${toolName}`;
|
|
2031
|
+
}
|
|
2032
|
+
function toolFailureName(error) {
|
|
2033
|
+
const name = error && typeof error === "object" ? error.name : void 0;
|
|
2034
|
+
return typeof name === "string" && name ? name.slice(0, 128) : "ToolError";
|
|
2035
|
+
}
|
|
2036
|
+
var PARSE_FAILURE_PREFIX = "Failed to parse AgentScript code: ";
|
|
2037
|
+
var PARSE_POSITION_RE = /\((\d+):(\d+)\)$/;
|
|
2038
|
+
function parseFailureOf(message) {
|
|
2039
|
+
const match = PARSE_POSITION_RE.exec(message);
|
|
2040
|
+
const line = match ? Number(match[1]) - 1 : 0;
|
|
2041
|
+
if (!match || line < 1) return { message };
|
|
2042
|
+
const column = Number(match[2]);
|
|
2043
|
+
return { message: `${message.slice(0, match.index)}(${line}:${column})`, location: { line, column } };
|
|
2044
|
+
}
|
|
1906
2045
|
var ScriptTooLargeError = class extends Error {
|
|
1907
2046
|
code = "SCRIPT_TOO_LARGE";
|
|
1908
2047
|
scriptLength;
|
|
@@ -1940,8 +2079,20 @@ var EnclaveService = class {
|
|
|
1940
2079
|
throw new ScriptTooLargeError(code.length, maxLength);
|
|
1941
2080
|
}
|
|
1942
2081
|
}
|
|
2082
|
+
const toolFailures = [];
|
|
1943
2083
|
const toolHandler = async (toolName, args) => {
|
|
1944
|
-
|
|
2084
|
+
try {
|
|
2085
|
+
return await environment.callTool(toolName, args);
|
|
2086
|
+
} catch (error) {
|
|
2087
|
+
const code2 = error?.code;
|
|
2088
|
+
toolFailures.push({
|
|
2089
|
+
toolName,
|
|
2090
|
+
name: toolFailureName(error),
|
|
2091
|
+
message: toolFailureMessage(error, toolName),
|
|
2092
|
+
...typeof code2 === "string" ? { code: code2 } : {}
|
|
2093
|
+
});
|
|
2094
|
+
throw error;
|
|
2095
|
+
}
|
|
1945
2096
|
};
|
|
1946
2097
|
const sidecar = this.sidecarOptions.enabled ? {
|
|
1947
2098
|
enabled: true,
|
|
@@ -1951,8 +2102,29 @@ var EnclaveService = class {
|
|
|
1951
2102
|
maxResolvedSize: this.sidecarOptions.maxResolvedSize,
|
|
1952
2103
|
allowComposites: this.sidecarOptions.allowComposites
|
|
1953
2104
|
} : void 0;
|
|
1954
|
-
const
|
|
1955
|
-
const
|
|
2105
|
+
const { mcpLog, mcpNotify } = environment;
|
|
2106
|
+
const globals = {
|
|
2107
|
+
// Provide getTool as a custom global
|
|
2108
|
+
getTool: environment.getTool,
|
|
2109
|
+
// Provide logging functions if available
|
|
2110
|
+
...mcpLog ? {
|
|
2111
|
+
mcpLog: (level, message, metadata) => {
|
|
2112
|
+
mcpLog(level, message, metadata);
|
|
2113
|
+
logs.push(`[mcp:${level}] ${message}`);
|
|
2114
|
+
}
|
|
2115
|
+
} : {},
|
|
2116
|
+
...mcpNotify ? {
|
|
2117
|
+
mcpNotify: (event, payload) => {
|
|
2118
|
+
mcpNotify(event, payload);
|
|
2119
|
+
logs.push(`[notify] ${event}`);
|
|
2120
|
+
}
|
|
2121
|
+
} : {}
|
|
2122
|
+
// Note: enclave-vm v2.0.0+ provides its own __safe_console internally with rate limiting
|
|
2123
|
+
// and output size limits. Passing console in globals causes "Cannot redefine property"
|
|
2124
|
+
// errors due to Double VM architecture. Console output from user scripts goes to stdout
|
|
2125
|
+
// via enclave's internal console, not to this logs array. Only mcpLog/mcpNotify are captured.
|
|
2126
|
+
};
|
|
2127
|
+
const options = {
|
|
1956
2128
|
timeout: this.vmOptions.timeoutMs,
|
|
1957
2129
|
maxToolCalls: this.vmOptions.maxSteps || 100,
|
|
1958
2130
|
maxIterations: 1e4,
|
|
@@ -1962,33 +2134,20 @@ var EnclaveService = class {
|
|
|
1962
2134
|
validate: true,
|
|
1963
2135
|
transform: true,
|
|
1964
2136
|
sidecar,
|
|
2137
|
+
// Error stacks name no host file, whatever stage raised them.
|
|
2138
|
+
sanitizeStackTraces: true,
|
|
1965
2139
|
// Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
|
|
1966
2140
|
allowFunctionsInGlobals: true,
|
|
1967
|
-
globals
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
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
|
-
});
|
|
2141
|
+
globals,
|
|
2142
|
+
// Tool namespaces (`acme.getUser()` for a tool named `acme.getUser`) are built by the sandbox
|
|
2143
|
+
// itself: a method call is exactly `callTool('acme.getUser', args, options)`, with the
|
|
2144
|
+
// sandbox's tool-call cap, rate limit, suspicious-sequence checks and `throwOnError`.
|
|
2145
|
+
toolNamespaces: toSandboxToolNamespaces(environment.toolNamespaces, Object.keys(globals))
|
|
2146
|
+
};
|
|
2147
|
+
const enclave = createEnclave(options);
|
|
1989
2148
|
try {
|
|
1990
|
-
const result = await enclave.run(
|
|
1991
|
-
return this.mapEnclaveResult(result, logs);
|
|
2149
|
+
const result = await enclave.run(code);
|
|
2150
|
+
return this.mapEnclaveResult(result, logs, toolFailures, code);
|
|
1992
2151
|
} finally {
|
|
1993
2152
|
enclave.dispose();
|
|
1994
2153
|
}
|
|
@@ -1996,58 +2155,58 @@ var EnclaveService = class {
|
|
|
1996
2155
|
/**
|
|
1997
2156
|
* Map Enclave ExecutionResult to EnclaveExecutionResult
|
|
1998
2157
|
*/
|
|
1999
|
-
mapEnclaveResult(result, logs) {
|
|
2158
|
+
mapEnclaveResult(result, logs, toolFailures = [], script = "") {
|
|
2159
|
+
const stats = {
|
|
2160
|
+
duration: result.stats.duration,
|
|
2161
|
+
toolCallCount: result.stats.toolCallCount,
|
|
2162
|
+
iterationCount: result.stats.iterationCount
|
|
2163
|
+
};
|
|
2000
2164
|
if (result.success) {
|
|
2165
|
+
return { success: true, result: result.value, logs, timedOut: false, stats };
|
|
2166
|
+
}
|
|
2167
|
+
const error = result.error ?? { name: "Error", message: "Script execution failed" };
|
|
2168
|
+
if (error.code === "ENCLAVE_ERROR" && error.message?.startsWith(PARSE_FAILURE_PREFIX)) {
|
|
2169
|
+
const { message, location } = parseFailureOf(error.message);
|
|
2001
2170
|
return {
|
|
2002
|
-
success:
|
|
2003
|
-
|
|
2171
|
+
success: false,
|
|
2172
|
+
error: { message, name: "SyntaxError", code: "SYNTAX_ERROR", ...location ? { location } : {} },
|
|
2004
2173
|
logs,
|
|
2005
2174
|
timedOut: false,
|
|
2006
|
-
stats
|
|
2007
|
-
duration: result.stats.duration,
|
|
2008
|
-
toolCallCount: result.stats.toolCallCount,
|
|
2009
|
-
iterationCount: result.stats.iterationCount
|
|
2010
|
-
}
|
|
2175
|
+
stats
|
|
2011
2176
|
};
|
|
2012
2177
|
}
|
|
2013
|
-
const
|
|
2014
|
-
|
|
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))];
|
|
2178
|
+
const toolFailure = [...toolFailures].reverse().find((failure) => failure.message === error.message && failure.name === error.name);
|
|
2179
|
+
if (toolFailure) {
|
|
2018
2180
|
return {
|
|
2019
2181
|
success: false,
|
|
2020
2182
|
error: {
|
|
2021
2183
|
message: error.message,
|
|
2022
|
-
name:
|
|
2023
|
-
code:
|
|
2024
|
-
|
|
2184
|
+
name: error.name,
|
|
2185
|
+
...toolFailure.code ? { code: toolFailure.code } : {},
|
|
2186
|
+
toolName: toolFailure.toolName
|
|
2025
2187
|
},
|
|
2026
2188
|
logs,
|
|
2027
2189
|
timedOut: false,
|
|
2028
|
-
stats
|
|
2029
|
-
duration: result.stats.duration,
|
|
2030
|
-
toolCallCount: result.stats.toolCallCount,
|
|
2031
|
-
iterationCount: result.stats.iterationCount
|
|
2032
|
-
}
|
|
2190
|
+
stats
|
|
2033
2191
|
};
|
|
2034
2192
|
}
|
|
2035
|
-
const
|
|
2036
|
-
|
|
2037
|
-
|
|
2193
|
+
const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
|
|
2194
|
+
if (error.code === "VALIDATION_ERROR") {
|
|
2195
|
+
const issues = error.data?.issues ?? [];
|
|
2196
|
+
const blockedPatterns = [...new Set(issues.map((issue) => issue.code).filter((code) => !!code))];
|
|
2038
2197
|
return {
|
|
2039
2198
|
success: false,
|
|
2040
2199
|
error: {
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2200
|
+
// The enclave names lines of the code it validated (the script wrapped and printed again),
|
|
2201
|
+
// not the script's own.
|
|
2202
|
+
message: withScriptLines(error.message, script),
|
|
2203
|
+
name: "ValidationError",
|
|
2044
2204
|
code: error.code,
|
|
2045
|
-
|
|
2046
|
-
toolInput: errorData?.["toolInput"],
|
|
2047
|
-
details: errorData?.["details"]
|
|
2205
|
+
...blockedPatterns.length > 0 ? { blockedPatterns } : {}
|
|
2048
2206
|
},
|
|
2049
2207
|
logs,
|
|
2050
|
-
timedOut
|
|
2208
|
+
timedOut: false,
|
|
2209
|
+
stats
|
|
2051
2210
|
};
|
|
2052
2211
|
}
|
|
2053
2212
|
return {
|
|
@@ -2060,11 +2219,7 @@ var EnclaveService = class {
|
|
|
2060
2219
|
},
|
|
2061
2220
|
logs,
|
|
2062
2221
|
timedOut,
|
|
2063
|
-
stats
|
|
2064
|
-
duration: result.stats.duration,
|
|
2065
|
-
toolCallCount: result.stats.toolCallCount,
|
|
2066
|
-
iterationCount: result.stats.iterationCount
|
|
2067
|
-
}
|
|
2222
|
+
stats
|
|
2068
2223
|
};
|
|
2069
2224
|
}
|
|
2070
2225
|
};
|
|
@@ -2075,6 +2230,16 @@ EnclaveService = __decorateClass([
|
|
|
2075
2230
|
scope: import_sdk3.ProviderScope.GLOBAL
|
|
2076
2231
|
})
|
|
2077
2232
|
], EnclaveService);
|
|
2233
|
+
function createEnclave(options) {
|
|
2234
|
+
try {
|
|
2235
|
+
return new import_core.Enclave(options);
|
|
2236
|
+
} catch (error) {
|
|
2237
|
+
if (!options.toolNamespaces || !(error instanceof TypeError) || !error.message.startsWith("Invalid toolNamespaces")) {
|
|
2238
|
+
throw error;
|
|
2239
|
+
}
|
|
2240
|
+
return new import_core.Enclave({ ...options, toolNamespaces: void 0 });
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2078
2243
|
|
|
2079
2244
|
// plugins/plugin-codecall/src/tools/search.tool.ts
|
|
2080
2245
|
var import_sdk4 = require("@frontmcp/sdk");
|
|
@@ -3175,7 +3340,11 @@ var runtimeErrorPayloadSchema = import_lazy_zod7.z.object({
|
|
|
3175
3340
|
var toolErrorPayloadSchema = import_lazy_zod7.z.object({
|
|
3176
3341
|
source: import_lazy_zod7.z.literal("tool"),
|
|
3177
3342
|
toolName: import_lazy_zod7.z.string(),
|
|
3178
|
-
|
|
3343
|
+
/**
|
|
3344
|
+
* @deprecated Never set: the sandbox resolves sidecar references in a call's arguments before
|
|
3345
|
+
* the tool sees them, so they may hold data the script itself never had.
|
|
3346
|
+
*/
|
|
3347
|
+
toolInput: import_lazy_zod7.z.unknown().optional(),
|
|
3179
3348
|
message: import_lazy_zod7.z.string(),
|
|
3180
3349
|
code: import_lazy_zod7.z.string().optional(),
|
|
3181
3350
|
/** @deprecated Never set: the enclave's error data is not sanitized for clients. */
|
|
@@ -3394,6 +3563,16 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3394
3563
|
}
|
|
3395
3564
|
if (!executionResult.success) {
|
|
3396
3565
|
const error = executionResult.error;
|
|
3566
|
+
if (error.code === "SYNTAX_ERROR") {
|
|
3567
|
+
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
3568
|
+
return {
|
|
3569
|
+
status: "syntax_error",
|
|
3570
|
+
error: {
|
|
3571
|
+
message: toClientErrorMessage(error.message) || "Syntax error in script",
|
|
3572
|
+
...error.location ? { location: error.location } : {}
|
|
3573
|
+
}
|
|
3574
|
+
};
|
|
3575
|
+
}
|
|
3397
3576
|
if (error.code === "VALIDATION_ERROR" || error.name === "ValidationError") {
|
|
3398
3577
|
audit?.logSecurityAstBlocked(executionId, error.blockedPatterns?.join(", ") ?? "UNKNOWN", error.message);
|
|
3399
3578
|
audit?.logExecutionFailure(executionId, script, durationMs, error.message);
|
|
@@ -3412,10 +3591,10 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3412
3591
|
error: {
|
|
3413
3592
|
source: "tool",
|
|
3414
3593
|
toolName: error.toolName,
|
|
3415
|
-
toolInput: error.toolInput,
|
|
3416
3594
|
message: toClientErrorMessage(error.message),
|
|
3417
3595
|
code: error.code
|
|
3418
|
-
// No `details`: the enclave's error data is not sanitized for clients.
|
|
3596
|
+
// No `details`: the enclave's error data is not sanitized for clients. No `toolInput`:
|
|
3597
|
+
// the arguments the tool saw may hold sidecar-resolved data the script never had.
|
|
3419
3598
|
}
|
|
3420
3599
|
};
|
|
3421
3600
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-codecall",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.5",
|
|
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",
|
|
@@ -48,14 +48,16 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"@enclave-vm/ast": "^2.15.3",
|
|
51
52
|
"@enclave-vm/core": "^2.15.3",
|
|
52
|
-
"@frontmcp/protocol": "1.8.
|
|
53
|
-
"@frontmcp/sdk": "1.8.
|
|
53
|
+
"@frontmcp/protocol": "1.8.5",
|
|
54
|
+
"@frontmcp/sdk": "1.8.5",
|
|
55
|
+
"acorn": "^8.15.0",
|
|
54
56
|
"vectoriadb": "^2.3.2"
|
|
55
57
|
},
|
|
56
58
|
"peerDependencies": {
|
|
57
|
-
"@frontmcp/lazy-zod": "1.8.
|
|
58
|
-
"@frontmcp/plugin-cache": "1.8.
|
|
59
|
+
"@frontmcp/lazy-zod": "1.8.5",
|
|
60
|
+
"@frontmcp/plugin-cache": "1.8.5"
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|
|
61
63
|
"reflect-metadata": "^0.2.2"
|
|
@@ -12,8 +12,12 @@ export interface EnclaveExecutionResult {
|
|
|
12
12
|
stack?: string;
|
|
13
13
|
code?: string;
|
|
14
14
|
toolName?: string;
|
|
15
|
-
toolInput?: unknown;
|
|
16
15
|
details?: unknown;
|
|
16
|
+
/** For a script that doesn't parse (`code: 'SYNTAX_ERROR'`): where, in the script's own lines. */
|
|
17
|
+
location?: {
|
|
18
|
+
line: number;
|
|
19
|
+
column: number;
|
|
20
|
+
};
|
|
17
21
|
/**
|
|
18
22
|
* Distinct AST-validation issue codes, for the `codecall:security:ast-blocked` audit event.
|
|
19
23
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enclave.service.d.ts","sourceRoot":"","sources":["../../src/services/enclave.service.ts"],"names":[],"mappings":"
|
|
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;AAIhE;;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,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;CAyFzB"}
|
|
@@ -44,7 +44,7 @@ export declare const codeCallToolErrorResultSchema: import("@frontmcp/lazy-zod")
|
|
|
44
44
|
error: import("@frontmcp/lazy-zod").ZodObject<{
|
|
45
45
|
source: import("@frontmcp/lazy-zod").ZodLiteral<"tool">;
|
|
46
46
|
toolName: import("@frontmcp/lazy-zod").ZodString;
|
|
47
|
-
toolInput: import("@frontmcp/lazy-zod").ZodUnknown
|
|
47
|
+
toolInput: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodUnknown>;
|
|
48
48
|
message: import("@frontmcp/lazy-zod").ZodString;
|
|
49
49
|
code: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodString>;
|
|
50
50
|
details: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodUnknown>;
|
|
@@ -91,7 +91,7 @@ export declare const executeToolOutputSchema: import("@frontmcp/lazy-zod").ZodDi
|
|
|
91
91
|
error: import("@frontmcp/lazy-zod").ZodObject<{
|
|
92
92
|
source: import("@frontmcp/lazy-zod").ZodLiteral<"tool">;
|
|
93
93
|
toolName: import("@frontmcp/lazy-zod").ZodString;
|
|
94
|
-
toolInput: import("@frontmcp/lazy-zod").ZodUnknown
|
|
94
|
+
toolInput: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodUnknown>;
|
|
95
95
|
message: import("@frontmcp/lazy-zod").ZodString;
|
|
96
96
|
code: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodString>;
|
|
97
97
|
details: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodUnknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.schema.d.ts","sourceRoot":"","sources":["../../src/tools/execute.schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AAKvC,eAAO,MAAM,sBAAsB,o7BAoBe,CAAC;AAEnD,eAAO,MAAM,sBAAsB;;;CAclC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"execute.schema.d.ts","sourceRoot":"","sources":["../../src/tools/execute.schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AAKvC,eAAO,MAAM,sBAAsB,o7BAoBe,CAAC;AAEnD,eAAO,MAAM,sBAAsB;;;CAclC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC;AAkDnF;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;gCAIjC,CAAC;AAEH,eAAO,MAAM,+BAA+B;;;;;;;;;gCAG1C,CAAC;AAEH,eAAO,MAAM,iCAAiC;;;;;;gCAG5C,CAAC;AAEH,eAAO,MAAM,gCAAgC;;;;;;;;gCAG3C,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;gCAGxC,CAAC;AAEH,eAAO,MAAM,2BAA2B;;;;;gCAGtC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CAOlC,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AACxF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAC1F,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACpF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,MAAM,MAAM,qBAAqB,GAC7B,gBAAgB,GAChB,yBAAyB,GACzB,2BAA2B,GAC3B,0BAA0B,GAC1B,uBAAuB,GACvB,qBAAqB,CAAC"}
|
|
@@ -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;
|
|
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
|
|
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)
|
|
38
|
-
*
|
|
39
|
-
* `
|
|
40
|
-
*
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
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
|
|
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,
|
|
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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A function from a line of the code the enclave validated (for this script) to the script's own
|
|
3
|
+
* line, or `undefined` for a line that is none of the script's (the wrapper, a line the loop
|
|
4
|
+
* transform added) or a script that can't be parsed.
|
|
5
|
+
*
|
|
6
|
+
* @param script The script as the caller sent it
|
|
7
|
+
*/
|
|
8
|
+
export declare function validatedLineMapper(script: string): (printedLine: number) => number | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* `message` with the lines its issues name mapped back to the script's own lines. A line that is
|
|
11
|
+
* none of the script's is dropped rather than left pointing at the wrong line.
|
|
12
|
+
*/
|
|
13
|
+
export declare function withScriptLines(message: string, script: string): string;
|
|
14
|
+
//# sourceMappingURL=script-lines.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-lines.d.ts","sourceRoot":"","sources":["../../src/utils/script-lines.ts"],"names":[],"mappings":"AA4IA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAmB/F;AAKD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOvE"}
|