The snippet stated the collision rule under factory mode, which implied the
runtime resolves it for you there. It does not.
Config mode is where the runtime decides: `index.ts` builds the tool set from
`convertToolsToVercelAITools(input.tools)` and then spreads the configured
tools over it, so a shared name resolves to the backend tool. The rule now
sits in that step.
In factory mode the factory owns precedence, because nothing merges the two
lists on its behalf. The showcase's own TanStack factory shows one choice,
filtering forwarded tools through `!serverToolNames.has(t.name)`, but that is
its decision rather than runtime behavior. The factory step now says so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both snippets named namespaces that do not resolve.
`Microsoft.Agents.AI.Harness` is the NuGet package name, not a namespace.
`AsHarnessAgent` and `HarnessAgentOptions` come from `Microsoft.Agents.AI`,
and no `.cs` file in the repo carries a `using` for the package name.
Both snippets also needed `using Microsoft.Extensions.AI`, which is where
`AsIChatClient()` and `ChatOptions` live. Every agent file in both showcase
columns uses exactly `Microsoft.Agents.AI` plus `Microsoft.Extensions.AI`.
Also define `HarnessMaxContextWindowTokens` and `HarnessMaxOutputTokens` in the
harness snippet, which referenced them without showing a value.
No .NET SDK is available here, so this was checked against the `using` blocks
of the twelve agent files that call `AsHarnessAgent`, not by compiling.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`generative-ui/tool-based` is the terminal page every onboarding run fetches,
and its `## How it works in code` section renders a per-framework snippet.
Nine frameworks shipped no snippet, so the section rendered nothing and the
silence meant both "no agent-side wiring is needed" and "wiring is needed and
nobody wrote it down".
Each verdict was read out of the pinned adapter rather than the docs:
- ag2: `run_stream` builds `client_tools` from `incoming.tools`.
- mastra: the adapter reduces `input.tools` into `clientTools`.
- strands, strands-typescript: a proxy tool per forwarded tool is registered
in the agent's tool registry, and a native tool of the same name wins.
- agno: its AG-UI interface never reads `RunAgentInput.tools`, so a component
needs an `external_execution=True` stub and a `db` for the paused run.
- deepagents: `CopilotKitMiddleware` merges `copilotkit.actions` into
`request.tools`, so the middleware is load-bearing.
- built-in-agent: config mode forwards for you, a factory does not. It is also
the root framework, so this is the unscoped default page.
The two .NET columns rest on their own demo agents, which render charts with
an empty tool list. No .NET SDK was available to read the NuGet hosting
package.
REQUIREMENT_NOT_ESTABLISHED is now empty. The shape test pins each snippet,
and setup-concept.test.ts no longer depends on ag2 being an open gap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Why
`showcase/scripts/validate-parity.ts` keys spec and QA filenames to the
demo id. Seven integrations still carried the pre-rename `byoc-*` names
for the hashbrown / json-render demos, so the validator emitted spurious
`demo 'declarative-hashbrown' has no qa/declarative-hashbrown.md` style
warnings.
**454 → 446 warnings, still 21/21 pass.**
## What changed
**QA docs (14 files, all 7 integrations)** — renamed `qa/byoc-*.md` →
`qa/declarative-*.md` and reconciled against the `langgraph-python`
north star. Test Steps and Expected Results are now identical per demo
across every integration; genuinely per-integration facts (agent mount
path, env var, prompt module, preserved regression guards) live in an
`Integration notes` section. Every fact was verified against source —
pill labels, `data-testid`s, header text, package pins, API route →
`AGENT_URL` mappings — rather than carried over from the old doc.
**E2E specs (10 files, 5 integrations) — deleted, not renamed.** Those
integrations already ship `declarative-hashbrown.spec.ts` /
`declarative-json-render.spec.ts` byte-identical to the north star (md5
`57eee13d…` / `638e2ac4…`). The `byoc-*` copies point at `/demos/byoc-*`
routes that no longer exist and assert little beyond "page loads", so
renaming them would have clobbered the good specs. Spec counts stay
above demo count in all five packages, so no under-coverage warning
appears.
Python backend modules keep their `byoc_` prefix
(`byoc_hashbrown_agent.py`, `byoc_json_render_agent.py`) — the north
star uses those names too and integration `manifest.yaml` files
reference them under `highlight:`.
`claude-sdk-python` is deliberately untouched (handled in OSS-578 /
#6235).
## Found along the way, NOT fixed here
1. **`declarative-json-render` is broken in both crewai packages.** The
demo page (a north-star copy) mounts
`runtimeUrl="/api/copilotkit-declarative-json-render"`, but those
packages only ship `src/app/api/copilotkit-byoc-json-render/route.ts`,
and `next.config.ts` has no covering rewrite — the runtime URL 404s. The
rename was only half applied: page renamed, API route not. Documented as
a known break in the affected QA docs; fixing it is a runtime change, so
it wants its own PR.
2. **`langgraph-python/qa/declarative-json-render.md` still has the
stale title** `# QA: BYOC json-render — LangGraph (Python)`. Left alone
so as not to collide with #6235.
Minor, also left as-is: `crewai-*/src/app/demos/byoc-hashbrown/page.tsx`
are one-line alias re-exports of the declarative page, and the
`crewai-*` / `llamaindex` manifests still list `byoc-hashbrown` /
`byoc-json-render` under `features:` (a separate id namespace the
validator does not read).
## Verification
```
cd showcase/scripts && npx tsx validate-parity.ts
# 21 package(s) checked, 21 pass, 0 fail, 446 warning(s)
```
Diff of validator output before/after shows exactly the 8 target
warnings removed and nothing new. `showcase/scripts` suite: 78 files /
2539 tests pass. No dangling references to the deleted paths anywhere in
`showcase/` or `.github/`.
validate-parity.ts keys QA filenames to the demo id, so the seven
integrations still carrying byoc-* names produced spurious "demo
'declarative-hashbrown' has no qa/declarative-hashbrown.md" warnings.
454 -> 446 warnings, still 21/21 pass.
Test steps and expected results are now identical to the langgraph-python
north star per demo. Per-integration facts (agent mount, env var, prompt
module) moved into an Integration notes section and were verified against
source rather than carried over. Python backend modules keep their byoc_
prefix -- manifests reference them under highlight:.
Also records a pre-existing break in both crewai packages: the
declarative-json-render page requests
/api/copilotkit-declarative-json-render, but only
copilotkit-byoc-json-render exists, so the demo 404s on its runtime URL.
The declarative-* specs these five integrations already ship are
byte-identical to the langgraph-python north star and cover the canonical
routes. The byoc-* copies point at /demos/byoc-* routes that no longer
exist and assert little beyond "page loads", so they can never fail
meaningfully -- renaming them would have clobbered the good specs.
claude-sdk-python is deliberately untouched (OSS-578 / #6235).
## What
Brings the just-released A2UI support and the latest Microsoft Agent
Framework
(Python) into the showcase, and moves the MAF A2UI demos onto the native
subagent / auto-inject technique so they match the langgraph-python
reference
instead of the pre-1.2.0 hand-rolled path.
- **Bump MAF to 1.2.0.** `agent-framework-ag-ui[a2ui]==1.2.0`,
`agent-framework-openai==1.14.0`, `agent-framework-core==1.15.0`,
`ag-ui-a2ui-toolkit==0.0.4`. 1.2.0 is A2UI's first release; the `[a2ui]`
extra
pulls the toolkit.
- **New `a2ui-recovery` demo** (the one A2UI demo the integration lacked
vs
langgraph-python). Backend-owned via the adapter's native `enable_a2ui`
(`injectA2UITool: false`), which runs the shared toolkit's
validate/retry
recovery loop in-process. Heal pill recovers a malformed first render;
exhaust
pill hits the attempt cap and surfaces the `a2ui_recovery_exhausted`
fallback.
Adds the agent, route, page, chat, suggestions, a D6 fixture, and an e2e
spec.
- **Migrate `declarative-gen-ui` to native A2UI auto-injection.**
Removed the
hand-rolled `generate_a2ui` (a raw secondary OpenAI call to
`_design_a2ui_surface`); the agent now binds no A2UI tool and the route
sets
`injectA2UITool: true`, so the adapter's `plan_a2ui_injection`
auto-injects the
native `generate_a2ui` sub-agent. Reworked the D6 fixture to the native
`render_a2ui` shape.
- **Migrate `beautiful-chat` to native A2UI auto-injection.** Same
change for the
flagship composite: removed its hand-rolled `generate_a2ui`, flipped the
route
to `injectA2UITool: true`. The adapter now auto-injects the native
sub-agent
alongside the agent's own tools (todos, query, flights).
- **`a2ui-fixed-schema` unchanged** — it is the fixed-schema pattern
(client
authored schema, agent streams data via a backend `display` tool,
`injectA2UITool: false`), which langgraph-python does identically. Not
hand-rolled generation.
- **Docs.** Enriched the MAF A2UI docs page into real "how to A2UI"
content
(dynamic / fixed / recovery) and connected the A2UI demos via
`docs-links`.
## Why native (not the dojo's example agents)
The showcase mirrors langgraph-python's frontend-catalog + auto-inject
pattern,
not the AG-UI dojo's `a2ui_config` example agents. Before 1.2.0 the MAF
adapter
had no native A2UI, so the showcase hand-rolled `generate_a2ui`. 1.2.0
ships the
native path, so these demos now use it and match langgraph 1:1.
## Validation
- `validate-pins` clean (FAIL count + hash unchanged), `validate-parity`
PASS,
`generate-registry` clean.
- **D6 (full frontend, aimock replay), all green:**
- `a2ui-recovery` — heal paints the recovered surface; exhaust shows the
hard-failure UI.
- `declarative-gen-ui` — all 4 dashboard pills paint on the auto-inject
path
(confirms the runtime forwards `injectA2UITool: true` and
`plan_a2ui_injection` fires).
- `beautiful-chat` — regression across all 5 features (pie/bar chart,
schedule-meeting, search-flights, toggle-theme): wrapping the multi-tool
agent in the A2UI planner loop does not break its non-A2UI tools.
- **AG-UI protocol layer** (published 1.2.0 wheel + aimock): recovery
(heal/exhaust), declarative, and beautiful-chat all stream real
`a2ui_operations` / `a2ui_recovery_exhausted`, RUN_FINISHED, no
RUN_ERROR.
- **Remove the last hand-rolled A2UI (default agent).** The
general-purpose
default agent (`agent.py`, catch-all `/` endpoint) also carried a
hand-rolled
`generate_a2ui`; removed it (the default agent no longer offers A2UI,
matching
langgraph's default agent). Stripped the stale `_design_a2ui_surface`
fixture
residue and refreshed the e2e-spec comments that described the old
mechanism.
After this PR the MAF-python integration has **zero hand-rolled A2UI
anywhere
except the fixed-schema demo** (which is the intended fixed-schema
pattern,
identical to langgraph). The shared `tools/generate_a2ui.py` module is
intentionally left intact — it is symlinked by other integrations (ag2,
agno, …)
that have not migrated; MAF-python simply no longer imports it.
## Pre-existing, out of scope
Full D6 for ms-agent-python is 37/40. The 3 red cells — `multimodal`,
`voice`,
`hitl-approve-deny` — are **not** touched by this PR: their agents are
byte-identical to main and never used `generate_a2ui`. `multimodal` is
the known
shared-CopilotKit frontend bug (`runStartCount=0`, the run never
starts);
`voice`/`hitl-approve-deny` complete the run but their text does not
settle inside
the probe's tight budget. All A2UI and default-agent cells pass.
`generative-ui/tool-based` is now the terminal page every onboarding run fetches
(OSS-1034), and its "How it works in code" section is a bundled
`frontend-tools-setup` concept. Only 5 of 19 frameworks shipped one, so for the
rest the section rendered nothing and absence encoded two different facts: this
framework needs no agent-side wiring, or it needs some and nobody wrote it down.
Four frameworks whose own gen-ui-tool-based demo agent settles the question get
a snippet. pydantic-ai, llamaindex and ms-agent-python declare no tools at all --
the AG-UI request forwards them and their demo agents say so in as many words --
so their snippet states that, and then states the half that is easy to miss: a
model with no instruction about the tool answers in prose and the component never
renders. CrewAI is the opposite case. A Flow owns its own model call, so it has
to read `state.copilotkit.actions` and pass them itself, wrap the call in
`copilotkit_stream`, and drive `tool_choice`.
A compile failure in a bundled snippet no longer returns null. It shared that
return with "nobody bundled this", so a rendering defect shipped looking exactly
like a deliberate omission, traceable only through a console.error nobody reads
in production. Absence stays quiet; a broken snippet throws.
The nine frameworks still undetermined are named in a list a test reads, so a new
framework cannot join the gap silently and closing one means deleting a name.
Refs OSS-1036
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review caught a real bug: the endpoint only applies `a2ui_config` while wrapping a
RAW agent. The recovery factory returned an already-wrapped `AgentFrameworkAgent`,
so its `a2ui_config` was dropped and recovery silently ran on toolkit defaults
instead of the configured `maxAttempts: 3`.
- recovery_agent.py: `create_agent` now returns a raw `Agent`; the /a2ui_recovery
endpoint wraps it and applies `A2UI_RECOVERY_CONFIG` (verified: the wrapper carries
the config). D6 green with the config actually applied.
- Refresh the remaining old-path descriptions to the auto-inject + a2ui_config wording:
manifest.yaml, the demo page.tsx header, the e2e spec contract, and the fixture _note.
Address review: switch the a2ui-recovery demo from an explicit enable_a2ui() wrap
to MAF's auto-injection path, matching the other MAF A2UI demos and MAF's upstream
recovery example.
- recovery_agent.py: now a plain Agent (no enable_a2ui, no tool). The recovery cap
+ catalog live in A2UI_RECOVERY_CONFIG.
- agent_server.py: the /a2ui_recovery endpoint passes a2ui_config=A2UI_RECOVERY_CONFIG
({"recovery": {"maxAttempts": 3}, "default_catalog_id": "declarative-gen-ui-catalog"}).
- route.ts: injectA2UITool false -> true (auto-inject, same as declarative-gen-ui).
- docs: dynamic-schema "backend recovery policy" section now shows the a2ui_config
path instead of enable_a2ui.
Verified: the agent constructs as a plain Agent; plan_a2ui_injection fires from the
forwarded injectA2UITool flag and reads recovery{maxAttempts:3} + catalog from
a2ui_config, producing the same A2UIAgent recovery engine (no separate subagent
client / attempt callback / flag-independent injection needed, so enable_a2ui was
not required).
The MAF A2UI docs were a single page rendering the shared A2UI stub, which
matched neither of the two prod shapes. Restructure to shape #2 — the a2ui
submenu — mirroring the langgraph / strands / deepagents tree 1:1:
generative-ui/a2ui/
index.mdx (Overview)
fixed-schema.mdx
dynamic-schema.mdx
styling.mdx
advanced.mdx
meta.json
Content is ported page-by-page from the reference tree (deepagents, authored
mode) with MAF-accurate code — `agent-framework-ag-ui` auto-injection
(`injectA2UITool`), the real `a2ui_dynamic` / `a2ui_fixed` agents, and the
`enable_a2ui` backend-owned recovery note. styling/advanced are the
framework-agnostic frontend pages. docs-links now point the demos at the
matching sub-pages (dynamic-schema / fixed-schema).
The general-purpose default agent (agent.py, catch-all `/` endpoint) still
carried a hand-rolled `generate_a2ui` (raw secondary OpenAI call to
`_design_a2ui_surface`). Removed it: the default agent no longer offers A2UI at
all, matching the langgraph-python default agent. The main route enables no A2UI
middleware, so this was latent/dead A2UI anyway.
- agent.py: drop the hand-rolled generate_a2ui tool + its import.
- render-a2ui.json: strip the stale `_design_a2ui_surface` fixture entries (the
native `render_a2ui` + generate_a2ui entries remain).
- e2e specs: refresh the declarative-gen-ui + beautiful-chat comments that
described the old `_design_a2ui_surface` mechanism to the native auto-inject path.
The shared `tools/generate_a2ui.py` module is intentionally kept: it is symlinked
by other integrations (ag2, agno, ...) that still hand-roll A2UI; MAF-python
simply no longer imports it. After this, the MAF-python integration has ZERO
hand-rolled A2UI anywhere except the fixed-schema demo (which is the intended
fixed-schema pattern, identical to langgraph). Full D6: all A2UI + default-agent
cells green.
Remove the flagship demo's hand-rolled `generate_a2ui` (raw secondary OpenAI
call to `_design_a2ui_surface`) and flip its route to `injectA2UITool: true`, so
the agent-framework-ag-ui adapter auto-injects the native `generate_a2ui`
sub-agent alongside the agent's own tools (manage_todos, query_data,
search_flights). Matches the langgraph-python reference.
Regression: D6 green across all 5 beautiful-chat features (pie/bar chart,
schedule-meeting, search-flights, toggle-theme) — wrapping the multi-tool agent
in the A2UI planner loop does not break its non-A2UI tools; search_flights still
emits its fixed-schema a2ui_operations. The native dynamic A2UI path
(generate_a2ui -> render_a2ui -> a2ui_operations) is verified against aimock on
the published 1.2.0 wheel. fixed-schema remains backend-owned, matching langgraph.
Replace the pre-1.2.0 hand-rolled `generate_a2ui` (a raw secondary OpenAI call
to `_design_a2ui_surface`) with the native auto-inject path, matching the
langgraph-python reference:
- a2ui_dynamic.py binds NO A2UI tool; the route sets `injectA2UITool: true`, so
the agent-framework-ag-ui adapter's `plan_a2ui_injection` auto-injects the
native `generate_a2ui` sub-agent (progressive `render_a2ui` streaming + the
shared toolkit recovery loop) in-process.
- Reworked the D6 fixture from the `_design_a2ui_surface` shape to the native
`render_a2ui` shape (inner keyed on the full pill prompt; outer emit + narration).
D6 green: all 4 declarative pills pass through the real frontend + browser on the
auto-inject path (verified `plan_a2ui_injection` fires and the runtime forwards
the flag). fixed-schema stays backend-owned (`injectA2UITool: false`), matching
langgraph; that is the fixed-schema pattern, not hand-rolled generation.
Bring the just-released A2UI support and latest Microsoft Agent Framework
(Python) into the showcase, following the langgraph-python A2UI pattern.
- Bump agent-framework-ag-ui[a2ui]/openai/core to 1.2.0/1.14.0/1.15.0. 1.2.0
is A2UI's first release; the [a2ui] extra pulls ag-ui-a2ui-toolkit.
- Add the a2ui-recovery demo, mirroring langgraph-python's recovery demo:
backend-owned A2UI via the adapter's native enable_a2ui (injectA2UITool=false),
which runs the shared toolkit validate/retry recovery loop in-process. The
heal pill recovers a malformed first render into a valid surface; the exhaust
pill hits the attempt cap and surfaces the a2ui_recovery_exhausted fallback.
Reuses the declarative-gen-ui catalog. Adds the agent, route, page, chat,
suggestions, a D6 aimock fixture (framework-unique prompts), and an e2e spec.
- Enrich the MAF A2UI docs page with how-to content covering the three A2UI
flavors (dynamic, fixed, recovery) and connect the three A2UI demos through
docs-links.
Validation: validate-pins clean (count/hash unchanged), validate-parity PASS,
generate-registry clean. The recovery loop is verified at the AG-UI protocol
layer against aimock on the published 1.2.0 wheel (heal streams a2ui_operations
after an invalid-then-valid render; exhaust returns a2ui_recovery_exhausted
after 3 attempts; RUN_FINISHED, no RUN_ERROR).
## Summary
- enable the LlamaIndex multimodal Showcase demo
- update the LlamaIndex AG-UI protocol pin and its compatible
core/OpenAI adapter pins
- register the multimodal agent and refresh the integration parity notes
## Verification
- Showcase manifest and route validation passed
- LlamaIndex integration production build passed
- container dependency check passed with core 0.14.24, llms-openai
0.7.10, and protocols-ag-ui 0.4.1
- live local OpenAI smoke passed for both image and PDF attachments
## Known harness issue
The shared D6 image assertion still expects the exact contiguous phrase
`copilotkit logo`; OpenAI returned the semantically equivalent `a logo
for CopilotKit`. The browser validation confirmed one intact image
attachment and a relevant response.
## What
Eleven demos in the `crewai-crews` showcase column (labelled **CrewAI
Flows** in the UI) were served by `add_crewai_crew_fastapi_endpoint`
through a root catch-all, not by the Flow helper. This moves them onto a
real CrewAI Flow and removes the catch-all.
Affected demos: agentic-chat, gen-ui-tool-based, prebuilt-sidebar,
prebuilt-popup, chat-slots, chat-customization-css, headless-simple,
readonly-state-agent-context, agent-config, auth, voice.
## Why
`add_crewai_crew_fastapi_endpoint` wraps the crew in `ChatWithCrewFlow`,
which composes its system message with CrewAI's `build_system_message`.
That boilerplate is unconditional: it instructs the model to introduce
itself and to steer every answer back to the crew's purpose, using a
research-report example. Because the catch-all served the scaffold
research crew, those demos answered the user's question and then offered
to research the latest AI developments.
Measured against real OpenAI on `main`, first turn:
> Hey! I'm here to help you with researching cutting-edge developments
and producing detailed, actionable reports.
> The capital of France is **Paris**. If you'd like, I can also help by
generating a **current research report** on a topic of your choice.
Second turn, arithmetic question:
> 12 × 12 = 144.
> I'm here to help with researching the latest AI developments and
producing actionable reports.
Pre-seeding a hand-written `crew_description` (the existing
`_chat_flow_helpers.preseed_system_prompt`) only retargets that tail, it
does not remove it — verified on `/mcp-apps`, which is pre-seeded and
still introduces itself and offers a diagram.
## How
- New `src/agents/chat_flow.py` holds `PromptedChatFlow`, a one-turn
Flow that owns its own prompt and forwards frontend tools.
`crewai-conversational-flows` already had this class inline; it now
imports the same file, so both columns share one prompt.
- `agent_server.py` registers it at `/chat` via
`add_crewai_flow_fastapi_endpoint`, and the root catch-all registration
is gone. An unrouted agent name now fails loudly instead of landing on
someone else's backend.
- The runtime route's default target becomes `/chat`; the
`agent-config`, `auth`, and `voice` routes point there too.
- The remaining crew endpoints (`/mcp-apps`, `/byoc-hashbrown`,
`/byoc-json-render`) are untouched — each already overrides the composed
system message explicitly.
- Comments that described the removed catch-all were corrected in both
CrewAI columns.
## Verification
Against real OpenAI on the patched backend:
- `/chat` answers `Paris.` and `12 × 12 = 144.` with no purpose-reminder
tail.
- A frontend tool still round-trips: `generate_haiku` emits
`TOOL_CALL_START` / `TOOL_CALL_ARGS` / `TOOL_CALL_END`.
- `POST /` returns 404.
Python suites: 162 passed (`crewai-crews`), 164 passed
(`crewai-conversational-flows`). New coverage in `test_chat_flow.py`
(prompt contract, no crew-chat boilerplate, tool forwarding) plus a
routing contract test asserting no cell can reach a crew endpoint by
fall-through.
D6 replay: see the checklist below.
### D6 replay (local, `--d6 --direct`, warm stack)
All fourteen green: the eleven affected cells (agentic-chat,
gen-ui-tool-based, prebuilt-sidebar, prebuilt-popup, chat-slots,
chat-customization-css, headless-simple, readonly-state-agent-context,
agent-config, auth, voice) plus tool-rendering, hitl-in-chat and
shared-state-read-write as untouched controls.
gen-ui-tool-based needed the second commit: the shared probe had both
CrewAI columns off its chart-integration list, so it sent the haiku
prompt and waited for a haiku card the page cannot draw. The probe's own
unit tests still pass (11).
The conversational column's D6 was not run — it is not deployed, and its
image was not built in this session. Its Python suite passes and its
wiring mirrors the crews column line for line.
Closes OSS-901.
## Problem
`/mastra/generative-ui/a2ui/fixed-schema` could not be followed. A
Mastra onboarding run on Codex stopped there rather than invent an API,
reporting that the guide "depends on unbundled showcase helpers."
That is true, and the mechanism is worse than the report. Region bodies
are assembled at bundle time, so what ships is invisible in a source
diff. The `backend-render-operations` marker sits on **line 1** of
`mastra/src/mastra/tools/index.ts` — put there by the marker-hoist sweep
in 34b6418 so snippets would carry their imports — and the closing
marker is at the bottom of the file. The page therefore published **all
432 lines** of the tools barrel: weather, stock price, dice, d20,
query-data, schedule-meeting, search-flights, the aimock
header-forwarding import, and
```ts
import { generateA2uiImpl, buildA2uiOperationsFromToolCall } from "@copilotkit/showcase-shared-tools";
```
`@copilotkit/showcase-shared-tools` is not a package. It is a tsconfig
`paths` entry (`mastra/tsconfig.json:23`) pointing at `./shared-tools`,
a symlink to `showcase/shared/typescript/tools`. There is nothing for a
reader to install.
Same defect on the strands page from the same sweep: 586 lines of a
1688-line `agents/agent.py`.
## What changed
**The two cells get a dedicated module for the A2UI tool**, so hoisting
the marker to the top of the file yields exactly the tool plus its own
imports. This is the shape of the reference cell
(`langgraph-typescript/src/agent/a2ui-fixed.ts`, which likewise builds
A2UI operations locally) and, on the Python side, of `gen_ui_agent.py` /
`a2ui_dynamic.py`.
| page | before | after |
| --- | --- | --- |
| mastra fixed-schema | 432 lines, 16.5 KB | 166 lines |
| strands fixed-schema | 586 lines | 171 lines |
Every line in the new snippets either installs from npm or is a visibly
local `./` / `@/` module carrying a comment about what a real app uses
instead. Mastra keeps a single operation builder — the beautiful-chat
flight tool now calls the same one. The strands cell also highlights
`tools/generate_a2ui.py` so the guide shows the helper the tool calls.
**A guard in the bundler**, because neither failure mode shows up in
review:
- any `@copilotkit/showcase-*` specifier in a published body fails the
build (corpus is at zero after this change, so no baseline);
- over 200 lines fails the build (median region is 28, p90 is 125; the
48 already over the line are baselined by `slug::region::file` and the
list only shrinks).
**The entrypoint half of the issue lands differently than I first read
it.** OSS-901 flagged `@copilotkit/runtime` +
`@copilotkit/react-core/v2` on the shared A2UI page as a v1/v2 trap. It
is not a broken pairing — v1's `CopilotRuntime` forwards `a2ui` (and
`mcpApps` / `openGenerativeUI`) straight to the v2 runtime
(`packages/runtime/src/lib/runtime/copilot-runtime.ts:414`). But #6618
landed while this branch was open and retired the v1 runtime adapter
across every showcase integration, so the page's v1 root import *was*
the stale half. The block also imported `ExperimentalEmptyAdapter` and
`copilotRuntimeNextJSAppRouterEndpoint` and used neither, so it was a
route a reader could not run. It now shows `createCopilotRuntimeHandler`
from `@copilotkit/runtime/v2` in the single-route form, matching the
rebased showcase route and `/runtime-server-adapter`, with a note that
the legacy form still works.
**On the systemic question.** A separate sweep counted ~50 regions whose
marker sits on line 1 with a matching close at end-of-file, and proposed
failing a region that spans >=90% of its file. That rule does not
survive contact with the published bodies: of 141 regions at >=90% span,
only **2** publish more than 200 lines, and 98 publish under 100 —
dedicated single-purpose files whose whole content *is* the intended
snippet. It would also flag this PR's own fix
(`strands/a2ui_generate.py` is 171/188 = 91%) and the langgraph
reference cells. Published size is the signal that separates the defect
from the pattern, which is what the guard here measures.
## Testing
**Guard catches the pre-fix tree** (restored HEAD sources, moved the new
modules aside, ran the bundler):
```
REAL EXIT=1
Region bodies importing repo-only modules:
mastra::agentic-chat: region "weather-tool-backend" (src/mastra/tools/index.ts) imports "@copilotkit/showcase-shared-tools", ...
mastra::agentic-chat: region "backend-render-operations" (src/mastra/tools/index.ts) imports "@copilotkit/showcase-shared-tools", ...
Region bodies over the published-snippet limit:
strands::a2ui-fixed-schema: region "backend-render-operations" (src/agents/agent.py) publishes 586 lines (limit 200) ...
```
and passes on this branch (`bundler exit=0`, 801 demos bundled).
**Guard unit tests** —
`showcase/scripts/lib/__tests__/demo-region-guard.test.ts`, 10 passed.
Mutation-checked: raising `MAX_REGION_LINES` and short-circuiting the
alias scan fails exactly 2 of them; restoring passes 10/10.
**Showcase script suites** — `demo-region-guard`, `bundle-demo-content`,
`validate-parity`, `verify-shell-docs`, `validate-shared-symlinks`:
**151 passed (5 files)**.
**Mastra vitest** — `tests/vitest/a2ui-context.test.ts`, 5 passed. The
prompt builder lives in the dependency-free `a2ui-context.ts` so this
regression test still runs without the Mastra SDK installed, as it did
before. Mutation-checked: breaking the join fails 1 of 5.
**Strands pytest** — `tests/python/test_generate_a2ui_errors.py` 11
passed (was 1 failed / 10 passed after the move, because the happy-path
test patched `agents.agent.build_a2ui_operations_from_tool_call`;
retargeted at the new module). Whole runnable suite: **40 passed**
across `test_generate_a2ui_errors`, `test_hook_injection`,
`test_sales_state_from_args`, `test_tool_call_cap`. Mutation-checked:
stubbing out the builder call fails the happy-path test.
`test_cvdiag_boundaries` / `test_instrumentor_patch` need `starlette` /
`opentelemetry-instrumentation-threading`, absent from this venv —
unrelated to this change.
**Published snippet, rendered** (`demo-content.json` after bundling):
```
mastra snippet lines: 166 | file: src/mastra/tools/a2ui-generate.ts
import { createTool } from "@mastra/core/tools";
import { z } from "zod";
import { generateText, tool as aiTool } from "ai";
// In your own app this is `import { openai } from "@ai-sdk/openai"`. ...
```
**Docs verification** — `verify-shell-docs.ts` produces a byte-identical
finding set with my two MDX edits toggled on and off (empty diff), so
the edits add no new findings. `component-imports`, `essential-content`
and the rest are unchanged; the suite's pre-existing failures are
untouched.
**Lint / format** — `oxfmt` on all changed TS, `oxlint` clean on the new
and edited files.
## Follow-ups (not in this PR)
- The 48 baselined regions are the same class of defect on other pages —
`strands::supervisor-delegation-tools` publishes 795 lines,
`strands::subagent-setup` 625, `ms-agent-dotnet::weather-tool-backend`
549. Each wants the same split.
- `claude-sdk-typescript/shared-tools/` and
`langgraph-typescript/shared-tools/` are real directories where symlinks
belong — the erosion `showcase/AGENTS.md` documents. Untouched here.
- Dropping the strands commit (`14c7351`) is safe on its own; it only
requires adding
`strands::backend-render-operations::src/agents/agent.py` to the guard
baseline.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reapplies the formatter fix the CI auto-format job pushed (13bed7d4),
which a rebase force-push dropped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same defect as the mastra cell, same page: the `backend-render-operations`
marker is hoisted to the top of `agents/agent.py`, so
`/aws-strands/generative-ui/a2ui/fixed-schema` published 586 lines of a
1688-line module — the messages-snapshot wrapper, the weather/dice/query
tools, everything — instead of the A2UI tool the step is about.
Move `generate_a2ui` and its `_A2uiError` shape into
`agents/a2ui_generate.py`, which is what `gen_ui_agent.py` /
`a2ui_dynamic.py` already do for their own surfaces ("this module lives in
its own file so the surface area is reviewable in isolation"). `agent.py`
imports the tool back for the shared agent's tool list, so the wiring and
the tool id are unchanged; the published snippet is now 171 lines with its
own imports. `tools/generate_a2ui.py` joins the cell's highlighted files so
the guide also shows the helper the tool calls.
The error-handling suite patched `agents.agent.build_a2ui_operations_from_tool_call`,
which now lives on the new module — retargeted, and it still imports the
tool via `agents.agent` so the re-export stays covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`/mastra/generative-ui/a2ui/fixed-schema` published the entire 432-line
tools barrel, because the `backend-render-operations` marker sits at the
top of the file (the marker-hoist sweep in 34b6418 put it there so the
snippet would carry its imports). The published body therefore included
every unrelated tool plus
`import { ... } from "@copilotkit/showcase-shared-tools"` — a tsconfig
path alias to a symlink in this repo, not a package a reader can install.
A Mastra onboarding run stopped there rather than invent an API (OSS-901).
Move `generateA2uiTool` into its own module and mark the region there, so
hoisting to the top of the file yields exactly the tool plus its own
imports — the same shape as the reference cell,
`langgraph-typescript/src/agent/a2ui-fixed.ts`, which likewise builds the
A2UI operations locally instead of importing the showcase's shared tools.
The published snippet goes from 432 lines to 166, and everything in it
either installs from npm or is a visibly local `./` / `@/` module with a
comment saying what a real app would use instead.
`buildA2uiOperations` and `systemPromptFrom` replace the two shared-tools
helpers so mastra keeps a single operation builder: the beautiful-chat
flight tool now calls the same one. The prompt builder lands in the
dependency-free `a2ui-context.ts` so its regression test keeps running
without the Mastra SDK installed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Retires the v1 runtime adapter from `showcase/integrations/`. After
this, **no code under `showcase/integrations/` calls
`copilotRuntimeNextJSAppRouterEndpoint`** — 239 routes across 20
integrations.
This is the cheap path we discussed: single-route mode, which is a
genuine drop-in. **No demo page changes, no route path changes, no `GET`
exports, no new dependencies.**
## The shape
```ts
const copilotHandler = createCopilotRuntimeHandler({
runtime,
basePath: "/api/copilotkit-x",
mode: "single-route",
});
...
return await copilotHandler(req);
```
**Why single-route:** these demos' frontends are `<CopilotKit
runtimeUrl="/api/copilotkit-x">` with no transport prop, and every
released provider pins the single-route transport. So single-route mode
is what the v1 adapter was already serving. Migrating to multi-route
instead would have meant editing every demo page in lockstep, for no
functional gain — nothing in the showcase probes `/info`.
**Why `createCopilotRuntimeHandler`** rather than
`createCopilotEndpointSingleRoute`: that helper is itself deprecated in
favour of the `mode` option (per the deprecated-aliases table in
`docs/backend/runtime-endpoints.mdx`), and the fetch handler needs no
`hono` dependency and composes directly with the wrappers these routes
already have.
The statement is rewritten **in place**, inside whatever wrapper it
already sat in, so `withForwardedHeaders`, the try/catch envelopes,
`wrapStreamingResponse` and `withCvdiagBackend` are untouched. 75 of
these routes construct the runtime inline in the call; rewriting in
place preserves that per-request construction exactly as v1 did. No
`runner` is added — it's optional and none of these routes passed one.
13 `copilotkit-auth/[[...slug]]` routes already use the v2 fetch handler
and are left alone; they only name v1 in comments.
## Verified
The shape was proved end-to-end **before** the rollout, in a real
running app with an untouched provider (aimock as the model backend):
`POST /api/copilotkit` → 200 twice, chat turn rendered in the browser.
It also typechecks against the exact version these integrations pin
(1.68.2).
`mastra` is the integration installed and exercised locally — 19 routes,
the `withCvdiagBackend` main route, and the only vitest suites that
touch routes. Measured against `origin/main` **in the same tree**:
| | baseline (`origin/main`) | after |
| --- | --- | --- |
| `tsc --noEmit` | errors in 10 files | errors in **9** |
| `vitest run` | 2 files / 13 tests failed, 21 passed | 2 files / 13
tests failed, 21 passed |
- **New type errors introduced: none.**
- **Fixed:** `src/app/api/copilotkit-mcp-apps/route.ts`, whose
`@ts-expect-error` was *already* unused on `main`.
- **Test-neutral:** those 13 failures are pre-existing on `main` (mostly
`extractXHeaders` dereferencing `req.headers` on a `{}` fake request).
Structural audit over all 239 routes, re-run after the pre-commit
formatter: none still imports the v1 root, calls the v1 adapter,
references `ExperimentalEmptyAdapter` or `handleRequest` in code, or is
missing `createCopilotRuntimeHandler` / `basePath` / `mode:
"single-route"`.
**CI has now built all 21 integrations green** — `showcase_build_check`
Docker-builds each changed integration and `next build` typechecks
inside it. That covers the ones I could not stand up locally, including
the non-JS backends (`spring-ai`, `ms-agent-dotnet`, `ms-agent-python`,
`ms-agent-harness-dotnet`, `langroid`, `strands`). Full run: 41 pass / 3
skip / 0 fail.
To be precise about what each gate proves: CI proves these 21 apps still
**build and typecheck**. It does not exercise a chat turn per
integration — that came from the pre-rollout live proof of the shape
itself, plus mastra's local test suite.
## Runtime verification on real cells
Docker is not running on my machine, and `bin/showcase test --d6
--direct` requires it
(`--direct` only swaps the in-process driver for the fleet
control-plane; the containers are
not optional). **So the sanctioned Iron Rule 4 probe has NOT been run**
— that gap is real and
a reviewer should weigh it. What I did instead was run a real
integration directly.
`showcase/integrations/mastra` on this branch, `npm run dev`, aimock as
the model backend.
Probing eight migrated routes with the exact envelope the released
provider sends
(`POST {basePath}` with `{"method":"info"}`) — all **200** with real
runtime payloads:
copilotkit-multimodal 200 agents: multimodal-demo
copilotkit-mcp-apps 200 agents: headless-complete
copilotkit-a2ui-fixed-schema 200 agents: a2ui-fixed-schema
copilotkit-beautiful-chat 200 agents: beautiful-chat
copilotkit-agent-config 200 agents: agent-config-demo
copilotkit-ogui 200 agents: open-gen-ui
copilotkit-declarative-gen-ui 200 agents: declarative-gen-ui
copilotkit-background-agents 200 agents: background-agents
Then three real demo cells driven in a browser, each rendering and
completing a chat turn
through its migrated route (two POSTs, both 200, assistant message
rendered):
/demos/beautiful-chat -> POST /api/copilotkit-beautiful-chat 200, 200
/demos/a2ui-fixed-schema -> POST /api/copilotkit-a2ui-fixed-schema 200,
200
/demos/multimodal -> POST /api/copilotkit-multimodal 200, 200
Deliberately spread across different route configs — plain, `a2ui`, and
a dedicated
vision-model route — rather than three variations of the same one.
### One route is 500, and it is pre-existing
`POST /api/copilotkit` (mastra's main route) returns 500 in dev:
`module-not-found` on
`./schema.js` from `src/cvdiag/cvdiag-emitter.ts`. `schema.ts` **is**
tracked and present —
Turbopack in dev just does not resolve the ESM-style `.js` specifier to
it. Proven
pre-existing by restoring **only** that file to `origin/main` and
re-probing: same 500 on the
v1 code. Its Docker build passes, which is why CI is green.
### A mistake in my own verification, recorded
My first pass at the above ran against the wrong branch — I was still on
the docs branch,
where these routes are v1, so the first six 200s I collected were the
**v1** routes. Caught it,
switched to this branch, and re-ran everything above against the
migrated code. The accidental
run was not wasted: it independently confirms the premise of this PR,
that the v1 adapter and
v2 single-route mode answer the same envelope the same way.
## Judgement call worth reviewing
27 `@ts-expect-error` directives guarded the **v1** `CopilotRuntime`
agents type ("wraps `Record` in `MaybePromise<NonEmptyRecord<...>>`").
Under `/v2` that hole is gone, which makes the directive *unused* — a
hard compile error.
I demoted them to `@ts-ignore`, which compiles whether or not the
mismatch survives in a given integration. The honest reason at the time:
19 of these apps can't be built locally, so I couldn't prove per-file
which still need a suppression, and `@ts-ignore` is what 190 sibling
files already use.
Now that CI has built all 21 green, that constraint is gone — the ~220
now-stale suppressions (all of which cite a **v1** type hole that no
longer applies) can be removed and verified by the same 21 builds. I've
left them in place here to keep this PR mechanical and reviewable; say
the word and I'll do it as a second pass.
## Two pre-existing problems found on the way
- **`npm ci` fails in `showcase/integrations/mastra`**: `Missing:
@types/http-errors@2.0.5 from lock file`. The Dockerfile uses `npm ci
--legacy-peer-deps`, which *does* succeed, so the image still builds —
but a plain `npm ci` doesn't. No manifest or lockfile is in this diff.
- **`mastra`'s vitest suite is red on `main`** — 13 failures, as tabled
above.
## What's left of the v1 entrypoint
| Surface | Before | After |
| --- | --- | --- |
| `showcase/integrations/**` | 239 | **0** |
| `examples/v1/**` | 12 | 12 (deliberately v1, never in scope) |
| everything else in `examples/` | ~61 | ~61 (not in this PR) |
Stacks cleanly with #6617 (docs) — no file overlap. It also **unblocks
the two Claude SDK quickstart pages** I had to revert there:
single-route keeps the starter file at the plain `route.ts` path, so
those pages' prose claims and `verify-shell-docs.ts` starter-path checks
stay valid, and only the fence body needs updating.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Review found two gaps in the previous commit.
`RunAgentInput.context` never reached the model. The bridge puts it on state
under `context`, but `CopilotKitState` does not declare the field, so pydantic
drops it when the endpoint validates the request into the Flow's state and the
"Application context" block rendered without it. That hollowed out the two cells
whose whole point is reading application context: readonly-state-agent-context
(now on the default `/chat` route) and agent-config. `ChatState` declares the
field so it survives validation.
Neither existing test caught it: the readonly probe only asserts the browser
request body carries its sentinel, the agent-config probe encodes the expected
value in the user message, and Flow-level unit tests bypass endpoint state
initialization. The new endpoint test drives the real FastAPI route with two
context entries and asserts both appear in the model's system message; it fails
without the declared field.
The Channels setup fragment still told readers the shared crew sits at the
server root and set `AGENT_URL` accordingly, which the removed catch-all turned
into a dead endpoint. It now points at `/chat` and says Flow rather than crew.
Migrating the consumer was preferred over restoring `/`, which would put the
silent-fallback trap back in place.
Verified: 164 and 166 Python tests pass across the two columns, the docs
setup-content tests pass (16), and readonly-state-agent-context, agent-config
and agentic-chat are green on D6 replay.