Three React components for the AG-UI event inspector webview:
- FilterBar: category toggles, text search, agent/run dropdowns
- EventList: scrollable event stream with sticky auto-scroll and event summaries
- EventDetail: side panel with metadata display and collapsible JSON tree viewer
## Summary
- `sendMessage` returns `Promise<void>` but `usePushToTalk` accessed
`.id` on the result, causing a `TypeError` crash during push-to-talk
transcription
- Added guard to check if `sendFunction` returns a message before
accessing `.id`
- Updated `SendFunction` type to accept `Promise<Message | void>`
Fixes#3011
## Test plan
- [x] Added unit tests verifying void return handling
- [x] Added unit tests verifying message-with-id return handling
- [x] All react-ui tests pass
## Summary
- `.copilotKitMessagesFooter` used `width: 97%` which caused horizontal
overflow in constrained containers
- Changed to `width: 100%` with `box-sizing: border-box` so padding is
included in the width calculation
Fixes#2325
## Test plan
- [x] All react-ui tests pass
- [ ] Visual verification: render CopilotKit chat in a constrained
container (e.g. 300px sidebar) and confirm no horizontal scrollbar
appears
## Summary
Fixes#2194
`CopilotModal` and `CopilotModalProps` were not exported from
`@copilotkit/react-ui`, making them inaccessible to consumers. Adds both
exports to the chat barrel file.
## Test plan
- [ ] Verify `CopilotModal` and `CopilotModalProps` are importable from
`@copilotkit/react-ui`
## Summary
- `Button` component wrapped with `React.forwardRef` to fix ref warning
from Radix UI's `DropdownMenuTrigger asChild` pattern
Closes#2947
## Test plan
- [x] All affected packages build successfully
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- The loading spinner only appeared when the last message had role
"user"
- During tool execution the last message can have role "tool", hiding
the indicator
- Now also show it when the trailing message is a tool result
Closes#3055
## Summary
- Skips keydown handling during IME composition (e.g. CJK input) by
checking `isComposing`/`keyCode 229`
- Guards textarea measurement from resetting value mid-composition,
which would break the composition session
Closes#3318
## Summary
- Always calls `onChange("")` after send in controlled mode so the
parent component is notified to clear its state
- Previously `onChange` was only called inside the `!isControlled`
branch, leaving controlled parents unaware the input was submitted
Closes#3593
Closes#1921
The Markdown component accepted `components` but not `urlTransform`,
preventing users from customizing URL sanitization. Added `urlTransform`
as an optional prop with passthrough to `MemoizedReactMarkdown`.
Split from #3838.
## Summary
- `buildFrontendTools` only checked `tool.available !== false` but the
`available` field can also be the string `"disabled"`, which is truthy
- Added `tool.available !== "disabled"` to the filter so disabled tools
are properly excluded
- Added test verifying tools with `available: "disabled"` are filtered
out
## Test plan
- [x] New test: tool with `available: "disabled"` is excluded from
`buildFrontendTools`
- [x] All existing available-filtering tests still pass
- [x] Full core test suite passes (338 tests)
## Summary
- onThumbsUp/onThumbsDown/onReadAloud/onRegenerate callbacks on
CopilotChatAssistantMessage were receiving the browser SyntheticEvent
instead of the AssistantMessage object
- Wrapped the onClick handlers to pass the message explicitly:
`onClick={() => onThumbsUp(message)}`
- Same fix applied to onReadAloud and onRegenerate for consistency
## Test plan
- [x] Red-green verified: test confirms callback arg has `id`, `role`,
`content` and NOT `nativeEvent`/`target`
- [x] Full react-core test suite passes (1071 tests)
- [x] Build passes
Closes#3457
## Summary
- useRenderCustomMessages threw "Agent not found" when the agent was
undefined during the connecting state
- Changed the throw to return null, allowing the component to render
gracefully while the agent is being resolved
## Test plan
- [x] Red-green verified: test calls hook with nonexistent agent,
asserts returns null not throws
- [x] Full react-core test suite passes (1071 tests)
- [x] Build passes
Closes#3497
## Summary
- CopilotListeners called useAgent() unconditionally, which throws when
no agents are registered and no runtimeUrl is configured
- Split into CopilotListeners (outer, handles error subscription) and
CopilotListenersAgentSubscription (inner, uses useAgent)
- Inner component only renders when agents exist or a runtime is
configured
## Test plan
- [x] Red-green verified: render CopilotListeners with no agents, assert
no throw
- [x] Full react-core test suite passes (1071 tests)
- [x] Build passes
Closes#3249
## Summary
- `checkForUpdates()` in dev console is now gated on `showDevConsole`,
preventing unnecessary network requests when the console is disabled
Closes#2751
## Test plan
- [x] `@copilotkit/react-core` tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- LangChain adapter now always uses `randomId()` for message stream IDs
instead of relying on `lc_kwargs.id` (which GoogleGenerativeAI sets to
`"0"` for all messages)
Closes#2929
## Test plan
- [x] `@copilotkit/runtime` tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Default `max_tokens` was hardcoded to 1024, causing truncated
responses for most use cases
- Increased default from 1024 to 4096 while still respecting
user-provided `maxTokens`
Fixes#2354
## Test plan
- [x] Added test verifying default max_tokens is 4096 when not specified
- [x] Added test verifying user-provided maxTokens is passed through
- [x] All existing anthropic adapter tests pass
## Summary
- Providers like `@ai-sdk/openai-compatible` emit sequential IDs
(`txt-0`, `reasoning-0`, `msg-0`) that are treated as unique but collide
across requests
- The existing check only caught the literal `"0"` — expanded to match
the `^(txt|reasoning|msg)-0$` pattern
- Both `text-start` and `reasoning-start` event handlers now use
`randomUUID()` for these non-unique IDs
## Test plan
- [x] Added `provider-id-collision.test.ts` with 4 tests covering txt-0,
reasoning-0, msg-0, and legitimate ID preservation
- [x] All 1226 runtime tests pass (including existing
config-tools-execution tests)
Fixes#3410, #3623
## Summary
- `onAfterRequest` middleware callback was called with `{}` instead of
actual hook parameters
- Now forwards `threadId`, `runId`, `inputMessages`, `outputMessages`,
and `url` from the v2 runtime's `hookParams`
- The `onBeforeRequest` handler at line 566 already did this correctly;
this fix makes `onAfterRequest` consistent
## Test plan
- [x] Added `on-after-request.test.ts` verifying the callback receives
threadId and runId (not empty object)
- [x] All 1226 runtime tests pass
Fixes#2124
## Summary
Fixes#2450
Token trimming in the Anthropic adapter could remove assistant messages
containing `tool_use` blocks while keeping the corresponding user
messages with `tool_result` blocks. Anthropic's API rejects orphaned
`tool_result` messages. This adds a post-processing step that removes
orphaned `tool_result` blocks after trimming.
## Test plan
- [ ] Verify token trimming preserves tool_use/tool_result pairing
- [ ] Verify orphaned tool_result blocks are removed
- [ ] Verify messages with mixed content (tool_result + text) retain
non-orphaned content
## Summary
Fixes#2405
Both OpenAI and Anthropic adapters used a hardcoded token limit for
message trimming. This adds an optional `maxInputTokens` constructor
parameter to both adapter classes, allowing consumers to override the
default context window limit.
## Test plan
- [ ] Verify default behavior unchanged when `maxInputTokens` not
provided
- [ ] Verify custom `maxInputTokens` is passed through to
`limitMessagesToTokenCount`
## Summary
Fixes#2504
When Anthropic returns a `tool_use` block for a tool not registered in
the current action set, the adapter would crash trying to process it.
This adds a check against known action names and skips unknown tool_use
blocks gracefully.
## Test plan
- [ ] Verify known tools are still processed normally
- [ ] Verify unknown tool_use blocks are skipped without crashing
## Summary
- langchain-mcp-adapters sends tool call result content as an array of
`{type:"text", text:string}` objects
- Extracts and joins text parts so downstream consumers always receive a
plain string
Closes#2922
Closes#3141
`applyCredentialsToAgents`/`applyCredentialsToAgent` were defined but
never called from `initialize()`, `setAgents__unsafe_dev_only()`, or
`addAgent__unsafe_dev_only()`. Only remote agents received credentials
via constructor. Local agents now receive credentials alongside headers.
Split from #3838.
Closes#3741
Add `toolCallId` prop to all three status branches (InProgress,
Executing, Complete) of the ToolCallRenderer, and update the
`ReactToolCallRenderer` type to include it in the discriminated union.
Split from #3838.
Closes#1936
`extractParametersFromSchema` now preserves enum values as structured
data on string parameters, recursively extracts nested object
attributes, and converts object arrays to `object[]` type with
attributes. This ensures complex MCP tool schemas survive the
`Parameter[]` -> Zod conversion without losing nested structure.
Split from #3838.
Core test (core-subscribe-to-agent.test.ts):
- Replace `as any` on RunErrorEvent with proper EventType.RUN_ERROR type
- Consolidate 4 identical lifecycle tests into one `it.each` block
- Extract silenceConsoleError() helper used across 10 error-path tests
- Document the remaining intentional `as any` in guardAll JS-consumer test
Angular test (agent.spec.ts):
- Make MockAgent extend AbstractAgent, removing all 4 `as unknown as AbstractAgent` double casts
- Replace all `any` types with proper AG-UI types (Message, State, AgentSubscriber, etc.)
- Emit helpers now pass correct AgentSubscriberParams instead of empty objects
- Use userMsg() factory for properly typed test data
- Simplify ProxiedCopilotRuntimeAgent assertion with single narrowing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
React's useAgent was missing onRunErrorEvent under OnRunStatusChanged,
causing isRunning to stay true after protocol-level RUN_ERROR events
(infinite spinner). Angular already handled this via PR #3749.
Also updates SubscribeToAgentSubscriber JSDoc to accurately document
onRunErrorEvent as the sixth allowed callback with rationale for its
inclusion despite having stopPropagation in its return type, and adds
a core-level test for onRunErrorEvent firing immediately during
throttle windows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Restore `let active` guard in useAgent subscription effect (removed
during refactor, causing ReferenceError in batchedForceUpdate)
- Add onRunErrorEvent to SubscribeToAgentSubscriber allowed keys
(added on main for Angular AgentStore)
- Fix throttle tests to use async act() for onStateChanged assertions
(batchedForceUpdate uses queueMicrotask, needs await to flush)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hand-rolled leading+trailing throttle (scheduleOrFlush,
setTimeout, throttleActive state machine) with TanStack Pacer's
Throttler class. Clean up comments to describe the method generically
rather than only in terms of throttling.
No public API changes — throttleMs / defaultThrottleMs stay as-is.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address review feedback: replace Object.entries loop in guardAll
with explicit per-key wrapping so TypeScript can verify all
callback signatures without any casts. Also replace Promise
duck-typing in safeCall with instanceof Promise, and use
Record<string, unknown> instead of any in the unsupported keys check.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the unsupported-key scan from guardAll into a pre-scan that runs
before the throttled/unthrottled branch, so JS consumers passing
unsupported callbacks (e.g. onEvent via `as any`) get a console.warn
regardless of whether throttling is active. Previously, guardAll only
ran on lifecycleOnly in the throttled path, silently dropping
unsupported keys without any diagnostic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Error state handling in Angular CopilotkitAgentFactory (parity with React)
- Add emitError to guardAll for dropped callbacks (structured monitoring)
- Add emitError to setDefaultThrottleMs for monitoring parity
- Replace empty .catch(() => {}) with logging pattern on emitError
- Add generic typing to safeCall for call-site parameter safety
- Clarify flushPending comment re: sync-only unsubscribe guard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `satisfies readonly (keyof AgentSubscriber)[]` to SUBSCRIBE_TO_AGENT_KEYS
so upstream AG-UI renames are caught at compile time
- Fix SubscribeToAgentSubscriber JSDoc: separate AG-UI event handlers (mutation
semantics) from per-item callbacks (void return, excluded for surface area),
note that included lifecycle callbacks also return AgentStateMutation
- Emit invalid throttleMs through emitError so monitoring systems see
misconfiguration, not just console.error
- Add flushPending comment explaining the re-checked `active` flag between
onMessagesChanged and onStateChanged dispatch
- Derive SubscribeToAgentFn from CopilotKitCore['subscribeToAgent'] instead
of manually duplicating the signature (eliminates desync risk)
- Derive StubCore from Pick<CopilotKitCore, ...> in Angular tests
- Add test: async rejection during throttled trailing-edge flush
- Add test: clearing defaultThrottleMs(undefined) makes new subs unthrottled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add .catch() to emitError() in reportError to prevent unhandled
promise rejections if the error reporting infrastructure itself fails
- Add console.warn in guardAll when unsupported callback keys are
dropped, so JS consumers discover they need agent.subscribe() directly
- Hoist ALLOWED_KEYS Set to module scope to avoid per-call allocation
- Fix SubscribeToAgentSubscriber JSDoc: explicitly list the 5 supported
callbacks and mention onNewMessage/onNewToolCall as excluded
- DRY subscribeToAgent() method JSDoc by referencing the type instead
of duplicating the AG-UI exclusion rationale
- Fix safeCall comment: async error path returns Promise<undefined>,
not undefined
- Add JSDoc to setDefaultThrottleMs setter; simplify getter JSDoc
- Fix use-agent.tsx: consistent camelCase callback names, fix @default
tag format, replace unresolvable {@link} with plain text reference
- Add test: emitError/onError integration for SUBSCRIBER_CALLBACK_FAILED
- Add test: unsubscribe + throw combination in onMessagesChanged
- Add test: unsupported callback keys dropped with console.warn
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AgentStore now receives a SubscribeToAgentFn function instead of a
CopilotKitCore instance. The factory binds core.subscribeToAgent once
and passes the bound function, keeping AgentStore decoupled from the
core class while preserving all throttle and error-protection behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add SUBSCRIBER_CALLBACK_FAILED error code and call emitError() in
safeCall so subscriber failures are visible to monitoring systems
and onError handlers, not just console.error
- Document that safeCall intentionally discards return values on the
error path (including lifecycle AgentStateMutation)
- Extract SUBSCRIBE_TO_AGENT_KEYS tuple as single source of truth for
both the SubscribeToAgentSubscriber type and the runtime ALLOWED_KEYS
set, eliminating the one-directional sync risk from the satisfies pattern
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>