A fresh Angular 21 setup hit two blockers: a clean install failed with
ERESOLVE, and the README pointed at @copilotkit/angular, which 404s on npm.
Widen the @angular/* peer range to 19-21 and correct the README package name,
install command, and the TS7016 types workaround.
Gate the Angular SDK's "CopilotKit Unlicensed" watermark and "License
Required" console warning behind a LICENSE_WATERMARK_ENABLED flag set to
false. The watermark implementation is retained for easy re-enablement;
the licenseKey option and its X-CopilotCloud-Public-Api-Key header
injection are unchanged.
When the chat's connect effect re-fires due to React effect-dep churn, it
calls copilotkit.connectAgent({ agent }) on the same thread again. The
RunHandler previously called agent.setMessages([]), agent.setState({}),
and (transitively) clearReconnectCursor on every such call. That forced
the realtime gateway to replay the topic's full event history on every
churn re-connect — sending the same persisted cpki_event_ids 2-3 times
per thread switch — and produced both halves of Tyler's bug: duplicate
rows in the inspector AG-UI Events tab plus "Message not found" toasts
when the next runAgent fired with an empty agent.messages.
This change makes the orchestrator detect actual thread switches:
- RunHandler tracks _lastConnectedThreadId across connectAgent calls.
On a fresh restore (different threadId from last call) it does the
reset and clears the replay cursor. On churn (same threadId) it
skips the reset entirely so local messages/state are preserved and
the gateway resumes from lastSeenEventId instead of replaying.
- IntelligenceAgent.connect() no longer auto-clears the cursor;
cursor management is the caller's decision now. clearReconnectCursor
is made public so RunHandler (and tests) can call it explicitly.
- ProxiedCopilotRuntimeAgent exposes a clearReplayCursor(threadId)
method that delegates to the IntelligenceAgent. Non-Intelligence
runtime modes are a safe no-op.
This supersedes #4720, which attempted to solve only the inspector
duplicate-row symptom by adding a dispatcher dedup. That approach
introduced a blocking regression in the A → B → A restore path: the
dedup persisted across thread switches and suppressed the gateway's
restore replay, leaving agent.messages at [] and triggering the very
"Message not found" toast we were trying to prevent. Mike's review
caught it. The dispatcher dedup is removed entirely; the
orchestrator-level gate is sufficient and a smaller surface change.
Tests:
- intelligence-agent.test.ts: updated 4 tests that codified the old
"always clear cursor" semantics. Added an explicit test that
clearReconnectCursor() empties the cursor for the next connect, and
Mike's regression — replay the same cpki_event_id after A → B → A
and verify the rehydrate still produces the user message.
- core-connect-thread-switch.test.ts (new): four tests for the
RunHandler gate covering churn, single switch, A → B → A, and the
no-clearReplayCursor non-Intelligence path.
All 430 core tests pass.
New license verifier release adds a required telemetry_id field to the
signed LicensePayload. Runtime consumes verified payloads unchanged;
shared re-exports the widened type. See the intelligence repo ENT-251
PR for the motivation (per-license analytics correlation ID).
- Rename sortThreadsByUpdatedAt → sortThreadsByRecency to match the
lastRunAt-preferring sort introduced in the previous commit.
- useThreads: correct the context-dispatch comment to describe what the
code actually does (null only when runtimeUrl is absent; transient
status states leave the previous context in place).
- CopilotChatInput: rewrite the `bottomAnchored` prop doc so the
layout/positioning distinction is self-evident.
- Add changeset calling out the behavior change to suggestions (now
hidden while `isRunning`) and summarizing the ENT-314 fixes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
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.
- Fix JSDoc: "while keeping the latest toolCalls" was wrong for the ?? case;
now says "recovers toolCalls from earlier occurrences if the latest is
undefined" and notes that [] is treated as intentional
- Add @internal annotation to signal export is for testing only
- Remove redundant AssistantMessage casts inside the role-narrowed branch
- Add missing content assertion to "uses latest content" render test
- Add test: [] toolCalls from later chunk is kept (not fallen back from)
- Add test: undefined content on both sides is handled without error
- Add changeset for @copilotkit/react-core patch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Kill useJsonStable in CopilotChatConfigurationProvider — useShallowStableRef
is now the single stabilization primitive for both messageView props and labels
- Harden useShallowStableRef with isPlainObject guard so arrays, Dates, and
class instances are never shallow-compared (reference-only for non-plain objects)
- Remove redundant stableParentLabels — parentConfig?.labels is already stabilized
by the parent provider's own useShallowStableRef
- Uninstall ts-deepmerge from react-core dependencies (no longer imported anywhere)
- Add unit tests for useShallowStableRef in slots.test.ts (5 tests)
- Add changeset (patch) describing the FOR-75 performance fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>