## Summary
- Adds `CachingDiscoverySource` wrapper with 24h TTL and concurrent
enumeration collapsing — serves stale discovery results when Railway API
is unreachable, preventing total probe blindness
- Adds `DiscoveryAuthTracker` that detects persistent auth failures (3
consecutive) and writes system status to PocketBase with rate-limited
sustained alerting
- Adds `DiscoveryAuthBanner` dashboard component that surfaces auth
failures and browser pool degradation above all tabs, with auto-dismiss
on recovery
- Caches Railway `listServices` in the adapter (60s TTL) to eliminate
N+1 GraphQL round-trips per probe tick
- Writes system status on browser pool init failure so stub-driver
degradation is visible
## Why
A Railway API token expired and the showcase dashboard showed stale
green data for 24+ hours with no operator-visible alert. Discovery runs
from scratch every probe tick with no caching or fallback. This PR adds
resilience (cache), observability (auth tracker + dashboard banner), and
fixes a pre-existing silent degradation path (browser pool).
## Test plan
- [ ] 18 caching-source tests (success, failure, TTL, eviction,
concurrent collapse, tracker integration)
- [ ] 9 auth-tracker tests (threshold, rate-limiting, recovery,
interleaved errors)
- [ ] 12 banner tests (render variants, signal validation,
accessibility, both banners simultaneously)
- [ ] Full harness suite regression check (1612/1614 pass, 2
pre-existing failures on main)
- [ ] TypeScript typecheck clean on new code
DiscoveryAuthBanner renders above all tabs when discovery auth fails.
Two variants: serving-stale (probes running against cached data) and
no-cache (probes offline). Also surfaces browser pool degradation.
Runtime signal shape validation, auto-dismiss on recovery. 12 tests.
Wraps railwayServicesSource with withCache (24h TTL). Instantiates
DiscoveryAuthTracker with threshold 3. Adds system dimension. Caches
listServices in Railway adapter (60s TTL). Writes system status on
browser pool init failure so degradation is visible in the dashboard.
Tracks auth failures per source since last success. After 3 failures,
writes system:discovery-auth-failed to PocketBase. Sustained alerts
rate-limited to one PB write per 5 minutes. Auto-recovers on next
success. Non-auth errors are no-ops. 9 test cases.
Transparent wrapper at the DiscoverySource interface level. Caches
successful enumerate() results in memory (24h TTL), serves stale
data on upstream failure, collapses concurrent callers into a single
upstream request. Auth tracker side-effects are try-caught to never
block the primary data path. Evicts entries older than 2x TTL.
18 test cases covering success, failure, TTL, collapse, eviction,
non-JSON config guard, and tracker integration.
## Summary
Three independent, layered fixes that came out of a focused google-adk
D5 debugging session on top of the recently-merged ag-ui-adk + aimock
parity work:
1. **Harness probe fixes** (`showcase/harness/...`) —
`E2eDeepPage.locator` is now wired through the structurally-typed
wrapper so probes that need count-based polling can use
`page.locator(sel).count()` instead of erroring with "pw.locator is not
a function" at runtime. Paired with a native `setTimeout` between polls
in `d5-tool-rendering-reasoning-chain` (Playwright's
`page.waitForTimeout` also wasn't in the wrapper surface). Added
`google-adk` to `d5-gen-ui-custom`'s `CHART_INTEGRATIONS` set —
google-adk's `gen-ui-tool-based` page was ported from LGP verbatim and
now registers `render_pie_chart`/`render_bar_chart`, so the haiku-shape
probe path no longer applies.
2. **`ag-ui-adk 0.6.1 → 0.6.3`** (`requirements.txt`) — pulls in the
`FunctionResponse.name` fix
([ag-ui-protocol/ag-ui#1682](https://github.com/ag-ui-protocol/ag-ui/pull/1682)).
Without it, every multi-leg D5 fixture keyed on `toolCallId` fell
through to the first-leg `userMessage` matcher and looped indefinitely.
3. **E2E spec parity** (`tests/e2e/*.spec.ts`) — 16 diverged specs
synced verbatim from langgraph-python plus 3 previously-missing ones
(`chat-customization-css`, `prebuilt-sidebar`, `reasoning-custom`).
Per-package `pnpm test:e2e` is the local dev validation loop; without
parity here, tiny divergences accumulate silently until they surface as
D5 regressions in CI.
## Context
These changes were extracted from a broader google-adk D5 investigation
branch. Several other fixes from that session (the `@ag-ui/client 0.0.43
→ 0.0.53` bump, the `stop_on_terminal_text` callback, most agent-side
edits, several diverged demo pages) landed independently on `main`
during the same window, so this PR carries only the work that does not
overlap with those commits.
## Test plan
- [ ] CI: harness vitest + showcase package builds pass.
- [ ] Local: `./showcase/bin/showcase test
google-adk:tool-rendering-reasoning-chain --d5` no longer errors with
`pw.locator is not a function`; the chain advances past leg 1.
- [ ] Local: `./showcase/bin/showcase test google-adk:gen-ui-tool-based
--d5` exercises the chart path (not haiku) and goes green.
- [ ] Local: `cd showcase/integrations/google-adk && pnpm test:e2e` runs
the synced specs against the dev server.
Pairs with merged upstream work:
- aimock [#199](https://github.com/CopilotKit/aimock/pull/199) (egress
`functionCall.id` round-trip, v1.24.1)
- ag-ui-protocol/ag-ui
[#1682](https://github.com/ag-ui-protocol/ag-ui/pull/1682)
(`FunctionResponse.name` correction, 0.6.3)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Aligns A2UI integration docs (DeepAgents, LangGraph, A2A) with the
v0.9 helpers exported by
[`sdk-python/copilotkit/a2ui.py`](https://github.com/CopilotKit/CopilotKit/blob/main/sdk-python/copilotkit/a2ui.py):
`create_surface`, `update_components`, `update_data_model`.
- Updates Python helper names, wire-format op keys in prose
(`surfaceUpdate` → `updateComponents`, etc.), and the TS action-handler
return shapes in `advanced.mdx`. Reorders operations so `createSurface`
comes first (no trailing `beginRendering`).
- A2A example: renames the root component id from `root-column` to
`root` per the v0.9 catalog convention (exactly one component must have
`id: "root"`).
Frontend dispatch confirmation:
`packages/react-core/src/v2/a2ui/A2UIMessageRenderer.tsx`
(`getOperationSurfaceId`) routes only on `createSurface |
updateComponents | updateDataModel | deleteSurface`, so the pre-v0.9
names in the docs would not round-trip through the runtime.
Tracks #4821 (point 1).
## Files changed
9 mdx files, +45 / −47:
-
`docs/content/docs/integrations/deepagents/generative-ui/a2ui/{index,fixed-schema,dynamic-schema,advanced}.mdx`
-
`docs/content/docs/integrations/langgraph/generative-ui/a2ui/{index,fixed-schema,dynamic-schema,advanced}.mdx`
-
`docs/content/docs/integrations/a2a/generative-ui/declarative-a2ui.mdx`
No SDK or runtime changes.
## Out of scope (filed under #4821)
- `action_handlers=` kwarg appears in fixed-schema examples but is not
yet on `a2ui.render`'s signature. Left in place — separate decision
needed (SDK feature vs. doc rollback).
- No runnable DeepAgents showcase. Filed for tracking, not addressed
here.
## Test plan
- [ ] Vercel preview renders all 9 pages without MDX errors
- [ ] Code snippets visually match the v0.9 SDK surface
(`create_surface` / `update_components` / `update_data_model`)
- [ ] A2A example: root component id reads `"root"` (not
`"root-column"`)
- [ ] `dynamic-schema.mdx` prose lists `createSurface` +
`updateComponents` (not the pre-v0.9 names)
`headless_complete` was wired to `_simple_chat` (zero backend tools)
in the registry. The d5-gen-ui-headless-complete probe sends prompts
that need `get_weather` / `get_stock_price` / `get_revenue_chart`
to mount their respective per-tool renderer cards on the frontend
(`useRenderTool` keys on tool name), so without the backend tools the
fixture's tool-call response had no matching Python function to run
and the cards never mounted.
Ports the three mock tools verbatim from
`langgraph-python/src/agents/headless_complete.py` (same payload
shapes, same system-prompt routing rules) onto a dedicated
`headless_complete_agent` LlmAgent and re-points the registry slot.
The frontend's `highlight_note` is a useComponent-style frontend
tool and the Excalidraw MCP tools are injected by the runtime
middleware — neither needs a backend Python function, matching the
LGP shape.
Local D5: google-adk:headless-complete flips from red to green.
Ports 16 diverged Playwright e2e specs verbatim from langgraph-python
and adds 3 previously-missing specs (chat-customization-css,
prebuilt-sidebar, reasoning-custom). All 19 files are byte-identical
to LGP, mirroring the same approach the recent ADK parity push used
for the demo pages.
Why this matters even though D5 is the gold standard: the per-package
Playwright suites (`pnpm test:e2e`) are the local dev validation loop.
Without parity here, a contributor editing google-adk's CopilotChat
surface has no local check that matches what langgraph-python ships,
and tiny divergences between the two surfaces (missing testids, stale
selectors, wrong assertion shapes) silently accumulate until they
surface as D5 regressions in CI.
0.6.3 ships the FunctionResponse.name fix
(ag-ui-protocol/ag-ui#1682) — the converter now sets the response's
name field to the called function's name (e.g. `get_weather`) instead
of the tool_call_id. Without this, downstream consumers that recover
the originating call's id by name (Gemini's session correlator,
aimock's gemini->openai translator that locates a prior tool_call by
name to recover its id) hit a UUID-shaped `name` that no prior call
matches and the round-trip silently breaks — multi-leg D5 fixtures
keyed on `toolCallId` (tool-rendering-reasoning-chain, the gen-ui-*
chains, shared-state-streaming) fall through to the first-leg fixture
on every follow-up, looping indefinitely or stranding the UI.
Pairs with aimock 1.24.1 (CopilotKit/aimock#199) which surfaces the
`tool_call.id` on the egress side so there's actually an id for the
ADK middleware to preserve in the round-trip.
The Python A2UI SDK (sdk-python/copilotkit/a2ui.py) exposes v0.9 helpers
create_surface, update_components, and update_data_model, but the
integration docs still referenced earlier names (surface_update,
data_model_update, begin_rendering) and the corresponding wire-format
op keys (surfaceUpdate, dataModelUpdate, beginRendering). The frontend
renderer (A2UIMessageRenderer) routes operations by the v0.9 keys, so
the docs as written would not round-trip through the runtime.
Updates 9 mdx files under integrations/{deepagents,langgraph,a2a}:
- Python helper names in code snippets
- Op keys in prose and TS action-handler return values
- Reorders operations so createSurface comes first (no trailing
beginRendering)
- a2a example: renames the root component id from "root-column" to
"root" per the v0.9 catalog convention (exactly one component must
have id="root")
No SDK or runtime changes.
5 dependencies drifted between package.json and package-lock.json in
the nested src/agent sub-package, causing npm ci to fail in Docker
builds. Lock file regenerated to match current package.json.
## Summary
- **Health endpoints**: All 18 integration health endpoints simplified
from
two-hop agent proxy (3s timeout, false reds) to local-only (just
confirms
Next.js process is alive). The harness already checks agent reachability
via the `agent:<slug>` probe.
- **Column headers**: U/W/C/T badges removed from Coverage tab. They
read
integration-level probes independent of per-feature cell data, causing
contradictory states (e.g. U red while all cells green). The tally
derived
from `buildCellModel` is now the sole column summary.
## Why
Google ADK's U badge showed red while all 34 feature tests passed. Root
cause:
the health endpoint proxied to the Python agent's `/health` with a
3-second
timeout. Under load, the agent's health middleware exceeded 3s even
though
AG-UI protocol endpoints (used by e2e tests, with 30-60s timeouts)
responded
fine. The health probe was testing a different code path with a tighter
timeout
than the actual tests.
## Test plan
- [x] 100 dashboard tests pass (cell-model, unified-cell, depth-chip,
cell-matrix)
- [x] oxfmt clean
- [ ] Health probes flip to green after deploy (harness smoke tick
within 5 min)
- [ ] Coverage tab headers show tally only, no U/W/C/T badges
Previously achievedDepth=0 always produced gray regardless of whether
tests existed. Now: ceilingDepth=0 (no tests) = gray, ceilingDepth>0
with achievedDepth=0 (tests exist, all fail) = red. Tally dimension
derived from model instead of hardcoded "e2e".
Remove misleading header badges that read integration-level probes
independent of per-feature cell data. Replace 5 duplicate local
Overlay types with canonical import. Remove dead connection prop.
Add exhaustive state handling in level-strip. Remove redundant
?? false in isSupported expressions.
All 18 integration health endpoints previously proxied to the backend
agent /health with a 3s timeout, causing false reds when agents were
slow but functional. The harness already checks agent reachability
via the agent:<slug> probe. Health endpoints now return a simple 200
confirming the Next.js process is alive.
## Summary
One-time `ruff format .` across the Python codebase — 320 files,
7759+/4678-.
This aligns the existing Python code with the ruff format check added in
#4812. Without this, the push-to-main format job fails on every merge
that touches the `static / quality` workflow.
## Why a separate PR
Pure formatting, no behavioral changes. Keeping it isolated makes git
blame clean — one commit to skip, not interleaved with logic changes.
## Test plan
- [ ] CI format job passes (the whole point)
- [ ] Python unit tests unaffected (formatting only)
## Summary
- **lefthook**: Widen pre-commit glob from JS/TS-only to match CI's full
file
coverage (json, md, css, yml, yaml, html, vue, py). Add `ruff format`
for
Python files.
- **CI format job**: Replace full `pnpm install` (~8min) with standalone
binary
installs (`oxfmt` + `ruff`, ~10s). Add Python formatting to both PR and
push-to-main paths.
## Why
The format job took 8+ minutes because it ran `pnpm install
--frozen-lockfile`
to get the oxfmt binary. oxfmt and ruff are both standalone Rust
binaries that
don't need the monorepo's node_modules. Local lefthook only covered
JS/TS
extensions, so JSON/YAML/MD/Python formatting was only enforced in CI —
after
the push.
## Test plan
- [x] lefthook.yml YAML validates
- [x] static_quality.yml YAML validates
- [ ] CI format job runs in <30s (was ~8min)
- [ ] Lefthook pre-commit catches formatting in JSON/MD/Python files
Tallies now count by CellModel.chipColor instead of resolveCell rollup,
ensuring header numbers match what cells actually render. Gray cells
(no data) are excluded from counts.
Replaces full pnpm install with standalone binary installs. Adds ruff
format for Python files in both PR and push-to-main paths. Reduces
format job from ~8min to ~30s.
Pre-commit lint-fix now covers json, md, css, yml, yaml, html, vue, py
in addition to js/ts. Adds ruff format for Python files. Local
formatting coverage now matches CI.
## Summary
Consolidates all Coverage-tab cell rendering into a single codepath
driven by `CellModel` — a unified type that captures support status +
D3/D4/D5 test existence and status. Replaces the fragmented
multi-codepath architecture where `deriveDepth()`, `resolveCell()`,
`ComposedCell`, `DepthLayer`, and `HealthLayer` each independently
resolved different aspects of cell state.
**Fixes three dashboard rendering bugs:**
- **D0 despite passing tests** — old depth ladder required
`health:<slug>` green before checking e2e; new model resolves D3
directly from `e2e:<slug>/<feature>`
- **Yellow D4 at ceiling** — old `depthColorClass()` hardcoded D4 as
blue/accent with no ceiling concept; new model computes chip color
relative to ceiling (green when achieved == ceiling)
- **No-entry icon alongside test badges** — old
`DepthLayer`/`HealthLayer` rendered independently without cross-checking
support status; new `UnifiedCell` short-circuits on unsupported
## Changes
- **`cell-model.ts`** — New `CellModel` type and `buildCellModel()`
function. Single derivation: resolves D3 (e2e), D4 (chat/tools
worst-state), D5 (CATALOG_TO_D5_KEY multi-key worst-state). Computes
contiguous ceiling depth and chip color relative to ceiling.
- **`unified-cell.tsx`** — New `UnifiedCell` component consuming
`CellModel`. Unsupported = no-entry only. Badges only for existing test
levels. `arePropsEqual` synced with `buildCellModel` reads.
- **`depth-chip.tsx`** — Added `chipColor` prop for pre-computed color
override (green/amber/red/gray).
- **`page.tsx`** — Replaced `ComposedCell` with `UnifiedCell`,
`deriveDepth` with `buildCellModel` in healthStats/depthDistribution.
- **`feature-grid.tsx`** — Ref-depth column migrated to
`buildCellModel`.
- **`cell-matrix.tsx`** — Baseline tab migrated from `deriveDepth` to
`buildCellModel`.
- **`composed-cell.tsx` / `depth-utils.ts`** — Deprecated.
## Test plan
- [x] 22 unit tests for `buildCellModel` covering all bug regressions,
D4 worst-state, D5 multi-key, contiguous chain, degraded mapping
- [x] 14 unit tests for `UnifiedCell` covering unsupported guard, badge
existence, overlay gating, chipColor passthrough
- [x] 12 existing `DepthChip` tests + 12 new `chipColor` tests (52
total)
- [x] 12 `CellMatrix` tests updated for D3/D4/D5 model
- [x] 100 tests pass, 0 fail
- [x] 7-agent CR converged in 2 rounds (3 bucket-a fixes in round 1, 0
in confirmation)
DepthChip accepts pre-computed chipColor prop (green when achieved equals
ceiling). UnifiedCell is the single rendering codepath: unsupported cells
show only the no-entry icon, badges render only for existing test levels.
arePropsEqual synced with buildCellModel reads (e2e/chat/tools/d5 keys).
Single source of truth for Coverage-tab cell state. Replaces fragmented
depth/badge resolution. Resolves D3/D4/D5 test existence and status
independently, computes contiguous ceiling depth and chip color relative
to ceiling (green at ceiling, gray for no data, amber/red below).
## Summary
31 feature-parity aimock fixtures had `userMessage` match + `toolCalls`
response but **no `hasToolResult` constraint**. They re-matched on
follow-up turns (where a tool result was already present), returning
another tool call — creating infinite loops across all ADK demos.
Combined with aimock v1.23.1
([CopilotKit/aimock#196](https://github.com/CopilotKit/aimock/pull/196)
— preserves `functionCall.id` in Gemini conversation conversion), this
eliminates the showcase infinite tool call loop.
## Root cause
The loop was caused by aimock fixture re-matching, not Gemini model
behavior.
## Test plan
- [x] RED: "What is the weather in Paris?" on tool-rendering demo with
old fixtures → `max_llm_calls limit of 15 exceeded`, 15 Tokyo weather
cards rendered
- [ ] GREEN: Same query with fixed fixtures → single tool call, text
response, done
31 fixtures had userMessage match + toolCalls response but no
hasToolResult constraint. They re-matched on follow-up turns where
a tool result was present, returning another tool call — infinite loop.
## What does this PR do?
Fixes the Task Manager (Shared State) pill in the langgraph-python
`beautiful-chat` showcase, where clicking the pill flipped the canvas to
App mode but the To Do column stayed empty even though the backend graph
had populated `state.todos`.
**Root cause.** `<CopilotKit agent="beautiful-chat">` in
[`page.tsx`](showcase/integrations/langgraph-python/src/app/demos/beautiful-chat/page.tsx)
routes the chat through agent id `"beautiful-chat"`. The chat is
required to be on that id so the cell's `useComponent` /
`useFrontendTool` / `useDefaultRenderTool` registrations (chart, flight,
dashboard pills) resolve. `ExampleCanvas`, however, called `useAgent()`
with no args, which defaults to `DEFAULT_AGENT_ID` (`"default"`). The
frontend's agent registry creates a separate
`ProxiedCopilotRuntimeAgent` instance per id even though the route had a
`default: beautifulChatAgent` alias on the backend — state-deltas from
`manage_todos` landed on the chat's `"beautiful-chat"` instance and
never reached the canvas's `"default"` subscription.
**Fix.** Pin the canvas to the same agent id and drop the now-unused
backend alias:
- `src/app/demos/beautiful-chat/components/example-canvas/index.tsx` —
`useAgent({ agentId: "beautiful-chat" })`
- `src/app/api/copilotkit-beautiful-chat/route.ts` — drop the `default:
beautifulChatAgent` alias (the only consumer was the canvas's old
default fallback)
Both halves now share one `ProxiedCopilotRuntimeAgent` on the frontend,
so `manage_todos` state-deltas flow into `agent.state.todos` and the
canvas re-renders.
**Regression coverage.**
- `tests/e2e/beautiful-chat.spec.ts` — Playwright test clicks the Task
Manager pill and asserts the 3 verbatim todo titles render in the To Do
column. Includes a `waitForLoadState("networkidle")` before the click so
the pill-driven `runAgent` doesn't race the v1 CopilotKit context setup.
- `showcase/aimock/feature-parity.json` — 3 fixtures for the multi-turn
flow (`parallel_tool_calls=False`, so each step is its own LLM call):
1. `userMessage: "three todos about learning CopilotKit"` +
`hasToolResult: false` → `enableAppMode` tool call
2. `toolCallId: call_fp_beautiful_chat_enable_app_mode_001` →
`manage_todos` tool call with three pending todos
3. `toolCallId: call_fp_beautiful_chat_manage_todos_001` → final
plain-text confirmation
Verified end-to-end against local aimock + langgraph + Next.js. With the
fix the test passes in ~5s; reverting just the `useAgent` change
reproduces the empty-canvas failure.
## Related PRs and Issues
- N/A
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
`<CopilotKit agent="beautiful-chat">` routes the chat to agent id
"beautiful-chat", but ExampleCanvas called `useAgent()` with no args and
fell back to DEFAULT_AGENT_ID ("default"). The frontend's agent registry
tracks state per id, so `manage_todos` state-deltas from the chat run
landed on "beautiful-chat" and never reached the canvas's "default"
subscription — the Task Manager pill auto-flipped the panel to App mode
but the To Do column stayed empty. Drop the unused "default" alias from
the runtime route and pin the canvas to `useAgent({ agentId:
"beautiful-chat" })` so both halves share one ProxiedCopilotRuntimeAgent
instance. Adds a Playwright regression test asserting the 3 verbatim
todo titles render after the pill click, plus 3 aimock fixtures for the
multi-turn flow (enableAppMode -> manage_todos -> confirmation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Release monorepo v1.57.2
**Scope:** `monorepo` | **Bump:** `patch`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `monorepo` packages to `1.57.2`
and generated AI-enhanced release notes.
2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
must pass before merging. This is the review gate.
3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.
4. **When this PR is merged**, the `release / publish` workflow
automatically:
- Builds all packages
- Publishes the `monorepo` packages to npm at version `1.57.2`
- Creates git tag `monorepo/v1.57.2`
- Creates a GitHub Release with the final release notes
### Before merging
- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)
---
> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
Unit, integration, and e2e tests covering hooks, components,
providers, types, and utilities with mock agent context,
mount helpers, and web-inspector stubs for full React
parity verification.
components
CopilotKit and chat configuration providers with injection
keys, type definitions for tools/HITL/interrupts/renderers,
HTML processing and transcription utilities, inspector,
license warning, activity renderers, and barrel exports.