State the Tailwind dependency and leave it there. A reader who skips the build
does not need the failure enumerated for them, and the symptoms stay on #4777
for anyone diagnosing from the other end.
The example tool-call card styles its header row with unprefixed Tailwind
utilities. Copied into a project that does not compile Tailwind, every class
resolves to nothing, the `<summary>` falls back to block layout, and the icons
and tool name stack in one column with the native `<details>` marker showing.
That is the layout reported in #4777, and nothing in the card says it depends
on a Tailwind build.
Add a file header recording the dependency, the two symptoms that identify it,
and the two ways out. The claude-sdk copies declare this file in
allowedDivergence, so they are annotated by hand; the rest come from
parity:sync.
The marked block that wires managed Intelligence is the region a hosted reader
copies verbatim, and nothing checked it. Both gaps were deliberate: the parity
manifest lists `src/app/api/copilotkit/**` under `allowedDivergence` for every
instance it tracks, and no `docker-compose.test.yml` sets
`COPILOTKIT_LICENSE_TOKEN`, so every smoke-tested starter takes the else arm and
the `intelligence:` arm has never run in CI.
The cost was already visible. The block's code was byte-identical in 21 of 22
starters, but its warning comment had drifted into five variants and the two
`ms-agent-framework-*` starters shipped the `demo-user` stub with no warning at
all. That drift is how the localhost default of OSS-981 survived in all 22
copies at once.
Add `scripts/validate-intelligence-wiring-block.ts`, which greps the opening
marker, compares every site against the north-star starter, and fails on the
first line that differs. Two normalisations keep it usable: the block is
dedented, because `agentcore` nests it deeper, and the else arm's runner name is
masked, because `agentcore` runs `AgentCoreRunner` in front of a Bedrock session
where an in-process runner has nothing to run. Everything else, comment text
included, must match to the byte.
Then unify the warning at all 22 sites on the fullest wording, which also says
the id must exist in Intelligence or thread operations can fail.
The check passes on day one, so it is a ratchet rather than a migration. It is a
shape gate, not a content gate: 22 identically wrong copies still pass. What it
guarantees is that a fix reaches all of them or none.
Not covered: enrolling the `intelligence:` arm in the smoke path. That needs a
license token in CI and a reachable endpoint from the compose network, and is
tracked separately.
CopilotKitIntelligence resolves apiUrl/wsUrl to the managed hosts when they are
omitted, and its own docstring says leaving both unset is always correct against
the managed service. Every starter's runtime route supplied
`?? "http://localhost:4201"` instead, so a managed reader who copied the block
got a runtime aimed at a local stack that is not running -- the failure the
starter's own .env.example warns about two files away.
Replace the fallbacks with the conditional spread these same starters already use
in channel-host.mts, so a self-hosted override still works and the managed
default applies when it is absent. Three .env.example files also set the values
uncommented, two of them directly under a comment telling the reader to leave
them unset; comment those out to match the other nineteen starters.
Guard both shapes in validate-intelligence-env-names.ts, which already polices
the canonical Intelligence key name and hosts and runs unfiltered on every PR.
The rule is the pattern rather than the literal, so a staging host substituted
for localhost fails the same way. Local e2e harnesses and demo stacks that
genuinely target a local deployment are allowlisted with their reasons.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same extraction as langgraph-python across the nine starters whose runtime
route builds a generic HttpAgent (adk, agno, claude-sdk-python,
claude-sdk-typescript, crewai-flows, ms-agent-framework-dotnet,
ms-agent-framework-python, pydantic-ai, strands-python). Each starter's
route now imports createDefaultAgent() from a new src/agent.ts instead of
constructing HttpAgent inline; the URL fallback/normalisation logic is
preserved verbatim per starter (identical bodies for four, agno's "/agui"
suffix, claude-sdk-*'s trailing-slash strip, crewai-flows' bare strip,
strands-python's second STRANDS_AGENT_URL fallback).
Each starter also gets channel-host.mts (copied verbatim from
langgraph-python) and its own tsconfig.channel.json, needed because the
starters' shared allowJs:true tsconfig makes tsx's resolver prefer a stray
index.ts the published fast-json-patch package ships without its source,
which otherwise crashes the channel script before it runs. package.json
gains the channel/typecheck:channel scripts and three deps: dotenv,
@copilotkit/channels (pinned to the same 1785633429 canary build as the
other @copilotkit/* packages), and tsx as a devDependency. This canary pin
is a temporary workaround pending a stable release with identifyUser
support, and must move before this path ships to users.
ms-agent-framework-dotnet's postinstall (a .NET SDK probe) fails on
machines without the SDK installed; dependency resolution itself succeeds
and is unaffected. crewai-flows, pydantic-ai, and strands-python each carry
a small number of pre-existing, unrelated tsc --noEmit errors (JSX/recharts
typing clashes and a missing exported type) not touched by this change.
- ModeToggle: one style on both breakpoints (top-4/right-4 = 16px gutter,
46px min-height, 4px corners); symmetric p-1.5 + fixed 20px button leading
so the selected pill has an even gap on all four sides (was tight L/R vs T/B).
- Launcher: uniform 16px gutter (top + left) on both breakpoints so it mirrors
the toggle; drop the mobile-only 7px override.
- Logo: centered on the launcher/toggle middle line (pt-[23px]); wordmark
padding normalized so its height matches on both breakpoints.
- Inspector FAB: sits beneath the toggle, gap = the 16px top gutter (one rule,
no media query, since the toggle is identical across breakpoints).
Net: launcher, logo, toggle share center-y; launcher + toggle are both 46px;
the FAB tucks under the toggle with a matching gap; the selected toggle pill is
evenly inset.
- ModeToggle: move left (right-[72px]) so the top-right inspector FAB no longer
covers the App segment; grow to 46px (lg:min-h) + center on the logo line
(top-6) to match the launcher; keep the 4px corners.
- Launcher: left gutter -> 16px to match the right-side controls' inset.
- Logo: pt-7 so it centers on the same line as the launcher + toggle.
- Mobile header: max-lg:pb-0 -> pb-4 so chat content clears the fixed launcher/
toggle strip instead of butting right under it (no boundary).
- Chat/App ModeToggle: rounded-full -> rounded-[4px] container + rounded-[2px]
buttons, matching the drawer's 4px radius cap so the header controls are
visually consistent.
The 7px/16px launcher inset was tuned for the mobile off-canvas launcher; on
desktop it leaked onto the collapsed cluster. Move it into the mobile media
query so desktop-collapse uses the element's own 24px gutter default.
The floating launcher/collapsed cluster is fixed at the top-left corner. Below
1024px it always shows (already cleared via max-lg:pl-24); on desktop it appears
only when the drawer is COLLAPSED. Drive the header's left padding off
--cpk-drawer-reserved-width (0px when collapsed, 320px default otherwise) so the
logo starts at ~6rem when collapsed and pl-6 when expanded — no overlap. No-op
on current packages (var never set → stays pl-6).
Read grid-template-columns' first track from var(--cpk-drawer-reserved-width, 320px)
so when the drawer collapses on desktop (it sets the var to 0) the reserved
column collapses and the chat reclaims the space — instead of leaving an empty
placeholder column. Mobile (single-column) is unchanged.
Rename import + usage from CopilotDrawer to CopilotThreadsDrawer across the
de-forked examples, and drop the now-redundant onUpsell handler: ENT-1027
makes the element open the Intelligence docs URL by default via licenseUrl.
Comments updated; --cpk-drawer-* tokens unchanged. Holds until the drawer
packages are published.
Replace the hand-rolled threads-drawer fork in every threads-enabled
integration example with the SDK <CopilotDrawer> (uncontrolled
CopilotChatConfigurationProvider + reserved-column layout + theme no-flash
where applicable). 16 examples; all browser/build-validated locally.
DRAFT — depends on #5707 and the subsequent npm release; not mergeable until
the SDK publishes @copilotkit/web-components and react-core bumps. Pre-merge
TODOs in the PR description.
## What
1. Add `suppressHydrationWarning` to `<body>` across **all 14
integration demo templates**
(`examples/integrations/*/src/app/layout.tsx`).
2. Fix a pre-existing **double-escaped Windows path** bug in the parity
manifest's `packageJsonOverrides`.
## Why (hydration)
**Mike Ryan hit a hydration error on first load of a fresh
`langgraph-python` init — caused by his Grammarly browser extension.**
Grammarly (and similar extensions) inject attributes onto `<body>`
*before* React hydrates:
```
data-new-gr-c-s-check-loaded="9.98.0"
data-gr-ext-installed=""
```
Those attributes are in the client DOM but absent from the server HTML,
so Next.js reports:
> A tree hydrated but some attributes of the server rendered HTML didn't
match the client properties.
It's a **false positive** — the app works, and end users (without dev
extensions) never see it — but it's a red console error on the first
load of our flagship eval/showcase templates, which is a poor first
impression.
## Fix (hydration)
`suppressHydrationWarning` on `<body>` is the React/Next.js-recommended
escape hatch for this. It is **scoped and one level deep**: it only
relaxes the check for `<body>`'s *own* attributes/text — **everything
rendered inside `<body>` (the whole app) is still fully
hydration-checked** — and `<body>`'s only attribute here is a static
`className`, so none of our own markup is masked. An inline comment
documents this so a future maintainer who adds dynamic `<body>`
attributes knows the check is relaxed.
`agent-spec` already had `suppressHydrationWarning` on `<html>`; the
Grammarly attributes land on `<body>`, so it needed the body-level
relaxation too (the `<html>` one is a level up and doesn't cover
`<body>`'s attributes).
## Commits
1. `b1fa482a7` — north-star (`langgraph-python`) + parity instances
(`langgraph-js`, `langgraph-fastapi`, `strands-python`) via `pnpm
parity:sync`.
2. `9f9c415d9` — the non-parity templates (not tracked by
`_parity/manifest.json`): `adk`, `agno`, `crewai-crews`, `crewai-flows`,
`llamaindex`, `mastra`, `ms-agent-framework-dotnet`,
`ms-agent-framework-python`, `pydantic-ai`, `agent-spec`. *(The repo's
`oxfmt` pre-commit hook also collapsed some multiline `<CopilotKit …>`
JSX in these files — standard auto-format on touched files; the only
semantic change is the suppression.)*
3. `7d60e49de` — parity manifest path-escaping fix (see below).
## The manifest bug (commit 3)
While syncing I found the `langgraph-js` and `strands-python`
`packageJsonOverrides` double-escaped the Windows `.bat` fallback,
producing `scripts\\run-agent.bat` (two backslashes) instead of
`scripts\run-agent.bat`:
- `langgraph-js/package.json` had already been synced with the broken
value.
- `strands-python/package.json` was still correct — and `parity:sync`
would have **corrupted** it on the next run (which is what surfaced
this).
Fixed the three overrides and re-ran `parity:sync`, which corrects
`langgraph-js/package.json` and leaves `strands-python`'s correct value
intact.
## Test plan
- [x] `pnpm parity:verify` → 0 errors
- [x] lefthook pre-commit green on all 3 commits (lint + `packages/**`
tests + commitlint)
- [x] All 14 templates confirmed to have body-level
`suppressHydrationWarning`
- [ ] Reviewer with Grammarly installed: run/`init` a template and
confirm no hydration error on first load
Browser extensions such as Grammarly inject attributes onto <body>
(data-gr-ext-installed, data-new-gr-c-s-check-loaded) before React
hydrates, which surfaces as a hydration mismatch error on first load of
the generated Next.js app.
Add suppressHydrationWarning to <body> in the langgraph-python north-star
and propagate to the parity instances (langgraph-js, langgraph-fastapi,
strands-python) via parity:sync. This only relaxes the check for <body>'s
own attributes (one level deep); everything rendered inside <body> is
still fully hydration-checked, and <body>'s className is static so none of
our own markup is masked.
The locked-state card told users to add an Intelligence license with
`copilotkit add-intelligence`, but that command only drops the
Intelligence overlay and does not issue a license (and is not yet wired
into the CLI dispatch). The command that issues a license key is
`copilotkit license`.
Addresses the blocking review on #5151:
1. Starter smoke 'Popup Assistant' timeouts (adk, agno, mastra,
ms-agent-framework-python/dotnet): the pages passed the dead v1 label keys
(labels.title / labels.initial), which v2 CopilotSidebar ignores — the
header rendered the default 'CopilotKit Chat' so the smoke's
text=Popup Assistant wait timed out. Map to the v2 CopilotChatLabels keys:
title -> modalHeaderTitle, initial -> welcomeMessageText. Verified live:
SSR now renders 'Popup Assistant'.
2. llamaindex TypeError (reading 'proverbs'): the page still used the v1
useAgent API ({ state, setState } = useAgent({ name, initialState })) —
v2 returns { agent }, so state was undefined at render. Migrate to the v2
pattern (agent.state with a guarded default + agent.setState + one-time
seed effect), and fix its dead v1 label keys too. Verified: next build
prerenders all pages cleanly.
3. parity-check drift: ran _parity/sync.ts --all — syncs example-layout
(mobile-header fix), layout.tsx, docker-route-override.ts to the
north-star, and bumps @copilotkit/* to 1.59.1 in langgraph-fastapi and
strands-python. parity verify: 0 errors across all instances.
(next-env.d.ts is gitignored repo-wide; verify warns-and-skips it in a
clean checkout, so it is intentionally not committed.)
4. Trailing-whitespace diff-check failure (showcase google-adk agent): already
resolved by the merge of main (the file matches main; diff --check clean).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previous commit wired LANGGRAPH_DEPLOYMENT_URL into the strands route.ts
to satisfy the verbatim langgraph-python docker-compose.test.yml — that
leaks the wrong runtime's name into a strands-only file.
Instead, mark docker-compose.test.yml as allowed-divergence for strands
(parity manifest) and give strands its own compose stack:
- agent on port 8000 (matches `npm run dev:agent` locally)
- healthcheck on /health (already exposed by main.py)
- app reads AGENT_URL (strands-native), no LANGGRAPH_DEPLOYMENT_URL
Reverts:
- Dockerfile.agent: EXPOSE/uvicorn back to 8000
- main.py: drop the /ok endpoint added in 2994b3a96
- route.ts: drop the LANGGRAPH_DEPLOYMENT_URL fallback
Keeps:
- Dockerfile.agent: COPY src/ ./src/ (needed for db.csv + a2ui schemas)
The smoke-test docker-compose.test.yml is a verbatim copy of the canonical
langgraph-python stack, which expects:
- agent listening on port 8123
- readiness probe at /ok
- app reading LANGGRAPH_DEPLOYMENT_URL for the agent URL
Bring the strands agent in line:
- Dockerfile.agent: COPY src/ so main.py can find db.csv +
a2ui/schemas/flight_schema.json; expose 8123 and bind uvicorn to it
- main.py: add /ok alongside existing /health endpoint
- route.ts: read LANGGRAPH_DEPLOYMENT_URL as a fallback so the verbatim
compose env wires through; AGENT_URL / STRANDS_AGENT_URL still win
for local dev overrides
Every starter's route.ts now reads process.env.AGENT_URL as the
primary agent endpoint, with framework-specific vars as fallback:
- AGENT_URL || "http://localhost:8000" (pydantic-ai, adk, agno, crewai,
ms-agent-python, ms-agent-dotnet, strands-python)
- AGENT_URL || LANGGRAPH_DEPLOYMENT_URL || "http://localhost:8123"
(langgraph-python, langgraph-js)
- AGENT_URL || "http://localhost:9000" (llamaindex)
Also adds .env.example with AGENT_URL to all starters.
Run prettier on ~1,865 files across examples/ to match the monorepo's
formatting standards. These files were imported as-is from standalone
repos that used different prettier configs.