The failure banner lived inside the element the interrupt hook renders, and the
hook caches that element, so a picker already on screen never saw an updated
prop. One rejected resume left the flag set, and the next booking showed a
failure the moment a slot was selected, before its own resume had started.
The banner now lives outside the hook-owned element and the flag clears as the
first step of a new selection, so the card only ever reports its own outcome.
That makes the prop the card took for this purpose unreachable, so the prop and
the branch it fed are gone rather than left as dead state.
The cancel specs read the resumed run's final text as soon as a new assistant
bubble appeared. A bubble shows up when streaming starts, and the guard is a
negative assertion, so a response streaming "B" before "Booked: ..." would pass
while it was still one character long. Each cancel test now waits for the run
to finish first. Confirmed the wait is doing work rather than passing on
arrival: probing the same control right after the bubble appears finds it
enabled, so the assertion that follows genuinely blocks until the run ends.
Verified live against a real model on both columns, 8 of 8 each.
The parity notes described the interrupt reason channel and the resume envelope
as live differences between the two bridges. Both are closed as of the released
versions this branch pins: the TypeScript bridge now carries the reason under
the interrupt's metadata and wraps a resolved answer the same way Python does.
Checked against the released packages rather than their changelogs.
The reading code still accepts the older shapes so the demos keep working
against an earlier bridge, which is why the note now explains the normalisation
as backwards compatibility rather than as a divergence to work around.
Takes ag_ui_strands 0.4.0 and @ag-ui/aws-strands 0.3.0, the releases of the
alphas this branch was already tracking. Their companion requirements are the
ones already pinned here: the Python bridge needs ag-ui-protocol 0.1.22 and the
TypeScript one needs the AG-UI core packages at 0.0.59.
Verified against a live model on both columns with the released versions in the
containers: python 138 passed, typescript 141 passed. The multimodal round trip
that the TypeScript bridge used to break is fixed at both levels, returning the
user message with its parts intact for inline data, url and pdf sources, and
rendering the image and the document chip in the demo.
The remaining failures are the same 46 on both columns. They are specs that
assert the wording aimock's fixtures author, so they cannot pass against a real
model: one expects a humidity reading of 55% where the model answered 77%,
another expects a greeting quoted verbatim from the fixture. An identical
failure set across two independently written bridges is the evidence that the
cause is the specs rather than either bridge.
Hide the running footer when the activity is completed, failed,
cancelled, or paused. Keep the activity renderer array stable
across page renders.
Verified the only renderer registration and existing status mapping;
no public API changes. Browser regression checks failed before the
changes and passed afterward with the real CopilotKit/Mastra runtime
and a deterministic model.
Both alphas need a companion bump the showcase owns, and neither container
starts without it: the Python bridge requires ag-ui-protocol 0.1.22, and the
TypeScript canary requires the AG-UI core packages at 0.0.59.
The Python agent picked its model from a hardcoded literal while the TypeScript
one already read MODEL_ID, so the Python column could not be pointed at another
model without a rebuild. It now reads the same variable, resolved at call time
because the server imports the module before it loads the environment file.
The interrupt pick-path specs asserted the fixture's exact wording, which made
them aimock-only, so a live run could never exercise them. Rewriting them to
count assistant bubbles turned out to be worse than the problem: a resume that
loses the user's answer still narrates "user did not pick a time", so the count
passed on a deliberately broken resume. Verified by breaking the tool's resume
read, rebuilding, and watching the suite stay green.
They now assert the slot the user actually picked and the absence of the
did-not-pick branch. That pins the data rather than the phrasing, so a live
model saying "Monday at 9:00 AM" passes while a lost answer fails. Confirmed
both ways against a real model: 8 of 8 pass on the real code, and the two
pick-path specs fail on the broken one. The cancel paths keep the regression
guard that matters, the absence of a booking confirmation, and drop the
fixture-worded positive that a live model has no reason to reproduce.
## Problem
The documentation does not clearly establish the Google ADK agent-side
path for `useAgentContext`. The hook reference points generically to
integration guides, while the existing ADK setup uses
`state["copilotkit"]["context"]`, which is not where the adapter stores
the AG-UI request context.
## Why
Reproduced using the actual adapter and ADK Runner with an instruction
provider and a deterministic local model. Given
`RunAgentInput.context=[{description: "Incident records", value:
"[{\"id\":\"INC-1041\"}]"}]` and empty request state:
```text
state["copilotkit"]["context"]: []
state[CONTEXT_STATE_KEY] (_ag_ui_context):
[{"description": "Incident records", "value": "[{\"id\":\"INC-1041\"}]"}]
```
## Fix
Document the public `CONTEXT_STATE_KEY` export in the hook reference and
link directly to the ADK context guide. Replace that guide's setup with
a standalone instruction-provider example using the correct state key.
Preserve the Gemini final-text termination safeguard with a fully
defined callback in the same example. Explain the string values, parsing
structured values, callback/tool access, and the distinction between
exposing frontend tools and including context in model instructions.
Validation: regenerated the setup-content bundle through Nx, extracted
and executed its Python example, and drove the documented instruction
provider through the real adapter/Runner. It delivered `INC-1041` into
the model request's system instruction and completed with text events
and `RUN_FINISHED`. This is a deterministic integration reproduction,
not a live Gemini/browser test. `git diff --check` passes.
Targeted shell-docs checks: **26 passed across 3 files**
(`setup-content`, `setup-concept`, and `frontend-tools-setup-coverage`),
run through Nx after generating the normal docs prerequisites.
The context example also passes nine copied-code callback checks
(registration, final text, partial/thinking output, tool calls, and
private-context compatibility). The bundled context-to-model execution
check and 26 targeted docs tests pass after this correction.
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>
The gate added in the previous commit would have failed on a clean checkout.
The Strands TypeScript integration sits outside the pnpm workspace on purpose:
its agent process resolves openai and the Strands SDK from its own
node_modules, which the Dockerfile installs with npm ci. The workflow ran only
the repo-root pnpm install, so two of the four test files could not resolve
their imports. It passed locally only because that directory already existed on
this machine. Reproduced by moving it aside (2 files fail), then confirmed the
workflow's own steps green it again (16 tests pass).
The job now installs the agent package the same way the Dockerfile does, and
runs the vitest config that already lives beside those tests rather than a
second config layered above them, so CI runs exactly what `npm test` in that
directory runs. The path filters narrow to the sources the run actually covers,
so a PR touching an unrelated integration no longer collects a green check from
a job that exercised none of its code.
The showcase integrations sit outside the pnpm workspace, so nx never
discovers them and the harness unit workflow does not cover them. Their Python
tests are already gated by showcase_validate.yml, but nothing anywhere ran
vitest under showcase/integrations: four test files in the Strands TypeScript
agent, including the one this branch adds for the resume envelope, could fail
indefinitely without CI noticing.
A small config at showcase/integrations/vitest.config.ts gives those tests one
home, driven by the monorepo's own vitest so no integration has to carry a test
dependency into its Docker image. Its include list is an allowlist rather than
a wildcard: most integrations' existing tests resolve imports through their own
Next.js path aliases and only run under their own config, so sweeping them all
in would fail on arrival. Adding an integration to the list means first making
its tests runnable from the repo root.
The workflow runs that config on pull requests touching the integration agent
sources or their tests. It does not pass --passWithNoTests, so an include list
that stops matching fails the job rather than reporting an empty success, and
it interpolates no discovered filenames into a shell command, since a fork
controls the file names in this tree.
The instrumentor test serves `strands.*` stubs through a meta-path finder,
but the import machinery reads the parent module's `__path__` BEFORE it
consults any finder, so every submodule import died with "'strands' is not
a package" and the finder never ran. It only ever passed because earlier
test modules left `agents.*` cached in `sys.modules`, which skipped the
`from strands... import` lines entirely.
Adding `build_reasoning_agent()` at agent_server import time broke that
accident: its `from strands.models.openai_responses import ...` sits inside
the function body, so module caching cannot hide it and the whole suite
went red.
Give the three package-like stubs (`strands`, `strands.models`,
`strands.types`) an empty `__path__` so the finder is reached, and drop the
stub entries from `sys.modules` afterwards so the real-package probe later
in the file does not inherit a `strands` whose empty `__path__` hides the
installed submodules.
Suite is now green both as a whole and file by file, which it was not
before on either this branch or main.
Cancel handling was asymmetric in both languages: the TypeScript-shaped
sentinel was only recognised on the outer envelope, so a cancel that arrived
wrapped as {"response": {"status": "cancelled"}} was reported to the model as
"did not pick a time". Both tools now check the sentinel at both levels, and
the test matrix covers the shape it missed (three cases fail without the fix).
The TypeScript tool also coalesced the label with ?? where Python uses or, so
an empty label swallowed a valid time; both now fall back the same way.
Frontend and probes:
- The headless resume waited on a single animation frame, which never fires in
a background tab, stranding the run. A timer races it and the resume fires
once, whichever lands first.
- The picker latches after the first answer, so a second click cannot race the
resume already in flight.
- A rejected resume now replaces the green "Booked" badge with a failure state
instead of leaving a success the user never got.
- The headless probe searched the whole page for booking words, which the
demo's own static copy already satisfies; it now reads assistant bubbles only
and matches phrases the page does not contain.
- The pick-path spec asserted a bubble was visible, which the pre-pause bubble
already satisfies; it now asserts the post-resume narration. The headless
spec scopes the picker to the app-surface pane, which is the contract it
claims to hold.
Prose and fixtures:
- The dedicated-mount rationale named the wrong tool: the in-chat demo
registers book_call, and the real collision is the shared agent's own
immediate schedule_meeting. Corrected in the parity notes and both tools.
- The TypeScript integration no longer cites Python API names, and the picker
no longer cites a slot generator that does not exist.
- The shared docs no longer describe a hook the embedded snippet does not
show, no longer place a standard interrupt's payload on event.value, and
carry the correct RUN_FINISHED outcome shape.
- The d4 fixture edits are back to UTF-8 literals, dropping 86 lines of
re-encoding noise so only the six intended key changes remain.
- The agent package exposes a test script, so the new unit test is runnable.
Review pass over the native-interrupt work. The load-bearing fix is the
resume envelope: the TypeScript tool read only the shape the Python bridge
produces, so a valid time pick came back to the model as "user did not pick
a time" and the meeting was never scheduled. Reproduced at the wire on a
real model, fixed with a normaliser that accepts both the wrapped and the
raw payload, and covered by a unit test that fails when the read is reverted.
The Python tool had the mirror-image gap and is fixed and tested the same way.
Also in this pass:
- Both demo pages read the interrupt payload without throwing during render,
and a rejected resume is surfaced instead of leaving a green "Booked" badge.
- The e2e specs asserted the pre-pause assistant bubble, so a broken resume
passed; they now assert the last bubble, plus a cancel-path narration guard.
- Fixture legs that gated on turn number now gate on whether the tool has
already answered, matching the reference integration, and two bare D4 keys
that shadowed D6 prompts are narrowed.
- The reasoning parameter is Responses-API only and the reasoning model id no
longer reaches the Anthropic or Bedrock branches.
- Stale prose corrected in the parity notes, the setup docs, the shared
human-in-the-loop pages, both probe docblocks and the agent server docstring.
Both Strands showcase columns claimed no interrupt support and drove their two
interrupt demos through a frontend tool with an async handler. Both AG-UI Strands
bridges implement the AG-UI interrupt protocol on top of Strands' own interrupt
primitive, so the demos now use it.
- Dedicated interrupt agents whose schedule_meeting tool pauses itself with
tool_context.interrupt(...) / context.interrupt(...), mounted on their own
sub-path because hitl-in-chat registers a FRONTEND tool of the same name.
- interrupt-headless rebuilt on useInterrupt({ renderInChat: false }), replacing
a hand-rolled subscriber that only matched LangGraph's legacy interrupt shape.
- Quarantine lifted, interrupt_pattern is native, and the prose claiming Strands
has no interrupt primitive is corrected.
- reasoning-default, reasoning-custom and tool-rendering-reasoning-chain were
registered under deprecated ids and pointed at agent names the runtime never
registered; they now run against dedicated Responses-API agents with reasoning
summaries enabled.
- Fixture repairs: interrupt keys that matched neither pill prompt, generic legs
gated on turn position rather than on whether the tool had answered, and two
short D4 keys that shadowed D6 prompts.
- Shared probes: the interrupt turns settle on the picker mounting instead of
assistant-text stability, which an interrupt turn never produces.
- Pins raised to ag_ui_strands 0.3.0, strands-agents 1.54.0, openai 2.54.0 and
@strands-agents/sdk 1.16.0.
The OSS-136 sales-analyst rework migrated the D6 fixtures but left the
demo page pills on the pre-OSS-136 KPI set in 8 integrations. Port the
4 canonical sales-analyst pills from the langgraph-python reference so
the pills match what the fixtures answer.
Fixes#6791.
## 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.