@copilotkit/react-core 1.70.3 → 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-BU3OvveB.cjs → copilotkit-BkVEkUS0.cjs} +369 -154
- package/dist/copilotkit-BkVEkUS0.cjs.map +1 -0
- package/dist/{copilotkit-Bs98akp9.d.mts → copilotkit-ChjzWqn6.d.mts} +33 -8
- package/dist/copilotkit-ChjzWqn6.d.mts.map +1 -0
- package/dist/{copilotkit-Ap_yisA5.mjs → copilotkit-D5BTo0YG.mjs} +368 -153
- package/dist/copilotkit-D5BTo0YG.mjs.map +1 -0
- package/dist/{copilotkit-X2eGbOwf.d.cts → copilotkit-DCIXZawe.d.cts} +33 -8
- package/dist/copilotkit-DCIXZawe.d.cts.map +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +180 -139
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/context.cjs +4 -0
- package/dist/v2/context.cjs.map +1 -1
- package/dist/v2/context.d.cts +3 -1
- package/dist/v2/context.d.cts.map +1 -1
- package/dist/v2/context.d.mts +3 -1
- package/dist/v2/context.d.mts.map +1 -1
- package/dist/v2/context.mjs +3 -1
- package/dist/v2/context.mjs.map +1 -1
- package/dist/v2/headless.cjs +20 -5
- package/dist/v2/headless.cjs.map +1 -1
- package/dist/v2/headless.d.cts +9 -4
- package/dist/v2/headless.d.cts.map +1 -1
- package/dist/v2/headless.d.mts +9 -4
- package/dist/v2/headless.d.mts.map +1 -1
- package/dist/v2/headless.mjs +21 -6
- package/dist/v2/headless.mjs.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +1 -1
- package/dist/v2/index.d.mts +1 -1
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/index.umd.js +495 -278
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +8 -9
- package/dist/copilotkit-Ap_yisA5.mjs.map +0 -1
- package/dist/copilotkit-BU3OvveB.cjs.map +0 -1
- package/dist/copilotkit-Bs98akp9.d.mts.map +0 -1
- package/dist/copilotkit-X2eGbOwf.d.cts.map +0 -1
- package/skills/react-core/SKILL.md +0 -110
- package/skills/react-core/references/agent-access.md +0 -355
- 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 -229
- 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,138 +0,0 @@
|
|
|
1
|
-
# CopilotKit Capabilities (React)
|
|
2
|
-
|
|
3
|
-
This skill builds on `copilotkit/agent-access`. `useCapabilities` internally
|
|
4
|
-
calls `useAgent` and reads the `capabilities` field populated from the
|
|
5
|
-
runtime `/info` response.
|
|
6
|
-
|
|
7
|
-
`AgentCapabilities` is from `@ag-ui/core`. The hook is synchronous — there
|
|
8
|
-
is no loading state, but the value is `undefined` until the handshake
|
|
9
|
-
completes.
|
|
10
|
-
|
|
11
|
-
## Setup
|
|
12
|
-
|
|
13
|
-
```tsx
|
|
14
|
-
"use client";
|
|
15
|
-
import { useCapabilities } from "@copilotkit/react-core/v2";
|
|
16
|
-
|
|
17
|
-
export function VoiceButton() {
|
|
18
|
-
const caps = useCapabilities(); // defaults to DEFAULT_AGENT_ID
|
|
19
|
-
|
|
20
|
-
// Handshake pending — show a placeholder
|
|
21
|
-
if (caps === undefined) return <div className="skeleton h-8 w-8" />;
|
|
22
|
-
|
|
23
|
-
// Handshake complete — feature-gate
|
|
24
|
-
if (!caps.transcription) return null;
|
|
25
|
-
|
|
26
|
-
return <button>Record</button>;
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Core Patterns
|
|
31
|
-
|
|
32
|
-
### Scope to a specific agent
|
|
33
|
-
|
|
34
|
-
```tsx
|
|
35
|
-
const caps = useCapabilities("research");
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Feature-gate tools UI
|
|
39
|
-
|
|
40
|
-
```tsx
|
|
41
|
-
const caps = useCapabilities("default");
|
|
42
|
-
|
|
43
|
-
if (caps === undefined) return <ToolsSkeleton />;
|
|
44
|
-
if (caps.tools?.supported === false) return null;
|
|
45
|
-
return <ToolsPanel />;
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Narrow optional fields defensively
|
|
49
|
-
|
|
50
|
-
`AgentCapabilities` is a partial declaration — fields may be absent when
|
|
51
|
-
the agent opts not to declare them.
|
|
52
|
-
|
|
53
|
-
```tsx
|
|
54
|
-
const caps = useCapabilities();
|
|
55
|
-
const maxTokens = caps?.maxOutputTokens ?? "unknown";
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Common Mistakes
|
|
59
|
-
|
|
60
|
-
### HIGH — Treating `undefined` as "no capabilities"
|
|
61
|
-
|
|
62
|
-
Wrong:
|
|
63
|
-
|
|
64
|
-
```tsx
|
|
65
|
-
function VoiceButton() {
|
|
66
|
-
const caps = useCapabilities();
|
|
67
|
-
if (!caps?.transcription) return null; // hides button forever while handshake pending
|
|
68
|
-
return <button>Record</button>;
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Correct:
|
|
73
|
-
|
|
74
|
-
```tsx
|
|
75
|
-
function VoiceButton() {
|
|
76
|
-
const caps = useCapabilities();
|
|
77
|
-
if (caps === undefined) return <div className="skeleton h-8 w-8" />;
|
|
78
|
-
if (!caps.transcription) return null;
|
|
79
|
-
return <button>Record</button>;
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
`useCapabilities` returns `undefined` until the runtime `/info` handshake
|
|
84
|
-
completes. Treating `undefined` the same as `{ transcription: false }`
|
|
85
|
-
hides features that should be visible post-handshake.
|
|
86
|
-
|
|
87
|
-
Source: `packages/react-core/src/v2/hooks/use-capabilities.tsx:7-9`
|
|
88
|
-
|
|
89
|
-
### MEDIUM — Non-null assertion on optional fields
|
|
90
|
-
|
|
91
|
-
Wrong:
|
|
92
|
-
|
|
93
|
-
```tsx
|
|
94
|
-
const caps = useCapabilities();
|
|
95
|
-
return <div>Max tokens: {caps!.maxOutputTokens}</div>;
|
|
96
|
-
// Crashes if agent didn't declare capabilities, or didn't declare maxOutputTokens.
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Correct:
|
|
100
|
-
|
|
101
|
-
```tsx
|
|
102
|
-
const caps = useCapabilities();
|
|
103
|
-
return <div>Max tokens: {caps?.maxOutputTokens ?? "unknown"}</div>;
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
`AgentCapabilities` is a partial declaration. Agents opt in to each
|
|
107
|
-
capability, so every field is optional. Narrow before deref.
|
|
108
|
-
|
|
109
|
-
Source: `packages/react-core/src/v2/hooks/use-capabilities.tsx:20-22`
|
|
110
|
-
|
|
111
|
-
### MEDIUM — Expecting deep merge from server-side `capabilities`
|
|
112
|
-
|
|
113
|
-
Wrong:
|
|
114
|
-
|
|
115
|
-
```ts
|
|
116
|
-
// Server:
|
|
117
|
-
new BuiltInAgent({
|
|
118
|
-
// ...
|
|
119
|
-
capabilities: { tools: { supported: true } },
|
|
120
|
-
});
|
|
121
|
-
// Client expects caps.tools.clientProvided to still be set by the default
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Correct:
|
|
125
|
-
|
|
126
|
-
```ts
|
|
127
|
-
// Server — provide full category:
|
|
128
|
-
new BuiltInAgent({
|
|
129
|
-
// ...
|
|
130
|
-
capabilities: { tools: { supported: true, clientProvided: true } },
|
|
131
|
-
});
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
BuiltInAgent shallow-merges capabilities at the category level — providing
|
|
135
|
-
`tools: {...}` replaces the whole category, not just the specified fields.
|
|
136
|
-
The client then sees exactly what was declared.
|
|
137
|
-
|
|
138
|
-
Source: `packages/runtime/src/agent/index.ts:821-829,883-887`
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
# CopilotKit Chat Components (React)
|
|
2
|
-
|
|
3
|
-
This skill builds on `copilotkit/provider-setup`. Read it first — every
|
|
4
|
-
chat component must be inside the `CopilotKit` provider (from
|
|
5
|
-
`@copilotkit/react-core/v2`).
|
|
6
|
-
|
|
7
|
-
All chat components live on `@copilotkit/react-core/v2`. The legacy
|
|
8
|
-
`@copilotkit/react-ui` package is v1-only; its `/v2` subpath is a CSS-only
|
|
9
|
-
import.
|
|
10
|
-
|
|
11
|
-
## Setup
|
|
12
|
-
|
|
13
|
-
```tsx
|
|
14
|
-
"use client";
|
|
15
|
-
import { CopilotChat } from "@copilotkit/react-core/v2";
|
|
16
|
-
import "@copilotkit/react-core/v2/styles.css";
|
|
17
|
-
|
|
18
|
-
export function ChatPanel() {
|
|
19
|
-
return <CopilotChat agentId="default" />;
|
|
20
|
-
}
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
`<CopilotChat>` manages messages, input, streaming, attachments, and
|
|
24
|
-
suggestions internally via `useAgent`. You do not pass `messages` or
|
|
25
|
-
`isRunning` — they are managed for you.
|
|
26
|
-
|
|
27
|
-
## Core Patterns
|
|
28
|
-
|
|
29
|
-
### Floating popup
|
|
30
|
-
|
|
31
|
-
```tsx
|
|
32
|
-
import { CopilotPopup } from "@copilotkit/react-core/v2";
|
|
33
|
-
|
|
34
|
-
<CopilotPopup agentId="default" defaultOpen={false} />;
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Persistent sidebar
|
|
38
|
-
|
|
39
|
-
```tsx
|
|
40
|
-
import { CopilotSidebar } from "@copilotkit/react-core/v2";
|
|
41
|
-
|
|
42
|
-
<CopilotSidebar agentId="default">
|
|
43
|
-
<MainAppContent />
|
|
44
|
-
</CopilotSidebar>;
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Headless composition with slot primitives
|
|
48
|
-
|
|
49
|
-
Use `CopilotChatView` plus the individual slot components when you need
|
|
50
|
-
full control over messages, input, or layout. This is the path when you
|
|
51
|
-
want to manage `messages`/`isRunning` yourself.
|
|
52
|
-
|
|
53
|
-
```tsx
|
|
54
|
-
import {
|
|
55
|
-
CopilotChatView,
|
|
56
|
-
useAgent,
|
|
57
|
-
useCopilotKit,
|
|
58
|
-
} from "@copilotkit/react-core/v2";
|
|
59
|
-
|
|
60
|
-
export function HeadlessChat() {
|
|
61
|
-
const { agent } = useAgent({ agentId: "default" });
|
|
62
|
-
const { copilotkit } = useCopilotKit();
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<CopilotChatView
|
|
66
|
-
messages={agent.messages}
|
|
67
|
-
isRunning={agent.isRunning}
|
|
68
|
-
onSubmitMessage={async (text) => {
|
|
69
|
-
agent.addMessage({
|
|
70
|
-
id: crypto.randomUUID(),
|
|
71
|
-
role: "user",
|
|
72
|
-
content: text,
|
|
73
|
-
});
|
|
74
|
-
await copilotkit.runAgent({ agent });
|
|
75
|
-
}}
|
|
76
|
-
>
|
|
77
|
-
{({ messageView, input }) => (
|
|
78
|
-
<>
|
|
79
|
-
{messageView}
|
|
80
|
-
{input}
|
|
81
|
-
</>
|
|
82
|
-
)}
|
|
83
|
-
</CopilotChatView>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Custom labels
|
|
89
|
-
|
|
90
|
-
```tsx
|
|
91
|
-
<CopilotChat
|
|
92
|
-
agentId="default"
|
|
93
|
-
labels={{
|
|
94
|
-
chatInputPlaceholder: "Ask about the data…",
|
|
95
|
-
welcomeMessageText: "What would you like to analyze?",
|
|
96
|
-
}}
|
|
97
|
-
/>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Common Mistakes
|
|
101
|
-
|
|
102
|
-
### CRITICAL — Importing `CopilotPanel`
|
|
103
|
-
|
|
104
|
-
Wrong:
|
|
105
|
-
|
|
106
|
-
```tsx
|
|
107
|
-
import { CopilotPanel } from "@copilotkit/react-core/v2";
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Correct:
|
|
111
|
-
|
|
112
|
-
```tsx
|
|
113
|
-
import {
|
|
114
|
-
CopilotChat,
|
|
115
|
-
CopilotPopup,
|
|
116
|
-
CopilotSidebar,
|
|
117
|
-
CopilotChatView,
|
|
118
|
-
} from "@copilotkit/react-core/v2";
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
`CopilotPanel` does not exist in v2 (or v1). This is a common hallucination.
|
|
122
|
-
The four chat surfaces are `CopilotChat`, `CopilotPopup`, `CopilotSidebar`,
|
|
123
|
-
and the headless `CopilotChatView`.
|
|
124
|
-
|
|
125
|
-
Source: `packages/react-core/src/v2/components/chat/index.ts` (no `CopilotPanel` export)
|
|
126
|
-
|
|
127
|
-
### CRITICAL — Importing chat components from `@copilotkit/react-ui` in v2
|
|
128
|
-
|
|
129
|
-
Wrong:
|
|
130
|
-
|
|
131
|
-
```tsx
|
|
132
|
-
import { CopilotPopup } from "@copilotkit/react-ui";
|
|
133
|
-
import "@copilotkit/react-ui/styles.css";
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Correct:
|
|
137
|
-
|
|
138
|
-
```tsx
|
|
139
|
-
import { CopilotPopup } from "@copilotkit/react-core/v2";
|
|
140
|
-
import "@copilotkit/react-core/v2/styles.css";
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
`@copilotkit/react-ui` is v1 only. The v2 subpath of `react-ui` is a
|
|
144
|
-
CSS-only import — the components are not there. All v2 chat components ship
|
|
145
|
-
from `@copilotkit/react-core/v2`.
|
|
146
|
-
|
|
147
|
-
Source: `packages/react-ui/src/v2/index.ts` (CSS-only); v2 migration guide
|
|
148
|
-
|
|
149
|
-
### HIGH — Passing `messages` or `isRunning` to `<CopilotChat>`
|
|
150
|
-
|
|
151
|
-
Wrong:
|
|
152
|
-
|
|
153
|
-
```tsx
|
|
154
|
-
<CopilotChat agentId="default" messages={myMessages} isRunning={busy} />
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
Correct:
|
|
158
|
-
|
|
159
|
-
```tsx
|
|
160
|
-
// CopilotChat manages messages and isRunning internally.
|
|
161
|
-
<CopilotChat agentId="default" />
|
|
162
|
-
|
|
163
|
-
// For manual control, drop down to headless CopilotChatView. Its `children`
|
|
164
|
-
// is a render prop that receives the bound slot elements:
|
|
165
|
-
<CopilotChatView
|
|
166
|
-
messages={myMessages}
|
|
167
|
-
isRunning={busy}
|
|
168
|
-
onSubmitMessage={handleSubmit}
|
|
169
|
-
>
|
|
170
|
-
{({ messageView, input }) => (
|
|
171
|
-
<>
|
|
172
|
-
{messageView}
|
|
173
|
-
{input}
|
|
174
|
-
</>
|
|
175
|
-
)}
|
|
176
|
-
</CopilotChatView>
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
`CopilotChatProps` explicitly `Omit`s `messages` and `isRunning` — passing
|
|
180
|
-
them is a TypeScript error, and `<CopilotChat>` always reads from its
|
|
181
|
-
internal `useAgent` call.
|
|
182
|
-
|
|
183
|
-
Source: `packages/react-core/src/v2/components/chat/CopilotChat.tsx:37-52`
|
|
184
|
-
|
|
185
|
-
### MEDIUM — Two `<CopilotChat>` with the same `agentId` + `threadId`
|
|
186
|
-
|
|
187
|
-
Wrong:
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
<CopilotChat agentId="research" threadId="t1" />
|
|
191
|
-
<CopilotChat agentId="research" threadId="t1" />
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Correct:
|
|
195
|
-
|
|
196
|
-
```tsx
|
|
197
|
-
// Either use distinct threadIds...
|
|
198
|
-
<CopilotChat agentId="research" threadId="panel-a" />
|
|
199
|
-
<CopilotChat agentId="research" threadId="panel-b" />
|
|
200
|
-
|
|
201
|
-
// ...or mount only one <CopilotChat> instance per agent/thread.
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Both components resolve to the same per-thread clone (cached in a
|
|
205
|
-
module-level WeakMap) and submit duplicate messages. See `agent-access` for
|
|
206
|
-
the clone semantics.
|
|
207
|
-
|
|
208
|
-
Source: `packages/react-core/src/v2/hooks/use-agent.tsx:78-119`
|
|
209
|
-
|
|
210
|
-
### MEDIUM — Missing the v2 CSS import
|
|
211
|
-
|
|
212
|
-
Wrong:
|
|
213
|
-
|
|
214
|
-
```tsx
|
|
215
|
-
import { CopilotChat } from "@copilotkit/react-core/v2";
|
|
216
|
-
// …no styles imported
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
Correct:
|
|
220
|
-
|
|
221
|
-
```tsx
|
|
222
|
-
import { CopilotChat } from "@copilotkit/react-core/v2";
|
|
223
|
-
import "@copilotkit/react-core/v2/styles.css";
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
The chat components ship unstyled without the v2 stylesheet. Import it once
|
|
227
|
-
at the root of the app or in the same file that sets up the provider.
|
|
228
|
-
|
|
229
|
-
Source: `packages/react-core/src/v2/index.ts:3` (imports `./index.css`)
|
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
# CopilotKit Client-Side Tools (React)
|
|
2
|
-
|
|
3
|
-
This skill builds on `copilotkit/provider-setup`. Tools registered via
|
|
4
|
-
`useFrontendTool` execute in the browser and are exposed to the agent over
|
|
5
|
-
AG-UI.
|
|
6
|
-
|
|
7
|
-
Hook signature:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
useFrontendTool<T>(tool: ReactFrontendTool<T>, deps?: ReadonlyArray<unknown>);
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
The hook re-registers when `tool.name`, `tool.available`, or any entry in
|
|
14
|
-
`deps` changes. Closures inside `handler` capture React state at
|
|
15
|
-
registration time — pass `deps` when the handler references state.
|
|
16
|
-
|
|
17
|
-
## UI-kit detection rule
|
|
18
|
-
|
|
19
|
-
Before writing any `render` JSX, check the consumer's `package.json` for a
|
|
20
|
-
UI kit and reuse its primitives:
|
|
21
|
-
|
|
22
|
-
- `components/ui/*` (shadcn)
|
|
23
|
-
- `@mui/material` (MUI)
|
|
24
|
-
- `@chakra-ui/react` (Chakra)
|
|
25
|
-
- `antd` (Ant Design)
|
|
26
|
-
- `@mantine/core` (Mantine)
|
|
27
|
-
|
|
28
|
-
Only write raw JSX if no kit is present.
|
|
29
|
-
|
|
30
|
-
## Setup
|
|
31
|
-
|
|
32
|
-
```tsx
|
|
33
|
-
"use client";
|
|
34
|
-
import { useFrontendTool } from "@copilotkit/react-core/v2";
|
|
35
|
-
import { z } from "zod";
|
|
36
|
-
|
|
37
|
-
export function SearchToolHost() {
|
|
38
|
-
useFrontendTool({
|
|
39
|
-
name: "searchDocs",
|
|
40
|
-
description: "Search the in-app documentation",
|
|
41
|
-
parameters: z.object({ query: z.string() }),
|
|
42
|
-
handler: async ({ query }, { signal }) => {
|
|
43
|
-
const r = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
|
|
44
|
-
signal,
|
|
45
|
-
});
|
|
46
|
-
return (await r.json()).results.join("\n");
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
`zod` is a hard peer dependency — install it alongside `@copilotkit/react-core`.
|
|
54
|
-
|
|
55
|
-
## Core Patterns
|
|
56
|
-
|
|
57
|
-
### Handler with React state + deps
|
|
58
|
-
|
|
59
|
-
```tsx
|
|
60
|
-
const [cart, setCart] = useState<string[]>([]);
|
|
61
|
-
|
|
62
|
-
useFrontendTool(
|
|
63
|
-
{
|
|
64
|
-
name: "addItem",
|
|
65
|
-
parameters: z.object({ id: z.string() }),
|
|
66
|
-
handler: async ({ id }) => {
|
|
67
|
-
setCart((c) => [...c, id]);
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
[setCart],
|
|
71
|
-
);
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Forward `signal` into fetch (so `stopAgent` cancels in-flight calls)
|
|
75
|
-
|
|
76
|
-
```tsx
|
|
77
|
-
useFrontendTool({
|
|
78
|
-
name: "search",
|
|
79
|
-
parameters: z.object({ q: z.string() }),
|
|
80
|
-
handler: async ({ q }, { signal }) => {
|
|
81
|
-
const r = await fetch(`/search?q=${q}`, { signal });
|
|
82
|
-
return r.text();
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Render progress UI for a tool (reuse the consumer's UI kit)
|
|
88
|
-
|
|
89
|
-
```tsx
|
|
90
|
-
// Consumer has shadcn → use Card + Skeleton
|
|
91
|
-
import { Card, CardContent } from "@/components/ui/card";
|
|
92
|
-
import { Skeleton } from "@/components/ui/skeleton";
|
|
93
|
-
|
|
94
|
-
useFrontendTool({
|
|
95
|
-
name: "show",
|
|
96
|
-
parameters: z.object({ id: z.string() }),
|
|
97
|
-
handler: async ({ id }) => fetchItem(id),
|
|
98
|
-
render: ({ status, parameters, result }) => (
|
|
99
|
-
<Card>
|
|
100
|
-
{status === "inProgress" ? (
|
|
101
|
-
<Skeleton className="h-24 w-full" />
|
|
102
|
-
) : (
|
|
103
|
-
<CardContent>{result}</CardContent>
|
|
104
|
-
)}
|
|
105
|
-
</Card>
|
|
106
|
-
),
|
|
107
|
-
});
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### Programmatic invocation with string follow-up
|
|
111
|
-
|
|
112
|
-
`copilotkit.runTool` accepts `followUp: boolean | "generate" | string`.
|
|
113
|
-
A string is injected as a synthetic user message before the agent runs.
|
|
114
|
-
|
|
115
|
-
```tsx
|
|
116
|
-
import { useCopilotKit } from "@copilotkit/react-core/v2";
|
|
117
|
-
|
|
118
|
-
const { copilotkit } = useCopilotKit();
|
|
119
|
-
|
|
120
|
-
await copilotkit.runTool({
|
|
121
|
-
name: "searchDocs",
|
|
122
|
-
parameters: { query: "zod" },
|
|
123
|
-
followUp: "Summarize these results in 3 bullets", // inject as user message, run agent
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Common Mistakes
|
|
128
|
-
|
|
129
|
-
### CRITICAL — Writing JSX from scratch for `render` when the app has a UI kit
|
|
130
|
-
|
|
131
|
-
Wrong:
|
|
132
|
-
|
|
133
|
-
```tsx
|
|
134
|
-
useFrontendTool({
|
|
135
|
-
name: "show",
|
|
136
|
-
parameters: z.object({ id: z.string() }),
|
|
137
|
-
handler,
|
|
138
|
-
render: ({ status }) => <div style={{ padding: 12 }}>…</div>,
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Correct:
|
|
143
|
-
|
|
144
|
-
```tsx
|
|
145
|
-
// First check package.json for shadcn / @mui/* / @chakra-ui/* / antd / @mantine/*, then:
|
|
146
|
-
import { Card, CardContent } from "@/components/ui/card";
|
|
147
|
-
import { Skeleton } from "@/components/ui/skeleton";
|
|
148
|
-
|
|
149
|
-
useFrontendTool({
|
|
150
|
-
name: "show",
|
|
151
|
-
parameters: z.object({ id: z.string() }),
|
|
152
|
-
handler,
|
|
153
|
-
render: ({ status, result }) => (
|
|
154
|
-
<Card>
|
|
155
|
-
{status === "inProgress" ? (
|
|
156
|
-
<Skeleton />
|
|
157
|
-
) : (
|
|
158
|
-
<CardContent>{result}</CardContent>
|
|
159
|
-
)}
|
|
160
|
-
</Card>
|
|
161
|
-
),
|
|
162
|
-
});
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
Consumers almost always have a UI kit. Raw JSX produces unbranded output
|
|
166
|
-
and skips the accessibility patterns their existing primitives encode.
|
|
167
|
-
|
|
168
|
-
Source: maintainer interview (Phase 2c)
|
|
169
|
-
|
|
170
|
-
### HIGH — Stale closure inside `handler`
|
|
171
|
-
|
|
172
|
-
Wrong:
|
|
173
|
-
|
|
174
|
-
```tsx
|
|
175
|
-
useFrontendTool({
|
|
176
|
-
name: "addItem",
|
|
177
|
-
parameters: z.object({ id: z.string() }),
|
|
178
|
-
handler: async ({ id }) => {
|
|
179
|
-
addTo(cart, id); // `cart` is captured at registration — goes stale
|
|
180
|
-
},
|
|
181
|
-
});
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Correct:
|
|
185
|
-
|
|
186
|
-
```tsx
|
|
187
|
-
useFrontendTool(
|
|
188
|
-
{
|
|
189
|
-
name: "addItem",
|
|
190
|
-
parameters: z.object({ id: z.string() }),
|
|
191
|
-
handler: async ({ id }) => {
|
|
192
|
-
addTo(cart, id);
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
[cart],
|
|
196
|
-
);
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
`useFrontendTool` only re-registers when `name`, `available`, or `deps`
|
|
200
|
-
change. Without `deps`, closures over React state freeze at first mount.
|
|
201
|
-
|
|
202
|
-
Source: `packages/react-core/src/v2/hooks/use-frontend-tool.tsx:45`
|
|
203
|
-
|
|
204
|
-
### HIGH — Ignoring `signal` in async handlers
|
|
205
|
-
|
|
206
|
-
Wrong:
|
|
207
|
-
|
|
208
|
-
```tsx
|
|
209
|
-
useFrontendTool({
|
|
210
|
-
name: "search",
|
|
211
|
-
parameters: z.object({ q: z.string() }),
|
|
212
|
-
handler: async ({ q }) => (await fetch(`/search?q=${q}`)).text(),
|
|
213
|
-
});
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Correct:
|
|
217
|
-
|
|
218
|
-
```tsx
|
|
219
|
-
useFrontendTool({
|
|
220
|
-
name: "search",
|
|
221
|
-
parameters: z.object({ q: z.string() }),
|
|
222
|
-
handler: async ({ q }, { signal }) =>
|
|
223
|
-
(await fetch(`/search?q=${q}`, { signal })).text(),
|
|
224
|
-
});
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
`stopAgent` / `agent.abortRun` abort via `AbortSignal`. A handler that
|
|
228
|
-
doesn't forward `signal` keeps fetching after cancel, racing the next turn.
|
|
229
|
-
|
|
230
|
-
Source: `packages/core/src/types.ts:24-30`
|
|
231
|
-
|
|
232
|
-
### HIGH — Assuming `followUp` defaults to `false`
|
|
233
|
-
|
|
234
|
-
Wrong:
|
|
235
|
-
|
|
236
|
-
```tsx
|
|
237
|
-
useFrontendTool({
|
|
238
|
-
name: "logAnalyticsEvent",
|
|
239
|
-
parameters: z.object({ name: z.string() }),
|
|
240
|
-
handler: async ({ name }) => {
|
|
241
|
-
analytics.track(name);
|
|
242
|
-
},
|
|
243
|
-
// followUp omitted → defaults to TRUE. Agent re-runs after every analytics call.
|
|
244
|
-
});
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
Correct:
|
|
248
|
-
|
|
249
|
-
```tsx
|
|
250
|
-
useFrontendTool({
|
|
251
|
-
name: "logAnalyticsEvent",
|
|
252
|
-
parameters: z.object({ name: z.string() }),
|
|
253
|
-
handler: async ({ name }) => {
|
|
254
|
-
analytics.track(name);
|
|
255
|
-
},
|
|
256
|
-
followUp: false, // side-effect tool — don't re-invoke the agent
|
|
257
|
-
});
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
For agent-invoked tools, run-handler checks `tool?.followUp !== false` — so
|
|
261
|
-
`undefined` AND `true` both fire a follow-up `runAgent`. Only explicit
|
|
262
|
-
`false` suppresses it. Pure side-effect tools must opt out or they loop.
|
|
263
|
-
|
|
264
|
-
Source: `packages/core/src/core/run-handler.ts:607`
|
|
265
|
-
|
|
266
|
-
### HIGH — Missing `zod` peer dependency
|
|
267
|
-
|
|
268
|
-
Wrong:
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
pnpm install @copilotkit/react-core
|
|
272
|
-
# zod missing — the CopilotKit provider fails to load
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
Correct:
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
pnpm install @copilotkit/react-core zod
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
`zod` is a hard peer of `@copilotkit/react-core` and is imported at
|
|
282
|
-
provider module scope. Without it the provider module throws on load.
|
|
283
|
-
|
|
284
|
-
Source: `packages/react-core/package.json` (peerDependencies)
|
|
285
|
-
|
|
286
|
-
### MEDIUM — Duplicate tool name across hooks
|
|
287
|
-
|
|
288
|
-
Wrong:
|
|
289
|
-
|
|
290
|
-
```tsx
|
|
291
|
-
// ComponentA
|
|
292
|
-
useFrontendTool({ name: "save", parameters, handler: saveA });
|
|
293
|
-
// ComponentB mounted in same tree:
|
|
294
|
-
useFrontendTool({ name: "save", parameters, handler: saveB });
|
|
295
|
-
// console.warn: "Tool 'save' already exists … Overriding"
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
Correct:
|
|
299
|
-
|
|
300
|
-
```tsx
|
|
301
|
-
useFrontendTool({
|
|
302
|
-
name: "save",
|
|
303
|
-
agentId: "research",
|
|
304
|
-
parameters,
|
|
305
|
-
handler: saveA,
|
|
306
|
-
});
|
|
307
|
-
useFrontendTool({
|
|
308
|
-
name: "save",
|
|
309
|
-
agentId: "coding",
|
|
310
|
-
parameters,
|
|
311
|
-
handler: saveB,
|
|
312
|
-
});
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
Tool names must be globally unique per `agentId`. Second mount warns and
|
|
316
|
-
replaces the first. Scope with `agentId` when multiple agents need their
|
|
317
|
-
own "save" handler.
|
|
318
|
-
|
|
319
|
-
Source: `packages/react-core/src/v2/hooks/use-frontend-tool.tsx:17-22`
|
|
320
|
-
|
|
321
|
-
### MEDIUM — Passing `"generate"` or a string to `useFrontendTool`'s `followUp`
|
|
322
|
-
|
|
323
|
-
Wrong:
|
|
324
|
-
|
|
325
|
-
```tsx
|
|
326
|
-
useFrontendTool({
|
|
327
|
-
name: "searchDocs",
|
|
328
|
-
parameters: z.object({ q: z.string() }),
|
|
329
|
-
handler,
|
|
330
|
-
followUp: "Summarize these results" as any, // silently truthy on registered tools
|
|
331
|
-
});
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
Correct:
|
|
335
|
-
|
|
336
|
-
```tsx
|
|
337
|
-
// Registered tools — boolean only:
|
|
338
|
-
useFrontendTool({
|
|
339
|
-
name: "searchDocs",
|
|
340
|
-
parameters: z.object({ q: z.string() }),
|
|
341
|
-
handler,
|
|
342
|
-
followUp: true,
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
// For string follow-ups, call runTool programmatically:
|
|
346
|
-
const { copilotkit } = useCopilotKit();
|
|
347
|
-
await copilotkit.runTool({
|
|
348
|
-
name: "searchDocs",
|
|
349
|
-
parameters: { q: "zod" },
|
|
350
|
-
followUp: "Summarize these results", // injects user message, runs agent
|
|
351
|
-
});
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
`FrontendTool.followUp` is typed `boolean`. Strings are silently truthy
|
|
355
|
-
(treated as `true`). The `"generate"` and custom-string modes only work
|
|
356
|
-
on `copilotkit.runTool({ followUp })`.
|
|
357
|
-
|
|
358
|
-
Source: `packages/core/src/types.ts:39`; `packages/core/src/core/run-handler.ts:47,763,848-863`
|