[pull] main from CopilotKit:main#292
Merged
Merged
Conversation
Allow callers to supply a custom tool call ID for correlation, idempotency, and observability. Falls back to uuid4 when omitted. Applied consistently across Python LangGraph, Python CrewAI, and JS SDK. Also returns the tool call ID from all variants for downstream reference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…in JS
Address review feedback on copilotkit_emit_tool_call:
- Add non-empty string validation for the tool call ID in all 3 SDKs
- Rename Python `id` param to `tool_call_id` to avoid shadowing the builtin
- Refactor JS 4th positional arg to options bag `{ id?: string }` for extensibility
- Document that the ID is also used as parentMessageId in AG-UI events
- Add JS tests for the new parameter (generated ID, custom ID, validation)
- Add Python validation tests (empty string, whitespace rejection)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Align JS whitespace-only ID rejection with Python (.trim()), show returned ID in docstring examples, strengthen CrewAI test assertions to verify event payloads structurally, and stop miscategorizing dispatch errors as CopilotKitMisuseError (preserve original stack). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Restore CopilotKitMisuseError for dispatch failures in JS (was bare Error) - Rename JS options.id to options.toolCallId for cross-SDK naming parity - Add name/args validation to Python LangGraph and CrewAI variants - Add defensive field validation in AG-UI dispatch handler - Add missing CrewAI whitespace-only ID test - Add JS dispatch failure test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…SDKs Address code review findings: stop mislabeling dispatch errors as CopilotKitMisuseError in JS (let them propagate naturally), add CopilotKitMisuseError(ValueError) to Python SDK, pre-serialize args in AG-UI handler to prevent partial event emission, align whitespace validation across all SDKs and the dispatch layer, tighten JS args type to Record<string, unknown>, and add comprehensive negative tests for AG-UI dispatch validation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address code review findings: - Wrap AG-UI tool call dispatch in try/except with compensating TOOL_CALL_END to prevent clients hanging on partial emission - Reject non-dict/non-str args at the dispatch layer (lists, ints, None) - Guard against None event value before calling .get() - Fix docstring examples that reuse variable names (won't compile) - Introduce CopilotKitError base class; all exceptions now inherit from it; CopilotKitMisuseError inherits from both CopilotKitError and ValueError - Add missing validation tests for name and args across LangGraph and CrewAI Python variants, plus AG-UI dispatch edge cases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add missing @returns tag to JS copilotkitEmitToolCall JSDoc, and add 4 tests for the AG-UI compensating TOOL_CALL_END error-recovery path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address code review findings across all three SDK variants: - Shield asyncio.sleep(0.02) with asyncio.shield() so task cancellation doesn't prevent returning the tool_call_id after dispatch - Revert args validation to original permissiveness (JS: undefined-only check, Python: no isinstance check) to avoid breaking existing callers - Add upfront json.dumps() serializability check in Python variants - Fix compensating TOOL_CALL_END double-emit by tracking dispatched_end - Add compensating action_execution_end to CrewAI variant (queue_put is non-atomic) - Use exc_info=True in compensating-END error logging - Export all exception types from copilotkit package root (__init__.py) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… args validation - Re-raise CancelledError after logging in langgraph copilotkit_emit_tool_call to honor asyncio cancellation contract (was silently un-cancelling tasks) - Move dispatched_end flag to after ToolCallEndEvent dispatch in AG-UI agent so compensating END fires when END itself throws - Add dispatched_end tracking to CrewAI variant to prevent double-END on partial failure - Add JSON.stringify(args) validation in JS SDK matching Python parity - Add 4 CrewAI compensating-END tests and 1 JS serializability test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… validation order - Wrap JS dispatchCustomEvent in try/catch that enriches error messages with tool name and ID for debuggability - Apply asyncio.shield to copilotkit_emit_message's post-dispatch sleep to match copilotkit_emit_tool_call's behavior under task cancellation - Reorder validation in LangGraph Python and JS to name → toolCallId → args, matching CrewAI's order (cheap checks before serialization) - Narrow AG-UI dispatcher's except clause around json.dumps from Exception to (TypeError, ValueError), matching sibling SDK variants - Add CancelledError propagation and warning-log tests for the shielded sleep path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, use UUID for tool call IDs - Replace JS error.message mutation with wrapped Error + cause chain (safe for frozen errors, shared references, non-Error throwables) - Rename dispatched_end → end_attempted, set before END dispatch to prevent duplicate TOOL_CALL_END when END partially flushes before throwing - Switch JS randomId() (ck-prefixed) to randomUUID() for cross-SDK parity with Python's str(uuid.uuid4()) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… flag ordering The split queue_put calls introduced interleaving risk (6 yield points vs 2) and the end_attempted flag was set before the END dispatch, causing the compensating END to be skipped when END itself failed. - CrewAI: restore single batched queue_put(start, args, end) call; compensating END is now unconditional on batch failure - AG-UI agent: rename end_attempted → end_dispatched, set after successful END dispatch so compensation fires for all failure modes - Tests: rewrite CrewAI compensating tests for batch semantics, add test_failure_on_end_emits_compensating_end for AG-UI agent Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… in sdk.py Remove no-op `try/except CancelledError: raise` around asyncio.shield in copilotkit_emit_message (the shield handles cancellation on its own). Remove unused CopilotKitError and CopilotKitMisuseError imports from sdk.py (already re-exported via __init__.py). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tracts The AG-UI dispatcher's ManuallyEmitToolCall handler rejected non-dict, non-string args with CopilotKitMisuseError, but all three emitters (JS, Python LangGraph, Python CrewAI) accept any JSON-serializable value. This mismatch caused JS-emitted list/number args to crash the Python dispatcher. Replace the strict isinstance(dict, str) check with a None guard and rely on the existing json.dumps try/except for serializability. Call-site enumeration: - langgraph_agui_agent.py:129 — changed from isinstance check to None guard - test_emit_tool_call_optional_id.py — updated test_missing_args_raises match, test_non_serializable_args_raises match, converted test_list_args_raises and test_int_args_raises from negative to positive tests - No other call sites reference the removed isinstance pattern Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The new test_emit_tool_call_optional_id.py uses async test methods decorated with @pytest.mark.asyncio, but pytest-asyncio was missing from dev dependencies — causing all 11 async tests to fail in CI across all Python versions (3.10–3.14). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
) ## Summary - Adds an optional `tool_call_id` / `toolCallId` parameter to `copilotkit_emit_tool_call` across all three SDK variants (Python LangGraph, Python CrewAI, JS/TS) - When provided, the custom ID is used as the `toolCallId` in AG-UI protocol events; when omitted, a random UUID is generated (backwards compatible) - All variants now return the tool call ID (`str` / `Promise<string>`) instead of `True` / `void`, aligning the LangGraph Python and JS variants with CrewAI which already returned it - Introduces `CopilotKitError` base exception class and `CopilotKitMisuseError` for invalid API usage in the Python SDK - Adds compensating `TOOL_CALL_END` / `action_execution_end` dispatch when mid-stream failures occur, preventing stuck client UIs - Exports all exception types from the `copilotkit` package root ## Motivation Customer request — enables use cases including: - **Correlation across agent steps** — emit a tool call with a known ID and reference it later - **Idempotency / replay safety** — deterministic IDs prevent duplicate tool calls on LangGraph checkpoint replays - **External system linking** — use a job ID, trace span ID, or request correlation ID as the tool call ID - **Custom HITL flows** — programmatically approve/reject a specific tool call by predictable ID - **Testing** — deterministic IDs make snapshot/integration tests stable ## Behavioral changes - **JS dispatch errors are no longer wrapped in `CopilotKitMisuseError`**: Previously, `copilotkitEmitToolCall` wrapped any `dispatchCustomEvent` failure in a `CopilotKitMisuseError`. Transport/dispatch errors now propagate as their native error types. This is more correct — a dispatch failure is not a misuse error — but callers that specifically caught `CopilotKitMisuseError` for transport errors will need to update their catch clauses. - **Python return type changed**: `copilotkit_emit_tool_call` previously returned `True` (documented as `Awaitable[bool]`). It now returns `str` (the tool call ID). Code using truthiness checks (`if result:`) is unaffected; strict equality (`== True`) will break. ## Test plan - [x] 4 unit tests for LangGraph Python variant (default UUID, custom ID, return value, explicit None) - [x] 3 unit tests for CrewAI Python variant (skipped when crewai not installed) - [x] 3 integration tests for AG-UI dispatch (custom ID propagates to all TOOL_CALL events) - [x] All 26 existing `test_agui_agent.py` tests pass (no regressions) - [x] JS SDK builds cleanly (`nx run @copilotkit/sdk-js:build`) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…page - Rewrites the root build-with-agents page to document Skills + MCP Docs Server - Skills section: intro, npx skills add command, skills table, starter prompt - Removes hideTOC so the new TOC (Skills / MCP Docs Server) renders - Updates icon to BrainCircuit and description to keyword-rich copy - Renames mcp-server-setup snippet heading: ## Overview → ## MCP Docs Server so both sections compose cleanly on the root page without a double Overview Closes out the last AC on OSS-133: npx skills add flow documented with table of skills, starter prompt, and page structure matching Mastra reference.
The table was incomplete (6 skills listed, 12 actually install) and has no CI check to keep it in sync. Replace with a one-line prose summary and a link to the canonical skills/ directory on GitHub, which is always accurate.
Add `metadata.internal: true` so the skill is hidden from `npx skills add CopilotKit/CopilotKit` public installs. Same pattern as PR #4937 for git-hooks and copilotkit-demo-parity.
built-in-agent uses docs_mode: 'authored' so it loads the per-framework integrations/built-in-agent/build-with-agents.mdx directly, bypassing the root page entirely. Other frameworks (generated mode) fall through to the root page and already show the skills section. Fix: extract the full page body into a shared snippet snippets/shared/guides/build-with-agents.mdx register it as <BuildWithAgents /> in SNIPPET_MAP + mdx-registry, and switch built-in-agent's page to <BuildWithAgents />. Root page keeps its inline content (for crawlability). The snippet is the source used by authored-mode frameworks; root is the source for generated-mode and crawlers — same pattern as MCPSetup / coding-agents.
…ntent Root page was duplicating the snippet content inline. Switch it to <BuildWithAgents /> so snippets/shared/guides/build-with-agents.mdx is the one source — root page and built-in-agent both render from it.
## Summary Adds `metadata.internal: true` to `.claude/skills/showcase-demo-debugging/SKILL.md` so it is hidden from `npx skills add CopilotKit/CopilotKit` public installs. ## Why `showcase-demo-debugging` is a contributor-only skill (for working on showcase demos, fixtures, and CI regressions). It has no value to a user building a CopilotKit app, but was showing up in the install summary. This brings it in line with `git-hooks` and `copilotkit-demo-parity` which were fixed the same way in PR #4937. ## Change ```yaml metadata: internal: true ``` One field, same pattern as #4937.
…page (#5039) ## Summary Adds the Skills installation section to the shell-docs `/build-with-agents` page ## Changes **`showcase/shell-docs/src/content/snippets/shared/guides/build-with-agents.mdx`** (new) - Single source of truth for the page content - Intro paragraph, `## CopilotKit Skills` section (Callout + Steps: install + starter prompt), `<MCPSetup />` as second section **`showcase/shell-docs/src/content/docs/build-with-agents.mdx`** - Replaced inline content with `<BuildWithAgents />` — delegates to the shared snippet - Icon updated to `BrainCircuit`, description updated to keyword-rich copy, `hideTOC: true` removed **`showcase/shell-docs/src/content/docs/integrations/built-in-agent/build-with-agents.mdx`** - Replaced `<SharedContent />` with `<BuildWithAgents />` so the skills section appears here too (built-in-agent uses `docs_mode: authored` and never falls through to the root page) **`showcase/shell-docs/src/content/snippets/shared/guides/mcp-server-setup.mdx`** - `## Overview` → `## MCP Docs Server` **`showcase/shell-docs/src/lib/docs-render.tsx`** + **`mdx-registry.tsx`** - Register `BuildWithAgents` in `SNIPPET_MAP` and component registry ## Acceptance criteria status - [x] `/coding-agents` redirects to `/build-with-agents` (PR #5023) - [x] React Native moved to Platforms section (PR #4927 / #5023) - [x] `npx skills add` flow documented with skills link and starter prompt - [x] MCP server install instructions present (existing content, now labeled correctly) - [x] Page structure follows Mastra "Build with AI" reference
) 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>
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>
) (#5042) ## Summary - Wires `useAgent` to sync `agent.threadId` from `CopilotChatConfigurationProvider` when the caller marked the threadId as explicit. AbstractAgent's constructor was auto-minting a UUID, so `ProxiedCopilotRuntimeAgent` was shipping that random UUID in `/agent/run`, `/agent/connect`, `/agent/stop` instead of the value the caller passed via `<CopilotKit threadId={x}>` — breaking thread persistence and causing 404s on lookup. - Fixes #5041 (V1 `<CopilotChat>` path) and #4739 (headless `useAgent` path), which share the same root cause. ## Why this regressed PR #3525 originally solved this with per-thread agent cloning. That cloning was reverted in commit `762370a4e5` (May 2026) because it wiped state on tool calls. The revert restored the explicit `agent.threadId = resolvedThreadId` assignment only in the V2 `CopilotChat` component — the V1 chat hook (`use-copilot-chat_internal.ts`) and headless `useAgent` paths were left without an equivalent sync. `use-agent-thread-isolation.test.tsx` (333 lines) was deleted in the revert and never replaced for those surfaces, which is why this regressed silently. The fix lives in `useAgent` rather than the V1 chat hook because V1 chat already routes through `useAgent` for agent acquisition, and a headless `useAgent` user (issue #4739) gets the sync for free. The gate on `hasExplicitThreadId` matches V2 `CopilotChat`'s gating so a `ThreadsProvider`-minted placeholder doesn't overwrite the agent's auto-minted UUID before a real thread exists. ## Test plan - [x] 4 new unit tests in `use-agent-threadid-sync.test.tsx` covering: explicit sync, the non-explicit no-op path, threadId change re-sync, and the no-provider headless case. - [x] 2 new integration tests in `v1-explicit-threadid-bridge.test.tsx` covering the customer's exact scenario: `<CopilotKit threadId={x}>` → `useAgent` → `agent.threadId === x`. - [x] All 1177 existing `@copilotkit/react-core` tests still pass. - [x] `@copilotkit/react-core:build` succeeds. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary - Reworked the search modal into a clearer docs-scoped search experience with a visible "Searching docs for" framework selector - Removed demo results from search and improved framework-specific deduping for docs content - Tightened the homepage and header chrome spacing to better fit the updated shell-docs theme - Ensured the CopilotKit kite mark is used consistently for the built-in agent selector ## Testing - `npm run typecheck` passed - `npm run lint` passed with existing repository warnings only - Verified the updated search modal and selector behavior in the local browser
The prerelease workflow has been OOMing on Upload workspace since the build/publish split in 770759a. The artifact upload enumerates ~6M files (root + per-package node_modules with pnpm symlinks all materialized, plus build caches) and actions/upload-artifact builds the full manifest in memory before streaming, blowing past the 4GB Node heap limit. publish-release.yml already had the working pattern: exclude node_modules/.next/.turbo/.nx and re-run pnpm install --frozen-lockfile in the publish job. Port it over so prerelease publishes succeed again. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary - The `release / pre` workflow has been failing on every run since commit 770759a ("fix(ci): separate build and publish jobs in release workflows", May 11). The `Upload workspace` step hits a JS heap OOM in `actions/upload-artifact@v7` after enumerating 5,923,811 files. - Root cause: the build → publish handoff uses `path: .` + `include-hidden-files: true`, which packs the entire workspace **after `pnpm install`** — including every `node_modules/`, every nested `.pnpm/` store entry (symlinks materialized), `.next`, `.turbo`, `.nx` caches, plus every `examples/*/node_modules` and showcase app subtree. The action holds the full manifest in memory before streaming → ~4GB heap, OOM. - `publish-release.yml` already encountered this and was patched: it excludes `node_modules`/`.next`/`.turbo`/`.nx` and re-runs `pnpm install --frozen-lockfile` in the publish job. This PR ports the same pattern to `prerelease.yml` — no novel design. ## Why this preserves the security split The original 770759a change separated `build` and `publish` so `NPM_TOKEN` is only in the publish job's process tree, preventing token exfiltration from build-time code. That property is untouched: the artifact still carries pre-built `dist/` directories with the already-bumped versions, and the publish job still runs only the publish script. The `pnpm install --frozen-lockfile` in the publish job runs against the same `pnpm-lock.yaml` carried in the artifact — deterministic and at the same versions the build job validated. ## Test plan - [ ] Trigger a `release / pre` run with `--dry-run=true` against this branch — expect `Upload workspace` to complete in seconds instead of OOMing after 11 minutes. - [ ] Verify the `Publish prerelease` step still finds `tsx` and `pnpm pack` works (i.e. the added `Install Dependencies` step restores node_modules correctly). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )