@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
|
@@ -1,664 +0,0 @@
|
|
|
1
|
-
import { useAgent } from "../../hooks/use-agent";
|
|
2
|
-
import { useAttachments } from "../../hooks/use-attachments";
|
|
3
|
-
import { useSuggestions } from "../../hooks/use-suggestions";
|
|
4
|
-
import { CopilotChatView, CopilotChatViewProps } from "./CopilotChatView";
|
|
5
|
-
import { CopilotChatInputMode } from "./CopilotChatInput";
|
|
6
|
-
import {
|
|
7
|
-
CopilotChatConfigurationProvider,
|
|
8
|
-
CopilotChatLabels,
|
|
9
|
-
useCopilotChatConfiguration,
|
|
10
|
-
} from "../../providers/CopilotChatConfigurationProvider";
|
|
11
|
-
import {
|
|
12
|
-
DEFAULT_AGENT_ID,
|
|
13
|
-
randomUUID,
|
|
14
|
-
TranscriptionErrorCode,
|
|
15
|
-
} from "@copilotkit/shared";
|
|
16
|
-
import type { AttachmentsConfig, InputContent } from "@copilotkit/shared";
|
|
17
|
-
import { Suggestion, CopilotKitCoreErrorCode } from "@copilotkit/core";
|
|
18
|
-
import React, {
|
|
19
|
-
useCallback,
|
|
20
|
-
useEffect,
|
|
21
|
-
useMemo,
|
|
22
|
-
useRef,
|
|
23
|
-
useState,
|
|
24
|
-
} from "react";
|
|
25
|
-
import {
|
|
26
|
-
useCopilotKit,
|
|
27
|
-
useLicenseContext,
|
|
28
|
-
} from "../../providers/CopilotKitProvider";
|
|
29
|
-
import { InlineFeatureWarning } from "../../components/license-warning-banner";
|
|
30
|
-
import { AbstractAgent, HttpAgent } from "@ag-ui/client";
|
|
31
|
-
import { renderSlot, useShallowStableRef, SlotValue } from "../../lib/slots";
|
|
32
|
-
import {
|
|
33
|
-
transcribeAudio,
|
|
34
|
-
TranscriptionError,
|
|
35
|
-
} from "../../lib/transcription-client";
|
|
36
|
-
import {
|
|
37
|
-
LastUserMessageContext,
|
|
38
|
-
type LastUserMessageState,
|
|
39
|
-
} from "./last-user-message-context";
|
|
40
|
-
|
|
41
|
-
export type CopilotChatProps = Omit<
|
|
42
|
-
CopilotChatViewProps,
|
|
43
|
-
| "messages"
|
|
44
|
-
| "isRunning"
|
|
45
|
-
| "suggestions"
|
|
46
|
-
| "suggestionLoadingIndexes"
|
|
47
|
-
| "onSelectSuggestion"
|
|
48
|
-
// Attachment state props — managed internally based on `attachments` config
|
|
49
|
-
| "attachments"
|
|
50
|
-
| "onRemoveAttachment"
|
|
51
|
-
| "onAddFile"
|
|
52
|
-
| "dragOver"
|
|
53
|
-
| "onDragOver"
|
|
54
|
-
| "onDragLeave"
|
|
55
|
-
| "onDrop"
|
|
56
|
-
> & {
|
|
57
|
-
agentId?: string;
|
|
58
|
-
threadId?: string;
|
|
59
|
-
labels?: Partial<CopilotChatLabels>;
|
|
60
|
-
chatView?: SlotValue<typeof CopilotChatView>;
|
|
61
|
-
isModalDefaultOpen?: boolean;
|
|
62
|
-
/** Enable multimodal file attachments (images, audio, video, documents). */
|
|
63
|
-
attachments?: AttachmentsConfig;
|
|
64
|
-
/**
|
|
65
|
-
* Error handler scoped to this chat's agent. Fires in addition to the
|
|
66
|
-
* provider-level onError (does not suppress it). Receives only errors
|
|
67
|
-
* whose context.agentId matches this chat's agent.
|
|
68
|
-
*/
|
|
69
|
-
onError?: (event: {
|
|
70
|
-
error: Error;
|
|
71
|
-
code: CopilotKitCoreErrorCode;
|
|
72
|
-
context: Record<string, any>;
|
|
73
|
-
}) => void | Promise<void>;
|
|
74
|
-
/**
|
|
75
|
-
* Throttle interval (in milliseconds) for re-renders triggered by message
|
|
76
|
-
* change notifications. Overrides the provider-level `defaultThrottleMs`
|
|
77
|
-
* for this chat instance. Forwarded to the internal `useAgent()` hook,
|
|
78
|
-
* which resolves the effective throttle value.
|
|
79
|
-
*
|
|
80
|
-
* @default undefined — inherits from provider `defaultThrottleMs`;
|
|
81
|
-
* if that is also unset, re-renders are unthrottled. Note: passing
|
|
82
|
-
* `throttleMs={0}` explicitly disables throttling for this instance
|
|
83
|
-
* even when the provider specifies a non-zero `defaultThrottleMs`.
|
|
84
|
-
*/
|
|
85
|
-
throttleMs?: number;
|
|
86
|
-
};
|
|
87
|
-
export function CopilotChat({
|
|
88
|
-
agentId,
|
|
89
|
-
threadId,
|
|
90
|
-
labels,
|
|
91
|
-
chatView,
|
|
92
|
-
isModalDefaultOpen,
|
|
93
|
-
attachments: attachmentsConfig,
|
|
94
|
-
onError,
|
|
95
|
-
throttleMs,
|
|
96
|
-
...props
|
|
97
|
-
}: CopilotChatProps) {
|
|
98
|
-
// Check for existing configuration provider
|
|
99
|
-
const existingConfig = useCopilotChatConfiguration();
|
|
100
|
-
|
|
101
|
-
// Apply priority: props > existing config > defaults
|
|
102
|
-
const resolvedAgentId =
|
|
103
|
-
agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
104
|
-
const providedThreadId = threadId ?? existingConfig?.threadId;
|
|
105
|
-
const resolvedThreadId = useMemo(
|
|
106
|
-
() => providedThreadId ?? randomUUID(),
|
|
107
|
-
[providedThreadId],
|
|
108
|
-
);
|
|
109
|
-
// "Explicit" means a caller actually picked this thread — via the
|
|
110
|
-
// `threadId` prop on CopilotChat or a wrapping provider that marked its
|
|
111
|
-
// threadId as caller-chosen. An auto-minted UUID leaking down through a
|
|
112
|
-
// CopilotChatConfigurationProvider (e.g. from the v1 CopilotKit →
|
|
113
|
-
// ThreadsProvider chain) does NOT count; treating it as explicit is
|
|
114
|
-
// what made /connect fire against 404s and the welcome screen stay
|
|
115
|
-
// hidden for fresh empty chats.
|
|
116
|
-
const hasExplicitThreadId =
|
|
117
|
-
!!threadId || !!existingConfig?.hasExplicitThreadId;
|
|
118
|
-
|
|
119
|
-
const { agent } = useAgent({
|
|
120
|
-
agentId: resolvedAgentId,
|
|
121
|
-
throttleMs,
|
|
122
|
-
});
|
|
123
|
-
const { copilotkit } = useCopilotKit();
|
|
124
|
-
const { suggestions: autoSuggestions } = useSuggestions({
|
|
125
|
-
agentId: resolvedAgentId,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const { checkFeature } = useLicenseContext();
|
|
129
|
-
const isChatLicensed = checkFeature("chat");
|
|
130
|
-
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
if (!isChatLicensed) {
|
|
133
|
-
console.warn(
|
|
134
|
-
'[CopilotKit] Warning: "chat" feature is not licensed. Visit copilotkit.ai/pricing',
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}, [isChatLicensed]);
|
|
138
|
-
|
|
139
|
-
// onError subscription — forward core errors scoped to this chat's agent
|
|
140
|
-
const onErrorRef = useRef(onError);
|
|
141
|
-
useEffect(() => {
|
|
142
|
-
onErrorRef.current = onError;
|
|
143
|
-
}, [onError]);
|
|
144
|
-
|
|
145
|
-
useEffect(() => {
|
|
146
|
-
if (!onErrorRef.current) return;
|
|
147
|
-
|
|
148
|
-
const subscription = copilotkit.subscribe({
|
|
149
|
-
onError: (event) => {
|
|
150
|
-
// Only forward errors that match this chat's agent
|
|
151
|
-
if (
|
|
152
|
-
event.context?.agentId === resolvedAgentId ||
|
|
153
|
-
!event.context?.agentId
|
|
154
|
-
) {
|
|
155
|
-
onErrorRef.current?.({
|
|
156
|
-
error: event.error,
|
|
157
|
-
code: event.code,
|
|
158
|
-
context: event.context,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
return () => {
|
|
165
|
-
subscription.unsubscribe();
|
|
166
|
-
};
|
|
167
|
-
}, [copilotkit, resolvedAgentId]);
|
|
168
|
-
|
|
169
|
-
// Transcription state
|
|
170
|
-
const [transcribeMode, setTranscribeMode] =
|
|
171
|
-
useState<CopilotChatInputMode>("input");
|
|
172
|
-
const [inputValue, setInputValue] = useState("");
|
|
173
|
-
const [transcriptionError, setTranscriptionError] = useState<string | null>(
|
|
174
|
-
null,
|
|
175
|
-
);
|
|
176
|
-
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
177
|
-
|
|
178
|
-
// Attachments
|
|
179
|
-
const {
|
|
180
|
-
attachments: selectedAttachments,
|
|
181
|
-
enabled: attachmentsEnabled,
|
|
182
|
-
dragOver,
|
|
183
|
-
fileInputRef,
|
|
184
|
-
containerRef: chatContainerRef,
|
|
185
|
-
handleFileUpload,
|
|
186
|
-
handleDragOver,
|
|
187
|
-
handleDragLeave,
|
|
188
|
-
handleDrop,
|
|
189
|
-
removeAttachment,
|
|
190
|
-
consumeAttachments,
|
|
191
|
-
} = useAttachments({ config: attachmentsConfig });
|
|
192
|
-
|
|
193
|
-
// Check if transcription is enabled
|
|
194
|
-
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
|
|
195
|
-
|
|
196
|
-
// Check if browser supports MediaRecorder
|
|
197
|
-
const isMediaRecorderSupported =
|
|
198
|
-
typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
|
|
199
|
-
|
|
200
|
-
const {
|
|
201
|
-
messageView: providedMessageView,
|
|
202
|
-
suggestionView: providedSuggestionView,
|
|
203
|
-
onStop: providedStopHandler,
|
|
204
|
-
...restProps
|
|
205
|
-
} = props;
|
|
206
|
-
|
|
207
|
-
// Tracks the last threadId for which connectAgent has completed (success or
|
|
208
|
-
// failure). When the user supplies a threadId, we're in "resume existing
|
|
209
|
-
// thread" mode — the welcome screen should be suppressed until the connect
|
|
210
|
-
// resolves, otherwise switching threads flashes the welcome screen while the
|
|
211
|
-
// new thread's messages are still en route.
|
|
212
|
-
const [lastConnectedThreadId, setLastConnectedThreadId] = useState<
|
|
213
|
-
string | null
|
|
214
|
-
>(null);
|
|
215
|
-
const isConnecting =
|
|
216
|
-
hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
|
|
217
|
-
|
|
218
|
-
useEffect(() => {
|
|
219
|
-
// When the caller hasn't picked a specific thread, resolvedThreadId is a
|
|
220
|
-
// UUID minted locally (either in this CopilotChat or in a wrapping
|
|
221
|
-
// ThreadsProvider). The backend has never seen it, so /connect would
|
|
222
|
-
// always 404 — skip the call. A real thread is only created once the
|
|
223
|
-
// user runs the agent for the first time.
|
|
224
|
-
if (!hasExplicitThreadId) return;
|
|
225
|
-
|
|
226
|
-
let detached = false;
|
|
227
|
-
|
|
228
|
-
// Create a fresh AbortController so we can cancel the HTTP request on cleanup.
|
|
229
|
-
// HttpAgent (parent of ProxiedCopilotRuntimeAgent) uses this.abortController.signal
|
|
230
|
-
// in its fetch config. Unlike runAgent(), connectAgent() does NOT create a new
|
|
231
|
-
// AbortController automatically, so we must set one before connecting.
|
|
232
|
-
const connectAbortController = new AbortController();
|
|
233
|
-
if (agent instanceof HttpAgent) {
|
|
234
|
-
agent.abortController = connectAbortController;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
agent.threadId = resolvedThreadId;
|
|
238
|
-
|
|
239
|
-
const connect = async (agent: AbstractAgent) => {
|
|
240
|
-
try {
|
|
241
|
-
await copilotkit.connectAgent({ agent });
|
|
242
|
-
} catch (error) {
|
|
243
|
-
// Ignore errors from aborted connections (e.g., React StrictMode cleanup)
|
|
244
|
-
if (detached) return;
|
|
245
|
-
// connectAgent already emits via the subscriber system, but catch
|
|
246
|
-
// here to prevent unhandled rejections from unexpected errors.
|
|
247
|
-
console.error("CopilotChat: connectAgent failed", error);
|
|
248
|
-
} finally {
|
|
249
|
-
// Whether the connect succeeded or failed, we're no longer in the
|
|
250
|
-
// transitional "connecting" state for this thread — unblock the
|
|
251
|
-
// welcome-screen-suppression so the view can settle.
|
|
252
|
-
//
|
|
253
|
-
// Defer one animation frame so any trailing React commits from the
|
|
254
|
-
// bootstrap replay (final assistant message content) paint before
|
|
255
|
-
// isConnecting flips off. Without this, suggestions + copy button
|
|
256
|
-
// can briefly appear against an incompletely-laid-out message tree
|
|
257
|
-
// and visibly snap once the last text chunk lands.
|
|
258
|
-
if (!detached) {
|
|
259
|
-
const raf =
|
|
260
|
-
typeof requestAnimationFrame === "function"
|
|
261
|
-
? requestAnimationFrame
|
|
262
|
-
: (cb: () => void) => setTimeout(cb, 16);
|
|
263
|
-
raf(() => {
|
|
264
|
-
if (!detached) setLastConnectedThreadId(resolvedThreadId);
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
connect(agent);
|
|
270
|
-
return () => {
|
|
271
|
-
// Abort the HTTP request and detach the active run.
|
|
272
|
-
// This is critical for React StrictMode which unmounts+remounts in dev,
|
|
273
|
-
// preventing duplicate /connect requests from reaching the server.
|
|
274
|
-
detached = true;
|
|
275
|
-
connectAbortController.abort();
|
|
276
|
-
// The .catch() is required to prevent a false-positive "Uncaught (in promise)
|
|
277
|
-
// AbortError" in browser devtools. detachActiveRun() itself does not reject,
|
|
278
|
-
// but without an attached handler V8 flags the promise chain as unhandled
|
|
279
|
-
// when the abort signal propagates through connected promises internally.
|
|
280
|
-
void agent.detachActiveRun().catch(() => {});
|
|
281
|
-
};
|
|
282
|
-
// copilotkit is intentionally excluded — it is a stable ref that never changes.
|
|
283
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
284
|
-
}, [resolvedThreadId, agent, resolvedAgentId, hasExplicitThreadId]);
|
|
285
|
-
|
|
286
|
-
const onSubmitInput = useCallback(
|
|
287
|
-
async (value: string) => {
|
|
288
|
-
// Block if uploads in progress
|
|
289
|
-
const hasUploading = selectedAttachments.some(
|
|
290
|
-
(a) => a.status === "uploading",
|
|
291
|
-
);
|
|
292
|
-
if (hasUploading) {
|
|
293
|
-
console.error(
|
|
294
|
-
"[CopilotKit] Cannot send while attachments are uploading",
|
|
295
|
-
);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const readyAttachments = consumeAttachments();
|
|
300
|
-
|
|
301
|
-
if (readyAttachments.length > 0) {
|
|
302
|
-
const contentParts: InputContent[] = [];
|
|
303
|
-
if (value.trim()) {
|
|
304
|
-
contentParts.push({ type: "text", text: value });
|
|
305
|
-
}
|
|
306
|
-
for (const att of readyAttachments) {
|
|
307
|
-
contentParts.push({
|
|
308
|
-
type: att.type,
|
|
309
|
-
source: att.source,
|
|
310
|
-
metadata: {
|
|
311
|
-
...(att.filename ? { filename: att.filename } : {}),
|
|
312
|
-
...att.metadata,
|
|
313
|
-
},
|
|
314
|
-
} as InputContent);
|
|
315
|
-
}
|
|
316
|
-
agent.addMessage({
|
|
317
|
-
id: randomUUID(),
|
|
318
|
-
role: "user",
|
|
319
|
-
content: contentParts,
|
|
320
|
-
});
|
|
321
|
-
} else {
|
|
322
|
-
agent.addMessage({
|
|
323
|
-
id: randomUUID(),
|
|
324
|
-
role: "user",
|
|
325
|
-
content: value,
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Clear input after submitting
|
|
330
|
-
setInputValue("");
|
|
331
|
-
try {
|
|
332
|
-
await copilotkit.runAgent({ agent });
|
|
333
|
-
} catch (error) {
|
|
334
|
-
console.error("CopilotChat: runAgent failed", error);
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
// copilotkit is intentionally excluded — it is a stable ref that never changes.
|
|
338
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
339
|
-
[agent, selectedAttachments, consumeAttachments],
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
const handleSelectSuggestion = useCallback(
|
|
343
|
-
async (suggestion: Suggestion) => {
|
|
344
|
-
agent.addMessage({
|
|
345
|
-
id: randomUUID(),
|
|
346
|
-
role: "user",
|
|
347
|
-
content: suggestion.message,
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
try {
|
|
351
|
-
await copilotkit.runAgent({ agent });
|
|
352
|
-
} catch (error) {
|
|
353
|
-
console.error(
|
|
354
|
-
"CopilotChat: runAgent failed after selecting suggestion",
|
|
355
|
-
error,
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
},
|
|
359
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
360
|
-
[agent],
|
|
361
|
-
);
|
|
362
|
-
|
|
363
|
-
const stopCurrentRun = useCallback(() => {
|
|
364
|
-
try {
|
|
365
|
-
copilotkit.stopAgent({ agent });
|
|
366
|
-
} catch (error) {
|
|
367
|
-
console.error("CopilotChat: stopAgent failed", error);
|
|
368
|
-
try {
|
|
369
|
-
agent.abortRun();
|
|
370
|
-
} catch (abortError) {
|
|
371
|
-
console.error("CopilotChat: abortRun fallback failed", abortError);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
375
|
-
}, [agent]);
|
|
376
|
-
|
|
377
|
-
// Transcription handlers
|
|
378
|
-
const handleStartTranscribe = useCallback(() => {
|
|
379
|
-
setTranscriptionError(null);
|
|
380
|
-
setTranscribeMode("transcribe");
|
|
381
|
-
}, []);
|
|
382
|
-
|
|
383
|
-
const handleCancelTranscribe = useCallback(() => {
|
|
384
|
-
setTranscriptionError(null);
|
|
385
|
-
setTranscribeMode("input");
|
|
386
|
-
}, []);
|
|
387
|
-
|
|
388
|
-
const handleFinishTranscribe = useCallback(() => {
|
|
389
|
-
setTranscribeMode("input");
|
|
390
|
-
}, []);
|
|
391
|
-
|
|
392
|
-
// Handle audio blob from CopilotChatInput and transcribe it
|
|
393
|
-
const handleFinishTranscribeWithAudio = useCallback(
|
|
394
|
-
async (audioBlob: Blob) => {
|
|
395
|
-
setIsTranscribing(true);
|
|
396
|
-
try {
|
|
397
|
-
setTranscriptionError(null);
|
|
398
|
-
|
|
399
|
-
// Send to transcription endpoint
|
|
400
|
-
const result = await transcribeAudio(copilotkit, audioBlob);
|
|
401
|
-
|
|
402
|
-
// Insert transcribed text into input
|
|
403
|
-
setInputValue((prev) => {
|
|
404
|
-
const trimmedPrev = prev.trim();
|
|
405
|
-
if (trimmedPrev) {
|
|
406
|
-
return `${trimmedPrev} ${result.text}`;
|
|
407
|
-
}
|
|
408
|
-
return result.text;
|
|
409
|
-
});
|
|
410
|
-
} catch (error) {
|
|
411
|
-
console.error("CopilotChat: Transcription failed", error);
|
|
412
|
-
|
|
413
|
-
// Show contextual error message based on error type
|
|
414
|
-
if (error instanceof TranscriptionError) {
|
|
415
|
-
const { code, retryable, message } = error.info;
|
|
416
|
-
switch (code) {
|
|
417
|
-
case TranscriptionErrorCode.RATE_LIMITED:
|
|
418
|
-
setTranscriptionError("Too many requests. Please wait a moment.");
|
|
419
|
-
break;
|
|
420
|
-
case TranscriptionErrorCode.AUTH_FAILED:
|
|
421
|
-
setTranscriptionError(
|
|
422
|
-
"Authentication error. Please check your configuration.",
|
|
423
|
-
);
|
|
424
|
-
break;
|
|
425
|
-
case TranscriptionErrorCode.AUDIO_TOO_LONG:
|
|
426
|
-
setTranscriptionError(
|
|
427
|
-
"Recording is too long. Please try a shorter recording.",
|
|
428
|
-
);
|
|
429
|
-
break;
|
|
430
|
-
case TranscriptionErrorCode.AUDIO_TOO_SHORT:
|
|
431
|
-
setTranscriptionError(
|
|
432
|
-
"Recording is too short. Please try again.",
|
|
433
|
-
);
|
|
434
|
-
break;
|
|
435
|
-
case TranscriptionErrorCode.INVALID_AUDIO_FORMAT:
|
|
436
|
-
setTranscriptionError("Audio format not supported.");
|
|
437
|
-
break;
|
|
438
|
-
case TranscriptionErrorCode.SERVICE_NOT_CONFIGURED:
|
|
439
|
-
setTranscriptionError("Transcription service is not available.");
|
|
440
|
-
break;
|
|
441
|
-
case TranscriptionErrorCode.NETWORK_ERROR:
|
|
442
|
-
setTranscriptionError(
|
|
443
|
-
"Network error. Please check your connection.",
|
|
444
|
-
);
|
|
445
|
-
break;
|
|
446
|
-
default:
|
|
447
|
-
// For retryable errors, show more helpful message
|
|
448
|
-
setTranscriptionError(
|
|
449
|
-
retryable ? "Transcription failed. Please try again." : message,
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
} else {
|
|
453
|
-
// Fallback for unexpected errors
|
|
454
|
-
setTranscriptionError("Transcription failed. Please try again.");
|
|
455
|
-
}
|
|
456
|
-
} finally {
|
|
457
|
-
setIsTranscribing(false);
|
|
458
|
-
}
|
|
459
|
-
},
|
|
460
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
461
|
-
[],
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
// Clear transcription error after a delay
|
|
465
|
-
useEffect(() => {
|
|
466
|
-
if (transcriptionError) {
|
|
467
|
-
const timer = setTimeout(() => {
|
|
468
|
-
setTranscriptionError(null);
|
|
469
|
-
}, 5000);
|
|
470
|
-
return () => clearTimeout(timer);
|
|
471
|
-
}
|
|
472
|
-
}, [transcriptionError]);
|
|
473
|
-
|
|
474
|
-
// Stabilize slot object references so inline props (new object reference on
|
|
475
|
-
// every parent render) don't defeat MemoizedSlotWrapper's shallow equality
|
|
476
|
-
// check and cause unnecessary re-renders of the message list on each keystroke.
|
|
477
|
-
const stableMessageView = useShallowStableRef(
|
|
478
|
-
typeof providedMessageView === "string"
|
|
479
|
-
? { className: providedMessageView }
|
|
480
|
-
: providedMessageView,
|
|
481
|
-
);
|
|
482
|
-
const stableSuggestionView = useShallowStableRef(providedSuggestionView);
|
|
483
|
-
|
|
484
|
-
// Stabilize the `onAddFile` handler. Without useCallback, a new arrow
|
|
485
|
-
// function is created inline on every render, causing CopilotChatView to
|
|
486
|
-
// re-render on every keystroke even when nothing else changed.
|
|
487
|
-
const handleAddFile = useCallback(() => {
|
|
488
|
-
// Delay to let Radix dropdown menu close before triggering file input
|
|
489
|
-
setTimeout(() => {
|
|
490
|
-
fileInputRef.current?.click();
|
|
491
|
-
}, 100);
|
|
492
|
-
}, []);
|
|
493
|
-
|
|
494
|
-
// Use shallow spread instead of ts-deepmerge. ts-deepmerge deep-clones plain
|
|
495
|
-
// objects even from a single source, which would defeat the reference
|
|
496
|
-
// stability we just established for stableMessageView and other slot values.
|
|
497
|
-
const mergedProps: Partial<CopilotChatViewProps> = {
|
|
498
|
-
isRunning: agent.isRunning,
|
|
499
|
-
suggestions: autoSuggestions,
|
|
500
|
-
onSelectSuggestion: handleSelectSuggestion,
|
|
501
|
-
suggestionView: stableSuggestionView,
|
|
502
|
-
...restProps,
|
|
503
|
-
};
|
|
504
|
-
if (stableMessageView !== undefined)
|
|
505
|
-
mergedProps.messageView = stableMessageView;
|
|
506
|
-
|
|
507
|
-
const hasMessages = agent.messages.length > 0;
|
|
508
|
-
const shouldAllowStop = agent.isRunning && hasMessages;
|
|
509
|
-
const effectiveStopHandler = shouldAllowStop
|
|
510
|
-
? (providedStopHandler ?? stopCurrentRun)
|
|
511
|
-
: providedStopHandler;
|
|
512
|
-
|
|
513
|
-
// Determine if transcription feature should be available
|
|
514
|
-
const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
|
|
515
|
-
|
|
516
|
-
// Determine mode: transcribing takes priority, then transcribe mode, then default to input
|
|
517
|
-
const effectiveMode: CopilotChatInputMode = isTranscribing
|
|
518
|
-
? "processing"
|
|
519
|
-
: transcribeMode;
|
|
520
|
-
|
|
521
|
-
// Memoize messages array — only create a new reference when content changes.
|
|
522
|
-
// We build a lightweight fingerprint instead of JSON.stringify to avoid
|
|
523
|
-
// serializing large base64 attachment data on every render. The key captures:
|
|
524
|
-
// - message id, role, content length (text streaming)
|
|
525
|
-
// - content part count (multimodal additions)
|
|
526
|
-
// - tool call ids + argument lengths (tool call streaming)
|
|
527
|
-
const messagesMemoKey = agent.messages
|
|
528
|
-
.map((m) => {
|
|
529
|
-
const contentKey =
|
|
530
|
-
typeof m.content === "string"
|
|
531
|
-
? m.content.length
|
|
532
|
-
: Array.isArray(m.content)
|
|
533
|
-
? m.content.length
|
|
534
|
-
: 0;
|
|
535
|
-
const toolCallsKey =
|
|
536
|
-
"toolCalls" in m && Array.isArray(m.toolCalls)
|
|
537
|
-
? m.toolCalls
|
|
538
|
-
.map(
|
|
539
|
-
(tc: any) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`,
|
|
540
|
-
)
|
|
541
|
-
.join(";")
|
|
542
|
-
: "";
|
|
543
|
-
return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
|
|
544
|
-
})
|
|
545
|
-
.join(",");
|
|
546
|
-
const messages = useMemo(
|
|
547
|
-
() => [...agent.messages],
|
|
548
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
549
|
-
[messagesMemoKey],
|
|
550
|
-
);
|
|
551
|
-
|
|
552
|
-
// Compute the ID of the last user message for scroll-pinning logic.
|
|
553
|
-
const lastUserMessageId = useMemo(() => {
|
|
554
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
555
|
-
if (messages[i].role === "user") return messages[i].id;
|
|
556
|
-
}
|
|
557
|
-
return null;
|
|
558
|
-
}, [messages]);
|
|
559
|
-
|
|
560
|
-
// Track a nonce that increments each time a new user message ID appears.
|
|
561
|
-
// Using useState ensures the context value propagates correctly on the
|
|
562
|
-
// render that follows the state update (approach b from the design doc).
|
|
563
|
-
const [sendNonce, setSendNonce] = useState(0);
|
|
564
|
-
// Seed with the current value so restoring a thread with existing messages
|
|
565
|
-
// does not count as a new send. Only later-render id transitions bump.
|
|
566
|
-
const prevLastUserMessageIdRef = useRef<string | null>(lastUserMessageId);
|
|
567
|
-
|
|
568
|
-
useEffect(() => {
|
|
569
|
-
if (
|
|
570
|
-
lastUserMessageId &&
|
|
571
|
-
lastUserMessageId !== prevLastUserMessageIdRef.current
|
|
572
|
-
) {
|
|
573
|
-
setSendNonce((n) => n + 1);
|
|
574
|
-
prevLastUserMessageIdRef.current = lastUserMessageId;
|
|
575
|
-
}
|
|
576
|
-
}, [lastUserMessageId]);
|
|
577
|
-
|
|
578
|
-
const lastUserMessageState = useMemo<LastUserMessageState>(
|
|
579
|
-
() => ({ id: lastUserMessageId, sendNonce }),
|
|
580
|
-
[lastUserMessageId, sendNonce],
|
|
581
|
-
);
|
|
582
|
-
|
|
583
|
-
const finalProps: CopilotChatViewProps = {
|
|
584
|
-
...mergedProps,
|
|
585
|
-
messages,
|
|
586
|
-
// Input behavior props
|
|
587
|
-
onSubmitMessage: onSubmitInput,
|
|
588
|
-
onStop: effectiveStopHandler,
|
|
589
|
-
inputMode: effectiveMode,
|
|
590
|
-
inputValue,
|
|
591
|
-
onInputChange: setInputValue,
|
|
592
|
-
// Only provide transcription handlers if feature is available
|
|
593
|
-
onStartTranscribe: showTranscription ? handleStartTranscribe : undefined,
|
|
594
|
-
onCancelTranscribe: showTranscription ? handleCancelTranscribe : undefined,
|
|
595
|
-
onFinishTranscribe: showTranscription ? handleFinishTranscribe : undefined,
|
|
596
|
-
onFinishTranscribeWithAudio: showTranscription
|
|
597
|
-
? handleFinishTranscribeWithAudio
|
|
598
|
-
: undefined,
|
|
599
|
-
// Attachment props
|
|
600
|
-
attachments: selectedAttachments,
|
|
601
|
-
onRemoveAttachment: removeAttachment,
|
|
602
|
-
onAddFile: attachmentsEnabled ? handleAddFile : undefined,
|
|
603
|
-
dragOver,
|
|
604
|
-
onDragOver: handleDragOver,
|
|
605
|
-
onDragLeave: handleDragLeave,
|
|
606
|
-
onDrop: handleDrop,
|
|
607
|
-
isConnecting,
|
|
608
|
-
hasExplicitThreadId,
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
// Always create a provider with merged values
|
|
612
|
-
// This ensures priority: props > existing config > defaults
|
|
613
|
-
const RenderedChatView = renderSlot(chatView, CopilotChatView, finalProps);
|
|
614
|
-
|
|
615
|
-
return (
|
|
616
|
-
<CopilotChatConfigurationProvider
|
|
617
|
-
agentId={resolvedAgentId}
|
|
618
|
-
threadId={resolvedThreadId}
|
|
619
|
-
hasExplicitThreadId={hasExplicitThreadId}
|
|
620
|
-
labels={labels}
|
|
621
|
-
isModalDefaultOpen={isModalDefaultOpen}
|
|
622
|
-
>
|
|
623
|
-
<div ref={chatContainerRef} style={{ display: "contents" }}>
|
|
624
|
-
{attachmentsEnabled && (
|
|
625
|
-
<input
|
|
626
|
-
type="file"
|
|
627
|
-
multiple
|
|
628
|
-
ref={fileInputRef}
|
|
629
|
-
onChange={handleFileUpload}
|
|
630
|
-
accept={attachmentsConfig?.accept ?? "*/*"}
|
|
631
|
-
style={{ display: "none" }}
|
|
632
|
-
/>
|
|
633
|
-
)}
|
|
634
|
-
{!isChatLicensed && <InlineFeatureWarning featureName="Chat" />}
|
|
635
|
-
{transcriptionError && (
|
|
636
|
-
<div
|
|
637
|
-
style={{
|
|
638
|
-
position: "absolute",
|
|
639
|
-
bottom: "100px",
|
|
640
|
-
left: "50%",
|
|
641
|
-
transform: "translateX(-50%)",
|
|
642
|
-
backgroundColor: "#ef4444",
|
|
643
|
-
color: "white",
|
|
644
|
-
padding: "8px 16px",
|
|
645
|
-
borderRadius: "8px",
|
|
646
|
-
fontSize: "14px",
|
|
647
|
-
zIndex: 50,
|
|
648
|
-
}}
|
|
649
|
-
>
|
|
650
|
-
{transcriptionError}
|
|
651
|
-
</div>
|
|
652
|
-
)}
|
|
653
|
-
<LastUserMessageContext.Provider value={lastUserMessageState}>
|
|
654
|
-
{RenderedChatView}
|
|
655
|
-
</LastUserMessageContext.Provider>
|
|
656
|
-
</div>
|
|
657
|
-
</CopilotChatConfigurationProvider>
|
|
658
|
-
);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
662
|
-
export namespace CopilotChat {
|
|
663
|
-
export const View = CopilotChatView;
|
|
664
|
-
}
|