@copilotkit/react-core 1.55.0-next.8 → 1.55.0
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/CHANGELOG.md +48 -5
- package/dist/{copilotkit-DNYSFuz5.mjs → copilotkit-BY5S1-0P.mjs} +2772 -858
- package/dist/copilotkit-BY5S1-0P.mjs.map +1 -0
- package/dist/{copilotkit-Dy5w3qEV.d.mts → copilotkit-BuhSUZHb.d.mts} +230 -17
- package/dist/copilotkit-BuhSUZHb.d.mts.map +1 -0
- package/dist/{copilotkit-B3Mb1yVE.cjs → copilotkit-Bz5-ImDl.cjs} +2776 -832
- package/dist/copilotkit-Bz5-ImDl.cjs.map +1 -0
- package/dist/{copilotkit-DBzgOMby.d.cts → copilotkit-dwDWYpya.d.cts} +230 -17
- package/dist/copilotkit-dwDWYpya.d.cts.map +1 -0
- package/dist/index.cjs +9 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +9 -4
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1624 -396
- package/dist/index.umd.js.map +1 -1
- package/dist/v2/index.cjs +13 -1
- package/dist/v2/index.css +1 -1
- package/dist/v2/index.d.cts +3 -3
- package/dist/v2/index.d.mts +3 -3
- package/dist/v2/index.mjs +3 -2
- package/dist/v2/index.umd.js +2746 -790
- package/dist/v2/index.umd.js.map +1 -1
- package/package.json +62 -54
- package/scripts/scope-preflight.mjs +1 -2
- package/src/components/CopilotListeners.tsx +41 -8
- package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +92 -0
- package/src/components/copilot-provider/copilotkit-props.tsx +4 -2
- package/src/components/copilot-provider/copilotkit.tsx +3 -3
- package/src/components/toast/toast-provider.tsx +269 -194
- package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +27 -16
- package/src/hooks/use-copilot-chat_internal.ts +15 -4
- package/src/v2/__tests__/A2UIMessageRenderer.test.tsx +86 -22
- package/src/v2/__tests__/utils/test-helpers.tsx +107 -7
- package/src/v2/a2ui/A2UICatalogContext.tsx +79 -0
- package/src/v2/a2ui/A2UIMessageRenderer.tsx +125 -37
- package/src/v2/a2ui/A2UIToolCallRenderer.tsx +290 -0
- package/src/v2/components/CopilotKitInspector.tsx +2 -0
- package/src/v2/components/OpenGenerativeUIRenderer.tsx +598 -0
- package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.tsx +665 -0
- package/src/v2/components/chat/CopilotChat.tsx +197 -52
- package/src/v2/components/chat/CopilotChatAssistantMessage.tsx +17 -2
- package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +481 -0
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.tsx +139 -0
- package/src/v2/components/chat/CopilotChatInput.tsx +146 -77
- package/src/v2/components/chat/CopilotChatMessageView.tsx +260 -151
- package/src/v2/components/chat/CopilotChatSuggestionView.tsx +1 -0
- package/src/v2/components/chat/CopilotChatUserMessage.tsx +54 -0
- package/src/v2/components/chat/CopilotChatView.tsx +179 -66
- package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.tsx +168 -0
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +63 -2
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.tsx +544 -1
- package/src/v2/components/chat/__tests__/CopilotChatPerf.e2e.test.tsx +268 -0
- package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.tsx +249 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +5 -2
- package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +60 -3
- package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +138 -0
- package/src/v2/components/chat/index.ts +9 -0
- package/src/v2/components/chat/scroll-element-context.ts +13 -0
- package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +8 -0
- package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +327 -0
- package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +1003 -0
- package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +13 -2
- package/src/v2/hooks/__tests__/use-attachments.test.tsx +169 -0
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +23 -4
- package/src/v2/hooks/__tests__/use-threads.test.tsx +54 -0
- package/src/v2/hooks/index.ts +5 -0
- package/src/v2/hooks/use-agent.tsx +220 -15
- package/src/v2/hooks/use-attachments.tsx +269 -0
- package/src/v2/hooks/use-frontend-tool.tsx +5 -2
- package/src/v2/hooks/use-render-activity-message.tsx +9 -2
- package/src/v2/hooks/use-render-custom-messages.tsx +6 -1
- package/src/v2/hooks/use-threads.tsx +35 -15
- package/src/v2/index.ts +5 -1
- package/src/v2/lib/__tests__/processPartialHtml.test.ts +112 -0
- package/src/v2/lib/__tests__/slots.test.ts +56 -0
- package/src/v2/lib/processPartialHtml.ts +45 -0
- package/src/v2/lib/slots.tsx +42 -1
- package/src/v2/providers/CopilotChatConfigurationProvider.tsx +9 -3
- package/src/v2/providers/CopilotKitProvider.tsx +268 -32
- package/src/v2/providers/SandboxFunctionsContext.ts +10 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.tsx +198 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.test.tsx +71 -0
- package/src/v2/providers/index.ts +7 -0
- package/src/v2/styles/globals.css +2 -1
- package/src/v2/types/index.ts +1 -0
- package/src/v2/types/sandbox-function.ts +11 -0
- package/dist/copilotkit-B3Mb1yVE.cjs.map +0 -1
- package/dist/copilotkit-DBzgOMby.d.cts.map +0 -1
- package/dist/copilotkit-DNYSFuz5.mjs.map +0 -1
- package/dist/copilotkit-Dy5w3qEV.d.mts.map +0 -1
- package/src/v2/components/__tests__/license-warning-banner.test.tsx +0 -46
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React$1 from "react";
|
|
2
|
-
import React, { createContext, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import React, { createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
|
|
4
4
|
import { HttpAgent } from "@ag-ui/client";
|
|
5
5
|
import { extendTailwindMerge, twMerge } from "tailwind-merge";
|
|
6
|
-
import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Volume2, X } from "lucide-react";
|
|
7
|
-
import { COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, createLicenseContextValue, dataToUUID, parseJson, partialJSONParse, randomId, randomUUID } from "@copilotkit/shared";
|
|
6
|
+
import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
|
|
7
|
+
import { A2UI_DEFAULT_DESIGN_GUIDELINES, A2UI_DEFAULT_GENERATION_GUIDELINES, COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, createLicenseContextValue, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID, readFileAsBase64, schemaToJsonSchema } from "@copilotkit/shared";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { Slot } from "@radix-ui/react-slot";
|
|
10
10
|
import { cva } from "class-variance-authority";
|
|
@@ -14,19 +14,120 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
|
14
14
|
import { Streamdown } from "streamdown";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
import { createComponent } from "@lit-labs/react";
|
|
17
|
-
import { A2UIProvider, A2UIRenderer, DEFAULT_SURFACE_ID, initializeDefaultCatalog, injectStyles, useA2UIActions, viewerTheme } from "@copilotkit/a2ui-renderer";
|
|
17
|
+
import { A2UIProvider, A2UIRenderer, A2UI_SCHEMA_CONTEXT_DESCRIPTION, DEFAULT_SURFACE_ID, buildCatalogContextValue, extractCatalogComponentSchemas, initializeDefaultCatalog, injectStyles, useA2UIActions, useA2UIError, viewerTheme } from "@copilotkit/a2ui-renderer";
|
|
18
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
19
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
20
|
+
import { createPortal, flushSync } from "react-dom";
|
|
18
21
|
import { StickToBottom, useStickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
19
|
-
import { merge } from "ts-deepmerge";
|
|
20
|
-
import { flushSync } from "react-dom";
|
|
21
22
|
import ReactMarkdown from "react-markdown";
|
|
22
23
|
|
|
24
|
+
//#region src/v2/lib/slots.tsx
|
|
25
|
+
/**
|
|
26
|
+
* Shallow equality comparison for objects.
|
|
27
|
+
*/
|
|
28
|
+
function shallowEqual(obj1, obj2) {
|
|
29
|
+
const keys1 = Object.keys(obj1);
|
|
30
|
+
const keys2 = Object.keys(obj2);
|
|
31
|
+
if (keys1.length !== keys2.length) return false;
|
|
32
|
+
for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
|
|
37
|
+
* class instances, and other exotic objects that happen to have typeof "object".
|
|
38
|
+
*/
|
|
39
|
+
function isPlainObject(obj) {
|
|
40
|
+
return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns the same reference as long as the value is shallowly equal to the
|
|
44
|
+
* previous render's value.
|
|
45
|
+
*
|
|
46
|
+
* - Identical references bail out immediately (O(1)).
|
|
47
|
+
* - Plain objects ({}) are shallow-compared key-by-key.
|
|
48
|
+
* - Arrays, Dates, class instances, functions, and primitives are compared by
|
|
49
|
+
* reference only — shallowEqual is never called on non-plain objects, which
|
|
50
|
+
* avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
|
|
51
|
+
*
|
|
52
|
+
* Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
|
|
53
|
+
* equality check isn't defeated by a new object reference on every render.
|
|
54
|
+
*/
|
|
55
|
+
function useShallowStableRef(value) {
|
|
56
|
+
const ref = useRef(value);
|
|
57
|
+
if (ref.current === value) return ref.current;
|
|
58
|
+
if (isPlainObject(ref.current) && isPlainObject(value)) {
|
|
59
|
+
if (shallowEqual(ref.current, value)) return ref.current;
|
|
60
|
+
}
|
|
61
|
+
ref.current = value;
|
|
62
|
+
return ref.current;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
|
|
66
|
+
*/
|
|
67
|
+
function isReactComponentType(value) {
|
|
68
|
+
if (typeof value === "function") return true;
|
|
69
|
+
if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Internal function to render a slot value as a React element (non-memoized).
|
|
74
|
+
*/
|
|
75
|
+
function renderSlotElement(slot, DefaultComponent, props) {
|
|
76
|
+
if (typeof slot === "string") {
|
|
77
|
+
const existingClassName = props.className;
|
|
78
|
+
return React.createElement(DefaultComponent, {
|
|
79
|
+
...props,
|
|
80
|
+
className: twMerge(existingClassName, slot)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (isReactComponentType(slot)) return React.createElement(slot, props);
|
|
84
|
+
if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
|
|
85
|
+
...props,
|
|
86
|
+
...slot
|
|
87
|
+
});
|
|
88
|
+
return React.createElement(DefaultComponent, props);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Internal memoized wrapper component for renderSlot.
|
|
92
|
+
* Uses forwardRef to support ref forwarding.
|
|
93
|
+
*/
|
|
94
|
+
const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
|
|
95
|
+
const { $slot, $component, ...rest } = props;
|
|
96
|
+
return renderSlotElement($slot, $component, ref !== null ? {
|
|
97
|
+
...rest,
|
|
98
|
+
ref
|
|
99
|
+
} : rest);
|
|
100
|
+
}), (prev, next) => {
|
|
101
|
+
if (prev.$slot !== next.$slot) return false;
|
|
102
|
+
if (prev.$component !== next.$component) return false;
|
|
103
|
+
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
|
|
104
|
+
const { $slot: _ns, $component: _nc, ...nextRest } = next;
|
|
105
|
+
return shallowEqual(prevRest, nextRest);
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* Renders a slot value as a memoized React element.
|
|
109
|
+
* Automatically prevents unnecessary re-renders using shallow prop comparison.
|
|
110
|
+
* Supports ref forwarding.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
|
|
114
|
+
*/
|
|
115
|
+
function renderSlot(slot, DefaultComponent, props) {
|
|
116
|
+
return React.createElement(MemoizedSlotWrapper, {
|
|
117
|
+
...props,
|
|
118
|
+
$slot: slot,
|
|
119
|
+
$component: DefaultComponent
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
23
124
|
//#region src/v2/providers/CopilotChatConfigurationProvider.tsx
|
|
24
125
|
const CopilotChatDefaultLabels = {
|
|
25
126
|
chatInputPlaceholder: "Type a message...",
|
|
26
127
|
chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
|
|
27
128
|
chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
|
|
28
129
|
chatInputToolbarFinishTranscribeButtonLabel: "Finish",
|
|
29
|
-
chatInputToolbarAddButtonLabel: "Add
|
|
130
|
+
chatInputToolbarAddButtonLabel: "Add attachments",
|
|
30
131
|
chatInputToolbarToolsButtonLabel: "Tools",
|
|
31
132
|
assistantMessageToolbarCopyCodeLabel: "Copy",
|
|
32
133
|
assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
|
|
@@ -46,11 +147,12 @@ const CopilotChatDefaultLabels = {
|
|
|
46
147
|
const CopilotChatConfiguration = createContext(null);
|
|
47
148
|
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
|
|
48
149
|
const parentConfig = useContext(CopilotChatConfiguration);
|
|
150
|
+
const stableLabels = useShallowStableRef(labels);
|
|
49
151
|
const mergedLabels = useMemo(() => ({
|
|
50
152
|
...CopilotChatDefaultLabels,
|
|
51
153
|
...parentConfig?.labels ?? {},
|
|
52
|
-
...
|
|
53
|
-
}), [
|
|
154
|
+
...stableLabels ?? {}
|
|
155
|
+
}), [stableLabels, parentConfig?.labels]);
|
|
54
156
|
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
55
157
|
const resolvedThreadId = useMemo(() => {
|
|
56
158
|
if (threadId) return threadId;
|
|
@@ -473,82 +575,11 @@ const CopilotChatAudioRecorder = forwardRef((props, ref) => {
|
|
|
473
575
|
});
|
|
474
576
|
CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
|
|
475
577
|
|
|
476
|
-
//#endregion
|
|
477
|
-
//#region src/v2/lib/slots.tsx
|
|
478
|
-
/**
|
|
479
|
-
* Shallow equality comparison for objects.
|
|
480
|
-
*/
|
|
481
|
-
function shallowEqual(obj1, obj2) {
|
|
482
|
-
const keys1 = Object.keys(obj1);
|
|
483
|
-
const keys2 = Object.keys(obj2);
|
|
484
|
-
if (keys1.length !== keys2.length) return false;
|
|
485
|
-
for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
|
|
486
|
-
return true;
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
|
|
490
|
-
*/
|
|
491
|
-
function isReactComponentType(value) {
|
|
492
|
-
if (typeof value === "function") return true;
|
|
493
|
-
if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Internal function to render a slot value as a React element (non-memoized).
|
|
498
|
-
*/
|
|
499
|
-
function renderSlotElement(slot, DefaultComponent, props) {
|
|
500
|
-
if (typeof slot === "string") {
|
|
501
|
-
const existingClassName = props.className;
|
|
502
|
-
return React.createElement(DefaultComponent, {
|
|
503
|
-
...props,
|
|
504
|
-
className: twMerge(existingClassName, slot)
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
if (isReactComponentType(slot)) return React.createElement(slot, props);
|
|
508
|
-
if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
|
|
509
|
-
...props,
|
|
510
|
-
...slot
|
|
511
|
-
});
|
|
512
|
-
return React.createElement(DefaultComponent, props);
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Internal memoized wrapper component for renderSlot.
|
|
516
|
-
* Uses forwardRef to support ref forwarding.
|
|
517
|
-
*/
|
|
518
|
-
const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
|
|
519
|
-
const { $slot, $component, ...rest } = props;
|
|
520
|
-
return renderSlotElement($slot, $component, ref !== null ? {
|
|
521
|
-
...rest,
|
|
522
|
-
ref
|
|
523
|
-
} : rest);
|
|
524
|
-
}), (prev, next) => {
|
|
525
|
-
if (prev.$slot !== next.$slot) return false;
|
|
526
|
-
if (prev.$component !== next.$component) return false;
|
|
527
|
-
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
|
|
528
|
-
const { $slot: _ns, $component: _nc, ...nextRest } = next;
|
|
529
|
-
return shallowEqual(prevRest, nextRest);
|
|
530
|
-
});
|
|
531
|
-
/**
|
|
532
|
-
* Renders a slot value as a memoized React element.
|
|
533
|
-
* Automatically prevents unnecessary re-renders using shallow prop comparison.
|
|
534
|
-
* Supports ref forwarding.
|
|
535
|
-
*
|
|
536
|
-
* @example
|
|
537
|
-
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
|
|
538
|
-
*/
|
|
539
|
-
function renderSlot(slot, DefaultComponent, props) {
|
|
540
|
-
return React.createElement(MemoizedSlotWrapper, {
|
|
541
|
-
...props,
|
|
542
|
-
$slot: slot,
|
|
543
|
-
$component: DefaultComponent
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
|
|
547
578
|
//#endregion
|
|
548
579
|
//#region src/v2/components/chat/CopilotChatInput.tsx
|
|
549
580
|
const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
|
|
550
581
|
const SLASH_MENU_ITEM_HEIGHT_PX = 40;
|
|
551
|
-
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus =
|
|
582
|
+
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
|
|
552
583
|
const isControlled = value !== void 0;
|
|
553
584
|
const [internalValue, setInternalValue] = useState(() => value ?? "");
|
|
554
585
|
useEffect(() => {
|
|
@@ -577,6 +608,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
577
608
|
paddingLeft: 0,
|
|
578
609
|
paddingRight: 0
|
|
579
610
|
});
|
|
611
|
+
const containerCacheRef = useRef(null);
|
|
580
612
|
const commandItems = useMemo(() => {
|
|
581
613
|
const entries = [];
|
|
582
614
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -621,7 +653,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
621
653
|
previousModalStateRef.current = config?.isModalOpen;
|
|
622
654
|
return;
|
|
623
655
|
}
|
|
624
|
-
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus();
|
|
656
|
+
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus({ preventScroll: true });
|
|
625
657
|
previousModalStateRef.current = config?.isModalOpen;
|
|
626
658
|
}, [config?.isModalOpen, autoFocus]);
|
|
627
659
|
useEffect(() => {
|
|
@@ -864,6 +896,25 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
864
896
|
return nextLayout;
|
|
865
897
|
});
|
|
866
898
|
}, []);
|
|
899
|
+
const updateContainerCache = useCallback(() => {
|
|
900
|
+
const grid = gridRef.current;
|
|
901
|
+
const addContainer = addButtonContainerRef.current;
|
|
902
|
+
const actionsContainer = actionsContainerRef.current;
|
|
903
|
+
if (!grid || !addContainer || !actionsContainer) return null;
|
|
904
|
+
const gridStyles = window.getComputedStyle(grid);
|
|
905
|
+
const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
|
|
906
|
+
const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
|
|
907
|
+
const columnGap = parseFloat(gridStyles.columnGap) || 0;
|
|
908
|
+
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
|
|
909
|
+
if (gridAvailableWidth <= 0) return null;
|
|
910
|
+
const addWidth = addContainer.getBoundingClientRect().width;
|
|
911
|
+
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
912
|
+
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
|
|
913
|
+
if (compactWidth <= 0) return null;
|
|
914
|
+
const result = { compactWidth };
|
|
915
|
+
containerCacheRef.current = result;
|
|
916
|
+
return result;
|
|
917
|
+
}, []);
|
|
867
918
|
const evaluateLayout = useCallback(() => {
|
|
868
919
|
if (mode !== "input") {
|
|
869
920
|
updateLayout("compact");
|
|
@@ -889,31 +940,31 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
889
940
|
const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
|
|
890
941
|
let shouldExpand = hasExplicitBreak || renderedMultiline;
|
|
891
942
|
if (!shouldExpand) {
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
|
|
897
|
-
if (gridAvailableWidth > 0) {
|
|
898
|
-
const addWidth = addContainer.getBoundingClientRect().width;
|
|
899
|
-
const actionsWidth = actionsContainer.getBoundingClientRect().width;
|
|
900
|
-
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
|
|
901
|
-
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
|
|
902
|
-
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
|
|
903
|
-
const context = canvas.getContext("2d");
|
|
904
|
-
if (context) {
|
|
943
|
+
const cache = containerCacheRef.current ?? updateContainerCache();
|
|
944
|
+
if (cache && cache.compactWidth > 0) {
|
|
945
|
+
const compactInnerWidth = Math.max(cache.compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
|
|
946
|
+
if (compactInnerWidth > 0) {
|
|
905
947
|
const textareaStyles = window.getComputedStyle(textarea);
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
let longestWidth = 0;
|
|
911
|
-
for (const line of lines) {
|
|
912
|
-
const metrics = context.measureText(line || " ");
|
|
913
|
-
if (metrics.width > longestWidth) longestWidth = metrics.width;
|
|
914
|
-
}
|
|
915
|
-
if (longestWidth > compactInnerWidth) shouldExpand = true;
|
|
948
|
+
let font = textareaStyles.font;
|
|
949
|
+
if (!font) {
|
|
950
|
+
const { fontStyle, fontVariant, fontWeight, fontSize, lineHeight, fontFamily } = textareaStyles;
|
|
951
|
+
if (fontSize && fontFamily) font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
|
|
916
952
|
}
|
|
953
|
+
if (font?.trim()) {
|
|
954
|
+
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
|
|
955
|
+
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
|
|
956
|
+
const context = canvas.getContext("2d");
|
|
957
|
+
if (context) {
|
|
958
|
+
context.font = font;
|
|
959
|
+
const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
|
|
960
|
+
let longestWidth = 0;
|
|
961
|
+
for (const line of lines) {
|
|
962
|
+
const metrics = context.measureText(line || " ");
|
|
963
|
+
if (metrics.width > longestWidth) longestWidth = metrics.width;
|
|
964
|
+
}
|
|
965
|
+
if (longestWidth > compactInnerWidth) shouldExpand = true;
|
|
966
|
+
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] canvas.getContext('2d') returned null. Text-width-based expansion will be unavailable.");
|
|
967
|
+
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] Could not resolve textarea font for layout measurement. Text-width-based expansion will be skipped until the next evaluation.");
|
|
917
968
|
}
|
|
918
969
|
}
|
|
919
970
|
}
|
|
@@ -923,6 +974,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
923
974
|
ensureMeasurements,
|
|
924
975
|
mode,
|
|
925
976
|
resolvedValue,
|
|
977
|
+
updateContainerCache,
|
|
926
978
|
updateLayout
|
|
927
979
|
]);
|
|
928
980
|
useLayoutEffect(() => {
|
|
@@ -935,11 +987,17 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
935
987
|
const addContainer = addButtonContainerRef.current;
|
|
936
988
|
const actionsContainer = actionsContainerRef.current;
|
|
937
989
|
if (!textarea || !grid || !addContainer || !actionsContainer) return;
|
|
938
|
-
const
|
|
990
|
+
const containerTargets = new Set([
|
|
991
|
+
grid,
|
|
992
|
+
addContainer,
|
|
993
|
+
actionsContainer
|
|
994
|
+
]);
|
|
995
|
+
const scheduleEvaluation = (invalidateCache) => {
|
|
939
996
|
if (ignoreResizeRef.current) {
|
|
940
997
|
ignoreResizeRef.current = false;
|
|
941
998
|
return;
|
|
942
999
|
}
|
|
1000
|
+
if (invalidateCache) containerCacheRef.current = null;
|
|
943
1001
|
if (typeof window === "undefined") {
|
|
944
1002
|
evaluateLayout();
|
|
945
1003
|
return;
|
|
@@ -950,8 +1008,13 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
950
1008
|
evaluateLayout();
|
|
951
1009
|
});
|
|
952
1010
|
};
|
|
953
|
-
const observer = new ResizeObserver(() => {
|
|
954
|
-
|
|
1011
|
+
const observer = new ResizeObserver((entries) => {
|
|
1012
|
+
let shouldInvalidate = false;
|
|
1013
|
+
for (const entry of entries) if (containerTargets.has(entry.target)) {
|
|
1014
|
+
shouldInvalidate = true;
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
scheduleEvaluation(shouldInvalidate);
|
|
955
1018
|
});
|
|
956
1019
|
observer.observe(grid);
|
|
957
1020
|
observer.observe(addContainer);
|
|
@@ -1096,6 +1159,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1096
1159
|
});
|
|
1097
1160
|
_CopilotChatInput.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
|
|
1098
1161
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1162
|
+
const [mounted, setMounted] = useState(false);
|
|
1163
|
+
useEffect(() => setMounted(true), []);
|
|
1099
1164
|
const menuItems = useMemo(() => {
|
|
1100
1165
|
const items = [];
|
|
1101
1166
|
if (onAddFile) items.push({
|
|
@@ -1126,26 +1191,28 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1126
1191
|
}), []);
|
|
1127
1192
|
const hasMenuItems = menuItems.length > 0;
|
|
1128
1193
|
const isDisabled = disabled || !hasMenuItems;
|
|
1194
|
+
const button = /* @__PURE__ */ jsx(Button, {
|
|
1195
|
+
type: "button",
|
|
1196
|
+
"data-testid": "copilot-add-menu-button",
|
|
1197
|
+
variant: "chatInputToolbarSecondary",
|
|
1198
|
+
size: "chatInputToolbarIcon",
|
|
1199
|
+
className: twMerge("cpk:ml-1", className),
|
|
1200
|
+
disabled: isDisabled,
|
|
1201
|
+
...props,
|
|
1202
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "cpk:size-[20px]" })
|
|
1203
|
+
});
|
|
1204
|
+
if (!mounted) return button;
|
|
1129
1205
|
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
1130
1206
|
asChild: true,
|
|
1131
1207
|
children: /* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
1132
1208
|
asChild: true,
|
|
1133
|
-
children:
|
|
1134
|
-
type: "button",
|
|
1135
|
-
"data-testid": "copilot-add-menu-button",
|
|
1136
|
-
variant: "chatInputToolbarSecondary",
|
|
1137
|
-
size: "chatInputToolbarIcon",
|
|
1138
|
-
className: twMerge("cpk:ml-1", className),
|
|
1139
|
-
disabled: isDisabled,
|
|
1140
|
-
...props,
|
|
1141
|
-
children: /* @__PURE__ */ jsx(Plus, { className: "cpk:size-[20px]" })
|
|
1142
|
-
})
|
|
1209
|
+
children: button
|
|
1143
1210
|
})
|
|
1144
1211
|
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
1145
1212
|
side: "bottom",
|
|
1146
1213
|
children: /* @__PURE__ */ jsxs("p", {
|
|
1147
1214
|
className: "cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium",
|
|
1148
|
-
children: [/* @__PURE__ */ jsx("span", { children: "Add
|
|
1215
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Add attachments" }), /* @__PURE__ */ jsx("code", {
|
|
1149
1216
|
className: "cpk:rounded cpk:bg-[#4a4a4a] cpk:px-1 cpk:py-[1px] cpk:font-mono cpk:text-[11px] cpk:text-white cpk:dark:bg-[#e0e0e0] cpk:dark:text-black",
|
|
1150
1217
|
children: "/"
|
|
1151
1218
|
})]
|
|
@@ -1161,21 +1228,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
|
|
|
1161
1228
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
1162
1229
|
useImperativeHandle(ref, () => internalTextareaRef.current);
|
|
1163
1230
|
useEffect(() => {
|
|
1164
|
-
|
|
1165
|
-
if (!textarea) return;
|
|
1166
|
-
const handleFocus = () => {
|
|
1167
|
-
setTimeout(() => {
|
|
1168
|
-
textarea.scrollIntoView({
|
|
1169
|
-
behavior: "smooth",
|
|
1170
|
-
block: "nearest"
|
|
1171
|
-
});
|
|
1172
|
-
}, 300);
|
|
1173
|
-
};
|
|
1174
|
-
textarea.addEventListener("focus", handleFocus);
|
|
1175
|
-
return () => textarea.removeEventListener("focus", handleFocus);
|
|
1176
|
-
}, []);
|
|
1177
|
-
useEffect(() => {
|
|
1178
|
-
if (autoFocus) internalTextareaRef.current?.focus();
|
|
1231
|
+
if (autoFocus) internalTextareaRef.current?.focus({ preventScroll: true });
|
|
1179
1232
|
}, [autoFocus]);
|
|
1180
1233
|
return /* @__PURE__ */ jsx("textarea", {
|
|
1181
1234
|
ref: internalTextareaRef,
|
|
@@ -1883,125 +1936,981 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
|
|
|
1883
1936
|
};
|
|
1884
1937
|
|
|
1885
1938
|
//#endregion
|
|
1886
|
-
//#region src/v2/
|
|
1887
|
-
|
|
1888
|
-
function
|
|
1889
|
-
|
|
1890
|
-
initializeDefaultCatalog();
|
|
1891
|
-
injectStyles();
|
|
1892
|
-
initialized = true;
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
function createA2UIMessageRenderer(options) {
|
|
1896
|
-
const { theme } = options;
|
|
1897
|
-
return {
|
|
1898
|
-
activityType: "a2ui-surface",
|
|
1899
|
-
content: z.any(),
|
|
1900
|
-
render: ({ content, agent }) => {
|
|
1901
|
-
ensureInitialized();
|
|
1902
|
-
const [operations, setOperations] = useState([]);
|
|
1903
|
-
const lastSignatureRef = useRef(null);
|
|
1904
|
-
const { copilotkit } = useCopilotKit();
|
|
1905
|
-
useEffect(() => {
|
|
1906
|
-
if (!content || !Array.isArray(content.operations)) {
|
|
1907
|
-
lastSignatureRef.current = null;
|
|
1908
|
-
setOperations([]);
|
|
1909
|
-
return;
|
|
1910
|
-
}
|
|
1911
|
-
const incoming = content.operations;
|
|
1912
|
-
const signature = stringifyOperations(incoming);
|
|
1913
|
-
if (signature && signature === lastSignatureRef.current) return;
|
|
1914
|
-
lastSignatureRef.current = signature;
|
|
1915
|
-
setOperations(incoming);
|
|
1916
|
-
}, [content]);
|
|
1917
|
-
const groupedOperations = useMemo(() => {
|
|
1918
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1919
|
-
for (const operation of operations) {
|
|
1920
|
-
const surfaceId = getOperationSurfaceId(operation) ?? DEFAULT_SURFACE_ID;
|
|
1921
|
-
if (!groups.has(surfaceId)) groups.set(surfaceId, []);
|
|
1922
|
-
groups.get(surfaceId).push(operation);
|
|
1923
|
-
}
|
|
1924
|
-
return groups;
|
|
1925
|
-
}, [operations]);
|
|
1926
|
-
if (!groupedOperations.size) return null;
|
|
1927
|
-
return /* @__PURE__ */ jsx("div", {
|
|
1928
|
-
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
|
|
1929
|
-
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ jsx(ReactSurfaceHost, {
|
|
1930
|
-
surfaceId,
|
|
1931
|
-
operations: ops,
|
|
1932
|
-
theme,
|
|
1933
|
-
agent,
|
|
1934
|
-
copilotkit
|
|
1935
|
-
}, surfaceId))
|
|
1936
|
-
});
|
|
1937
|
-
}
|
|
1938
|
-
};
|
|
1939
|
+
//#region src/v2/providers/SandboxFunctionsContext.ts
|
|
1940
|
+
const SandboxFunctionsContext = createContext([]);
|
|
1941
|
+
function useSandboxFunctions() {
|
|
1942
|
+
return useContext(SandboxFunctionsContext);
|
|
1939
1943
|
}
|
|
1944
|
+
|
|
1945
|
+
//#endregion
|
|
1946
|
+
//#region src/v2/lib/processPartialHtml.ts
|
|
1940
1947
|
/**
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1948
|
+
* Extracts all complete `<style>` blocks from the raw HTML.
|
|
1949
|
+
* Returns the concatenated style tags, suitable for injection into `<head>`.
|
|
1943
1950
|
*/
|
|
1944
|
-
function
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
children: /* @__PURE__ */ jsxs(A2UIProvider, {
|
|
1948
|
-
onAction: useCallback(async (message) => {
|
|
1949
|
-
if (!agent) return;
|
|
1950
|
-
try {
|
|
1951
|
-
console.info("[A2UI] Action dispatched", message.userAction);
|
|
1952
|
-
copilotkit.setProperties({
|
|
1953
|
-
...copilotkit.properties ?? {},
|
|
1954
|
-
a2uiAction: message
|
|
1955
|
-
});
|
|
1956
|
-
await copilotkit.runAgent({ agent });
|
|
1957
|
-
} finally {
|
|
1958
|
-
if (copilotkit.properties) {
|
|
1959
|
-
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
1960
|
-
copilotkit.setProperties(rest);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
}, [agent, copilotkit]),
|
|
1964
|
-
theme,
|
|
1965
|
-
children: [/* @__PURE__ */ jsx(SurfaceMessageProcessor, {
|
|
1966
|
-
surfaceId,
|
|
1967
|
-
operations
|
|
1968
|
-
}), /* @__PURE__ */ jsx(A2UIRenderer, {
|
|
1969
|
-
surfaceId,
|
|
1970
|
-
className: "cpk:flex cpk:flex-1"
|
|
1971
|
-
})]
|
|
1972
|
-
})
|
|
1973
|
-
});
|
|
1951
|
+
function extractCompleteStyles(html) {
|
|
1952
|
+
const matches = html.match(/<style\b[^>]*>[\s\S]*?<\/style>/gi);
|
|
1953
|
+
return matches ? matches.join("") : "";
|
|
1974
1954
|
}
|
|
1975
1955
|
/**
|
|
1976
|
-
* Processes
|
|
1977
|
-
*
|
|
1956
|
+
* Processes raw accumulated HTML for safe preview via innerHTML injection.
|
|
1957
|
+
* Pure function, no DOM dependencies.
|
|
1958
|
+
*
|
|
1959
|
+
* Pipeline (order matters):
|
|
1960
|
+
* 1. Strip incomplete tag at end
|
|
1961
|
+
* 2. Strip complete <style>, <script>, and <head> blocks
|
|
1962
|
+
* 3. Strip incomplete <style>/<script>/<head> blocks
|
|
1963
|
+
* 4. Strip incomplete HTML entities
|
|
1964
|
+
* 5. Extract body content (or use full string if no <body>)
|
|
1978
1965
|
*/
|
|
1979
|
-
function
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1966
|
+
function processPartialHtml(html) {
|
|
1967
|
+
let result = html;
|
|
1968
|
+
result = result.replace(/<[^>]*$/, "");
|
|
1969
|
+
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
|
|
1970
|
+
result = result.replace(/<(style|script|head)\b[^>]*>[\s\S]*$/gi, "");
|
|
1971
|
+
result = result.replace(/&[a-zA-Z0-9#]*$/, "");
|
|
1972
|
+
const bodyMatch = result.match(/<body[^>]*>([\s\S]*)/i);
|
|
1973
|
+
if (bodyMatch) {
|
|
1974
|
+
result = bodyMatch[1];
|
|
1975
|
+
result = result.replace(/<\/body>[\s\S]*/i, "");
|
|
1976
|
+
}
|
|
1977
|
+
return result;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
//#endregion
|
|
1981
|
+
//#region src/v2/components/OpenGenerativeUIRenderer.tsx
|
|
1982
|
+
const OpenGenerativeUIActivityType = "open-generative-ui";
|
|
1983
|
+
const OpenGenerativeUIContentSchema = z.object({
|
|
1984
|
+
initialHeight: z.number().optional(),
|
|
1985
|
+
generating: z.boolean().optional(),
|
|
1986
|
+
css: z.string().optional(),
|
|
1987
|
+
cssComplete: z.boolean().optional(),
|
|
1988
|
+
html: z.array(z.string()).optional(),
|
|
1989
|
+
htmlComplete: z.boolean().optional(),
|
|
1990
|
+
jsFunctions: z.string().optional(),
|
|
1991
|
+
jsFunctionsComplete: z.boolean().optional(),
|
|
1992
|
+
jsExpressions: z.array(z.string()).optional(),
|
|
1993
|
+
jsExpressionsComplete: z.boolean().optional()
|
|
1994
|
+
});
|
|
1995
|
+
/**
|
|
1996
|
+
* Schema for the generateSandboxedUi tool call arguments.
|
|
1997
|
+
* Used by the frontend tool renderer to display placeholder messages.
|
|
1998
|
+
*/
|
|
1999
|
+
const GenerateSandboxedUiArgsSchema = z.object({
|
|
2000
|
+
initialHeight: z.number().optional(),
|
|
2001
|
+
placeholderMessages: z.array(z.string()).optional(),
|
|
2002
|
+
css: z.string().optional(),
|
|
2003
|
+
html: z.string().optional(),
|
|
2004
|
+
jsFunctions: z.string().optional(),
|
|
2005
|
+
jsExpressions: z.array(z.string()).optional()
|
|
2006
|
+
});
|
|
2007
|
+
const THROTTLE_MS = 1e3;
|
|
2008
|
+
/**
|
|
2009
|
+
* Returns true when the inner component should re-render immediately
|
|
2010
|
+
* (no throttle delay).
|
|
2011
|
+
*/
|
|
2012
|
+
function shouldFlushImmediately(prev, next) {
|
|
2013
|
+
if (next.cssComplete && (!prev || !prev.cssComplete)) return true;
|
|
2014
|
+
if (next.htmlComplete) return true;
|
|
2015
|
+
if (next.generating === false) return true;
|
|
2016
|
+
if (next.jsFunctions && (!prev || !prev.jsFunctions)) return true;
|
|
2017
|
+
if ((next.jsExpressions?.length ?? 0) > (prev?.jsExpressions?.length ?? 0)) return true;
|
|
2018
|
+
if (next.html?.length && (!prev || !prev.html?.length)) return true;
|
|
2019
|
+
return false;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Outer wrapper — absorbs every parent re-render but only forwards
|
|
2023
|
+
* throttled content snapshots to the memoized inner component.
|
|
2024
|
+
*/
|
|
2025
|
+
const OpenGenerativeUIActivityRenderer = function OpenGenerativeUIActivityRenderer({ content }) {
|
|
2026
|
+
const latestContentRef = useRef(content);
|
|
2027
|
+
latestContentRef.current = content;
|
|
2028
|
+
const [throttledContent, setThrottledContent] = useState(content);
|
|
2029
|
+
const throttledContentRef = useRef(throttledContent);
|
|
2030
|
+
const timerRef = useRef(null);
|
|
2031
|
+
if (throttledContentRef.current !== content) {
|
|
2032
|
+
if (shouldFlushImmediately(throttledContentRef.current, content)) {
|
|
2033
|
+
if (timerRef.current !== null) {
|
|
2034
|
+
clearTimeout(timerRef.current);
|
|
2035
|
+
timerRef.current = null;
|
|
2036
|
+
}
|
|
2037
|
+
throttledContentRef.current = content;
|
|
2038
|
+
setThrottledContent(content);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
const flush = useCallback(() => {
|
|
2042
|
+
timerRef.current = null;
|
|
2043
|
+
const latest = latestContentRef.current;
|
|
2044
|
+
throttledContentRef.current = latest;
|
|
2045
|
+
setThrottledContent(latest);
|
|
2046
|
+
}, []);
|
|
2047
|
+
useEffect(() => {
|
|
2048
|
+
if (throttledContentRef.current === content) return;
|
|
2049
|
+
if (timerRef.current === null) timerRef.current = setTimeout(flush, THROTTLE_MS);
|
|
2050
|
+
}, [content, flush]);
|
|
2051
|
+
useEffect(() => {
|
|
2052
|
+
return () => {
|
|
2053
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
2054
|
+
};
|
|
2055
|
+
}, []);
|
|
2056
|
+
return /* @__PURE__ */ jsx(OpenGenerativeUIActivityRendererInner, { content: throttledContent });
|
|
2057
|
+
};
|
|
2058
|
+
function ensureHead(html) {
|
|
2059
|
+
if (/<head[\s>]/i.test(html)) return html;
|
|
2060
|
+
return `<head></head>${html}`;
|
|
2061
|
+
}
|
|
2062
|
+
function injectCssIntoHtml(html, css) {
|
|
2063
|
+
const headCloseIdx = html.indexOf("</head>");
|
|
2064
|
+
if (headCloseIdx !== -1) return html.slice(0, headCloseIdx) + `<style>${css}</style>` + html.slice(headCloseIdx);
|
|
2065
|
+
return `<head><style>${css}</style></head>${html}`;
|
|
2066
|
+
}
|
|
2067
|
+
const OpenGenerativeUIActivityRendererInner = React.memo(function OpenGenerativeUIActivityRendererInner({ content }) {
|
|
2068
|
+
const initialHeight = content.initialHeight ?? 200;
|
|
2069
|
+
const [autoHeight, setAutoHeight] = useState(null);
|
|
2070
|
+
const sandboxFunctions = useSandboxFunctions();
|
|
2071
|
+
const localApi = useMemo(() => {
|
|
2072
|
+
const api = {};
|
|
2073
|
+
for (const fn of sandboxFunctions) api[fn.name] = fn.handler;
|
|
2074
|
+
return api;
|
|
2075
|
+
}, [sandboxFunctions]);
|
|
2076
|
+
const fullHtml = content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
|
|
2077
|
+
const css = content.cssComplete ? content.css : void 0;
|
|
2078
|
+
const cssReady = !!content.cssComplete;
|
|
2079
|
+
const partialHtml = !content.htmlComplete && content.html?.length ? content.html.join("") : void 0;
|
|
2080
|
+
const previewBody = partialHtml ? processPartialHtml(partialHtml) : void 0;
|
|
2081
|
+
const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : "";
|
|
2082
|
+
const hasPreview = cssReady && !!previewBody?.trim();
|
|
2083
|
+
const hasVisibleSandbox = !!fullHtml || hasPreview;
|
|
2084
|
+
const containerRef = useRef(null);
|
|
2085
|
+
const sandboxRef = useRef(null);
|
|
2086
|
+
const previewSandboxRef = useRef(null);
|
|
2087
|
+
const previewReadyRef = useRef(false);
|
|
2088
|
+
const sandboxReadyRef = useRef(false);
|
|
2089
|
+
const executedIndexRef = useRef(0);
|
|
2090
|
+
const pendingQueueRef = useRef([]);
|
|
2091
|
+
const jsFunctionsInjectedRef = useRef(false);
|
|
2092
|
+
useEffect(() => {
|
|
2093
|
+
const container = containerRef.current;
|
|
2094
|
+
if (!container || fullHtml || !hasPreview || previewSandboxRef.current) return;
|
|
2095
|
+
let cancelled = false;
|
|
2096
|
+
import("@jetbrains/websandbox").then((mod) => {
|
|
2097
|
+
if (cancelled) return;
|
|
2098
|
+
const sandbox = (mod.default?.default ?? mod.default).create({}, {
|
|
2099
|
+
frameContainer: container,
|
|
2100
|
+
frameContent: "<head></head><body></body>",
|
|
2101
|
+
allowAdditionalAttributes: ""
|
|
2102
|
+
});
|
|
2103
|
+
previewSandboxRef.current = sandbox;
|
|
2104
|
+
sandbox.iframe.style.width = "100%";
|
|
2105
|
+
sandbox.iframe.style.height = "100%";
|
|
2106
|
+
sandbox.iframe.style.border = "none";
|
|
2107
|
+
sandbox.iframe.style.backgroundColor = "transparent";
|
|
2108
|
+
sandbox.promise.then(() => {
|
|
2109
|
+
if (cancelled) return;
|
|
2110
|
+
previewReadyRef.current = true;
|
|
2111
|
+
sandbox.run(`
|
|
2112
|
+
var s = document.createElement('style');
|
|
2113
|
+
s.textContent = 'html, body { overflow: hidden !important; }';
|
|
2114
|
+
document.head.appendChild(s);
|
|
2115
|
+
`);
|
|
2116
|
+
const headParts = [];
|
|
2117
|
+
if (css) headParts.push(`<style>${css}</style>`);
|
|
2118
|
+
if (previewStyles) headParts.push(previewStyles);
|
|
2119
|
+
if (headParts.length) sandbox.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
|
|
2120
|
+
if (previewBody) sandbox.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
|
|
2121
|
+
});
|
|
2122
|
+
}).catch((err) => {
|
|
2123
|
+
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
|
|
2124
|
+
});
|
|
2125
|
+
return () => {
|
|
2126
|
+
cancelled = true;
|
|
2127
|
+
};
|
|
2128
|
+
}, [hasPreview, fullHtml]);
|
|
2129
|
+
useEffect(() => {
|
|
2130
|
+
if (!previewSandboxRef.current || !previewReadyRef.current) return;
|
|
2131
|
+
const headParts = [];
|
|
2132
|
+
if (css) headParts.push(`<style>${css}</style>`);
|
|
2133
|
+
if (previewStyles) headParts.push(previewStyles);
|
|
2134
|
+
if (headParts.length) previewSandboxRef.current.run(`document.head.innerHTML = ${JSON.stringify(headParts.join(""))}`);
|
|
2135
|
+
if (!previewBody) return;
|
|
2136
|
+
previewSandboxRef.current.run(`document.body.innerHTML = ${JSON.stringify(previewBody)}`);
|
|
2137
|
+
}, [
|
|
2138
|
+
previewBody,
|
|
2139
|
+
previewStyles,
|
|
2140
|
+
css
|
|
2141
|
+
]);
|
|
2142
|
+
useEffect(() => {
|
|
2143
|
+
const container = containerRef.current;
|
|
2144
|
+
if (!container || !fullHtml) return;
|
|
2145
|
+
if (previewSandboxRef.current) {
|
|
2146
|
+
previewSandboxRef.current.destroy();
|
|
2147
|
+
previewSandboxRef.current = null;
|
|
2148
|
+
previewReadyRef.current = false;
|
|
2149
|
+
}
|
|
2150
|
+
let cancelled = false;
|
|
2151
|
+
executedIndexRef.current = 0;
|
|
2152
|
+
jsFunctionsInjectedRef.current = false;
|
|
2153
|
+
sandboxReadyRef.current = false;
|
|
2154
|
+
pendingQueueRef.current = [];
|
|
2155
|
+
const htmlContent = css ? injectCssIntoHtml(fullHtml, css) : fullHtml;
|
|
2156
|
+
import("@jetbrains/websandbox").then((mod) => {
|
|
2157
|
+
if (cancelled) return;
|
|
2158
|
+
const sandbox = (mod.default?.default ?? mod.default).create(localApi, {
|
|
2159
|
+
frameContainer: container,
|
|
2160
|
+
frameContent: ensureHead(htmlContent),
|
|
2161
|
+
allowAdditionalAttributes: ""
|
|
2162
|
+
});
|
|
2163
|
+
sandboxRef.current = sandbox;
|
|
2164
|
+
sandbox.iframe.style.width = "100%";
|
|
2165
|
+
sandbox.iframe.style.height = "100%";
|
|
2166
|
+
sandbox.iframe.style.border = "none";
|
|
2167
|
+
sandbox.iframe.style.backgroundColor = "transparent";
|
|
2168
|
+
sandbox.promise.then(() => {
|
|
2169
|
+
if (cancelled) return;
|
|
2170
|
+
sandboxReadyRef.current = true;
|
|
2171
|
+
sandbox.run(`
|
|
2172
|
+
var s = document.createElement('style');
|
|
2173
|
+
s.textContent = 'html, body { overflow: hidden !important; }';
|
|
2174
|
+
document.head.appendChild(s);
|
|
2175
|
+
`);
|
|
2176
|
+
const queue = pendingQueueRef.current;
|
|
2177
|
+
pendingQueueRef.current = [];
|
|
2178
|
+
for (const code of queue) sandbox.run(code);
|
|
2179
|
+
});
|
|
2180
|
+
}).catch((err) => {
|
|
2181
|
+
console.error("[OpenGenerativeUI] Failed to load sandbox module:", err);
|
|
2182
|
+
});
|
|
2183
|
+
return () => {
|
|
2184
|
+
cancelled = true;
|
|
2185
|
+
if (previewSandboxRef.current) {
|
|
2186
|
+
previewSandboxRef.current.destroy();
|
|
2187
|
+
previewSandboxRef.current = null;
|
|
2188
|
+
previewReadyRef.current = false;
|
|
2189
|
+
}
|
|
2190
|
+
if (sandboxRef.current) {
|
|
2191
|
+
sandboxRef.current.destroy();
|
|
2192
|
+
sandboxRef.current = null;
|
|
2193
|
+
}
|
|
2194
|
+
sandboxReadyRef.current = false;
|
|
2195
|
+
setAutoHeight(null);
|
|
2196
|
+
};
|
|
2197
|
+
}, [
|
|
2198
|
+
fullHtml,
|
|
2199
|
+
css,
|
|
2200
|
+
localApi
|
|
2201
|
+
]);
|
|
2202
|
+
useEffect(() => {
|
|
2203
|
+
if (!content.jsFunctions || jsFunctionsInjectedRef.current) return;
|
|
2204
|
+
jsFunctionsInjectedRef.current = true;
|
|
2205
|
+
const sandbox = sandboxRef.current;
|
|
2206
|
+
if (sandboxReadyRef.current && sandbox) sandbox.run(content.jsFunctions);
|
|
2207
|
+
else pendingQueueRef.current.push(content.jsFunctions);
|
|
2208
|
+
}, [content.jsFunctions]);
|
|
2209
|
+
useEffect(() => {
|
|
2210
|
+
const expressions = content.jsExpressions;
|
|
2211
|
+
if (!expressions || expressions.length === 0) return;
|
|
2212
|
+
const startIndex = executedIndexRef.current;
|
|
2213
|
+
if (startIndex >= expressions.length) return;
|
|
2214
|
+
const newExprs = expressions.slice(startIndex);
|
|
2215
|
+
executedIndexRef.current = expressions.length;
|
|
2216
|
+
const sandbox = sandboxRef.current;
|
|
2217
|
+
if (sandboxReadyRef.current && sandbox) (async () => {
|
|
2218
|
+
for (const expr of newExprs) await sandbox.run(expr);
|
|
2219
|
+
})();
|
|
2220
|
+
else pendingQueueRef.current.push(...newExprs);
|
|
2221
|
+
}, [content.jsExpressions?.length]);
|
|
2222
|
+
const generationDone = content.generating === false;
|
|
2223
|
+
useEffect(() => {
|
|
2224
|
+
const sandbox = sandboxRef.current;
|
|
2225
|
+
if (!generationDone || !sandbox) return;
|
|
2226
|
+
let handled = false;
|
|
2227
|
+
const onMessage = (e) => {
|
|
2228
|
+
if (handled) return;
|
|
2229
|
+
if (e.source === sandbox.iframe.contentWindow && e.data?.type === "__ck_resize") {
|
|
2230
|
+
handled = true;
|
|
2231
|
+
setAutoHeight(e.data.height);
|
|
2232
|
+
window.removeEventListener("message", onMessage);
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
window.addEventListener("message", onMessage);
|
|
2236
|
+
const measureOnce = `
|
|
2237
|
+
(function() {
|
|
2238
|
+
var s = document.createElement('style');
|
|
2239
|
+
s.textContent = 'body { height: auto !important; min-height: 0 !important; }';
|
|
2240
|
+
document.head.appendChild(s);
|
|
2241
|
+
var h = document.body.scrollHeight;
|
|
2242
|
+
var cs = getComputedStyle(document.body);
|
|
2243
|
+
h += parseFloat(cs.marginTop) || 0;
|
|
2244
|
+
h += parseFloat(cs.marginBottom) || 0;
|
|
2245
|
+
s.remove();
|
|
2246
|
+
parent.postMessage({ type: "__ck_resize", height: Math.ceil(h) }, "*");
|
|
2247
|
+
})();
|
|
2248
|
+
`;
|
|
2249
|
+
if (sandboxReadyRef.current) sandbox.run(measureOnce);
|
|
2250
|
+
else pendingQueueRef.current.push(measureOnce);
|
|
2251
|
+
return () => {
|
|
2252
|
+
window.removeEventListener("message", onMessage);
|
|
2253
|
+
};
|
|
2254
|
+
}, [generationDone]);
|
|
2255
|
+
const height = autoHeight ?? initialHeight;
|
|
2256
|
+
const isGenerating = content.generating !== false;
|
|
2257
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2258
|
+
ref: containerRef,
|
|
2259
|
+
style: {
|
|
2260
|
+
position: "relative",
|
|
2261
|
+
width: "100%",
|
|
2262
|
+
height: `${height}px`,
|
|
2263
|
+
borderRadius: "8px",
|
|
2264
|
+
backgroundColor: hasVisibleSandbox ? "transparent" : "#f5f5f5",
|
|
2265
|
+
border: hasVisibleSandbox ? "none" : "1px solid #e0e0e0",
|
|
2266
|
+
display: hasVisibleSandbox ? "block" : "flex",
|
|
2267
|
+
alignItems: hasVisibleSandbox ? void 0 : "center",
|
|
2268
|
+
justifyContent: hasVisibleSandbox ? void 0 : "center",
|
|
2269
|
+
overflow: "hidden"
|
|
2270
|
+
},
|
|
2271
|
+
children: isGenerating && /* @__PURE__ */ jsxs("div", {
|
|
2272
|
+
style: {
|
|
2273
|
+
position: "absolute",
|
|
2274
|
+
inset: 0,
|
|
2275
|
+
zIndex: 10,
|
|
2276
|
+
pointerEvents: "all",
|
|
2277
|
+
backgroundColor: "rgba(255, 255, 255, 0.5)",
|
|
2278
|
+
display: "flex",
|
|
2279
|
+
alignItems: "center",
|
|
2280
|
+
justifyContent: "center"
|
|
2281
|
+
},
|
|
2282
|
+
children: [/* @__PURE__ */ jsxs("svg", {
|
|
2283
|
+
width: "48",
|
|
2284
|
+
height: "48",
|
|
2285
|
+
viewBox: "0 0 24 24",
|
|
2286
|
+
fill: "none",
|
|
2287
|
+
style: { animation: "ck-spin 1s linear infinite" },
|
|
2288
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
2289
|
+
cx: "12",
|
|
2290
|
+
cy: "12",
|
|
2291
|
+
r: "10",
|
|
2292
|
+
stroke: "#e0e0e0",
|
|
2293
|
+
strokeWidth: "3"
|
|
2294
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
2295
|
+
d: "M12 2a10 10 0 0 1 10 10",
|
|
2296
|
+
stroke: "#999",
|
|
2297
|
+
strokeWidth: "3",
|
|
2298
|
+
strokeLinecap: "round"
|
|
2299
|
+
})]
|
|
2300
|
+
}), /* @__PURE__ */ jsx("style", { children: `@keyframes ck-spin { to { transform: rotate(360deg) } }` })]
|
|
2301
|
+
})
|
|
2302
|
+
});
|
|
2303
|
+
}, (prev, next) => prev.content === next.content);
|
|
2304
|
+
/**
|
|
2305
|
+
* Frontend tool renderer for generateSandboxedUi.
|
|
2306
|
+
* Displays placeholder messages while the UI is being generated.
|
|
2307
|
+
*/
|
|
2308
|
+
const OpenGenerativeUIToolRenderer = function OpenGenerativeUIToolRenderer(props) {
|
|
2309
|
+
const [visibleMessageIndex, setVisibleMessageIndex] = useState(0);
|
|
2310
|
+
const prevMessageCountRef = useRef(0);
|
|
2311
|
+
const messages = props.args.placeholderMessages;
|
|
2312
|
+
useEffect(() => {
|
|
2313
|
+
if (!messages || messages.length === 0) return;
|
|
2314
|
+
if (messages.length !== prevMessageCountRef.current) {
|
|
2315
|
+
prevMessageCountRef.current = messages.length;
|
|
2316
|
+
setVisibleMessageIndex(messages.length - 1);
|
|
2317
|
+
}
|
|
2318
|
+
if (props.status === ToolCallStatus.Complete) return;
|
|
2319
|
+
const timer = setInterval(() => {
|
|
2320
|
+
setVisibleMessageIndex((i) => (i + 1) % messages.length);
|
|
2321
|
+
}, 5e3);
|
|
2322
|
+
return () => clearInterval(timer);
|
|
2323
|
+
}, [messages?.length, props.status]);
|
|
2324
|
+
if (props.status === ToolCallStatus.Complete) return null;
|
|
2325
|
+
if (!messages || messages.length === 0) return null;
|
|
2326
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2327
|
+
style: {
|
|
2328
|
+
padding: "8px 12px",
|
|
2329
|
+
color: "#999",
|
|
2330
|
+
fontSize: "14px"
|
|
2331
|
+
},
|
|
2332
|
+
children: messages[visibleMessageIndex] ?? messages[0]
|
|
2333
|
+
});
|
|
2334
|
+
};
|
|
2335
|
+
|
|
2336
|
+
//#endregion
|
|
2337
|
+
//#region src/v2/a2ui/A2UIMessageRenderer.tsx
|
|
2338
|
+
/**
|
|
2339
|
+
* The container key used to wrap A2UI operations for explicit detection.
|
|
2340
|
+
* Must match A2UI_OPERATIONS_KEY in @ag-ui/a2ui-middleware and copilotkit.a2ui (Python).
|
|
2341
|
+
*/
|
|
2342
|
+
const A2UI_OPERATIONS_KEY = "a2ui_operations";
|
|
2343
|
+
let initialized = false;
|
|
2344
|
+
function ensureInitialized() {
|
|
2345
|
+
if (!initialized) {
|
|
2346
|
+
initializeDefaultCatalog();
|
|
2347
|
+
injectStyles();
|
|
2348
|
+
initialized = true;
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
function createA2UIMessageRenderer(options) {
|
|
2352
|
+
const { theme, catalog, loadingComponent } = options;
|
|
2353
|
+
return {
|
|
2354
|
+
activityType: "a2ui-surface",
|
|
2355
|
+
content: z.any(),
|
|
2356
|
+
render: ({ content, agent }) => {
|
|
2357
|
+
ensureInitialized();
|
|
2358
|
+
const [operations, setOperations] = useState([]);
|
|
2359
|
+
const { copilotkit } = useCopilotKit();
|
|
2360
|
+
const lastContentRef = useRef(null);
|
|
2361
|
+
useEffect(() => {
|
|
2362
|
+
if (content === lastContentRef.current) return;
|
|
2363
|
+
lastContentRef.current = content;
|
|
2364
|
+
const incoming = content?.[A2UI_OPERATIONS_KEY];
|
|
2365
|
+
if (!content || !Array.isArray(incoming)) {
|
|
2366
|
+
setOperations([]);
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
setOperations(incoming);
|
|
2370
|
+
}, [content]);
|
|
2371
|
+
const groupedOperations = useMemo(() => {
|
|
2372
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2373
|
+
for (const operation of operations) {
|
|
2374
|
+
const surfaceId = getOperationSurfaceId(operation) ?? DEFAULT_SURFACE_ID;
|
|
2375
|
+
if (!groups.has(surfaceId)) groups.set(surfaceId, []);
|
|
2376
|
+
groups.get(surfaceId).push(operation);
|
|
2377
|
+
}
|
|
2378
|
+
return groups;
|
|
2379
|
+
}, [operations]);
|
|
2380
|
+
if (!groupedOperations.size) return /* @__PURE__ */ jsx(loadingComponent ?? DefaultA2UILoading, {});
|
|
2381
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2382
|
+
className: "cpk:flex cpk:min-h-0 cpk:flex-1 cpk:flex-col cpk:gap-6 cpk:overflow-auto cpk:py-6",
|
|
2383
|
+
children: Array.from(groupedOperations.entries()).map(([surfaceId, ops]) => /* @__PURE__ */ jsx(ReactSurfaceHost, {
|
|
2384
|
+
surfaceId,
|
|
2385
|
+
operations: ops,
|
|
2386
|
+
theme,
|
|
2387
|
+
agent,
|
|
2388
|
+
copilotkit,
|
|
2389
|
+
catalog
|
|
2390
|
+
}, surfaceId))
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* Renders a single A2UI surface using the React renderer.
|
|
2397
|
+
* Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
|
|
2398
|
+
*/
|
|
2399
|
+
function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog }) {
|
|
2400
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2401
|
+
className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
|
|
2402
|
+
children: /* @__PURE__ */ jsxs(A2UIProvider, {
|
|
2403
|
+
onAction: useCallback(async (message) => {
|
|
2404
|
+
if (!agent) return;
|
|
2405
|
+
message.userAction;
|
|
2406
|
+
try {
|
|
2407
|
+
copilotkit.setProperties({
|
|
2408
|
+
...copilotkit.properties ?? {},
|
|
2409
|
+
a2uiAction: message
|
|
2410
|
+
});
|
|
2411
|
+
await copilotkit.runAgent({ agent });
|
|
2412
|
+
} finally {
|
|
2413
|
+
if (copilotkit.properties) {
|
|
2414
|
+
const { a2uiAction, ...rest } = copilotkit.properties;
|
|
2415
|
+
copilotkit.setProperties(rest);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
}, [agent, copilotkit]),
|
|
2419
|
+
theme,
|
|
2420
|
+
catalog,
|
|
2421
|
+
children: [/* @__PURE__ */ jsx(SurfaceMessageProcessor, {
|
|
2422
|
+
surfaceId,
|
|
2423
|
+
operations
|
|
2424
|
+
}), /* @__PURE__ */ jsx(A2UISurfaceOrError, { surfaceId })]
|
|
2425
|
+
})
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* Renders the A2UI surface, or an error message if processing failed.
|
|
2430
|
+
* Must be a child of A2UIProvider to access the error state.
|
|
2431
|
+
*/
|
|
2432
|
+
function A2UISurfaceOrError({ surfaceId }) {
|
|
2433
|
+
const error = useA2UIError();
|
|
2434
|
+
if (error) return /* @__PURE__ */ jsxs("div", {
|
|
2435
|
+
className: "cpk:rounded-lg cpk:border cpk:border-red-200 cpk:bg-red-50 cpk:p-3 cpk:text-sm cpk:text-red-700",
|
|
2436
|
+
children: ["A2UI render error: ", error]
|
|
2437
|
+
});
|
|
2438
|
+
return /* @__PURE__ */ jsx(A2UIRenderer, {
|
|
2439
|
+
surfaceId,
|
|
2440
|
+
className: "cpk:flex cpk:flex-1"
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* Processes A2UI operations into the provider's message processor.
|
|
2445
|
+
* Must be a child of A2UIProvider to access the actions context.
|
|
2446
|
+
*/
|
|
2447
|
+
function SurfaceMessageProcessor({ surfaceId, operations }) {
|
|
2448
|
+
const { processMessages, getSurface } = useA2UIActions();
|
|
2449
|
+
const lastHashRef = useRef("");
|
|
2450
|
+
useEffect(() => {
|
|
2451
|
+
const hash = JSON.stringify(operations);
|
|
2452
|
+
if (hash === lastHashRef.current) return;
|
|
2453
|
+
lastHashRef.current = hash;
|
|
2454
|
+
processMessages(getSurface(surfaceId) ? operations.filter((op) => !op?.createSurface) : operations);
|
|
2455
|
+
}, [
|
|
2456
|
+
processMessages,
|
|
2457
|
+
getSurface,
|
|
2458
|
+
surfaceId,
|
|
2459
|
+
operations
|
|
2460
|
+
]);
|
|
2461
|
+
return null;
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* Default loading component shown while an A2UI surface is generating.
|
|
2465
|
+
* Displays an animated shimmer skeleton.
|
|
2466
|
+
*/
|
|
2467
|
+
function DefaultA2UILoading() {
|
|
2468
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2469
|
+
className: "cpk:flex cpk:flex-col cpk:gap-3 cpk:rounded-xl cpk:border cpk:border-gray-100 cpk:bg-gray-50/50 cpk:p-5",
|
|
2470
|
+
style: { minHeight: 120 },
|
|
2471
|
+
children: [
|
|
2472
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2473
|
+
className: "cpk:flex cpk:items-center cpk:gap-2",
|
|
2474
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2475
|
+
className: "cpk:h-3 cpk:w-3 cpk:rounded-full cpk:bg-gray-200",
|
|
2476
|
+
style: { animation: "cpk-a2ui-pulse 1.5s ease-in-out infinite" }
|
|
2477
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2478
|
+
className: "cpk:text-xs cpk:font-medium cpk:text-gray-400",
|
|
2479
|
+
children: "Generating UI..."
|
|
2480
|
+
})]
|
|
2481
|
+
}),
|
|
2482
|
+
/* @__PURE__ */ jsx("div", {
|
|
2483
|
+
className: "cpk:flex cpk:flex-col cpk:gap-2",
|
|
2484
|
+
children: [
|
|
2485
|
+
.8,
|
|
2486
|
+
.6,
|
|
2487
|
+
.4
|
|
2488
|
+
].map((width, i) => /* @__PURE__ */ jsx("div", {
|
|
2489
|
+
className: "cpk:h-3 cpk:rounded cpk:bg-gray-200/70",
|
|
2490
|
+
style: {
|
|
2491
|
+
width: `${width * 100}%`,
|
|
2492
|
+
animation: `cpk-a2ui-pulse 1.5s ease-in-out ${i * .15}s infinite`
|
|
2493
|
+
}
|
|
2494
|
+
}, i))
|
|
2495
|
+
}),
|
|
2496
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
2497
|
+
@keyframes cpk-a2ui-pulse {
|
|
2498
|
+
0%, 100% { opacity: 0.4; }
|
|
2499
|
+
50% { opacity: 1; }
|
|
2500
|
+
}
|
|
2501
|
+
` })
|
|
2502
|
+
]
|
|
2503
|
+
});
|
|
1993
2504
|
}
|
|
1994
2505
|
function getOperationSurfaceId(operation) {
|
|
1995
2506
|
if (!operation || typeof operation !== "object") return null;
|
|
1996
2507
|
if (typeof operation.surfaceId === "string") return operation.surfaceId;
|
|
1997
|
-
return operation?.
|
|
2508
|
+
return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
//#endregion
|
|
2512
|
+
//#region src/v2/types/defineToolCallRenderer.ts
|
|
2513
|
+
function defineToolCallRenderer(def) {
|
|
2514
|
+
const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
|
|
2515
|
+
return {
|
|
2516
|
+
name: def.name,
|
|
2517
|
+
args: argsSchema,
|
|
2518
|
+
render: def.render,
|
|
2519
|
+
...def.agentId ? { agentId: def.agentId } : {}
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
//#endregion
|
|
2524
|
+
//#region src/v2/a2ui/A2UIToolCallRenderer.tsx
|
|
2525
|
+
/**
|
|
2526
|
+
* Tool name used by the dynamic A2UI generation secondary LLM.
|
|
2527
|
+
* This renderer is auto-registered when A2UI is enabled.
|
|
2528
|
+
*/
|
|
2529
|
+
const RENDER_A2UI_TOOL_NAME = "render_a2ui";
|
|
2530
|
+
/**
|
|
2531
|
+
* Built-in progress indicator for dynamic A2UI generation.
|
|
2532
|
+
* Shows a skeleton wireframe that progressively reveals as tokens stream in.
|
|
2533
|
+
*
|
|
2534
|
+
* Registered automatically when A2UI is enabled. Users can override by
|
|
2535
|
+
* providing their own `useRenderTool({ name: "render_a2ui", ... })`.
|
|
2536
|
+
*/
|
|
2537
|
+
function A2UIProgressIndicator({ parameters }) {
|
|
2538
|
+
const lastRef = useRef({
|
|
2539
|
+
time: 0,
|
|
2540
|
+
tokens: 0
|
|
2541
|
+
});
|
|
2542
|
+
const now = Date.now();
|
|
2543
|
+
let { tokens } = lastRef.current;
|
|
2544
|
+
if (now - lastRef.current.time > 200) {
|
|
2545
|
+
const chars = JSON.stringify(parameters ?? {}).length;
|
|
2546
|
+
tokens = Math.round(chars / 4);
|
|
2547
|
+
lastRef.current = {
|
|
2548
|
+
time: now,
|
|
2549
|
+
tokens
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
const phase = tokens < 50 ? 0 : tokens < 200 ? 1 : tokens < 400 ? 2 : 3;
|
|
2553
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2554
|
+
style: {
|
|
2555
|
+
margin: "12px 0",
|
|
2556
|
+
maxWidth: 320
|
|
2557
|
+
},
|
|
2558
|
+
children: [
|
|
2559
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2560
|
+
style: {
|
|
2561
|
+
position: "relative",
|
|
2562
|
+
overflow: "hidden",
|
|
2563
|
+
borderRadius: 12,
|
|
2564
|
+
border: "1px solid rgba(228,228,231,0.8)",
|
|
2565
|
+
backgroundColor: "#fff",
|
|
2566
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
2567
|
+
padding: "16px 18px 14px"
|
|
2568
|
+
},
|
|
2569
|
+
children: [
|
|
2570
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2571
|
+
style: {
|
|
2572
|
+
display: "flex",
|
|
2573
|
+
alignItems: "center",
|
|
2574
|
+
gap: 8,
|
|
2575
|
+
marginBottom: 12
|
|
2576
|
+
},
|
|
2577
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2578
|
+
style: {
|
|
2579
|
+
display: "flex",
|
|
2580
|
+
gap: 4
|
|
2581
|
+
},
|
|
2582
|
+
children: [
|
|
2583
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2584
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2585
|
+
/* @__PURE__ */ jsx(Dot, {})
|
|
2586
|
+
]
|
|
2587
|
+
}), /* @__PURE__ */ jsx(Bar, {
|
|
2588
|
+
w: 64,
|
|
2589
|
+
h: 6,
|
|
2590
|
+
bg: "#e4e4e7",
|
|
2591
|
+
opacity: phase >= 1 ? 1 : .4,
|
|
2592
|
+
transition: "opacity 0.5s"
|
|
2593
|
+
})]
|
|
2594
|
+
}),
|
|
2595
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2596
|
+
style: {
|
|
2597
|
+
display: "grid",
|
|
2598
|
+
gap: 7
|
|
2599
|
+
},
|
|
2600
|
+
children: [
|
|
2601
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2602
|
+
show: phase >= 0,
|
|
2603
|
+
children: [/* @__PURE__ */ jsx(Bar, {
|
|
2604
|
+
w: 36,
|
|
2605
|
+
h: 7,
|
|
2606
|
+
bg: "rgba(147,197,253,0.7)",
|
|
2607
|
+
anim: 0
|
|
2608
|
+
}), /* @__PURE__ */ jsx(Bar, {
|
|
2609
|
+
w: 80,
|
|
2610
|
+
h: 7,
|
|
2611
|
+
bg: "rgba(219,234,254,0.8)",
|
|
2612
|
+
anim: .2
|
|
2613
|
+
})]
|
|
2614
|
+
}),
|
|
2615
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2616
|
+
show: phase >= 0,
|
|
2617
|
+
delay: .1,
|
|
2618
|
+
children: [
|
|
2619
|
+
/* @__PURE__ */ jsx(Spacer, {}),
|
|
2620
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2621
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2622
|
+
w: 100,
|
|
2623
|
+
h: 7,
|
|
2624
|
+
bg: "rgba(24,24,27,0.2)",
|
|
2625
|
+
anim: .3
|
|
2626
|
+
})
|
|
2627
|
+
]
|
|
2628
|
+
}),
|
|
2629
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2630
|
+
show: phase >= 1,
|
|
2631
|
+
delay: .15,
|
|
2632
|
+
children: [
|
|
2633
|
+
/* @__PURE__ */ jsx(Spacer, {}),
|
|
2634
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2635
|
+
w: 48,
|
|
2636
|
+
h: 7,
|
|
2637
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2638
|
+
anim: .1
|
|
2639
|
+
}),
|
|
2640
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2641
|
+
w: 40,
|
|
2642
|
+
h: 7,
|
|
2643
|
+
bg: "rgba(153,246,228,0.6)",
|
|
2644
|
+
anim: .5
|
|
2645
|
+
}),
|
|
2646
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2647
|
+
w: 56,
|
|
2648
|
+
h: 7,
|
|
2649
|
+
bg: "rgba(147,197,253,0.6)",
|
|
2650
|
+
anim: .3
|
|
2651
|
+
})
|
|
2652
|
+
]
|
|
2653
|
+
}),
|
|
2654
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2655
|
+
show: phase >= 1,
|
|
2656
|
+
delay: .2,
|
|
2657
|
+
children: [
|
|
2658
|
+
/* @__PURE__ */ jsx(Spacer, {}),
|
|
2659
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2660
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2661
|
+
w: 60,
|
|
2662
|
+
h: 7,
|
|
2663
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2664
|
+
anim: .4
|
|
2665
|
+
})
|
|
2666
|
+
]
|
|
2667
|
+
}),
|
|
2668
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2669
|
+
show: phase >= 2,
|
|
2670
|
+
delay: .25,
|
|
2671
|
+
children: [
|
|
2672
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2673
|
+
w: 40,
|
|
2674
|
+
h: 7,
|
|
2675
|
+
bg: "rgba(153,246,228,0.5)",
|
|
2676
|
+
anim: .2
|
|
2677
|
+
}),
|
|
2678
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2679
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2680
|
+
w: 48,
|
|
2681
|
+
h: 7,
|
|
2682
|
+
bg: "rgba(24,24,27,0.15)",
|
|
2683
|
+
anim: .6
|
|
2684
|
+
}),
|
|
2685
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2686
|
+
w: 64,
|
|
2687
|
+
h: 7,
|
|
2688
|
+
bg: "rgba(147,197,253,0.5)",
|
|
2689
|
+
anim: .1
|
|
2690
|
+
})
|
|
2691
|
+
]
|
|
2692
|
+
}),
|
|
2693
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2694
|
+
show: phase >= 2,
|
|
2695
|
+
delay: .3,
|
|
2696
|
+
children: [/* @__PURE__ */ jsx(Bar, {
|
|
2697
|
+
w: 36,
|
|
2698
|
+
h: 7,
|
|
2699
|
+
bg: "rgba(147,197,253,0.6)",
|
|
2700
|
+
anim: .5
|
|
2701
|
+
}), /* @__PURE__ */ jsx(Bar, {
|
|
2702
|
+
w: 36,
|
|
2703
|
+
h: 7,
|
|
2704
|
+
bg: "rgba(24,24,27,0.12)",
|
|
2705
|
+
anim: .7
|
|
2706
|
+
})]
|
|
2707
|
+
}),
|
|
2708
|
+
/* @__PURE__ */ jsxs(Row, {
|
|
2709
|
+
show: phase >= 3,
|
|
2710
|
+
delay: .35,
|
|
2711
|
+
children: [
|
|
2712
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2713
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2714
|
+
w: 44,
|
|
2715
|
+
h: 7,
|
|
2716
|
+
bg: "rgba(24,24,27,0.18)",
|
|
2717
|
+
anim: .3
|
|
2718
|
+
}),
|
|
2719
|
+
/* @__PURE__ */ jsx(Dot, {}),
|
|
2720
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2721
|
+
w: 56,
|
|
2722
|
+
h: 7,
|
|
2723
|
+
bg: "rgba(153,246,228,0.5)",
|
|
2724
|
+
anim: .8
|
|
2725
|
+
}),
|
|
2726
|
+
/* @__PURE__ */ jsx(Bar, {
|
|
2727
|
+
w: 48,
|
|
2728
|
+
h: 7,
|
|
2729
|
+
bg: "rgba(147,197,253,0.5)",
|
|
2730
|
+
anim: .4
|
|
2731
|
+
})
|
|
2732
|
+
]
|
|
2733
|
+
})
|
|
2734
|
+
]
|
|
2735
|
+
}),
|
|
2736
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
2737
|
+
pointerEvents: "none",
|
|
2738
|
+
position: "absolute",
|
|
2739
|
+
inset: 0,
|
|
2740
|
+
background: "linear-gradient(105deg, transparent 0%, transparent 40%, rgba(255,255,255,0.6) 50%, transparent 60%, transparent 100%)",
|
|
2741
|
+
backgroundSize: "250% 100%",
|
|
2742
|
+
animation: "cpk-a2ui-sweep 3s ease-in-out infinite"
|
|
2743
|
+
} })
|
|
2744
|
+
]
|
|
2745
|
+
}),
|
|
2746
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2747
|
+
style: {
|
|
2748
|
+
display: "flex",
|
|
2749
|
+
alignItems: "center",
|
|
2750
|
+
justifyContent: "center",
|
|
2751
|
+
gap: 8,
|
|
2752
|
+
marginTop: 8
|
|
2753
|
+
},
|
|
2754
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2755
|
+
style: {
|
|
2756
|
+
fontSize: 12,
|
|
2757
|
+
color: "#a1a1aa",
|
|
2758
|
+
letterSpacing: "0.025em"
|
|
2759
|
+
},
|
|
2760
|
+
children: "Building interface"
|
|
2761
|
+
}), tokens > 0 && /* @__PURE__ */ jsxs("span", {
|
|
2762
|
+
style: {
|
|
2763
|
+
fontSize: 11,
|
|
2764
|
+
color: "#d4d4d8",
|
|
2765
|
+
fontVariantNumeric: "tabular-nums"
|
|
2766
|
+
},
|
|
2767
|
+
children: [
|
|
2768
|
+
"~",
|
|
2769
|
+
tokens.toLocaleString(),
|
|
2770
|
+
" tokens"
|
|
2771
|
+
]
|
|
2772
|
+
})]
|
|
2773
|
+
}),
|
|
2774
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
2775
|
+
@keyframes cpk-a2ui-fade {
|
|
2776
|
+
0%, 100% { opacity: 1; }
|
|
2777
|
+
50% { opacity: 0.5; }
|
|
2778
|
+
}
|
|
2779
|
+
@keyframes cpk-a2ui-sweep {
|
|
2780
|
+
0% { background-position: 250% 0; }
|
|
2781
|
+
100% { background-position: -250% 0; }
|
|
2782
|
+
}
|
|
2783
|
+
` })
|
|
2784
|
+
]
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
function Dot() {
|
|
2788
|
+
return /* @__PURE__ */ jsx("div", { style: {
|
|
2789
|
+
width: 7,
|
|
2790
|
+
height: 7,
|
|
2791
|
+
borderRadius: "50%",
|
|
2792
|
+
backgroundColor: "#d4d4d8",
|
|
2793
|
+
flexShrink: 0
|
|
2794
|
+
} });
|
|
2795
|
+
}
|
|
2796
|
+
function Spacer() {
|
|
2797
|
+
return /* @__PURE__ */ jsx("div", { style: { width: 12 } });
|
|
2798
|
+
}
|
|
2799
|
+
function Bar({ w, h, bg, anim, opacity, transition }) {
|
|
2800
|
+
return /* @__PURE__ */ jsx("div", { style: {
|
|
2801
|
+
width: w,
|
|
2802
|
+
height: h,
|
|
2803
|
+
borderRadius: 9999,
|
|
2804
|
+
backgroundColor: bg,
|
|
2805
|
+
...anim !== void 0 ? { animation: `cpk-a2ui-fade 2.4s ease-in-out ${anim}s infinite` } : {},
|
|
2806
|
+
...opacity !== void 0 ? { opacity } : {},
|
|
2807
|
+
...transition ? { transition } : {}
|
|
2808
|
+
} });
|
|
2809
|
+
}
|
|
2810
|
+
function Row({ children, show, delay = 0 }) {
|
|
2811
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2812
|
+
style: {
|
|
2813
|
+
display: "flex",
|
|
2814
|
+
alignItems: "center",
|
|
2815
|
+
gap: 6,
|
|
2816
|
+
opacity: show ? 1 : 0,
|
|
2817
|
+
transition: `opacity 0.4s ${delay}s`
|
|
2818
|
+
},
|
|
2819
|
+
children
|
|
2820
|
+
});
|
|
1998
2821
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2822
|
+
/**
|
|
2823
|
+
* Registers the built-in `render_a2ui` tool call renderer via the props-based
|
|
2824
|
+
* `setRenderToolCalls` mechanism (not `useRenderTool`).
|
|
2825
|
+
*
|
|
2826
|
+
* This ensures user-registered `useRenderTool({ name: "render_a2ui", ... })`
|
|
2827
|
+
* hooks automatically override the built-in, since the merge logic in
|
|
2828
|
+
* react-core.ts gives hook-based entries priority over prop-based entries.
|
|
2829
|
+
*/
|
|
2830
|
+
function A2UIBuiltInToolCallRenderer() {
|
|
2831
|
+
const { copilotkit } = useCopilotKit();
|
|
2832
|
+
useEffect(() => {
|
|
2833
|
+
const renderer = defineToolCallRenderer({
|
|
2834
|
+
name: RENDER_A2UI_TOOL_NAME,
|
|
2835
|
+
args: z.any(),
|
|
2836
|
+
render: ({ status, args: parameters }) => {
|
|
2837
|
+
if (status === "complete") return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2838
|
+
const params = parameters;
|
|
2839
|
+
const items = params?.items;
|
|
2840
|
+
if (Array.isArray(items) && items.length > 0) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2841
|
+
const components = params?.components;
|
|
2842
|
+
if (Array.isArray(components) && components.length > 2) return /* @__PURE__ */ jsx(Fragment$1, {});
|
|
2843
|
+
return /* @__PURE__ */ jsx(A2UIProgressIndicator, { parameters });
|
|
2844
|
+
}
|
|
2845
|
+
});
|
|
2846
|
+
const existing = copilotkit._renderToolCalls ?? [];
|
|
2847
|
+
copilotkit.setRenderToolCalls([...existing.filter((rc) => rc.name !== RENDER_A2UI_TOOL_NAME), renderer]);
|
|
2848
|
+
}, [copilotkit]);
|
|
2849
|
+
return null;
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
//#endregion
|
|
2853
|
+
//#region src/v2/hooks/use-agent-context.tsx
|
|
2854
|
+
function useAgentContext(context) {
|
|
2855
|
+
const { description, value } = context;
|
|
2856
|
+
const { copilotkit } = useCopilotKit();
|
|
2857
|
+
const stringValue = useMemo(() => {
|
|
2858
|
+
if (typeof value === "string") return value;
|
|
2859
|
+
return JSON.stringify(value);
|
|
2860
|
+
}, [value]);
|
|
2861
|
+
useLayoutEffect(() => {
|
|
2862
|
+
if (!copilotkit) return;
|
|
2863
|
+
const id = copilotkit.addContext({
|
|
2864
|
+
description,
|
|
2865
|
+
value: stringValue
|
|
2866
|
+
});
|
|
2867
|
+
return () => {
|
|
2868
|
+
copilotkit.removeContext(id);
|
|
2869
|
+
};
|
|
2870
|
+
}, [
|
|
2871
|
+
description,
|
|
2872
|
+
stringValue,
|
|
2873
|
+
copilotkit
|
|
2874
|
+
]);
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
//#endregion
|
|
2878
|
+
//#region src/v2/a2ui/A2UICatalogContext.tsx
|
|
2879
|
+
/**
|
|
2880
|
+
* Renders agent context describing the available A2UI catalog and custom components.
|
|
2881
|
+
* Only mount this component when A2UI is enabled.
|
|
2882
|
+
*
|
|
2883
|
+
* When `includeSchema` is true, the full component schemas (JSON Schema) are also
|
|
2884
|
+
* sent as context using the same description key as the A2UI middleware, so the
|
|
2885
|
+
* middleware can optionally overwrite it with a server-side schema.
|
|
2886
|
+
*/
|
|
2887
|
+
function A2UICatalogContext({ catalog, includeSchema }) {
|
|
2888
|
+
useAgentContext({
|
|
2889
|
+
description: "A2UI catalog capabilities: available catalog IDs and custom component definitions the client can render.",
|
|
2890
|
+
value: buildCatalogContextValue(catalog)
|
|
2891
|
+
});
|
|
2892
|
+
const { copilotkit } = useCopilotKit();
|
|
2893
|
+
const schemaValue = useMemo(() => includeSchema !== false ? JSON.stringify(extractCatalogComponentSchemas(catalog)) : null, [catalog, includeSchema]);
|
|
2894
|
+
useLayoutEffect(() => {
|
|
2895
|
+
if (!copilotkit || !schemaValue) return;
|
|
2896
|
+
const ids = [];
|
|
2897
|
+
ids.push(copilotkit.addContext({
|
|
2898
|
+
description: A2UI_SCHEMA_CONTEXT_DESCRIPTION,
|
|
2899
|
+
value: schemaValue
|
|
2900
|
+
}));
|
|
2901
|
+
ids.push(copilotkit.addContext({
|
|
2902
|
+
description: "A2UI generation guidelines — protocol rules, tool arguments, path rules, data model format, and form/two-way-binding instructions.",
|
|
2903
|
+
value: A2UI_DEFAULT_GENERATION_GUIDELINES
|
|
2904
|
+
}));
|
|
2905
|
+
ids.push(copilotkit.addContext({
|
|
2906
|
+
description: "A2UI design guidelines — visual design rules, component hierarchy tips, and action handler patterns.",
|
|
2907
|
+
value: A2UI_DEFAULT_DESIGN_GUIDELINES
|
|
2908
|
+
}));
|
|
2909
|
+
return () => {
|
|
2910
|
+
for (const id of ids) copilotkit.removeContext(id);
|
|
2911
|
+
};
|
|
2912
|
+
}, [copilotkit, schemaValue]);
|
|
2913
|
+
return null;
|
|
2005
2914
|
}
|
|
2006
2915
|
|
|
2007
2916
|
//#endregion
|
|
@@ -2104,6 +3013,17 @@ var CopilotKitCoreReact = class extends CopilotKitCore {
|
|
|
2104
3013
|
//#region src/v2/providers/CopilotKitProvider.tsx
|
|
2105
3014
|
const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
|
|
2106
3015
|
const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
|
|
3016
|
+
const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
|
|
3017
|
+
|
|
3018
|
+
- Use a minimal, flat aesthetic. Avoid drop shadows and gradients — rely on subtle borders (1px solid, light gray like #e5e7eb) to define surfaces.
|
|
3019
|
+
- Neutral base palette: white backgrounds, zinc/slate gray text (#09090b for headings, #71717a for secondary text). One accent color for interactive elements.
|
|
3020
|
+
- Use system font stacks (system-ui, -apple-system, sans-serif) at readable sizes (14px body, 600 weight for headings). Tight line-heights.
|
|
3021
|
+
- Small, consistent border-radius (6–8px). Cards and containers use border, not shadow, for separation.
|
|
3022
|
+
- Buttons: solid fill for primary (dark bg, white text), outline for secondary (border + transparent bg). Subtle hover state (slight opacity or background shift).
|
|
3023
|
+
- Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
|
|
3024
|
+
- Minimal transitions (150ms) for hover/focus states only. No decorative animations.
|
|
3025
|
+
- Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
|
|
3026
|
+
const GENERATE_SANDBOXED_UI_DESCRIPTION = "Generate sandboxed UI. IMPORTANT: The generated code runs in a sandboxed iframe WITHOUT same-origin access. Do NOT use localStorage, sessionStorage, document.cookie, IndexedDB, or fetch/XMLHttpRequest to same-origin URLs. To communicate with the host application, use Websandbox.connection.remote.<functionName>(args) which returns a Promise.\n\nYou CAN use external libraries from CDNs by including <script> or <link> tags in the HTML <head> (e.g., Chart.js, D3, Three.js, x-data-spreadsheet, etc.). CDN resources load normally inside the sandbox.\n\nPARAMETER ORDER IS CRITICAL — generate parameters in exactly this order:\n1. initialHeight + placeholderMessages (shown to user while generating)\n2. css (all styles FIRST — the user sees a placeholder until CSS is complete)\n3. html (streams in live — the user watches the UI build as HTML is generated)\n4. jsFunctions (reusable helper functions)\n5. jsExpressions (applied one-by-one — the user sees each expression take effect)";
|
|
2107
3027
|
const CopilotKitContext = createContext({
|
|
2108
3028
|
copilotkit: null,
|
|
2109
3029
|
executingToolCallIds: /* @__PURE__ */ new Set()
|
|
@@ -2119,9 +3039,11 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
|
|
|
2119
3039
|
}, [value, warningMessage]);
|
|
2120
3040
|
return value;
|
|
2121
3041
|
}
|
|
2122
|
-
const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, showDevConsole = false, useSingleEndpoint
|
|
3042
|
+
const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, publicApiKey, publicLicenseKey, licenseToken, properties = {}, agents__unsafe_dev_only: agents = {}, selfManagedAgents = {}, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, showDevConsole = false, useSingleEndpoint, onError, a2ui, defaultThrottleMs, inspectorDefaultAnchor }) => {
|
|
2123
3043
|
const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
|
|
2124
3044
|
const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = useState(false);
|
|
3045
|
+
const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = useState(false);
|
|
3046
|
+
const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
|
|
2125
3047
|
const [runtimeLicenseStatus, setRuntimeLicenseStatus] = useState(void 0);
|
|
2126
3048
|
useEffect(() => {
|
|
2127
3049
|
if (typeof window === "undefined") return;
|
|
@@ -2147,9 +3069,22 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2147
3069
|
content: MCPAppsActivityContentSchema,
|
|
2148
3070
|
render: MCPAppsActivityRenderer
|
|
2149
3071
|
}];
|
|
2150
|
-
if (
|
|
3072
|
+
if (openGenUIActive) renderers.push({
|
|
3073
|
+
activityType: OpenGenerativeUIActivityType,
|
|
3074
|
+
content: OpenGenerativeUIContentSchema,
|
|
3075
|
+
render: OpenGenerativeUIActivityRenderer
|
|
3076
|
+
});
|
|
3077
|
+
if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
|
|
3078
|
+
theme: a2ui?.theme ?? viewerTheme,
|
|
3079
|
+
catalog: a2ui?.catalog,
|
|
3080
|
+
loadingComponent: a2ui?.loadingComponent
|
|
3081
|
+
}));
|
|
2151
3082
|
return renderers;
|
|
2152
|
-
}, [
|
|
3083
|
+
}, [
|
|
3084
|
+
runtimeA2UIEnabled,
|
|
3085
|
+
openGenUIActive,
|
|
3086
|
+
a2ui
|
|
3087
|
+
]);
|
|
2153
3088
|
const allActivityRenderers = useMemo(() => {
|
|
2154
3089
|
return [...renderActivityMessagesList, ...builtInActivityRenderers];
|
|
2155
3090
|
}, [renderActivityMessagesList, builtInActivityRenderers]);
|
|
@@ -2175,6 +3110,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2175
3110
|
const chatApiEndpoint = runtimeUrl ?? (resolvedPublicKey ? COPILOT_CLOUD_CHAT_URL$1 : void 0);
|
|
2176
3111
|
const frontendToolsList = useStableArrayProp(frontendTools, "frontendTools must be a stable array. If you want to dynamically add or remove tools, use `useFrontendTool` instead.");
|
|
2177
3112
|
const humanInTheLoopList = useStableArrayProp(humanInTheLoop, "humanInTheLoop must be a stable array. If you want to dynamically add or remove human-in-the-loop tools, use `useHumanInTheLoop` instead.");
|
|
3113
|
+
const sandboxFunctionsList = useStableArrayProp(openGenerativeUI?.sandboxFunctions, "openGenerativeUI.sandboxFunctions must be a stable array.");
|
|
2178
3114
|
const processedHumanInTheLoopTools = useMemo(() => {
|
|
2179
3115
|
const processedTools = [];
|
|
2180
3116
|
const processedRenderToolCalls = [];
|
|
@@ -2205,15 +3141,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2205
3141
|
renderToolCalls: processedRenderToolCalls
|
|
2206
3142
|
};
|
|
2207
3143
|
}, [humanInTheLoopList]);
|
|
3144
|
+
const builtInFrontendTools = useMemo(() => {
|
|
3145
|
+
if (!openGenUIActive) return [];
|
|
3146
|
+
return [{
|
|
3147
|
+
name: "generateSandboxedUi",
|
|
3148
|
+
description: GENERATE_SANDBOXED_UI_DESCRIPTION,
|
|
3149
|
+
parameters: GenerateSandboxedUiArgsSchema,
|
|
3150
|
+
handler: async () => "UI generated",
|
|
3151
|
+
followUp: true,
|
|
3152
|
+
render: OpenGenerativeUIToolRenderer
|
|
3153
|
+
}];
|
|
3154
|
+
}, [openGenUIActive]);
|
|
2208
3155
|
const allTools = useMemo(() => {
|
|
2209
3156
|
const tools = [];
|
|
2210
3157
|
tools.push(...frontendToolsList);
|
|
3158
|
+
tools.push(...builtInFrontendTools);
|
|
2211
3159
|
tools.push(...processedHumanInTheLoopTools.tools);
|
|
2212
3160
|
return tools;
|
|
2213
|
-
}, [
|
|
3161
|
+
}, [
|
|
3162
|
+
frontendToolsList,
|
|
3163
|
+
builtInFrontendTools,
|
|
3164
|
+
processedHumanInTheLoopTools
|
|
3165
|
+
]);
|
|
2214
3166
|
const allRenderToolCalls = useMemo(() => {
|
|
2215
3167
|
const combined = [...renderToolCallsList];
|
|
2216
|
-
frontendToolsList.forEach((tool) => {
|
|
3168
|
+
[...frontendToolsList, ...builtInFrontendTools].forEach((tool) => {
|
|
2217
3169
|
if (tool.render) {
|
|
2218
3170
|
const args = tool.parameters || (tool.name === "*" ? z.any() : void 0);
|
|
2219
3171
|
if (args) combined.push({
|
|
@@ -2228,25 +3180,31 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2228
3180
|
}, [
|
|
2229
3181
|
renderToolCallsList,
|
|
2230
3182
|
frontendToolsList,
|
|
3183
|
+
builtInFrontendTools,
|
|
2231
3184
|
processedHumanInTheLoopTools
|
|
2232
3185
|
]);
|
|
2233
3186
|
const copilotkitRef = useRef(null);
|
|
2234
|
-
if (copilotkitRef.current === null)
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
3187
|
+
if (copilotkitRef.current === null) {
|
|
3188
|
+
copilotkitRef.current = new CopilotKitCoreReact({
|
|
3189
|
+
runtimeUrl: chatApiEndpoint,
|
|
3190
|
+
runtimeTransport: useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto",
|
|
3191
|
+
headers: mergedHeaders,
|
|
3192
|
+
credentials,
|
|
3193
|
+
properties,
|
|
3194
|
+
agents__unsafe_dev_only: mergedAgents,
|
|
3195
|
+
tools: allTools,
|
|
3196
|
+
renderToolCalls: allRenderToolCalls,
|
|
3197
|
+
renderActivityMessages: allActivityRenderers,
|
|
3198
|
+
renderCustomMessages: renderCustomMessagesList
|
|
3199
|
+
});
|
|
3200
|
+
if (defaultThrottleMs !== void 0) copilotkitRef.current.setDefaultThrottleMs(defaultThrottleMs);
|
|
3201
|
+
}
|
|
2246
3202
|
const copilotkit = copilotkitRef.current;
|
|
2247
3203
|
useEffect(() => {
|
|
3204
|
+
setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
|
|
2248
3205
|
const subscription = copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
|
|
2249
3206
|
setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
|
|
3207
|
+
setRuntimeOpenGenUIEnabled(copilotkit.openGenerativeUIEnabled);
|
|
2250
3208
|
setRuntimeLicenseStatus(copilotkit.licenseStatus);
|
|
2251
3209
|
} });
|
|
2252
3210
|
return () => {
|
|
@@ -2305,7 +3263,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2305
3263
|
}, [copilotkit]);
|
|
2306
3264
|
useEffect(() => {
|
|
2307
3265
|
copilotkit.setRuntimeUrl(chatApiEndpoint);
|
|
2308
|
-
copilotkit.setRuntimeTransport(useSingleEndpoint ? "single" : "rest");
|
|
3266
|
+
copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
|
|
2309
3267
|
copilotkit.setHeaders(mergedHeaders);
|
|
2310
3268
|
copilotkit.setCredentials(credentials);
|
|
2311
3269
|
copilotkit.setProperties(properties);
|
|
@@ -2339,23 +3297,75 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers = {}, credentials, p
|
|
|
2339
3297
|
useEffect(() => {
|
|
2340
3298
|
didMountRef.current = true;
|
|
2341
3299
|
}, []);
|
|
3300
|
+
useEffect(() => {
|
|
3301
|
+
if (defaultThrottleMs !== void 0 && (!Number.isFinite(defaultThrottleMs) || defaultThrottleMs < 0)) console.error(`CopilotKitProvider: defaultThrottleMs must be a non-negative finite number, got ${defaultThrottleMs}. useAgent hooks without an explicit throttleMs will fall back to unthrottled.`);
|
|
3302
|
+
copilotkit.setDefaultThrottleMs(defaultThrottleMs);
|
|
3303
|
+
}, [copilotkit, defaultThrottleMs]);
|
|
3304
|
+
const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
|
|
3305
|
+
useLayoutEffect(() => {
|
|
3306
|
+
if (!copilotkit || !openGenUIActive) return;
|
|
3307
|
+
const id = copilotkit.addContext({
|
|
3308
|
+
description: "Design guidelines for the generateSandboxedUi tool. Follow these when building UI.",
|
|
3309
|
+
value: designSkill
|
|
3310
|
+
});
|
|
3311
|
+
return () => {
|
|
3312
|
+
copilotkit.removeContext(id);
|
|
3313
|
+
};
|
|
3314
|
+
}, [
|
|
3315
|
+
copilotkit,
|
|
3316
|
+
designSkill,
|
|
3317
|
+
openGenUIActive
|
|
3318
|
+
]);
|
|
3319
|
+
const sandboxFunctionsDescriptors = useMemo(() => {
|
|
3320
|
+
if (sandboxFunctionsList.length === 0) return null;
|
|
3321
|
+
return JSON.stringify(sandboxFunctionsList.map((fn) => ({
|
|
3322
|
+
name: fn.name,
|
|
3323
|
+
description: fn.description,
|
|
3324
|
+
parameters: schemaToJsonSchema(fn.parameters, { zodToJsonSchema })
|
|
3325
|
+
})));
|
|
3326
|
+
}, [sandboxFunctionsList]);
|
|
3327
|
+
useLayoutEffect(() => {
|
|
3328
|
+
if (!copilotkit || !sandboxFunctionsDescriptors || !openGenUIActive) return;
|
|
3329
|
+
const id = copilotkit.addContext({
|
|
3330
|
+
description: "Sandbox functions available in generated sandboxed UI code. Call via: await Websandbox.connection.remote.<functionName>(args)",
|
|
3331
|
+
value: sandboxFunctionsDescriptors
|
|
3332
|
+
});
|
|
3333
|
+
return () => {
|
|
3334
|
+
copilotkit.removeContext(id);
|
|
3335
|
+
};
|
|
3336
|
+
}, [
|
|
3337
|
+
copilotkit,
|
|
3338
|
+
sandboxFunctionsDescriptors,
|
|
3339
|
+
openGenUIActive
|
|
3340
|
+
]);
|
|
2342
3341
|
const contextValue = useMemo(() => ({
|
|
2343
3342
|
copilotkit,
|
|
2344
3343
|
executingToolCallIds
|
|
2345
3344
|
}), [copilotkit, executingToolCallIds]);
|
|
2346
3345
|
const licenseContextValue = useMemo(() => createLicenseContextValue(null), []);
|
|
2347
|
-
return /* @__PURE__ */ jsx(
|
|
2348
|
-
value:
|
|
2349
|
-
children: /* @__PURE__ */
|
|
2350
|
-
value:
|
|
2351
|
-
children:
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
3346
|
+
return /* @__PURE__ */ jsx(SandboxFunctionsContext.Provider, {
|
|
3347
|
+
value: sandboxFunctionsList,
|
|
3348
|
+
children: /* @__PURE__ */ jsx(CopilotKitContext.Provider, {
|
|
3349
|
+
value: contextValue,
|
|
3350
|
+
children: /* @__PURE__ */ jsxs(LicenseContext.Provider, {
|
|
3351
|
+
value: licenseContextValue,
|
|
3352
|
+
children: [
|
|
3353
|
+
runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UIBuiltInToolCallRenderer, {}),
|
|
3354
|
+
runtimeA2UIEnabled && /* @__PURE__ */ jsx(A2UICatalogContext, {
|
|
3355
|
+
catalog: a2ui?.catalog,
|
|
3356
|
+
includeSchema: a2ui?.includeSchema
|
|
3357
|
+
}),
|
|
3358
|
+
children,
|
|
3359
|
+
shouldRenderInspector ? /* @__PURE__ */ jsx(CopilotKitInspector, {
|
|
3360
|
+
core: copilotkit,
|
|
3361
|
+
defaultAnchor: inspectorDefaultAnchor
|
|
3362
|
+
}) : null,
|
|
3363
|
+
runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "no_license" }),
|
|
3364
|
+
runtimeLicenseStatus === "expired" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expired" }),
|
|
3365
|
+
runtimeLicenseStatus === "invalid" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "invalid" }),
|
|
3366
|
+
runtimeLicenseStatus === "expiring" && /* @__PURE__ */ jsx(LicenseWarningBanner, { type: "expiring" })
|
|
3367
|
+
]
|
|
3368
|
+
})
|
|
2359
3369
|
})
|
|
2360
3370
|
});
|
|
2361
3371
|
};
|
|
@@ -2370,75 +3380,258 @@ const useCopilotKit = () => {
|
|
|
2370
3380
|
return () => {
|
|
2371
3381
|
subscription.unsubscribe();
|
|
2372
3382
|
};
|
|
2373
|
-
}, []);
|
|
2374
|
-
return context;
|
|
2375
|
-
};
|
|
2376
|
-
|
|
2377
|
-
//#endregion
|
|
2378
|
-
//#region src/v2/hooks/use-render-tool-call.tsx
|
|
2379
|
-
/**
|
|
2380
|
-
* Memoized component that renders a single tool call.
|
|
2381
|
-
* This prevents unnecessary re-renders when parent components update
|
|
2382
|
-
* but the tool call data hasn't changed.
|
|
2383
|
-
*/
|
|
2384
|
-
const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
|
|
2385
|
-
const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
|
|
2386
|
-
const toolName = toolCall.function.name;
|
|
2387
|
-
if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
|
|
2388
|
-
name: toolName,
|
|
2389
|
-
args,
|
|
2390
|
-
status: ToolCallStatus.Complete,
|
|
2391
|
-
result: toolMessage.content
|
|
2392
|
-
});
|
|
2393
|
-
else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
|
|
2394
|
-
name: toolName,
|
|
2395
|
-
args,
|
|
2396
|
-
status: ToolCallStatus.Executing,
|
|
2397
|
-
result: void 0
|
|
2398
|
-
});
|
|
2399
|
-
else return /* @__PURE__ */ jsx(RenderComponent, {
|
|
2400
|
-
name: toolName,
|
|
2401
|
-
args,
|
|
2402
|
-
status: ToolCallStatus.InProgress,
|
|
2403
|
-
result: void 0
|
|
2404
|
-
});
|
|
2405
|
-
}, (prevProps, nextProps) => {
|
|
2406
|
-
if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
|
|
2407
|
-
if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
|
|
2408
|
-
if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
|
|
2409
|
-
if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
|
|
2410
|
-
if (prevProps.isExecuting !== nextProps.isExecuting) return false;
|
|
2411
|
-
if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
|
|
2412
|
-
return true;
|
|
2413
|
-
});
|
|
2414
|
-
/**
|
|
2415
|
-
* Hook that returns a function to render tool calls based on the render functions
|
|
2416
|
-
* defined in CopilotKitProvider.
|
|
2417
|
-
*
|
|
2418
|
-
* @returns A function that takes a tool call and optional tool message and returns the rendered component
|
|
2419
|
-
*/
|
|
2420
|
-
function useRenderToolCall() {
|
|
2421
|
-
const { copilotkit, executingToolCallIds } = useCopilotKit();
|
|
2422
|
-
const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
|
|
2423
|
-
const renderToolCalls = useSyncExternalStore((callback) => {
|
|
2424
|
-
return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
|
|
2425
|
-
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
2426
|
-
return useCallback(({ toolCall, toolMessage }) => {
|
|
2427
|
-
const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
|
|
2428
|
-
const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
|
|
2429
|
-
if (!renderConfig) return null;
|
|
2430
|
-
const RenderComponent = renderConfig.render;
|
|
2431
|
-
return /* @__PURE__ */ jsx(ToolCallRenderer, {
|
|
2432
|
-
toolCall,
|
|
2433
|
-
toolMessage,
|
|
2434
|
-
RenderComponent,
|
|
2435
|
-
isExecuting: executingToolCallIds.has(toolCall.id)
|
|
2436
|
-
}, toolCall.id);
|
|
3383
|
+
}, []);
|
|
3384
|
+
return context;
|
|
3385
|
+
};
|
|
3386
|
+
|
|
3387
|
+
//#endregion
|
|
3388
|
+
//#region src/v2/hooks/use-render-tool-call.tsx
|
|
3389
|
+
/**
|
|
3390
|
+
* Memoized component that renders a single tool call.
|
|
3391
|
+
* This prevents unnecessary re-renders when parent components update
|
|
3392
|
+
* but the tool call data hasn't changed.
|
|
3393
|
+
*/
|
|
3394
|
+
const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
|
|
3395
|
+
const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
|
|
3396
|
+
const toolName = toolCall.function.name;
|
|
3397
|
+
if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
|
|
3398
|
+
name: toolName,
|
|
3399
|
+
args,
|
|
3400
|
+
status: ToolCallStatus.Complete,
|
|
3401
|
+
result: toolMessage.content
|
|
3402
|
+
});
|
|
3403
|
+
else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
|
|
3404
|
+
name: toolName,
|
|
3405
|
+
args,
|
|
3406
|
+
status: ToolCallStatus.Executing,
|
|
3407
|
+
result: void 0
|
|
3408
|
+
});
|
|
3409
|
+
else return /* @__PURE__ */ jsx(RenderComponent, {
|
|
3410
|
+
name: toolName,
|
|
3411
|
+
args,
|
|
3412
|
+
status: ToolCallStatus.InProgress,
|
|
3413
|
+
result: void 0
|
|
3414
|
+
});
|
|
3415
|
+
}, (prevProps, nextProps) => {
|
|
3416
|
+
if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
|
|
3417
|
+
if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
|
|
3418
|
+
if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
|
|
3419
|
+
if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
|
|
3420
|
+
if (prevProps.isExecuting !== nextProps.isExecuting) return false;
|
|
3421
|
+
if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
|
|
3422
|
+
return true;
|
|
3423
|
+
});
|
|
3424
|
+
/**
|
|
3425
|
+
* Hook that returns a function to render tool calls based on the render functions
|
|
3426
|
+
* defined in CopilotKitProvider.
|
|
3427
|
+
*
|
|
3428
|
+
* @returns A function that takes a tool call and optional tool message and returns the rendered component
|
|
3429
|
+
*/
|
|
3430
|
+
function useRenderToolCall() {
|
|
3431
|
+
const { copilotkit, executingToolCallIds } = useCopilotKit();
|
|
3432
|
+
const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
|
|
3433
|
+
const renderToolCalls = useSyncExternalStore((callback) => {
|
|
3434
|
+
return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
|
|
3435
|
+
}, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
|
|
3436
|
+
return useCallback(({ toolCall, toolMessage }) => {
|
|
3437
|
+
const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
|
|
3438
|
+
const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
|
|
3439
|
+
if (!renderConfig) return null;
|
|
3440
|
+
const RenderComponent = renderConfig.render;
|
|
3441
|
+
return /* @__PURE__ */ jsx(ToolCallRenderer, {
|
|
3442
|
+
toolCall,
|
|
3443
|
+
toolMessage,
|
|
3444
|
+
RenderComponent,
|
|
3445
|
+
isExecuting: executingToolCallIds.has(toolCall.id)
|
|
3446
|
+
}, toolCall.id);
|
|
3447
|
+
}, [
|
|
3448
|
+
renderToolCalls,
|
|
3449
|
+
executingToolCallIds,
|
|
3450
|
+
agentId
|
|
3451
|
+
]);
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3454
|
+
//#endregion
|
|
3455
|
+
//#region src/v2/hooks/use-agent.tsx
|
|
3456
|
+
let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
|
|
3457
|
+
UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
|
|
3458
|
+
UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
|
|
3459
|
+
UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
|
|
3460
|
+
return UseAgentUpdate;
|
|
3461
|
+
}({});
|
|
3462
|
+
const ALL_UPDATES = [
|
|
3463
|
+
UseAgentUpdate.OnMessagesChanged,
|
|
3464
|
+
UseAgentUpdate.OnStateChanged,
|
|
3465
|
+
UseAgentUpdate.OnRunStatusChanged
|
|
3466
|
+
];
|
|
3467
|
+
/**
|
|
3468
|
+
* Clone a registry agent for per-thread isolation.
|
|
3469
|
+
* Copies agent configuration (transport, headers, etc.) but resets conversation
|
|
3470
|
+
* state (messages, threadId, state) so each thread starts fresh.
|
|
3471
|
+
*/
|
|
3472
|
+
function cloneForThread(source, threadId, headers) {
|
|
3473
|
+
const clone = source.clone();
|
|
3474
|
+
if (clone === source) throw new Error(`useAgent: ${source.constructor.name}.clone() returned the same instance. clone() must return a new, independent object.`);
|
|
3475
|
+
clone.threadId = threadId;
|
|
3476
|
+
clone.setMessages([]);
|
|
3477
|
+
clone.setState({});
|
|
3478
|
+
if (clone instanceof HttpAgent) clone.headers = { ...headers };
|
|
3479
|
+
return clone;
|
|
3480
|
+
}
|
|
3481
|
+
/**
|
|
3482
|
+
* Module-level WeakMap: registryAgent → (threadId → clone).
|
|
3483
|
+
* Shared across all useAgent() calls so that every component using the same
|
|
3484
|
+
* (agentId, threadId) pair receives the same agent instance. Using WeakMap
|
|
3485
|
+
* ensures the clone map is garbage-collected when the registry agent is
|
|
3486
|
+
* replaced (e.g. after reconnect or hot-reload).
|
|
3487
|
+
*/
|
|
3488
|
+
const globalThreadCloneMap = /* @__PURE__ */ new WeakMap();
|
|
3489
|
+
/**
|
|
3490
|
+
* Look up an existing per-thread clone without creating one.
|
|
3491
|
+
* Returns undefined when no clone has been created yet for this pair.
|
|
3492
|
+
*/
|
|
3493
|
+
function getThreadClone(registryAgent, threadId) {
|
|
3494
|
+
if (!registryAgent || !threadId) return void 0;
|
|
3495
|
+
return globalThreadCloneMap.get(registryAgent)?.get(threadId);
|
|
3496
|
+
}
|
|
3497
|
+
function getOrCreateThreadClone(existing, threadId, headers) {
|
|
3498
|
+
let byThread = globalThreadCloneMap.get(existing);
|
|
3499
|
+
if (!byThread) {
|
|
3500
|
+
byThread = /* @__PURE__ */ new Map();
|
|
3501
|
+
globalThreadCloneMap.set(existing, byThread);
|
|
3502
|
+
}
|
|
3503
|
+
const cached = byThread.get(threadId);
|
|
3504
|
+
if (cached) return cached;
|
|
3505
|
+
const clone = cloneForThread(existing, threadId, headers);
|
|
3506
|
+
byThread.set(threadId, clone);
|
|
3507
|
+
return clone;
|
|
3508
|
+
}
|
|
3509
|
+
function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
|
|
3510
|
+
agentId ??= DEFAULT_AGENT_ID;
|
|
3511
|
+
const { copilotkit } = useCopilotKit();
|
|
3512
|
+
const providerThrottleMs = copilotkit.defaultThrottleMs;
|
|
3513
|
+
const chatConfig = useCopilotChatConfiguration();
|
|
3514
|
+
threadId ??= chatConfig?.threadId;
|
|
3515
|
+
const effectiveThrottleMs = useMemo(() => {
|
|
3516
|
+
const resolved = throttleMs ?? providerThrottleMs ?? 0;
|
|
3517
|
+
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
3518
|
+
const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
|
|
3519
|
+
console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
|
|
3520
|
+
return 0;
|
|
3521
|
+
}
|
|
3522
|
+
return resolved;
|
|
3523
|
+
}, [throttleMs, providerThrottleMs]);
|
|
3524
|
+
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
3525
|
+
const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
|
|
3526
|
+
const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
|
|
3527
|
+
const agent = useMemo(() => {
|
|
3528
|
+
const cacheKey = threadId ? `${agentId}:${threadId}` : agentId;
|
|
3529
|
+
const existing = copilotkit.getAgent(agentId);
|
|
3530
|
+
if (existing) {
|
|
3531
|
+
provisionalAgentCache.current.delete(cacheKey);
|
|
3532
|
+
provisionalAgentCache.current.delete(agentId);
|
|
3533
|
+
if (!threadId) return existing;
|
|
3534
|
+
return getOrCreateThreadClone(existing, threadId, copilotkit.headers);
|
|
3535
|
+
}
|
|
3536
|
+
const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
|
|
3537
|
+
const status = copilotkit.runtimeConnectionStatus;
|
|
3538
|
+
if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
|
|
3539
|
+
const cached = provisionalAgentCache.current.get(cacheKey);
|
|
3540
|
+
if (cached) {
|
|
3541
|
+
cached.headers = { ...copilotkit.headers };
|
|
3542
|
+
return cached;
|
|
3543
|
+
}
|
|
3544
|
+
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
3545
|
+
runtimeUrl: copilotkit.runtimeUrl,
|
|
3546
|
+
agentId,
|
|
3547
|
+
transport: copilotkit.runtimeTransport,
|
|
3548
|
+
runtimeMode: "pending"
|
|
3549
|
+
});
|
|
3550
|
+
provisional.headers = { ...copilotkit.headers };
|
|
3551
|
+
if (threadId) provisional.threadId = threadId;
|
|
3552
|
+
provisionalAgentCache.current.set(cacheKey, provisional);
|
|
3553
|
+
return provisional;
|
|
3554
|
+
}
|
|
3555
|
+
if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
|
|
3556
|
+
const cached = provisionalAgentCache.current.get(cacheKey);
|
|
3557
|
+
if (cached) {
|
|
3558
|
+
cached.headers = { ...copilotkit.headers };
|
|
3559
|
+
return cached;
|
|
3560
|
+
}
|
|
3561
|
+
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
3562
|
+
runtimeUrl: copilotkit.runtimeUrl,
|
|
3563
|
+
agentId,
|
|
3564
|
+
transport: copilotkit.runtimeTransport,
|
|
3565
|
+
runtimeMode: "pending"
|
|
3566
|
+
});
|
|
3567
|
+
provisional.headers = { ...copilotkit.headers };
|
|
3568
|
+
if (threadId) provisional.threadId = threadId;
|
|
3569
|
+
provisionalAgentCache.current.set(cacheKey, provisional);
|
|
3570
|
+
return provisional;
|
|
3571
|
+
}
|
|
3572
|
+
const knownAgents = Object.keys(copilotkit.agents ?? {});
|
|
3573
|
+
const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
|
|
3574
|
+
throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
|
|
3575
|
+
}, [
|
|
3576
|
+
agentId,
|
|
3577
|
+
threadId,
|
|
3578
|
+
copilotkit.agents,
|
|
3579
|
+
copilotkit.runtimeConnectionStatus,
|
|
3580
|
+
copilotkit.runtimeUrl,
|
|
3581
|
+
copilotkit.runtimeTransport,
|
|
3582
|
+
JSON.stringify(copilotkit.headers)
|
|
3583
|
+
]);
|
|
3584
|
+
useEffect(() => {
|
|
3585
|
+
if (updateFlags.length === 0) return;
|
|
3586
|
+
const handlers = {};
|
|
3587
|
+
let timerId = null;
|
|
3588
|
+
let active = true;
|
|
3589
|
+
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
|
|
3590
|
+
const ms = effectiveThrottleMs;
|
|
3591
|
+
if (ms > 0) {
|
|
3592
|
+
let throttleActive = false;
|
|
3593
|
+
let pending = false;
|
|
3594
|
+
const throttledNotify = () => {
|
|
3595
|
+
if (!active) return;
|
|
3596
|
+
if (!throttleActive) {
|
|
3597
|
+
throttleActive = true;
|
|
3598
|
+
pending = false;
|
|
3599
|
+
forceUpdate();
|
|
3600
|
+
timerId = setTimeout(function trailingEdge() {
|
|
3601
|
+
timerId = null;
|
|
3602
|
+
if (active && pending) {
|
|
3603
|
+
pending = false;
|
|
3604
|
+
forceUpdate();
|
|
3605
|
+
timerId = setTimeout(trailingEdge, ms);
|
|
3606
|
+
} else throttleActive = false;
|
|
3607
|
+
}, ms);
|
|
3608
|
+
} else pending = true;
|
|
3609
|
+
};
|
|
3610
|
+
handlers.onMessagesChanged = throttledNotify;
|
|
3611
|
+
} else handlers.onMessagesChanged = forceUpdate;
|
|
3612
|
+
}
|
|
3613
|
+
if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
|
|
3614
|
+
if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
|
|
3615
|
+
handlers.onRunInitialized = forceUpdate;
|
|
3616
|
+
handlers.onRunFinalized = forceUpdate;
|
|
3617
|
+
handlers.onRunFailed = forceUpdate;
|
|
3618
|
+
}
|
|
3619
|
+
const subscription = agent.subscribe(handlers);
|
|
3620
|
+
return () => {
|
|
3621
|
+
active = false;
|
|
3622
|
+
if (timerId !== null) clearTimeout(timerId);
|
|
3623
|
+
subscription.unsubscribe();
|
|
3624
|
+
};
|
|
2437
3625
|
}, [
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
3626
|
+
agent,
|
|
3627
|
+
forceUpdate,
|
|
3628
|
+
effectiveThrottleMs,
|
|
3629
|
+
updateFlags
|
|
2441
3630
|
]);
|
|
3631
|
+
useEffect(() => {
|
|
3632
|
+
if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
|
|
3633
|
+
}, [agent, JSON.stringify(copilotkit.headers)]);
|
|
3634
|
+
return { agent };
|
|
2442
3635
|
}
|
|
2443
3636
|
|
|
2444
3637
|
//#endregion
|
|
@@ -2458,7 +3651,8 @@ function useRenderCustomMessages() {
|
|
|
2458
3651
|
const { message, position } = params;
|
|
2459
3652
|
const resolvedRunId = copilotkit.getRunIdForMessage(agentId, threadId, message.id) ?? copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
|
|
2460
3653
|
const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
|
|
2461
|
-
const
|
|
3654
|
+
const registryAgent = copilotkit.getAgent(agentId);
|
|
3655
|
+
const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
|
|
2462
3656
|
if (!agent) throw new Error("Agent not found");
|
|
2463
3657
|
const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
|
|
2464
3658
|
const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
|
|
@@ -2490,7 +3684,8 @@ function useRenderCustomMessages() {
|
|
|
2490
3684
|
//#region src/v2/hooks/use-render-activity-message.tsx
|
|
2491
3685
|
function useRenderActivityMessage() {
|
|
2492
3686
|
const { copilotkit } = useCopilotKit();
|
|
2493
|
-
const
|
|
3687
|
+
const config = useCopilotChatConfiguration();
|
|
3688
|
+
const agentId = config?.agentId ?? DEFAULT_AGENT_ID;
|
|
2494
3689
|
const renderers = copilotkit.renderActivityMessages;
|
|
2495
3690
|
const findRenderer = useCallback((activityType) => {
|
|
2496
3691
|
if (!renderers.length) return null;
|
|
@@ -2506,7 +3701,8 @@ function useRenderActivityMessage() {
|
|
|
2506
3701
|
return null;
|
|
2507
3702
|
}
|
|
2508
3703
|
const Component = renderer.render;
|
|
2509
|
-
const
|
|
3704
|
+
const registryAgent = copilotkit.getAgent(agentId);
|
|
3705
|
+
const agent = getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
|
|
2510
3706
|
return /* @__PURE__ */ jsx(Component, {
|
|
2511
3707
|
activityType: message.activityType,
|
|
2512
3708
|
content: parseResult.data,
|
|
@@ -2515,6 +3711,7 @@ function useRenderActivityMessage() {
|
|
|
2515
3711
|
}, message.id);
|
|
2516
3712
|
}, [
|
|
2517
3713
|
agentId,
|
|
3714
|
+
config?.threadId,
|
|
2518
3715
|
copilotkit,
|
|
2519
3716
|
findRenderer
|
|
2520
3717
|
]);
|
|
@@ -2540,7 +3737,7 @@ function useFrontendTool(tool, deps) {
|
|
|
2540
3737
|
copilotkit.removeTool(name, tool.agentId);
|
|
2541
3738
|
}
|
|
2542
3739
|
copilotkit.addTool(tool);
|
|
2543
|
-
if (tool.render
|
|
3740
|
+
if (tool.render) copilotkit.addHookRenderToolCall({
|
|
2544
3741
|
name,
|
|
2545
3742
|
args: tool.parameters,
|
|
2546
3743
|
agentId: tool.agentId,
|
|
@@ -2625,18 +3822,6 @@ function useComponent(config, deps) {
|
|
|
2625
3822
|
}, deps);
|
|
2626
3823
|
}
|
|
2627
3824
|
|
|
2628
|
-
//#endregion
|
|
2629
|
-
//#region src/v2/types/defineToolCallRenderer.ts
|
|
2630
|
-
function defineToolCallRenderer(def) {
|
|
2631
|
-
const argsSchema = def.name === "*" && !def.args ? z.any() : def.args;
|
|
2632
|
-
return {
|
|
2633
|
-
name: def.name,
|
|
2634
|
-
args: argsSchema,
|
|
2635
|
-
render: def.render,
|
|
2636
|
-
...def.agentId ? { agentId: def.agentId } : {}
|
|
2637
|
-
};
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
3825
|
//#endregion
|
|
2641
3826
|
//#region src/v2/hooks/use-render-tool.tsx
|
|
2642
3827
|
const EMPTY_DEPS = [];
|
|
@@ -2874,206 +4059,95 @@ function DefaultToolCallRenderer({ name, parameters, status, result }) {
|
|
|
2874
4059
|
})] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
2875
4060
|
style: {
|
|
2876
4061
|
fontSize: "10px",
|
|
2877
|
-
textTransform: "uppercase",
|
|
2878
|
-
letterSpacing: "0.05em",
|
|
2879
|
-
color: "#71717a"
|
|
2880
|
-
},
|
|
2881
|
-
children: "Result"
|
|
2882
|
-
}), /* @__PURE__ */ jsx("pre", {
|
|
2883
|
-
style: {
|
|
2884
|
-
marginTop: "6px",
|
|
2885
|
-
maxHeight: "200px",
|
|
2886
|
-
overflow: "auto",
|
|
2887
|
-
borderRadius: "6px",
|
|
2888
|
-
backgroundColor: "#f4f4f5",
|
|
2889
|
-
padding: "10px",
|
|
2890
|
-
fontSize: "11px",
|
|
2891
|
-
lineHeight: 1.6,
|
|
2892
|
-
color: "#27272a",
|
|
2893
|
-
whiteSpace: "pre-wrap",
|
|
2894
|
-
wordBreak: "break-word"
|
|
2895
|
-
},
|
|
2896
|
-
children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
2897
|
-
})] })]
|
|
2898
|
-
})]
|
|
2899
|
-
})
|
|
2900
|
-
});
|
|
2901
|
-
}
|
|
2902
|
-
|
|
2903
|
-
//#endregion
|
|
2904
|
-
//#region src/v2/hooks/use-human-in-the-loop.tsx
|
|
2905
|
-
function useHumanInTheLoop(tool, deps) {
|
|
2906
|
-
const { copilotkit } = useCopilotKit();
|
|
2907
|
-
const resolvePromiseRef = useRef(null);
|
|
2908
|
-
const respond = useCallback(async (result) => {
|
|
2909
|
-
if (resolvePromiseRef.current) {
|
|
2910
|
-
resolvePromiseRef.current(result);
|
|
2911
|
-
resolvePromiseRef.current = null;
|
|
2912
|
-
}
|
|
2913
|
-
}, []);
|
|
2914
|
-
const handler = useCallback(async () => {
|
|
2915
|
-
return new Promise((resolve) => {
|
|
2916
|
-
resolvePromiseRef.current = resolve;
|
|
2917
|
-
});
|
|
2918
|
-
}, []);
|
|
2919
|
-
const RenderComponent = useCallback((props) => {
|
|
2920
|
-
const ToolComponent = tool.render;
|
|
2921
|
-
if (props.status === "inProgress") {
|
|
2922
|
-
const enhancedProps = {
|
|
2923
|
-
...props,
|
|
2924
|
-
name: tool.name,
|
|
2925
|
-
description: tool.description || "",
|
|
2926
|
-
respond: void 0
|
|
2927
|
-
};
|
|
2928
|
-
return React.createElement(ToolComponent, enhancedProps);
|
|
2929
|
-
} else if (props.status === "executing") {
|
|
2930
|
-
const enhancedProps = {
|
|
2931
|
-
...props,
|
|
2932
|
-
name: tool.name,
|
|
2933
|
-
description: tool.description || "",
|
|
2934
|
-
respond
|
|
2935
|
-
};
|
|
2936
|
-
return React.createElement(ToolComponent, enhancedProps);
|
|
2937
|
-
} else if (props.status === "complete") {
|
|
2938
|
-
const enhancedProps = {
|
|
2939
|
-
...props,
|
|
2940
|
-
name: tool.name,
|
|
2941
|
-
description: tool.description || "",
|
|
2942
|
-
respond: void 0
|
|
2943
|
-
};
|
|
2944
|
-
return React.createElement(ToolComponent, enhancedProps);
|
|
2945
|
-
}
|
|
2946
|
-
return React.createElement(ToolComponent, props);
|
|
2947
|
-
}, [
|
|
2948
|
-
tool.render,
|
|
2949
|
-
tool.name,
|
|
2950
|
-
tool.description,
|
|
2951
|
-
respond
|
|
2952
|
-
]);
|
|
2953
|
-
useFrontendTool({
|
|
2954
|
-
...tool,
|
|
2955
|
-
handler,
|
|
2956
|
-
render: RenderComponent
|
|
2957
|
-
}, deps);
|
|
2958
|
-
useEffect(() => {
|
|
2959
|
-
return () => {
|
|
2960
|
-
copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
|
|
2961
|
-
};
|
|
2962
|
-
}, [
|
|
2963
|
-
copilotkit,
|
|
2964
|
-
tool.name,
|
|
2965
|
-
tool.agentId
|
|
2966
|
-
]);
|
|
2967
|
-
}
|
|
2968
|
-
|
|
2969
|
-
//#endregion
|
|
2970
|
-
//#region src/v2/hooks/use-agent.tsx
|
|
2971
|
-
let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
|
|
2972
|
-
UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
|
|
2973
|
-
UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
|
|
2974
|
-
UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
|
|
2975
|
-
return UseAgentUpdate;
|
|
2976
|
-
}({});
|
|
2977
|
-
const ALL_UPDATES = [
|
|
2978
|
-
UseAgentUpdate.OnMessagesChanged,
|
|
2979
|
-
UseAgentUpdate.OnStateChanged,
|
|
2980
|
-
UseAgentUpdate.OnRunStatusChanged
|
|
2981
|
-
];
|
|
2982
|
-
function useAgent({ agentId, updates } = {}) {
|
|
2983
|
-
agentId ??= DEFAULT_AGENT_ID;
|
|
2984
|
-
const { copilotkit } = useCopilotKit();
|
|
2985
|
-
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
2986
|
-
const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
|
|
2987
|
-
const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
|
|
2988
|
-
const agent = useMemo(() => {
|
|
2989
|
-
const existing = copilotkit.getAgent(agentId);
|
|
2990
|
-
if (existing) {
|
|
2991
|
-
provisionalAgentCache.current.delete(agentId);
|
|
2992
|
-
return existing;
|
|
2993
|
-
}
|
|
2994
|
-
const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
|
|
2995
|
-
const status = copilotkit.runtimeConnectionStatus;
|
|
2996
|
-
if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
|
|
2997
|
-
const cached = provisionalAgentCache.current.get(agentId);
|
|
2998
|
-
if (cached) {
|
|
2999
|
-
cached.headers = { ...copilotkit.headers };
|
|
3000
|
-
return cached;
|
|
3001
|
-
}
|
|
3002
|
-
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
3003
|
-
runtimeUrl: copilotkit.runtimeUrl,
|
|
3004
|
-
agentId,
|
|
3005
|
-
transport: copilotkit.runtimeTransport,
|
|
3006
|
-
runtimeMode: "pending"
|
|
3007
|
-
});
|
|
3008
|
-
provisional.headers = { ...copilotkit.headers };
|
|
3009
|
-
provisionalAgentCache.current.set(agentId, provisional);
|
|
3010
|
-
return provisional;
|
|
3011
|
-
}
|
|
3012
|
-
if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
|
|
3013
|
-
const provisional = new ProxiedCopilotRuntimeAgent({
|
|
3014
|
-
runtimeUrl: copilotkit.runtimeUrl,
|
|
3015
|
-
agentId,
|
|
3016
|
-
transport: copilotkit.runtimeTransport,
|
|
3017
|
-
runtimeMode: "pending"
|
|
3018
|
-
});
|
|
3019
|
-
provisional.headers = { ...copilotkit.headers };
|
|
3020
|
-
return provisional;
|
|
3021
|
-
}
|
|
3022
|
-
const knownAgents = Object.keys(copilotkit.agents ?? {});
|
|
3023
|
-
const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
|
|
3024
|
-
throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
|
|
3025
|
-
}, [
|
|
3026
|
-
agentId,
|
|
3027
|
-
copilotkit.agents,
|
|
3028
|
-
copilotkit.runtimeConnectionStatus,
|
|
3029
|
-
copilotkit.runtimeUrl,
|
|
3030
|
-
copilotkit.runtimeTransport,
|
|
3031
|
-
JSON.stringify(copilotkit.headers)
|
|
3032
|
-
]);
|
|
3033
|
-
useEffect(() => {
|
|
3034
|
-
if (updateFlags.length === 0) return;
|
|
3035
|
-
const handlers = {};
|
|
3036
|
-
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = () => {
|
|
3037
|
-
forceUpdate();
|
|
3038
|
-
};
|
|
3039
|
-
if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
|
|
3040
|
-
if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
|
|
3041
|
-
handlers.onRunInitialized = forceUpdate;
|
|
3042
|
-
handlers.onRunFinalized = forceUpdate;
|
|
3043
|
-
handlers.onRunFailed = forceUpdate;
|
|
3044
|
-
}
|
|
3045
|
-
const subscription = agent.subscribe(handlers);
|
|
3046
|
-
return () => subscription.unsubscribe();
|
|
3047
|
-
}, [
|
|
3048
|
-
agent,
|
|
3049
|
-
forceUpdate,
|
|
3050
|
-
JSON.stringify(updateFlags)
|
|
3051
|
-
]);
|
|
3052
|
-
return { agent };
|
|
4062
|
+
textTransform: "uppercase",
|
|
4063
|
+
letterSpacing: "0.05em",
|
|
4064
|
+
color: "#71717a"
|
|
4065
|
+
},
|
|
4066
|
+
children: "Result"
|
|
4067
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
4068
|
+
style: {
|
|
4069
|
+
marginTop: "6px",
|
|
4070
|
+
maxHeight: "200px",
|
|
4071
|
+
overflow: "auto",
|
|
4072
|
+
borderRadius: "6px",
|
|
4073
|
+
backgroundColor: "#f4f4f5",
|
|
4074
|
+
padding: "10px",
|
|
4075
|
+
fontSize: "11px",
|
|
4076
|
+
lineHeight: 1.6,
|
|
4077
|
+
color: "#27272a",
|
|
4078
|
+
whiteSpace: "pre-wrap",
|
|
4079
|
+
wordBreak: "break-word"
|
|
4080
|
+
},
|
|
4081
|
+
children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
4082
|
+
})] })]
|
|
4083
|
+
})]
|
|
4084
|
+
})
|
|
4085
|
+
});
|
|
3053
4086
|
}
|
|
3054
4087
|
|
|
3055
4088
|
//#endregion
|
|
3056
|
-
//#region src/v2/hooks/use-
|
|
3057
|
-
function
|
|
3058
|
-
const { description, value } = context;
|
|
4089
|
+
//#region src/v2/hooks/use-human-in-the-loop.tsx
|
|
4090
|
+
function useHumanInTheLoop(tool, deps) {
|
|
3059
4091
|
const { copilotkit } = useCopilotKit();
|
|
3060
|
-
const
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
4092
|
+
const resolvePromiseRef = useRef(null);
|
|
4093
|
+
const respond = useCallback(async (result) => {
|
|
4094
|
+
if (resolvePromiseRef.current) {
|
|
4095
|
+
resolvePromiseRef.current(result);
|
|
4096
|
+
resolvePromiseRef.current = null;
|
|
4097
|
+
}
|
|
4098
|
+
}, []);
|
|
4099
|
+
const handler = useCallback(async () => {
|
|
4100
|
+
return new Promise((resolve) => {
|
|
4101
|
+
resolvePromiseRef.current = resolve;
|
|
3069
4102
|
});
|
|
4103
|
+
}, []);
|
|
4104
|
+
const RenderComponent = useCallback((props) => {
|
|
4105
|
+
const ToolComponent = tool.render;
|
|
4106
|
+
if (props.status === "inProgress") {
|
|
4107
|
+
const enhancedProps = {
|
|
4108
|
+
...props,
|
|
4109
|
+
name: tool.name,
|
|
4110
|
+
description: tool.description || "",
|
|
4111
|
+
respond: void 0
|
|
4112
|
+
};
|
|
4113
|
+
return React.createElement(ToolComponent, enhancedProps);
|
|
4114
|
+
} else if (props.status === "executing") {
|
|
4115
|
+
const enhancedProps = {
|
|
4116
|
+
...props,
|
|
4117
|
+
name: tool.name,
|
|
4118
|
+
description: tool.description || "",
|
|
4119
|
+
respond
|
|
4120
|
+
};
|
|
4121
|
+
return React.createElement(ToolComponent, enhancedProps);
|
|
4122
|
+
} else if (props.status === "complete") {
|
|
4123
|
+
const enhancedProps = {
|
|
4124
|
+
...props,
|
|
4125
|
+
name: tool.name,
|
|
4126
|
+
description: tool.description || "",
|
|
4127
|
+
respond: void 0
|
|
4128
|
+
};
|
|
4129
|
+
return React.createElement(ToolComponent, enhancedProps);
|
|
4130
|
+
}
|
|
4131
|
+
return React.createElement(ToolComponent, props);
|
|
4132
|
+
}, [
|
|
4133
|
+
tool.render,
|
|
4134
|
+
tool.name,
|
|
4135
|
+
tool.description,
|
|
4136
|
+
respond
|
|
4137
|
+
]);
|
|
4138
|
+
useFrontendTool({
|
|
4139
|
+
...tool,
|
|
4140
|
+
handler,
|
|
4141
|
+
render: RenderComponent
|
|
4142
|
+
}, deps);
|
|
4143
|
+
useEffect(() => {
|
|
3070
4144
|
return () => {
|
|
3071
|
-
copilotkit.
|
|
4145
|
+
copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
|
|
3072
4146
|
};
|
|
3073
4147
|
}, [
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
4148
|
+
copilotkit,
|
|
4149
|
+
tool.name,
|
|
4150
|
+
tool.agentId
|
|
3077
4151
|
]);
|
|
3078
4152
|
}
|
|
3079
4153
|
|
|
@@ -3486,11 +4560,19 @@ function useThreadStoreSelector(store, selector) {
|
|
|
3486
4560
|
function useThreads$1({ agentId, includeArchived, limit }) {
|
|
3487
4561
|
const { copilotkit } = useCopilotKit();
|
|
3488
4562
|
const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
|
|
3489
|
-
const
|
|
4563
|
+
const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
|
|
4564
|
+
const threads = useMemo(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
|
|
4565
|
+
id,
|
|
4566
|
+
agentId,
|
|
4567
|
+
name,
|
|
4568
|
+
archived,
|
|
4569
|
+
createdAt,
|
|
4570
|
+
updatedAt
|
|
4571
|
+
})), [coreThreads]);
|
|
3490
4572
|
const storeIsLoading = useThreadStoreSelector(store, ɵselectThreadsIsLoading);
|
|
3491
4573
|
const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
|
|
3492
|
-
const
|
|
3493
|
-
const
|
|
4574
|
+
const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
|
|
4575
|
+
const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
|
|
3494
4576
|
const headersKey = useMemo(() => {
|
|
3495
4577
|
return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
|
|
3496
4578
|
}, [copilotkit.headers]);
|
|
@@ -3533,15 +4615,173 @@ function useThreads$1({ agentId, includeArchived, limit }) {
|
|
|
3533
4615
|
threads,
|
|
3534
4616
|
isLoading,
|
|
3535
4617
|
error,
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
4618
|
+
hasMoreThreads,
|
|
4619
|
+
isFetchingMoreThreads,
|
|
4620
|
+
fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
|
|
3539
4621
|
renameThread,
|
|
3540
4622
|
archiveThread,
|
|
3541
4623
|
deleteThread
|
|
3542
4624
|
};
|
|
3543
4625
|
}
|
|
3544
4626
|
|
|
4627
|
+
//#endregion
|
|
4628
|
+
//#region src/v2/hooks/use-attachments.tsx
|
|
4629
|
+
/**
|
|
4630
|
+
* Hook that manages file attachment state — uploads, drag-and-drop, paste,
|
|
4631
|
+
* and lifecycle. All returned callbacks are referentially stable across
|
|
4632
|
+
* renders (via useCallback) to avoid destabilizing downstream memoization.
|
|
4633
|
+
*/
|
|
4634
|
+
function useAttachments({ config }) {
|
|
4635
|
+
const enabled = config?.enabled ?? false;
|
|
4636
|
+
const [attachments, setAttachments] = useState([]);
|
|
4637
|
+
const [dragOver, setDragOver] = useState(false);
|
|
4638
|
+
const fileInputRef = useRef(null);
|
|
4639
|
+
const containerRef = useRef(null);
|
|
4640
|
+
const configRef = useRef(config);
|
|
4641
|
+
configRef.current = config;
|
|
4642
|
+
const attachmentsRef = useRef([]);
|
|
4643
|
+
attachmentsRef.current = attachments;
|
|
4644
|
+
const processFiles = useCallback(async (files) => {
|
|
4645
|
+
const cfg = configRef.current;
|
|
4646
|
+
const accept = cfg?.accept ?? "*/*";
|
|
4647
|
+
const maxSize = cfg?.maxSize ?? 20 * 1024 * 1024;
|
|
4648
|
+
const rejectedFiles = files.filter((file) => !matchesAcceptFilter(file, accept));
|
|
4649
|
+
for (const file of rejectedFiles) cfg?.onUploadFailed?.({
|
|
4650
|
+
reason: "invalid-type",
|
|
4651
|
+
file,
|
|
4652
|
+
message: `File "${file.name}" is not accepted. Supported types: ${accept}`
|
|
4653
|
+
});
|
|
4654
|
+
const validFiles = files.filter((file) => matchesAcceptFilter(file, accept));
|
|
4655
|
+
for (const file of validFiles) {
|
|
4656
|
+
if (exceedsMaxSize(file, maxSize)) {
|
|
4657
|
+
cfg?.onUploadFailed?.({
|
|
4658
|
+
reason: "file-too-large",
|
|
4659
|
+
file,
|
|
4660
|
+
message: `File "${file.name}" exceeds the maximum size of ${formatFileSize(maxSize)}`
|
|
4661
|
+
});
|
|
4662
|
+
continue;
|
|
4663
|
+
}
|
|
4664
|
+
const modality = getModalityFromMimeType(file.type);
|
|
4665
|
+
const placeholderId = randomUUID();
|
|
4666
|
+
const placeholder = {
|
|
4667
|
+
id: placeholderId,
|
|
4668
|
+
type: modality,
|
|
4669
|
+
source: {
|
|
4670
|
+
type: "data",
|
|
4671
|
+
value: "",
|
|
4672
|
+
mimeType: file.type
|
|
4673
|
+
},
|
|
4674
|
+
filename: file.name,
|
|
4675
|
+
size: file.size,
|
|
4676
|
+
status: "uploading"
|
|
4677
|
+
};
|
|
4678
|
+
setAttachments((prev) => [...prev, placeholder]);
|
|
4679
|
+
try {
|
|
4680
|
+
let source;
|
|
4681
|
+
let uploadMetadata;
|
|
4682
|
+
if (cfg?.onUpload) {
|
|
4683
|
+
const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
|
|
4684
|
+
source = uploadSource;
|
|
4685
|
+
uploadMetadata = meta;
|
|
4686
|
+
} else source = {
|
|
4687
|
+
type: "data",
|
|
4688
|
+
value: await readFileAsBase64(file),
|
|
4689
|
+
mimeType: file.type
|
|
4690
|
+
};
|
|
4691
|
+
let thumbnail;
|
|
4692
|
+
if (modality === "video") thumbnail = await generateVideoThumbnail(file);
|
|
4693
|
+
setAttachments((prev) => prev.map((att) => att.id === placeholderId ? {
|
|
4694
|
+
...att,
|
|
4695
|
+
source,
|
|
4696
|
+
status: "ready",
|
|
4697
|
+
thumbnail,
|
|
4698
|
+
metadata: uploadMetadata
|
|
4699
|
+
} : att));
|
|
4700
|
+
} catch (error) {
|
|
4701
|
+
setAttachments((prev) => prev.filter((att) => att.id !== placeholderId));
|
|
4702
|
+
console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
|
|
4703
|
+
cfg?.onUploadFailed?.({
|
|
4704
|
+
reason: "upload-failed",
|
|
4705
|
+
file,
|
|
4706
|
+
message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
|
|
4707
|
+
});
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
}, []);
|
|
4711
|
+
const handleFileUpload = useCallback(async (e) => {
|
|
4712
|
+
if (!e.target.files?.length) return;
|
|
4713
|
+
try {
|
|
4714
|
+
await processFiles(Array.from(e.target.files));
|
|
4715
|
+
} catch (error) {
|
|
4716
|
+
console.error("[CopilotKit] Upload error:", error);
|
|
4717
|
+
}
|
|
4718
|
+
}, [processFiles]);
|
|
4719
|
+
const handleDragOver = useCallback((e) => {
|
|
4720
|
+
if (!configRef.current?.enabled) return;
|
|
4721
|
+
e.preventDefault();
|
|
4722
|
+
e.stopPropagation();
|
|
4723
|
+
setDragOver(true);
|
|
4724
|
+
}, []);
|
|
4725
|
+
const handleDragLeave = useCallback((e) => {
|
|
4726
|
+
e.preventDefault();
|
|
4727
|
+
e.stopPropagation();
|
|
4728
|
+
setDragOver(false);
|
|
4729
|
+
}, []);
|
|
4730
|
+
const handleDrop = useCallback(async (e) => {
|
|
4731
|
+
e.preventDefault();
|
|
4732
|
+
e.stopPropagation();
|
|
4733
|
+
setDragOver(false);
|
|
4734
|
+
if (!configRef.current?.enabled) return;
|
|
4735
|
+
const files = Array.from(e.dataTransfer.files);
|
|
4736
|
+
if (files.length > 0) try {
|
|
4737
|
+
await processFiles(files);
|
|
4738
|
+
} catch (error) {
|
|
4739
|
+
console.error("[CopilotKit] Drop error:", error);
|
|
4740
|
+
}
|
|
4741
|
+
}, [processFiles]);
|
|
4742
|
+
useEffect(() => {
|
|
4743
|
+
if (!enabled) return;
|
|
4744
|
+
const handlePaste = async (e) => {
|
|
4745
|
+
const target = e.target;
|
|
4746
|
+
if (!target || !containerRef.current?.contains(target)) return;
|
|
4747
|
+
const accept = configRef.current?.accept ?? "*/*";
|
|
4748
|
+
const fileItems = Array.from(e.clipboardData?.items || []).filter((item) => item.kind === "file" && item.getAsFile() !== null && matchesAcceptFilter(item.getAsFile(), accept));
|
|
4749
|
+
if (fileItems.length === 0) return;
|
|
4750
|
+
e.preventDefault();
|
|
4751
|
+
const files = fileItems.map((item) => item.getAsFile()).filter((f) => f !== null);
|
|
4752
|
+
try {
|
|
4753
|
+
await processFiles(files);
|
|
4754
|
+
} catch (error) {
|
|
4755
|
+
console.error("[CopilotKit] Paste error:", error);
|
|
4756
|
+
}
|
|
4757
|
+
};
|
|
4758
|
+
document.addEventListener("paste", handlePaste);
|
|
4759
|
+
return () => document.removeEventListener("paste", handlePaste);
|
|
4760
|
+
}, [enabled, processFiles]);
|
|
4761
|
+
return {
|
|
4762
|
+
attachments,
|
|
4763
|
+
enabled,
|
|
4764
|
+
dragOver,
|
|
4765
|
+
fileInputRef,
|
|
4766
|
+
containerRef,
|
|
4767
|
+
processFiles,
|
|
4768
|
+
handleFileUpload,
|
|
4769
|
+
handleDragOver,
|
|
4770
|
+
handleDragLeave,
|
|
4771
|
+
handleDrop,
|
|
4772
|
+
removeAttachment: useCallback((id) => {
|
|
4773
|
+
setAttachments((prev) => prev.filter((a) => a.id !== id));
|
|
4774
|
+
}, []),
|
|
4775
|
+
consumeAttachments: useCallback(() => {
|
|
4776
|
+
const ready = attachmentsRef.current.filter((a) => a.status === "ready");
|
|
4777
|
+
if (ready.length === 0) return ready;
|
|
4778
|
+
setAttachments((prev) => prev.filter((a) => a.status !== "ready"));
|
|
4779
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
4780
|
+
return ready;
|
|
4781
|
+
}, [])
|
|
4782
|
+
};
|
|
4783
|
+
}
|
|
4784
|
+
|
|
3545
4785
|
//#endregion
|
|
3546
4786
|
//#region src/v2/components/chat/CopilotChatToolCallsView.tsx
|
|
3547
4787
|
function CopilotChatToolCallsView({ message, messages = [] }) {
|
|
@@ -3658,9 +4898,19 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
|
|
|
3658
4898
|
_CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
|
|
3659
4899
|
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
|
|
3660
4900
|
const [copied, setCopied] = useState(false);
|
|
4901
|
+
const timerRef = useRef(null);
|
|
4902
|
+
useEffect(() => {
|
|
4903
|
+
return () => {
|
|
4904
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
4905
|
+
};
|
|
4906
|
+
}, []);
|
|
3661
4907
|
const handleClick = (event) => {
|
|
3662
4908
|
setCopied(true);
|
|
3663
|
-
|
|
4909
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
4910
|
+
timerRef.current = setTimeout(() => {
|
|
4911
|
+
timerRef.current = null;
|
|
4912
|
+
setCopied(false);
|
|
4913
|
+
}, 2e3);
|
|
3664
4914
|
if (onClick) onClick(event);
|
|
3665
4915
|
};
|
|
3666
4916
|
return /* @__PURE__ */ jsx(ToolbarButton, {
|
|
@@ -3718,6 +4968,79 @@ CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantM
|
|
|
3718
4968
|
CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
|
|
3719
4969
|
var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
|
|
3720
4970
|
|
|
4971
|
+
//#endregion
|
|
4972
|
+
//#region src/v2/components/chat/CopilotChatAttachmentRenderer.tsx
|
|
4973
|
+
const ImageAttachment = memo(function ImageAttachment({ src, className }) {
|
|
4974
|
+
const [error, setError] = useState(false);
|
|
4975
|
+
if (error) return /* @__PURE__ */ jsx("div", {
|
|
4976
|
+
className: cn("cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:rounded-lg cpk:bg-muted cpk:p-4 cpk:text-sm cpk:text-muted-foreground", className),
|
|
4977
|
+
children: /* @__PURE__ */ jsx("span", { children: "Failed to load image" })
|
|
4978
|
+
});
|
|
4979
|
+
return /* @__PURE__ */ jsx("img", {
|
|
4980
|
+
src,
|
|
4981
|
+
alt: "Image attachment",
|
|
4982
|
+
className: cn("cpk:max-w-full cpk:h-auto cpk:rounded-lg", className),
|
|
4983
|
+
onError: () => setError(true)
|
|
4984
|
+
});
|
|
4985
|
+
});
|
|
4986
|
+
const AudioAttachment = memo(function AudioAttachment({ src, filename, className }) {
|
|
4987
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4988
|
+
className: cn("cpk:flex cpk:flex-col cpk:gap-1", className),
|
|
4989
|
+
children: [/* @__PURE__ */ jsx("audio", {
|
|
4990
|
+
src,
|
|
4991
|
+
controls: true,
|
|
4992
|
+
preload: "metadata",
|
|
4993
|
+
className: "cpk:max-w-[300px] cpk:w-full cpk:h-10"
|
|
4994
|
+
}), filename && /* @__PURE__ */ jsx("span", {
|
|
4995
|
+
className: "cpk:text-xs cpk:text-muted-foreground cpk:truncate cpk:max-w-[300px]",
|
|
4996
|
+
children: filename
|
|
4997
|
+
})]
|
|
4998
|
+
});
|
|
4999
|
+
});
|
|
5000
|
+
const VideoAttachment = memo(function VideoAttachment({ src, className }) {
|
|
5001
|
+
return /* @__PURE__ */ jsx("video", {
|
|
5002
|
+
src,
|
|
5003
|
+
controls: true,
|
|
5004
|
+
preload: "metadata",
|
|
5005
|
+
className: cn("cpk:max-w-[400px] cpk:w-full cpk:rounded-lg", className)
|
|
5006
|
+
});
|
|
5007
|
+
});
|
|
5008
|
+
const DocumentAttachment = memo(function DocumentAttachment({ source, filename, className }) {
|
|
5009
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5010
|
+
className: cn("cpk:inline-flex cpk:items-center cpk:gap-2 cpk:px-3 cpk:py-2 cpk:border cpk:border-border cpk:rounded-lg cpk:bg-muted", className),
|
|
5011
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5012
|
+
className: "cpk:text-xs cpk:font-bold cpk:uppercase",
|
|
5013
|
+
children: getDocumentIcon(source.mimeType ?? "")
|
|
5014
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
5015
|
+
className: "cpk:text-sm cpk:text-muted-foreground cpk:truncate",
|
|
5016
|
+
children: filename || source.mimeType || "Unknown type"
|
|
5017
|
+
})]
|
|
5018
|
+
});
|
|
5019
|
+
});
|
|
5020
|
+
const CopilotChatAttachmentRenderer = ({ type, source, filename, className }) => {
|
|
5021
|
+
const src = getSourceUrl(source);
|
|
5022
|
+
switch (type) {
|
|
5023
|
+
case "image": return /* @__PURE__ */ jsx(ImageAttachment, {
|
|
5024
|
+
src,
|
|
5025
|
+
className
|
|
5026
|
+
});
|
|
5027
|
+
case "audio": return /* @__PURE__ */ jsx(AudioAttachment, {
|
|
5028
|
+
src,
|
|
5029
|
+
filename,
|
|
5030
|
+
className
|
|
5031
|
+
});
|
|
5032
|
+
case "video": return /* @__PURE__ */ jsx(VideoAttachment, {
|
|
5033
|
+
src,
|
|
5034
|
+
className
|
|
5035
|
+
});
|
|
5036
|
+
case "document": return /* @__PURE__ */ jsx(DocumentAttachment, {
|
|
5037
|
+
source,
|
|
5038
|
+
filename,
|
|
5039
|
+
className
|
|
5040
|
+
});
|
|
5041
|
+
}
|
|
5042
|
+
};
|
|
5043
|
+
|
|
3721
5044
|
//#endregion
|
|
3722
5045
|
//#region src/v2/components/chat/CopilotChatUserMessage.tsx
|
|
3723
5046
|
function flattenUserMessageContent(content) {
|
|
@@ -3728,8 +5051,17 @@ function flattenUserMessageContent(content) {
|
|
|
3728
5051
|
return "";
|
|
3729
5052
|
}).filter((text) => text.length > 0).join("\n");
|
|
3730
5053
|
}
|
|
5054
|
+
function getMediaParts(content) {
|
|
5055
|
+
if (!content || typeof content === "string") return [];
|
|
5056
|
+
return content.filter((part) => part.type === "image" || part.type === "audio" || part.type === "video" || part.type === "document");
|
|
5057
|
+
}
|
|
5058
|
+
function getFilename(part) {
|
|
5059
|
+
const meta = part.metadata;
|
|
5060
|
+
if (meta != null && typeof meta === "object" && "filename" in meta && typeof meta.filename === "string") return meta.filename;
|
|
5061
|
+
}
|
|
3731
5062
|
function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfBranches, onSwitchToBranch, additionalToolbarItems, messageRenderer, toolbar, copyButton, editButton, branchNavigation, children, className, ...props }) {
|
|
3732
5063
|
const flattenedContent = useMemo(() => flattenUserMessageContent(message.content), [message.content]);
|
|
5064
|
+
const mediaParts = useMemo(() => getMediaParts(message.content), [message.content]);
|
|
3733
5065
|
const BoundMessageRenderer = renderSlot(messageRenderer, CopilotChatUserMessage.MessageRenderer, { content: flattenedContent });
|
|
3734
5066
|
const BoundCopyButton = renderSlot(copyButton, CopilotChatUserMessage.CopyButton, { onClick: async () => {
|
|
3735
5067
|
if (flattenedContent) try {
|
|
@@ -3776,7 +5108,18 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
|
|
|
3776
5108
|
className: twMerge("copilotKitMessage copilotKitUserMessage cpk:flex cpk:flex-col cpk:items-end cpk:group cpk:pt-10", className),
|
|
3777
5109
|
"data-message-id": message.id,
|
|
3778
5110
|
...props,
|
|
3779
|
-
children: [
|
|
5111
|
+
children: [
|
|
5112
|
+
BoundMessageRenderer,
|
|
5113
|
+
mediaParts.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
5114
|
+
className: "cpk:flex cpk:flex-col cpk:items-end cpk:gap-2 cpk:mt-2",
|
|
5115
|
+
children: mediaParts.map((part, index) => /* @__PURE__ */ jsx(CopilotChatAttachmentRenderer, {
|
|
5116
|
+
type: part.type,
|
|
5117
|
+
source: part.source,
|
|
5118
|
+
filename: getFilename(part)
|
|
5119
|
+
}, index))
|
|
5120
|
+
}),
|
|
5121
|
+
BoundToolbar
|
|
5122
|
+
]
|
|
3780
5123
|
});
|
|
3781
5124
|
}
|
|
3782
5125
|
(function(_CopilotChatUserMessage) {
|
|
@@ -4074,6 +5417,7 @@ const CopilotChatSuggestionView = React.forwardRef(function CopilotChatSuggestio
|
|
|
4074
5417
|
const isLoading = loadingSet.has(index) || suggestion.isLoading === true;
|
|
4075
5418
|
const pill = renderSlot(suggestionSlot, CopilotChatSuggestionPill, {
|
|
4076
5419
|
children: suggestion.title,
|
|
5420
|
+
className: suggestion.className,
|
|
4077
5421
|
isLoading,
|
|
4078
5422
|
type: "button",
|
|
4079
5423
|
onClick: () => onSelectSuggestion?.(suggestion, index)
|
|
@@ -4110,9 +5454,43 @@ const CopilotChatSuggestionView = React.forwardRef(function CopilotChatSuggestio
|
|
|
4110
5454
|
});
|
|
4111
5455
|
CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
|
|
4112
5456
|
|
|
5457
|
+
//#endregion
|
|
5458
|
+
//#region src/v2/components/chat/scroll-element-context.ts
|
|
5459
|
+
/**
|
|
5460
|
+
* Provides the scroll container element to child components that need it for
|
|
5461
|
+
* virtualization. Set by CopilotChatView.ScrollView; consumed by
|
|
5462
|
+
* CopilotChatMessageView to feed useVirtualizer's getScrollElement.
|
|
5463
|
+
*
|
|
5464
|
+
* Carries the element itself (not a ref) so that context consumers re-render
|
|
5465
|
+
* reactively when the scroll container is first mounted.
|
|
5466
|
+
*/
|
|
5467
|
+
const ScrollElementContext = React.createContext(null);
|
|
5468
|
+
|
|
4113
5469
|
//#endregion
|
|
4114
5470
|
//#region src/v2/components/chat/CopilotChatMessageView.tsx
|
|
4115
5471
|
/**
|
|
5472
|
+
* Resolves a slot value into a { Component, slotProps } pair, handling the three
|
|
5473
|
+
* slot forms: a component type, a className string, or a partial-props object.
|
|
5474
|
+
*/
|
|
5475
|
+
function resolveSlotComponent(slot, DefaultComponent) {
|
|
5476
|
+
if (isReactComponentType(slot)) return {
|
|
5477
|
+
Component: slot,
|
|
5478
|
+
slotProps: void 0
|
|
5479
|
+
};
|
|
5480
|
+
if (typeof slot === "string") return {
|
|
5481
|
+
Component: DefaultComponent,
|
|
5482
|
+
slotProps: { className: slot }
|
|
5483
|
+
};
|
|
5484
|
+
if (slot && typeof slot === "object") return {
|
|
5485
|
+
Component: DefaultComponent,
|
|
5486
|
+
slotProps: slot
|
|
5487
|
+
};
|
|
5488
|
+
return {
|
|
5489
|
+
Component: DefaultComponent,
|
|
5490
|
+
slotProps: void 0
|
|
5491
|
+
};
|
|
5492
|
+
}
|
|
5493
|
+
/**
|
|
4116
5494
|
* Memoized wrapper for assistant messages to prevent re-renders when other messages change.
|
|
4117
5495
|
*/
|
|
4118
5496
|
const MemoizedAssistantMessage = React.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
|
|
@@ -4131,7 +5509,6 @@ const MemoizedAssistantMessage = React.memo(function MemoizedAssistantMessage({
|
|
|
4131
5509
|
if (prevToolCalls && nextToolCalls) for (let i = 0; i < prevToolCalls.length; i++) {
|
|
4132
5510
|
const prevTc = prevToolCalls[i];
|
|
4133
5511
|
const nextTc = nextToolCalls[i];
|
|
4134
|
-
if (!prevTc || !nextTc) return false;
|
|
4135
5512
|
if (prevTc.id !== nextTc.id) return false;
|
|
4136
5513
|
if (prevTc.function.arguments !== nextTc.function.arguments) return false;
|
|
4137
5514
|
}
|
|
@@ -4210,6 +5587,7 @@ const MemoizedCustomMessage = React.memo(function MemoizedCustomMessage({ messag
|
|
|
4210
5587
|
if (JSON.stringify(prevProps.stateSnapshot) !== JSON.stringify(nextProps.stateSnapshot)) return false;
|
|
4211
5588
|
return true;
|
|
4212
5589
|
});
|
|
5590
|
+
const VIRTUALIZE_THRESHOLD = 50;
|
|
4213
5591
|
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
|
|
4214
5592
|
const renderCustomMessage = useRenderCustomMessages();
|
|
4215
5593
|
const { renderActivityMessage } = useRenderActivityMessage();
|
|
@@ -4218,12 +5596,14 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4218
5596
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
4219
5597
|
useEffect(() => {
|
|
4220
5598
|
if (!config?.agentId) return;
|
|
4221
|
-
const
|
|
5599
|
+
const registryAgent = copilotkit.getAgent(config.agentId);
|
|
5600
|
+
const agent = getThreadClone(registryAgent, config.threadId) ?? registryAgent;
|
|
4222
5601
|
if (!agent) return;
|
|
4223
5602
|
const subscription = agent.subscribe({ onStateChanged: forceUpdate });
|
|
4224
5603
|
return () => subscription.unsubscribe();
|
|
4225
5604
|
}, [
|
|
4226
5605
|
config?.agentId,
|
|
5606
|
+
config?.threadId,
|
|
4227
5607
|
copilotkit,
|
|
4228
5608
|
forceUpdate
|
|
4229
5609
|
]);
|
|
@@ -4241,9 +5621,34 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4241
5621
|
if (!resolvedRunId) return void 0;
|
|
4242
5622
|
return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
|
|
4243
5623
|
};
|
|
4244
|
-
const deduplicatedMessages = [...new Map(messages.map((m) => [m.id, m])).values()];
|
|
5624
|
+
const deduplicatedMessages = useMemo(() => [...new Map(messages.map((m) => [m.id, m])).values()], [messages]);
|
|
4245
5625
|
if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Deduplicated ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
|
|
4246
|
-
const
|
|
5626
|
+
const { Component: AssistantComponent, slotProps: assistantSlotProps } = useMemo(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
|
|
5627
|
+
const { Component: UserComponent, slotProps: userSlotProps } = useMemo(() => resolveSlotComponent(userMessage, CopilotChatUserMessage_default), [userMessage]);
|
|
5628
|
+
const { Component: ReasoningComponent, slotProps: reasoningSlotProps } = useMemo(() => resolveSlotComponent(reasoningMessage, CopilotChatReasoningMessage_default), [reasoningMessage]);
|
|
5629
|
+
const scrollElementFromCtx = useContext(ScrollElementContext);
|
|
5630
|
+
const scrollElement = scrollElementFromCtx && scrollElementFromCtx.clientHeight > 0 ? scrollElementFromCtx : null;
|
|
5631
|
+
useEffect(() => {
|
|
5632
|
+
if (process.env.NODE_ENV !== "production" && scrollElementFromCtx && scrollElementFromCtx.clientHeight === 0) console.warn("[CopilotKit] Chat scroll container has clientHeight=0 — virtualization disabled. Ensure the chat is rendered in a visible container with a non-zero height.");
|
|
5633
|
+
}, [scrollElementFromCtx]);
|
|
5634
|
+
const shouldVirtualize = !!scrollElement && !children && deduplicatedMessages.length > VIRTUALIZE_THRESHOLD;
|
|
5635
|
+
const virtualizer = useVirtualizer({
|
|
5636
|
+
count: shouldVirtualize ? deduplicatedMessages.length : 0,
|
|
5637
|
+
getScrollElement: () => scrollElement,
|
|
5638
|
+
estimateSize: () => 100,
|
|
5639
|
+
overscan: 5,
|
|
5640
|
+
measureElement: (el) => el?.getBoundingClientRect().height ?? 0,
|
|
5641
|
+
initialRect: {
|
|
5642
|
+
width: 0,
|
|
5643
|
+
height: 600
|
|
5644
|
+
}
|
|
5645
|
+
});
|
|
5646
|
+
const firstMessageId = deduplicatedMessages[0]?.id;
|
|
5647
|
+
useLayoutEffect(() => {
|
|
5648
|
+
if (!shouldVirtualize || !deduplicatedMessages.length) return;
|
|
5649
|
+
virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
|
|
5650
|
+
}, [shouldVirtualize, firstMessageId]);
|
|
5651
|
+
const renderMessageBlock = (message) => {
|
|
4247
5652
|
const elements = [];
|
|
4248
5653
|
const stateSnapshot = getStateSnapshotForMessage(message.id);
|
|
4249
5654
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
|
|
@@ -4252,58 +5657,38 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4252
5657
|
renderCustomMessage,
|
|
4253
5658
|
stateSnapshot
|
|
4254
5659
|
}, `${message.id}-custom-before`));
|
|
4255
|
-
if (message.role === "assistant") {
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
}, message.id));
|
|
4279
|
-
} else if (message.role === "activity") {
|
|
4280
|
-
const activityMsg = message;
|
|
4281
|
-
elements.push(/* @__PURE__ */ jsx(MemoizedActivityMessage, {
|
|
4282
|
-
message: activityMsg,
|
|
4283
|
-
renderActivityMessage
|
|
4284
|
-
}, message.id));
|
|
4285
|
-
} else if (message.role === "reasoning") {
|
|
4286
|
-
let ReasoningComponent = CopilotChatReasoningMessage_default;
|
|
4287
|
-
let reasoningSlotProps;
|
|
4288
|
-
if (isReactComponentType(reasoningMessage)) ReasoningComponent = reasoningMessage;
|
|
4289
|
-
else if (typeof reasoningMessage === "string") reasoningSlotProps = { className: reasoningMessage };
|
|
4290
|
-
else if (reasoningMessage && typeof reasoningMessage === "object") reasoningSlotProps = reasoningMessage;
|
|
4291
|
-
elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
|
|
4292
|
-
message,
|
|
4293
|
-
messages,
|
|
4294
|
-
isRunning,
|
|
4295
|
-
ReasoningMessageComponent: ReasoningComponent,
|
|
4296
|
-
slotProps: reasoningSlotProps
|
|
4297
|
-
}, message.id));
|
|
4298
|
-
}
|
|
5660
|
+
if (message.role === "assistant") elements.push(/* @__PURE__ */ jsx(MemoizedAssistantMessage, {
|
|
5661
|
+
message,
|
|
5662
|
+
messages,
|
|
5663
|
+
isRunning,
|
|
5664
|
+
AssistantMessageComponent: AssistantComponent,
|
|
5665
|
+
slotProps: assistantSlotProps
|
|
5666
|
+
}, message.id));
|
|
5667
|
+
else if (message.role === "user") elements.push(/* @__PURE__ */ jsx(MemoizedUserMessage, {
|
|
5668
|
+
message,
|
|
5669
|
+
UserMessageComponent: UserComponent,
|
|
5670
|
+
slotProps: userSlotProps
|
|
5671
|
+
}, message.id));
|
|
5672
|
+
else if (message.role === "activity") elements.push(/* @__PURE__ */ jsx(MemoizedActivityMessage, {
|
|
5673
|
+
message,
|
|
5674
|
+
renderActivityMessage
|
|
5675
|
+
}, message.id));
|
|
5676
|
+
else if (message.role === "reasoning") elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
|
|
5677
|
+
message,
|
|
5678
|
+
messages,
|
|
5679
|
+
isRunning,
|
|
5680
|
+
ReasoningMessageComponent: ReasoningComponent,
|
|
5681
|
+
slotProps: reasoningSlotProps
|
|
5682
|
+
}, message.id));
|
|
4299
5683
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
|
|
4300
5684
|
message,
|
|
4301
5685
|
position: "after",
|
|
4302
5686
|
renderCustomMessage,
|
|
4303
5687
|
stateSnapshot
|
|
4304
5688
|
}, `${message.id}-custom-after`));
|
|
4305
|
-
return elements;
|
|
4306
|
-
}
|
|
5689
|
+
return elements.filter(Boolean);
|
|
5690
|
+
};
|
|
5691
|
+
const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
|
|
4307
5692
|
if (children) return /* @__PURE__ */ jsx("div", {
|
|
4308
5693
|
"data-copilotkit": true,
|
|
4309
5694
|
style: { display: "contents" },
|
|
@@ -4314,30 +5699,356 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
4314
5699
|
interruptElement
|
|
4315
5700
|
})
|
|
4316
5701
|
});
|
|
4317
|
-
const lastMessage = messages[messages.length - 1];
|
|
4318
|
-
const showCursor = isRunning && lastMessage?.role !== "reasoning";
|
|
5702
|
+
const lastMessage = messages[messages.length - 1];
|
|
5703
|
+
const showCursor = isRunning && lastMessage?.role !== "reasoning";
|
|
5704
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5705
|
+
"data-copilotkit": true,
|
|
5706
|
+
"data-testid": "copilot-message-list",
|
|
5707
|
+
className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
5708
|
+
...props,
|
|
5709
|
+
children: [
|
|
5710
|
+
shouldVirtualize ? /* @__PURE__ */ jsx("div", {
|
|
5711
|
+
style: {
|
|
5712
|
+
height: virtualizer.getTotalSize(),
|
|
5713
|
+
position: "relative"
|
|
5714
|
+
},
|
|
5715
|
+
children: virtualizer.getVirtualItems().map((virtualItem) => {
|
|
5716
|
+
const message = deduplicatedMessages[virtualItem.index];
|
|
5717
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5718
|
+
"data-index": virtualItem.index,
|
|
5719
|
+
ref: virtualizer.measureElement,
|
|
5720
|
+
style: {
|
|
5721
|
+
position: "absolute",
|
|
5722
|
+
top: 0,
|
|
5723
|
+
left: 0,
|
|
5724
|
+
width: "100%",
|
|
5725
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
5726
|
+
},
|
|
5727
|
+
children: renderMessageBlock(message)
|
|
5728
|
+
}, message.id);
|
|
5729
|
+
})
|
|
5730
|
+
}) : messageElements,
|
|
5731
|
+
interruptElement,
|
|
5732
|
+
showCursor && /* @__PURE__ */ jsx("div", {
|
|
5733
|
+
className: "cpk:mt-2",
|
|
5734
|
+
children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
|
|
5735
|
+
})
|
|
5736
|
+
]
|
|
5737
|
+
});
|
|
5738
|
+
}
|
|
5739
|
+
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
|
|
5740
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5741
|
+
"data-testid": "copilot-loading-cursor",
|
|
5742
|
+
className: twMerge("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
|
|
5743
|
+
...props
|
|
5744
|
+
});
|
|
5745
|
+
};
|
|
5746
|
+
|
|
5747
|
+
//#endregion
|
|
5748
|
+
//#region src/v2/components/chat/CopilotChatAttachmentQueue.tsx
|
|
5749
|
+
const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
|
|
5750
|
+
if (attachments.length === 0) return null;
|
|
5751
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5752
|
+
className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
|
|
5753
|
+
children: attachments.map((attachment) => {
|
|
5754
|
+
const isMedia = attachment.type === "image" || attachment.type === "video";
|
|
5755
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5756
|
+
className: cn("cpk:relative cpk:inline-flex cpk:rounded-lg cpk:overflow-hidden cpk:border cpk:border-border", isMedia ? "cpk:w-[72px] cpk:h-[72px]" : attachment.type === "audio" ? "cpk:min-w-[200px] cpk:max-w-[280px] cpk:flex-col cpk:p-1 cpk:pr-8" : "cpk:p-2 cpk:px-3 cpk:pr-8 cpk:max-w-[240px]"),
|
|
5757
|
+
children: [
|
|
5758
|
+
attachment.status === "uploading" && /* @__PURE__ */ jsx(UploadingOverlay, {}),
|
|
5759
|
+
/* @__PURE__ */ jsx(AttachmentPreview, { attachment }),
|
|
5760
|
+
/* @__PURE__ */ jsx("button", {
|
|
5761
|
+
onClick: () => onRemoveAttachment(attachment.id),
|
|
5762
|
+
className: cn("cpk:absolute cpk:bg-black/60 cpk:text-white cpk:border-none cpk:rounded-full cpk:w-5 cpk:h-5 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:text-[10px] cpk:z-20", isMedia ? "cpk:top-1 cpk:right-1" : "cpk:top-1.5 cpk:right-1.5"),
|
|
5763
|
+
"aria-label": "Remove attachment",
|
|
5764
|
+
children: "✕"
|
|
5765
|
+
})
|
|
5766
|
+
]
|
|
5767
|
+
}, attachment.id);
|
|
5768
|
+
})
|
|
5769
|
+
});
|
|
5770
|
+
};
|
|
5771
|
+
function UploadingOverlay() {
|
|
5772
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5773
|
+
className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/40 cpk:z-10",
|
|
5774
|
+
children: /* @__PURE__ */ jsx("div", { className: "cpk:w-5 cpk:h-5 cpk:border-2 cpk:border-white cpk:border-t-transparent cpk:rounded-full cpk:animate-spin" })
|
|
5775
|
+
});
|
|
5776
|
+
}
|
|
5777
|
+
function AttachmentPreview({ attachment }) {
|
|
5778
|
+
if (attachment.status === "uploading") return /* @__PURE__ */ jsx("div", { className: "cpk:w-full cpk:h-full" });
|
|
5779
|
+
switch (attachment.type) {
|
|
5780
|
+
case "image": return /* @__PURE__ */ jsx(ImagePreview, { attachment });
|
|
5781
|
+
case "audio": return /* @__PURE__ */ jsx(AudioPreview, { attachment });
|
|
5782
|
+
case "video": return /* @__PURE__ */ jsx(VideoPreview, { attachment });
|
|
5783
|
+
case "document": return /* @__PURE__ */ jsx(DocumentPreview, { attachment });
|
|
5784
|
+
}
|
|
5785
|
+
}
|
|
5786
|
+
function Lightbox({ onClose, children }) {
|
|
5787
|
+
useEffect(() => {
|
|
5788
|
+
const handleKey = (e) => {
|
|
5789
|
+
if (e.key === "Escape") onClose();
|
|
5790
|
+
};
|
|
5791
|
+
document.addEventListener("keydown", handleKey);
|
|
5792
|
+
return () => document.removeEventListener("keydown", handleKey);
|
|
5793
|
+
}, [onClose]);
|
|
5794
|
+
if (typeof document === "undefined") return null;
|
|
5795
|
+
return createPortal(/* @__PURE__ */ jsxs("div", {
|
|
5796
|
+
className: "cpk:fixed cpk:inset-0 cpk:z-[9999] cpk:flex cpk:items-center cpk:justify-center cpk:bg-black/80 cpk:animate-fade-in",
|
|
5797
|
+
onClick: onClose,
|
|
5798
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
5799
|
+
onClick: onClose,
|
|
5800
|
+
className: "cpk:absolute cpk:top-4 cpk:right-4 cpk:text-white cpk:bg-white/10 cpk:hover:bg-white/20 cpk:rounded-full cpk:w-10 cpk:h-10 cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer cpk:border-none cpk:z-10",
|
|
5801
|
+
"aria-label": "Close preview",
|
|
5802
|
+
children: /* @__PURE__ */ jsx(X, { className: "cpk:w-5 cpk:h-5" })
|
|
5803
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
5804
|
+
onClick: (e) => e.stopPropagation(),
|
|
5805
|
+
children
|
|
5806
|
+
})]
|
|
5807
|
+
}), document.body);
|
|
5808
|
+
}
|
|
5809
|
+
/**
|
|
5810
|
+
* Hook that manages lightbox open/close and uses the View Transition API to
|
|
5811
|
+
* morph the thumbnail into fullscreen content.
|
|
5812
|
+
*
|
|
5813
|
+
* The trick: `view-transition-name` must live on exactly ONE element at a time.
|
|
5814
|
+
* - Old state (thumbnail visible): name is on the thumbnail.
|
|
5815
|
+
* - New state (lightbox visible): name moves to the lightbox content.
|
|
5816
|
+
* `flushSync` ensures React commits the DOM change synchronously inside the
|
|
5817
|
+
* `startViewTransition` callback so the API can snapshot old → new correctly.
|
|
5818
|
+
*/
|
|
5819
|
+
function useLightbox() {
|
|
5820
|
+
const thumbnailRef = useRef(null);
|
|
5821
|
+
const [open, setOpen] = useState(false);
|
|
5822
|
+
const vtName = useId();
|
|
5823
|
+
return {
|
|
5824
|
+
thumbnailRef,
|
|
5825
|
+
vtName,
|
|
5826
|
+
open,
|
|
5827
|
+
openLightbox: useCallback(() => {
|
|
5828
|
+
const thumb = thumbnailRef.current;
|
|
5829
|
+
const doc = document;
|
|
5830
|
+
if (doc.startViewTransition && thumb) {
|
|
5831
|
+
thumb.style.viewTransitionName = vtName;
|
|
5832
|
+
doc.startViewTransition(() => {
|
|
5833
|
+
thumb.style.viewTransitionName = "";
|
|
5834
|
+
flushSync(() => setOpen(true));
|
|
5835
|
+
});
|
|
5836
|
+
} else setOpen(true);
|
|
5837
|
+
}, []),
|
|
5838
|
+
closeLightbox: useCallback(() => {
|
|
5839
|
+
const thumb = thumbnailRef.current;
|
|
5840
|
+
const doc = document;
|
|
5841
|
+
if (doc.startViewTransition && thumb) doc.startViewTransition(() => {
|
|
5842
|
+
flushSync(() => setOpen(false));
|
|
5843
|
+
thumb.style.viewTransitionName = vtName;
|
|
5844
|
+
}).finished.then(() => {
|
|
5845
|
+
thumb.style.viewTransitionName = "";
|
|
5846
|
+
}).catch(() => {
|
|
5847
|
+
thumb.style.viewTransitionName = "";
|
|
5848
|
+
});
|
|
5849
|
+
else setOpen(false);
|
|
5850
|
+
}, [])
|
|
5851
|
+
};
|
|
5852
|
+
}
|
|
5853
|
+
function ImagePreview({ attachment }) {
|
|
5854
|
+
const src = getSourceUrl(attachment.source);
|
|
5855
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
5856
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("img", {
|
|
5857
|
+
ref: thumbnailRef,
|
|
5858
|
+
src,
|
|
5859
|
+
alt: attachment.filename || "Image attachment",
|
|
5860
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover cpk:cursor-pointer",
|
|
5861
|
+
onClick: openLightbox
|
|
5862
|
+
}), open && /* @__PURE__ */ jsx(Lightbox, {
|
|
5863
|
+
onClose: closeLightbox,
|
|
5864
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
5865
|
+
style: { viewTransitionName: vtName },
|
|
5866
|
+
src,
|
|
5867
|
+
alt: attachment.filename || "Image attachment",
|
|
5868
|
+
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:object-contain cpk:rounded-lg"
|
|
5869
|
+
})
|
|
5870
|
+
})] });
|
|
5871
|
+
}
|
|
5872
|
+
function AudioPreview({ attachment }) {
|
|
5873
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5874
|
+
className: "cpk:flex cpk:flex-col cpk:gap-1 cpk:w-full",
|
|
5875
|
+
children: [/* @__PURE__ */ jsx("audio", {
|
|
5876
|
+
src: getSourceUrl(attachment.source),
|
|
5877
|
+
controls: true,
|
|
5878
|
+
preload: "metadata",
|
|
5879
|
+
className: "cpk:w-full cpk:h-8"
|
|
5880
|
+
}), attachment.filename && /* @__PURE__ */ jsx("span", {
|
|
5881
|
+
className: "cpk:text-xs cpk:font-medium cpk:overflow-hidden cpk:text-ellipsis cpk:whitespace-nowrap",
|
|
5882
|
+
children: attachment.filename
|
|
5883
|
+
})]
|
|
5884
|
+
});
|
|
5885
|
+
}
|
|
5886
|
+
function VideoPreview({ attachment }) {
|
|
5887
|
+
const src = getSourceUrl(attachment.source);
|
|
5888
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
5889
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
5890
|
+
/* @__PURE__ */ jsx("div", {
|
|
5891
|
+
ref: thumbnailRef,
|
|
5892
|
+
className: "cpk:w-full cpk:h-full",
|
|
5893
|
+
children: attachment.thumbnail ? /* @__PURE__ */ jsx("img", {
|
|
5894
|
+
src: attachment.thumbnail,
|
|
5895
|
+
alt: attachment.filename || "Video thumbnail",
|
|
5896
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover"
|
|
5897
|
+
}) : /* @__PURE__ */ jsx("video", {
|
|
5898
|
+
src,
|
|
5899
|
+
preload: "metadata",
|
|
5900
|
+
muted: true,
|
|
5901
|
+
className: "cpk:w-full cpk:h-full cpk:object-cover"
|
|
5902
|
+
})
|
|
5903
|
+
}),
|
|
5904
|
+
/* @__PURE__ */ jsx("button", {
|
|
5905
|
+
onClick: openLightbox,
|
|
5906
|
+
className: "cpk:absolute cpk:inset-0 cpk:flex cpk:items-center cpk:justify-center cpk:z-10 cpk:cursor-pointer cpk:bg-black/20 cpk:border-none cpk:p-0",
|
|
5907
|
+
"aria-label": "Play video",
|
|
5908
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
5909
|
+
className: "cpk:w-8 cpk:h-8 cpk:rounded-full cpk:bg-black/60 cpk:flex cpk:items-center cpk:justify-center",
|
|
5910
|
+
children: /* @__PURE__ */ jsx(Play, { className: "cpk:w-4 cpk:h-4 cpk:text-white cpk:ml-0.5" })
|
|
5911
|
+
})
|
|
5912
|
+
}),
|
|
5913
|
+
open && /* @__PURE__ */ jsx(Lightbox, {
|
|
5914
|
+
onClose: closeLightbox,
|
|
5915
|
+
children: /* @__PURE__ */ jsx("video", {
|
|
5916
|
+
style: { viewTransitionName: vtName },
|
|
5917
|
+
src,
|
|
5918
|
+
controls: true,
|
|
5919
|
+
autoPlay: true,
|
|
5920
|
+
className: "cpk:max-w-[90vw] cpk:max-h-[90vh] cpk:rounded-lg"
|
|
5921
|
+
})
|
|
5922
|
+
})
|
|
5923
|
+
] });
|
|
5924
|
+
}
|
|
5925
|
+
function isPdf(mimeType) {
|
|
5926
|
+
return !!mimeType && mimeType.includes("pdf");
|
|
5927
|
+
}
|
|
5928
|
+
function isText(mimeType) {
|
|
5929
|
+
return !!mimeType && mimeType.startsWith("text/");
|
|
5930
|
+
}
|
|
5931
|
+
function canPreviewInBrowser(mimeType) {
|
|
5932
|
+
return isPdf(mimeType) || isText(mimeType);
|
|
5933
|
+
}
|
|
5934
|
+
/**
|
|
5935
|
+
* Convert a base64-encoded data source to a blob: URL that browsers will
|
|
5936
|
+
* render inside an iframe (data: URLs are blocked for PDFs in most browsers).
|
|
5937
|
+
*/
|
|
5938
|
+
function useBlobUrl(attachment) {
|
|
5939
|
+
const [url, setUrl] = useState(null);
|
|
5940
|
+
useEffect(() => {
|
|
5941
|
+
if (attachment.source.type !== "data") return;
|
|
5942
|
+
try {
|
|
5943
|
+
const binary = atob(attachment.source.value);
|
|
5944
|
+
const bytes = new Uint8Array(binary.length);
|
|
5945
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
5946
|
+
const blob = new Blob([bytes], { type: attachment.source.mimeType || "application/octet-stream" });
|
|
5947
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
5948
|
+
setUrl(blobUrl);
|
|
5949
|
+
return () => URL.revokeObjectURL(blobUrl);
|
|
5950
|
+
} catch (error) {
|
|
5951
|
+
console.error("[CopilotKit] Failed to decode attachment data:", error);
|
|
5952
|
+
setUrl(null);
|
|
5953
|
+
}
|
|
5954
|
+
}, [
|
|
5955
|
+
attachment.source.type,
|
|
5956
|
+
attachment.source.value,
|
|
5957
|
+
attachment.source.mimeType
|
|
5958
|
+
]);
|
|
5959
|
+
if (attachment.source.type === "url") return attachment.source.value;
|
|
5960
|
+
return url;
|
|
5961
|
+
}
|
|
5962
|
+
function DocumentLightboxContent({ attachment, vtName }) {
|
|
5963
|
+
const mimeType = attachment.source.mimeType;
|
|
5964
|
+
const blobUrl = useBlobUrl(attachment);
|
|
5965
|
+
if (isPdf(mimeType)) {
|
|
5966
|
+
if (!blobUrl) return null;
|
|
5967
|
+
return /* @__PURE__ */ jsx("iframe", {
|
|
5968
|
+
style: { viewTransitionName: vtName },
|
|
5969
|
+
src: blobUrl,
|
|
5970
|
+
title: attachment.filename || "PDF preview",
|
|
5971
|
+
className: "cpk:w-[90vw] cpk:h-[90vh] cpk:max-w-[1000px] cpk:rounded-lg cpk:bg-white"
|
|
5972
|
+
});
|
|
5973
|
+
}
|
|
5974
|
+
if (isText(mimeType)) {
|
|
5975
|
+
const textContent = attachment.source.type === "data" ? (() => {
|
|
5976
|
+
try {
|
|
5977
|
+
return atob(attachment.source.value);
|
|
5978
|
+
} catch {
|
|
5979
|
+
return attachment.source.value;
|
|
5980
|
+
}
|
|
5981
|
+
})() : null;
|
|
5982
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5983
|
+
style: { viewTransitionName: vtName },
|
|
5984
|
+
className: "cpk:w-[90vw] cpk:max-w-[800px] cpk:max-h-[90vh] cpk:overflow-auto cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900 cpk:p-6",
|
|
5985
|
+
children: [attachment.filename && /* @__PURE__ */ jsx("div", {
|
|
5986
|
+
className: "cpk:text-sm cpk:font-medium cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mb-4 cpk:pb-2 cpk:border-b cpk:border-gray-200 cpk:dark:border-gray-700",
|
|
5987
|
+
children: attachment.filename
|
|
5988
|
+
}), textContent ? /* @__PURE__ */ jsx("pre", {
|
|
5989
|
+
className: "cpk:text-sm cpk:whitespace-pre-wrap cpk:break-words cpk:text-gray-800 cpk:dark:text-gray-200 cpk:font-mono cpk:m-0",
|
|
5990
|
+
children: textContent
|
|
5991
|
+
}) : blobUrl ? /* @__PURE__ */ jsx("iframe", {
|
|
5992
|
+
src: blobUrl,
|
|
5993
|
+
title: attachment.filename || "Text preview",
|
|
5994
|
+
className: "cpk:w-full cpk:h-[80vh] cpk:border-none"
|
|
5995
|
+
}) : null]
|
|
5996
|
+
});
|
|
5997
|
+
}
|
|
4319
5998
|
return /* @__PURE__ */ jsxs("div", {
|
|
4320
|
-
|
|
4321
|
-
"
|
|
4322
|
-
className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
4323
|
-
...props,
|
|
5999
|
+
style: { viewTransitionName: vtName },
|
|
6000
|
+
className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-4 cpk:p-8 cpk:rounded-lg cpk:bg-white cpk:dark:bg-gray-900",
|
|
4324
6001
|
children: [
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
6002
|
+
/* @__PURE__ */ jsx("div", {
|
|
6003
|
+
className: "cpk:w-16 cpk:h-16 cpk:rounded-xl cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-xl cpk:font-bold",
|
|
6004
|
+
children: getDocumentIcon(mimeType ?? "")
|
|
6005
|
+
}),
|
|
6006
|
+
/* @__PURE__ */ jsxs("div", {
|
|
6007
|
+
className: "cpk:text-center",
|
|
6008
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
6009
|
+
className: "cpk:text-base cpk:font-medium cpk:text-gray-800 cpk:dark:text-gray-200",
|
|
6010
|
+
children: attachment.filename || "Document"
|
|
6011
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
6012
|
+
className: "cpk:text-sm cpk:text-gray-500 cpk:dark:text-gray-400 cpk:mt-1",
|
|
6013
|
+
children: [mimeType || "Unknown type", attachment.size != null && ` · ${formatFileSize(attachment.size)}`]
|
|
6014
|
+
})]
|
|
6015
|
+
}),
|
|
6016
|
+
/* @__PURE__ */ jsx("div", {
|
|
6017
|
+
className: "cpk:text-xs cpk:text-gray-400 cpk:dark:text-gray-500",
|
|
6018
|
+
children: "No preview available for this file type"
|
|
4330
6019
|
})
|
|
4331
6020
|
]
|
|
4332
6021
|
});
|
|
4333
6022
|
}
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
6023
|
+
function DocumentPreview({ attachment }) {
|
|
6024
|
+
const { thumbnailRef, vtName, open, openLightbox, closeLightbox } = useLightbox();
|
|
6025
|
+
const mimeType = attachment.source.mimeType;
|
|
6026
|
+
const previewable = canPreviewInBrowser(mimeType);
|
|
6027
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
6028
|
+
ref: thumbnailRef,
|
|
6029
|
+
className: cn("cpk:flex cpk:items-center cpk:gap-2", previewable && "cpk:cursor-pointer"),
|
|
6030
|
+
onClick: previewable ? openLightbox : void 0,
|
|
6031
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
6032
|
+
className: "cpk:w-8 cpk:h-8 cpk:rounded-md cpk:bg-primary cpk:text-primary-foreground cpk:flex cpk:items-center cpk:justify-center cpk:text-[10px] cpk:font-semibold cpk:shrink-0",
|
|
6033
|
+
children: getDocumentIcon(mimeType ?? "")
|
|
6034
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
6035
|
+
className: "cpk:flex cpk:flex-col cpk:min-w-0",
|
|
6036
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
6037
|
+
className: "cpk:text-xs cpk:font-medium cpk:break-all cpk:leading-tight",
|
|
6038
|
+
children: attachment.filename || "Document"
|
|
6039
|
+
}), attachment.size != null && /* @__PURE__ */ jsx("span", {
|
|
6040
|
+
className: "cpk:text-[11px] cpk:text-muted-foreground",
|
|
6041
|
+
children: formatFileSize(attachment.size)
|
|
6042
|
+
})]
|
|
6043
|
+
})]
|
|
6044
|
+
}), open && /* @__PURE__ */ jsx(Lightbox, {
|
|
6045
|
+
onClose: closeLightbox,
|
|
6046
|
+
children: /* @__PURE__ */ jsx(DocumentLightboxContent, {
|
|
6047
|
+
attachment,
|
|
6048
|
+
vtName
|
|
6049
|
+
})
|
|
6050
|
+
})] });
|
|
6051
|
+
}
|
|
4341
6052
|
|
|
4342
6053
|
//#endregion
|
|
4343
6054
|
//#region src/v2/hooks/use-keyboard-height.tsx
|
|
@@ -4383,7 +6094,19 @@ function useKeyboardHeight() {
|
|
|
4383
6094
|
//#endregion
|
|
4384
6095
|
//#region src/v2/components/chat/CopilotChatView.tsx
|
|
4385
6096
|
const FEATHER_HEIGHT = 96;
|
|
4386
|
-
function
|
|
6097
|
+
function DropOverlay() {
|
|
6098
|
+
return /* @__PURE__ */ jsx("div", {
|
|
6099
|
+
className: cn("cpk:absolute cpk:inset-0 cpk:z-50 cpk:pointer-events-none", "cpk:flex cpk:items-center cpk:justify-center", "cpk:bg-primary/5 cpk:backdrop-blur-[2px]", "cpk:border-2 cpk:border-dashed cpk:border-primary/40 cpk:rounded-lg cpk:m-2"),
|
|
6100
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
6101
|
+
className: "cpk:flex cpk:flex-col cpk:items-center cpk:gap-2 cpk:text-primary/70",
|
|
6102
|
+
children: [/* @__PURE__ */ jsx(Upload, { className: "cpk:w-8 cpk:h-8" }), /* @__PURE__ */ jsx("span", {
|
|
6103
|
+
className: "cpk:text-sm cpk:font-medium",
|
|
6104
|
+
children: "Drop files here"
|
|
6105
|
+
})]
|
|
6106
|
+
})
|
|
6107
|
+
});
|
|
6108
|
+
}
|
|
6109
|
+
function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, disclaimer, children, className, ...props }) {
|
|
4387
6110
|
const inputContainerRef = useRef(null);
|
|
4388
6111
|
const [inputContainerHeight, setInputContainerHeight] = useState(0);
|
|
4389
6112
|
const [isResizing, setIsResizing] = useState(false);
|
|
@@ -4430,6 +6153,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4430
6153
|
onCancelTranscribe,
|
|
4431
6154
|
onFinishTranscribe,
|
|
4432
6155
|
onFinishTranscribeWithAudio,
|
|
6156
|
+
onAddFile,
|
|
4433
6157
|
positioning: "static",
|
|
4434
6158
|
keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
|
|
4435
6159
|
containerRef: inputContainerRef,
|
|
@@ -4470,21 +6194,34 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4470
6194
|
onCancelTranscribe,
|
|
4471
6195
|
onFinishTranscribe,
|
|
4472
6196
|
onFinishTranscribeWithAudio,
|
|
6197
|
+
onAddFile,
|
|
4473
6198
|
positioning: "static",
|
|
4474
6199
|
showDisclaimer: true,
|
|
4475
6200
|
...disclaimer !== void 0 ? { disclaimer } : {}
|
|
4476
6201
|
});
|
|
4477
|
-
const
|
|
4478
|
-
|
|
6202
|
+
const welcomeScreenSlot = welcomeScreen === true ? void 0 : welcomeScreen;
|
|
6203
|
+
const inputWithAttachments = /* @__PURE__ */ jsxs("div", {
|
|
6204
|
+
className: "cpk:w-full",
|
|
6205
|
+
children: [attachments && attachments.length > 0 && /* @__PURE__ */ jsx(CopilotChatAttachmentQueue, {
|
|
6206
|
+
attachments,
|
|
6207
|
+
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
|
|
6208
|
+
className: "cpk:mb-2"
|
|
6209
|
+
}), BoundInputForWelcome]
|
|
6210
|
+
});
|
|
6211
|
+
const BoundWelcomeScreen = renderSlot(welcomeScreenSlot, CopilotChatView.WelcomeScreen, {
|
|
6212
|
+
input: inputWithAttachments,
|
|
4479
6213
|
suggestionView: BoundSuggestionView ?? /* @__PURE__ */ jsx(Fragment$1, {})
|
|
4480
6214
|
});
|
|
4481
|
-
return /* @__PURE__ */
|
|
6215
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4482
6216
|
"data-copilotkit": true,
|
|
4483
6217
|
"data-testid": "copilot-chat",
|
|
4484
6218
|
"data-copilot-running": isRunning ? "true" : "false",
|
|
4485
|
-
|
|
6219
|
+
onDragOver,
|
|
6220
|
+
onDragLeave,
|
|
6221
|
+
onDrop,
|
|
6222
|
+
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
4486
6223
|
...props,
|
|
4487
|
-
children: BoundWelcomeScreen
|
|
6224
|
+
children: [dragOver && /* @__PURE__ */ jsx(DropOverlay, {}), BoundWelcomeScreen]
|
|
4488
6225
|
});
|
|
4489
6226
|
}
|
|
4490
6227
|
if (children) return /* @__PURE__ */ jsx("div", {
|
|
@@ -4501,39 +6238,66 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4501
6238
|
"data-copilotkit": true,
|
|
4502
6239
|
"data-testid": "copilot-chat",
|
|
4503
6240
|
"data-copilot-running": isRunning ? "true" : "false",
|
|
4504
|
-
|
|
6241
|
+
onDragOver,
|
|
6242
|
+
onDragLeave,
|
|
6243
|
+
onDrop,
|
|
6244
|
+
className: cn("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className),
|
|
4505
6245
|
...props,
|
|
4506
|
-
children: [
|
|
6246
|
+
children: [
|
|
6247
|
+
dragOver && /* @__PURE__ */ jsx(DropOverlay, {}),
|
|
6248
|
+
BoundScrollView,
|
|
6249
|
+
/* @__PURE__ */ jsx("div", {
|
|
6250
|
+
className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full",
|
|
6251
|
+
children: attachments && attachments.length > 0 && /* @__PURE__ */ jsx(CopilotChatAttachmentQueue, {
|
|
6252
|
+
attachments,
|
|
6253
|
+
onRemoveAttachment: (id) => onRemoveAttachment?.(id),
|
|
6254
|
+
className: "cpk:px-4"
|
|
6255
|
+
})
|
|
6256
|
+
}),
|
|
6257
|
+
BoundInput
|
|
6258
|
+
]
|
|
4507
6259
|
});
|
|
4508
6260
|
}
|
|
4509
6261
|
(function(_CopilotChatView) {
|
|
4510
6262
|
const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
|
|
4511
|
-
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
6263
|
+
const { isAtBottom, scrollToBottom, scrollRef } = useStickToBottomContext();
|
|
6264
|
+
const [scrollEl, setScrollEl] = useState(null);
|
|
6265
|
+
useLayoutEffect(() => {
|
|
6266
|
+
setScrollEl(scrollRef.current ?? null);
|
|
6267
|
+
}, []);
|
|
4512
6268
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
4513
|
-
return /* @__PURE__ */
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
children
|
|
6269
|
+
return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
|
|
6270
|
+
value: scrollEl,
|
|
6271
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
6272
|
+
/* @__PURE__ */ jsx(StickToBottom.Content, {
|
|
6273
|
+
className: "cpk:overflow-y-auto cpk:overflow-x-hidden",
|
|
6274
|
+
style: {
|
|
6275
|
+
flex: "1 1 0%",
|
|
6276
|
+
minHeight: 0
|
|
6277
|
+
},
|
|
6278
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
6279
|
+
className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
6280
|
+
children
|
|
6281
|
+
})
|
|
6282
|
+
}),
|
|
6283
|
+
BoundFeather,
|
|
6284
|
+
!isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", {
|
|
6285
|
+
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6286
|
+
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
6287
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
4523
6288
|
})
|
|
4524
|
-
})
|
|
4525
|
-
|
|
4526
|
-
!isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", {
|
|
4527
|
-
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
4528
|
-
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
4529
|
-
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
4530
|
-
})
|
|
4531
|
-
] });
|
|
6289
|
+
] })
|
|
6290
|
+
});
|
|
4532
6291
|
};
|
|
4533
6292
|
_CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
|
|
4534
6293
|
const [hasMounted, setHasMounted] = useState(false);
|
|
4535
6294
|
const { scrollRef, contentRef, scrollToBottom } = useStickToBottom();
|
|
4536
6295
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
6296
|
+
const [nonAutoScrollEl, setNonAutoScrollEl] = useState(null);
|
|
6297
|
+
const nonAutoScrollRefCallback = useCallback((el) => {
|
|
6298
|
+
scrollRef.current = el;
|
|
6299
|
+
setNonAutoScrollEl(el);
|
|
6300
|
+
}, []);
|
|
4537
6301
|
useEffect(() => {
|
|
4538
6302
|
setHasMounted(true);
|
|
4539
6303
|
}, []);
|
|
@@ -4562,23 +6326,26 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
|
|
|
4562
6326
|
});
|
|
4563
6327
|
if (!autoScroll) {
|
|
4564
6328
|
const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
|
|
4565
|
-
return /* @__PURE__ */
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
6329
|
+
return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
|
|
6330
|
+
value: nonAutoScrollEl,
|
|
6331
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
6332
|
+
ref: nonAutoScrollRefCallback,
|
|
6333
|
+
className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden cpk:relative", className),
|
|
6334
|
+
...props,
|
|
6335
|
+
children: [
|
|
6336
|
+
/* @__PURE__ */ jsx("div", {
|
|
6337
|
+
ref: contentRef,
|
|
6338
|
+
className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
|
|
6339
|
+
children
|
|
6340
|
+
}),
|
|
6341
|
+
BoundFeather,
|
|
6342
|
+
showScrollButton && !isResizing && /* @__PURE__ */ jsx("div", {
|
|
6343
|
+
className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
|
|
6344
|
+
style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
|
|
6345
|
+
children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
|
|
6346
|
+
})
|
|
6347
|
+
]
|
|
6348
|
+
})
|
|
4582
6349
|
});
|
|
4583
6350
|
}
|
|
4584
6351
|
return /* @__PURE__ */ jsx(StickToBottom, {
|
|
@@ -4770,11 +6537,15 @@ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
|
|
|
4770
6537
|
|
|
4771
6538
|
//#endregion
|
|
4772
6539
|
//#region src/v2/components/chat/CopilotChat.tsx
|
|
4773
|
-
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, onError, ...props }) {
|
|
6540
|
+
function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
|
|
4774
6541
|
const existingConfig = useCopilotChatConfiguration();
|
|
4775
6542
|
const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
4776
6543
|
const resolvedThreadId = useMemo(() => threadId ?? existingConfig?.threadId ?? randomUUID(), [threadId, existingConfig?.threadId]);
|
|
4777
|
-
const { agent } = useAgent({
|
|
6544
|
+
const { agent } = useAgent({
|
|
6545
|
+
agentId: resolvedAgentId,
|
|
6546
|
+
threadId: resolvedThreadId,
|
|
6547
|
+
throttleMs
|
|
6548
|
+
});
|
|
4778
6549
|
const { copilotkit } = useCopilotKit();
|
|
4779
6550
|
const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
|
|
4780
6551
|
const { checkFeature } = useLicenseContext();
|
|
@@ -4803,6 +6574,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4803
6574
|
const [inputValue, setInputValue] = useState("");
|
|
4804
6575
|
const [transcriptionError, setTranscriptionError] = useState(null);
|
|
4805
6576
|
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
6577
|
+
const { attachments: selectedAttachments, enabled: attachmentsEnabled, dragOver, fileInputRef, containerRef: chatContainerRef, handleFileUpload, handleDragOver, handleDragLeave, handleDrop, removeAttachment, consumeAttachments } = useAttachments({ config: attachmentsConfig });
|
|
4806
6578
|
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
|
|
4807
6579
|
const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
|
|
4808
6580
|
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
|
|
@@ -4818,7 +6590,6 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4818
6590
|
console.error("CopilotChat: connectAgent failed", error);
|
|
4819
6591
|
}
|
|
4820
6592
|
};
|
|
4821
|
-
agent.threadId = resolvedThreadId;
|
|
4822
6593
|
connect(agent);
|
|
4823
6594
|
return () => {
|
|
4824
6595
|
detached = true;
|
|
@@ -4831,7 +6602,31 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4831
6602
|
resolvedAgentId
|
|
4832
6603
|
]);
|
|
4833
6604
|
const onSubmitInput = useCallback(async (value) => {
|
|
4834
|
-
|
|
6605
|
+
if (selectedAttachments.some((a) => a.status === "uploading")) {
|
|
6606
|
+
console.error("[CopilotKit] Cannot send while attachments are uploading");
|
|
6607
|
+
return;
|
|
6608
|
+
}
|
|
6609
|
+
const readyAttachments = consumeAttachments();
|
|
6610
|
+
if (readyAttachments.length > 0) {
|
|
6611
|
+
const contentParts = [];
|
|
6612
|
+
if (value.trim()) contentParts.push({
|
|
6613
|
+
type: "text",
|
|
6614
|
+
text: value
|
|
6615
|
+
});
|
|
6616
|
+
for (const att of readyAttachments) contentParts.push({
|
|
6617
|
+
type: att.type,
|
|
6618
|
+
source: att.source,
|
|
6619
|
+
metadata: {
|
|
6620
|
+
...att.filename ? { filename: att.filename } : {},
|
|
6621
|
+
...att.metadata
|
|
6622
|
+
}
|
|
6623
|
+
});
|
|
6624
|
+
agent.addMessage({
|
|
6625
|
+
id: randomUUID(),
|
|
6626
|
+
role: "user",
|
|
6627
|
+
content: contentParts
|
|
6628
|
+
});
|
|
6629
|
+
} else agent.addMessage({
|
|
4835
6630
|
id: randomUUID(),
|
|
4836
6631
|
role: "user",
|
|
4837
6632
|
content: value
|
|
@@ -4842,7 +6637,11 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4842
6637
|
} catch (error) {
|
|
4843
6638
|
console.error("CopilotChat: runAgent failed", error);
|
|
4844
6639
|
}
|
|
4845
|
-
}, [
|
|
6640
|
+
}, [
|
|
6641
|
+
agent,
|
|
6642
|
+
selectedAttachments,
|
|
6643
|
+
consumeAttachments
|
|
6644
|
+
]);
|
|
4846
6645
|
const handleSelectSuggestion = useCallback(async (suggestion) => {
|
|
4847
6646
|
agent.addMessage({
|
|
4848
6647
|
id: randomUUID(),
|
|
@@ -4929,21 +6728,33 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4929
6728
|
return () => clearTimeout(timer);
|
|
4930
6729
|
}
|
|
4931
6730
|
}, [transcriptionError]);
|
|
4932
|
-
const
|
|
6731
|
+
const stableMessageView = useShallowStableRef(typeof providedMessageView === "string" ? { className: providedMessageView } : providedMessageView);
|
|
6732
|
+
const stableSuggestionView = useShallowStableRef(providedSuggestionView);
|
|
6733
|
+
const handleAddFile = useCallback(() => {
|
|
6734
|
+
setTimeout(() => {
|
|
6735
|
+
fileInputRef.current?.click();
|
|
6736
|
+
}, 100);
|
|
6737
|
+
}, []);
|
|
6738
|
+
const mergedProps = {
|
|
4933
6739
|
isRunning: agent.isRunning,
|
|
4934
6740
|
suggestions: autoSuggestions,
|
|
4935
6741
|
onSelectSuggestion: handleSelectSuggestion,
|
|
4936
|
-
suggestionView:
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
});
|
|
6742
|
+
suggestionView: stableSuggestionView,
|
|
6743
|
+
...restProps
|
|
6744
|
+
};
|
|
6745
|
+
if (stableMessageView !== void 0) mergedProps.messageView = stableMessageView;
|
|
4941
6746
|
const hasMessages = agent.messages.length > 0;
|
|
4942
6747
|
const effectiveStopHandler = agent.isRunning && hasMessages ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
|
|
4943
6748
|
const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
|
|
4944
6749
|
const effectiveMode = isTranscribing ? "processing" : transcribeMode;
|
|
4945
|
-
const
|
|
4946
|
-
|
|
6750
|
+
const messages = useMemo(() => [...agent.messages], [agent.messages.map((m) => {
|
|
6751
|
+
const contentKey = typeof m.content === "string" ? m.content.length : Array.isArray(m.content) ? m.content.length : 0;
|
|
6752
|
+
const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
|
|
6753
|
+
return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
|
|
6754
|
+
}).join(",")]);
|
|
6755
|
+
const RenderedChatView = renderSlot(chatView, CopilotChatView, {
|
|
6756
|
+
...mergedProps,
|
|
6757
|
+
messages,
|
|
4947
6758
|
onSubmitMessage: onSubmitInput,
|
|
4948
6759
|
onStop: effectiveStopHandler,
|
|
4949
6760
|
inputMode: effectiveMode,
|
|
@@ -4952,32 +6763,51 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
|
|
|
4952
6763
|
onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
|
|
4953
6764
|
onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
|
|
4954
6765
|
onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
|
|
4955
|
-
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0
|
|
4956
|
-
|
|
4957
|
-
|
|
6766
|
+
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0,
|
|
6767
|
+
attachments: selectedAttachments,
|
|
6768
|
+
onRemoveAttachment: removeAttachment,
|
|
6769
|
+
onAddFile: attachmentsEnabled ? handleAddFile : void 0,
|
|
6770
|
+
dragOver,
|
|
6771
|
+
onDragOver: handleDragOver,
|
|
6772
|
+
onDragLeave: handleDragLeave,
|
|
6773
|
+
onDrop: handleDrop
|
|
6774
|
+
});
|
|
6775
|
+
return /* @__PURE__ */ jsx(CopilotChatConfigurationProvider, {
|
|
4958
6776
|
agentId: resolvedAgentId,
|
|
4959
6777
|
threadId: resolvedThreadId,
|
|
4960
6778
|
labels,
|
|
4961
6779
|
isModalDefaultOpen,
|
|
4962
|
-
children:
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
6780
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
6781
|
+
ref: chatContainerRef,
|
|
6782
|
+
style: { display: "contents" },
|
|
6783
|
+
children: [
|
|
6784
|
+
attachmentsEnabled && /* @__PURE__ */ jsx("input", {
|
|
6785
|
+
type: "file",
|
|
6786
|
+
multiple: true,
|
|
6787
|
+
ref: fileInputRef,
|
|
6788
|
+
onChange: handleFileUpload,
|
|
6789
|
+
accept: attachmentsConfig?.accept ?? "*/*",
|
|
6790
|
+
style: { display: "none" }
|
|
6791
|
+
}),
|
|
6792
|
+
!isChatLicensed && /* @__PURE__ */ jsx(InlineFeatureWarning, { featureName: "Chat" }),
|
|
6793
|
+
transcriptionError && /* @__PURE__ */ jsx("div", {
|
|
6794
|
+
style: {
|
|
6795
|
+
position: "absolute",
|
|
6796
|
+
bottom: "100px",
|
|
6797
|
+
left: "50%",
|
|
6798
|
+
transform: "translateX(-50%)",
|
|
6799
|
+
backgroundColor: "#ef4444",
|
|
6800
|
+
color: "white",
|
|
6801
|
+
padding: "8px 16px",
|
|
6802
|
+
borderRadius: "8px",
|
|
6803
|
+
fontSize: "14px",
|
|
6804
|
+
zIndex: 50
|
|
6805
|
+
},
|
|
6806
|
+
children: transcriptionError
|
|
6807
|
+
}),
|
|
6808
|
+
RenderedChatView
|
|
6809
|
+
]
|
|
6810
|
+
})
|
|
4981
6811
|
});
|
|
4982
6812
|
}
|
|
4983
6813
|
(function(_CopilotChat) {
|
|
@@ -5842,6 +7672,227 @@ function useToast() {
|
|
|
5842
7672
|
if (!context) throw new Error("useToast must be used within a ToastProvider");
|
|
5843
7673
|
return context;
|
|
5844
7674
|
}
|
|
7675
|
+
function formatBannerMessage(message) {
|
|
7676
|
+
const jsonMatch = message.match(/'message':\s*'([^']+)'/);
|
|
7677
|
+
if (jsonMatch) return jsonMatch[1];
|
|
7678
|
+
let cleaned = message.split(" - ")[0];
|
|
7679
|
+
cleaned = cleaned.split(": Error code")[0];
|
|
7680
|
+
cleaned = cleaned.replace(/:\s*\d{3}$/, "");
|
|
7681
|
+
cleaned = cleaned.replace(/See more:.*$/g, "");
|
|
7682
|
+
cleaned = cleaned.trim();
|
|
7683
|
+
return cleaned || "An error occurred.";
|
|
7684
|
+
}
|
|
7685
|
+
function extractUrl(message) {
|
|
7686
|
+
const markdownMatch = /\[([^\]]+)\]\(([^)]+)\)/.exec(message);
|
|
7687
|
+
if (markdownMatch) return {
|
|
7688
|
+
url: markdownMatch[2],
|
|
7689
|
+
text: "See More"
|
|
7690
|
+
};
|
|
7691
|
+
const plainMatch = /(https?:\/\/[^\s)]+)/.exec(message);
|
|
7692
|
+
if (plainMatch) return {
|
|
7693
|
+
url: plainMatch[0].replace(/[.,;:'"]*$/, ""),
|
|
7694
|
+
text: "See More"
|
|
7695
|
+
};
|
|
7696
|
+
return null;
|
|
7697
|
+
}
|
|
7698
|
+
function BannerErrorDisplay({ bannerError, onDismiss }) {
|
|
7699
|
+
const [detailsExpanded, setDetailsExpanded] = useState(false);
|
|
7700
|
+
const colors = getErrorColors(getErrorSeverity(bannerError));
|
|
7701
|
+
const details = bannerError.details;
|
|
7702
|
+
const link = extractUrl(bannerError.message);
|
|
7703
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
7704
|
+
style: {
|
|
7705
|
+
position: "fixed",
|
|
7706
|
+
bottom: "20px",
|
|
7707
|
+
left: "50%",
|
|
7708
|
+
transform: "translateX(-50%)",
|
|
7709
|
+
zIndex: 9999,
|
|
7710
|
+
backgroundColor: colors.background,
|
|
7711
|
+
border: `1px solid ${colors.border}`,
|
|
7712
|
+
borderLeft: `4px solid ${colors.border}`,
|
|
7713
|
+
borderRadius: "8px",
|
|
7714
|
+
padding: "12px 16px",
|
|
7715
|
+
fontSize: "13px",
|
|
7716
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
7717
|
+
backdropFilter: "blur(8px)",
|
|
7718
|
+
maxWidth: "min(90vw, 700px)",
|
|
7719
|
+
width: "100%",
|
|
7720
|
+
boxSizing: "border-box",
|
|
7721
|
+
overflow: "hidden"
|
|
7722
|
+
},
|
|
7723
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
7724
|
+
style: {
|
|
7725
|
+
display: "flex",
|
|
7726
|
+
justifyContent: "space-between",
|
|
7727
|
+
alignItems: "center",
|
|
7728
|
+
gap: "10px"
|
|
7729
|
+
},
|
|
7730
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
7731
|
+
style: {
|
|
7732
|
+
display: "flex",
|
|
7733
|
+
alignItems: "center",
|
|
7734
|
+
gap: "8px",
|
|
7735
|
+
flex: 1,
|
|
7736
|
+
minWidth: 0
|
|
7737
|
+
},
|
|
7738
|
+
children: [/* @__PURE__ */ jsx("div", { style: {
|
|
7739
|
+
width: "12px",
|
|
7740
|
+
height: "12px",
|
|
7741
|
+
borderRadius: "50%",
|
|
7742
|
+
backgroundColor: colors.border,
|
|
7743
|
+
flexShrink: 0
|
|
7744
|
+
} }), /* @__PURE__ */ jsxs("div", {
|
|
7745
|
+
style: {
|
|
7746
|
+
display: "flex",
|
|
7747
|
+
alignItems: "center",
|
|
7748
|
+
gap: "10px",
|
|
7749
|
+
flex: 1,
|
|
7750
|
+
minWidth: 0
|
|
7751
|
+
},
|
|
7752
|
+
children: [
|
|
7753
|
+
/* @__PURE__ */ jsx("div", {
|
|
7754
|
+
style: {
|
|
7755
|
+
color: colors.text,
|
|
7756
|
+
lineHeight: "1.4",
|
|
7757
|
+
fontWeight: "400",
|
|
7758
|
+
fontSize: "13px",
|
|
7759
|
+
flex: 1,
|
|
7760
|
+
wordBreak: "break-all",
|
|
7761
|
+
overflowWrap: "break-word",
|
|
7762
|
+
maxWidth: "550px",
|
|
7763
|
+
overflow: "hidden",
|
|
7764
|
+
display: "-webkit-box",
|
|
7765
|
+
WebkitLineClamp: 10,
|
|
7766
|
+
WebkitBoxOrient: "vertical"
|
|
7767
|
+
},
|
|
7768
|
+
children: formatBannerMessage(bannerError.message)
|
|
7769
|
+
}),
|
|
7770
|
+
link && /* @__PURE__ */ jsx("button", {
|
|
7771
|
+
onClick: () => window.open(link.url, "_blank", "noopener,noreferrer"),
|
|
7772
|
+
style: {
|
|
7773
|
+
background: colors.border,
|
|
7774
|
+
color: "white",
|
|
7775
|
+
border: "none",
|
|
7776
|
+
borderRadius: "5px",
|
|
7777
|
+
padding: "4px 10px",
|
|
7778
|
+
fontSize: "11px",
|
|
7779
|
+
fontWeight: "500",
|
|
7780
|
+
cursor: "pointer",
|
|
7781
|
+
transition: "all 0.2s ease",
|
|
7782
|
+
flexShrink: 0
|
|
7783
|
+
},
|
|
7784
|
+
onMouseEnter: (e) => {
|
|
7785
|
+
e.currentTarget.style.opacity = "0.9";
|
|
7786
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
7787
|
+
},
|
|
7788
|
+
onMouseLeave: (e) => {
|
|
7789
|
+
e.currentTarget.style.opacity = "1";
|
|
7790
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
7791
|
+
},
|
|
7792
|
+
children: link.text
|
|
7793
|
+
}),
|
|
7794
|
+
details && /* @__PURE__ */ jsx("button", {
|
|
7795
|
+
onClick: () => setDetailsExpanded(!detailsExpanded),
|
|
7796
|
+
style: {
|
|
7797
|
+
background: "transparent",
|
|
7798
|
+
border: `1px solid ${colors.border}`,
|
|
7799
|
+
borderRadius: "5px",
|
|
7800
|
+
padding: "4px 10px",
|
|
7801
|
+
fontSize: "11px",
|
|
7802
|
+
fontWeight: "500",
|
|
7803
|
+
cursor: "pointer",
|
|
7804
|
+
color: colors.text,
|
|
7805
|
+
flexShrink: 0,
|
|
7806
|
+
transition: "all 0.2s ease"
|
|
7807
|
+
},
|
|
7808
|
+
onMouseEnter: (e) => {
|
|
7809
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
7810
|
+
},
|
|
7811
|
+
onMouseLeave: (e) => {
|
|
7812
|
+
e.currentTarget.style.background = "transparent";
|
|
7813
|
+
},
|
|
7814
|
+
children: detailsExpanded ? "Hide Details" : "Show Details"
|
|
7815
|
+
})
|
|
7816
|
+
]
|
|
7817
|
+
})]
|
|
7818
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
7819
|
+
onClick: onDismiss,
|
|
7820
|
+
style: {
|
|
7821
|
+
background: "transparent",
|
|
7822
|
+
border: "none",
|
|
7823
|
+
color: colors.text,
|
|
7824
|
+
cursor: "pointer",
|
|
7825
|
+
padding: "2px",
|
|
7826
|
+
borderRadius: "3px",
|
|
7827
|
+
fontSize: "14px",
|
|
7828
|
+
lineHeight: "1",
|
|
7829
|
+
opacity: .6,
|
|
7830
|
+
transition: "all 0.2s ease",
|
|
7831
|
+
flexShrink: 0
|
|
7832
|
+
},
|
|
7833
|
+
title: "Dismiss",
|
|
7834
|
+
onMouseEnter: (e) => {
|
|
7835
|
+
e.currentTarget.style.opacity = "1";
|
|
7836
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
7837
|
+
},
|
|
7838
|
+
onMouseLeave: (e) => {
|
|
7839
|
+
e.currentTarget.style.opacity = "0.6";
|
|
7840
|
+
e.currentTarget.style.background = "transparent";
|
|
7841
|
+
},
|
|
7842
|
+
children: "x"
|
|
7843
|
+
})]
|
|
7844
|
+
}), detailsExpanded && details && /* @__PURE__ */ jsxs("div", {
|
|
7845
|
+
style: {
|
|
7846
|
+
marginTop: "10px",
|
|
7847
|
+
padding: "10px",
|
|
7848
|
+
background: "rgba(0, 0, 0, 0.04)",
|
|
7849
|
+
borderRadius: "6px",
|
|
7850
|
+
fontSize: "11px",
|
|
7851
|
+
fontFamily: "monospace",
|
|
7852
|
+
color: colors.text,
|
|
7853
|
+
lineHeight: "1.5",
|
|
7854
|
+
maxHeight: "200px",
|
|
7855
|
+
overflowY: "auto",
|
|
7856
|
+
whiteSpace: "pre-wrap",
|
|
7857
|
+
wordBreak: "break-all"
|
|
7858
|
+
},
|
|
7859
|
+
children: [
|
|
7860
|
+
details.code && /* @__PURE__ */ jsxs("div", { children: [
|
|
7861
|
+
/* @__PURE__ */ jsx("strong", { children: "Code:" }),
|
|
7862
|
+
" ",
|
|
7863
|
+
details.code
|
|
7864
|
+
] }),
|
|
7865
|
+
details.originalMessage && /* @__PURE__ */ jsxs("div", {
|
|
7866
|
+
style: { marginTop: "4px" },
|
|
7867
|
+
children: [
|
|
7868
|
+
/* @__PURE__ */ jsx("strong", { children: "Message:" }),
|
|
7869
|
+
" ",
|
|
7870
|
+
details.originalMessage
|
|
7871
|
+
]
|
|
7872
|
+
}),
|
|
7873
|
+
details.context && Object.keys(details.context).length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
7874
|
+
style: { marginTop: "4px" },
|
|
7875
|
+
children: [
|
|
7876
|
+
/* @__PURE__ */ jsx("strong", { children: "Context:" }),
|
|
7877
|
+
" ",
|
|
7878
|
+
JSON.stringify(details.context, null, 2)
|
|
7879
|
+
]
|
|
7880
|
+
}),
|
|
7881
|
+
details.stack && /* @__PURE__ */ jsxs("div", {
|
|
7882
|
+
style: {
|
|
7883
|
+
marginTop: "4px",
|
|
7884
|
+
opacity: .7
|
|
7885
|
+
},
|
|
7886
|
+
children: [
|
|
7887
|
+
/* @__PURE__ */ jsx("strong", { children: "Stack:" }),
|
|
7888
|
+
"\n",
|
|
7889
|
+
details.stack
|
|
7890
|
+
]
|
|
7891
|
+
})
|
|
7892
|
+
]
|
|
7893
|
+
})]
|
|
7894
|
+
});
|
|
7895
|
+
}
|
|
5845
7896
|
function ToastProvider({ enabled, children }) {
|
|
5846
7897
|
const [toasts, setToasts] = useState([]);
|
|
5847
7898
|
const [bannerError, setBannerErrorState] = useState(null);
|
|
@@ -5879,156 +7930,10 @@ function ToastProvider({ enabled, children }) {
|
|
|
5879
7930
|
};
|
|
5880
7931
|
return /* @__PURE__ */ jsxs(ToastContext.Provider, {
|
|
5881
7932
|
value,
|
|
5882
|
-
children: [bannerError && (
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
position: "fixed",
|
|
5887
|
-
bottom: "20px",
|
|
5888
|
-
left: "50%",
|
|
5889
|
-
transform: "translateX(-50%)",
|
|
5890
|
-
zIndex: 9999,
|
|
5891
|
-
backgroundColor: colors.background,
|
|
5892
|
-
border: `1px solid ${colors.border}`,
|
|
5893
|
-
borderLeft: `4px solid ${colors.border}`,
|
|
5894
|
-
borderRadius: "8px",
|
|
5895
|
-
padding: "12px 16px",
|
|
5896
|
-
fontSize: "13px",
|
|
5897
|
-
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
5898
|
-
backdropFilter: "blur(8px)",
|
|
5899
|
-
maxWidth: "min(90vw, 700px)",
|
|
5900
|
-
width: "100%",
|
|
5901
|
-
boxSizing: "border-box",
|
|
5902
|
-
overflow: "hidden"
|
|
5903
|
-
},
|
|
5904
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
5905
|
-
style: {
|
|
5906
|
-
display: "flex",
|
|
5907
|
-
justifyContent: "space-between",
|
|
5908
|
-
alignItems: "center",
|
|
5909
|
-
gap: "10px"
|
|
5910
|
-
},
|
|
5911
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
5912
|
-
style: {
|
|
5913
|
-
display: "flex",
|
|
5914
|
-
alignItems: "center",
|
|
5915
|
-
gap: "8px",
|
|
5916
|
-
flex: 1,
|
|
5917
|
-
minWidth: 0
|
|
5918
|
-
},
|
|
5919
|
-
children: [/* @__PURE__ */ jsx("div", { style: {
|
|
5920
|
-
width: "12px",
|
|
5921
|
-
height: "12px",
|
|
5922
|
-
borderRadius: "50%",
|
|
5923
|
-
backgroundColor: colors.border,
|
|
5924
|
-
flexShrink: 0
|
|
5925
|
-
} }), /* @__PURE__ */ jsxs("div", {
|
|
5926
|
-
style: {
|
|
5927
|
-
display: "flex",
|
|
5928
|
-
alignItems: "center",
|
|
5929
|
-
gap: "10px",
|
|
5930
|
-
flex: 1,
|
|
5931
|
-
minWidth: 0
|
|
5932
|
-
},
|
|
5933
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
5934
|
-
style: {
|
|
5935
|
-
color: colors.text,
|
|
5936
|
-
lineHeight: "1.4",
|
|
5937
|
-
fontWeight: "400",
|
|
5938
|
-
fontSize: "13px",
|
|
5939
|
-
flex: 1,
|
|
5940
|
-
wordBreak: "break-all",
|
|
5941
|
-
overflowWrap: "break-word",
|
|
5942
|
-
maxWidth: "550px",
|
|
5943
|
-
overflow: "hidden",
|
|
5944
|
-
display: "-webkit-box",
|
|
5945
|
-
WebkitLineClamp: 10,
|
|
5946
|
-
WebkitBoxOrient: "vertical"
|
|
5947
|
-
},
|
|
5948
|
-
children: (() => {
|
|
5949
|
-
let message = bannerError.message;
|
|
5950
|
-
const jsonMatch = message.match(/'message':\s*'([^']+)'/);
|
|
5951
|
-
if (jsonMatch) return jsonMatch[1];
|
|
5952
|
-
message = message.split(" - ")[0];
|
|
5953
|
-
message = message.split(": Error code")[0];
|
|
5954
|
-
message = message.replace(/:\s*\d{3}$/, "");
|
|
5955
|
-
message = message.replace(/See more:.*$/g, "");
|
|
5956
|
-
message = message.trim();
|
|
5957
|
-
return message || "Configuration error occurred.";
|
|
5958
|
-
})()
|
|
5959
|
-
}), (() => {
|
|
5960
|
-
const message = bannerError.message;
|
|
5961
|
-
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
5962
|
-
const plainUrlRegex = /(https?:\/\/[^\s)]+)/g;
|
|
5963
|
-
let url = null;
|
|
5964
|
-
let buttonText = "See More";
|
|
5965
|
-
const markdownMatch = markdownLinkRegex.exec(message);
|
|
5966
|
-
if (markdownMatch) {
|
|
5967
|
-
url = markdownMatch[2];
|
|
5968
|
-
buttonText = "See More";
|
|
5969
|
-
} else {
|
|
5970
|
-
const urlMatch = plainUrlRegex.exec(message);
|
|
5971
|
-
if (urlMatch) {
|
|
5972
|
-
url = urlMatch[0].replace(/[.,;:'"]*$/, "");
|
|
5973
|
-
buttonText = "See More";
|
|
5974
|
-
}
|
|
5975
|
-
}
|
|
5976
|
-
if (!url) return null;
|
|
5977
|
-
return /* @__PURE__ */ jsx("button", {
|
|
5978
|
-
onClick: () => window.open(url, "_blank", "noopener,noreferrer"),
|
|
5979
|
-
style: {
|
|
5980
|
-
background: colors.border,
|
|
5981
|
-
color: "white",
|
|
5982
|
-
border: "none",
|
|
5983
|
-
borderRadius: "5px",
|
|
5984
|
-
padding: "4px 10px",
|
|
5985
|
-
fontSize: "11px",
|
|
5986
|
-
fontWeight: "500",
|
|
5987
|
-
cursor: "pointer",
|
|
5988
|
-
transition: "all 0.2s ease",
|
|
5989
|
-
flexShrink: 0
|
|
5990
|
-
},
|
|
5991
|
-
onMouseEnter: (e) => {
|
|
5992
|
-
e.currentTarget.style.opacity = "0.9";
|
|
5993
|
-
e.currentTarget.style.transform = "translateY(-1px)";
|
|
5994
|
-
},
|
|
5995
|
-
onMouseLeave: (e) => {
|
|
5996
|
-
e.currentTarget.style.opacity = "1";
|
|
5997
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
5998
|
-
},
|
|
5999
|
-
children: buttonText
|
|
6000
|
-
});
|
|
6001
|
-
})()]
|
|
6002
|
-
})]
|
|
6003
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
6004
|
-
onClick: () => setBannerError(null),
|
|
6005
|
-
style: {
|
|
6006
|
-
background: "transparent",
|
|
6007
|
-
border: "none",
|
|
6008
|
-
color: colors.text,
|
|
6009
|
-
cursor: "pointer",
|
|
6010
|
-
padding: "2px",
|
|
6011
|
-
borderRadius: "3px",
|
|
6012
|
-
fontSize: "14px",
|
|
6013
|
-
lineHeight: "1",
|
|
6014
|
-
opacity: .6,
|
|
6015
|
-
transition: "all 0.2s ease",
|
|
6016
|
-
flexShrink: 0
|
|
6017
|
-
},
|
|
6018
|
-
title: "Dismiss",
|
|
6019
|
-
onMouseEnter: (e) => {
|
|
6020
|
-
e.currentTarget.style.opacity = "1";
|
|
6021
|
-
e.currentTarget.style.background = "rgba(0, 0, 0, 0.05)";
|
|
6022
|
-
},
|
|
6023
|
-
onMouseLeave: (e) => {
|
|
6024
|
-
e.currentTarget.style.opacity = "0.6";
|
|
6025
|
-
e.currentTarget.style.background = "transparent";
|
|
6026
|
-
},
|
|
6027
|
-
children: "×"
|
|
6028
|
-
})]
|
|
6029
|
-
})
|
|
6030
|
-
});
|
|
6031
|
-
})(), children]
|
|
7933
|
+
children: [bannerError && /* @__PURE__ */ jsx(BannerErrorDisplay, {
|
|
7934
|
+
bannerError,
|
|
7935
|
+
onDismiss: () => setBannerError(null)
|
|
7936
|
+
}), children]
|
|
6032
7937
|
});
|
|
6033
7938
|
}
|
|
6034
7939
|
|
|
@@ -7035,12 +8940,21 @@ function CopilotListeners() {
|
|
|
7035
8940
|
const { agent } = useAgent({ agentId: resolvedAgentId });
|
|
7036
8941
|
usePredictStateSubscription(agent);
|
|
7037
8942
|
useEffect(() => {
|
|
7038
|
-
const subscription = copilotkit.subscribe({ onError: ({ error }) => {
|
|
7039
|
-
|
|
8943
|
+
const subscription = copilotkit.subscribe({ onError: ({ error, code, context }) => {
|
|
8944
|
+
if (error.name === "AbortError" || error.message === "Fetch is aborted" || error.message === "signal is aborted without reason" || error.message === "component unmounted" || !error.message) return;
|
|
8945
|
+
if (process.env.NODE_ENV === "development") console.error("[CopilotKit] Agent error:", error.message, "\n Code:", code, "\n Context:", context, "\n Stack:", error.stack);
|
|
8946
|
+
const ckError = new CopilotKitLowLevelError({
|
|
7040
8947
|
error,
|
|
7041
8948
|
message: error.message,
|
|
7042
8949
|
url: typeof window !== "undefined" ? window.location.href : ""
|
|
7043
|
-
})
|
|
8950
|
+
});
|
|
8951
|
+
ckError.details = {
|
|
8952
|
+
code,
|
|
8953
|
+
context,
|
|
8954
|
+
stack: error.stack,
|
|
8955
|
+
originalMessage: error.message
|
|
8956
|
+
};
|
|
8957
|
+
setBannerError(ckError);
|
|
7044
8958
|
} });
|
|
7045
8959
|
return () => {
|
|
7046
8960
|
subscription.unsubscribe();
|
|
@@ -7514,7 +9428,7 @@ function CopilotKitInternal(cpkProps) {
|
|
|
7514
9428
|
children: [
|
|
7515
9429
|
/* @__PURE__ */ jsx(CopilotListeners, {}),
|
|
7516
9430
|
/* @__PURE__ */ jsx(CopilotKitErrorBridge, {}),
|
|
7517
|
-
/* @__PURE__ */ jsxs(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ jsx(MessagesTapProvider, { children: /* @__PURE__ */ jsxs(CopilotMessages, { children: [memoizedChildren, /* @__PURE__ */ jsx(RegisteredActionsRenderer, {})] }) }), bannerError && showDevConsole && /* @__PURE__ */ jsx(UsageBanner, {
|
|
9431
|
+
/* @__PURE__ */ jsxs(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ jsx(MessagesTapProvider, { children: /* @__PURE__ */ jsxs(CopilotMessages, { children: [/* @__PURE__ */ jsx(React.Fragment, { children: memoizedChildren }, "children"), /* @__PURE__ */ jsx(RegisteredActionsRenderer, {}, "actions")] }) }), bannerError && showDevConsole && /* @__PURE__ */ jsx(UsageBanner, {
|
|
7518
9432
|
severity: bannerError.severity,
|
|
7519
9433
|
message: bannerError.message,
|
|
7520
9434
|
onClose: () => setBannerError(null),
|
|
@@ -7558,5 +9472,5 @@ function validateProps(props) {
|
|
|
7558
9472
|
}
|
|
7559
9473
|
|
|
7560
9474
|
//#endregion
|
|
7561
|
-
export {
|
|
7562
|
-
//# sourceMappingURL=copilotkit-
|
|
9475
|
+
export { useCopilotKit as $, CopilotChatSuggestionView as A, useConfigureSuggestions as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAssistantMessage_default as F, useComponent as G, useHumanInTheLoop as H, CopilotChatToolCallsView as I, useRenderCustomMessages as J, useFrontendTool as K, useAttachments as L, CopilotChatReasoningMessage_default as M, CopilotChatUserMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatAttachmentRenderer as P, CopilotKitProvider as Q, useThreads$1 as R, CopilotModalHeader as S, DefaultOpenIcon as T, useDefaultRenderTool as U, useSuggestions as V, useRenderTool as W, useAgent as X, UseAgentUpdate as Y, useRenderToolCall as Z, WildcardToolCallRender as _, ThreadsProvider as a, useSandboxFunctions as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, MCPAppsActivityType as ct, shouldShowDevConsole as d, AudioRecorderError as dt, CopilotKitCoreReact as et, useToast as f, CopilotChatAudioRecorder as ft, useCopilotContext as g, CopilotContext as h, ThreadsContext as i, SandboxFunctionsContext as it, CopilotChatSuggestionPill as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, CopilotKitInspector as lt, useCopilotMessagesContext as m, useCopilotChatConfiguration as mt, defaultCopilotContextCategories as n, defineToolCallRenderer as nt, useThreads as o, MCPAppsActivityContentSchema as ot, CopilotMessagesContext as p, CopilotChatConfigurationProvider as pt, useRenderActivityMessage as q, CoAgentStateRenderBridge as r, createA2UIMessageRenderer as rt, CoAgentStateRendersContext as s, MCPAppsActivityRenderer as st, CopilotKit as t, useAgentContext as tt, useAsyncCallback as u, CopilotChatInput_default as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useInterrupt as z };
|
|
9476
|
+
//# sourceMappingURL=copilotkit-BY5S1-0P.mjs.map
|