Run prettier on ~1,865 files across examples/ to match the monorepo's
formatting standards. These files were imported as-is from standalone
repos that used different prettier configs.
- base-command: wrap checkCLIVersion() fetch in try-catch so CLI
doesn't crash when offline (version check is non-critical)
- init: forward argv to Create instead of dropping all flags, remove
misleading flag definitions (init is just a deprecated redirect)
- detect-endpoint-type: fix Promise.all destructuring (5 promises but
only 4 variables caused isCopilot to receive the LangGraph FastAPI
result), add missing isMCP branch, use isLangGraphFastAPI result
- auth.service: validate OAuth state parameter before tracking analytics
with user data, add reject()+server.close() on state mismatch so the
Promise settles instead of hanging the CLI forever
- create.test: fix quote-style assertion to match prettier output
- Remove copy-paste triplication in scaffoldAgent (LangSmith block
duplicated twice, causing duplicate .env entries and redundant writes)
- Update AgentTemplates URLs to point to monorepo paths instead of
archived standalone repos
- Fix flags.projectName → args.projectName in banner logic (projectName
is an Args field, not Flags)
- Remove stale trpc-cli path mapping and project reference from
tsconfig.json (package doesn't exist in this monorepo)
Migrate the copilotkit CLI (npx copilotkit create/init/dev) from
CopilotCloud into packages/v1/cli. TEMPLATE_REPOS updated to use
monorepo subdirectory sparse checkout for all CopilotKit-owned
templates, with tarball fallback for external repos (ag2).
Mark all publishable @copilotkitnext/* packages as deprecated in
package.json, pointing users to the @copilotkit/* equivalents.
V2 features will be available under the /v2 subpath.
Affected packages: shared, core, agent, angular, react, runtime,
sqlite-runner, web-inspector.
Remove throw on isArgumentError — matches main's behavior where parse
errors are emitted via subscribers and the run continues, rather than
crashing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new `runTool()` method to `CopilotKitCore` that allows programmatic
execution of registered frontend tools without requiring an LLM turn. This
enables UI-driven tool invocations (e.g., button clicks triggering tool
handlers) while maintaining proper message history and subscriber notifications.
Key changes:
- Extract `executeToolHandler` helper from duplicated logic in
`executeSpecificTool` to share with `runTool`
- Add `runTool()` on `RunHandler` with tool/agent lookup, message creation,
handler execution, and optional follow-up support
- Add `TOOL_NOT_FOUND` and `AGENT_NOT_FOUND` error codes
- Add `CopilotKitCoreRunToolParams` and `CopilotKitCoreRunToolResult` types
- Add delegation method on `CopilotKitCore`
- Add 16 comprehensive tests covering all runTool behaviors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same fix as zod-regression — the mock was missing addHookRenderToolCall
and removeHookRenderToolCall methods.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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
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>
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.
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.
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.
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.
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
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.
## 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)
## 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.
Covers the fix for stale messagesInProcess cleanup, plus general
filtering behavior for both copilotkit:emit-messages and
copilotkit:emit-tool-calls metadata.
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.