@copilotkit/react-core 1.79.1 → 1.80.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.
Files changed (42) hide show
  1. package/dist/{copilotkit-CD3uPUN3.mjs → copilotkit-BNLtgOyD.mjs} +1855 -221
  2. package/dist/copilotkit-BNLtgOyD.mjs.map +1 -0
  3. package/dist/{copilotkit-Yz8qXx00.d.mts → copilotkit-CWOSXhRO.d.mts} +277 -40
  4. package/dist/copilotkit-CWOSXhRO.d.mts.map +1 -0
  5. package/dist/{copilotkit-DEaA0Cat.cjs → copilotkit-DKICi3nC.cjs} +1843 -196
  6. package/dist/copilotkit-DKICi3nC.cjs.map +1 -0
  7. package/dist/{copilotkit-k7IYk2AN.d.cts → copilotkit-Dfw9HzO9.d.cts} +277 -40
  8. package/dist/copilotkit-Dfw9HzO9.d.cts.map +1 -0
  9. package/dist/index.cjs +15 -3
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts +1 -1
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +15 -3
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +127 -16
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/v2/context.d.cts +7 -0
  20. package/dist/v2/context.d.cts.map +1 -1
  21. package/dist/v2/context.d.mts +7 -0
  22. package/dist/v2/context.d.mts.map +1 -1
  23. package/dist/v2/headless.cjs +106 -16
  24. package/dist/v2/headless.cjs.map +1 -1
  25. package/dist/v2/headless.d.cts +20 -2
  26. package/dist/v2/headless.d.cts.map +1 -1
  27. package/dist/v2/headless.d.mts +20 -2
  28. package/dist/v2/headless.d.mts.map +1 -1
  29. package/dist/v2/headless.mjs +106 -16
  30. package/dist/v2/headless.mjs.map +1 -1
  31. package/dist/v2/index.cjs +3 -1
  32. package/dist/v2/index.css +1 -1
  33. package/dist/v2/index.d.cts +2 -2
  34. package/dist/v2/index.d.mts +2 -2
  35. package/dist/v2/index.mjs +2 -2
  36. package/dist/v2/index.umd.js +1858 -222
  37. package/dist/v2/index.umd.js.map +1 -1
  38. package/package.json +11 -8
  39. package/dist/copilotkit-CD3uPUN3.mjs.map +0 -1
  40. package/dist/copilotkit-DEaA0Cat.cjs.map +0 -1
  41. package/dist/copilotkit-Yz8qXx00.d.mts.map +0 -1
  42. package/dist/copilotkit-k7IYk2AN.d.cts.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import * as React$1 from "react";
2
- import React, { createContext, createElement, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
2
+ import React, { Fragment, createContext, createElement, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCore as CopilotKitCore$1, CopilotKitCoreErrorCode, CopilotKitCoreErrorCode as CopilotKitCoreErrorCode$1, CopilotKitCoreRuntimeConnectionStatus, CopilotKitCoreRuntimeConnectionStatus as CopilotKitCoreRuntimeConnectionStatus$1, CopilotKitHeaderResolutionError, INSPECTOR_THREAD_BRIDGE_PLUGIN_ID, IntelligenceAgent, MEMORY_ERROR_REGISTRY, MemoryError, ProxiedCopilotRuntimeAgent, ProxiedCopilotRuntimeAgent as ProxiedCopilotRuntimeAgent$1, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, ToolCallStatus as ToolCallStatus$1, WebMCPRegistry, completePartialMarkdown, createActionGroup, createEffect, createInspectorThreadRequestId, createReducer, createSelector, createStore, emitInspectorActiveThread, emitInspectorActiveThread as emitInspectorActiveThread$1, emitInspectorStopViewing, emitInspectorViewThread, emitInspectorViewThreadResult, emitInspectorViewThreadResult as emitInspectorViewThreadResult$1, empty, ensureObjectArgs, getWebMCPModelContext, isAbortError, isInspectorThreadBridgeEnabled, isInspectorThreadBridgeEnabled as isInspectorThreadBridgeEnabled$1, isRunCompletionAware, isRunCompletionAware as isRunCompletionAware$1, ofType, on, onInspectorActiveThread, onInspectorStopViewing, onInspectorStopViewing as onInspectorStopViewing$1, onInspectorViewThread, onInspectorViewThread as onInspectorViewThread$1, onInspectorViewThreadResult, parseToolArguments, props, select, ɵCOPILOTKIT_FEATURES, ɵInterruptState, ɵInterruptState as ɵInterruptState$1, ɵMAX_SOCKET_RETRIES, ɵRUNTIME_PROBE_TIMEOUT_MS, ɵTHREAD_REQUEST_TIMEOUT_MS, ɵcreateMemoryStore, ɵcreateThreadSelectors, ɵcreateThreadStore, ɵcreateThreadStore as ɵcreateThreadStore$1, ɵisCopilotKitFeature, ɵisHttpAgent, ɵisHttpAgent as ɵisHttpAgent$1, ɵisRetryableMemoryStatus, ɵjoinPhoenixChannel$, ɵmapMemoryMetadataEvent, ɵmemoryAdapterEvents, ɵmemoryDomainEvents, ɵmemoryReducer, ɵmemoryRestEvents, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectFetchMoreError, ɵselectFetchMoreError as ɵselectFetchMoreError$1, ɵselectHasNextPage, ɵselectHasNextPage as ɵselectHasNextPage$1, ɵselectIsFetchingNextPage, ɵselectIsFetchingNextPage as ɵselectIsFetchingNextPage$1, ɵselectIsMutating, ɵselectIsMutating as ɵselectIsMutating$1, ɵselectMemories, ɵselectMemories as ɵselectMemories$1, ɵselectMemoriesAvailable, ɵselectMemoriesAvailable as ɵselectMemoriesAvailable$1, ɵselectMemoriesError, ɵselectMemoriesError as ɵselectMemoriesError$1, ɵselectMemoriesIsLoading, ɵselectMemoriesIsLoading as ɵselectMemoriesIsLoading$1, ɵselectMemoriesIsMutating, ɵselectMemoriesRealtimeStatus, ɵselectMemoriesRealtimeStatus as ɵselectMemoriesRealtimeStatus$1, ɵselectThreads, ɵselectThreads as ɵselectThreads$1, ɵselectThreadsError, ɵselectThreadsError as ɵselectThreadsError$1, ɵselectThreadsIsLoading, ɵselectThreadsIsLoading as ɵselectThreadsIsLoading$1, ɵthreadAdapterEvents, ɵwithHeaderDefaults, ɵwithHeaderDefaults as ɵwithHeaderDefaults$1 } from "@copilotkit/core";
4
4
  import { ACTIVITY_HISTORY_METADATA, AGUIConnectNotImplementedError as AGUIConnectNotImplementedError$1, AGUIError, AGUI_METADATA_KEY, AbstractAgent as AbstractAgent$1, BackwardCompatibility_0_0_39, BackwardCompatibility_0_0_45, BackwardCompatibility_0_0_57, CompatibilityBoundary, DebugLogger, EventType, FilterToolCallsMiddleware, FunctionMiddleware, HttpAgent, Middleware, PROTOCOL_VERSION, aggregateTokenUsage, authoritativeActivityTypes, buildResumeArray, compactEvents, contentHasMedia, contentToText as contentToText$1, convertToLegacyEvents, createDebugLogger, defaultApplyEvents, enforceEvents, enforceOutgoingInput, getRunOutcome, isInterruptExpired, isRecognizedEvent, mergeMetadata, omitOptionalNulls, parseProtoStream, parseSSEStream, randomUUID, randomUUID as randomUUID$2, resolveAgentDebugConfig, resolveDebugLogger, runHttpRequest, structuredClone_, tokenUsageFromAiSdkUsage, tokenUsageFromLangChainMetadata, transformChunks, transformHttpEventStream, verifyEvents, withAuthoritativeActivityTypes } from "@ag-ui/client";
5
5
  import { ActivityDeltaEventSchema, ActivityMessageSchema, ActivitySnapshotEventSchema, AddOperationSchema, AgentCapabilitiesSchema, AssistantMessageSchema, AttributableSchema, AudioInputContentSchema, AudioInputPartSchema, AudioPartSchema, BaseEventSchema, BaseMessageSchema, ContentPartSchema, ContextSchema, CopyOperationSchema, CustomEventSchema, DataSourceSchema, DeveloperMessageSchema, DocumentInputContentSchema, DocumentInputPartSchema, DocumentPartSchema, EventSchema, EventSchemas, EventTypeSchema, ExecutionCapabilitiesSchema, FileSourceSchema, FunctionCallSchema, HumanInTheLoopCapabilitiesSchema, IdentityCapabilitiesSchema, ImageInputContentSchema, ImageInputPartSchema, ImagePartSchema, InputContentDataSourceSchema, InputContentSchema, InputContentSourceSchema, InputContentUrlSourceSchema, InterruptSchema, JsonPatchOperationSchema, JsonPatchSchema, JsonPointerSchema, MessageSchema, MessagesSnapshotEventSchema, MetadataSchema, MoveOperationSchema, MultiAgentCapabilitiesSchema, MultimodalCapabilitiesSchema, MultimodalInputCapabilitiesSchema, MultimodalOutputCapabilitiesSchema, OptionalMetadataSchema, OutputCapabilitiesSchema, PartSourceSchema, RawEventSchema, ReasoningCapabilitiesSchema, ReasoningEncryptedValueEventSchema, ReasoningEncryptedValueSubtypeSchema, ReasoningEndEventSchema, ReasoningMessageChunkEventSchema, ReasoningMessageContentEventSchema, ReasoningMessageEndEventSchema, ReasoningMessageSchema, ReasoningMessageStartEventSchema, ReasoningStartEventSchema, RemoveOperationSchema, ReplaceOperationSchema, ResumeEntrySchema, RoleSchema, RunAgentInputSchema, RunErrorEventSchema, RunFinishedCancelledOutcomeSchema, RunFinishedEventSchema, RunFinishedInterruptOutcomeSchema, RunFinishedOutcomeSchema, RunFinishedSuccessOutcomeSchema, RunStartedEventSchema, StateCapabilitiesSchema, StateDeltaEventSchema, StateSchema, StateSnapshotEventSchema, StepFinishedEventSchema, StepStartedEventSchema, SubagentErrorEventSchema, SubagentFinishedEventSchema, SubagentFinishedOutcomeSchema, SubagentFinishedSuccessOutcomeSchema, SubagentFinishedSuspendedOutcomeSchema, SubagentInfoSchema, SubagentRunIdSchema, SubagentStartedEventSchema, SystemMessageSchema, TestOperationSchema, TextInputContentSchema, TextMessageChunkEventSchema, TextMessageContentEventSchema, TextMessageEndEventSchema, TextMessageRoleSchema, TextMessageStartEventSchema, TextPartSchema, TokenUsageSchema, ToolCallArgsEventSchema, ToolCallChunkEventSchema, ToolCallEndEventSchema, ToolCallResultEventSchema, ToolCallSchema, ToolCallStartEventSchema, ToolMessageSchema, ToolSchema, ToolsCapabilitiesSchema, TransportCapabilitiesSchema, UrlSourceSchema, UserMessageSchema, VideoInputContentSchema, VideoInputPartSchema, VideoPartSchema } from "@ag-ui/core/schemas";
6
6
  import { extendTailwindMerge, twMerge } from "tailwind-merge";
7
- import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, EyeOff, Loader2, MessageCircle, Mic, PanelLeftOpen, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, Wrench, X } from "lucide-react";
7
+ import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Circle, Copy, Edit, EyeOff, Loader2, MessageCircle, Mic, PanelLeftOpen, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, Wrench, X } from "lucide-react";
8
8
  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, GENERATE_SANDBOXED_UI_DESCRIPTION, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, commitRowKeyStore, copyToClipboard, createAttachmentContent, createLicenseContextValue, createRowKeyStore, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID as randomUUID$1, readFileAsBase64, resolveRowRenderKeysById, schemaToJsonSchema, shouldEnableInspector } from "@copilotkit/shared";
9
9
  import { CopilotKitAgentIdContext, CopilotKitContext, LicenseContext, useCopilotKit, useCopilotKit as useCopilotKit$1, useDefaultAgentId, useLicenseContext, useLicenseContext as useLicenseContext$1 } from "@copilotkit/react-core/v2/context";
10
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -19,6 +19,7 @@ import { z } from "zod";
19
19
  import { MCPAppsActivityContentSchema, MCPAppsActivityType, ɵrunMcpFollowUp } from "@copilotkit/mcp-apps-renderer/activity";
20
20
  import { A2UIProvider, A2UIRenderer, A2UI_SCHEMA_CONTEXT_DESCRIPTION, Catalog, DEFAULT_SURFACE_ID, ROOT_COMPONENT_ID, buildCatalogContextValue, extractCatalogComponentSchemas, filterCatalog, initializeDefaultCatalog, injectStyles, useA2UIActions, useA2UIError, viewerTheme } from "@copilotkit/a2ui-renderer";
21
21
  import { zodToJsonSchema } from "zod-to-json-schema";
22
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
22
23
  import { createPortal, flushSync } from "react-dom";
23
24
  import { useVirtualizer } from "@tanstack/react-virtual";
24
25
  import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
@@ -40,7 +41,7 @@ function shallowEqual(obj1, obj2) {
40
41
  * Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
41
42
  * class instances, and other exotic objects that happen to have typeof "object".
42
43
  */
43
- function isPlainObject(obj) {
44
+ function isPlainObject$1(obj) {
44
45
  return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
45
46
  }
46
47
  /**
@@ -59,7 +60,7 @@ function isPlainObject(obj) {
59
60
  function useShallowStableRef(value) {
60
61
  const ref = useRef(value);
61
62
  if (ref.current === value) return ref.current;
62
- if (isPlainObject(ref.current) && isPlainObject(value)) {
63
+ if (isPlainObject$1(ref.current) && isPlainObject$1(value)) {
63
64
  if (shallowEqual(ref.current, value)) return ref.current;
64
65
  }
65
66
  ref.current = value;
@@ -75,6 +76,8 @@ const CopilotChatDefaultLabels = {
75
76
  chatInputToolbarFinishTranscribeButtonLabel: "Finish",
76
77
  chatInputToolbarAddButtonLabel: "Add attachments",
77
78
  chatInputToolbarToolsButtonLabel: "Tools",
79
+ chatInputToolbarSendButtonLabel: "Send message",
80
+ chatInputToolbarStopButtonLabel: "Stop",
78
81
  assistantMessageToolbarCopyCodeLabel: "Copy",
79
82
  assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
80
83
  assistantMessageToolbarCopyMessageLabel: "Copy",
@@ -87,13 +90,18 @@ const CopilotChatDefaultLabels = {
87
90
  assistantMessageToolbarInspectorHideDescription: "Until you reload this page",
88
91
  assistantMessageToolbarThumbsUpLabel: "Good response",
89
92
  assistantMessageToolbarThumbsDownLabel: "Bad response",
93
+ assistantMessageFeedbackCommentPlaceholder: "Add a comment (optional)",
94
+ assistantMessageFeedbackCommentSubmitLabel: "Send comment",
90
95
  assistantMessageToolbarReadAloudLabel: "Read aloud",
91
96
  assistantMessageToolbarRegenerateLabel: "Regenerate",
92
97
  userMessageToolbarCopyMessageLabel: "Copy",
93
98
  userMessageToolbarEditMessageLabel: "Edit",
99
+ userMessageToolbarPreviousBranchLabel: "Previous branch",
100
+ userMessageToolbarNextBranchLabel: "Next branch",
94
101
  chatDisclaimerText: "AI can make mistakes. Please verify important information.",
95
102
  chatToggleOpenLabel: "Open chat",
96
103
  chatToggleCloseLabel: "Close chat",
104
+ chatScrollToBottomLabel: "Scroll to bottom",
97
105
  modalHeaderTitle: "CopilotKit Chat",
98
106
  welcomeMessageText: "How can I help you today?"
99
107
  };
@@ -805,6 +813,11 @@ function renderSlot(slot, DefaultComponent, props) {
805
813
  //#region src/v2/components/chat/CopilotChatInput.tsx
806
814
  const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
807
815
  const SLASH_MENU_ITEM_HEIGHT_PX = 40;
816
+ /**
817
+ * The chat composer: a text area with the add-menu, voice and send/stop
818
+ * buttons, and a slash-command menu that lists `toolsMenu` items once the text
819
+ * starts with "/".
820
+ */
808
821
  function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, bottomAnchored = false, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
809
822
  const isControlled = value !== void 0;
810
823
  const [internalValue, setInternalValue] = useState(() => value ?? "");
@@ -1024,10 +1037,12 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1024
1037
  send();
1025
1038
  };
1026
1039
  const BoundAudioRecorder = renderSlot(audioRecorder, CopilotChatAudioRecorder, { ref: audioRecorderRef });
1040
+ const showsStop = isProcessing && canStop;
1027
1041
  const BoundSendButton = renderSlot(sendButton, CopilotChatInput.SendButton, {
1028
1042
  onClick: handleSendButtonClick,
1029
1043
  disabled: isProcessing ? !canStop : !canSend,
1030
- children: isProcessing && canStop ? /* @__PURE__ */ jsx(Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
1044
+ "aria-label": showsStop ? labels.chatInputToolbarStopButtonLabel : labels.chatInputToolbarSendButtonLabel,
1045
+ children: showsStop ? /* @__PURE__ */ jsx(Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
1031
1046
  });
1032
1047
  const BoundStartTranscribeButton = renderSlot(startTranscribeButton, CopilotChatInput.StartTranscribeButton, { onClick: onStartTranscribe });
1033
1048
  const BoundCancelTranscribeButton = renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, { onClick: onCancelTranscribe });
@@ -1339,18 +1354,22 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1339
1354
  });
1340
1355
  }
1341
1356
  (function(_CopilotChatInput) {
1342
- _CopilotChatInput.SendButton = ({ className, children, ...props }) => /* @__PURE__ */ jsx("div", {
1343
- className: "cpk:mr-[10px]",
1344
- children: /* @__PURE__ */ jsx(Button, {
1345
- type: "button",
1346
- "data-testid": "copilot-send-button",
1347
- variant: "chatInputToolbarPrimary",
1348
- size: "chatInputToolbarIcon",
1349
- className,
1350
- ...props,
1351
- children: children ?? /* @__PURE__ */ jsx(ArrowUp, { className: "cpk:size-[18px]" })
1352
- })
1353
- });
1357
+ _CopilotChatInput.SendButton = ({ className, children, ...props }) => {
1358
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1359
+ return /* @__PURE__ */ jsx("div", {
1360
+ className: "cpk:mr-[10px]",
1361
+ children: /* @__PURE__ */ jsx(Button, {
1362
+ type: "button",
1363
+ "data-testid": "copilot-send-button",
1364
+ variant: "chatInputToolbarPrimary",
1365
+ size: "chatInputToolbarIcon",
1366
+ className,
1367
+ "aria-label": labels.chatInputToolbarSendButtonLabel,
1368
+ ...props,
1369
+ children: children ?? /* @__PURE__ */ jsx(ArrowUp, { className: "cpk:size-[18px]" })
1370
+ })
1371
+ });
1372
+ };
1354
1373
  const ToolbarButton = _CopilotChatInput.ToolbarButton = ({ icon, labelKey, defaultClassName, className, ...props }) => {
1355
1374
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
1356
1375
  return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
@@ -1360,6 +1379,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1360
1379
  variant: "chatInputToolbarSecondary",
1361
1380
  size: "chatInputToolbarIcon",
1362
1381
  className: twMerge(defaultClassName, className),
1382
+ "aria-label": labels[labelKey],
1363
1383
  ...props,
1364
1384
  children: icon
1365
1385
  })
@@ -1430,6 +1450,7 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1430
1450
  size: "chatInputToolbarIcon",
1431
1451
  className: twMerge("cpk:ml-1", className),
1432
1452
  disabled: isDisabled,
1453
+ "aria-label": labels.chatInputToolbarAddButtonLabel,
1433
1454
  ...props,
1434
1455
  children: /* @__PURE__ */ jsx(Plus, { className: "cpk:size-[20px]" })
1435
1456
  });
@@ -1579,6 +1600,32 @@ function warnAboutUnrenderedToolCalls(toolNames, registered, alreadyWarned) {
1579
1600
  }
1580
1601
  }
1581
1602
  /**
1603
+ * The tool-call renderers registered on the provider, kept current.
1604
+ * @internal
1605
+ */
1606
+ function useToolCallRenderers() {
1607
+ const { copilotkit } = useCopilotKit();
1608
+ return useSyncExternalStore((callback) => copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1609
+ }
1610
+ /**
1611
+ * Which tool names have a renderer of their own, whether a wildcard (`"*"`)
1612
+ * renderer is registered, and which of them opted into grouping.
1613
+ * @internal
1614
+ */
1615
+ function useToolCallRendererNames() {
1616
+ const renderers = useToolCallRenderers();
1617
+ return useMemo(() => {
1618
+ const named = renderers.filter((rc) => rc.name !== "*");
1619
+ const wildcards = renderers.filter((rc) => rc.name === "*");
1620
+ return {
1621
+ named: new Set(named.map((rc) => rc.name)),
1622
+ groupedNames: new Set(named.filter((rc) => rc.group).map((rc) => rc.name)),
1623
+ wildcard: wildcards.length > 0,
1624
+ wildcardGroups: wildcards.some((rc) => rc.group)
1625
+ };
1626
+ }, [renderers]);
1627
+ }
1628
+ /**
1582
1629
  * Hook that returns a function to render tool calls based on the render functions
1583
1630
  * defined in CopilotKitProvider.
1584
1631
  *
@@ -1589,9 +1636,7 @@ function useRenderToolCall() {
1589
1636
  const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
1590
1637
  const unrenderedToolNames = useRef(/* @__PURE__ */ new Set());
1591
1638
  const warnedToolNames = useRef(/* @__PURE__ */ new Set());
1592
- const renderToolCalls = useSyncExternalStore((callback) => {
1593
- return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1594
- }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1639
+ const renderToolCalls = useToolCallRenderers();
1595
1640
  const renderToolCall = useCallback(({ toolCall, toolMessage }) => {
1596
1641
  const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1597
1642
  const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
@@ -1626,7 +1671,7 @@ function useRenderToolCall() {
1626
1671
 
1627
1672
  //#endregion
1628
1673
  //#region package.json
1629
- var version = "1.79.1";
1674
+ var version = "1.80.0";
1630
1675
 
1631
1676
  //#endregion
1632
1677
  //#region src/v2/components/CopilotKitInspector.tsx
@@ -1862,7 +1907,7 @@ function InlineFeatureWarning({ featureName }) {
1862
1907
  * Fetches resource content on-demand via proxied MCP requests. The React shell
1863
1908
  * owns the iframe; `bindMcpApp` owns the protocol.
1864
1909
  */
1865
- const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, message, agent }) {
1910
+ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, message, agent, allowSameOriginFallback }) {
1866
1911
  const { copilotkit } = useCopilotKit$1();
1867
1912
  const messageId = message?.id;
1868
1913
  const containerRef = useRef(null);
@@ -1912,6 +1957,7 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, mess
1912
1957
  getAgent: () => agentRef.current,
1913
1958
  host: copilotkit,
1914
1959
  messageId,
1960
+ options: { allowSameOriginFallback },
1915
1961
  hooks: {
1916
1962
  onResource: (resource) => {
1917
1963
  if (!mounted) return;
@@ -1953,6 +1999,7 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, mess
1953
1999
  agent,
1954
2000
  copilotkit,
1955
2001
  messageId,
2002
+ allowSameOriginFallback,
1956
2003
  content.resourceUri,
1957
2004
  content.serverHash,
1958
2005
  content.serverId
@@ -3167,7 +3214,8 @@ function defineToolCallRenderer(def) {
3167
3214
  name: def.name,
3168
3215
  args: argsSchema,
3169
3216
  render: def.render,
3170
- ...def.agentId ? { agentId: def.agentId } : {}
3217
+ ...def.agentId ? { agentId: def.agentId } : {},
3218
+ ...def.group ? { group: true } : {}
3171
3219
  };
3172
3220
  }
3173
3221
 
@@ -3383,6 +3431,11 @@ const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follo
3383
3431
  - Use CSS Grid or Flexbox for layout. Ensure the UI looks good at any width.
3384
3432
  - Minimal transitions (150ms) for hover/focus states only. No decorative animations.
3385
3433
  - Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
3434
+ /** The MCP Apps renderer with `mcpApps.allowSameOriginFallback` applied. */
3435
+ const SameOriginMCPAppsActivityRenderer = (props) => /* @__PURE__ */ jsx(MCPAppsActivityRenderer, {
3436
+ ...props,
3437
+ allowSameOriginFallback: true
3438
+ });
3386
3439
  const DEFAULT_LEARNING = Object.freeze({});
3387
3440
  function toLearningConfig(learning) {
3388
3441
  if (learning === true) return DEFAULT_LEARNING;
@@ -3399,7 +3452,8 @@ function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
3399
3452
  }, [value, warningMessage]);
3400
3453
  return value;
3401
3454
  }
3402
- const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY_HEADERS, credentials, messageFilter, publicApiKey, publicLicenseKey, licenseToken, properties = EMPTY_PROPERTIES, agents__unsafe_dev_only: agents = EMPTY_AGENTS, selfManagedAgents = EMPTY_AGENTS, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, enableInspector, agentId, showIntelligenceIndicator = true, useSingleEndpoint, onError, a2ui, defaultThrottleMs, debug, learning }) => {
3455
+ /** Provide a shared core instance and synchronize runtime, tool, and UI configuration. */
3456
+ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY_HEADERS, credentials, messageFilter, publicApiKey, publicLicenseKey, licenseToken, properties = EMPTY_PROPERTIES, agents__unsafe_dev_only: agents = EMPTY_AGENTS, selfManagedAgents = EMPTY_AGENTS, renderToolCalls, renderActivityMessages, renderCustomMessages, frontendTools, humanInTheLoop, openGenerativeUI, enableInspector, agentId, showIntelligenceIndicator = true, useSingleEndpoint, onError, a2ui, mcpApps, defaultThrottleMs, debug, learning }) => {
3403
3457
  const learningConfig = useMemo(() => toLearningConfig(learning), [learning]);
3404
3458
  const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
3405
3459
  const [inspectorVisible, setInspectorVisible] = useState(false);
@@ -3456,11 +3510,12 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3456
3510
  const core = copilotkitRef.current;
3457
3511
  return filterCatalog(rawCatalog, (name) => core ? core.isCatalogComponentEnabled(name) : true);
3458
3512
  }, [rawCatalog, catalogToggleVersion]);
3513
+ const mcpAppsAllowSameOrigin = mcpApps?.allowSameOriginFallback === true;
3459
3514
  const builtInActivityRenderers = useMemo(() => {
3460
3515
  const renderers = [{
3461
3516
  activityType: MCPAppsActivityType,
3462
3517
  content: MCPAppsActivityContentSchema,
3463
- render: MCPAppsActivityRenderer
3518
+ render: mcpAppsAllowSameOrigin ? SameOriginMCPAppsActivityRenderer : MCPAppsActivityRenderer
3464
3519
  }];
3465
3520
  if (openGenUIActive) renderers.push({
3466
3521
  activityType: OpenGenerativeUIActivityType,
@@ -3478,7 +3533,8 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3478
3533
  a2uiActive,
3479
3534
  openGenUIActive,
3480
3535
  a2ui,
3481
- filteredCatalog
3536
+ filteredCatalog,
3537
+ mcpAppsAllowSameOrigin
3482
3538
  ]);
3483
3539
  const allActivityRenderers = useMemo(() => {
3484
3540
  return [...renderActivityMessagesList, ...builtInActivityRenderers];
@@ -3586,6 +3642,7 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = EMPTY
3586
3642
  description: GENERATE_SANDBOXED_UI_DESCRIPTION,
3587
3643
  parameters: GenerateSandboxedUiArgsSchema,
3588
3644
  handler: async () => "UI generated",
3645
+ reconnectBehavior: "resume-pending",
3589
3646
  followUp: true,
3590
3647
  render: OpenGenerativeUIToolRenderer
3591
3648
  }];
@@ -3991,12 +4048,86 @@ function useRenderActivityMessage() {
3991
4048
  }), [renderActivityMessage, findRenderer]);
3992
4049
  }
3993
4050
 
4051
+ //#endregion
4052
+ //#region src/v2/hooks/tool-registration.ts
4053
+ /**
4054
+ * Deps comparison shared by `useFrontendTool` (and through it
4055
+ * `useHumanInTheLoop` and `useComponent`) and `useRenderTool`. Not part of the
4056
+ * public API.
4057
+ */
4058
+ const EMPTY_DEPS$1 = [];
4059
+ function isPlainObject(value) {
4060
+ const proto = Object.getPrototypeOf(value);
4061
+ return proto === Object.prototype || proto === null;
4062
+ }
4063
+ /**
4064
+ * Plain data (primitives, plain objects, arrays, dates) compares by value.
4065
+ * Functions always compare equal: these hooks used to compare
4066
+ * `JSON.stringify(deps)`, which turns every function into `null`, and a
4067
+ * function that counted as changed would re-register on every render when it
4068
+ * is written inline (and loop in a component that also reads
4069
+ * `useRenderToolCall`). Everything else (class instances, maps, sets) compares
4070
+ * by identity. A cycle inside plain data is safe: a pair already being
4071
+ * compared counts as equal for that branch.
4072
+ */
4073
+ function depEqual(a, b, seen) {
4074
+ if (Object.is(a, b)) return true;
4075
+ if (typeof a === "function" && typeof b === "function") return true;
4076
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
4077
+ if (a instanceof Date && b instanceof Date) return Object.is(a.getTime(), b.getTime());
4078
+ const aIsArray = Array.isArray(a);
4079
+ if (aIsArray !== Array.isArray(b)) return false;
4080
+ if (!aIsArray && !(isPlainObject(a) && isPlainObject(b))) return false;
4081
+ if (seen.get(a) === b) return true;
4082
+ seen.set(a, b);
4083
+ if (aIsArray) {
4084
+ const left = a;
4085
+ const right = b;
4086
+ if (left.length !== right.length) return false;
4087
+ for (let i = 0; i < left.length; i++) if (!depEqual(left[i], right[i], seen)) return false;
4088
+ return true;
4089
+ }
4090
+ const left = a;
4091
+ const right = b;
4092
+ const leftKeys = Object.keys(left);
4093
+ if (leftKeys.length !== Object.keys(right).length) return false;
4094
+ for (const key of leftKeys) {
4095
+ if (!Object.prototype.hasOwnProperty.call(right, key)) return false;
4096
+ if (!depEqual(left[key], right[key], seen)) return false;
4097
+ }
4098
+ return true;
4099
+ }
4100
+ /** Whether two deps arrays are equal. A change in length is always a change. */
4101
+ function depsEqual(prev, next) {
4102
+ if (prev.length !== next.length) return false;
4103
+ const seen = /* @__PURE__ */ new Map();
4104
+ for (let i = 0; i < prev.length; i++) if (!depEqual(prev[i], next[i], seen)) return false;
4105
+ return true;
4106
+ }
4107
+ /**
4108
+ * Turns a caller's `deps` array into a number that changes exactly when the
4109
+ * deps change, by the rules of {@link depsEqual}. Put the number, not the
4110
+ * array, in the registration effect's dependency list: that keeps the list a
4111
+ * constant length and never serializes a value.
4112
+ */
4113
+ function useDepsVersion(deps) {
4114
+ const next = deps ?? EMPTY_DEPS$1;
4115
+ const ref = useRef({
4116
+ deps: next,
4117
+ version: 0
4118
+ });
4119
+ if (ref.current.deps !== next && !depsEqual(ref.current.deps, next)) ref.current = {
4120
+ deps: next,
4121
+ version: ref.current.version + 1
4122
+ };
4123
+ return ref.current.version;
4124
+ }
4125
+
3994
4126
  //#endregion
3995
4127
  //#region src/v2/hooks/use-frontend-tool.tsx
3996
- const EMPTY_DEPS$2 = [];
3997
4128
  function useFrontendTool(tool, deps) {
3998
4129
  const { copilotkit } = useCopilotKit();
3999
- const extraDeps = deps ?? EMPTY_DEPS$2;
4130
+ const depsVersion = useDepsVersion(deps);
4000
4131
  useLayoutEffect(() => {
4001
4132
  const name = tool.name;
4002
4133
  if (copilotkit.getTool({
@@ -4020,14 +4151,14 @@ function useFrontendTool(tool, deps) {
4020
4151
  tool.name,
4021
4152
  tool.available,
4022
4153
  copilotkit,
4023
- JSON.stringify(extraDeps),
4154
+ depsVersion,
4024
4155
  JSON.stringify(tool.webmcp ?? null)
4025
4156
  ]);
4026
4157
  }
4027
4158
 
4028
4159
  //#endregion
4029
4160
  //#region src/v2/hooks/use-frontend-tools.tsx
4030
- const EMPTY_DEPS$1 = [];
4161
+ const EMPTY_DEPS = [];
4031
4162
  const EMPTY_TOOLS = [];
4032
4163
  /**
4033
4164
  * Register a list of frontend tools whose length can change between renders.
@@ -4081,7 +4212,7 @@ const EMPTY_TOOLS = [];
4081
4212
  function useFrontendTools(tools, deps) {
4082
4213
  const { copilotkit } = useCopilotKit();
4083
4214
  const toolList = tools ?? EMPTY_TOOLS;
4084
- const extraDeps = deps ?? EMPTY_DEPS$1;
4215
+ const extraDeps = deps ?? EMPTY_DEPS;
4085
4216
  useLayoutEffect(() => {
4086
4217
  const deduped = /* @__PURE__ */ new Map();
4087
4218
  for (const tool of toolList) {
@@ -4192,7 +4323,6 @@ function useComponent(config, deps) {
4192
4323
 
4193
4324
  //#endregion
4194
4325
  //#region src/v2/hooks/use-render-tool.tsx
4195
- const EMPTY_DEPS = [];
4196
4326
  /**
4197
4327
  * Registers a renderer entry in CopilotKit's `renderToolCalls` registry.
4198
4328
  *
@@ -4236,7 +4366,7 @@ const EMPTY_DEPS = [];
4236
4366
  */
4237
4367
  function useRenderTool(config, deps) {
4238
4368
  const { copilotkit } = useCopilotKit();
4239
- const extraDeps = deps ?? EMPTY_DEPS;
4369
+ const depsVersion = useDepsVersion(deps);
4240
4370
  useEffect(() => {
4241
4371
  const renderer = config.name === "*" && !config.parameters ? defineToolCallRenderer({
4242
4372
  name: "*",
@@ -4244,7 +4374,8 @@ function useRenderTool(config, deps) {
4244
4374
  ...props,
4245
4375
  parameters: props.args
4246
4376
  }),
4247
- ...config.agentId ? { agentId: config.agentId } : {}
4377
+ ...config.agentId ? { agentId: config.agentId } : {},
4378
+ group: config.group
4248
4379
  }) : defineToolCallRenderer({
4249
4380
  name: config.name,
4250
4381
  args: config.parameters,
@@ -4262,16 +4393,395 @@ function useRenderTool(config, deps) {
4262
4393
  parameters: props.args
4263
4394
  });
4264
4395
  },
4265
- ...config.agentId ? { agentId: config.agentId } : {}
4396
+ ...config.agentId ? { agentId: config.agentId } : {},
4397
+ group: config.group
4266
4398
  });
4267
4399
  copilotkit.addHookRenderToolCall(renderer);
4268
4400
  }, [
4269
4401
  config.name,
4402
+ config.group,
4270
4403
  copilotkit,
4271
- JSON.stringify(extraDeps)
4404
+ depsVersion
4272
4405
  ]);
4273
4406
  }
4274
4407
 
4408
+ //#endregion
4409
+ //#region src/v2/components/chat/CopilotChatToolCallGroup.tsx
4410
+ /**
4411
+ * Set by a group around the calls it shows, so CopilotKit's built-in tool card
4412
+ * draws itself as a compact row that fits the group instead of a card.
4413
+ * @internal
4414
+ */
4415
+ const ToolCallGroupContext = createContext(null);
4416
+ /**
4417
+ * A call without a result is running only while the group is active: once
4418
+ * the run has moved on or ended, its result is never coming.
4419
+ */
4420
+ const entryStatus = ({ toolMessage }, active) => toolMessage ? toolMessage.error ? "failed" : "done" : active ? "running" : "stopped";
4421
+ /** @internal */
4422
+ const statusLabels = {
4423
+ running: "Running",
4424
+ done: "Done",
4425
+ failed: "Failed",
4426
+ stopped: "Stopped"
4427
+ };
4428
+ /** The first non-empty string argument: usually a command, path or query. */
4429
+ function argumentPreview(parameters) {
4430
+ if (!parameters || typeof parameters !== "object") return void 0;
4431
+ return Object.values(parameters).find((v) => typeof v === "string" && v.trim().length > 0)?.replace(/\s+/g, " ");
4432
+ }
4433
+ const entryPreview = (entry) => argumentPreview(partialJSONParse(entry.toolCall.function.arguments));
4434
+ /** "run_command ×2, read_file" */
4435
+ function toolNames(calls) {
4436
+ const counts = /* @__PURE__ */ new Map();
4437
+ for (const { toolCall } of calls) {
4438
+ const name = toolCall.function.name;
4439
+ counts.set(name, (counts.get(name) ?? 0) + 1);
4440
+ }
4441
+ return [...counts].map(([name, count]) => count > 1 ? `${name} ×${count}` : name).join(", ");
4442
+ }
4443
+ const pluralize = (count, noun) => `${count} ${noun}${count === 1 ? "" : "s"}`;
4444
+ /**
4445
+ * Height transition between closed and its content's height. Closed content
4446
+ * stays mounted (so its state survives) but is inert.
4447
+ * @internal
4448
+ */
4449
+ function Collapsible({ open, id, className, children }) {
4450
+ const ref = useRef(null);
4451
+ useLayoutEffect(() => {
4452
+ ref.current?.toggleAttribute("inert", !open);
4453
+ }, [open]);
4454
+ return /* @__PURE__ */ jsx("div", {
4455
+ ref,
4456
+ id,
4457
+ className: twMerge("cpk:grid cpk:transition-[grid-template-rows] cpk:duration-300 cpk:ease-[cubic-bezier(0.16,1,0.3,1)] cpk:motion-reduce:transition-none", className),
4458
+ style: { gridTemplateRows: open ? "1fr" : "0fr" },
4459
+ children: /* @__PURE__ */ jsx("div", {
4460
+ className: "cpk:min-h-0 cpk:overflow-hidden",
4461
+ children
4462
+ })
4463
+ });
4464
+ }
4465
+ function StatusIcon({ status, className }) {
4466
+ const Icon = {
4467
+ running: Loader2,
4468
+ done: Check,
4469
+ failed: X,
4470
+ stopped: Circle
4471
+ }[status];
4472
+ return /* @__PURE__ */ jsx(Icon, {
4473
+ "aria-hidden": "true",
4474
+ className: twMerge("cpk:size-3.5 cpk:shrink-0 cpk:text-muted-foreground", status === "running" && "cpk:animate-spin", status === "failed" && "cpk:text-destructive", className)
4475
+ });
4476
+ }
4477
+ function Chevron({ open, className }) {
4478
+ return /* @__PURE__ */ jsx(ChevronRight, {
4479
+ "aria-hidden": "true",
4480
+ className: twMerge("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", open && "cpk:rotate-90", className)
4481
+ });
4482
+ }
4483
+ function FailedCount({ calls }) {
4484
+ const failed = calls.filter((call) => call.toolMessage?.error).length;
4485
+ if (!failed) return null;
4486
+ return /* @__PURE__ */ jsxs("span", {
4487
+ className: "cpk:shrink-0 cpk:text-destructive",
4488
+ children: [failed, " failed"]
4489
+ });
4490
+ }
4491
+ /**
4492
+ * Guarded JSON.stringify for the payload blocks. A circular reference would
4493
+ * otherwise crash the entire React tree on render.
4494
+ */
4495
+ function stringifyPayload(value) {
4496
+ if (typeof value === "string") return value;
4497
+ try {
4498
+ return JSON.stringify(value, null, 2);
4499
+ } catch (err) {
4500
+ console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload; falling back to String():", err);
4501
+ try {
4502
+ return String(value);
4503
+ } catch {
4504
+ return "[unserializable]";
4505
+ }
4506
+ }
4507
+ }
4508
+ function Payload({ label, value }) {
4509
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
4510
+ className: "cpk:text-[11px] cpk:font-medium cpk:uppercase cpk:tracking-wide cpk:text-muted-foreground",
4511
+ children: label
4512
+ }), /* @__PURE__ */ jsx("pre", {
4513
+ className: "cpk:m-0 cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-lg cpk:bg-muted cpk:p-2.5 cpk:font-mono cpk:text-xs cpk:leading-relaxed cpk:text-foreground cpk:whitespace-pre-wrap cpk:break-words",
4514
+ children: stringifyPayload(value)
4515
+ })] });
4516
+ }
4517
+ /**
4518
+ * A tool call's arguments and, once it has one, its result.
4519
+ * @internal
4520
+ */
4521
+ function ToolCallPayloads({ parameters, result, className }) {
4522
+ return /* @__PURE__ */ jsxs("div", {
4523
+ className: twMerge("cpk:grid cpk:gap-3", className),
4524
+ children: [/* @__PURE__ */ jsx(Payload, {
4525
+ label: "Arguments",
4526
+ value: parameters ?? {}
4527
+ }), result !== void 0 && /* @__PURE__ */ jsx(Payload, {
4528
+ label: "Result",
4529
+ value: result
4530
+ })]
4531
+ });
4532
+ }
4533
+ /**
4534
+ * The built-in tool card drawn as a row inside a group: status, name and main
4535
+ * argument, opening to the payloads.
4536
+ * @internal
4537
+ */
4538
+ function ToolCallRow({ name, parameters, result, status, appearance, className, ...props }) {
4539
+ const [open, setOpen] = useState(false);
4540
+ const detailsId = useId();
4541
+ const preview = argumentPreview(parameters);
4542
+ const line = appearance === "line";
4543
+ return /* @__PURE__ */ jsxs("div", {
4544
+ "data-copilotkit": true,
4545
+ className: twMerge(!line && "cpk:border-t cpk:border-border", className),
4546
+ ...props,
4547
+ children: [/* @__PURE__ */ jsxs("button", {
4548
+ type: "button",
4549
+ "aria-expanded": open,
4550
+ "aria-controls": detailsId,
4551
+ onClick: () => setOpen((value) => !value),
4552
+ className: twMerge("cpk:group/row cpk:m-0 cpk:flex cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-2 cpk:border-none cpk:bg-transparent cpk:text-left cpk:text-[13px] cpk:text-inherit cpk:transition-colors cpk:hover:bg-accent/60 cpk:focus-visible:outline-2 cpk:focus-visible:-outline-offset-2 cpk:focus-visible:outline-ring", line ? "cpk:-mx-1.5 cpk:w-[calc(100%+0.75rem)] cpk:rounded-md cpk:px-1.5 cpk:py-1" : "cpk:w-full cpk:px-3.5 cpk:py-2"),
4553
+ children: [
4554
+ /* @__PURE__ */ jsx(StatusIcon, { status }),
4555
+ /* @__PURE__ */ jsx("span", {
4556
+ "data-testid": "copilot-tool-render-name",
4557
+ className: twMerge("cpk:shrink-0 cpk:font-mono cpk:text-[13px] cpk:font-medium cpk:text-foreground", status === "running" && "cpk-shimmer"),
4558
+ children: name
4559
+ }),
4560
+ preview && /* @__PURE__ */ jsx("span", {
4561
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-[13px] cpk:text-muted-foreground",
4562
+ children: preview
4563
+ }),
4564
+ /* @__PURE__ */ jsx("span", {
4565
+ "data-testid": "copilot-tool-render-status",
4566
+ className: "cpk:sr-only",
4567
+ children: statusLabels[status]
4568
+ }),
4569
+ /* @__PURE__ */ jsx(Chevron, {
4570
+ open,
4571
+ className: twMerge("cpk:ml-auto cpk:text-muted-foreground", !open && "cpk:opacity-0 cpk:group-hover/row:opacity-100 cpk:group-focus-visible/row:opacity-100")
4572
+ })
4573
+ ]
4574
+ }), /* @__PURE__ */ jsx(Collapsible, {
4575
+ open,
4576
+ id: detailsId,
4577
+ children: /* @__PURE__ */ jsx(ToolCallPayloads, {
4578
+ parameters,
4579
+ result,
4580
+ className: line ? "cpk:mt-1 cpk:mb-2 cpk:ml-5.5" : "cpk:px-3.5 cpk:pt-1 cpk:pb-3"
4581
+ })
4582
+ })]
4583
+ });
4584
+ }
4585
+ /**
4586
+ * Tool calls made in a row, collapsed into one summary. While a call runs the
4587
+ * summary names it ("Running read_file src/app.ts"); once they're all done it
4588
+ * reads "Used 3 tools" and opens to the calls.
4589
+ *
4590
+ * Each call is drawn by its registered renderer. CopilotKit's built-in card
4591
+ * (`useDefaultRenderTool`) draws a compact row inside the card and line
4592
+ * appearances; a custom wildcard renderer draws whatever it draws.
4593
+ */
4594
+ function CopilotChatToolCallGroup({ calls, renderCall, appearance = "card", isActive = false, className, ...props }) {
4595
+ const [open, setOpen] = useState(false);
4596
+ const bodyId = useId();
4597
+ const latest = calls[calls.length - 1];
4598
+ if (!latest) return null;
4599
+ const current = isActive ? calls.find((call) => !call.toolMessage) ?? latest : void 0;
4600
+ const preview = current ? entryPreview(current) : void 0;
4601
+ const done = calls.filter((call) => call.toolMessage).length;
4602
+ const statuses = calls.map((call) => entryStatus(call, isActive));
4603
+ const failedIds = new Set(calls.filter((_, index) => statuses[index] === "failed").map((call) => call.toolCall.id));
4604
+ const status = current ? "running" : failedIds.size > 0 ? "failed" : statuses.includes("stopped") ? "stopped" : "done";
4605
+ const rows = (rowAppearance) => /* @__PURE__ */ jsx(ToolCallGroupContext.Provider, {
4606
+ value: {
4607
+ appearance: rowAppearance,
4608
+ failedIds,
4609
+ active: isActive
4610
+ },
4611
+ children: calls.map((call) => /* @__PURE__ */ jsx(React.Fragment, { children: renderCall(call) }, call.toolCall.id))
4612
+ });
4613
+ const groupProps = {
4614
+ "data-copilotkit": true,
4615
+ "data-testid": "copilot-tool-call-group",
4616
+ "data-appearance": appearance,
4617
+ "data-state": status,
4618
+ ...props
4619
+ };
4620
+ const runningLabel = current && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
4621
+ className: "cpk:shrink-0 cpk:font-medium cpk-shimmer",
4622
+ children: ["Running ", current.toolCall.function.name]
4623
+ }), preview && /* @__PURE__ */ jsx("span", {
4624
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:opacity-70",
4625
+ children: preview
4626
+ })] });
4627
+ if (appearance === "line") {
4628
+ if (calls.length === 1 && !current) return /* @__PURE__ */ jsx("div", {
4629
+ ...groupProps,
4630
+ className: twMerge("cpk:my-2", className),
4631
+ children: /* @__PURE__ */ jsx(ToolCallGroupContext.Provider, {
4632
+ value: {
4633
+ appearance: "line",
4634
+ failedIds,
4635
+ active: isActive
4636
+ },
4637
+ children: renderCall(latest)
4638
+ })
4639
+ });
4640
+ return /* @__PURE__ */ jsxs("div", {
4641
+ ...groupProps,
4642
+ className: twMerge("cpk:my-3", className),
4643
+ children: [/* @__PURE__ */ jsxs("button", {
4644
+ type: "button",
4645
+ "aria-expanded": open,
4646
+ "aria-controls": bodyId,
4647
+ onClick: () => setOpen((value) => !value),
4648
+ className: "cpk:m-0 cpk:inline-flex cpk:max-w-full cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:hover:text-foreground cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring",
4649
+ children: [runningLabel || /* @__PURE__ */ jsxs(Fragment$1, { children: [
4650
+ /* @__PURE__ */ jsxs("span", {
4651
+ className: "cpk:shrink-0 cpk:font-medium",
4652
+ children: ["Used ", pluralize(calls.length, "tool")]
4653
+ }),
4654
+ /* @__PURE__ */ jsx("span", {
4655
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:opacity-70",
4656
+ children: toolNames(calls)
4657
+ }),
4658
+ /* @__PURE__ */ jsx(FailedCount, { calls })
4659
+ ] }), /* @__PURE__ */ jsx(Chevron, { open })]
4660
+ }), /* @__PURE__ */ jsx(Collapsible, {
4661
+ open,
4662
+ id: bodyId,
4663
+ children: /* @__PURE__ */ jsx("div", {
4664
+ className: "cpk:mt-1 cpk:ml-1 cpk:border-l-2 cpk:border-border cpk:py-0.5 cpk:pl-3",
4665
+ children: rows("line")
4666
+ })
4667
+ })]
4668
+ });
4669
+ }
4670
+ if (appearance === "stack") {
4671
+ if (calls.length === 1) return /* @__PURE__ */ jsx("div", {
4672
+ ...groupProps,
4673
+ className: twMerge("cpk:py-1", className),
4674
+ children: renderCall(latest)
4675
+ });
4676
+ const behind = open ? 0 : Math.min(calls.length - 1, 2);
4677
+ const layer = "cpk:rounded-xl cpk:border cpk:border-border cpk:bg-card cpk:text-card-foreground";
4678
+ const latestStatus = entryStatus(latest, isActive);
4679
+ const latestPreview = entryPreview(latest);
4680
+ return /* @__PURE__ */ jsxs("div", {
4681
+ ...groupProps,
4682
+ className: twMerge("cpk:my-3", className),
4683
+ style: {
4684
+ paddingBottom: behind * 6,
4685
+ ...props.style
4686
+ },
4687
+ children: [/* @__PURE__ */ jsxs("div", {
4688
+ className: "cpk:relative cpk:isolate",
4689
+ children: [Array.from({ length: behind }, (_, index) => /* @__PURE__ */ jsx("div", {
4690
+ "aria-hidden": "true",
4691
+ className: twMerge(layer, "cpk:absolute cpk:inset-0"),
4692
+ style: {
4693
+ transform: `translateY(${(index + 1) * 6}px) scaleX(${1 - (index + 1) * .045})`,
4694
+ zIndex: -1 - index,
4695
+ opacity: 1 - (index + 1) * .28
4696
+ }
4697
+ }, index)), /* @__PURE__ */ jsx("button", {
4698
+ type: "button",
4699
+ "aria-expanded": open,
4700
+ "aria-controls": open ? bodyId : void 0,
4701
+ onClick: () => setOpen((value) => !value),
4702
+ className: twMerge("cpk:relative cpk:m-0 cpk:flex cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:text-left cpk:transition-colors cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring", open ? "cpk:mb-1 cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-xs cpk:text-muted-foreground cpk:hover:text-foreground" : twMerge(layer, "cpk:w-full cpk:gap-2 cpk:px-3.5 cpk:py-2.5 cpk:text-[13px] cpk:shadow-[0_1px_2px_rgb(0_0_0/0.04)] cpk:hover:bg-accent")),
4703
+ children: open ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
4704
+ pluralize(calls.length, "tool call"),
4705
+ /* @__PURE__ */ jsx(FailedCount, { calls }),
4706
+ /* @__PURE__ */ jsx(Chevron, {
4707
+ open: true,
4708
+ className: "cpk:-rotate-90"
4709
+ }),
4710
+ "Collapse"
4711
+ ] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
4712
+ className: "cpk:flex cpk:min-w-0 cpk:flex-1 cpk:items-center cpk:gap-2 cpk:animate-[cpk-stack-drop_260ms_cubic-bezier(0.16,1,0.3,1)] cpk:motion-reduce:animate-none",
4713
+ children: [
4714
+ /* @__PURE__ */ jsx(StatusIcon, { status: latestStatus }),
4715
+ /* @__PURE__ */ jsx("span", {
4716
+ className: twMerge("cpk:shrink-0 cpk:font-mono cpk:font-medium cpk:text-foreground", latestStatus === "running" && "cpk-shimmer"),
4717
+ children: latest.toolCall.function.name
4718
+ }),
4719
+ latestPreview && /* @__PURE__ */ jsx("span", {
4720
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-muted-foreground",
4721
+ children: latestPreview
4722
+ })
4723
+ ]
4724
+ }, latest.toolCall.id), /* @__PURE__ */ jsxs("span", {
4725
+ className: "cpk:ml-2 cpk:inline-flex cpk:shrink-0 cpk:items-center cpk:gap-2 cpk:text-xs cpk:text-muted-foreground",
4726
+ children: [
4727
+ /* @__PURE__ */ jsx(FailedCount, { calls }),
4728
+ /* @__PURE__ */ jsx("span", {
4729
+ className: "cpk:tabular-nums",
4730
+ children: pluralize(calls.length, "call")
4731
+ }),
4732
+ /* @__PURE__ */ jsx(Chevron, {
4733
+ open: false,
4734
+ className: "cpk:rotate-90"
4735
+ })
4736
+ ]
4737
+ })] })
4738
+ })]
4739
+ }), open && /* @__PURE__ */ jsx("div", {
4740
+ id: bodyId,
4741
+ className: "cpk:[&>*]:animate-[cpk-stack-fan_300ms_cubic-bezier(0.16,1,0.3,1)_both] cpk:motion-reduce:[&>*]:animate-none",
4742
+ children: calls.map((call) => /* @__PURE__ */ jsx(React.Fragment, { children: renderCall(call) }, call.toolCall.id))
4743
+ })]
4744
+ });
4745
+ }
4746
+ if (calls.length === 1) return /* @__PURE__ */ jsx("div", {
4747
+ ...groupProps,
4748
+ className: twMerge("cpk:py-1", className),
4749
+ children: renderCall(latest)
4750
+ });
4751
+ return /* @__PURE__ */ jsxs("div", {
4752
+ ...groupProps,
4753
+ className: twMerge("cpk:my-3 cpk:overflow-hidden cpk:rounded-xl cpk:border cpk:border-border cpk:bg-card cpk:text-card-foreground", className),
4754
+ children: [/* @__PURE__ */ jsxs("button", {
4755
+ type: "button",
4756
+ "aria-expanded": open,
4757
+ "aria-controls": bodyId,
4758
+ onClick: () => setOpen((value) => !value),
4759
+ className: "cpk:m-0 cpk:flex cpk:w-full cpk:min-w-0 cpk:cursor-pointer cpk:items-center cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:px-3.5 cpk:py-2.5 cpk:text-left cpk:text-[13px] cpk:text-inherit cpk:transition-colors cpk:hover:bg-accent/60 cpk:focus-visible:outline-2 cpk:focus-visible:-outline-offset-2 cpk:focus-visible:outline-ring",
4760
+ children: [
4761
+ /* @__PURE__ */ jsx(StatusIcon, { status }),
4762
+ runningLabel || /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
4763
+ className: "cpk:shrink-0 cpk:font-medium cpk:text-foreground",
4764
+ children: ["Used ", pluralize(calls.length, "tool")]
4765
+ }), /* @__PURE__ */ jsx("span", {
4766
+ className: "cpk:min-w-0 cpk:truncate cpk:font-mono cpk:text-xs cpk:text-muted-foreground",
4767
+ children: toolNames(calls)
4768
+ })] }),
4769
+ /* @__PURE__ */ jsxs("span", {
4770
+ className: "cpk:ml-auto cpk:flex cpk:shrink-0 cpk:items-center cpk:gap-2 cpk:pl-2 cpk:text-xs cpk:text-muted-foreground",
4771
+ children: [status === "failed" ? /* @__PURE__ */ jsx(FailedCount, { calls }) : /* @__PURE__ */ jsx("span", {
4772
+ className: "cpk:tabular-nums",
4773
+ children: status === "running" ? `${done} done` : statusLabels[status]
4774
+ }), /* @__PURE__ */ jsx(Chevron, { open })]
4775
+ })
4776
+ ]
4777
+ }), /* @__PURE__ */ jsx(Collapsible, {
4778
+ open,
4779
+ id: bodyId,
4780
+ children: /* @__PURE__ */ jsx("div", { children: rows("card") })
4781
+ })]
4782
+ });
4783
+ }
4784
+
4275
4785
  //#endregion
4276
4786
  //#region src/v2/hooks/use-default-render-tool.tsx
4277
4787
  /**
@@ -4355,7 +4865,8 @@ function useDefaultRenderTool(config, deps) {
4355
4865
  const userRender = config?.render;
4356
4866
  useRenderTool({
4357
4867
  name: "*",
4358
- render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(raw) })
4868
+ render: userRender ? (raw) => userRender(adaptRendererProps(raw)) : (raw) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(raw) }),
4869
+ group: userRender ? config?.group === true : true
4359
4870
  }, deps);
4360
4871
  }
4361
4872
  /**
@@ -4376,9 +4887,23 @@ function safeStringifyForPre(value) {
4376
4887
  }
4377
4888
  }
4378
4889
  function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
4890
+ const group = useContext(ToolCallGroupContext);
4379
4891
  const [isExpanded, setIsExpanded] = useState(false);
4380
4892
  const isActive = status === "inProgress" || status === "executing";
4381
4893
  const isComplete = status === "complete";
4894
+ if (group) return /* @__PURE__ */ jsx(ToolCallRow, {
4895
+ "data-testid": "copilot-tool-render",
4896
+ "data-tool-name": name,
4897
+ "data-tool-call-id": toolCallId,
4898
+ "data-status": status,
4899
+ "data-args": safeStringifyForAttr(parameters),
4900
+ "data-result": safeStringifyForAttr(result),
4901
+ name,
4902
+ parameters,
4903
+ result,
4904
+ status: isComplete ? group.failedIds.has(toolCallId) ? "failed" : "done" : group.active ? "running" : "stopped",
4905
+ appearance: group.appearance
4906
+ });
4382
4907
  const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
4383
4908
  const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
4384
4909
  const badgeClassName = isActive ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : isComplete ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300";
@@ -6075,38 +6600,227 @@ function useLearningContainersInCurrentThread({ learningContainers }) {
6075
6600
  });
6076
6601
  }
6077
6602
 
6603
+ //#endregion
6604
+ //#region src/v2/components/chat/tool-call-display-context.ts
6605
+ /** @internal */
6606
+ const ToolCallDisplayContext = createContext(null);
6607
+
6608
+ //#endregion
6609
+ //#region src/v2/components/chat/tool-call-layout.ts
6610
+ function contentLength(content) {
6611
+ return typeof content === "string" ? content.length : JSON.stringify(content ?? "").length;
6612
+ }
6613
+ const entrySignature = ({ toolCall, toolMessage }) => [
6614
+ toolCall.id,
6615
+ toolCall.function.name,
6616
+ toolCall.function.arguments.length,
6617
+ toolMessage ? `${contentLength(toolMessage.content)}${toolMessage.error ? "!" : ""}` : "-"
6618
+ ].join(":");
6619
+ function signatureOf(segments) {
6620
+ return segments.map((segment) => segment.kind === "call" ? `c(${entrySignature(segment.call)})` : `g${segment.trailing ? "*" : ""}(${segment.calls.map(entrySignature).join(",")})`).join("|");
6621
+ }
6622
+ /** Index of tool results by the call they answer. */
6623
+ function getToolResults(messages) {
6624
+ const results = /* @__PURE__ */ new Map();
6625
+ for (const message of messages) if (message.role === "tool") results.set(message.toolCallId, message);
6626
+ return results;
6627
+ }
6628
+ /**
6629
+ * Lays out the tool calls of every assistant message in `rendered`.
6630
+ *
6631
+ * Calls drawn by a renderer that opted into grouping join the group that's
6632
+ * open; anything else that shows between them closes it: assistant text, a
6633
+ * user, reasoning or activity message, or a call drawn by any other renderer. A group belongs to
6634
+ * the message where it starts, so later messages whose calls joined it render
6635
+ * no tool calls themselves.
6636
+ *
6637
+ * @param rendered - The messages as the chat renders them, in order.
6638
+ * @param all - Every message, for finding tool results (a transform may hide them).
6639
+ * @param renderers - The registered renderers.
6640
+ */
6641
+ function getToolCallLayouts(rendered, all, renderers) {
6642
+ const results = getToolResults(all);
6643
+ const layouts = /* @__PURE__ */ new Map();
6644
+ let open;
6645
+ for (const message of rendered) {
6646
+ if (message.role === "tool") continue;
6647
+ if (message.role !== "assistant") {
6648
+ open = void 0;
6649
+ continue;
6650
+ }
6651
+ if (typeof message.content === "string" && message.content.trim()) open = void 0;
6652
+ const segments = [];
6653
+ layouts.set(message.id, segments);
6654
+ const toolCalls = /* @__PURE__ */ new Map();
6655
+ for (const toolCall of message.toolCalls ?? []) toolCalls.set(toolCall.id, toolCall);
6656
+ for (const toolCall of toolCalls.values()) {
6657
+ const entry = {
6658
+ toolCall,
6659
+ toolMessage: results.get(toolCall.id)
6660
+ };
6661
+ const name = toolCall.function.name;
6662
+ const named = renderers.named.has(name);
6663
+ const rendered = named || renderers.wildcard;
6664
+ const groups = named ? renderers.groupedNames.has(name) : renderers.wildcardGroups;
6665
+ if (!rendered || !groups) {
6666
+ if (rendered) open = void 0;
6667
+ segments.push({
6668
+ kind: "call",
6669
+ id: toolCall.id,
6670
+ call: entry,
6671
+ rendered
6672
+ });
6673
+ } else if (open) open.calls.push(entry);
6674
+ else {
6675
+ open = {
6676
+ kind: "group",
6677
+ id: toolCall.id,
6678
+ calls: [entry],
6679
+ trailing: false
6680
+ };
6681
+ segments.push(open);
6682
+ }
6683
+ }
6684
+ }
6685
+ if (open) open.trailing = true;
6686
+ const layout = /* @__PURE__ */ new Map();
6687
+ for (const [id, segments] of layouts) layout.set(id, {
6688
+ segments,
6689
+ signature: signatureOf(segments)
6690
+ });
6691
+ return layout;
6692
+ }
6693
+ /**
6694
+ * Finds the working phase of every reply in `rendered`: its messages up to the
6695
+ * last one that calls a tool. Text in there was narration; what follows is the
6696
+ * reply itself. Replies that called no tools are omitted, except the latest,
6697
+ * which may be about to.
6698
+ */
6699
+ function getTurnWork(rendered, layouts) {
6700
+ const turns = /* @__PURE__ */ new Map();
6701
+ let start = 0;
6702
+ while (start < rendered.length) {
6703
+ let end = start;
6704
+ while (end < rendered.length && rendered[end].role !== "user") end++;
6705
+ const key = start > 0 ? rendered[start - 1].id : "start";
6706
+ const visible = rendered.slice(start, end).filter((message) => message.role !== "tool");
6707
+ const isLatest = end === rendered.length;
6708
+ const calls = [];
6709
+ for (const message of visible) for (const segment of layouts.get(message.id)?.segments ?? []) if (segment.kind === "group") calls.push(...segment.calls);
6710
+ else if (segment.rendered) calls.push(segment.call);
6711
+ const shown = new Set(calls.map((entry) => entry.toolCall.id));
6712
+ let lastWork = -1;
6713
+ visible.forEach((message, index) => {
6714
+ if (message.role === "assistant" && message.toolCalls?.some((call) => shown.has(call.id))) lastWork = index;
6715
+ });
6716
+ const first = visible[0];
6717
+ if (first && (calls.length > 0 || isLatest)) {
6718
+ let lastAssistant = -1;
6719
+ visible.forEach((message, index) => {
6720
+ if (message.role === "assistant") lastAssistant = index;
6721
+ });
6722
+ const replyText = visible[lastAssistant]?.content;
6723
+ const endsWithWork = lastAssistant >= 0 && lastAssistant <= lastWork && typeof replyText === "string" && replyText.trim().length > 0;
6724
+ turns.set(key, {
6725
+ key,
6726
+ firstMessageId: first.id,
6727
+ workMessageIds: new Set(visible.slice(0, lastWork + 1).map((message) => message.id)),
6728
+ replyTextMessageId: endsWithWork ? visible[lastAssistant].id : void 0,
6729
+ calls,
6730
+ isLatest
6731
+ });
6732
+ }
6733
+ start = end + 1;
6734
+ }
6735
+ return turns;
6736
+ }
6737
+
6078
6738
  //#endregion
6079
6739
  //#region src/v2/components/chat/CopilotChatToolCallsView.tsx
6080
- function CopilotChatToolCallsView({ message, messages = [] }) {
6740
+ const appearances = {
6741
+ grouped: "card",
6742
+ line: "line",
6743
+ stacked: "stack"
6744
+ };
6745
+ function CopilotChatToolCallsView({ message, messages = [], display: displayProp, group }) {
6081
6746
  const renderToolCall = useRenderToolCall();
6747
+ const rendererNames = useToolCallRendererNames();
6082
6748
  const { copilotkit } = useCopilotKit();
6083
6749
  const subscribe = useCallback((onChange) => copilotkit.ɵsubscribeToLearningConfigured(onChange), [copilotkit]);
6084
6750
  const getSnapshot = () => copilotkit.ɵlearningConfigured;
6085
6751
  const learningConfigured = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
6752
+ const context = useContext(ToolCallDisplayContext);
6753
+ const display = displayProp ?? context?.display ?? "grouped";
6754
+ const viewLayout = context?.layouts.get(message.id);
6755
+ const layout = useMemo(() => {
6756
+ if (display === "separate") return void 0;
6757
+ return viewLayout ?? getToolCallLayouts([message], messages, rendererNames).get(message.id);
6758
+ }, [
6759
+ display,
6760
+ viewLayout,
6761
+ message,
6762
+ messages,
6763
+ rendererNames
6764
+ ]);
6086
6765
  if (!message.toolCalls || message.toolCalls.length === 0) return null;
6087
- return /* @__PURE__ */ jsx(Fragment$1, { children: message.toolCalls.map((toolCall) => {
6766
+ const renderCall = (entry) => {
6088
6767
  const rendered = renderToolCall({
6089
- toolCall,
6090
- toolMessage: messages.find((m) => m.role === "tool" && m.toolCallId === toolCall.id)
6768
+ toolCall: entry.toolCall,
6769
+ toolMessage: entry.toolMessage
6091
6770
  });
6092
- if (rendered === null) return null;
6093
- if (!learningConfigured) return rendered;
6771
+ if (rendered === null || !learningConfigured) return rendered;
6094
6772
  return /* @__PURE__ */ jsx("div", {
6095
- "data-tool-call-id": toolCall.id,
6773
+ "data-tool-call-id": entry.toolCall.id,
6096
6774
  style: { display: "contents" },
6097
6775
  children: rendered
6098
- }, toolCall.id);
6776
+ });
6777
+ };
6778
+ if (!layout) return /* @__PURE__ */ jsx(Fragment$1, { children: message.toolCalls.map((toolCall) => {
6779
+ const toolMessage = messages.find((m) => m.role === "tool" && m.toolCallId === toolCall.id);
6780
+ return /* @__PURE__ */ jsx(React.Fragment, { children: renderCall({
6781
+ toolCall,
6782
+ toolMessage
6783
+ }) }, toolCall.id);
6784
+ }) });
6785
+ const workOpen = context?.workOpen.get(message.id);
6786
+ const groupActive = (trailing) => trailing && (context ? context.isRunning : true);
6787
+ return /* @__PURE__ */ jsx(Fragment$1, { children: layout.segments.map((segment) => {
6788
+ if (segment.kind === "call") return /* @__PURE__ */ jsx(React.Fragment, { children: renderCall(segment.call) }, segment.id);
6789
+ if (display === "turn") {
6790
+ const failedIds = new Set(segment.calls.filter((call) => call.toolMessage?.error).map((call) => call.toolCall.id));
6791
+ return /* @__PURE__ */ jsx(Collapsible, {
6792
+ open: workOpen ?? true,
6793
+ className: "cpk:-mx-2",
6794
+ children: /* @__PURE__ */ jsx("div", {
6795
+ className: "cpk:px-2 cpk:py-1.5",
6796
+ children: /* @__PURE__ */ jsx(ToolCallGroupContext.Provider, {
6797
+ value: {
6798
+ appearance: "line",
6799
+ failedIds,
6800
+ active: groupActive(segment.trailing)
6801
+ },
6802
+ children: segment.calls.map((call) => /* @__PURE__ */ jsx(React.Fragment, { children: renderCall(call) }, call.toolCall.id))
6803
+ })
6804
+ })
6805
+ }, segment.id);
6806
+ }
6807
+ return /* @__PURE__ */ jsx(React.Fragment, { children: renderSlot(group, CopilotChatToolCallGroup, {
6808
+ calls: segment.calls,
6809
+ renderCall,
6810
+ appearance: appearances[display] ?? "card",
6811
+ isActive: groupActive(segment.trailing)
6812
+ }) }, segment.id);
6099
6813
  }) });
6100
6814
  }
6101
6815
 
6102
6816
  //#endregion
6103
6817
  //#region src/v2/components/chat/CopilotChatInspectorButton.tsx
6104
6818
  let shortcutsHidden = false;
6105
- const listeners = /* @__PURE__ */ new Set();
6819
+ const listeners$1 = /* @__PURE__ */ new Set();
6106
6820
  const subscribe = (listener) => {
6107
- listeners.add(listener);
6821
+ listeners$1.add(listener);
6108
6822
  return () => {
6109
- listeners.delete(listener);
6823
+ listeners$1.delete(listener);
6110
6824
  };
6111
6825
  };
6112
6826
  const getSnapshot = () => shortcutsHidden;
@@ -6116,7 +6830,7 @@ function useInspectorShortcutsHidden() {
6116
6830
  }
6117
6831
  function hideShortcuts() {
6118
6832
  shortcutsHidden = true;
6119
- listeners.forEach((listener) => listener());
6833
+ listeners$1.forEach((listener) => listener());
6120
6834
  }
6121
6835
  function InspectorKiteIcon({ className, ...props }) {
6122
6836
  const id = useId();
@@ -6383,47 +7097,544 @@ function CopilotChatInspectorButton({ title, className, onClick, onPointerDown,
6383
7097
  }
6384
7098
 
6385
7099
  //#endregion
6386
- //#region src/v2/components/chat/CopilotChatAssistantMessage.tsx
6387
- /**
6388
- * Binds the Inspector action to the active chat without making the full
6389
- * assistant message subscribe to chat configuration changes.
6390
- */
6391
- function BoundInspectorButton({ inspectorButton, messageId, openInspector, ...props }) {
6392
- const chatConfiguration = useCopilotChatConfiguration();
6393
- return renderSlot(inspectorButton, CopilotChatAssistantMessage.InspectorButton, {
6394
- ...props,
6395
- onClick: () => openInspector({
6396
- messageId,
6397
- threadId: chatConfiguration?.threadId,
6398
- agentId: chatConfiguration?.agentId
6399
- })
7100
+ //#region src/v2/components/ui/popover.tsx
7101
+ function Popover({ ...props }) {
7102
+ return /* @__PURE__ */ jsx(PopoverPrimitive.Root, {
7103
+ "data-slot": "popover",
7104
+ ...props
6400
7105
  });
6401
7106
  }
6402
- function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, toolCallsView, children, className, ...props }) {
6403
- useKatexStyles();
6404
- const { isInspectorEnabled, openInspector } = useCopilotKitInspector();
6405
- const shortcutsHidden = useInspectorShortcutsHidden();
6406
- const showInspectorShortcut = isInspectorEnabled && !shortcutsHidden;
6407
- const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
6408
- const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
6409
- if (message.content) return await copyToClipboard(message.content);
6410
- return false;
6411
- } });
6412
- const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, { onClick: onThumbsUp ? () => onThumbsUp(message) : void 0 });
6413
- const boundInspectorButton = showInspectorShortcut ? /* @__PURE__ */ jsx(BoundInspectorButton, {
7107
+ function PopoverAnchor({ ...props }) {
7108
+ return /* @__PURE__ */ jsx(PopoverPrimitive.Anchor, {
7109
+ "data-slot": "popover-anchor",
7110
+ ...props
7111
+ });
7112
+ }
7113
+ /**
7114
+ * Popover content in a portal, so it floats over the page instead of taking
7115
+ * room in the layout, with the CopilotKit style scope on it.
7116
+ */
7117
+ const PopoverContent = React$1.forwardRef(({ className, align = "start", sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(PopoverPrimitive.Content, {
7118
+ ref,
7119
+ "data-copilotkit": true,
7120
+ "data-copilotkit-popover": true,
7121
+ "data-slot": "popover-content",
7122
+ align,
7123
+ sideOffset,
7124
+ className: cn("cpk:z-50 cpk:outline-none cpk:data-[state=open]:animate-in cpk:data-[state=open]:fade-in-0 cpk:data-[state=open]:zoom-in-95 cpk:data-[side=bottom]:slide-in-from-top-1 cpk:data-[side=top]:slide-in-from-bottom-1", className),
7125
+ ...props
7126
+ }) }));
7127
+ PopoverContent.displayName = "PopoverContent";
7128
+
7129
+ //#endregion
7130
+ //#region src/v2/lib/message-feedback.ts
7131
+ /** Trajectory event name for a rating of one assistant message. */
7132
+ const MESSAGE_FEEDBACK_EVENT_NAME = "message.feedback";
7133
+ /** Longest comment kept, in characters; the rest is cut so the event stays small. */
7134
+ const MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH = 1e3;
7135
+ /** Longest excerpt of the rated response sent with the rating, in characters. */
7136
+ const RESPONSE_EXCERPT_LENGTH = 500;
7137
+ const ratings = /* @__PURE__ */ new Map();
7138
+ const listeners = /* @__PURE__ */ new Map();
7139
+ const threadKey = (agentId, threadId) => JSON.stringify([agentId, threadId]);
7140
+ const messageKey = (agentId, threadId, messageId) => JSON.stringify([
7141
+ agentId,
7142
+ threadId,
7143
+ messageId
7144
+ ]);
7145
+ /** Cuts `text` to `max` characters without splitting a surrogate pair. */
7146
+ const clip = (text, max) => {
7147
+ const characters = Array.from(text);
7148
+ return characters.length > max ? characters.slice(0, max).join("") : text;
7149
+ };
7150
+ /** The comment as kept and sent: trimmed and capped, or none. */
7151
+ const normalizeComment = (comment) => {
7152
+ if (typeof comment !== "string") return void 0;
7153
+ const text = clip(comment.trim(), MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH);
7154
+ return text === "" ? void 0 : text;
7155
+ };
7156
+ /** The rating this page gave one message, if any. */
7157
+ function getMessageFeedback(agentId, threadId, messageId) {
7158
+ return ratings.get(messageKey(agentId, threadId, messageId));
7159
+ }
7160
+ /** Calls `listener` whenever a rating in this Thread changes. */
7161
+ function subscribeToMessageFeedback(agentId, threadId, listener) {
7162
+ const key = threadKey(agentId, threadId);
7163
+ let set = listeners.get(key);
7164
+ if (set === void 0) {
7165
+ set = /* @__PURE__ */ new Set();
7166
+ listeners.set(key, set);
7167
+ }
7168
+ set.add(listener);
7169
+ return () => {
7170
+ set.delete(listener);
7171
+ if (set.size === 0) listeners.delete(key);
7172
+ };
7173
+ }
7174
+ /**
7175
+ * Sets (or, with `null`, clears) this page's rating of one message and sends
7176
+ * it to the active Trajectory. Every change is a new event; the newest event
7177
+ * for a `feedbackId` is the current one. Like any Trajectory event, a change
7178
+ * made while no Trajectory captures, for example during a reconnect, is not
7179
+ * sent; the page still shows it.
7180
+ */
7181
+ function setMessageFeedback(params) {
7182
+ const { copilotkit, agentId, threadId, message } = params;
7183
+ const comment = normalizeComment(params.feedback?.comment);
7184
+ const feedback = params.feedback && {
7185
+ rating: params.feedback.rating,
7186
+ ...comment ? { comment } : {}
7187
+ };
7188
+ const key = messageKey(agentId, threadId, message.id);
7189
+ if (feedback) ratings.set(key, feedback);
7190
+ else ratings.delete(key);
7191
+ for (const listener of listeners.get(threadKey(agentId, threadId)) ?? []) listener();
7192
+ const runId = copilotkit.getRunIdForMessage(agentId, threadId, message.id);
7193
+ copilotkit.emitTrajectoryEvent(MESSAGE_FEEDBACK_EVENT_NAME, {
7194
+ feedbackId: `${threadId}:${message.id}`,
7195
+ threadId,
7196
+ messageId: message.id,
7197
+ agentId,
7198
+ ...runId ? { runId } : {},
7199
+ rating: feedback?.rating ?? null,
7200
+ ...feedback?.comment ? { comment: feedback.comment } : {},
7201
+ ...copilotkit.ɵlearningCapturesAgentText ? { response: clip(message.content ?? "", RESPONSE_EXCERPT_LENGTH) } : {}
7202
+ });
7203
+ }
7204
+
7205
+ //#endregion
7206
+ //#region src/v2/components/chat/message-feedback.tsx
7207
+ /**
7208
+ * Set by the message view while Learning is on for this chat. Assistant
7209
+ * messages without it show thumbs only when the app passes its own handlers
7210
+ * or thumb slots.
7211
+ */
7212
+ const MessageFeedbackScopeContext = createContext(null);
7213
+ const capturedCores = /* @__PURE__ */ new WeakSet();
7214
+ const rememberCapture = (copilotkit) => {
7215
+ if (capturedCores.has(copilotkit)) return;
7216
+ capturedCores.add(copilotkit);
7217
+ const unsubscribe = copilotkit.ɵsubscribeToLearningConfigured(() => {
7218
+ if (copilotkit.ɵlearningConfigured) return;
7219
+ capturedCores.delete(copilotkit);
7220
+ unsubscribe();
7221
+ });
7222
+ };
7223
+ /**
7224
+ * Whether the thumbs record feedback for Learning. True once a Trajectory of
7225
+ * this Core has started and as long as Learning stays configured, also while
7226
+ * capture reconnects or is stopped, so the buttons do not flicker. Ratings
7227
+ * given meanwhile still show but, like any Trajectory event, are not
7228
+ * sent. A capture that never starts (for example without a license) leaves it
7229
+ * false.
7230
+ */
7231
+ function useLearningFeedbackEnabled(copilotkit) {
7232
+ const configured = useSyncExternalStore(useCallback((onChange) => copilotkit.ɵsubscribeToLearningConfigured(onChange), [copilotkit]), () => copilotkit.ɵlearningConfigured, () => false);
7233
+ const capturing = useSyncExternalStore(useCallback((onChange) => copilotkit.subscribe({ onTrajectoryChanged: onChange }).unsubscribe, [copilotkit]), () => copilotkit.trajectoryId !== null, () => false);
7234
+ useEffect(() => {
7235
+ if (capturing) rememberCapture(copilotkit);
7236
+ }, [capturing, copilotkit]);
7237
+ return configured && (capturing || capturedCores.has(copilotkit));
7238
+ }
7239
+ const noSubscription = () => () => {};
7240
+ /** Feedback controls for `message`, or `null` when this chat collects none. */
7241
+ function useMessageFeedback(message) {
7242
+ const scope = useContext(MessageFeedbackScopeContext);
7243
+ const subscribe = useCallback((onChange) => scope ? subscribeToMessageFeedback(scope.agentId, scope.threadId, onChange) : noSubscription(), [scope]);
7244
+ const getSnapshot = () => scope ? getMessageFeedback(scope.agentId, scope.threadId, message.id) : void 0;
7245
+ const feedback = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
7246
+ if (!scope) return null;
7247
+ const save = (next) => setMessageFeedback({
7248
+ ...scope,
7249
+ message,
7250
+ feedback: next
7251
+ });
7252
+ return {
7253
+ feedback,
7254
+ rate: (rating) => {
7255
+ if (feedback?.rating !== rating) save({ rating });
7256
+ },
7257
+ clear: () => {
7258
+ if (feedback) save(null);
7259
+ },
7260
+ comment: (comment) => {
7261
+ if (feedback) save({
7262
+ rating: feedback.rating,
7263
+ comment
7264
+ });
7265
+ }
7266
+ };
7267
+ }
7268
+ /**
7269
+ * How long the pointer may be away from the rating and its comment before the
7270
+ * comment closes: enough to cross the gap between them, short enough that
7271
+ * passing over a rating does not leave it open.
7272
+ */
7273
+ const FEEDBACK_COMMENT_CLOSE_DELAY_MS = 150;
7274
+ /**
7275
+ * Drives the comment popover of a rated message.
7276
+ *
7277
+ * It opens when a rating is given and whenever the pointer or keyboard focus
7278
+ * reaches the chosen thumb, with the saved comment in the field. It closes by
7279
+ * itself shortly after the pointer leaves the thumb and the popover, unless
7280
+ * focus is in the field: then it stays until focus moves away, Enter, or
7281
+ * Escape. Closing saves a changed comment; Escape, a click on a thumb, and
7282
+ * removing the rating discard it.
7283
+ */
7284
+ function useFeedbackComment(controls) {
7285
+ const [open, setOpen] = useState(false);
7286
+ const [draft, setDraft] = useState("");
7287
+ const [focusRequest, setFocusRequest] = useState(0);
7288
+ const inputRef = useRef(null);
7289
+ const timer = useRef(void 0);
7290
+ const editing = useRef(false);
7291
+ const focusWaiting = useRef(false);
7292
+ const pointerOver = useRef(false);
7293
+ const returning = useRef(false);
7294
+ const latest = useRef({
7295
+ controls,
7296
+ draft,
7297
+ open
7298
+ });
7299
+ latest.current = {
7300
+ ...latest.current,
7301
+ controls,
7302
+ draft,
7303
+ open
7304
+ };
7305
+ const takeFocusRequest = useCallback(() => {
7306
+ const waiting = focusWaiting.current;
7307
+ focusWaiting.current = false;
7308
+ return waiting;
7309
+ }, []);
7310
+ const cancelClose = useCallback(() => {
7311
+ if (timer.current === void 0) return;
7312
+ clearTimeout(timer.current);
7313
+ timer.current = void 0;
7314
+ }, []);
7315
+ useEffect(() => cancelClose, [cancelClose]);
7316
+ const savedComment = useCallback(() => latest.current.controls?.feedback?.comment ?? "", []);
7317
+ useEffect(() => () => {
7318
+ const { open: wasOpen, controls: current, draft: text } = latest.current;
7319
+ if (wasOpen && current?.feedback && text.trim() !== savedComment()) current.comment(text);
7320
+ }, []);
7321
+ const close = useCallback(({ save = true } = {}) => {
7322
+ cancelClose();
7323
+ focusWaiting.current = false;
7324
+ pointerOver.current = false;
7325
+ if (!latest.current.open) return;
7326
+ const current = latest.current.controls;
7327
+ if (save && current?.feedback && latest.current.draft.trim() !== savedComment()) current.comment(latest.current.draft);
7328
+ editing.current = false;
7329
+ latest.current = {
7330
+ ...latest.current,
7331
+ open: false
7332
+ };
7333
+ setOpen(false);
7334
+ }, [cancelClose, savedComment]);
7335
+ const show = ({ comment, focus }) => {
7336
+ cancelClose();
7337
+ latest.current = {
7338
+ ...latest.current,
7339
+ open: true,
7340
+ draft: comment
7341
+ };
7342
+ setDraft(comment);
7343
+ if (focus) {
7344
+ focusWaiting.current = true;
7345
+ setFocusRequest((request) => request + 1);
7346
+ }
7347
+ setOpen(true);
7348
+ };
7349
+ return {
7350
+ open,
7351
+ draft,
7352
+ setDraft,
7353
+ focusRequest,
7354
+ takeFocusRequest,
7355
+ inputRef,
7356
+ show,
7357
+ enter: (via = "focus") => {
7358
+ cancelClose();
7359
+ if (via === "pointer") pointerOver.current = true;
7360
+ if (returning.current) return;
7361
+ if (!latest.current.open && latest.current.controls?.feedback) show({
7362
+ comment: savedComment(),
7363
+ focus: false
7364
+ });
7365
+ },
7366
+ leave: (via = "focus") => {
7367
+ if (via === "pointer") pointerOver.current = false;
7368
+ else if (pointerOver.current) return;
7369
+ cancelClose();
7370
+ timer.current = setTimeout(() => {
7371
+ timer.current = void 0;
7372
+ if (!editing.current) close();
7373
+ }, FEEDBACK_COMMENT_CLOSE_DELAY_MS);
7374
+ },
7375
+ setEditing: (value) => {
7376
+ editing.current = value;
7377
+ },
7378
+ close,
7379
+ returnFocus: (target) => {
7380
+ returning.current = true;
7381
+ try {
7382
+ target?.focus();
7383
+ } finally {
7384
+ returning.current = false;
7385
+ }
7386
+ }
7387
+ };
7388
+ }
7389
+
7390
+ //#endregion
7391
+ //#region src/v2/components/chat/CopilotChatAssistantMessage.tsx
7392
+ /**
7393
+ * In the `"turn"` tool-call display, text in a reply's working phase was
7394
+ * narration: it fades to muted once a call follows it, and folds away with the
7395
+ * rest of the work when the reply is done. The wrapper is there for every
7396
+ * message in that display, so text that turns into narration keeps its node
7397
+ * (and its place) rather than remounting.
7398
+ */
7399
+ function WorkText({ messageId, children }) {
7400
+ const context = useContext(ToolCallDisplayContext);
7401
+ if (context?.display !== "turn") return children;
7402
+ const open = context.workTextOpen.get(messageId);
7403
+ return /* @__PURE__ */ jsx(Collapsible, {
7404
+ open: open ?? true,
7405
+ className: "cpk:-mx-2",
7406
+ children: /* @__PURE__ */ jsx("div", {
7407
+ className: "cpk:px-2",
7408
+ "data-work-text": open === void 0 ? "reply" : "narration",
7409
+ children
7410
+ })
7411
+ });
7412
+ }
7413
+ /**
7414
+ * Binds the Inspector action to the active chat without making the full
7415
+ * assistant message subscribe to chat configuration changes.
7416
+ */
7417
+ function BoundInspectorButton({ inspectorButton, messageId, openInspector, ...props }) {
7418
+ const chatConfiguration = useCopilotChatConfiguration();
7419
+ return renderSlot(inspectorButton, CopilotChatAssistantMessage.InspectorButton, {
7420
+ ...props,
7421
+ onClick: () => openInspector({
7422
+ messageId,
7423
+ threadId: chatConfiguration?.threadId,
7424
+ agentId: chatConfiguration?.agentId
7425
+ })
7426
+ });
7427
+ }
7428
+ /** Whether focus came from the keyboard; a pointer click also focuses buttons. */
7429
+ function isFocusVisible(element) {
7430
+ try {
7431
+ return element.matches(":focus-visible");
7432
+ } catch {
7433
+ return false;
7434
+ }
7435
+ }
7436
+ const HIDDEN_TOO = {
7437
+ checkVisibilityCSS: true,
7438
+ visibilityProperty: true
7439
+ };
7440
+ const FOCUSABLE = [
7441
+ "a[href]",
7442
+ "button:not([disabled])",
7443
+ "input:not([disabled])",
7444
+ "select:not([disabled])",
7445
+ "textarea:not([disabled])",
7446
+ "[tabindex]"
7447
+ ].map((selector) => `${selector}:not([tabindex="-1"]):not([data-radix-focus-guard])`).join(", ");
7448
+ /** Approximately the element Tab reaches after `element`, outside `skip`. */
7449
+ function nextFocusable(element, skip) {
7450
+ const all = Array.from(element.ownerDocument.querySelectorAll(FOCUSABLE)).filter((candidate) => candidate === element || !skip?.contains(candidate) && !candidate.closest("[hidden], [inert]"));
7451
+ const index = all.indexOf(element);
7452
+ return index === -1 ? null : all[index + 1] ?? null;
7453
+ }
7454
+ /**
7455
+ * The comment popover of a rated message, pointing at the chosen thumb. Kept
7456
+ * apart so only it, not the whole message, follows chat configuration.
7457
+ *
7458
+ * The popover lives in a portal, so it keeps keyboard order by hand: Shift+Tab
7459
+ * from its start returns to the thumb, Tab from its end moves on to the
7460
+ * control after the thumb, and Enter or Escape inside it returns to the thumb.
7461
+ */
7462
+ function FeedbackCommentPopover({ feedbackComment, comment, savedComment, rating, anchor, messageId }) {
7463
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
7464
+ const contentRef = useRef(null);
7465
+ const focusInside = () => contentRef.current?.contains(document.activeElement) ?? false;
7466
+ const { focusRequest, takeFocusRequest, inputRef } = comment;
7467
+ const focusField = useCallback(() => {
7468
+ if (inputRef.current && takeFocusRequest()) inputRef.current.focus();
7469
+ }, [inputRef, takeFocusRequest]);
7470
+ useEffect(() => focusField(), [focusRequest, focusField]);
7471
+ /** Whether `target` is a thumb of this message, whose click decides itself. */
7472
+ const isOwnThumb = (target) => {
7473
+ return (target instanceof Element ? target.closest("[data-copilotkit-rating]") : null)?.closest("[data-message-id]")?.getAttribute("data-message-id") === messageId;
7474
+ };
7475
+ return /* @__PURE__ */ jsxs(Popover, {
7476
+ open: comment.open,
7477
+ onOpenChange: (open) => {
7478
+ if (!open) comment.close();
7479
+ },
7480
+ children: [/* @__PURE__ */ jsx(PopoverAnchor, { virtualRef: anchor }), /* @__PURE__ */ jsx(PopoverContent, {
7481
+ ref: contentRef,
7482
+ side: "bottom",
7483
+ collisionPadding: 8,
7484
+ hideWhenDetached: true,
7485
+ "aria-label": labels.assistantMessageFeedbackCommentPlaceholder,
7486
+ "data-copilotkit-ignore": true,
7487
+ onOpenAutoFocus: (event) => {
7488
+ event.preventDefault();
7489
+ focusField();
7490
+ },
7491
+ onCloseAutoFocus: (event) => event.preventDefault(),
7492
+ onInteractOutside: (event) => {
7493
+ if (isOwnThumb(event.target)) event.preventDefault();
7494
+ },
7495
+ onEscapeKeyDown: (event) => {
7496
+ const returnFocus = focusInside();
7497
+ if (returnFocus) event.stopPropagation();
7498
+ comment.close({ save: false });
7499
+ if (returnFocus) comment.returnFocus(anchor.current);
7500
+ },
7501
+ onKeyDown: (event) => {
7502
+ if (event.key !== "Tab") return;
7503
+ const focusables = Array.from(contentRef.current?.querySelectorAll(FOCUSABLE) ?? []);
7504
+ if (!(event.shiftKey ? document.activeElement === focusables[0] : document.activeElement === focusables[focusables.length - 1]) || !anchor.current?.isConnected) return;
7505
+ event.preventDefault();
7506
+ const target = event.shiftKey ? anchor.current : nextFocusable(anchor.current, contentRef.current) ?? anchor.current;
7507
+ comment.close();
7508
+ if (target === anchor.current) comment.returnFocus(target);
7509
+ else target.focus();
7510
+ },
7511
+ onPointerEnter: () => comment.enter("pointer"),
7512
+ onPointerLeave: () => comment.leave("pointer"),
7513
+ className: "cpk:z-[1300]",
7514
+ children: /* @__PURE__ */ jsx(Fragment, { children: renderSlot(feedbackComment, CopilotChatAssistantMessage.FeedbackComment, {
7515
+ value: comment.draft,
7516
+ savedComment,
7517
+ inputRef: comment.inputRef,
7518
+ onChange: comment.setDraft,
7519
+ onSubmit: () => {
7520
+ comment.close();
7521
+ comment.returnFocus(anchor.current);
7522
+ },
7523
+ onEditingChange: comment.setEditing,
7524
+ onLeave: (next) => {
7525
+ if (next && (contentRef.current?.contains(next) || isOwnThumb(next))) return;
7526
+ comment.leave();
7527
+ }
7528
+ }) }, rating)
7529
+ })]
7530
+ });
7531
+ }
7532
+ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, feedbackComment: feedbackCommentSlot, toolCallsView, children, className, ...props }) {
7533
+ useKatexStyles();
7534
+ const { isInspectorEnabled, openInspector } = useCopilotKitInspector();
7535
+ const shortcutsHidden = useInspectorShortcutsHidden();
7536
+ const showInspectorShortcut = isInspectorEnabled && !shortcutsHidden;
7537
+ const feedback = useMessageFeedback(message);
7538
+ const comment = useFeedbackComment(feedback);
7539
+ const rating = feedback?.feedback?.rating;
7540
+ const root = useRef(null);
7541
+ const commentAnchor = useRef(null);
7542
+ const commentRating = useRef(rating);
7543
+ useLayoutEffect(() => {
7544
+ if (comment.open && !commentAnchor.current?.isConnected && rating) commentAnchor.current = root.current?.querySelector(`[data-copilotkit-rating="${rating}"]`) ?? root.current?.querySelector("button[aria-pressed=\"true\"]") ?? null;
7545
+ });
7546
+ /**
7547
+ * Thumbs are toggles: a new rating is saved and opens its comment, and
7548
+ * clicking the chosen thumb again removes the rating. A keyboard click
7549
+ * (`detail` 0) moves focus into the comment, as there is no pointer.
7550
+ * Custom thumb slots may call `onClick` without an event.
7551
+ *
7552
+ * @returns Whether the app's handler should run: true for a new rating and
7553
+ * whenever this chat does not record ratings, false when one is removed.
7554
+ */
7555
+ const rate = (value, event) => {
7556
+ if (!feedback) return true;
7557
+ if (rating === value) {
7558
+ comment.close({ save: false });
7559
+ feedback.clear();
7560
+ return false;
7561
+ }
7562
+ feedback.rate(value);
7563
+ commentRating.current = value;
7564
+ commentAnchor.current = event?.currentTarget ?? null;
7565
+ comment.show({
7566
+ comment: "",
7567
+ focus: event?.detail === 0
7568
+ });
7569
+ return true;
7570
+ };
7571
+ /** Props that let the chosen thumb open and point at its comment. */
7572
+ const thumbFeedbackProps = (value) => {
7573
+ if (!feedback) return {};
7574
+ const thumb = { "data-copilotkit-rating": value };
7575
+ if (rating !== value) return {
7576
+ ...thumb,
7577
+ "aria-pressed": false
7578
+ };
7579
+ const enter = (target, via) => {
7580
+ if (!comment.open) commentRating.current = value;
7581
+ commentAnchor.current = target;
7582
+ comment.enter(via);
7583
+ };
7584
+ return {
7585
+ ...thumb,
7586
+ "aria-pressed": true,
7587
+ "aria-haspopup": "dialog",
7588
+ "aria-expanded": comment.open,
7589
+ onPointerEnter: (event) => {
7590
+ if (event.pointerType !== "touch") enter(event.currentTarget, "pointer");
7591
+ },
7592
+ onPointerLeave: (event) => {
7593
+ if (event.pointerType !== "touch") comment.leave("pointer");
7594
+ },
7595
+ onFocus: (event) => {
7596
+ if (isFocusVisible(event.currentTarget)) enter(event.currentTarget, "focus");
7597
+ },
7598
+ onBlur: () => comment.leave(),
7599
+ onKeyDown: (event) => {
7600
+ const input = comment.inputRef.current;
7601
+ if (event.key === "Tab" && !event.shiftKey && comment.open && input && input.checkVisibility?.(HIDDEN_TOO) !== false) {
7602
+ event.preventDefault();
7603
+ input.focus();
7604
+ }
7605
+ }
7606
+ };
7607
+ };
7608
+ const boundMarkdownRenderer = renderSlot(markdownRenderer, CopilotChatAssistantMessage.MarkdownRenderer, { content: message.content || "" });
7609
+ const boundCopyButton = renderSlot(copyButton, CopilotChatAssistantMessage.CopyButton, { onClick: async () => {
7610
+ if (message.content) return await copyToClipboard(message.content);
7611
+ return false;
7612
+ } });
7613
+ const boundThumbsUpButton = renderSlot(thumbsUpButton, CopilotChatAssistantMessage.ThumbsUpButton, {
7614
+ onClick: onThumbsUp || feedback ? (event) => {
7615
+ if (rate("up", event)) onThumbsUp?.(message);
7616
+ } : void 0,
7617
+ ...thumbFeedbackProps("up")
7618
+ });
7619
+ const boundInspectorButton = showInspectorShortcut ? /* @__PURE__ */ jsx(BoundInspectorButton, {
6414
7620
  inspectorButton,
6415
7621
  messageId: message.id,
6416
7622
  openInspector
6417
7623
  }) : /* @__PURE__ */ jsx(Fragment$1, {});
6418
- const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, { onClick: onThumbsDown ? () => onThumbsDown(message) : void 0 });
7624
+ const boundThumbsDownButton = renderSlot(thumbsDownButton, CopilotChatAssistantMessage.ThumbsDownButton, {
7625
+ onClick: onThumbsDown || feedback ? (event) => {
7626
+ if (rate("down", event)) onThumbsDown?.(message);
7627
+ } : void 0,
7628
+ ...thumbFeedbackProps("down")
7629
+ });
6419
7630
  const boundReadAloudButton = renderSlot(readAloudButton, CopilotChatAssistantMessage.ReadAloudButton, { onClick: onReadAloud ? () => onReadAloud(message) : void 0 });
6420
7631
  const boundRegenerateButton = renderSlot(regenerateButton, CopilotChatAssistantMessage.RegenerateButton, { onClick: onRegenerate ? () => onRegenerate(message) : void 0 });
6421
7632
  const boundToolbar = renderSlot(toolbar, CopilotChatAssistantMessage.Toolbar, { children: /* @__PURE__ */ jsxs("div", {
6422
7633
  className: "cpk:flex cpk:w-full cpk:items-center cpk:gap-1",
6423
7634
  children: [
6424
7635
  boundCopyButton,
6425
- (onThumbsUp || thumbsUpButton) && boundThumbsUpButton,
6426
- (onThumbsDown || thumbsDownButton) && boundThumbsDownButton,
7636
+ (onThumbsUp || thumbsUpButton || feedback) && boundThumbsUpButton,
7637
+ (onThumbsDown || thumbsDownButton || feedback) && boundThumbsDownButton,
6427
7638
  (onReadAloud || readAloudButton) && boundReadAloudButton,
6428
7639
  (onRegenerate || regenerateButton) && boundRegenerateButton,
6429
7640
  additionalToolbarItems,
@@ -6437,11 +7648,31 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
6437
7648
  const hasContent = !!(message.content && message.content.trim().length > 0);
6438
7649
  const isLatestAssistantMessage = message.role === "assistant" && (isLatest ?? messages?.[messages.length - 1]?.id === message.id);
6439
7650
  const shouldShowToolbar = toolbarVisible && (hasContent || showInspectorShortcut) && !(isRunning && isLatestAssistantMessage);
6440
- if (children) return /* @__PURE__ */ jsx("div", {
7651
+ const { open: commentOpen, close: closeComment } = comment;
7652
+ useEffect(() => {
7653
+ if (!commentOpen) return;
7654
+ if (rating !== commentRating.current) closeComment({ save: false });
7655
+ else if (!shouldShowToolbar) closeComment();
7656
+ }, [
7657
+ commentOpen,
7658
+ shouldShowToolbar,
7659
+ rating,
7660
+ closeComment
7661
+ ]);
7662
+ const feedbackComment = feedback?.feedback && shouldShowToolbar && /* @__PURE__ */ jsx(FeedbackCommentPopover, {
7663
+ feedbackComment: feedbackCommentSlot,
7664
+ comment,
7665
+ savedComment: feedback.feedback.comment ?? "",
7666
+ rating: feedback.feedback.rating,
7667
+ anchor: commentAnchor,
7668
+ messageId: message.id
7669
+ });
7670
+ if (children) return /* @__PURE__ */ jsxs("div", {
7671
+ ref: root,
6441
7672
  "data-copilotkit": true,
6442
7673
  "data-message-id": message.id,
6443
7674
  style: { display: "contents" },
6444
- children: children({
7675
+ children: [children({
6445
7676
  markdownRenderer: boundMarkdownRenderer,
6446
7677
  toolbar: boundToolbar,
6447
7678
  toolCallsView: boundToolCallsView,
@@ -6460,21 +7691,26 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
6460
7691
  onRegenerate,
6461
7692
  additionalToolbarItems,
6462
7693
  toolbarVisible: shouldShowToolbar
6463
- })
7694
+ }), feedbackComment]
6464
7695
  });
6465
7696
  return /* @__PURE__ */ jsxs("div", {
7697
+ ref: root,
6466
7698
  "data-copilotkit": true,
6467
7699
  "data-testid": "copilot-assistant-message",
6468
7700
  className: twMerge("copilotKitMessage copilotKitAssistantMessage", className),
6469
7701
  ...props,
6470
7702
  "data-message-id": message.id,
6471
7703
  children: [
6472
- /* @__PURE__ */ jsx("div", {
6473
- className: "cpk:prose cpk:max-w-full cpk:break-words cpk:dark:prose-invert",
6474
- children: boundMarkdownRenderer
7704
+ /* @__PURE__ */ jsx(WorkText, {
7705
+ messageId: message.id,
7706
+ children: /* @__PURE__ */ jsx("div", {
7707
+ className: "cpk:prose cpk:max-w-full cpk:break-words cpk:dark:prose-invert",
7708
+ children: boundMarkdownRenderer
7709
+ })
6475
7710
  }),
6476
7711
  boundToolCallsView,
6477
- shouldShowToolbar && boundToolbar
7712
+ shouldShowToolbar && boundToolbar,
7713
+ feedbackComment
6478
7714
  ]
6479
7715
  });
6480
7716
  }
@@ -6490,20 +7726,27 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
6490
7726
  ...props
6491
7727
  });
6492
7728
  const ToolbarButton = _CopilotChatAssistantMessage.ToolbarButton = ({ title, tooltip, tooltipClassName, children, ...props }) => {
6493
- return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
6494
- asChild: true,
6495
- children: /* @__PURE__ */ jsx(Button, {
6496
- type: "button",
6497
- variant: "assistantMessageToolbarButton",
6498
- "aria-label": title,
6499
- ...props,
6500
- children
6501
- })
6502
- }), /* @__PURE__ */ jsx(TooltipContent, {
6503
- side: "bottom",
6504
- className: tooltipClassName,
6505
- children: tooltip ?? /* @__PURE__ */ jsx("p", { children: title })
6506
- })] });
7729
+ const [tooltipOpen, setTooltipOpen] = useState(false);
7730
+ const popupOpen = props["aria-expanded"] === true;
7731
+ if (popupOpen && tooltipOpen) setTooltipOpen(false);
7732
+ return /* @__PURE__ */ jsxs(Tooltip, {
7733
+ open: tooltipOpen && !popupOpen,
7734
+ onOpenChange: (open) => setTooltipOpen(open && !popupOpen),
7735
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, {
7736
+ asChild: true,
7737
+ children: /* @__PURE__ */ jsx(Button, {
7738
+ type: "button",
7739
+ variant: "assistantMessageToolbarButton",
7740
+ "aria-label": title,
7741
+ ...props,
7742
+ children
7743
+ })
7744
+ }), /* @__PURE__ */ jsx(TooltipContent, {
7745
+ side: "bottom",
7746
+ className: tooltipClassName,
7747
+ children: tooltip ?? /* @__PURE__ */ jsx("p", { children: title })
7748
+ })]
7749
+ });
6507
7750
  };
6508
7751
  _CopilotChatAssistantMessage.CopyButton = ({ className, title, onClick, ...props }) => {
6509
7752
  const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
@@ -6542,7 +7785,10 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
6542
7785
  "data-testid": "copilot-thumbs-up-button",
6543
7786
  title: title || labels.assistantMessageToolbarThumbsUpLabel,
6544
7787
  ...props,
6545
- children: /* @__PURE__ */ jsx(ThumbsUp, { className: "cpk:size-[18px]" })
7788
+ children: /* @__PURE__ */ jsx(ThumbsUp, {
7789
+ className: "cpk:size-[18px]",
7790
+ fill: props["aria-pressed"] === true ? "currentColor" : "none"
7791
+ })
6546
7792
  });
6547
7793
  };
6548
7794
  _CopilotChatAssistantMessage.ThumbsDownButton = ({ title, ...props }) => {
@@ -6551,7 +7797,45 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, o
6551
7797
  "data-testid": "copilot-thumbs-down-button",
6552
7798
  title: title || labels.assistantMessageToolbarThumbsDownLabel,
6553
7799
  ...props,
6554
- children: /* @__PURE__ */ jsx(ThumbsDown, { className: "cpk:size-[18px]" })
7800
+ children: /* @__PURE__ */ jsx(ThumbsDown, {
7801
+ className: "cpk:size-[18px]",
7802
+ fill: props["aria-pressed"] === true ? "currentColor" : "none"
7803
+ })
7804
+ });
7805
+ };
7806
+ _CopilotChatAssistantMessage.FeedbackComment = ({ value, savedComment, inputRef, onChange, onSubmit, onEditingChange, onLeave, className }) => {
7807
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
7808
+ const changed = value.trim() !== savedComment;
7809
+ return /* @__PURE__ */ jsxs("form", {
7810
+ "data-testid": "copilot-feedback-comment",
7811
+ className: twMerge("cpk:flex cpk:w-80 cpk:max-w-[calc(100vw-1rem)] cpk:items-center cpk:gap-1 cpk:rounded-full cpk:border cpk:border-border cpk:bg-background cpk:py-0.5 cpk:pl-3 cpk:pr-0.5 cpk:text-sm cpk:shadow-md", className),
7812
+ onSubmit: (event) => {
7813
+ event.preventDefault();
7814
+ onSubmit();
7815
+ },
7816
+ onFocus: () => onEditingChange?.(true),
7817
+ onBlur: (event) => {
7818
+ const next = event.relatedTarget instanceof Element ? event.relatedTarget : null;
7819
+ if (event.currentTarget.contains(next)) return;
7820
+ if (typeof document !== "undefined" && !document.hasFocus()) return;
7821
+ onEditingChange?.(false);
7822
+ onLeave?.(next);
7823
+ },
7824
+ children: [/* @__PURE__ */ jsx("input", {
7825
+ ref: inputRef,
7826
+ value,
7827
+ maxLength: MESSAGE_FEEDBACK_MAX_COMMENT_LENGTH,
7828
+ onChange: (event) => onChange(event.target.value),
7829
+ placeholder: labels.assistantMessageFeedbackCommentPlaceholder,
7830
+ "aria-label": labels.assistantMessageFeedbackCommentPlaceholder,
7831
+ className: "cpk:min-w-0 cpk:flex-1 cpk:bg-transparent cpk:py-1 cpk:text-foreground cpk:outline-none cpk:placeholder:text-muted-foreground"
7832
+ }), changed && /* @__PURE__ */ jsx(Button, {
7833
+ type: "submit",
7834
+ variant: "assistantMessageToolbarButton",
7835
+ "aria-label": labels.assistantMessageFeedbackCommentSubmitLabel,
7836
+ title: labels.assistantMessageFeedbackCommentSubmitLabel,
7837
+ children: /* @__PURE__ */ jsx(ArrowUp, { className: "cpk:size-[16px]" })
7838
+ })]
6555
7839
  });
6556
7840
  };
6557
7841
  _CopilotChatAssistantMessage.ReadAloudButton = ({ title, ...props }) => {
@@ -6579,6 +7863,7 @@ CopilotChatAssistantMessage.CopyButton.displayName = "CopilotChatAssistantMessag
6579
7863
  CopilotChatAssistantMessage.InspectorButton.displayName = "CopilotChatAssistantMessage.InspectorButton";
6580
7864
  CopilotChatAssistantMessage.ThumbsUpButton.displayName = "CopilotChatAssistantMessage.ThumbsUpButton";
6581
7865
  CopilotChatAssistantMessage.ThumbsDownButton.displayName = "CopilotChatAssistantMessage.ThumbsDownButton";
7866
+ CopilotChatAssistantMessage.FeedbackComment.displayName = "CopilotChatAssistantMessage.FeedbackComment";
6582
7867
  CopilotChatAssistantMessage.ReadAloudButton.displayName = "CopilotChatAssistantMessage.ReadAloudButton";
6583
7868
  CopilotChatAssistantMessage.RegenerateButton.displayName = "CopilotChatAssistantMessage.RegenerateButton";
6584
7869
  var CopilotChatAssistantMessage_default = CopilotChatAssistantMessage;
@@ -6884,6 +8169,7 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
6884
8169
  });
6885
8170
  };
6886
8171
  _CopilotChatUserMessage.BranchNavigation = ({ className, currentBranch = 0, numberOfBranches = 1, onSwitchToBranch, message, ...props }) => {
8172
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
6887
8173
  if (!numberOfBranches || numberOfBranches <= 1 || !onSwitchToBranch) return null;
6888
8174
  const canGoPrev = currentBranch > 0;
6889
8175
  const canGoNext = currentBranch < numberOfBranches - 1;
@@ -6902,6 +8188,7 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
6902
8188
  }),
6903
8189
  disabled: !canGoPrev,
6904
8190
  className: "cpk:h-6 cpk:w-6 cpk:p-0",
8191
+ "aria-label": labels.userMessageToolbarPreviousBranchLabel,
6905
8192
  children: /* @__PURE__ */ jsx(ChevronLeft, { className: "cpk:size-[20px]" })
6906
8193
  }),
6907
8194
  /* @__PURE__ */ jsxs("span", {
@@ -6922,6 +8209,7 @@ function CopilotChatUserMessage({ message, onEditMessage, branchIndex, numberOfB
6922
8209
  }),
6923
8210
  disabled: !canGoNext,
6924
8211
  className: "cpk:h-6 cpk:w-6 cpk:p-0",
8212
+ "aria-label": labels.userMessageToolbarNextBranchLabel,
6925
8213
  children: /* @__PURE__ */ jsx(ChevronRight, { className: "cpk:size-[20px]" })
6926
8214
  })
6927
8215
  ]
@@ -6942,7 +8230,7 @@ var CopilotChatUserMessage_default = CopilotChatUserMessage;
6942
8230
  /**
6943
8231
  * Formats an elapsed duration (in seconds) to a human-readable string.
6944
8232
  */
6945
- function formatDuration(seconds) {
8233
+ function formatDuration$1(seconds) {
6946
8234
  if (seconds < 1) return "a few seconds";
6947
8235
  if (seconds < 60) return `${Math.round(seconds)} seconds`;
6948
8236
  const mins = Math.floor(seconds / 60);
@@ -6980,7 +8268,7 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, isLatest: i
6980
8268
  userToggledRef.current = true;
6981
8269
  setIsOpen((prev) => !prev);
6982
8270
  } : void 0;
6983
- const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`;
8271
+ const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration$1(elapsed)}`;
6984
8272
  const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {
6985
8273
  isOpen,
6986
8274
  label,
@@ -7202,6 +8490,71 @@ const ScrollElementContext = React.createContext(null);
7202
8490
  */
7203
8491
  const ScrollPinnedContext = React.createContext(false);
7204
8492
 
8493
+ //#endregion
8494
+ //#region src/v2/components/chat/CopilotChatWorkSummary.tsx
8495
+ /** "45s", "1m 5s", "2m". Used for the live timer and the final duration. */
8496
+ function formatDuration(ms) {
8497
+ const seconds = Math.max(0, Math.floor(ms / 1e3));
8498
+ if (seconds < 60) return `${seconds}s`;
8499
+ const minutes = Math.floor(seconds / 60);
8500
+ const rest = seconds % 60;
8501
+ return rest ? `${minutes}m ${rest}s` : `${minutes}m`;
8502
+ }
8503
+ /** Re-renders every second while `active`, returning the current time. */
8504
+ function useNow(active) {
8505
+ const [now, setNow] = useState(() => Date.now());
8506
+ useEffect(() => {
8507
+ if (!active) return;
8508
+ setNow(Date.now());
8509
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
8510
+ return () => clearInterval(timer);
8511
+ }, [active]);
8512
+ return now;
8513
+ }
8514
+ /**
8515
+ * The line at the top of a reply in the `"turn"` tool-call display. While the
8516
+ * agent works it reads "Working 4s"; once the reply is done it reads "Worked
8517
+ * for 9s · 7 tools" and toggles the work it folded away.
8518
+ */
8519
+ function CopilotChatWorkSummary({ isWorking, isOpen, calls, elapsedMs, workingSince, className, ...props }) {
8520
+ const now = useNow(isWorking);
8521
+ const failed = calls.filter((call) => call.toolMessage?.error).length;
8522
+ const liveMs = elapsedMs !== void 0 && workingSince !== void 0 ? elapsedMs + Math.max(0, now - workingSince) : void 0;
8523
+ const duration = elapsedMs !== void 0 ? formatDuration(Math.max(1e3, elapsedMs)) : void 0;
8524
+ return /* @__PURE__ */ jsxs("button", {
8525
+ type: "button",
8526
+ "data-copilotkit": true,
8527
+ "data-testid": "copilot-work-summary",
8528
+ "data-state": isWorking ? "working" : "done",
8529
+ "aria-expanded": isOpen,
8530
+ className: twMerge("cpk:m-0 cpk:inline-flex cpk:max-w-full cpk:cursor-pointer cpk:items-center cpk:gap-1.5 cpk:rounded-md cpk:border-none cpk:bg-transparent cpk:p-0 cpk:py-0.5 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:hover:text-foreground cpk:focus-visible:outline-2 cpk:focus-visible:outline-ring", className),
8531
+ ...props,
8532
+ children: [isWorking ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
8533
+ className: "cpk:font-medium cpk-shimmer",
8534
+ children: "Working"
8535
+ }), liveMs !== void 0 && /* @__PURE__ */ jsx("span", {
8536
+ className: "cpk:text-xs cpk:tabular-nums cpk:opacity-70",
8537
+ children: formatDuration(liveMs)
8538
+ })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
8539
+ /* @__PURE__ */ jsx("span", {
8540
+ className: "cpk:font-medium",
8541
+ children: duration ? `Worked for ${duration}` : `Used ${pluralize(calls.length, "tool")}`
8542
+ }),
8543
+ duration && /* @__PURE__ */ jsx("span", {
8544
+ className: "cpk:text-xs cpk:opacity-70",
8545
+ children: pluralize(calls.length, "tool")
8546
+ }),
8547
+ failed > 0 && /* @__PURE__ */ jsxs("span", {
8548
+ className: "cpk:text-destructive",
8549
+ children: [failed, " failed"]
8550
+ })
8551
+ ] }), /* @__PURE__ */ jsx(ChevronRight, {
8552
+ "aria-hidden": "true",
8553
+ className: twMerge("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", isOpen && "cpk:rotate-90")
8554
+ })]
8555
+ });
8556
+ }
8557
+
7205
8558
  //#endregion
7206
8559
  //#region src/v2/components/intelligence-indicator/IntelligenceIndicatorView.tsx
7207
8560
  /**
@@ -7688,7 +9041,7 @@ function messageGroup(group) {
7688
9041
  }
7689
9042
  const GROUP_ROW_KEY_PREFIX = "copilotkit-group:";
7690
9043
  const VIRTUALIZE_THRESHOLD = 50;
7691
- function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, transformMessages, groupMessages, children, className, ...props }) {
9044
+ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, workSummary, isRunning = false, transformMessages, groupMessages, messageFeedback = true, toolCallDisplay = "grouped", children, className, ...props }) {
7692
9045
  const isPinnedToBottom = useContext(ScrollPinnedContext);
7693
9046
  const renderCustomMessage = useRenderCustomMessages();
7694
9047
  const { renderActivityMessage } = useRenderActivityMessage();
@@ -7729,6 +9082,100 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7729
9082
  transformedMessages
7730
9083
  ]);
7731
9084
  const latestRenderedId = renderedMessages[renderedMessages.length - 1]?.id;
9085
+ const rendererNames = useToolCallRendererNames();
9086
+ const toolCallLayouts = useMemo(() => toolCallDisplay === "separate" ? /* @__PURE__ */ new Map() : getToolCallLayouts(renderedMessages, messages, rendererNames), [
9087
+ toolCallDisplay,
9088
+ renderedMessages,
9089
+ messages,
9090
+ rendererNames
9091
+ ]);
9092
+ const turnWork = useMemo(() => toolCallDisplay === "turn" ? getTurnWork(renderedMessages, toolCallLayouts) : void 0, [
9093
+ toolCallDisplay,
9094
+ renderedMessages,
9095
+ toolCallLayouts
9096
+ ]);
9097
+ const turnByFirstMessage = useMemo(() => {
9098
+ const byFirst = /* @__PURE__ */ new Map();
9099
+ for (const turn of turnWork?.values() ?? []) byFirst.set(turn.firstMessageId, turn);
9100
+ return byFirst;
9101
+ }, [turnWork]);
9102
+ let latestTurnKey = "start";
9103
+ for (let i = renderedMessages.length - 1; i >= 0; i--) if (renderedMessages[i].role === "user") {
9104
+ latestTurnKey = renderedMessages[i].id;
9105
+ break;
9106
+ }
9107
+ const isWorkingOn = (turnKey) => isRunning && turnKey === latestTurnKey;
9108
+ const [toggledTurns, setToggledTurns] = useState(() => /* @__PURE__ */ new Map());
9109
+ const isWorkOpen = (turnKey) => toggledTurns.get(turnKey) ?? isWorkingOn(turnKey);
9110
+ const workTimesRef = useRef(/* @__PURE__ */ new Map());
9111
+ if (toolCallDisplay === "turn") {
9112
+ const now = Date.now();
9113
+ const workTimes = workTimesRef.current;
9114
+ if (isRunning) {
9115
+ const times = workTimes.get(latestTurnKey);
9116
+ if (times) times.workingSince ??= now;
9117
+ else if (renderedMessages[renderedMessages.length - 1]?.role === "user") workTimes.set(latestTurnKey, {
9118
+ elapsedMs: 0,
9119
+ workingSince: now
9120
+ });
9121
+ } else for (const times of workTimes.values()) {
9122
+ if (times.workingSince === void 0) continue;
9123
+ times.elapsedMs += now - times.workingSince;
9124
+ times.workingSince = void 0;
9125
+ }
9126
+ }
9127
+ const { workOpen, workTextOpen } = useMemo(() => {
9128
+ const open = /* @__PURE__ */ new Map();
9129
+ const textOpen = /* @__PURE__ */ new Map();
9130
+ for (const turn of turnWork?.values() ?? []) {
9131
+ const turnOpen = toggledTurns.get(turn.key) ?? (isRunning && turn.key === latestTurnKey);
9132
+ for (const id of turn.workMessageIds) {
9133
+ open.set(id, turnOpen);
9134
+ if (id !== turn.replyTextMessageId) textOpen.set(id, turnOpen);
9135
+ }
9136
+ }
9137
+ return {
9138
+ workOpen: open,
9139
+ workTextOpen: textOpen
9140
+ };
9141
+ }, [
9142
+ turnWork,
9143
+ toggledTurns,
9144
+ isRunning,
9145
+ latestTurnKey
9146
+ ]);
9147
+ const toolCallDisplayContext = useMemo(() => ({
9148
+ display: toolCallDisplay,
9149
+ isRunning,
9150
+ layouts: toolCallLayouts,
9151
+ workOpen,
9152
+ workTextOpen
9153
+ }), [
9154
+ toolCallDisplay,
9155
+ isRunning,
9156
+ toolCallLayouts,
9157
+ workOpen,
9158
+ workTextOpen
9159
+ ]);
9160
+ const renderWorkSummary = (turnKey, calls) => {
9161
+ const working = isWorkingOn(turnKey);
9162
+ const open = isWorkOpen(turnKey);
9163
+ const times = workTimesRef.current.get(turnKey);
9164
+ return /* @__PURE__ */ jsx(Collapsible, {
9165
+ open: working || calls.length > 0,
9166
+ children: /* @__PURE__ */ jsx("div", {
9167
+ className: "cpk:pt-1 cpk:pb-2",
9168
+ children: renderSlot(workSummary, CopilotChatWorkSummary, {
9169
+ isWorking: working,
9170
+ isOpen: open,
9171
+ calls,
9172
+ elapsedMs: times?.elapsedMs,
9173
+ workingSince: times?.workingSince,
9174
+ onClick: () => setToggledTurns((toggled) => new Map(toggled).set(turnKey, !open))
9175
+ })
9176
+ })
9177
+ }, `work-summary-${turnKey}`);
9178
+ };
7732
9179
  const transformDuplicateId = useMemo(() => {
7733
9180
  if (process.env.NODE_ENV === "production" || !transformMessages && !groupMessages) return;
7734
9181
  const seen = /* @__PURE__ */ new Set();
@@ -7794,6 +9241,18 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7794
9241
  const { Component: AssistantComponent, slotProps: assistantSlotProps } = useMemo(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
7795
9242
  const agentId = config?.agentId;
7796
9243
  const threadId = config?.threadId;
9244
+ const learningFeedbackEnabled = useLearningFeedbackEnabled(copilotkit);
9245
+ const feedbackScope = useMemo(() => messageFeedback && learningFeedbackEnabled && agentId !== void 0 && threadId !== void 0 ? {
9246
+ copilotkit,
9247
+ agentId,
9248
+ threadId
9249
+ } : null, [
9250
+ messageFeedback,
9251
+ learningFeedbackEnabled,
9252
+ copilotkit,
9253
+ agentId,
9254
+ threadId
9255
+ ]);
7797
9256
  const assistantSlotPropsWithFeedback = useMemo(() => {
7798
9257
  const onThumbsUp = assistantSlotProps?.onThumbsUp;
7799
9258
  const onThumbsDown = assistantSlotProps?.onThumbsDown;
@@ -7860,7 +9319,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7860
9319
  measuredRef.current.total = total - (sizes.get(index) ?? 0) + height;
7861
9320
  sizes.set(index, height);
7862
9321
  }
7863
- return height;
9322
+ return height > 0 ? height : measuredRef.current.sizes.get(index) ?? 100;
7864
9323
  }, []);
7865
9324
  const isPinnedToBottomRef = React.useRef(isPinnedToBottom);
7866
9325
  const shouldAdjustScrollOnResize = React.useCallback((item, _delta, instance) => {
@@ -7870,7 +9329,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7870
9329
  }, []);
7871
9330
  const virtualizer = useVirtualizer({
7872
9331
  count: shouldVirtualize ? rows.length : 0,
7873
- getScrollElement: () => scrollElement,
9332
+ getScrollElement: () => shouldVirtualize ? scrollElement : null,
7874
9333
  estimateSize: estimateRowSize,
7875
9334
  overscan: 5,
7876
9335
  measureElement: measureRowElement,
@@ -7881,8 +9340,11 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7881
9340
  });
7882
9341
  isPinnedToBottomRef.current = isPinnedToBottom;
7883
9342
  virtualizer.shouldAdjustScrollPositionOnItemSizeChange = shouldAdjustScrollOnResize;
9343
+ const lastEndAlignedFirstMessageRef = React.useRef(void 0);
7884
9344
  useLayoutEffect(() => {
7885
9345
  if (!shouldVirtualize || !rows.length) return;
9346
+ if (lastEndAlignedFirstMessageRef.current === firstMessageId) return;
9347
+ lastEndAlignedFirstMessageRef.current = firstMessageId;
7886
9348
  virtualizer.scrollToIndex(rows.length - 1, { align: "end" });
7887
9349
  }, [shouldVirtualize, firstMessageId]);
7888
9350
  const intelligenceTurnAnchors = useMemo(() => getIntelligenceTurnAnchors(deduplicatedMessages, renderedMessages), [deduplicatedMessages, renderedMessages]);
@@ -7890,6 +9352,8 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7890
9352
  const elements = [];
7891
9353
  const stateSnapshot = renderCustomMessage ? getStateSnapshotForMessage(message.id) : void 0;
7892
9354
  const rowKey = rowRenderKeys.get(message.id) ?? message.id;
9355
+ const turn = turnByFirstMessage.get(message.id);
9356
+ if (turn) elements.push(renderWorkSummary(turn.key, turn.calls));
7893
9357
  if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
7894
9358
  message,
7895
9359
  position: "before",
@@ -7913,14 +9377,20 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7913
9377
  message,
7914
9378
  renderActivityMessage
7915
9379
  }, rowKey));
7916
- else if (message.role === "reasoning") elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
7917
- message,
7918
- messages,
7919
- isRunning,
7920
- isLatest: message.id === latestRenderedId,
7921
- ReasoningMessageComponent: ReasoningComponent,
7922
- slotProps: reasoningSlotProps
7923
- }, rowKey));
9380
+ else if (message.role === "reasoning") {
9381
+ const reasoning = /* @__PURE__ */ jsx(MemoizedReasoningMessage, {
9382
+ message,
9383
+ messages,
9384
+ isRunning,
9385
+ isLatest: message.id === latestRenderedId,
9386
+ ReasoningMessageComponent: ReasoningComponent,
9387
+ slotProps: reasoningSlotProps
9388
+ }, rowKey);
9389
+ elements.push(toolCallDisplay === "turn" ? /* @__PURE__ */ jsx(Collapsible, {
9390
+ open: workOpen.get(message.id) ?? true,
9391
+ children: reasoning
9392
+ }, rowKey) : reasoning);
9393
+ }
7924
9394
  if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
7925
9395
  message,
7926
9396
  position: "after",
@@ -7948,52 +9418,157 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7948
9418
  children: row.messages.flatMap(renderMessageBlock)
7949
9419
  }, rowKey(row))];
7950
9420
  };
9421
+ const rowKeysRef = useRef([]);
9422
+ rowKeysRef.current = rows.map(rowKey);
9423
+ const virtualModeRef = useRef(shouldVirtualize);
9424
+ virtualModeRef.current = shouldVirtualize;
9425
+ useEffect(() => {
9426
+ const element = scrollElementFromCtx;
9427
+ if (!element) return;
9428
+ let anchor = null;
9429
+ let suspended = false;
9430
+ let frame = 0;
9431
+ const visibleRows = () => Array.from(element.querySelectorAll("[data-scroll-anchor]"));
9432
+ const remember = () => {
9433
+ if (suspended || element.clientHeight === 0 || !virtualModeRef.current) return;
9434
+ if (isPinnedToBottomRef.current) {
9435
+ anchor = null;
9436
+ return;
9437
+ }
9438
+ const viewport = element.getBoundingClientRect();
9439
+ const row = visibleRows().find((row) => {
9440
+ const bounds = row.getBoundingClientRect();
9441
+ return bounds.bottom > viewport.top && bounds.top < viewport.bottom;
9442
+ });
9443
+ if (row) anchor = {
9444
+ key: row.dataset.scrollAnchor,
9445
+ offset: row.getBoundingClientRect().top - viewport.top
9446
+ };
9447
+ };
9448
+ const observer = new ResizeObserver(() => {
9449
+ if (element.clientHeight === 0) {
9450
+ suspended = true;
9451
+ cancelAnimationFrame(frame);
9452
+ return;
9453
+ }
9454
+ if (!suspended || !anchor) {
9455
+ suspended = false;
9456
+ remember();
9457
+ return;
9458
+ }
9459
+ let attempts = 0;
9460
+ let stableFrames = 0;
9461
+ const restore = () => {
9462
+ if (!anchor || element.clientHeight === 0) return;
9463
+ const index = rowKeysRef.current.indexOf(anchor.key);
9464
+ if (index < 0) {
9465
+ anchor = null;
9466
+ suspended = false;
9467
+ return;
9468
+ }
9469
+ const row = visibleRows().find((row) => row.dataset.scrollAnchor === anchor.key);
9470
+ if (row) {
9471
+ const delta = row.getBoundingClientRect().top - element.getBoundingClientRect().top - anchor.offset;
9472
+ element.scrollTop += delta;
9473
+ stableFrames = Math.abs(delta) < 1 ? stableFrames + 1 : 0;
9474
+ } else {
9475
+ const offset = virtualizer.getOffsetForIndex(index, "start")?.[0];
9476
+ if (offset !== void 0) virtualizer.scrollToOffset(offset - anchor.offset);
9477
+ stableFrames = 0;
9478
+ }
9479
+ if (++attempts < 60 && stableFrames < 3) frame = requestAnimationFrame(restore);
9480
+ else {
9481
+ suspended = false;
9482
+ remember();
9483
+ }
9484
+ };
9485
+ frame = requestAnimationFrame(restore);
9486
+ });
9487
+ const interrupt = () => {
9488
+ cancelAnimationFrame(frame);
9489
+ anchor = null;
9490
+ suspended = false;
9491
+ };
9492
+ element.addEventListener("scroll", remember, { passive: true });
9493
+ element.addEventListener("wheel", interrupt, { passive: true });
9494
+ element.addEventListener("touchstart", interrupt, { passive: true });
9495
+ element.addEventListener("keydown", interrupt);
9496
+ observer.observe(element);
9497
+ remember();
9498
+ return () => {
9499
+ cancelAnimationFrame(frame);
9500
+ observer.disconnect();
9501
+ element.removeEventListener("scroll", remember);
9502
+ element.removeEventListener("wheel", interrupt);
9503
+ element.removeEventListener("touchstart", interrupt);
9504
+ element.removeEventListener("keydown", interrupt);
9505
+ };
9506
+ }, [
9507
+ scrollElementFromCtx,
9508
+ virtualizer,
9509
+ firstMessageId
9510
+ ]);
7951
9511
  const messageElements = shouldVirtualize ? [] : rows.flatMap(renderRow);
7952
- if (children) return /* @__PURE__ */ jsx("div", {
7953
- "data-copilotkit": true,
7954
- style: { display: "contents" },
7955
- children: children({
7956
- messageElements,
7957
- messages,
7958
- isRunning,
7959
- interruptElement
9512
+ if (children) return /* @__PURE__ */ jsx(MessageFeedbackScopeContext.Provider, {
9513
+ value: feedbackScope,
9514
+ children: /* @__PURE__ */ jsx(ToolCallDisplayContext.Provider, {
9515
+ value: toolCallDisplayContext,
9516
+ children: /* @__PURE__ */ jsx("div", {
9517
+ "data-copilotkit": true,
9518
+ style: { display: "contents" },
9519
+ children: children({
9520
+ messageElements,
9521
+ messages,
9522
+ isRunning,
9523
+ interruptElement
9524
+ })
9525
+ })
7960
9526
  })
7961
9527
  });
7962
9528
  const lastMessage = renderedMessages[renderedMessages.length - 1];
7963
- const showCursor = isRunning && lastMessage?.role !== "reasoning";
7964
- return /* @__PURE__ */ jsxs("div", {
7965
- "data-copilotkit": true,
7966
- "data-testid": "copilot-message-list",
7967
- className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
7968
- ...props,
7969
- children: [
7970
- shouldVirtualize ? /* @__PURE__ */ jsx("div", {
7971
- style: {
7972
- height: virtualizer.getTotalSize(),
7973
- position: "relative"
7974
- },
7975
- children: virtualizer.getVirtualItems().map((virtualItem) => {
7976
- const row = rows[virtualItem.index];
7977
- return /* @__PURE__ */ jsx("div", {
7978
- "data-index": virtualItem.index,
7979
- ref: virtualizer.measureElement,
9529
+ const showCursor = isRunning && lastMessage?.role !== "reasoning" && toolCallDisplay !== "turn";
9530
+ const pendingWorkSummary = toolCallDisplay === "turn" && isRunning && lastMessage?.role === "user";
9531
+ return /* @__PURE__ */ jsx(MessageFeedbackScopeContext.Provider, {
9532
+ value: feedbackScope,
9533
+ children: /* @__PURE__ */ jsx(ToolCallDisplayContext.Provider, {
9534
+ value: toolCallDisplayContext,
9535
+ children: /* @__PURE__ */ jsxs("div", {
9536
+ "data-copilotkit": true,
9537
+ "data-testid": "copilot-message-list",
9538
+ className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
9539
+ ...props,
9540
+ children: [
9541
+ shouldVirtualize ? /* @__PURE__ */ jsx("div", {
7980
9542
  style: {
7981
- position: "absolute",
7982
- top: 0,
7983
- left: 0,
7984
- width: "100%",
7985
- transform: `translateY(${virtualItem.start}px)`
9543
+ height: virtualizer.getTotalSize(),
9544
+ position: "relative"
7986
9545
  },
7987
- children: renderRow(row)
7988
- }, rowKey(row));
7989
- })
7990
- }) : messageElements,
7991
- interruptElement,
7992
- showCursor && /* @__PURE__ */ jsx("div", {
7993
- className: "cpk:mt-2",
7994
- children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
9546
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
9547
+ const row = rows[virtualItem.index];
9548
+ return /* @__PURE__ */ jsx("div", {
9549
+ "data-index": virtualItem.index,
9550
+ "data-scroll-anchor": rowKey(row),
9551
+ ref: virtualizer.measureElement,
9552
+ style: {
9553
+ position: "absolute",
9554
+ top: 0,
9555
+ left: 0,
9556
+ width: "100%",
9557
+ transform: `translateY(${virtualItem.start}px)`
9558
+ },
9559
+ children: renderRow(row)
9560
+ }, rowKey(row));
9561
+ })
9562
+ }) : messageElements,
9563
+ interruptElement,
9564
+ pendingWorkSummary && renderWorkSummary(latestTurnKey, []),
9565
+ showCursor && /* @__PURE__ */ jsx("div", {
9566
+ className: "cpk:mt-2",
9567
+ children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
9568
+ })
9569
+ ]
7995
9570
  })
7996
- ]
9571
+ })
7997
9572
  });
7998
9573
  }
7999
9574
  CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
@@ -8378,6 +9953,16 @@ function DropOverlay() {
8378
9953
  });
8379
9954
  }
8380
9955
  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, isConnecting = false, hasExplicitThreadId = false, disclaimer, intelligenceIndicator, children, className, ...props }) {
9956
+ const configuredThreadId = useCopilotChatConfiguration()?.threadId;
9957
+ const [fallbackThreadId, setFallbackThreadId] = useState(messages[0]?.id);
9958
+ const previousMessageIds = useRef(/* @__PURE__ */ new Set());
9959
+ useEffect(() => {
9960
+ if (messages.length === 0) return;
9961
+ const overlaps = messages.some((message) => previousMessageIds.current.has(message.id));
9962
+ previousMessageIds.current = new Set(messages.map((message) => message.id));
9963
+ if (!overlaps) setFallbackThreadId(messages[0].id);
9964
+ }, [messages]);
9965
+ const scrollThreadId = configuredThreadId ?? fallbackThreadId;
8381
9966
  const [inputContainerEl, setInputContainerEl] = useState(null);
8382
9967
  const [inputContainerHeight, setInputContainerHeight] = useState(0);
8383
9968
  const [isResizing, setIsResizing] = useState(false);
@@ -8444,6 +10029,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
8444
10029
  }) : null;
8445
10030
  const BoundScrollView = renderSlot(scrollView, CopilotChatView.ScrollView, {
8446
10031
  autoScroll,
10032
+ scrollThreadId,
8447
10033
  inputContainerHeight,
8448
10034
  isResizing,
8449
10035
  children: /* @__PURE__ */ jsx("div", {
@@ -8539,12 +10125,102 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
8539
10125
  });
8540
10126
  }
8541
10127
  (function(_CopilotChatView) {
8542
- const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => {
8543
- const { isAtBottom, scrollToBottom, scrollRef, state } = useStickToBottomContext();
10128
+ const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing, scrollThreadId }) => {
10129
+ const { isAtBottom, scrollToBottom, stopScroll, scrollRef, state } = useStickToBottomContext();
8544
10130
  const [scrollEl, setScrollEl] = useState(null);
10131
+ const readingOffsetRef = useRef(null);
10132
+ const pinIntentRef = useRef(false);
10133
+ const stateRef = useRef(state);
10134
+ stateRef.current = state;
8545
10135
  useLayoutEffect(() => {
8546
10136
  setScrollEl(scrollRef.current ?? null);
8547
10137
  }, []);
10138
+ useEffect(() => {
10139
+ if (!scrollEl) return;
10140
+ let hidden = false;
10141
+ let resumeBottom = false;
10142
+ let pendingFrame = 0;
10143
+ let resumeFrame = 0;
10144
+ let settleFrame = 0;
10145
+ readingOffsetRef.current = null;
10146
+ const rememberScroll = () => {
10147
+ if (hidden || scrollEl.clientHeight === 0) return;
10148
+ if (resumeBottom) {
10149
+ readingOffsetRef.current = null;
10150
+ return;
10151
+ }
10152
+ if (pinIntentRef.current) if (stateRef.current.escapedFromLock) pinIntentRef.current = false;
10153
+ else {
10154
+ readingOffsetRef.current = null;
10155
+ if (!stateRef.current.isNearBottom) return;
10156
+ pinIntentRef.current = false;
10157
+ }
10158
+ readingOffsetRef.current = !stateRef.current.isAtBottom && (stateRef.current.escapedFromLock || !stateRef.current.isNearBottom) ? scrollEl.scrollTop : null;
10159
+ };
10160
+ const onScroll = () => {
10161
+ rememberScroll();
10162
+ cancelAnimationFrame(pendingFrame);
10163
+ pendingFrame = requestAnimationFrame(rememberScroll);
10164
+ };
10165
+ const onWheel = (event) => {
10166
+ if (event.deltaY < 0) {
10167
+ pinIntentRef.current = false;
10168
+ resumeBottom = false;
10169
+ }
10170
+ };
10171
+ const resizeObserver = new ResizeObserver(() => {
10172
+ if (scrollEl.clientHeight === 0) {
10173
+ hidden = true;
10174
+ resumeBottom = pinIntentRef.current || readingOffsetRef.current === null;
10175
+ if (!resumeBottom) stopScroll();
10176
+ } else if (hidden) {
10177
+ hidden = false;
10178
+ if (resumeBottom) {
10179
+ stopScroll();
10180
+ resumeFrame = requestAnimationFrame(() => {
10181
+ settleFrame = requestAnimationFrame(() => {
10182
+ if (scrollEl.clientHeight > 0 && resumeBottom) {
10183
+ resumeBottom = false;
10184
+ pinIntentRef.current = true;
10185
+ scrollToBottom({ animation: "instant" });
10186
+ }
10187
+ });
10188
+ });
10189
+ } else if (readingOffsetRef.current !== null) {
10190
+ stopScroll();
10191
+ scrollEl.scrollTop = readingOffsetRef.current;
10192
+ }
10193
+ }
10194
+ });
10195
+ rememberScroll();
10196
+ scrollEl.addEventListener("scroll", onScroll);
10197
+ scrollEl.addEventListener("wheel", onWheel, { passive: true });
10198
+ resizeObserver.observe(scrollEl);
10199
+ return () => {
10200
+ scrollEl.removeEventListener("scroll", onScroll);
10201
+ scrollEl.removeEventListener("wheel", onWheel);
10202
+ cancelAnimationFrame(pendingFrame);
10203
+ cancelAnimationFrame(resumeFrame);
10204
+ cancelAnimationFrame(settleFrame);
10205
+ resizeObserver.disconnect();
10206
+ };
10207
+ }, [
10208
+ scrollEl,
10209
+ scrollToBottom,
10210
+ stopScroll,
10211
+ scrollThreadId
10212
+ ]);
10213
+ const startBottomPin = useCallback((instant = false) => {
10214
+ readingOffsetRef.current = null;
10215
+ pinIntentRef.current = true;
10216
+ scrollToBottom(instant ? { animation: "instant" } : void 0);
10217
+ }, [scrollToBottom]);
10218
+ const previousScrollThreadId = useRef(scrollThreadId);
10219
+ useEffect(() => {
10220
+ if (previousScrollThreadId.current === scrollThreadId) return;
10221
+ previousScrollThreadId.current = scrollThreadId;
10222
+ startBottomPin(true);
10223
+ }, [scrollThreadId, startBottomPin]);
8548
10224
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
8549
10225
  return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
8550
10226
  value: scrollEl,
@@ -8566,7 +10242,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
8566
10242
  !isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", {
8567
10243
  className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
8568
10244
  style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
8569
- children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
10245
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => startBottomPin() })
8570
10246
  })
8571
10247
  ] })
8572
10248
  })
@@ -8614,7 +10290,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
8614
10290
  })
8615
10291
  });
8616
10292
  };
8617
- _CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
10293
+ _CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollThreadId, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
8618
10294
  const mode = normalizeAutoScroll(autoScroll);
8619
10295
  const [hasMounted, setHasMounted] = useState(false);
8620
10296
  const scrollRef = useRef(null);
@@ -8703,6 +10379,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
8703
10379
  initial: "smooth",
8704
10380
  ...props,
8705
10381
  children: /* @__PURE__ */ jsx(ScrollContent, {
10382
+ scrollThreadId,
8706
10383
  scrollToBottomButton,
8707
10384
  feather,
8708
10385
  inputContainerHeight,
@@ -8711,14 +10388,18 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
8711
10388
  })
8712
10389
  });
8713
10390
  };
8714
- _CopilotChatView.ScrollToBottomButton = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, {
8715
- "data-testid": "copilot-scroll-to-bottom",
8716
- variant: "outline",
8717
- size: "sm",
8718
- className: twMerge("cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto", "cpk:bg-white cpk:dark:bg-gray-900", "cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700", "cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800", "cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer", className),
8719
- ...props,
8720
- children: /* @__PURE__ */ jsx(ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" })
8721
- });
10391
+ _CopilotChatView.ScrollToBottomButton = ({ className, ...props }) => {
10392
+ const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
10393
+ return /* @__PURE__ */ jsx(Button, {
10394
+ "data-testid": "copilot-scroll-to-bottom",
10395
+ variant: "outline",
10396
+ size: "sm",
10397
+ className: twMerge("cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto", "cpk:bg-white cpk:dark:bg-gray-900", "cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700", "cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800", "cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer", className),
10398
+ "aria-label": labels.chatScrollToBottomLabel,
10399
+ ...props,
10400
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" })
10401
+ });
10402
+ };
8722
10403
  _CopilotChatView.Feather = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
8723
10404
  className,
8724
10405
  ...props
@@ -10101,6 +11782,7 @@ function CopilotPopupViewInternal({ header, toggleButton, width, height, clickOu
10101
11782
  if (containerRef.current?.contains(target)) return;
10102
11783
  const toggleButton = document.querySelector("[data-slot='chat-toggle-button']");
10103
11784
  if (toggleButton && toggleButton.contains(target)) return;
11785
+ if (target instanceof Element && target.closest("[data-copilotkit-popover]")) return;
10104
11786
  setModalOpen?.(false);
10105
11787
  };
10106
11788
  document.addEventListener("pointerdown", handlePointerDown);
@@ -10611,62 +12293,14 @@ CopilotThreadsDrawer.displayName = "CopilotThreadsDrawer";
10611
12293
 
10612
12294
  //#endregion
10613
12295
  //#region src/v2/components/WildcardToolCallRender.tsx
12296
+ /**
12297
+ * Catch-all (`"*"`) tool-call renderer that shows CopilotKit's default
12298
+ * tool-call card for any tool without a dedicated renderer.
12299
+ */
10614
12300
  const WildcardToolCallRender = defineToolCallRenderer({
10615
12301
  name: "*",
10616
- render: ({ args, result, name, status }) => {
10617
- const [isExpanded, setIsExpanded] = useState(false);
10618
- const statusString = String(status);
10619
- return /* @__PURE__ */ jsx("div", {
10620
- className: "cpk:mt-2 cpk:pb-2",
10621
- children: /* @__PURE__ */ jsxs("div", {
10622
- className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:dark:border-zinc-800/60 cpk:bg-white/70 cpk:dark:bg-zinc-900/50 cpk:shadow-sm cpk:backdrop-blur cpk:p-4",
10623
- children: [/* @__PURE__ */ jsxs("div", {
10624
- className: "cpk:flex cpk:items-center cpk:justify-between cpk:gap-3 cpk:cursor-pointer",
10625
- onClick: () => setIsExpanded(!isExpanded),
10626
- children: [/* @__PURE__ */ jsxs("div", {
10627
- className: "cpk:flex cpk:items-center cpk:gap-2 cpk:min-w-0",
10628
- children: [
10629
- /* @__PURE__ */ jsx("svg", {
10630
- className: `cpk:h-4 cpk:w-4 cpk:text-zinc-500 cpk:dark:text-zinc-400 cpk:transition-transform ${isExpanded ? "cpk:rotate-90" : ""}`,
10631
- fill: "none",
10632
- viewBox: "0 0 24 24",
10633
- strokeWidth: 2,
10634
- stroke: "currentColor",
10635
- children: /* @__PURE__ */ jsx("path", {
10636
- strokeLinecap: "round",
10637
- strokeLinejoin: "round",
10638
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
10639
- })
10640
- }),
10641
- /* @__PURE__ */ jsx("span", { className: "cpk:inline-block cpk:h-2 cpk:w-2 cpk:rounded-full cpk:bg-blue-500" }),
10642
- /* @__PURE__ */ jsx("span", {
10643
- className: "cpk:truncate cpk:text-sm cpk:font-medium cpk:text-zinc-900 cpk:dark:text-zinc-100",
10644
- children: name
10645
- })
10646
- ]
10647
- }), /* @__PURE__ */ jsx("span", {
10648
- className: `cpk:inline-flex cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-1 cpk:text-xs cpk:font-medium ${statusString === "inProgress" || statusString === "executing" ? "cpk:bg-amber-100 cpk:text-amber-800 cpk:dark:bg-amber-500/15 cpk:dark:text-amber-400" : statusString === "complete" ? "cpk:bg-emerald-100 cpk:text-emerald-800 cpk:dark:bg-emerald-500/15 cpk:dark:text-emerald-400" : "cpk:bg-zinc-100 cpk:text-zinc-800 cpk:dark:bg-zinc-700/40 cpk:dark:text-zinc-300"}`,
10649
- children: String(status)
10650
- })]
10651
- }), isExpanded && /* @__PURE__ */ jsxs("div", {
10652
- className: "cpk:mt-3 cpk:grid cpk:gap-4",
10653
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
10654
- className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
10655
- children: "Arguments"
10656
- }), /* @__PURE__ */ jsx("pre", {
10657
- className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
10658
- children: JSON.stringify(args ?? {}, null, 2)
10659
- })] }), result !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
10660
- className: "cpk:text-xs cpk:uppercase cpk:tracking-wide cpk:text-zinc-500 cpk:dark:text-zinc-400",
10661
- children: "Result"
10662
- }), /* @__PURE__ */ jsx("pre", {
10663
- className: "cpk:mt-2 cpk:max-h-64 cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-50 cpk:dark:bg-zinc-800/60 cpk:p-3 cpk:text-xs cpk:leading-relaxed cpk:text-zinc-800 cpk:dark:text-zinc-200 cpk:whitespace-pre-wrap cpk:break-words",
10664
- children: typeof result === "string" ? result : JSON.stringify(result, null, 2)
10665
- })] })]
10666
- })]
10667
- })
10668
- });
10669
- }
12302
+ group: true,
12303
+ render: (props) => /* @__PURE__ */ jsx(DefaultToolCallRenderer, { ...adaptRendererProps(props) })
10670
12304
  });
10671
12305
 
10672
12306
  //#endregion
@@ -12849,5 +14483,5 @@ function validateProps(props) {
12849
14483
  }
12850
14484
 
12851
14485
  //#endregion
12852
- export { useConfigureSuggestions as $, ɵselectMemoriesIsMutating as $a, ofType as $i, MultiAgentCapabilitiesSchema as $n, TextMessageEndEventSchema as $r, AudioPartSchema as $t, CopilotChatMessageView as A, ɵisCopilotKitFeature as Aa, createDebugLogger as Ai, HttpAgent as An, RunHandler as Ar, CopilotKitInspector as At, CopilotChatUserMessage_default as B, ɵobservePhoenixJoinOutcome$ as Ba, emitInspectorViewThreadResult$1 as Bi, InputContentUrlSourceSchema as Bn, SubagentFinishedEventSchema as Br, AGUI_METADATA_KEY as Bt, CopilotModalHeader as C, ɵInterruptState$1 as Ca, buildResumeArray as Ci, EventType as Cn, RunAgentInputSchema as Cr, OpenGenerativeUIToolRenderer as Ct, CopilotChat as D, ɵcreateMemoryStore as Da, contentToText$1 as Di, FilterToolCallsMiddleware as Dn, RunFinishedInterruptOutcomeSchema as Dr, MCPAppsActivityRenderer as Dt, DefaultOpenIcon as E, ɵTHREAD_REQUEST_TIMEOUT_MS as Ea, contentHasMedia as Ei, FileSourceSchema as En, RunFinishedEventSchema as Er, MCPAppsActivityContentSchema as Et, getIntelligenceTurnAnchors as F, ɵmemoryAdapterEvents as Fa, createStore as Fi, ImageInputPartSchema as Fn, StateSchema as Fr, CopilotChatConfigurationProvider as Ft, useLearningContainers as G, ɵselectFetchMoreError$1 as Ga, getRunOutcome as Gi, JsonPointerSchema as Gn, SubagentRunIdSchema as Gr, AddOperationSchema as Gt, CopilotChatAssistantMessage_default as H, ɵobservePhoenixSocketSignals$ as Ha, enforceEvents as Hi, InterruptSchema as Hn, SubagentFinishedSuccessOutcomeSchema as Hr, ActivityDeltaEventSchema as Ht, IntelligenceIndicatorView as I, ɵmemoryDomainEvents as Ia, defaultApplyEvents as Ii, ImagePartSchema as In, StateSnapshotEventSchema as Ir, useCopilotChatConfiguration as It, useLearnFromUserAction as J, ɵselectIsMutating$1 as Ja, isInspectorThreadBridgeEnabled$1 as Ji, MessageSchema as Jn, SystemMessageSchema as Jr, AgentThreadLockedError as Jt, useAttachments as K, ɵselectHasNextPage$1 as Ka, getWebMCPModelContext as Ki, MEMORY_ERROR_REGISTRY as Kn, SubagentStartedEventSchema as Kr, AgentCapabilitiesSchema as Kt, CopilotChatSuggestionView as L, ɵmemoryReducer as La, emitInspectorActiveThread$1 as Li, InputContentDataSourceSchema as Ln, StepFinishedEventSchema as Lr, ACTIVITY_HISTORY_METADATA as Lt, messageRow as M, ɵisRetryableMemoryStatus as Ma, createInspectorThreadRequestId as Mi, INSPECTOR_THREAD_BRIDGE_PLUGIN_ID as Mn, StateCapabilitiesSchema as Mr, CopilotChatInput_default as Mt, INTELLIGENCE_TURN_HEAD as N, ɵjoinPhoenixChannel$ as Na, createReducer as Ni, IdentityCapabilitiesSchema as Nn, StateDeltaEventSchema as Nr, AudioRecorderError as Nt, CopilotChatView_default as O, ɵcreateThreadSelectors as Oa, convertToLegacyEvents as Oi, FunctionCallSchema as On, RunFinishedOutcomeSchema as Or, MCPAppsActivityType as Ot, IntelligenceIndicator as P, ɵmapMemoryMetadataEvent as Pa, createSelector as Pi, ImageInputContentSchema as Pn, StateManager as Pr, CopilotChatAudioRecorder as Pt, INTERRUPT_EVENT_NAME as Q, ɵselectMemoriesIsLoading$1 as Qa, mergeMetadata as Qi, MoveOperationSchema as Qn, TextMessageContentEventSchema as Qr, AudioInputPartSchema as Qt, CopilotChatSuggestionPill as R, ɵmemoryRestEvents as Ra, emitInspectorStopViewing as Ri, InputContentSchema as Rn, StepStartedEventSchema as Rr, AGUIConnectNotImplementedError$1 as Rt, CopilotSidebarView as S, ɵCOPILOTKIT_FEATURES as Sa, authoritativeActivityTypes as Si, EventSchemas as Sn, RoleSchema as Sr, OpenGenerativeUIContentSchema as St, DefaultCloseIcon as T, ɵRUNTIME_PROBE_TIMEOUT_MS as Ta, completePartialMarkdown as Ti, ExecutionCapabilitiesSchema as Tn, RunFinishedCancelledOutcomeSchema as Tr, useSandboxFunctions as Tt, CopilotChatToolCallsView as U, ɵphoenixChannel$ as Ua, enforceOutgoingInput as Ui, JsonPatchOperationSchema as Un, SubagentFinishedSuspendedOutcomeSchema as Ur, ActivityMessageSchema as Ut, CopilotChatAttachmentRenderer as V, ɵobservePhoenixSocketHealth$ as Va, empty as Vi, IntelligenceAgent as Vn, SubagentFinishedOutcomeSchema as Vr, AbstractAgent$1 as Vt, useLearningContainersInCurrentThread as W, ɵphoenixSocket$ as Wa, ensureObjectArgs as Wi, JsonPatchSchema as Wn, SubagentInfoSchema as Wr, ActivitySnapshotEventSchema as Wt, useThreads$1 as X, ɵselectMemoriesAvailable$1 as Xa, isRecognizedEvent as Xi, MetadataSchema as Xn, TextInputContentSchema as Xr, AttributableSchema as Xt, useMemories as Y, ɵselectMemories$1 as Ya, isInterruptExpired as Yi, MessagesSnapshotEventSchema as Yn, TestOperationSchema as Yr, AssistantMessageSchema as Yt, useInterrupt as Z, ɵselectMemoriesError$1 as Za, isRunCompletionAware$1 as Zi, Middleware as Zn, TextMessageChunkEventSchema as Zr, AudioInputContentSchema as Zt, WildcardToolCallRender as _, tokenUsageFromLangChainMetadata as _a, VideoInputContentSchema as _i, DeveloperMessageSchema as _n, ReasoningMessageStartEventSchema as _r, defineToolCallRenderer as _t, ThreadsProvider as a, onInspectorViewThreadResult as aa, ToolCallChunkEventSchema as ai, CompatibilityBoundary as an, ɵwithHeaderDefaults$1 as ao, PROTOCOL_VERSION as ar, useHumanInTheLoop as at, CopilotSidebar as b, verifyEvents as ba, WebMCPRegistry as bi, DocumentPartSchema as bn, ReplaceOperationSchema as br, OpenGenerativeUIActivityRenderer as bt, CoAgentStateRendersProvider as c, parseToolArguments as ca, ToolCallSchema as ci, ContextStore as cn, RawEventSchema as cr, useComponent as ct, shouldShowDevConsole as d, resolveAgentDebugConfig as da, ToolMessageSchema as di, CopilotKitCoreRuntimeConnectionStatus$1 as dn, ReasoningEncryptedValueSubtypeSchema as dr, useRenderActivityMessage as dt, omitOptionalNulls as ea, TextMessageRoleSchema as ei, BackwardCompatibility_0_0_39 as en, ɵselectMemoriesRealtimeStatus$1 as eo, MultimodalCapabilitiesSchema as er, useSuggestions as et, useToast as f, resolveDebugLogger as fa, ToolSchema as fi, CopilotKitHeaderResolutionError as fn, ReasoningEndEventSchema as fr, useRenderCustomMessages as ft, useCopilotContext as g, tokenUsageFromAiSdkUsage as ga, UserMessageSchema as gi, DebugLogger as gn, ReasoningMessageSchema as gr, CopilotKitCoreReact as gt, CopilotContext as h, structuredClone_ as ha, UrlSourceSchema as hi, DataSourceSchema as hn, ReasoningMessageEndEventSchema as hr, useLicenseContext$1 as ht, ThreadsContext as i, onInspectorViewThread$1 as ia, ToolCallArgsEventSchema as ii, BaseMessageSchema as in, ɵthreadAdapterEvents as io, OutputCapabilitiesSchema as ir, useAgent as it, messageGroup as j, ɵisHttpAgent$1 as ja, createEffect as ji, HumanInTheLoopCapabilitiesSchema as jn, RunStartedEventSchema as jr, useRenderToolCall as jt, CopilotChatAttachmentQueue as k, ɵcreateThreadStore$1 as ka, createActionGroup as ki, FunctionMiddleware as kn, RunFinishedSuccessOutcomeSchema as kr, ɵrunMcpFollowUp as kt, useCoAgentStateRenders as l, props as la, ToolCallStartEventSchema as li, CopilotKitCore$1 as ln, ReasoningCapabilitiesSchema as lr, useFrontendTools as lt, useCopilotMessagesContext as m, select as ma, TransportCapabilitiesSchema as mi, CustomEventSchema as mn, ReasoningMessageContentEventSchema as mr, useCopilotKit$1 as mt, defaultCopilotContextCategories as n, onInspectorActiveThread as na, TextPartSchema as ni, BackwardCompatibility_0_0_57 as nn, ɵselectThreadsError$1 as no, MultimodalOutputCapabilitiesSchema as nr, useCapabilities as nt, useThreads as o, parseProtoStream as oa, ToolCallEndEventSchema as oi, ContentPartSchema as on, PartSourceSchema as or, useDefaultRenderTool as ot, CopilotMessagesContext as p, runHttpRequest as pa, ToolsCapabilitiesSchema as pi, CopyOperationSchema as pn, ReasoningMessageChunkEventSchema as pr, CopilotKitProvider as pt, useLearnFromUserActionInCurrentThread as q, ɵselectIsFetchingNextPage$1 as qa, isAbortError as qi, MemoryError as qn, SuggestionEngine as qr, AgentRegistry as qt, CoAgentStateRenderBridge as r, onInspectorStopViewing$1 as ra, TokenUsageSchema as ri, BaseEventSchema as rn, ɵselectThreadsIsLoading$1 as ro, OptionalMetadataSchema as rr, UseAgentUpdate as rt, CoAgentStateRendersContext as s, parseSSEStream as sa, ToolCallResultEventSchema as si, ContextSchema as sn, ProxiedCopilotRuntimeAgent$1 as sr, useRenderTool as st, CopilotKit as t, on as ta, TextMessageStartEventSchema as ti, BackwardCompatibility_0_0_45 as tn, ɵselectThreads$1 as to, MultimodalInputCapabilitiesSchema as tr, useAgentContext as tt, useAsyncCallback as u, randomUUID$2 as ua, ToolCallStatus$1 as ui, CopilotKitCoreErrorCode$1 as un, ReasoningEncryptedValueEventSchema as ur, useFrontendTool as ut, CopilotThreadsDrawer as v, transformChunks as va, VideoInputPartSchema as vi, DocumentInputContentSchema as vn, ReasoningStartEventSchema as vr, createA2UIMessageRenderer as vt, CopilotChatToggleButton as w, ɵMAX_SOCKET_RETRIES as wa, compactEvents as wi, EventTypeSchema as wn, RunErrorEventSchema as wr, SandboxFunctionsContext as wt, CopilotPopupView as x, withAuthoritativeActivityTypes as xa, aggregateTokenUsage as xi, EventSchema as xn, ResumeEntrySchema as xr, OpenGenerativeUIActivityType as xt, CopilotPopup as y, transformHttpEventStream as ya, VideoPartSchema as yi, DocumentInputPartSchema as yn, RemoveOperationSchema as yr, GenerateSandboxedUiArgsSchema as yt, CopilotChatReasoningMessage_default as z, ɵobservePhoenixEvent$ as za, emitInspectorViewThread as zi, InputContentSourceSchema as zn, SubagentErrorEventSchema as zr, AGUIError as zt };
12853
- //# sourceMappingURL=copilotkit-CD3uPUN3.mjs.map
14486
+ export { INTERRUPT_EVENT_NAME as $, ɵselectMemoriesError$1 as $a, isRunCompletionAware$1 as $i, Middleware as $n, TextMessageChunkEventSchema as $r, AudioInputContentSchema as $t, CopilotChatMessageView as A, ɵcreateThreadSelectors as Aa, convertToLegacyEvents as Ai, FunctionCallSchema as An, RunFinishedOutcomeSchema as Ar, MCPAppsActivityType as At, CopilotChatReasoningMessage_default as B, ɵmemoryRestEvents as Ba, emitInspectorStopViewing as Bi, InputContentSchema as Bn, StepStartedEventSchema as Br, AGUIConnectNotImplementedError$1 as Bt, CopilotModalHeader as C, withAuthoritativeActivityTypes as Ca, aggregateTokenUsage as Ci, EventSchema as Cn, ResumeEntrySchema as Cr, OpenGenerativeUIActivityType as Ct, CopilotChat as D, ɵRUNTIME_PROBE_TIMEOUT_MS as Da, completePartialMarkdown as Di, ExecutionCapabilitiesSchema as Dn, RunFinishedCancelledOutcomeSchema as Dr, useSandboxFunctions as Dt, DefaultOpenIcon as E, ɵMAX_SOCKET_RETRIES as Ea, compactEvents as Ei, EventTypeSchema as En, RunErrorEventSchema as Er, SandboxFunctionsContext as Et, getIntelligenceTurnAnchors as F, ɵjoinPhoenixChannel$ as Fa, createReducer as Fi, IdentityCapabilitiesSchema as Fn, StateDeltaEventSchema as Fr, AudioRecorderError as Ft, useLearningContainersInCurrentThread as G, ɵphoenixChannel$ as Ga, enforceOutgoingInput as Gi, JsonPatchOperationSchema as Gn, SubagentFinishedSuspendedOutcomeSchema as Gr, ActivityMessageSchema as Gt, CopilotChatAttachmentRenderer as H, ɵobservePhoenixJoinOutcome$ as Ha, emitInspectorViewThreadResult$1 as Hi, InputContentUrlSourceSchema as Hn, SubagentFinishedEventSchema as Hr, AGUI_METADATA_KEY as Ht, IntelligenceIndicatorView as I, ɵmapMemoryMetadataEvent as Ia, createSelector as Ii, ImageInputContentSchema as In, StateManager as Ir, CopilotChatAudioRecorder as It, useLearnFromUserActionInCurrentThread as J, ɵselectHasNextPage$1 as Ja, getWebMCPModelContext as Ji, MEMORY_ERROR_REGISTRY as Jn, SubagentStartedEventSchema as Jr, AgentCapabilitiesSchema as Jt, useLearningContainers as K, ɵphoenixSocket$ as Ka, ensureObjectArgs as Ki, JsonPatchSchema as Kn, SubagentInfoSchema as Kr, ActivitySnapshotEventSchema as Kt, CopilotChatWorkSummary as L, ɵmemoryAdapterEvents as La, createStore as Li, ImageInputPartSchema as Ln, StateSchema as Lr, CopilotChatConfigurationProvider as Lt, messageRow as M, ɵisCopilotKitFeature as Ma, createDebugLogger as Mi, HttpAgent as Mn, RunHandler as Mr, CopilotKitInspector as Mt, INTELLIGENCE_TURN_HEAD as N, ɵisHttpAgent$1 as Na, createEffect as Ni, HumanInTheLoopCapabilitiesSchema as Nn, RunStartedEventSchema as Nr, useRenderToolCall as Nt, CopilotChatView_default as O, ɵTHREAD_REQUEST_TIMEOUT_MS as Oa, contentHasMedia as Oi, FileSourceSchema as On, RunFinishedEventSchema as Or, MCPAppsActivityContentSchema as Ot, IntelligenceIndicator as P, ɵisRetryableMemoryStatus as Pa, createInspectorThreadRequestId as Pi, INSPECTOR_THREAD_BRIDGE_PLUGIN_ID as Pn, StateCapabilitiesSchema as Pr, CopilotChatInput_default as Pt, useInterrupt as Q, ɵselectMemoriesAvailable$1 as Qa, isRecognizedEvent as Qi, MetadataSchema as Qn, TextInputContentSchema as Qr, AttributableSchema as Qt, CopilotChatSuggestionView as R, ɵmemoryDomainEvents as Ra, defaultApplyEvents as Ri, ImagePartSchema as Rn, StateSnapshotEventSchema as Rr, useCopilotChatConfiguration as Rt, CopilotSidebarView as S, verifyEvents as Sa, WebMCPRegistry as Si, DocumentPartSchema as Sn, ReplaceOperationSchema as Sr, OpenGenerativeUIActivityRenderer as St, DefaultCloseIcon as T, ɵInterruptState$1 as Ta, buildResumeArray as Ti, EventType as Tn, RunAgentInputSchema as Tr, OpenGenerativeUIToolRenderer as Tt, CopilotChatAssistantMessage_default as U, ɵobservePhoenixSocketHealth$ as Ua, empty as Ui, IntelligenceAgent as Un, SubagentFinishedOutcomeSchema as Ur, AbstractAgent$1 as Ut, CopilotChatUserMessage_default as V, ɵobservePhoenixEvent$ as Va, emitInspectorViewThread as Vi, InputContentSourceSchema as Vn, SubagentErrorEventSchema as Vr, AGUIError as Vt, CopilotChatToolCallsView as W, ɵobservePhoenixSocketSignals$ as Wa, enforceEvents as Wi, InterruptSchema as Wn, SubagentFinishedSuccessOutcomeSchema as Wr, ActivityDeltaEventSchema as Wt, useMemories as X, ɵselectIsMutating$1 as Xa, isInspectorThreadBridgeEnabled$1 as Xi, MessageSchema as Xn, SystemMessageSchema as Xr, AgentThreadLockedError as Xt, useLearnFromUserAction as Y, ɵselectIsFetchingNextPage$1 as Ya, isAbortError as Yi, MemoryError as Yn, SuggestionEngine as Yr, AgentRegistry as Yt, useThreads$1 as Z, ɵselectMemories$1 as Za, isInterruptExpired as Zi, MessagesSnapshotEventSchema as Zn, TestOperationSchema as Zr, AssistantMessageSchema as Zt, WildcardToolCallRender as _, structuredClone_ as _a, UrlSourceSchema as _i, DataSourceSchema as _n, ReasoningMessageEndEventSchema as _r, useLicenseContext$1 as _t, ThreadsProvider as a, onInspectorStopViewing$1 as aa, TokenUsageSchema as ai, BaseEventSchema as an, ɵselectThreadsIsLoading$1 as ao, OptionalMetadataSchema as ar, useAgent as at, CopilotSidebar as b, transformChunks as ba, VideoInputPartSchema as bi, DocumentInputContentSchema as bn, ReasoningStartEventSchema as br, createA2UIMessageRenderer as bt, CoAgentStateRendersProvider as c, parseProtoStream as ca, ToolCallEndEventSchema as ci, ContentPartSchema as cn, PartSourceSchema as cr, CopilotChatToolCallGroup as ct, shouldShowDevConsole as d, props as da, ToolCallStartEventSchema as di, CopilotKitCore$1 as dn, ReasoningCapabilitiesSchema as dr, useFrontendTools as dt, mergeMetadata as ea, TextMessageContentEventSchema as ei, AudioInputPartSchema as en, ɵselectMemoriesIsLoading$1 as eo, MoveOperationSchema as er, useConfigureSuggestions as et, useToast as f, randomUUID$2 as fa, ToolCallStatus$1 as fi, CopilotKitCoreErrorCode$1 as fn, ReasoningEncryptedValueEventSchema as fr, useFrontendTool as ft, useCopilotContext as g, select as ga, TransportCapabilitiesSchema as gi, CustomEventSchema as gn, ReasoningMessageContentEventSchema as gr, useCopilotKit$1 as gt, CopilotContext as h, runHttpRequest as ha, ToolsCapabilitiesSchema as hi, CopyOperationSchema as hn, ReasoningMessageChunkEventSchema as hr, CopilotKitProvider as ht, ThreadsContext as i, onInspectorActiveThread as ia, TextPartSchema as ii, BackwardCompatibility_0_0_57 as in, ɵselectThreadsError$1 as io, MultimodalOutputCapabilitiesSchema as ir, UseAgentUpdate as it, messageGroup as j, ɵcreateThreadStore$1 as ja, createActionGroup as ji, FunctionMiddleware as jn, RunFinishedSuccessOutcomeSchema as jr, ɵrunMcpFollowUp as jt, CopilotChatAttachmentQueue as k, ɵcreateMemoryStore as ka, contentToText$1 as ki, FilterToolCallsMiddleware as kn, RunFinishedInterruptOutcomeSchema as kr, MCPAppsActivityRenderer as kt, useCoAgentStateRenders as l, parseSSEStream as la, ToolCallResultEventSchema as li, ContextSchema as ln, ProxiedCopilotRuntimeAgent$1 as lr, useRenderTool as lt, useCopilotMessagesContext as m, resolveDebugLogger as ma, ToolSchema as mi, CopilotKitHeaderResolutionError as mn, ReasoningEndEventSchema as mr, useRenderCustomMessages as mt, defaultCopilotContextCategories as n, omitOptionalNulls as na, TextMessageRoleSchema as ni, BackwardCompatibility_0_0_39 as nn, ɵselectMemoriesRealtimeStatus$1 as no, MultimodalCapabilitiesSchema as nr, useAgentContext as nt, useThreads as o, onInspectorViewThread$1 as oa, ToolCallArgsEventSchema as oi, BaseMessageSchema as on, ɵthreadAdapterEvents as oo, OutputCapabilitiesSchema as or, useHumanInTheLoop as ot, CopilotMessagesContext as p, resolveAgentDebugConfig as pa, ToolMessageSchema as pi, CopilotKitCoreRuntimeConnectionStatus$1 as pn, ReasoningEncryptedValueSubtypeSchema as pr, useRenderActivityMessage as pt, useAttachments as q, ɵselectFetchMoreError$1 as qa, getRunOutcome as qi, JsonPointerSchema as qn, SubagentRunIdSchema as qr, AddOperationSchema as qt, CoAgentStateRenderBridge as r, on as ra, TextMessageStartEventSchema as ri, BackwardCompatibility_0_0_45 as rn, ɵselectThreads$1 as ro, MultimodalInputCapabilitiesSchema as rr, useCapabilities as rt, CoAgentStateRendersContext as s, onInspectorViewThreadResult as sa, ToolCallChunkEventSchema as si, CompatibilityBoundary as sn, ɵwithHeaderDefaults$1 as so, PROTOCOL_VERSION as sr, useDefaultRenderTool as st, CopilotKit as t, ofType as ta, TextMessageEndEventSchema as ti, AudioPartSchema as tn, ɵselectMemoriesIsMutating as to, MultiAgentCapabilitiesSchema as tr, useSuggestions as tt, useAsyncCallback as u, parseToolArguments as ua, ToolCallSchema as ui, ContextStore as un, RawEventSchema as ur, useComponent as ut, CopilotThreadsDrawer as v, tokenUsageFromAiSdkUsage as va, UserMessageSchema as vi, DebugLogger as vn, ReasoningMessageSchema as vr, CopilotKitCoreReact as vt, CopilotChatToggleButton as w, ɵCOPILOTKIT_FEATURES as wa, authoritativeActivityTypes as wi, EventSchemas as wn, RoleSchema as wr, OpenGenerativeUIContentSchema as wt, CopilotPopupView as x, transformHttpEventStream as xa, VideoPartSchema as xi, DocumentInputPartSchema as xn, RemoveOperationSchema as xr, GenerateSandboxedUiArgsSchema as xt, CopilotPopup as y, tokenUsageFromLangChainMetadata as ya, VideoInputContentSchema as yi, DeveloperMessageSchema as yn, ReasoningMessageStartEventSchema as yr, defineToolCallRenderer as yt, CopilotChatSuggestionPill as z, ɵmemoryReducer as za, emitInspectorActiveThread$1 as zi, InputContentDataSourceSchema as zn, StepFinishedEventSchema as zr, ACTIVITY_HISTORY_METADATA as zt };
14487
+ //# sourceMappingURL=copilotkit-BNLtgOyD.mjs.map