feat: context files for A2UI format, artifact conventions, researcher instructions
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
# A2UI Block Format
|
||||
|
||||
A2UI (AI-to-UI) lets you generate interactive UI controls directly in the chat. The
|
||||
frontend parses `a2ui` code blocks and renders them as Tabler components — filter
|
||||
toggles, sortable tables, card grids, and forms — without any page reload.
|
||||
|
||||
## How to Generate
|
||||
|
||||
Wrap a JSON object in a fenced code block with the language tag `a2ui`:
|
||||
|
||||
````
|
||||
```a2ui
|
||||
{ "type": "...", ... }
|
||||
```
|
||||
````
|
||||
|
||||
The JSON must be a single object with a `type` field. The frontend selects the
|
||||
renderer based on `type` and passes the remaining fields as props.
|
||||
|
||||
---
|
||||
|
||||
## Block Types
|
||||
|
||||
### filters
|
||||
|
||||
Renders a row of toggle chips. Use for quick category or attribute filtering.
|
||||
|
||||
```a2ui
|
||||
{
|
||||
"type": "filters",
|
||||
"options": [
|
||||
{ "label": "Toyota", "active": true },
|
||||
{ "label": "Honda", "active": false },
|
||||
{ "label": "Hyundai", "active": false },
|
||||
{ "label": "Under $5K", "active": false }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `options` | array | List of filter chips |
|
||||
| `options[].label` | string | Display text for the chip |
|
||||
| `options[].active` | boolean | Whether the chip is pre-selected |
|
||||
|
||||
---
|
||||
|
||||
### table
|
||||
|
||||
Renders a sortable data table. Use for structured comparisons across multiple items.
|
||||
|
||||
```a2ui
|
||||
{
|
||||
"type": "table",
|
||||
"headers": ["Make/Model", "Year", "Price", "MPG", "Mileage"],
|
||||
"rows": [
|
||||
["Toyota Prius", "2019", "$8,500", "52 mpg", "61,000 mi"],
|
||||
["Honda Insight", "2018", "$9,200", "49 mpg", "55,000 mi"],
|
||||
["Hyundai Ioniq", "2019", "$8,900", "55 mpg", "48,000 mi"],
|
||||
["Toyota Camry Hybrid", "2017", "$7,800", "46 mpg", "72,000 mi"],
|
||||
["Ford Fusion Hybrid", "2018", "$7,200", "42 mpg", "68,000 mi"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `headers` | string[] | Column header labels |
|
||||
| `rows` | string[][] | Data rows; each row must match header count |
|
||||
|
||||
---
|
||||
|
||||
### cards
|
||||
|
||||
Renders a card grid. Use for item listings where each result deserves its own tile.
|
||||
|
||||
```a2ui
|
||||
{
|
||||
"type": "cards",
|
||||
"items": [
|
||||
{
|
||||
"title": "2019 Toyota Prius LE",
|
||||
"description": "61,000 miles · Hybrid · Clean title · Seattle, WA",
|
||||
"price": "$8,500",
|
||||
"url": "https://www.cargurus.com/Cars/listings/prius-example"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `items` | array | List of card objects |
|
||||
| `items[].title` | string | Card heading |
|
||||
| `items[].description` | string | Supporting detail text |
|
||||
| `items[].price` | string | Price label (formatted string, e.g. `"$8,500"`) |
|
||||
| `items[].url` | string | Link destination for the card action |
|
||||
|
||||
---
|
||||
|
||||
### form
|
||||
|
||||
Renders a data-collection form. Use when you need user input to refine a search or
|
||||
configure a follow-up action.
|
||||
|
||||
```a2ui
|
||||
{
|
||||
"type": "form",
|
||||
"fields": [
|
||||
{ "name": "max_price", "type": "number", "label": "Max Price ($)", "value": 10000 },
|
||||
{ "name": "radius", "type": "number", "label": "Search Radius (miles)", "value": 25 },
|
||||
{
|
||||
"name": "fuel_type",
|
||||
"type": "select",
|
||||
"label": "Fuel Type",
|
||||
"options": ["hybrid", "electric", "any"]
|
||||
}
|
||||
],
|
||||
"submitLabel": "Search"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `fields` | array | Ordered list of form fields |
|
||||
| `fields[].name` | string | Field identifier (sent on submit) |
|
||||
| `fields[].type` | string | `"number"`, `"text"`, `"select"` |
|
||||
| `fields[].label` | string | Human-readable label |
|
||||
| `fields[].value` | any | Default value (for `number`/`text` fields) |
|
||||
| `fields[].options` | string[] | Choice list (for `select` fields) |
|
||||
| `submitLabel` | string | Text on the submit button |
|
||||
|
||||
---
|
||||
|
||||
## Rules
|
||||
|
||||
- **Always use the `a2ui` language tag.** Any other tag (e.g. `json`) will render as
|
||||
plain code; the frontend will not parse it.
|
||||
- **JSON must be a single object with a `type` field.** Arrays and multi-root
|
||||
documents are not supported.
|
||||
- **Generate A2UI when the user benefits from interactive controls.** Filter chips,
|
||||
sortable tables, card grids, and forms add real value when the user is comparing
|
||||
options or needs to refine results.
|
||||
- **Don't overuse.** Plain markdown lists and tables are fine for simple answers. Use
|
||||
A2UI only when interactivity meaningfully improves the experience.
|
||||
@@ -0,0 +1,84 @@
|
||||
# Artifact Conventions
|
||||
|
||||
Artifacts are persistent markdown documents written to the filesystem. They appear
|
||||
in the right panel and survive across sessions, making them ideal for research worth
|
||||
saving: comparison guides, curated lists, reports, and actionable recommendations.
|
||||
|
||||
## Writing Pattern
|
||||
|
||||
Use the `write_file` tool to create artifacts:
|
||||
|
||||
```python
|
||||
write_file("artifacts/<session_id>/<name>.md", content)
|
||||
```
|
||||
|
||||
The `session_id` is available in your context. Use it to namespace files per session
|
||||
so multiple sessions don't overwrite each other's work.
|
||||
|
||||
## File Naming
|
||||
|
||||
- **Format**: kebab-case
|
||||
- **Extension**: `.md`
|
||||
- **Style**: Descriptive, specific — say what the file contains
|
||||
|
||||
```
|
||||
artifacts/<session_id>/hybrid-cars-under-10k-woodinville.md
|
||||
artifacts/<session_id>/project-management-tools-comparison.md
|
||||
artifacts/<session_id>/seattle-tokyo-flights-may-2025.md
|
||||
```
|
||||
|
||||
## Content Format
|
||||
|
||||
Each artifact should follow this structure:
|
||||
|
||||
1. **H1 title** — Clear, specific heading
|
||||
2. **Date and scope summary** — When the research was done and what it covers
|
||||
3. **Markdown tables** for side-by-side comparisons (price, features, ratings)
|
||||
4. **Bullet points** for recommendations, pros/cons, and key takeaways
|
||||
5. **Source URLs** — Link to each page or listing referenced
|
||||
6. **Closing section** — End with "Next Steps" or "Summary"
|
||||
|
||||
```markdown
|
||||
# Hybrid Cars Under $10K — Woodinville WA (May 2025)
|
||||
|
||||
**Researched:** May 2025 · **Scope:** CarGurus, Autotrader, Craigslist within 30 mi
|
||||
|
||||
## Comparison
|
||||
|
||||
| Make/Model | Year | Price | MPG | Mileage |
|
||||
|---|---|---|---|---|
|
||||
| Toyota Prius LE | 2019 | $8,500 | 52 | 61,000 mi |
|
||||
| Honda Insight EX | 2018 | $9,200 | 49 | 55,000 mi |
|
||||
|
||||
## Recommendations
|
||||
|
||||
- **Best value**: 2019 Prius LE — lowest price/mile ratio, clean title
|
||||
- **Best mileage**: Hyundai Ioniq at 55 mpg city/highway combined
|
||||
|
||||
## Sources
|
||||
|
||||
- https://www.cargurus.com/Cars/new/nl-Hybrid-d2335
|
||||
- https://www.autotrader.com/cars-for-sale/hybrid
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Schedule test drives for the Prius and Ioniq listings
|
||||
- Verify CarFax reports before committing
|
||||
```
|
||||
|
||||
## When to Create an Artifact
|
||||
|
||||
Create an artifact when the output is:
|
||||
|
||||
- A **guide, report, or summary** the user will want to reference later
|
||||
- A **comparison table** across multiple options or products
|
||||
- **Actionable recommendations** with supporting evidence
|
||||
- Something **printable or shareable** with others
|
||||
|
||||
## When NOT to Create an Artifact
|
||||
|
||||
Skip artifact creation for:
|
||||
|
||||
- **Simple answers** — a one-sentence reply doesn't need a file
|
||||
- **Intermediate research steps** — notes you take while browsing, not the final output
|
||||
- **Status updates** — progress messages mid-task
|
||||
@@ -0,0 +1,24 @@
|
||||
# Research Workbench — Session Context
|
||||
|
||||
This session runs inside the **Research Workbench**. You have access to the
|
||||
following capabilities:
|
||||
|
||||
## Available Capabilities
|
||||
|
||||
**Browser** — Live Chromium browser controlled via `playwright-cli -s=research`.
|
||||
The browser is persistent across commands and visible to the user via VNC (noVNC
|
||||
panel). The user can take over at any time.
|
||||
|
||||
**Artifacts** — Persistent markdown documents written to `artifacts/<session_id>/`.
|
||||
They appear in the right panel and survive across sessions.
|
||||
|
||||
**A2UI** — Inline interactive controls (filter chips, tables, card grids, forms)
|
||||
rendered directly in chat via `a2ui` code blocks.
|
||||
|
||||
**Inline Visuals** — Rich visual output (charts, maps, comparison tables with color
|
||||
coding) rendered directly in chat via `visual` code blocks.
|
||||
|
||||
## Full Command Reference
|
||||
|
||||
For complete command syntax, workflow guidance, and best practices, see the
|
||||
researcher agent system prompt.
|
||||
Executable
+191
@@ -0,0 +1,191 @@
|
||||
#!/bin/bash
|
||||
# test_context_files.sh - Verify bundle/context/{a2ui-format,artifact-conventions,researcher-instructions}.md (task-4)
|
||||
# RED: Run before files are created (should fail)
|
||||
# GREEN: Run after files are created (should pass)
|
||||
|
||||
REPO=/home/ken/workspace/research-workbench
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
check_file_exists() {
|
||||
local path="$1"
|
||||
if [ -f "$REPO/$path" ]; then
|
||||
echo "PASS: '$path' exists"
|
||||
((PASS++))
|
||||
else
|
||||
echo "FAIL: '$path' should exist but is missing"
|
||||
((FAIL++))
|
||||
fi
|
||||
}
|
||||
|
||||
check_contains() {
|
||||
local file="$1"
|
||||
local description="$2"
|
||||
local pattern="$3"
|
||||
if grep -qF -- "$pattern" "$REPO/$file" 2>/dev/null; then
|
||||
echo "PASS: $file contains '$description'"
|
||||
((PASS++))
|
||||
else
|
||||
echo "FAIL: $file is missing '$description'"
|
||||
((FAIL++))
|
||||
fi
|
||||
}
|
||||
|
||||
check_regex() {
|
||||
local file="$1"
|
||||
local description="$2"
|
||||
local pattern="$3"
|
||||
if grep -qE "$pattern" "$REPO/$file" 2>/dev/null; then
|
||||
echo "PASS: $file matches '$description'"
|
||||
((PASS++))
|
||||
else
|
||||
echo "FAIL: $file is missing '$description' (pattern: $pattern)"
|
||||
((FAIL++))
|
||||
fi
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# a2ui-format.md
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
|
||||
A2UI="bundle/context/a2ui-format.md"
|
||||
|
||||
echo "=== Checking $A2UI exists ==="
|
||||
check_file_exists "$A2UI"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking a2ui-format.md: fenced code block with language 'a2ui' ==="
|
||||
check_contains "$A2UI" "a2ui fenced code block" '```a2ui'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking a2ui-format.md: block type 'filters' ==="
|
||||
check_contains "$A2UI" "filters type" '"type": "filters"'
|
||||
check_contains "$A2UI" "filters options array" '"options"'
|
||||
check_contains "$A2UI" "Toyota option" '"Toyota"'
|
||||
check_contains "$A2UI" "Honda option" '"Honda"'
|
||||
check_contains "$A2UI" "Hyundai option" '"Hyundai"'
|
||||
check_contains "$A2UI" "Under \$5K option" '"Under $5K"'
|
||||
check_contains "$A2UI" "active field" '"active"'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking a2ui-format.md: block type 'table' ==="
|
||||
check_contains "$A2UI" "table type" '"type": "table"'
|
||||
check_contains "$A2UI" "table headers" '"headers"'
|
||||
check_contains "$A2UI" "table rows" '"rows"'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking a2ui-format.md: block type 'cards' ==="
|
||||
check_contains "$A2UI" "cards type" '"type": "cards"'
|
||||
check_contains "$A2UI" "cards items" '"items"'
|
||||
check_contains "$A2UI" "card title field" '"title"'
|
||||
check_contains "$A2UI" "card description field" '"description"'
|
||||
check_contains "$A2UI" "card price field" '"price"'
|
||||
check_contains "$A2UI" "card url field" '"url"'
|
||||
check_contains "$A2UI" "Prius listing" 'Prius'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking a2ui-format.md: block type 'form' ==="
|
||||
check_contains "$A2UI" "form type" '"type": "form"'
|
||||
check_contains "$A2UI" "form fields" '"fields"'
|
||||
check_contains "$A2UI" "submitLabel field" '"submitLabel"'
|
||||
check_contains "$A2UI" "max_price field name" '"max_price"'
|
||||
check_contains "$A2UI" "radius field name" '"radius"'
|
||||
check_contains "$A2UI" "fuel_type field name" '"fuel_type"'
|
||||
check_contains "$A2UI" "select field type for fuel_type" '"select"'
|
||||
check_contains "$A2UI" "hybrid option" '"hybrid"'
|
||||
check_contains "$A2UI" "electric option" '"electric"'
|
||||
check_contains "$A2UI" "any option" '"any"'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking a2ui-format.md: Rules section ==="
|
||||
check_regex "$A2UI" "Rules section" "^## Rules|^# Rules|Rules"
|
||||
check_contains "$A2UI" "always use a2ui language tag" 'a2ui'
|
||||
check_regex "$A2UI" "JSON must be single object with type field" 'type.*field|\"type\"'
|
||||
check_regex "$A2UI" "generate when user benefits" 'benefit|interactive'
|
||||
check_regex "$A2UI" "don't overuse" "overuse|don.t overuse|sparingly"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# artifact-conventions.md
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
|
||||
ARTIFACT="bundle/context/artifact-conventions.md"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking $ARTIFACT exists ==="
|
||||
check_file_exists "$ARTIFACT"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking artifact-conventions.md: write_file tool usage ==="
|
||||
check_contains "$ARTIFACT" "write_file tool call" 'write_file'
|
||||
check_regex "$ARTIFACT" "artifacts/<session_id> path pattern" 'artifacts/.*session'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking artifact-conventions.md: file naming conventions ==="
|
||||
check_regex "$ARTIFACT" "kebab-case naming" 'kebab.case|kebab-case'
|
||||
check_contains "$ARTIFACT" ".md extension" '.md'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking artifact-conventions.md: content format ==="
|
||||
check_regex "$ARTIFACT" "H1 title requirement" 'H1|# title|heading'
|
||||
check_regex "$ARTIFACT" "markdown tables for comparisons" '[Mm]arkdown.*table|table.*comparison|comparison.*table'
|
||||
check_regex "$ARTIFACT" "bullet points for recommendations" 'bullet|recommendation'
|
||||
check_regex "$ARTIFACT" "source URLs" '[Ss]ource.*URL|URL.*source|source url'
|
||||
check_regex "$ARTIFACT" "Next Steps or Summary ending" 'Next Steps|Summary'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking artifact-conventions.md: when to create ==="
|
||||
check_regex "$ARTIFACT" "guides/reports/summaries" 'guide|report|summar'
|
||||
check_regex "$ARTIFACT" "comparison tables" 'comparison'
|
||||
check_regex "$ARTIFACT" "actionable recommendations" 'actionable|recommendation'
|
||||
check_regex "$ARTIFACT" "printable/shareable" 'printable|shareable'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking artifact-conventions.md: when NOT to create ==="
|
||||
check_regex "$ARTIFACT" "not for simple answers" '[Ss]imple answer|not.*simple|simple.*answer'
|
||||
check_regex "$ARTIFACT" "not for intermediate research" 'intermediate|research step|status update'
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# researcher-instructions.md
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
|
||||
RESEARCHER="bundle/context/researcher-instructions.md"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking $RESEARCHER exists ==="
|
||||
check_file_exists "$RESEARCHER"
|
||||
|
||||
echo ""
|
||||
echo "=== Checking researcher-instructions.md: Research Workbench context ==="
|
||||
check_regex "$RESEARCHER" "Research Workbench mention" '[Rr]esearch [Ww]orkbench'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking researcher-instructions.md: Browser capability ==="
|
||||
check_regex "$RESEARCHER" "Browser with Playwright/Chromium" '[Bb]rowser|playwright|Chromium'
|
||||
check_contains "$RESEARCHER" "playwright-cli session flag" '-s=research'
|
||||
check_regex "$RESEARCHER" "VNC mention" 'VNC|vnc|noVNC'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking researcher-instructions.md: Artifacts capability ==="
|
||||
check_regex "$RESEARCHER" "Artifacts capability" '[Aa]rtifact'
|
||||
check_regex "$RESEARCHER" "artifacts/<session_id> path" 'artifacts/.*session|session.*artifacts'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking researcher-instructions.md: A2UI capability ==="
|
||||
check_regex "$RESEARCHER" "A2UI capability" 'A2UI|a2ui'
|
||||
check_regex "$RESEARCHER" "inline a2ui code blocks" 'inline|code block'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking researcher-instructions.md: Inline Visuals capability ==="
|
||||
check_regex "$RESEARCHER" "Inline Visuals" '[Ii]nline [Vv]isual|visual.*block'
|
||||
check_regex "$RESEARCHER" "visual code blocks" 'visual.*block|code.*block.*visual'
|
||||
|
||||
echo ""
|
||||
echo "=== Checking researcher-instructions.md: pointer to researcher agent ==="
|
||||
check_regex "$RESEARCHER" "pointer to researcher agent" 'researcher.*agent|researcher.*command|full.*command|command.*reference'
|
||||
|
||||
echo ""
|
||||
echo "=============================="
|
||||
echo "Results: $PASS passed, $FAIL failed"
|
||||
echo "=============================="
|
||||
|
||||
[ $FAIL -eq 0 ] && exit 0 || exit 1
|
||||
Reference in New Issue
Block a user