@frontmcp/plugin-codecall 0.0.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codecall.plugin.d.ts +42 -0
- package/codecall.plugin.d.ts.map +1 -0
- package/codecall.symbol.d.ts +107 -0
- package/codecall.symbol.d.ts.map +1 -0
- package/codecall.types.d.ts +353 -0
- package/codecall.types.d.ts.map +1 -0
- package/errors/index.d.ts +2 -0
- package/errors/index.d.ts.map +1 -0
- package/errors/tool-call.errors.d.ts +80 -0
- package/errors/tool-call.errors.d.ts.map +1 -0
- package/esm/index.mjs +2625 -0
- package/esm/package.json +59 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js +2668 -0
- package/package.json +3 -3
- package/providers/code-call.config.d.ts +30 -0
- package/providers/code-call.config.d.ts.map +1 -0
- package/security/index.d.ts +3 -0
- package/security/index.d.ts.map +1 -0
- package/security/self-reference-guard.d.ts +33 -0
- package/security/self-reference-guard.d.ts.map +1 -0
- package/security/tool-access-control.service.d.ts +105 -0
- package/security/tool-access-control.service.d.ts.map +1 -0
- package/services/audit-logger.service.d.ts +187 -0
- package/services/audit-logger.service.d.ts.map +1 -0
- package/services/enclave.service.d.ts +63 -0
- package/services/enclave.service.d.ts.map +1 -0
- package/services/error-enrichment.service.d.ts +95 -0
- package/services/error-enrichment.service.d.ts.map +1 -0
- package/services/index.d.ts +7 -0
- package/services/index.d.ts.map +1 -0
- package/services/output-sanitizer.d.ts +87 -0
- package/services/output-sanitizer.d.ts.map +1 -0
- package/services/synonym-expansion.service.d.ts +67 -0
- package/services/synonym-expansion.service.d.ts.map +1 -0
- package/services/tool-search.service.d.ts +196 -0
- package/services/tool-search.service.d.ts.map +1 -0
- package/tools/describe.schema.d.ts +29 -0
- package/tools/describe.schema.d.ts.map +1 -0
- package/tools/describe.tool.d.ts +36 -0
- package/tools/describe.tool.d.ts.map +1 -0
- package/tools/execute.schema.d.ts +116 -0
- package/tools/execute.schema.d.ts.map +1 -0
- package/tools/execute.tool.d.ts +6 -0
- package/tools/execute.tool.d.ts.map +1 -0
- package/tools/index.d.ts +5 -0
- package/tools/index.d.ts.map +1 -0
- package/tools/invoke.schema.d.ts +105 -0
- package/tools/invoke.schema.d.ts.map +1 -0
- package/tools/invoke.tool.d.ts +14 -0
- package/tools/invoke.tool.d.ts.map +1 -0
- package/tools/search.schema.d.ts +31 -0
- package/tools/search.schema.d.ts.map +1 -0
- package/tools/search.tool.d.ts +6 -0
- package/tools/search.tool.d.ts.map +1 -0
- package/utils/describe.utils.d.ts +87 -0
- package/utils/describe.utils.d.ts.map +1 -0
- package/utils/index.d.ts +3 -0
- package/utils/index.d.ts.map +1 -0
- package/utils/mcp-result.d.ts +7 -0
- package/utils/mcp-result.d.ts.map +1 -0
package/esm/index.mjs
ADDED
|
@@ -0,0 +1,2625 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// plugins/plugin-codecall/src/codecall.plugin.ts
|
|
13
|
+
import { DynamicPlugin, ListToolsHook, Plugin, ScopeEntry } from "@frontmcp/sdk";
|
|
14
|
+
|
|
15
|
+
// plugins/plugin-codecall/src/codecall.types.ts
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
var directCallsFilterSchema = z.custom((val) => typeof val === "function", {
|
|
18
|
+
message: "filter must be a function with signature (tool: DirectCallsFilterToolInfo) => boolean"
|
|
19
|
+
});
|
|
20
|
+
var includeToolsFilterSchema = z.custom((val) => typeof val === "function", {
|
|
21
|
+
message: "includeTools must be a function with signature (tool: IncludeToolsFilterToolInfo) => boolean"
|
|
22
|
+
});
|
|
23
|
+
var codeCallModeSchema = z.enum(["codecall_only", "codecall_opt_in", "metadata_driven"]).default("codecall_only");
|
|
24
|
+
var codeCallVmPresetSchema = z.enum(["locked_down", "secure", "balanced", "experimental"]).default("secure");
|
|
25
|
+
var DEFAULT_VM_OPTIONS = {
|
|
26
|
+
preset: "secure"
|
|
27
|
+
};
|
|
28
|
+
var codeCallVmOptionsSchema = z.object({
|
|
29
|
+
/**
|
|
30
|
+
* CSP-like preset; see README.
|
|
31
|
+
* @default 'secure'
|
|
32
|
+
*/
|
|
33
|
+
preset: codeCallVmPresetSchema,
|
|
34
|
+
/**
|
|
35
|
+
* Timeout for script execution in milliseconds
|
|
36
|
+
* Defaults vary by preset
|
|
37
|
+
*/
|
|
38
|
+
timeoutMs: z.number().positive().optional(),
|
|
39
|
+
/**
|
|
40
|
+
* Allow loop constructs (for, while, do-while)
|
|
41
|
+
* Defaults vary by preset
|
|
42
|
+
*/
|
|
43
|
+
allowLoops: z.boolean().optional(),
|
|
44
|
+
/**
|
|
45
|
+
* Maximum number of steps (if applicable)
|
|
46
|
+
* Defaults vary by preset
|
|
47
|
+
*/
|
|
48
|
+
maxSteps: z.number().positive().optional(),
|
|
49
|
+
/**
|
|
50
|
+
* List of disabled builtin functions
|
|
51
|
+
* Defaults vary by preset
|
|
52
|
+
*/
|
|
53
|
+
disabledBuiltins: z.array(z.string()).optional(),
|
|
54
|
+
/**
|
|
55
|
+
* List of disabled global variables
|
|
56
|
+
* Defaults vary by preset
|
|
57
|
+
*/
|
|
58
|
+
disabledGlobals: z.array(z.string()).optional(),
|
|
59
|
+
/**
|
|
60
|
+
* Allow console.log/warn/error
|
|
61
|
+
* Defaults vary by preset
|
|
62
|
+
*/
|
|
63
|
+
allowConsole: z.boolean().optional()
|
|
64
|
+
}).default(() => DEFAULT_VM_OPTIONS);
|
|
65
|
+
var codeCallDirectCallsOptionsSchema = z.object({
|
|
66
|
+
/**
|
|
67
|
+
* Enable/disable the `codecall.invoke` meta-tool.
|
|
68
|
+
*/
|
|
69
|
+
enabled: z.boolean(),
|
|
70
|
+
/**
|
|
71
|
+
* Optional allowlist of tool names.
|
|
72
|
+
*/
|
|
73
|
+
allowedTools: z.array(z.string()).optional(),
|
|
74
|
+
/**
|
|
75
|
+
* Optional advanced filter function.
|
|
76
|
+
* Signature: (tool: DirectCallsFilterToolInfo) => boolean
|
|
77
|
+
*/
|
|
78
|
+
filter: directCallsFilterSchema.optional()
|
|
79
|
+
});
|
|
80
|
+
var embeddingStrategySchema = z.enum(["tfidf", "ml"]).default("tfidf");
|
|
81
|
+
var synonymExpansionConfigSchema = z.object({
|
|
82
|
+
/**
|
|
83
|
+
* Enable/disable synonym expansion for TF-IDF search.
|
|
84
|
+
* When enabled, queries are expanded with synonyms to improve relevance.
|
|
85
|
+
* For example, "add user" will also match tools containing "create user".
|
|
86
|
+
* @default true
|
|
87
|
+
*/
|
|
88
|
+
enabled: z.boolean().default(true),
|
|
89
|
+
/**
|
|
90
|
+
* Additional synonym groups beyond the defaults.
|
|
91
|
+
* Each group is an array of related terms that should be treated as equivalent.
|
|
92
|
+
* @example [['customer', 'client', 'buyer'], ['order', 'purchase', 'transaction']]
|
|
93
|
+
*/
|
|
94
|
+
additionalSynonyms: z.array(z.array(z.string())).optional(),
|
|
95
|
+
/**
|
|
96
|
+
* Replace default synonyms entirely with additionalSynonyms.
|
|
97
|
+
* @default false
|
|
98
|
+
*/
|
|
99
|
+
replaceDefaults: z.boolean().default(false),
|
|
100
|
+
/**
|
|
101
|
+
* Maximum number of synonym expansions per term.
|
|
102
|
+
* Prevents query explosion for terms with many synonyms.
|
|
103
|
+
* @default 5
|
|
104
|
+
*/
|
|
105
|
+
maxExpansionsPerTerm: z.number().positive().default(5)
|
|
106
|
+
}).default({ enabled: true, replaceDefaults: false, maxExpansionsPerTerm: 5 });
|
|
107
|
+
var DEFAULT_EMBEDDING_OPTIONS = {
|
|
108
|
+
strategy: "tfidf",
|
|
109
|
+
modelName: "Xenova/all-MiniLM-L6-v2",
|
|
110
|
+
cacheDir: "./.cache/transformers",
|
|
111
|
+
useHNSW: false,
|
|
112
|
+
synonymExpansion: { enabled: true, replaceDefaults: false, maxExpansionsPerTerm: 5 }
|
|
113
|
+
};
|
|
114
|
+
var codeCallEmbeddingOptionsSchema = z.object({
|
|
115
|
+
/**
|
|
116
|
+
* Embedding strategy to use for tool search
|
|
117
|
+
* - 'tfidf': Lightweight, synchronous TF-IDF based search (no ML models required)
|
|
118
|
+
* - 'ml': ML-based semantic search using transformers.js (better quality, requires model download)
|
|
119
|
+
* @default 'tfidf'
|
|
120
|
+
*/
|
|
121
|
+
strategy: embeddingStrategySchema.optional(),
|
|
122
|
+
/**
|
|
123
|
+
* Model name for ML-based embeddings (only used when strategy='ml')
|
|
124
|
+
* @default 'Xenova/all-MiniLM-L6-v2'
|
|
125
|
+
*/
|
|
126
|
+
modelName: z.string().optional(),
|
|
127
|
+
/**
|
|
128
|
+
* Cache directory for ML models (only used when strategy='ml')
|
|
129
|
+
* @default './.cache/transformers'
|
|
130
|
+
*/
|
|
131
|
+
cacheDir: z.string().optional(),
|
|
132
|
+
/**
|
|
133
|
+
* Enable HNSW index for faster search (only used when strategy='ml')
|
|
134
|
+
* When enabled, provides O(log n) search instead of O(n) brute-force
|
|
135
|
+
* @default false
|
|
136
|
+
*/
|
|
137
|
+
useHNSW: z.boolean().optional(),
|
|
138
|
+
/**
|
|
139
|
+
* Synonym expansion configuration for TF-IDF search.
|
|
140
|
+
* When enabled, queries like "add user" will match tools for "create user".
|
|
141
|
+
* Only applies when strategy is 'tfidf' (ML already handles semantic similarity).
|
|
142
|
+
* Set to false to disable, or provide a config object to customize.
|
|
143
|
+
* @default { enabled: true }
|
|
144
|
+
*/
|
|
145
|
+
synonymExpansion: z.union([z.literal(false), synonymExpansionConfigSchema]).optional()
|
|
146
|
+
}).optional().transform((opts) => ({
|
|
147
|
+
strategy: opts?.strategy ?? DEFAULT_EMBEDDING_OPTIONS.strategy,
|
|
148
|
+
modelName: opts?.modelName ?? DEFAULT_EMBEDDING_OPTIONS.modelName,
|
|
149
|
+
cacheDir: opts?.cacheDir ?? DEFAULT_EMBEDDING_OPTIONS.cacheDir,
|
|
150
|
+
useHNSW: opts?.useHNSW ?? DEFAULT_EMBEDDING_OPTIONS.useHNSW,
|
|
151
|
+
synonymExpansion: opts?.synonymExpansion ?? DEFAULT_EMBEDDING_OPTIONS.synonymExpansion
|
|
152
|
+
}));
|
|
153
|
+
var codeCallSidecarOptionsSchema = z.object({
|
|
154
|
+
/**
|
|
155
|
+
* Enable pass-by-reference support via sidecar
|
|
156
|
+
* When enabled, large strings are automatically lifted to a sidecar
|
|
157
|
+
* and resolved at the callTool boundary
|
|
158
|
+
* @default false
|
|
159
|
+
*/
|
|
160
|
+
enabled: z.boolean().default(false),
|
|
161
|
+
/**
|
|
162
|
+
* Maximum total size of all stored references in bytes
|
|
163
|
+
* @default 16MB (from security level)
|
|
164
|
+
*/
|
|
165
|
+
maxTotalSize: z.number().positive().optional(),
|
|
166
|
+
/**
|
|
167
|
+
* Maximum size of a single reference in bytes
|
|
168
|
+
* @default 4MB (from security level)
|
|
169
|
+
*/
|
|
170
|
+
maxReferenceSize: z.number().positive().optional(),
|
|
171
|
+
/**
|
|
172
|
+
* Threshold in bytes to trigger extraction from source code
|
|
173
|
+
* Strings larger than this are lifted to the sidecar
|
|
174
|
+
* @default 64KB (from security level)
|
|
175
|
+
*/
|
|
176
|
+
extractionThreshold: z.number().positive().optional(),
|
|
177
|
+
/**
|
|
178
|
+
* Maximum expanded size when resolving references for tool calls
|
|
179
|
+
* @default 8MB (from security level)
|
|
180
|
+
*/
|
|
181
|
+
maxResolvedSize: z.number().positive().optional(),
|
|
182
|
+
/**
|
|
183
|
+
* Whether to allow composite handles from string concatenation
|
|
184
|
+
* If false, concatenating references throws an error
|
|
185
|
+
* @default false (strict mode)
|
|
186
|
+
*/
|
|
187
|
+
allowComposites: z.boolean().optional(),
|
|
188
|
+
/**
|
|
189
|
+
* Maximum script length (in characters) when sidecar is disabled
|
|
190
|
+
* Prevents large inline data from being embedded in script
|
|
191
|
+
* If null, no limit is enforced
|
|
192
|
+
* @default 64KB
|
|
193
|
+
*/
|
|
194
|
+
maxScriptLengthWhenDisabled: z.number().positive().nullable().default(64 * 1024)
|
|
195
|
+
}).default(() => ({
|
|
196
|
+
enabled: false,
|
|
197
|
+
maxScriptLengthWhenDisabled: 64 * 1024
|
|
198
|
+
}));
|
|
199
|
+
var codeCallPluginOptionsObjectSchema = z.object({
|
|
200
|
+
/**
|
|
201
|
+
* CodeCall mode
|
|
202
|
+
* @default 'codecall_only'
|
|
203
|
+
*/
|
|
204
|
+
mode: codeCallModeSchema,
|
|
205
|
+
/**
|
|
206
|
+
* Default number of tools to return in search results
|
|
207
|
+
* @default 8
|
|
208
|
+
*/
|
|
209
|
+
topK: z.number().positive().default(8),
|
|
210
|
+
/**
|
|
211
|
+
* Maximum number of tool definitions to include
|
|
212
|
+
* @default 8
|
|
213
|
+
*/
|
|
214
|
+
maxDefinitions: z.number().positive().default(8),
|
|
215
|
+
/**
|
|
216
|
+
* Optional filter function for including tools.
|
|
217
|
+
* Signature: (tool: IncludeToolsFilterToolInfo) => boolean
|
|
218
|
+
*/
|
|
219
|
+
includeTools: includeToolsFilterSchema.optional(),
|
|
220
|
+
/**
|
|
221
|
+
* Direct calls configuration
|
|
222
|
+
*/
|
|
223
|
+
directCalls: codeCallDirectCallsOptionsSchema.optional(),
|
|
224
|
+
/**
|
|
225
|
+
* VM execution options
|
|
226
|
+
*/
|
|
227
|
+
vm: codeCallVmOptionsSchema,
|
|
228
|
+
/**
|
|
229
|
+
* Embedding configuration for tool search
|
|
230
|
+
*/
|
|
231
|
+
embedding: codeCallEmbeddingOptionsSchema,
|
|
232
|
+
/**
|
|
233
|
+
* Sidecar (pass-by-reference) configuration
|
|
234
|
+
* When enabled, large data is stored outside the sandbox and resolved at callTool boundary
|
|
235
|
+
*/
|
|
236
|
+
sidecar: codeCallSidecarOptionsSchema
|
|
237
|
+
});
|
|
238
|
+
var DEFAULT_PLUGIN_OPTIONS = {
|
|
239
|
+
mode: "codecall_only",
|
|
240
|
+
topK: 8,
|
|
241
|
+
maxDefinitions: 8,
|
|
242
|
+
vm: DEFAULT_VM_OPTIONS,
|
|
243
|
+
embedding: DEFAULT_EMBEDDING_OPTIONS,
|
|
244
|
+
sidecar: {
|
|
245
|
+
enabled: false,
|
|
246
|
+
maxScriptLengthWhenDisabled: 64 * 1024
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
var codeCallPluginOptionsSchema = codeCallPluginOptionsObjectSchema.prefault(DEFAULT_PLUGIN_OPTIONS);
|
|
250
|
+
|
|
251
|
+
// plugins/plugin-codecall/src/services/tool-search.service.ts
|
|
252
|
+
import { TFIDFVectoria, VectoriaDB } from "vectoriadb";
|
|
253
|
+
|
|
254
|
+
// plugins/plugin-codecall/src/services/synonym-expansion.service.ts
|
|
255
|
+
var DEFAULT_SYNONYM_GROUPS = [
|
|
256
|
+
// ===========================================================================
|
|
257
|
+
// 1. CORE DATA OPERATIONS (CRUD+)
|
|
258
|
+
// ===========================================================================
|
|
259
|
+
// Creation / Instantiation
|
|
260
|
+
[
|
|
261
|
+
"create",
|
|
262
|
+
"add",
|
|
263
|
+
"new",
|
|
264
|
+
"insert",
|
|
265
|
+
"make",
|
|
266
|
+
"append",
|
|
267
|
+
"register",
|
|
268
|
+
"generate",
|
|
269
|
+
"produce",
|
|
270
|
+
"build",
|
|
271
|
+
"construct",
|
|
272
|
+
"provision",
|
|
273
|
+
"instantiate",
|
|
274
|
+
"define",
|
|
275
|
+
"compose",
|
|
276
|
+
"draft"
|
|
277
|
+
],
|
|
278
|
+
// Destructive Removal
|
|
279
|
+
[
|
|
280
|
+
"delete",
|
|
281
|
+
"remove",
|
|
282
|
+
"destroy",
|
|
283
|
+
"drop",
|
|
284
|
+
"erase",
|
|
285
|
+
"clear",
|
|
286
|
+
"purge",
|
|
287
|
+
"discard",
|
|
288
|
+
"eliminate",
|
|
289
|
+
"nuke",
|
|
290
|
+
"unbind",
|
|
291
|
+
"unregister"
|
|
292
|
+
],
|
|
293
|
+
// Retrieval / Access
|
|
294
|
+
["get", "fetch", "retrieve", "read", "obtain", "load", "pull", "access", "grab", "snag", "receive"],
|
|
295
|
+
// Modification
|
|
296
|
+
[
|
|
297
|
+
"update",
|
|
298
|
+
"edit",
|
|
299
|
+
"modify",
|
|
300
|
+
"change",
|
|
301
|
+
"patch",
|
|
302
|
+
"alter",
|
|
303
|
+
"revise",
|
|
304
|
+
"refresh",
|
|
305
|
+
"correct",
|
|
306
|
+
"amend",
|
|
307
|
+
"adjust",
|
|
308
|
+
"tweak",
|
|
309
|
+
"rectify",
|
|
310
|
+
"refine"
|
|
311
|
+
],
|
|
312
|
+
// Viewing / Listing
|
|
313
|
+
[
|
|
314
|
+
"list",
|
|
315
|
+
"show",
|
|
316
|
+
"display",
|
|
317
|
+
"enumerate",
|
|
318
|
+
"browse",
|
|
319
|
+
"view",
|
|
320
|
+
"peek",
|
|
321
|
+
"index",
|
|
322
|
+
"catalog",
|
|
323
|
+
"survey",
|
|
324
|
+
"inspect",
|
|
325
|
+
"ls",
|
|
326
|
+
"dir"
|
|
327
|
+
],
|
|
328
|
+
// Searching / Discovery
|
|
329
|
+
[
|
|
330
|
+
"find",
|
|
331
|
+
"search",
|
|
332
|
+
"lookup",
|
|
333
|
+
"query",
|
|
334
|
+
"locate",
|
|
335
|
+
"filter",
|
|
336
|
+
"scan",
|
|
337
|
+
"explore",
|
|
338
|
+
"investigate",
|
|
339
|
+
"detect",
|
|
340
|
+
"scout",
|
|
341
|
+
"seek"
|
|
342
|
+
],
|
|
343
|
+
// Soft Delete / Archival
|
|
344
|
+
["archive", "shelve", "retire", "hide", "suppress", "mute"],
|
|
345
|
+
["unarchive", "restore", "recover", "undelete", "unhide"],
|
|
346
|
+
// ===========================================================================
|
|
347
|
+
// 2. STATE & LIFECYCLE
|
|
348
|
+
// ===========================================================================
|
|
349
|
+
// Activation
|
|
350
|
+
[
|
|
351
|
+
"enable",
|
|
352
|
+
"activate",
|
|
353
|
+
"start",
|
|
354
|
+
"turn on",
|
|
355
|
+
"switch on",
|
|
356
|
+
"boot",
|
|
357
|
+
"init",
|
|
358
|
+
"initialize",
|
|
359
|
+
"setup",
|
|
360
|
+
"spin up",
|
|
361
|
+
"resume",
|
|
362
|
+
"unpause"
|
|
363
|
+
],
|
|
364
|
+
// Deactivation
|
|
365
|
+
[
|
|
366
|
+
"disable",
|
|
367
|
+
"deactivate",
|
|
368
|
+
"stop",
|
|
369
|
+
"turn off",
|
|
370
|
+
"switch off",
|
|
371
|
+
"shutdown",
|
|
372
|
+
"halt",
|
|
373
|
+
"kill",
|
|
374
|
+
"terminate",
|
|
375
|
+
"suspend",
|
|
376
|
+
"pause",
|
|
377
|
+
"cease"
|
|
378
|
+
],
|
|
379
|
+
// Execution
|
|
380
|
+
["run", "execute", "invoke", "trigger", "launch", "call", "perform", "operate", "handle", "process", "fire"],
|
|
381
|
+
// Reset cycles
|
|
382
|
+
["restart", "reboot", "reset", "reload", "bounce", "recycle", "refresh"],
|
|
383
|
+
// Validation & Check
|
|
384
|
+
["validate", "verify", "check", "confirm", "assert", "test", "audit", "assess", "healthcheck", "ping"],
|
|
385
|
+
// Analysis & Math
|
|
386
|
+
[
|
|
387
|
+
"analyze",
|
|
388
|
+
"interpret",
|
|
389
|
+
"diagnose",
|
|
390
|
+
"evaluate",
|
|
391
|
+
"review",
|
|
392
|
+
"summarize",
|
|
393
|
+
"count",
|
|
394
|
+
"calculate",
|
|
395
|
+
"compute",
|
|
396
|
+
"measure",
|
|
397
|
+
"aggregate",
|
|
398
|
+
"summarise"
|
|
399
|
+
],
|
|
400
|
+
// ===========================================================================
|
|
401
|
+
// 3. TRANSFER, IO & MANIPULATION
|
|
402
|
+
// ===========================================================================
|
|
403
|
+
// Duplication
|
|
404
|
+
["copy", "duplicate", "clone", "replicate", "mirror", "fork", "repro"],
|
|
405
|
+
// Movement
|
|
406
|
+
["move", "transfer", "migrate", "relocate", "rename", "shift", "mv", "slide"],
|
|
407
|
+
// Persistence
|
|
408
|
+
["save", "store", "write", "persist", "commit", "stash", "record", "log"],
|
|
409
|
+
// Synchronization
|
|
410
|
+
["sync", "synchronize", "resync", "reconcile", "align", "pair"],
|
|
411
|
+
// Import/Export
|
|
412
|
+
["import", "ingest", "upload", "push", "feed"],
|
|
413
|
+
["export", "download", "dump", "backup", "extract"],
|
|
414
|
+
// Connection
|
|
415
|
+
["connect", "link", "bind", "attach", "join", "bridge", "associate", "mount", "map"],
|
|
416
|
+
["disconnect", "unlink", "unbind", "detach", "leave", "dissociate", "unmount", "unmap"],
|
|
417
|
+
// ===========================================================================
|
|
418
|
+
// 4. DEVOPS, SECURITY & TECHNICAL
|
|
419
|
+
// ===========================================================================
|
|
420
|
+
// Auth
|
|
421
|
+
["login", "log in", "sign in", "authenticate", "auth"],
|
|
422
|
+
["logout", "log out", "sign out", "disconnect"],
|
|
423
|
+
// Permissions
|
|
424
|
+
["approve", "authorize", "grant", "permit", "allow", "sanction", "whitelist"],
|
|
425
|
+
["deny", "reject", "revoke", "forbid", "block", "ban", "blacklist"],
|
|
426
|
+
// Encryption
|
|
427
|
+
["encrypt", "secure", "lock", "seal", "protect", "scramble", "hash"],
|
|
428
|
+
["decrypt", "unlock", "unseal", "reveal", "decode"],
|
|
429
|
+
// Deployment
|
|
430
|
+
["deploy", "release", "ship", "publish", "roll out", "promote", "distribute", "install"],
|
|
431
|
+
// Development
|
|
432
|
+
["debug", "troubleshoot", "fix", "repair", "resolve", "trace"],
|
|
433
|
+
["compile", "transpile", "build", "assemble", "package", "bundle", "minify"],
|
|
434
|
+
// ===========================================================================
|
|
435
|
+
// 5. COMMERCE & BUSINESS LOGIC
|
|
436
|
+
// ===========================================================================
|
|
437
|
+
// Financial Transactions
|
|
438
|
+
["buy", "purchase", "order", "pay", "checkout", "spend"],
|
|
439
|
+
["sell", "refund", "reimburse", "charge", "invoice", "bill"],
|
|
440
|
+
["subscribe", "upgrade", "upsell"],
|
|
441
|
+
["unsubscribe", "cancel", "downgrade"],
|
|
442
|
+
// Scheduling
|
|
443
|
+
["schedule", "book", "appoint", "reserve", "plan", "calendar"],
|
|
444
|
+
["reschedule", "postpone", "delay", "defer"],
|
|
445
|
+
// ===========================================================================
|
|
446
|
+
// 6. COMMUNICATION & SOCIAL
|
|
447
|
+
// ===========================================================================
|
|
448
|
+
// Outbound
|
|
449
|
+
[
|
|
450
|
+
"send",
|
|
451
|
+
"dispatch",
|
|
452
|
+
"deliver",
|
|
453
|
+
"transmit",
|
|
454
|
+
"post",
|
|
455
|
+
"broadcast",
|
|
456
|
+
"notify",
|
|
457
|
+
"alert",
|
|
458
|
+
"email",
|
|
459
|
+
"text",
|
|
460
|
+
"message",
|
|
461
|
+
"chat"
|
|
462
|
+
],
|
|
463
|
+
// Social Interactions
|
|
464
|
+
["reply", "respond", "answer", "retort"],
|
|
465
|
+
["share", "forward", "retweet", "repost"],
|
|
466
|
+
["like", "favorite", "star", "upvote", "heart"],
|
|
467
|
+
["dislike", "downvote"],
|
|
468
|
+
["follow", "watch", "track"],
|
|
469
|
+
["unfollow", "ignore", "mute"],
|
|
470
|
+
// ===========================================================================
|
|
471
|
+
// 7. COMMON ENTITIES (NOUNS)
|
|
472
|
+
// ===========================================================================
|
|
473
|
+
// Users & Roles
|
|
474
|
+
[
|
|
475
|
+
"user",
|
|
476
|
+
"account",
|
|
477
|
+
"member",
|
|
478
|
+
"profile",
|
|
479
|
+
"identity",
|
|
480
|
+
"customer",
|
|
481
|
+
"principal",
|
|
482
|
+
"admin",
|
|
483
|
+
"operator",
|
|
484
|
+
"client",
|
|
485
|
+
"employee",
|
|
486
|
+
"staff"
|
|
487
|
+
],
|
|
488
|
+
["role", "group", "team", "squad", "unit", "department"],
|
|
489
|
+
// Data Artifacts
|
|
490
|
+
["file", "document", "attachment", "blob", "asset", "object", "resource", "content", "media"],
|
|
491
|
+
["image", "picture", "photo", "screenshot"],
|
|
492
|
+
["video", "clip", "recording", "footage"],
|
|
493
|
+
// System Artifacts
|
|
494
|
+
["message", "notification", "alert", "event", "signal", "webhook", "ping"],
|
|
495
|
+
["log", "trace", "metric", "telemetry", "audit trail", "history"],
|
|
496
|
+
["settings", "config", "configuration", "preferences", "options", "params", "env", "environment", "variables"],
|
|
497
|
+
["permission", "privilege", "access right", "policy", "rule", "scope"],
|
|
498
|
+
// Business Artifacts
|
|
499
|
+
["organization", "company", "tenant", "workspace", "org", "project", "repo", "repository"],
|
|
500
|
+
["product", "item", "sku", "inventory", "stock"],
|
|
501
|
+
["task", "ticket", "issue", "bug", "story", "epic", "todo", "job", "workitem"],
|
|
502
|
+
// Identification
|
|
503
|
+
["id", "identifier", "key", "uuid", "guid", "token", "hash", "fingerprint"]
|
|
504
|
+
];
|
|
505
|
+
var SynonymExpansionService = class {
|
|
506
|
+
synonymMap;
|
|
507
|
+
maxExpansions;
|
|
508
|
+
constructor(config = {}) {
|
|
509
|
+
this.maxExpansions = config.maxExpansionsPerTerm ?? 5;
|
|
510
|
+
const groups = config.replaceDefaults ? config.additionalSynonyms || [] : [...DEFAULT_SYNONYM_GROUPS, ...config.additionalSynonyms || []];
|
|
511
|
+
this.synonymMap = this.buildSynonymMap(groups);
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Build a bidirectional synonym map from groups.
|
|
515
|
+
* Each term maps to all other terms in its group(s).
|
|
516
|
+
*/
|
|
517
|
+
buildSynonymMap(groups) {
|
|
518
|
+
const map = /* @__PURE__ */ new Map();
|
|
519
|
+
for (const group of groups) {
|
|
520
|
+
const normalizedGroup = group.map((term) => term.toLowerCase());
|
|
521
|
+
for (const term of normalizedGroup) {
|
|
522
|
+
if (!map.has(term)) {
|
|
523
|
+
map.set(term, /* @__PURE__ */ new Set());
|
|
524
|
+
}
|
|
525
|
+
const synonyms = map.get(term);
|
|
526
|
+
for (const synonym of normalizedGroup) {
|
|
527
|
+
if (synonym !== term) {
|
|
528
|
+
synonyms.add(synonym);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return map;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Get synonyms for a single term.
|
|
537
|
+
* Returns empty array if no synonyms found.
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* getSynonyms('add') // ['create', 'new', 'insert', 'make']
|
|
541
|
+
*/
|
|
542
|
+
getSynonyms(term) {
|
|
543
|
+
const normalized = term.toLowerCase();
|
|
544
|
+
const synonyms = this.synonymMap.get(normalized);
|
|
545
|
+
if (!synonyms) {
|
|
546
|
+
return [];
|
|
547
|
+
}
|
|
548
|
+
return Array.from(synonyms).slice(0, this.maxExpansions);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Expand a query string by adding synonyms for each term.
|
|
552
|
+
* Returns the expanded query string with original terms and their synonyms.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* expandQuery('add user') // 'add create new insert make user account member profile'
|
|
556
|
+
*/
|
|
557
|
+
expandQuery(query) {
|
|
558
|
+
const terms = query.toLowerCase().split(/\s+/).filter((term) => term.length > 1);
|
|
559
|
+
const expandedTerms = [];
|
|
560
|
+
for (const term of terms) {
|
|
561
|
+
expandedTerms.push(term);
|
|
562
|
+
const synonyms = this.getSynonyms(term);
|
|
563
|
+
expandedTerms.push(...synonyms);
|
|
564
|
+
}
|
|
565
|
+
return expandedTerms.join(" ");
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Check if synonym expansion is available for any term in the query.
|
|
569
|
+
*/
|
|
570
|
+
hasExpansions(query) {
|
|
571
|
+
const terms = query.toLowerCase().split(/\s+/);
|
|
572
|
+
return terms.some((term) => this.synonymMap.has(term));
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Get statistics about the synonym dictionary.
|
|
576
|
+
*/
|
|
577
|
+
getStats() {
|
|
578
|
+
const termCount = this.synonymMap.size;
|
|
579
|
+
let totalSynonyms = 0;
|
|
580
|
+
for (const synonyms of this.synonymMap.values()) {
|
|
581
|
+
totalSynonyms += synonyms.size;
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
termCount,
|
|
585
|
+
avgSynonymsPerTerm: termCount > 0 ? totalSynonyms / termCount : 0
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// plugins/plugin-codecall/src/services/tool-search.service.ts
|
|
591
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
592
|
+
// Articles & Determiners
|
|
593
|
+
"the",
|
|
594
|
+
"a",
|
|
595
|
+
"an",
|
|
596
|
+
"this",
|
|
597
|
+
"that",
|
|
598
|
+
"these",
|
|
599
|
+
"those",
|
|
600
|
+
// Prepositions
|
|
601
|
+
"in",
|
|
602
|
+
"on",
|
|
603
|
+
"at",
|
|
604
|
+
"to",
|
|
605
|
+
"for",
|
|
606
|
+
"of",
|
|
607
|
+
"with",
|
|
608
|
+
"by",
|
|
609
|
+
"from",
|
|
610
|
+
"into",
|
|
611
|
+
"over",
|
|
612
|
+
"after",
|
|
613
|
+
"before",
|
|
614
|
+
"between",
|
|
615
|
+
"under",
|
|
616
|
+
"about",
|
|
617
|
+
"against",
|
|
618
|
+
"during",
|
|
619
|
+
"through",
|
|
620
|
+
// Conjunctions
|
|
621
|
+
"and",
|
|
622
|
+
"or",
|
|
623
|
+
"but",
|
|
624
|
+
"nor",
|
|
625
|
+
"so",
|
|
626
|
+
"yet",
|
|
627
|
+
"as",
|
|
628
|
+
"than",
|
|
629
|
+
"if",
|
|
630
|
+
"because",
|
|
631
|
+
"while",
|
|
632
|
+
"when",
|
|
633
|
+
"where",
|
|
634
|
+
"unless",
|
|
635
|
+
// Pronouns (Subject/Object/Possessive)
|
|
636
|
+
"i",
|
|
637
|
+
"me",
|
|
638
|
+
"my",
|
|
639
|
+
"mine",
|
|
640
|
+
"myself",
|
|
641
|
+
"you",
|
|
642
|
+
"your",
|
|
643
|
+
"yours",
|
|
644
|
+
"yourself",
|
|
645
|
+
"he",
|
|
646
|
+
"him",
|
|
647
|
+
"his",
|
|
648
|
+
"himself",
|
|
649
|
+
"she",
|
|
650
|
+
"her",
|
|
651
|
+
"hers",
|
|
652
|
+
"herself",
|
|
653
|
+
"it",
|
|
654
|
+
"its",
|
|
655
|
+
"itself",
|
|
656
|
+
"we",
|
|
657
|
+
"us",
|
|
658
|
+
"our",
|
|
659
|
+
"ours",
|
|
660
|
+
"ourselves",
|
|
661
|
+
"they",
|
|
662
|
+
"them",
|
|
663
|
+
"their",
|
|
664
|
+
"theirs",
|
|
665
|
+
"themselves",
|
|
666
|
+
"who",
|
|
667
|
+
"whom",
|
|
668
|
+
"whose",
|
|
669
|
+
"which",
|
|
670
|
+
"what",
|
|
671
|
+
// Auxiliary/Linking Verbs (State of being is usually noise, Action is signal)
|
|
672
|
+
"is",
|
|
673
|
+
"was",
|
|
674
|
+
"are",
|
|
675
|
+
"were",
|
|
676
|
+
"been",
|
|
677
|
+
"be",
|
|
678
|
+
"being",
|
|
679
|
+
"have",
|
|
680
|
+
"has",
|
|
681
|
+
"had",
|
|
682
|
+
"having",
|
|
683
|
+
"do",
|
|
684
|
+
"does",
|
|
685
|
+
"did",
|
|
686
|
+
"doing",
|
|
687
|
+
// "do" is usually auxiliary ("do you have..."). "run" or "execute" is better.
|
|
688
|
+
"will",
|
|
689
|
+
"would",
|
|
690
|
+
"shall",
|
|
691
|
+
"should",
|
|
692
|
+
"can",
|
|
693
|
+
"could",
|
|
694
|
+
"may",
|
|
695
|
+
"might",
|
|
696
|
+
"must",
|
|
697
|
+
// Quantifiers / Adverbs of degree
|
|
698
|
+
"all",
|
|
699
|
+
"any",
|
|
700
|
+
"both",
|
|
701
|
+
"each",
|
|
702
|
+
"few",
|
|
703
|
+
"more",
|
|
704
|
+
"most",
|
|
705
|
+
"other",
|
|
706
|
+
"some",
|
|
707
|
+
"such",
|
|
708
|
+
"no",
|
|
709
|
+
"nor",
|
|
710
|
+
"not",
|
|
711
|
+
"only",
|
|
712
|
+
"own",
|
|
713
|
+
"same",
|
|
714
|
+
"too",
|
|
715
|
+
"very",
|
|
716
|
+
"just",
|
|
717
|
+
"even",
|
|
718
|
+
// Conversational / Chat Fillers (Common in LLM prompts)
|
|
719
|
+
"please",
|
|
720
|
+
"pls",
|
|
721
|
+
"plz",
|
|
722
|
+
"thanks",
|
|
723
|
+
"thank",
|
|
724
|
+
"thx",
|
|
725
|
+
"hello",
|
|
726
|
+
"hi",
|
|
727
|
+
"hey",
|
|
728
|
+
"ok",
|
|
729
|
+
"okay",
|
|
730
|
+
"yes",
|
|
731
|
+
"no",
|
|
732
|
+
"actually",
|
|
733
|
+
"basically",
|
|
734
|
+
"literally",
|
|
735
|
+
"maybe",
|
|
736
|
+
"perhaps",
|
|
737
|
+
"now",
|
|
738
|
+
"then",
|
|
739
|
+
"here",
|
|
740
|
+
"there",
|
|
741
|
+
"again",
|
|
742
|
+
"once",
|
|
743
|
+
"back",
|
|
744
|
+
// "back" can be tricky, but usually implies direction not action
|
|
745
|
+
// Meta/Structural words
|
|
746
|
+
"example",
|
|
747
|
+
"context",
|
|
748
|
+
"optionally",
|
|
749
|
+
"optional",
|
|
750
|
+
// Users rarely search for "optional", they search for the thing itself.
|
|
751
|
+
"etc",
|
|
752
|
+
"ie",
|
|
753
|
+
"eg"
|
|
754
|
+
]);
|
|
755
|
+
var ToolSearchService = class _ToolSearchService {
|
|
756
|
+
static MAX_SUBSCRIPTION_RETRIES = 100;
|
|
757
|
+
static INITIAL_RETRY_DELAY_MS = 10;
|
|
758
|
+
static MAX_RETRY_DELAY_MS = 1e3;
|
|
759
|
+
vectorDB;
|
|
760
|
+
strategy;
|
|
761
|
+
initialized = false;
|
|
762
|
+
mlInitialized = false;
|
|
763
|
+
config;
|
|
764
|
+
scope;
|
|
765
|
+
unsubscribe;
|
|
766
|
+
synonymService = null;
|
|
767
|
+
// Subscription tracking for async initialization
|
|
768
|
+
subscriptionPromise;
|
|
769
|
+
subscriptionResolved = false;
|
|
770
|
+
subscriptionResolve = null;
|
|
771
|
+
subscriptionReject = null;
|
|
772
|
+
retryTimeoutId = null;
|
|
773
|
+
disposed = false;
|
|
774
|
+
constructor(config = {}, scope) {
|
|
775
|
+
this.scope = scope;
|
|
776
|
+
const embeddingOptions = config.embeddingOptions || {
|
|
777
|
+
strategy: "tfidf",
|
|
778
|
+
modelName: "Xenova/all-MiniLM-L6-v2",
|
|
779
|
+
cacheDir: "./.cache/transformers",
|
|
780
|
+
useHNSW: false,
|
|
781
|
+
synonymExpansion: { enabled: true, replaceDefaults: false, maxExpansionsPerTerm: 5 }
|
|
782
|
+
};
|
|
783
|
+
this.strategy = config.strategy || embeddingOptions.strategy || "tfidf";
|
|
784
|
+
this.config = {
|
|
785
|
+
strategy: this.strategy,
|
|
786
|
+
embeddingOptions,
|
|
787
|
+
defaultTopK: config.defaultTopK ?? 8,
|
|
788
|
+
defaultSimilarityThreshold: config.defaultSimilarityThreshold ?? 0,
|
|
789
|
+
mode: config.mode ?? "codecall_only",
|
|
790
|
+
includeTools: config.includeTools
|
|
791
|
+
};
|
|
792
|
+
const validModes = ["codecall_only", "codecall_opt_in", "metadata_driven"];
|
|
793
|
+
if (!validModes.includes(this.config.mode)) {
|
|
794
|
+
throw new Error(`Invalid CodeCall mode: ${this.config.mode}. Valid modes: ${validModes.join(", ")}`);
|
|
795
|
+
}
|
|
796
|
+
if (this.strategy === "ml") {
|
|
797
|
+
this.vectorDB = new VectoriaDB({
|
|
798
|
+
modelName: embeddingOptions.modelName || "Xenova/all-MiniLM-L6-v2",
|
|
799
|
+
cacheDir: embeddingOptions.cacheDir || "./.cache/transformers",
|
|
800
|
+
defaultTopK: this.config.defaultTopK,
|
|
801
|
+
defaultSimilarityThreshold: this.config.defaultSimilarityThreshold,
|
|
802
|
+
useHNSW: embeddingOptions.useHNSW || false
|
|
803
|
+
});
|
|
804
|
+
} else {
|
|
805
|
+
this.vectorDB = new TFIDFVectoria({
|
|
806
|
+
defaultTopK: this.config.defaultTopK,
|
|
807
|
+
defaultSimilarityThreshold: this.config.defaultSimilarityThreshold
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
if (config.synonymExpansion === false) {
|
|
811
|
+
this.synonymService = null;
|
|
812
|
+
} else if (this.strategy === "tfidf") {
|
|
813
|
+
const synonymConfig = typeof config.synonymExpansion === "object" ? config.synonymExpansion : {};
|
|
814
|
+
if (synonymConfig.enabled !== false) {
|
|
815
|
+
this.synonymService = new SynonymExpansionService(synonymConfig);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
this.subscriptionPromise = new Promise((resolve, reject) => {
|
|
819
|
+
this.subscriptionResolve = resolve;
|
|
820
|
+
this.subscriptionReject = reject;
|
|
821
|
+
});
|
|
822
|
+
this.setupSubscription();
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Ensures the service is subscribed to tool changes before proceeding.
|
|
826
|
+
* Public methods should call this before accessing tools.
|
|
827
|
+
*/
|
|
828
|
+
async ensureSubscribed() {
|
|
829
|
+
if (this.subscriptionResolved) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
await this.subscriptionPromise;
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Sets up subscription to tool changes with exponential backoff retry.
|
|
836
|
+
* Handles the case where scope.tools may not be available yet during plugin initialization.
|
|
837
|
+
*/
|
|
838
|
+
setupSubscription(retryCount = 0, delayMs = _ToolSearchService.INITIAL_RETRY_DELAY_MS) {
|
|
839
|
+
if (this.scope.tools) {
|
|
840
|
+
this.subscribeToToolChanges();
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (retryCount >= _ToolSearchService.MAX_SUBSCRIPTION_RETRIES) {
|
|
844
|
+
this.scope.logger.warn(
|
|
845
|
+
"ToolSearchService: scope.tools not available after max retries. Tool search will return incomplete results until tools are registered."
|
|
846
|
+
);
|
|
847
|
+
this.markSubscribed();
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
const nextDelay = Math.min(delayMs * 2, _ToolSearchService.MAX_RETRY_DELAY_MS);
|
|
851
|
+
this.retryTimeoutId = setTimeout(() => {
|
|
852
|
+
this.retryTimeoutId = null;
|
|
853
|
+
if (this.disposed) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
this.setupSubscription(retryCount + 1, nextDelay);
|
|
857
|
+
}, delayMs);
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Subscribes to tool changes once scope.tools is available.
|
|
861
|
+
*/
|
|
862
|
+
subscribeToToolChanges() {
|
|
863
|
+
this.unsubscribe = this.scope.tools.subscribe({ immediate: true }, (event) => {
|
|
864
|
+
this.handleToolChange(event.snapshot);
|
|
865
|
+
});
|
|
866
|
+
this.markSubscribed();
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Marks the subscription as resolved, allowing pending operations to proceed.
|
|
870
|
+
*/
|
|
871
|
+
markSubscribed() {
|
|
872
|
+
this.subscriptionResolved = true;
|
|
873
|
+
if (this.subscriptionResolve) {
|
|
874
|
+
this.subscriptionResolve();
|
|
875
|
+
this.subscriptionResolve = null;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Handles tool change events by reindexing all tools from the snapshot
|
|
880
|
+
*/
|
|
881
|
+
async handleToolChange(tools) {
|
|
882
|
+
this.vectorDB.clear();
|
|
883
|
+
if (tools.length === 0) {
|
|
884
|
+
this.initialized = true;
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (!this.mlInitialized && this.strategy === "ml" && this.vectorDB instanceof VectoriaDB) {
|
|
888
|
+
await this.vectorDB.initialize();
|
|
889
|
+
this.mlInitialized = true;
|
|
890
|
+
}
|
|
891
|
+
const filteredTools = tools.filter((tool) => this.shouldIndexTool(tool));
|
|
892
|
+
if (filteredTools.length === 0) {
|
|
893
|
+
this.initialized = true;
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const documents = filteredTools.map((tool) => {
|
|
897
|
+
const searchableText = this.extractSearchableText(tool);
|
|
898
|
+
const appId = this.extractAppId(tool);
|
|
899
|
+
const toolName = tool.name;
|
|
900
|
+
const qualifiedName = tool.fullName || toolName;
|
|
901
|
+
return {
|
|
902
|
+
id: toolName,
|
|
903
|
+
text: searchableText,
|
|
904
|
+
metadata: {
|
|
905
|
+
id: toolName,
|
|
906
|
+
toolName,
|
|
907
|
+
qualifiedName,
|
|
908
|
+
appId,
|
|
909
|
+
toolInstance: tool
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
});
|
|
913
|
+
if (this.strategy === "ml" && this.vectorDB instanceof VectoriaDB) {
|
|
914
|
+
await this.vectorDB.addMany(documents);
|
|
915
|
+
} else if (this.vectorDB instanceof TFIDFVectoria) {
|
|
916
|
+
this.vectorDB.addDocuments(documents);
|
|
917
|
+
this.vectorDB.reindex();
|
|
918
|
+
}
|
|
919
|
+
this.initialized = true;
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Determines if a tool should be indexed in the search database.
|
|
923
|
+
* Filters based on:
|
|
924
|
+
* - Excludes codecall:* meta-tools (they should not be searchable)
|
|
925
|
+
* - Mode-based filtering (codecall_only, codecall_opt_in, metadata_driven)
|
|
926
|
+
* - Per-tool metadata.codecall.enabledInCodeCall
|
|
927
|
+
* - Custom includeTools filter function
|
|
928
|
+
*/
|
|
929
|
+
shouldIndexTool(tool) {
|
|
930
|
+
const toolName = tool.name || tool.fullName;
|
|
931
|
+
if (toolName.startsWith("codecall:")) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
const codecallMeta = this.getCodeCallMetadata(tool);
|
|
935
|
+
switch (this.config.mode) {
|
|
936
|
+
case "codecall_only":
|
|
937
|
+
if (codecallMeta?.enabledInCodeCall === false) {
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
break;
|
|
941
|
+
case "codecall_opt_in":
|
|
942
|
+
if (codecallMeta?.enabledInCodeCall !== true) {
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
break;
|
|
946
|
+
case "metadata_driven":
|
|
947
|
+
if (codecallMeta?.enabledInCodeCall === false) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
break;
|
|
951
|
+
default:
|
|
952
|
+
throw new Error(`Unknown CodeCall mode: ${this.config.mode}`);
|
|
953
|
+
}
|
|
954
|
+
if (this.config.includeTools) {
|
|
955
|
+
const appId = this.extractAppId(tool);
|
|
956
|
+
const filterInfo = {
|
|
957
|
+
name: toolName,
|
|
958
|
+
appId,
|
|
959
|
+
source: codecallMeta?.source,
|
|
960
|
+
description: tool.metadata.description,
|
|
961
|
+
tags: codecallMeta?.tags || tool.metadata.tags
|
|
962
|
+
};
|
|
963
|
+
if (!this.config.includeTools(filterInfo)) {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Extract CodeCall-specific metadata from a tool.
|
|
971
|
+
*/
|
|
972
|
+
getCodeCallMetadata(tool) {
|
|
973
|
+
return tool.metadata?.codecall;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Initializes the search service by indexing all tools from the registry.
|
|
977
|
+
* NOTE: This method is now a no-op. Initialization is handled reactively
|
|
978
|
+
* via subscription to tool change events in the constructor.
|
|
979
|
+
* This method exists for interface compatibility.
|
|
980
|
+
*/
|
|
981
|
+
async initialize() {
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Cleanup subscription and pending retries when service is destroyed
|
|
985
|
+
*/
|
|
986
|
+
dispose() {
|
|
987
|
+
this.disposed = true;
|
|
988
|
+
if (this.retryTimeoutId) {
|
|
989
|
+
clearTimeout(this.retryTimeoutId);
|
|
990
|
+
this.retryTimeoutId = null;
|
|
991
|
+
}
|
|
992
|
+
if (this.subscriptionReject) {
|
|
993
|
+
this.subscriptionReject(new Error("ToolSearchService disposed before subscription completed"));
|
|
994
|
+
this.subscriptionReject = null;
|
|
995
|
+
this.subscriptionResolve = null;
|
|
996
|
+
}
|
|
997
|
+
if (this.unsubscribe) {
|
|
998
|
+
this.unsubscribe();
|
|
999
|
+
this.unsubscribe = void 0;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Extracts searchable text from a tool instance.
|
|
1004
|
+
* Uses term weighting to improve relevance:
|
|
1005
|
+
* - Description terms are heavily weighted (most important for semantic matching)
|
|
1006
|
+
* - Tool name parts are tokenized and weighted
|
|
1007
|
+
* - Tags provide additional context
|
|
1008
|
+
*/
|
|
1009
|
+
extractSearchableText(tool) {
|
|
1010
|
+
const parts = [];
|
|
1011
|
+
if (tool.name) {
|
|
1012
|
+
const nameParts = tool.name.split(/[:\-_.]/).filter(Boolean);
|
|
1013
|
+
for (const part of nameParts) {
|
|
1014
|
+
parts.push(part, part);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (tool.metadata.description) {
|
|
1018
|
+
const description = tool.metadata.description;
|
|
1019
|
+
parts.push(description, description, description);
|
|
1020
|
+
const keyTerms = description.toLowerCase().split(/\s+/).filter((word) => word.length >= 4 && !this.isStopWord(word));
|
|
1021
|
+
parts.push(...keyTerms);
|
|
1022
|
+
}
|
|
1023
|
+
if (tool.metadata.tags && tool.metadata.tags.length > 0) {
|
|
1024
|
+
for (const tag of tool.metadata.tags) {
|
|
1025
|
+
parts.push(tag, tag);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (tool.rawInputSchema && typeof tool.rawInputSchema === "object") {
|
|
1029
|
+
const schema = tool.rawInputSchema;
|
|
1030
|
+
if (schema.properties) {
|
|
1031
|
+
parts.push(...Object.keys(schema.properties));
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
const examples = tool.metadata?.examples;
|
|
1035
|
+
if (examples && Array.isArray(examples)) {
|
|
1036
|
+
for (const ex of examples) {
|
|
1037
|
+
if (ex.description) {
|
|
1038
|
+
parts.push(ex.description, ex.description);
|
|
1039
|
+
}
|
|
1040
|
+
if (ex.input && typeof ex.input === "object") {
|
|
1041
|
+
for (const [key, value] of Object.entries(ex.input)) {
|
|
1042
|
+
parts.push(key);
|
|
1043
|
+
if (typeof value === "string") {
|
|
1044
|
+
parts.push(value);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return parts.join(" ");
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Checks if a word is a common stop word that should not receive extra weighting.
|
|
1054
|
+
* Uses module-level STOP_WORDS constant to avoid recreating the Set on each call.
|
|
1055
|
+
*/
|
|
1056
|
+
isStopWord(word) {
|
|
1057
|
+
return STOP_WORDS.has(word);
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Extracts app ID from tool's owner lineage
|
|
1061
|
+
*/
|
|
1062
|
+
extractAppId(tool) {
|
|
1063
|
+
if (!tool.owner) return void 0;
|
|
1064
|
+
if (tool.owner.kind === "app") {
|
|
1065
|
+
return tool.owner.id;
|
|
1066
|
+
}
|
|
1067
|
+
return void 0;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Searches for tools matching the query
|
|
1071
|
+
* Implements the ToolSearch interface
|
|
1072
|
+
*/
|
|
1073
|
+
async search(query, options = {}) {
|
|
1074
|
+
await this.ensureSubscribed();
|
|
1075
|
+
const { topK = this.config.defaultTopK, appIds, excludeToolNames = [] } = options;
|
|
1076
|
+
const minScore = this.config.defaultSimilarityThreshold;
|
|
1077
|
+
const filter = (metadata) => {
|
|
1078
|
+
if (excludeToolNames.includes(metadata.toolName)) {
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
if (appIds && appIds.length > 0) {
|
|
1082
|
+
if (!metadata.appId || !appIds.includes(metadata.appId)) {
|
|
1083
|
+
return false;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return true;
|
|
1087
|
+
};
|
|
1088
|
+
const effectiveQuery = this.synonymService ? this.synonymService.expandQuery(query) : query;
|
|
1089
|
+
const results = await this.vectorDB.search(effectiveQuery, {
|
|
1090
|
+
topK,
|
|
1091
|
+
threshold: minScore,
|
|
1092
|
+
filter
|
|
1093
|
+
});
|
|
1094
|
+
return results.map((result) => ({
|
|
1095
|
+
toolName: result.metadata.toolName,
|
|
1096
|
+
appId: result.metadata.appId,
|
|
1097
|
+
description: result.metadata.toolInstance.metadata.description || "",
|
|
1098
|
+
relevanceScore: result.score
|
|
1099
|
+
}));
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Gets all indexed tool names
|
|
1103
|
+
*/
|
|
1104
|
+
getAllToolNames() {
|
|
1105
|
+
if (this.vectorDB instanceof VectoriaDB) {
|
|
1106
|
+
return this.vectorDB.getAll().map((doc) => doc.id);
|
|
1107
|
+
} else {
|
|
1108
|
+
return this.vectorDB.getAllDocumentIds();
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Gets the total number of indexed tools
|
|
1113
|
+
*/
|
|
1114
|
+
getTotalCount() {
|
|
1115
|
+
if (this.vectorDB instanceof VectoriaDB) {
|
|
1116
|
+
return this.vectorDB.size();
|
|
1117
|
+
} else {
|
|
1118
|
+
return this.vectorDB.getDocumentCount();
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Checks if a tool exists in the index
|
|
1123
|
+
*/
|
|
1124
|
+
hasTool(toolName) {
|
|
1125
|
+
if (this.vectorDB instanceof VectoriaDB) {
|
|
1126
|
+
return this.vectorDB.has(toolName);
|
|
1127
|
+
} else {
|
|
1128
|
+
return this.vectorDB.hasDocument(toolName);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Clears the entire index
|
|
1133
|
+
*/
|
|
1134
|
+
clear() {
|
|
1135
|
+
this.vectorDB.clear();
|
|
1136
|
+
this.initialized = false;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Get the current embedding strategy
|
|
1140
|
+
*/
|
|
1141
|
+
getStrategy() {
|
|
1142
|
+
return this.strategy;
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Check if the service is initialized
|
|
1146
|
+
*/
|
|
1147
|
+
isInitialized() {
|
|
1148
|
+
return this.initialized;
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
|
|
1152
|
+
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1153
|
+
import { Provider, ProviderScope } from "@frontmcp/sdk";
|
|
1154
|
+
import { Enclave } from "enclave-vm";
|
|
1155
|
+
var ScriptTooLargeError = class extends Error {
|
|
1156
|
+
code = "SCRIPT_TOO_LARGE";
|
|
1157
|
+
scriptLength;
|
|
1158
|
+
maxLength;
|
|
1159
|
+
constructor(scriptLength, maxLength) {
|
|
1160
|
+
super(
|
|
1161
|
+
`Script length (${scriptLength} characters) exceeds maximum allowed length (${maxLength} characters). Enable sidecar to handle large data, or reduce script size.`
|
|
1162
|
+
);
|
|
1163
|
+
this.name = "ScriptTooLargeError";
|
|
1164
|
+
this.scriptLength = scriptLength;
|
|
1165
|
+
this.maxLength = maxLength;
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
var EnclaveService = class {
|
|
1169
|
+
vmOptions;
|
|
1170
|
+
sidecarOptions;
|
|
1171
|
+
constructor(config) {
|
|
1172
|
+
const all = config.getAll();
|
|
1173
|
+
this.vmOptions = all.resolvedVm;
|
|
1174
|
+
this.sidecarOptions = all.sidecar;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Execute AgentScript code in the enclave
|
|
1178
|
+
*
|
|
1179
|
+
* @param code - The AgentScript code to execute (raw, not transformed)
|
|
1180
|
+
* @param environment - The VM environment with callTool, getTool, etc.
|
|
1181
|
+
* @returns Execution result with success/error and logs
|
|
1182
|
+
* @throws ScriptTooLargeError if script exceeds max length and sidecar is disabled
|
|
1183
|
+
*/
|
|
1184
|
+
async execute(code, environment) {
|
|
1185
|
+
const logs = [];
|
|
1186
|
+
if (!this.sidecarOptions.enabled && this.sidecarOptions.maxScriptLengthWhenDisabled !== null) {
|
|
1187
|
+
const maxLength = this.sidecarOptions.maxScriptLengthWhenDisabled;
|
|
1188
|
+
if (code.length > maxLength) {
|
|
1189
|
+
throw new ScriptTooLargeError(code.length, maxLength);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
const toolHandler = async (toolName, args) => {
|
|
1193
|
+
return environment.callTool(toolName, args);
|
|
1194
|
+
};
|
|
1195
|
+
const sidecar = this.sidecarOptions.enabled ? {
|
|
1196
|
+
enabled: true,
|
|
1197
|
+
maxTotalSize: this.sidecarOptions.maxTotalSize,
|
|
1198
|
+
maxReferenceSize: this.sidecarOptions.maxReferenceSize,
|
|
1199
|
+
extractionThreshold: this.sidecarOptions.extractionThreshold,
|
|
1200
|
+
maxResolvedSize: this.sidecarOptions.maxResolvedSize,
|
|
1201
|
+
allowComposites: this.sidecarOptions.allowComposites
|
|
1202
|
+
} : void 0;
|
|
1203
|
+
const enclave = new Enclave({
|
|
1204
|
+
timeout: this.vmOptions.timeoutMs,
|
|
1205
|
+
maxToolCalls: this.vmOptions.maxSteps || 100,
|
|
1206
|
+
maxIterations: 1e4,
|
|
1207
|
+
toolHandler,
|
|
1208
|
+
validate: true,
|
|
1209
|
+
transform: true,
|
|
1210
|
+
sidecar,
|
|
1211
|
+
// Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
|
|
1212
|
+
allowFunctionsInGlobals: true,
|
|
1213
|
+
globals: {
|
|
1214
|
+
// Provide getTool as a custom global
|
|
1215
|
+
getTool: environment.getTool,
|
|
1216
|
+
// Provide logging functions if available
|
|
1217
|
+
...environment.mcpLog ? {
|
|
1218
|
+
mcpLog: (level, message, metadata) => {
|
|
1219
|
+
environment.mcpLog(level, message, metadata);
|
|
1220
|
+
logs.push(`[mcp:${level}] ${message}`);
|
|
1221
|
+
}
|
|
1222
|
+
} : {},
|
|
1223
|
+
...environment.mcpNotify ? {
|
|
1224
|
+
mcpNotify: (event, payload) => {
|
|
1225
|
+
environment.mcpNotify(event, payload);
|
|
1226
|
+
logs.push(`[notify] ${event}`);
|
|
1227
|
+
}
|
|
1228
|
+
} : {}
|
|
1229
|
+
// Note: enclave-vm v2.0.0+ provides its own __safe_console internally with rate limiting
|
|
1230
|
+
// and output size limits. Passing console in globals causes "Cannot redefine property"
|
|
1231
|
+
// errors due to Double VM architecture. Console output from user scripts goes to stdout
|
|
1232
|
+
// via enclave's internal console, not to this logs array. Only mcpLog/mcpNotify are captured.
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
try {
|
|
1236
|
+
const result = await enclave.run(code);
|
|
1237
|
+
return this.mapEnclaveResult(result, logs);
|
|
1238
|
+
} finally {
|
|
1239
|
+
enclave.dispose();
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Map Enclave ExecutionResult to EnclaveExecutionResult
|
|
1244
|
+
*/
|
|
1245
|
+
mapEnclaveResult(result, logs) {
|
|
1246
|
+
if (result.success) {
|
|
1247
|
+
return {
|
|
1248
|
+
success: true,
|
|
1249
|
+
result: result.value,
|
|
1250
|
+
logs,
|
|
1251
|
+
timedOut: false,
|
|
1252
|
+
stats: {
|
|
1253
|
+
duration: result.stats.duration,
|
|
1254
|
+
toolCallCount: result.stats.toolCallCount,
|
|
1255
|
+
iterationCount: result.stats.iterationCount
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
const error = result.error;
|
|
1260
|
+
const timedOut = error.message?.includes("timed out") || error.code === "TIMEOUT";
|
|
1261
|
+
if (error.code === "VALIDATION_ERROR") {
|
|
1262
|
+
return {
|
|
1263
|
+
success: false,
|
|
1264
|
+
error: {
|
|
1265
|
+
message: error.message,
|
|
1266
|
+
name: "ValidationError",
|
|
1267
|
+
code: error.code
|
|
1268
|
+
},
|
|
1269
|
+
logs,
|
|
1270
|
+
timedOut: false
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
const errorData = error.data;
|
|
1274
|
+
const toolName = errorData?.["toolName"];
|
|
1275
|
+
if (toolName) {
|
|
1276
|
+
return {
|
|
1277
|
+
success: false,
|
|
1278
|
+
error: {
|
|
1279
|
+
message: error.message,
|
|
1280
|
+
name: error.name,
|
|
1281
|
+
stack: error.stack,
|
|
1282
|
+
code: error.code,
|
|
1283
|
+
toolName,
|
|
1284
|
+
toolInput: errorData?.["toolInput"],
|
|
1285
|
+
details: errorData?.["details"]
|
|
1286
|
+
},
|
|
1287
|
+
logs,
|
|
1288
|
+
timedOut
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
return {
|
|
1292
|
+
success: false,
|
|
1293
|
+
error: {
|
|
1294
|
+
message: error.message,
|
|
1295
|
+
name: error.name,
|
|
1296
|
+
stack: error.stack,
|
|
1297
|
+
code: error.code
|
|
1298
|
+
},
|
|
1299
|
+
logs,
|
|
1300
|
+
timedOut,
|
|
1301
|
+
stats: {
|
|
1302
|
+
duration: result.stats.duration,
|
|
1303
|
+
toolCallCount: result.stats.toolCallCount,
|
|
1304
|
+
iterationCount: result.stats.iterationCount
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
EnclaveService = __decorateClass([
|
|
1310
|
+
Provider({
|
|
1311
|
+
name: "codecall:enclave",
|
|
1312
|
+
description: "Executes AgentScript code in a secure enclave",
|
|
1313
|
+
scope: ProviderScope.GLOBAL
|
|
1314
|
+
})
|
|
1315
|
+
], EnclaveService);
|
|
1316
|
+
|
|
1317
|
+
// plugins/plugin-codecall/src/tools/search.tool.ts
|
|
1318
|
+
import { Tool, ToolContext } from "@frontmcp/sdk";
|
|
1319
|
+
|
|
1320
|
+
// plugins/plugin-codecall/src/tools/search.schema.ts
|
|
1321
|
+
import { z as z2 } from "zod";
|
|
1322
|
+
var searchToolDescription = `Find tools by splitting user request into atomic actions.
|
|
1323
|
+
|
|
1324
|
+
DECOMPOSE: "delete users and send email" \u2192 queries: ["delete user", "send email"]
|
|
1325
|
+
DECOMPOSE: "get order and refund" \u2192 queries: ["get order", "calculate refund"]
|
|
1326
|
+
|
|
1327
|
+
AVOID RE-SEARCHING: Use excludeToolNames for already-discovered tools.
|
|
1328
|
+
RE-SEARCH WHEN: describe fails (typo?) OR execute returns tool_not_found.
|
|
1329
|
+
|
|
1330
|
+
INPUT:
|
|
1331
|
+
- queries: string[] (required) - atomic action phrases, max 10
|
|
1332
|
+
- appIds?: string[] - filter by app
|
|
1333
|
+
- excludeToolNames?: string[] - skip known tools
|
|
1334
|
+
- topK?: number (default 5) - results per query
|
|
1335
|
+
- minRelevanceScore?: number (default 0.3) - minimum match threshold
|
|
1336
|
+
|
|
1337
|
+
OUTPUT: Flat deduplicated tool list. relevanceScore: 0.5+=good, 0.7+=strong match.
|
|
1338
|
+
|
|
1339
|
+
FLOW: search \u2192 describe \u2192 execute/invoke`;
|
|
1340
|
+
var searchToolInputSchema = z2.object({
|
|
1341
|
+
queries: z2.array(z2.string().min(2).max(256)).min(1).max(10).describe("Atomic action queries. Split complex requests into simple actions."),
|
|
1342
|
+
appIds: z2.array(z2.string()).max(10).optional().describe("Filter by app IDs"),
|
|
1343
|
+
excludeToolNames: z2.array(z2.string()).max(50).optional().describe("Skip already-known tool names"),
|
|
1344
|
+
topK: z2.number().int().positive().max(50).optional().default(10).describe("Results per query (default 10)"),
|
|
1345
|
+
minRelevanceScore: z2.number().min(0).max(1).optional().default(0.1).describe("Minimum relevance threshold (default 0.1)")
|
|
1346
|
+
});
|
|
1347
|
+
var searchToolOutputSchema = z2.object({
|
|
1348
|
+
tools: z2.array(
|
|
1349
|
+
z2.object({
|
|
1350
|
+
name: z2.string().describe('Tool name (e.g., "users:list")'),
|
|
1351
|
+
appId: z2.string().optional().describe("App ID"),
|
|
1352
|
+
description: z2.string().describe("What this tool does"),
|
|
1353
|
+
relevanceScore: z2.number().min(0).max(1).describe("Match score (0-1)"),
|
|
1354
|
+
matchedQueries: z2.array(z2.string()).describe("Which queries matched this tool")
|
|
1355
|
+
})
|
|
1356
|
+
).describe("Deduplicated tools sorted by relevance"),
|
|
1357
|
+
warnings: z2.array(
|
|
1358
|
+
z2.object({
|
|
1359
|
+
type: z2.enum(["excluded_tool_not_found", "no_results", "low_relevance"]).describe("Warning type"),
|
|
1360
|
+
message: z2.string().describe("Warning message"),
|
|
1361
|
+
affectedTools: z2.array(z2.string()).optional().describe("Affected tool names")
|
|
1362
|
+
})
|
|
1363
|
+
).describe("Search warnings"),
|
|
1364
|
+
totalAvailableTools: z2.number().int().nonnegative().describe("Total tools in index")
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
// plugins/plugin-codecall/src/tools/search.tool.ts
|
|
1368
|
+
var SearchTool = class extends ToolContext {
|
|
1369
|
+
async execute(input) {
|
|
1370
|
+
const { queries, appIds, excludeToolNames = [], topK = 5, minRelevanceScore = 0.3 } = input;
|
|
1371
|
+
const searchService = this.get(ToolSearchService);
|
|
1372
|
+
const warnings = [];
|
|
1373
|
+
const nonExistentExcludedTools = excludeToolNames.filter((toolName) => !searchService.hasTool(toolName));
|
|
1374
|
+
if (nonExistentExcludedTools.length > 0) {
|
|
1375
|
+
warnings.push({
|
|
1376
|
+
type: "excluded_tool_not_found",
|
|
1377
|
+
message: `Excluded tools not found: ${nonExistentExcludedTools.join(", ")}`,
|
|
1378
|
+
affectedTools: nonExistentExcludedTools
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
const toolMap = /* @__PURE__ */ new Map();
|
|
1382
|
+
let lowRelevanceCount = 0;
|
|
1383
|
+
for (const query of queries) {
|
|
1384
|
+
const searchResults = await searchService.search(query, {
|
|
1385
|
+
topK,
|
|
1386
|
+
appIds,
|
|
1387
|
+
excludeToolNames
|
|
1388
|
+
});
|
|
1389
|
+
for (const result of searchResults) {
|
|
1390
|
+
if (result.relevanceScore < minRelevanceScore) {
|
|
1391
|
+
lowRelevanceCount++;
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
const existing = toolMap.get(result.toolName);
|
|
1395
|
+
if (existing) {
|
|
1396
|
+
existing.matchedQueries.push(query);
|
|
1397
|
+
existing.relevanceScore = Math.max(existing.relevanceScore, result.relevanceScore);
|
|
1398
|
+
} else {
|
|
1399
|
+
toolMap.set(result.toolName, {
|
|
1400
|
+
name: result.toolName,
|
|
1401
|
+
appId: result.appId,
|
|
1402
|
+
description: result.description,
|
|
1403
|
+
relevanceScore: result.relevanceScore,
|
|
1404
|
+
matchedQueries: [query]
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
const tools = Array.from(toolMap.values()).sort((a, b) => b.relevanceScore - a.relevanceScore).map((tool) => ({
|
|
1410
|
+
name: tool.name,
|
|
1411
|
+
appId: tool.appId,
|
|
1412
|
+
description: tool.description,
|
|
1413
|
+
relevanceScore: tool.relevanceScore,
|
|
1414
|
+
matchedQueries: tool.matchedQueries
|
|
1415
|
+
}));
|
|
1416
|
+
if (tools.length === 0) {
|
|
1417
|
+
warnings.push({
|
|
1418
|
+
type: "no_results",
|
|
1419
|
+
message: `No tools found for queries: ${queries.join(", ")}${appIds?.length ? ` in apps: ${appIds.join(", ")}` : ""}`
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
if (lowRelevanceCount > 0 && tools.length > 0) {
|
|
1423
|
+
warnings.push({
|
|
1424
|
+
type: "low_relevance",
|
|
1425
|
+
message: `${lowRelevanceCount} result(s) filtered due to relevance below ${minRelevanceScore}`
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
return {
|
|
1429
|
+
tools,
|
|
1430
|
+
warnings,
|
|
1431
|
+
totalAvailableTools: searchService.getTotalCount()
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
SearchTool = __decorateClass([
|
|
1436
|
+
Tool({
|
|
1437
|
+
name: "codecall:search",
|
|
1438
|
+
cache: {
|
|
1439
|
+
ttl: 60,
|
|
1440
|
+
// 1 minute
|
|
1441
|
+
slideWindow: false
|
|
1442
|
+
},
|
|
1443
|
+
codecall: {
|
|
1444
|
+
enabledInCodeCall: false,
|
|
1445
|
+
visibleInListTools: true
|
|
1446
|
+
},
|
|
1447
|
+
description: searchToolDescription,
|
|
1448
|
+
inputSchema: searchToolInputSchema,
|
|
1449
|
+
outputSchema: searchToolOutputSchema,
|
|
1450
|
+
annotations: {
|
|
1451
|
+
readOnlyHint: true,
|
|
1452
|
+
openWorldHint: true
|
|
1453
|
+
}
|
|
1454
|
+
})
|
|
1455
|
+
], SearchTool);
|
|
1456
|
+
|
|
1457
|
+
// plugins/plugin-codecall/src/tools/describe.tool.ts
|
|
1458
|
+
import { Tool as Tool2, ToolContext as ToolContext2 } from "@frontmcp/sdk";
|
|
1459
|
+
import { toJSONSchema } from "zod/v4";
|
|
1460
|
+
import { z as z4, ZodType } from "zod";
|
|
1461
|
+
|
|
1462
|
+
// plugins/plugin-codecall/src/tools/describe.schema.ts
|
|
1463
|
+
import { z as z3 } from "zod";
|
|
1464
|
+
import { ToolAnnotationsSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1465
|
+
var describeToolDescription = `Get input/output schemas for tools from search results.
|
|
1466
|
+
|
|
1467
|
+
INPUT: toolNames: string[] - tool names from search
|
|
1468
|
+
OUTPUT per tool: inputSchema (JSON Schema), outputSchema (JSON Schema), usageExamples (up to 5 callTool examples)
|
|
1469
|
+
|
|
1470
|
+
IMPORTANT: If notFound array is non-empty \u2192 re-search with corrected queries.
|
|
1471
|
+
FLOW: search \u2192 describe \u2192 execute/invoke`;
|
|
1472
|
+
var describeToolInputSchema = z3.object({
|
|
1473
|
+
toolNames: z3.array(z3.string()).min(1).superRefine((toolNames, ctx) => {
|
|
1474
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1475
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
1476
|
+
for (const name of toolNames) {
|
|
1477
|
+
if (seen.has(name)) {
|
|
1478
|
+
duplicates.add(name);
|
|
1479
|
+
}
|
|
1480
|
+
seen.add(name);
|
|
1481
|
+
}
|
|
1482
|
+
if (duplicates.size > 0) {
|
|
1483
|
+
ctx.addIssue({
|
|
1484
|
+
code: z3.ZodIssueCode.custom,
|
|
1485
|
+
message: `Duplicate tool names are not allowed: ${Array.from(duplicates).join(", ")}`
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
}).describe(
|
|
1489
|
+
'Array of unique tool names (from codecall:search results) to fetch their detailed schemas and usage examples. Example: ["users:list", "billing:getInvoice"]'
|
|
1490
|
+
)
|
|
1491
|
+
});
|
|
1492
|
+
var describeToolOutputSchema = z3.object({
|
|
1493
|
+
tools: z3.array(
|
|
1494
|
+
z3.object({
|
|
1495
|
+
name: z3.string().describe("Tool name to be used in callTool() within codecall:execute scripts"),
|
|
1496
|
+
appId: z3.string().describe("The app ID this tool belongs to"),
|
|
1497
|
+
description: z3.string().describe("Detailed description of what this tool does"),
|
|
1498
|
+
inputSchema: z3.record(z3.string(), z3.unknown()).nullable().describe("JSON Schema object describing the tool input parameters"),
|
|
1499
|
+
outputSchema: z3.record(z3.string(), z3.unknown()).nullable().describe("JSON Schema object describing the tool output structure"),
|
|
1500
|
+
annotations: ToolAnnotationsSchema.optional().describe("MCP tool annotations (metadata)"),
|
|
1501
|
+
usageExamples: z3.array(
|
|
1502
|
+
z3.object({
|
|
1503
|
+
description: z3.string().describe("Description of what this example demonstrates"),
|
|
1504
|
+
code: z3.string().describe(
|
|
1505
|
+
'JavaScript code example showing how to call this tool using callTool(). Format: const result = await callTool("tool:name", { ...params });'
|
|
1506
|
+
)
|
|
1507
|
+
})
|
|
1508
|
+
).max(5).describe("Up to 5 practical examples of how to use this tool in a codecall:execute script")
|
|
1509
|
+
})
|
|
1510
|
+
).describe("Array of tool descriptions with schemas and usage examples"),
|
|
1511
|
+
notFound: z3.array(z3.string()).optional().describe("Tool names that were requested but not found in the index. Check these for typos.")
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
// plugins/plugin-codecall/src/utils/describe.utils.ts
|
|
1515
|
+
var INTENT_PATTERNS = {
|
|
1516
|
+
create: /^(create|add|new|insert|make|append|register|generate|produce|build|construct|provision|instantiate|define|compose|draft)/i,
|
|
1517
|
+
delete: /^(delete|remove|destroy|drop|erase|clear|purge|discard|eliminate|unbind|unregister)/i,
|
|
1518
|
+
get: /^(get|fetch|retrieve|read|obtain|load|pull|access|grab|receive)/i,
|
|
1519
|
+
update: /^(update|edit|modify|change|patch|set|alter|revise|adjust|amend|correct|fix|refresh|sync|upgrade|downgrade)/i,
|
|
1520
|
+
list: /^(list|all|index|enumerate|show|display|view|browse|scan|inventory)/i,
|
|
1521
|
+
search: /^(search|find|query|lookup|locate|discover|explore|seek|match|filter)/i
|
|
1522
|
+
};
|
|
1523
|
+
function detectToolIntent(toolName, description) {
|
|
1524
|
+
const parts = toolName.split(":");
|
|
1525
|
+
const actionPart = parts.length > 1 ? parts[parts.length - 1] : toolName;
|
|
1526
|
+
for (const [intent, pattern] of Object.entries(INTENT_PATTERNS)) {
|
|
1527
|
+
if (pattern.test(actionPart)) {
|
|
1528
|
+
return intent;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
if (description) {
|
|
1532
|
+
const descLower = description.toLowerCase();
|
|
1533
|
+
if (/creates?\s|adding\s|inserts?/i.test(descLower)) return "create";
|
|
1534
|
+
if (/deletes?\s|removes?\s|destroys?/i.test(descLower)) return "delete";
|
|
1535
|
+
if (/gets?\s|fetche?s?\s|retrieves?/i.test(descLower)) return "get";
|
|
1536
|
+
if (/updates?\s|modif(?:y|ies)\s|edits?/i.test(descLower)) return "update";
|
|
1537
|
+
if (/lists?\s|shows?\s|displays?/i.test(descLower)) return "list";
|
|
1538
|
+
if (/search(?:es)?\s|find(?:s)?\s|quer(?:y|ies)/i.test(descLower)) return "search";
|
|
1539
|
+
}
|
|
1540
|
+
return "unknown";
|
|
1541
|
+
}
|
|
1542
|
+
function generateBasicExample(toolName, inputSchema) {
|
|
1543
|
+
const params = generateSampleParams(inputSchema);
|
|
1544
|
+
const paramsStr = params ? JSON.stringify(params, null, 2) : "{}";
|
|
1545
|
+
return {
|
|
1546
|
+
description: `Basic usage of ${toolName}`,
|
|
1547
|
+
code: `const result = await callTool('${toolName}', ${paramsStr});
|
|
1548
|
+
return result;`
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
function generateSampleParams(schema) {
|
|
1552
|
+
if (!schema || schema.type !== "object" || !schema.properties) {
|
|
1553
|
+
return null;
|
|
1554
|
+
}
|
|
1555
|
+
const params = {};
|
|
1556
|
+
const required = new Set(schema.required || []);
|
|
1557
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
1558
|
+
if (typeof propSchema === "boolean") continue;
|
|
1559
|
+
if (!required.has(key)) continue;
|
|
1560
|
+
params[key] = getSampleValue(propSchema, key);
|
|
1561
|
+
}
|
|
1562
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
1563
|
+
}
|
|
1564
|
+
function getSampleValue(schema, key) {
|
|
1565
|
+
if (schema.default !== void 0) {
|
|
1566
|
+
return schema.default;
|
|
1567
|
+
}
|
|
1568
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
1569
|
+
return schema.enum[0];
|
|
1570
|
+
}
|
|
1571
|
+
if (schema.const !== void 0) {
|
|
1572
|
+
return schema.const;
|
|
1573
|
+
}
|
|
1574
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
1575
|
+
switch (type) {
|
|
1576
|
+
case "string":
|
|
1577
|
+
if (key.toLowerCase().includes("id")) return "abc123";
|
|
1578
|
+
if (key.toLowerCase().includes("email")) return "user@example.com";
|
|
1579
|
+
if (key.toLowerCase().includes("name")) return "Example";
|
|
1580
|
+
if (key.toLowerCase().includes("url")) return "https://example.com";
|
|
1581
|
+
return "string";
|
|
1582
|
+
case "number":
|
|
1583
|
+
case "integer":
|
|
1584
|
+
if (key.toLowerCase().includes("limit")) return 10;
|
|
1585
|
+
if (key.toLowerCase().includes("offset")) return 0;
|
|
1586
|
+
if (key.toLowerCase().includes("page")) return 1;
|
|
1587
|
+
return 0;
|
|
1588
|
+
case "boolean":
|
|
1589
|
+
return true;
|
|
1590
|
+
case "array":
|
|
1591
|
+
return [];
|
|
1592
|
+
case "object":
|
|
1593
|
+
return {};
|
|
1594
|
+
default:
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
function hasPaginationParams(schema) {
|
|
1599
|
+
if (!schema || schema.type !== "object" || !schema.properties) {
|
|
1600
|
+
return false;
|
|
1601
|
+
}
|
|
1602
|
+
const props = Object.keys(schema.properties);
|
|
1603
|
+
return props.some((p) => ["limit", "offset", "page", "pageSize", "cursor"].includes(p.toLowerCase()));
|
|
1604
|
+
}
|
|
1605
|
+
function generatePaginationExample(toolName) {
|
|
1606
|
+
return {
|
|
1607
|
+
description: `Pagination example for ${toolName}`,
|
|
1608
|
+
code: `// Fetch first page
|
|
1609
|
+
const page1 = await callTool('${toolName}', { limit: 10, offset: 0 });
|
|
1610
|
+
|
|
1611
|
+
// Fetch second page
|
|
1612
|
+
const page2 = await callTool('${toolName}', { limit: 10, offset: 10 });
|
|
1613
|
+
|
|
1614
|
+
// Combine results
|
|
1615
|
+
return [...page1.items, ...page2.items];`
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
function extractEntityName(toolName) {
|
|
1619
|
+
const parts = toolName.split(":");
|
|
1620
|
+
const entity = parts.length > 1 ? parts[0] : toolName;
|
|
1621
|
+
return entity.endsWith("s") && !entity.endsWith("ss") ? entity.slice(0, -1) : entity;
|
|
1622
|
+
}
|
|
1623
|
+
function generateCreateExample(toolName, inputSchema) {
|
|
1624
|
+
const entity = extractEntityName(toolName);
|
|
1625
|
+
const params = generateSampleParams(inputSchema);
|
|
1626
|
+
const paramsStr = params ? JSON.stringify(params, null, 2) : "{ /* required fields */ }";
|
|
1627
|
+
return {
|
|
1628
|
+
description: `Create a new ${entity}`,
|
|
1629
|
+
code: `const result = await callTool('${toolName}', ${paramsStr});
|
|
1630
|
+
return result;`
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
function generateGetExample(toolName, inputSchema) {
|
|
1634
|
+
const entity = extractEntityName(toolName);
|
|
1635
|
+
const idParam = findIdParameter(inputSchema);
|
|
1636
|
+
if (idParam) {
|
|
1637
|
+
return {
|
|
1638
|
+
description: `Get ${entity} by ${idParam}`,
|
|
1639
|
+
code: `const ${entity} = await callTool('${toolName}', { ${idParam}: 'abc123' });
|
|
1640
|
+
return ${entity};`
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
const params = generateSampleParams(inputSchema);
|
|
1644
|
+
const paramsStr = params ? JSON.stringify(params, null, 2) : "{}";
|
|
1645
|
+
return {
|
|
1646
|
+
description: `Get ${entity} details`,
|
|
1647
|
+
code: `const ${entity} = await callTool('${toolName}', ${paramsStr});
|
|
1648
|
+
return ${entity};`
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function generateListExample(toolName, inputSchema) {
|
|
1652
|
+
const entity = extractEntityName(toolName);
|
|
1653
|
+
return {
|
|
1654
|
+
description: `List all ${entity}s`,
|
|
1655
|
+
code: `const result = await callTool('${toolName}', {});
|
|
1656
|
+
return result.items || result;`
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
function generateUpdateExample(toolName, inputSchema) {
|
|
1660
|
+
const entity = extractEntityName(toolName);
|
|
1661
|
+
const idParam = findIdParameter(inputSchema);
|
|
1662
|
+
const updateFields = generateUpdateFields(inputSchema);
|
|
1663
|
+
const fieldsStr = updateFields ? JSON.stringify(updateFields, null, 2).replace(/\n/g, "\n ") : "{ /* fields to update */ }";
|
|
1664
|
+
if (idParam) {
|
|
1665
|
+
return {
|
|
1666
|
+
description: `Update ${entity} by ${idParam}`,
|
|
1667
|
+
code: `const updated = await callTool('${toolName}', {
|
|
1668
|
+
${idParam}: 'abc123',
|
|
1669
|
+
...${fieldsStr}
|
|
1670
|
+
});
|
|
1671
|
+
return updated;`
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
return {
|
|
1675
|
+
description: `Update ${entity}`,
|
|
1676
|
+
code: `const updated = await callTool('${toolName}', ${fieldsStr});
|
|
1677
|
+
return updated;`
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
function generateDeleteExample(toolName, inputSchema) {
|
|
1681
|
+
const entity = extractEntityName(toolName);
|
|
1682
|
+
const idParam = findIdParameter(inputSchema);
|
|
1683
|
+
if (idParam) {
|
|
1684
|
+
return {
|
|
1685
|
+
description: `Delete ${entity} by ${idParam}`,
|
|
1686
|
+
code: `const result = await callTool('${toolName}', { ${idParam}: 'abc123' });
|
|
1687
|
+
return result;`
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
const params = generateSampleParams(inputSchema);
|
|
1691
|
+
const paramsStr = params ? JSON.stringify(params, null, 2) : "{ /* identifier */ }";
|
|
1692
|
+
return {
|
|
1693
|
+
description: `Delete ${entity}`,
|
|
1694
|
+
code: `const result = await callTool('${toolName}', ${paramsStr});
|
|
1695
|
+
return result;`
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
function generateSearchExample(toolName, inputSchema) {
|
|
1699
|
+
const entity = extractEntityName(toolName);
|
|
1700
|
+
const queryParam = findQueryParameter(inputSchema);
|
|
1701
|
+
if (queryParam) {
|
|
1702
|
+
return {
|
|
1703
|
+
description: `Search for ${entity}s`,
|
|
1704
|
+
code: `const results = await callTool('${toolName}', { ${queryParam}: 'search term' });
|
|
1705
|
+
return results.items || results;`
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
return {
|
|
1709
|
+
description: `Search for ${entity}s`,
|
|
1710
|
+
code: `const results = await callTool('${toolName}', { query: 'search term' });
|
|
1711
|
+
return results.items || results;`
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
function findIdParameter(schema) {
|
|
1715
|
+
if (!schema || schema.type !== "object" || !schema.properties) {
|
|
1716
|
+
return null;
|
|
1717
|
+
}
|
|
1718
|
+
const props = Object.keys(schema.properties);
|
|
1719
|
+
const required = new Set(schema.required || []);
|
|
1720
|
+
const idPatterns = ["id", "Id", "ID", "_id", "uuid", "key"];
|
|
1721
|
+
for (const pattern of idPatterns) {
|
|
1722
|
+
const found = props.find((p) => (p === pattern || p.endsWith(pattern)) && required.has(p));
|
|
1723
|
+
if (found) return found;
|
|
1724
|
+
}
|
|
1725
|
+
for (const pattern of idPatterns) {
|
|
1726
|
+
const found = props.find((p) => p === pattern || p.endsWith(pattern));
|
|
1727
|
+
if (found) return found;
|
|
1728
|
+
}
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
function findQueryParameter(schema) {
|
|
1732
|
+
if (!schema || schema.type !== "object" || !schema.properties) {
|
|
1733
|
+
return null;
|
|
1734
|
+
}
|
|
1735
|
+
const props = Object.keys(schema.properties);
|
|
1736
|
+
const queryPatterns = ["query", "search", "q", "term", "keyword", "filter"];
|
|
1737
|
+
for (const pattern of queryPatterns) {
|
|
1738
|
+
const found = props.find((p) => p.toLowerCase() === pattern || p.toLowerCase().includes(pattern));
|
|
1739
|
+
if (found) return found;
|
|
1740
|
+
}
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1743
|
+
function generateUpdateFields(schema) {
|
|
1744
|
+
if (!schema || schema.type !== "object" || !schema.properties) {
|
|
1745
|
+
return null;
|
|
1746
|
+
}
|
|
1747
|
+
const fields = {};
|
|
1748
|
+
const idPatterns = ["id", "Id", "ID", "_id", "uuid", "key"];
|
|
1749
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
1750
|
+
if (typeof propSchema === "boolean") continue;
|
|
1751
|
+
const isIdField = idPatterns.some((p) => key === p || key.endsWith(p));
|
|
1752
|
+
if (isIdField) continue;
|
|
1753
|
+
if (Object.keys(fields).length < 2) {
|
|
1754
|
+
fields[key] = getSampleValue(propSchema, key);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
return Object.keys(fields).length > 0 ? fields : null;
|
|
1758
|
+
}
|
|
1759
|
+
function generateSmartExample(toolName, inputSchema, description) {
|
|
1760
|
+
const intent = detectToolIntent(toolName, description);
|
|
1761
|
+
switch (intent) {
|
|
1762
|
+
case "create":
|
|
1763
|
+
return generateCreateExample(toolName, inputSchema);
|
|
1764
|
+
case "get":
|
|
1765
|
+
return generateGetExample(toolName, inputSchema);
|
|
1766
|
+
case "list":
|
|
1767
|
+
return hasPaginationParams(inputSchema) ? generatePaginationExample(toolName) : generateListExample(toolName, inputSchema);
|
|
1768
|
+
case "update":
|
|
1769
|
+
return generateUpdateExample(toolName, inputSchema);
|
|
1770
|
+
case "delete":
|
|
1771
|
+
return generateDeleteExample(toolName, inputSchema);
|
|
1772
|
+
case "search":
|
|
1773
|
+
return generateSearchExample(toolName, inputSchema);
|
|
1774
|
+
default:
|
|
1775
|
+
return generateBasicExample(toolName, inputSchema);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// plugins/plugin-codecall/src/utils/mcp-result.ts
|
|
1780
|
+
function extractResultFromCallToolResult(mcpResult) {
|
|
1781
|
+
if (mcpResult.isError) {
|
|
1782
|
+
const errorContent = mcpResult.content?.[0];
|
|
1783
|
+
if (errorContent && "text" in errorContent) {
|
|
1784
|
+
throw new Error(errorContent.text);
|
|
1785
|
+
}
|
|
1786
|
+
throw new Error("Tool execution failed");
|
|
1787
|
+
}
|
|
1788
|
+
const content = mcpResult.content;
|
|
1789
|
+
if (!content || content.length === 0) {
|
|
1790
|
+
return void 0;
|
|
1791
|
+
}
|
|
1792
|
+
if (content.length === 1 && content[0].type === "text") {
|
|
1793
|
+
try {
|
|
1794
|
+
return JSON.parse(content[0].text);
|
|
1795
|
+
} catch {
|
|
1796
|
+
return content[0].text;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return content;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// plugins/plugin-codecall/src/errors/tool-call.errors.ts
|
|
1803
|
+
var TOOL_CALL_ERROR_CODES = {
|
|
1804
|
+
NOT_FOUND: "NOT_FOUND",
|
|
1805
|
+
VALIDATION: "VALIDATION",
|
|
1806
|
+
EXECUTION: "EXECUTION",
|
|
1807
|
+
TIMEOUT: "TIMEOUT",
|
|
1808
|
+
ACCESS_DENIED: "ACCESS_DENIED",
|
|
1809
|
+
SELF_REFERENCE: "SELF_REFERENCE"
|
|
1810
|
+
};
|
|
1811
|
+
function createToolCallError(code, toolName, rawMessage) {
|
|
1812
|
+
const sanitizedMessage = getSanitizedMessage(code, toolName, rawMessage);
|
|
1813
|
+
return Object.freeze({
|
|
1814
|
+
code,
|
|
1815
|
+
message: sanitizedMessage,
|
|
1816
|
+
toolName
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
function getSanitizedMessage(code, toolName, rawMessage) {
|
|
1820
|
+
switch (code) {
|
|
1821
|
+
case TOOL_CALL_ERROR_CODES.NOT_FOUND:
|
|
1822
|
+
return `Tool "${toolName}" was not found`;
|
|
1823
|
+
case TOOL_CALL_ERROR_CODES.VALIDATION:
|
|
1824
|
+
return rawMessage ? sanitizeValidationMessage(rawMessage) : `Input validation failed for tool "${toolName}"`;
|
|
1825
|
+
case TOOL_CALL_ERROR_CODES.EXECUTION:
|
|
1826
|
+
return `Tool "${toolName}" execution failed`;
|
|
1827
|
+
case TOOL_CALL_ERROR_CODES.TIMEOUT:
|
|
1828
|
+
return `Tool "${toolName}" execution timed out`;
|
|
1829
|
+
case TOOL_CALL_ERROR_CODES.ACCESS_DENIED:
|
|
1830
|
+
return `Access denied for tool "${toolName}"`;
|
|
1831
|
+
case TOOL_CALL_ERROR_CODES.SELF_REFERENCE:
|
|
1832
|
+
return `Cannot call CodeCall tools from within AgentScript`;
|
|
1833
|
+
default:
|
|
1834
|
+
return `An error occurred while calling "${toolName}"`;
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
function sanitizeValidationMessage(message) {
|
|
1838
|
+
let sanitized = message.replace(/(?:\/[\w.-]+)+|(?:[A-Za-z]:\\[\w\\.-]+)+/g, "[path]");
|
|
1839
|
+
sanitized = sanitized.replace(/\bat line \d+/gi, "");
|
|
1840
|
+
sanitized = sanitized.replace(/\bline \d+/gi, "");
|
|
1841
|
+
sanitized = sanitized.replace(/:\d+:\d+/g, "");
|
|
1842
|
+
sanitized = sanitized.replace(/\n\s*at .*/g, "");
|
|
1843
|
+
if (sanitized.length > 200) {
|
|
1844
|
+
sanitized = sanitized.substring(0, 200) + "...";
|
|
1845
|
+
}
|
|
1846
|
+
return sanitized.trim();
|
|
1847
|
+
}
|
|
1848
|
+
var SelfReferenceError = class extends Error {
|
|
1849
|
+
code = TOOL_CALL_ERROR_CODES.SELF_REFERENCE;
|
|
1850
|
+
toolName;
|
|
1851
|
+
constructor(toolName) {
|
|
1852
|
+
super(`Self-reference attack: Attempted to call CodeCall tool "${toolName}" from within AgentScript`);
|
|
1853
|
+
this.name = "SelfReferenceError";
|
|
1854
|
+
this.toolName = toolName;
|
|
1855
|
+
Object.freeze(this);
|
|
1856
|
+
}
|
|
1857
|
+
};
|
|
1858
|
+
|
|
1859
|
+
// plugins/plugin-codecall/src/security/self-reference-guard.ts
|
|
1860
|
+
var CODECALL_TOOL_PREFIX = "codecall:";
|
|
1861
|
+
var BLOCKED_CODECALL_TOOLS = Object.freeze(
|
|
1862
|
+
/* @__PURE__ */ new Set(["codecall:search", "codecall:describe", "codecall:execute", "codecall:invoke"])
|
|
1863
|
+
);
|
|
1864
|
+
function isBlockedSelfReference(toolName) {
|
|
1865
|
+
const normalizedName = toolName.toLowerCase().trim();
|
|
1866
|
+
if (normalizedName.startsWith(CODECALL_TOOL_PREFIX)) {
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1869
|
+
if (BLOCKED_CODECALL_TOOLS.has(toolName)) {
|
|
1870
|
+
return true;
|
|
1871
|
+
}
|
|
1872
|
+
return false;
|
|
1873
|
+
}
|
|
1874
|
+
function assertNotSelfReference(toolName) {
|
|
1875
|
+
if (isBlockedSelfReference(toolName)) {
|
|
1876
|
+
throw new SelfReferenceError(toolName);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// plugins/plugin-codecall/src/tools/describe.tool.ts
|
|
1881
|
+
var DescribeTool = class extends ToolContext2 {
|
|
1882
|
+
async execute(input) {
|
|
1883
|
+
const { toolNames } = input;
|
|
1884
|
+
const tools = [];
|
|
1885
|
+
const notFound = [];
|
|
1886
|
+
const allTools = this.scope.tools.getTools(true);
|
|
1887
|
+
const toolMap = new Map(allTools.map((t) => [t.name, t]));
|
|
1888
|
+
const fullNameMap = new Map(allTools.map((t) => [t.fullName, t]));
|
|
1889
|
+
for (const toolName of toolNames) {
|
|
1890
|
+
if (isBlockedSelfReference(toolName)) {
|
|
1891
|
+
notFound.push(toolName);
|
|
1892
|
+
continue;
|
|
1893
|
+
}
|
|
1894
|
+
const tool = toolMap.get(toolName) || fullNameMap.get(toolName);
|
|
1895
|
+
if (!tool) {
|
|
1896
|
+
notFound.push(toolName);
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
1899
|
+
const appId = this.extractAppId(tool);
|
|
1900
|
+
const inputSchema = this.getInputSchema(tool);
|
|
1901
|
+
const outputSchema = this.toJsonSchema(tool.outputSchema);
|
|
1902
|
+
const usageExamples = this.generateExamples(tool, inputSchema ?? void 0);
|
|
1903
|
+
tools.push({
|
|
1904
|
+
name: tool.name,
|
|
1905
|
+
appId,
|
|
1906
|
+
description: tool.metadata?.description || `Tool: ${tool.name}`,
|
|
1907
|
+
inputSchema: inputSchema || null,
|
|
1908
|
+
outputSchema: outputSchema || null,
|
|
1909
|
+
annotations: tool.metadata?.annotations,
|
|
1910
|
+
usageExamples
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
return {
|
|
1914
|
+
tools,
|
|
1915
|
+
notFound: notFound.length > 0 ? notFound : void 0
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Get the input schema for a tool, converting from Zod to JSON Schema.
|
|
1920
|
+
* This ensures that property descriptions from .describe() are included.
|
|
1921
|
+
*
|
|
1922
|
+
* Priority:
|
|
1923
|
+
* 1. Convert from tool.inputSchema (Zod) to get descriptions
|
|
1924
|
+
* 2. Fall back to rawInputSchema if conversion fails
|
|
1925
|
+
* 3. Return null if no schema available
|
|
1926
|
+
*/
|
|
1927
|
+
getInputSchema(tool) {
|
|
1928
|
+
if (tool.inputSchema && typeof tool.inputSchema === "object" && Object.keys(tool.inputSchema).length > 0) {
|
|
1929
|
+
try {
|
|
1930
|
+
const firstValue = Object.values(tool.inputSchema)[0];
|
|
1931
|
+
if (firstValue instanceof ZodType) {
|
|
1932
|
+
return toJSONSchema(z4.object(tool.inputSchema));
|
|
1933
|
+
}
|
|
1934
|
+
} catch {
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
if (tool.rawInputSchema) {
|
|
1938
|
+
return tool.rawInputSchema;
|
|
1939
|
+
}
|
|
1940
|
+
return null;
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Convert a schema to JSON Schema format.
|
|
1944
|
+
* Handles Zod schemas, raw shapes, and already-JSON-Schema objects.
|
|
1945
|
+
*
|
|
1946
|
+
* Uses Zod v4's built-in z.toJSONSchema() for conversion.
|
|
1947
|
+
*/
|
|
1948
|
+
toJsonSchema(schema) {
|
|
1949
|
+
if (!schema) {
|
|
1950
|
+
return null;
|
|
1951
|
+
}
|
|
1952
|
+
if (schema instanceof ZodType) {
|
|
1953
|
+
try {
|
|
1954
|
+
return toJSONSchema(schema);
|
|
1955
|
+
} catch {
|
|
1956
|
+
return null;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
if (typeof schema === "object" && schema !== null && !Array.isArray(schema)) {
|
|
1960
|
+
const obj = schema;
|
|
1961
|
+
const firstValue = Object.values(obj)[0];
|
|
1962
|
+
if (firstValue instanceof ZodType) {
|
|
1963
|
+
try {
|
|
1964
|
+
return toJSONSchema(z4.object(obj));
|
|
1965
|
+
} catch {
|
|
1966
|
+
return null;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
if ("type" in obj || "properties" in obj || "$schema" in obj) {
|
|
1970
|
+
return schema;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
if (typeof schema === "string") {
|
|
1974
|
+
return null;
|
|
1975
|
+
}
|
|
1976
|
+
if (Array.isArray(schema)) {
|
|
1977
|
+
return null;
|
|
1978
|
+
}
|
|
1979
|
+
return null;
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Extract app ID from tool metadata or owner.
|
|
1983
|
+
*/
|
|
1984
|
+
extractAppId(tool) {
|
|
1985
|
+
if (tool.metadata?.codecall?.appId) {
|
|
1986
|
+
return tool.metadata.codecall.appId;
|
|
1987
|
+
}
|
|
1988
|
+
if (tool.metadata?.source) {
|
|
1989
|
+
return tool.metadata.source;
|
|
1990
|
+
}
|
|
1991
|
+
if (tool.owner?.id) {
|
|
1992
|
+
return tool.owner.id;
|
|
1993
|
+
}
|
|
1994
|
+
const nameParts = tool.name?.split(":");
|
|
1995
|
+
if (nameParts && nameParts.length > 1) {
|
|
1996
|
+
return nameParts[0];
|
|
1997
|
+
}
|
|
1998
|
+
return "unknown";
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* Generate up to 5 usage examples for a tool.
|
|
2002
|
+
*
|
|
2003
|
+
* Priority:
|
|
2004
|
+
* 1. User-provided examples from @Tool decorator metadata (up to 5)
|
|
2005
|
+
* 2. Smart intent-based generation to fill remaining slots
|
|
2006
|
+
* 3. Returns at least 1 example
|
|
2007
|
+
*/
|
|
2008
|
+
generateExamples(tool, inputSchema) {
|
|
2009
|
+
const result = [];
|
|
2010
|
+
const examples = tool.metadata?.examples;
|
|
2011
|
+
if (examples && Array.isArray(examples)) {
|
|
2012
|
+
for (const ex of examples.slice(0, 5)) {
|
|
2013
|
+
result.push({
|
|
2014
|
+
description: ex.description || "Example usage",
|
|
2015
|
+
code: `const result = await callTool('${tool.name}', ${JSON.stringify(ex.input, null, 2)});
|
|
2016
|
+
return result;`
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
if (result.length < 5) {
|
|
2021
|
+
result.push(generateSmartExample(tool.name, inputSchema, tool.metadata?.description));
|
|
2022
|
+
}
|
|
2023
|
+
return result.slice(0, 5);
|
|
2024
|
+
}
|
|
2025
|
+
};
|
|
2026
|
+
DescribeTool = __decorateClass([
|
|
2027
|
+
Tool2({
|
|
2028
|
+
name: "codecall:describe",
|
|
2029
|
+
cache: {
|
|
2030
|
+
ttl: 60,
|
|
2031
|
+
// 1 minute
|
|
2032
|
+
slideWindow: false
|
|
2033
|
+
},
|
|
2034
|
+
codecall: {
|
|
2035
|
+
enabledInCodeCall: false,
|
|
2036
|
+
visibleInListTools: true
|
|
2037
|
+
},
|
|
2038
|
+
description: describeToolDescription,
|
|
2039
|
+
inputSchema: describeToolInputSchema,
|
|
2040
|
+
outputSchema: describeToolOutputSchema,
|
|
2041
|
+
annotations: {
|
|
2042
|
+
readOnlyHint: true,
|
|
2043
|
+
openWorldHint: true
|
|
2044
|
+
}
|
|
2045
|
+
})
|
|
2046
|
+
], DescribeTool);
|
|
2047
|
+
|
|
2048
|
+
// plugins/plugin-codecall/src/tools/execute.tool.ts
|
|
2049
|
+
import { Tool as Tool3, ToolContext as ToolContext3 } from "@frontmcp/sdk";
|
|
2050
|
+
|
|
2051
|
+
// plugins/plugin-codecall/src/tools/execute.schema.ts
|
|
2052
|
+
import { z as z5 } from "zod";
|
|
2053
|
+
var MIN_EXECUTE_SCRIPT_LENGTH = 'return callTool("a",{})'.length;
|
|
2054
|
+
var executeToolDescription = `Execute AgentScript (safe JS subset) for multi-tool orchestration.
|
|
2055
|
+
|
|
2056
|
+
API: await callTool(name, args, opts?)
|
|
2057
|
+
- Default: throws on error
|
|
2058
|
+
- Safe mode: { throwOnError: false } \u2192 returns { success, data?, error? }
|
|
2059
|
+
|
|
2060
|
+
EXAMPLE:
|
|
2061
|
+
const users = await callTool('users:list', { active: true });
|
|
2062
|
+
const results = [];
|
|
2063
|
+
for (const u of users.items) {
|
|
2064
|
+
const orders = await callTool('orders:list', { userId: u.id });
|
|
2065
|
+
results.push({ id: u.id, total: orders.items.reduce((s,o) => s + o.amount, 0) });
|
|
2066
|
+
}
|
|
2067
|
+
return results;
|
|
2068
|
+
|
|
2069
|
+
ALLOWED: for, for-of, arrow fn, map/filter/reduce/find, Math.*, JSON.*, if/else, destructuring, spread, template literals
|
|
2070
|
+
BLOCKED: while, do-while, function decl, eval, require, fetch, setTimeout, process, globalThis
|
|
2071
|
+
|
|
2072
|
+
ERRORS: NOT_FOUND | VALIDATION | EXECUTION | TIMEOUT | ACCESS_DENIED
|
|
2073
|
+
STATUS: ok | syntax_error | illegal_access | runtime_error | tool_error | timeout
|
|
2074
|
+
LIMITS: 10K iter/loop, 30s timeout, 100 calls max`;
|
|
2075
|
+
var executeToolInputSchema = z5.object({
|
|
2076
|
+
script: z5.string().min(MIN_EXECUTE_SCRIPT_LENGTH).max(100 * 1024).describe(
|
|
2077
|
+
"JavaScript code to execute in the sandbox. Must return a value (implicitly or via explicit return). Use callTool(name, input) to invoke tools."
|
|
2078
|
+
),
|
|
2079
|
+
allowedTools: z5.array(z5.string()).optional().describe(
|
|
2080
|
+
'Optional whitelist of tool names that can be called from this script. If not provided, all indexed tools are available. Example: ["users:list", "billing:getInvoice"]'
|
|
2081
|
+
)
|
|
2082
|
+
});
|
|
2083
|
+
var syntaxErrorPayloadSchema = z5.object({
|
|
2084
|
+
message: z5.string(),
|
|
2085
|
+
location: z5.object({
|
|
2086
|
+
line: z5.number(),
|
|
2087
|
+
column: z5.number()
|
|
2088
|
+
}).optional()
|
|
2089
|
+
});
|
|
2090
|
+
var illegalAccessErrorPayloadSchema = z5.object({
|
|
2091
|
+
message: z5.string(),
|
|
2092
|
+
kind: z5.union([
|
|
2093
|
+
z5.literal("IllegalBuiltinAccess"),
|
|
2094
|
+
z5.literal("DisallowedGlobal"),
|
|
2095
|
+
z5.string()
|
|
2096
|
+
// same as your original type: 'A' | 'B' | string
|
|
2097
|
+
])
|
|
2098
|
+
});
|
|
2099
|
+
var runtimeErrorPayloadSchema = z5.object({
|
|
2100
|
+
source: z5.literal("script"),
|
|
2101
|
+
message: z5.string(),
|
|
2102
|
+
name: z5.string().optional(),
|
|
2103
|
+
stack: z5.string().optional()
|
|
2104
|
+
});
|
|
2105
|
+
var toolErrorPayloadSchema = z5.object({
|
|
2106
|
+
source: z5.literal("tool"),
|
|
2107
|
+
toolName: z5.string(),
|
|
2108
|
+
toolInput: z5.unknown(),
|
|
2109
|
+
message: z5.string(),
|
|
2110
|
+
code: z5.string().optional(),
|
|
2111
|
+
details: z5.unknown().optional()
|
|
2112
|
+
});
|
|
2113
|
+
var timeoutErrorPayloadSchema = z5.object({
|
|
2114
|
+
message: z5.string()
|
|
2115
|
+
});
|
|
2116
|
+
var codeCallOkResultSchema = z5.object({
|
|
2117
|
+
status: z5.literal("ok"),
|
|
2118
|
+
result: z5.unknown(),
|
|
2119
|
+
logs: z5.array(z5.string()).optional()
|
|
2120
|
+
});
|
|
2121
|
+
var codeCallSyntaxErrorResultSchema = z5.object({
|
|
2122
|
+
status: z5.literal("syntax_error"),
|
|
2123
|
+
error: syntaxErrorPayloadSchema
|
|
2124
|
+
});
|
|
2125
|
+
var codeCallIllegalAccessResultSchema = z5.object({
|
|
2126
|
+
status: z5.literal("illegal_access"),
|
|
2127
|
+
error: illegalAccessErrorPayloadSchema
|
|
2128
|
+
});
|
|
2129
|
+
var codeCallRuntimeErrorResultSchema = z5.object({
|
|
2130
|
+
status: z5.literal("runtime_error"),
|
|
2131
|
+
error: runtimeErrorPayloadSchema
|
|
2132
|
+
});
|
|
2133
|
+
var codeCallToolErrorResultSchema = z5.object({
|
|
2134
|
+
status: z5.literal("tool_error"),
|
|
2135
|
+
error: toolErrorPayloadSchema
|
|
2136
|
+
});
|
|
2137
|
+
var codeCallTimeoutResultSchema = z5.object({
|
|
2138
|
+
status: z5.literal("timeout"),
|
|
2139
|
+
error: timeoutErrorPayloadSchema
|
|
2140
|
+
});
|
|
2141
|
+
var executeToolOutputSchema = z5.discriminatedUnion("status", [
|
|
2142
|
+
codeCallOkResultSchema,
|
|
2143
|
+
codeCallSyntaxErrorResultSchema,
|
|
2144
|
+
codeCallIllegalAccessResultSchema,
|
|
2145
|
+
codeCallRuntimeErrorResultSchema,
|
|
2146
|
+
codeCallToolErrorResultSchema,
|
|
2147
|
+
codeCallTimeoutResultSchema
|
|
2148
|
+
]);
|
|
2149
|
+
|
|
2150
|
+
// plugins/plugin-codecall/src/providers/code-call.config.ts
|
|
2151
|
+
import { Provider as Provider2, ProviderScope as ProviderScope2, BaseConfig } from "@frontmcp/sdk";
|
|
2152
|
+
var CodeCallConfig = class extends BaseConfig {
|
|
2153
|
+
constructor(options = {}) {
|
|
2154
|
+
const parsedConfig = codeCallPluginOptionsSchema.parse(options);
|
|
2155
|
+
const resolvedVm = resolveVmOptions(parsedConfig.vm);
|
|
2156
|
+
super({ ...parsedConfig, resolvedVm });
|
|
2157
|
+
}
|
|
2158
|
+
};
|
|
2159
|
+
CodeCallConfig = __decorateClass([
|
|
2160
|
+
Provider2({
|
|
2161
|
+
name: "codecall:config",
|
|
2162
|
+
description: "CodeCall plugin configuration with validated defaults",
|
|
2163
|
+
scope: ProviderScope2.GLOBAL
|
|
2164
|
+
})
|
|
2165
|
+
], CodeCallConfig);
|
|
2166
|
+
function resolveVmOptions(vmOptions) {
|
|
2167
|
+
const preset = vmOptions?.preset ?? "secure";
|
|
2168
|
+
const base = presetDefaults(preset);
|
|
2169
|
+
return {
|
|
2170
|
+
...base,
|
|
2171
|
+
...vmOptions,
|
|
2172
|
+
disabledBuiltins: vmOptions?.disabledBuiltins ?? base.disabledBuiltins,
|
|
2173
|
+
disabledGlobals: vmOptions?.disabledGlobals ?? base.disabledGlobals
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
function presetDefaults(preset) {
|
|
2177
|
+
switch (preset) {
|
|
2178
|
+
case "locked_down":
|
|
2179
|
+
return {
|
|
2180
|
+
preset,
|
|
2181
|
+
timeoutMs: 2e3,
|
|
2182
|
+
allowLoops: false,
|
|
2183
|
+
allowConsole: false,
|
|
2184
|
+
maxSteps: 2e3,
|
|
2185
|
+
disabledBuiltins: ["eval", "Function", "AsyncFunction"],
|
|
2186
|
+
disabledGlobals: [
|
|
2187
|
+
"require",
|
|
2188
|
+
"process",
|
|
2189
|
+
"fetch",
|
|
2190
|
+
"setTimeout",
|
|
2191
|
+
"setInterval",
|
|
2192
|
+
"setImmediate",
|
|
2193
|
+
"global",
|
|
2194
|
+
"globalThis"
|
|
2195
|
+
]
|
|
2196
|
+
};
|
|
2197
|
+
case "balanced":
|
|
2198
|
+
return {
|
|
2199
|
+
preset,
|
|
2200
|
+
timeoutMs: 5e3,
|
|
2201
|
+
allowLoops: true,
|
|
2202
|
+
allowConsole: true,
|
|
2203
|
+
maxSteps: 1e4,
|
|
2204
|
+
disabledBuiltins: ["eval", "Function", "AsyncFunction"],
|
|
2205
|
+
disabledGlobals: ["require", "process", "fetch"]
|
|
2206
|
+
};
|
|
2207
|
+
case "experimental":
|
|
2208
|
+
return {
|
|
2209
|
+
preset,
|
|
2210
|
+
timeoutMs: 1e4,
|
|
2211
|
+
allowLoops: true,
|
|
2212
|
+
allowConsole: true,
|
|
2213
|
+
maxSteps: 2e4,
|
|
2214
|
+
disabledBuiltins: ["eval", "Function", "AsyncFunction"],
|
|
2215
|
+
disabledGlobals: ["require", "process"]
|
|
2216
|
+
};
|
|
2217
|
+
case "secure":
|
|
2218
|
+
default:
|
|
2219
|
+
return {
|
|
2220
|
+
preset: "secure",
|
|
2221
|
+
timeoutMs: 3500,
|
|
2222
|
+
allowLoops: false,
|
|
2223
|
+
allowConsole: true,
|
|
2224
|
+
maxSteps: 5e3,
|
|
2225
|
+
disabledBuiltins: ["eval", "Function", "AsyncFunction"],
|
|
2226
|
+
disabledGlobals: [
|
|
2227
|
+
"require",
|
|
2228
|
+
"process",
|
|
2229
|
+
"fetch",
|
|
2230
|
+
"setTimeout",
|
|
2231
|
+
"setInterval",
|
|
2232
|
+
"setImmediate",
|
|
2233
|
+
"global",
|
|
2234
|
+
"globalThis"
|
|
2235
|
+
]
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
// plugins/plugin-codecall/src/tools/execute.tool.ts
|
|
2241
|
+
function getErrorCode(error) {
|
|
2242
|
+
if (!(error instanceof Error)) {
|
|
2243
|
+
return TOOL_CALL_ERROR_CODES.EXECUTION;
|
|
2244
|
+
}
|
|
2245
|
+
if (error.name === "ZodError" || error.message?.includes("validation")) {
|
|
2246
|
+
return TOOL_CALL_ERROR_CODES.VALIDATION;
|
|
2247
|
+
}
|
|
2248
|
+
if (error.message?.includes("timeout") || error.message?.includes("timed out")) {
|
|
2249
|
+
return TOOL_CALL_ERROR_CODES.TIMEOUT;
|
|
2250
|
+
}
|
|
2251
|
+
if (error.name === "ToolNotFoundError" || error.message?.includes("not found")) {
|
|
2252
|
+
return TOOL_CALL_ERROR_CODES.NOT_FOUND;
|
|
2253
|
+
}
|
|
2254
|
+
return TOOL_CALL_ERROR_CODES.EXECUTION;
|
|
2255
|
+
}
|
|
2256
|
+
var ExecuteTool = class extends ToolContext3 {
|
|
2257
|
+
async execute(input) {
|
|
2258
|
+
const { script, allowedTools } = input;
|
|
2259
|
+
const allowedToolSet = allowedTools ? new Set(allowedTools) : null;
|
|
2260
|
+
const environment = {
|
|
2261
|
+
callTool: async (name, toolInput, options) => {
|
|
2262
|
+
const throwOnError = options?.throwOnError !== false;
|
|
2263
|
+
assertNotSelfReference(name);
|
|
2264
|
+
if (allowedToolSet && !allowedToolSet.has(name)) {
|
|
2265
|
+
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name);
|
|
2266
|
+
if (throwOnError) {
|
|
2267
|
+
throw error;
|
|
2268
|
+
}
|
|
2269
|
+
return { success: false, error };
|
|
2270
|
+
}
|
|
2271
|
+
try {
|
|
2272
|
+
const request = {
|
|
2273
|
+
method: "tools/call",
|
|
2274
|
+
params: {
|
|
2275
|
+
name,
|
|
2276
|
+
arguments: toolInput
|
|
2277
|
+
}
|
|
2278
|
+
};
|
|
2279
|
+
const ctx = {
|
|
2280
|
+
authInfo: this.authInfo
|
|
2281
|
+
};
|
|
2282
|
+
const mcpResult = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
2283
|
+
if (!mcpResult) {
|
|
2284
|
+
const error = createToolCallError(TOOL_CALL_ERROR_CODES.EXECUTION, name, "Flow returned no result");
|
|
2285
|
+
if (throwOnError) {
|
|
2286
|
+
throw error;
|
|
2287
|
+
}
|
|
2288
|
+
return { success: false, error };
|
|
2289
|
+
}
|
|
2290
|
+
const result = extractResultFromCallToolResult(mcpResult);
|
|
2291
|
+
if (throwOnError) {
|
|
2292
|
+
return result;
|
|
2293
|
+
}
|
|
2294
|
+
return { success: true, data: result };
|
|
2295
|
+
} catch (error) {
|
|
2296
|
+
const errorCode = getErrorCode(error);
|
|
2297
|
+
const rawMessage = error instanceof Error ? error.message : void 0;
|
|
2298
|
+
const sanitizedError = createToolCallError(errorCode, name, rawMessage);
|
|
2299
|
+
if (throwOnError) {
|
|
2300
|
+
throw sanitizedError;
|
|
2301
|
+
}
|
|
2302
|
+
return { success: false, error: sanitizedError };
|
|
2303
|
+
}
|
|
2304
|
+
},
|
|
2305
|
+
getTool: (name) => {
|
|
2306
|
+
try {
|
|
2307
|
+
const tools = this.scope.tools.getTools(true);
|
|
2308
|
+
const tool = tools.find((t) => t.name === name || t.fullName === name);
|
|
2309
|
+
if (!tool) return void 0;
|
|
2310
|
+
return {
|
|
2311
|
+
name: tool.name,
|
|
2312
|
+
description: tool.metadata?.description,
|
|
2313
|
+
inputSchema: tool.rawInputSchema,
|
|
2314
|
+
outputSchema: tool.outputSchema
|
|
2315
|
+
};
|
|
2316
|
+
} catch {
|
|
2317
|
+
return void 0;
|
|
2318
|
+
}
|
|
2319
|
+
},
|
|
2320
|
+
console: this.get(CodeCallConfig).get("resolvedVm.allowConsole") ? console : void 0,
|
|
2321
|
+
mcpLog: (level, message, metadata) => {
|
|
2322
|
+
this.logger?.[level](message, metadata);
|
|
2323
|
+
},
|
|
2324
|
+
mcpNotify: (event, payload) => {
|
|
2325
|
+
this.logger?.debug("Notification sent", { event, payload });
|
|
2326
|
+
}
|
|
2327
|
+
};
|
|
2328
|
+
const enclaveService = this.get(EnclaveService);
|
|
2329
|
+
const config = this.get(CodeCallConfig);
|
|
2330
|
+
try {
|
|
2331
|
+
const executionResult = await enclaveService.execute(script, environment);
|
|
2332
|
+
if (executionResult.timedOut) {
|
|
2333
|
+
return {
|
|
2334
|
+
status: "timeout",
|
|
2335
|
+
error: {
|
|
2336
|
+
message: `Script execution timed out after ${config.getAll().resolvedVm.timeoutMs}ms`
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
if (!executionResult.success) {
|
|
2341
|
+
const error = executionResult.error;
|
|
2342
|
+
if (error.code === "VALIDATION_ERROR" || error.name === "ValidationError") {
|
|
2343
|
+
return {
|
|
2344
|
+
status: "illegal_access",
|
|
2345
|
+
error: {
|
|
2346
|
+
kind: "IllegalBuiltinAccess",
|
|
2347
|
+
message: error.message
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
if (error.toolName) {
|
|
2352
|
+
return {
|
|
2353
|
+
status: "tool_error",
|
|
2354
|
+
error: {
|
|
2355
|
+
source: "tool",
|
|
2356
|
+
toolName: error.toolName,
|
|
2357
|
+
toolInput: error.toolInput,
|
|
2358
|
+
message: error.message,
|
|
2359
|
+
code: error.code,
|
|
2360
|
+
details: error.details
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
return {
|
|
2365
|
+
status: "runtime_error",
|
|
2366
|
+
error: {
|
|
2367
|
+
source: "script",
|
|
2368
|
+
message: error.message,
|
|
2369
|
+
name: error.name,
|
|
2370
|
+
stack: error.stack
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
return {
|
|
2375
|
+
status: "ok",
|
|
2376
|
+
result: executionResult.result,
|
|
2377
|
+
logs: executionResult.logs.length > 0 ? executionResult.logs : void 0
|
|
2378
|
+
};
|
|
2379
|
+
} catch (error) {
|
|
2380
|
+
const errorName = error instanceof Error ? error.name : "Error";
|
|
2381
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2382
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
2383
|
+
const errorLoc = error.loc;
|
|
2384
|
+
if (errorName === "SyntaxError" || errorMessage?.includes("syntax")) {
|
|
2385
|
+
return {
|
|
2386
|
+
status: "syntax_error",
|
|
2387
|
+
error: {
|
|
2388
|
+
message: errorMessage || "Syntax error in script",
|
|
2389
|
+
location: errorLoc ? { line: errorLoc.line, column: errorLoc.column } : void 0
|
|
2390
|
+
}
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
return {
|
|
2394
|
+
status: "runtime_error",
|
|
2395
|
+
error: {
|
|
2396
|
+
source: "script",
|
|
2397
|
+
message: errorMessage || "An unexpected error occurred during script execution",
|
|
2398
|
+
name: errorName,
|
|
2399
|
+
stack: errorStack
|
|
2400
|
+
}
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
};
|
|
2405
|
+
ExecuteTool = __decorateClass([
|
|
2406
|
+
Tool3({
|
|
2407
|
+
name: "codecall:execute",
|
|
2408
|
+
cache: {
|
|
2409
|
+
ttl: 0,
|
|
2410
|
+
// No caching - each execution is unique
|
|
2411
|
+
slideWindow: false
|
|
2412
|
+
},
|
|
2413
|
+
codecall: {
|
|
2414
|
+
enabledInCodeCall: false,
|
|
2415
|
+
visibleInListTools: true
|
|
2416
|
+
},
|
|
2417
|
+
description: executeToolDescription,
|
|
2418
|
+
inputSchema: executeToolInputSchema,
|
|
2419
|
+
outputSchema: executeToolOutputSchema
|
|
2420
|
+
})
|
|
2421
|
+
], ExecuteTool);
|
|
2422
|
+
|
|
2423
|
+
// plugins/plugin-codecall/src/tools/invoke.tool.ts
|
|
2424
|
+
import { Tool as Tool4, ToolContext as ToolContext4 } from "@frontmcp/sdk";
|
|
2425
|
+
|
|
2426
|
+
// plugins/plugin-codecall/src/tools/invoke.schema.ts
|
|
2427
|
+
import { z as z6 } from "zod";
|
|
2428
|
+
import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
2429
|
+
var invokeToolDescription = `Call ONE tool directly. Returns standard MCP CallToolResult.
|
|
2430
|
+
|
|
2431
|
+
USE invoke: single tool, no transformation
|
|
2432
|
+
USE execute: multiple tools, loops, filtering, joining
|
|
2433
|
+
|
|
2434
|
+
INPUT: tool (string), input (object matching tool schema)
|
|
2435
|
+
OUTPUT: MCP CallToolResult (same as standard tool call)
|
|
2436
|
+
ERRORS: tool_not_found (\u2192 re-search) | validation_error | execution_error | permission_denied
|
|
2437
|
+
|
|
2438
|
+
FLOW: search \u2192 describe \u2192 invoke`;
|
|
2439
|
+
var invokeToolInputSchema = z6.object({
|
|
2440
|
+
tool: z6.string().describe(
|
|
2441
|
+
'The name of the tool to invoke (e.g., "users:getById", "billing:getInvoice"). Must be a tool you discovered via codecall:search.'
|
|
2442
|
+
),
|
|
2443
|
+
input: z6.record(z6.string(), z6.unknown()).describe(
|
|
2444
|
+
"The input parameters for the tool. Structure must match the tool's input schema (check codecall:describe for schema details)."
|
|
2445
|
+
)
|
|
2446
|
+
});
|
|
2447
|
+
var invokeToolOutputSchema = CallToolResultSchema;
|
|
2448
|
+
|
|
2449
|
+
// plugins/plugin-codecall/src/tools/invoke.tool.ts
|
|
2450
|
+
function buildErrorResult(message) {
|
|
2451
|
+
return {
|
|
2452
|
+
content: [{ type: "text", text: message }],
|
|
2453
|
+
isError: true
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
var InvokeTool = class extends ToolContext4 {
|
|
2457
|
+
async execute(input) {
|
|
2458
|
+
const { tool: toolName, input: toolInput } = input;
|
|
2459
|
+
if (isBlockedSelfReference(toolName)) {
|
|
2460
|
+
return buildErrorResult(
|
|
2461
|
+
`Tool "${toolName}" cannot be invoked directly. CodeCall tools are internal and not accessible via codecall:invoke.`
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
const request = {
|
|
2465
|
+
method: "tools/call",
|
|
2466
|
+
params: {
|
|
2467
|
+
name: toolName,
|
|
2468
|
+
arguments: toolInput
|
|
2469
|
+
}
|
|
2470
|
+
};
|
|
2471
|
+
const ctx = {
|
|
2472
|
+
authInfo: this.authInfo
|
|
2473
|
+
};
|
|
2474
|
+
const result = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
2475
|
+
if (!result) {
|
|
2476
|
+
return buildErrorResult(`Tool "${toolName}" not found. Use codecall:search to discover available tools.`);
|
|
2477
|
+
}
|
|
2478
|
+
return result;
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
InvokeTool = __decorateClass([
|
|
2482
|
+
Tool4({
|
|
2483
|
+
name: "codecall:invoke",
|
|
2484
|
+
cache: {
|
|
2485
|
+
ttl: 0,
|
|
2486
|
+
// No caching - each invocation is unique
|
|
2487
|
+
slideWindow: false
|
|
2488
|
+
},
|
|
2489
|
+
codecall: {
|
|
2490
|
+
enabledInCodeCall: false,
|
|
2491
|
+
visibleInListTools: true
|
|
2492
|
+
},
|
|
2493
|
+
description: invokeToolDescription,
|
|
2494
|
+
inputSchema: invokeToolInputSchema,
|
|
2495
|
+
outputSchema: invokeToolOutputSchema
|
|
2496
|
+
})
|
|
2497
|
+
], InvokeTool);
|
|
2498
|
+
|
|
2499
|
+
// plugins/plugin-codecall/src/codecall.plugin.ts
|
|
2500
|
+
import CachePlugin from "@frontmcp/plugin-cache";
|
|
2501
|
+
var CodeCallPlugin = class extends DynamicPlugin {
|
|
2502
|
+
options;
|
|
2503
|
+
constructor(options = {}) {
|
|
2504
|
+
super();
|
|
2505
|
+
this.options = codeCallPluginOptionsSchema.parse(options);
|
|
2506
|
+
console.log("[CodeCall] Plugin initialized with mode:", this.options.mode);
|
|
2507
|
+
}
|
|
2508
|
+
/**
|
|
2509
|
+
* Dynamic providers allow you to configure the plugin with custom options
|
|
2510
|
+
* without touching the plugin decorator.
|
|
2511
|
+
*/
|
|
2512
|
+
static dynamicProviders(options) {
|
|
2513
|
+
const parsedOptions = codeCallPluginOptionsSchema.parse(options);
|
|
2514
|
+
const config = new CodeCallConfig(parsedOptions);
|
|
2515
|
+
return [
|
|
2516
|
+
{
|
|
2517
|
+
name: "codecall:config",
|
|
2518
|
+
provide: CodeCallConfig,
|
|
2519
|
+
useValue: config
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
name: "codecall:enclave",
|
|
2523
|
+
provide: EnclaveService,
|
|
2524
|
+
inject: () => [CodeCallConfig],
|
|
2525
|
+
useFactory: async (cfg) => {
|
|
2526
|
+
return new EnclaveService(cfg);
|
|
2527
|
+
}
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
name: "codecall:tool-search",
|
|
2531
|
+
provide: ToolSearchService,
|
|
2532
|
+
inject: () => [ScopeEntry],
|
|
2533
|
+
useFactory: async (scope) => {
|
|
2534
|
+
return new ToolSearchService(
|
|
2535
|
+
{
|
|
2536
|
+
embeddingOptions: parsedOptions.embedding,
|
|
2537
|
+
mode: parsedOptions.mode,
|
|
2538
|
+
includeTools: parsedOptions["includeTools"]
|
|
2539
|
+
},
|
|
2540
|
+
scope
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
];
|
|
2545
|
+
}
|
|
2546
|
+
async adjustListTools(flowCtx) {
|
|
2547
|
+
const { resolvedTools } = flowCtx.state;
|
|
2548
|
+
console.log("[CodeCall] adjustListTools hook called, mode:", this.options.mode);
|
|
2549
|
+
console.log("[CodeCall] Tools before filter:", resolvedTools?.length ?? 0);
|
|
2550
|
+
if (!resolvedTools || resolvedTools.length === 0) {
|
|
2551
|
+
console.log("[CodeCall] No tools to filter, returning early");
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
const filteredTools = resolvedTools.filter(({ tool }) => {
|
|
2555
|
+
return this.shouldShowInListTools(tool, this.options.mode);
|
|
2556
|
+
});
|
|
2557
|
+
console.log("[CodeCall] Tools after filter:", filteredTools.length);
|
|
2558
|
+
flowCtx.state.set("resolvedTools", filteredTools);
|
|
2559
|
+
}
|
|
2560
|
+
/**
|
|
2561
|
+
* Determine if a tool should be visible in list_tools based on mode.
|
|
2562
|
+
*
|
|
2563
|
+
* @param tool - The tool entry to check
|
|
2564
|
+
* @param mode - The current CodeCall mode
|
|
2565
|
+
* @returns true if tool should be visible
|
|
2566
|
+
*/
|
|
2567
|
+
shouldShowInListTools(tool, mode) {
|
|
2568
|
+
if (this.isCodeCallTool(tool)) {
|
|
2569
|
+
return true;
|
|
2570
|
+
}
|
|
2571
|
+
const codecallMeta = this.getCodeCallMetadata(tool);
|
|
2572
|
+
switch (mode) {
|
|
2573
|
+
case "codecall_only":
|
|
2574
|
+
return codecallMeta?.visibleInListTools === true;
|
|
2575
|
+
case "codecall_opt_in":
|
|
2576
|
+
return true;
|
|
2577
|
+
case "metadata_driven":
|
|
2578
|
+
if (codecallMeta?.visibleInListTools === false) {
|
|
2579
|
+
return false;
|
|
2580
|
+
}
|
|
2581
|
+
return true;
|
|
2582
|
+
default:
|
|
2583
|
+
return true;
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
/**
|
|
2587
|
+
* Check if a tool is a CodeCall meta-tool.
|
|
2588
|
+
* CodeCall meta-tools always remain visible.
|
|
2589
|
+
*/
|
|
2590
|
+
isCodeCallTool(tool) {
|
|
2591
|
+
const name = tool.name || tool.fullName;
|
|
2592
|
+
return name.startsWith("codecall:");
|
|
2593
|
+
}
|
|
2594
|
+
/**
|
|
2595
|
+
* Extract CodeCall-specific metadata from a tool.
|
|
2596
|
+
*/
|
|
2597
|
+
getCodeCallMetadata(tool) {
|
|
2598
|
+
return tool.metadata?.codecall;
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2601
|
+
__decorateClass([
|
|
2602
|
+
ListToolsHook.Did("resolveConflicts", { priority: 1e3 })
|
|
2603
|
+
], CodeCallPlugin.prototype, "adjustListTools", 1);
|
|
2604
|
+
CodeCallPlugin = __decorateClass([
|
|
2605
|
+
Plugin({
|
|
2606
|
+
name: "codecall",
|
|
2607
|
+
description: "CodeCall plugin: AgentScript-based meta-tools for orchestrating MCP tools",
|
|
2608
|
+
providers: [],
|
|
2609
|
+
plugins: [CachePlugin],
|
|
2610
|
+
tools: [SearchTool, DescribeTool, ExecuteTool, InvokeTool]
|
|
2611
|
+
})
|
|
2612
|
+
], CodeCallPlugin);
|
|
2613
|
+
export {
|
|
2614
|
+
CodeCallPlugin,
|
|
2615
|
+
codeCallDirectCallsOptionsSchema,
|
|
2616
|
+
codeCallEmbeddingOptionsSchema,
|
|
2617
|
+
codeCallModeSchema,
|
|
2618
|
+
codeCallPluginOptionsSchema,
|
|
2619
|
+
codeCallSidecarOptionsSchema,
|
|
2620
|
+
codeCallVmOptionsSchema,
|
|
2621
|
+
codeCallVmPresetSchema,
|
|
2622
|
+
CodeCallPlugin as default,
|
|
2623
|
+
embeddingStrategySchema,
|
|
2624
|
+
synonymExpansionConfigSchema
|
|
2625
|
+
};
|