## 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)

The injected/streamed a2ui fixtures all included catalogId, so aimock
replay never exercised the basic-catalog fallback that broke production
(real models omit catalogId per the tool-usage guide). Strip catalogId
from the langgraph-python sales-dashboard secondary-call fixtures and
hard-assert "Catalog not found" is absent outside the charts-rendered
soft branch, so the spec fails without a route defaultCatalogId.
Also repoint the on-demand e2e workflow at the d4/d5-recorded/d6/shared
fixture dirs — it still referenced feature-parity.json, deleted in the
1e66a5f8d fixture reorg, so every /test-aimock run died at aimock start.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a "React Native" SDK to the reference section, documenting the full
public surface of @copilotkit/react-native at /reference/react-native.
Navigation wiring:
- Register `react-native` in REFERENCE_VERSIONS / VERSION_SUBDIRS
- Add the "React Native" label to the SDK version selector
- Add a React Native card to the reference overview page
Content (22 pages, sourced from package source for accuracy):
- index: install, polyfills, provider setup, and the headless vs prebuilt
two-tier model
- components: CopilotKitProvider, CopilotChat, CopilotModal, CopilotSidebar,
CopilotPopup, CopilotMarkdown, AssistantMessage, UserMessage — disambiguating
the headless (root) and prebuilt-UI (/components) variants of CopilotChat and
CopilotModal by import path
- hooks: RN-specific useAttachments and useRenderTool, plus the platform-
agnostic hooks re-exported from react-core/v2 adapted to RN imports and
primitives (useAgent, useCopilotKit, useFrontendTool, useAgentContext,
useThreads, useCapabilities, useComponent, useHumanInTheLoop, useInterrupt,
useSuggestions, useConfigureSuggestions)
Content is picked up automatically by llms.txt / llms-full.txt via the
reference content walker.
OSS-250
The injected render_a2ui tool guide instructs models to omit catalogId
("the catalog id is set by the host"), and backend-owned generate_a2ui
tools see real models omit or late-stream it. Without defaultCatalogId
the a2ui middleware falls back to the spec basic catalog, which no
showcase page registers — surfaces fail with "Catalog not found:
https://a2ui.org/specification/v0_9/basic_catalog.json" (reported on
beautiful-chat / langgraph-python).
Pin each route to the catalog its page registers: beautiful-chat ->
copilotkit://app-dashboard-catalog, declarative-gen-ui ->
declarative-gen-ui-catalog. Routes with no a2ui block never attach the
middleware and are left untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## What does this PR do?
Fixes#5282.
Provider header updates already reach the core instance via
`setHeaders`, but React consumers of `useCopilotKit()` were only
re-rendered for runtime connection status changes. That left
`useThreads()` with stale context after the provider `headers` prop
changed, so subsequent `/threads` requests could miss headers such as
`X-CSRF`.
This subscribes the React context hook to `onHeadersChanged` and adds a
provider-level regression test that verifies `/threads` is refetched
with the updated header.
## Related PRs and Issues
- Fixes https://github.com/CopilotKit/CopilotKit/issues/5282
## Testing
- `pnpm install --frozen-lockfile`
- `pnpm --dir packages/react-core exec vitest run
src/v2/hooks/__tests__/use-threads-provider-headers.e2e.test.tsx`
(failed before the production change, passed after)
- `pnpm --dir packages/react-core exec vitest run
src/v2/hooks/__tests__/use-threads.test.tsx
src/v2/hooks/__tests__/use-threads-provider-headers.e2e.test.tsx`
- `pnpm exec oxlint packages/react-core/src/v2/context.ts
packages/react-core/src/v2/hooks/__tests__/use-threads-provider-headers.e2e.test.tsx`
- `pnpm exec oxfmt --check packages/react-core/src/v2/context.ts
packages/react-core/src/v2/hooks/__tests__/use-threads-provider-headers.e2e.test.tsx`
- `pnpm nx run @copilotkit/react-core:build`
- `pnpm nx run @copilotkit/react-core:test`
- `git commit -m "fix(react-core): refresh thread headers on provider
updates"` (pre-commit ran `pnpm run test && pnpm run check:packages`;
commit-msg ran `pnpm commitlint --edit`)
Also checked: `pnpm nx run @copilotkit/react-core:check-types` currently
fails before checking this package because dependency package type/build
errors are reported in `@copilotkit/shared` and
`@copilotkit/a2ui-renderer`; I did not change those packages.
Note: I used Codex while preparing this change, reviewed the final diff,
and ran the listed checks locally.
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation (not applicable: internal header propagation bug
fix)
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly - faster turnaround for everyone)
Addresses review feedback on #4215 / OSS-192.
- Thread an explicit `data-testid` prop through `AutoResizingTextarea` so it
reaches the rendered <textarea>. The component destructures a fixed prop set
with no `{...rest}` spread, so the id passed from Input.tsx was silently
dropped and never landed in the DOM.
- Align selector names with the V2 components in @copilotkit/react-core:
`copilot-chat-textarea` on the textarea and `copilot-send-button` on the send
control. The legacy `data-test-id` values are preserved for back-compat.
- Add a source-level test asserting the Input wiring and that Textarea forwards
the prop (the dropped-prop guard); the package's vitest runs in a node env
with no DOM harness, matching the existing testids.test.ts convention.
Stable selectors let tests locate the controls; the headless input-driving
issue in #4215 remains a separate follow-up and should stay open.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ports the usePinToSend half of #5386 to the Vue package, which is a
documented 1:1 parity port and retained the shrink-only ResizeObserver.
When content below the anchored user message loses height (suggestions
swap, input resize), the spacer now grows back so total scrollable space
below the bubble stays constant and the message stays pinned (#5355).
Vue has no scroll-to-bottom button, so the listener half of #5386 does
not apply.
## Symptom
Dashboard banner false alarm: *"Worker family E2E demos has not
completed successfully since 2h 40m ago"* (and the same on D5/D6) firing
while workers were healthy and emitting results every cycle.
## Triage (already done)
The D5/D6 family-silence banner fires because:
- `lastSuccessAt` is computed via `maxFinishedAtIso(completed)` where
`completed` requires the §5.2.1 `deriveOutcome` to return `"completed"`
(all jobs `done`, zero cell failures) — see
`showcase/harness/src/fleet/control-plane/run-view.ts` (pre-fix
`findCompletedBatch` + the `lastSuccessAt` line in `projectFamily`).
- With chronic content-reds present, every batch lands with
`jobs.done=1, jobs.failed=17` (cells failing inside the probes). The
outcome derives `"failed"`, no batch satisfies `outcome ===
"completed"`, and `findCompletedBatch` returns null.
- The family-silence monitor falls back to oldest-batch-`enqueuedAt` and
fires after `now − oldest > 3 × period` (see
`family-silence-monitor.ts`).
- The worker is fine: per-job `commError: null`, jobs completing with
real cell results, just `<100%` pass-rate.
This is a DEFINITIONAL bug. The worker is healthy; the definition of
"success" was conflating worker-completion with cells-all-green.
## The fix
Redefine "success" as **terminal-state accounting**, not pass-rate.
A batch counts as a terminal completion when:
1. Every job is in a terminal status (`done` or `failed`), AND
2. No job carries a `result.commError` (i.e. no worker-crashed-mid-job,
lease-expired, or other comm-level outage signal).
Cells red without a `commError` IS a terminal completion — the worker
reached the pool, ran the probe, and returned a result. Chronic content
reds become "known bad" instead of "stalled."
The strict §5.2.1 `outcome=="completed"` all-green semantic is unchanged
— it still drives `lastRun.outcome` and §6.2 dashboard rendering. No
external caller of run-view consumed the old strict `lastSuccessAt` for
logic beyond the banner and monitor (grep verified — every reference
outside this file is either a test fixture or the silence-banner display
surface), so renaming was unnecessary; only the definition was
redirected.
The fallback path in `family-silence-monitor.ts`
(oldest-batch-enqueuedAt when `lastSuccessAt` is null) is intentionally
**untouched** — with the new semantics, `lastSuccessAt` advances every
sweep on a healthy family, so the fallback only fires on true
never-completed envs (the rule it was designed for).
## Affected files
- `showcase/harness/src/fleet/control-plane/run-view.ts` — new
`isTerminalCompletionBatch` + `findTerminalCompletionBatch` predicates;
`projectFamily` now sources `lastSuccessAt` from terminal completion;
doc comment on the type updated.
- `showcase/harness/src/fleet/control-plane/run-view.test.ts` — 3 new
tests; 1 existing walk-back fixture updated to add a `commError` so its
original intent (skip non-completion batches) survives the new
predicate.
-
`showcase/harness/src/fleet/control-plane/family-silence-monitor.test.ts`
— 2 new tests: chronic-reds stays silent; real outage still fires.
- `showcase/harness/src/http/fleet-runs.test.ts` — T5 multi-page
walk-back fixture updated to flag its 20 "outage" batches with a
`commError` (so the walk-back still has to extend to page 2);
`batchFixture` gained an optional `resultFor` parameter.
## Tests (RED → GREEN)
Confirmed the 2 net-new predicate tests fail under the old
implementation and pass under the new:
- `lastSuccessAt advances when all jobs reach a terminal state with no
commError, even if cells failed` — old: `null`, new: newest finishedAt
- `lastSuccessAt does NOT count a batch where any job carries a
commError (worker-outage signal)` — old: `null` (walk-back to prior
worked but for the wrong reason — strict all-green semantics), new:
walks back past the comm-erroring batch to the terminal-completion batch
Negative cases preserved:
- `lastSuccessAt is null when every batch in the capped window has a
commError` — real outage stays loud
- `lastSuccessAt does NOT count a batch with non-terminal jobs` —
stall/abandon still excluded
- Family-silence monitor still alerts on real outages
Final test counts: harness `2700/2700` green, fleet/control-plane +
fleet-runs slice `355/355` green; format / lint / typecheck / build all
clean.
## Follow-up
Per the minimize-PRs directive, the D5+D6 banner remediation and
`redsCleared` semantic refresh are tracked separately in the coordinate
channel handoff — this PR is scoped to the `lastSuccessAt` definitional
fix.
## What does this PR do?
Fixes `pin-to-send` scrolling in the v2 chat view.
- Re-attaches the non-autoscroll scroll listener after the real scroll
element mounts by depending on `nonAutoScrollEl`, not the stable
`scrollRef` object.
- Lets the `usePinToSend` spacer adjust in both directions as content
below the pinned user message changes, so the user message stays
anchored after streaming finishes and layout height changes.
- Adds regression coverage for the scroll-to-bottom button and spacer
adjustment behavior.
## Related PRs and Issues
Fixes#5355
## Tests
- `corepack pnpm -C packages/react-core exec vitest run
src/v2/hooks/__tests__/use-pin-to-send.test.tsx
src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.tsx`
- `corepack pnpm exec oxfmt --check
packages/react-core/src/v2/components/chat/CopilotChatView.tsx
packages/react-core/src/v2/hooks/use-pin-to-send.ts
packages/react-core/src/v2/hooks/__tests__/use-pin-to-send.test.tsx
packages/react-core/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.tsx`
- `git diff --check`
Attempted:
- `corepack pnpm -C packages/react-core run check-types`
- This failed in the local workspace on existing/type-resolution issues
outside this diff, including `react-markdown` JSX namespace errors,
missing `@copilotkit/runtime-client-gql` declarations, and existing e2e
mock `AbstractAgent` private member mismatches.
## 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 (N/A: bug fix only, no API/docs change)
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
## Summary
Small followups to PR #5413 (BIA D6 readiness, merged 2026-06-12).
- **`PARITY_NOTES.md` NSF naming**: corrected `hitl` (which is supported
in BIA via Strategy-B `useFrontendTool`) to call out the actual
NSF-quarantined demos `gen-ui-interrupt` and `shared-state-streaming`
per the manifest. PR #5413's NSF banner commit (`3585c33b8`) mounted the
banners on the correct demos.
- **Sub-Agents TODO removed**: post-merge staging dashboard showed
sub-agents RED; diagnose confirmed code on `5e828aed9` is correct (local
`--direct` D6 PASSES). The staging RED was stale dashboard image /
fixture cache, not code. PARITY_NOTES no longer claims sub-agents is a
known-issue.
- **gen-ui-agent STATE_DELTA op fix**: BIA's `set_steps` tool emission
used RFC-6902 `op: "replace"` on path `/steps`, but the agent's initial
state is `{}` and `fast-json-patch` rejects unresolvable paths in strict
mode (`@ag-ui/client@0.0.57` swallows the throw with `console.warn`).
Changing to `op: "add"` creates the path on first emission and
idempotently overwrites on subsequent calls. Single-line fix in
`tanstack-factory.ts`.
## Out-of-scope (tracked separately)
- **a2ui-fixed-schema + declarative-gen-ui** (PR #5413 documented
known-issues): root cause is in `@copilotkit/runtime/v2` v2-stack
pipeline OR `@ag-ui/a2ui-middleware@0.0.8` JSON.parse gap — one upstream
fix likely unblocks both. Diagnose reports at
`/tmp/cr/bia-rxr-diagnose/{a2ui-fixed-schema,declarative-gen-ui}.md`.
Needs separate packages PR.
- **tool-rendering port + headless-complete missing components**: BIA
needs StockCard, ChartCard, `get_revenue_chart` server tool, UI
primitives. Separate component-port PR.
- **shadcn catchall**: product/design call pending (escalate to PM).
## Test plan
- [x] Local `--direct` D6 verify on `gen-ui-agent` after the JSON-Patch
op fix
- [x] CI green on PR
Chronic content-reds in the D5/D6 worker families left `lastSuccessAt`
pinned to null on /api/runs, which tripped the dashboard "worker family X
has not completed successfully since Yh ago" silence banner even though
the workers were healthy. Each batch landed with jobs.done=1,
jobs.failed=17 (cells failing inside the probes), the §5.2.1 outcome
precedence derived "failed", findCompletedBatch returned null, and the
§9 family-silence monitor fell back to oldest-batch-enqueuedAt and fired
after 3 x period.
The fix is definitional: a batch counts as a "terminal completion" for
lastSuccessAt purposes when every job reaches a terminal state (done |
failed) with no `result.commError` on any row. Cell-level reds (rollup
counts) no longer block the timestamp; only worker-level outages
(crashed / reclaimed / lease-expired, surfaced via commError) do.
The strict §5.2.1 outcome=="completed" all-green semantic is unchanged
(it still drives lastRun.outcome and §6.2 dashboard rendering). No
caller of run-view consumed the old strict lastSuccessAt for logic
beyond the banner and monitor (grep verified — every external reference
is either a test fixture or the silence-banner display surface).
Tests added (RED -> GREEN):
- run-view.test.ts: lastSuccessAt advances when all jobs reach a terminal
state with no commError, even if cells failed (the regression case)
- run-view.test.ts: lastSuccessAt does NOT count a batch where any job
carries a commError (worker-outage signal stays loud)
- run-view.test.ts: lastSuccessAt does NOT count a batch with non-terminal
jobs (stalled / abandoned still excluded)
- family-silence-monitor.test.ts: does not alert when the family runs every
cycle with cell-level reds but no commError
- family-silence-monitor.test.ts: still alerts when the family stops
emitting results entirely (real outage)
Two existing tests updated to add commError to their "no completion"
fixtures so their original intent (walk-back across non-completion
batches) survives the new predicate:
- run-view.test.ts: lastSuccessAt walk-back through a commError batch
- fleet-runs.test.ts: T5 multi-page walk-back coverage