The YAML manifest was missing the app_home block the JSON variant has, so
apps created from it showed "Sending messages to this app has been turned
off". Add app_home with the Messages tab enabled (not read-only), matching
slack-app-manifest.json.
examples/slack depends on @copilotkit/bot* at "~0.0.1" so the example stays
deployable (mirrors a real npm install). Under pnpm 10 (link-workspace-packages
defaults off) that resolved the PUBLISHED 0.0.1 from npm instead of the local
workspace packages, so the example couldn't exercise local changes. Add root
pnpm.overrides mapping the three @copilotkit/bot* packages to workspace:*, which
forces local installs to link the workspace copies while leaving the example's
published version range intact.
The D5 e2e-deep probe for tool-rendering-{default,custom}-catchall sends
"weather in Tokyo" as the test input (harness/src/probes/scripts/
d5-tool-rendering-{default,custom}-catchall.ts). The fixture userMessage
matcher uses substring match. Main's catchall fixtures had a stale
"check Tokyo weather forecast" string that could not substring-match
the probe input, causing the fixture to miss and the probe to fall
through to the live LLM — surfacing as CV x red D4 on the dashboard.
Rename to the canonical "weather in Tokyo" string across affected
integrations' tool-rendering-{default,custom}-catchall.json files.
No agent or page.tsx changes; fixture content otherwise unchanged.
Update package READMEs + ARCHITECTURE for the assistant pane, native streaming,
and the new onThreadStarted / setSuggestedPrompts / setTitle surface. Reverse
the slack.mdx callout that told users to delete the assistant scopes (now
required), and enable the pane in the examples/slack manifest (assistant_view +
assistant:write + assistant_thread_* events) with a dev-ex onThreadStarted
greeting and the assistant config.
Activate Slack's assistant pane and native streaming as the default experience,
with zero config and safe degradation. The portable surface lands in
@copilotkit/bot (onThreadStarted lifecycle, capability-gated
thread.setSuggestedPrompts/setTitle, two SurfaceCapabilities flags + optional
adapter methods); @copilotkit/bot-slack adds the Bolt Assistant middleware
(assistant.ts), the chat.startStream transport with automatic legacy fallback
(native-stream.ts), a per-thread no-double-delivery listener guard, and
renderer pane-status mode. Everything degrades, never throws.
lefthook invokes a multi-line `run` as `sh -c "<script>"` and (in lefthook
2.1.1 / Windows git-sh) does not escape the script's embedded double quotes,
so `"$@"` / `[ "$#" ]` / `x=""` close the `-c` string early and the shell
aborts with "unexpected EOF", failing every commit. Rewrite the lint-fix
script double-quote-free (unquoted $@/$# — staged paths have no spaces; JSON
is already glob-excluded so oxfmt never sees package.json; ruff scoped to .py
via case).
## Summary
The `built-in-agent:tool-rendering-custom-catchall` probe sends
`"weather in Tokyo"` then `"What's the current price of AAPL?"` in one
session. After the weather tool runs in turn 1, `hasToolResult` is true
across the rest of the thread — which fires `tool-rendering.json`'s AAPL
`hasToolResult:true` narration prematurely on turn 2 iteration 1,
returning prose without ever emitting `get_stock_price`. The
custom-catchall assertion (both tools rendered through the wildcard
testid) then fails with missing `get_stock_price`.
The bug was structural: the (hasToolResult:true narration +
hasToolResult:false/turnIndex:0 emitter) layered fallbacks were authored
as if hasToolResult tracked the CURRENT pill's tool, but the matcher
checks for ANY tool result in history.
## Fix
Replace the layered hasToolResult fallbacks with a `sequenceIndex:0`
emitter ordered BEFORE a bare `userMessage+context` narration. The
per-test fixture-match counter resets each run, so the emitter fires
exactly once on iteration 1 regardless of prior pills' tool history,
then falls through to the narration on iteration 2. The toolCallId-keyed
narration above each block is retained for the non-BIA fast path.
Applied symmetrically to the two AAPL blocks in `tool-rendering.json`
(the `"What's the current price of AAPL?"` block at the top and the
legacy `"current price of AAPL"` alias block lower down).
The earlier partial fix to `tool-rendering-custom-catchall.json` is kept
— those fixtures never match real probe traffic (they use unique `"check
Tokyo weather forecast"` substring) but the reordering is consistent
with the cross-file pattern.
## Verification
- `./bin/showcase test built-in-agent:tool-rendering --d6 --direct` →
green
- `./bin/showcase test built-in-agent:tool-rendering-custom-catchall
--d6 --direct` → green (turn 2 emits get_stock_price; cross-tool
signature pass)
- `./bin/showcase test built-in-agent:tool-rendering-default-catchall
--d6 --direct` → green
- `pnpm vitest run __tests__/aimock-fixtures.test.ts` (showcase/scripts)
→ 737 pass; collision/shadow ceilings unchanged.
## Test plan
- [x] tool-rendering local green
- [x] tool-rendering-custom-catchall local green
- [x] tool-rendering-default-catchall local green
- [x] aimock-fixtures.test.ts collision/shadow ceilings unchanged
- [ ] CI green
The custom-catchall probe sends 'weather in Tokyo' then 'AAPL'. After the
weather tool runs in turn 1, hasToolResult is true across the rest of the
thread — which fires tool-rendering.json's AAPL 'hasToolResult:true' narration
prematurely on turn 2 iteration 1, returning prose without ever emitting the
get_stock_price tool. The custom-catchall assertion (both tools rendered
through the wildcard testid) then fails with missing get_stock_price.
Replace the (hasToolResult:true narration + hasToolResult:false/turnIndex:0
emitter) layered fallbacks with a sequenceIndex:0 emitter ordered before a
bare userMessage+context narration. The per-test fixture-match counter resets
each run, so the emitter fires exactly once on iteration 1 regardless of
prior pills' tool history, then falls through to the narration on
iteration 2. Applied symmetrically to the two AAPL blocks in
tool-rendering.json (the 'What\'s the current price of AAPL?' block at the
top and the legacy 'current price of AAPL' alias block lower down). The
toolCallId-keyed narration above each block is retained for the non-BIA
fast path.
The earlier partial fix to tool-rendering-custom-catchall.json is kept (it
adds toolCallId-scoped narration legs ordered before the existing
hasToolResult:true narrations); those fixtures never match real probe
traffic (the probe sends 'weather in Tokyo' / 'current price of AAPL', not
the unique 'check Tokyo weather forecast' substring in this file) but the
reordering is consistent with the cross-file pattern and harmless.
Verified locally: built-in-agent:tool-rendering and
built-in-agent:tool-rendering-custom-catchall both green via
`./bin/showcase test ... --d6 --direct`; built-in-agent:tool-rendering-default-catchall
also green; aimock-fixtures.test.ts (collision/shadow ceilings) unchanged.
The default framework's docs now live at bare root URLs (/quickstart,
/server-tools, ...) instead of under /built-in-agent/. The root
catch-all resolves BIA-authored pages first, /built-in-agent/:path*
permanently redirects to /:path*, and sidebar/landing/selector hrefs
are root-relative.
The whole root surface shares ONE sidebar: the Built-in Agent IA with
the agnostic root sections (Concepts, Runtime, Deploy, Platforms, Other)
folded in via buildRootSurfaceNav. Empty ---Section--- placeholders in
the BIA meta.json position each folded-in section; appendSharedRootSections
fills them, dropEmptySections clears any that stay empty, and route-group
nodes (e.g. the (other) tree) are excluded so the fold never emits a bogus
/(other)/... href. Without this, navigating from a BIA page to an agnostic
page (/concepts/*, /backend/*) swapped the sidebar between two overlapping
IAs. The fold is scoped to the root surface only — deepagents keeps
Platforms-only and generated frameworks are untouched.
Duplicate pages that the fold would otherwise double up are consolidated
onto their canonical root homes:
- The three BIA backend wrappers (copilot-runtime, custom-agent, ag-ui)
are retired; the folded-in Runtime section is their single home. This
resolves the /backend/ag-ui collision (the wrapper had shadowed the
real root page). The bare /ag-ui segment belongs to the AG-UI protocol
docs, so old /built-in-agent/ag-ui links redirect to /backend/ag-ui.
- The BIA troubleshooting wrappers are retired in favor of the canonical
/troubleshooting/* pages surfaced by the folded-in Other section, so
Troubleshooting appears once.
Stale redirect R25 (/runtime-server-adapter -> /backend/copilot-runtime)
is removed: runtime-server-adapter is a distinct, current 'Deploy to any
runtime' page linked from the sidebar, and the redirect had made it
unreachable at its own URL.
Middleware rules that would shadow or loop against the new surface
(M2 /quickstart, BIA_DEFAULT_ROOT_REDIRECTS, MV-telemetry) are retired,
and remaining live destinations move off the old prefix. The sitemap,
llms.txt, per-page .md/.mdx, and OG image routes resolve the same
content the pages serve. The client-side RouterPivot bounce is removed
since root URLs now render real content in place.
Override the basic catalog's Text (its built-in 8px margin misaligned
card rows), keep badges content-sized instead of stretched by flex
parents, and prefix each badge with a hardcoded lucide icon per variant
(error/warning/success/info). Renderer-only — payloads and fixtures are
unaffected.
Hero loses its surrounding card (bare KPI strip over the chart cards,
pinned to all six months); team performance pairs the rep table with a
quota-attainment bar chart; top account pairs the fact card with a
product-line pie (new dataset entry); at-risk becomes a risk panel — KPI
strip (ARR at risk / accounts / biggest exposure) over three side-by-side
severity cards with reason + next action. Fixtures re-captured from live
responses; D5 probe drops declarative-card from the hero set; e2e asserts
the accompanying charts and the risk panel; QA docs updated.
Ports beautiful-chat's exact visual language into the catalog renderers:
DashboardCard chrome (12px radius, 20px padding, soft shadow) for Card and
chart wrappers, its Metric typography with colored trend deltas, a recharts
donut (innerRadius 40, paddingAngle 2, tooltip, no legend) replacing the
custom SVG donut, and uniform blue bars on a dashed grid. E2E pie
fingerprints move from circle/legend assertions to recharts sectors; the
hero surface-count guard allows the two ResponsiveContainers (pie + bar)
one composed dashboard now produces.
Click a pill, then require the user-message bubble before asserting on
the surface; retry the click if it was swallowed. On slow dev-server
hydration the first click can land before the chat send pipeline is
wired, which previously burned the full surface-assertion budget and
masked the real failure point.
Replaces the hand-authored surface payloads with real gpt-5.4 responses
captured via the langgraph dev threads API during OSS-136 prompt
iteration (catalogId injected, since the replay path resolves the catalog
from recorded tool args). Verified: fixture suite 738/738, LGP container
e2e 6/6, ADK container e2e 6/6.
Same three-call choreography per pill (outer generate_a2ui, inner design
toolcall discriminated by toolName, narration matched by toolCallId), now
keyed to the sales-analyst prompts and emitting Vantage Threads payloads:
composed hero dashboard, rep DataTable, at-risk StatusBadge cards, and
top-account InfoRows. Verified by the LGP Playwright run against the
aimock-backed container (6/6).
Hero pill asserts the composed dashboard conjunctively (card + metric +
pie + bar); pills 2-4 each require a testid the hero is steered not to
mount (data-table / status-badge / info-row), preserving the
newly-mounted anti-masking gate.
The demo now plays an embedded sales analyst for a fictional company:
suggestion pills are natural business questions (chart-type steering moved
from user prompts into the system prompt), the hero pill composes a full
dashboard (KPI metrics + pie + bar in one surface) modelled on
beautiful-chat's sales dashboard, and the catalog gains DataTable,
gap-aware Row/Column, Metric trendValue, and the beautiful-chat palette.
Dataset + composition rules ship as frontend agent context
(sales-context.ts) so they reach both the primary agent and the secondary
A2UI planner in LGP and ADK alike. E2E specs and QA docs updated to the
new pill set.
* duplicate ceiling 290→291: tool-rendering.json's tightened 'current
price of AAPL' matchers now share two match keys with the existing
tool-rendering-custom-catchall.json entries in the same BIA context,
runtime-disambiguated by feature route.
* shadow ceiling 134→132 (ratchet down): the bare 'AAPL' vs 'current
price of AAPL' shadow pair on the tool-rendering.json side is gone.
* PARITY_NOTES: replaces the 'headless-complete turns 3+4 server-tool
reprompt loop' known-issue section with a resolved-via-sequenceIndex
description; the architectural reprompt loop now converges via the
sequenceIndex-gated emitter + narration-fallback pattern in
gen-ui-headless-complete.json.
BIA registers headless-complete's tools (get_weather, get_stock_price,
get_revenue_chart, highlight_note) as server-executed via TanStack's
chat() engine. After the LLM returns a tool call, TanStack runs the
server tool and reprompts the LLM with the result. The userMessage-keyed
toolcall fixtures fired again on every reprompt because the original
user pill text stays in conversation history — and the toolCallId-keyed
narration fallback never matched because BIA's /v1/responses endpoint
rewrites the assistant tool_call_id to a runtime-generated fc-… value.
Net effect: turns 3+4 (highlight, revenue chart) ballooned to 70+
assistant messages within the 60s timeout window.
Restructured each pill as a (sequenceIndex:0 emitter, narration
fallback) pair. The emitter matches the FIRST request for the pill
prompt (counter starts at 0) and emits the tool call; subsequent BIA
reprompt iterations fall through the now-exhausted emitter to the
narration fallback (no tool call), so the loop converges. sequenceIndex
is chosen over hasToolResult:false because hasToolResult is computed
across the entire thread — any earlier pill's tool result would
permanently disable a hasToolResult:false emitter, breaking multi-turn
sessions where turn 2+ would have hasToolResult=true globally.
The legacy bare 'AAPL' userMessage matchers in tool-rendering.json were
substrings of gen-ui-headless-complete's 'price of AAPL right now'
pill prompt, so when the BIA TanStack server-tool reprompt loop hit
iteration 2 (toolCallId chain broken by /v1/responses id rewriting),
the request fell through to tool-rendering.json's loose 'AAPL'
matchers and leaked wrong-card content. Tightened all four bare 'AAPL'
match keys to 'current price of AAPL' — preserves matching for the
tool-rendering 'Stock price' pill ("What's the current price of
AAPL?") and the custom-catchall probe's second prompt ("What's the
current price of AAPL?") while no longer matching the headless
probe's "What's the price of AAPL right now?".
## Problem
Asking for a sales dashboard in the beautiful-chat demo (reported on
`dojo.showcase.copilotkit.ai/?integration=langgraph-python&demo=beautiful-chat`)
fails with:
> A2UI render error: Catalog not found:
https://a2ui.org/specification/v0_9/basic_catalog.json
## Root cause
**Introduced by #5245** ("chore(showcase): upgrade langgraph A2UI to
stable releases + rewrite dynamic-schema docs", merged 2026-06-05): it
flipped the beautiful-chat routes to `injectA2UITool: true` — switching
them onto the runtime-injected `render_a2ui` tool — without adding the
`defaultCatalogId` the middleware contract expects the host to provide.
/cc @ranst91
The runtime-injected `render_a2ui` tool guide explicitly tells the model
**not** to send a `catalogId` ("the catalog id is set by the host, not
by you" — `@ag-ui/a2ui-middleware` `tools.ts`), and integrations whose
backend owns `generate_a2ui` see real models omit or late-stream the
argument. When the streamed `createSurface` is emitted without a
configured `defaultCatalogId`, the middleware falls back to the spec
basic catalog URL — which no showcase page registers, so the renderer
throws "Catalog not found".
The middleware contract expects the host to pin the catalog via
`a2ui.defaultCatalogId`; none of the showcase routes did. The pinned
`@ag-ui/a2ui-middleware@0.0.6` (resolved via
`@copilotkit/runtime@1.59.4` in the integration lockfiles) already
prefers the configured value over streamed args, so this is config-only.
## Fix
Add `defaultCatalogId` matching the catalog each page registers:
- 17 × `copilotkit-beautiful-chat` routes →
`copilotkit://app-dashboard-catalog` (every beautiful-chat page
registers this id)
- 14 × `copilotkit-declarative-gen-ui` routes →
`declarative-gen-ui-catalog` (all declarative catalogs use this id)
Left untouched: the 3 declarative routes with no `a2ui` block (ag2,
claude-sdk-typescript, strands) — `isA2UIEnabled` is false there, the
middleware never attaches, so the fallback path can't fire.
`copilotkit-a2ui-fixed-schema` routes are also untouched (direct tools
carry their catalog in the result envelope).
## Verification
- RUNBOOK-canonical D6 isolate run (`bin/showcase test langgraph-python
--d6 --isolate ...`) on this branch: **all A2UI pills green**
(`gen-ui-declarative`, `gen-ui-a2ui-fixed`, all five beautiful-chat
pills) — these stream through the middleware with the new
`defaultCatalogId` active (config preference). Aggregate green; the only
non-green features are pre-existing and unrelated (2×
`crypto.randomUUID` insecure-context in headless demos, 1× agent-config
fixture-length assertion).
- All 31 patched files parse-checked clean; full `tsc --noEmit` on
langgraph-python shows zero errors in the patched routes.
- Confirmed published middleware 0.0.6 (what the integration lockfiles
resolve) contains the host-preference logic for `defaultCatalogId`.
- Caveat: the Sales Dashboard pill itself could not be manually verified
end-to-end — its aimock fixture chain is broken by a pre-existing
fixture-shadowing collision from the `1e66a5f8d` fixture reorg (a
`userMessage: "First"` substring catch-all shadows the pill prompt;
three overlapping fixture chains mismatch on `toolCallId`
mid-conversation → `404 no_fixture_match` → `RUN_ERROR`). Broken
identically on `main`; tracked as a follow-up issue.
## Follow-ups (not in this PR)
- The canonical `examples/integrations/langgraph-python` route has the
same gap (`a2ui: { injectA2UITool: false }`, no `defaultCatalogId`,
pages register `copilotkit://app-dashboard-catalog`) — needs the same
fix through the parity sync flow.
- Test gap that let this ship: the D5 declarative fixture is a text-only
stub and beautiful-chat's D5 mapping reuses the agentic-chat
conversation, so no fixture exercises a `generate_a2ui` call that omits
`catalogId` the way real models do.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds a **React Native** SDK to the reference docs at
`/reference/react-native`, covering the full `@copilotkit/react-native`
public API. Modeled on the existing React/Core/Bots reference sections.
**Nav:** register `react-native` in `reference-items.ts`, add the SDK
selector label + an overview card.
**Content (22 pages, written from package source):** index, 8
components, 13 hooks. `CopilotChat` and `CopilotModal` exist in both a
headless (root) and prebuilt-UI (`/components`) form; each page
documents both, split by import path.
**Verified:** `next build` passes, all pages render, `oxlint` + `tsc`
clean, and content appears in `llms.txt` / `llms-full.txt`.
Out of scope per the issue: guide content and new demos.
---
## Preview
**Overview + SDK nav** — `/reference/react-native` (left sidebar shows
the new “React Native” SDK selector and all 8 components + 13 hooks)

**Component page — `CopilotChat`** —
`/reference/react-native/components/CopilotChat` (headless + prebuilt-UI
split by import path)

**Hook page — `useAgent`** — `/reference/react-native/hooks/useAgent`
(re-export callout, react-native code examples)
