The 1.62.3 release publishes the CopilotThreadsDrawer redesign (web-components +
react-core wrapper) and the stateless /suggest feature. Bump the 15 integration
examples that consume the drawer from 1.62.2 -> 1.62.3 (package.json + lockfiles)
so they pick up the released packages alongside this branch's example CSS.
Validated: langgraph-js runs on the published 1.62.3 (no local links) — the
redesigned drawer renders (New Conversation, Recent Conversations, filter funnel,
desktop collapse toggle, per-row kebab), threads are licensed, and a real agent
message round-trips.
- 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.
- Add full StateGraph + Annotation setup with CopilotKitStateAnnotation.spec
- Show complete tool implementation with proper ToolMessage handling
- Include graph compilation with nodes, edges, and routing logic
- Pattern examples after working shared-state-streaming.ts reference
- Fix both Deep Agents and LangGraph docs versions
- Include formatter fixes for JSON files
Fixes FAC-101
The langgraph/strands examples pinned @copilotkit/runtime via a
self-referential npm alias (npm:@copilotkit/runtime@x) left over from the
de-fork branch (2155821b8), where it forced registry resolution while
react-core used workspace:*. That alias is functionally identical to a
raw pin and react-core was already reverted, so drop the alias in the four
remaining files (langgraph-fastapi, langgraph-js, langgraph-python,
strands-python) and regenerate their lockfiles.
Bump the 16 integration examples already on the 1.62.x line from 1.62.1
to 1.62.2 (the just-published release), including their in-tree agent
sub-packages (langgraph-js/agent sdk-js, agentcore CDK lambda runtime)
that had drifted to 1.61.0. Regenerated the co-located package-lock.json
files against the published 1.62.2.
a2a-a2ui and agent-spec remain at 1.61.0 (QA-held; out of scope).
Address PR review: delete the 4 stray package.json.bak files left by the
version-bump tooling, and restore examples/teams/appPackage/{color,outline}.png
to main (the de-fork commit had rewritten them into raw LFS-pointer text —
incidental fallout unrelated to this PR's scope).
The drawer shipped in 1.62.1 (react-core + the previously-missing
web-components package). Move the de-forked examples off the workspace:*
placeholder (and 1.61.0 runtime) onto the published 1.62.1, so they consume
the real SDK CopilotThreadsDrawer. Verified: a standalone install + next build
of pydantic-ai resolves CopilotThreadsDrawer from the published packages.
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.
Same class as the toggle-theme fix: langgraph-js's 'schedule a meeting' chip
returned text, so the meeting picker never rendered, while langgraph-python emits
the scheduleTime tool call. scheduleTime is a registered langgraph-js frontend
tool (reasonForScheduling/meetingDuration) — swap text -> tool call, mirroring
langgraph-python. Because scheduleTime is Human-in-the-Loop (the user's pick
returns as a tool result and re-invokes the LLM), add a terminating
call_schedule_time_001 result fixture in BOTH templates so the turn doesn't
re-match the user message and loop (langgraph-python lacked it too — latent).
Verified both terminate at 2 steps (tool call -> terminating text).
The chart-chain fixtures looped under multi-step tool execution: langgraph-python
emitted a 2nd tool call (pieChart/barChart/dashboard) with no terminating
toolCallId result fixture, so it fell through to the still-matching userMessage
fixture and re-called query_data. langgraph-js had its toolCallId result fixtures
ordered after the userMessage fixtures (first array match wins -> re-call).
Add the missing terminating result fixtures (langgraph-python) and reorder so all
toolCallId fixtures precede userMessage fixtures (langgraph-js). Verified termination
via the 2-3 step multi-turn repro; mastra fixtures already terminated.
The 6 OpenAI drop-in integration examples enabled for the CLI's keyless
AIMock mock mode shipped fixtures that did not cover the prompts each
starter's own UI suggests, so a keyless first-run user clicking the demo
suggestions mostly hit the generic catch-all. Re-key/extend each
example's fixtures/default.json (substring, most-specific-first,
catch-all last) so the surfaced suggestions return scripted replies.
ENT-1003.
## What
Removes the live-consumed local Intelligence overlay and the dead
references the deletion would leave behind.
- Deletes `examples/integrations/_intelligence/` (`docker-compose.yml`,
`.env.intelligence`, `README.md`) — the overlay the currently-shipped
CLI clones at runtime.
- Strips the now-dangling `# see
examples/integrations/_intelligence/.env.intelligence for the seed
value` pointer from 8 integration `.env.example` files (adk, agno,
langgraph-fastapi, langgraph-js, langgraph-python,
ms-agent-framework-{dotnet,python}, strands-python).
Closes ENT-834.
## ⚠️ DO NOT MERGE until launch
The **currently-shipped** CLI clones
`CopilotKit/CopilotKit@main:examples/integrations/_intelligence` at
runtime via `fetchIntelligenceOverlay` (hardcoded to `main`). Deleting
this dir from `main` **immediately breaks** the shipped CLI's
threads-framework `init` — the overlay fetch 404s.
**Merge gate (verify at merge time):**
- [ ] The managed-only CLI build has **dropped
`fetchIntelligenceOverlay`** (Intelligence-repo removal ticket)
- [ ] That managed-only CLI has been **released**
- [ ] Merge in lockstep with launch
## Scope
- ✅ **Included:** the `_intelligence/` overlay dir + the 8 dead
`.env.example` pointers it leaves behind.
- ✅ **Already done elsewhere:**
`examples/integrations/langgraph-python-threads/` (the ticket's "maybe"
scope) was already removed via ENT-800 — it is no longer on
`origin/main`.
- ⏭️ **Deferred to a launch-coordinated follow-up:** the local-stack
`INTELLIGENCE_API_URL`/`GATEWAY_WS_URL` defaults (`localhost:4201` /
`ws://localhost:4401`) baked into ~18 `copilotkit:intelligence` route
blocks + `agentcore/docker/docker-compose.yml`, and the matching
local-dev block in each `.env.example`. These depend on the managed
CLI's hosted env contract (Intelligence repo) and shouldn't be guessed
at now. The `copilotkit license` locked-state copy is owned by ENT-804.
## Not affected
The `docker-compose.test.yml` + `docker/Dockerfile.{agent,app}` files
across the integrations are the **e2e/CI test harness** for the example
apps, unrelated to the Intelligence pivot. The Threads feature, the
activation-gated `copilotkit:intelligence` block, and the themed
threads-drawer UI are the product and work against hosted Intelligence —
only the local-stack scaffolding is being removed.
## Notes
Branched off fresh `origin/main` (`c540734143`). No in-repo code
references the overlay dir (the `_intelligence` matches under
`packages/` are an unrelated private field on the agent registry).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## 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.
## Summary
- Update locked Threads drawer copy in integration examples to use `npx
copilotkit@latest license`
- Keep the command consistent across the example variants that render
the licensed feature panel
## Validation
- Pre-commit hooks ran lint/check package hooks successfully
- Verified integration examples no longer render the stale `copilotkit
license` command
1.61.0 is now published. Regenerates all 20 starter package-lock.json files
so @copilotkit/* resolves to 1.61.0 and the transitive
@copilotkit/license-verifier moves 0.4.2 -> 0.5.0 (shipped by runtime@1.61.0).
ENT-939
Prep for the 1.61.0 release. Pins every @copilotkit/* dependency in the
examples/integrations starter projects to exact 1.61.0 (not yet published).
ENT-939
The deleted examples/integrations/_intelligence/ dir was referenced by a
"see .../.env.intelligence for the seed value" comment on the
INTELLIGENCE_API_KEY line in 8 integration .env.example files. Removing the
overlay leaves those pointers dangling, so strip them in the same PR.
Localhost INTELLIGENCE_API_URL/GATEWAY_WS_URL defaults are intentionally left
in place — the hosted-only rewrite of those is a launch-coordinated follow-up
(depends on the managed CLI env contract).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The chat never responds and the browser console shows
"TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation"
on every agent run (onRunFailed, agentId: default).
Root cause is in @ag-ui/client@0.0.56's HttpAgent: it stores the global
fetch unbound (`this.fetch = config.fetch ?? fetch`) and later invokes it
as `this.fetch(...)`, so native fetch runs with the agent instance as its
receiver instead of `window`. Native fetch is brand-checked and throws
"Illegal invocation". It surfaces in both dev and prod.
Fixed upstream in @ag-ui/client@0.0.57 (`config.fetch ?? ((url, init) =>
fetch(url, init))`), which shipped in @copilotkit/* 1.60.1. Bumping these
examples 1.60.0 -> 1.60.1 pulls 0.0.57 transitively; lockfiles regenerated.
Scope: only the examples that take @ag-ui transitively from react-core are
bumped here. Other integration examples force-pin @ag-ui via `overrides`
to 0.0.53/0.0.55, where 1.60.1 would create a version skew; those need a
per-integration bump and are intentionally left out of this change.
Bumps every @copilotkit/* pin (react-core, react-ui, runtime, a2ui-renderer,
sdk-js) from 1.59.5 to 1.60.0 across the threads-enabled integration examples,
and regenerates each package-lock.json to the 1.60.0 dependency closure.
Excludes the vestigial langgraph-python-threads example.
Bring the starter agents' Python dependency pins (pyproject.toml + uv.lock
for adk, langgraph-fastapi, langgraph-python, pydantic-ai, strands-python;
requirements.txt + docker override for crewai-crews) in line with the
showcase fleet pin standard.
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`.
Restores #5151 (north-star + batch 1 + crewai-flows + llamaindex),
#5196 (pydantic-ai), #5205 (a2a-middleware), #5211 (mcp-apps), reconciled
onto the current main baseline rather than the pre-revert tree:
- keep main's 1.59.3 pins, AGENT_URL normalization, default agent keys,
useConfigureSuggestions, available:false, useRenderTool
status/parameters API, call-time agent.state reads, and crewai-crews'
rebuilt page (not yet threads-migrated)
- graft the threads layer (drawer/gate/provider, env-gated route
intelligence block, next.config gate, env docs, drawer deps) on top
- drop threads-era sidebar suggestions props where main now registers
suggestions via useConfigureSuggestions (or omits them)
- fix the stale pydantic-ai doc link main reintroduced in ms-af-dotnet
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
copilotkit 0.1.93 re-exports StateStreamingMiddleware/StateItem FROM
ag_ui_langgraph.middlewares.state_streaming, which only exists in
ag-ui-langgraph>=0.0.35. The Dockerfiles installed copilotkit with --no-deps
and pinned ag-ui-langgraph[fastapi]==0.0.22, so the middlewares submodule was
missing and the agent crash-looped at boot with
'ModuleNotFoundError: No module named ag_ui_langgraph.middlewares'.
Bump the pin to 0.0.37 (the version copilotkit 0.1.93 resolves with deps).
Verified by booting the langgraph-python image: the agent now reaches
'Application startup complete' / 'Uvicorn running on 0.0.0.0:8123' with no
ImportError, and the container serves HTTP 200 on /.
The build-starters Docker images hardcoded copilotkit==0.1.78 in the
langgraph-python and langgraph-fastapi Dockerfiles, but their agents import
StateStreamingMiddleware and StateItem, which 0.1.78 does not export. This
crash-loops the Python agent on boot with an ImportError. Bump the hardcoded
pin to 0.1.93 (latest stable, re-exports both symbols, matches local
sdk-python). Align all three langgraph-family agent pyproject pins
(0.1.87 -> 0.1.93) for consistency and regenerate the strands uv.lock.
The no-license locked panel is a fixed-width (w-80 / 18-20rem) block. In the
single-column mobile grid it left a dead background strip beside it and pushed
the app content down. Hide it below 1024px (max-lg:hidden / .lockedPanel
display:none) — consistent with the real drawer + first-paint placeholder,
which also reserve no column on mobile. Content now gets the full width.
Applied across all 7 migrated examples.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On the no-license locked threads panel:
- widen the panel slightly (w-72 -> w-80 / lockedPanel 18rem -> 20rem) and
add white-space: nowrap so `copilotkit add-intelligence` stays on one line
instead of wrapping mid-command
- add a 'with:' lead-in above the command box so it reads as a runnable command
rather than loose text
Applied across all 7 migrated examples (Card-based locked state in adk/agno/
langgraph-js/langgraph-python; themed .lockedPanel in mastra/ms-agent-*).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The disabled-state ThreadsPanelGate (shown when no Intelligence license is
present) rendered its locked card in a bare `w-72` container with no
background, so the threads column showed the page background (a black column in
dark-themed examples, white in light) instead of the drawer surface.
Give that container the drawer surface (bg + hairline right border) so the
locked card sits in a panel that matches the drawer, consistent with the
already-surfaced .lockedPanel examples (mastra, ms-agent-framework-*).
Affects adk, agno, langgraph-js, langgraph-python (the Card-based locked state).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ThreadsPanelGate renders a first-paint placeholder while the client-only
drawer mounts. It was either a bare `w-72` div (no surface → a black/white
column flash in the page bg) or a fixed 18rem inline-styled div (correct color
but, on mobile where the mounted drawer floats, the reserved 18rem collapsed on
mount → content shifted left).
Replace both with a shared `.drawerPlaceholder` class that matches the open
drawer's footprint + surface (18rem, drawer bg, hairline border) on desktop and
`display: none` below 1024px (the mobile drawer floats, so reserve no column).
Result: no color flash and no content shift on load. Applied across all 7
migrated examples (themed vs raw surface tokens per example).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On phones/tablets (≤1024px) the threads drawer's floating launcher is fixed
at the top-left corner, and it collided with this example's top-left
CopilotKit header. Three small fixes so the header reads as one tidy row:
- max-lg:pl-24 on the header so the logo clears the launcher pill
- max-lg:pt-2.5 + pb-0 (and drop the logo span's pb-1.5 on mobile) so the
logo is vertically centered with the launcher + the Chat/App toggle
(was sitting ~7px low)
- trim the collapsed launcher's icon buttons (2rem -> 1.75rem) + tighter
padding so the pill height (~36px) lines up with the toggle instead of
towering over it
Local to langgraph-python (the only rollout example with a top-left header).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Below 1024px the in-flow threads column squeezed the demo content and the
(full-screen) CopilotSidebar chat into slivers. Raise the responsive
breakpoint from 900→1024 and make the panel a true off-canvas overlay:
- collapsed → a small floating launcher pill pinned top-left (z-1300),
above the full-screen mobile chat, so threads stay reachable
- open → a fixed full-height panel sliding in from the left (z-1300),
content + chat use the full width behind it
- default the drawer to collapsed when innerWidth ≤ 1024 on mount
Applied across all 7 integration examples' shared threads-drawer, each
adapted to its own theme tokens (themed examples use --threads-drawer-*,
raw examples use --card/--border for the launcher surface).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Below 900px the drawer already overlays the content when open (existing media
query), but it defaulted to open — so on phones the 16rem panel covered the
content + chat on load. Default isOpen from window.innerWidth (> 900) so narrow
screens start at the 3.5rem rail; the user expands to the overlay on demand.
The drawer is client-mounted, so reading window in the initializer is safe.
Applied to the shared threads-drawer.tsx across all examples + north-star.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New thread (header + collapsed rail) now switches to a fresh threadId
(crypto.randomUUID()) instead of setting it to undefined. Setting threadId
undefined after a thread was selected did not reset the V2 chat — it kept
showing the previous conversation. A new id forces the configuration provider
to re-thread to an empty conversation.
- Collapsed-rail buttons use a native title tooltip instead of the styled
::after one, which clipped horizontally against the viewport's left edge in
the narrow rail. The wider expanded row keeps the styled tooltip.
Applied to the shared threads-drawer.tsx across all examples + north-star.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The threads-drawer had drifted into two near-identical .tsx variants (a 1-char
encoding diff) during the bespoke passes. Unify all examples onto a single
shared threads-drawer.tsx, which also propagates the mastra review fixes:
- collapsed rail: hover tooltips on the expand + new-thread buttons, and the
non-interactive decorative icon (read as a dead button) removed;
- tooltip renders below the trigger in each example's themed module.css (the
styled ::after tooltip was clipped when shown above the trigger).
Per-example theming (the module.css token values) is unchanged — only the
shared logic + the tooltip positioning rule are reconciled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Next route handlers only declared GET/POST, so the threads REST API's
DELETE (delete thread) and PATCH (archive/restore) were 405'd by Next before
reaching the runtime — thread deletion and archive were broken in every
example. Export PATCH + DELETE → handle(app) across all 7 routes.
mastra threads-drawer review fixes:
- tooltip renders below the trigger (the styled ::after tooltip was clipped by
the drawer's overflow containers when shown above);
- collapsed rail drops a non-interactive decorative icon (read as a dead button)
and adds hover tooltips to the expand + new-thread buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tokenize the last hardcoded literals in threads-drawer.module.css into --threads-*
CSS variables (with fallbacks to the existing design tokens / prior literals, so
the north-star is visually unchanged). Add THEME.md documenting the token contract
so each example can theme the drawer by defining tokens — no per-example CSS edits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>