@assistant-ui/x-generative-compiler 0.0.5 → 0.0.8
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 +286 -45
- package/dist/compile.js.map +1 -1
- package/dist/package.js +1 -1
- package/package.json +9 -9
- package/src/compile.test.ts +477 -9
- package/src/compile.ts +474 -47
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,UA2CK,cAAA;EA3CL;EA6CV,MAAA,EAAQ,MAAM;;EAEd,QAAA;EA/CkB;EAiDlB,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
|
@@ -28,16 +28,25 @@ const DISTRIBUTION_PACKAGES = [
|
|
|
28
28
|
"@assistant-ui/react-native",
|
|
29
29
|
"@assistant-ui/react-ink"
|
|
30
30
|
];
|
|
31
|
+
/** Package that exports the generative UI runtime split by export condition. */
|
|
32
|
+
const GENERATIVE_UI_PACKAGE = "@assistant-ui/react-generative-ui";
|
|
31
33
|
/**
|
|
32
34
|
* The class whose instances expose split-by-condition tools (`present()`,
|
|
33
35
|
* `promptUser()`). A toolkit entry that calls a method on one of these passes
|
|
34
36
|
* through untouched — the library, not this compiler, routes its halves.
|
|
35
37
|
*/
|
|
36
38
|
const GENERATIVE_FACTORY = "JSONGenerativeUI";
|
|
39
|
+
/**
|
|
40
|
+
* The factory producing an interactable's complete tool entry. Unlike
|
|
41
|
+
* `JSONGenerativeUI`, its package has no per-target builds, so this compiler
|
|
42
|
+
* splits the inline config: the client keeps `render`, the server drops it.
|
|
43
|
+
* The factory's own `execute` is internal and client-safe (frontend tool).
|
|
44
|
+
*/
|
|
45
|
+
const INTERACTABLE_TOOL_FACTORY = "unstable_interactableTool";
|
|
37
46
|
/** Thrown when a `"use generative"` file violates an authoring constraint. */
|
|
38
47
|
var GenerativeCompileError = class extends Error {
|
|
39
48
|
constructor(message, filename) {
|
|
40
|
-
super(`[assistant-ui/
|
|
49
|
+
super(`[assistant-ui/use-generative]${filename ? ` ${filename}:` : ""} ${message}`);
|
|
41
50
|
this.name = "GenerativeCompileError";
|
|
42
51
|
}
|
|
43
52
|
};
|
|
@@ -56,7 +65,89 @@ function isGenerativeModule(code) {
|
|
|
56
65
|
i = end + 2;
|
|
57
66
|
} else break;
|
|
58
67
|
}
|
|
59
|
-
|
|
68
|
+
const quote = code[i];
|
|
69
|
+
if (quote !== "\"" && quote !== "'") return false;
|
|
70
|
+
const directiveStart = i + 1;
|
|
71
|
+
if (!code.startsWith("use generative", directiveStart)) return false;
|
|
72
|
+
const directiveEnd = directiveStart + DIRECTIVE.length;
|
|
73
|
+
if (code[directiveEnd] !== quote) return false;
|
|
74
|
+
return hasDirectiveTerminator(code, directiveEnd + 1);
|
|
75
|
+
}
|
|
76
|
+
function hasDirectiveTerminator(code, start) {
|
|
77
|
+
let i = start;
|
|
78
|
+
let sawLineTerminator = false;
|
|
79
|
+
for (;;) {
|
|
80
|
+
if (i >= code.length) return true;
|
|
81
|
+
const char = code.charCodeAt(i);
|
|
82
|
+
if (isSemicolon(char)) return true;
|
|
83
|
+
if (isLineTerminator(char)) {
|
|
84
|
+
sawLineTerminator = true;
|
|
85
|
+
i++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (code.startsWith("//", i)) {
|
|
89
|
+
const lineEnd = nextLineTerminatorIndex(code, i + 2);
|
|
90
|
+
if (lineEnd === -1) return true;
|
|
91
|
+
sawLineTerminator = true;
|
|
92
|
+
i = lineEnd + 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (code.startsWith("/*", i)) {
|
|
96
|
+
const end = code.indexOf("*/", i + 2);
|
|
97
|
+
if (end === -1) return false;
|
|
98
|
+
sawLineTerminator ||= containsLineTerminator(code, i + 2, end);
|
|
99
|
+
i = end + 2;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (/\s/.test(code[i])) {
|
|
103
|
+
i++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
return sawLineTerminator && !startsExpressionContinuation(code, i);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function nextLineTerminatorIndex(code, start) {
|
|
110
|
+
for (let i = start; i < code.length; i++) if (isLineTerminator(code.charCodeAt(i))) return i;
|
|
111
|
+
return -1;
|
|
112
|
+
}
|
|
113
|
+
function containsLineTerminator(code, start, end) {
|
|
114
|
+
for (let i = start; i < end; i++) if (isLineTerminator(code.charCodeAt(i))) return true;
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
function startsExpressionContinuation(code, start) {
|
|
118
|
+
switch (code[start]) {
|
|
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 "[":
|
|
136
|
+
case "(":
|
|
137
|
+
case "`": return true;
|
|
138
|
+
}
|
|
139
|
+
return startsKeywordContinuation(code, start, "as") || startsKeywordContinuation(code, start, "in") || startsKeywordContinuation(code, start, "instanceof") || startsKeywordContinuation(code, start, "satisfies");
|
|
140
|
+
}
|
|
141
|
+
function startsKeywordContinuation(code, start, keyword) {
|
|
142
|
+
if (!code.startsWith(keyword, start)) return false;
|
|
143
|
+
const next = code[start + keyword.length];
|
|
144
|
+
return next === void 0 || !/[\p{ID_Continue}$]/u.test(next);
|
|
145
|
+
}
|
|
146
|
+
function isSemicolon(char) {
|
|
147
|
+
return char === 59;
|
|
148
|
+
}
|
|
149
|
+
function isLineTerminator(char) {
|
|
150
|
+
return char === 10 || char === 13 || char === 8232 || char === 8233;
|
|
60
151
|
}
|
|
61
152
|
/**
|
|
62
153
|
* Rewrites a `"use generative"` module for a single build target, keeping only the
|
|
@@ -76,7 +167,8 @@ function compileGenerative(code, options) {
|
|
|
76
167
|
ensureCompilerCompatibleWithCore(ast, filename);
|
|
77
168
|
ensureDefaultExport(ast, filename);
|
|
78
169
|
const generativeInstances = collectGenerativeInstances(ast);
|
|
79
|
-
const
|
|
170
|
+
const interactableToolImports = collectInteractableToolImports(ast);
|
|
171
|
+
const toolkitSpreadNames = collectToolkitSpreadNames(ast, filename, createToolkitNameContext());
|
|
80
172
|
const flags = {
|
|
81
173
|
keptRender: false,
|
|
82
174
|
keptBackendExecute: false
|
|
@@ -93,7 +185,7 @@ function compileGenerative(code, options) {
|
|
|
93
185
|
}
|
|
94
186
|
if (t.isIdentifier(callee, { name: TOOLKIT_WRAPPER })) {
|
|
95
187
|
if (!object) throw new GenerativeCompileError(`${TOOLKIT_WRAPPER}() takes an inline object literal of tools`, filename);
|
|
96
|
-
compileToolkit(object, target, generativeInstances,
|
|
188
|
+
compileToolkit(object, target, generativeInstances, interactableToolImports, toolkitSpreadNames, flags, filename);
|
|
97
189
|
path.replaceWith(object);
|
|
98
190
|
path.skip();
|
|
99
191
|
}
|
|
@@ -234,17 +326,56 @@ function unwrapToCall(node, name) {
|
|
|
234
326
|
*/
|
|
235
327
|
function collectGenerativeInstances(ast) {
|
|
236
328
|
const names = /* @__PURE__ */ new Set();
|
|
329
|
+
const generativeFactories = collectGenerativeFactoryImports(ast);
|
|
237
330
|
for (const statement of ast.program.body) {
|
|
238
|
-
|
|
239
|
-
|
|
331
|
+
const variableDeclaration = t.isVariableDeclaration(statement) ? statement : t.isExportNamedDeclaration(statement) && t.isVariableDeclaration(statement.declaration) ? statement.declaration : null;
|
|
332
|
+
if (!variableDeclaration) continue;
|
|
333
|
+
for (const declaration of variableDeclaration.declarations) {
|
|
240
334
|
const { id, init } = declaration;
|
|
241
|
-
if (t.isIdentifier(id) && t.isNewExpression(init) && t.isIdentifier(init.callee
|
|
335
|
+
if (t.isIdentifier(id) && t.isNewExpression(init) && t.isIdentifier(init.callee) && generativeFactories.has(init.callee.name)) names.add(id.name);
|
|
242
336
|
}
|
|
243
337
|
}
|
|
244
338
|
return names;
|
|
245
339
|
}
|
|
340
|
+
function collectGenerativeFactoryImports(ast) {
|
|
341
|
+
const names = /* @__PURE__ */ new Set();
|
|
342
|
+
for (const statement of ast.program.body) {
|
|
343
|
+
if (!t.isImportDeclaration(statement) || statement.source.value !== GENERATIVE_UI_PACKAGE) continue;
|
|
344
|
+
for (const specifier of statement.specifiers) if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported, { name: GENERATIVE_FACTORY })) names.add(specifier.local.name);
|
|
345
|
+
}
|
|
346
|
+
return names;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Collects the local names `unstable_interactableTool` is imported under from a
|
|
350
|
+
* distribution package, so toolkit entries calling it can be recognized
|
|
351
|
+
* (and a same-named local function can't smuggle an arbitrary call through).
|
|
352
|
+
*/
|
|
353
|
+
function collectInteractableToolImports(ast) {
|
|
354
|
+
const names = /* @__PURE__ */ new Set();
|
|
355
|
+
for (const statement of ast.program.body) {
|
|
356
|
+
if (!t.isImportDeclaration(statement)) continue;
|
|
357
|
+
if (!packageNameFromSpecifier(statement.source.value)) continue;
|
|
358
|
+
for (const specifier of statement.specifiers) if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported, { name: INTERACTABLE_TOOL_FACTORY })) names.add(specifier.local.name);
|
|
359
|
+
}
|
|
360
|
+
return names;
|
|
361
|
+
}
|
|
362
|
+
function createToolkitNameContext() {
|
|
363
|
+
return {
|
|
364
|
+
importedToolkitNamesByFile: /* @__PURE__ */ new Map(),
|
|
365
|
+
resolvingImportedToolkitNames: /* @__PURE__ */ new Set()
|
|
366
|
+
};
|
|
367
|
+
}
|
|
246
368
|
/**
|
|
247
|
-
*
|
|
369
|
+
* The inline config of an `unstable_interactableTool({ ... })` toolkit entry, or `null`
|
|
370
|
+
* when the entry is some other expression.
|
|
371
|
+
*/
|
|
372
|
+
function interactableToolConfig(value, imports) {
|
|
373
|
+
return t.isCallExpression(value) && t.isIdentifier(value.callee) && imports.has(value.callee.name) && t.isObjectExpression(value.arguments[0]) ? value.arguments[0] : null;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Toolkit identifiers that are safe to spread into a `defineToolkit({ ... })`,
|
|
377
|
+
* paired with the static tool names they contain. A `null` name list means the
|
|
378
|
+
* spread is safe, but its names are not statically known for duplicate checks.
|
|
248
379
|
*
|
|
249
380
|
* Two kinds qualify:
|
|
250
381
|
*
|
|
@@ -257,30 +388,38 @@ function collectGenerativeInstances(ast) {
|
|
|
257
388
|
* export crosses the generative-module boundary, so named imports don't
|
|
258
389
|
* qualify — they would be `undefined` once that module is build-split.
|
|
259
390
|
*/
|
|
260
|
-
function
|
|
261
|
-
const
|
|
262
|
-
const
|
|
391
|
+
function collectToolkitSpreadNames(ast, filename, context) {
|
|
392
|
+
const spreadNames = /* @__PURE__ */ new Map();
|
|
393
|
+
const localToolkitCalls = /* @__PURE__ */ new Map();
|
|
263
394
|
for (const statement of ast.program.body) {
|
|
264
395
|
if (t.isVariableDeclaration(statement)) {
|
|
265
396
|
for (const declaration of statement.declarations) {
|
|
266
397
|
const { id, init } = declaration;
|
|
267
|
-
if (t.isIdentifier(id) && init
|
|
398
|
+
if (t.isIdentifier(id) && init) {
|
|
399
|
+
const toolkitCall = unwrapToToolkitCall(init);
|
|
400
|
+
if (toolkitCall) localToolkitCalls.set(id.name, toolkitCall);
|
|
401
|
+
}
|
|
268
402
|
}
|
|
269
403
|
continue;
|
|
270
404
|
}
|
|
271
405
|
if (t.isImportDeclaration(statement)) {
|
|
272
406
|
const defaultSpecifier = statement.specifiers.find((specifier) => t.isImportDefaultSpecifier(specifier));
|
|
273
407
|
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);
|
|
408
|
+
const names = getGenerativeImportToolkitNames(statement.source.value, filename, context);
|
|
409
|
+
if (names !== void 0) spreadNames.set(defaultSpecifier.local.name, names);
|
|
281
410
|
}
|
|
282
411
|
}
|
|
283
|
-
|
|
412
|
+
const resolveLocal = (name) => {
|
|
413
|
+
if (spreadNames.has(name)) return spreadNames.get(name);
|
|
414
|
+
const call = localToolkitCalls.get(name);
|
|
415
|
+
if (!call) return void 0;
|
|
416
|
+
const object = t.isObjectExpression(call.arguments[0]) ? call.arguments[0] : null;
|
|
417
|
+
const publicNames = uniqueToolkitNames(object ? collectToolkitObjectNames(object, spreadNames) : null);
|
|
418
|
+
spreadNames.set(name, publicNames);
|
|
419
|
+
return publicNames;
|
|
420
|
+
};
|
|
421
|
+
for (const name of localToolkitCalls.keys()) resolveLocal(name);
|
|
422
|
+
return spreadNames;
|
|
284
423
|
}
|
|
285
424
|
const MODULE_EXTENSIONS = [
|
|
286
425
|
".ts",
|
|
@@ -293,29 +432,64 @@ const MODULE_EXTENSIONS = [
|
|
|
293
432
|
".cjs"
|
|
294
433
|
];
|
|
295
434
|
/** Extensions a specifier may carry that actually map to a TS source file. */
|
|
296
|
-
const REWRITABLE_JS_EXTENSIONS = new Set([
|
|
435
|
+
const REWRITABLE_JS_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
297
436
|
".js",
|
|
298
437
|
".jsx",
|
|
299
438
|
".mjs",
|
|
300
439
|
".cjs"
|
|
301
440
|
]);
|
|
302
441
|
/**
|
|
303
|
-
*
|
|
304
|
-
* Relative specifiers and `tsconfig` path aliases
|
|
305
|
-
* anything else (a bare package, an unresolvable
|
|
306
|
-
* non-generative, and thus an unsafe spread.
|
|
442
|
+
* Reads the static tool names from the default export of an imported
|
|
443
|
+
* `"use generative"` module. Relative specifiers and `tsconfig` path aliases
|
|
444
|
+
* (e.g. `@/tools`) are resolved; anything else (a bare package, an unresolvable
|
|
445
|
+
* alias) is treated as non-generative, and thus an unsafe spread.
|
|
307
446
|
*/
|
|
308
|
-
function
|
|
447
|
+
function getGenerativeImportToolkitNames(source, filename, context) {
|
|
309
448
|
const cleanFilename = cleanAbsoluteFilename(filename);
|
|
310
|
-
if (!cleanFilename) return
|
|
449
|
+
if (!cleanFilename) return void 0;
|
|
311
450
|
const resolved = resolveImportedModuleFile(source, cleanFilename);
|
|
312
|
-
if (!resolved) return
|
|
451
|
+
if (!resolved) return void 0;
|
|
452
|
+
if (context.importedToolkitNamesByFile.has(resolved)) return context.importedToolkitNamesByFile.get(resolved);
|
|
453
|
+
if (context.resolvingImportedToolkitNames.has(resolved)) return null;
|
|
454
|
+
let code;
|
|
313
455
|
try {
|
|
314
|
-
|
|
456
|
+
code = readFileSync(resolved, "utf8");
|
|
315
457
|
} catch {
|
|
316
|
-
|
|
458
|
+
context.importedToolkitNamesByFile.set(resolved, void 0);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (!isGenerativeModule(code)) {
|
|
462
|
+
context.importedToolkitNamesByFile.set(resolved, void 0);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
context.resolvingImportedToolkitNames.add(resolved);
|
|
466
|
+
try {
|
|
467
|
+
const names = getDefaultExportToolkitNames(parse(code, {
|
|
468
|
+
sourceType: "module",
|
|
469
|
+
plugins: [
|
|
470
|
+
"typescript",
|
|
471
|
+
"jsx",
|
|
472
|
+
"explicitResourceManagement"
|
|
473
|
+
]
|
|
474
|
+
}), resolved, context);
|
|
475
|
+
context.importedToolkitNamesByFile.set(resolved, names);
|
|
476
|
+
return names;
|
|
477
|
+
} catch (error) {
|
|
478
|
+
if (error instanceof GenerativeCompileError) throw error;
|
|
479
|
+
context.importedToolkitNamesByFile.set(resolved, null);
|
|
480
|
+
return null;
|
|
481
|
+
} finally {
|
|
482
|
+
context.resolvingImportedToolkitNames.delete(resolved);
|
|
317
483
|
}
|
|
318
484
|
}
|
|
485
|
+
function getDefaultExportToolkitNames(ast, filename, context) {
|
|
486
|
+
const def = ast.program.body.find((stmt) => t.isExportDefaultDeclaration(stmt));
|
|
487
|
+
if (!def) return null;
|
|
488
|
+
const toolkitCall = unwrapToToolkitCall(def.declaration);
|
|
489
|
+
if (!toolkitCall || !t.isObjectExpression(toolkitCall.arguments[0])) return null;
|
|
490
|
+
const spreadNames = collectToolkitSpreadNames(ast, filename, context);
|
|
491
|
+
return uniqueToolkitNames(collectToolkitObjectNames(toolkitCall.arguments[0], spreadNames));
|
|
492
|
+
}
|
|
319
493
|
/** Resolves an import specifier (relative or `tsconfig`-aliased) to a file on disk. */
|
|
320
494
|
function resolveImportedModuleFile(source, fromFilename) {
|
|
321
495
|
if (source.startsWith(".")) return resolveModuleFileAtPath(nodePath.resolve(nodePath.dirname(fromFilename), source));
|
|
@@ -456,13 +630,64 @@ function parseJsonc(text) {
|
|
|
456
630
|
function unwrapToToolkitCall(node) {
|
|
457
631
|
return unwrapToCall(node, TOOLKIT_WRAPPER) ?? unwrapToCall(node, MCP_TOOLKIT_WRAPPER);
|
|
458
632
|
}
|
|
459
|
-
function
|
|
460
|
-
|
|
633
|
+
function collectToolkitObjectNames(object, toolkitSpreadNames) {
|
|
634
|
+
const names = [];
|
|
635
|
+
for (const entry of object.properties) {
|
|
636
|
+
const entryNames = toolkitEntryNames(entry, toolkitSpreadNames);
|
|
637
|
+
if (!entryNames) return null;
|
|
638
|
+
names.push(...entryNames);
|
|
639
|
+
}
|
|
640
|
+
return names;
|
|
641
|
+
}
|
|
642
|
+
function uniqueToolkitNames(names) {
|
|
643
|
+
return names ? [...new Set(names)] : names;
|
|
644
|
+
}
|
|
645
|
+
function toolkitEntryNames(entry, toolkitSpreadNames) {
|
|
646
|
+
if (t.isSpreadElement(entry)) {
|
|
647
|
+
if (t.isIdentifier(entry.argument)) return toolkitSpreadNames.get(entry.argument.name) ?? null;
|
|
648
|
+
const directMcpToolkit = unwrapToCall(entry.argument, MCP_TOOLKIT_WRAPPER);
|
|
649
|
+
if (directMcpToolkit && t.isObjectExpression(directMcpToolkit.arguments[0])) return collectToolkitObjectNames(directMcpToolkit.arguments[0], toolkitSpreadNames);
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
if (t.isObjectProperty(entry) || t.isObjectMethod(entry)) {
|
|
653
|
+
const name = memberName(entry.key, entry.computed);
|
|
654
|
+
return name ? [name] : [];
|
|
655
|
+
}
|
|
656
|
+
return [];
|
|
657
|
+
}
|
|
658
|
+
function warnDuplicateToolkitNames(object, toolkitSpreadNames, filename) {
|
|
659
|
+
const names = collectToolkitObjectNames(object, toolkitSpreadNames);
|
|
660
|
+
if (!names) return;
|
|
661
|
+
const seen = /* @__PURE__ */ new Set();
|
|
662
|
+
const warned = /* @__PURE__ */ new Set();
|
|
663
|
+
for (const name of names) {
|
|
664
|
+
if (seen.has(name)) {
|
|
665
|
+
if (!warned.has(name)) {
|
|
666
|
+
console.warn(new GenerativeCompileError(`Duplicate tool name "${name}" while composing toolkits. JavaScript object spread keeps the last definition.`, filename).message);
|
|
667
|
+
warned.add(name);
|
|
668
|
+
}
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
seen.add(name);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function isSafeToolkitSpread(entry, toolkitSpreadNames) {
|
|
675
|
+
if (t.isIdentifier(entry.argument)) return toolkitSpreadNames.has(entry.argument.name);
|
|
461
676
|
const directMcpToolkit = unwrapToCall(entry.argument, MCP_TOOLKIT_WRAPPER);
|
|
462
677
|
return !!directMcpToolkit && t.isObjectExpression(directMcpToolkit.arguments[0]);
|
|
463
678
|
}
|
|
679
|
+
function genericUnsafeToolkitEntryMessage() {
|
|
680
|
+
return "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";
|
|
681
|
+
}
|
|
682
|
+
function describeUnsafeToolkitEntry(entry) {
|
|
683
|
+
if (!t.isObjectProperty(entry)) return genericUnsafeToolkitEntryMessage();
|
|
684
|
+
const toolName = memberName(entry.key, entry.computed);
|
|
685
|
+
const raw = entryRawValue(entry);
|
|
686
|
+
if (!toolName || !raw) return genericUnsafeToolkitEntryMessage();
|
|
687
|
+
return `tool "${toolName}" cannot be \`${generate(raw).code}\`; use an inline object literal (\`${toolName}: { ... }\`) or a compiler-visible toolkit spread / generative tool so its \`execute\` can be routed`;
|
|
688
|
+
}
|
|
464
689
|
/** The `JSONGenerativeUI` methods that produce a split-by-condition tool. */
|
|
465
|
-
const GENERATIVE_TOOL_METHODS = new Set(["present", "promptUser"]);
|
|
690
|
+
const GENERATIVE_TOOL_METHODS = /* @__PURE__ */ new Set(["present", "promptUser"]);
|
|
466
691
|
/**
|
|
467
692
|
* Whether a toolkit entry's value is a call to a tool-producing method on a
|
|
468
693
|
* collected `JSONGenerativeUI` instance (`generative.present()`), which passes
|
|
@@ -493,12 +718,13 @@ function compileComponents(object, target, flags, filename) {
|
|
|
493
718
|
* `generative.present()` passes through, the library having already split it.
|
|
494
719
|
* Mutates the object in place and records outcomes in {@link TargetFlags}.
|
|
495
720
|
*/
|
|
496
|
-
function compileToolkit(object, target, instances,
|
|
721
|
+
function compileToolkit(object, target, instances, interactableToolImports, toolkitSpreadNames, flags, filename) {
|
|
722
|
+
if (target === "client") warnDuplicateToolkitNames(object, toolkitSpreadNames, filename);
|
|
497
723
|
const nextProperties = [];
|
|
498
724
|
for (const entry of object.properties) {
|
|
499
725
|
const value = entryValue(entry);
|
|
500
726
|
if (!value) {
|
|
501
|
-
if (t.isSpreadElement(entry) && isSafeToolkitSpread(entry,
|
|
727
|
+
if (t.isSpreadElement(entry) && isSafeToolkitSpread(entry, toolkitSpreadNames)) {
|
|
502
728
|
nextProperties.push(entry);
|
|
503
729
|
continue;
|
|
504
730
|
}
|
|
@@ -507,19 +733,27 @@ function compileToolkit(object, target, instances, safeToolkitSpreads, flags, fi
|
|
|
507
733
|
nextProperties.push(entry);
|
|
508
734
|
continue;
|
|
509
735
|
}
|
|
510
|
-
|
|
736
|
+
const config = raw && interactableToolConfig(raw, interactableToolImports);
|
|
737
|
+
if (config) {
|
|
738
|
+
if (target === "client") flags.keptRender = true;
|
|
739
|
+
else removeMember(config, "render");
|
|
740
|
+
nextProperties.push(entry);
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
throw new GenerativeCompileError(describeUnsafeToolkitEntry(entry), filename);
|
|
511
744
|
}
|
|
745
|
+
const toolName = t.isObjectProperty(entry) ? memberName(entry.key, entry.computed) : void 0;
|
|
512
746
|
const execute = findMember(value, "execute");
|
|
513
747
|
const isStub = execute ? executeIsStubTool(execute) : false;
|
|
514
748
|
const isExternal = execute ? executeIsExternalTool(execute) : false;
|
|
515
|
-
const type = inferToolType(value, filename);
|
|
749
|
+
const type = inferToolType(value, toolName, filename);
|
|
516
750
|
const hasRender = !!findMember(value, "render");
|
|
517
751
|
const hasRenderText = !!findMember(value, "renderText");
|
|
518
|
-
if (type === "frontend" && !hasRender && !hasRenderText) throw new GenerativeCompileError("
|
|
519
|
-
if (type === "human" && !hasRender) throw new GenerativeCompileError("
|
|
520
|
-
if (type === "provider" && execute) applyProviderToolConfig(value, execute, filename);
|
|
752
|
+
if (type === "frontend" && !hasRender && !hasRenderText) throw new GenerativeCompileError(`${typedToolSubject("frontend", toolName)} must declare a \`render\` or \`renderText\` (it has no server execute to show otherwise)`, filename);
|
|
753
|
+
if (type === "human" && !hasRender) throw new GenerativeCompileError(`${typedToolSubject("human", toolName)} must declare a \`render\` so it can collect input`, filename);
|
|
754
|
+
if (type === "provider" && execute) applyProviderToolConfig(value, execute, toolName, filename);
|
|
521
755
|
if (isExternal) {
|
|
522
|
-
if (!hasRender && !hasRenderText) throw new GenerativeCompileError("
|
|
756
|
+
if (!hasRender && !hasRenderText) throw new GenerativeCompileError(`${typedToolSubject("external", toolName)} must declare a \`render\` or \`renderText\` (assistant-ui only renders calls for tools defined elsewhere)`, filename);
|
|
523
757
|
if (target === "server") continue;
|
|
524
758
|
stripExternalToolMetadata(value);
|
|
525
759
|
}
|
|
@@ -539,7 +773,7 @@ function compileToolkit(object, target, instances, safeToolkitSpreads, flags, fi
|
|
|
539
773
|
}
|
|
540
774
|
object.properties = nextProperties;
|
|
541
775
|
}
|
|
542
|
-
function applyProviderToolConfig(object, execute, filename) {
|
|
776
|
+
function applyProviderToolConfig(object, execute, toolName, filename) {
|
|
543
777
|
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
778
|
const existingNames = new Set(object.properties.flatMap((prop) => {
|
|
545
779
|
if (!t.isObjectProperty(prop) && !t.isObjectMethod(prop)) return [];
|
|
@@ -552,7 +786,7 @@ function applyProviderToolConfig(object, execute, filename) {
|
|
|
552
786
|
const name = memberName(prop.key, prop.computed);
|
|
553
787
|
if (!name) throw new GenerativeCompileError("`providerTool(...)` config can only contain static property names", filename);
|
|
554
788
|
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(
|
|
789
|
+
if (existingNames.has(name) || configNames.has(name)) throw new GenerativeCompileError(`\`providerTool(...)\` config${toolName ? ` for "${toolName}"` : ""} duplicates "${name}"`, filename);
|
|
556
790
|
configNames.add(name);
|
|
557
791
|
object.properties.push(prop);
|
|
558
792
|
}
|
|
@@ -615,15 +849,22 @@ function stripUseClient(member) {
|
|
|
615
849
|
* The loader writes the result back as a `type` field (see {@link setToolType})
|
|
616
850
|
* so the runtime keeps it.
|
|
617
851
|
*/
|
|
618
|
-
function inferToolType(object, filename) {
|
|
852
|
+
function inferToolType(object, toolName, filename) {
|
|
619
853
|
const execute = findMember(object, "execute");
|
|
620
|
-
if (!execute) throw new GenerativeCompileError(
|
|
854
|
+
if (!execute) throw new GenerativeCompileError(`${toolSubject(toolName)} must declare an \`execute\`; use \`humanTool()\` for a human-in-the-loop tool`, filename);
|
|
621
855
|
if (executeIsHumanTool(execute)) return "human";
|
|
622
856
|
if (executeIsProviderTool(execute)) return "provider";
|
|
623
857
|
if (executeIsStubTool(execute)) return "frontend";
|
|
624
858
|
if (executeIsExternalTool(execute)) return "backend";
|
|
625
859
|
return executeIsClient(execute) ? "frontend" : "backend";
|
|
626
860
|
}
|
|
861
|
+
function toolSubject(toolName) {
|
|
862
|
+
return toolName ? `tool "${toolName}"` : "every tool";
|
|
863
|
+
}
|
|
864
|
+
function typedToolSubject(type, toolName) {
|
|
865
|
+
if (toolName) return `${type} tool "${toolName}"`;
|
|
866
|
+
return `${/^[aeiou]/i.test(type) ? "an" : "a"} ${type} tool`;
|
|
867
|
+
}
|
|
627
868
|
function stripExternalToolMetadata(object) {
|
|
628
869
|
removeMember(object, "description");
|
|
629
870
|
removeMember(object, "parameters");
|