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.
The v1 bridge (@ag-ui/mastra 1.1.0-alpha.0) emits reasoning start/content/end
and STATE_DELTA shared-state streaming, so the already-wired demos + e2e +
aimock fixtures go live. Moves out of not_supported_features into features:
- agentic-chat-reasoning, reasoning-default-render, tool-rendering-reasoning-chain (OSS-384)
- shared-state-streaming (OSS-423)
Adds the missing reasoning-default / reasoning-custom manifest demo entries.
not_supported_features now holds only gen-ui-interrupt + interrupt-headless,
matching the langgraph-python gold standard, which quarantines the same two
cells on an upstream @copilotkit/react-core v2 resume-path hook bug (published-
package fix, out of scope). The native interrupt + RUN_FINISHED outcome path
ships in the bridge; the showcase cell is blocked by the same upstream bug.
Adds an OSS-424 execution-tracing note (tracingOptions inbound / traceId on
RUN_FINISHED.result outbound) to the Mastra Copilot Runtime doc.
Bumps the showcase Mastra integration onto the v1 bridge alpha that ships
interrupt outcome, reasoning emit, STATE_DELTA shared-state streaming,
background-task + observational-memory activity events, tracing passthrough,
and the a2ui-toolkit dep. next build passes (40 routes); unit tests identical
to the beta.2 baseline (13 pre-existing failures, unrelated to the bump).
The ADD-2 block suppresses the streamed TOOL_CALL_CHUNK for all frontend
tools, relying on the bare snapshot's ag_ui_tool_calls to deliver the call
(emitting both doubles the args and breaks scheduleTime / pie-bar
useComponent). But the open-generative-ui runtime middleware builds the
sandboxed iframe exclusively from streamed TOOL_CALL_* events and never
reads the snapshot, so open-gen-ui and open-gen-ui-advanced rendered 0
iframes.
Add a name-scoped exemption that streams the chunk only for
generateSandboxedUi, keeping snapshot-only delivery for every other
frontend tool. The exemption is intentionally narrow to preserve the
double-args fix.
Red->green (D6, --direct, real Docker page):
- open-gen-ui: RED "saw 0 iframe(s), longest srcdoc=0" -> GREEN (iframe + srcdoc)
- open-gen-ui-advanced: RED "selector cascade matched 0 elements" -> GREEN
Regression (all still green): beautiful-chat 5/5 (toggle-theme, pie-chart,
bar-chart, search-flights, schedule-meeting), agentic-chat, mcp-apps.
(cherry picked from commit 03f8d02cedbe737ec83aeefa708a9146f438a904)
The no-tools branch of ReasoningAGUIChatWorkflow.chat streams the answer via
astream_chat over OpenAIResponses, which (unlike astream_chat_with_tools on the
tools branch and the GREEN tool_rendering_reasoning_chain_agent) does not
accumulate resp.delta back onto the terminal resp.message.content. The
content-empty message was then snapshotted into MESSAGES_SNAPSHOT, clobbering
the ~284-char streamed answer and rendering an empty assistant bubble
(reasoning-display failed text-unstable: reasoning block painted, answer gone).
Accumulate the streamed text deltas in the no-tools path only (track_text =
not tools) and fold them onto resp.message before _finalize_chat snapshots it.
Strictly additive: only fills a message the stream left empty, never overwrites
content the LLM already accumulated, and is inert when tools are present so the
tools branch / reasoning-chain agent are untouched.
(cherry picked from commit 46afd0e040a669d14f91b8c136df9c94a91950d0)
The shared FixedAGUIChatWorkflow catch-all dropped request-injected query_notes,
so NotesCard never mounted. Give the cell its own agent (mirrors beautiful_chat_agent)
so request-time frontend tools forward. Verified GREEN via control-plane --direct.
(cherry picked from commit 9c1b8ce2c33afc89000355d83c995f03da208f0f)
Same root cause as the sibling declarative-gen-ui (A2UI Dynamic Schema) fix:
the A2UI middleware mounts the surface from a STREAMED render-tool CALL whose
name is in its watched set, not from a TOOL_CALL_RESULT. The prior approach had
display_flight return an a2ui_operations container in the tool RESULT, which the
llama-index AG-UI adapter only re-emits via MESSAGES_SNAPSHOT — a shape the
middleware never inspects — so the flight-card surface stayed unmounted
(reason=surface-missing; the a2ui-fixed-card testid never appeared).
- route.ts: set a2ui.injectA2UITool: true so the middleware watches render_a2ui.
- a2ui_fixed.py: display_flight now returns the fixed-schema render_a2ui args
(surfaceId/catalogId/components/data) as JSON; a workflow override
(_A2UIRenderToolCallWorkflow) parses each backend tool result and re-emits it
as a streamed render_a2ui tool-CALL (TOOL_CALL_START name=render_a2ui ->
chunked TOOL_CALL_ARGS carrying the components+data JSON -> TOOL_CALL_END),
mirroring how google-adk drives the middleware. These events are already in
the upstream AG_UI_EVENTS allow-list the SSE router streams against.
Backend still produces the pre-authored flight schema (no stub). Only
display_flight (one backend tool, name unchanged) is involved, so the d6 fixture
needs no re-keying. Integration-code only; no shared/@ag-ui package touched.
(cherry picked from commit 74d61eddf7eccf22bcc96c37f0e35a70dd823a2f)
The render re-emit override had three silent failure paths: a non-JSON tool
output (broad except swallowing TypeError/ValueError), the {"error": ...} dict
from generate_a2ui's no-tool-call branch, and a valid-JSON result missing
components. Each produced a blank UI with no diagnostic trail. Narrow the parse
except to json.JSONDecodeError (guarding that content is a str) and log a
contextual warning on each path. Happy path unchanged.
(cherry picked from commit 94b0a69aa4772758e3bcc05f67a6c28b1b0a503d)
The inlined planner SYSTEM_PROMPT listed every A2UI catalog component except
DataTable, even though the TS catalog and a team-performance suggestion pill
target a DataTable surface. Since the planner is a separate OpenAI call driven
solely by this hardcoded prompt (it never sees the TS Zod schema), DataTable
emission was unreliable. Add DataTable to the catalog list, mirroring the TS
definition (columns/rows shape) and the other entries' wording.
(cherry picked from commit a054e41b8d9394540e1cf7b84ccf9e8e0722c519)
The override docstring claimed it reproduces the upstream aggregate_tool_calls body byte-for-byte; it is functionally equivalent with two cosmetic diffs (Optional type hint, list comprehension). Reword to match reality.
(cherry picked from commit 5e91118b3a463dcefcd228f6343e472db6081c0f)
The page header still described the runtime as configured with
`injectA2UITool: false` and the backend agent as owning `generate_a2ui`,
mirroring beautiful-chat. This PR inverted the route to
`injectA2UITool: true`, so the comment was stale. Rewrite the step-3 block
to describe the current mechanism: `injectA2UITool: true` populates the A2UI
middleware's watched-names set, which mounts the surface from a STREAMED
`render_a2ui` tool-call the agent re-emits via its `aggregate_tool_calls`
override in a2ui_dynamic.py. Drops the stale generate_a2ui framing and
matches the accurate header in route.ts.
(cherry picked from commit 407d755638ebe28418f1f8ce2c558f202995284e)
The InfoRow renderer was the only catalog component missing a
data-testid. The top-account pill's _design_a2ui_surface leg emits
7 InfoRow facts + a PieChart, and the d5-gen-ui-declarative probe
asserts declarative-info-row (minCount 1) as top-account's
distinguishing testid. Because the renderer never painted that
testid, the completeOnMount gate (whose surfaceTestIds include
declarative-info-row) never observed a mount, the turn never
completed, and the run reported reason=surface-missing. Every other
pill passed because its distinguishing testid (metric / status-badge /
data-table) was already emitted.
The A2UI middleware mounts the surface from a STREAMED render-tool CALL whose
name is in its watched set, not from a TOOL_CALL_RESULT. The prior approach
emitted a TOOL_CALL_RESULT carrying an a2ui_operations container, which the
middleware never inspects, so the surface stayed unmounted (surface-missing).
- route.ts: set a2ui.injectA2UITool: true so the middleware watches render_a2ui.
- a2ui_dynamic.py: generate_a2ui now returns the planner's render_a2ui args
(surfaceId/catalogId/components/data) as JSON; the workflow override
(_A2UIRenderToolCallWorkflow) parses each backend tool result and re-emits it
as a streamed render_a2ui tool-CALL (TOOL_CALL_START name=render_a2ui →
chunked TOOL_CALL_ARGS carrying the components JSON → TOOL_CALL_END), mirroring
how google-adk drives the middleware. These events are already in the upstream
AG_UI_EVENTS allow-list the SSE router streams against.
Backend still produces the components (no stub). Inner planner tool stays
_design_a2ui_surface, so the d6 fixture needs no re-keying.
The llamaindex copilotkit API route logged '[copilotkit/route] POST ...'
and '[copilotkit/route] Response status: 200' unconditionally on EVERY
request. Under d6 probe fan-out this exceeded Railway's 500-logs/sec cap
('Messages dropped' -> 'Stopping Container'), killing the replica.
Gate both per-request console.log lines behind a SHOWCASE_ROUTE_DEBUG env
flag (default off). Module-load logs and error logging are unchanged.
This chatty pattern is shared/copied across ~16 integrations (including
the langgraph-python gold standard); this commit scopes the fix to
llamaindex. The others are flagged as follow-up.
Emit CVDIAG backend boundary markers from the agent process for strands-typescript (byteLength fix on sseChunkByteLength), enable the emitter in docker-compose.local.yml, vendor src/cvdiag, and exclude tests from tsconfig.
Forward inbound X-AIMock-Strict header through the two-process strands-typescript hop (Next route -> agent -> sub-agent fetch), with null-guard on the forwarding proxy fetch and supporting unit tests.
Bring the agnostic root A2UI docs up to the catalog-on-provider model and
make every generated framework serve them consistently.
- Root /generative-ui/a2ui (index, fixed-schema, dynamic-schema): lead with
passing a catalog on the provider (auto-enables A2UI and auto-injects the
generate_a2ui tool), add a manual opt-out section explaining the two pieces
you wire yourself (the generate_a2ui agent tool and the A2UIMiddleware), and
set fixed-schema to injectA2UITool: false since the agent owns the tool.
- Flip langgraph-fastapi, strands, strands-typescript to docs_mode: generated
so they serve the shared root A2UI docs 1:1 with langgraph-python.
Generated frameworks covered: langgraph-python/fastapi/typescript, google-adk,
strands, strands-typescript. deepagents (authored) is handled separately.
## What
Brings the **Authentication demo** of three integrations into 1:1
conformance with the `langgraph-python` (LGP) gold standard, completing
the work started in #5713. The showcase Iron Law: LGP is the reference;
every integration must have (1) identical tests, (2) near-identical
frontends, (3) minimal backends, (4) per-integration fixtures.
A conformance audit against LGP found 3 violators (the other 17
integrations already conform):
| Integration | Violation | Fix |
|---|---|---|
| **claude-sdk-python** | Legacy auth-*first* shape: class
`ChatErrorBoundary` + `lastError`, no `handleAuthError`, missing
`sign-in-card.tsx`, divergent banner/hook | Ported
`page.tsx`/`use-demo-auth.ts`/`auth-banner.tsx` **byte-identical** to
LGP + new `sign-in-card.tsx`; added the shared shadcn primitives it
lacked (`lib/utils.ts`, `components/ui/{button,card}.tsx`) +
`radix-ui@^1.4.3` (matching the claude-sdk-typescript peer) |
| **built-in-agent** | Distinct legacy variant:
`ChatErrorBoundary`→`auth-demo-chat-boundary`, local 401-regex
`onError`, auth-first hook | Normalized error-handling shape + hook to
LGP; **preserved** the forced `<CopilotKitProvider>` (default-agent) +
raw-Tailwind divergences (documented in a new `README.md`) |
| **ms-agent-harness-dotnet** | Missing `tests/e2e/auth.spec.ts` (rule
1) | Added LGP's spec **byte-identical** (sha256 `603a68e5…`) |
After this PR, all auth `page.tsx`/hook files are byte-identical to LGP
except documented, forced per-integration wiring; all `auth.spec.ts`
share LGP's sha256.
## Red–green proof (per integration, on the real probe surface)
The shared `d5-auth.ts` probe accepts *either* `auth-demo-error` *or*
`auth-demo-chat-boundary`, so it passes leniently on the legacy shape —
the **discriminating gate is the byte-identical `auth.spec.ts`**
(asserts unauth-first `SignInCard` + `auth-authenticate-button` +
post-sign-out `auth-demo-error`):
- **claude-sdk-python:** legacy frontend → `auth.spec.ts` **6/6 FAIL**
(timeout on `auth-sign-in-button`); conformed → **6/6 PASS** (`next
build` clean).
- **built-in-agent:** legacy → 6/6 FAIL; conformed → 4 conformance
assertions flip FAIL→PASS incl. unauthenticated-send surfaces
`auth-demo-error` (`next build` clean).
- **ms-agent-harness-dotnet:** spec absent (coverage gap) → added →
`--d5 --isolate` green, full real-browser auth flow passes.
## Review
7-agent CR round + mandatory 7-agent confirmation round → **converged to
zero findings** (correctness, conformance, types/build, deps/lockfile,
tests, silent-failures, cross-integration regressions). 2 P2 conformance
nits found and fixed (import-style alignment; restored `DEMO_TOKEN` so
built-in-agent's hook is byte-identical to LGP).
## Known limitation (non-blocking, pre-existing infra)
The GHA workflow `test_e2e-showcase-on-demand.yml` runs Playwright only
for slugs with a Python agent, so the **built-in-agent /
ms-agent-harness-dotnet auth specs are not executed in PR CI**. This is
a pre-existing infra gap (those integrations have no Python agent), not
introduced here. Coverage **does** exist post-merge: the Railway staging
**d6 harness** enumerates services language-agnostically and runs the
auth probe against live `/demos/auth` for both — verified, and it's what
drives their dashboard cells green at D6. A follow-up to add a
non-Python e2e execution path is warranted.
## Notes (pre-existing, not introduced)
- `npm ci`/`npm install` in `showcase/integrations/claude-sdk-python`
shows a micromark/unified desync and a zod/openai ERESOLVE peer conflict
— both reproduce identically at the base commit `ab85b939ac`
(independent of the `radix-ui` add); handled by the existing
`--legacy-peer-deps` path.
Ref: #5713 (original post-sign-out auth rejection fix).
getA2UITools changed signature: 0.0.39 is getA2UITools(model, options) (positional),
0.0.42 is getA2UITools(params) (single object). The agent code (recovery-agent.ts
and graph.ts) calls the single-object form, but the override pinned 0.0.39, so the
whole params object was treated as the model -> e.bindTools undefined -> the tool
returned {"error":"Provided model does not support bindTools"} and the render
sub-agent never ran. Bumping the override to 0.0.42 aligns the dep with the API the
code uses; verified the recovery graph now emits a healed a2ui_operations surface
(invalid seq0 -> valid seq1) and fires the render_a2ui sub-agent.
The lg-ts agent serves graphs from a hardcoded graphSpec in src/agent/server.mjs
(mirrors langgraph.json). The a2ui_recovery graph was added to langgraph.json but
not graphSpec, so the langgraph server returned 404 on its runs and the demo
never dispatched. Add a2ui_recovery to graphSpec.
NOTE: this fixes graph REGISTRATION. The lg-ts recovery render does not yet fire
(getA2UITools 0.0.39 returns from generate_a2ui without invoking the render
sub-agent); tracked separately, likely needs @ag-ui/langgraph >= 0.0.42.
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.
claude-sdk-python was the last integration still on the legacy auth-first
shape: an authenticated-on-load page guarded by a class-based
`ChatErrorBoundary`, a `useDemoAuth` exposing `authenticate`/`authenticated`,
an `auth-banner` with an `onAuthenticate` prop and bespoke buttons, and NO
`sign-in-card`. The byte-identical `auth.spec.ts` (which asserts an
unauthenticated-first `SignInCard` with `auth-sign-in-button` /
`auth-demo-token`) therefore failed all six cases against it.
Port the four auth files verbatim from the langgraph-python gold standard
(adapting nothing — the per-integration wiring, `agent="auth-demo"` and
`runtimeUrl="/api/copilotkit-auth"`, was already identical):
- use-demo-auth.ts: unauth-first, localStorage-backed, exposes
`isAuthenticated`/`hasEverSignedIn`/`signIn`/`signOut`.
- page.tsx: render `SignInCard` until first sign-in, then keep `<CopilotKit>`
mounted across the sign-out cycle; shared `handleAuthError` on BOTH the
provider and agent-scoped `<CopilotChat onError>`; clear-on-auth effect;
amber `auth-demo-error` surface.
- auth-banner.tsx: shared `<Button>`, `onSignIn`/`onSignOut` props.
- sign-in-card.tsx: new, ported from the gold standard.
Add the shared shadcn primitives the gold-standard frontend depends on and
which claude-sdk-python was missing (`src/lib/utils.ts`,
`src/components/ui/button.tsx`, `src/components/ui/card.tsx`) plus the
`radix-ui` dependency they require, matching the claude-sdk-typescript peer.
Red/green on the real surfaces: against the legacy frontend `auth.spec.ts`
fails 6/6 (every test times out waiting for `auth-sign-in-button`); against
the rebuilt frontend it passes 6/6 and the `--d5 --isolate` auth probe is
green.
built-in-agent was the lone integration left on the legacy auth variant
when 5057efce1a brought the other 19 into conformance ("built-in-agent
already passes via its ChatErrorBoundary"). It rendered the post-sign-out
401 via a React ChatErrorBoundary (auth-demo-chat-boundary) + a local
401-regex onError, and defaulted to authenticated on first paint — so the
byte-identical auth.spec.ts (the CI conformance gate) failed every
unauth-first assertion.
Normalize to the langgraph-python gold shape:
- use-demo-auth.ts: unauth-first hook (hasEverSignedIn/signIn/signOut,
localStorage-backed token, isAuthenticated/authorizationHeader).
- page.tsx: drop ChatErrorBoundary/lastError/local-401-regex; wire a shared
handleAuthError onto BOTH <CopilotKitProvider onError> and the agent-scoped
<CopilotChat onError>; clear-on-auth useEffect keyed off authError alone;
unauth-first SignInCard gate; amber [data-testid="auth-demo-error"] surface.
- auth-banner.tsx / sign-in-card.tsx: align prop contract to gold
(onSignIn, onSignIn(token)).
Forced divergences preserved: built-in-agent IS the built-in agent, so it
keeps <CopilotKitProvider> (runtime registers the agent under the default
key) rather than <CopilotKit agent="auth-demo">, and uses raw Tailwind
elements (no shadcn @/components/ui in this integration). The error-handling
shape, auth hook, and testid contract match gold exactly.
Proven RED->GREEN on the byte-identical auth.spec.ts (the discriminating
surface; the --d5 probe accepts both shapes and was green for the legacy
frontend): all unauth-first conformance assertions flip FAIL->PASS, and the
canonical built-in-agent:auth --d5 --isolate probe is green.
The Authentication demo frontend conforms to the langgraph-python gold
standard but was missing its tests/e2e/auth.spec.ts (conformance rule 1:
e2e tests must be byte-identical to LGP). Add the LGP auth.spec.ts verbatim
(sha256 match) so the auth flow is e2e-covered. Verified green via
showcase test ms-agent-harness-dotnet:auth --d5.
The auth demo capped at D4 across integrations because the post-sign-out
rejection banner never rendered. The post-sign-out `agent_run_failed` is
delivered only on the agent-scoped `<CopilotChat onError>` channel — never the
provider-level `<CopilotKit onError>` the demos listened on — so the D5/D6 auth
probe's rejection-surface assertion failed and the cell was capped at D4.
Fix (applied to all 19 integrations whose auth demo reproduced the bug): wire a
stable `handleAuthError` onto the agent-scoped `<CopilotChat onError>` (keeping
the provider handler), key the error surface off auth-error STATE alone with a
clear-on-auth effect (removing the `&& !isAuthenticated` cross-slice race), and
harden the rejection-banner message fallback against nullish error events.
Scope: 19 of 20 integrations. built-in-agent already passes (renders via its
ChatErrorBoundary); claude-sdk-python adapted to its legacy/error-boundary shape.
Bump the canonical CopilotKit pin across all showcase integrations + shell
to 1.61.2 (canonical-pins.json, every package.json + package-lock.json),
which carries CopilotKit#5611: passing a catalog to the provider
(`<CopilotKit a2ui={{ catalog }}>`) now auto-enables A2UI and defaults tool
injection on, so the runtime no longer needs an explicit `a2ui` config.
Demonstrate the feature on the A2UI dynamic (declarative-gen-ui) demos by
removing the now-redundant runtime `a2ui` block (`injectA2UITool: true` +
`defaultCatalogId`) from:
- langgraph-python, langgraph-fastapi, langgraph-typescript
- strands, strands-typescript
- google-adk
The forwarded catalog supplies its own catalogId (sdk-js A2UI middleware
auto-derives `defaultCatalogId` from it), so the previous "Catalog not found"
fallback no longer applies.
Verified: validate-pins drift ratchet unchanged (38 / same hash);
langgraph-python D6 `gen-ui-declarative` green end-to-end (no Catalog-not-found).
The secondary-LLM prompt was far thinner than the canonical generation guidelines,
so it emitted trees that (correctly) failed the renderer's paint gate → surface-missing.
Port the canonical generation rules into the prompt, add output validation, add catalog
parity (DataTable + info-row), ground the planner with sales-context, and record
multi-turn aimock fixtures. Includes CR fixes: two-arg z.record for the DataTable rows
schema (zod@4 API), index-based DataTable row key, and Metric trendValue rendering for
neutral trend.
The showcase authors A2UI catalog defs with root zod@4, but @a2ui/web_core's
GenericBinder schema scraper inspects Zod-3 internals (_def.typeName==='ZodUnion').
A zod@4 union reports _def.typeName===undefined → misclassified STATIC → the raw
{path} binding object reaches render → React error #31. Author this demo's catalog
with a zod-v3 (npm:zod@3.25.76) alias so the binder resolves bindings. Includes CR
hardening of the shared a2ui factory validation (plain-object data guard, unique-id
check, fail-loud on non-string secondary-LLM return).
The built-in-agent gen-ui-agent D6 cell already passes end-to-end locally;
the PARITY_NOTES entry that documented it as RED/blocked on a STATE_DELTA
to useAgent gap in @copilotkit/react-core was stale. The set_steps to
STATE_DELTA {op:"add", path:"/steps"} workaround merged in
tanstack-factory.ts closed that gap: @ag-ui/client applies the patch and
fires onStateChanged, the core state-manager fans it to subscribers, and
useAgent re-renders off agent.state.steps. No react-core change is needed.
Rewrites the gen-ui-agent entry to GREEN/reclaimed and rescopes the
section header to the remaining A2UI render-layer demos (a2ui-fixed-schema,
declarative-gen-ui), whose fixes belong to @copilotkit/a2ui-renderer, not
react-core. Doc-only; no config quarantine existed (gen-ui-agent was never
in manifest not_supported_features), so the cell stays a counted green.
Local RED baseline: cell passes (1 passed) despite the stale RED doc.
Local GREEN value-test: --repeat 3 => 3 passed (130.1s), stable.
Add the shared-state-read demo entry to the strands and strands-typescript
manifests, mirroring the gold-standard langgraph-python entry. The fleet
enumerates D6 cells only from manifest demos that have both an id and a
route; shared-state-read was declared as a feature (and is not in
not_supported_features) but had no demo entry, so it resolved to status
unshipped and never ran on staging.
This makes the aimock fixture fix from #5673 actually take effect on the
fleet: both integrations now enumerate and run the shared-state-read cell
green.
## Summary
- Adds a `thread_persistence_pattern` manifest flag so shared docs can
render selected-framework Threads guidance.
- Marks LangGraph Python, LangGraph TypeScript, LangGraph FastAPI, and
Google ADK with the appropriate thread persistence pattern.
- Extends `WhenFrameworkHas` support so the shared Threads guide can
show LangGraph-only and ADK-only callouts.
- Clarifies that `useThreads` manages Enterprise Intelligence Platform
thread records, not native framework stores.
- Adds framework-selected callouts to the root/shared Threads guide
without adding a third setup path.
## Notes
The new callouts intentionally avoid claiming external store listing,
lifecycle sync, migration/import tooling, or durable ADK sessions by
default. Those remain product/runtime follow-ups tracked separately.
## Validation
- `git diff --check`
- `npm run pretypecheck` in `showcase/shell-docs`
- `npm run lint` in `showcase/shell-docs` (passes with existing
warnings)
- `npm run typecheck` in `showcase/shell-docs`
- `npm run build` in `showcase/shell-docs` (passes with existing
Turbopack/NFT warning)
- Local route smoke checks:
- `/threads` hides framework callouts
- `/langgraph-python/threads` shows LangGraph callout only
- `/langgraph-typescript/threads` shows LangGraph callout only
- `/langgraph-fastapi/threads` shows LangGraph callout only
- `/google-adk/threads` shows ADK callout only
Wire the strands-typescript showcase integration for staging deployment,
mirroring how the Python strands integration is deployed.
- manifest: flip deployed: true so the shell lists it in the integration menu
- railway-envs.ts: add showcase-strands-typescript SSOT entry (staging-only
for now: prod instance not yet provisioned, so it omits the prod env and is
gateIgnore'd until promoted dual-env); regenerate railway-envs.generated.json
- showcase_build.yml + showcase_build_check.yml: add the strands-typescript
build matrix entry, change-detection filter, and dispatch option (railway_id
is the new Railway service id)
- golden fixture + image-ref-gate inventory tests updated for the new service
Railway staging service showcase-strands-typescript provisioned
(showcase-strands-typescript-staging.up.railway.app, health /api/health,
OpenAI-via-aimock env). Prod is added later via the promote pipeline.
Brings google-adk to 39/39 D6 (reproduced across two independent full-matrix
runs, zero regressions). Four changes:
- entrypoint.sh: remove ADK_DISABLE_PROGRESSIVE_SSE_STREAMING=1. That flag's
non-progressive aggregation path ended ADK's agentic loop after the first
tool round (no post-tool LLM re-invoke), which broke every demo needing a
second turn: the subagents chain (research -> writing -> critique),
tool-rendering-reasoning-chain (AAPL -> MSFT), shared-state-read-write's
confirmation, and the custom-catchall narration. The partial-event abort it
guarded against is already handled in-callback by stop_on_terminal_text.
- manifest.yaml: un-skip-list tool-rendering-reasoning-chain (now passes with
the loop restored).
- headless_complete_agent.py: add AGUIToolset() so the frontend highlight_note
tool is injected and routed to the browser. Removing the flag unmasked this
pre-existing gap — turn 3 dispatched highlight_note server-side and the
backend registry rejected it. langgraph-python auto-injects frontend tools;
ADK needs AGUIToolset() in the agent's tools list.
- aimock/d6/google-adk/gen-ui-interrupt.json: order each pill's narration leg
(toolCallId) before its emit leg and drop the thread-global hasToolResult
gate, so the alice pill no longer 503s after the sales pill leaves a tool
result in the thread.
Flatten AG-UI attachment content into native pydantic-ai content types in
the OUTGOING request only, via a WrapperModel-scoped flatten rather than a
history_processor (so the flatten never persists into ctx.state.message_history
and leak into UI state). Normalize mime types, gate on supported content types,
and degrade unsupported types at the single emission choke point. Fixes the
_map_user_prompt assert_never crash on raw AG-UI multimodal content.
@tanstack/ai-openai@0.15.6 (via @tanstack/openai-base@0.9.2) fixes the upstream
isStrictModeCompatible bug: it now detects typeless / z.any() properties and
sends strict:false, so OpenAI accepts them instead of 400ing with
"schema must have a type key".
With the upstream fix the showcase needs no schema change: revert the
z.string() workaround so the state tools use z.any() again (state-tools.ts is
now identical to main). Net change vs main is just the version bump:
@tanstack/ai 0.18.0 -> 0.35.0, @tanstack/ai-openai 0.9.1 -> 0.15.6.
Verified on the real OpenAI Responses API with all three z.any() state tools
attached (AGUISendStateSnapshot / AGUISendStateDelta / set_steps): adapter
emits strict:false, request ACCEPTED, model calls the tool.