@webmcp-auto-ui/agent 2.5.24 → 2.5.26
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/package.json +1 -1
- package/src/autoui-server.ts +17 -0
- package/src/diagnostics.ts +6 -6
- package/src/discovery-cache.ts +17 -3
- package/src/index.ts +3 -3
- package/src/loop.ts +27 -19
- package/src/providers/wasm.ts +184 -330
- package/src/recipes/_generated.ts +273 -0
- package/src/recipes/canary-data.md +50 -0
- package/src/recipes/canary-display.md +99 -0
- package/src/recipes/canary-middle.md +32 -0
- package/src/recipes/hummingbird-data.md +32 -0
- package/src/recipes/hummingbird-display.md +36 -0
- package/src/recipes/hummingbird-middle.md +18 -0
- package/src/tool-layers.ts +303 -31
- package/src/types.ts +6 -1
- package/tests/loop.test.ts +2 -2
- package/src/providers/gemma.worker.legacy.ts +0 -123
- package/src/providers/litert.worker.ts +0 -294
- package/src/recipes/widgets/actions.md +0 -28
- package/src/recipes/widgets/alert.md +0 -27
- package/src/recipes/widgets/cards.md +0 -41
- package/src/recipes/widgets/carousel.md +0 -39
- package/src/recipes/widgets/chart-rich.md +0 -51
- package/src/recipes/widgets/chart.md +0 -32
- package/src/recipes/widgets/code.md +0 -21
- package/src/recipes/widgets/d3.md +0 -36
- package/src/recipes/widgets/data-table.md +0 -46
- package/src/recipes/widgets/gallery.md +0 -39
- package/src/recipes/widgets/grid-data.md +0 -57
- package/src/recipes/widgets/hemicycle.md +0 -43
- package/src/recipes/widgets/js-sandbox.md +0 -32
- package/src/recipes/widgets/json-viewer.md +0 -27
- package/src/recipes/widgets/kv.md +0 -31
- package/src/recipes/widgets/list.md +0 -24
- package/src/recipes/widgets/log.md +0 -39
- package/src/recipes/widgets/map.md +0 -49
- package/src/recipes/widgets/profile.md +0 -49
- package/src/recipes/widgets/recipe-browser.md +0 -102
- package/src/recipes/widgets/sankey.md +0 -54
- package/src/recipes/widgets/stat-card.md +0 -43
- package/src/recipes/widgets/stat.md +0 -35
- package/src/recipes/widgets/tags.md +0 -30
- package/src/recipes/widgets/text.md +0 -19
- package/src/recipes/widgets/timeline.md +0 -38
- package/src/recipes/widgets/trombinoscope.md +0 -39
package/package.json
CHANGED
package/src/autoui-server.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
5
|
import { createWebMcpServer, parseFrontmatter } from '@webmcp-auto-ui/core';
|
|
6
|
+
import { RAW_RECIPES } from './recipes/_generated.js';
|
|
6
7
|
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
// Inline recipes (frontmatter + body)
|
|
@@ -1007,6 +1008,22 @@ for (const recipe of RECIPES) {
|
|
|
1007
1008
|
autoui.registerWidget(recipe, undefined);
|
|
1008
1009
|
}
|
|
1009
1010
|
|
|
1011
|
+
// Register flow recipes (multi-step procedures) from the global recipe registry
|
|
1012
|
+
// that declare this server (autoui) in their frontmatter.
|
|
1013
|
+
for (const [key, rawMd] of Object.entries(RAW_RECIPES)) {
|
|
1014
|
+
const { frontmatter } = parseFrontmatter(rawMd);
|
|
1015
|
+
const servers = (Array.isArray(frontmatter.servers) ? frontmatter.servers : []) as string[];
|
|
1016
|
+
if (servers.map((s) => s.toLowerCase()).includes('autoui')) {
|
|
1017
|
+
try {
|
|
1018
|
+
autoui.registerRecipe(rawMd);
|
|
1019
|
+
} catch (e) {
|
|
1020
|
+
// Skip malformed recipes (missing id/name) — log for debugging
|
|
1021
|
+
// eslint-disable-next-line no-console
|
|
1022
|
+
console.warn(`[autoui] Failed to register flow recipe "${key}":`, e);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1010
1027
|
// Expose recipe summaries to the UI browser
|
|
1011
1028
|
const parsedRecipes = RECIPES.map((md) => {
|
|
1012
1029
|
const { frontmatter, body } = parseFrontmatter(md);
|
package/src/diagnostics.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { ToolLayer } from './tool-layers.js';
|
|
4
4
|
import type { ProviderTool } from './types.js';
|
|
5
|
-
import { sanitizeServerName } from './tool-layers.js';
|
|
5
|
+
import { sanitizeServerName, protocolToken } from './tool-layers.js';
|
|
6
6
|
import { sanitizeSchemaWithReport } from '@webmcp-auto-ui/core';
|
|
7
7
|
import type { JsonSchema, SchemaPatch } from '@webmcp-auto-ui/core';
|
|
8
8
|
|
|
@@ -41,8 +41,8 @@ export function runDiagnostics(
|
|
|
41
41
|
diagnostics.push({
|
|
42
42
|
severity: 'error',
|
|
43
43
|
title: `Bruit dans le prefixe "${prefix}"`,
|
|
44
|
-
detail: `Le serveur "${layer.serverName}" produit un prefixe contenant "${noise.join(', ')}". Les outils auront des noms ambigus comme ${prefix}
|
|
45
|
-
quickFix: `Ajoutez au system prompt:\n"Note: le serveur ${layer.serverName} utilise le prefixe '${prefix}'. Utilisez ${prefix}
|
|
44
|
+
detail: `Le serveur "${layer.serverName}" produit un prefixe contenant "${noise.join(', ')}". Les outils auront des noms ambigus comme ${prefix}_data_list_recipes.`,
|
|
45
|
+
quickFix: `Ajoutez au system prompt:\n"Note: le serveur ${layer.serverName} utilise le prefixe '${prefix}'. Utilisez ${prefix}_data_<tool> ou ${prefix}_ui_<tool>."`,
|
|
46
46
|
codeFix: `Renommer le serveur MCP cote serveur pour ne pas inclure "mcp"/"server" dans le nom.`,
|
|
47
47
|
});
|
|
48
48
|
}
|
|
@@ -88,14 +88,14 @@ export function runDiagnostics(
|
|
|
88
88
|
// Include discovery tools (search_tools, list_tools) which are generated dynamically
|
|
89
89
|
const toolNames = new Set(tools.map(t => t.name));
|
|
90
90
|
for (const layer of layers) {
|
|
91
|
-
const prefix = `${sanitizeServerName(layer.serverName)}_${layer.protocol}_`;
|
|
91
|
+
const prefix = `${sanitizeServerName(layer.serverName)}_${protocolToken(layer.protocol)}_`;
|
|
92
92
|
toolNames.add(`${prefix}search_tools`);
|
|
93
93
|
toolNames.add(`${prefix}list_tools`);
|
|
94
94
|
toolNames.add(`${prefix}search_recipes`);
|
|
95
95
|
toolNames.add(`${prefix}list_recipes`);
|
|
96
96
|
toolNames.add(`${prefix}get_recipe`);
|
|
97
97
|
}
|
|
98
|
-
const toolPattern = /\b([a-z][a-z0-9_]{2,})_(
|
|
98
|
+
const toolPattern = /\b([a-z][a-z0-9_]{2,})_(data|ui)_([a-z][a-z0-9_]+)\b/g;
|
|
99
99
|
let match;
|
|
100
100
|
while ((match = toolPattern.exec(systemPrompt)) !== null) {
|
|
101
101
|
const fullName = match[0];
|
|
@@ -112,7 +112,7 @@ export function runDiagnostics(
|
|
|
112
112
|
// 5. Strict mode — schemas that were auto-patched
|
|
113
113
|
// Must run on raw (pre-sanitize) schemas; sanitized tools will never show patches.
|
|
114
114
|
// Skipped for Gemma — it doesn't use additionalProperties/strict mode at all.
|
|
115
|
-
if (!skipSchemaChecks) {
|
|
115
|
+
if (!skipSchemaChecks && schemaOptions?.strict) {
|
|
116
116
|
const checkTools = rawTools ?? tools;
|
|
117
117
|
for (const tool of checkTools) {
|
|
118
118
|
const { patches } = sanitizeSchemaWithReport(tool.input_schema as JsonSchema);
|
package/src/discovery-cache.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ProviderTool } from './types.js';
|
|
8
|
+
import type { PipelineTrace } from './pipeline-trace.js';
|
|
8
9
|
|
|
9
10
|
/** Tool names that are resolved locally from cache — hidden from user-facing browsers. */
|
|
10
11
|
export const DISCOVERY_TOOL_NAMES = new Set(['list_recipes', 'search_recipes', 'get_recipe', 'list_tools', 'search_tools']);
|
|
@@ -100,13 +101,18 @@ export class DiscoveryCache {
|
|
|
100
101
|
serverPrefix: string,
|
|
101
102
|
realToolName: string,
|
|
102
103
|
params: Record<string, unknown>,
|
|
104
|
+
trace?: PipelineTrace,
|
|
103
105
|
): string | null {
|
|
104
106
|
const cache = this.servers.get(serverPrefix);
|
|
105
107
|
if (!cache) return null;
|
|
106
108
|
|
|
107
109
|
switch (realToolName) {
|
|
108
110
|
case 'search_recipes': {
|
|
109
|
-
const
|
|
111
|
+
const rawQuery = params.query ?? params.q ?? params.keyword ?? params.search;
|
|
112
|
+
const query = String(rawQuery ?? '').toLowerCase();
|
|
113
|
+
if (rawQuery === undefined && Object.keys(params).length > 0) {
|
|
114
|
+
trace?.push('discovery', realToolName, `unknown param keys: ${Object.keys(params).join(',')} — expected query/q/keyword/search`, 'warn');
|
|
115
|
+
}
|
|
110
116
|
const results = query
|
|
111
117
|
? cache.recipes.filter(r =>
|
|
112
118
|
r.name.toLowerCase().includes(query) ||
|
|
@@ -121,7 +127,11 @@ export class DiscoveryCache {
|
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
case 'get_recipe': {
|
|
124
|
-
const
|
|
130
|
+
const rawKey = params.name ?? params.id ?? params.recipe_id ?? params.key;
|
|
131
|
+
const key = String(rawKey ?? '').toLowerCase();
|
|
132
|
+
if (rawKey === undefined && Object.keys(params).length > 0) {
|
|
133
|
+
trace?.push('discovery', realToolName, `unknown param keys: ${Object.keys(params).join(',')} — expected name/id/recipe_id/key`, 'warn');
|
|
134
|
+
}
|
|
125
135
|
const recipe = cache.recipes.find(r =>
|
|
126
136
|
(r.name?.toLowerCase() === key) ||
|
|
127
137
|
((r as Record<string, unknown>).id as string | undefined)?.toLowerCase() === key
|
|
@@ -131,7 +141,11 @@ export class DiscoveryCache {
|
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
case 'search_tools': {
|
|
134
|
-
const
|
|
144
|
+
const rawQuery = params.query ?? params.q ?? params.keyword ?? params.search;
|
|
145
|
+
const query = String(rawQuery ?? '').toLowerCase();
|
|
146
|
+
if (rawQuery === undefined && Object.keys(params).length > 0) {
|
|
147
|
+
trace?.push('discovery', realToolName, `unknown param keys: ${Object.keys(params).join(',')} — expected query/q/keyword/search`, 'warn');
|
|
148
|
+
}
|
|
135
149
|
const results = query
|
|
136
150
|
? cache.tools.filter(t =>
|
|
137
151
|
t.name.toLowerCase().includes(query) ||
|
package/src/index.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Providers
|
|
4
4
|
export { RemoteLLMProvider } from './providers/remote.js';
|
|
5
5
|
export type { RemoteLLMProviderOptions } from './providers/remote.js';
|
|
6
|
-
export { WasmProvider } from './providers/wasm.js';
|
|
7
|
-
export type { WasmProviderOptions, WasmStatus } from './providers/wasm.js';
|
|
6
|
+
export { WasmProvider, buildGemmaPrompt } from './providers/wasm.js';
|
|
7
|
+
export type { WasmProviderOptions, WasmStatus, BuildGemmaPromptInput } from './providers/wasm.js';
|
|
8
8
|
export { LocalLLMProvider } from './providers/local.js';
|
|
9
9
|
export type { LocalLLMProviderOptions, LocalBackend } from './providers/local.js';
|
|
10
10
|
export { createProvider } from './providers/factory.js';
|
|
@@ -26,7 +26,7 @@ export { autoui, NATIVE_WIDGET_NAMES } from './autoui-server.js';
|
|
|
26
26
|
|
|
27
27
|
// Tool layers
|
|
28
28
|
export { buildToolsFromLayers, buildDiscoveryTools, buildDiscoveryToolsWithAliases, activateServerTools, resolveCanonicalTools, toolAliasMap, buildSystemPromptWithAliases, flattenPathMaps, buildDiscoveryCache } from './tool-layers.js';
|
|
29
|
-
export type { ToolLayer, McpLayer, WebMcpLayer, SystemPromptResult, DiscoveryToolsResult, SchemaTransformOptions, BuildToolsResult } from './tool-layers.js';
|
|
29
|
+
export type { ToolLayer, McpLayer, WebMcpLayer, SystemPromptResult, DiscoveryToolsResult, SchemaTransformOptions, BuildToolsResult, ProviderKind } from './tool-layers.js';
|
|
30
30
|
|
|
31
31
|
// Discovery cache
|
|
32
32
|
export { DiscoveryCache, DISCOVERY_TOOL_NAMES } from './discovery-cache.js';
|
package/src/loop.ts
CHANGED
|
@@ -28,11 +28,18 @@ const DISCOVERY_TOOL_NAMES = new Set([
|
|
|
28
28
|
]);
|
|
29
29
|
|
|
30
30
|
function isDiscoveryTool(prefixedName: string): boolean {
|
|
31
|
-
const match = prefixedName.match(/^.+?_(
|
|
31
|
+
const match = prefixedName.match(/^.+?_(data|ui)_(.+)$/);
|
|
32
32
|
if (!match) return false;
|
|
33
33
|
return DISCOVERY_TOOL_NAMES.has(match[2]);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** Map a tool-name token (data/ui) back to its protocol discriminator (mcp/webmcp). */
|
|
37
|
+
function tokenToProtocol(token: string): 'mcp' | 'webmcp' | null {
|
|
38
|
+
if (token === 'data') return 'mcp';
|
|
39
|
+
if (token === 'ui') return 'webmcp';
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
/**
|
|
37
44
|
* Compress old tool_result blocks in conversation history.
|
|
38
45
|
* IMPORTANT: mutates messages in-place intentionally to reduce memory usage.
|
|
@@ -91,7 +98,6 @@ export interface AgentLoopOptions {
|
|
|
91
98
|
layers?: ToolLayer[];
|
|
92
99
|
maxIterations?: number;
|
|
93
100
|
maxTokens?: number;
|
|
94
|
-
maxTools?: number;
|
|
95
101
|
temperature?: number;
|
|
96
102
|
topK?: number;
|
|
97
103
|
cacheEnabled?: boolean;
|
|
@@ -124,7 +130,6 @@ export async function runAgentLoop(
|
|
|
124
130
|
provider,
|
|
125
131
|
maxIterations = 5,
|
|
126
132
|
maxTokens,
|
|
127
|
-
maxTools,
|
|
128
133
|
temperature,
|
|
129
134
|
topK,
|
|
130
135
|
cacheEnabled = true,
|
|
@@ -179,14 +184,14 @@ export async function runAgentLoop(
|
|
|
179
184
|
if (options.systemPrompt) {
|
|
180
185
|
baseSystemPrompt = options.systemPrompt;
|
|
181
186
|
} else {
|
|
182
|
-
const sp = buildSystemPromptWithAliases(options.layers ?? []
|
|
187
|
+
const sp = buildSystemPromptWithAliases(options.layers ?? [], {
|
|
188
|
+
providerKind: provider.promptKind ?? 'generic',
|
|
189
|
+
});
|
|
183
190
|
baseSystemPrompt = sp.prompt;
|
|
184
191
|
for (const [k, v] of sp.aliasMap) localAliasMap.set(k, v);
|
|
185
192
|
}
|
|
186
193
|
|
|
187
|
-
const systemPrompt =
|
|
188
|
-
? `${baseSystemPrompt}\n\nIMPORTANT: Limit your responses to ${maxTokens} tokens.`
|
|
189
|
-
: baseSystemPrompt;
|
|
194
|
+
const systemPrompt = baseSystemPrompt;
|
|
190
195
|
|
|
191
196
|
const messages: ChatMessage[] = [
|
|
192
197
|
...initialMessages.map(m => ({
|
|
@@ -264,7 +269,7 @@ export async function runAgentLoop(
|
|
|
264
269
|
const t0 = performance.now();
|
|
265
270
|
let streamingText = '';
|
|
266
271
|
const response = await provider.chat(messages, iterationTools, {
|
|
267
|
-
signal, cacheEnabled, system: iterationSystemPrompt, maxTokens,
|
|
272
|
+
signal, cacheEnabled, system: iterationSystemPrompt, maxTokens, temperature, topK,
|
|
268
273
|
onToken: callbacks.onToken ? (token) => {
|
|
269
274
|
callbacks.onToken!(token);
|
|
270
275
|
streamingText += token;
|
|
@@ -332,12 +337,12 @@ export async function runAgentLoop(
|
|
|
332
337
|
// Resolve alias (canonical name → real tool name) if needed
|
|
333
338
|
const resolvedName = localAliasMap.get(name) ?? name;
|
|
334
339
|
|
|
335
|
-
// Parse tool name: {serverName}_{
|
|
336
|
-
const toolMatch = resolvedName.match(/^(.+?)_(
|
|
340
|
+
// Parse tool name: {serverName}_{token}_{toolName} where token ∈ {data, ui}
|
|
341
|
+
const toolMatch = resolvedName.match(/^(.+?)_(data|ui)_(.+)$/);
|
|
337
342
|
|
|
338
343
|
// ── Discovery cache — resolve search/list/get locally if cached ──
|
|
339
344
|
if (discoveryCache && toolMatch) {
|
|
340
|
-
const cached = discoveryCache.resolve(toolMatch[1], toolMatch[3], block.input as Record<string, unknown
|
|
345
|
+
const cached = discoveryCache.resolve(toolMatch[1], toolMatch[3], block.input as Record<string, unknown>, trace);
|
|
341
346
|
if (cached !== null) {
|
|
342
347
|
result = cached;
|
|
343
348
|
// Store + push result, then continue to next tool block
|
|
@@ -355,7 +360,8 @@ export async function runAgentLoop(
|
|
|
355
360
|
// ── Intercept list_tools / search_tools (local pseudo-tools) ──
|
|
356
361
|
// These are read-only discovery operations — do NOT activate the server.
|
|
357
362
|
if (toolMatch && (toolMatch[3] === 'list_tools' || toolMatch[3] === 'search_tools')) {
|
|
358
|
-
const [, serverName,
|
|
363
|
+
const [, serverName, token, pseudoTool] = toolMatch;
|
|
364
|
+
const protocol = tokenToProtocol(token);
|
|
359
365
|
const layer = (options.layers ?? []).find(l => sanitizeServerName(l.serverName) === serverName && l.protocol === protocol);
|
|
360
366
|
if (!layer) {
|
|
361
367
|
result = 'Error: server not found';
|
|
@@ -381,10 +387,11 @@ export async function runAgentLoop(
|
|
|
381
387
|
|
|
382
388
|
// Parse tool name to extract server — activate on first contact
|
|
383
389
|
{
|
|
384
|
-
const activateMatch = resolvedName.match(/^(.+?)_(
|
|
390
|
+
const activateMatch = resolvedName.match(/^(.+?)_(data|ui)_(.+)$/);
|
|
385
391
|
if (activateMatch) {
|
|
386
|
-
const [, serverName,
|
|
387
|
-
const
|
|
392
|
+
const [, serverName, token] = activateMatch;
|
|
393
|
+
const protocol = tokenToProtocol(token);
|
|
394
|
+
const serverKey = `${serverName}_${token}`;
|
|
388
395
|
if (!activatedServers.has(serverKey)) {
|
|
389
396
|
activatedServers.add(serverKey);
|
|
390
397
|
const layer = (options.layers ?? []).find(l => sanitizeServerName(l.serverName) === serverName && l.protocol === protocol);
|
|
@@ -395,10 +402,11 @@ export async function runAgentLoop(
|
|
|
395
402
|
}
|
|
396
403
|
}
|
|
397
404
|
if (!toolMatch) {
|
|
398
|
-
trace.push('dispatch', name, `unknown tool format, expected {source}_{
|
|
399
|
-
result = `Error: unknown tool format "${name}". Expected {source}_{
|
|
405
|
+
trace.push('dispatch', name, `unknown tool format, expected {source}_{token}_{tool} where token in {data,ui}`, 'error');
|
|
406
|
+
result = `Error: unknown tool format "${name}". Expected {source}_{token}_{tool} where token in {data,ui}.`;
|
|
400
407
|
} else {
|
|
401
|
-
const [, serverName,
|
|
408
|
+
const [, serverName, token, realToolName] = toolMatch;
|
|
409
|
+
const protocol = tokenToProtocol(token);
|
|
402
410
|
|
|
403
411
|
// Auto-repair + validate params before dispatch
|
|
404
412
|
let toolInput = block.input as Record<string, unknown>;
|
|
@@ -584,7 +592,7 @@ export async function runAgentLoop(
|
|
|
584
592
|
|
|
585
593
|
// Track iterations without render — widget_display means a render happened
|
|
586
594
|
const renderedThisIteration = toolBlocks.some(b => {
|
|
587
|
-
const match = b.name.match(/^.+?_(
|
|
595
|
+
const match = b.name.match(/^.+?_(data|ui)_(.+)$/);
|
|
588
596
|
return match && match[2] === 'widget_display';
|
|
589
597
|
});
|
|
590
598
|
if (renderedThisIteration) {
|