@copilotkit/react-core 1.71.0 → 1.71.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{copilotkit-B4Jb1QEy.cjs → copilotkit-BkVEkUS0.cjs} +5 -2
- package/dist/copilotkit-BkVEkUS0.cjs.map +1 -0
- package/dist/{copilotkit-snbJkMqQ.mjs → copilotkit-D5BTo0YG.mjs} +5 -2
- package/dist/copilotkit-D5BTo0YG.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.umd.js +4 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/headless.cjs +4 -1
- package/dist/v2/headless.cjs.map +1 -1
- package/dist/v2/headless.mjs +4 -1
- package/dist/v2/headless.mjs.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/index.umd.js +4 -1
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +8 -9
- package/dist/copilotkit-B4Jb1QEy.cjs.map +0 -1
- package/dist/copilotkit-snbJkMqQ.mjs.map +0 -1
- package/skills/react-core/SKILL.md +0 -110
- package/skills/react-core/references/agent-access.md +0 -398
- package/skills/react-core/references/attachments.md +0 -311
- package/skills/react-core/references/capabilities.md +0 -138
- package/skills/react-core/references/chat-components.md +0 -246
- package/skills/react-core/references/client-side-tools.md +0 -358
- package/skills/react-core/references/custom-message-renderers.md +0 -223
- package/skills/react-core/references/debug-mode.md +0 -140
- package/skills/react-core/references/human-in-the-loop.md +0 -312
- package/skills/react-core/references/provider-setup.md +0 -358
- package/skills/react-core/references/rendering-activity-messages.md +0 -201
- package/skills/react-core/references/rendering-tool-calls.md +0 -319
- package/skills/react-core/references/suggestions.md +0 -211
- package/skills/react-core/references/switching-agents-recipes.md +0 -161
- package/skills/react-core/references/switching-agents.md +0 -240
- package/skills/react-core/references/threads.md +0 -289
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: react-core
|
|
3
|
-
description: >
|
|
4
|
-
@copilotkit/react-core — mount the CopilotKit provider (from @copilotkit/react-core/v2)
|
|
5
|
-
in a Next.js App Router / React Router v7 / TanStack Start / SPA app, drop in CopilotChat/CopilotPopup/CopilotSidebar (v2 chat
|
|
6
|
-
components ship from react-core/v2 — NOT react-ui, which is CSS-only in v2), access and
|
|
7
|
-
subscribe to agents with useAgent / useAgentContext / useCapabilities, switch between
|
|
8
|
-
multiple agents, manage durable Intelligence threads with useThreads, register
|
|
9
|
-
browser-side tools via useFrontendTool, render tool calls with useRenderTool /
|
|
10
|
-
useComponent / useDefaultRenderTool, gate execution with useHumanInTheLoop, wire file
|
|
11
|
-
attachments with useAttachments, configure suggestion pills, and register activity- and
|
|
12
|
-
custom-message renderers. publicLicenseKey is canonical (publicApiKey is deprecated
|
|
13
|
-
alias). Load the reference under references/ that matches your task.
|
|
14
|
-
type: framework
|
|
15
|
-
library: copilotkit
|
|
16
|
-
library_version: "1.71.0"
|
|
17
|
-
requires:
|
|
18
|
-
- copilotkit/runtime
|
|
19
|
-
sources:
|
|
20
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/providers/CopilotKitProvider.tsx"
|
|
21
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/index.ts"
|
|
22
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/components/chat/index.ts"
|
|
23
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/components/chat/CopilotChat.tsx"
|
|
24
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/components/chat/CopilotChatView.tsx"
|
|
25
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/components/CopilotKitInspector.tsx"
|
|
26
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-agent.tsx"
|
|
27
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-agent-context.tsx"
|
|
28
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-capabilities.tsx"
|
|
29
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-threads.tsx"
|
|
30
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-frontend-tool.tsx"
|
|
31
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-render-tool.tsx"
|
|
32
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-render-tool-call.tsx"
|
|
33
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-default-render-tool.tsx"
|
|
34
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-component.tsx"
|
|
35
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-human-in-the-loop.tsx"
|
|
36
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-attachments.tsx"
|
|
37
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-configure-suggestions.tsx"
|
|
38
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-suggestions.tsx"
|
|
39
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-render-activity-message.tsx"
|
|
40
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/hooks/use-render-custom-messages.tsx"
|
|
41
|
-
- "CopilotKit/CopilotKit:packages/react-core/src/v2/lib/slots.tsx"
|
|
42
|
-
- "CopilotKit/CopilotKit:packages/core/src/core/core.ts"
|
|
43
|
-
- "CopilotKit/CopilotKit:packages/core/src/core/agent-registry.ts"
|
|
44
|
-
- "CopilotKit/CopilotKit:packages/core/src/core/run-handler.ts"
|
|
45
|
-
- "CopilotKit/CopilotKit:packages/core/src/types.ts"
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
# CopilotKit React Core
|
|
49
|
-
|
|
50
|
-
`@copilotkit/react-core` is the React frontend half of CopilotKit: it mounts a provider,
|
|
51
|
-
speaks AG-UI over SSE to a runtime (or directly to CopilotKit Intelligence in SPA mode), and
|
|
52
|
-
exposes hooks for every interaction surface.
|
|
53
|
-
|
|
54
|
-
This SKILL.md is the **index**. Read the reference under `references/` that matches
|
|
55
|
-
your task — do not try to absorb the whole package from this file.
|
|
56
|
-
|
|
57
|
-
## Mental model — three shells you compose
|
|
58
|
-
|
|
59
|
-
1. **Provider shell** — the `CopilotKit` provider (from `@copilotkit/react-core/v2`) at or near the root (inside `"use client"` for
|
|
60
|
-
Next.js App Router). Carries `runtimeUrl` (or `publicLicenseKey` for SPA), `headers`,
|
|
61
|
-
`credentials`, `properties`, `onError`, `debug`, `enableInspector`.
|
|
62
|
-
2. **Chat shell** — `CopilotChat` / `CopilotPopup` / `CopilotSidebar` or a composed
|
|
63
|
-
`CopilotChatView` + slot primitives (`CopilotChatInput`, `CopilotChatMessageView`, etc.).
|
|
64
|
-
All chat components ship from `@copilotkit/react-core/v2`. **`CopilotPanel` does not
|
|
65
|
-
exist** — it's a common hallucination.
|
|
66
|
-
3. **Hook shell** — inside any component under the provider, call `useAgent`,
|
|
67
|
-
`useFrontendTool`, `useRenderTool`, etc. Every hook takes optional `{ agentId }` for
|
|
68
|
-
agent-scoped registration.
|
|
69
|
-
|
|
70
|
-
## When to load which reference
|
|
71
|
-
|
|
72
|
-
| Task | Reference |
|
|
73
|
-
| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
74
|
-
| Mount the `CopilotKit` provider, pick `runtimeUrl` vs `publicLicenseKey`, RSC boundary rules | `references/provider-setup.md` |
|
|
75
|
-
| Drop in `CopilotChat` / `CopilotPopup` / `CopilotSidebar`, compose `CopilotChatView` with slot primitives | `references/chat-components.md` |
|
|
76
|
-
| File / image attachments via `useAttachments` — drag-drop, click, paste, custom upload | `references/attachments.md` |
|
|
77
|
-
| Client-side debug tooling — `enableInspector`, `debug` prop, lazy-loaded web inspector | `references/debug-mode.md` |
|
|
78
|
-
| Read / subscribe to an agent (`useAgent`) and push global context (`useAgentContext`) | `references/agent-access.md` |
|
|
79
|
-
| Feature-gate UI on declared agent capabilities (`useCapabilities`) | `references/capabilities.md` |
|
|
80
|
-
| Build a multi-agent UI (per-panel `useAgent`, agent-scoped tools, key-remount pattern) | `references/switching-agents.md` (+ `switching-agents-recipes.md` for concrete layouts) |
|
|
81
|
-
| List / rename / archive / delete durable Intelligence threads (`useThreads`) | `references/threads.md` (**requires runtime Intelligence mode**) |
|
|
82
|
-
| Register browser-side tools (`useFrontendTool`) | `references/client-side-tools.md` |
|
|
83
|
-
| Render per-tool UI (`useRenderTool`, `useComponent`, `useDefaultRenderTool`, `useRenderToolCall`) | `references/rendering-tool-calls.md` |
|
|
84
|
-
| Gate tool execution behind user approval (`useHumanInTheLoop`) | `references/human-in-the-loop.md` |
|
|
85
|
-
| Configure dynamic or static suggestion pills (`useConfigureSuggestions`, `useSuggestions`) | `references/suggestions.md` |
|
|
86
|
-
| Render non-chat activity messages (`useRenderActivityMessage`) | `references/rendering-activity-messages.md` |
|
|
87
|
-
| Inject custom UI before/after specific messages (`useRenderCustomMessages`) | `references/custom-message-renderers.md` |
|
|
88
|
-
|
|
89
|
-
## Invariants and gotchas (load-once, before any reference)
|
|
90
|
-
|
|
91
|
-
- `publicLicenseKey` is canonical. `publicApiKey` is a **deprecated alias** — expect it in legacy code.
|
|
92
|
-
- `agents__unsafe_dev_only` and `selfManagedAgents` are dev-only aliases of each other. **Not production-safe.** See `packages/a2ui-renderer` or the `spa-without-runtime` lifecycle skill for the supported SPA path.
|
|
93
|
-
- `CopilotPanel` does not exist. v2 chat components ship from `react-core/v2` — **not** `react-ui` (v2 `react-ui` is CSS-only).
|
|
94
|
-
- No `useAgents()` hook exists. Discover agents via `copilotkit.subscribe({ onAgentsChanged })`.
|
|
95
|
-
- `useRenderToolCall` is a **resolver** (for custom chat surfaces), **not** a registration hook. Register with `useRenderTool` / `useComponent` / `useDefaultRenderTool`.
|
|
96
|
-
- UI-kit detection rule — any `render` or tool-call UI MUST reuse the consumer's shadcn / MUI / Chakra / Ant / Mantine primitives before writing raw JSX. This applies across `client-side-tools`, `rendering-tool-calls`, and `human-in-the-loop`.
|
|
97
|
-
- Tool-call `status` values are camelCase: `'inProgress' | 'executing' | 'complete'`. In-progress args are `Partial<T>`.
|
|
98
|
-
- `useHumanInTheLoop` synthesized handler **MUST** call `respond(result)` (including reject paths), otherwise the agent run hangs. `respond` is `undefined` outside `Executing` status. Unmounting mid-Executing abandons the run.
|
|
99
|
-
- `useThreads` errors with `'Runtime URL is not configured'` outside Intelligence mode.
|
|
100
|
-
- `useAgent` returns `{ agent, isReady }`. While `isReady` is `false`, `agent` is a provisional stand-in that is **swapped** for the real instance when `/info` resolves — `agent` changes reference and every effect depending on it re-runs. Never initialize app state (correlation maps, in-flight request records) inside an effect keyed on `agent`.
|
|
101
|
-
- A React `key` discards **all** state below it. `key={activeAgent}` / `key={threadId}` belongs on the smallest subtree that genuinely owns that state — on a layout-level provider it silently wipes unrelated app state, and with Intelligence wired the thread id changes asynchronously after mount, so it fires mid-interaction.
|
|
102
|
-
- `v1 → v2` migration renames: `useCopilotAction` → `useFrontendTool` + `useHumanInTheLoop`; `imageUploadsEnabled` → `attachments`. See the `v1-to-v2-migration` lifecycle skill.
|
|
103
|
-
|
|
104
|
-
## Reading order for a first-time reader
|
|
105
|
-
|
|
106
|
-
1. `provider-setup` — mount the provider.
|
|
107
|
-
2. `chat-components` — wire a chat surface.
|
|
108
|
-
3. `agent-access` — talk to agents.
|
|
109
|
-
4. `client-side-tools` + `rendering-tool-calls` — add tool-call UI.
|
|
110
|
-
5. Anything else as your feature requires.
|
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
# CopilotKit Agent Access (React)
|
|
2
|
-
|
|
3
|
-
This skill builds on `copilotkit/provider-setup`. `useAgent` reads from the
|
|
4
|
-
same registry the provider populates from `/info`.
|
|
5
|
-
|
|
6
|
-
Two complementary surfaces:
|
|
7
|
-
|
|
8
|
-
- `useAgent` — imperative access to an agent instance, subscribe to
|
|
9
|
-
messages/state/run-status changes.
|
|
10
|
-
- `useAgentContext` — declarative push of app state to every agent run.
|
|
11
|
-
|
|
12
|
-
## Setup
|
|
13
|
-
|
|
14
|
-
```tsx
|
|
15
|
-
"use client";
|
|
16
|
-
import {
|
|
17
|
-
useAgent,
|
|
18
|
-
useAgentContext,
|
|
19
|
-
UseAgentUpdate,
|
|
20
|
-
} from "@copilotkit/react-core/v2";
|
|
21
|
-
import { useMemo } from "react";
|
|
22
|
-
|
|
23
|
-
export function ChatDriver({
|
|
24
|
-
route,
|
|
25
|
-
userId,
|
|
26
|
-
}: {
|
|
27
|
-
route: string;
|
|
28
|
-
userId: string;
|
|
29
|
-
}) {
|
|
30
|
-
const { agent } = useAgent({
|
|
31
|
-
agentId: "default",
|
|
32
|
-
updates: [
|
|
33
|
-
UseAgentUpdate.OnMessagesChanged,
|
|
34
|
-
UseAgentUpdate.OnRunStatusChanged,
|
|
35
|
-
],
|
|
36
|
-
throttleMs: 100,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const context = useMemo(() => ({ route, userId }), [route, userId]);
|
|
40
|
-
useAgentContext({ description: "app context", value: context });
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<div>
|
|
44
|
-
{agent.isRunning ? "…thinking" : "idle"} — {agent.messages.length}{" "}
|
|
45
|
-
messages
|
|
46
|
-
</div>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Core Patterns
|
|
52
|
-
|
|
53
|
-
### Send a message and stream the response
|
|
54
|
-
|
|
55
|
-
```tsx
|
|
56
|
-
const { agent } = useAgent({ agentId: "default" });
|
|
57
|
-
const { copilotkit } = useCopilotKit();
|
|
58
|
-
|
|
59
|
-
async function ask(text: string) {
|
|
60
|
-
agent.addMessage({ id: crypto.randomUUID(), role: "user", content: text });
|
|
61
|
-
await copilotkit.runAgent({ agent });
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Subscribe only to run-status to reduce re-renders
|
|
66
|
-
|
|
67
|
-
```tsx
|
|
68
|
-
const { agent } = useAgent({
|
|
69
|
-
agentId: "default",
|
|
70
|
-
updates: [UseAgentUpdate.OnRunStatusChanged],
|
|
71
|
-
});
|
|
72
|
-
const isRunning = agent.isRunning;
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
`useAgent` returns `{ agent, isReady }`; `isRunning` lives on the agent
|
|
76
|
-
itself. Subscribing to `OnRunStatusChanged` forces a re-render when the
|
|
77
|
-
value flips, so reading `agent.isRunning` stays live.
|
|
78
|
-
|
|
79
|
-
### Share app state with every agent run (global)
|
|
80
|
-
|
|
81
|
-
```tsx
|
|
82
|
-
const value = useMemo(
|
|
83
|
-
() => ({ cartItems: cart.items, currentRoute: router.pathname }),
|
|
84
|
-
[cart.items, router.pathname],
|
|
85
|
-
);
|
|
86
|
-
useAgentContext({ description: "user cart + route", value });
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Abort the run
|
|
90
|
-
|
|
91
|
-
```tsx
|
|
92
|
-
const { agent } = useAgent({ agentId: "default" });
|
|
93
|
-
<button onClick={() => agent.abortRun()}>Stop</button>;
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Wait for the real agent before attaching to it
|
|
97
|
-
|
|
98
|
-
```tsx
|
|
99
|
-
const { agent, isReady } = useAgent({ agentId: "default" });
|
|
100
|
-
|
|
101
|
-
useEffect(() => {
|
|
102
|
-
if (!isReady) return; // provisional stand-in — don't attach yet
|
|
103
|
-
const sub = agent.subscribe({ onRunStartedEvent: handleRunStarted });
|
|
104
|
-
return () => sub.unsubscribe();
|
|
105
|
-
}, [agent, isReady]);
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Until the runtime `/info` sync resolves, `agent` is a provisional
|
|
109
|
-
stand-in. It is a fully-constructed `AbstractAgent`, so every call on it
|
|
110
|
-
is safe — but it is then **replaced**, and `agent` changes reference.
|
|
111
|
-
Anything keyed to the old instance goes with it.
|
|
112
|
-
|
|
113
|
-
### Scope a private agent to a thread
|
|
114
|
-
|
|
115
|
-
`useAgent` admits exactly two shapes and nothing in between:
|
|
116
|
-
|
|
117
|
-
- **Bind to an agent** — `useAgent()` or `useAgent({ agentId })`. The shared
|
|
118
|
-
instance from the registry; the thread comes from the chat configuration.
|
|
119
|
-
- **Bind a private agent to a thread** —
|
|
120
|
-
`useAgent({ agentId, runtimeAgentId, threadId })`. All three are required.
|
|
121
|
-
|
|
122
|
-
```tsx
|
|
123
|
-
// A panel with a thread of its own, routed to the runtime's "default" agent.
|
|
124
|
-
const { agent, isReady } = useAgent({
|
|
125
|
-
agentId: "extraction-panel", // local id this hook registers under
|
|
126
|
-
runtimeAgentId: "default", // the runtime agent to route to
|
|
127
|
-
threadId: extractionThreadId,
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
The second shape registers a private `ProxiedCopilotRuntimeAgent` under the
|
|
132
|
-
local `agentId` and unregisters it on unmount, so the thread is pinned to an
|
|
133
|
-
instance nothing else shares. The registration is a single balanced effect, so
|
|
134
|
-
it survives a StrictMode double-invoke: the cleanup unregisters before the
|
|
135
|
-
remount re-registers.
|
|
136
|
-
|
|
137
|
-
Pick a local `agentId` no real agent uses, and a different one per surface.
|
|
138
|
-
|
|
139
|
-
Source: `packages/react-core/src/v2/hooks/use-agent.tsx:126-137` (the two
|
|
140
|
-
shapes), `:239-254` (register and unregister)
|
|
141
|
-
|
|
142
|
-
## Common Mistakes
|
|
143
|
-
|
|
144
|
-
### CRITICAL — Custom `AbstractAgent.clone()` that returns `this`
|
|
145
|
-
|
|
146
|
-
Wrong:
|
|
147
|
-
|
|
148
|
-
```tsx
|
|
149
|
-
class MyAgent extends AbstractAgent {
|
|
150
|
-
clone() {
|
|
151
|
-
return this; // aliases one instance everywhere a copy is expected
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Correct:
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
class MyAgent extends AbstractAgent {
|
|
160
|
-
clone() {
|
|
161
|
-
// Pass the same constructor arguments this instance was built with.
|
|
162
|
-
// `AbstractAgent` takes `config` as a parameter and does not retain it, so
|
|
163
|
-
// keep whatever your subclass needs on a field of its own.
|
|
164
|
-
const next = new MyAgent(this.myConfig);
|
|
165
|
-
next.threadId = this.threadId;
|
|
166
|
-
next.setState(this.state);
|
|
167
|
-
next.setMessages(this.messages);
|
|
168
|
-
return next;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Nothing validates the return value, so returning `this` fails silently rather
|
|
174
|
-
than throwing. On the stateful suggestions path the engine clones the provider
|
|
175
|
-
agent and then writes a suggestion thread id, seeded messages, and seeded state
|
|
176
|
-
onto the copy — given `this`, it writes all three onto the live agent the user
|
|
177
|
-
is talking to. (With `suggestions: true` on a multi-route runtime it builds a
|
|
178
|
-
fresh `HttpAgent` instead and never clones, so the fault is configuration
|
|
179
|
-
dependent.) Delegate cloning aliases the same way.
|
|
180
|
-
|
|
181
|
-
`useAgent` itself does not clone. It either binds the shared registry instance
|
|
182
|
-
or registers a private proxied agent; see the two shapes above.
|
|
183
|
-
|
|
184
|
-
Source: `packages/core/src/core/suggestion-engine.ts:218-249` (the branch, the
|
|
185
|
-
clone, then the seeding); `packages/core/src/agent.ts:448-472`
|
|
186
|
-
(`ProxiedCopilotRuntimeAgent.clone`, the reference implementation — it rebuilds
|
|
187
|
-
field by field, then copies threadId, state, and messages)
|
|
188
|
-
|
|
189
|
-
### HIGH — Deriving app state from `agent` without guarding on `isReady`
|
|
190
|
-
|
|
191
|
-
Wrong:
|
|
192
|
-
|
|
193
|
-
```tsx
|
|
194
|
-
const { agent } = useAgent({ agentId: "default" });
|
|
195
|
-
|
|
196
|
-
// Correlation map for matching responses back to the row that asked.
|
|
197
|
-
const pending = useRef(new Map<string, string>());
|
|
198
|
-
|
|
199
|
-
useEffect(() => {
|
|
200
|
-
pending.current = new Map(); // re-runs when `agent` is swapped
|
|
201
|
-
const sub = agent.subscribe({ onRunFinishedEvent: resolvePending });
|
|
202
|
-
return () => sub.unsubscribe();
|
|
203
|
-
}, [agent]);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Correct:
|
|
207
|
-
|
|
208
|
-
```tsx
|
|
209
|
-
const { agent, isReady } = useAgent({ agentId: "default" });
|
|
210
|
-
|
|
211
|
-
// Owned by the component, not by the agent — survives the swap.
|
|
212
|
-
const pending = useRef(new Map<string, string>());
|
|
213
|
-
|
|
214
|
-
useEffect(() => {
|
|
215
|
-
if (!isReady) return;
|
|
216
|
-
const sub = agent.subscribe({ onRunFinishedEvent: resolvePending });
|
|
217
|
-
return () => sub.unsubscribe();
|
|
218
|
-
}, [agent, isReady]);
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
`agent` changes reference exactly once per mount, when `/info` resolves and
|
|
222
|
-
the provisional stand-in is swapped for the real instance. Any effect with
|
|
223
|
-
`agent` in its dependency array re-runs at that moment — so app state
|
|
224
|
-
initialized inside such an effect is silently reset partway through the
|
|
225
|
-
first interaction.
|
|
226
|
-
|
|
227
|
-
This bites hardest with Intelligence configured, because license
|
|
228
|
-
verification and thread-endpoint discovery lengthen the provisional window
|
|
229
|
-
past the first user action. In plain SSE mode the window usually closes
|
|
230
|
-
before anyone can interact, which is why the bug does not reproduce in
|
|
231
|
-
OSS-only development.
|
|
232
|
-
|
|
233
|
-
Never put per-component bookkeeping (correlation maps, in-flight request
|
|
234
|
-
records, refs) behind an `agent` dependency. Initialize it in the ref
|
|
235
|
-
itself and let the effect only manage the subscription.
|
|
236
|
-
|
|
237
|
-
Source: `packages/react-core/src/v2/hooks/use-agent.tsx:226-290,465-481`
|
|
238
|
-
|
|
239
|
-
### HIGH — Mutating `agent.messages` directly
|
|
240
|
-
|
|
241
|
-
Wrong:
|
|
242
|
-
|
|
243
|
-
```tsx
|
|
244
|
-
agent.messages.push({ id, role: "user", content: "hi" });
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
Correct:
|
|
248
|
-
|
|
249
|
-
```tsx
|
|
250
|
-
agent.addMessage({ id: crypto.randomUUID(), role: "user", content: "hi" });
|
|
251
|
-
// or:
|
|
252
|
-
agent.setMessages([...agent.messages, newMessage]);
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
AG-UI fires `onMessagesChanged` subscribers via `addMessage` /
|
|
256
|
-
`setMessages`. Direct array mutation bypasses subscribers and the UI never
|
|
257
|
-
re-renders.
|
|
258
|
-
|
|
259
|
-
Source: `packages/react-core/src/v2/hooks/use-agent.tsx` (throughout)
|
|
260
|
-
|
|
261
|
-
### HIGH — Registering non-serializable values via `useAgentContext`
|
|
262
|
-
|
|
263
|
-
Wrong:
|
|
264
|
-
|
|
265
|
-
```tsx
|
|
266
|
-
useAgentContext({
|
|
267
|
-
description: "user",
|
|
268
|
-
value: {
|
|
269
|
-
name: "Alice",
|
|
270
|
-
lastLogin: new Date(),
|
|
271
|
-
onLogout: () => logout(), // dropped silently
|
|
272
|
-
},
|
|
273
|
-
});
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
Correct:
|
|
277
|
-
|
|
278
|
-
```tsx
|
|
279
|
-
useAgentContext({
|
|
280
|
-
description: "user",
|
|
281
|
-
value: { name: "Alice", lastLogin: new Date().toISOString() },
|
|
282
|
-
});
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
`useAgentContext` runs the value through `JSON.stringify`. Functions are
|
|
286
|
-
dropped, `Date` coerces to an ISO string (which the agent has to parse), and
|
|
287
|
-
circular references throw.
|
|
288
|
-
|
|
289
|
-
Source: `packages/react-core/src/v2/hooks/use-agent-context.tsx:30-35`
|
|
290
|
-
|
|
291
|
-
### MEDIUM — Expecting lifecycle callbacks to be throttled
|
|
292
|
-
|
|
293
|
-
Wrong:
|
|
294
|
-
|
|
295
|
-
```tsx
|
|
296
|
-
useAgent({
|
|
297
|
-
agentId: "default",
|
|
298
|
-
throttleMs: 300,
|
|
299
|
-
// expecting onRunInitialized / onRunFinalized / onRunFailed to also be throttled
|
|
300
|
-
});
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
Correct:
|
|
304
|
-
|
|
305
|
-
```tsx
|
|
306
|
-
// Only OnMessagesChanged / OnStateChanged / OnRunStatusChanged are throttled.
|
|
307
|
-
// Lifecycle callbacks always fire immediately — handle them synchronously.
|
|
308
|
-
useAgent({ agentId: "default", throttleMs: 300 });
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
`throttleMs` only applies to the three subscribed updates enumerated in
|
|
312
|
-
`UseAgentUpdate`. Lifecycle callbacks bypass the throttler.
|
|
313
|
-
|
|
314
|
-
Source: `packages/react-core/src/v2/hooks/use-agent.tsx:36-48`
|
|
315
|
-
|
|
316
|
-
### MEDIUM — Unstable context value identity
|
|
317
|
-
|
|
318
|
-
Wrong:
|
|
319
|
-
|
|
320
|
-
```tsx
|
|
321
|
-
useAgentContext({ description: "cart", value: { items: cart.items } });
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
Correct:
|
|
325
|
-
|
|
326
|
-
```tsx
|
|
327
|
-
const value = useMemo(() => ({ items: cart.items }), [cart.items]);
|
|
328
|
-
useAgentContext({ description: "cart", value });
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
A fresh object literal on every render invalidates the `useMemo` inside
|
|
332
|
-
`useAgentContext` that serializes the value, causing constant
|
|
333
|
-
remove/re-add churn in the core context store.
|
|
334
|
-
|
|
335
|
-
Source: `packages/react-core/src/v2/hooks/use-agent-context.tsx:30-35`
|
|
336
|
-
|
|
337
|
-
### MEDIUM — Expecting `useAgentContext` or `copilotkit.addContext` to scope context per agent
|
|
338
|
-
|
|
339
|
-
Wrong:
|
|
340
|
-
|
|
341
|
-
```tsx
|
|
342
|
-
useAgentContext({ agentId: "research", description: "paper list", value });
|
|
343
|
-
// or the imperative form:
|
|
344
|
-
copilotkit.addContext({
|
|
345
|
-
description: "paper list",
|
|
346
|
-
value: JSON.stringify(value),
|
|
347
|
-
agentId: "research",
|
|
348
|
-
});
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
Correct:
|
|
352
|
-
|
|
353
|
-
```tsx
|
|
354
|
-
// Context is global — every agent run sees every registered entry.
|
|
355
|
-
useAgentContext({ description: "paper list", value });
|
|
356
|
-
|
|
357
|
-
// When only one agent should key off a value, branch inside its prompt
|
|
358
|
-
// or tool logic instead of trying to scope the context entry.
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
Context is intentionally global and there is no per-agent scoping hook.
|
|
362
|
-
`useAgentContext` has no `agentId` parameter, and `copilotkit.addContext`
|
|
363
|
-
destructures only `{ description, value }` — any `agentId` passed is
|
|
364
|
-
silently dropped. Treat context as "state of the world" that every agent
|
|
365
|
-
sees.
|
|
366
|
-
|
|
367
|
-
Source: `packages/react-core/src/v2/hooks/use-agent-context.tsx` (no `agentId` parameter); `packages/core/src/core/context-store.ts:26-31`
|
|
368
|
-
|
|
369
|
-
### MEDIUM — Scoping a thread with `agentId` and `threadId` alone
|
|
370
|
-
|
|
371
|
-
Wrong:
|
|
372
|
-
|
|
373
|
-
```tsx
|
|
374
|
-
// A compile error. Bypass the types and a runtime guard throws instead.
|
|
375
|
-
useAgent({ agentId: "default", threadId: "t1" });
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
Correct:
|
|
379
|
-
|
|
380
|
-
```tsx
|
|
381
|
-
useAgent({
|
|
382
|
-
agentId: "panel-1", // a local id of this hook's own
|
|
383
|
-
runtimeAgentId: "default", // the runtime agent to route to
|
|
384
|
-
threadId: "t1",
|
|
385
|
-
});
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
`threadId` is written onto a single agent instance, and an agent resolved by
|
|
389
|
-
`agentId` alone is a shared singleton, so pinning a thread to it would clobber
|
|
390
|
-
every other `useAgent` caller. The three keys are therefore a matched set: the
|
|
391
|
-
type rejects every partial combination, and three runtime guards throw for
|
|
392
|
-
callers who bypass the types, each naming the correct call.
|
|
393
|
-
|
|
394
|
-
Give each surface its own local `agentId`. Two mounted hooks registering the
|
|
395
|
-
same one throw `already registered` rather than quietly sharing state.
|
|
396
|
-
|
|
397
|
-
Source: `packages/react-core/src/v2/hooks/use-agent.tsx:160-198` (the guards);
|
|
398
|
-
`packages/core/src/core/agent-registry.ts:486-491` (the duplicate-id throw)
|