Adds purely additive data-testid markers to the error and loading UI
surfaces across the frontend framework packages (react-core, react-ui,
react-native, angular, vue) so e2e tests can deterministically detect
errored-out vs still-loading states. Without these, e2e probes hit
~30-60s timeouts instead of failing fast.
Testids (aligned with existing repo convention; copilot-<kebab>):
- copilot-error-banner on react-core BannerErrorDisplay (toast
provider) and UsageBanner, plus react-ui legacy in-chat ErrorMessage.
- copilot-loading-cursor on react-ui legacy LoadingIcon sites
(Messages.tsx, AssistantMessage.tsx), angular
CopilotChatMessageViewCursor, react-native TypingIndicator (via
RN testID convention), and vue CopilotChatMessageView. The v2
react-core Cursor already exposed this testid; this change broadens
it to every frontend framework so a single selector works across all.
Vue's prior copilot-chat-cursor testid is renamed to
copilot-loading-cursor for cross-framework consistency; the two e2e
tests in packages/vue that referenced the old name are updated.
No behavior, rendering, or styling changes. Adds small static
source-asserting tests in each touched package that verify the markers
stay in place.
vue: replace the string-literal deps array (which was laundered through
`as unknown as any[]` because string is not a valid WatchSource) with
a getter-style deps array (`() => "compact"`), which is a valid
WatchSource<unknown>. The reference-identity assertion still holds.
react-core: rewrite the toolCallId comment to accurately describe what
this test verifies. The test calls config.render directly with
useRenderTool mocked, so it does not exercise the spread-adapter path
end-to-end — it only locks that useDefaultRenderTool passes the user's
render through untouched.
The 3 "default renderer" tests in use-default-render-tool.test.tsx were
narrowing config.render via an as-cast that omitted the now-required
toolCallId field on DefaultRenderProps, laundering the type. Switch the
casts to the real DefaultRenderProps shape and pass a realistic
toolCallId on every <DefaultRenderer/> invocation. No behavior change.
The runtime path already forwarded toolCallId to wildcard render functions
(useRenderTool spreads ReactToolCallRenderer props, which include toolCallId),
but the static DefaultRenderProps type omitted it. Vue's sibling type already
declared the field. This divergence forced an `as unknown as { toolCallId }`
cast in the react-core test.
Declare toolCallId on DefaultRenderProps (mirroring vue), thread it through
the defaultToolCallRenderAdapter so the now-required field is genuinely
populated, export the type, and drop the cast plus stale comments in the
test that claimed the field was runtime-only.
Mirror the Vue sibling test 'forwards toolCallId to custom wildcard render
function' so the react-core suite locks the same regression: the wildcard
hook must forward toolCallId to a custom render function. Closes a
symmetry gap in the cross-framework testid PR.
E2E tests for the chat surface (e.g. showcase's
tool-rendering-default-catchall canonical spec) need a stable selector
to count and inspect tool-call cards rendered by the framework's
built-in DefaultToolCallRenderer when an integration registers zero
custom render hooks. react-core's renderer already emits a
data-testid="copilot-tool-render" wrapper with data-tool-name,
data-status, data-args and data-result; vue's equivalent renderer was
missing them, so the same e2e test counted 0 cards there.
Mirrors react-core's contract onto the vue DefaultToolCallRenderer:
- packages/vue/src/v2/hooks/use-default-render-tool.ts: wrap the card
in a div carrying data-testid="copilot-tool-render", data-tool-name,
data-status, data-args and data-result (via the same
safeStringifyForAttr helper shape as react-core); tag the inner
name/status spans with copilot-tool-render-name and
copilot-tool-render-status.
Locks the contract into unit tests in both frameworks so the markers
cannot silently disappear in a future refactor:
- packages/vue/src/v2/hooks/__tests__/use-default-render-tool.test.ts:
new "default renderer emits stable copilot-tool-render testid and
metadata attrs" test (red-green proven locally by stashing the
source change).
- packages/react-core/src/v2/hooks/__tests__/use-default-render-tool.test.tsx:
mirroring test asserting the same wrapper/data-* attrs and inner
testids (red-green proven by sentinel-swapping the testid).
Scope: purely additive — no behavior, rendering, or styling changes.
The new attributes are inert at runtime; only e2e and unit tests read
them. angular has no built-in default renderer (only renders when the
user registers a wildcard) and react-native deliberately excludes the
default renderer (web DOM-only), so no changes are needed there.
This unblocks the showcase tool-rendering-default-catchall D6 spec
across frontends; a react-core release will follow once merged.
Adds two CI signals for keeping the published packages small and broadly compatible:
- Bundle size: size-limit file-mode config across packages plus a
CopilotChat import-size regression signal (gzip) so growth in the
headline consumer entrypoint is visible on every PR. A bundle-size
workflow comments results on the PR (Phase 1: no hard-fail).
- ES compatibility: a compat-check (es-check) script across 9 packages
with a root .browserslistrc, validating built .mjs/.cjs against the
es2022 build target.
The measure script is importable (measureBundle) and unit-tested. Dev
docs live under dev-docs/ (bundle-size.md, browser-compat.md). All
action refs are pinned to full commit SHAs for supply-chain safety.
## Summary
- keep `CopilotChat` agents aligned to SDK-generated thread IDs even
when `/connect` is intentionally skipped for non-explicit threads
- stabilize `CopilotKitProvider` default object props so rerenders do
not re-sync an empty local agent registry and replace the live
remote/Intelligence agent mid-run
- add regression coverage for SDK-generated thread frontend-tool
follow-up runs and provider empty-agent rerender stability
- add a focused langgraph-python showcase demo, aimock fixture,
Playwright smoke, and QA checklist for ENT-658
- add a patch changeset for `@copilotkit/react-core`
## Testing
- `npx nx run @copilotkit/react-core:test --
src/v2/components/chat/__tests__/CopilotChat.absentThreadConnect.test.tsx`
- `npx nx run @copilotkit/react-core:test --
src/v2/providers/__tests__/CopilotKitProvider.stability.test.tsx`
- Pre-commit hook passed: `pnpm run test` and `pnpm run check:packages`
- Verified exact `CopilotKit/Intelligence` repro branch
`mme/threadid-repro`: unchecked `Explicit threadId`, sent `invoke
testFrontendToolCalling with label X`, confirmed user message/tool
card/assistant reply remain visible
- Verified the same Intelligence repro with `Explicit threadId` checked
- `pnpm exec playwright test
tests/e2e/threadid-frontend-tool-roundtrip.spec.ts --project=chromium
--workers=1` from `showcase/integrations/langgraph-python`
## QA Checklist
- [x] Reproduce the reset in `CopilotKit/Intelligence` branch
`mme/threadid-repro` with `Explicit threadId` unchecked
- [x] Confirm generated-thread frontend-tool round-trip preserves the
user message, tool card, and assistant response
- [x] Confirm explicit-thread frontend-tool round-trip still preserves
the user message, tool card, and assistant response
- [x] Open `/demos/threadid-frontend-tool-roundtrip` in the
langgraph-python showcase demo
- [x] Confirm `Explicit threadId` is unchecked and the chat starts in
SDK-generated thread mode
- [x] Send `invoke testFrontendToolCalling with label X`
- [x] Confirm the user message remains visible
- [x] Confirm the `testFrontendToolCalling` card remains visible and
shows `label: X` plus `result: handled X`
- [x] Confirm the assistant reply `Frontend tool finished for X.`
appears
- [x] Confirm the chat does not return to the empty state
- [x] Repeat with `Explicit threadId` checked and confirm the
explicit-thread path is unchanged
## Notes
The visible reset had two frontend-side causes. First, the chat and
agent could diverge when the SDK generated the thread ID. Second, in
Intelligence mode, provider rerenders could re-sync an empty local agent
registry and replace the live remote agent instance mid-run, dropping
the in-memory chat stream. Both fixes live in `@copilotkit/react-core`.
The Playwright file is intentionally a smoke test for the demo
route/toggle. The source-level regressions live in
`CopilotChat.absentThreadConnect.test.tsx` and
`CopilotKitProvider.stability.test.tsx`.
The previous regression (#5041, shared root cause with #4739) slipped through
because the original coverage (use-agent-thread-isolation.test.tsx) lived
next to the per-thread-cloning feature and was deleted alongside it when
cloning was reverted. The invariant outlived the feature but the tests didn't.
Relocate to packages/react-core/src/__tests__/ and rename as a contract test
so future implementation swaps (cloning, effect, prop drilling, context) keep
it in scope. Tightened the header docstring to spell out the invariant and the
reason for the placement.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
useAgent now syncs agent.threadId from CopilotChatConfigurationProvider when
the caller marked the threadId as explicit. Without this, AbstractAgent's
constructor mints a random UUID and ProxiedCopilotRuntimeAgent ships it in
/agent/run, /agent/connect, /agent/stop — diverging from the threadId app code
reads via useThreads, breaking thread persistence and causing 404s on lookup.
This was originally fixed by per-thread agent cloning in #3525. That cloning
was reverted in May 2026 because it wiped state on tool calls, and the revert
only restored the explicit assignment in V2 CopilotChat — leaving headless
useAgent (issue #4739) and the V1 chat hook path unfixed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Zod 4 made the key schema mandatory for z.record, so the single-argument
z.record(valueType) form is a compile-time error (TS2554) when built against
Zod 4. @copilotkit/react-core declares zod ">=3.0.0", so downstream apps on
Zod 4 are affected; runtime parsing is unaffected under both majors.
- react-core + vue MCPAppsActivityContentSchema: toolInput now uses the
two-argument z.record(z.string(), z.unknown()) form
- react-core defineToolCallRenderer test: same fix for a metadata schema
- add a toolInput field-contract test (round-trips mixed value types)
- add copilotkit/no-single-arg-zod-record oxlint rule (autofix), enabled as
error for packages/**; the incompatibility is type-level, so no runtime
test can guard it while the workspace lockfile pins Zod 3
Closes#4295
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace docs URLs that currently 301 through the legacy redirect catalog
with their canonical post-cutover destinations so users clicking links
from console warnings, JSDoc, and in-product help land in one hop.
URLs updated:
- /premium#how-do-i-get-access-to-premium-features
-> /premium/overview#getting-access
- /coagents/quickstart/langgraph -> /langgraph-python/quickstart
- /coagents/shared-state/predictive-state-updates
-> /langgraph-python/shared-state/predictive-state-updates
- /reference/v1/hooks/useCopilotChatHeadless_c
-> /reference/v2/hooks/useCopilotChatHeadless_c
- /coagents/troubleshooting/common-issues
-> /langgraph-python/troubleshooting/common-issues
- /quickstart#get-a-copilot-cloud-public-api-key
-> /built-in-agent/quickstart#create-a-free-account
- /premium -> /premium/overview
URLs left as-is because they already resolve 200 with no redirect:
/migration-guides/migrate-attachments, /migration/render-message,
/telemetry.
Hook bypassed: pre-commit test failed in @copilotkit/web-inspector due
to missing jsdom dependency in its package.json (unrelated to this
change; no overlap with edited files or URLs). Tests for the four
affected packages (react-core, react-ui, shared, runtime) pass.
The release-PR workflow's pre-commit hook ran the full test suite and
failed because @tanstack/virtual-core 3.13.18 has a latent bug — its
scrollToIndex schedules a nested rAF that calls
`this.targetWindow.requestAnimationFrame(verify)` with no null-check.
The virtualizer's cleanup nulls `targetWindow` on React unmount, so the
queued rAF fires post-unmount and throws. All 1170 tests passed, but
vitest exited non-zero from the unhandled error.
Wrap rAF on both globalThis and window (separate bindings in
vitest+jsdom; tanstack uses `targetWindow.rAF` which resolves to
`window.rAF`) so callbacks hitting this specific error are swallowed.
Also fix the lint-fix lefthook command — `[ -n "{staged_files}" ]`
broke on multi-file expansion ("[: <path>: unexpected operator")
because lefthook interpolates files as space-separated words, not a
quoted string. Use `set --` to put them in positional args.
## Release monorepo v1.57.2
**Scope:** `monorepo` | **Bump:** `patch`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `monorepo` packages to `1.57.2`
and generated AI-enhanced release notes.
2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
must pass before merging. This is the review gate.
3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.
4. **When this PR is merged**, the `release / publish` workflow
automatically:
- Builds all packages
- Publishes the `monorepo` packages to npm at version `1.57.2`
- Creates git tag `monorepo/v1.57.2`
- Creates a GitHub Release with the final release notes
### Before merging
- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)
---
> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
15+ type re-exports from headless layer. expo-document-picker and
expo-file-system as optional peer deps. InterruptEvent,
ReactFrontendTool, ReactHumanInTheLoop added to headless.ts.
## What does this PR do?
Adds a `position?: \"left\" | \"right\"` prop to the v2 `CopilotSidebar`
(and the underlying `CopilotSidebarView`), letting consumers anchor the
sidebar to either side of the viewport. Defaults to `\"right\"` so
existing usage is unchanged.
```tsx
<CopilotSidebar position=\"left\" />
```
### What changes when `position` flips
- **Anchor:** `cpk:right-0` ↔ `cpk:left-0`
- **Border side:** `cpk:border-l` ↔ `cpk:border-r`
- **Off-screen translate (closed state):** `cpk:translate-x-full` ↔
`cpk:-translate-x-full`
- **Body push margin:** `document.body.style.marginInlineEnd` ↔
`marginInlineStart` (with the matching `transition` CSS property name)
- **Aside element:** picks up a `data-position` attribute for
styling/test hooks
`position` is in the `useLayoutEffect` deps, so toggling it at runtime
cleans up the prior side's body margin before applying the new one.
### Tests
New `CopilotSidebarView.position.test.tsx` (7 cases) —
default/right/left class assertions, off-screen translate direction, and
verification that the wrapper forwards through to the view. All 32
sidebar-area tests pass; full react-core suite (1167 tests) green with
no regressions.
### Storybook
Added `RightPosition` and `LeftPosition` stories under
`UI/CopilotSidebarView` for visual diffing.
## Related PRs and Issues
- N/A
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] \"Allow edits by maintainers\" is checked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The toggle button is hardcoded right-anchored (cpk:bottom-6 cpk:right-6).
When the sidebar sits on the left, the button should mirror to the left
so it lives behind/under the chat panel — otherwise it floats on the
opposite side from the sidebar it controls.
CopilotSidebarView now passes a position-aware className override into
the toggle slot (left-6 + right-auto, merged via tailwind-merge so the
default right-6 is dropped). Behavior on the right is unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lets consumers anchor the v2 CopilotSidebar to either side of the
viewport instead of the hardcoded right side. The prop flips the fixed
anchor, the border side, the off-screen translate direction, and the
body push margin (marginInlineStart vs marginInlineEnd) so the layout
mirrors correctly. Defaults to "right" for backward compatibility.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add stable testids and rendering surfaces for the three tool-rendering
cells so the e2e suite can distinguish each cell's strategy:
- tool-rendering: register useRenderTool for get_stock_price and
roll_d20; new StockCard / D20Card components with testids
stock-card / d20-card / stock-price / stock-change / d20-value.
Rename FlightListCard testid flight-list-card -> flights-card.
- tool-rendering-default-catchall: drop the custom shadcn
useDefaultRenderTool registration so the cell is truly zero
custom-render-hooks. The framework's built-in
DefaultToolCallRenderer now paints every tool call, with stable
data-testid='copilot-tool-render' wrapper plus data-tool-name,
data-args, and data-result attributes for inspection without
expanding the card.
- tool-rendering-custom-catchall: rename the wildcard renderer's
testids from custom-catchall-* to custom-wildcard-* so the cell
is distinguishable from the (now-OOTB) default-catchall demo.
- packages/react-core: when no per-tool / wildcard renderer is
registered, useRenderToolCall now falls back to the built-in
DefaultToolCallRenderer instead of returning null.
## Summary
- Extract `CopilotKitContext` and `useCopilotKit` into standalone
`context.ts` in react-core, enabling cross-platform reuse without web
dependencies
- Add new `@copilotkit/react-native` package with lightweight provider,
polyfills, and streaming fetch
- All hooks (`useAgent`, `useFrontendTool`, `useHumanInTheLoop`, etc.)
are re-exported directly from react-core — no reimplementation
## Motivation
CopilotKit's React hooks are platform-agnostic, but the barrel import in
`@copilotkit/react-core` pulls in web-only dependencies (Radix UI, Lit,
A2UI renderer, react-dom, CSS). This makes the package unusable in React
Native without extensive Metro shimming.
By extracting the React context into a standalone entry point
(`@copilotkit/react-core/v2/context`), the new
`@copilotkit/react-native` package can provide its own lightweight
provider while reusing all existing hooks.
## What's in `@copilotkit/react-native`
| Export | Description |
|--------|-------------|
| `CopilotKitProvider` | Lightweight provider — no DOM, CSS, Radix, Lit,
or A2UI deps |
| `installStreamingFetch()` | XHR-based streaming fetch for
`response.body.getReader()` support |
| `@copilotkit/react-native/polyfills` | All polyfills at once
(ReadableStream, TextEncoder, crypto, DOMException, window.location) |
| `@copilotkit/react-native/polyfills/*` | Granular per-polyfill imports
(`/streams`, `/encoding`, `/crypto`, `/dom`, `/location`) for users who
need to avoid overriding their own shims |
| `useAgent`, `useFrontendTool`, etc. | Re-exported from react-core
(shared context) |
## Usage
```tsx
// index.js (entry point, before other imports)
import "@copilotkit/react-native/polyfills";
import { installStreamingFetch } from "@copilotkit/react-native";
installStreamingFetch();
// App.tsx
import { CopilotKitProvider, useAgent, useCopilotKit } from "@copilotkit/react-native";
function App() {
return (
<CopilotKitProvider runtimeUrl="https://your-server/api/copilotkit">
<ChatScreen />
</CopilotKitProvider>
);
}
```
## Test plan
- [x] `nx run react-core:build` passes
- [x] `nx run @copilotkit/react-native:build` passes
- [x] `nx run react-core:test` — all 1153 tests pass
- [x] Manual test in React Native app (tested during development with
bare RN 0.84 project)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Extract CopilotKitContext, useCopilotKit, and LicenseContext into
src/v2/context.ts. Add src/v2/headless.ts barrel export for
platform-agnostic hooks. Add v2/context and v2/headless entry
points to tsdown config and package.json exports. Update all hook
imports to use the new context module. Always subscribe to onError
in web provider (matching RN pattern). Use batchedForceUpdate for
onMessagesChanged. Replace extraDeps spread with JSON.stringify in
useFrontendTool and useRenderTool dependency arrays.
The Intelligence platform's MCP tool was renamed from `bash` to
`copilotkit_knowledge_base_shell` (intelligence/mme/integrate-sl
4256c13). Update the indicator's `DEFAULT_TOOL_PATTERNS` to match the
new name so the pill keeps rendering on the right assistant slots.
Test fixtures that previously used the bare `bash` name follow the
rename — both the default in `emitAssistantMessageWithToolCalls` and
the explicit `tc_match` entry in the tool-match condition test.
The previous comment referenced a "200 ms poll interval" that the
indicator no longer uses (polling was removed when we switched to the
tool-call pending-grace timer). Updates the rationale to mention the
current self-gates (latest matching-assistant slot + pending grace
window).
Reverts the cosmetic changes that crept in alongside the auto-mount
edit — restores `stateSnapshot?` (always passed at runtime, no functional
difference), the original concise comments around the memo's comparison
function, and the original combined value+type imports. The only
remaining framework change in this PR is the new auto-mount block:
when `copilotkit.intelligence !== undefined` and the message is an
assistant message, push an `<IntelligenceIndicator>` after the message
slot.
Indicator e2e suite still green (10/10).
Renames the proxy-config field, the field on the agent instance, and
all matching references in tests and the useCopilotKit reference page.
"runtime" reads more naturally now that the proxy concept is documented
as "a local agent that delegates to a runtime agent" rather than
"remote agent" — the latter conflates with `remoteAgents` (the
registry of agents fetched from the runtime), which keeps its name.
No behavioral change; the field still controls the outbound REST URL
used by the proxy.
Removes the numberOfMessagesInRun, isInLatestRun, and isRunning props on
MemoizedCustomMessage along with the per-render runMetadata derivation
that fed them. Authored renderers observe run state via useAgent's
OnRunStatusChanged / OnMessagesChanged subscriptions, which forceUpdate
the renderer independently of the memo's bail-out — the extra
invalidation inputs added nothing for that canonical path and only
masked staleness for renderers that read run state from closure
without subscribing.
The IntelligenceIndicator itself uses useAgent and remains correct.
Net change: ~95 lines removed; one less O(n) scan through messages per
chat re-render. All chat e2e tests (587) pass, including the indicator
suite (10).
Replaces the agent.isRunning-driven phase entry (and its 200 ms polling
fallback) with a 100 ms grace timer on unresolved matching tool calls.
Replay flashes (tool call + result in the same tick during connectAgent
history hydration) no longer cross the threshold, so the pill stops
appearing on completed historical runs.
Spinner exits as soon as either agent.isRunning falls or a "real
follow-up" message arrives — assistant prose, a fresh user turn, or
anything that isn't a tool result / empty-content tool-call wrapper.
Multi-step tool chains stay on a single continuous pill (the
latest-matching-assistant slot still moves between messages without a
fade animation when the next bash assistant lands).
Polling and the snapshot-subscriber comment were a misdiagnosis of a
test artifact: useAgent's OnRunStatusChanged subscription is what the
rest of CopilotKit (CopilotChat stop button, MCPAppsActivityRenderer,
chat suggestions) relies on for isRunning falling-edge re-renders.
Tests cover three new cases: replay-flash suppression, multi-step
continuity across tool-result interleaving, and exit-on-prose-followup.