Commit Graph

1227 Commits

Author SHA1 Message Date
Maxim e5cbcb670a fix(react): update zod-regression test mock for hook-based render tool calls
The mock was missing addHookRenderToolCall/removeHookRenderToolCall methods
introduced by the separate hook vs prop render tool call storage refactor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:39:27 +04:00
Maxim a57dc40fca fix(react): remove as casts and clean up unused imports
Address review feedback: remove `as ReactToolCallRenderer` cast in
useFrontendTool by guarding with `tool.parameters` check (matching
CopilotKitProvider's pattern). Remove unused ReactToolCallRenderer
imports from use-frontend-tool.tsx and use-render-tool.tsx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:28:26 +04:00
Maxim fd44b5ba5f test(react): add StrictMode render tool call preservation tests
Add tests verifying:
- useFrontendTool render registrations survive StrictMode remount
- Hook and prop render entries coexist through StrictMode remount
- Hook render entries survive provider prop changes
- Update useRenderTool test mock to match new addHookRenderToolCall API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:28:26 +04:00
Maxim 829589a625 fix(react): separate hook vs prop render tool call storage to fix StrictMode race
CopilotKitProvider's didMountRef guard breaks under React Strict Mode:
refs persist across the simulated unmount/remount cycle, causing the
provider's setRenderToolCalls effect to overwrite registrations from
useFrontendTool and useRenderTool hooks.

Split render tool call storage in CopilotKitCoreReact into two layers:
- _renderToolCalls: prop-based entries (set by provider)
- _hookRenderToolCalls: hook-based entries (set by useFrontendTool/useRenderTool)

The renderToolCalls getter merges both with a cached result for
useSyncExternalStore referential stability. Provider and hooks can
never overwrite each other, eliminating the race by design.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:28:26 +04:00
github-actions[bot] 60de47cda2 chore(post-release): update version to 1.54.0-next.3 2026-03-11 11:14:53 +00:00
Tyler Slaton 1550e5d541 fix(angular): address issue with the angular build failing due to typing
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-03-10 15:50:14 -07:00
Tyler Slaton 26541a9268 chore(lint): run formatter on changes
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-03-10 15:49:34 -07:00
Alem Tuzlak ea98c8649c test: add Zod regression tests for Standard Schema migration
Adds 77 regression tests across shared, core, react, and agent packages
proving that existing Zod usage produces identical results after the
Standard Schema V1 migration. Covers JSON Schema output identity,
additionalProperties stripping, schema pass-through to AI SDK, React
hook compatibility, and complex Zod patterns (unions, discriminated
unions, nested objects, arrays, constraints, defaults, etc.).
2026-03-10 15:36:27 -07:00
Alem Tuzlak 55f64f08d5 chore: fix eslint issue 2026-03-10 15:36:27 -07:00
Alem Tuzlak f64a815cea feat: migrate public API from Zod to Standard Schema V1
Replace Zod-specific types (z.ZodType, z.infer) with Standard Schema V1
interfaces across the V2 public API surface. This allows users to use any
Standard Schema V1 compatible library (Zod 3.24+, Valibot v1+, ArkType v2+)
for tool parameters, with zero breaking changes for existing Zod users.

- Add schemaToJsonSchema() utility in shared with Standard JSON Schema V1
  support and Zod fallback
- Update FrontendTool, ToolDefinition, useRenderTool, useComponent, and
  Angular tool types to accept StandardSchemaV1
- Move zod from dependencies to devDependencies in core and angular
- Add runtime tests (40) and type-level tests (38) across all packages
  verifying Zod, Valibot, and ArkType compatibility
2026-03-10 15:36:26 -07:00
Tyler Slaton ddf8346911 chore: run pnpm format
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-03-10 17:48:08 -04:00
giulio-leone 921dd1944d fix: address all 7 PR review feedback items for tool args guard
CRITICAL 1: ensureObjectArgs (renamed from safeParseToolArgs) in
run-handler.ts now throws on non-object parsed results so the catch
block fires TOOL_ARGUMENT_PARSE_FAILED structured errors.

CRITICAL 2: partialJSONParse return type reverted from
Record<string,unknown> to unknown — callers handle the type.

IMPORTANT 3: safeParseToolArgs consolidated into shared/utils.ts and
exported. Agent/index.ts imports from @copilotkitnext/shared. V1 keeps
a local copy with a comment noting it mirrors the shared version.

IMPORTANT 4: All non-object fallback sites now emit console.warn with
consistent [CopilotKit] prefix format.

IMPORTANT 5: Removed console.warn from getPartialArguments catch block
(incomplete JSON is expected during streaming). Warning is now only in
the try-block non-object guard.

TEST 6: Added run-handler-ensureObjectArgs.test.ts covering valid
object, string, number, array, null, boolean, and undefined inputs.

TEST 7: Added conversion.test.ts for v1 safeParseToolArgs covering
valid object, string, number, array, malformed JSON, null, boolean,
and empty string inputs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-10 21:13:16 +01:00
Ran Shem Tov 5a9cf7d26e feat: set default a2ui theme to fit cpk main theme 2026-03-10 10:04:11 +01:00
Jordan Ritter ed61eefb67 fix: extend non-object tool argument guards to all parse sites
Guard partialJSONParse, both agui-to-gql converters, and
run-handler.ts against LLMs returning non-object JSON (strings,
arrays, numbers, booleans, null) as tool arguments. Adds
safeParseToolArgs helper to run-handler.ts and tests for all sites.
2026-03-09 15:10:00 +01:00
Jordan Ritter 792a0b1241 fix: guard against malformed JSON in tool argument parsing
Rename ensureObjectArgs to safeParseToolArgs in v2/agent and v1/runtime,
folding JSON.parse into the guard so malformed JSON (e.g. "{broken") is
caught instead of throwing an unhandled SyntaxError up the call stack.

Add console.warn on parse failure in all three locations (v2/agent,
v1/runtime, v1/runtime-client-gql) so malformed tool arguments are
visible in logs rather than silently swallowed.

Update the v2/agent unparseable-JSON test to expect {} instead of throw.
2026-03-09 15:09:59 +01:00
Jordan Ritter a7662957fc test: add coverage for non-object tool argument guards
Tests for ensureObjectArgs in @copilotkitnext/agent (via
convertMessagesToVercelAISDKMessages) and getPartialArguments in
@copilotkit/runtime-client-gql (via convertGqlOutputToMessages).

Covers string, array, null, number, boolean, empty args, and
unparseable JSON — all should fall back to {}.

Also documents a pre-existing gap: JSON.parse is unguarded before
ensureObjectArgs in v2/agent, so unparseable JSON throws instead of
falling back to {}. The test explicitly expects the throw.

v1/runtime's convertGqlInputToMessages uses class-transformer which
makes isolated unit testing impractical; the same guard logic is
covered by the other two test suites.
2026-03-09 15:09:59 +01:00
Jordan Ritter 7315be6271 fix: restore parseError variable in run-handler catch block and remove changeset
The contributor's diff accidentally deleted the `const parseError` assignment
but left references to `parseError.message` and `error: parseError`, causing
a ReferenceError at runtime. Restore it.

Remove changeset — versioning is managed separately.
2026-03-09 15:09:59 +01:00
g97iulio1609 aa8a8a3d11 fix: guard against non-object tool arguments from LLMs
When LLMs return non-object values (e.g. empty string) as tool call
arguments, the parsed result is stored in conversation history. On
subsequent requests, providers like Anthropic reject the non-dictionary
tool_use input with a 400 error, making the conversation permanently
broken.

Fixed in 4 locations across v1 and v2:
- v1 runtime conversion.ts: validate parsed arguments
- v1 client conversion.ts: validate partial arguments
- v2 core run-handler.ts: validate both specific and wildcard tool args
- v2 agent index.ts: validate tool call input conversion

All locations now ensure parsed args are a plain object, falling back to
{} for non-object values.

Fixes #3300
2026-03-09 15:09:59 +01:00
github-actions[bot] d2cf028903 chore(post-release): update version to 1.53.1-next.2 2026-03-07 08:47:03 +00:00
Ran Shemtov 0c90355847 Merge branch 'main' into feat/a2ui-eager-default-renderer 2026-03-07 09:34:25 +01:00
Jordan Ritter 729a053842 test(react): add tests for useKatexStyles hook
Covers the dynamic CSS import behavior, graceful error handling,
and a regression guard ensuring the static katex CSS import doesn't
creep back into CopilotChatAssistantMessage.
2026-03-06 11:35:17 -08:00
Jordan Ritter e2b2dd9993 fix(react): replace static KaTeX CSS import with dynamic runtime injection (#3314)
## Summary

Fixes #2851

### Problem

Next.js rejects global CSS imports from within `node_modules`, causing
build failures:

```
Global CSS cannot be imported from within node_modules.
Location: node_modules/@copilotkitnext/react/dist/index.mjs

Import trace:
  katex/dist/katex.min.css
  @copilotkitnext/react/dist/index.mjs
  @copilotkit/react-core/dist/v2/index.mjs
```

This affects all Next.js consumers of CopilotKit (versions 1.50.0+).

### Root Cause

The static `import 'katex/dist/katex.min.css'` in
`CopilotChatAssistantMessage.tsx` (line 22) is preserved in the dist
bundle by tsdown's unbundle mode. Next.js webpack statically analyzes
imports and rejects global CSS from `node_modules`.

### Fix

Replace the static CSS import with a `useKatexStyles()` hook that
dynamically loads the stylesheet at runtime via `useEffect`:

- Created `useKatexStyles.ts` — a singleton hook that uses `import()` to
lazy-load KaTeX CSS
- Dynamic imports bypass Next.js static analysis (the css-npm check only
applies to static imports)
- Uses a module-level flag to ensure the CSS is only injected once

### Testing

- 2 consecutive clean test passes: **748/748 tests** (38 test files)
- Type-check shows only pre-existing errors from
`@copilotkitnext/web-inspector` (unrelated)
2026-03-06 15:22:40 -04:00
Jordan Ritter 5884dde045 fix(runtime): clean up stale messagesInProcess when copilotkit:emit-messages suppresses events (#3270)
## Description

Fixes #3208

When using a LangGraph agent with an orchestrator node that routes to
terminal nodes,
the orchestrator typically uses `copilotkit_customize_config(config,
emit_messages=False)`
to suppress its internal LLM output (e.g., routing decisions like
"left_intent") from
being streamed to the frontend.

However, the `dispatchEvent` override in the CopilotKit `LangGraphAgent`
was only
suppressing the events from reaching the `verifyEvents` pipeline — it
was **not**
cleaning up the `messagesInProcess` tracking state. This caused a stale
message record
from the orchestrator to persist and leak into subsequent nodes that
have
`emit_messages=True`, ultimately triggering a `verifyEvents` error:

```
Cannot send 'TEXT_MESSAGE_END' event: No active text message found with ID '...'.
A 'TEXT_MESSAGE_START' event must be sent first.
```


### Root cause (step by step)

1. Orchestrator calls LLM with `emit_messages=False` → metadata has
`copilotkit:emit-messages: false`
2. Orchestrator's LLM streams text → `handleSingleEvent` (in
`@ag-ui/langgraph`) calls
`setMessageInProgress()` unconditionally, then calls `dispatchEvent()`
for `TEXT_MESSAGE_START`
3. CopilotKit's `dispatchEvent` suppresses the event (returns `false`),
but the message
   record is already tracked in `messagesInProcess`
4. When `TEXT_MESSAGE_END` is later suppressed, it also returns `false`
— so
`messagesInProcess` is **never cleared** (the cleanup is gated on `if
(resolved)`)
5. The next node (e.g., `left_node`) starts with `emit_messages=True`.
Its first LLM chunk
(often empty content) sees the **stale** `messagesInProcess` record and
determines
   `isMessageEndEvent = true`
6. It dispatches `TEXT_MESSAGE_END` with the orchestrator's message ID,
but since the
raw event now carries `copilotkit:emit-messages: true`, CopilotKit does
**not** suppress it
7. `verifyEvents` sees a `TEXT_MESSAGE_END` for a message ID that never
had a
   `TEXT_MESSAGE_START` → throws the error

### Fix

When `dispatchEvent` suppresses message events due to
`copilotkit:emit-messages === false`,
it now also nullifies the corresponding
`messagesInProcess[activeRun.id]` entry. This
prevents stale records from leaking across node boundaries.

## Type of Change

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)

## How Has This Been Tested?

Tested manually with a LangGraph agent containing:
- An **orchestrator node** using `copilotkit_customize_config(config,
emit_messages=False)` that classifies user intent and routes via
`Command(goto=...)` to one of two terminal subgraphs
- Two **terminal nodes** (`left_node`, `right_node`) using
`copilotkit_customize_config(config, emit_messages=True)` that call an
LLM and stream the response

Before the fix: every message sent through CopilotSidebar triggered the
`TEXT_MESSAGE_END` error and `INCOMPLETE_STREAM`.
After the fix: messages flow correctly — the orchestrator's internal
output is suppressed and the terminal node's response streams to the
frontend as expected.
2026-03-06 14:46:18 -04:00
Jordan Ritter e27df8947d test(runtime): add tests for dispatchEvent emit-messages/emit-tool-calls filtering
Covers the fix for stale messagesInProcess cleanup, plus general
filtering behavior for both copilotkit:emit-messages and
copilotkit:emit-tool-calls metadata.
2026-03-06 10:46:06 -08:00
github-actions[bot] 6c4aab7d6f chore(post-release): update version to 1.53.1-next.1 2026-03-06 18:32:39 +00:00
Jordan Ritter 3d088cfec3 fix(react-textarea): restore focus to textarea on Escape in CMD+K popup (#3257)
Fixes #399

When the CopilotTextarea hovering popup (CMD+K / CTRL+K) is open and the
user presses `Escape`, the popup loses focus entirely, instead of safely
closing and returning focus to the underlying textarea.

This PR:
1. Adds an `Escape` key handler to the `HoveringInsertionPromptBoxCore`
component so the textarea can intercept the key.
2. Adds a document-level `Escape` listener to `HoveringToolbar` to
cleanly close the popup and use `ReactEditor.focus()` to restore focus
to the Slate editor.

## Testing
1. Run `textarea` example.
2. Type in the input, press `CMD+K` (or `CTRL+K`).
3. Press `Escape`. 
4. The popup closes and focus is successfully returned to the editor
input without needing an extra mouse click.
2026-03-06 14:29:02 -04:00
Jordan Ritter d11d13f869 chore: add comment explaining dual Escape handler interaction 2026-03-06 10:28:47 -08:00
Ran Shem Tov 4a294d2be3 chore: add tests 2026-03-06 19:24:00 +01:00
Ran Shem Tov a53bc556b2 feat: only add a2ui renderer when a2ui is enabled in runtime 2026-03-06 19:24:00 +01:00
Ran Shem Tov 4966e5cf1f feat: eagerly create default A2UI renderer when middleware is enabled 2026-03-06 19:24:00 +01:00
Ben Taylor faf7399919 feat(core): Add Micro Redux helpers for cross-framework state management (#3391)
<!--
Thank you for sending the PR! We appreciate you spending the time to
work on these changes.

Help us understand your motivation by explaining why you decided to make
this change.


**Please PLEASE reach out to us first before starting any significant
work on new or existing features.**

By the time you've gotten here, you're looking at creating a pull
request so hopefully we're not too late.

We love community contributions! That said, we want to make sure we're
all on the same page before you start.
Investing a lot of time and effort just to find out it doesn't align
with the upstream project feels awful, and we don't want that to happen.
It also helps to make sure the work you're planning isn't already in
progress.

As described in our contributing guide, please file an issue first:
https://github.com/ag-ui-protocol/ag-ui/issues
Or, reach out to us on Discord: https://discord.com/invite/6dffbvGU3D


You can learn more about contributing to copilotkit here:
https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md

Happy contributing!

-->

## What does this PR do?

(Describe the changes introduced in this PR)

## Related PRs and Issues

- (Direct link to related PR or issue, if relevant)

## Checklist

- [ ] 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
2026-03-06 11:30:39 -06:00
Mike Ryan c943907737 feat(core): Add Micro Redux helpers for cross-framework state management 2026-03-06 09:16:04 -08:00
github-actions[bot] 1fd878c582 chore(post-release): update version to 1.53.1-next.0 2026-03-06 17:10:16 +00:00
Ran Shemtov ad697dea79 Merge branch 'main' into feat/a2ui-message-renderer-in-react-core 2026-03-06 18:01:06 +01:00
Ran Shem Tov 8667fc7606 feat: move a2ui message renderer to v2s react package 2026-03-06 16:33:36 +01:00
Alem Tuzlak 69d9e869e3 feat(react-core): export CopilotKit from v2 subpath
Re-export the CopilotKit component and CopilotKitProps type from
@copilotkit/react-core/v2 so users can import the full-featured
provider (with 1.50 compatibility layer) directly from the v2 subpath.
2026-03-06 14:04:06 +01:00
Ben Taylor a5ce8abdcc feat(runtime, react): Explicit error handling and retires for IntelligenceAgent* (#3379)
<!--
Thank you for sending the PR! We appreciate you spending the time to
work on these changes.

Help us understand your motivation by explaining why you decided to make
this change.


**Please PLEASE reach out to us first before starting any significant
work on new or existing features.**

By the time you've gotten here, you're looking at creating a pull
request so hopefully we're not too late.

We love community contributions! That said, we want to make sure we're
all on the same page before you start.
Investing a lot of time and effort just to find out it doesn't align
with the upstream project feels awful, and we don't want that to happen.
It also helps to make sure the work you're planning isn't already in
progress.

As described in our contributing guide, please file an issue first:
https://github.com/ag-ui-protocol/ag-ui/issues
Or, reach out to us on Discord: https://discord.com/invite/6dffbvGU3D


You can learn more about contributing to copilotkit here:
https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md

Happy contributing!

-->

## What does this PR do?

This PR expands on reconnection and error handling for the
IntelligenceAgent and IntelligenceAgentRunner interactions with the
Phoenix lib/server, like timeouts and disconnects.

It explicitly defines exponential backoff behavior (Phoenix has default
settings, but they are silently disabled if you add error callbacks.
While that was only done in the AgentRunner to begin with, it felt
strange to leave it as a foot gun elsewhere).

There is also added logic to ensure cleanup messages are sent (at best
effort) before channels are closed.

Additionally:
* each agent run through the IntelligenceAgentRunner now gets its own
web socket connection (before, it only got its own _channel_)
* the IntelligenceAgent now stops a running agent by publishing a stop
(custom AG-UI) message to Phoenix, which is handled by the
IntelligenceAgentRunner.

## Related PRs and Issues

- https://linear.app/copilotkit/issue/ENT-54
- https://linear.app/copilotkit/issue/ENT-59

## Checklist

- [ ] 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
2026-03-05 20:38:15 -06:00
github-actions[bot] 57eb016a10 chore(post-release): update version to 1.53.0 2026-03-05 23:57:25 +00:00
github-actions[bot] 671fd0ae47 chore(post-release): update version to 1.53.0-next.6 2026-03-05 23:41:37 +00:00
Maxim 501e8d70ae fix(runtime): skip afterRequestMiddleware when user didn't configure one
The v1 CopilotRuntime always set afterRequestMiddleware on the v2 runtime,
causing parseSSEResponse to attempt reading the cloned SSE stream body even
when no user hooks existed. This results in "Failed to read SSE response
body in afterRequestMiddleware" warnings. Only register the handler when
the user actually provided afterRequestMiddleware or middleware.onAfterRequest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:38:06 -05:00
github-actions[bot] 3ba2352ef8 chore(post-release): update version to 1.53.0-next.5 2026-03-05 23:06:01 +00:00
Tyler Slaton f822c051de refactor(runtime): mcp -> mcpApps w/ granular config
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-03-05 17:40:28 -05:00
github-actions[bot] 9f12589107 chore(post-release): update version to 1.52.2-next.4 2026-03-05 21:28:52 +00:00
Maxim 2bf990b74b chore: formatting 2026-03-05 16:25:16 -05:00
Maxim d975e98892 feat: add onError prop to CopilotChat, simplify connectAgent error handling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:25:16 -05:00
Maxim 94e4dce4f2 feat: add onError prop to CopilotKitProvider (no publicApiKey gate)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:25:16 -05:00
Maxim e97790e4ae fix: useAgent returns provisional agent on runtime error state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:25:16 -05:00
Maxim 8ccd943284 fix: tool argument parse errors no longer crash the app
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:25:16 -05:00
Maxim bcb5f350ca fix: runAgent emits error instead of re-throwing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:25:16 -05:00
Maxim 1fa0eed9a0 fix: connectAgent emits error instead of re-throwing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:25:16 -05:00