## Summary
Fixes#2234
React's hydration fails when block-level elements (like `<div>`) are
nested inside `<p>` tags in the Markdown component. Replaces the `<p>`
wrapper with `<div>` (adding `copilotKitParagraph` class for styling) to
prevent SSR hydration mismatches.
## Test plan
- [ ] Verify Markdown rendering in SSR context has no hydration errors
- [ ] Verify paragraph styling is preserved via `copilotKitParagraph`
class
## Summary
- **console.css + input.css:** Scope `.dark` CSS selectors to CopilotKit
container elements (`.copilotKitDevConsole
.copilotKitDebugMenuTriggerButton` and `.poweredBy` respectively). The
original selectors had bare `.dark,` as standalone entries in
comma-separated selector lists, which applied styles to *any* element
with class `.dark` instead of scoping to CopilotKit elements.
- **colors.css:** Remove broken `:root` pseudo-element from
`body[style*="color-scheme: dark"] :root` — `:root` is `<html>`, which
cannot be a descendant of `<body>`, so this selector never matched
anything.
- **E2E tests:** 4 Playwright regression tests verifying dark mode
styles don't leak into host application elements.
Prevents CopilotKit dark-mode styles from leaking into the host
application.
Closes#2920
---
*Split from #3847*
## Summary
- The V1 `CopilotRuntime` constructor did `{...endpointAgents,
...agents}` which silently spread a factory function to `{}`, destroying
all agents with no error or warning
- Anyone using the V1 API with a factory function (introduced in #3854)
got zero agents — complete silent data loss
- This wraps factory functions so endpoint agents are merged at
resolution time instead of construction time, matching how V2 already
handles it via `resolveAgents()`
Fixes the V1 path regression from #3854 (per-request agent factory,
issue #2941).
## Test plan
- [x] New test: factory function is preserved through constructor (not
spread to `{}`)
- [x] New test: factory resolves different agents per-request based on
headers
- [x] New test: endpoint agents are correctly merged with
factory-resolved agents
- [x] New test: static agent records still work (backward compat)
- [x] New test: promised agent records still work (backward compat)
- [x] Red-green verified: 4/5 tests fail without the fix, all 5 pass
with it
- [x] Full runtime test suite passes (1234 tests)
- [x] Full build passes
The compatibility layer correctly handles v6 (same API surface as v5),
but the version range ^5.0.0 excluded v6.x. Changed to >=5.0.0 so
users on the latest openai SDK don't get peer dependency warnings.
- Remove all `as any`, `as Function`, and `as Record<string, unknown>` casts
from production code in the OpenAI compatibility layer
- Introduce typed `OpenAIV4Beta` interface and `hasV4BetaChat` type guard
for safe runtime detection of v4 vs v5 clients
- Extract `retrieveThreadRun` and `submitToolOutputsStream` helper functions
into utils.ts with properly typed generic signatures, moving the v4/v5
dispatch logic out of the assistant adapter
- Give `getChatCompletionsForStreaming` an explicit return type so callers
no longer need secondary casts
- Update tests to exercise the new helpers directly instead of
duplicating dispatch logic inline
The CopyButton in react-core was showing the "copied" checkmark based on
an independent clipboard availability check rather than the actual copy
result. This meant a failed copy (e.g. permission denied) would still
show the success indicator. Now the onClick handler returns the boolean
from copyToClipboard, and handleClick uses that to drive the UI state.
Also removes unsafe type casts of onClick to Promise<void>.
Address review feedback: extract the repeated clipboard availability check +
writeText + error handling pattern into a shared copyToClipboard() utility in
@copilotkit/shared. All 9 call sites across angular, react-core, and react-ui
now use the shared utility instead of duplicating the same code block.
Same bug pattern as console.css and input.css: bare `.dark,` was a
standalone selector leaking all --copilot-kit-* CSS custom properties
onto any element with a .dark class. Also removed broken `:root`
pseudo-element from `body[style*="color-scheme: dark"] :root` — :root
cannot be a descendant of body, so this selector never matched.
Fix: drop standalone `.dark,` (redundant with `html.dark` and
`body.dark`) and remove the non-functional `:root` descendant.
Two new test cases covering the core behavioral fix:
- User collapses during streaming → panel stays collapsed after stream ends
- User collapses then re-expands during streaming → panel stays open after stream ends
Without act(), React 18 defers the state update through its scheduler,
which can race with waitFor polling on slow CI runners (Node 20.x/22.x).
Wrapping in act() forces synchronous flush.
The auto-collapse useEffect unconditionally called setIsOpen(false) when
streaming ended, overriding any manual expand/collapse the user had
performed. Add a userToggledRef that tracks explicit clicks so the
effect only auto-collapses when the user hasn't interacted.
- Fix clone() dropping debug config on ProxiedCopilotRuntimeAgent
- Pass raw DebugConfig to agents instead of collapsing to boolean
- Clamp verbose to false when enabled is false
- Fix pino-pretty log format in docs (parentheses, timestamps, levels)
- Add loggedEventCount to doc example
- Fix "Agent run started/finished" → actual log messages in arch docs
- Count all emitted events unconditionally (eventCount), track separately
how many were logged (loggedEventCount) — completion log shows both
- Remove dead `?? undefined` on runtime.debug (non-optional type)
- Update test to verify eventCount reflects actual events; add test with
events: true to verify loggedEventCount
## Summary
- Widen `TanStackChatMessage.content` from `TanStackContentPart[]`
to `any[]` so messages from `convertInputToTanStackAI` are directly
passable to any TanStack AI adapter without `as any` casts
- Split `TanStackContentPart` into a proper discriminated union with
separate variants per modality
- Add `env.d.ts` with `vite/client` reference for CSS import types
- Fix `onError` callback shape in the example
## Test plan
- [x] All 17 multimodal TanStack tests pass
- [x] All 294 agent tests pass
- [ ] Verify no TS errors in react-router example IDE
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- New ThreadStoreRegistry suite (8 tests) covers register/get round-trip,
replacement on duplicate id, unregister no-op, and subscriber events for
both register and unregister
- handle-threads suite gains handleClearThreads (InMemory path + intelligence
path) and handleGetThreadMessages (InMemory, unknown thread, intelligence
delegation, 422 fallback) describe blocks
- in-memory-runner suite: clearThreads() in beforeEach fixes GLOBAL_STORE
isolation; vacuous empty-array test replaced with a meaningful post-clear
assertion
- use-threads suite: new test asserts registerThreadStore is called on mount
and unregisterThreadStore is called on unmount
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wires the new Angular Custom Elements into the web inspector's Threads tab.
The tab is hidden behind an early-access gate (isEarlyAccessUser check);
_demoMode defaults to false. The inspector registers its own per-agentId
thread store unless one is already registered by useThreads(), preventing
accidental overwrites for apps that use both.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exports cpk-thread-list, cpk-thread-details, and cpk-empty-events as
Custom Elements so the thread panel UI works in any framework (Angular,
React, plain HTML). Components use RxJS observables and Angular signals
internally, then render via defineElements() to register as standard
Web Components.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CopilotKitCore gains a ThreadStoreRegistry (register/unregister by agentId)
and a new onAgentRunStarted subscriber event so the inspector can subscribe
before agent.runAgent() snapshots the subscriber list
- Runtime gains handleListThreads, handleUpdateThread, handleArchiveThread,
handleDeleteThread, handleSubscribeToThreads, and handleGetThreadMessages
handlers; all mutations are authenticated via identifyUser (request body
userId is ignored)
- InMemoryAgentRunner now stores thread history for the local-dev fallback
path; debug console.log removed; InMemoryThread uses literal types for
constant-value fields (organizationId: "", createdById: "", archived: false)
- useThreads hook registers its store with CopilotKitCore on mount and
unregisters on unmount so the inspector can read thread state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
useAgent() returns per-thread clones that are not in the agent registry.
The inspector only subscribed to registry agents via onAgentsChanged, so
it never received events from clones — resulting in an empty event panel.
Add onAgentRunStarted subscriber event that fires before connectAgent()
and runAgent(), passing the actual agent instance (which may be a clone).
The inspector subscribes to that agent, matching the existing StateManager
pattern (subscribeAgentToStateManager).
The blockquote.copilotKitMarkdownElement p selector targeted literal <p>
elements inside blockquotes, but paragraphs now render as <div> after
the hydration fix. Updated to target .copilotKitParagraph class instead.
Also removes unused import and adds a regression test for this selector.
Add null checks for navigator.clipboard across all copy-to-clipboard
calls to prevent TypeError in non-localhost environments where the
Clipboard API is unavailable. The copied indicator now only appears
after a confirmed successful write, preventing false positive UX
feedback when the clipboard API is missing or the write fails.
Cover isOpenAIV5 detection, getChatCompletionsForStreaming dispatch,
and named-path-param calling conventions for runs.retrieve and
submitToolOutputsStream.
The shared visitedRefs Set was mutated in place, so when two sibling
properties referenced the same $def (e.g. billing and shipping both
referencing Address), the second resolution was incorrectly flagged as
circular. Clone the set before recursing so each branch has its own
ancestry path. Added regression test that fails without this fix.
OpenAI SDK v5 removed beta.chat (promoted to chat.completions) and
changed multi-path-param methods to use named params (e.g.
runs.retrieve(runId, { thread_id }) instead of positional args).
- Add isOpenAIV5() detection and getChatCompletionsForStreaming() helper
- Migrate OpenAIAdapter to use helper for streaming completions
- Migrate OpenAIAssistantAdapter runs.retrieve() and
submitToolOutputsStream() to branch on SDK version
- Add openai ^4.85.1 || ^5.0.0 to peerDependencies
Extract getErrorSuppression as a pure testable function from the
routeError closure. Replace the mock-only test that only proved mock
wiring with 12 real assertions covering all visibility x isDev
combinations against the actual logic.
Recursive JSON schemas that reference themselves via $ref would cause
infinite recursion and stack overflow. This adds a visited set that
tracks which $ref paths have been seen during resolution. When a cycle
is detected, it breaks with z.any() and logs a console.warn so users
get feedback. Also adds console.warn for the generic z.any() fallback
on unsupported schema types.
Adds tests for circular refs, non-circular $ref resolution, anyOf with
$ref variants, integer type, null type, and unsupported type warning.
The original Math.random() key caused React to remount the CodeBlock on
every render. The PR's content-based key (language + content prefix) still
changed every streaming token, causing the same flickering. Removing the
key entirely lets React use positional identity, which is stable across
re-renders while content streams in.
Closes#2669
The V1 CopilotRuntime constructor did `{...endpointAgents, ...agents}`
which silently spread a factory function to `{}`, losing all agents.
Anyone using the V1 API with a factory function got zero agents and no
error. This wraps factory functions so endpoint agents are merged at
resolution time instead of construction time.
The p tag was changed to div to fix hydration errors, but CSS selectors
still targeted p.copilotKitMarkdownElement. Updated to use the
.copilotKitParagraph class selector so paragraph styling (line-height,
font-size, margin) applies correctly to the new div element.
- Restore getCapabilities() return type to Promise<AgentCapabilities>
with ?? {} fallback, honoring AbstractAgent base class contract
- Simplify type annotation in get-runtime-info.ts to explicit
AgentCapabilities | undefined instead of complex Awaited<ReturnType<...>>
- Add console.warn logging to per-agent capabilities error catch block
- Fix useCapabilities JSDoc to accurately describe behavior during
runtime handshake
- Add test for empty capabilities object {} (truthy, included in response)
- Update error isolation test to verify warning is logged
- Add per-agent error isolation in /info handler so a single
getCapabilities() failure doesn't 500 the whole endpoint
- Normalize getCapabilities() to return undefined (not {}) when no
capabilities are set, matching the sync getter
- Replace instanceof ProxiedCopilotRuntimeAgent with duck-type check
in useCapabilities hook for extensibility
- Add JSDoc warning about shallow-merge behavior on capabilities config
- Add useCapabilities hook tests (5 cases covering both branches)
- Add per-agent error isolation test for get-runtime-info
When useAgent subscribes to multiple update types (OnMessagesChanged,
OnStateChanged, OnRunStatusChanged), each event fires forceUpdate()
independently. During streaming this causes dozens of re-renders per
second, leading to brief content height fluctuations that trigger
scroll jumping in use-stick-to-bottom.
Coalesce OnStateChanged and OnRunStatusChanged notifications using
queueMicrotask so multiple synchronous events within the same tick
produce a single React re-render. OnMessagesChanged retains its
existing behavior (direct or throttled).