@webmcp-auto-ui/agent 2.5.37 → 2.5.39
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 +166 -23
- package/src/index.ts +1 -1
- package/src/loop.ts +18 -8
- package/src/prompts/claude-prompt-builder.ts +2 -0
- package/src/prompts/gemma4-prompt-builder.ts +2 -0
- package/src/prompts/mistral-prompt-builder.ts +2 -0
- package/src/prompts/qwen-prompt-builder.ts +2 -0
- package/src/trace-observer.ts +78 -1
package/package.json
CHANGED
package/src/autoui-server.ts
CHANGED
|
@@ -340,24 +340,37 @@ schema:
|
|
|
340
340
|
title:
|
|
341
341
|
type: string
|
|
342
342
|
columns:
|
|
343
|
+
description: Column definitions. Also accepts plain strings ["A","B"]. Alias "headers" is also accepted.
|
|
343
344
|
type: array
|
|
344
345
|
items:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
346
|
+
oneOf:
|
|
347
|
+
- type: string
|
|
348
|
+
- type: object
|
|
349
|
+
required:
|
|
350
|
+
- key
|
|
351
|
+
- label
|
|
352
|
+
properties:
|
|
353
|
+
key:
|
|
354
|
+
type: string
|
|
355
|
+
label:
|
|
356
|
+
type: string
|
|
357
|
+
align:
|
|
358
|
+
type: string
|
|
359
|
+
enum: [left, center, right]
|
|
360
|
+
headers:
|
|
361
|
+
description: Alias for columns. Accepts plain strings ["A","B"] or full column objects.
|
|
362
|
+
type: array
|
|
363
|
+
items:
|
|
364
|
+
oneOf:
|
|
365
|
+
- type: string
|
|
366
|
+
- type: object
|
|
357
367
|
rows:
|
|
368
|
+
description: Array of row objects OR array of arrays (positionally mapped to columns/headers keys).
|
|
358
369
|
type: array
|
|
359
370
|
items:
|
|
360
|
-
|
|
371
|
+
oneOf:
|
|
372
|
+
- type: object
|
|
373
|
+
- type: array
|
|
361
374
|
---
|
|
362
375
|
|
|
363
376
|
## When to use
|
|
@@ -365,6 +378,7 @@ Display structured data in a table with column sorting.
|
|
|
365
378
|
|
|
366
379
|
## How to use
|
|
367
380
|
Call widget_display({name: "data-table", params: {columns: [{key:"name",label:"Nom"}], rows: [{name:"Alice"}]}}).
|
|
381
|
+
Rows can also be arrays: {headers:["name","age"], rows:[["Alice",30],["Bob",25]]}.
|
|
368
382
|
`,
|
|
369
383
|
|
|
370
384
|
// ── timeline ─────────────────────────────────────────────────────────────
|
|
@@ -579,8 +593,6 @@ schema:
|
|
|
579
593
|
type: array
|
|
580
594
|
items:
|
|
581
595
|
type: object
|
|
582
|
-
required:
|
|
583
|
-
- values
|
|
584
596
|
properties:
|
|
585
597
|
label:
|
|
586
598
|
type: string
|
|
@@ -588,6 +600,9 @@ schema:
|
|
|
588
600
|
type: array
|
|
589
601
|
items:
|
|
590
602
|
type: number
|
|
603
|
+
value:
|
|
604
|
+
type: number
|
|
605
|
+
description: Scalar shorthand — treated as values:[value]. Use for single-point series.
|
|
591
606
|
color:
|
|
592
607
|
type: string
|
|
593
608
|
---
|
|
@@ -597,6 +612,7 @@ Pour des graphiques multi-series (barres, lignes, aires, camembert, donut).
|
|
|
597
612
|
|
|
598
613
|
## How to use
|
|
599
614
|
Call widget_display({name: "chart-rich", params: {type: "bar", labels: ["Q1","Q2"], data: [{label:"Ventes", values:[10,20]}]}}).
|
|
615
|
+
Single-point shorthand: data: [{label:"A", value:5}, {label:"B", value:8}].
|
|
600
616
|
`,
|
|
601
617
|
|
|
602
618
|
// ── cards ────────────────────────────────────────────────────────────────
|
|
@@ -605,13 +621,30 @@ widget: cards
|
|
|
605
621
|
description: Card grid (results, records, entities).
|
|
606
622
|
schema:
|
|
607
623
|
type: object
|
|
608
|
-
required:
|
|
609
|
-
- cards
|
|
610
624
|
properties:
|
|
611
625
|
title:
|
|
612
626
|
type: string
|
|
613
627
|
cards:
|
|
614
628
|
type: array
|
|
629
|
+
description: List of cards (alias: items)
|
|
630
|
+
items:
|
|
631
|
+
type: object
|
|
632
|
+
required:
|
|
633
|
+
- title
|
|
634
|
+
properties:
|
|
635
|
+
title:
|
|
636
|
+
type: string
|
|
637
|
+
description:
|
|
638
|
+
type: string
|
|
639
|
+
subtitle:
|
|
640
|
+
type: string
|
|
641
|
+
tags:
|
|
642
|
+
type: array
|
|
643
|
+
items:
|
|
644
|
+
type: string
|
|
645
|
+
items:
|
|
646
|
+
type: array
|
|
647
|
+
description: Alias for cards — use either cards or items
|
|
615
648
|
items:
|
|
616
649
|
type: object
|
|
617
650
|
required:
|
|
@@ -807,12 +840,11 @@ Call widget_display({name: "carousel", params: {slides: [{src: "https://...", ti
|
|
|
807
840
|
// ── stat-card ────────────────────────────────────────────────────────────
|
|
808
841
|
`---
|
|
809
842
|
widget: stat-card
|
|
810
|
-
description:
|
|
843
|
+
description: >
|
|
844
|
+
Enriched KPI card with unit, delta, icon, and colored variant.
|
|
845
|
+
Supports single card OR a grid of N cards via the "items" array.
|
|
811
846
|
schema:
|
|
812
847
|
type: object
|
|
813
|
-
required:
|
|
814
|
-
- label
|
|
815
|
-
- value
|
|
816
848
|
properties:
|
|
817
849
|
label:
|
|
818
850
|
type: string
|
|
@@ -821,6 +853,11 @@ schema:
|
|
|
821
853
|
unit:
|
|
822
854
|
type: string
|
|
823
855
|
description: Unite affichee apres la valeur (ex "%", "km")
|
|
856
|
+
icon:
|
|
857
|
+
type: string
|
|
858
|
+
description: >
|
|
859
|
+
Emoji, unicode symbol, or keyword (e.g. "fire", "trending-up", "star").
|
|
860
|
+
Unknown keywords fall back silently to ℹ.
|
|
824
861
|
delta:
|
|
825
862
|
type: string
|
|
826
863
|
description: Variation affichee (ex "+12%")
|
|
@@ -832,13 +869,47 @@ schema:
|
|
|
832
869
|
variant:
|
|
833
870
|
type: string
|
|
834
871
|
enum: [default, success, warning, error, info]
|
|
872
|
+
items:
|
|
873
|
+
type: array
|
|
874
|
+
description: >
|
|
875
|
+
Grid mode: pass an array of stat items to render N cards side by side.
|
|
876
|
+
When present, top-level label/value/icon are ignored.
|
|
877
|
+
items:
|
|
878
|
+
type: object
|
|
879
|
+
required:
|
|
880
|
+
- label
|
|
881
|
+
- value
|
|
882
|
+
properties:
|
|
883
|
+
label:
|
|
884
|
+
type: string
|
|
885
|
+
value:
|
|
886
|
+
type: string
|
|
887
|
+
unit:
|
|
888
|
+
type: string
|
|
889
|
+
icon:
|
|
890
|
+
type: string
|
|
891
|
+
delta:
|
|
892
|
+
type: string
|
|
893
|
+
trend:
|
|
894
|
+
type: string
|
|
895
|
+
enum: [up, down, flat]
|
|
896
|
+
previousValue:
|
|
897
|
+
type: string
|
|
898
|
+
variant:
|
|
899
|
+
type: string
|
|
900
|
+
enum: [default, success, warning, error, info]
|
|
835
901
|
---
|
|
836
902
|
|
|
837
903
|
## When to use
|
|
838
|
-
Pour un KPI enrichi avec delta, unite et variante de couleur.
|
|
904
|
+
Pour un KPI enrichi avec delta, unite, icone et variante de couleur.
|
|
905
|
+
Pour plusieurs KPIs cote a cote, utiliser le champ "items".
|
|
839
906
|
|
|
840
907
|
## How to use
|
|
841
|
-
|
|
908
|
+
Single card:
|
|
909
|
+
Call widget_display({name: "stat-card", params: {label: "Uptime", value: "99.9", unit: "%", trend: "up", variant: "success", icon: "🟢"}}).
|
|
910
|
+
|
|
911
|
+
Grid of cards:
|
|
912
|
+
Call widget_display({name: "stat-card", params: {items: [{label: "CPU", value: "42", unit: "%", icon: "💻", variant: "info"}, {label: "RAM", value: "8", unit: "GB", icon: "🗄", variant: "warning"}]}}).
|
|
842
913
|
`,
|
|
843
914
|
|
|
844
915
|
// ── grid-data ────────────────────────────────────────────────────────────
|
|
@@ -927,6 +998,78 @@ Pour des visualisations custom, animations, ou prototypes interactifs en JS pur.
|
|
|
927
998
|
|
|
928
999
|
## How to use
|
|
929
1000
|
Call widget_display({name: "js-sandbox", params: {code: "document.getElementById('root').innerHTML = '<h1>Hello</h1>'"}}).
|
|
1001
|
+
`,
|
|
1002
|
+
|
|
1003
|
+
// ── map ─────────────────────────────────────────────────────────────────
|
|
1004
|
+
`---
|
|
1005
|
+
widget: map
|
|
1006
|
+
description: Interactive geospatial map (MapLibre GL, light theme) with markers and/or GeoJSON overlay. Auto-fits view to data via Turf bbox.
|
|
1007
|
+
group: rich
|
|
1008
|
+
schema:
|
|
1009
|
+
type: object
|
|
1010
|
+
properties:
|
|
1011
|
+
markers:
|
|
1012
|
+
type: array
|
|
1013
|
+
description: Points to display as map markers. Aliases tolerated per marker — \`lng\` accepted for \`lon\`, \`popup\` accepted for \`label\`.
|
|
1014
|
+
items:
|
|
1015
|
+
type: object
|
|
1016
|
+
required:
|
|
1017
|
+
- lat
|
|
1018
|
+
properties:
|
|
1019
|
+
lat:
|
|
1020
|
+
type: number
|
|
1021
|
+
lon:
|
|
1022
|
+
type: number
|
|
1023
|
+
description: Longitude. Alias \`lng\` also accepted.
|
|
1024
|
+
label:
|
|
1025
|
+
type: string
|
|
1026
|
+
description: Popup text shown when the marker is clicked. Alias \`popup\` also accepted.
|
|
1027
|
+
color:
|
|
1028
|
+
type: string
|
|
1029
|
+
description: CSS color for the marker pin (defaults to blue).
|
|
1030
|
+
geojson:
|
|
1031
|
+
type: object
|
|
1032
|
+
description: GeoJSON Feature or FeatureCollection rendered as fill/line/circle layers.
|
|
1033
|
+
center:
|
|
1034
|
+
description: "Initial center. Either \`[lon, lat]\` array (MapLibre/Turf convention) OR object \`{lat, lon}\` / \`{lat, lng}\`. If omitted, computed from data via Turf bbox."
|
|
1035
|
+
zoom:
|
|
1036
|
+
type: number
|
|
1037
|
+
description: Initial zoom. If omitted, derived by fitting the bbox of the data.
|
|
1038
|
+
height:
|
|
1039
|
+
type: string
|
|
1040
|
+
description: CSS height of the map container (default "400px").
|
|
1041
|
+
cluster:
|
|
1042
|
+
type: boolean
|
|
1043
|
+
description: When true, group nearby markers into clusters (MapLibre native clustering, clusterRadius 50). Click a cluster to zoom in.
|
|
1044
|
+
tileLayers:
|
|
1045
|
+
type: array
|
|
1046
|
+
description: Raster tile overlays drawn on top of the base style (e.g. NASA GIBS imagery).
|
|
1047
|
+
items:
|
|
1048
|
+
type: object
|
|
1049
|
+
required:
|
|
1050
|
+
- url
|
|
1051
|
+
properties:
|
|
1052
|
+
name:
|
|
1053
|
+
type: string
|
|
1054
|
+
description: Optional human-readable layer name.
|
|
1055
|
+
url:
|
|
1056
|
+
type: string
|
|
1057
|
+
description: Tile URL template with \`{z}/{x}/{y}\` placeholders.
|
|
1058
|
+
opacity:
|
|
1059
|
+
type: number
|
|
1060
|
+
description: Layer opacity 0..1 (default 1).
|
|
1061
|
+
required: []
|
|
1062
|
+
---
|
|
1063
|
+
|
|
1064
|
+
## When to use
|
|
1065
|
+
Pour visualiser des donnees geospatiales : positions, itineraires, polygones (zones, regions). Choisir cette carte pour points + popups + overlays GeoJSON sur fond clair (Carto Positron).
|
|
1066
|
+
|
|
1067
|
+
## How to use
|
|
1068
|
+
Call widget_display({name: "map", params: {markers: [{lat: 48.85, lon: 2.35, label: "Paris"}]}}).
|
|
1069
|
+
|
|
1070
|
+
## Common mistakes
|
|
1071
|
+
- Coordonnees au format [lon, lat] (PAS [lat, lon]) pour le champ \`center\` et pour les Point GeoJSON — convention Turf/MapLibre.
|
|
1072
|
+
- Ne PAS fournir un \`zoom\` sans \`center\` si tu veux l'auto-fit : laisse les deux vides pour que la vue s'adapte automatiquement aux donnees.
|
|
930
1073
|
`,
|
|
931
1074
|
|
|
932
1075
|
`---
|
package/src/index.ts
CHANGED
|
@@ -88,7 +88,7 @@ export {
|
|
|
88
88
|
} from './ort-version.js';
|
|
89
89
|
|
|
90
90
|
// Trace observer — live visual trace for runAgentLoop
|
|
91
|
-
export { createTraceObserver, type TraceObserver, type TraceObserverContext, type RoundTripDetail } from './trace-observer.js';
|
|
91
|
+
export { createTraceObserver, type TraceObserver, type TraceObserverContext, type RoundTripDetail, type WidgetLineage } from './trace-observer.js';
|
|
92
92
|
|
|
93
93
|
// Nano-RAG — context compaction
|
|
94
94
|
export { ContextRAG, type ContextRAGOptions } from './nano-rag/mod.js';
|
package/src/loop.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { ToolLayer, SchemaTransformOptions } from './tool-layers.js';
|
|
|
12
12
|
import { buildToolsFromLayers, buildDiscoveryToolsWithAliases, activateServerTools, toProviderTools, sanitizeServerName } from './tool-layers.js';
|
|
13
13
|
import { buildSystemPromptWithAliases } from './prompts/index.js';
|
|
14
14
|
import type { DiscoveryCache } from './discovery-cache.js';
|
|
15
|
+
import { DISCOVERY_TOOL_NAMES } from './discovery-cache.js';
|
|
15
16
|
import { unflattenParams, validateJsonSchema } from '@webmcp-auto-ui/core';
|
|
16
17
|
import type { JsonSchema } from '@webmcp-auto-ui/core';
|
|
17
18
|
import { autoRepairParams } from './auto-repair.js';
|
|
@@ -22,12 +23,6 @@ export { buildSystemPrompt } from './prompts/index.js';
|
|
|
22
23
|
|
|
23
24
|
const MAX_RESULT_LEN = 10_000;
|
|
24
25
|
|
|
25
|
-
/** Tool names (after prefix strip) that indicate discovery/exploration */
|
|
26
|
-
const DISCOVERY_TOOL_NAMES = new Set([
|
|
27
|
-
'search_recipes', 'get_recipe', 'list_recipes', 'list_tables', 'describe_table',
|
|
28
|
-
'get_json_schemas', 'get_typescript_types', 'recall',
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
26
|
function isDiscoveryTool(prefixedName: string): boolean {
|
|
32
27
|
const match = prefixedName.match(/^.+?_(data|ui)_(.+)$/);
|
|
33
28
|
if (!match) return false;
|
|
@@ -364,14 +359,28 @@ export async function runAgentLoop(
|
|
|
364
359
|
}
|
|
365
360
|
|
|
366
361
|
// ── Intercept list_tools / search_tools (local pseudo-tools) ──
|
|
367
|
-
//
|
|
362
|
+
// The agent inspecting a server's tools is a strong signal of intent to use them.
|
|
363
|
+
// Activate the server now so the next iteration can invoke its data tools directly,
|
|
364
|
+
// matching the system-prompt promise that "list_tools → use it directly in STEP 3".
|
|
368
365
|
if (toolMatch && (toolMatch[3] === 'list_tools' || toolMatch[3] === 'search_tools')) {
|
|
369
366
|
const [, serverName, token, pseudoTool] = toolMatch;
|
|
370
367
|
const protocol = tokenToProtocol(token);
|
|
371
368
|
const layer = (options.layers ?? []).find(l => sanitizeServerName(l.serverName) === serverName && l.protocol === protocol);
|
|
372
369
|
if (!layer) {
|
|
373
370
|
result = 'Error: server not found';
|
|
374
|
-
} else
|
|
371
|
+
} else {
|
|
372
|
+
const serverKey = `${serverName}_${token}`;
|
|
373
|
+
if (!activatedServers.has(serverKey)) {
|
|
374
|
+
try {
|
|
375
|
+
const act = activateServerTools(activeTools, layer, schemaOptions, trace);
|
|
376
|
+
activeTools = act.tools;
|
|
377
|
+
for (const [k, v] of act.pathMaps) localPathMaps.set(k, v);
|
|
378
|
+
activatedServers.add(serverKey);
|
|
379
|
+
} catch (e) {
|
|
380
|
+
trace.push('activate', name, `activation failed for ${serverKey}: ${e instanceof Error ? e.message : String(e)}`, 'error');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (pseudoTool === 'list_tools') {
|
|
375
384
|
const tools = layer.tools.map(t => ({ name: t.name, description: t.description, inputSchema: (t as any).inputSchema }));
|
|
376
385
|
result = JSON.stringify(tools, null, 2);
|
|
377
386
|
} else {
|
|
@@ -388,6 +397,7 @@ export async function runAgentLoop(
|
|
|
388
397
|
result = JSON.stringify(tools, null, 2);
|
|
389
398
|
}
|
|
390
399
|
}
|
|
400
|
+
}
|
|
391
401
|
} else {
|
|
392
402
|
// ── Normal tool dispatch — activate server on first contact ──
|
|
393
403
|
|
|
@@ -47,6 +47,8 @@ ${searchTools.join('\n')}
|
|
|
47
47
|
|
|
48
48
|
FLEX picks the most relevant tool(s) and use it directly in STEP 3.
|
|
49
49
|
|
|
50
|
+
IMPORTANT — Recipe and tool indexes are in English. When the user writes in another language (French, Spanish, etc.), FLEX translates the keywords to English BEFORE calling search_recipes / search_tools. Example: user asks "biodiversité du Limousin" → search with "biodiversity", not "biodiversité". Empty results from a non-English keyword usually mean a language mismatch — FLEX retries in English before falling back to STEP 1c/1d or STEP 5.
|
|
51
|
+
|
|
50
52
|
STEP 2 — FLEX ingests the recipe in its context
|
|
51
53
|
|
|
52
54
|
${getRecipes.join('\n')}
|
|
@@ -126,6 +126,8 @@ ${searchTools.join('\n')}
|
|
|
126
126
|
|
|
127
127
|
FLEX picks the most relevant tool(s) and use it directly in STEP 3.
|
|
128
128
|
|
|
129
|
+
IMPORTANT — Recipe and tool indexes are in English. When the user writes in another language (French, Spanish, etc.), FLEX translates the keywords to English BEFORE calling search_recipes / search_tools. Example: user asks "biodiversité du Limousin" → search with "biodiversity", not "biodiversité". Empty results from a non-English keyword usually mean a language mismatch — FLEX retries in English before falling back to STEP 1c/1d or STEP 5.
|
|
130
|
+
|
|
129
131
|
STEP 2 — FLEX ingests the recipe in its context
|
|
130
132
|
|
|
131
133
|
${getRecipes.join('\n')}
|
|
@@ -50,6 +50,8 @@ ${searchTools.join('\n')}
|
|
|
50
50
|
|
|
51
51
|
FLEX picks the most relevant tool(s) and use it directly in STEP 3.
|
|
52
52
|
|
|
53
|
+
IMPORTANT — Recipe and tool indexes are in English. When the user writes in another language (French, Spanish, etc.), FLEX translates the keywords to English BEFORE calling search_recipes / search_tools. Example: user asks "biodiversité du Limousin" → search with "biodiversity", not "biodiversité". Empty results from a non-English keyword usually mean a language mismatch — FLEX retries in English before falling back to STEP 1c/1d or STEP 5.
|
|
54
|
+
|
|
53
55
|
STEP 2 — FLEX ingests the recipe in its context
|
|
54
56
|
|
|
55
57
|
${getRecipes.join('\n')}
|
|
@@ -51,6 +51,8 @@ ${searchTools.join('\n')}
|
|
|
51
51
|
|
|
52
52
|
FLEX picks the most relevant tool(s) and use it directly in STEP 3.
|
|
53
53
|
|
|
54
|
+
IMPORTANT — Recipe and tool indexes are in English. When the user writes in another language (French, Spanish, etc.), FLEX translates the keywords to English BEFORE calling search_recipes / search_tools. Example: user asks "biodiversité du Limousin" → search with "biodiversity", not "biodiversité". Empty results from a non-English keyword usually mean a language mismatch — FLEX retries in English before falling back to STEP 1c/1d or STEP 5.
|
|
55
|
+
|
|
54
56
|
STEP 2 — FLEX ingests the recipe in its context
|
|
55
57
|
|
|
56
58
|
${getRecipes.join('\n')}
|
package/src/trace-observer.ts
CHANGED
|
@@ -42,6 +42,19 @@ export interface RoundTripDetail {
|
|
|
42
42
|
originRecipe?: string;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/** Lineage of a widget mount: tool_calls in the same iteration that produced it. */
|
|
46
|
+
export interface WidgetLineage {
|
|
47
|
+
widgetType: string;
|
|
48
|
+
widgetParams: Record<string, unknown>;
|
|
49
|
+
toolCalls: Array<{
|
|
50
|
+
serverName: string;
|
|
51
|
+
toolName: string;
|
|
52
|
+
args: Record<string, unknown>;
|
|
53
|
+
resultPreview?: string;
|
|
54
|
+
}>;
|
|
55
|
+
originRecipe?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
export interface TraceObserver {
|
|
46
59
|
/** Partial AgentCallbacks to merge into runAgentLoop callbacks. */
|
|
47
60
|
callbacks: Partial<AgentCallbacks>;
|
|
@@ -57,6 +70,8 @@ export interface TraceObserver {
|
|
|
57
70
|
getLoadedRecipes: () => Map<string, string>;
|
|
58
71
|
/** Current recipe context: name of the most recently loaded recipe via get_recipe, or undefined. */
|
|
59
72
|
getCurrentRecipeContext: () => string | undefined;
|
|
73
|
+
/** Resolve the chain of tool_calls in the same iteration that produced a widget. */
|
|
74
|
+
getWidgetLineage: (blockIdOrTraceNodeId: string) => WidgetLineage | null;
|
|
60
75
|
}
|
|
61
76
|
|
|
62
77
|
type NodeKind =
|
|
@@ -552,11 +567,12 @@ export function createTraceObserver(ctx: TraceObserverContext): TraceObserver {
|
|
|
552
567
|
scheduleFlush();
|
|
553
568
|
},
|
|
554
569
|
|
|
555
|
-
onWidget: (type: string,
|
|
570
|
+
onWidget: (type: string, data: Record<string, unknown>, serverName?: string): void => {
|
|
556
571
|
const node = addNode('widget', `${serverName ?? '?'}/${type}`, {
|
|
557
572
|
iterationId: currentIterationId,
|
|
558
573
|
type,
|
|
559
574
|
serverName,
|
|
575
|
+
data,
|
|
560
576
|
});
|
|
561
577
|
const parent = lastToolCallId ?? currentLlmRespId;
|
|
562
578
|
if (parent) {
|
|
@@ -654,5 +670,66 @@ export function createTraceObserver(ctx: TraceObserverContext): TraceObserver {
|
|
|
654
670
|
getCurrentRecipeContext(): string | undefined {
|
|
655
671
|
return currentRecipeContext;
|
|
656
672
|
},
|
|
673
|
+
getWidgetLineage(blockIdOrTraceNodeId: string): WidgetLineage | null {
|
|
674
|
+
let widgetNode = nodes.find(
|
|
675
|
+
(n) => n.kind === 'widget' && n.id === blockIdOrTraceNodeId,
|
|
676
|
+
);
|
|
677
|
+
if (!widgetNode) {
|
|
678
|
+
widgetNode = nodes.find((n) => {
|
|
679
|
+
if (n.kind !== 'widget') return false;
|
|
680
|
+
for (const v of Object.values(n.meta)) {
|
|
681
|
+
if (v === blockIdOrTraceNodeId) return true;
|
|
682
|
+
if (v && typeof v === 'object') {
|
|
683
|
+
for (const vv of Object.values(v as Record<string, unknown>)) {
|
|
684
|
+
if (vv === blockIdOrTraceNodeId) return true;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return false;
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
if (!widgetNode) return null;
|
|
692
|
+
const iter = iterationAncestor(widgetNode);
|
|
693
|
+
if (!iter) {
|
|
694
|
+
return {
|
|
695
|
+
widgetType: (widgetNode.meta.type as string) ?? '',
|
|
696
|
+
widgetParams: ((widgetNode.meta.data as Record<string, unknown>) ?? {}) as Record<string, unknown>,
|
|
697
|
+
toolCalls: [],
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
const callNodes = nodes
|
|
701
|
+
.filter(
|
|
702
|
+
(n) =>
|
|
703
|
+
n.kind === 'tool_call' &&
|
|
704
|
+
n.meta.iterationId === iter.id &&
|
|
705
|
+
n.startMs <= widgetNode!.startMs,
|
|
706
|
+
)
|
|
707
|
+
.sort((a, b) => a.startMs - b.startMs);
|
|
708
|
+
const toolCalls: WidgetLineage['toolCalls'] = [];
|
|
709
|
+
let originRecipe: string | undefined;
|
|
710
|
+
for (const c of callNodes) {
|
|
711
|
+
const detail = nodeDetails.get(c.id);
|
|
712
|
+
const toolName = detail?.toolName ?? '';
|
|
713
|
+
const serverName = (c.meta.serverName as string | undefined)
|
|
714
|
+
?? (toolName.includes('_') ? toolName.split('_')[0]! : '');
|
|
715
|
+
const args = ((c.meta.args as Record<string, unknown>) ?? {}) as Record<string, unknown>;
|
|
716
|
+
let resultPreview: string | undefined;
|
|
717
|
+
if (detail?.toolResult !== undefined) {
|
|
718
|
+
resultPreview = detail.toolResult.slice(0, 4096);
|
|
719
|
+
}
|
|
720
|
+
toolCalls.push({ serverName, toolName, args, resultPreview });
|
|
721
|
+
if (toolName === 'get_recipe' || toolName.endsWith('_get_recipe')) {
|
|
722
|
+
const r = (args.recipe_name ?? args.name ?? args.id) as unknown;
|
|
723
|
+
if (typeof r === 'string') originRecipe = r;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
const lineage: WidgetLineage = {
|
|
727
|
+
widgetType: (widgetNode.meta.type as string) ?? '',
|
|
728
|
+
widgetParams: ((widgetNode.meta.data as Record<string, unknown>) ?? {}) as Record<string, unknown>,
|
|
729
|
+
toolCalls,
|
|
730
|
+
};
|
|
731
|
+
if (originRecipe) lineage.originRecipe = originRecipe;
|
|
732
|
+
return lineage;
|
|
733
|
+
},
|
|
657
734
|
};
|
|
658
735
|
}
|