Commit Graph

282 Commits

Author SHA1 Message Date
Mark Fogle 4640d6304e fix(showcase/mastra): useComponent (gen-ui-tool-based) — probe took the wrong path
The "Generative UI: useComponent" cell (gen-ui-tool-based) went red on mastra
the moment OSS-381 took it out of not_supported: the D5 gen-ui-custom probe
sent the *haiku* prompt and hunted for a haiku card, but mastra's demo is the
LGP-style `useComponent` chart demo (render_pie_chart / render_bar_chart) with
no haiku tool — so the assistant bubble came back empty ("haiku card
[data-testid=copilot-assistant-message] rendered but has no text content").

Root cause: the probe's CHART_INTEGRATIONS allowlist in
harness/src/probes/scripts/d5-gen-ui-custom.ts omitted mastra, so
isChartIntegration("mastra") was false and it took the haiku branch. mastra's
gen-ui-tool-based page registers render_pie_chart / render_bar_chart via
useComponent exactly like langgraph-python and google-adk.

- Add "mastra" to CHART_INTEGRATIONS so the probe sends the pie-chart prompt
  and asserts the donut SVG + "pie"/"chart" follow-up tokens.
- Add aimock/d6/mastra/gen-ui-custom.json (mirrors langgraph-python's, context:
  mastra; the pie schema is identical — {title, description, data:[{label,value}]})
  so the cell is deterministic under aimock replay instead of falling through to
  the live upstream.
- Repoint the probe unit test's haiku-empty-card case from "mastra" to "agno"
  (a genuine haiku integration) now that mastra is a chart integration.

Not a v1-bridge streaming regression — a harness/fixture gap exposed when the
cell was un-suppressed. Harness unit tests not run locally (sparse showcase
checkout has no vitest); logic-only changes.

--no-verify: sparse showcase checkout has no monorepo lefthook/commitlint binaries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 05:13:29 +00:00
Mark f986cff0cb Merge branch 'main' into claude/brave-kirch-8dbf00 2026-07-21 10:10:45 -07:00
Mark Fogle f98b73b3d3 fix(showcase/mastra): headless-simple fun-fact pill — stop d4 chat fixture hijacking it
headless-simple's "Give me a fun fact." pill rendered the wrong text
("Here's a fun fact: honey never spoils…") and the e2e failed. Root cause:
d4/mastra/chat.json keyed the prebuilt-sidebar agentic-chat fixture on the
bare "fun fact", which is a substring of the headless-simple pill "Give me a
fun fact." Because d4 loads before d6, that fixture shadowed headless-simple's
own d6 fixture ("A fun fact: Honey never spoils!").

Re-key it to the unique "fun fact for the prebuilt sidebar" phrase (mirrors
gold langgraph-python), so it no longer substring-collides. The prebuilt-sidebar
demo pill "Give me a fun fact." now shares headless-simple's d6 fun-fact
fixture, exactly as gold does; prebuilt-sidebar/popup e2e unaffected.

Verified (Node 22 + next start + aimock 1.37.4 replay): headless-simple 4/4,
prebuilt-sidebar 4/4, prebuilt-popup 4/4 — and the full mastra card/chat suite
green (tool-rendering 6/6, tool-rendering-reasoning-chain 5/5, headless-complete
5/5, beautiful-chat 8/8, agentic-chat 4/4). Only a userMessage key changed
(more specific → strictly fewer aimock substring-shadows, well under the
KNOWN_SHADOW_CEILING).

--no-verify: sparse showcase checkout has no monorepo lefthook/commitlint binaries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 17:10:08 +00:00
Mark Fogle 3c13a0881e fix(showcase/mastra): headless-complete cards render — agent tool-registration + get_revenue_chart + scriptable weather
headless-complete is marked supported but its WeatherCard / StockCard / ChartCard
stalled in the "running" state. headlessCompleteAgent registered tools via object
shorthand ({ weatherTool, stockPriceTool }), which exposes the JS variable names
instead of the snake_case names the aimock fixtures + useRenderTool renderers emit
(get_weather / get_stock_price / get_revenue_chart) — so the scripted tool calls
were never executable — and get_revenue_chart had no backend tool at all.

- Re-key headlessCompleteAgent to explicit { get_weather, get_stock_price,
  get_revenue_chart } (mirrors gold langgraph-python headless_complete.py).
- Add revenueChartTool (id get-revenue-chart) returning gold's fixed payload
  { title: "Quarterly revenue", subtitle, data: [6x {label,value}] }.
- Make weatherTool accept optional scripted temperature/conditions/humidity/
  wind_speed (echoed when provided, else the seeded getWeatherImpl) — mirrors
  get_stock_price's scripted price_usd. Gold's headless get_weather is a fixed
  68 degF / Sunny mock while mastra's is seeded, so the headless weather fixtures
  script 68/Sunny to match gold's card; tool-rendering's SF pill keeps its seeded
  value. Scripted the winning headless-complete + gen-ui-headless-complete
  "What's the weather in Tokyo" legs and aligned the narration to gold. (No gold/
  shared backend touched — mastra tool + mastra fixtures only.)

Verified (Node 22 + next build/start + aimock 1.37.4 replay): headless-complete
5/5; tool-rendering 6/6, tool-rendering-reasoning-chain 5/5, beautiful-chat 8/8,
agentic-chat and headless-simple weather unaffected — no regression.

--no-verify: sparse showcase checkout has no monorepo lefthook/commitlint binaries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 16:57:22 +00:00
Mark Fogle 9bf38034a6 fix(showcase/mastra): tool-rendering + reasoning-chain e2e green via gold fixture parity
The tool-rendering cell (marked supported) had 5/6 e2e failing and
tool-rendering-reasoning-chain 2/5, all from mastra aimock fixtures diverging
from the langgraph-python gold standard. Root cause: several fixtures were
keyed on generic substrings where gold uses unique tails, so they
substring-collided with the longer chain pills and — loading earlier
(alphabetical file order) — hijacked them.

Verified on a faithful rig (Node 22 + next build/start + aimock 1.37.4 replay):

- Stock: the pill's scripted $338.37 fixture was shadowed by
  headless-complete's ticker-only "price of AAPL" leg (tool's 189.42 default).
  Restore gold's unique "price of AAPL right now" key; key the tool-rendering
  emit leg on toolName (gold parity).
- d20: the first-roll leg gated on hasToolResult:false never matched once
  prior-pill tool results lingered in thread history -> 0 cards. Match on
  userMessage only (gold). Add stopWhen: stepCountIs(8) to toolRenderingAgent
  so the 5-roll sequence + narration (and the 3-tool chain-tools turn) run to
  completion instead of stopping at the default step cap.
- chain-tools: headless-complete's generic "weather in Tokyo" leg hijacked the
  "...get the weather in Tokyo..." pill and emitted only get_weather. Restore
  gold's "What's the weather in Tokyo" key.
- reasoning-chain flights+weather + sequential: beautiful-chat's generic
  "Find flights from SFO to JFK" legs hijacked the "...JFK and show me the
  weather there" pill. Restore gold's "for next Tuesday" key.

Result: tool-rendering 6/6, tool-rendering-reasoning-chain 5/5, beautiful-chat
8/8 (no regression) under aimock replay.

Note: headless-complete's own weather/stock/revenue cards remain red on a
separate pre-existing bug (headlessCompleteAgent tool-registration + a missing
get_revenue_chart tool) — addressed in a follow-up commit.

--no-verify: this sparse showcase checkout has no monorepo lefthook/commitlint
binaries (matches prior commits on this branch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 16:33:48 +00:00
Ran Shem Tov 60f2771830 fix(showcase/mastra): tool-rendering parity — dedicated agent, roll_d20, gold search_flights (CR #5798)
Addresses the tool-rendering CHANGES_REQUESTED on #5798 (5/6 e2e failing),
mirroring gold langgraph-python tool_rendering_agent.py:

1. Dedicated toolRenderingAgent bound to all four demo tools (get_weather,
   search_flights, get_stock_price, roll_d20) + route tool-rendering and its
   default/custom-catchall variants to it. Previously routed to weatherAgent,
   which lacks get_stock_price/roll_d20, so the Stock, d20, and Chain pills
   emitted uncallable tool calls the AI SDK dropped (no card).
2. New deterministic roll_d20 tool (echoes a scripted value) and get_stock_price
   now echoes optional price_usd/change_pct — lets the fixtures script exact
   values, mirroring gold's roll_d20/get_stock_price.
3. search_flights now accepts gold {origin,destination} and GENERATES the
   flights list (kept lenient — still accepts a legacy {flights} array so the
   D5 harness probe keeps working). Fixes the reasoning-chain flights leg that
   failed input validation.
4. Re-keyed the tool-rendering + reasoning-chain fixtures off the fragile
   turnIndex onto hasToolResult:false (turn-scoped in aimock >=1.37.x) and
   added the missing Find-flights first leg (was 'No fixture matched' 404).
5. Weather e2e assertion -> 77%/3 (mastra's seeded getWeatherImpl for SF; a
   documented divergence from gold's fixed 55/10).

Route /api/copilotkit compiles + loads cleanly; full e2e to be confirmed via
/eval d5 mastra on the harness (local next-dev verification is blocked by this
machine's 7.7GiB Docker VM OOMing on the heavy route compile).
2026-07-21 08:34:11 +00:00
Jordan Ritter 107a17e493 fix(showcase): wire langroid declarative-gen-ui via Option A (JS-injected A2UI) (#6070)
## Summary

Supersedes #6058's two-stage approach (outer `generate_a2ui` → secondary
Python LLM call → `render_a2ui`), which severed across the prod
streaming boundary.

**Option A** (mirroring crewai-crews #6067): `injectA2UITool` defaults
to `true`, so CopilotKit's `A2UIMiddleware` injects `render_a2ui` into
`RunAgentInput.tools`. The langroid `agui_adapter.py` now merges those
injected tools into its OpenAI call, so the LLM calls `render_a2ui`
directly. The middleware intercepts the tool call stream, builds
`a2ui_operations`, and fires `RUN_FINISHED` — no secondary Python LLM
pass needed.

## Changes

- **`agent.py`**: Remove ~550 lines of two-stage A2UI infrastructure
(`generate_a2ui_via_llm`, `_a2ui_error`, `_resolve_a2ui_model`,
`_get_a2ui_llm`, `_RENDER_A2UI_FUNCTION_SPEC`, etc.). Replace
`GenerateA2UITool.handle` with a stub that logs loudly on regression
(middleware should always intercept before reaching Python).
- **`agui_adapter.py`**: Merge `run_input.tools` (AG-UI-injected) into
the OpenAI tools list so `render_a2ui` is visible to the LLM. Remove
`set_last_user_message` call (ContextVar no longer needed).
- **`route.ts`**: Remove `injectA2UITool: false`; keep
`defaultCatalogId` pin.
- **`gen-ui-declarative.json`**: Replace 9 two-stage fixtures with 4
single-stage fixtures matching `toolName: render_a2ui` + `context:
langroid`.

## Root cause of prior RED

The langroid adapter builds its OpenAI tool list from `ALL_TOOLS`
(Python-side registry) via `_get_openai_tools()`, which does NOT include
`render_a2ui`. The `A2UIMiddleware` injects `render_a2ui` into
`RunAgentInput.tools` at the AG-UI protocol level, but `agui_adapter.py`
ignored `run_input.tools` entirely — so the LLM never saw `render_a2ui`
in its tool list, never called it, and the fixture never matched.

## Red-green proof

**RED** (from main, before changes):
```
✗ d6:langroid/gen-ui-declarative  red  (0.0s)
  state=red
0 passed, 1 failed
```

**GREEN** (after this PR's changes, rebuild from worktree):
```
✓ d6:langroid/gen-ui-declarative  green  (0.0s)
1 passed
✓ Tests passed for langroid:declarative-gen-ui
```

Test command: `bin/showcase test langroid:declarative-gen-ui --d6
--isolate --rebuild`

## Related

- Supersedes #6058 (two-stage approach, now reverted in this
integration)
- Mirrors #6067 (crewai-crews Option A fix, same pattern)
2026-07-20 12:25:40 -07:00
Jordan Ritter 46f810779c fix(showcase): wire langroid declarative-gen-ui via Option A (JS-injected A2UI)
Supersedes #6058's two-stage approach (outer generate_a2ui → secondary Python
LLM call → render_a2ui), which severed across the prod streaming boundary.

Option A: `injectA2UITool` (default true) causes CopilotKit's A2UIMiddleware to
inject `render_a2ui` into RunAgentInput.tools. The langroid agui_adapter now
merges those injected tools into its OpenAI call so the LLM can call render_a2ui
directly. The middleware intercepts the tool call stream, builds a2ui_operations,
and fires RUN_FINISHED — no secondary Python LLM pass needed.

Changes:
- agent.py: remove ~550 lines of two-stage A2UI infrastructure (generate_a2ui_via_llm,
  _a2ui_error, _resolve_a2ui_model, _get_a2ui_llm, etc.); replace GenerateA2UITool.handle
  with a stub that logs loudly if middleware interception regresses
- agui_adapter.py: merge run_input.tools (AG-UI-injected) into the OpenAI tools list
  so render_a2ui is visible to the LLM; remove set_last_user_message call
- route.ts: remove injectA2UITool: false; keep defaultCatalogId pin
- gen-ui-declarative.json: replace 9 two-stage fixtures with 4 single-stage fixtures
  matching toolName: render_a2ui + context: langroid (mirrors crewai-crews Option A)

Mirrors the crewai-crews fix from #6067.
2026-07-20 12:04:40 -07:00
Jordan Ritter 0408f821a7 fix(showcase): wire ag2 declarative-gen-ui via Option A (JS-injected A2UI)
Remove the backend two-stage inner-LLM pattern (injectA2UITool:false +
Python-side secondary openai call) in favour of Option A: the CopilotKit
JS runtime middleware intercepts the agent's no-arg generate_a2ui toolcall
and drives the render_a2ui secondary LLM pass itself, synthesising the
tool result and firing RUN_FINISHED. Matches the just-merged crewai-crews
fix (#6067) and mirrors langgraph-python's green reference pattern.

Changes:
- route.ts: drop `injectA2UITool: false` (default true enables JS injection)
- a2ui_dynamic.py: replace complex inner-LLM body with a fail-loud stub
  (no more openai/AsyncOpenAI import, no _request_context dependency,
  no tools/RENDER_A2UI_TOOL_SCHEMA import)
- gen-ui-declarative.json: update _meta note + _comment fields to reflect
  Option A (fixture structure was already correct for two-stage aimock
  matching; outer generate_a2ui matched by context:ag2, inner render_a2ui
  matched by toolName:render_a2ui)

Red→Green: D6 control-plane harness confirmed red before (state=red,
exit 1) and green after (1 passed, exit 0).
2026-07-20 11:43:54 -07:00
Jordan Ritter f44cb57669 fix(showcase): wire crewai-crews declarative-gen-ui via Option A (JS-injected A2UI)
Switch the crewai-crews gen-ui-declarative cell from a broken Option B
(Python-side injection, which has no mechanism in the ag_ui_crewai adapter)
to Option A (JS-runtime-injected A2UI):

- route.ts: remove `injectA2UITool: false`; keep `defaultCatalogId` to pin
  the catalog so models that omit catalogId don't get a "Catalog not found"
  render error.
- declarative_gen_ui.py: replace `GenerateA2uiTool` with a no-arg
  `_GenerateA2uiNoArgTool` that raises loudly if called directly (the
  A2UIMiddleware should always intercept before Python).
- definitions.ts + renderers.tsx: add `DataTable` component (columns/rows
  schema + `data-testid="declarative-data-table"` renderer); add missing
  `data-testid="declarative-info-row"` to the `InfoRow` renderer.  Both
  testids are required by the D5 harness (turns 2 and 4 respectively) and
  were present in the langgraph-python reference catalog but absent here.
- gen-ui-declarative.json: rewrite D6 aimock fixtures from the old broken
  two-stage pattern (generate_a2ui → inner render_a2ui) to the correct
  single-stage pattern (LLM calls render_a2ui directly); all four pills
  now match `toolName: render_a2ui, context: crewai-crews` and return full
  component trees that satisfy the harness minCounts assertions.

RED (main): "CrewAI flow failed; see server logs" on every pill —
  `injectA2UITool: false` disabled the middleware; no render_a2ui tool in
  the agent's tool list; aimock fixture matcher never fired; no surface.
GREEN (this branch): all 4 turns pass with assertions, 1 passed (0.0s).
2026-07-20 10:02:07 -07:00
Ran Shem Tov dd8495bb25 fix(showcase/mastra): render declarative-gen-ui + wire hashbrown/json-render demos
declarative-gen-ui (render-a2ui.json): the outer generate_a2ui fixtures
used the stale {context} signature (the generate-a2ui tool now requires
'messages' -> input validation failed, so the inner secondary LLM never
ran) and the inner render_a2ui fixtures gated on context:mastra, which
the secondary-LLM request never carries. Rewrite generate_a2ui args to
carry 'messages' and match the inner render_a2ui on toolName only. All
four pills (KPI, pie, bar, status) now render their A2UI surface.

declarative-hashbrown / declarative-json-render (page.tsx): the demos
pointed runtimeUrl at non-existent routes (/api/copilotkit-declarative-*,
404 -> agent not found). Point them at the existing byoc runtime routes
(/api/copilotkit-byoc-hashbrown, /api/copilotkit-byoc-json-render) and
fix the hashbrown agent id to the registered 'byoc-hashbrown-demo'.

Verified with Playwright (gen-ui on the live container; hashbrown and
json-render on a next-dev build of the fixed pages).
2026-07-20 14:21:41 +00:00
Ran Shem Tov 6299420e20 fix(showcase/mastra): render Sales Dashboard + Search Flights A2UI in beautiful-chat
Sales Dashboard (A2UI Dynamic) never produced a surface: the outer
generate_a2ui fixture carried empty {} args (generate_a2ui input
validation failed on the required 'messages' field) and the inner
render_a2ui fixture gated on context:mastra, which the secondary-LLM
request never carries. Fix, mirroring the working gen-ui-declarative
pattern: give generate_a2ui the pill message, match the inner
render_a2ui on toolName only, gate the outer on userMessage+context
only (dropping the thread-global hasToolResult gate that broke 2nd+
clicks), and add a toolCallId-anchored narration to prevent a
generate_a2ui loop on the post-tool turn.

Search Flights (A2UI Fixed Schema): add the 3-leg generate_a2ui ->
render_a2ui -> narration fixtures (United $349, Delta $289) against
app-dashboard-catalog.

Verified with Playwright: both render as first-click, 2nd-click, and
repeat clicks, with no 'Catalog not found' error.
2026-07-20 13:53:16 +00:00
Ran Shem Tov 93c7369e85 Merge remote-tracking branch 'origin/main' into claude/brave-kirch-8dbf00
# Conflicts:
#	showcase/scripts/__tests__/aimock-fixtures.test.ts
2026-07-20 11:14:15 +02:00
Jordan Ritter 090c080581 fix(showcase): langroid declarative gen-ui → two-stage A2UI north-star (D6 turn-1 surface-missing) (#6058)
## What

Brings the **langroid** `declarative-gen-ui` D6 cell to sibling parity
with the two-stage dynamic-schema A2UI north-star (google-adk /
strands). The cell was red at turn 1 with `reason=surface-missing`: the
demo was still on the pre-D6 (D5-era) shape, and four independent
defects each blocked the A2UI surface from painting.

Second-wave fan-out of the proven pattern (pilots #6051/#6052/#6053,
first wave #6054 agno / #6055 claude-sdk-typescript).

## Root cause (four defects, each verified against a live isolated
stack)

1. **Stale suggestions + fixtures.** `suggestions.ts` still offered the
old D5 pills; the aimock fixture only mocked those. The D6 driver sends
the four current business-question prompts. Re-authored both to the four
current prompts mirroring the google-adk north-star (outer
`generate_a2ui` no-arg → inner forced `render_a2ui` → outer narration).
2. **Required `context` on the outer tool.** `GenerateA2UITool.context`
was a required pydantic field, so the mocked outer `arguments: {}`
raised `ValidationError` before the tool ran → no inner call, no
surface. Made optional (default `""`) to match the no-arg sibling tools.
3. **Legacy functions API hid the inner tool from aimock's matcher.**
The inner planner used langroid's `functions=`/`function_call=` (legacy
OpenAI) path; aimock's `toolName` matcher only inspects the modern
`tools[]` array, so the inner `render_a2ui` fixture never matched and
the call fell through to the outer `generate_a2ui` fixture (empty
surface, wrong catalogId). Switched the inner call to the modern
`tools=`/`tool_choice=` API. The response extractor already reads the
modern `oai_tool_calls` path first, so nothing downstream changes.
4. **Inner call could not be discriminated per pill.** langroid has no
framework middleware to forward the run's conversation into the inner
call (unlike `ag_ui_adk` / `ag_ui_strands`), so its inner user message
was a fixed generic string across all four pills. Added an explicit
last-user-turn thread (a `ContextVar` set by the adapter, consumed by
the planner) so the pill prompt rides as the inner `userMessage` — the
discriminator the sibling fixtures rely on.

**Renderer/catalog parity:** added the missing `declarative-info-row`
testid on InfoRow (turn 4) and a full `DataTable` definition + renderer
(`declarative-data-table`, turn 2), plus `trendValue` on Metric. Added
`sales-context.ts` (byte-identical dataset + composition rules to the
strands/google-adk siblings) and wired it via `chat.tsx`.

Backend family mirrored: **google-adk / strands** — outer
`generate_a2ui` (no args) + inner forced `render_a2ui`,
`declarative-gen-ui-catalog`.

## Red-green proof (isolated control-plane, `--isolate --rebuild`, slot
16)

**RED (pre-fix, 3 runs):**
```
✗ d6:langroid/gen-ui-declarative red — state=red
  waitForTurnComplete: turn 1 did not complete within 90000ms (reason=surface-missing)
```
First diagnosis (direct backend SSE): outer `generate_a2ui` → `{"error":
"Tool generate_a2ui failed: ValidationError"}`. After fixing that: outer
succeeded but emitted `catalogId: copilotkit://app-dashboard-catalog`
with `components: []` (inner never matched — legacy functions API).
After the tools-API + threading fix:

**GREEN (post-fix, 2 runs):**
```
✓ d6:langroid/gen-ui-declarative green
  1 passed
```
aimock journal confirms all four pills' outer `generate_a2ui` + inner
`render_a2ui` (tool_choice forced) calls return **200** and emit
`declarative-gen-ui-catalog` surfaces.

Per-turn backend SSE verified:
- turn 1 (sales-dashboard): 4 × Metric + PieChart + BarChart
- turn 2 (team-performance): DataTable + BarChart
- turn 4 (top-account): 7 × InfoRow + PieChart

## Visual

`langroid-turn1-sales-dashboard.png` — live Playwright render
(X-AIMock-Context: langroid): 4 metric tiles + revenue-by-region pie +
monthly-revenue bar. Turns 2–4 surface renders are asserted and pass in
the authoritative D6 run (conjunctive per-turn testid checks) and
confirmed via backend SSE above.

## Notes

- Draft: not for merge/promote (user-gated).
- A concurrent `ms-agent-harness-dotnet` fan-out agent was hitting the
shared local aimock during manual browser capture (interleaved 404s in
the journal); it does not affect the isolated D6 result, which is the
binding proof.

---

**CI-driven follow-ups (in this same commit):**
- Updated `integrations/langroid/tests/python/test_generate_a2ui.py` to
assert the modern `tools=`/`tool_choice=` kwargs (was pinning the legacy
`functions=`/`function_call=` API this fix intentionally replaced). 78
passed / 1 skipped locally.
- Scoped the four inner `render_a2ui` fixtures to `context: langroid`
(langroid forwards `x-aimock-context` to the inner planner call, unlike
`ag_ui_adk`/`ag_ui_strands`) so they don't collide in the shared scope
with the sibling integrations' identical inner keys — keeps the
`aimock-fixtures` exact-duplicate ceiling at 297 (no bump). Full
`aimock-fixtures.test.ts` suite: 837 passed. D6 re-run after this
change: still green 4/4.
2026-07-18 16:20:05 -07:00
Jordan Ritter 04aefd9dcd fix(showcase): repair ms-agent-dotnet D6 gen-ui-declarative (surface-missing) (#6057)
## Summary

The D6 e2e-full probe `d6:ms-agent-dotnet/gen-ui-declarative` was
failing at turn 1 with `reason=surface-missing`. Two root causes, both
fixed at the layer the real captured backend behaviour revealed.

### Root cause 1 — stale aimock fixture
`showcase/aimock/d6/ms-agent-dotnet/gen-ui-declarative.json` still
carried the old D5 pill prompts (KPI / pie / bar / status) plus a lone
outer `generate_a2ui` entry for the sales-dashboard prompt with **no**
matching inner `_design_a2ui_surface`, so turn 1 never produced a
surface.

Re-authored to the current 4 VantageThreads sales prompts, mirroring the
**llamaindex / ms-agent-python green north-stars** for this
`_design_a2ui_surface` backend family (confirmed identical two-stage
pattern in `agent/DeclarativeGenUiAgent.cs` +
`agent/A2uiSecondaryToolCaller.cs`): the outer `generate_a2ui` returns a
per-pill `context` steering phrase that becomes the inner secondary
call's `user_content`; the inner `_design_a2ui_surface` fixture matches
that phrase (not the full prompt).

**ms-agent-dotnet–specific discriminator.** Unlike llamaindex /
ms-agent-python, the ms-agent-dotnet `ChatClientAgent` session
**accumulates prior-turn tool results** into each subsequent turn's
request, so aimock's `hasToolResult` predicate is `true` from turn 2
onward and can no longer discriminate outer vs narration — turn 2+ would
short-circuit straight to the narration fixture and emit no surface
(verified live). The narration is therefore keyed on the **current
turn's outer `toolCallId`** (aimock only matches `toolCallId` when the
LAST message is that tool result) and ordered **before** the outer per
pill, so a tool-result turn resolves to narration while a user-message
turn resolves to the outer.

### Root cause 2 — renderer / catalog drift
The declarative catalog lagged the green cluster: `InfoRow` was missing
its `declarative-info-row` testid (turn 4 assert) and `DataTable` was
absent entirely (turn 2 assert). Added the testid and the `DataTable`
renderer + definition, matching the green cluster.

## Red → Green proof (real control-plane surface)

`./bin/showcase test ms-agent-dotnet:declarative-gen-ui --d6 --isolate
--rebuild`

| | result |
|---|---|
| **RED** (pristine stale fixture + missing DataTable/info-row testid) |
`d6:ms-agent-dotnet/gen-ui-declarative = red` — exit 1, turn 1
`surface-missing`, `0 passed, 1 failed` |
| **GREEN** (fix applied) | `d6:ms-agent-dotnet/gen-ui-declarative =
green` — exit 0, `1 passed` |

An intermediate rebuild flipped turn 1 green but exposed the turn-2
`surface-missing` (accumulated-history bug); the `toolCallId` narration
re-keying fixed all four turns.

## Visual verification

Drove all 4 turns via Playwright (header-injected `X-AIMock-Context:
ms-agent-dotnet` + `X-AIMock-Strict: true` to replicate the harness
proxy). Confirmed real painted surfaces:
- **turn 1 sales-dashboard**: 4 KPI metrics ($4.2M revenue, 186
customers, 31% win rate, $22.6k deal) + Revenue-by-Region pie +
Monthly-Revenue bar
- **turn 2 team-performance**: rep-quota `DataTable` (Dana Whitfield
124% … Elena Vasquez 71%) + attainment `BarChart`
- **turn 3 at-risk**: 3 `StatusBadge` severity cards + KPI metric strip
- **turn 4 top-account**: 7 `InfoRow` account facts (Meridian Apparel
Group) + product-line `PieChart`

Screenshots under
`~/.local/share/copilotkit/cr/2ndwave-shots/msdotnet-turn{1..4}-full.png`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-18 16:20:01 -07:00
Jordan Ritter 92d0fab67e fix(showcase): repair ms-agent-harness-dotnet D6 gen-ui-declarative (surface-missing) (#6056)
## Summary

The D6 e2e-full probe `d6:ms-agent-harness-dotnet/gen-ui-declarative`
was failing at turn 1 with `reason=surface-missing`. Three layers, each
fixed at the layer the real captured request revealed. Part of the
second-wave declarative fan-out (siblings #6051–#6055 / #6053
ms-agent-python).

### Root cause 1 — stale aimock fixture
`showcase/aimock/d6/ms-agent-harness-dotnet/gen-ui-declarative.json`
still carried the old D5 pill set (KPI / pie / bar / status prompts)
with inner `_design_a2ui_surface` entries keyed on those stale prompts.
The current driver sends four VantageThreads sales prompts, so turn 1
("Show me my sales dashboard for this quarter.") had **no matching inner
surface** — the backend looped `generate_a2ui` to its invocation limit
and the frontend painted the stale KPI catch-all instead of the sales
dashboard. Re-authored to the four current prompts, mirroring the
**llamaindex green north-star** for this `_design_a2ui_surface`
two-stage backend family (ms-agent-harness-dotnet's inner tool is
`_design_a2ui_surface`, per `agent/DeclarativeGenUiAgent.cs` +
`agent/A2uiSecondaryToolCaller.cs`).

### Root cause 2 — `hasToolResult` breaks the interleaved thread
This is where the dotnet family diverges from the #6053 ms-agent-python
template. ms-agent-python starts a fresh session per turn; the **.NET
harness backend threads the FULL interleaved conversation**.
`hasToolResult` is a thread-global predicate (see
`showcase/GOTCHAS.md`), so once turn 1 leaves a `role:"tool"` message in
the thread, every later pill's outer `generate_a2ui` call sees
`hasToolResult:true` and matches the **narration** fixture instead of
emitting the tool call → surface-missing on turns 2–4. (Reproduced live:
turn 2's outer call, replayed with turn-1 history, returned the
narration string instead of `generate_a2ui`.) Switched to the sanctioned
interleaved-safe pattern: **narration keyed on this pill's outer
`toolCallId`** (ordered before the outer), **outer keyed on
`userMessage` only**. Verified all four pills resolve correctly through
the full interleaved thread against live aimock.

### Root cause 3 — renderer / catalog drift
`renderers.tsx` and `definitions.ts` lagged the green cluster — missing
the `DataTable` and `InfoRow` components (the `declarative-data-table` /
`declarative-info-row` testids that turns 2 and 4 assert), plus
`Metric.trendValue` and the Row/Column/Text gap overrides. Brought both
to parity with the langgraph-python / llamaindex green cluster.

## Red → Green proof (real control-plane surface)

`SHOWCASE_ISO_SLOT=18 ./bin/showcase test
ms-agent-harness-dotnet:declarative-gen-ui --d6 --isolate --rebuild`

| | result |
|---|---|
| **RED** (pristine stale fixture + drifted renderers) | `state=red`,
exit 1 — turn 1 `waitForTurnComplete: turn 1 did not complete within
90000ms (reason=surface-missing, runsFinished=1, count=41)`; body showed
the stale "Quarterly KPIs / $1.24M / SIGNUPS 8,420" surface +
`generate_a2ui` looping |
| **GREEN** (fix applied) | `state=green`, exit 0 — `1 passed` |

## Visual verification

Drove all 4 turns via Playwright with network-level route injection of
`x-aimock-context: ms-agent-harness-dotnet` (replicating the
harness/production proxy). Confirmed real painted surfaces:
- **turn 1 sales-dashboard**: 4 KPI metrics ($4.2M revenue, 186
customers, 31% win rate, $22.6k deal) + Revenue-by-Region donut +
Monthly-Revenue bar
- **turn 2 rep-quota**: rep-attainment `DataTable` (Dana Whitfield 124%
… Elena Vasquez 71%) + quota-attainment `BarChart`
- **turn 3 at-risk**: 3 severity `StatusBadge` cards (Northwind /
Cascadia / Atlas) + KPI metric strip
- **turn 4 top-account**: 7 `InfoRow` account facts (Meridian Apparel
Group) + product-line `PieChart`

Screenshots under
`~/.local/share/copilotkit/cr/2ndwave-shots/msharness-turn{1..4}-*.png`.
2026-07-18 16:19:57 -07:00
Jordan Ritter c13415f743 fix(showcase): repair claude-sdk-typescript D6 gen-ui-declarative (surface-missing) (#6055)
## What

Repairs the `claude-sdk-typescript` D6 `gen-ui-declarative` cell, which
was **red on turn-1 surface-missing**. The aimock fixture carried stale
D5-era prompts and only a partial turn-1 outer entry, so the two-stage
Anthropic A2UI flow (outer `generate_a2ui` → secondary `render_a2ui` →
narration) never painted a surface for the four current sales-analyst
pills.

Fan-out of the proven pattern from the pilots (#6051 claude-sdk-python,
#6052 mastra, #6053 ms-agent-python). This is the Anthropic two-stage
family — `injectA2UITool: false`, backend owns `generate_a2ui` +
secondary `render_a2ui`.

## Root cause

`aimock/d6/claude-sdk-typescript/gen-ui-declarative.json` had the old D5
prompts (`Show me a quick KPI dashboard` / `pie chart of sales by
region` / `bar chart of quarterly revenue` / `status report on system
health`) plus one lone turn-1 outer entry for the new sales prompt. The
driver's four current prompts had no complete triads, so aimock returned
`STRICT: No fixture matched` and no surface mounted.

## Fix (3 layers)

1. **Fixture re-author** — 12 fixtures (4 pills × {outer
`generate_a2ui`, inner `render_a2ui`, narration}) in the two-stage
shape, mirroring the #6051 sibling + google-adk data. Render payloads
mount the per-pill catalog components the driver asserts: Metric×4 + Pie
+ Bar (sales-dashboard); DataTable + Bar (team-performance); Metric×3 +
StatusBadge×3 (at-risk); InfoRow + Pie (top-account). Render payloads
are byte-identical to #6051.

**Ordering/matcher fix vs a naive python mirror:** the CSTS runtime
accumulates full conversation history across pills, so on turns 2-4 the
outer `generate_a2ui` call carries prior pills' tool results and a
`hasToolResult:false` matcher never fires. Each pill triad is ordered
**narration (`toolCallId`) first** so it claims the last-role:tool
calls, and the outer matcher drops `hasToolResult` and gates on
`userMessage` + `toolName generate_a2ui` (last-role:user). Verified live
via the aimock journal.

2. **InfoRow testid** — add `data-testid="declarative-info-row"` to the
InfoRow renderer (turn-4 top-account parity; CSTS was missed by #6050).

3. **Suggestions refresh** — `suggestions.ts` had stale D5-era pill
labels that emitted unmatched prompts (live 404 banner). Now the four
sales-analyst pills, matching google-adk.

## Red → green proof (control-plane, slot 30, `--isolate --rebuild`)

**RED (origin/main):**
```
✗ d6:claude-sdk-typescript/gen-ui-declarative red — state=red
[aimock] STRICT: No fixture matched for POST /v1/messages   (×6)
```

**GREEN (fixed):**
```
✓ d6:claude-sdk-typescript/gen-ui-declarative green — 1 passed
```
aimock journal after the green run: **all 12 calls returned 200, zero
503, zero no-match** across all 4 turns.

## Visual proof (Playwright, `x-aimock-context: claude-sdk-typescript`)

Drove all 4 pills live; per-turn DOM testid counts (no fixture error on
any turn):

| Turn | Pill | Newly-mounted testids |
|---|---|---|
| 1 | sales-dashboard | metric=4, pie=1, bar=1 |
| 2 | team-performance | data-table=1, bar +1 (→2) |
| 3 | at-risk | status-badge=3, metric +3 (→7) |
| 4 | top-account | **info-row=7**, pie +1 (→2) |

Screenshots:
`~/.local/share/copilotkit/cr/2ndwave-shots/csts-turn{1..4}-*.png`.

## Unit tests
- `scripts/__tests__/aimock-fixtures.test.ts`: 837 passed (validates the
new fixture shape).
- `harness/src/probes/scripts/d5-gen-ui-declarative.test.ts`: 31 passed.

## Notes
- Worktree tsc module-not-found / TS2322-ButtonProps noise is benign
symlink noise; trust the PR's real `check-types` CI check.
- Local red-green ran on slot 30 (slot 8 and several low slots were held
by concurrent isolate stacks).
2026-07-18 16:19:54 -07:00
Jordan Ritter 29ed44d913 fix(showcase): flip agno gen-ui-declarative D6 cell green (4-turn sales flow + DataTable/InfoRow parity) (#6054)
## What

Flips the `d6:agno/gen-ui-declarative` cell from **red (turn-1
dom-missing) → green**. Fan-out of the proven 2nd-wave declarative fix
pattern (pilots #6051 claude-sdk-python, #6052 mastra, #6053
ms-agent-python).

## Root cause (verified at the request level)

agno's declarative-gen-ui shipped a **stale D5 aimock fixture** keyed on
the old prompts (KPI dashboard / pie chart of sales by region / bar
chart / status report), while the current D6 driver sends the OSS-136
sales prompts. Captured from the aimock journal on a RED run: agno's
OUTER agent hit aimock with `tools=[generate_a2ui]`, `userMessage="Show
me my sales dashboard for this quarter."`, `x-aimock-strict:true`,
`context=agno` — the stale fixture matched **none**, aimock returned 503
(strict), the outer agent never emitted `generate_a2ui`, no surface
rendered → turn-1 dom-missing → `state=red`.

## Backend family + north-star

agno uses the plain **`render_a2ui` two-stage** family
(`src/agents/a2ui_dynamic_agent.py`): an OUTER `generate_a2ui(context:
str)` tool, then a forced-`render_a2ui` secondary call, then narration.
Mirrored the **google-adk** green north-star (same VantageThreads
surfaces + `declarative-gen-ui-catalog`; `render_a2ui` args copied
verbatim).

Key agno-specific wrinkle: the inner secondary call's **user message is
hardcoded and identical across all four pills** ("Generate a dynamic
A2UI dashboard based on the conversation."), so the inner `render_a2ui`
fixtures cannot key on `userMessage`. They discriminate on
`toolName:render_a2ui` + `context:agno` + a `systemMessage` substring
equal to the per-pill context phrase the outer injects ("Conversation
context:\n<context>"). aimock's CLI server uses substring matching, so
this works; verified live against the journal.

## Fix layers

- **Fixture** (`aimock/d6/agno/gen-ui-declarative.json`): 4 sales
prompts × 3 calls (outer/inner/narration) = 12 fixtures.
- **Renderers** (`.../a2ui/renderers.tsx`): `declarative-info-row`
testid on InfoRow (turn 4) + new `DataTable` renderer with
`declarative-data-table` testid (turn 2), mirroring google-adk.
- **Definitions** (`.../a2ui/definitions.ts`): `DataTable` schema,
`Metric.trendValue`, `z.unknown()` PrimaryButton action, refreshed
descriptions.
- **Backend** (`a2ui_dynamic_agent.py`): sales-analyst system prompt for
live-mode steering.
- **Test** (`scripts/__tests__/aimock-fixtures.test.ts`):
`KNOWN_DUPLICATE_CEILING` 297→300 (+3) — the 4 inner render fixtures
collapse to one `toolName=render_a2ui` matchKey (matchKey omits
systemMessage/context) but aimock's router disambiguates them at
runtime.

### Bug caught during green

First green attempt still red with a client-side exception: adding
`Row`/`Column`/`Text` to `myDefinitions` **without matching renderers**
(agno relies on `includeBasicCatalog:true` for those) made
`createCatalog` produce a definition set wider than its renderer set →
render crash. Fixed by not declaring Row/Column/Text in definitions.

## Local RED → GREEN proof (isolated D6 slots)

**RED** (control-plane, stale fixture):
```
✗ d6:agno/gen-ui-declarative red — state=red
```

**GREEN** (control-plane, after fix):
```
✓ d6:agno/gen-ui-declarative green
1 passed
```

**GREEN** (`--direct`, per-turn DOM assertions — authoritative):
```
turn 1/4 — assertions passed   (sales-dashboard: metric×4 + pie + bar)
turn 2/4 — assertions passed   (team-performance: data-table + bar)
turn 3/4 — assertions passed   (at-risk: status-badge×3 + metric×3)
turn 4/4 — assertions passed   (top-account: info-row + pie)
state=green, 1 passed
```

GREEN aimock journal: 12 requests = 4× OUTER `generate_a2ui` + 4× INNER
`render_a2ui` + 4× narration, all matched.

**Live Playwright visual** (all 4 surfaces painted, testids counted):
turn1 metric×4/pie/bar, turn2 data-table×1/bar, turn3
status-badge×3/metric, turn4 info-row×7/pie.

`aimock-fixtures.test.ts`: 837 passed after the ceiling bump.
2026-07-18 16:19:50 -07:00
Jordan Ritter 3d8eff5e70 fix(showcase): repair ms-agent-python D6 gen-ui-declarative (surface-missing) (#6053)
## Summary

The D6 e2e-full probe `d6:ms-agent-python/gen-ui-declarative` was
failing at turn 1 with `reason=surface-missing`. Two independent root
causes, both fixed at the layer the real captured request revealed.

### Root cause 1 — stale aimock fixture
`showcase/aimock/d6/ms-agent-python/gen-ui-declarative.json` still
carried the old D5 pill prompts (KPI / pie / bar / status) plus a lone
outer `generate_a2ui` entry for the current sales-dashboard prompt with
**no** matching inner `_design_a2ui_surface` and **no** narration. The
backend looped `generate_a2ui` to its invocation limit and
`RUN_FINISHED` was blocked while the tool call stayed active (`Cannot
send 'RUN_FINISHED' while tool calls are still active`).

Re-authored to the current 4 VantageThreads sales prompts, mirroring the
**llamaindex green north-star** for this backend shape
(ms-agent-python's inner tool is `_design_a2ui_surface`, not
google-adk's `render_a2ui`):
- outer `generate_a2ui` returns a `context` steering phrase — the
ms-agent-framework session does not surface the latest user message to
the secondary LLM, so the phrase becomes the inner call's
`user_content`;
- the inner `_design_a2ui_surface` fixture matches that phrase (not the
full prompt);
- `hasToolResult` discriminates outer (false) vs narration (true).

This also eliminates the stale `render-a2ui.json` "KPI dashboard"
catch-all collision that was rendering the wrong (KPI) surface for the
sales prompt.

### Root cause 2 — renderer / catalog drift
`renderers.tsx` and `definitions.ts` for ms-agent-python's
declarative-gen-ui lagged the green cluster — missing the `DataTable`
and `InfoRow` components (the `declarative-data-table` /
`declarative-info-row` testids that turns 2 and 4 assert), plus
`Metric.trendValue` and the `Row`/`Column`/`Text` gap overrides. Brought
both files to parity with the langgraph-python / google-adk green
cluster.

## Red → Green proof (real control-plane surface)

`SHOWCASE_ISO_SLOT=11 ./bin/showcase test
ms-agent-python:declarative-gen-ui --d6 --isolate`

| | result |
|---|---|
| **RED** (pristine fixture + stale renderers) |
`d6:ms-agent-python/gen-ui-declarative = red` — exit 1, turn 1
`surface-missing` |
| **GREEN** (fix applied) | `d6:ms-agent-python/gen-ui-declarative =
green` — exit 0, `1 passed` |

## Visual verification

Drove all 4 turns via Playwright (header-injected `x-aimock-context:
ms-agent-python` to replicate the harness/production proxy). Confirmed
real painted surfaces:
- **turn 1 sales-dashboard**: 4 KPI metrics ($4.2M revenue, 186
customers, 31% win rate, $22.6k deal) + Revenue-by-Region pie +
Monthly-Revenue bar
- **turn 2 team-performance**: rep-quota `DataTable` + attainment
`BarChart`
- **turn 3 at-risk**: 3 `StatusBadge` severity cards + KPI metric strip
- **turn 4 top-account**: 7 `InfoRow` account facts + product-line
`PieChart`

Screenshots captured under
`~/.local/share/copilotkit/cr/2ndwave-shots/mspy-turn{1..4}-*.png`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-18 16:19:46 -07:00
Jordan Ritter c01bde3759 fix(showcase): flip mastra gen-ui-declarative D6 cell green (#6052)
## What

Flips the `d6:mastra/gen-ui-declarative` showcase cell from **red** to
**green**.

The cell failed turn 1 with `reason=surface-missing` (the dom-missing
family). Reading the real failure surface (aimock logs + mastra
container logs + harness worker logs) exposed **three** distinct
defects, each fixed at its own layer.

## Root cause (empirically confirmed)

1. **Stale aimock fixture.** `aimock/d6/mastra/gen-ui-declarative.json`
carried the OLD D5 pill prompts (`"Show me a quick KPI dashboard"`,
`"pie chart of sales by region"`, ...) and the stale inner tool name
`_design_a2ui_surface`. The current D6 driver sends 4 different prompts,
so aimock matched **0** fixtures:
   ```
[aimock] STRICT: No fixture matched for POST /v1/responses (x2 = the
two-stage flow's outer + inner calls)
   ```
   Nothing emitted → no render → `surface-missing`.

2. **Mastra outer-tool arg schema (mastra-specific).** Unlike the green
`google-adk` peer whose outer `generate_a2ui` takes `{}`, mastra's
`generateA2uiTool` (`integrations/mastra/src/mastra/tools/index.ts`) has
an `inputSchema` that **requires a `messages` array**. After
re-authoring the fixture to the green shape (which emits `generate_a2ui`
with `{}`), the mastra runtime rejected it:
   ```
Tool input validation failed for generate_a2ui — messages: Required.
Provided arguments: {}
   ```
The outer tool never executed → no `a2ui_operations` container →
`surface-missing` (narration bubble rendered, but no A2UI surface).

3. **Renderer testid parity.** The D6 probe DOM-asserts
`declarative-data-table` (turn 2) and `declarative-info-row` (turn 4).
Mastra's `renderers.tsx` had **no DataTable renderer at all** and its
InfoRow renderer **lacked the `data-testid`**; `definitions.ts` had no
DataTable definition. Turns 2 and 4 could never satisfy their
assertions.

## Fix (3 files)

- **`aimock/d6/mastra/gen-ui-declarative.json`** — re-authored to the
current 4 driver prompts + the green two-stage shape (outer
`generate_a2ui` + inner forced `render_a2ui`, `context: "mastra"`,
`catalogId: "declarative-gen-ui-catalog"`, per-pill narration). Each
outer `generate_a2ui` call now carries a valid `messages` array (mastra
schema requirement).
- **`integrations/mastra/.../a2ui/definitions.ts`** — added the
`DataTable` catalog definition (mirrors the green `google-adk` peer).
- **`integrations/mastra/.../a2ui/renderers.tsx`** — added
`data-testid="declarative-info-row"` to the InfoRow renderer, and added
a `DataTable` renderer carrying `data-testid="declarative-data-table"`.

## Red → Green proof (real control-plane surface, slot 13, `--rebuild`)

Command: `SHOWCASE_ISO_SLOT=13 ./bin/showcase test
mastra:declarative-gen-ui --d6 --isolate --rebuild`

**RED (pristine main):**
```
✗ d6:mastra/gen-ui-declarative red — state=red
turn 1 did not complete within 90000ms (reason=surface-missing)
```

**GREEN (fixed):**
```
✓ d6:mastra/gen-ui-declarative green
1 passed
[conversation-runner] turn 1/4 — assertions passed   (metric x4, pie, bar; baseline 0)
[conversation-runner] turn 2/4 — assertions passed   (data-table NEW, bar)
[conversation-runner] turn 3/4 — assertions passed   (status-badge x3, metric x3)
[conversation-runner] turn 4/4 — assertions passed   (info-row NEW, pie)
[conversation-runner] conversation completed successfully { turnsCompleted: 4 }
```

## Visual evidence

Drove the live cell through all 4 turns via Playwright (route-level
`x-aimock-context` injection) and screenshotted each painted surface —
all real renders, no error states:

- **turn 1** sales-dashboard: 4 KPI metric tiles + donut PieChart
(Revenue by Region) + BarChart (Monthly Revenue)
- **turn 2** team-performance: DataTable (Rep attainment: Dana Whitfield
124%, ...) — the new renderer
- **turn 3** at-risk: 3 severity cards each with a StatusBadge + 3 KPI
metric tiles
- **turn 4** top-account: Card of InfoRow facts (Owner/Region/ARR/...) +
PieChart — the new testid

Note: `google-adk` remains the only D6 declarative fixture already on
the current prompts; the other integrations (`langgraph-typescript`,
etc.) still carry the same stale-fixture shape and are a follow-up wave.
2026-07-18 16:19:43 -07:00
Jordan Ritter 45cdc02ee0 fix(showcase): re-author langroid declarative gen-ui to the two-stage A2UI north-star (D6 turn-1 surface-missing)
The langroid `declarative-gen-ui` D6 cell was red at turn 1 with
`reason=surface-missing`: the demo was still on the pre-D6 (D5-era) shape
while the google-adk / strands siblings had moved to the two-stage
dynamic-schema A2UI pattern. Four independent defects each blocked the
surface from painting; all four are fixed here to bring langroid to
sibling parity.

Root causes (each verified against a live isolated stack):

1. Stale suggestions + fixtures. `suggestions.ts` still offered the old
   D5 pills ("Show a KPI dashboard", "pie chart of sales by region", …)
   and `aimock/d6/langroid/gen-ui-declarative.json` only mocked those old
   prompts. The D6 driver sends the four current business-question pills
   ("Show me my sales dashboard for this quarter.", etc.). Re-authored both
   to the four current prompts, mirroring the google-adk north-star
   (outer `generate_a2ui` no-arg → inner forced `render_a2ui` → outer
   narration, three fixtures per pill).

2. Required `context` on the outer tool. `GenerateA2UITool.context` was a
   required pydantic field, so the mocked outer call's `arguments: {}`
   raised `ValidationError` before the tool could run — no inner call, no
   surface. Made it optional (default "") to match the no-arg sibling tools.

3. Legacy functions API hid the inner tool from aimock's matcher. The
   inner planner call used langroid's `functions=`/`function_call=` (legacy
   OpenAI) path; aimock's `toolName` matcher only inspects the modern
   `tools[]` array, so the inner `render_a2ui` fixture never matched and the
   call fell through to the outer `generate_a2ui` fixture (empty surface,
   wrong catalogId). Switched the inner call to the modern
   `tools=`/`tool_choice=` API via a new `_RENDER_A2UI_TOOL_SPEC`. The
   response extractor already reads the modern `oai_tool_calls` path first.

4. Inner call could not be discriminated per pill. langroid has no
   framework middleware to forward the run's conversation into the inner
   call (unlike ag_ui_adk / ag_ui_strands), so its inner user message was a
   fixed generic string across all four pills. Added an explicit
   last-user-turn thread (ContextVar set by the adapter, consumed by the
   planner) so the pill prompt rides as the inner `userMessage` — the same
   discriminator the sibling fixtures rely on.

Renderer/catalog parity: added the missing `declarative-info-row` testid
to InfoRow (turn 4) and a full `DataTable` definition + renderer
(`declarative-data-table`, turn 2), plus `trendValue` on Metric. Added
`sales-context.ts` (byte-identical dataset + composition rules to the
strands/google-adk siblings) and wired it via `chat.tsx`.

Red-green (isolated control-plane, slot 16):
- RED (3 runs, pre-fix): `d6:langroid/gen-ui-declarative` red,
  turn-1 surface-missing.
- GREEN (2 runs, post-fix, --rebuild): 4/4 turns pass. aimock journal
  confirms all four pills' outer `generate_a2ui` + inner `render_a2ui`
  calls return 200 and emit the correct `declarative-gen-ui-catalog`
  surfaces. Backend SSE verified per turn: turn-1 4 metrics + pie + bar,
  turn-2 DataTable, turn-4 7 InfoRows.
2026-07-18 16:02:47 -07:00
Jordan Ritter bf75022c78 fix(showcase): repair ms-agent-dotnet D6 gen-ui-declarative (surface-missing)
The D6 e2e-full probe d6:ms-agent-dotnet/gen-ui-declarative failed at turn 1
with reason=surface-missing. Two root causes fixed at the layer the real
captured backend behaviour revealed.

Root cause 1 - stale aimock fixture. The fixture still carried the old D5
pill prompts (KPI/pie/bar/status) plus a lone outer generate_a2ui entry for
the sales-dashboard prompt with no matching inner _design_a2ui_surface, so
turn 1 never produced a surface. Re-authored to the current 4 VantageThreads
sales prompts mirroring the llamaindex/ms-agent-python green north-stars for
this _design_a2ui_surface backend family (outer generate_a2ui returns a
context steering phrase; the inner _design_a2ui_surface fixture matches that
phrase).

Unlike llamaindex/ms-agent-python, the ms-agent-dotnet ChatClientAgent session
ACCUMULATES prior-turn tool results into each subsequent turn's request, so
hasToolResult is true from turn 2 onward and cannot discriminate outer vs
narration (turn 2+ would short-circuit straight to narration, no surface).
The narration is therefore keyed on the CURRENT turn's outer toolCallId
(aimock only matches toolCallId when the LAST message is that tool result)
and ordered before the outer per pill so the tool-result turn resolves to
narration while the user-message turn resolves to the outer.

Root cause 2 - renderer/catalog drift. The declarative catalog lagged the
green cluster: InfoRow was missing its declarative-info-row testid (turn 4)
and DataTable was absent entirely (turn 2). Added the testid and the DataTable
renderer + definition, matching the green cluster.

Red -> Green (real control-plane, --isolate --rebuild):
  RED:   d6:ms-agent-dotnet/gen-ui-declarative = red (turn 1 surface-missing)
  GREEN: d6:ms-agent-dotnet/gen-ui-declarative = green (1 passed)

Visual: drove all 4 turns via Playwright (X-AIMock-Context: ms-agent-dotnet)
- turn 1 4 KPI metrics + region pie + monthly bar
- turn 2 rep-quota DataTable + attainment bar
- turn 3 3 severity StatusBadges + KPI metric strip
- turn 4 7 account InfoRows + product-line pie
2026-07-18 15:22:45 -07:00
Jordan Ritter adc897f485 fix(showcase): repair ms-agent-harness-dotnet D6 gen-ui-declarative (surface-missing)
The D6 e2e-full probe `d6:ms-agent-harness-dotnet/gen-ui-declarative`
failed at turn 1 with `reason=surface-missing`. Three layers, all fixed
at the layer the real captured request revealed.

Root cause 1 — stale aimock fixture
The fixture still carried the old D5 pill set (KPI / pie / bar / status
prompts) with inner `_design_a2ui_surface` entries keyed on those stale
prompts. The current driver sends four VantageThreads sales prompts, so
turn 1 ("Show me my sales dashboard for this quarter.") had no matching
inner surface — the backend looped `generate_a2ui` to its limit and the
frontend painted the stale KPI catch-all instead of the sales dashboard.
Re-authored to the four current prompts, mirroring the llamaindex green
north-star for this `_design_a2ui_surface` two-stage backend family.

Root cause 2 — `hasToolResult` breaks the interleaved thread
Unlike ms-agent-python (fresh session per turn), the .NET harness backend
threads the FULL interleaved conversation. `hasToolResult` is a
thread-global predicate (GOTCHAS.md), so once turn 1 leaves a tool result
in the thread, every later pill's outer `generate_a2ui` call sees
`hasToolResult:true` and matches the narration fixture instead of emitting
the tool call — surface-missing on turns 2-4. Switched to the sanctioned
interleaved-safe pattern: narration keyed on this pill's outer
`toolCallId` (ordered before the outer), outer keyed on `userMessage`
only.

Root cause 3 — renderer / catalog drift
`renderers.tsx` and `definitions.ts` lagged the green cluster — missing
the `DataTable` and `InfoRow` testids (`declarative-data-table` turn 2 /
`declarative-info-row` turn 4) plus `Metric.trendValue` and the
Row/Column/Text gap overrides. Brought both to parity with the
langgraph-python / llamaindex green cluster.

Red → Green (real control-plane surface, SHOWCASE_ISO_SLOT=18 --isolate)
  RED  (pristine): turn 1 surface-missing, state=red, exit 1
  GREEN (fixed):   1 passed, state=green, exit 0

Visual: drove all 4 turns via Playwright (network-injected
x-aimock-context: ms-agent-harness-dotnet). Confirmed real painted
surfaces — turn 1 sales dashboard (4 KPIs + region pie + monthly bar),
turn 2 rep-quota DataTable + attainment bar, turn 3 three at-risk status
badges + KPI strip, turn 4 seven account InfoRows + product-line pie.
Screenshots under ~/.local/share/copilotkit/cr/2ndwave-shots/.
2026-07-18 15:22:36 -07:00
Jordan Ritter 92be110c62 fix(showcase): repair claude-sdk-typescript D6 gen-ui-declarative (surface-missing)
The claude-sdk-typescript declarative-gen-ui cell was red on turn-1
surface-missing: the aimock fixture carried stale D5-era prompts (KPI
dashboard / pie / bar / status report) and only a partial turn-1 outer
entry, so the two-stage Anthropic A2UI flow (outer generate_a2ui ->
secondary render_a2ui -> narration) never painted a surface for the
four current sales-analyst pills.

Three fix layers, mirroring the proven claude-sdk-python (#6051) sibling
and the google-adk north-star:

1. Re-author aimock/d6/claude-sdk-typescript/gen-ui-declarative.json to
   the 4 current driver prompts in the two-stage shape (12 fixtures = 4
   pills x {outer generate_a2ui, inner render_a2ui, narration}), with
   render payloads mounting the per-pill catalog components the driver
   asserts (Metric x4 + Pie + Bar; DataTable + Bar; Metric x3 +
   StatusBadge x3; InfoRow + Pie). Render payloads are byte-identical to
   the #6051 data.

   Ordering/matcher fix vs the naive python mirror: the CSTS runtime
   accumulates full conversation history across pills, so on turns 2-4
   the outer generate_a2ui call carries prior pills' tool results and a
   hasToolResult:false matcher never fires. Each pill triad is ordered
   narration (toolCallId) FIRST so it claims the last-role:tool calls,
   and the outer matcher drops hasToolResult and gates on
   userMessage + toolName generate_a2ui (last-role:user).

2. Add data-testid="declarative-info-row" to the InfoRow renderer
   (turn-4 top-account parity; CSTS was missed by #6050).

3. Refresh suggestions.ts to the 4 sales-analyst pills (were stale
   D5-era labels that emitted unmatched prompts -> live 404 banner).

Red-green (control-plane, slot 30, --isolate --rebuild):
- RED  (origin/main): d6:claude-sdk-typescript/gen-ui-declarative red;
  aimock STRICT: No fixture matched for POST /v1/messages.
- GREEN (fixed): 1 passed; aimock journal shows all 12 calls 200, zero
  503/no-match across all 4 turns.
Visual: Playwright 4-turn walk (x-aimock-context claude-sdk-typescript)
confirms metric=4/pie=1/bar=1 (t1), data-table=1/bar+1 (t2),
status-badge=3/metric+3 (t3), info-row=7/pie+1 (t4); no fixture error.
Unit: aimock-fixtures 837 passed; d5-gen-ui-declarative 31 passed.
2026-07-18 14:46:02 -07:00
Jordan Ritter 3370a452b5 fix(showcase): flip agno gen-ui-declarative D6 cell green (4-turn sales flow + DataTable/InfoRow parity)
agno's declarative-gen-ui D6 cell failed turn-1 dom-missing: the aimock
fixture was keyed on the stale D5 prompts (KPI/pie/bar/status) while the
current driver sends the OSS-136 sales prompts, so the agno OUTER agent's
generate_a2ui call matched no fixture, aimock returned 503 (strict), and no
surface rendered.

Re-authored the fixture to the 4 sales prompts x 3 calls each (outer
generate_a2ui + inner render_a2ui + narration), mirroring the google-adk green
north-star (agno is the plain render_a2ui two-stage family). agno's inner
secondary call sends a HARDCODED user message identical across pills, so the
inner render_a2ui fixtures discriminate on toolName + context + a systemMessage
substring equal to the per-pill context phrase the outer injects (verified live
against the aimock journal).

Renderer/testid parity with the green cluster: added declarative-info-row
testid on InfoRow (turn 4) and a DataTable renderer with declarative-data-table
testid (turn 2). definitions.ts gains DataTable, Metric.trendValue, and an
z.unknown() PrimaryButton action. Backend system prompt updated to the
sales-analyst persona for live-mode steering. Bumped the aimock-fixtures
duplicate ceiling 297->300: the 4 inner render fixtures collapse to one
toolName=render_a2ui matchKey (matchKey omits systemMessage/context) but
aimock's router disambiguates them at runtime.

RED->GREEN proven locally on isolated D6 slots: control-plane RED
(state=red) with the stale fixture; control-plane GREEN (1 passed) + --direct
GREEN with all 4 turns' assertions passing after the fix; plus a live
Playwright pass through all 4 surfaces (metric x4/pie/bar, data-table/bar,
status-badge x3/metric x3, info-row/pie).
2026-07-18 14:27:30 -07:00
Jordan Ritter ece15c1016 fix(showcase): repair ms-agent-python D6 gen-ui-declarative (surface-missing)
The D6 e2e-full probe for ms-agent-python:gen-ui-declarative failed at turn 1
with reason=surface-missing. Two root causes, both fixed:

1. Stale aimock fixture. The fixture still carried the old D5 pill prompts
   (KPI/pie/bar/status) plus a lone outer generate_a2ui entry for the current
   sales-dashboard prompt with no matching inner _design_a2ui_surface or
   narration. The backend looped generate_a2ui to its invocation limit and
   RUN_FINISHED was blocked while the tool call stayed active. Re-authored to
   the current 4 VantageThreads sales prompts mirroring the llamaindex green
   north-star for this backend shape: the outer generate_a2ui returns a
   `context` steering phrase (the ms-agent-framework session does not surface
   the latest user message to the secondary LLM), the inner
   _design_a2ui_surface fixture matches that phrase (not the full prompt), and
   hasToolResult discriminates outer vs narration. This also avoids the stale
   render-a2ui "KPI dashboard" catch-all collision that was rendering the wrong
   surface.

2. Renderer/catalog drift. ms-agent-python's declarative-gen-ui renderers.tsx
   and definitions.ts lagged the green peers — missing the DataTable and
   InfoRow components (declarative-data-table / declarative-info-row testids)
   that turns 2 and 4 assert, plus Metric trendValue and the Row/Column/Text
   gap overrides. Brought both files to parity with the langgraph-python /
   google-adk green cluster.

Red-green proof on the real control-plane surface (SHOWCASE_ISO_SLOT=11,
--d6 --isolate):
- RED:   d6:ms-agent-python/gen-ui-declarative = red  (exit 1, turn 1 surface-missing)
- GREEN: d6:ms-agent-python/gen-ui-declarative = green (exit 0, 1 passed)

Visually verified all 4 turns via Playwright (header-injected to replicate the
harness x-aimock-context): turn 1 renders 4 KPI metrics + region pie + monthly
bar; turn 2 the rep-quota DataTable + attainment bar; turn 3 three at-risk
StatusBadges + KPI metrics; turn 4 seven InfoRow account facts + product-line
pie.
2026-07-18 13:45:16 -07:00
Jordan Ritter 4440e727af fix(showcase): flip mastra gen-ui-declarative D6 cell green
The mastra declarative-gen-ui D6 cell failed turn 1 with reason=surface-missing.
Three defects fixed at the layers the real failure surface showed:

1. Stale aimock fixture: aimock/d6/mastra/gen-ui-declarative.json carried the
   old D5 pill prompts and the stale inner tool name _design_a2ui_surface, so
   aimock matched 0 fixtures against the current 4 driver prompts (STRICT: No
   fixture matched x2). Re-authored to the current prompts + the green two-stage
   shape (outer generate_a2ui + inner forced render_a2ui, context mastra,
   catalogId declarative-gen-ui-catalog, per-pill narration).

2. Mastra outer-tool arg schema: unlike the google-adk peer whose generate_a2ui
   takes {}, mastra's generateA2uiTool requires a messages array. The outer
   generate_a2ui fixture calls now carry a valid messages payload, so the tool
   passes input validation and emits the a2ui_operations container.

3. Renderer testid parity: added the DataTable catalog definition + renderer
   (data-testid declarative-data-table, turn 2) and added
   data-testid declarative-info-row to the InfoRow renderer (turn 4), mirroring
   the green google-adk peer.

Verified RED->GREEN on the control-plane surface (slot 13, --rebuild):
red state=red -> green 1 passed, all 4 turns complete, real-Playwright DOM
assertions passed. Live-browser screenshots confirm each turn paints its
surface (KPI dashboard, DataTable, StatusBadge cards, InfoRow facts).
2026-07-18 13:37:43 -07:00
Jordan Ritter c5ad5caefd fix(showcase): complete claude-sdk-python declarative gen-ui D6 (4-turn sales flow + DataTable/InfoRow parity)
The claude-sdk-python gen-ui-declarative D6 cell went red with
reason=done-signal-missing: the aimock fixture still carried the legacy
D5 pills (KPI/pie/bar/status) plus a single stray hero `generate_a2ui`
entry, so the current 4-prompt sales-analyst driver (Show me my sales
dashboard / How are reps performing / accounts at risk / biggest account)
had no matching fixtures. aimock STRICT mode 404'd the unmatched outer
and inner Claude calls, so the run never emitted the expected render
per turn.

Two-part fix:
- Re-author aimock/d6/claude-sdk-python/gen-ui-declarative.json into the
  two-stage Anthropic-transport shape (mirrors the claude-sdk-typescript
  sibling + google-adk data): per turn (a) outer generate_a2ui emit
  matched by userMessage+toolName+hasToolResult, (b) inner render_a2ui
  design matched by toolName, (c) outer narration matched by toolCallId.
  Covers all 4 current sales prompts.
- Renderer/definition parity: add the DataTable catalog component
  (definition + renderer, testid declarative-data-table) that turn 2
  requires, and add the missing declarative-info-row testid to the
  InfoRow renderer that turn 4 requires. Both were absent on
  claude-sdk-python (present on google-adk).

Local red-green proof (control-plane, slot 12, --isolate --rebuild):
- RED  (pristine): d6:claude-sdk-python/gen-ui-declarative = red,
  aimock log 'STRICT: No fixture matched for POST /v1/messages'.
- GREEN (fixed):    d6:claude-sdk-python/gen-ui-declarative = green,
  1 passed, zero aimock no-match.
Visual verify (Playwright, harness X-AIMock-Context header): all 4 turns
paint with correct per-testid deltas (metric x4/pie/bar; data-table/bar;
status-badge x3/metric x3; info-row/pie).
2026-07-18 13:33:45 -07:00
Ran Shem Tov 4eed87c75b fix(showcase/mastra): correct cancel-path narration via aimock toolResultContains gate
Pick and cancel resume the native schedule_meeting suspend tool with the
SAME toolCallId; the requests differ only inside the tool-result payload,
so the cancel resume previously hit the pick-confirmation fixture and the
assistant replayed "Booked: ... confirmed" after the user cancelled. The
"__cancelled" toolCallId gates on the Denied fixtures were fictional and
never matched.

aimock 1.37.0 (CopilotKit/aimock#299) adds a JSON-expressible
match.toolResultContains substring gate on the last tool-result message.

- gen-ui-interrupt.json: cancelled legs now gate on the real toolCallId +
  toolResultContains "cancelled", ordered before the confirmation legs
- interrupt-headless.json: gained the same cancelled legs (the demo's
  Cancel button had no fixture at all)
- aimock-fixtures.test.ts: matchKey learns toolResultContains; duplicate
  ceiling 303 -> 305 (headless cancelled legs share exact keys AND
  response text with the gen-ui-interrupt ones, one pair per pill)
- e2e specs: cancel tests now assert the Denied narration and reject
  Booked/Scheduled, so the regression cannot silently return

Verified live against aimock built from source (fixture replay):
8/8 Playwright e2e across both demos, plus manual pick + cancel runs on
/demos/gen-ui-interrupt and /demos/interrupt-headless.

Commit uses --no-verify: this worktree's lefthook runner is broken
(pre-existing, see daa501daa); commitlint + prettier + the fixtures
vitest were run manually and pass.

Follow-up (blocked on aimock#299 npm publish): bump the vendored
@copilotkit/aimock pin in showcase/scripts/package.json and pull the
refreshed ghcr.io/copilotkit/aimock:latest.
2026-07-15 13:09:41 -07:00
Ran Shem Tov cb9696fc57 Merge remote-tracking branch 'origin/main' into claude/brave-kirch-8dbf00 2026-07-15 11:45:56 -07:00
Ran Shem Tov daa501daa2 fix(showcase/mastra): interrupt resume-loop, browse_web card, reasoning replay order
Playwright-verified fixes for the Mastra demo validation round:

- aimock interrupt fixtures (gen-ui-interrupt, interrupt-headless): add
  hasToolResult:false to the schedule_meeting suspend legs so the resume
  request falls through to the toolCallId confirmation fixture instead of
  re-matching the suspend leg (picker loop, duplicated intro). Mirrors
  hitl-in-chat.json.
- aimock-fixtures test: ceiling 301 -> 303; the two suspend keys now
  intentionally collide across the three mastra interrupt cells
  (runtime-disambiguated by route/fixtureFile like existing aliases).
- browse-web tool: return the result OBJECT instead of JSON.stringify;
  the bridge encodes once more so stringifying double-encoded the result
  and BrowseResultsCard showed "0 results" despite a successful browse.
- reasoning-chain pill: "Roll a d20 ..." instead of "Roll a 20-sided die
  ..." — the d4 agentic-chat fixture shadowed the first leg under replay
  (d4 loads before d6) and pushed reasoning a step late. Real-LLM order
  verified correct.
- header-forwarding shim: default x-aimock-context to "mastra" when absent
  so browser-driven demos replay against aimock instead of 404ing. Harness
  header wins when present; real providers ignore it.
- docker-compose.local: make OPENAI_BASE_URL overridable via .env (default
  aimock unchanged) so real-LLM cells like browser-use can be tested live.

(--no-verify: commitlint binary missing in this worktree after the session
crash — ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL, infra not message)
2026-07-15 11:45:36 -07:00
Jordan Ritter add6d29b7b fix(showcase): emit A2UI v0.9 nested operations from TS builders
Flip the 4 TS a2ui builders (shared/typescript + mastra,
claude-sdk-typescript, langgraph-typescript) from the legacy flat
operation shape to v0.9 nested (createSurface / updateComponents /
updateDataModel), matching the Python builder and what A2UI consumers
process. Flat ops were never processed as valid nested operations, so
the surface schema and components were never applied.

Also align the empty-data guard to Python's `if data:` semantics (empty
object -> no updateDataModel), add a v0.9 parity guard test to all 4
test files, and add 12 gen-ui-a2ui-fixed aimock fixtures.
2026-07-14 13:28:29 -07:00
Ran Shemtov ca1df2415b Merge branch 'main' into claude/brave-kirch-8dbf00 2026-07-13 19:45:50 +02:00
Jordan Ritter 0be474d562 fix(showcase/langroid): emit a2ui container via TOOL_CALL_RESULT so the card mounts
Two bugs fixed:

1. Tool result event path: the `a2ui_operations` container was emitted
   inside a `TextMessageContentEvent` block. The A2UI middleware only scans
   `TOOL_CALL_RESULT` events for the container, so the card never mounted
   and the raw JSON appeared as plain text in the chat.  Fixed by emitting a
   `ToolCallResultEvent` (matching the claude-sdk-python peer).

2. Operation shape: the ops used the legacy flat form
   (`{"type": "create_surface", ...}`) which the renderer silently ignores.
   Updated to the v0.9 nested form (`{"version": "v0.9", "createSurface":
   {...}}`) used by every other working peer (claude-sdk-python, strands,
   google-adk).

Also adds the missing langroid aimock D6 fixture for `gen-ui-a2ui-fixed`
(`display_flight` → tool result → confirmation text) so the D6 probe has
a mock response to drive the full surface-render assertion.

D6 cell: d6:langroid/gen-ui-a2ui-fixed  red → green
2026-07-06 22:18:10 -07:00
Jordan Ritter fe96b3f254 fix(showcase): resolve aimock exact-duplicate ceiling regression from #5426
PR #5426 (ag2 multimodal unquarantine) added showcase/aimock/d6/ag2/multimodal.json
with two fixtures keyed on:
  - userMessage: "can you tell me what is in this demo image I just attached", turnIndex: 0, context: ag2
  - userMessage: "can you tell me what is in this demo pdf I just attached",  turnIndex: 0, context: ag2

Those same keys already existed in showcase/aimock/d6/ag2/agentic-chat.json,
creating 2 exact duplicates within the ag2 context scope and pushing the
collision count from 297 → 299 (ceiling = 297), breaking the validate CI job.

Fix: remove the two multimodal-probe entries from agentic-chat.json since
the dedicated multimodal.json is now the authoritative home. The agentic-chat
probe does not send image/PDF turns; the multimodal probe matches via context
"ag2" against multimodal.json directly.

RED: AssertionError: Exact duplicate count (299) exceeds ceiling (297)
  → confirmed in CI run #28840506865 (Showcase: Validate main)
GREEN: all 824 tests pass after removing the duplicate entries
2026-07-06 21:15:06 -07:00
Jordan Ritter 3b1f628266 fix(showcase/ag2): unquarantine multimodal — normalize AG-UI image/document/binary content parts to autogen image_url
AG2's ConversableAgent runs every user message through
``autogen.code_utils.content_str``, which only accepts content-part
types in {"text", "input_text", "image_url", "input_image", "function",
"tool_call", "tool_calls"}. CopilotChat / the AG-UI runtime emits image
and document attachments as the modern shape

  {"type": "image" | "document", "source": {...}}

and the demo page's legacy-converter-shim.tsx ALSO appends a legacy

  {"type": "binary", mimeType, data | url}

mirror alongside it (to keep the @ag-ui/langgraph converter happy on
LangChain-based integrations — it rides through on the ag2 path too).
Both shapes trip autogen's allowed-types gate with

  ValueError("Wrong content format: unknown type image within the
  content")

…BEFORE the request reaches the vision model — observed live in the
D6 multimodal probe (commit d8a0a25db, which originally quarantined
the feature as NSF).

Fix
---
Add ``agents/_multimodal_normalize.py``: a ``NormalizingAGUIStream``
subclass of ``AGUIStream`` that overrides ``dispatch()`` to normalize
AG-UI image/document/binary content parts to OpenAI Chat Completions
``image_url`` parts AFTER ``RunAgentInput`` Pydantic parsing and BEFORE
``AgentService`` serialises the messages for autogen.

This is the only correct interception point:
- Too early (ASGI body rewrite before Pydantic): ``RunAgentInput``
  rejects ``image_url`` because it is not an AG-UI standard type —
  the discriminated union only accepts image/document/binary/text.
- Too late (inside ConversableAgent): requires patching autogen
  internals.

The override works by calling ``normalize_messages_for_autogen()`` on
the dict-serialised messages (same form as ``run_stream`` produces via
``model_dump()``) and re-injecting them via a ``_PatchedRunAgentInput``
wrapper that overrides only ``.messages``, delegating all other
attribute access to the original ``RunAgentInput``.

Conversions:
- {"type": "image", "source": {"type": "data", value, mime_type}} →
  {"type": "image_url", "image_url": {"url": "data:<mime>;base64,<value>"}}
- {"type": "image", "source": {"type": "url", value}} →
  {"type": "image_url", "image_url": {"url": value}}
- {"type": "document", "source": ...} → image_url with the document's
  mime preserved (data:application/pdf;base64,...). The vision model
  still can't natively read PDFs, but the request reaches the model
  instead of being rejected upstream, which is the failure mode this
  fix targets.
- {"type": "binary", mimeType, data | url} → image_url (the
  legacy-shim parts ride through cleanly).
- {"type": "text", ...} and already-normalised image_url parts pass
  through unchanged (identity-preserved on no-op turns).

Failure path: any normalization error is logged at WARNING and the
original messages are forwarded unchanged — autogen's own ValueError
fires verbatim with its error surface intact.

Manifest + fixture
------------------
- showcase/integrations/ag2/manifest.yaml: remove multimodal from
  not_supported_features (with its now-stale comment) and add it back
  to the features list next to voice.
- showcase/aimock/d6/ag2/multimodal.json: add the D6 fixture pair
  using the actual autoPrompt strings from sample-attachment-buttons.tsx
  ("can you tell me what is in this demo image I just attached" /
  "can you tell me what is in this demo pdf I just attached").

TDD evidence (red-green)
------------------------
showcase/integrations/ag2/tests/python/test_multimodal_normalize.py
contains 14 unit tests, pinned at three layers:

1. RED/GREEN against autogen's actual content gate:
   * test_autogen_rejects_raw_agui_image_part — confirms
     content_str([{type: image, source: ...}]) raises the verbatim
     ValueError the D6 probe surfaced. This is the regression pin: if
     autogen ever relaxes the gate, this test fails and we know to
     revisit the normalizer.
   * test_normalized_content_is_accepted_by_autogen — after
     normalize_messages_for_autogen(...), content_str accepts every
     part and renders "<image>" for the image_url part.
2. Shape coverage: modern image data/url, modern document, legacy
   binary data/url, mimeType camelCase alias, plain-text passthrough,
   plain-string content, assistant/tool messages untouched,
   unrecognised source → text placeholder, idempotency.
3. NormalizingAGUIStream class surface tripwire.

Control-plane D6 RED→GREEN:
  RED  (no normalizer, pre-fix container): d6:ag2/multimodal → red
       (HTTP 500 agent_run_error_event from content_str ValueError)
  GREEN (NormalizingAGUIStream applied):   d6:ag2/multimodal → green
2026-07-06 20:47:46 -07:00
Tyler Slaton db667891a4 showcase(claude): add SDK demo parity (#5508)
## Summary

- Productizes the Claude SDK Python and TypeScript showcase demos with
LangGraph-parity frontends.
- Wires the Claude demo backends through the official Claude Agent
SDK/AG-UI adapter paths using `claude-sonnet-4.6`.
- Keeps Claude integration docs hidden for this PR and excludes
generated/authored docs artifacts from scope.

## Why

The goal is to bring the productized LangGraph demo surface to Claude
Agents SDKs without publishing integration docs in this pass. This keeps
the PR focused on local showcase demos, runtime behavior, fixtures, and
validation support.

## How

- Ported the demo frontend surfaces and local shell-dojo support for
Claude SDK Python/TypeScript.
- Added official Claude SDK adapter/backend wiring plus real-Claude
local compose support.
- Updated Claude aimock fixtures and validation ratchets for the
expanded demo set.
- Set both Claude manifests to `docs_mode: hidden` and removed docs
setup/snippet artifacts from the PR scope.
2026-07-06 15:51:14 -07:00
Jordan Ritter b2811f4feb fix(showcase): update multimodal fixture match keys to match actual autoPrompts
Commit 7c3edca changed sample-attachment-buttons.tsx across all integrations
to auto-send via agent.addMessage with autoPrompt strings:
  - "can you tell me what is in this demo image I just attached"
  - "can you tell me what is in this demo pdf I just attached"

But the d5 harness fixture and all 19 d6 per-integration multimodal.json
fixtures still matched on the old strings:
  - "describe the sample image"
  - "summarize the sample document"

Aimock received requests with the new prompts, found no match, returned
a STRICT 404, and the agent emitted a streaming error back to the UI
(exact symptom: "An internal error has occurred while streaming events").

Also update agentic-chat.json across all 20 integrations (those files had
duplicate fallback entries for the old prompts) and fix split-fixtures.ts
to route the new strings to the "multimodal" feature bucket.

Local RED: ms-agent-python and crewai-crews both fail with fixture-miss
  status=miss before this change.
Local GREEN: langgraph-typescript passes after this change (both turns
  settle with "image" / "document" keywords confirmed in transcript).

Remaining failures after this fix are pre-existing Python backend issues
(ChatClientException on binary content parts in ms-agent-python; CrewAI
flow failure on binary content in crewai-crews) — unrelated to fixture
keys and tracked separately in the pydantic-ai multimodal work.
2026-07-06 15:31:51 -07:00
Tyler Slaton a79032e4dd feat(showcase): add claude sdk demo parity 2026-07-06 14:49:57 -07:00
Ran Shem Tov c27e87a107 fix(showcase/mastra): reasoning cells emit reasoning via a dedicated reasoning agent (OSS-384)
The reasoning demos (reasoning-default, reasoning-custom,
tool-rendering-reasoning-chain) never rendered a reasoning message. Two
root causes, both showcase-side wiring (the @ag-ui/mastra bridge forwards
reasoning correctly):

1. Agent-not-found: the reasoning-default and reasoning-custom pages request
   agent="reasoning-default"/"reasoning-custom", but the runtime registry
   listed the stale feature keys "reasoning-default-render" and
   "agentic-chat-reasoning" instead, so the chat never started. Register the
   real agent names (the demoAgentNames parity test enforces this).

2. Non-reasoning model: all reasoning demos mapped to the default weatherAgent
   (gpt-4o), which the OpenAI Responses API never emits reasoning-summary
   items for, so the reasoning slot stayed dark. Add a dedicated reasoningAgent
   (gpt-5-mini via OPENAI_REASONING_MODEL) with
   providerOptions.openai.{reasoningEffort,reasoningSummary:"detailed"} on the
   agent's default stream options, mirroring langgraph-python's reasoning_agent.
   Map reasoning-default and reasoning-custom to it.

For tool-rendering-reasoning-chain, add a dedicated reasoningChainAgent that
registers the four chain tools (get_weather, search_flights, get_stock_price,
roll_dice, the latter new) under the exact tool-call names the aimock fixtures
emit, on the reasoning model, so Mastra executes each leg and the multi-turn
chain advances through its toolCallId-keyed fixtures to the closing narration.

Fixture parity fixes so the chain's gold toolCallId scheme wins on the
Responses path (matching langgraph-python):
- d4/mastra/chat.json: rename the broad "weather" and "flights from SFO to JFK"
  probes to the non-colliding "_d4_unused_*" sentinels gold uses.
- d6/mastra/tool-rendering.json: the basic AAPL fixture used turnIndex:0, which
  matched as a behind-count turn and stole the stock chain's later turns; switch
  to hasToolResult:false (gold parity) so it only answers the no-tool-result turn.

Manifest features aligned to gold (reasoning-default, reasoning-custom). Removed
the stray agentic-chat-reasoning e2e spec (no page, no gold equivalent) and
renamed the reasoning QA docs to match the cell ids.

Verified via Playwright against the up --dev mastra container + aimock:
reasoning-default 2/2, reasoning-custom 7/7, tool-rendering-reasoning-chain 5/5.
2026-07-02 20:58:27 +02:00
Ran Shem Tov c920272ccc fix(showcase/mastra): shared-state-streaming multi-pill fixture + state-streaming docs (OSS-423)
Follow-up to the sharedStateStreamingAgent commit.

- fixture: key the confirmation leg on the updateWorkingMemory toolCallId
  (ordered before the leg-1 entry) instead of hasToolResult. hasToolResult is
  a thread-global predicate, so in the sequential 3-pill D6 probe pill 2's
  leg-1 stopped matching once pill 1 left a tool result in the thread. The
  toolCallId anchor keeps each pill's two legs disambiguated across the
  interleaved conversation (matches the langgraph-python gold pattern).
- docs: add shell-docs shared-state/predictive-state-updates.mdx ("State
  streaming") documenting the Mastra updateWorkingMemory -> STATE_DELTA path,
  at parity with the 7 other integrations that ship this page; registered in
  the shared-state meta.json.
2026-07-02 20:00:58 +02:00
Ran Shem Tov e8b1652427 fix(showcase/mastra): stream shared state via updateWorkingMemory STATE_DELTA (OSS-423)
The shared-state-streaming demo mapped to the generic weatherAgent (state
{proverbs}, no `document` field) and its aimock fixture returned plain text
gated on a stale "stream the counter to 5" match, so nothing ever streamed
into `state.document` and the e2e failed.

Add a dedicated `sharedStateStreamingAgent` with working memory enabled on a
`{ document: string }` schema. It writes drafts through Mastra's built-in
`updateWorkingMemory` tool; the @ag-ui/mastra bridge intercepts the streamed
tool-call args (OSS-414) and emits a leading STATE_SNAPSHOT followed by
incremental STATE_DELTA on /document, so the UI renders the document
token-by-token. This is the Mastra-native equivalent of langgraph-python's
StateStreamingMiddleware / predictive-state pattern.

- agents: add sharedStateStreamingAgent + SharedStateStreamingAgentState
- index: register the agent on the Mastra instance
- route: map shared-state-streaming -> sharedStateStreamingAgent with a
  dedicated resourceId, build guard, and LocalMastraAgentName entry
- fixture: drive updateWorkingMemory with the streamed document for all three
  pills (poem / email / quantum) plus a confirmation turn on hasToolResult
2026-07-02 19:07:52 +02:00
Ran Shem Tov 3058e215e3 feat(showcase/mastra): background-agents, observational-memory, browser-use demos (OSS-426/427/91)
Integrates 3 gap demos (authored in parallel worktrees, ported onto the
upgraded branch):

- background-agents (OSS-426): run_deep_research tool flagged
  background:{enabled:true} + Mastra backgroundTasks:{enabled:true} → the bridge
  maps background-task-started → a live 'working' activity card. Completion is
  out-of-band by design (not asserted). Dedicated route + fixture + e2e + qa.
- observational-memory (OSS-427): OM enabled on the agent Memory
  (scope:thread, observation 600/300) + surfaced via getLocalAgents({
  observationalMemory:true }). SIZABLE pills trip the token-size trigger.
  NOTE: OM data-om-* chunks come from the OM processor + observer LLM, not the
  mocked completion — so it does NOT replay deterministically under aimock; the
  e2e asserts the deterministic subset (page + pills + completing turn) and
  full OM-card verification needs a real-LLM run (documented in qa).
- browser-use (OSS-91): Mastra-only, real-LLM. browse_web tool drives a LOCAL
  headless Playwright Chromium (NO Browserbase) — top HN / page read, rendered
  as in-chat cards. Non-deterministic → no aimock D6 fixture; smoke e2e only.
  Needs 'npx playwright install chromium' at runtime (documented in Dockerfile+qa).

Shared: 3 agents + registrations + backgroundTasks toggle, 2 tool exports,
manifest features+demos (now 42 demos, not_supported_features still []),
playwright dep, demoAgentNames excludes for the 4 dedicated-route cells.
next build clean (all routes); validate-parity 0-fail; validate-pins baseline (38).
2026-07-02 15:06:00 +02:00
Ran Shem Tov aa0fc4290c feat(showcase/mastra): A2UI error recovery demo (OSS-422)
Adds the a2ui-recovery cell — full parity with langgraph-python / strands / ADK.
Backend a2uiRecoveryAgent owns generate_a2ui via getA2UITools (@ag-ui/mastra/a2ui
subpath) which runs the forced render_a2ui subagent + the toolkit validate->retry
recovery loop + a2ui_recovery_exhausted hard-fail. Dedicated route
/api/copilotkit-a2ui-recovery sets a2ui.injectA2UITool=false (agent owns the
tool); reuses the declarative-gen-ui catalog. Frontend mirrors strands
(page/chat/suggestions, unique mastra prompts). aimock fixture: HEAL seq0-invalid
->seq1-valid, EXHAUST always-invalid; e2e asserts heal paints (declarative-metric
x2) + exhaust shows the hard-failure UI. manifest feature + demo entry + qa.
next build clean; validate-parity 0-fail; validate-pins baseline (38).
2026-07-02 14:36:00 +02:00
Ran Shem Tov adf6d73d84 feat(showcase/mastra): native useInterrupt + bump @copilotkit/* 1.62.1 (OSS-383)
The @copilotkit/react-core v2 resume-path bug that quarantined gen-ui-interrupt
+ interrupt-headless is fixed as of 1.62.1, so migrate both cells to the native
interrupt path and move them out of not_supported_features.

- Bump @copilotkit/* 1.61.2 -> 1.62.1 (react-core/runtime/shared/voice/
  a2ui-renderer + web-inspector core override). next build clean (40 routes).
- Backend: new src/mastra/tools/interrupt.ts — a real Mastra suspend tool
  (schedule_meeting) with suspend/resume schemas; returns suspend() directly so
  the agentic loop pauses. Wired into interruptAgent (was tools:{}). Instance
  storage (src/mastra/index.ts) already satisfies the resume snapshot prereq;
  emitInterruptOutcome defaults true in the v1 bridge so the standard
  RUN_FINISHED outcome fires (resumable on client >=1.61.2).
- gen-ui-interrupt: useHumanInTheLoop workaround -> native useInterrupt
  (renderInChat), reading the Mastra suspend wrapper's suspendPayload.
- interrupt-headless: hand-rolled on_interrupt subscription -> native
  useInterrupt({renderInChat:false}) placed in the app surface (the hook handles
  both the standard outcome and legacy on_interrupt + the correct resume array).
- manifest: not_supported_features now []; gen-ui-interrupt + interrupt-headless
  in features; interrupt_pattern: native (parity with langgraph-python).
- fixture: gen-ui-interrupt.json userMessage keys (introductory sales team call
  / one-on-one with Alice) didn't substring-match the D6 probe + e2e prompts
  (never caught while skipped) -> aligned to 'intro call with the sales team' /
  '1:1 with Alice'. interrupt-headless.json already correct.

Local D6 e2e not run (Docker daemon container-create wedged in this env);
build-verified + relying on CI/harness. Native suspend/resume path is
dojo-proven on CopilotKit >=1.61.2.
2026-07-02 13:56:03 +02:00
Jordan Ritter 9dd97fecb0 fix(showcase): gate llamaindex d4 chat 'weather' fixture on get_weather toolName
The bare-substring 'weather' fixture in aimock/d4/llamaindex/chat.json emitted a
get_weather tool call with no toolName gate. The tool-free voice agent's prompt
"What is the weather in Tokyo?" (substring "weather") leaked into this fixture,
emitting a get_weather call the voice agent could never resolve, so the voice D6
cell hung (done-signal-missing, body stuck on get_weather/Running).

Add toolName:"get_weather" so the fixture only fires when the requesting agent
actually registers get_weather (mirrors the gate in d6 tool-rendering.json). The
tool-free voice request now falls through to voice.json's exact content match.

Local red->green proof (showcase test llamaindex:voice --d6 --direct):
- RED:   done-signal-missing; body "What is the weather in Tokyo? get_weather Running"
- GREEN: assistant settled "The weather in Tokyo is currently 22C with partly
         cloudy skies and light easterly winds."; 1 passed (3.0s)
Direct aimock probes confirm the gate: tool-free -> content; with get_weather tool
-> tool call still fires. Regression: tool-rendering D6 still green; headless-complete
weather turn still passes (uses its own gen-ui-headless-complete.json fixture).

(cherry picked from commit cf6ff7c08153367239437d6c4fff425d546eb245)
2026-06-28 11:26:28 -07:00
Jordan Ritter f1f9dc2890 fix(showcase): narrow llamaindex d4 'summarize' fixture to 'Summarize the sales pipeline'
The bare 'summarize' userMessage in d4/llamaindex/chat.json substring-matched
the D6 gen-ui-agent pill 'Research our top competitor and summarize their
strengths and weaknesses.', returning the sales-pipeline text fixture instead
of the gen-ui-agent set_steps tool call. The competitor pill then produced
no/duplicate steps, failing d6:llamaindex. Narrow the match to the verbatim D4
toolbar probe 'Summarize the sales pipeline' (langgraph-python parity), which
no demo pill contains as a substring. D4 llamaindex stays green (the bare entry
was unused by any D4 cell).

(cherry picked from commit c23801c8b32d292cacf6fb2e7e2a68270eebaa84)
2026-06-28 10:22:53 -07:00
Jordan Ritter 196cf1dc6f docs(showcase/aimock): correct stale llamaindex gen-ui-declarative _note to streamed render_a2ui contract
The _note asserted injectA2UITool:false (unchanged) and that flipping to true
would blank-render, and that generate_a2ui returns an a2ui_operations container
for the middleware to forward. Both are now false: this PR set injectA2UITool:true,
generate_a2ui returns raw planner args, and the surface mounts from a streamed
render_a2ui tool-call (START/ARGS/END) the agent re-emits, which the middleware
watches under injectA2UITool:true. Prose-only; no match keys or payloads changed.

(cherry picked from commit 5679b001580615f2e7d988d8c7063994076ace29)
2026-06-28 07:24:59 -07:00
Jordan Ritter b1b4ae6d83 fix(showcase): llamaindex declarative-gen-ui — d6 fixture, DataTable catalog, shared pills
Rebuild the per-integration d6 fixture to kill the missing-arg

generate_a2ui OOM loop; add the DataTable catalog component

(definitions + renderer); align suggestions.ts to shared probe pills.

Completes the integration-only fix: 4/4 pills mount, surface renders.
2026-06-28 07:04:56 -07:00
Ran Shem Tov b985449e50 feat(showcase): add A2UI Error Recovery demo for langgraph + strands
Port the google-adk a2ui-recovery demo to langgraph (python, fastapi,
typescript) and aws-strands (python, typescript). Each ships a dedicated
recovery agent, route, demo page/chat/suggestions, manifest entry, aimock
d6 fixtures, e2e spec, and QA doc.

Backend-owned recovery on langgraph via get_a2ui_tools / getA2UITools
(injectA2UITool=false); auto-inject recovery on the strands adapter path.
Heal stages an invalid-then-valid render via aimock sequenceIndex (the
toolkit validate->retry loop rejects the whole surface, so a single-pass
parse_and_fix heal is ADK-specific and does not apply here). Recovery
prompts are unique per framework and the fixtures carry no context match
field, so they fire for real browser (dojo) traffic, not just the harness.

Also harden the strands declarative-gen-ui composition guide to name the
exact catalog component (Metric, not MetricTile) and update the
generate-catalog + aimock-fixtures test expectations.
2026-06-26 16:17:58 +02:00