mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
2d7f188b29
Ports PR #6141's React contract to Vue and deletes the per-thread cloning Vue used instead. Cloning is gone: cloneForThread, getOrCreateThreadClone, getThreadClone and the module-level globalThreadCloneMap. Those clones were reachable only through a WeakMap, components had to look them up to find the agent actually in use, and nothing tied a clone's lifetime to the scope that created it. In its place, the same two-shape contract React now has: useAgent() // shared registry agent useAgent({ agentId }) // shared registry agent useAgent({ agentId, runtimeAgentId, threadId }) // private proxied agent UseAgentProps becomes a base plus a two-branch union, so every partial set — { agentId, threadId }, { agentId, runtimeAgentId }, { runtimeAgentId, threadId } — is a compile error, with the same three runtime guards and the same messages as React for callers TypeScript doesn't reach. Vue's branches take MaybeRefOrGetter where React takes plain values; the shapes are otherwise identical. Thread resolution mirrors React exactly: an explicit `threadId` prop wins, otherwise the chat configuration's thread gated on `hasExplicitThreadId`, so a ThreadsProvider-minted placeholder UUID never overwrites the agent's own. Two Vue-specific details, both load-bearing: - The pin watcher's first source is `() => agent.value`, not `agent`. Vue sets `forceTrigger` when any array watch source is a shallow ref, which would re-run the pin on every `triggerRef(agent)` — i.e. every streamed message — and re-pin the inherited thread over one CopilotChat had deliberately set for the chat it renders. Two suites cover this ("uses the explicit agentId and threadId over inherited configuration"). - CopilotChat assigns `agent.threadId` inside its /connect watcher rather than a separate one, because CopilotKitCore.connectAgent reads that field synchronously to decide whether a restore is fresh; a later assignment would let /connect address the previous thread. React does the same, in the same place. CopilotChatMessageView resolves the registry agent directly instead of consulting the clone map, and reads `copilotkit.agents` so it recomputes when the registry changes. Tests: use-agent-thread-isolation.test.ts covered clone semantics that no longer exist; use-agent-thread-pinning.test.ts replaces it with the new invariants — one instance per agentId, config-thread pinning gated on explicitness, and all three all-or-nothing guards. Four component suites used getThreadClone purely as a lookup and now read from the registry. MockMCPProxyAgent recorded addMessage only inside its clone() override, so those assertions passed only because cloning existed; the recording moves onto the class. clone() itself is left intact everywhere, since CopilotKitCore's SuggestionEngine still clones agents (packages/core/src/core/suggestion-engine.ts). Call-site enumeration: cloneForThread and getOrCreateThreadClone were module-private, zero references. getThreadClone and globalThreadCloneMap were used by CopilotChatMessageView (rewritten) and 4 test files (retargeted); zero remain, including in prose. useAgent's other callers, use-capabilities and use-interrupt, pass `{ agentId }` only and match the unscoped branch unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>