@webmcp-auto-ui/agent 2.5.19 → 2.5.21
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 +12 -1
- package/src/diagnostics.ts +1 -1
- package/src/discovery-cache.ts +5 -0
- package/src/index.ts +2 -2
- package/src/loop.ts +15 -8
- package/src/providers/remote.ts +2 -2
- package/src/providers/wasm.ts +5 -13
- package/src/recipe-browser.ts +9 -4
- package/src/tool-layers.ts +2 -1
- package/src/types.ts +1 -4
- package/tests/loop.test.ts +11 -11
package/package.json
CHANGED
package/src/autoui-server.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// AutoUI WebMCP Server — built-in UI widgets + canvas/recall tools
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
import { createWebMcpServer } from '@webmcp-auto-ui/core';
|
|
5
|
+
import { createWebMcpServer, parseFrontmatter } from '@webmcp-auto-ui/core';
|
|
6
6
|
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
8
|
// Inline recipes (frontmatter + body)
|
|
@@ -1007,6 +1007,17 @@ for (const recipe of RECIPES) {
|
|
|
1007
1007
|
autoui.registerWidget(recipe, undefined);
|
|
1008
1008
|
}
|
|
1009
1009
|
|
|
1010
|
+
// Expose recipe summaries to the UI browser
|
|
1011
|
+
const parsedRecipes = RECIPES.map((md) => {
|
|
1012
|
+
const { frontmatter, body } = parseFrontmatter(md);
|
|
1013
|
+
return {
|
|
1014
|
+
name: (frontmatter.widget as string) ?? '',
|
|
1015
|
+
description: (frontmatter.description as string) ?? '',
|
|
1016
|
+
body,
|
|
1017
|
+
};
|
|
1018
|
+
}).filter((r) => r.name);
|
|
1019
|
+
autoui.setRecipes(parsedRecipes);
|
|
1020
|
+
|
|
1010
1021
|
// ---------------------------------------------------------------------------
|
|
1011
1022
|
// Custom tool: canvas
|
|
1012
1023
|
// ---------------------------------------------------------------------------
|
package/src/diagnostics.ts
CHANGED
|
@@ -22,7 +22,7 @@ export function runDiagnostics(
|
|
|
22
22
|
layers: ToolLayer[],
|
|
23
23
|
tools: ProviderTool[],
|
|
24
24
|
systemPrompt: string,
|
|
25
|
-
schemaOptions?: { sanitize?: boolean; flatten?: boolean },
|
|
25
|
+
schemaOptions?: { sanitize?: boolean; flatten?: boolean; strict?: boolean },
|
|
26
26
|
/** Original (pre-sanitize) tools — used for check #5 to detect patchable schemas */
|
|
27
27
|
rawTools?: ProviderTool[],
|
|
28
28
|
): Diagnostic[] {
|
package/src/discovery-cache.ts
CHANGED
|
@@ -69,6 +69,11 @@ export class DiscoveryCache {
|
|
|
69
69
|
return this.servers.get(serverPrefix)?.recipes.length ?? 0;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** Get the cached recipes for a specific server prefix. */
|
|
73
|
+
recipesFor(serverPrefix: string): CachedRecipe[] {
|
|
74
|
+
return this.servers.get(serverPrefix)?.recipes ?? [];
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
/** Tool count for a specific server */
|
|
73
78
|
toolCount(serverPrefix: string): number {
|
|
74
79
|
return this.servers.get(serverPrefix)?.tools.length ?? 0;
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { GemmaProvider } from './providers/gemma.js';
|
|
|
17
17
|
export type { GemmaProviderOptions, GemmaStatus } from './providers/gemma.js';
|
|
18
18
|
|
|
19
19
|
// Agent loop
|
|
20
|
-
export { runAgentLoop, toProviderTools,
|
|
20
|
+
export { runAgentLoop, toProviderTools, fromMcpTools, trimConversationHistory } from './loop.js';
|
|
21
21
|
export { buildSystemPrompt } from './tool-layers.js';
|
|
22
22
|
export type { AgentLoopOptions } from './loop.js';
|
|
23
23
|
|
|
@@ -69,7 +69,7 @@ export { ContextRAG, type ContextRAGOptions } from './nano-rag/mod.js';
|
|
|
69
69
|
// Types
|
|
70
70
|
export type {
|
|
71
71
|
RemoteModelId, WasmModelId, LLMId, ModelId,
|
|
72
|
-
ChatMessage, ContentBlock, McpToolDef, ProviderTool,
|
|
72
|
+
ChatMessage, ContentBlock, McpToolDef, ProviderTool,
|
|
73
73
|
LLMProvider, LLMResponse, ToolCall, AgentMetrics, AgentResult, AgentCallbacks,
|
|
74
74
|
Recipe, McpRecipe,
|
|
75
75
|
} from './types.js';
|
package/src/loop.ts
CHANGED
|
@@ -68,8 +68,6 @@ function compressOldToolResults(messages: ChatMessage[], previewSize: number, re
|
|
|
68
68
|
|
|
69
69
|
// Re-export toProviderTools
|
|
70
70
|
export { toProviderTools };
|
|
71
|
-
/** @deprecated Use toProviderTools */
|
|
72
|
-
export const mcpToolsToAnthropic = toProviderTools;
|
|
73
71
|
|
|
74
72
|
function truncateResult(result: string, maxLen: number = MAX_RESULT_LEN): string {
|
|
75
73
|
if (result.length <= maxLen) return result;
|
|
@@ -113,6 +111,8 @@ export interface AgentLoopOptions {
|
|
|
113
111
|
discoveryCache?: DiscoveryCache;
|
|
114
112
|
/** Nano-RAG context compaction — ingest tool results, query before LLM calls */
|
|
115
113
|
contextRAG?: import('./nano-rag/context-rag.js').ContextRAG;
|
|
114
|
+
/** Size of inline residue (chars) left in tool_result after nano-RAG ingestion. 0 = stub only. Default: 200 */
|
|
115
|
+
ragResidueSize?: number;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
export async function runAgentLoop(
|
|
@@ -137,6 +137,7 @@ export async function runAgentLoop(
|
|
|
137
137
|
schemaOptions,
|
|
138
138
|
discoveryCache,
|
|
139
139
|
contextRAG,
|
|
140
|
+
ragResidueSize = 200,
|
|
140
141
|
} = options;
|
|
141
142
|
|
|
142
143
|
// Buffer for recall — stores full tool results keyed by tool_use_id
|
|
@@ -536,25 +537,31 @@ export async function runAgentLoop(
|
|
|
536
537
|
// Store full result in buffer for later recall
|
|
537
538
|
resultBuffer.set(block.id, result);
|
|
538
539
|
|
|
539
|
-
// Nano-RAG: ingest tool result
|
|
540
|
-
|
|
540
|
+
// Nano-RAG: ingest tool result and replace with compact stub
|
|
541
|
+
let compactedResult = result;
|
|
542
|
+
if (contextRAG && result && !isDiscoveryTool(block.name)) {
|
|
541
543
|
const realName = toolMatch ? toolMatch[3] : block.name;
|
|
542
544
|
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
|
543
|
-
// Find the last user message for contextual embeddings
|
|
544
545
|
const lastUserText = [...messages].reverse()
|
|
545
546
|
.find(m => m.role === 'user')?.content;
|
|
546
547
|
const userQuery = typeof lastUserText === 'string'
|
|
547
548
|
? lastUserText
|
|
548
549
|
: (lastUserText as any[])?.find((b: any) => b.type === 'text')?.text ?? '';
|
|
549
|
-
|
|
550
|
+
try {
|
|
551
|
+
const chunkCount = await contextRAG.ingest(realName, block.id, resultStr, userQuery);
|
|
550
552
|
if (chunkCount > 0) {
|
|
551
553
|
callbacks.onTrace?.(`[nano-rag] ingested ${chunkCount} chunks from ${realName} (${resultStr.length} chars)`);
|
|
554
|
+
// Replace with compact stub — full data retrievable via RAG query
|
|
555
|
+
const residue = ragResidueSize > 0 ? resultStr.slice(0, ragResidueSize) : '';
|
|
556
|
+
compactedResult = residue
|
|
557
|
+
? `${residue}… [${resultStr.length} chars ingested into RAG]`
|
|
558
|
+
: `[${resultStr.length} chars ingested into RAG — query context for details]`;
|
|
552
559
|
}
|
|
553
|
-
}
|
|
560
|
+
} catch { /* RAG failure is non-fatal — keep full result */ }
|
|
554
561
|
}
|
|
555
562
|
|
|
556
563
|
call.result = result;
|
|
557
|
-
toolResults.push({ type: 'tool_result', tool_use_id: block.id, content:
|
|
564
|
+
toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: compactedResult });
|
|
558
565
|
} catch (e) {
|
|
559
566
|
call.error = e instanceof Error ? e.message : String(e);
|
|
560
567
|
toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: `Error: ${call.error}` });
|
package/src/providers/remote.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LLMProvider, LLMResponse, ChatMessage,
|
|
1
|
+
import type { LLMProvider, LLMResponse, ChatMessage, ProviderTool, RemoteModelId, ContentBlock } from '../types.js';
|
|
2
2
|
|
|
3
3
|
export interface RemoteLLMProviderOptions {
|
|
4
4
|
proxyUrl: string;
|
|
@@ -34,7 +34,7 @@ export class RemoteLLMProvider implements LLMProvider {
|
|
|
34
34
|
|
|
35
35
|
async chat(
|
|
36
36
|
messages: ChatMessage[],
|
|
37
|
-
tools:
|
|
37
|
+
tools: ProviderTool[],
|
|
38
38
|
options?: { signal?: AbortSignal; cacheEnabled?: boolean; system?: string; maxTokens?: number; temperature?: number; topK?: number }
|
|
39
39
|
): Promise<LLMResponse> {
|
|
40
40
|
const cache = options?.cacheEnabled ?? false;
|
package/src/providers/wasm.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* No Web Worker needed — @mediapipe/tasks-genai is not compatible with ES module workers.
|
|
4
4
|
* Uses dynamic import() to avoid bundling MediaPipe when only Claude is used.
|
|
5
5
|
*/
|
|
6
|
-
import type { LLMProvider, LLMResponse, ChatMessage,
|
|
6
|
+
import type { LLMProvider, LLMResponse, ChatMessage, ProviderTool, WasmModelId, ContentBlock } from '../types.js';
|
|
7
7
|
import type { PipelineTrace } from '../pipeline-trace.js';
|
|
8
8
|
|
|
9
9
|
export type WasmStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
@@ -175,7 +175,7 @@ export class WasmProvider implements LLMProvider {
|
|
|
175
175
|
|
|
176
176
|
async chat(
|
|
177
177
|
messages: ChatMessage[],
|
|
178
|
-
tools:
|
|
178
|
+
tools: ProviderTool[],
|
|
179
179
|
options?: { signal?: AbortSignal; maxTokens?: number; temperature?: number; topK?: number; onToken?: (token: string) => void; system?: string; maxTools?: number }
|
|
180
180
|
): Promise<LLMResponse> {
|
|
181
181
|
if (this.status !== 'ready') await this.initialize();
|
|
@@ -201,7 +201,7 @@ export class WasmProvider implements LLMProvider {
|
|
|
201
201
|
|
|
202
202
|
private async _chat(
|
|
203
203
|
messages: ChatMessage[],
|
|
204
|
-
tools:
|
|
204
|
+
tools: ProviderTool[],
|
|
205
205
|
options?: { signal?: AbortSignal; maxTokens?: number; temperature?: number; topK?: number; onToken?: (token: string) => void; system?: string; maxTools?: number }
|
|
206
206
|
): Promise<LLMResponse> {
|
|
207
207
|
// Apply per-request options
|
|
@@ -601,7 +601,7 @@ export class WasmProvider implements LLMProvider {
|
|
|
601
601
|
/**
|
|
602
602
|
* Format a tool declaration in Gemma 4 native syntax.
|
|
603
603
|
*/
|
|
604
|
-
private static formatToolDeclaration(tool:
|
|
604
|
+
private static formatToolDeclaration(tool: ProviderTool): string {
|
|
605
605
|
const q = '<|"|>';
|
|
606
606
|
let decl = `<|tool>declaration:${tool.name}{\n`;
|
|
607
607
|
decl += ` description:${q}${tool.description}${q}`;
|
|
@@ -671,7 +671,7 @@ export class WasmProvider implements LLMProvider {
|
|
|
671
671
|
return `<|tool_call>call:${name}{${entries.join(',')}}<tool_call|>`;
|
|
672
672
|
}
|
|
673
673
|
|
|
674
|
-
private buildPrompt(messages: ChatMessage[], tools:
|
|
674
|
+
private buildPrompt(messages: ChatMessage[], tools: ProviderTool[], systemPrompt?: string, maxTools?: number): string {
|
|
675
675
|
const systemParts: string[] = [];
|
|
676
676
|
|
|
677
677
|
// Inject system prompt from settings if provided
|
|
@@ -691,16 +691,8 @@ export class WasmProvider implements LLMProvider {
|
|
|
691
691
|
]
|
|
692
692
|
: tools;
|
|
693
693
|
|
|
694
|
-
// Minimal instruction — Gemma 4 is trained on native tool format
|
|
695
|
-
systemParts.push(`You are a UI assistant connected to MCP servers.
|
|
696
|
-
Use the available tools to respond. After each DATA call, render visually with component().
|
|
697
|
-
Do NOT ask for confirmation. Execute directly.`);
|
|
698
|
-
|
|
699
694
|
// Native Gemma 4 tool declarations
|
|
700
695
|
systemParts.push(limitedTools.map(t => WasmProvider.formatToolDeclaration(t)).join('\n'));
|
|
701
|
-
|
|
702
|
-
// Enable thinking mode — Gemma 4 reasons before tool calls
|
|
703
|
-
systemParts.push('Before each tool call, think briefly in a <|channel>thought block then call the tool.');
|
|
704
696
|
}
|
|
705
697
|
|
|
706
698
|
// Build a map of tool_use_id → tool_name from all messages for tool_result resolution
|
package/src/recipe-browser.ts
CHANGED
|
@@ -10,11 +10,16 @@ export function filterRecipes<T extends { name: string; description?: string }>(
|
|
|
10
10
|
): T[] {
|
|
11
11
|
const q = query.trim().toLowerCase();
|
|
12
12
|
if (!q) return recipes;
|
|
13
|
-
return recipes.filter(
|
|
14
|
-
(r)
|
|
13
|
+
return recipes.filter((r) => {
|
|
14
|
+
const srv = ((r as Record<string, unknown>).server as string | undefined)
|
|
15
|
+
?? ((r as Record<string, unknown>).serverName as string | undefined)
|
|
16
|
+
?? '';
|
|
17
|
+
return (
|
|
15
18
|
r.name.toLowerCase().includes(q) ||
|
|
16
|
-
(r.description && r.description.toLowerCase().includes(q))
|
|
17
|
-
|
|
19
|
+
(r.description && r.description.toLowerCase().includes(q)) ||
|
|
20
|
+
srv.toLowerCase().includes(q)
|
|
21
|
+
);
|
|
22
|
+
});
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
/**
|
package/src/tool-layers.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { McpToolDef, ProviderTool } from './types.js';
|
|
4
4
|
import type { McpRecipe } from './recipes/types.js';
|
|
5
|
-
import type { WebMcpToolDef } from '@webmcp-auto-ui/core';
|
|
5
|
+
import type { WebMcpToolDef, McpRecipeSummary } from '@webmcp-auto-ui/core';
|
|
6
6
|
import { sanitizeSchema, sanitizeSchemaWithReport, flattenSchema } from '@webmcp-auto-ui/core';
|
|
7
7
|
import type { SchemaPatch } from '@webmcp-auto-ui/core';
|
|
8
8
|
import { DiscoveryCache, type ServerCache } from './discovery-cache.js';
|
|
@@ -39,6 +39,7 @@ export interface WebMcpLayer {
|
|
|
39
39
|
serverName: string;
|
|
40
40
|
description: string;
|
|
41
41
|
tools: WebMcpToolDef[];
|
|
42
|
+
recipes?: McpRecipeSummary[];
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export type ToolLayer = McpLayer | WebMcpLayer;
|
package/src/types.ts
CHANGED
|
@@ -35,12 +35,9 @@ export interface ProviderTool {
|
|
|
35
35
|
name: string;
|
|
36
36
|
description: string;
|
|
37
37
|
input_schema: Record<string, unknown>;
|
|
38
|
-
strict?: boolean; //
|
|
38
|
+
strict?: boolean; // Strict tool use — grammar-constrained sampling (provider-dependent)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/** @deprecated Use ProviderTool */
|
|
42
|
-
export type AnthropicTool = ProviderTool;
|
|
43
|
-
|
|
44
41
|
export interface LLMResponse {
|
|
45
42
|
content: ContentBlock[];
|
|
46
43
|
stopReason: string;
|
package/tests/loop.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { buildSystemPrompt,
|
|
3
|
-
import type { McpToolDef, LLMProvider, LLMResponse,
|
|
2
|
+
import { buildSystemPrompt, toProviderTools, fromMcpTools } from '../src/loop.js';
|
|
3
|
+
import type { McpToolDef, LLMProvider, LLMResponse, ProviderTool, ChatMessage } from '../src/types.js';
|
|
4
4
|
|
|
5
5
|
const TOOLS: McpToolDef[] = [
|
|
6
6
|
{ name: 'search', description: 'Search for things', inputSchema: { type: 'object', properties: { q: { type: 'string' } } } },
|
|
@@ -16,12 +16,12 @@ describe('buildSystemPrompt', () => {
|
|
|
16
16
|
});
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
describe('
|
|
19
|
+
describe('toProviderTools', () => {
|
|
20
20
|
it('converts tool definitions correctly', () => {
|
|
21
|
-
const
|
|
22
|
-
expect(
|
|
23
|
-
expect(
|
|
24
|
-
expect(
|
|
21
|
+
const result = toProviderTools(TOOLS);
|
|
22
|
+
expect(result[0].name).toBe('search');
|
|
23
|
+
expect(result[0].description).toBe('Search for things');
|
|
24
|
+
expect(result[0].input_schema).toBeDefined();
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
it('strips oneOf/anyOf/allOf via sanitizeSchema', () => {
|
|
@@ -30,14 +30,14 @@ describe('mcpToolsToAnthropic', () => {
|
|
|
30
30
|
description: 'x',
|
|
31
31
|
inputSchema: { type: 'object', oneOf: [{ type: 'string' }], properties: {} } as Record<string,unknown>,
|
|
32
32
|
}];
|
|
33
|
-
const
|
|
34
|
-
expect((
|
|
33
|
+
const converted = toProviderTools(tools);
|
|
34
|
+
expect((converted[0].input_schema as Record<string, unknown>)['oneOf']).toBeUndefined();
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('falls back to empty schema when inputSchema absent', () => {
|
|
38
38
|
const tools: McpToolDef[] = [{ name: 'bare', description: 'no schema' }];
|
|
39
|
-
const
|
|
40
|
-
expect(
|
|
39
|
+
const converted = toProviderTools(tools);
|
|
40
|
+
expect(converted[0].input_schema).toBeDefined();
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
|