@copilotkit/react-core 1.57.3 → 1.58.0-canary.thread-id-propagation
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/LICENSE +21 -0
- package/dist/{copilotkit-CtXcs1ea.cjs → copilotkit-B4ouY7qC.cjs} +14 -3
- package/dist/copilotkit-B4ouY7qC.cjs.map +1 -0
- package/dist/copilotkit-BK9CVq9A.d.cts.map +1 -1
- package/dist/{copilotkit-CC8DjOiC.mjs → copilotkit-L4mM_JqG.mjs} +14 -3
- package/dist/copilotkit-L4mM_JqG.mjs.map +1 -0
- package/dist/copilotkit-WlmeVijs.d.mts.map +1 -1
- package/dist/index.cjs +3 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -77
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +15 -78
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/headless.cjs +11 -0
- package/dist/v2/headless.cjs.map +1 -1
- package/dist/v2/headless.d.cts.map +1 -1
- package/dist/v2/headless.d.mts.map +1 -1
- package/dist/v2/headless.mjs +11 -0
- 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 +13 -2
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +12 -13
- package/skills/react-core/SKILL.md +108 -0
- package/skills/react-core/references/agent-access.md +288 -0
- package/skills/react-core/references/attachments.md +291 -0
- package/skills/react-core/references/capabilities.md +138 -0
- package/skills/react-core/references/chat-components.md +221 -0
- package/skills/react-core/references/client-side-tools.md +358 -0
- package/skills/react-core/references/custom-message-renderers.md +226 -0
- package/skills/react-core/references/debug-mode.md +153 -0
- package/skills/react-core/references/human-in-the-loop.md +312 -0
- package/skills/react-core/references/provider-setup.md +326 -0
- package/skills/react-core/references/rendering-activity-messages.md +207 -0
- package/skills/react-core/references/rendering-tool-calls.md +319 -0
- package/skills/react-core/references/suggestions.md +211 -0
- package/skills/react-core/references/switching-agents-recipes.md +160 -0
- package/skills/react-core/references/switching-agents.md +231 -0
- package/skills/react-core/references/threads.md +226 -0
- package/.attw.json +0 -3
- package/CHANGELOG.md +0 -5043
- package/dist/copilotkit-CC8DjOiC.mjs.map +0 -1
- package/dist/copilotkit-CtXcs1ea.cjs.map +0 -1
- package/scripts/scope-preflight.mjs +0 -100
- package/src/components/CopilotListeners.tsx +0 -137
- package/src/components/__tests__/CopilotListeners.test.tsx +0 -38
- package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +0 -92
- package/src/components/copilot-provider/__tests__/copilotkit-error.test.tsx +0 -77
- package/src/components/copilot-provider/__tests__/error-visibility-prod.test.tsx +0 -70
- package/src/components/copilot-provider/__tests__/v1-explicit-threadid-bridge.test.tsx +0 -107
- package/src/components/copilot-provider/copilot-messages.tsx +0 -314
- package/src/components/copilot-provider/copilotkit-props.tsx +0 -214
- package/src/components/copilot-provider/copilotkit.tsx +0 -853
- package/src/components/copilot-provider/index.ts +0 -3
- package/src/components/dev-console/console-trigger.tsx +0 -283
- package/src/components/dev-console/developer-console-modal.tsx +0 -1016
- package/src/components/dev-console/icons.tsx +0 -106
- package/src/components/error-boundary/error-boundary.tsx +0 -99
- package/src/components/error-boundary/error-utils.tsx +0 -105
- package/src/components/index.ts +0 -1
- package/src/components/toast/exclamation-mark-icon.tsx +0 -27
- package/src/components/toast/toast-provider.tsx +0 -448
- package/src/components/usage-banner.tsx +0 -266
- package/src/context/__tests__/threads-context.test.tsx +0 -141
- package/src/context/coagent-state-renders-context.tsx +0 -89
- package/src/context/copilot-context.tsx +0 -365
- package/src/context/copilot-messages-context.tsx +0 -35
- package/src/context/index.ts +0 -22
- package/src/context/threads-context.tsx +0 -69
- package/src/hooks/__tests__/use-coagent-config.test.ts +0 -352
- package/src/hooks/__tests__/use-coagent-state-render-bridge.helpers.test.ts +0 -107
- package/src/hooks/__tests__/use-coagent-state-render.e2e.test.tsx +0 -1209
- package/src/hooks/__tests__/use-coagent-state-render.test.tsx +0 -356
- package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +0 -241
- package/src/hooks/__tests__/use-frontend-tool-available.test.tsx +0 -72
- package/src/hooks/__tests__/use-frontend-tool-remount.e2e.test.tsx +0 -102
- package/src/hooks/index.ts +0 -33
- package/src/hooks/use-agent-nodename.ts +0 -33
- package/src/hooks/use-coagent-state-render-bridge.helpers.ts +0 -345
- package/src/hooks/use-coagent-state-render-bridge.tsx +0 -222
- package/src/hooks/use-coagent-state-render-registry.ts +0 -230
- package/src/hooks/use-coagent-state-render.ts +0 -163
- package/src/hooks/use-coagent.ts +0 -377
- package/src/hooks/use-configure-chat-suggestions.tsx +0 -96
- package/src/hooks/use-copilot-action.ts +0 -245
- package/src/hooks/use-copilot-additional-instructions.ts +0 -98
- package/src/hooks/use-copilot-authenticated-action.ts +0 -73
- package/src/hooks/use-copilot-chat-headless_c.ts +0 -264
- package/src/hooks/use-copilot-chat-suggestions.tsx +0 -134
- package/src/hooks/use-copilot-chat.ts +0 -132
- package/src/hooks/use-copilot-chat_internal.ts +0 -875
- package/src/hooks/use-copilot-readable.ts +0 -135
- package/src/hooks/use-copilot-runtime-client.ts +0 -178
- package/src/hooks/use-default-tool.ts +0 -13
- package/src/hooks/use-flat-category-store.ts +0 -109
- package/src/hooks/use-frontend-tool.ts +0 -113
- package/src/hooks/use-human-in-the-loop.ts +0 -138
- package/src/hooks/use-langgraph-interrupt.ts +0 -103
- package/src/hooks/use-lazy-tool-renderer.tsx +0 -30
- package/src/hooks/use-make-copilot-document-readable.ts +0 -30
- package/src/hooks/use-render-tool-call.ts +0 -89
- package/src/hooks/use-tree.ts +0 -222
- package/src/index.tsx +0 -7
- package/src/lib/copilot-task.ts +0 -215
- package/src/lib/index.ts +0 -1
- package/src/lib/status-checker.ts +0 -67
- package/src/setupTests.ts +0 -37
- package/src/test-helpers/copilot-context.ts +0 -91
- package/src/types/chat-suggestion-configuration.ts +0 -23
- package/src/types/coagent-action.ts +0 -35
- package/src/types/coagent-state.ts +0 -13
- package/src/types/crew.ts +0 -89
- package/src/types/document-pointer.ts +0 -7
- package/src/types/frontend-action.ts +0 -213
- package/src/types/index.ts +0 -17
- package/src/types/interrupt-action.ts +0 -58
- package/src/types/system-message.ts +0 -4
- package/src/utils/dev-console.ts +0 -19
- package/src/utils/index.ts +0 -2
- package/src/utils/suggestions-constants.ts +0 -8
- package/src/utils/utils.test.ts +0 -7
- package/src/utils/utils.ts +0 -6
- package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +0 -240
- package/src/v2/__tests__/globalSetup.ts +0 -14
- package/src/v2/__tests__/setup.ts +0 -93
- package/src/v2/__tests__/utils/test-helpers.tsx +0 -570
- package/src/v2/a2ui/A2UICatalogContext.tsx +0 -79
- package/src/v2/a2ui/A2UIMessageRenderer.tsx +0 -294
- package/src/v2/a2ui/A2UIToolCallRenderer.tsx +0 -290
- package/src/v2/components/CopilotKitInspector.tsx +0 -52
- package/src/v2/components/MCPAppsActivityRenderer.tsx +0 -815
- package/src/v2/components/OpenGenerativeUIRenderer.tsx +0 -598
- package/src/v2/components/WildcardToolCallRender.tsx +0 -86
- package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +0 -665
- package/src/v2/components/chat/CopilotChat.tsx +0 -664
- package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +0 -393
- package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +0 -374
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +0 -159
- package/src/v2/components/chat/CopilotChatAudioRecorder.tsx +0 -350
- package/src/v2/components/chat/CopilotChatInput.tsx +0 -1412
- package/src/v2/components/chat/CopilotChatMessageView.tsx +0 -716
- package/src/v2/components/chat/CopilotChatReasoningMessage.tsx +0 -265
- package/src/v2/components/chat/CopilotChatSuggestionPill.tsx +0 -59
- package/src/v2/components/chat/CopilotChatSuggestionView.tsx +0 -134
- package/src/v2/components/chat/CopilotChatToggleButton.tsx +0 -171
- package/src/v2/components/chat/CopilotChatToolCallsView.tsx +0 -40
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +0 -445
- package/src/v2/components/chat/CopilotChatView.tsx +0 -890
- package/src/v2/components/chat/CopilotModalHeader.tsx +0 -129
- package/src/v2/components/chat/CopilotPopup.tsx +0 -81
- package/src/v2/components/chat/CopilotPopupView.tsx +0 -317
- package/src/v2/components/chat/CopilotSidebar.tsx +0 -80
- package/src/v2/components/chat/CopilotSidebarView.tsx +0 -269
- package/src/v2/components/chat/Lightbox.tsx +0 -103
- package/src/v2/components/chat/__tests__/CopilotChat.absentThreadConnect.test.tsx +0 -66
- package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +0 -168
- package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.tsx +0 -1239
- package/src/v2/components/chat/__tests__/CopilotChat.onError.test.tsx +0 -73
- package/src/v2/components/chat/__tests__/CopilotChat.slots.e2e.test.tsx +0 -432
- package/src/v2/components/chat/__tests__/CopilotChat.suggestionsAlways.test.tsx +0 -183
- package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +0 -184
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +0 -649
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.tsx +0 -624
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.tsx +0 -702
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.thumbs.test.tsx +0 -72
- package/src/v2/components/chat/__tests__/CopilotChatCopyButton.clipboard.test.tsx +0 -241
- package/src/v2/components/chat/__tests__/CopilotChatCssClasses.test.tsx +0 -107
- package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.tsx +0 -929
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +0 -1567
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.slots.e2e.test.tsx +0 -1004
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.tsx +0 -279
- package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +0 -336
- package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +0 -249
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.tsx +0 -530
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +0 -785
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +0 -2416
- package/src/v2/components/chat/__tests__/CopilotChatUserMessage.slots.e2e.test.tsx +0 -621
- package/src/v2/components/chat/__tests__/CopilotChatView.connectingGate.test.tsx +0 -56
- package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.tsx +0 -264
- package/src/v2/components/chat/__tests__/CopilotChatView.onClick.e2e.test.tsx +0 -853
- package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.tsx +0 -94
- package/src/v2/components/chat/__tests__/CopilotChatView.slots.e2e.test.tsx +0 -1050
- package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.tsx +0 -484
- package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.tsx +0 -612
- package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.tsx +0 -159
- package/src/v2/components/chat/__tests__/CopilotSidebarView.slots.e2e.test.tsx +0 -502
- package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +0 -1068
- package/src/v2/components/chat/__tests__/MCPAppsProxy.e2e.test.tsx +0 -589
- package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.tsx +0 -403
- package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +0 -137
- package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +0 -37
- package/src/v2/components/chat/__tests__/setup.ts +0 -1
- package/src/v2/components/chat/index.ts +0 -90
- package/src/v2/components/chat/last-user-message-context.ts +0 -21
- package/src/v2/components/chat/normalize-auto-scroll.ts +0 -17
- package/src/v2/components/chat/scroll-element-context.ts +0 -13
- package/src/v2/components/index.ts +0 -8
- package/src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx +0 -286
- package/src/v2/components/intelligence-indicator/__tests__/IntelligenceIndicator.e2e.test.tsx +0 -464
- package/src/v2/components/intelligence-indicator/index.ts +0 -2
- package/src/v2/components/license-warning-banner.tsx +0 -217
- package/src/v2/components/ui/button.tsx +0 -124
- package/src/v2/components/ui/dropdown-menu.tsx +0 -258
- package/src/v2/components/ui/tooltip.tsx +0 -60
- package/src/v2/context.ts +0 -62
- package/src/v2/headless.ts +0 -64
- package/src/v2/hooks/__tests__/standard-schema-types.test.tsx +0 -152
- package/src/v2/hooks/__tests__/standard-schema.test.tsx +0 -282
- package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +0 -140
- package/src/v2/hooks/__tests__/use-agent-context.test.tsx +0 -401
- package/src/v2/hooks/__tests__/use-agent-error-state.test.tsx +0 -44
- package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +0 -211
- package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +0 -1029
- package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +0 -159
- package/src/v2/hooks/__tests__/use-attachments.test.tsx +0 -169
- package/src/v2/hooks/__tests__/use-capabilities.test.tsx +0 -76
- package/src/v2/hooks/__tests__/use-component.test.tsx +0 -126
- package/src/v2/hooks/__tests__/use-configure-suggestions.e2e.test.tsx +0 -696
- package/src/v2/hooks/__tests__/use-default-render-tool.test.tsx +0 -153
- package/src/v2/hooks/__tests__/use-frontend-tool-available.test.tsx +0 -167
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +0 -2148
- package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.tsx +0 -1261
- package/src/v2/hooks/__tests__/use-interrupt.test.tsx +0 -397
- package/src/v2/hooks/__tests__/use-katex-styles.test.tsx +0 -56
- package/src/v2/hooks/__tests__/use-keyboard-height.test.tsx +0 -192
- package/src/v2/hooks/__tests__/use-pin-to-send.test.tsx +0 -219
- package/src/v2/hooks/__tests__/use-render-custom-messages.test.tsx +0 -55
- package/src/v2/hooks/__tests__/use-render-tool.test.tsx +0 -259
- package/src/v2/hooks/__tests__/use-suggestions.e2e.test.tsx +0 -524
- package/src/v2/hooks/__tests__/use-threads.test.tsx +0 -757
- package/src/v2/hooks/__tests__/zod-regression.test.tsx +0 -311
- package/src/v2/hooks/index.ts +0 -24
- package/src/v2/hooks/use-agent-context.tsx +0 -45
- package/src/v2/hooks/use-agent.tsx +0 -227
- package/src/v2/hooks/use-attachments.tsx +0 -269
- package/src/v2/hooks/use-capabilities.tsx +0 -25
- package/src/v2/hooks/use-component.tsx +0 -91
- package/src/v2/hooks/use-configure-suggestions.tsx +0 -236
- package/src/v2/hooks/use-default-render-tool.tsx +0 -271
- package/src/v2/hooks/use-frontend-tool.tsx +0 -46
- package/src/v2/hooks/use-human-in-the-loop.tsx +0 -81
- package/src/v2/hooks/use-interrupt.tsx +0 -305
- package/src/v2/hooks/use-keyboard-height.tsx +0 -67
- package/src/v2/hooks/use-pin-to-send.ts +0 -94
- package/src/v2/hooks/use-render-activity-message.tsx +0 -72
- package/src/v2/hooks/use-render-custom-messages.tsx +0 -93
- package/src/v2/hooks/use-render-tool-call.tsx +0 -208
- package/src/v2/hooks/use-render-tool.tsx +0 -184
- package/src/v2/hooks/use-suggestions.tsx +0 -91
- package/src/v2/hooks/use-threads.tsx +0 -325
- package/src/v2/hooks/useKatexStyles.ts +0 -27
- package/src/v2/index.css +0 -1
- package/src/v2/index.ts +0 -27
- package/src/v2/lib/__tests__/completePartialMarkdown.test.ts +0 -495
- package/src/v2/lib/__tests__/processPartialHtml.test.ts +0 -112
- package/src/v2/lib/__tests__/renderSlot.test.tsx +0 -588
- package/src/v2/lib/__tests__/slots.test.ts +0 -56
- package/src/v2/lib/processPartialHtml.ts +0 -45
- package/src/v2/lib/react-core.ts +0 -156
- package/src/v2/lib/slots.tsx +0 -184
- package/src/v2/lib/transcription-client.ts +0 -184
- package/src/v2/lib/utils.ts +0 -8
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +0 -196
- package/src/v2/providers/CopilotKitProvider.tsx +0 -800
- package/src/v2/providers/SandboxFunctionsContext.ts +0 -10
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.tsx +0 -652
- package/src/v2/providers/__tests__/CopilotKitProvider.license.test.tsx +0 -101
- package/src/v2/providers/__tests__/CopilotKitProvider.onError.test.tsx +0 -69
- package/src/v2/providers/__tests__/CopilotKitProvider.renderCustomMessages.e2e.test.tsx +0 -881
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +0 -198
- package/src/v2/providers/__tests__/CopilotKitProvider.stability.test.tsx +0 -740
- package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +0 -713
- package/src/v2/providers/__tests__/CopilotKitProvider.wildcard.test.tsx +0 -294
- package/src/v2/providers/index.ts +0 -21
- package/src/v2/styles/globals.css +0 -349
- package/src/v2/types/__tests__/defineToolCallRenderer.test.tsx +0 -525
- package/src/v2/types/defineToolCallRenderer.ts +0 -68
- package/src/v2/types/frontend-tool.ts +0 -8
- package/src/v2/types/human-in-the-loop.ts +0 -33
- package/src/v2/types/index.ts +0 -8
- package/src/v2/types/interrupt.ts +0 -15
- package/src/v2/types/react-activity-message-renderer.ts +0 -27
- package/src/v2/types/react-custom-message-renderer.ts +0 -17
- package/src/v2/types/react-tool-call-renderer.ts +0 -35
- package/src/v2/types/sandbox-function.ts +0 -11
- package/tsconfig.json +0 -8
- package/tsdown.config.ts +0 -193
- package/typedoc.json +0 -4
- package/vitest.config.mjs +0 -31
|
@@ -0,0 +1,358 @@
|
|
|
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 — CopilotKitProvider 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`
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# CopilotKit Custom Message Renderers (React)
|
|
2
|
+
|
|
3
|
+
This skill builds on `copilotkit/provider-setup` and
|
|
4
|
+
`copilotkit/chat-components`. `useRenderCustomMessages` is consumed
|
|
5
|
+
internally by `<CopilotChat>` / `<CopilotChatView>`.
|
|
6
|
+
|
|
7
|
+
Key rules:
|
|
8
|
+
|
|
9
|
+
- Renderers are passed to `CopilotKitProvider` via `renderCustomMessages`.
|
|
10
|
+
- The hook returns `null` when called outside `CopilotChatConfigurationProvider`.
|
|
11
|
+
- First non-null result wins — agent-scoped renderers evaluated first.
|
|
12
|
+
- `stateSnapshot` is `undefined` before the run's `runId` resolves.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
"use client";
|
|
18
|
+
import { CopilotKitProvider } from "@copilotkit/react-core/v2";
|
|
19
|
+
import type { ReactCustomMessageRenderer } from "@copilotkit/react-core/v2";
|
|
20
|
+
import { useMemo } from "react";
|
|
21
|
+
import { Button } from "@/components/ui/button";
|
|
22
|
+
|
|
23
|
+
const CopyButton: ReactCustomMessageRenderer = {
|
|
24
|
+
render: ({ message, position }) => {
|
|
25
|
+
if (position !== "after") return null;
|
|
26
|
+
if (message.role !== "assistant") return null;
|
|
27
|
+
const content = typeof message.content === "string" ? message.content : "";
|
|
28
|
+
if (!content) return null;
|
|
29
|
+
return (
|
|
30
|
+
<Button
|
|
31
|
+
variant="ghost"
|
|
32
|
+
size="sm"
|
|
33
|
+
onClick={() => navigator.clipboard.writeText(content)}
|
|
34
|
+
>
|
|
35
|
+
Copy
|
|
36
|
+
</Button>
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
42
|
+
const renderers = useMemo(() => [CopyButton], []);
|
|
43
|
+
return (
|
|
44
|
+
<CopilotKitProvider
|
|
45
|
+
runtimeUrl="/api/copilotkit"
|
|
46
|
+
renderCustomMessages={renderers}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
</CopilotKitProvider>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Core Patterns
|
|
55
|
+
|
|
56
|
+
### State-snapshot viewer after completed runs
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
const StateSnapshotRenderer: ReactCustomMessageRenderer = {
|
|
60
|
+
render: ({ message, position, stateSnapshot }) => {
|
|
61
|
+
if (position !== "after") return null;
|
|
62
|
+
if (message.role !== "assistant") return null;
|
|
63
|
+
if (!stateSnapshot) return null; // run not yet resolved
|
|
64
|
+
return (
|
|
65
|
+
<details>
|
|
66
|
+
<summary>Agent state</summary>
|
|
67
|
+
<pre>{JSON.stringify(stateSnapshot, null, 2)}</pre>
|
|
68
|
+
</details>
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Agent-scoped renderer
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
const ResearchNotes: ReactCustomMessageRenderer = {
|
|
78
|
+
agentId: "research",
|
|
79
|
+
render: ({ message, position, stateSnapshot }) => {
|
|
80
|
+
if (position !== "after" || !stateSnapshot) return null;
|
|
81
|
+
const notes = (stateSnapshot as { notes?: string[] }).notes ?? [];
|
|
82
|
+
return (
|
|
83
|
+
<ul>
|
|
84
|
+
{notes.map((n, i) => (
|
|
85
|
+
<li key={i}>{n}</li>
|
|
86
|
+
))}
|
|
87
|
+
</ul>
|
|
88
|
+
);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Debug panel before user messages
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
const DebugBefore: ReactCustomMessageRenderer = {
|
|
97
|
+
render: ({ message, position, messageIndex, runId }) => {
|
|
98
|
+
if (position !== "before" || message.role !== "user") return null;
|
|
99
|
+
// `runId` is always a string, but it falls back to a synthetic
|
|
100
|
+
// "missing-run-id:<messageId>" value before a run is registered.
|
|
101
|
+
// Slice only when it looks like a real id, otherwise show a dash.
|
|
102
|
+
const shortId = runId?.startsWith("missing-run-id:")
|
|
103
|
+
? "—"
|
|
104
|
+
: (runId?.slice(0, 6) ?? "—");
|
|
105
|
+
return (
|
|
106
|
+
<div style={{ opacity: 0.5, fontSize: 11 }}>
|
|
107
|
+
#{messageIndex} · run {shortId}
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Common Mistakes
|
|
115
|
+
|
|
116
|
+
### HIGH — Using the hook outside a chat configuration provider
|
|
117
|
+
|
|
118
|
+
Wrong:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
// Component mounted outside <CopilotChat>/<CopilotChatView>
|
|
122
|
+
function StandaloneRenderer() {
|
|
123
|
+
const render = useRenderCustomMessages(); // returns null — no chat config in tree
|
|
124
|
+
return render ? render({ message, position: "after" }) : null;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Correct:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
// Option A — register renderers via the provider prop so <CopilotChat> picks them up:
|
|
132
|
+
<CopilotKitProvider renderCustomMessages={renderers}>
|
|
133
|
+
<CopilotChat agentId="default" />
|
|
134
|
+
</CopilotKitProvider>;
|
|
135
|
+
|
|
136
|
+
// Option B — call the hook only inside a chat-configured subtree:
|
|
137
|
+
import { CopilotChatConfigurationProvider } from "@copilotkit/react-core/v2";
|
|
138
|
+
<CopilotChatConfigurationProvider agentId="default">
|
|
139
|
+
<ComponentThatCallsUseRenderCustomMessages />
|
|
140
|
+
</CopilotChatConfigurationProvider>;
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`useRenderCustomMessages` returns `null` when there is no
|
|
144
|
+
`CopilotChatConfigurationProvider` in the tree. `<CopilotChat>` wraps its
|
|
145
|
+
children in one automatically; direct use outside a chat component
|
|
146
|
+
requires the explicit wrapper.
|
|
147
|
+
|
|
148
|
+
Source: `packages/react-core/src/v2/hooks/use-render-custom-messages.tsx:15-17`
|
|
149
|
+
|
|
150
|
+
### MEDIUM — Relying on `stateSnapshot` during early streaming
|
|
151
|
+
|
|
152
|
+
Wrong:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
render: ({ stateSnapshot }) => <pre>{JSON.stringify(stateSnapshot.items)}</pre>;
|
|
156
|
+
// Crashes during the first token — stateSnapshot is undefined before runId resolves.
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Correct:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
render: ({ stateSnapshot }) => (
|
|
163
|
+
<pre>{stateSnapshot ? JSON.stringify(stateSnapshot.items) : "…"}</pre>
|
|
164
|
+
);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`stateSnapshot` comes from `copilotkit.getStateByRun(agentId, threadId,
|
|
168
|
+
runId)`. `runId` is `undefined` until the run is registered, so the
|
|
169
|
+
snapshot starts `undefined` and only becomes truthy after the first
|
|
170
|
+
state emit. Guard with a fallback.
|
|
171
|
+
|
|
172
|
+
Source: `packages/react-core/src/v2/hooks/use-render-custom-messages.tsx:69-71`
|
|
173
|
+
|
|
174
|
+
### MEDIUM — Expecting every renderer in the array to run
|
|
175
|
+
|
|
176
|
+
Wrong:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// Both renderers want to add an "after assistant" button and return <div>…</div>
|
|
180
|
+
// Only the first one (or the agent-scoped one) fires — the second is skipped.
|
|
181
|
+
const renderers = [Renderer1, Renderer2];
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Correct:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// Merge the two into a single renderer that returns one element:
|
|
188
|
+
const Combined: ReactCustomMessageRenderer = {
|
|
189
|
+
render: (props) => (
|
|
190
|
+
<div className="flex gap-1">
|
|
191
|
+
<Renderer1Inner {...props} />
|
|
192
|
+
<Renderer2Inner {...props} />
|
|
193
|
+
</div>
|
|
194
|
+
),
|
|
195
|
+
};
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The hook iterates the sorted renderer list and breaks at the first non-null
|
|
199
|
+
result. Two independent renderers returning JSX for the same
|
|
200
|
+
`(message, position)` pair will have only one fire. Compose them into a
|
|
201
|
+
single renderer if you want both to appear.
|
|
202
|
+
|
|
203
|
+
Source: `packages/react-core/src/v2/hooks/use-render-custom-messages.tsx:73-95`
|
|
204
|
+
|
|
205
|
+
### MEDIUM — Memoization miss on `renderCustomMessages` array
|
|
206
|
+
|
|
207
|
+
Wrong:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
<CopilotKitProvider
|
|
211
|
+
renderCustomMessages={[CopyButton, DebugBefore]} // fresh array every render
|
|
212
|
+
/>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Correct:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
const renderers = useMemo(() => [CopyButton, DebugBefore], []);
|
|
219
|
+
<CopilotKitProvider renderCustomMessages={renderers} />;
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The provider's stable-array-prop diff console-errors when a new array
|
|
223
|
+
identity appears every render and thrashes renderer registration.
|
|
224
|
+
Memoize or hoist.
|
|
225
|
+
|
|
226
|
+
Source: `packages/react-core/src/v2/providers/CopilotKitProvider.tsx` (useStableArrayProp)
|