@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,201 +0,0 @@
|
|
|
1
|
-
# CopilotKit Rendering Activity Messages (React)
|
|
2
|
-
|
|
3
|
-
This skill builds on `copilotkit/provider-setup`. Activity-message
|
|
4
|
-
renderers are registered as entries in the `renderActivityMessages` array
|
|
5
|
-
prop on the `CopilotKit` provider and resolved at render time by
|
|
6
|
-
`useRenderActivityMessage` (consumed internally by chat components).
|
|
7
|
-
|
|
8
|
-
User renderers are placed first in the array so they override the built-in
|
|
9
|
-
`MCPAppsActivityType` and `OpenGenerativeUIActivityType` renderers for the
|
|
10
|
-
same `activityType`.
|
|
11
|
-
|
|
12
|
-
Resolver order:
|
|
13
|
-
|
|
14
|
-
1. `(activityType, agentId)` match
|
|
15
|
-
2. `(activityType, unscoped)` match
|
|
16
|
-
3. `'*'` wildcard
|
|
17
|
-
4. `null`
|
|
18
|
-
|
|
19
|
-
## Setup
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
"use client";
|
|
23
|
-
import { CopilotKit } from "@copilotkit/react-core/v2";
|
|
24
|
-
import type { ReactActivityMessageRenderer } from "@copilotkit/react-core/v2";
|
|
25
|
-
import { z } from "zod";
|
|
26
|
-
import { useMemo } from "react";
|
|
27
|
-
import { Card, CardContent } from "@/components/ui/card";
|
|
28
|
-
import { Progress } from "@/components/ui/progress";
|
|
29
|
-
|
|
30
|
-
const progressRenderer: ReactActivityMessageRenderer<{
|
|
31
|
-
percent: number;
|
|
32
|
-
label: string;
|
|
33
|
-
}> = {
|
|
34
|
-
activityType: "progress",
|
|
35
|
-
content: z.object({ percent: z.number().min(0).max(1), label: z.string() }),
|
|
36
|
-
render: ({ content }) => (
|
|
37
|
-
<Card>
|
|
38
|
-
<CardContent>
|
|
39
|
-
<div>{content.label}</div>
|
|
40
|
-
<Progress value={content.percent * 100} />
|
|
41
|
-
</CardContent>
|
|
42
|
-
</Card>
|
|
43
|
-
),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export function Providers({ children }: { children: React.ReactNode }) {
|
|
47
|
-
const renderers = useMemo(() => [progressRenderer], []);
|
|
48
|
-
return (
|
|
49
|
-
<CopilotKit runtimeUrl="/api/copilotkit" renderActivityMessages={renderers}>
|
|
50
|
-
{children}
|
|
51
|
-
</CopilotKit>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Core Patterns
|
|
57
|
-
|
|
58
|
-
### Agent-scoped renderer
|
|
59
|
-
|
|
60
|
-
```tsx
|
|
61
|
-
const researchProgress: ReactActivityMessageRenderer<{ step: string }> = {
|
|
62
|
-
activityType: "research-step",
|
|
63
|
-
agentId: "research",
|
|
64
|
-
content: z.object({ step: z.string() }),
|
|
65
|
-
render: ({ content }) => <ResearchStepBadge step={content.step} />,
|
|
66
|
-
};
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Override a built-in (MCP Apps)
|
|
70
|
-
|
|
71
|
-
Place your renderer for the same `activityType` — user renderers are
|
|
72
|
-
evaluated before built-ins.
|
|
73
|
-
|
|
74
|
-
```tsx
|
|
75
|
-
import { MCPAppsActivityType } from "@copilotkit/react-core/v2";
|
|
76
|
-
|
|
77
|
-
const customMcpRenderer: ReactActivityMessageRenderer<unknown> = {
|
|
78
|
-
activityType: MCPAppsActivityType, // "mcp-apps" — must match the exported constant
|
|
79
|
-
content: z.unknown(),
|
|
80
|
-
render: ({ content, message }) => <CustomMCPCard payload={content} />,
|
|
81
|
-
};
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Using the hook directly (custom chat surface)
|
|
85
|
-
|
|
86
|
-
```tsx
|
|
87
|
-
import { useRenderActivityMessage } from "@copilotkit/react-core/v2";
|
|
88
|
-
import type { ActivityMessage } from "@ag-ui/core";
|
|
89
|
-
|
|
90
|
-
export function ActivityList({ messages }: { messages: ActivityMessage[] }) {
|
|
91
|
-
const { renderActivityMessage } = useRenderActivityMessage();
|
|
92
|
-
return (
|
|
93
|
-
<div>
|
|
94
|
-
{messages.map((m) => (
|
|
95
|
-
<div key={m.id}>{renderActivityMessage(m)}</div>
|
|
96
|
-
))}
|
|
97
|
-
</div>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Common Mistakes
|
|
103
|
-
|
|
104
|
-
### HIGH — Incompatible content schema
|
|
105
|
-
|
|
106
|
-
Wrong:
|
|
107
|
-
|
|
108
|
-
```tsx
|
|
109
|
-
// Renderer expects `pct`
|
|
110
|
-
const r: ReactActivityMessageRenderer<{ pct: number }> = {
|
|
111
|
-
activityType: "progress",
|
|
112
|
-
content: z.object({ pct: z.number() }),
|
|
113
|
-
render: ({ content }) => <Bar value={content.pct} />,
|
|
114
|
-
};
|
|
115
|
-
// But the server emits { percent: 0.5 } — mismatched field name
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Correct:
|
|
119
|
-
|
|
120
|
-
```tsx
|
|
121
|
-
const r: ReactActivityMessageRenderer<{ percent: number }> = {
|
|
122
|
-
activityType: "progress",
|
|
123
|
-
content: z.object({ percent: z.number() }),
|
|
124
|
-
render: ({ content }) => <Bar value={content.percent} />,
|
|
125
|
-
};
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
`safeParse` is called on every incoming activity message. Mismatched
|
|
129
|
-
schemas return `null` with only a `console.warn("Failed to parse content
|
|
130
|
-
for activity message …")` — the UI renders nothing and the failure is
|
|
131
|
-
silent unless you read the console.
|
|
132
|
-
|
|
133
|
-
Source: `packages/react-core/src/v2/hooks/use-render-activity-message.tsx:44-50`
|
|
134
|
-
|
|
135
|
-
### MEDIUM — Side effects in `render`
|
|
136
|
-
|
|
137
|
-
Wrong:
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
render: ({ content }) => {
|
|
141
|
-
trackEvent(content); // fires on every re-render
|
|
142
|
-
return <Badge>{content.label}</Badge>;
|
|
143
|
-
};
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Wrong (Rules of Hooks violation):
|
|
147
|
-
|
|
148
|
-
```tsx
|
|
149
|
-
render: ({ content }) => {
|
|
150
|
-
// `render` is invoked as a plain function by the resolver — NOT as a
|
|
151
|
-
// React component — so calling hooks directly inside it is illegal.
|
|
152
|
-
useEffect(() => trackEvent(content), [content]);
|
|
153
|
-
return <Badge>{content.label}</Badge>;
|
|
154
|
-
};
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
Correct:
|
|
158
|
-
|
|
159
|
-
```tsx
|
|
160
|
-
function TrackedBadge({ content }: { content: { label: string } }) {
|
|
161
|
-
useEffect(() => {
|
|
162
|
-
trackEvent(content);
|
|
163
|
-
}, [content]);
|
|
164
|
-
return <Badge>{content.label}</Badge>;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// In the renderer:
|
|
168
|
-
render: ({ content }) => <TrackedBadge content={content} />;
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Activity-message renderers re-render on every message-list tick. Side
|
|
172
|
-
effects in the render body fire repeatedly. Hooks cannot be called
|
|
173
|
-
directly inside `render` because the resolver invokes it as a plain
|
|
174
|
-
function; hoist the effect into a wrapper component that React mounts as
|
|
175
|
-
a real element.
|
|
176
|
-
|
|
177
|
-
Source: `packages/react-core/src/v2/hooks/use-render-activity-message.tsx`
|
|
178
|
-
|
|
179
|
-
### MEDIUM — Building the `renderActivityMessages` array inline
|
|
180
|
-
|
|
181
|
-
Wrong:
|
|
182
|
-
|
|
183
|
-
```tsx
|
|
184
|
-
<CopilotKit
|
|
185
|
-
runtimeUrl="/api/copilotkit"
|
|
186
|
-
renderActivityMessages={[progressRenderer, customMcpRenderer]}
|
|
187
|
-
/>
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Correct:
|
|
191
|
-
|
|
192
|
-
```tsx
|
|
193
|
-
const renderers = useMemo(() => [progressRenderer, customMcpRenderer], []);
|
|
194
|
-
<CopilotKit runtimeUrl="/api/copilotkit" renderActivityMessages={renderers} />;
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
The provider uses `useStableArrayProp` and console-errors when a new array
|
|
198
|
-
identity appears every render. Memoize or hoist the array to module
|
|
199
|
-
scope.
|
|
200
|
-
|
|
201
|
-
Source: `packages/react-core/src/v2/providers/CopilotKitProvider.tsx` (useStableArrayProp)
|
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
# CopilotKit Rendering Tool Calls (React)
|
|
2
|
-
|
|
3
|
-
This skill builds on `copilotkit/provider-setup` and
|
|
4
|
-
`copilotkit/client-side-tools`.
|
|
5
|
-
|
|
6
|
-
Four hooks, distinct roles:
|
|
7
|
-
|
|
8
|
-
| Hook | Role |
|
|
9
|
-
| ---------------------- | ----------------------------------------------------------------- |
|
|
10
|
-
| `useRenderTool` | Primary registration hook for a named tool's progress/result UI |
|
|
11
|
-
| `useComponent` | Register a NEW render-only tool (agent calls it just to render) |
|
|
12
|
-
| `useDefaultRenderTool` | Sanctioned wildcard fallback for tools without a dedicated render |
|
|
13
|
-
| `useRenderToolCall` | Resolver — returns a function. For custom chat surfaces only |
|
|
14
|
-
|
|
15
|
-
Status is camelCase: `"inProgress" | "executing" | "complete"`. The
|
|
16
|
-
`RenderToolProps` discriminated union narrows `parameters` per state.
|
|
17
|
-
|
|
18
|
-
## UI-kit detection rule
|
|
19
|
-
|
|
20
|
-
Before writing raw JSX, check the consumer's `package.json` for shadcn /
|
|
21
|
-
MUI / Chakra / Ant / Mantine and reuse those primitives.
|
|
22
|
-
|
|
23
|
-
## Setup
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
"use client";
|
|
27
|
-
import { useRenderTool } from "@copilotkit/react-core/v2";
|
|
28
|
-
import { z } from "zod";
|
|
29
|
-
import { Card, CardContent } from "@/components/ui/card";
|
|
30
|
-
import { Skeleton } from "@/components/ui/skeleton";
|
|
31
|
-
|
|
32
|
-
export function SearchRenderer() {
|
|
33
|
-
useRenderTool({
|
|
34
|
-
name: "searchDocs",
|
|
35
|
-
parameters: z.object({ query: z.string() }),
|
|
36
|
-
render: ({ status, parameters, result }) => {
|
|
37
|
-
if (status === "inProgress") return <Skeleton className="h-16 w-full" />;
|
|
38
|
-
if (status === "executing") {
|
|
39
|
-
return (
|
|
40
|
-
<Card>
|
|
41
|
-
<CardContent>Searching "{parameters.query}"…</CardContent>
|
|
42
|
-
</Card>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
return (
|
|
46
|
-
<Card>
|
|
47
|
-
<CardContent>{result}</CardContent>
|
|
48
|
-
</Card>
|
|
49
|
-
);
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Core Patterns
|
|
57
|
-
|
|
58
|
-
### Wildcard fallback with the built-in card
|
|
59
|
-
|
|
60
|
-
```tsx
|
|
61
|
-
import { useDefaultRenderTool } from "@copilotkit/react-core/v2";
|
|
62
|
-
|
|
63
|
-
useDefaultRenderTool(); // renders the built-in expandable tool-call card
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Custom wildcard fallback
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
69
|
-
import { useDefaultRenderTool } from "@copilotkit/react-core/v2";
|
|
70
|
-
|
|
71
|
-
useDefaultRenderTool({
|
|
72
|
-
render: ({ name, status, parameters, result }) => {
|
|
73
|
-
// parameters is unknown — narrow by tool name
|
|
74
|
-
if (name === "search") {
|
|
75
|
-
const args = parameters as { q: string };
|
|
76
|
-
return <SearchCard q={args.q} status={status} result={result} />;
|
|
77
|
-
}
|
|
78
|
-
return <GenericCard name={name} status={status} />;
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Render-only tool (the agent's only reason to call it is to render)
|
|
84
|
-
|
|
85
|
-
```tsx
|
|
86
|
-
import { useComponent } from "@copilotkit/react-core/v2";
|
|
87
|
-
import { z } from "zod";
|
|
88
|
-
|
|
89
|
-
useComponent({
|
|
90
|
-
name: "productCard",
|
|
91
|
-
parameters: z.object({ productId: z.string() }),
|
|
92
|
-
render: ({ productId }) => <ProductCard id={productId} />,
|
|
93
|
-
});
|
|
94
|
-
// `useComponent` registers a NEW tool called "productCard".
|
|
95
|
-
// The agent calls it to render; there is no handler to run.
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Custom chat surface (resolver hook)
|
|
99
|
-
|
|
100
|
-
`useRenderToolCall` is for building your own message list, NOT for
|
|
101
|
-
registering renderers.
|
|
102
|
-
|
|
103
|
-
```tsx
|
|
104
|
-
import { useRenderToolCall } from "@copilotkit/react-core/v2";
|
|
105
|
-
import { useAgent } from "@copilotkit/react-core/v2";
|
|
106
|
-
|
|
107
|
-
export function CustomToolList() {
|
|
108
|
-
const { agent } = useAgent({ agentId: "default" });
|
|
109
|
-
const renderToolCall = useRenderToolCall();
|
|
110
|
-
|
|
111
|
-
const toolCalls = agent.messages.flatMap((m) =>
|
|
112
|
-
"toolCalls" in m ? (m.toolCalls ?? []) : [],
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
return (
|
|
116
|
-
<>
|
|
117
|
-
{toolCalls.map((tc) => (
|
|
118
|
-
<div key={tc.id}>{renderToolCall({ toolCall: tc })}</div>
|
|
119
|
-
))}
|
|
120
|
-
</>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Common Mistakes
|
|
126
|
-
|
|
127
|
-
### CRITICAL — Using `useRenderToolCall` for registration
|
|
128
|
-
|
|
129
|
-
Wrong:
|
|
130
|
-
|
|
131
|
-
```tsx
|
|
132
|
-
useRenderToolCall({
|
|
133
|
-
name: "search",
|
|
134
|
-
args: z.object({ q: z.string() }),
|
|
135
|
-
render: ({ status, args }) => <Card>…</Card>,
|
|
136
|
-
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Correct:
|
|
140
|
-
|
|
141
|
-
```tsx
|
|
142
|
-
useRenderTool({
|
|
143
|
-
name: "search",
|
|
144
|
-
parameters: z.object({ q: z.string() }),
|
|
145
|
-
render: ({ status, parameters }) => <Card>…</Card>,
|
|
146
|
-
});
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
`useRenderToolCall` takes no arguments — it returns a resolver function for
|
|
150
|
-
custom chat surfaces. Passing config to it does nothing. `useRenderTool` is
|
|
151
|
-
the registration hook.
|
|
152
|
-
|
|
153
|
-
Source: `packages/react-core/src/v2/hooks/index.ts:2,7`;
|
|
154
|
-
`packages/react-core/src/v2/hooks/use-render-tool.tsx:37-40`
|
|
155
|
-
|
|
156
|
-
### CRITICAL — Using hyphenated `"in-progress"` status
|
|
157
|
-
|
|
158
|
-
Wrong:
|
|
159
|
-
|
|
160
|
-
```tsx
|
|
161
|
-
render: ({ status, parameters, result }) => {
|
|
162
|
-
if (status === "in-progress") return <Spinner />;
|
|
163
|
-
if (status === "executing") return <RunningCard args={parameters} />;
|
|
164
|
-
return <ResultCard result={result} />;
|
|
165
|
-
};
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Correct:
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
render: ({ status, parameters, result }) => {
|
|
172
|
-
if (status === "inProgress") return <Spinner />;
|
|
173
|
-
if (status === "executing") return <RunningCard args={parameters} />;
|
|
174
|
-
return <ResultCard result={result} />;
|
|
175
|
-
};
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Real status values are camelCase: `"inProgress" | "executing" | "complete"`.
|
|
179
|
-
Hyphenated branches never match — users see no progress UI and the fallback
|
|
180
|
-
path fires.
|
|
181
|
-
|
|
182
|
-
Source: `packages/react-core/src/v2/hooks/use-render-tool.tsx:8-35`
|
|
183
|
-
|
|
184
|
-
### CRITICAL — Writing JSX from scratch when the app has a UI kit
|
|
185
|
-
|
|
186
|
-
Wrong:
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
useRenderTool({
|
|
190
|
-
name: "search",
|
|
191
|
-
parameters: z.object({ q: z.string() }),
|
|
192
|
-
render: () => <div className="my-badge">…</div>,
|
|
193
|
-
});
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Correct:
|
|
197
|
-
|
|
198
|
-
```tsx
|
|
199
|
-
import { Badge } from "@/components/ui/badge";
|
|
200
|
-
|
|
201
|
-
useRenderTool({
|
|
202
|
-
name: "search",
|
|
203
|
-
parameters: z.object({ q: z.string() }),
|
|
204
|
-
render: () => <Badge variant="secondary">…</Badge>,
|
|
205
|
-
});
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Check consumer `package.json` for shadcn / MUI / Chakra / Ant / Mantine
|
|
209
|
-
first. Raw JSX ignores their design system.
|
|
210
|
-
|
|
211
|
-
Source: maintainer interview (Phase 2c)
|
|
212
|
-
|
|
213
|
-
### HIGH — Dereferencing required fields from `Partial<T>` during `inProgress`
|
|
214
|
-
|
|
215
|
-
Wrong:
|
|
216
|
-
|
|
217
|
-
```tsx
|
|
218
|
-
render: ({ status, parameters }) => (
|
|
219
|
-
<span>{parameters.user.id.toUpperCase()}</span>
|
|
220
|
-
);
|
|
221
|
-
// `parameters` is Partial<T> during inProgress — `parameters.user` may be undefined.
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Correct:
|
|
225
|
-
|
|
226
|
-
```tsx
|
|
227
|
-
render: ({ status, parameters }) =>
|
|
228
|
-
status === "inProgress" ? (
|
|
229
|
-
<Skeleton />
|
|
230
|
-
) : (
|
|
231
|
-
<span>{parameters.user.id.toUpperCase()}</span>
|
|
232
|
-
);
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
During streaming, `RenderToolInProgressProps` has
|
|
236
|
-
`parameters: Partial<InferSchemaOutput<S>>`. Fields are `undefined` until
|
|
237
|
-
the stream completes. Narrow with `status === "inProgress"` first.
|
|
238
|
-
|
|
239
|
-
Source: `packages/react-core/src/v2/hooks/use-render-tool.tsx:8-14`
|
|
240
|
-
|
|
241
|
-
### HIGH — Using `useComponent` to decorate an existing tool
|
|
242
|
-
|
|
243
|
-
Wrong:
|
|
244
|
-
|
|
245
|
-
```tsx
|
|
246
|
-
useFrontendTool({ name: "search", parameters, handler });
|
|
247
|
-
useComponent({
|
|
248
|
-
name: "search", // creates a SECOND tool named "search" — collision
|
|
249
|
-
parameters: z.object({ q: z.string() }),
|
|
250
|
-
render: ({ q }) => <SearchCard q={q} />,
|
|
251
|
-
});
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
Correct:
|
|
255
|
-
|
|
256
|
-
```tsx
|
|
257
|
-
useFrontendTool({ name: "search", parameters, handler });
|
|
258
|
-
useRenderTool({
|
|
259
|
-
name: "search",
|
|
260
|
-
parameters: z.object({ q: z.string() }),
|
|
261
|
-
render: ({ status, parameters, result }) => {
|
|
262
|
-
if (status === "inProgress") return <Skeleton />;
|
|
263
|
-
if (status === "executing") return <div>Searching {parameters.q}…</div>;
|
|
264
|
-
return <div>{result}</div>;
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// useComponent is only for render-only tools the agent invokes:
|
|
269
|
-
useComponent({
|
|
270
|
-
name: "productCard",
|
|
271
|
-
parameters: z.object({ productId: z.string() }),
|
|
272
|
-
render: ({ productId }) => <ProductCard id={productId} />,
|
|
273
|
-
});
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
`useComponent` synthesizes a NEW tool whose only job is to render —
|
|
277
|
-
description is auto-prefixed with "Use this tool to display the …
|
|
278
|
-
component". It does NOT decorate an existing tool. The misleading name
|
|
279
|
-
trap: agents read "useComponent" as "register a component for this tool"
|
|
280
|
-
and end up with two tools colliding on the same name.
|
|
281
|
-
|
|
282
|
-
Source: `packages/react-core/src/v2/hooks/use-component.tsx:59-88`
|
|
283
|
-
|
|
284
|
-
### HIGH — Hand-rolling `useRenderTool({ name: "*" })` instead of `useDefaultRenderTool`
|
|
285
|
-
|
|
286
|
-
Wrong:
|
|
287
|
-
|
|
288
|
-
```tsx
|
|
289
|
-
useRenderTool({
|
|
290
|
-
name: "*",
|
|
291
|
-
render: ({ parameters }) => <pre>{JSON.stringify(parameters)}</pre>,
|
|
292
|
-
});
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
Correct:
|
|
296
|
-
|
|
297
|
-
```tsx
|
|
298
|
-
// Use the built-in default card:
|
|
299
|
-
useDefaultRenderTool();
|
|
300
|
-
|
|
301
|
-
// Or customize, with the correct DefaultRenderProps typing (parameters: unknown):
|
|
302
|
-
useDefaultRenderTool({
|
|
303
|
-
render: ({ name, status, parameters, result }) => {
|
|
304
|
-
if (name === "search") {
|
|
305
|
-
const args = parameters as { q: string };
|
|
306
|
-
return <SearchCard q={args.q} status={status} />;
|
|
307
|
-
}
|
|
308
|
-
return <GenericCard name={name} status={status} />;
|
|
309
|
-
},
|
|
310
|
-
});
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
The sanctioned wildcard API is `useDefaultRenderTool`. It wraps
|
|
314
|
-
`useRenderTool({ name: "*" })` with the correct `DefaultRenderProps`
|
|
315
|
-
typing (`parameters: unknown`) and provides a built-in default card when
|
|
316
|
-
no `render` is passed. Hand-rolling loses the default card and invites
|
|
317
|
-
the untyped-args footgun.
|
|
318
|
-
|
|
319
|
-
Source: `packages/react-core/src/v2/hooks/use-default-render-tool.tsx:15-64`
|