Commit Graph

1227 Commits

Author SHA1 Message Date
Martha Kelly Schumann e38d6ede25 fix: multiple CopilotChat components with different threadIds share message state (#3525) 2026-03-31 13:29:09 -07:00
Martha Schumann 9060f2a930 fix(runtime): address code review feedback on reasoning lifecycle fix
- Call closeReasoningIfOpen() once before the switch instead of in every case branch
- Remove as-casts from closeReasoningIfOpen by using typed const variables
- Remove as-casts from new test code; use typed imports and unknown-safe casts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 08:14:45 -07:00
Martha Schumann 0f01f3ea2d fix(state-manager): address code review feedback
- Replace boolean param with SubscribeConfig object on subscribeToAgent
- Invert handleNewMessage guard to early-return on missing input
- Remove module-scope isAnthropic variable in demo route (SSR safety)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 07:51:35 -07:00
Martha Schumann 5aa901f122 test(agent): strengthen reasoning lifecycle test coverage
- Add cardinality assertions (toHaveLength(1)) to all 5 auto-close tests
  so duplicate REASONING_MESSAGE_END / REASONING_END events are caught
- Add missing RUN_FINISHED final-event assertion to abort auto-close test
- Add test for consecutive reasoning blocks with no reasoning-end between
  them (exercises closeReasoningIfOpen inside reasoning-start)
- Add test for exception thrown mid-stream (exercises catch block path)
- Add test for stream exhaustion without terminal event while reasoning is
  open (exercises !terminalEventEmitted fallback)
- Expand changeset description to cover all three behavioral fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 13:09:15 -07:00
Martha Schumann f78bb6d9b0 fix(agent): make reasoning-end idempotent, close reasoning in catch block
- reasoning-end case now delegates to closeReasoningIfOpen() so a late
  SDK-emitted reasoning-end is a no-op when auto-close already fired,
  preventing duplicate REASONING_MESSAGE_END + REASONING_END events
- Hoist isInReasoning / reasoningMessageId / closeReasoningIfOpen before
  the try block so the catch block can call closeReasoningIfOpen() --
  exceptions thrown mid-reasoning now emit proper lifecycle events before
  RUN_ERROR instead of leaking an unclosed reasoning state
- Remove dead (part as any).delta fallback in reasoning-delta case; the
  SDK always provides part.text in this branch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 13:09:14 -07:00
Martha Schumann fb63dc0cc9 fix(agent): regenerate reasoningMessageId for consecutive blocks with no SDK id
When closeReasoningIfOpen() auto-closes a prior block and a second
reasoning-start arrives with id="0" or no id, the handler was silently
reusing the previous reasoningMessageId. Mirror the text-start pattern:
always call randomUUID() when the provider id is falsy or "0".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 13:09:14 -07:00
Martha Schumann 0b76b2979d fix(agent): close reasoning on consecutive-start, fallback block, abort, and error paths
- Call closeReasoningIfOpen() in the reasoning-start handler before opening
  a new block, guarding against consecutive reasoning blocks without an
  intervening reasoning-end (e.g. Anthropic extended thinking with tools)
- Call closeReasoningIfOpen() in the !terminalEventEmitted fallback block
  so streams that end without a finish/abort/error event still close properly
- Add abort() test-helper and tests for abort + error cases with open reasoning
- Strengthen auto-close ordering assertions to explicitly verify
  REASONING_MESSAGE_END precedes REASONING_END in all three auto-close tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 13:09:14 -07:00
Martha Schumann 8aafcbe2af fix(agent): skip empty reasoning deltas and auto-close reasoning lifecycle
Two fixes for Anthropic model stalls (fixes #3323):

1. Skip reasoning-delta events with empty text — EventSchemas.parse()
   rejects delta: "" and kills the RxJS Observable before any other
   events can fire.

2. Auto-close open reasoning lifecycle at every phase transition —
   @ai-sdk/anthropic never emits reasoning-end, leaving the state
   machine stuck.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 13:09:14 -07:00
Martha Schumann 092956d299 style: fix prettier formatting across all changed files 2026-03-30 13:09:10 -07:00
Martha Schumann 6395a9a42d style: fix prettier formatting in CopilotChatMessageView after merge 2026-03-30 13:04:44 -07:00
Martha Schumann 83f58ee3de Merge branch 'main' into fix/cpk-7155-thread-shared-state
Incorporates the V1/V2 flat package consolidation (96885b595). All our
thread-isolation changes auto-merged to the new package paths. Three
content conflicts resolved (import paths updated from @/ aliases to
relative paths and @copilotkitnext/* to @copilotkit/*):
- packages/react-core/src/v2/hooks/use-agent.tsx
- packages/react-core/src/v2/hooks/use-render-custom-messages.tsx
- packages/react-core/src/v2/components/chat/CopilotChatMessageView.tsx

File location conflict resolved: use-agent-thread-isolation.test.tsx
moved from packages/v2/react/... to packages/react-core/src/v2/...
2026-03-30 12:33:16 -07:00
Martha Schumann f1da7a9c61 fix(state-manager): restore registry agent cleanup and cache Error-state provisional
Two bugs found in CR:

1. unsubscribeFromAgent() was a no-op: subscribeToAgent() always used the
   composite agentId:threadId key (since agent.threadId defaults to a UUID),
   but unsubscribeFromAgent() looked for the bare agentId key, which was
   never written. Registry agents subscribed via onAgentsChanged were never
   cleaned up on replacement. Fix: add isClone flag to subscribeToAgent() —
   registry agents (isClone=false, default) use bare key; per-thread clones
   (isClone=true, via subscribeAgentToStateManager) use composite key.

2. Error-state provisional agent was not cached in provisionalAgentCache,
   unlike the Disconnected/Connecting path. Any dependency change while in
   Error state produced a new agent reference, triggering spurious
   re-subscriptions. Fix: cache the Error-state provisional using the same
   cacheKey and headers-refresh pattern as the other provisional paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 12:10:13 -07:00
Martha Schumann 6c209bb24a fix(suggestions): pass thread clone agent to reloadSuggestions
With per-thread agent clones, the registry agent has empty messages.
reloadSuggestions was checking messageCount on the registry agent, so
dynamic suggestions (which require messageCount > 0) never fired after
the first message. Fix by passing the thread clone (with actual messages)
through reloadSuggestions → generateSuggestions.

Also updates the demo to use claude-3-7-sonnet (extended thinking support)
when ANTHROPIC_API_KEY is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:57:23 -07:00
Martha Schumann 0ac2c27c66 fix(web-inspector): prevent flaky test from timer firing after jsdom teardown
Add afterEach with vi.clearAllTimers() to prevent the 300ms setTimeout in
WebInspectorElement (src/index.ts:2065) from firing after the jsdom environment
tears down, which caused intermittent "document is not defined" errors in CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:39:28 -07:00
Martha Schumann 2397e3aa9b fix(state-manager): isolate per-thread state and prevent run collisions in sequential runs
- StateManager now uses composite `agentId:threadId` subscription keys so
  per-thread clones get independent subscriptions without stomping the
  registry agent's subscription.
- Added `activeRun` map so `handleNewMessage` (called without `input` by
  ag-ui's addMessage path) can still associate messages with the correct run.
- Added `revoked` flag + `subRunId`/`runFinished` run-isolation logic to
  handle the timing race where a new run's events arrive through the previous
  pipeline before `subscribeAgentToStateManager` has set up a new one —
  detected via `runFinished && input.runId === subRunId`, resolved by minting
  a fresh UUID so both runs get distinct state entries.
- Empty state `{}` is no longer persisted; `getStateByRun` returns `undefined`
  so renderers can distinguish "no snapshot received yet" from a real state.
- `useAgent`: per-thread clones are shared via a module-level WeakMap so all
  components on the same (agentId, threadId) see the same instance.
- Tests updated to cover sequential-run state isolation and the empty-state
  semantics change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:05:04 -07:00
github-actions[bot] a1edf59eae chore(post-release): update version to 1.55.0-next.7 2026-03-29 00:19:22 +00:00
Claude 1ceb963a84 Revert "fix: resolve changeset version errors from stale @copilotkitnext package refs"
This reverts commit 1f6ae0d629.
2026-03-28 17:04:55 -07:00
Claude 369266f1be fix: resolve changeset version errors from stale @copilotkitnext package refs
After the V1/V2 consolidation merge, several changesets still referenced
old @copilotkitnext/* package names that no longer exist. Fixed package
references and ran changeset version to apply pending version bumps.

https://claude.ai/code/session_01A5aahBWFTmLPR3xZSeh4SD
2026-03-28 17:04:55 -07:00
Tyler Slaton 96885b5959 refactor: consolidate V1/V2 packages into flat @copilotkit/* structure
Flatten all packages from packages/v1/* and packages/v2/* into packages/* —
every package now lives directly under the @copilotkit/ scope with no v1/v2
subdirectories.

- Move all v1 packages (react-core, react-ui, runtime, shared, etc.) from
  packages/v1/* to packages/*
- Absorb v2 react code into packages/react-core/src/v2/ (exported via /v2 subpath)
- Absorb v2 agent code into packages/runtime/src/agent/ (exported via /v2 subpath)
- Move v2 packages (core, angular, demo-agents, etc.) to packages/*
- Replace all @copilotkitnext/* imports with @copilotkit/* equivalents
- Keep @copilotkitnext/angular as the sole exception (angular remains on next)
- Update CI workflows, renovate config, release scripts for flat structure
- No public API surface changes — all exports fields are preserved

Co-authored-by: Alem Tuzlak <t.zlak@hotmail.com>
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-03-28 16:45:10 -07:00
Martha Schumann ab8a92ef4f Merge remote-tracking branch 'origin/main' into fix/cpk-7155-thread-shared-state 2026-03-27 08:53:24 -07:00
Martha Schumann c198d13ec1 fix(use-agent): address critical review issues from ranst91
- Fix 1: store {source, clone} in threadAgentCache to invalidate stale
  clones when registry agent is replaced (reconnect/hot-reload)
- Fix 2: add lastConnectedAgentRef in V1 wrapper to prevent connectAgent
  firing on every Connected render; ref reset in cleanup for StrictMode
- Fix 3: throw if clone() returns same instance to prevent silent state
  destruction via setMessages([]) / setState({}) on the source agent
- Fix 4: clear bare agentId key from provisionalAgentCache on threadId
  transition to eliminate phantom provisionals
- Fix 5: clear threadAgentCache on unmount to release clones holding
  listeners, WebSocket connections, or timers
- Fix 6: move HttpAgent headers sync out of useMemo into useEffect to
  avoid side effects in concurrent-mode pure computation

Tests: T-1 registry replacement, T-2 threadId switching, T-3 provisional
→ real transition, T-4 V1 threadId passthrough, T-5 non-vacuous isolation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 08:53:17 -07:00
Martha Kelly Schumann 1993ca20de fix(react-ui): stabilize Markdown components ref to prevent re-renders on streaming (#3535)
## Summary

- `{ ...defaultComponents, ...components }` in `Markdown.tsx` created a
new object reference on every render
- `MemoizedReactMarkdown` has a custom memo comparator checking
`prevProps.components === nextProps.components` — referential equality
always failed, so every prior message re-rendered on every streaming
chunk
- Fix: wrap the merge in `useMemo([components])` so the reference is
stable

Fixes CPK-7156 / closes #2543

## Test plan

- [x] Verified locally with the form-filling example: completed messages
no longer log new renders while a new message streams
- [x] `nx build @copilotkit/react-ui` passes
- [x] `nx test @copilotkit/react-ui` passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-27 07:56:47 -07:00
Martha Schumann a03c6eb325 fix(react-ui): stabilize Markdown components ref to prevent full re-renders on streaming
{ ...defaultComponents, ...components } created a new object reference on
every render, causing MemoizedReactMarkdown's custom comparator to always
fail and re-render every prior message on each new streaming chunk.

Wrapping the merge in useMemo([components]) gives a stable reference so
the memo comparator works as intended.

Fixes CPK-7156

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 16:21:13 -07:00
Max Korp cd722213f4 chore: Update to published license-verifier 2026-03-26 16:19:03 -07:00
Max Korp 73bce637b5 feat: move license checks to the backend 2026-03-26 16:19:03 -07:00
Max Korp f43433c4c1 feat(runtime): add intelligence license checks to runtime
feat(runtime-v2): wire createLicenseChecker into CopilotRuntime

feat(runtime-v2): wire license checks into route handlers

feat(runtime-v1): pass licenseToken through to v2 runtime
2026-03-26 16:19:03 -07:00
Max Korp 66a80d99b0 feat(react-ui/react-v2): add intelligence license check to clients
feat(shared): re-export @copilotkit/license-verifier

feat(react-v2): add LicenseWarningBanner and InlineFeatureWarning components

Self-contained components with inline styles for displaying license
status warnings (no_license, expired, expiring, invalid, feature_unlicensed).

chore: add @copilotkit/shared dependency to v2 react and runtime

feat(react-v2): add licenseToken prop with verification, context, and warnings

feat(react-v2): add license check to CopilotChat

feat(react-v2): add license check to CopilotSidebar

feat(react-v2): add license check to CopilotPopup

feat(react-ui): respect remove_branding flag in PoweredByTag
2026-03-26 16:19:03 -07:00
Martha Schumann b56322e942 docs(use-agent): add comment explaining intentional thread clone cache lifetime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 09:48:23 -07:00
Benjamin Taylor 77764d043e feat(react): Restore lost changes 2026-03-26 10:47:36 -05:00
Benjamin Taylor 81edf42e6b feat(runtime): Bubble up thread lock error so it can be handled explicitly 2026-03-26 09:45:20 -05:00
Mike Ryan e7d74b832f feat(threads): remove client userId from thread flows 2026-03-25 17:09:41 -07:00
Maxim f6b9543511 feat(useInterrupt): attach interrupt event metadata to resume command
When resolve() is called, the interrupt event value is now included
alongside the user's response in forwardedProps.command.interruptEvent.
This enables agent adapters (e.g. Mastra) to round-trip interrupt
context (toolCallId, runId, type) without storing state between runs.

Uses a ref to avoid changing the resolve callback's dependency array,
preventing double-invocation of the handler effect.

The LangGraph adapter is unaffected — it ignores the extra field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:07:21 -07:00
Max Korp 303bc8691c fix: fix newly added tenantIds post rebase 2026-03-25 16:30:26 -07:00
Max Korp 04f131ad6e feat: rename tenantId to organizationId in CopilotKit SDK 2026-03-25 16:19:59 -07:00
Martha Kelly Schumann 53e56699e8 fix: isolate agent instances per threadId in useAgent hook
Multiple CopilotChat components with the same agentId but different
threadIds were sharing a single agent instance, causing message state
to bleed between them. The useAgent hook now creates per-thread clones
keyed by agentId:threadId, keeping messages and state isolated.

Closes #2957

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 16:02:30 -07:00
github-actions[bot] 80da7704c4 chore(post-release): update version to 1.54.1-next.6 2026-03-25 22:04:40 +00:00
Martha Kelly Schumann dfdc234130 fix: respect defaultOpen={false} in CopilotSidebar and CopilotPopup (#3513)
## Summary

`<CopilotSidebar defaultOpen={false} />` was opening anyway because the
prop was not being forwarded through the provider chain. This PR fixes
that - and also addresses the architectural trade-off flagged in review.

### Who is affected

**User A** just uses `<CopilotSidebar />` with no outer provider
Before the fix, their sidebar opened by default. After the fix, it still
opens by default. Nothing changed for them.
                                                            
**User B** uses `<CopilotSidebar defaultOpen={false} />`
Before: broken (opened anyway). After: works.
**User C** wraps their app in a bare `CopilotChatConfigurationProvider`
and reads `isModalOpen` from it.
Before our fix `isModalOpen` was stuck at true forever. After: it
reflects the sidebar's state.

### What changed

- The `defaultOpen` prop is forwarded correctly through the provider
chain to both `CopilotSidebar` and `CopilotPopup`.
- Outer `useCopilotChatConfiguration()` hooks now reflect sidebar state.
Fixed via bidirectional state sync between nested providers.

### Files changed

- `packages/v2/react/src/components/chat/CopilotChat.tsx` - accept and
forward `isModalDefaultOpen`
- `packages/v2/react/src/components/chat/CopilotSidebar.tsx` - pass
`isModalDefaultOpen={defaultOpen}`
- `packages/v2/react/src/components/chat/CopilotPopup.tsx` - same
- `packages/v2/react/src/providers/CopilotChatConfigurationProvider.tsx`
- fix resolution logic + bidirectional sync
-
`packages/v2/react/src/providers/__tests__/CopilotChatConfigurationProvider.test.tsx`
- add tests

### Test plan

- [x] 843 tests pass (including 4 new bidirectional sync tests)
- [x] New tests cover all three reproduction scenarios from
[tkt-modal-default-open](https://github.com/CopilotKit/deep-agent-cpk-experiments/tree/main/app/client/src/tickets/tkt-modal-default-open)

**Linear:**
[CPK-7152](https://linear.app/copilotkit/issue/CPK-7152/fix-defaultopenfalse-prop-ignored-in-copilotsidebar)
**GitHub Issue:** #3475
**Related PRs:** #3477, #3496 (stalled community PRs addressing the same
issue)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-25 15:00:36 -07:00
Benjamin Taylor 72d637b052 feat(threads): Remove straggling console.log 2026-03-25 13:56:29 -07:00
Benjamin Taylor b1cc32c277 feat(threads): 'includedArchived' and pagination for threads API 2026-03-25 13:56:29 -07:00
Martha Schumann ea9b0bbd2a fix: bidirectional modal state sync between nested providers (CPK-7152)
PR #3513 fixed Behavior A (defaultOpen={false} now respected) but broke
Behavior B: users who read isModalOpen from an outer
CopilotChatConfigurationProvider no longer see state changes originating
in the inner provider (e.g. CopilotSidebarView's provider).

Root cause: with hasExplicitDefault=true, the inner provider used its own
setter exclusively, so open/close events never reached ancestor providers.

Fix: when a provider owns its state (hasExplicitDefault=true), wrap
setModalOpen to also call parentConfig.setModalOpen — propagating changes
upward through the provider chain. Add a useEffect to sync parent→child
when an ancestor's state is changed externally (e.g. via an outer hook).
The mount-skip guard (isMounted ref) ensures the child's own
isModalDefaultOpen is honoured on first render and not overwritten.

Three new tests mirror the reproduction scenarios from:
https://github.com/CopilotKit/deep-agent-cpk-experiments/tree/main/app/client/src/tickets/tkt-modal-default-open

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:23:06 -07:00
Maxim 838565d623 Merge branch 'main' into fix/abort-running-frontend-tool 2026-03-25 18:58:36 +01:00
Maxim 0918071f86 Merge branch 'main' into fix/react-strict-mode-hmr-agent-disconnect-fix 2026-03-25 17:52:48 +01:00
github-actions[bot] 0683758931 chore(post-release): update version to 1.54.1-next.5 2026-03-25 16:45:07 +00:00
Maxim f00dfd2359 Merge branch 'main' into fix/react-strict-mode-hmr-agent-disconnect-fix 2026-03-25 17:41:48 +01:00
Maxim e149f67507 Merge branch 'main' into fix/react-strict-mode-hmr-agent-disconnect-fix 2026-03-25 17:33:54 +01:00
Maxim 98daf5c90a Merge branch 'main' into feat/builtin-agent-persistent-mcp 2026-03-25 17:30:12 +01:00
Max Korp 0827b4cd50 fix: update @ag-ui deps to 0.0.48 to fix detachActiveRun hang
@ag-ui/client 0.0.47 had a bug where connectAgent() hung forever
  for agents that don't implement connect(), causing detachActiveRun()
  and runAgent() to deadlock. Fixed in 0.0.48.
2026-03-24 19:30:16 -07:00
github-actions[bot] ed99910e51 chore(post-release): update version to 1.54.1-next.4 2026-03-24 23:04:43 +00:00
Martha Kelly Schumann 35073c8cab fix: race condition in RunHandler.runAgent() causes dropped runs (#3514)
## Summary

Fixes a race condition where `detachActiveRun()` was called without
`await`, allowing a new run to overwrite pipeline state before cleanup
finished — silently dropping the previous run.

- **`run-handler.ts`**: `void agent.detachActiveRun()` → `await
agent.detachActiveRun()`. The original deadlock that motivated
fire-and-forget (ConnectNotImplementedError cleanup) has been resolved
in the current `@ag-ui/client`. Confirmed via git history: added as
fire-and-forget in `5b3037a` on Mar 13 by Max Korp.
- **`use-copilot-chat_internal.ts`**: Added AbortController cleanup in
the `connectAgent` useEffect, mirroring the V2 `CopilotChat.tsx`
pattern. Guards state updates against aborted signals.
- Updated test mocks to include `detachActiveRun`

**Linear:**
[CPK-7151](https://linear.app/copilotkit/issue/CPK-7151/fix-race-condition-in-runhandlerrunagent-causes-dropped-runs)
**GitHub Issue:** #3482

## Files changed

- `packages/v2/core/src/core/run-handler.ts`
- `packages/v1/react-core/src/hooks/use-copilot-chat_internal.ts`
-
`packages/v1/react-core/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx`
-
`packages/v1/react-core/src/hooks/__tests__/use-coagent-state-render.e2e.test.tsx`

## Test plan

- [x] All tests pass (pre-commit hook ran full suite)
- [x] Manual: rapid sequential message sends don't drop runs
- [x] Manual: verify no deadlock on initial connectAgent → immediate
runAgent

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-24 16:00:57 -07:00
github-actions[bot] e384ad05fa chore(post-release): update version to 1.54.1-next.3 2026-03-24 22:19:10 +00:00