@webmcp-auto-ui/agent 2.5.27 → 2.5.29
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 +10 -2
- package/src/autoui-server.ts +57 -84
- package/src/index.ts +7 -2
- package/src/loop.ts +53 -23
- package/src/providers/factory.ts +15 -1
- package/src/providers/hawk-models.ts +23 -0
- package/src/providers/hawk.ts +181 -0
- package/src/providers/transformers.worker.ts +5 -32
- package/src/providers/wasm.ts +7 -5
- package/src/recipes/_generated.ts +64 -54
- package/src/recipes/hackathon-assemblee-nationale.md +4 -10
- package/src/recipes/notebook-playbook.md +60 -44
- package/src/server/hawkProxy.ts +54 -0
- package/src/server/index.ts +2 -0
- package/src/tool-layers.ts +26 -4
- package/src/trace-observer.ts +4 -15
- package/src/util/opfs-cache.ts +101 -2
- package/src/util/storage-inventory.ts +195 -0
- package/src/notebook-widgets/compact.ts +0 -312
- package/src/notebook-widgets/document.ts +0 -372
- package/src/notebook-widgets/editorial.ts +0 -348
- package/src/notebook-widgets/recipes/compact.md +0 -104
- package/src/notebook-widgets/recipes/document.md +0 -100
- package/src/notebook-widgets/recipes/editorial.md +0 -104
- package/src/notebook-widgets/recipes/workspace.md +0 -94
- package/src/notebook-widgets/shared.ts +0 -1064
- package/src/notebook-widgets/workspace.ts +0 -328
package/src/providers/wasm.ts
CHANGED
|
@@ -313,11 +313,13 @@ export class WasmProvider implements LLMProvider {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
// Strip hallucinated framework tokens the model should never emit on its own
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
// - <|
|
|
316
|
+
// Strip hallucinated framework tokens the model should never emit on its own.
|
|
317
|
+
// Thinking mode is disabled for Gemma (since commit 164a24d) but the model
|
|
318
|
+
// may still leak these tokens as pretraining artefacts — keep the defensive
|
|
319
|
+
// strip regardless of activation.
|
|
320
|
+
// - <|tool_response>...<tool_response|> (framework-injected, never generated)
|
|
321
|
+
// - <|channel>thought...<channel|> (ghost thought channels)
|
|
322
|
+
// - <|think|> (stray pretraining artefacts)
|
|
321
323
|
fullText = fullText
|
|
322
324
|
.replace(/<\|tool_response>[\s\S]*?<tool_response\|>/g, '')
|
|
323
325
|
.replace(/<\|channel>thought[\s\S]*?<channel\|>/g, '')
|
|
@@ -932,7 +932,7 @@ component("gallery", {
|
|
|
932
932
|
'hackathon-assemblee-nationale': `---
|
|
933
933
|
id: hackathon-assemblee-nationale-mcp-webmcp
|
|
934
934
|
name: Hackathon Assemblée Nationale · MCP & WebMCP starter
|
|
935
|
-
components_used: [notebook
|
|
935
|
+
components_used: [notebook]
|
|
936
936
|
when: the user mentions the Assemblée Nationale hackathon, wants to experiment with parliamentary data (Tricoteuses, Legifrance), or asks for a starter notebook to explore deputies, votes, amendments, or legislative texts in a hackathon context. Keywords include "hackathon Assemblée Nationale", "MCP WebMCP hackathon", "tricoteuses playbook", "parlement playground", "hackathon parlementaire".
|
|
937
937
|
servers: [autoui, tricoteuses]
|
|
938
938
|
layout:
|
|
@@ -951,14 +951,9 @@ This recipe is a **specialization** of the generic \`notebook-playbook\` recipe
|
|
|
951
951
|
|
|
952
952
|
## How to use
|
|
953
953
|
|
|
954
|
-
### Step 1 —
|
|
954
|
+
### Step 1 — Use the \`notebook\` widget
|
|
955
955
|
|
|
956
|
-
|
|
957
|
-
- Participants expect to collaborate and leave traces for each other (avatars, comments)
|
|
958
|
-
- The document layout reads naturally as "hackathon brief + starter code" rather than "dev tool"
|
|
959
|
-
- Margin comments can be seeded to hint at directions to explore
|
|
960
|
-
|
|
961
|
-
If the participant asks for a minimal dev-focused playground instead, fall back to \`notebook-compact\`.
|
|
956
|
+
The single \`notebook\` widget fits the hackathon brief: prose, SQL and JS share one drag-and-droppable flow, suited both to a "brief + starter code" read and to free exploration.
|
|
962
957
|
|
|
963
958
|
### Step 2 — Seed the cells
|
|
964
959
|
|
|
@@ -974,7 +969,7 @@ The notebook should contain 5–7 cells covering:
|
|
|
974
969
|
Example template (to be refined with actual hackathon organizers' briefs):
|
|
975
970
|
|
|
976
971
|
\`\`\`
|
|
977
|
-
widget_display({name: "notebook
|
|
972
|
+
widget_display({name: "notebook", params: {
|
|
978
973
|
title: "Hackathon Assemblée Nationale · starter",
|
|
979
974
|
cells: [
|
|
980
975
|
{
|
|
@@ -1039,7 +1034,6 @@ These recipes produce more specialized layouts than a generic notebook, and are
|
|
|
1039
1034
|
- **Forgetting the hackathon framing**: without the "bienvenue" and "à vous" markdown cells, participants land on a bare notebook and lose the playbook feeling.
|
|
1040
1035
|
- **SQL without LIMIT**: Tricoteuses queries without \`LIMIT\` can return thousands of rows and slow down the first impression.
|
|
1041
1036
|
- **Inventing data**: do not seed with fake French parliamentary content (fake deputies, fake votes). If the actual data is not known at seed time, use generic queries (\`SELECT * FROM scrutins LIMIT 5\`) and let the user discover from there.
|
|
1042
|
-
- **Picking the wrong layout**: if the user is solo and wants to code fast, \`notebook-compact\` is better than \`notebook-document\`. Default to document for hackathon context because of the collaborative framing, not as a hard rule.
|
|
1043
1037
|
`,
|
|
1044
1038
|
'hummingbird-data': `---
|
|
1045
1039
|
id: hummingbird-data
|
|
@@ -1133,8 +1127,8 @@ Do NOT render any widget.
|
|
|
1133
1127
|
'notebook-playbook': `---
|
|
1134
1128
|
id: create-interactive-notebook-playbook
|
|
1135
1129
|
name: Create an interactive notebook playbook
|
|
1136
|
-
components_used: [notebook
|
|
1137
|
-
when: the user wants to experiment with data, prototype a small analysis, share a reusable scenario,
|
|
1130
|
+
components_used: [notebook]
|
|
1131
|
+
when: the user wants to experiment with data, prototype a small analysis, share a reusable scenario, or prepare a hackathon-ready playground. Keywords include "playground", "playbook", "experiment", "try", "prototype", "hackathon", "share a notebook", "template", "starter", "publish", "memo", "report".
|
|
1138
1132
|
servers: [autoui]
|
|
1139
1133
|
layout:
|
|
1140
1134
|
type: single
|
|
@@ -1148,37 +1142,29 @@ The user asks for a **notebook-like interactive playground** that combines text,
|
|
|
1148
1142
|
- "I want to prototype a small analysis"
|
|
1149
1143
|
- "Set up a hackathon starter"
|
|
1150
1144
|
- "Make a reusable template for exploring CSVs / this API / these tables"
|
|
1145
|
+
- "Publish this analysis as a short memo"
|
|
1151
1146
|
|
|
1152
1147
|
This recipe applies across domains (parliamentary data, biodiversity, news, business datasets, etc.) — it only prescribes the **shape** of the answer, not its content.
|
|
1153
1148
|
|
|
1154
1149
|
## How to use
|
|
1155
1150
|
|
|
1156
|
-
### Step 1 —
|
|
1157
|
-
|
|
1158
|
-
Choose one of the four \`notebook-*\` widgets based on the user's implicit intent:
|
|
1159
|
-
|
|
1160
|
-
| Layout | Use when |
|
|
1161
|
-
|---|---|
|
|
1162
|
-
| \`notebook-compact\` | Quick data exploration, reactive dataflow with named outputs, minimal chrome. **Default for most "playground" and "hackathon" requests.** |
|
|
1163
|
-
| \`notebook-workspace\` | The user expects a multi-cell analyst workspace with sources, cell navigation, and a "publish" step. Use when they mention "dashboard", "app", "workspace". |
|
|
1164
|
-
| \`notebook-document\` | The user plans to share and discuss with a team. Use when "collaborate", "review", "comment" appear. |
|
|
1165
|
-
| \`notebook-editorial\` | The user wants a polished, article-like final deliverable mixing prose and code. Use for "memo", "report", "writeup", "blog-style". |
|
|
1151
|
+
### Step 1 — Use the \`notebook\` widget
|
|
1166
1152
|
|
|
1167
|
-
|
|
1153
|
+
There is a single notebook widget. Prose paragraphs (\`md\`), SQL queries (\`sql\`) and JS code (\`js\`) share one ordered flow, all drag-and-droppable together. Publication-ready serif prose, suitable for playgrounds, memos, collaborative reviews and analyst workspaces alike.
|
|
1168
1154
|
|
|
1169
1155
|
### Step 2 — Pre-fill the cells with context-aware seeds
|
|
1170
1156
|
|
|
1171
1157
|
Never create an empty notebook. Always seed with 3–5 cells that give the user an immediate starting point:
|
|
1172
1158
|
|
|
1173
1159
|
1. **First cell: markdown** — title + one-sentence context of what the notebook is for
|
|
1174
|
-
2. **Second cell:
|
|
1175
|
-
3. **Third cell: code** — a transformation or a visualization that uses the output of step 2. Use \`varname\` on the SQL cell (\`varname: "rows"\`) and reference it in the JS cell
|
|
1176
|
-
4. **Last cell: markdown** — a short "to you to play" note inviting the user to add cells
|
|
1160
|
+
2. **Second cell: sql or md** — if an MCP data source is connected, a starter query that returns something visible (e.g. \`SELECT * FROM {table} LIMIT 10\`). Otherwise a markdown cell describing the next step
|
|
1161
|
+
3. **Third cell: code** — a transformation or a visualization that uses the output of step 2. Use \`varname\` on the SQL cell (\`varname: "rows"\`) and reference it in the JS cell to activate the reactive dataflow
|
|
1162
|
+
4. **Last cell: markdown** — a short "to you to play" note inviting the user to add cells or edit
|
|
1177
1163
|
|
|
1178
1164
|
Example seed for a generic data playground:
|
|
1179
1165
|
|
|
1180
1166
|
\`\`\`
|
|
1181
|
-
widget_display({name: "notebook
|
|
1167
|
+
widget_display({name: "notebook", params: {
|
|
1182
1168
|
title: "Exploration playground",
|
|
1183
1169
|
cells: [
|
|
1184
1170
|
{type: "md", content: "### Exploration playground\\n\\nStart by running the first SQL cell, then iterate."},
|
|
@@ -1198,47 +1184,71 @@ If a specific MCP server is connected, replace the generic \`source\` and \`sele
|
|
|
1198
1184
|
|
|
1199
1185
|
Always keep queries **short** and **limited** so the first run returns quickly and visually.
|
|
1200
1186
|
|
|
1201
|
-
|
|
1187
|
+
SQL cells are dispatched automatically to the server's \`*_query_sql\` tool (first match). JS cells run in a Web Worker with upstream named outputs injected as scope.
|
|
1202
1188
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1189
|
+
### Step 4 — Exporting & publishing
|
|
1190
|
+
|
|
1191
|
+
The toolbar \`share\` button offers **four export formats**:
|
|
1192
|
+
|
|
1193
|
+
| Format | What it does |
|
|
1194
|
+
|---|---|
|
|
1195
|
+
| **Hyperskill link** | Copies both the canonical Hyperskill URL and a short domain-scoped URL (\`?n=<token>\`). The short URL opens the read-only public viewer at \`nb.hyperskills.net\`. |
|
|
1196
|
+
| **Markdown** | Downloads a \`.md\` file containing the notebook content. |
|
|
1197
|
+
| **PNG** | Snapshots the rendered notebook to an image. |
|
|
1198
|
+
| **JSON** | Exports the full widget state — re-importable for programmatic reuse. |
|
|
1199
|
+
|
|
1200
|
+
### Step 5 — Working with connected data servers
|
|
1201
|
+
|
|
1202
|
+
When one or more MCP data servers are connected, the notebook exposes a **collapsible left pane** (bookmark-bar styling, collapsed by default) that lists:
|
|
1203
|
+
- **Recipes** published by each server (\`{server}_list_recipes()\`)
|
|
1204
|
+
- **Tools / tables** exposed by each server (\`{server}_list_tools()\`)
|
|
1205
|
+
|
|
1206
|
+
Clicking any recipe opens a viewer modal. Each fenced code block inside the recipe has a \`↳ inject\` button that drops the snippet into the notebook as a new cell.
|
|
1207
1207
|
|
|
1208
|
-
|
|
1208
|
+
Two toolbar buttons flank the left pane:
|
|
1209
|
+
- **\`+ md\`** — 3-tab modal (New / File / URL) to create a markdown cell from scratch, from a local \`.md\` file, or from a URL
|
|
1210
|
+
- **\`+ recipe\`** — 3-tab modal (Browser / File / URL) to import a recipe from a connected server, a local \`.recipe.md\` file, or a URL
|
|
1211
|
+
|
|
1212
|
+
Pass the server metadata via the \`servers:\` param:
|
|
1213
|
+
|
|
1214
|
+
\`\`\`ts
|
|
1215
|
+
widget_display({
|
|
1216
|
+
name: 'notebook',
|
|
1217
|
+
params: {
|
|
1218
|
+
title: '...',
|
|
1219
|
+
cells: [...],
|
|
1220
|
+
servers: [{ name: 'tricoteuses', url: 'https://...', kind: 'data' }]
|
|
1221
|
+
}
|
|
1222
|
+
})
|
|
1223
|
+
\`\`\`
|
|
1224
|
+
|
|
1225
|
+
**Filter rule**: only MCP *data* servers (\`kind: 'data'\`) belong in \`servers:\`. Do NOT include WebMCP UI servers such as \`autoui\`.
|
|
1226
|
+
|
|
1227
|
+
### Step 6 — Hand-off guidance
|
|
1228
|
+
|
|
1229
|
+
After creating the notebook, mention to the user that they can:
|
|
1230
|
+
- **Share in four formats** via the toolbar \`share\` button (Hyperskill / Markdown / PNG / JSON)
|
|
1231
|
+
- **Switch to \`view\` mode** (read-only) when presenting
|
|
1232
|
+
- Access the \`⟲ history\` panel to see the edit trace and restore deleted cells
|
|
1233
|
+
- **Import recipes** from connected MCP servers via the left pane or the \`+ recipe\` modal
|
|
1209
1234
|
|
|
1210
1235
|
## Examples
|
|
1211
1236
|
|
|
1212
1237
|
### Generic CSV / table playground
|
|
1213
1238
|
\`\`\`
|
|
1214
|
-
|
|
1215
|
-
widget_display({name: "notebook-compact", params: {
|
|
1239
|
+
widget_display({name: "notebook", params: {
|
|
1216
1240
|
title: "CSV playground",
|
|
1217
1241
|
cells: [
|
|
1218
1242
|
{type: "md", content: "### CSV playground\\n\\nRun the SQL cell to see the first rows, then iterate."},
|
|
1219
1243
|
{type: "sql", content: "select * from source limit 20", varname: "rows"},
|
|
1220
|
-
{type: "js", content: "// summarize, chart, or filter rows here"}
|
|
1221
|
-
]
|
|
1222
|
-
}})
|
|
1223
|
-
\`\`\`
|
|
1224
|
-
|
|
1225
|
-
### Collaborative analysis
|
|
1226
|
-
\`\`\`
|
|
1227
|
-
// user: "Set up a notebook my team can edit together"
|
|
1228
|
-
widget_display({name: "notebook-document", params: {
|
|
1229
|
-
title: "Team analysis",
|
|
1230
|
-
cells: [
|
|
1231
|
-
{type: "md", content: "Kick-off: describe the question here."},
|
|
1232
|
-
{type: "sql", content: "select * from source limit 10"},
|
|
1233
|
-
{type: "md", content: "Your findings: add thoughts, highlights (<mark>key sentence</mark>), and comments on the code cells above."}
|
|
1244
|
+
{type: "js", content: "// summarize, chart, or filter rows here\\nconsole.table(rows)"}
|
|
1234
1245
|
]
|
|
1235
1246
|
}})
|
|
1236
1247
|
\`\`\`
|
|
1237
1248
|
|
|
1238
1249
|
### Final memo
|
|
1239
1250
|
\`\`\`
|
|
1240
|
-
|
|
1241
|
-
widget_display({name: "notebook-editorial", params: {
|
|
1251
|
+
widget_display({name: "notebook", params: {
|
|
1242
1252
|
title: "Findings memo",
|
|
1243
1253
|
kicker: "memo",
|
|
1244
1254
|
cells: [
|
|
@@ -1255,10 +1265,10 @@ widget_display({name: "notebook-editorial", params: {
|
|
|
1255
1265
|
## Common mistakes
|
|
1256
1266
|
|
|
1257
1267
|
- **Empty notebook**: never call \`widget_display\` without at least 3 seed cells. The user expects something they can immediately run.
|
|
1258
|
-
- **Wrong layout for the intent**: do not use \`notebook-editorial\` for quick exploration — it signals "finished article" and intimidates. Use \`notebook-compact\` unless the user explicitly asks for a publication feel.
|
|
1259
1268
|
- **Heavy initial queries**: always \`LIMIT 10\` or \`LIMIT 20\` in seed SQL cells. Users will expand later if needed.
|
|
1260
|
-
- **Missing \`varname\` on SQL cells
|
|
1261
|
-
- **Inventing UUIDs
|
|
1269
|
+
- **Missing \`varname\` on SQL cells**: the named output drives the reactive dataflow (downstream JS cells go stale when their upstream re-runs). Without it, the notebook loses half its story.
|
|
1270
|
+
- **Inventing UUIDs**: leave \`id\` unset — the widget generates a sensible default. Only pass \`id\` when restoring an existing notebook.
|
|
1271
|
+
- **Including \`autoui\` in \`servers:\`**: only MCP *data* servers (\`kind: 'data'\`) belong there.
|
|
1262
1272
|
`,
|
|
1263
1273
|
'parlementaire-profile': `---
|
|
1264
1274
|
id: display-parliamentary-profile-with-hemicycle-and-votes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: hackathon-assemblee-nationale-mcp-webmcp
|
|
3
3
|
name: Hackathon Assemblée Nationale · MCP & WebMCP starter
|
|
4
|
-
components_used: [notebook
|
|
4
|
+
components_used: [notebook]
|
|
5
5
|
when: the user mentions the Assemblée Nationale hackathon, wants to experiment with parliamentary data (Tricoteuses, Legifrance), or asks for a starter notebook to explore deputies, votes, amendments, or legislative texts in a hackathon context. Keywords include "hackathon Assemblée Nationale", "MCP WebMCP hackathon", "tricoteuses playbook", "parlement playground", "hackathon parlementaire".
|
|
6
6
|
servers: [autoui, tricoteuses]
|
|
7
7
|
layout:
|
|
@@ -20,14 +20,9 @@ This recipe is a **specialization** of the generic `notebook-playbook` recipe
|
|
|
20
20
|
|
|
21
21
|
## How to use
|
|
22
22
|
|
|
23
|
-
### Step 1 —
|
|
23
|
+
### Step 1 — Use the `notebook` widget
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
- Participants expect to collaborate and leave traces for each other (avatars, comments)
|
|
27
|
-
- The document layout reads naturally as "hackathon brief + starter code" rather than "dev tool"
|
|
28
|
-
- Margin comments can be seeded to hint at directions to explore
|
|
29
|
-
|
|
30
|
-
If the participant asks for a minimal dev-focused playground instead, fall back to `notebook-compact`.
|
|
25
|
+
The single `notebook` widget fits the hackathon brief: prose, SQL and JS share one drag-and-droppable flow, suited both to a "brief + starter code" read and to free exploration.
|
|
31
26
|
|
|
32
27
|
### Step 2 — Seed the cells
|
|
33
28
|
|
|
@@ -43,7 +38,7 @@ The notebook should contain 5–7 cells covering:
|
|
|
43
38
|
Example template (to be refined with actual hackathon organizers' briefs):
|
|
44
39
|
|
|
45
40
|
```
|
|
46
|
-
widget_display({name: "notebook
|
|
41
|
+
widget_display({name: "notebook", params: {
|
|
47
42
|
title: "Hackathon Assemblée Nationale · starter",
|
|
48
43
|
cells: [
|
|
49
44
|
{
|
|
@@ -108,4 +103,3 @@ These recipes produce more specialized layouts than a generic notebook, and are
|
|
|
108
103
|
- **Forgetting the hackathon framing**: without the "bienvenue" and "à vous" markdown cells, participants land on a bare notebook and lose the playbook feeling.
|
|
109
104
|
- **SQL without LIMIT**: Tricoteuses queries without `LIMIT` can return thousands of rows and slow down the first impression.
|
|
110
105
|
- **Inventing data**: do not seed with fake French parliamentary content (fake deputies, fake votes). If the actual data is not known at seed time, use generic queries (`SELECT * FROM scrutins LIMIT 5`) and let the user discover from there.
|
|
111
|
-
- **Picking the wrong layout**: if the user is solo and wants to code fast, `notebook-compact` is better than `notebook-document`. Default to document for hackathon context because of the collaborative framing, not as a hard rule.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: create-interactive-notebook-playbook
|
|
3
3
|
name: Create an interactive notebook playbook
|
|
4
|
-
components_used: [notebook
|
|
5
|
-
when: the user wants to experiment with data, prototype a small analysis, share a reusable scenario,
|
|
4
|
+
components_used: [notebook]
|
|
5
|
+
when: the user wants to experiment with data, prototype a small analysis, share a reusable scenario, or prepare a hackathon-ready playground. Keywords include "playground", "playbook", "experiment", "try", "prototype", "hackathon", "share a notebook", "template", "starter", "publish", "memo", "report".
|
|
6
6
|
servers: [autoui]
|
|
7
7
|
layout:
|
|
8
8
|
type: single
|
|
@@ -16,37 +16,29 @@ The user asks for a **notebook-like interactive playground** that combines text,
|
|
|
16
16
|
- "I want to prototype a small analysis"
|
|
17
17
|
- "Set up a hackathon starter"
|
|
18
18
|
- "Make a reusable template for exploring CSVs / this API / these tables"
|
|
19
|
+
- "Publish this analysis as a short memo"
|
|
19
20
|
|
|
20
21
|
This recipe applies across domains (parliamentary data, biodiversity, news, business datasets, etc.) — it only prescribes the **shape** of the answer, not its content.
|
|
21
22
|
|
|
22
23
|
## How to use
|
|
23
24
|
|
|
24
|
-
### Step 1 —
|
|
25
|
+
### Step 1 — Use the `notebook` widget
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
| Layout | Use when |
|
|
29
|
-
|---|---|
|
|
30
|
-
| `notebook-compact` | Quick data exploration, reactive dataflow with named outputs, minimal chrome. **Default for most "playground" and "hackathon" requests.** |
|
|
31
|
-
| `notebook-workspace` | The user expects a multi-cell analyst workspace with sources, cell navigation, and a "publish" step. Use when they mention "dashboard", "app", "workspace". |
|
|
32
|
-
| `notebook-document` | The user plans to share and discuss with a team. Use when "collaborate", "review", "comment" appear. |
|
|
33
|
-
| `notebook-editorial` | The user wants a polished, article-like final deliverable mixing prose and code. Use for "memo", "report", "writeup", "blog-style". |
|
|
34
|
-
|
|
35
|
-
When in doubt, pick `notebook-compact`.
|
|
27
|
+
There is a single notebook widget. Prose paragraphs (`md`), SQL queries (`sql`) and JS code (`js`) share one ordered flow, all drag-and-droppable together. Publication-ready serif prose, suitable for playgrounds, memos, collaborative reviews and analyst workspaces alike.
|
|
36
28
|
|
|
37
29
|
### Step 2 — Pre-fill the cells with context-aware seeds
|
|
38
30
|
|
|
39
31
|
Never create an empty notebook. Always seed with 3–5 cells that give the user an immediate starting point:
|
|
40
32
|
|
|
41
33
|
1. **First cell: markdown** — title + one-sentence context of what the notebook is for
|
|
42
|
-
2. **Second cell:
|
|
43
|
-
3. **Third cell: code** — a transformation or a visualization that uses the output of step 2. Use `varname` on the SQL cell (`varname: "rows"`) and reference it in the JS cell
|
|
44
|
-
4. **Last cell: markdown** — a short "to you to play" note inviting the user to add cells
|
|
34
|
+
2. **Second cell: sql or md** — if an MCP data source is connected, a starter query that returns something visible (e.g. `SELECT * FROM {table} LIMIT 10`). Otherwise a markdown cell describing the next step
|
|
35
|
+
3. **Third cell: code** — a transformation or a visualization that uses the output of step 2. Use `varname` on the SQL cell (`varname: "rows"`) and reference it in the JS cell to activate the reactive dataflow
|
|
36
|
+
4. **Last cell: markdown** — a short "to you to play" note inviting the user to add cells or edit
|
|
45
37
|
|
|
46
38
|
Example seed for a generic data playground:
|
|
47
39
|
|
|
48
40
|
```
|
|
49
|
-
widget_display({name: "notebook
|
|
41
|
+
widget_display({name: "notebook", params: {
|
|
50
42
|
title: "Exploration playground",
|
|
51
43
|
cells: [
|
|
52
44
|
{type: "md", content: "### Exploration playground\n\nStart by running the first SQL cell, then iterate."},
|
|
@@ -66,47 +58,71 @@ If a specific MCP server is connected, replace the generic `source` and `select
|
|
|
66
58
|
|
|
67
59
|
Always keep queries **short** and **limited** so the first run returns quickly and visually.
|
|
68
60
|
|
|
69
|
-
|
|
61
|
+
SQL cells are dispatched automatically to the server's `*_query_sql` tool (first match). JS cells run in a Web Worker with upstream named outputs injected as scope.
|
|
70
62
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
### Step 4 — Exporting & publishing
|
|
64
|
+
|
|
65
|
+
The toolbar `share` button offers **four export formats**:
|
|
66
|
+
|
|
67
|
+
| Format | What it does |
|
|
68
|
+
|---|---|
|
|
69
|
+
| **Hyperskill link** | Copies both the canonical Hyperskill URL and a short domain-scoped URL (`?n=<token>`). The short URL opens the read-only public viewer at `nb.hyperskills.net`. |
|
|
70
|
+
| **Markdown** | Downloads a `.md` file containing the notebook content. |
|
|
71
|
+
| **PNG** | Snapshots the rendered notebook to an image. |
|
|
72
|
+
| **JSON** | Exports the full widget state — re-importable for programmatic reuse. |
|
|
73
|
+
|
|
74
|
+
### Step 5 — Working with connected data servers
|
|
75
|
+
|
|
76
|
+
When one or more MCP data servers are connected, the notebook exposes a **collapsible left pane** (bookmark-bar styling, collapsed by default) that lists:
|
|
77
|
+
- **Recipes** published by each server (`{server}_list_recipes()`)
|
|
78
|
+
- **Tools / tables** exposed by each server (`{server}_list_tools()`)
|
|
79
|
+
|
|
80
|
+
Clicking any recipe opens a viewer modal. Each fenced code block inside the recipe has a `↳ inject` button that drops the snippet into the notebook as a new cell.
|
|
81
|
+
|
|
82
|
+
Two toolbar buttons flank the left pane:
|
|
83
|
+
- **`+ md`** — 3-tab modal (New / File / URL) to create a markdown cell from scratch, from a local `.md` file, or from a URL
|
|
84
|
+
- **`+ recipe`** — 3-tab modal (Browser / File / URL) to import a recipe from a connected server, a local `.recipe.md` file, or a URL
|
|
85
|
+
|
|
86
|
+
Pass the server metadata via the `servers:` param:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
widget_display({
|
|
90
|
+
name: 'notebook',
|
|
91
|
+
params: {
|
|
92
|
+
title: '...',
|
|
93
|
+
cells: [...],
|
|
94
|
+
servers: [{ name: 'tricoteuses', url: 'https://...', kind: 'data' }]
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Filter rule**: only MCP *data* servers (`kind: 'data'`) belong in `servers:`. Do NOT include WebMCP UI servers such as `autoui`.
|
|
75
100
|
|
|
76
|
-
|
|
101
|
+
### Step 6 — Hand-off guidance
|
|
102
|
+
|
|
103
|
+
After creating the notebook, mention to the user that they can:
|
|
104
|
+
- **Share in four formats** via the toolbar `share` button (Hyperskill / Markdown / PNG / JSON)
|
|
105
|
+
- **Switch to `view` mode** (read-only) when presenting
|
|
106
|
+
- Access the `⟲ history` panel to see the edit trace and restore deleted cells
|
|
107
|
+
- **Import recipes** from connected MCP servers via the left pane or the `+ recipe` modal
|
|
77
108
|
|
|
78
109
|
## Examples
|
|
79
110
|
|
|
80
111
|
### Generic CSV / table playground
|
|
81
112
|
```
|
|
82
|
-
|
|
83
|
-
widget_display({name: "notebook-compact", params: {
|
|
113
|
+
widget_display({name: "notebook", params: {
|
|
84
114
|
title: "CSV playground",
|
|
85
115
|
cells: [
|
|
86
116
|
{type: "md", content: "### CSV playground\n\nRun the SQL cell to see the first rows, then iterate."},
|
|
87
117
|
{type: "sql", content: "select * from source limit 20", varname: "rows"},
|
|
88
|
-
{type: "js", content: "// summarize, chart, or filter rows here"}
|
|
89
|
-
]
|
|
90
|
-
}})
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Collaborative analysis
|
|
94
|
-
```
|
|
95
|
-
// user: "Set up a notebook my team can edit together"
|
|
96
|
-
widget_display({name: "notebook-document", params: {
|
|
97
|
-
title: "Team analysis",
|
|
98
|
-
cells: [
|
|
99
|
-
{type: "md", content: "Kick-off: describe the question here."},
|
|
100
|
-
{type: "sql", content: "select * from source limit 10"},
|
|
101
|
-
{type: "md", content: "Your findings: add thoughts, highlights (<mark>key sentence</mark>), and comments on the code cells above."}
|
|
118
|
+
{type: "js", content: "// summarize, chart, or filter rows here\nconsole.table(rows)"}
|
|
102
119
|
]
|
|
103
120
|
}})
|
|
104
121
|
```
|
|
105
122
|
|
|
106
123
|
### Final memo
|
|
107
124
|
```
|
|
108
|
-
|
|
109
|
-
widget_display({name: "notebook-editorial", params: {
|
|
125
|
+
widget_display({name: "notebook", params: {
|
|
110
126
|
title: "Findings memo",
|
|
111
127
|
kicker: "memo",
|
|
112
128
|
cells: [
|
|
@@ -123,7 +139,7 @@ widget_display({name: "notebook-editorial", params: {
|
|
|
123
139
|
## Common mistakes
|
|
124
140
|
|
|
125
141
|
- **Empty notebook**: never call `widget_display` without at least 3 seed cells. The user expects something they can immediately run.
|
|
126
|
-
- **Wrong layout for the intent**: do not use `notebook-editorial` for quick exploration — it signals "finished article" and intimidates. Use `notebook-compact` unless the user explicitly asks for a publication feel.
|
|
127
142
|
- **Heavy initial queries**: always `LIMIT 10` or `LIMIT 20` in seed SQL cells. Users will expand later if needed.
|
|
128
|
-
- **Missing `varname` on SQL cells
|
|
129
|
-
- **Inventing UUIDs
|
|
143
|
+
- **Missing `varname` on SQL cells**: the named output drives the reactive dataflow (downstream JS cells go stale when their upstream re-runs). Without it, the notebook loses half its story.
|
|
144
|
+
- **Inventing UUIDs**: leave `id` unset — the widget generates a sensible default. Only pass `id` when restoring an existing notebook.
|
|
145
|
+
- **Including `autoui` in `servers:`**: only MCP *data* servers (`kind: 'data'`) belong there.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Hawk proxy handler — used by apps' /api/hawk/+server.ts
|
|
3
|
+
* Hawk = OpenAI-compatible endpoint at https://hawk.hyperskills.net/v1
|
|
4
|
+
* Bearer token lives server-side in HAWK_API_KEY env var.
|
|
5
|
+
*/
|
|
6
|
+
function sanitizeId(id: string | undefined): string {
|
|
7
|
+
if (!id) return 'x';
|
|
8
|
+
const clean = id.replace(/[^a-zA-Z0-9]/g, '');
|
|
9
|
+
return clean || 'x';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Some llama-cpp chat templates (Qwen, Mistral family) enforce alphanumeric
|
|
14
|
+
* tool_call IDs via Jinja raise_exception. Gemma's template drops tool_use_id
|
|
15
|
+
* entirely (cf. gemma4-prompt-builder.ts:194) — safe passthrough.
|
|
16
|
+
* Mirrors the convention of sanitizeServerName (tool-layers.ts:24).
|
|
17
|
+
*/
|
|
18
|
+
function needsIdSanitize(model: string | null | undefined): boolean {
|
|
19
|
+
if (!model) return false;
|
|
20
|
+
return /^(qwen|mistral|ministral|devstral|codestral|bielik)/.test(model);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function hawkProxy(
|
|
24
|
+
body: Record<string, unknown>,
|
|
25
|
+
apiKey: string,
|
|
26
|
+
model?: string | null,
|
|
27
|
+
): Promise<Response> {
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
return new Response('HAWK_API_KEY missing', { status: 500 });
|
|
30
|
+
}
|
|
31
|
+
const m = model ?? 'qwen35-4b';
|
|
32
|
+
const cloned = JSON.parse(JSON.stringify(body)) as Record<string, unknown>;
|
|
33
|
+
if (needsIdSanitize(m) && Array.isArray(cloned.messages)) {
|
|
34
|
+
for (const msg of cloned.messages as Array<Record<string, unknown>>) {
|
|
35
|
+
if (msg.role === 'assistant' && Array.isArray(msg.tool_calls)) {
|
|
36
|
+
for (const tc of msg.tool_calls as Array<Record<string, unknown>>) {
|
|
37
|
+
tc.id = sanitizeId(tc.id as string | undefined);
|
|
38
|
+
}
|
|
39
|
+
} else if (msg.role === 'tool' && typeof msg.tool_call_id === 'string') {
|
|
40
|
+
msg.tool_call_id = sanitizeId(msg.tool_call_id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const res = await fetch('https://hawk.hyperskills.net/v1/chat/completions', {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({ ...cloned, model: m }),
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) return new Response(await res.text(), { status: res.status });
|
|
53
|
+
return Response.json(await res.json());
|
|
54
|
+
}
|
package/src/tool-layers.ts
CHANGED
|
@@ -456,19 +456,31 @@ export function buildDiscoveryTools(layers: ToolLayer[]): ProviderTool[] {
|
|
|
456
456
|
return tools;
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
+
/** Result of activateServerTools — extended tool set + per-call path maps for newly activated tools */
|
|
460
|
+
export interface ActivateServerToolsResult {
|
|
461
|
+
tools: ProviderTool[];
|
|
462
|
+
/** Path maps for flattened schemas of newly added tools (empty if flatten is off). */
|
|
463
|
+
pathMaps: Map<string, Record<string, string[]>>;
|
|
464
|
+
}
|
|
465
|
+
|
|
459
466
|
/**
|
|
460
467
|
* Add all tools from a specific server layer to the active tool set.
|
|
461
468
|
* Called when a server is "touched" for the first time.
|
|
469
|
+
*
|
|
470
|
+
* Returns `{ tools, pathMaps }` — `pathMaps` is non-empty when `schemaOptions.flatten`
|
|
471
|
+
* is on, and must be merged into the loop-local pathMaps so `unflattenParams` works
|
|
472
|
+
* for lazily-activated tools. Backward-compat: if you only need `tools`, read `.tools`.
|
|
462
473
|
*/
|
|
463
474
|
export function activateServerTools(
|
|
464
475
|
currentTools: ProviderTool[],
|
|
465
476
|
layer: ToolLayer,
|
|
466
477
|
schemaOptions?: SchemaTransformOptions,
|
|
467
478
|
trace?: PipelineTrace,
|
|
468
|
-
):
|
|
479
|
+
): ActivateServerToolsResult {
|
|
469
480
|
const prefix = `${sanitizeServerName(layer.serverName)}_${protocolToken(layer.protocol)}_`;
|
|
470
481
|
const existing = new Set(currentTools.map(t => t.name));
|
|
471
482
|
const newTools = [...currentTools];
|
|
483
|
+
const pathMaps = new Map<string, Record<string, string[]>>();
|
|
472
484
|
|
|
473
485
|
const layerTools = layer.protocol === 'mcp'
|
|
474
486
|
? toProviderTools(layer.tools, schemaOptions, trace)
|
|
@@ -476,12 +488,22 @@ export function activateServerTools(
|
|
|
476
488
|
|
|
477
489
|
for (const tool of layerTools) {
|
|
478
490
|
const prefixed = `${prefix}${tool.name}`;
|
|
479
|
-
if (
|
|
480
|
-
|
|
491
|
+
if (existing.has(prefixed)) continue;
|
|
492
|
+
|
|
493
|
+
let finalTool = { ...tool, name: prefixed };
|
|
494
|
+
|
|
495
|
+
if (schemaOptions?.flatten) {
|
|
496
|
+
const { schema: flatSchema, pathMap } = flattenSchema(finalTool.input_schema as import('@webmcp-auto-ui/core').JsonSchema);
|
|
497
|
+
if (Object.keys(pathMap).length > 0) {
|
|
498
|
+
finalTool.input_schema = flatSchema as Record<string, unknown>;
|
|
499
|
+
pathMaps.set(prefixed, pathMap);
|
|
500
|
+
}
|
|
481
501
|
}
|
|
502
|
+
|
|
503
|
+
newTools.push(finalTool);
|
|
482
504
|
}
|
|
483
505
|
|
|
484
|
-
return newTools;
|
|
506
|
+
return { tools: newTools, pathMaps };
|
|
485
507
|
}
|
|
486
508
|
|
|
487
509
|
/**
|
package/src/trace-observer.ts
CHANGED
|
@@ -374,9 +374,7 @@ export function createTraceObserver(ctx: TraceObserverContext): TraceObserver {
|
|
|
374
374
|
|
|
375
375
|
function flush(): void {
|
|
376
376
|
if (ids === null) return;
|
|
377
|
-
ctx.updateWidget(ids.dagId, buildCytoscapeData());
|
|
378
377
|
ctx.updateWidget(ids.treeId, buildTreeData());
|
|
379
|
-
ctx.updateWidget(ids.sankeyId, buildSankeyData());
|
|
380
378
|
}
|
|
381
379
|
|
|
382
380
|
const callbacks: Partial<AgentCallbacks> = {
|
|
@@ -602,11 +600,11 @@ export function createTraceObserver(ctx: TraceObserverContext): TraceObserver {
|
|
|
602
600
|
callbacks,
|
|
603
601
|
mount(): { dagId: string; treeId: string; sankeyId: string } | null {
|
|
604
602
|
if (ids !== null) return ids;
|
|
605
|
-
const dag = ctx.addWidget('animated-flow', buildCytoscapeData(), 'cytoscape');
|
|
606
603
|
const tree = ctx.addWidget('tree', buildTreeData(), 'd3');
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
604
|
+
if (!tree) return null;
|
|
605
|
+
// dagId/sankeyId kept as empty strings for shape compatibility — the
|
|
606
|
+
// cytoscape + sankey sub-widgets were removed in favor of the tree only.
|
|
607
|
+
ids = { dagId: '', treeId: tree.id, sankeyId: '' };
|
|
610
608
|
// Synchronous immediate flush — guarantees widgets reflect full buffer
|
|
611
609
|
// when trace is toggled mid-run (retroactive within current session).
|
|
612
610
|
flush();
|
|
@@ -633,19 +631,10 @@ export function createTraceObserver(ctx: TraceObserverContext): TraceObserver {
|
|
|
633
631
|
flushTimer = null;
|
|
634
632
|
}
|
|
635
633
|
if (ids !== null) {
|
|
636
|
-
ctx.updateWidget(ids.dagId, {
|
|
637
|
-
elements: [],
|
|
638
|
-
style: CYTOSCAPE_STYLE,
|
|
639
|
-
layout: { name: 'cose', animate: true },
|
|
640
|
-
});
|
|
641
634
|
ctx.updateWidget(ids.treeId, {
|
|
642
635
|
root: { name: 'conv', children: [] },
|
|
643
636
|
orientation: 'horizontal',
|
|
644
637
|
});
|
|
645
|
-
ctx.updateWidget(ids.sankeyId, {
|
|
646
|
-
nodes: [],
|
|
647
|
-
links: [],
|
|
648
|
-
});
|
|
649
638
|
}
|
|
650
639
|
},
|
|
651
640
|
detach(): void {
|