Bumps the bare `.reference-content pre` rule from 0.5rem to 0.875rem
border-radius and adds a `shadow-sm` drop shadow to match canonical
docs.copilotkit.ai chrome. Updates the `<Snippet>` figure wrapper from
`rounded-lg` to `rounded-xl` plus `shadow-sm` so MDX-mounted snippets
share the same visual treatment.
Folds in four low-coupling visual slots that all sit on the canonical
color tokens shipped earlier on this branch:
- Typography: page H1 to 40px / 500 / line-height 48px (was 32px / 700);
H2 to 600 weight with normal letter-spacing (was 700 / -0.015em); drop
the Spline Sans Mono Google Font and fall back to system mono, matching
what canonical docs.copilotkit.ai now ships.
- Callouts: rewrite the boxed admonition card to the canonical pattern —
white surface (`--bg-surface`) with shadow-md, full 1px border, a thin
inner left-strip in the type color at 50% opacity, and a lucide SVG
glyph instead of the round-badge letter. Brand violet for "info"
matches the synced accent.
- Tables: bump font to 14px (was 13px), switch from per-cell borders to
row-bottom-only borders, and pad to 10px / 12px per the canonical
data-table pattern.
- Content max-width: relax `max-w-3xl` (768px) to `max-w-[860px]` so long
prose lines aren't visibly cramped.
Legacy `--violet` and `--violet-light` tokens were left at the pre-sync
shell values (#5a3cd1, #ebe7fa) when the canonical accent moved to
#6d45f9 / #ede5ff. They're consumed by AG-UI section pages and
OpsPlatformCTA, both of which should track the brand violet.
Re-alias to the canonical accent values so the AG-UI pages and the CTA
match the rest of the docs visually.
Per visual styling audit slot 1: align shell-docs CSS custom properties
with the canonical docs.copilotkit.ai palette. Updates --accent to the
brand violet (#6d45f9) with derived --accent-light and --accent-dim,
shifts --bg to the canonical cool-gray (#dedee9), and introduces a new
--glass-background token (the previous --bg value) reserved for header
and sidebar glass-panel surfaces consumed by later slots.
## Summary
Three small pre-cutover cleanup fixes batched into one PR (consolidation
of the prior #4680, #4681, #4682 — same content, fewer review queues).
- `b3ec38720` — Add `<WhenFrameworkHas absent>` fallback to
`programmatic-control.mdx` for the 7 frameworks without
`interrupt_pattern` (ag2, agno, built-in-agent, crewai-crews,
google-adk, mastra, spring-ai), mirroring the canonical pattern from PR
#4496.
- `c080ca062` — Retarget `/migrate/1.10.X` redirect destination from
`/migrate` (which 404s) to `/migrate/v2`. `permanent: false` preserved.
- `e61e8c36d` — Add `mcp-server-setup.mdx` exclusion to docs sync
script. Shell-docs version is intentionally ahead of upstream (HTTP/SSE
Tabs + `mcp-remote` + Tadata callout); without exclusion the next sync
would clobber it.
Supersedes #4680, #4681, #4682.
## Test plan
- [ ] `nx run shell-docs:dev` and visit `/programmatic-control` while
switching the framework selector to non-native fws — verify fallback
callout renders and links to `/human-in-the-loop`.
- [ ] Visit `/migrate/1.10.X` — should redirect to `/migrate/v2` and
render the V2 migration page.
- [ ] Inspect `showcase/scripts/sync-docs-from-main.ts` PATH_EXCLUSIONS
for the `mcp-server-setup` regex.
Bump @langchain/langgraph-cli from 1.2.1 to 2.0.0 in the langgraph-js
starter and showcase langgraph-typescript integration. The transitive
dep @langchain/langgraph-api@1.2.1 imports STREAM_EVENTS_V3_MODES from
@langchain/langgraph/web, but that symbol was never published in any
release of @langchain/langgraph (including 1.3.0 currently pinned).
langgraph-api@2.0.0 drops the broken import entirely.
The shell-docs Docker build fails because link-to-copilot-cloud.tsx
imports from lucide-react but the package was never added to
shell-docs/package.json. Adding it at ^0.469.0 to match sibling
showcase packages.
Two bugs in the Reo init <Script> in layout.tsx:
- If NEXT_PUBLIC_REO_KEY was unset, the script still ran with
e = "undefined" and fetched https://static.reo.dev/undefined/reo.js.
Sibling REB2B_KEY was correctly gated; Reo now matches.
- Bare interpolation into dangerouslySetInnerHTML — any future env
value containing " or </script> would break out of the inline
literal. Switches to JSON.stringify(REO_KEY) and reuses the
resulting binding inside Reo.init() so the key only appears once.
Pulls the dashboard.operations.copilotkit.ai href + UTM string back
into CLOUD_CTA so the desktop and mobile placements share a single
source of truth. UTM tweaks become a one-line change instead of two.
Two review fixes for framework-selector.tsx:
- Replaces the `posthog-js` singleton import with `usePostHog()` from
`posthog-js/react`, matching the rest of the codebase. Both work after
posthog.init() runs, but the singleton was an outlier.
- Renames the event from `framework_selected` to `docs.framework_selected`
to match the dotted-prefix convention used elsewhere in PostHog
Insights (oss.* / cloud.* / eip.* / pricing.cta_clicked). Cheap to do
now since the event is brand-new with no historical data.
The suppressor was carried over from the original telemetry-stack port
of upstream's posthog-provider.tsx. Its job was masking
ERR_BLOCKED_BY_CONTENT_BLOCKER noise from PostHog requests being
blocked by ad blockers targeting *.i.posthog.com directly.
This PR ships the /ingest reverse proxy (mirroring upstream #4554), so
PostHog calls now flow through the docs host and the underlying
hostname is invisible to ad blockers. With nothing to suppress, the
mask only hid real errors that mention "posthog". Three concrete
problems on top of the dead-code framing:
- Substring match on "posthog" swallows unrelated messages.
- isInitializedRef.current only guards the same component instance,
so HMR or StrictMode remounts re-capture the already-patched
console.error and layer wrappers on each remount.
- originalLog is captured but console.log is never patched.
Resolves PDX-112.
Mirrors upstream #4646 in two changes from the same PR:
- Drop the duplicate useRB2B hook in favor of the canonical
<Script id="reb2b-script"> in app/layout.tsx (gated on REB2B_KEY).
Env var renames from NEXT_PUBLIC_RB2B_ID (the hook's name) to
NEXT_PUBLIC_REB2B_KEY (upstream's canonical name) — deployment
ops will need updating.
- Set capture_dead_clicks: false on PostHog init so the
dead-clicks-autocapture.js bundle isn't pulled in on the critical
path.
Mirrors upstream #4643 + #4646 follow-ups. New trackCommandCopy helper
infers install_type from the leading token (covers npx/pnpm/pip/uv/
docker/curl/brew/helm/kubectl/make/bash/sh, falls back to "code"), and
the CopyTracker provider monkey-patches navigator.clipboard.writeText
once at app boot so every programmatic copy fires cli_command_copied
without per-component instrumentation. Preserves upstream's chained-
wrapper pattern so it coexists with Reo's writeText patch.
Final event shape is { install_type, location? } — the command body
and product props from the original draft were dropped upstream
(commit 0b7b3c77c) before merge.
Adds a Talk to Our Engineers CTA to the right side of the docs nav at
viewports >= 1400px and surfaces it in the mobile burger menu below
that. Fires posthog talk_to_us_clicked with { location: "docs_nav" }
on click, then navigates to copilotkit.ai/contact-us. Mirrors the
event the docs/ navbar fires so docs-originated clicks can be
segmented separately from website clicks (which use "nav").
Routes PostHog analytics through /ingest/* (rewrites to eu.i.posthog.com)
so requests bypass ad blockers and tracking-protection that target the
*.i.posthog.com hostname directly. Hardcodes POSTHOG_HOST = "/ingest"
in the provider, points ui_host at https://eu.posthog.com so PostHog UI
links still resolve, and excludes /ingest from the middleware matcher
so the proxy itself doesn't fire phantom pageviews. Mirrors the same
fix in docs/.
Sweeps 30 hardcoded https://docs.copilotkit.ai/* self-references across
15 mdx files in src/content/ to relative paths so they don't redirect-loop
once docs.copilotkit.ai cuts over to shell-docs.
Adds a Free Developer Access CTA to the brand nav (desktop + mobile
menu) that points at dashboard.operations.copilotkit.ai with UTM tags
and fires try_for_free_clicked with { location } on click.
Distinguishes docs_navbar (desktop) from docs_navbar_mobile so the
funnel can split the two layouts.
Also adds the LinkToCopilotCloud component for use by MDX content
references that deep-link into Copilot Cloud — those remain on
cloud.copilotkit.ai.
Fires framework_selected when a user picks a framework so the docs
funnel can attribute drop-off to specific frameworks. Net-new event
(no upstream equivalent in docs/).
Adds app/og/[...slug]/route.tsx for per-page social-share image
generation, and a vercel.json with maxDuration: 60 on app/og/** so
the Edge function has enough headroom to render OG cards.
Brings PostHog, GA4, HubSpot, Reo.dev, Scarf, and RB2B into shell-docs
with parity to docs/. Adds the client-side PostHog provider with
session-stitched bootstrap and pageview capture, the AnalyticsClient
wrapper that mounts RB2B + GA4 hooks behind a single client boundary,
the Scarf pixel for OSS attribution, and the HubSpot and Reo.dev
scripts.
Renames POSTHOG_PROJECT_KEY to POSTHOG_KEY across shell and shell-docs
middlewares so the env names match the upstream pattern, and env-drives
POSTHOG_HOST with eu.i.posthog.com as the fallback.
The aggregated multi-turn probe from #4672 hit a CopilotKit v2 quirk on
/demos/beautiful-chat: only the FIRST useComponent tool call in a
conversation paints its component. Subsequent tool calls emit (the
agent's followup content arrives) but the component never mounts.
Reproduced cleanly without any frontend tool involvement —
pie-chart turn 1 paints 5 svg circles in seconds, bar-chart turn 2
emits "Bar chart rendered above..." but paints zero recharts elements.
The runner can't sidestep this from inside one conversation without a
page.reload() between turns, which the structural Page type doesn't
expose. Splitting into per-pill scripts means each probe gets its own
browser launch — fresh page state, fresh conversation, no useComponent
ordering pollution. CATALOG_TO_D5_KEY maps `beautiful-chat` to all
listed literals; isD5Green requires every key green for the cell to
advance to D5, and per-pill failure isolation surfaces in PB row names.
Coverage in this PR (5 pills):
- beautiful-chat-toggle-theme (frontend tool, html.dark flip)
- beautiful-chat-pie-chart (controlled gen-UI useComponent)
- beautiful-chat-bar-chart (controlled gen-UI useComponent)
- beautiful-chat-search-flights (A2UI fixed-schema FlightCards)
- beautiful-chat-schedule-meeting (HITL with slot-click resolution)
All 5 verified locally end-to-end (5/5 pass against the local stack).
Out of scope, intentionally (track in follow-up):
- Excalidraw — depends on mcp.excalidraw.com reachability
- Calculator — sandboxed iframe; dup of d5-gen-ui-open
- Sales Dashboard — generate_a2ui → render_a2ui chain renders
Metric labels but Row-bound charts don't paint
recharts containers under aimock fixtures (live
pill against same fixture chain shows the
inverse symptom). Suggests aimock's
non-progressive arg streaming differs from a
live LLM in a way the A2UI binder is sensitive
to. Needs separate aimock/binder investigation.
- Task Manager — manage_todos dispatches and agent emits closing
content, but StateStreamingMiddleware's
state.todos propagation doesn't populate the
App pane TodoList through aimock — same suspected
root cause as Sales Dashboard.
Architecture details:
- _beautiful-chat-shared.ts factors DOM helpers + per-pill
assertions, mirroring _hitl-shared.ts's pattern for an extended
Page type with click() + a runtime guard
- Each fixture uses unique D5-prefixed userMessage substrings; the
multi-stage Schedule Meeting flow uses hasToolResult false→true
for round disambiguation (no toolCallId leakage since each probe
runs in its own fresh page session)
## Summary
- **Dashboard mapping fix.** `CATALOG_TO_D5_KEY` in
`showcase/shell-dashboard/src/lib/live-status.ts` was missing `voice →
["voice"]`, so `computeMaxPossible` capped the langgraph-python voice
cell at D4 even when the d5-voice probe row was green. The harness
`REGISTRY_TO_D5` already had the entry; only the dashboard mirror was
out of sync.
- **Sample-button decoupled from `/transcribe`.** The "Play sample"
button used to fetch `sample.wav` and POST it to the runtime's
transcription endpoint, which made the sample button and the mic
indistinguishable under aimock (both returned the canned transcription).
Reworked it into a synchronous static-text injector — sample button is
now a deterministic test/demo affordance, and the mic is the only path
that exercises real Whisper transcription. Synced across all 18
voice-enabled integrations. Phrase stays `"What is the weather in
Tokyo?"` so aimock's `weather in Tokyo` substring fixture still matches.
- **Probe-test parity.** Added the missing `d5-voice.test.ts` companion
(every other `d5-*.ts` script has one) — 9 tests covering registration,
`buildTurns`, `preFill` (sample-button click + textarea-poll path), and
the weather/Tokyo assertion.
- **QA + e2e cleanup** for langgraph-python: dropped the
no-longer-applicable "Transcribing…" mid-flight assertion and the `block
/demo-audio/sample.wav` error-state subsection. Other 16 integrations'
qa/e2e files follow in a parity sync PR.
## Test plan
- [x] `nx test @copilotkit/showcase-harness -- --run d5-voice` → 9/9
pass
- [x] `npm test` in `showcase/shell-dashboard` → 509/510 pass (1
skipped, 0 failed)
- [x] `nx build @copilotkit/showcase-harness` → clean
- [x] Local boot: `langgraph-cli dev` (port 8123) + `next dev` (port
3000) + dashboard (port 3002) — voice page at `/demos/voice` renders,
"Play sample" injects the canned phrase instantly, send → agent returns
weather, mic → real Whisper transcription with `OPENAI_API_KEY` set
- [ ] Reviewer: confirm the langgraph-python voice cell on the live
dashboard advances to D5 once the next d5-voice probe tick lands a green
row
The langgraph-python voice cell sat at D4 even when its d5-voice probe
row was green. Root cause: the dashboard's CATALOG_TO_D5_KEY mirror in
showcase/shell-dashboard/src/lib/live-status.ts was missing voice ->
["voice"], so computeMaxPossible capped voice at D4 regardless of probe
state. The harness REGISTRY_TO_D5 already had the entry; only the
dashboard mirror was out of sync.
Separately, the "Play sample" button used to fetch sample.wav and POST
it to /transcribe. With aimock that meant both the sample button AND
the mic returned the same canned response, which made it impossible to
demo the mic path locally without conflating the two affordances.
Reworked the button into a synchronous static-text injector
(onTranscribed(sampleText)) so:
- Sample button = deterministic test/demo affordance, no runtime calls.
- Mic = real Whisper transcription via /transcribe.
Synced across all 18 voice-enabled integrations. Phrase stays "What is
the weather in Tokyo?" so aimock's "weather in Tokyo" substring fixture
still matches.
Also adds the missing d5-voice.test.ts companion (every other d5-* probe
script has one) and trims the langgraph-python qa/voice.md + e2e steps
that depended on the now-removed async behavior.
## Summary
Brings the docs→dashboard sign-up funnel to shell-docs and pulls the
threads documentation stack into parity with upstream. Mirrors PRs
[#4592](https://github.com/CopilotKit/CopilotKit/pull/4592) (Wave 1, 30+
placements), [#4642](https://github.com/CopilotKit/CopilotKit/pull/4642)
(rebrand + threads restoration), and follow-up commit `0ada76b9a` (CTAs
on threads pages). Lands in four commits:
1. **Port the `OpsPlatformCTA` component** to
`showcase/shell-docs/src/components/react/ops-platform-cta.tsx` and wire
it into the shared MDX registry.
2. **Place 22 `<OpsPlatformCTA>` instances** across shell-docs MDX pages
with stable surface identifiers that match upstream's, so PostHog click
attribution stays continuous across the docs.copilotkit.ai cutover.
3. **Rebrand "CopilotKit platform" → "Enterprise Intelligence
Platform"** in shell-docs prose, mirroring the docs/ rebrand.
4. **Add useThreads at the canonical reference path** + threads-stack
content parity (see "Threads parity" below).
## Adaptations from upstream
- **Visual tokens** — swapped indigo/purple Tailwind utilities for
`--accent` / `--violet-light` / `--border` / `--text*` CSS variables
already used by the rest of shell-docs.
- **No dark mode** — dropped `dark:` modifiers; shell-docs has no dark
theme yet.
- **No lucide-react** — inlined `ArrowRight`, `Info`, and `Sparkles` as
SVG components.
- **No client-side PostHog** — shell-docs ships server-side PostHog
(middleware) only and has no `posthog-js`. UTM params on the dashboard
URL (`utm_source=docs`, `utm_medium=cta`, `utm_campaign=intelligence`,
`utm_content={surface}`) are the source of truth for click attribution.
- **Inline-style overrides** — `text-decoration: none` on the card/tile
wrappers and explicit `color` on the inline-button text and info-link to
defeat shell-docs's `.reference-content a` rule.
## Placement coverage
22 placements: 18 direct ports of upstream surfaces, 3 path-adjusted,
and 1 structural.
**Direct (18):** `inspector`, `shared-state`, `threads`,
`troubleshooting/event-inspector`; integration quickstarts for `a2a`,
`ag2`, `agent-spec`, `built-in-agent` (overview + quickstart),
`crewai-flows`, `deepagents`; `langgraph/prebuilt-components`; the four
shared snippets (`basics/prebuilt-components`, `premium/headless-ui`,
`premium/observability`, `premium/overview`); the shared
`threads/threads` snippet; the canonical `reference/hooks/useThreads`.
**Path-adjusted (3):** `learn/intelligence-platform` →
`premium/intelligence-platform`; `learn/threads` →
`premium/threads-explained`; `learn/tutorials/multi-conversation-chat` →
`tutorials/multi-conversation-chat`.
**Structural (1):** root `prebuilt-components.mdx` →
`prebuilt-components/index.mdx`.
**Skipped:** `quickstart.mdx` (root, routing shim), `(root)/index.mdx`
(no MDX docs landing), per-framework integration `index.mdx` files (no
FrameworkOverview equivalent),
`reference/v2/components/{CopilotChat,CopilotKit}.mdx` and
`reference/v2/index.mdx` (different layout).
## Routing note
A few placement targets are MDX files that shell-docs doesn't currently
expose at a public URL — they're orphans pending a registered framework
slug. Specifically: `agent-spec/quickstart.mdx`,
`deepagents/quickstart.mdx`, `a2a/quickstart.mdx`. Mirroring the
upstream placement here is defensive; tracked as part of the broader IA
gap work in PDX-84.
## Rebrand sweep
11 files updated, 50 occurrences collapsed into the canonical name.
Patterns: `CopilotKit platform` → `Enterprise Intelligence Platform`;
`CopilotKit Intelligence Platform` → `Enterprise Intelligence Platform`;
`Intelligence Platform` (standalone) → `Enterprise Intelligence
Platform`. Also fixes two `a Enterprise` → `an Enterprise` article
typos. URL slugs (`/learn/intelligence-platform`,
`/premium/intelligence-platform`) intentionally untouched.
## Threads parity
The previous version of shell-docs had `useThreads.mdx` at
`src/content/docs/reference/v2/hooks/useThreads.mdx`, but the canonical
reference renderer reads from `src/content/reference/`, not
`src/content/docs/reference/`. Result: `/reference/hooks/useThreads`
404'd while upstream's docs had a working useThreads page for weeks.
This PR moves useThreads to the canonical path so it resolves, and pulls
the rest of the threads stack into content parity with upstream:
- `OpsPlatformCTA` added to the reference page's component map so hook
reference pages can host sign-up CTAs (used here on useThreads itself).
- useThreads recreated at `src/content/reference/hooks/useThreads.mdx`.
Drops the `<ThreadsEarlyAccess>` wrapper (canonical reference dir
doesn't use it — threads is GA) and the `doc_type` frontmatter field
(canonical hooks use plain `title` + `description` only). Keeps the
local `lastRunAt` PropertyReference (deliberately preserved per commit
`9f18b50f0`) and the new CTA placement.
- Cross-links to useThreads in `threads.mdx` and the shared snippet
repointed at `/reference/hooks/useThreads` (canonical) instead of
`/reference/v2/hooks/useThreads` (404).
- Prerequisite version bumped from `@copilotkit/react-core v1.50+` to
`v1.56+` in `threads.mdx` and the shared snippet, matching upstream.
Page-structure differences from upstream are preserved per shell-docs
IA: `learn/threads` stays `premium/threads-explained`,
`learn/intelligence-platform` stays `premium/intelligence-platform`,
`learn/tutorials/...` stays `tutorials/...`.
## Test plan
- [ ] `/inspector`, `/threads`, `/shared-state`,
`/troubleshooting/event-inspector` render the inline CTA above the page
content
- [ ] `/built-in-agent`, `/built-in-agent/quickstart`,
`/ag2/quickstart`, `/crewai-crews/quickstart` render the card CTA above
the prerequisites/video, with no underlines on the title/body and white
button text on the violet button
- [ ] `/langgraph-python/prebuilt-components` renders the inline CTA
above the IframeSwitcher
- [ ] `/premium/intelligence-platform` and `/premium/threads-explained`
render their CTAs between the intro paragraph and the first concept
section
- [ ] `/tutorials/multi-conversation-chat` renders its CTA inside the
early-access wrapper
- [ ] `/prebuilt-components` renders the inline CTA above "Pre-built
components for agentic chat"
- [ ] `/reference/hooks/useThreads` resolves (no 404) and renders the
card CTA above "## Overview", with the `lastRunAt` PropertyReference
still present in the Return Value section
- [ ] CTA buttons all link to
`dashboard.operations.copilotkit.ai/?utm_source=docs&utm_medium=cta&utm_campaign=intelligence&utm_content={surface}`
with the correct surface ID
- [ ] Pages that mention the platform brand consistently say "Enterprise
Intelligence Platform" — no remaining "CopilotKit platform" /
"CopilotKit Intelligence Platform" / bare "Intelligence Platform"
- [ ] `/threads` "Next steps" → "API reference" link goes to the working
`/reference/hooks/useThreads` (not the 404
`/reference/v2/hooks/useThreads`)
The dashboard image was being built with only `OPS_BASE_URL` set; the
two other compile-time env vars used by the matrix view stayed unset:
- NEXT_PUBLIC_POCKETBASE_URL — left at the dev fallback in production
builds, which surfaces a "FATAL-CONFIG: NEXT_PUBLIC_POCKETBASE_URL
was unset at build time" banner in the browser console and causes
the matrix's PB SSE subscription to fail (no live updates).
- NEXT_PUBLIC_SHELL_URL — left unset, so every Demo / Code link in
the matrix renders as `about:blank#shell-url-missing`.
Pin both at build time:
- POCKETBASE_URL → http://localhost:8090 (host port the user's browser
reaches; container-internal `pocketbase:8090` is unresolvable from
outside the compose network).
- SHELL_URL → http://localhost:3100 (langgraph-python integration
host port; no shared "shell" service in the local stack).
PocketBase 0.22's email validator rejects `admin@localhost` (single-
label TLD) with `validation_is_email: Must be a valid email address`,
breaking PB auth from the harness CLI on a fresh local stack —
`pb-write-failed` / `pb-auth failed: 400` on every --live run.
Switch the hardcoded local creds to `admin@localhost.dev`. The
matching superuser must be created with the running server's data
directory pinned: `pocketbase admin create admin@localhost.dev
showcase-local-dev --dir=/pb_data` (the create command's default
`./pb_data` differs from the server's `--dir=/pb_data`, so the record
otherwise lands in a different SQLite file and the running server
can't see it).
`buildSmokeInputs` was passing `name: manifest.name` (the display name,
e.g. "LangGraph (Python)") which the liveness driver's `deriveSlug`
stripped of a leading `showcase-` prefix and used as the slug —
producing rows like `health:LangGraph (Python)` and
`agent:LangGraph (Python)` that didn't join with anything else on the
dashboard's depth ladder.
Production discovery sets `name` to the Railway service name
(`showcase-<slug>`), which strips cleanly to the slug. Mirror that
shape from the local CLI so all rows for a service share the same
keyspace and the dashboard cell can advance past D0.
The CLI's runner.ts created a `pbWriter` when --live was passed but
never plumbed it into `ctx.writer`, so every per-feature side row from
every driver (e2e-deep's `d5:<slug>/<featureType>`, liveness's
`health:<slug>` / `agent:<slug>`, etc.) was silently dropped. Only
the aggregate primary results made it to PocketBase, which is why the
dashboard matrix showed every cell as D0 even after a successful
--live probe run.
Now `ctx.writer = pbWriter` when --live is on; without --live the
field is omitted, matching the legacy "no side emission" behavior.
Promotes /demos/headless-complete to its own D5 feature type so the
dashboard cell can reach D5 instead of riding on the headless-simple
probe (which was navigating to /demos/headless-simple regardless of
which catalog feature triggered it).
- New gen-ui-headless-complete D5 feature type + script that clicks
each suggestion chip via preFill and asserts the right surface
renders: WeatherCard (get_weather), StockCard (get_stock_price),
HighlightNote (frontend useComponent), Excalidraw best-effort, and
the canonical "Asia is the largest continent" text reply.
- Existing gen-ui-headless script now drives both turns by chip
click (Profile card + Largest continent) instead of typing.
- Fixtures pin narration legs with both userMessage AND toolCallId
and order them before the bare userMessage toolCall fixture —
aimock's toolCallId matcher reads the LAST tool message in the
request, but in a multi-turn probe that "last tool" stays on a
previous turn's id until a new tool runs, which would otherwise
hijack a later turn's prompt with a stale narration.
- headless-complete UserBubble + AssistantBubble now carry
data-message-role so the harness conversation runner can detect
message arrivals (mirrors the headless-simple convention).
- Mappings updated in lockstep:
- REGISTRY_TO_D5: headless-complete -> ["gen-ui-headless-complete"]
- CATALOG_TO_D5_KEY (dashboard): same.
Builds on the initial Search Flights-only probe by extending to a
multi-turn ConversationTurn[] that asserts surface-specific render
fingerprints for 7 of the 9 pills in /demos/beautiful-chat.
Pills covered (turn order is load-bearing — see script jsdoc):
1. Toggle Theme — html.dark class flip (toggleTheme frontend tool)
2. Pie Chart — >= 3 svg circles (pieChart useComponent)
3. Bar Chart — recharts container + >= 2 bar rectangles
4. Search Flights — A2UI FlightCard literal fingerprints (#4668 path)
5. Schedule Meeting — MeetingTimePicker mounts; assertion CLICKS a slot
to resolve the HITL pause before subsequent turns
6. Sales Dashboard — A2UI dynamic generate_a2ui → secondary render_a2ui;
"Total Revenue" + recharts container (90s budget)
7. Task Manager — enableAppMode + manage_todos; "To Do" column +
canonical todo title visible. MUST be last
(flips layout, breaks chat input on narrow widths)
Pills intentionally skipped (track in follow-up):
- Excalidraw Diagram — depends on mcp.excalidraw.com reachability,
turning D5 reliability into a 3rd-party uptime bet
- Calculator App — sandboxed iframe makes assertions cross-frame-fragile;
generateSandboxedUi already covered by d5-gen-ui-open
on a different demo route (would duplicate coverage)
Probe uses the runner's structural ConversationPage type for most assertions
(via page.evaluate helpers in the d5-chat-css globalThis-cast pattern, which
keeps the harness's Node-only tsconfig clean of DOM lib types). Schedule
Meeting narrows to an extended Page type with click() — same runtime-guarded
cast used in d5-hitl-text-input — so it can dispatch the HITL slot click.
Fixture file expanded from 2 to 15 entries covering all 7 turns plus their
multi-stage chains (Sales Dashboard primary + secondary LLM + post-tool;
Task Manager enableAppMode → manage_todos → narration). Re-bundled into
showcase/aimock/d5-all.json (additions only — no other fixtures changed).
Conversation runner reports failure_turn so per-pill failure isolation is
preserved on the dashboard's drilldown without needing 7 separate D5 literals.
Beautiful Chat was capped at D4 in the dashboard because it had no
dedicated D5 probe and was deliberately excluded from CATALOG_TO_D5_KEY
(commit 974494ecb stripped the freeloading "agentic-chat" alias). PR
#4668 fixed the A2UI surface rendering and added e2e tests, but those
land at the D3 tier — D5 is a separate probe with its own driver.
Changes:
- New d5-beautiful-chat probe asserts the A2UI fixed-schema FlightCard
surface renders with literal United/Delta/$349/$289 fingerprints from
the search_flights tool. 60s budget on first card, 5s on siblings.
- New harness/fixtures/d5/beautiful-chat.json with two-stage fixture
(hasToolResult false→true) mirroring the gen-ui-headless pattern.
Fixture spliced into the bundled aimock/d5-all.json.
- New "beautiful-chat" D5FeatureType literal in the registry's union +
runtime mirror.
- d5-feature-mapping.ts: replace "beautiful-chat": ["agentic-chat"]
alias with ["beautiful-chat"] so the probe targets its own dedicated
PB key instead of freeloading agentic-chat's green status.
- live-status.ts CATALOG_TO_D5_KEY: re-add "beautiful-chat":
["beautiful-chat"] so computeMaxPossible lifts the D4 cap to D5.
Three CTA `body` strings rendered "publicLicenseKey" as plain prose,
which reads as code (camelCase identifier) where the surrounding
sentence is conversational. Switches to "public license key" — the
phrase, not the prop name. Code samples and reference docs that
reference the actual `publicLicenseKey` prop are unchanged.
The useThreads page existed in shell-docs at
\`src/content/docs/reference/v2/hooks/useThreads.mdx\` but that tree
isn't routed — the canonical reference renderer reads from
\`src/content/reference/\`, not \`src/content/docs/reference/\`. As a
result \`/reference/hooks/useThreads\` 404'd while the upstream docs
have had a working useThreads page for weeks.
Moves the page to the canonical location so it resolves at
\`/reference/hooks/useThreads\`, and pulls the rest of the threads
content stack into parity with upstream:
- Add \`OpsPlatformCTA\` to the reference renderer's component map so
hook reference pages can host sign-up CTAs (used here on useThreads
itself).
- Recreate useThreads at the canonical path. Drops the
\`<ThreadsEarlyAccess>\` wrapper (canonical reference dir doesn't use
it — threads is GA, no banner) and the \`doc_type: reference\`
frontmatter field (canonical hooks use plain title + description).
Keeps the local additions: \`<OpsPlatformCTA>\` placement and the
\`lastRunAt\` PropertyReference (deliberately kept per commit
9f18b50f0).
- Fix cross-links to useThreads in threads.mdx and the shared snippet
to point at the canonical \`/reference/hooks/useThreads\` instead of
the broken \`/reference/v2/hooks/useThreads\`.
- Bump the prerequisite from \`@copilotkit/react-core v1.50+\` to
\`v1.56+\` in threads.mdx and the shared snippet to match upstream.
Out of scope: the broader \`/reference/v2/hooks/\<Name\>\` broken-link
problem across other shell-docs pages (useFrontendTool, useAgent,
etc. — same dirname mismatch but for hooks unrelated to this PR).
That's tracked under PDX-103 / PDX-84.
Page-structure differences from upstream are preserved per the
shell-docs IA decision to drop the \`learn/\` concept: \`learn/threads\`
stays \`premium/threads-explained\`, \`learn/intelligence-platform\`
stays \`premium/intelligence-platform\`, \`learn/tutorials/...\` stays
\`tutorials/...\`.
Mirrors the placements upstream docs/ shipped in PR #4592 (Wave 1) and
PR #4642 (Wave 2 — restored threads pages). 22 placements total: 18
direct ports of upstream surfaces, 3 path-adjusted (learn/* → premium/*
or tutorials/*), and 1 structural (root prebuilt-components → its
shell-docs directory index). Surface identifiers match upstream so
PostHog `try_for_free_clicked` events can be reasoned about across
the cutover.
Skipped:
- `quickstart.mdx` (root) — routing shim, no real content
- `(root)/index.mdx` — shell-docs has no MDX docs landing
- Per-framework integration `index.mdx` files — shell-docs has no
per-framework overview pages, the upstream FrameworkOverview
afterFeatures slot has no equivalent
- `reference/v2/components/{CopilotChat,CopilotKit}.mdx` and
`reference/v2/index.mdx` — shell-docs reference layout differs
PDX-110.
Adds the four-variant sign-up CTA (card / inline / tile / info) that
upstream docs/ uses to drive the Enterprise Intelligence Platform
funnel. Wires it into the shared MDX registry so MDX pages can drop
`<OpsPlatformCTA>` inline.
Adapted to shell-docs conventions:
- Tokens swapped from indigo/purple Tailwind utilities to the
--accent / --violet-light / --border / --text* CSS variables that
shell-docs already uses, so the visuals match the rest of the site.
- Dropped the dark: modifiers (shell-docs has no dark theme yet).
- Replaced the lucide-react import with three inline SVG components
(ArrowRight, Info, Sparkles) — shell-docs deliberately avoids the
lucide dep, the mdx-registry uses emoji fallbacks for icons.
- Removed the client-side posthog.capture for try_for_free_clicked.
shell-docs ships server-side PostHog (middleware) only and has no
posthog-js; UTM params on the dashboard URL remain the source of
truth for click attribution. Surface props are preserved as the
utm_content value.
PDX-109.
## Summary
Adds hand-rolled persistent suggestion chips to the `headless-simple`
and `headless-complete` demos in the langgraph-python north-star,
propagates the same surface to the other 17 showcase integrations, and
adds a deterministic aimock fixture so a new chip-click e2e test
("Largest continent") rounds-trips against a stable `Asia is the largest
continent…` response across all 18 demos.
## What changed
**Phase 0 — north-star (commit `7cbc5ea8`)**
- `showcase/aimock/feature-parity.json` — new fixture: `What is the
largest continent?` → `Asia is the largest continent — about 30% of
Earth's land area, home to over 4.6 billion people.`
- `langgraph-python/src/app/demos/headless-{simple,complete}/page.tsx` —
refactor `send` / `handleSubmit` to accept `(override?: string)` so chip
clicks dispatch synchronously without a `setInput` round-trip; render a
persistent `<div data-testid="headless-suggestions">` chip row above the
composer with 5 canonical entries; remove the dead
`useConfigureSuggestions` call from headless-complete (it was
registering suggestions nothing rendered).
- `langgraph-python/tests/e2e/headless-{simple,complete}.spec.ts` —
append one new test in each spec asserting chip click → user message →
`Asia` reply.
**Phase 1 — parity propagation across 17 integrations (commit
`4882c61f`)**
- Spec files `headless-simple.spec.ts` and `headless-complete.spec.ts`
are now byte-identical to the north-star in every integration (10 tests
each = 5 simple + 5 complete; verified via `cmp` for all 34 spec files).
- The 5-entry `suggestions` const is byte-identical between every
integration's simple and complete demos.
- All 17 integrations now expose the same selector surface (canonical
headings, empty-state text, `data-testid="headless-complete-messages"`,
dynamic placeholder, `rounded-br-sm` user bubble, no CopilotChat-default
testids).
**Glue preserved per integration** (verified by post-blitz code review):
- `built-in-agent`: `<CopilotKitProvider runtimeUrl="/api/copilotkit"
useSingleEndpoint>` + `agentId: "default"`
- `google-adk` / `llamaindex`: `agentId: "headless_simple"` /
`"headless_complete"` (Python-style underscores)
- `claude-sdk-typescript`: headless-complete
`runtimeUrl="/api/copilotkit-headless-complete"`
- `spring-ai`: 70-line `deduplicateMessages` adapter workaround +
`useMemo` import preserved verbatim
- All `@region[...]` markers preserved in place
**Adapter-specific decisions worth flagging in review:**
- `google-adk` headless-complete: rewrote `message-list.tsx` from
`msg-user`/`msg-assistant`/`agent-thinking` testid scheme to the
canonical `headless-complete-messages` wrapper; rewrote `input-bar.tsx`
placeholder to canonical dynamic; added the missing subtitle and
empty-state hint
- `ms-agent-dotnet`: extracted inline composer to a new `input-bar.tsx`
to match north-star structure
- `llamaindex`, `ms-agent-python`: added the canonical empty-state hint
(was missing entirely)
- `agno`, `built-in-agent`, `crewai-crews`, `mastra`, `ms-agent-dotnet`,
`pydantic-ai`: replaced per-integration empty-state hint with the
canonical Excalidraw line — chosen for parity over per-integration
accuracy (some demos don't actually wire an Excalidraw tool; alignment
was the explicit goal)
## Verification
- `validate-parity.ts`: 18/18 packages pass, 0 MUST failures
- `aimock-fixtures` test suite: 18/18 pass
- aimock fixture probed directly: `What is the largest continent?`
returns the canonical Asia response
- Each propagation slot reported `tsc --noEmit` clean (0 new errors) +
`playwright --list` shows all 10 expected tests
- Code review (`pr-review-toolkit:code-reviewer`) on the full diff: 0
Critical / Important / Minor findings, 1 stylistic nit (north-star
`input-bar.tsx` `onSubmit` type contravariant-loose, harmless)
## What was NOT done
Live per-integration Playwright runs against rebuilt Docker images. The
17 containers would each need a no-cache rebuild (~5-15 min each = hours
total) and the canonical local-test path is `showcase test <slug>` per
the existing CLI / CI pipeline. Static + structural verification covers
the propagation pattern.
## Test plan
- [ ] Run `showcase test <slug>` (or equivalent CI job) for at least one
drift-heavy integration: `google-adk` (testid scheme rewrite),
`built-in-agent` (provider glue), `spring-ai` (dedup workaround),
`llamaindex` (added testid + empty-state)
- [ ] Run the existing per-integration Playwright suites for at least
the north-star (`langgraph-python`) to confirm the new chip test passes
against a real backend + aimock
- [ ] Confirm aimock fixture validation still passes after deploy
The validate-fixture-tool-surface check on PR #4669 flagged 18 drift
violations: every headless-simple demo carried 'Weather in Tokyo' /
'AAPL stock price' / 'Highlight a note' / 'Sketch a diagram' chips
that substring-match aimock fixtures returning tool calls
(get_weather / get_stock_price / highlight_note / etc.) — but
headless-simple demos only register 'show_card' via useComponent.
Tool-call dispatch had no matching renderer.
Trim the headless-simple chip list to two in-surface entries:
- 'Profile card' → 'Show me a profile card for Ada Lovelace' (existing
show_card fixture; show_card is already registered by useComponent).
- 'Largest continent' → 'What is the largest continent?' (text-only
fixture from Phase 0; no tool dependency).
The chip-click e2e test only asserts on the 'Largest continent' chip,
so the trim is test-compatible.
Headless-complete keeps the canonical 5-chip list (its tool surface
covers weather/stock/highlight/excalidraw via tool-renderers.tsx and
backend agents).
For google-adk/headless-complete: add a useDefaultRenderTool() wildcard
catch-all. The validator looks at page.tsx + hooks/* and a backend
agent file; google-adk's tool registrations live in tool-renderers.tsx
(unparsed) and there's no matching agents/headless_complete.py file,
so the validator saw an empty tool surface. The wildcard registers '*'
which matches every fixture tool — same pattern north-star already
uses in its own tool-renderers.tsx.
Follow-up to #4664 — same root cause, two more callsites and one
inflation.
## Two issues
### 1. Live cell + Ref Depth column still rendered red
`composed-cell.tsx`, `feature-grid.tsx` (via `ref-depth-column.tsx`) had
the same regression-vs-ceiling collision the matrix views did: they
passed `regression={depth.isRegression}` to `DepthChip`, and
`regression` short-circuits the chip to red regardless of `maxDepth`.
Switched them to pass `maxDepth={depth.maxPossible}` only.
`RefDepthCellProps.regression?` flips to `maxDepth?` to stop propagating
the dead prop.
### 2. `maxPossible` was inflated to 6
`computeMaxPossible` returned **6** whenever a D5 mapping existed, on
the assumption D6 was structurally reachable. There is no per-feature D6
registry, so D6 is a stretch goal in practice — every D5 cell rendered
amber as "1 below ceiling," even when it was the realistic ceiling. Cap
at 5; D6-green cells still render green because `depthColorClass` treats
`depth >= maxDepth` as at-ceiling.
This restores the cap from commit `eda75a512` (*"D5 mapping means max=5
not 6 (D6 probes are rare)"*) which was lost in the subsequent depth
thrash.
`page.tsx` `healthStats` also dropped its `isRegression` short-circuit
so the stats bar mirrors the chip's graduated logic instead of
double-counting non-ceiling cells as red.
## Verified locally
Pointed dev dashboard at production PocketBase + harness:
- Before: 539 D5 cells **amber**, 162 D4 green
- After: **701 green**, 0 amber, 0 red
## Test plan
- [x] `depth-utils`, `depth-chip`, `parity-matrix`, `cell-matrix`,
`composed-cell`, `feature-grid` test files — 115/115 passing
- [x] Visual check on local dashboard against prod data — all wired
cells now render green
- [ ] On deploy, confirm matrix view at
`dashboard.showcase.copilotkit.ai/#matrix:depth,health` matches local
## Summary
Search Flights and Sales Dashboard pills on the langgraph-python
beautiful-chat demo now produce visible A2UI surfaces. Three independent
bugs were masking each other:
- **Tool args were silently rejected.** `Flight` TypedDict required `id`
+ `statusIcon`, which the aimock fixture doesn't supply. langchain
bounced the call with `flights.0.id: Field required` and the agent
surfaced the validation error string as the "tool result" — so the LLM
summarized "Two flights shown above" without any cards ever rendering.
Made `Flight` permissive (`total=False` then `list[dict]`); only the
fields `_build_flight_components` reads need to be there.
- **`search_flights` now expands flights into literal-children
FlightCards server-side.** The structural-children template form
(`Row.children = { componentId, path }`) doesn't reliably expand for our
custom catalog — the working sibling demo (`a2ui-fixed-schema`) avoids
the form for the same reason.
- **Sales Dashboard pill went into a tool-call loop** because the
`userMessage + toolName` fixtures matched both the initial call and the
post-tool turn. Hoisted the `toolCallId` fixture above them so the
follow-up turn returns content and breaks the loop. Three new fixtures
at the top of `feature-parity.json`: a follow-up content match, the
secondary-LLM `render_a2ui` call, and the primary `generate_a2ui` call.
Reintroduced custom `Row`/`Column` renderers with `gap` support — the
basic catalog versions ignore it and the dashboard cards were squished.
Children are array-of-strings only (matches what the agent and fixture
emit).
## Test plan
- [x] All 7 tests in
`showcase/integrations/langgraph-python/tests/e2e/beautiful-chat.spec.ts`
pass (45s). Two new ones cover both pills end-to-end: `Search Flights
pill renders FlightCard surface from A2UI fixed schema` and `Sales
Dashboard pill renders A2UI dashboard surface`.
- [x] `pnpm test aimock-fixtures` passes (18/18) — fixture schema
validator is happy with the 3 new entries.
- [x] Manual visual confirmation: both pills render full surfaces with
proper card spacing.
- [x] No regression on existing `Toggle Theme` / `Pie Chart` / `Bar
Chart` pills.
## Notes
- Added a 3-second `waitForTimeout` to the spec's `beforeEach` so the v2
chat provider hydrates before suggestion clicks dispatch. Without it,
the click can race past hydration and silently no-op (the existing
pre-existing tests happened to be tolerant of this; the A2UI ones
aren't).
- System prompt cleaned up: dropped references to tools that don't exist
(`log_a2ui_event`, "enable app mode") and named `pieChart`/`barChart`
explicitly.