@assistant-ui/x-generative-compiler 0.0.5 → 0.0.6
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/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +247 -36
- package/dist/compile.js.map +1 -1
- package/dist/package.js +1 -1
- package/package.json +9 -9
- package/src/compile.test.ts +390 -4
- package/src/compile.ts +397 -36
package/dist/compile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","names":[],"sources":["../src/compile.ts"],"mappings":";;;KAoBY,QAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"compile.d.ts","names":[],"sources":["../src/compile.ts"],"mappings":";;;KAoBY,QAAA;AAAA,UAyCK,cAAA;EAzCL;EA2CV,MAAA,EAAQ,MAAM;;EAEd,QAAA;EA7CkB;EA+ClB,UAAA;EAN6B;;;;;;;;EAe7B,gBAAA;AAAA;AAAA,UAGe,aAAA;EACf,IAAA;EACA,GAAG;AAAA;AAAA;AAAA,cAIQ,sBAAA,SAA+B,KAAK;cACnC,OAAA,UAAiB,QAAA;AAAA;;iBASf,kBAAA,CAAmB,IAAY;;;;;iBA+I/B,iBAAA,CACd,IAAA,UACA,OAAA,EAAS,cAAA,GACR,aAAa"}
|
package/dist/compile.js
CHANGED
|
@@ -34,10 +34,17 @@ const DISTRIBUTION_PACKAGES = [
|
|
|
34
34
|
* through untouched — the library, not this compiler, routes its halves.
|
|
35
35
|
*/
|
|
36
36
|
const GENERATIVE_FACTORY = "JSONGenerativeUI";
|
|
37
|
+
/**
|
|
38
|
+
* The factory producing an interactable's complete tool entry. Unlike
|
|
39
|
+
* `JSONGenerativeUI`, its package has no per-target builds, so this compiler
|
|
40
|
+
* splits the inline config: the client keeps `render`, the server drops it.
|
|
41
|
+
* The factory's own `execute` is internal and client-safe (frontend tool).
|
|
42
|
+
*/
|
|
43
|
+
const INTERACTABLE_TOOL_FACTORY = "unstable_interactableTool";
|
|
37
44
|
/** Thrown when a `"use generative"` file violates an authoring constraint. */
|
|
38
45
|
var GenerativeCompileError = class extends Error {
|
|
39
46
|
constructor(message, filename) {
|
|
40
|
-
super(`[assistant-ui/
|
|
47
|
+
super(`[assistant-ui/use-generative]${filename ? ` ${filename}:` : ""} ${message}`);
|
|
41
48
|
this.name = "GenerativeCompileError";
|
|
42
49
|
}
|
|
43
50
|
};
|
|
@@ -56,7 +63,89 @@ function isGenerativeModule(code) {
|
|
|
56
63
|
i = end + 2;
|
|
57
64
|
} else break;
|
|
58
65
|
}
|
|
59
|
-
|
|
66
|
+
const quote = code[i];
|
|
67
|
+
if (quote !== "\"" && quote !== "'") return false;
|
|
68
|
+
const directiveStart = i + 1;
|
|
69
|
+
if (!code.startsWith("use generative", directiveStart)) return false;
|
|
70
|
+
const directiveEnd = directiveStart + DIRECTIVE.length;
|
|
71
|
+
if (code[directiveEnd] !== quote) return false;
|
|
72
|
+
return hasDirectiveTerminator(code, directiveEnd + 1);
|
|
73
|
+
}
|
|
74
|
+
function hasDirectiveTerminator(code, start) {
|
|
75
|
+
let i = start;
|
|
76
|
+
let sawLineTerminator = false;
|
|
77
|
+
for (;;) {
|
|
78
|
+
if (i >= code.length) return true;
|
|
79
|
+
const char = code.charCodeAt(i);
|
|
80
|
+
if (isSemicolon(char)) return true;
|
|
81
|
+
if (isLineTerminator(char)) {
|
|
82
|
+
sawLineTerminator = true;
|
|
83
|
+
i++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (code.startsWith("//", i)) {
|
|
87
|
+
const lineEnd = nextLineTerminatorIndex(code, i + 2);
|
|
88
|
+
if (lineEnd === -1) return true;
|
|
89
|
+
sawLineTerminator = true;
|
|
90
|
+
i = lineEnd + 1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (code.startsWith("/*", i)) {
|
|
94
|
+
const end = code.indexOf("*/", i + 2);
|
|
95
|
+
if (end === -1) return false;
|
|
96
|
+
sawLineTerminator ||= containsLineTerminator(code, i + 2, end);
|
|
97
|
+
i = end + 2;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (/\s/.test(code[i])) {
|
|
101
|
+
i++;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
return sawLineTerminator && !startsExpressionContinuation(code, i);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function nextLineTerminatorIndex(code, start) {
|
|
108
|
+
for (let i = start; i < code.length; i++) if (isLineTerminator(code.charCodeAt(i))) return i;
|
|
109
|
+
return -1;
|
|
110
|
+
}
|
|
111
|
+
function containsLineTerminator(code, start, end) {
|
|
112
|
+
for (let i = start; i < end; i++) if (isLineTerminator(code.charCodeAt(i))) return true;
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
function startsExpressionContinuation(code, start) {
|
|
116
|
+
switch (code[start]) {
|
|
117
|
+
case ".":
|
|
118
|
+
case "+":
|
|
119
|
+
case "-":
|
|
120
|
+
case "*":
|
|
121
|
+
case "/":
|
|
122
|
+
case "%":
|
|
123
|
+
case "<":
|
|
124
|
+
case ">":
|
|
125
|
+
case "=":
|
|
126
|
+
case "!":
|
|
127
|
+
case "&":
|
|
128
|
+
case "|":
|
|
129
|
+
case "^":
|
|
130
|
+
case "?":
|
|
131
|
+
case ":":
|
|
132
|
+
case ",":
|
|
133
|
+
case "[":
|
|
134
|
+
case "(":
|
|
135
|
+
case "`": return true;
|
|
136
|
+
}
|
|
137
|
+
return startsKeywordContinuation(code, start, "as") || startsKeywordContinuation(code, start, "in") || startsKeywordContinuation(code, start, "instanceof") || startsKeywordContinuation(code, start, "satisfies");
|
|
138
|
+
}
|
|
139
|
+
function startsKeywordContinuation(code, start, keyword) {
|
|
140
|
+
if (!code.startsWith(keyword, start)) return false;
|
|
141
|
+
const next = code[start + keyword.length];
|
|
142
|
+
return next === void 0 || !/[\p{ID_Continue}$]/u.test(next);
|
|
143
|
+
}
|
|
144
|
+
function isSemicolon(char) {
|
|
145
|
+
return char === 59;
|
|
146
|
+
}
|
|
147
|
+
function isLineTerminator(char) {
|
|
148
|
+
return char === 10 || char === 13 || char === 8232 || char === 8233;
|
|
60
149
|
}
|
|
61
150
|
/**
|
|
62
151
|
* Rewrites a `"use generative"` module for a single build target, keeping only the
|
|
@@ -76,7 +165,8 @@ function compileGenerative(code, options) {
|
|
|
76
165
|
ensureCompilerCompatibleWithCore(ast, filename);
|
|
77
166
|
ensureDefaultExport(ast, filename);
|
|
78
167
|
const generativeInstances = collectGenerativeInstances(ast);
|
|
79
|
-
const
|
|
168
|
+
const interactableToolImports = collectInteractableToolImports(ast);
|
|
169
|
+
const toolkitSpreadNames = collectToolkitSpreadNames(ast, filename, createToolkitNameContext());
|
|
80
170
|
const flags = {
|
|
81
171
|
keptRender: false,
|
|
82
172
|
keptBackendExecute: false
|
|
@@ -93,7 +183,7 @@ function compileGenerative(code, options) {
|
|
|
93
183
|
}
|
|
94
184
|
if (t.isIdentifier(callee, { name: TOOLKIT_WRAPPER })) {
|
|
95
185
|
if (!object) throw new GenerativeCompileError(`${TOOLKIT_WRAPPER}() takes an inline object literal of tools`, filename);
|
|
96
|
-
compileToolkit(object, target, generativeInstances,
|
|
186
|
+
compileToolkit(object, target, generativeInstances, interactableToolImports, toolkitSpreadNames, flags, filename);
|
|
97
187
|
path.replaceWith(object);
|
|
98
188
|
path.skip();
|
|
99
189
|
}
|
|
@@ -244,7 +334,36 @@ function collectGenerativeInstances(ast) {
|
|
|
244
334
|
return names;
|
|
245
335
|
}
|
|
246
336
|
/**
|
|
247
|
-
*
|
|
337
|
+
* Collects the local names `unstable_interactableTool` is imported under from a
|
|
338
|
+
* distribution package, so toolkit entries calling it can be recognized
|
|
339
|
+
* (and a same-named local function can't smuggle an arbitrary call through).
|
|
340
|
+
*/
|
|
341
|
+
function collectInteractableToolImports(ast) {
|
|
342
|
+
const names = /* @__PURE__ */ new Set();
|
|
343
|
+
for (const statement of ast.program.body) {
|
|
344
|
+
if (!t.isImportDeclaration(statement)) continue;
|
|
345
|
+
if (!packageNameFromSpecifier(statement.source.value)) continue;
|
|
346
|
+
for (const specifier of statement.specifiers) if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported, { name: INTERACTABLE_TOOL_FACTORY })) names.add(specifier.local.name);
|
|
347
|
+
}
|
|
348
|
+
return names;
|
|
349
|
+
}
|
|
350
|
+
function createToolkitNameContext() {
|
|
351
|
+
return {
|
|
352
|
+
importedToolkitNamesByFile: /* @__PURE__ */ new Map(),
|
|
353
|
+
resolvingImportedToolkitNames: /* @__PURE__ */ new Set()
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* The inline config of an `unstable_interactableTool({ ... })` toolkit entry, or `null`
|
|
358
|
+
* when the entry is some other expression.
|
|
359
|
+
*/
|
|
360
|
+
function interactableToolConfig(value, imports) {
|
|
361
|
+
return t.isCallExpression(value) && t.isIdentifier(value.callee) && imports.has(value.callee.name) && t.isObjectExpression(value.arguments[0]) ? value.arguments[0] : null;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Toolkit identifiers that are safe to spread into a `defineToolkit({ ... })`,
|
|
365
|
+
* paired with the static tool names they contain. A `null` name list means the
|
|
366
|
+
* spread is safe, but its names are not statically known for duplicate checks.
|
|
248
367
|
*
|
|
249
368
|
* Two kinds qualify:
|
|
250
369
|
*
|
|
@@ -257,30 +376,38 @@ function collectGenerativeInstances(ast) {
|
|
|
257
376
|
* export crosses the generative-module boundary, so named imports don't
|
|
258
377
|
* qualify — they would be `undefined` once that module is build-split.
|
|
259
378
|
*/
|
|
260
|
-
function
|
|
261
|
-
const
|
|
262
|
-
const
|
|
379
|
+
function collectToolkitSpreadNames(ast, filename, context) {
|
|
380
|
+
const spreadNames = /* @__PURE__ */ new Map();
|
|
381
|
+
const localToolkitCalls = /* @__PURE__ */ new Map();
|
|
263
382
|
for (const statement of ast.program.body) {
|
|
264
383
|
if (t.isVariableDeclaration(statement)) {
|
|
265
384
|
for (const declaration of statement.declarations) {
|
|
266
385
|
const { id, init } = declaration;
|
|
267
|
-
if (t.isIdentifier(id) && init
|
|
386
|
+
if (t.isIdentifier(id) && init) {
|
|
387
|
+
const toolkitCall = unwrapToToolkitCall(init);
|
|
388
|
+
if (toolkitCall) localToolkitCalls.set(id.name, toolkitCall);
|
|
389
|
+
}
|
|
268
390
|
}
|
|
269
391
|
continue;
|
|
270
392
|
}
|
|
271
393
|
if (t.isImportDeclaration(statement)) {
|
|
272
394
|
const defaultSpecifier = statement.specifiers.find((specifier) => t.isImportDefaultSpecifier(specifier));
|
|
273
395
|
if (!defaultSpecifier) continue;
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
if (isGenerative === void 0) {
|
|
277
|
-
isGenerative = isGenerativeImport(source, filename);
|
|
278
|
-
generativeBySource.set(source, isGenerative);
|
|
279
|
-
}
|
|
280
|
-
if (isGenerative) names.add(defaultSpecifier.local.name);
|
|
396
|
+
const names = getGenerativeImportToolkitNames(statement.source.value, filename, context);
|
|
397
|
+
if (names !== void 0) spreadNames.set(defaultSpecifier.local.name, names);
|
|
281
398
|
}
|
|
282
399
|
}
|
|
283
|
-
|
|
400
|
+
const resolveLocal = (name) => {
|
|
401
|
+
if (spreadNames.has(name)) return spreadNames.get(name);
|
|
402
|
+
const call = localToolkitCalls.get(name);
|
|
403
|
+
if (!call) return void 0;
|
|
404
|
+
const object = t.isObjectExpression(call.arguments[0]) ? call.arguments[0] : null;
|
|
405
|
+
const publicNames = uniqueToolkitNames(object ? collectToolkitObjectNames(object, spreadNames) : null);
|
|
406
|
+
spreadNames.set(name, publicNames);
|
|
407
|
+
return publicNames;
|
|
408
|
+
};
|
|
409
|
+
for (const name of localToolkitCalls.keys()) resolveLocal(name);
|
|
410
|
+
return spreadNames;
|
|
284
411
|
}
|
|
285
412
|
const MODULE_EXTENSIONS = [
|
|
286
413
|
".ts",
|
|
@@ -293,28 +420,63 @@ const MODULE_EXTENSIONS = [
|
|
|
293
420
|
".cjs"
|
|
294
421
|
];
|
|
295
422
|
/** Extensions a specifier may carry that actually map to a TS source file. */
|
|
296
|
-
const REWRITABLE_JS_EXTENSIONS = new Set([
|
|
423
|
+
const REWRITABLE_JS_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
297
424
|
".js",
|
|
298
425
|
".jsx",
|
|
299
426
|
".mjs",
|
|
300
427
|
".cjs"
|
|
301
428
|
]);
|
|
302
429
|
/**
|
|
303
|
-
*
|
|
304
|
-
* Relative specifiers and `tsconfig` path aliases
|
|
305
|
-
* anything else (a bare package, an unresolvable
|
|
306
|
-
* non-generative, and thus an unsafe spread.
|
|
430
|
+
* Reads the static tool names from the default export of an imported
|
|
431
|
+
* `"use generative"` module. Relative specifiers and `tsconfig` path aliases
|
|
432
|
+
* (e.g. `@/tools`) are resolved; anything else (a bare package, an unresolvable
|
|
433
|
+
* alias) is treated as non-generative, and thus an unsafe spread.
|
|
307
434
|
*/
|
|
308
|
-
function
|
|
435
|
+
function getGenerativeImportToolkitNames(source, filename, context) {
|
|
309
436
|
const cleanFilename = cleanAbsoluteFilename(filename);
|
|
310
|
-
if (!cleanFilename) return
|
|
437
|
+
if (!cleanFilename) return void 0;
|
|
311
438
|
const resolved = resolveImportedModuleFile(source, cleanFilename);
|
|
312
|
-
if (!resolved) return
|
|
439
|
+
if (!resolved) return void 0;
|
|
440
|
+
if (context.importedToolkitNamesByFile.has(resolved)) return context.importedToolkitNamesByFile.get(resolved);
|
|
441
|
+
if (context.resolvingImportedToolkitNames.has(resolved)) return null;
|
|
442
|
+
let code;
|
|
313
443
|
try {
|
|
314
|
-
|
|
444
|
+
code = readFileSync(resolved, "utf8");
|
|
315
445
|
} catch {
|
|
316
|
-
|
|
446
|
+
context.importedToolkitNamesByFile.set(resolved, void 0);
|
|
447
|
+
return;
|
|
317
448
|
}
|
|
449
|
+
if (!isGenerativeModule(code)) {
|
|
450
|
+
context.importedToolkitNamesByFile.set(resolved, void 0);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
context.resolvingImportedToolkitNames.add(resolved);
|
|
454
|
+
try {
|
|
455
|
+
const names = getDefaultExportToolkitNames(parse(code, {
|
|
456
|
+
sourceType: "module",
|
|
457
|
+
plugins: [
|
|
458
|
+
"typescript",
|
|
459
|
+
"jsx",
|
|
460
|
+
"explicitResourceManagement"
|
|
461
|
+
]
|
|
462
|
+
}), resolved, context);
|
|
463
|
+
context.importedToolkitNamesByFile.set(resolved, names);
|
|
464
|
+
return names;
|
|
465
|
+
} catch (error) {
|
|
466
|
+
if (error instanceof GenerativeCompileError) throw error;
|
|
467
|
+
context.importedToolkitNamesByFile.set(resolved, null);
|
|
468
|
+
return null;
|
|
469
|
+
} finally {
|
|
470
|
+
context.resolvingImportedToolkitNames.delete(resolved);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function getDefaultExportToolkitNames(ast, filename, context) {
|
|
474
|
+
const def = ast.program.body.find((stmt) => t.isExportDefaultDeclaration(stmt));
|
|
475
|
+
if (!def) return null;
|
|
476
|
+
const toolkitCall = unwrapToToolkitCall(def.declaration);
|
|
477
|
+
if (!toolkitCall || !t.isObjectExpression(toolkitCall.arguments[0])) return null;
|
|
478
|
+
const spreadNames = collectToolkitSpreadNames(ast, filename, context);
|
|
479
|
+
return uniqueToolkitNames(collectToolkitObjectNames(toolkitCall.arguments[0], spreadNames));
|
|
318
480
|
}
|
|
319
481
|
/** Resolves an import specifier (relative or `tsconfig`-aliased) to a file on disk. */
|
|
320
482
|
function resolveImportedModuleFile(source, fromFilename) {
|
|
@@ -456,13 +618,54 @@ function parseJsonc(text) {
|
|
|
456
618
|
function unwrapToToolkitCall(node) {
|
|
457
619
|
return unwrapToCall(node, TOOLKIT_WRAPPER) ?? unwrapToCall(node, MCP_TOOLKIT_WRAPPER);
|
|
458
620
|
}
|
|
459
|
-
function
|
|
460
|
-
|
|
621
|
+
function collectToolkitObjectNames(object, toolkitSpreadNames) {
|
|
622
|
+
const names = [];
|
|
623
|
+
for (const entry of object.properties) {
|
|
624
|
+
const entryNames = toolkitEntryNames(entry, toolkitSpreadNames);
|
|
625
|
+
if (!entryNames) return null;
|
|
626
|
+
names.push(...entryNames);
|
|
627
|
+
}
|
|
628
|
+
return names;
|
|
629
|
+
}
|
|
630
|
+
function uniqueToolkitNames(names) {
|
|
631
|
+
return names ? [...new Set(names)] : names;
|
|
632
|
+
}
|
|
633
|
+
function toolkitEntryNames(entry, toolkitSpreadNames) {
|
|
634
|
+
if (t.isSpreadElement(entry)) {
|
|
635
|
+
if (t.isIdentifier(entry.argument)) return toolkitSpreadNames.get(entry.argument.name) ?? null;
|
|
636
|
+
const directMcpToolkit = unwrapToCall(entry.argument, MCP_TOOLKIT_WRAPPER);
|
|
637
|
+
if (directMcpToolkit && t.isObjectExpression(directMcpToolkit.arguments[0])) return collectToolkitObjectNames(directMcpToolkit.arguments[0], toolkitSpreadNames);
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
if (t.isObjectProperty(entry) || t.isObjectMethod(entry)) {
|
|
641
|
+
const name = memberName(entry.key, entry.computed);
|
|
642
|
+
return name ? [name] : [];
|
|
643
|
+
}
|
|
644
|
+
return [];
|
|
645
|
+
}
|
|
646
|
+
function warnDuplicateToolkitNames(object, toolkitSpreadNames, filename) {
|
|
647
|
+
const names = collectToolkitObjectNames(object, toolkitSpreadNames);
|
|
648
|
+
if (!names) return;
|
|
649
|
+
const seen = /* @__PURE__ */ new Set();
|
|
650
|
+
const warned = /* @__PURE__ */ new Set();
|
|
651
|
+
for (const name of names) {
|
|
652
|
+
if (seen.has(name)) {
|
|
653
|
+
if (!warned.has(name)) {
|
|
654
|
+
console.warn(new GenerativeCompileError(`Duplicate tool name "${name}" while composing toolkits. JavaScript object spread keeps the last definition.`, filename).message);
|
|
655
|
+
warned.add(name);
|
|
656
|
+
}
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
seen.add(name);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function isSafeToolkitSpread(entry, toolkitSpreadNames) {
|
|
663
|
+
if (t.isIdentifier(entry.argument)) return toolkitSpreadNames.has(entry.argument.name);
|
|
461
664
|
const directMcpToolkit = unwrapToCall(entry.argument, MCP_TOOLKIT_WRAPPER);
|
|
462
665
|
return !!directMcpToolkit && t.isObjectExpression(directMcpToolkit.arguments[0]);
|
|
463
666
|
}
|
|
464
667
|
/** The `JSONGenerativeUI` methods that produce a split-by-condition tool. */
|
|
465
|
-
const GENERATIVE_TOOL_METHODS = new Set(["present", "promptUser"]);
|
|
668
|
+
const GENERATIVE_TOOL_METHODS = /* @__PURE__ */ new Set(["present", "promptUser"]);
|
|
466
669
|
/**
|
|
467
670
|
* Whether a toolkit entry's value is a call to a tool-producing method on a
|
|
468
671
|
* collected `JSONGenerativeUI` instance (`generative.present()`), which passes
|
|
@@ -493,12 +696,13 @@ function compileComponents(object, target, flags, filename) {
|
|
|
493
696
|
* `generative.present()` passes through, the library having already split it.
|
|
494
697
|
* Mutates the object in place and records outcomes in {@link TargetFlags}.
|
|
495
698
|
*/
|
|
496
|
-
function compileToolkit(object, target, instances,
|
|
699
|
+
function compileToolkit(object, target, instances, interactableToolImports, toolkitSpreadNames, flags, filename) {
|
|
700
|
+
if (target === "client") warnDuplicateToolkitNames(object, toolkitSpreadNames, filename);
|
|
497
701
|
const nextProperties = [];
|
|
498
702
|
for (const entry of object.properties) {
|
|
499
703
|
const value = entryValue(entry);
|
|
500
704
|
if (!value) {
|
|
501
|
-
if (t.isSpreadElement(entry) && isSafeToolkitSpread(entry,
|
|
705
|
+
if (t.isSpreadElement(entry) && isSafeToolkitSpread(entry, toolkitSpreadNames)) {
|
|
502
706
|
nextProperties.push(entry);
|
|
503
707
|
continue;
|
|
504
708
|
}
|
|
@@ -507,7 +711,14 @@ function compileToolkit(object, target, instances, safeToolkitSpreads, flags, fi
|
|
|
507
711
|
nextProperties.push(entry);
|
|
508
712
|
continue;
|
|
509
713
|
}
|
|
510
|
-
|
|
714
|
+
const config = raw && interactableToolConfig(raw, interactableToolImports);
|
|
715
|
+
if (config) {
|
|
716
|
+
if (target === "client") flags.keptRender = true;
|
|
717
|
+
else removeMember(config, "render");
|
|
718
|
+
nextProperties.push(entry);
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
throw new GenerativeCompileError("each tool must be an inline object literal (`name: { ... }`) or a compiler-visible toolkit spread / generative tool (e.g. `...defineMcpToolkit(...)`, `...baseToolkit`, `generative.present()`, or `unstable_interactableTool(...)`) so its `execute` can be routed", filename);
|
|
511
722
|
}
|
|
512
723
|
const execute = findMember(value, "execute");
|
|
513
724
|
const isStub = execute ? executeIsStubTool(execute) : false;
|
|
@@ -517,7 +728,7 @@ function compileToolkit(object, target, instances, safeToolkitSpreads, flags, fi
|
|
|
517
728
|
const hasRenderText = !!findMember(value, "renderText");
|
|
518
729
|
if (type === "frontend" && !hasRender && !hasRenderText) throw new GenerativeCompileError("a frontend tool must declare a `render` or `renderText` (it has no server execute to show otherwise)", filename);
|
|
519
730
|
if (type === "human" && !hasRender) throw new GenerativeCompileError("a human tool must declare a `render` so it can collect input", filename);
|
|
520
|
-
if (type === "provider" && execute) applyProviderToolConfig(value, execute, filename);
|
|
731
|
+
if (type === "provider" && execute) applyProviderToolConfig(value, execute, t.isObjectProperty(entry) ? memberName(entry.key, entry.computed) : void 0, filename);
|
|
521
732
|
if (isExternal) {
|
|
522
733
|
if (!hasRender && !hasRenderText) throw new GenerativeCompileError("an external tool must declare a `render` or `renderText` (assistant-ui only renders calls for tools defined elsewhere)", filename);
|
|
523
734
|
if (target === "server") continue;
|
|
@@ -539,7 +750,7 @@ function compileToolkit(object, target, instances, safeToolkitSpreads, flags, fi
|
|
|
539
750
|
}
|
|
540
751
|
object.properties = nextProperties;
|
|
541
752
|
}
|
|
542
|
-
function applyProviderToolConfig(object, execute, filename) {
|
|
753
|
+
function applyProviderToolConfig(object, execute, toolName, filename) {
|
|
543
754
|
if (!t.isObjectProperty(execute) || !t.isCallExpression(execute.value) || execute.value.arguments.length !== 1 || !t.isObjectExpression(execute.value.arguments[0])) throw new GenerativeCompileError("`providerTool(...)` must receive an inline object literal", filename);
|
|
544
755
|
const existingNames = new Set(object.properties.flatMap((prop) => {
|
|
545
756
|
if (!t.isObjectProperty(prop) && !t.isObjectMethod(prop)) return [];
|
|
@@ -552,7 +763,7 @@ function applyProviderToolConfig(object, execute, filename) {
|
|
|
552
763
|
const name = memberName(prop.key, prop.computed);
|
|
553
764
|
if (!name) throw new GenerativeCompileError("`providerTool(...)` config can only contain static property names", filename);
|
|
554
765
|
if (t.isFunctionExpression(prop.value) || t.isArrowFunctionExpression(prop.value)) throw new GenerativeCompileError("`providerTool(...)` config cannot contain function-valued properties", filename);
|
|
555
|
-
if (existingNames.has(name) || configNames.has(name)) throw new GenerativeCompileError(
|
|
766
|
+
if (existingNames.has(name) || configNames.has(name)) throw new GenerativeCompileError(`\`providerTool(...)\` config${toolName ? ` for "${toolName}"` : ""} duplicates "${name}"`, filename);
|
|
556
767
|
configNames.add(name);
|
|
557
768
|
object.properties.push(prop);
|
|
558
769
|
}
|