@frontmcp/plugin-codecall 1.8.2 → 1.8.3
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 +14 -14
- package/codecall.symbol.d.ts.map +1 -1
- package/codecall.types.d.ts +28 -5
- package/codecall.types.d.ts.map +1 -1
- package/errors/tool-call.errors.d.ts.map +1 -1
- package/esm/index.mjs +269 -123
- package/esm/package.json +6 -6
- package/index.js +265 -119
- package/package.json +6 -6
- package/security/codecall-tool-policy.d.ts +30 -2
- package/security/codecall-tool-policy.d.ts.map +1 -1
- package/services/audit-logger.service.d.ts.map +1 -1
- package/services/enclave.service.d.ts.map +1 -1
- package/services/output-sanitizer.d.ts.map +1 -1
- package/services/tool-search.service.d.ts +8 -12
- package/services/tool-search.service.d.ts.map +1 -1
- package/tools/describe.tool.d.ts.map +1 -1
- package/tools/execute.schema.d.ts.map +1 -1
- package/tools/execute.tool.d.ts.map +1 -1
- package/tools/invoke.tool.d.ts.map +1 -1
- package/tools/search.tool.d.ts.map +1 -1
- package/utils/build-tool-namespaces.d.ts +27 -20
- package/utils/build-tool-namespaces.d.ts.map +1 -1
- package/utils/client-error-message.d.ts +11 -0
- package/utils/client-error-message.d.ts.map +1 -0
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.3",
|
|
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,14 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@enclave-vm/core": "^2.15.
|
|
52
|
-
"@frontmcp/protocol": "1.8.
|
|
53
|
-
"@frontmcp/sdk": "1.8.
|
|
51
|
+
"@enclave-vm/core": "^2.15.3",
|
|
52
|
+
"@frontmcp/protocol": "1.8.3",
|
|
53
|
+
"@frontmcp/sdk": "1.8.3",
|
|
54
54
|
"vectoriadb": "^2.3.2"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@frontmcp/lazy-zod": "1.8.
|
|
58
|
-
"@frontmcp/plugin-cache": "1.8.
|
|
57
|
+
"@frontmcp/lazy-zod": "1.8.3",
|
|
58
|
+
"@frontmcp/plugin-cache": "1.8.3"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"reflect-metadata": "^0.2.2"
|
package/index.js
CHANGED
|
@@ -413,6 +413,46 @@ function presetDefaults(preset) {
|
|
|
413
413
|
|
|
414
414
|
// plugins/plugin-codecall/src/services/audit-logger.service.ts
|
|
415
415
|
var import_sdk2 = require("@frontmcp/sdk");
|
|
416
|
+
|
|
417
|
+
// plugins/plugin-codecall/src/utils/client-error-message.ts
|
|
418
|
+
var EMBEDDED_STACK_FRAME_RE = /\n[ \t]*at\s[^\n]*/g;
|
|
419
|
+
var QUOTED_PATH_RE = /(['"`])((?:~?\/|[A-Za-z]:[\\/]|\\\\)[^\s'"`\\/][^'"`\n]*)\1/g;
|
|
420
|
+
var URL_RE = /\b[A-Za-z][A-Za-z0-9+.-]{1,31}:\/\/(?:[^\s'"`<>,;]|[,;](?!~?\/|[A-Za-z]:[\\/]|\\\\))*/g;
|
|
421
|
+
var QUERY_PATH_VALUE_RE = /([?&][^=&#]*=)(?:~?\/|[A-Za-z]:[\\/]|\\\\)[^&#]*/g;
|
|
422
|
+
var POSIX_SEGMENT = String.raw`[^\s/\\'"\x60<>|,;:()\[\]{}]+`;
|
|
423
|
+
var WINDOWS_SEGMENT = String.raw`[^\s/\\'"\x60<>|,;:*?]+`;
|
|
424
|
+
function segments(segment, separator) {
|
|
425
|
+
const spaced = `${segment}(?: ${segment}){0,2}`;
|
|
426
|
+
return `(?:(?:${spaced}${separator})+(?:${segment})?|${segment})`;
|
|
427
|
+
}
|
|
428
|
+
var UNQUOTED_PATH_RE = new RegExp(
|
|
429
|
+
[
|
|
430
|
+
String.raw`(?<![\w.~/\\)\]-])~?/+${segments(POSIX_SEGMENT, "/+")}`,
|
|
431
|
+
String.raw`(?<![\w\\/])[A-Za-z]:[\\/]+${segments(WINDOWS_SEGMENT, String.raw`[\\/]+`)}?`,
|
|
432
|
+
String.raw`(?<![\w\\])\\\\${segments(WINDOWS_SEGMENT, String.raw`[\\/]+`)}`
|
|
433
|
+
].join("|"),
|
|
434
|
+
"g"
|
|
435
|
+
);
|
|
436
|
+
function redactPaths(text) {
|
|
437
|
+
return text.replace(QUOTED_PATH_RE, "$1[path]$1").replace(UNQUOTED_PATH_RE, "[path]");
|
|
438
|
+
}
|
|
439
|
+
function toClientErrorMessage(message) {
|
|
440
|
+
if (!message) return "";
|
|
441
|
+
return redactAbsolutePaths(message.replace(EMBEDDED_STACK_FRAME_RE, ""));
|
|
442
|
+
}
|
|
443
|
+
function redactAbsolutePaths(text) {
|
|
444
|
+
let result = "";
|
|
445
|
+
let last = 0;
|
|
446
|
+
for (const match of text.matchAll(URL_RE)) {
|
|
447
|
+
const start = match.index ?? 0;
|
|
448
|
+
result += redactPaths(text.slice(last, start));
|
|
449
|
+
result += /^file:/i.test(match[0]) ? "[path]" : match[0].replace(QUERY_PATH_VALUE_RE, "$1[path]");
|
|
450
|
+
last = start + match[0].length;
|
|
451
|
+
}
|
|
452
|
+
return result + redactPaths(text.slice(last));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// plugins/plugin-codecall/src/services/audit-logger.service.ts
|
|
416
456
|
var AUDIT_EVENT_TYPES = {
|
|
417
457
|
/** Script execution started */
|
|
418
458
|
EXECUTION_START: "codecall:execution:start",
|
|
@@ -689,9 +729,8 @@ var AuditLoggerService = class {
|
|
|
689
729
|
*/
|
|
690
730
|
sanitizeError(error) {
|
|
691
731
|
if (!error) return "Unknown error";
|
|
692
|
-
let sanitized = error
|
|
732
|
+
let sanitized = toClientErrorMessage(error);
|
|
693
733
|
sanitized = sanitized.replace(/:\d+:\d+/g, "");
|
|
694
|
-
sanitized = sanitized.replace(/\n\s*at .*/g, "");
|
|
695
734
|
if (sanitized.length > 200) {
|
|
696
735
|
sanitized = sanitized.substring(0, 200) + "...";
|
|
697
736
|
}
|
|
@@ -710,6 +749,10 @@ var import_vectoriadb = require("vectoriadb");
|
|
|
710
749
|
|
|
711
750
|
// plugins/plugin-codecall/src/security/codecall-tool-policy.ts
|
|
712
751
|
var BLOCKED_NAMESPACE_PATTERNS = Object.freeze([/^system:/, /^internal:/, /^__/]);
|
|
752
|
+
function isOfferedToCaller(availableWhen, surface) {
|
|
753
|
+
const offered = availableWhen?.surface;
|
|
754
|
+
return offered === void 0 || surface === void 0 || offered.includes(surface);
|
|
755
|
+
}
|
|
713
756
|
var ALLOWED = { allowed: true };
|
|
714
757
|
function deny(reason) {
|
|
715
758
|
return { allowed: false, reason };
|
|
@@ -730,16 +773,46 @@ function toCodeCallPolicyTool(entry, requestedName, scope) {
|
|
|
730
773
|
tags: metadata?.tags,
|
|
731
774
|
// `internal` tools refuse every external tools/call, CodeCall's included, so no surface may offer them.
|
|
732
775
|
hidden: metadata?.hideFromDiscovery === true || metadata?.visibility === "hidden" || metadata?.visibility === "internal",
|
|
733
|
-
codecall: metadata?.codecall
|
|
776
|
+
codecall: metadata?.codecall,
|
|
777
|
+
annotations: metadata?.annotations,
|
|
778
|
+
metadata
|
|
734
779
|
};
|
|
735
780
|
}
|
|
781
|
+
function isPlainData(value) {
|
|
782
|
+
if (Array.isArray(value)) return true;
|
|
783
|
+
const proto = Object.getPrototypeOf(value);
|
|
784
|
+
return proto === Object.prototype || proto === null;
|
|
785
|
+
}
|
|
786
|
+
function readOnlyCopy(value, copies) {
|
|
787
|
+
if (typeof value !== "object" || value === null || !isPlainData(value)) return value;
|
|
788
|
+
const existing = copies.get(value);
|
|
789
|
+
if (existing !== void 0) return existing;
|
|
790
|
+
const copy = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
|
|
791
|
+
copies.set(value, copy);
|
|
792
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
793
|
+
if (!Object.prototype.propertyIsEnumerable.call(value, key)) continue;
|
|
794
|
+
Object.defineProperty(copy, key, {
|
|
795
|
+
value: readOnlyCopy(Reflect.get(value, key), copies),
|
|
796
|
+
enumerable: true,
|
|
797
|
+
writable: true,
|
|
798
|
+
configurable: true
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
return Object.freeze(copy);
|
|
802
|
+
}
|
|
736
803
|
function toToolFilterInfo(tool) {
|
|
804
|
+
const copies = /* @__PURE__ */ new WeakMap();
|
|
805
|
+
const annotations = readOnlyCopy(tool.annotations, copies);
|
|
806
|
+
const metadata = tool.metadata ? readOnlyCopy({ ...tool.metadata, ...annotations ? { annotations: tool.annotations } : {} }, copies) : void 0;
|
|
737
807
|
return {
|
|
738
808
|
name: tool.name,
|
|
809
|
+
fullName: tool.fullName,
|
|
739
810
|
appId: tool.appId,
|
|
740
811
|
source: tool.codecall?.source,
|
|
741
812
|
description: tool.description,
|
|
742
|
-
tags: tool.codecall?.tags ?? tool.tags
|
|
813
|
+
tags: readOnlyCopy(tool.codecall?.tags ?? tool.tags, copies),
|
|
814
|
+
annotations,
|
|
815
|
+
metadata
|
|
743
816
|
};
|
|
744
817
|
}
|
|
745
818
|
function ownNamesOf(tool) {
|
|
@@ -826,6 +899,9 @@ function readCodeCallPolicyConfig(config) {
|
|
|
826
899
|
function checkCodeCallToolAccess(scope, config, name, options = {}) {
|
|
827
900
|
const entry = resolveCodeCallTool(scope, name);
|
|
828
901
|
if (!entry) return denyUnknownTool(name);
|
|
902
|
+
if (!isOfferedToCaller(entry.metadata?.availableWhen, options.surface)) {
|
|
903
|
+
return deny(`Tool "${name}" is not offered on the caller's surface "${options.surface}"`);
|
|
904
|
+
}
|
|
829
905
|
const policyTool = toCodeCallPolicyTool(entry, name, scope);
|
|
830
906
|
const baseDecision = checkCodeCallToolPolicy(policyTool, readCodeCallPolicyConfig(config));
|
|
831
907
|
if (!baseDecision.allowed) return baseDecision;
|
|
@@ -1359,6 +1435,8 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1359
1435
|
subscriptionReject = null;
|
|
1360
1436
|
retryTimeoutId = null;
|
|
1361
1437
|
disposed = false;
|
|
1438
|
+
/** `availableWhen` of each indexed tool, by indexed name, for per-caller surface filtering. */
|
|
1439
|
+
availabilityByTool = /* @__PURE__ */ new Map();
|
|
1362
1440
|
constructor(config = {}, scope) {
|
|
1363
1441
|
this.scope = scope;
|
|
1364
1442
|
const embeddingOptions = config.embeddingOptions || {
|
|
@@ -1468,6 +1546,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1468
1546
|
*/
|
|
1469
1547
|
async handleToolChange(tools) {
|
|
1470
1548
|
this.vectorDB.clear();
|
|
1549
|
+
this.availabilityByTool = /* @__PURE__ */ new Map();
|
|
1471
1550
|
if (tools.length === 0) {
|
|
1472
1551
|
this.initialized = true;
|
|
1473
1552
|
return;
|
|
@@ -1482,6 +1561,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1482
1561
|
return;
|
|
1483
1562
|
}
|
|
1484
1563
|
const documents = filteredTools.map((tool) => {
|
|
1564
|
+
this.availabilityByTool.set(tool.name, tool.metadata.availableWhen);
|
|
1485
1565
|
const searchableText = this.extractSearchableText(tool);
|
|
1486
1566
|
const appId = this.extractAppId(tool);
|
|
1487
1567
|
const toolName = tool.name;
|
|
@@ -1610,12 +1690,15 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1610
1690
|
*/
|
|
1611
1691
|
async search(query, options = {}) {
|
|
1612
1692
|
await this.ensureSubscribed();
|
|
1613
|
-
const { topK = this.config.defaultTopK, appIds, excludeToolNames = [] } = options;
|
|
1693
|
+
const { topK = this.config.defaultTopK, appIds, excludeToolNames = [], surface } = options;
|
|
1614
1694
|
const minScore = this.config.defaultSimilarityThreshold;
|
|
1615
1695
|
const filter = (metadata) => {
|
|
1616
1696
|
if (excludeToolNames.includes(metadata.toolName)) {
|
|
1617
1697
|
return false;
|
|
1618
1698
|
}
|
|
1699
|
+
if (!isOfferedToCaller(metadata.toolInstance.metadata.availableWhen, surface)) {
|
|
1700
|
+
return false;
|
|
1701
|
+
}
|
|
1619
1702
|
if (appIds && appIds.length > 0) {
|
|
1620
1703
|
if (!metadata.appId || !appIds.includes(metadata.appId)) {
|
|
1621
1704
|
return false;
|
|
@@ -1647,9 +1730,12 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1647
1730
|
}
|
|
1648
1731
|
}
|
|
1649
1732
|
/**
|
|
1650
|
-
* Gets the total number of indexed tools
|
|
1733
|
+
* Gets the total number of indexed tools, or of those a caller on `surface` may reach
|
|
1651
1734
|
*/
|
|
1652
|
-
getTotalCount() {
|
|
1735
|
+
getTotalCount(surface) {
|
|
1736
|
+
if (surface !== void 0) {
|
|
1737
|
+
return this.getAllToolNames().filter((toolName) => this.hasTool(toolName, surface)).length;
|
|
1738
|
+
}
|
|
1653
1739
|
if (this.vectorDB instanceof import_vectoriadb.VectoriaDB) {
|
|
1654
1740
|
return this.vectorDB.size();
|
|
1655
1741
|
} else {
|
|
@@ -1657,20 +1743,18 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1657
1743
|
}
|
|
1658
1744
|
}
|
|
1659
1745
|
/**
|
|
1660
|
-
* Checks if a tool exists in the index
|
|
1746
|
+
* Checks if a tool exists in the index, for a caller on `surface` when given
|
|
1661
1747
|
*/
|
|
1662
|
-
hasTool(toolName) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
} else {
|
|
1666
|
-
return this.vectorDB.hasDocument(toolName);
|
|
1667
|
-
}
|
|
1748
|
+
hasTool(toolName, surface) {
|
|
1749
|
+
const indexed = this.vectorDB instanceof import_vectoriadb.VectoriaDB ? this.vectorDB.has(toolName) : this.vectorDB.hasDocument(toolName);
|
|
1750
|
+
return indexed && isOfferedToCaller(this.availabilityByTool.get(toolName), surface);
|
|
1668
1751
|
}
|
|
1669
1752
|
/**
|
|
1670
1753
|
* Clears the entire index
|
|
1671
1754
|
*/
|
|
1672
1755
|
clear() {
|
|
1673
1756
|
this.vectorDB.clear();
|
|
1757
|
+
this.availabilityByTool = /* @__PURE__ */ new Map();
|
|
1674
1758
|
this.initialized = false;
|
|
1675
1759
|
}
|
|
1676
1760
|
/**
|
|
@@ -1690,6 +1774,135 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1690
1774
|
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1691
1775
|
var import_core = require("@enclave-vm/core");
|
|
1692
1776
|
var import_sdk3 = require("@frontmcp/sdk");
|
|
1777
|
+
|
|
1778
|
+
// plugins/plugin-codecall/src/utils/build-tool-namespaces.ts
|
|
1779
|
+
var IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1780
|
+
var PROTOTYPE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1781
|
+
var RESERVED_NAMESPACES = /* @__PURE__ */ new Set([
|
|
1782
|
+
"console",
|
|
1783
|
+
"Math",
|
|
1784
|
+
"JSON",
|
|
1785
|
+
"Object",
|
|
1786
|
+
"Array",
|
|
1787
|
+
"String",
|
|
1788
|
+
"Number",
|
|
1789
|
+
"Boolean",
|
|
1790
|
+
"Promise",
|
|
1791
|
+
"Symbol",
|
|
1792
|
+
"Map",
|
|
1793
|
+
"Set",
|
|
1794
|
+
"WeakMap",
|
|
1795
|
+
"WeakSet",
|
|
1796
|
+
"Date",
|
|
1797
|
+
"Error",
|
|
1798
|
+
"RegExp",
|
|
1799
|
+
"globalThis",
|
|
1800
|
+
"global",
|
|
1801
|
+
"window",
|
|
1802
|
+
"self",
|
|
1803
|
+
"undefined",
|
|
1804
|
+
"null",
|
|
1805
|
+
"true",
|
|
1806
|
+
"false",
|
|
1807
|
+
"callTool",
|
|
1808
|
+
"getTool",
|
|
1809
|
+
"mcpLog",
|
|
1810
|
+
"mcpNotify"
|
|
1811
|
+
]);
|
|
1812
|
+
var UNBINDABLE_NAMESPACES = new Set(
|
|
1813
|
+
[
|
|
1814
|
+
// Reserved words and strict-mode restricted bindings
|
|
1815
|
+
"break case catch class const continue debugger default delete do else enum export extends finally for",
|
|
1816
|
+
"function if import in instanceof new return super switch this throw try typeof var void while with yield",
|
|
1817
|
+
"let static implements interface package private protected public await eval arguments NaN Infinity",
|
|
1818
|
+
// AgentScript globals
|
|
1819
|
+
"parallel isNaN isFinite parseInt parseFloat encodeURI encodeURIComponent decodeURI decodeURIComponent",
|
|
1820
|
+
// Identifiers the AgentScript validator refuses
|
|
1821
|
+
"process require module exports Buffer Function AsyncFunction GeneratorFunction Reflect Proxy TypeError",
|
|
1822
|
+
"ReferenceError SyntaxError RangeError URIError EvalError AggregateError fetch XMLHttpRequest WebSocket",
|
|
1823
|
+
"localStorage sessionStorage indexedDB crypto performance structuredClone AbortController AbortSignal",
|
|
1824
|
+
"MessageChannel MessagePort BroadcastChannel TextEncoder TextDecoder Intl setTimeout setInterval",
|
|
1825
|
+
"setImmediate clearTimeout clearInterval clearImmediate queueMicrotask WebAssembly Worker SharedWorker",
|
|
1826
|
+
"ServiceWorker WeakRef FinalizationRegistry BigInt Atomics SharedArrayBuffer ArrayBuffer DataView"
|
|
1827
|
+
].join(" ").split(" ")
|
|
1828
|
+
);
|
|
1829
|
+
function buildToolNamespaces(tools) {
|
|
1830
|
+
const namespaces = /* @__PURE__ */ Object.create(null);
|
|
1831
|
+
const skipped = [];
|
|
1832
|
+
for (const tool of tools) {
|
|
1833
|
+
const name = tool?.name;
|
|
1834
|
+
if (typeof name !== "string" || name.length === 0) continue;
|
|
1835
|
+
const dot = name.indexOf(".");
|
|
1836
|
+
if (dot <= 0 || dot === name.length - 1) {
|
|
1837
|
+
skipped.push({ name, reason: "no-namespace-prefix" });
|
|
1838
|
+
continue;
|
|
1839
|
+
}
|
|
1840
|
+
const ns = name.slice(0, dot);
|
|
1841
|
+
const method = name.slice(dot + 1);
|
|
1842
|
+
if (!IDENT_RE.test(ns) || !IDENT_RE.test(method)) {
|
|
1843
|
+
skipped.push({ name, reason: "invalid-identifier" });
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
if (PROTOTYPE_KEYS.has(ns) || PROTOTYPE_KEYS.has(method)) {
|
|
1847
|
+
skipped.push({ name, reason: "prototype-key" });
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
if (RESERVED_NAMESPACES.has(ns) || UNBINDABLE_NAMESPACES.has(ns) || ns.startsWith("__")) {
|
|
1851
|
+
skipped.push({ name, reason: "reserved-namespace" });
|
|
1852
|
+
continue;
|
|
1853
|
+
}
|
|
1854
|
+
const bucket = namespaces[ns] ?? (namespaces[ns] = /* @__PURE__ */ Object.create(null));
|
|
1855
|
+
if (Object.prototype.hasOwnProperty.call(bucket, method)) {
|
|
1856
|
+
skipped.push({ name, reason: "duplicate-method" });
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
bucket[method] = name;
|
|
1860
|
+
}
|
|
1861
|
+
return { namespaces, skipped };
|
|
1862
|
+
}
|
|
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 = [];
|
|
1881
|
+
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;
|
|
1886
|
+
const methods = namespaces[ns];
|
|
1887
|
+
const entries = [];
|
|
1888
|
+
for (const method of Object.keys(methods)) {
|
|
1889
|
+
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
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
if (entries.length > 0) {
|
|
1897
|
+
declarations.push(`const ${ns} = { ${entries.join(", ")} };`);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
if (declarations.length === 0) return script;
|
|
1901
|
+
return `${NAMESPACE_CALL_HELPER_SOURCE} ${declarations.join(" ")} return await (async () => {${script}
|
|
1902
|
+
})();`;
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1693
1906
|
var ScriptTooLargeError = class extends Error {
|
|
1694
1907
|
code = "SCRIPT_TOO_LARGE";
|
|
1695
1908
|
scriptLength;
|
|
@@ -1738,10 +1951,7 @@ var EnclaveService = class {
|
|
|
1738
1951
|
maxResolvedSize: this.sidecarOptions.maxResolvedSize,
|
|
1739
1952
|
allowComposites: this.sidecarOptions.allowComposites
|
|
1740
1953
|
} : void 0;
|
|
1741
|
-
const
|
|
1742
|
-
const safeNamespaces = Object.fromEntries(
|
|
1743
|
-
Object.entries(environment.namespaces ?? {}).filter(([key]) => !RESERVED_GLOBALS.has(key))
|
|
1744
|
-
);
|
|
1954
|
+
const source = wrapScriptWithToolNamespaces(code, environment.toolNamespaces);
|
|
1745
1955
|
const enclave = new import_core.Enclave({
|
|
1746
1956
|
timeout: this.vmOptions.timeoutMs,
|
|
1747
1957
|
maxToolCalls: this.vmOptions.maxSteps || 100,
|
|
@@ -1755,11 +1965,6 @@ var EnclaveService = class {
|
|
|
1755
1965
|
// Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
|
|
1756
1966
|
allowFunctionsInGlobals: true,
|
|
1757
1967
|
globals: {
|
|
1758
|
-
// Tool namespaces (e.g. `acme.getUser`) flow through here. Spread
|
|
1759
|
-
// FIRST so the runtime reserved globals below are guaranteed
|
|
1760
|
-
// last-wins. Reserved keys have already been stripped above —
|
|
1761
|
-
// belt-and-braces ordering makes the invariant locally obvious.
|
|
1762
|
-
...safeNamespaces,
|
|
1763
1968
|
// Provide getTool as a custom global
|
|
1764
1969
|
getTool: environment.getTool,
|
|
1765
1970
|
// Provide logging functions if available
|
|
@@ -1782,7 +1987,7 @@ var EnclaveService = class {
|
|
|
1782
1987
|
}
|
|
1783
1988
|
});
|
|
1784
1989
|
try {
|
|
1785
|
-
const result = await enclave.run(
|
|
1990
|
+
const result = await enclave.run(source);
|
|
1786
1991
|
return this.mapEnclaveResult(result, logs);
|
|
1787
1992
|
} finally {
|
|
1788
1993
|
enclave.dispose();
|
|
@@ -1930,7 +2135,10 @@ var SearchTool = class extends import_sdk4.ToolContext {
|
|
|
1930
2135
|
const audit = this.tryGet(AuditLoggerService);
|
|
1931
2136
|
const executionId = audit ? audit.generateExecutionId() : "";
|
|
1932
2137
|
const startedAt = Date.now();
|
|
1933
|
-
const
|
|
2138
|
+
const surface = (0, import_sdk4.getCallSurface)();
|
|
2139
|
+
const nonExistentExcludedTools = excludeToolNames.filter(
|
|
2140
|
+
(toolName) => !searchService.hasTool(toolName, surface)
|
|
2141
|
+
);
|
|
1934
2142
|
if (nonExistentExcludedTools.length > 0) {
|
|
1935
2143
|
warnings.push({
|
|
1936
2144
|
type: "excluded_tool_not_found",
|
|
@@ -1944,7 +2152,8 @@ var SearchTool = class extends import_sdk4.ToolContext {
|
|
|
1944
2152
|
const searchResults = await searchService.search(query, {
|
|
1945
2153
|
topK,
|
|
1946
2154
|
appIds,
|
|
1947
|
-
excludeToolNames
|
|
2155
|
+
excludeToolNames,
|
|
2156
|
+
surface
|
|
1948
2157
|
});
|
|
1949
2158
|
for (const result of searchResults) {
|
|
1950
2159
|
if (result.relevanceScore < minRelevanceScore) {
|
|
@@ -1989,7 +2198,7 @@ var SearchTool = class extends import_sdk4.ToolContext {
|
|
|
1989
2198
|
return {
|
|
1990
2199
|
tools,
|
|
1991
2200
|
warnings,
|
|
1992
|
-
totalAvailableTools: searchService.getTotalCount()
|
|
2201
|
+
totalAvailableTools: searchService.getTotalCount(surface)
|
|
1993
2202
|
};
|
|
1994
2203
|
}
|
|
1995
2204
|
};
|
|
@@ -2340,11 +2549,10 @@ function getSanitizedMessage(code, toolName, rawMessage) {
|
|
|
2340
2549
|
}
|
|
2341
2550
|
}
|
|
2342
2551
|
function sanitizeValidationMessage(message) {
|
|
2343
|
-
let sanitized = message
|
|
2552
|
+
let sanitized = toClientErrorMessage(message);
|
|
2344
2553
|
sanitized = sanitized.replace(/\bat line \d+/gi, "");
|
|
2345
2554
|
sanitized = sanitized.replace(/\bline \d+/gi, "");
|
|
2346
2555
|
sanitized = sanitized.replace(/:\d+:\d+/g, "");
|
|
2347
|
-
sanitized = sanitized.replace(/\n\s*at .*/g, "");
|
|
2348
2556
|
if (sanitized.length > 200) {
|
|
2349
2557
|
sanitized = sanitized.substring(0, 200) + "...";
|
|
2350
2558
|
}
|
|
@@ -2382,75 +2590,6 @@ function assertNotSelfReference(toolName) {
|
|
|
2382
2590
|
}
|
|
2383
2591
|
}
|
|
2384
2592
|
|
|
2385
|
-
// plugins/plugin-codecall/src/utils/build-tool-namespaces.ts
|
|
2386
|
-
var IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
2387
|
-
var PROTOTYPE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
2388
|
-
var RESERVED_NAMESPACES = /* @__PURE__ */ new Set([
|
|
2389
|
-
"console",
|
|
2390
|
-
"Math",
|
|
2391
|
-
"JSON",
|
|
2392
|
-
"Object",
|
|
2393
|
-
"Array",
|
|
2394
|
-
"String",
|
|
2395
|
-
"Number",
|
|
2396
|
-
"Boolean",
|
|
2397
|
-
"Promise",
|
|
2398
|
-
"Symbol",
|
|
2399
|
-
"Map",
|
|
2400
|
-
"Set",
|
|
2401
|
-
"WeakMap",
|
|
2402
|
-
"WeakSet",
|
|
2403
|
-
"Date",
|
|
2404
|
-
"Error",
|
|
2405
|
-
"RegExp",
|
|
2406
|
-
"globalThis",
|
|
2407
|
-
"global",
|
|
2408
|
-
"window",
|
|
2409
|
-
"self",
|
|
2410
|
-
"undefined",
|
|
2411
|
-
"null",
|
|
2412
|
-
"true",
|
|
2413
|
-
"false",
|
|
2414
|
-
"callTool",
|
|
2415
|
-
"getTool",
|
|
2416
|
-
"mcpLog",
|
|
2417
|
-
"mcpNotify"
|
|
2418
|
-
]);
|
|
2419
|
-
function buildToolNamespaces(tools, callTool) {
|
|
2420
|
-
const namespaces = /* @__PURE__ */ Object.create(null);
|
|
2421
|
-
const skipped = [];
|
|
2422
|
-
for (const tool of tools) {
|
|
2423
|
-
const name = tool?.name;
|
|
2424
|
-
if (typeof name !== "string" || name.length === 0) continue;
|
|
2425
|
-
const dot = name.indexOf(".");
|
|
2426
|
-
if (dot <= 0 || dot === name.length - 1) {
|
|
2427
|
-
skipped.push({ name, reason: "no-namespace-prefix" });
|
|
2428
|
-
continue;
|
|
2429
|
-
}
|
|
2430
|
-
const ns = name.slice(0, dot);
|
|
2431
|
-
const method = name.slice(dot + 1);
|
|
2432
|
-
if (!IDENT_RE.test(ns) || !IDENT_RE.test(method)) {
|
|
2433
|
-
skipped.push({ name, reason: "invalid-identifier" });
|
|
2434
|
-
continue;
|
|
2435
|
-
}
|
|
2436
|
-
if (PROTOTYPE_KEYS.has(ns)) {
|
|
2437
|
-
skipped.push({ name, reason: "prototype-key" });
|
|
2438
|
-
continue;
|
|
2439
|
-
}
|
|
2440
|
-
if (RESERVED_NAMESPACES.has(ns)) {
|
|
2441
|
-
skipped.push({ name, reason: "reserved-namespace" });
|
|
2442
|
-
continue;
|
|
2443
|
-
}
|
|
2444
|
-
const bucket = namespaces[ns] ?? (namespaces[ns] = /* @__PURE__ */ Object.create(null));
|
|
2445
|
-
if (Object.prototype.hasOwnProperty.call(bucket, method)) {
|
|
2446
|
-
skipped.push({ name, reason: "duplicate-method" });
|
|
2447
|
-
continue;
|
|
2448
|
-
}
|
|
2449
|
-
bucket[method] = (input, options) => callTool(name, input, options);
|
|
2450
|
-
}
|
|
2451
|
-
return { namespaces, skipped };
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
2593
|
// plugins/plugin-codecall/src/utils/describe.utils.ts
|
|
2455
2594
|
var INTENT_PATTERNS = {
|
|
2456
2595
|
create: /^(create|add|new|insert|make|append|register|generate|produce|build|construct|provision|instantiate|define|compose|draft)/i,
|
|
@@ -2812,12 +2951,13 @@ var DescribeTool = class extends import_sdk7.ToolContext {
|
|
|
2812
2951
|
const tools = [];
|
|
2813
2952
|
const notFound = [];
|
|
2814
2953
|
const config = this.get(CodeCallConfig);
|
|
2954
|
+
const surface = (0, import_sdk7.getCallSurface)();
|
|
2815
2955
|
for (const toolName of toolNames) {
|
|
2816
2956
|
if (isBlockedSelfReference(toolName)) {
|
|
2817
2957
|
notFound.push(toolName);
|
|
2818
2958
|
continue;
|
|
2819
2959
|
}
|
|
2820
|
-
const access = checkCodeCallToolAccess(this.scope, config, toolName);
|
|
2960
|
+
const access = checkCodeCallToolAccess(this.scope, config, toolName, { surface });
|
|
2821
2961
|
if (!access.allowed) {
|
|
2822
2962
|
audit?.logSecurityAccessDenied(executionId, toolName, access.reason);
|
|
2823
2963
|
notFound.push(toolName);
|
|
@@ -3029,6 +3169,7 @@ var runtimeErrorPayloadSchema = import_lazy_zod7.z.object({
|
|
|
3029
3169
|
source: import_lazy_zod7.z.literal("script"),
|
|
3030
3170
|
message: import_lazy_zod7.z.string(),
|
|
3031
3171
|
name: import_lazy_zod7.z.string().optional(),
|
|
3172
|
+
/** @deprecated Never set: stack traces name server files, so results don't carry them. */
|
|
3032
3173
|
stack: import_lazy_zod7.z.string().optional()
|
|
3033
3174
|
});
|
|
3034
3175
|
var toolErrorPayloadSchema = import_lazy_zod7.z.object({
|
|
@@ -3037,6 +3178,7 @@ var toolErrorPayloadSchema = import_lazy_zod7.z.object({
|
|
|
3037
3178
|
toolInput: import_lazy_zod7.z.unknown(),
|
|
3038
3179
|
message: import_lazy_zod7.z.string(),
|
|
3039
3180
|
code: import_lazy_zod7.z.string().optional(),
|
|
3181
|
+
/** @deprecated Never set: the enclave's error data is not sanitized for clients. */
|
|
3040
3182
|
details: import_lazy_zod7.z.unknown().optional()
|
|
3041
3183
|
});
|
|
3042
3184
|
var timeoutErrorPayloadSchema = import_lazy_zod7.z.object({
|
|
@@ -3101,11 +3243,12 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3101
3243
|
* otherwise it would skip every metadata-driven check and reach `tools:call-tool`
|
|
3102
3244
|
* unexamined.
|
|
3103
3245
|
*/
|
|
3104
|
-
checkToolPolicy(name) {
|
|
3105
|
-
return checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), name);
|
|
3246
|
+
checkToolPolicy(name, surface) {
|
|
3247
|
+
return checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), name, { surface });
|
|
3106
3248
|
}
|
|
3107
3249
|
async execute(input) {
|
|
3108
3250
|
const { script, allowedTools } = input;
|
|
3251
|
+
const callerSurface = (0, import_sdk8.getCallSurface)();
|
|
3109
3252
|
const audit = this.tryGet(AuditLoggerService);
|
|
3110
3253
|
const executionId = audit ? audit.generateExecutionId() : "";
|
|
3111
3254
|
const callDepth = 1;
|
|
@@ -3118,7 +3261,7 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3118
3261
|
audit?.logSecuritySelfReference(executionId, name);
|
|
3119
3262
|
}
|
|
3120
3263
|
assertNotSelfReference(name);
|
|
3121
|
-
const decision = this.checkToolPolicy(name);
|
|
3264
|
+
const decision = this.checkToolPolicy(name, callerSurface);
|
|
3122
3265
|
if (!decision.allowed) {
|
|
3123
3266
|
audit?.logSecurityAccessDenied(executionId, name, decision.reason);
|
|
3124
3267
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name, decision.reason);
|
|
@@ -3147,7 +3290,8 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3147
3290
|
}
|
|
3148
3291
|
};
|
|
3149
3292
|
const ctx = {
|
|
3150
|
-
authInfo: this.authInfo
|
|
3293
|
+
authInfo: this.authInfo,
|
|
3294
|
+
surface: callerSurface
|
|
3151
3295
|
};
|
|
3152
3296
|
const mcpResult = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
3153
3297
|
if (!mcpResult) {
|
|
@@ -3187,7 +3331,7 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3187
3331
|
audit?.logSecuritySelfReference(executionId, name);
|
|
3188
3332
|
return void 0;
|
|
3189
3333
|
}
|
|
3190
|
-
const introspectionAccess = this.checkToolPolicy(name);
|
|
3334
|
+
const introspectionAccess = this.checkToolPolicy(name, callerSurface);
|
|
3191
3335
|
if (!introspectionAccess.allowed) {
|
|
3192
3336
|
audit?.logSecurityAccessDenied(executionId, name, introspectionAccess.reason);
|
|
3193
3337
|
return void 0;
|
|
@@ -3217,8 +3361,8 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3217
3361
|
const callableTools = this.scope.tools.getTools(true).filter(
|
|
3218
3362
|
(tool) => checkCodeCallToolPolicy(toCodeCallPolicyTool(tool, void 0, this.scope), policyConfig).allowed
|
|
3219
3363
|
);
|
|
3220
|
-
const { namespaces, skipped } = buildToolNamespaces(callableTools
|
|
3221
|
-
environment.
|
|
3364
|
+
const { namespaces, skipped } = buildToolNamespaces(callableTools);
|
|
3365
|
+
environment.toolNamespaces = namespaces;
|
|
3222
3366
|
if (skipped.length > 0) {
|
|
3223
3367
|
this.logger?.debug?.("codecall: tools skipped during namespace generation", {
|
|
3224
3368
|
count: skipped.length,
|
|
@@ -3269,9 +3413,9 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3269
3413
|
source: "tool",
|
|
3270
3414
|
toolName: error.toolName,
|
|
3271
3415
|
toolInput: error.toolInput,
|
|
3272
|
-
message: error.message,
|
|
3273
|
-
code: error.code
|
|
3274
|
-
details
|
|
3416
|
+
message: toClientErrorMessage(error.message),
|
|
3417
|
+
code: error.code
|
|
3418
|
+
// No `details`: the enclave's error data is not sanitized for clients.
|
|
3275
3419
|
}
|
|
3276
3420
|
};
|
|
3277
3421
|
}
|
|
@@ -3280,9 +3424,8 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3280
3424
|
status: "runtime_error",
|
|
3281
3425
|
error: {
|
|
3282
3426
|
source: "script",
|
|
3283
|
-
message: error.message,
|
|
3284
|
-
name: error.name
|
|
3285
|
-
stack: error.stack
|
|
3427
|
+
message: toClientErrorMessage(error.message),
|
|
3428
|
+
name: error.name
|
|
3286
3429
|
}
|
|
3287
3430
|
};
|
|
3288
3431
|
}
|
|
@@ -3296,13 +3439,12 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3296
3439
|
const errorName = error instanceof Error ? error.name : "Error";
|
|
3297
3440
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3298
3441
|
audit?.logExecutionFailure(executionId, script, elapsed(), errorMessage);
|
|
3299
|
-
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
3300
3442
|
const errorLoc = error.loc;
|
|
3301
3443
|
if (errorName === "SyntaxError" || errorMessage?.includes("syntax")) {
|
|
3302
3444
|
return {
|
|
3303
3445
|
status: "syntax_error",
|
|
3304
3446
|
error: {
|
|
3305
|
-
message: errorMessage || "Syntax error in script",
|
|
3447
|
+
message: toClientErrorMessage(errorMessage) || "Syntax error in script",
|
|
3306
3448
|
location: errorLoc ? { line: errorLoc.line, column: errorLoc.column } : void 0
|
|
3307
3449
|
}
|
|
3308
3450
|
};
|
|
@@ -3311,9 +3453,8 @@ var ExecuteTool = class extends import_sdk8.ToolContext {
|
|
|
3311
3453
|
status: "runtime_error",
|
|
3312
3454
|
error: {
|
|
3313
3455
|
source: "script",
|
|
3314
|
-
message: errorMessage || "An unexpected error occurred during script execution",
|
|
3315
|
-
name: errorName
|
|
3316
|
-
stack: errorStack
|
|
3456
|
+
message: toClientErrorMessage(errorMessage) || "An unexpected error occurred during script execution",
|
|
3457
|
+
name: errorName
|
|
3317
3458
|
}
|
|
3318
3459
|
};
|
|
3319
3460
|
}
|
|
@@ -3382,7 +3523,11 @@ var InvokeTool = class extends import_sdk9.ToolContext {
|
|
|
3382
3523
|
`Tool "${toolName}" cannot be invoked directly. CodeCall tools are internal and not accessible via codecall:invoke.`
|
|
3383
3524
|
);
|
|
3384
3525
|
}
|
|
3385
|
-
const
|
|
3526
|
+
const surface = (0, import_sdk9.getCallSurface)();
|
|
3527
|
+
const decision = checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), toolName, {
|
|
3528
|
+
directCall: true,
|
|
3529
|
+
surface
|
|
3530
|
+
});
|
|
3386
3531
|
if (!decision.allowed) {
|
|
3387
3532
|
audit?.logSecurityAccessDenied(executionId, toolName, decision.reason);
|
|
3388
3533
|
return buildErrorResult(`Tool "${toolName}" is not available. Use codecall:search to discover available tools.`);
|
|
@@ -3395,7 +3540,8 @@ var InvokeTool = class extends import_sdk9.ToolContext {
|
|
|
3395
3540
|
}
|
|
3396
3541
|
};
|
|
3397
3542
|
const ctx = {
|
|
3398
|
-
authInfo: this.authInfo
|
|
3543
|
+
authInfo: this.authInfo,
|
|
3544
|
+
surface
|
|
3399
3545
|
};
|
|
3400
3546
|
const result = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
3401
3547
|
if (!result) {
|