@copilotkit/react-core 1.57.0 → 1.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/{copilotkit-DFaI4j2r.d.mts → copilotkit-BN4I_y1n.d.mts} +64 -8
  2. package/dist/copilotkit-BN4I_y1n.d.mts.map +1 -0
  3. package/dist/{copilotkit-DGbvw8n2.cjs → copilotkit-C3k13WZn.cjs} +572 -435
  4. package/dist/copilotkit-C3k13WZn.cjs.map +1 -0
  5. package/dist/{copilotkit-CPe2-340.mjs → copilotkit-DjxXMYHG.mjs} +571 -440
  6. package/dist/copilotkit-DjxXMYHG.mjs.map +1 -0
  7. package/dist/{copilotkit-Dg4r4Gi_.d.cts → copilotkit-sQWiKtxA.d.cts} +64 -8
  8. package/dist/copilotkit-sQWiKtxA.d.cts.map +1 -0
  9. package/dist/index.cjs +2 -5
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.mts +1 -1
  13. package/dist/index.mjs +2 -5
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/index.umd.js +449 -502
  16. package/dist/index.umd.js.map +1 -1
  17. package/dist/v2/context.cjs +135 -0
  18. package/dist/v2/context.cjs.map +1 -0
  19. package/dist/v2/context.d.cts +148 -0
  20. package/dist/v2/context.d.cts.map +1 -0
  21. package/dist/v2/context.d.mts +148 -0
  22. package/dist/v2/context.d.mts.map +1 -0
  23. package/dist/v2/context.mjs +129 -0
  24. package/dist/v2/context.mjs.map +1 -0
  25. package/dist/v2/headless.cjs +1043 -0
  26. package/dist/v2/headless.cjs.map +1 -0
  27. package/dist/v2/headless.d.cts +605 -0
  28. package/dist/v2/headless.d.cts.map +1 -0
  29. package/dist/v2/headless.d.mts +512 -0
  30. package/dist/v2/headless.d.mts.map +1 -0
  31. package/dist/v2/headless.mjs +997 -0
  32. package/dist/v2/headless.mjs.map +1 -0
  33. package/dist/v2/index.cjs +2 -1
  34. package/dist/v2/index.css +1 -1
  35. package/dist/v2/index.d.cts +2 -2
  36. package/dist/v2/index.d.mts +2 -2
  37. package/dist/v2/index.mjs +2 -2
  38. package/dist/v2/index.umd.js +584 -441
  39. package/dist/v2/index.umd.js.map +1 -1
  40. package/package.json +14 -6
  41. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +5 -6
  42. package/src/hooks/use-copilot-chat_internal.ts +0 -1
  43. package/src/v2/components/chat/CopilotChat.tsx +2 -1
  44. package/src/v2/components/chat/CopilotChatMessageView.tsx +24 -9
  45. package/src/v2/components/chat/CopilotChatView.tsx +2 -2
  46. package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +1 -3
  47. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +29 -25
  48. package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.tsx +5 -60
  49. package/src/v2/components/index.ts +1 -0
  50. package/src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx +286 -0
  51. package/src/v2/components/intelligence-indicator/__tests__/IntelligenceIndicator.e2e.test.tsx +464 -0
  52. package/src/v2/components/intelligence-indicator/index.ts +2 -0
  53. package/src/v2/context.ts +62 -0
  54. package/src/v2/headless.ts +42 -0
  55. package/src/v2/hooks/__tests__/standard-schema.test.tsx +2 -2
  56. package/src/v2/hooks/__tests__/use-agent-context.test.tsx +3 -3
  57. package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +3 -3
  58. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +85 -85
  59. package/src/v2/hooks/__tests__/use-interrupt.test.tsx +2 -2
  60. package/src/v2/hooks/__tests__/use-render-tool.test.tsx +2 -2
  61. package/src/v2/hooks/__tests__/use-threads.test.tsx +2 -2
  62. package/src/v2/hooks/__tests__/zod-regression.test.tsx +2 -2
  63. package/src/v2/hooks/use-agent-context.tsx +1 -1
  64. package/src/v2/hooks/use-agent.tsx +9 -118
  65. package/src/v2/hooks/use-configure-suggestions.tsx +1 -1
  66. package/src/v2/hooks/use-frontend-tool.tsx +2 -2
  67. package/src/v2/hooks/use-human-in-the-loop.tsx +1 -1
  68. package/src/v2/hooks/use-interrupt.tsx +1 -1
  69. package/src/v2/hooks/use-render-activity-message.tsx +3 -11
  70. package/src/v2/hooks/use-render-custom-messages.tsx +1 -6
  71. package/src/v2/hooks/use-render-tool-call.tsx +1 -1
  72. package/src/v2/hooks/use-render-tool.tsx +2 -2
  73. package/src/v2/hooks/use-suggestions.tsx +1 -1
  74. package/src/v2/hooks/use-threads.tsx +1 -1
  75. package/src/v2/providers/CopilotKitProvider.tsx +19 -59
  76. package/src/v2/styles/globals.css +118 -0
  77. package/tsdown.config.ts +75 -0
  78. package/dist/copilotkit-CPe2-340.mjs.map +0 -1
  79. package/dist/copilotkit-DFaI4j2r.d.mts.map +0 -1
  80. package/dist/copilotkit-DGbvw8n2.cjs.map +0 -1
  81. package/dist/copilotkit-Dg4r4Gi_.d.cts.map +0 -1
  82. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +0 -333
@@ -0,0 +1,997 @@
1
+ import { CopilotKitCoreReact, useCopilotKit } from "@copilotkit/react-core/v2/context";
2
+ import React, { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
3
+ import { DEFAULT_AGENT_ID, randomUUID } from "@copilotkit/shared";
4
+ import { twMerge } from "tailwind-merge";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import { HttpAgent } from "@ag-ui/client";
7
+ import { CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
8
+
9
+ //#region src/v2/lib/slots.tsx
10
+ /**
11
+ * Shallow equality comparison for objects.
12
+ */
13
+ function shallowEqual(obj1, obj2) {
14
+ const keys1 = Object.keys(obj1);
15
+ const keys2 = Object.keys(obj2);
16
+ if (keys1.length !== keys2.length) return false;
17
+ for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
18
+ return true;
19
+ }
20
+ /**
21
+ * Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
22
+ * class instances, and other exotic objects that happen to have typeof "object".
23
+ */
24
+ function isPlainObject(obj) {
25
+ return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
26
+ }
27
+ /**
28
+ * Returns the same reference as long as the value is shallowly equal to the
29
+ * previous render's value.
30
+ *
31
+ * - Identical references bail out immediately (O(1)).
32
+ * - Plain objects ({}) are shallow-compared key-by-key.
33
+ * - Arrays, Dates, class instances, functions, and primitives are compared by
34
+ * reference only — shallowEqual is never called on non-plain objects, which
35
+ * avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
36
+ *
37
+ * Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
38
+ * equality check isn't defeated by a new object reference on every render.
39
+ */
40
+ function useShallowStableRef(value) {
41
+ const ref = useRef(value);
42
+ if (ref.current === value) return ref.current;
43
+ if (isPlainObject(ref.current) && isPlainObject(value)) {
44
+ if (shallowEqual(ref.current, value)) return ref.current;
45
+ }
46
+ ref.current = value;
47
+ return ref.current;
48
+ }
49
+ /**
50
+ * Check if a value is a React component type (function, class, forwardRef, memo, etc.)
51
+ */
52
+ function isReactComponentType(value) {
53
+ if (typeof value === "function") return true;
54
+ if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
55
+ return false;
56
+ }
57
+ /**
58
+ * Internal function to render a slot value as a React element (non-memoized).
59
+ */
60
+ function renderSlotElement(slot, DefaultComponent, props) {
61
+ if (typeof slot === "string") {
62
+ const existingClassName = props.className;
63
+ return React.createElement(DefaultComponent, {
64
+ ...props,
65
+ className: twMerge(existingClassName, slot)
66
+ });
67
+ }
68
+ if (isReactComponentType(slot)) return React.createElement(slot, props);
69
+ if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
70
+ ...props,
71
+ ...slot
72
+ });
73
+ return React.createElement(DefaultComponent, props);
74
+ }
75
+ /**
76
+ * Internal memoized wrapper component for renderSlot.
77
+ * Uses forwardRef to support ref forwarding.
78
+ */
79
+ const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
80
+ const { $slot, $component, ...rest } = props;
81
+ return renderSlotElement($slot, $component, ref !== null ? {
82
+ ...rest,
83
+ ref
84
+ } : rest);
85
+ }), (prev, next) => {
86
+ if (prev.$slot !== next.$slot) return false;
87
+ if (prev.$component !== next.$component) return false;
88
+ const { $slot: _ps, $component: _pc, ...prevRest } = prev;
89
+ const { $slot: _ns, $component: _nc, ...nextRest } = next;
90
+ return shallowEqual(prevRest, nextRest);
91
+ });
92
+
93
+ //#endregion
94
+ //#region src/v2/providers/CopilotChatConfigurationProvider.tsx
95
+ const CopilotChatDefaultLabels = {
96
+ chatInputPlaceholder: "Type a message...",
97
+ chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
98
+ chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
99
+ chatInputToolbarFinishTranscribeButtonLabel: "Finish",
100
+ chatInputToolbarAddButtonLabel: "Add attachments",
101
+ chatInputToolbarToolsButtonLabel: "Tools",
102
+ assistantMessageToolbarCopyCodeLabel: "Copy",
103
+ assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
104
+ assistantMessageToolbarCopyMessageLabel: "Copy",
105
+ assistantMessageToolbarThumbsUpLabel: "Good response",
106
+ assistantMessageToolbarThumbsDownLabel: "Bad response",
107
+ assistantMessageToolbarReadAloudLabel: "Read aloud",
108
+ assistantMessageToolbarRegenerateLabel: "Regenerate",
109
+ userMessageToolbarCopyMessageLabel: "Copy",
110
+ userMessageToolbarEditMessageLabel: "Edit",
111
+ chatDisclaimerText: "AI can make mistakes. Please verify important information.",
112
+ chatToggleOpenLabel: "Open chat",
113
+ chatToggleCloseLabel: "Close chat",
114
+ modalHeaderTitle: "CopilotKit Chat",
115
+ welcomeMessageText: "How can I help you today?"
116
+ };
117
+ const CopilotChatConfiguration = createContext(null);
118
+ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
119
+ const parentConfig = useContext(CopilotChatConfiguration);
120
+ const stableLabels = useShallowStableRef(labels);
121
+ const mergedLabels = useMemo(() => ({
122
+ ...CopilotChatDefaultLabels,
123
+ ...parentConfig?.labels,
124
+ ...stableLabels
125
+ }), [stableLabels, parentConfig?.labels]);
126
+ const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
127
+ const resolvedThreadId = useMemo(() => {
128
+ if (threadId) return threadId;
129
+ if (parentConfig?.threadId) return parentConfig.threadId;
130
+ return randomUUID();
131
+ }, [threadId, parentConfig?.threadId]);
132
+ const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
133
+ const [internalModalOpen, setInternalModalOpen] = useState(isModalDefaultOpen ?? true);
134
+ const hasExplicitDefault = isModalDefaultOpen !== void 0;
135
+ const setAndSync = useCallback((open) => {
136
+ setInternalModalOpen(open);
137
+ parentConfig?.setModalOpen(open);
138
+ }, [parentConfig?.setModalOpen]);
139
+ const isMounted = useRef(false);
140
+ useEffect(() => {
141
+ if (!hasExplicitDefault) return;
142
+ if (!isMounted.current) {
143
+ isMounted.current = true;
144
+ return;
145
+ }
146
+ if (parentConfig?.isModalOpen === void 0) return;
147
+ setInternalModalOpen(parentConfig.isModalOpen);
148
+ }, [parentConfig?.isModalOpen, hasExplicitDefault]);
149
+ const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
150
+ const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
151
+ const configurationValue = useMemo(() => ({
152
+ labels: mergedLabels,
153
+ agentId: resolvedAgentId,
154
+ threadId: resolvedThreadId,
155
+ hasExplicitThreadId: resolvedHasExplicitThreadId,
156
+ isModalOpen: resolvedIsModalOpen,
157
+ setModalOpen: resolvedSetModalOpen
158
+ }), [
159
+ mergedLabels,
160
+ resolvedAgentId,
161
+ resolvedThreadId,
162
+ resolvedHasExplicitThreadId,
163
+ resolvedIsModalOpen,
164
+ resolvedSetModalOpen
165
+ ]);
166
+ return /* @__PURE__ */ jsx(CopilotChatConfiguration.Provider, {
167
+ value: configurationValue,
168
+ children
169
+ });
170
+ };
171
+ const useCopilotChatConfiguration = () => {
172
+ return useContext(CopilotChatConfiguration);
173
+ };
174
+
175
+ //#endregion
176
+ //#region src/v2/hooks/use-agent.tsx
177
+ let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
178
+ UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
179
+ UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
180
+ UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
181
+ return UseAgentUpdate;
182
+ }({});
183
+ const ALL_UPDATES = [
184
+ UseAgentUpdate.OnMessagesChanged,
185
+ UseAgentUpdate.OnStateChanged,
186
+ UseAgentUpdate.OnRunStatusChanged
187
+ ];
188
+ function useAgent({ agentId, updates, throttleMs } = {}) {
189
+ agentId ??= DEFAULT_AGENT_ID;
190
+ const { copilotkit } = useCopilotKit();
191
+ const providerThrottleMs = copilotkit.defaultThrottleMs;
192
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
193
+ const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
194
+ const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
195
+ const agent = useMemo(() => {
196
+ const existing = copilotkit.getAgent(agentId);
197
+ if (existing) {
198
+ provisionalAgentCache.current.delete(agentId);
199
+ return existing;
200
+ }
201
+ const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
202
+ const status = copilotkit.runtimeConnectionStatus;
203
+ if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
204
+ const cached = provisionalAgentCache.current.get(agentId);
205
+ if (cached) {
206
+ cached.headers = { ...copilotkit.headers };
207
+ return cached;
208
+ }
209
+ const provisional = new ProxiedCopilotRuntimeAgent({
210
+ runtimeUrl: copilotkit.runtimeUrl,
211
+ agentId,
212
+ transport: copilotkit.runtimeTransport,
213
+ runtimeMode: "pending"
214
+ });
215
+ provisional.headers = { ...copilotkit.headers };
216
+ provisionalAgentCache.current.set(agentId, provisional);
217
+ return provisional;
218
+ }
219
+ if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
220
+ const cached = provisionalAgentCache.current.get(agentId);
221
+ if (cached) {
222
+ cached.headers = { ...copilotkit.headers };
223
+ return cached;
224
+ }
225
+ const provisional = new ProxiedCopilotRuntimeAgent({
226
+ runtimeUrl: copilotkit.runtimeUrl,
227
+ agentId,
228
+ transport: copilotkit.runtimeTransport,
229
+ runtimeMode: "pending"
230
+ });
231
+ provisional.headers = { ...copilotkit.headers };
232
+ provisionalAgentCache.current.set(agentId, provisional);
233
+ return provisional;
234
+ }
235
+ const knownAgents = Object.keys(copilotkit.agents ?? {});
236
+ const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
237
+ throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
238
+ }, [
239
+ agentId,
240
+ copilotkit.agents,
241
+ copilotkit.runtimeConnectionStatus,
242
+ copilotkit.runtimeUrl,
243
+ copilotkit.runtimeTransport,
244
+ JSON.stringify(copilotkit.headers)
245
+ ]);
246
+ useEffect(() => {
247
+ if (updateFlags.length === 0) return;
248
+ let active = true;
249
+ const handlers = {};
250
+ let batchScheduled = false;
251
+ const batchedForceUpdate = () => {
252
+ if (!active) return;
253
+ if (!batchScheduled) {
254
+ batchScheduled = true;
255
+ queueMicrotask(() => {
256
+ batchScheduled = false;
257
+ if (active) forceUpdate();
258
+ });
259
+ }
260
+ };
261
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = batchedForceUpdate;
262
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
263
+ if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
264
+ handlers.onRunInitialized = batchedForceUpdate;
265
+ handlers.onRunFinalized = batchedForceUpdate;
266
+ handlers.onRunFailed = batchedForceUpdate;
267
+ handlers.onRunErrorEvent = batchedForceUpdate;
268
+ }
269
+ const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
270
+ return () => {
271
+ active = false;
272
+ subscription.unsubscribe();
273
+ };
274
+ }, [
275
+ agent,
276
+ forceUpdate,
277
+ throttleMs,
278
+ providerThrottleMs,
279
+ updateFlags
280
+ ]);
281
+ useEffect(() => {
282
+ if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
283
+ }, [agent, JSON.stringify(copilotkit.headers)]);
284
+ return { agent };
285
+ }
286
+
287
+ //#endregion
288
+ //#region src/v2/hooks/use-frontend-tool.tsx
289
+ const EMPTY_DEPS = [];
290
+ function useFrontendTool(tool, deps) {
291
+ const { copilotkit } = useCopilotKit();
292
+ const extraDeps = deps ?? EMPTY_DEPS;
293
+ useEffect(() => {
294
+ const name = tool.name;
295
+ if (copilotkit.getTool({
296
+ toolName: name,
297
+ agentId: tool.agentId
298
+ })) {
299
+ console.warn(`Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`);
300
+ copilotkit.removeTool(name, tool.agentId);
301
+ }
302
+ copilotkit.addTool(tool);
303
+ if (tool.render) copilotkit.addHookRenderToolCall({
304
+ name,
305
+ args: tool.parameters,
306
+ agentId: tool.agentId,
307
+ render: tool.render
308
+ });
309
+ return () => {
310
+ copilotkit.removeTool(name, tool.agentId);
311
+ };
312
+ }, [
313
+ tool.name,
314
+ tool.available,
315
+ copilotkit,
316
+ JSON.stringify(extraDeps)
317
+ ]);
318
+ }
319
+
320
+ //#endregion
321
+ //#region src/v2/hooks/use-component.tsx
322
+ /**
323
+ * Registers a React component as a frontend tool renderer in chat.
324
+ *
325
+ * This hook is a convenience wrapper around `useFrontendTool` that:
326
+ * - builds a model-facing tool description,
327
+ * - forwards optional schema parameters (any Standard Schema V1 compatible library),
328
+ * - renders your component with tool call parameters.
329
+ *
330
+ * Use this when you want to display a typed visual component for a tool call
331
+ * without manually wiring a full frontend tool object.
332
+ *
333
+ * When `parameters` is provided, render props are inferred from the schema.
334
+ * When omitted, the render component may accept any props.
335
+ *
336
+ * @typeParam TSchema - Schema describing tool parameters, or `undefined` when no schema is given.
337
+ * @param config - Tool registration config.
338
+ * @param deps - Optional dependencies to refresh registration (same semantics as `useEffect`).
339
+ *
340
+ * @example
341
+ * ```tsx
342
+ * // Without parameters — render accepts any props
343
+ * useComponent({
344
+ * name: "showGreeting",
345
+ * render: ({ message }: { message: string }) => <div>{message}</div>,
346
+ * });
347
+ * ```
348
+ *
349
+ * @example
350
+ * ```tsx
351
+ * // With parameters — render props inferred from schema
352
+ * useComponent({
353
+ * name: "showWeatherCard",
354
+ * parameters: z.object({ city: z.string() }),
355
+ * render: ({ city }) => <div>{city}</div>,
356
+ * });
357
+ * ```
358
+ *
359
+ * @example
360
+ * ```tsx
361
+ * useComponent(
362
+ * {
363
+ * name: "renderProfile",
364
+ * parameters: z.object({ userId: z.string() }),
365
+ * render: ProfileCard,
366
+ * agentId: "support-agent",
367
+ * },
368
+ * [selectedAgentId],
369
+ * );
370
+ * ```
371
+ */
372
+ function useComponent(config, deps) {
373
+ const prefix = `Use this tool to display the "${config.name}" component in the chat. This tool renders a visual UI component for the user.`;
374
+ const fullDescription = config.description ? `${prefix}\n\n${config.description}` : prefix;
375
+ useFrontendTool({
376
+ name: config.name,
377
+ description: fullDescription,
378
+ parameters: config.parameters,
379
+ render: ({ args }) => {
380
+ const Component = config.render;
381
+ return /* @__PURE__ */ jsx(Component, { ...args });
382
+ },
383
+ agentId: config.agentId
384
+ }, deps);
385
+ }
386
+
387
+ //#endregion
388
+ //#region src/v2/hooks/use-human-in-the-loop.tsx
389
+ function useHumanInTheLoop(tool, deps) {
390
+ const { copilotkit } = useCopilotKit();
391
+ const resolvePromiseRef = useRef(null);
392
+ const respond = useCallback(async (result) => {
393
+ if (resolvePromiseRef.current) {
394
+ resolvePromiseRef.current(result);
395
+ resolvePromiseRef.current = null;
396
+ }
397
+ }, []);
398
+ const handler = useCallback(async () => {
399
+ return new Promise((resolve) => {
400
+ resolvePromiseRef.current = resolve;
401
+ });
402
+ }, []);
403
+ const RenderComponent = useCallback((props) => {
404
+ const ToolComponent = tool.render;
405
+ if (props.status === "inProgress") {
406
+ const enhancedProps = {
407
+ ...props,
408
+ name: tool.name,
409
+ description: tool.description || "",
410
+ respond: void 0
411
+ };
412
+ return React.createElement(ToolComponent, enhancedProps);
413
+ } else if (props.status === "executing") {
414
+ const enhancedProps = {
415
+ ...props,
416
+ name: tool.name,
417
+ description: tool.description || "",
418
+ respond
419
+ };
420
+ return React.createElement(ToolComponent, enhancedProps);
421
+ } else if (props.status === "complete") {
422
+ const enhancedProps = {
423
+ ...props,
424
+ name: tool.name,
425
+ description: tool.description || "",
426
+ respond: void 0
427
+ };
428
+ return React.createElement(ToolComponent, enhancedProps);
429
+ }
430
+ return React.createElement(ToolComponent, props);
431
+ }, [
432
+ tool.render,
433
+ tool.name,
434
+ tool.description,
435
+ respond
436
+ ]);
437
+ useFrontendTool({
438
+ ...tool,
439
+ handler,
440
+ render: RenderComponent
441
+ }, deps);
442
+ useEffect(() => {
443
+ return () => {
444
+ copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
445
+ };
446
+ }, [
447
+ copilotkit,
448
+ tool.name,
449
+ tool.agentId
450
+ ]);
451
+ }
452
+
453
+ //#endregion
454
+ //#region src/v2/hooks/use-interrupt.tsx
455
+ const INTERRUPT_EVENT_NAME = "on_interrupt";
456
+ function isPromiseLike(value) {
457
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
458
+ }
459
+ /**
460
+ * Handles agent interrupts (`on_interrupt`) with optional filtering, preprocessing, and resume behavior.
461
+ *
462
+ * The hook listens to custom events on the active agent, stores interrupt payloads per run,
463
+ * and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
464
+ * execution with user-provided data.
465
+ *
466
+ * - `renderInChat: true` (default): the element is published into `<CopilotChat>` and this hook returns `void`.
467
+ * - `renderInChat: false`: the hook returns the interrupt element so you can place it anywhere in your component tree.
468
+ *
469
+ * `event.value` is typed as `any` since the interrupt payload shape depends on your agent.
470
+ * Type-narrow it in your callbacks (e.g. `handler`, `enabled`, `render`) as needed.
471
+ *
472
+ * @typeParam TResult - Inferred from `handler` return type. Exposed as `result` in `render`.
473
+ * @param config - Interrupt configuration (renderer, optional handler/filter, and render mode).
474
+ * @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
475
+ * Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
476
+ * either the handler's resolved return value or `null` (including when no handler is provided,
477
+ * when filtering skips the interrupt, or when handler execution fails).
478
+ *
479
+ * @example
480
+ * ```tsx
481
+ * import { useInterrupt } from "@copilotkit/react-core/v2";
482
+ *
483
+ * function InterruptUI() {
484
+ * useInterrupt({
485
+ * render: ({ event, resolve }) => (
486
+ * <div>
487
+ * <p>{event.value.question}</p>
488
+ * <button onClick={() => resolve({ approved: true })}>Approve</button>
489
+ * <button onClick={() => resolve({ approved: false })}>Reject</button>
490
+ * </div>
491
+ * ),
492
+ * });
493
+ *
494
+ * return null;
495
+ * }
496
+ * ```
497
+ *
498
+ * @example
499
+ * ```tsx
500
+ * import { useInterrupt } from "@copilotkit/react-core/v2";
501
+ *
502
+ * function CustomPanel() {
503
+ * const interruptElement = useInterrupt({
504
+ * renderInChat: false,
505
+ * enabled: (event) => event.value.startsWith("approval:"),
506
+ * handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
507
+ * render: ({ event, result, resolve }) => (
508
+ * <aside>
509
+ * <strong>{result?.label ?? ""}</strong>
510
+ * <button onClick={() => resolve({ value: event.value })}>Continue</button>
511
+ * </aside>
512
+ * ),
513
+ * });
514
+ *
515
+ * return <>{interruptElement}</>;
516
+ * }
517
+ * ```
518
+ */
519
+ function useInterrupt(config) {
520
+ const { copilotkit } = useCopilotKit();
521
+ const { agent } = useAgent({ agentId: config.agentId });
522
+ const [pendingEvent, setPendingEvent] = useState(null);
523
+ const pendingEventRef = useRef(pendingEvent);
524
+ pendingEventRef.current = pendingEvent;
525
+ const [handlerResult, setHandlerResult] = useState(null);
526
+ useEffect(() => {
527
+ let localInterrupt = null;
528
+ const subscription = agent.subscribe({
529
+ onCustomEvent: ({ event }) => {
530
+ if (event.name === INTERRUPT_EVENT_NAME) localInterrupt = {
531
+ name: event.name,
532
+ value: event.value
533
+ };
534
+ },
535
+ onRunStartedEvent: () => {
536
+ localInterrupt = null;
537
+ setPendingEvent(null);
538
+ },
539
+ onRunFinalized: () => {
540
+ if (localInterrupt) {
541
+ setPendingEvent(localInterrupt);
542
+ localInterrupt = null;
543
+ }
544
+ },
545
+ onRunFailed: () => {
546
+ localInterrupt = null;
547
+ }
548
+ });
549
+ return () => subscription.unsubscribe();
550
+ }, [agent]);
551
+ const resolve = useCallback((response) => {
552
+ setPendingEvent(null);
553
+ copilotkit.runAgent({
554
+ agent,
555
+ forwardedProps: { command: {
556
+ resume: response,
557
+ interruptEvent: pendingEventRef.current?.value
558
+ } }
559
+ });
560
+ }, [agent, copilotkit]);
561
+ useEffect(() => {
562
+ if (!pendingEvent) {
563
+ setHandlerResult(null);
564
+ return;
565
+ }
566
+ if (config.enabled && !config.enabled(pendingEvent)) {
567
+ setHandlerResult(null);
568
+ return;
569
+ }
570
+ const handler = config.handler;
571
+ if (!handler) {
572
+ setHandlerResult(null);
573
+ return;
574
+ }
575
+ let cancelled = false;
576
+ const maybePromise = handler({
577
+ event: pendingEvent,
578
+ resolve
579
+ });
580
+ if (isPromiseLike(maybePromise)) Promise.resolve(maybePromise).then((resolved) => {
581
+ if (!cancelled) setHandlerResult(resolved);
582
+ }).catch(() => {
583
+ if (!cancelled) setHandlerResult(null);
584
+ });
585
+ else setHandlerResult(maybePromise);
586
+ return () => {
587
+ cancelled = true;
588
+ };
589
+ }, [
590
+ pendingEvent,
591
+ config.enabled,
592
+ config.handler,
593
+ resolve
594
+ ]);
595
+ const element = useMemo(() => {
596
+ if (!pendingEvent) return null;
597
+ if (config.enabled && !config.enabled(pendingEvent)) return null;
598
+ return config.render({
599
+ event: pendingEvent,
600
+ result: handlerResult,
601
+ resolve
602
+ });
603
+ }, [
604
+ pendingEvent,
605
+ handlerResult,
606
+ config.enabled,
607
+ config.render,
608
+ resolve
609
+ ]);
610
+ useEffect(() => {
611
+ if (config.renderInChat === false) return;
612
+ copilotkit.setInterruptElement(element);
613
+ return () => copilotkit.setInterruptElement(null);
614
+ }, [
615
+ element,
616
+ config.renderInChat,
617
+ copilotkit
618
+ ]);
619
+ if (config.renderInChat === false) return element;
620
+ }
621
+
622
+ //#endregion
623
+ //#region src/v2/hooks/use-suggestions.tsx
624
+ function useSuggestions({ agentId } = {}) {
625
+ const { copilotkit } = useCopilotKit();
626
+ const config = useCopilotChatConfiguration();
627
+ const resolvedAgentId = useMemo(() => agentId ?? config?.agentId ?? DEFAULT_AGENT_ID, [agentId, config?.agentId]);
628
+ const [suggestions, setSuggestions] = useState(() => {
629
+ return copilotkit.getSuggestions(resolvedAgentId).suggestions;
630
+ });
631
+ const [isLoading, setIsLoading] = useState(() => {
632
+ return copilotkit.getSuggestions(resolvedAgentId).isLoading;
633
+ });
634
+ useEffect(() => {
635
+ const result = copilotkit.getSuggestions(resolvedAgentId);
636
+ setSuggestions(result.suggestions);
637
+ setIsLoading(result.isLoading);
638
+ }, [copilotkit, resolvedAgentId]);
639
+ useEffect(() => {
640
+ const subscription = copilotkit.subscribe({
641
+ onSuggestionsChanged: ({ agentId: changedAgentId, suggestions }) => {
642
+ if (changedAgentId !== resolvedAgentId) return;
643
+ setSuggestions(suggestions);
644
+ },
645
+ onSuggestionsStartedLoading: ({ agentId: changedAgentId }) => {
646
+ if (changedAgentId !== resolvedAgentId) return;
647
+ setIsLoading(true);
648
+ },
649
+ onSuggestionsFinishedLoading: ({ agentId: changedAgentId }) => {
650
+ if (changedAgentId !== resolvedAgentId) return;
651
+ setIsLoading(false);
652
+ },
653
+ onSuggestionsConfigChanged: () => {
654
+ const result = copilotkit.getSuggestions(resolvedAgentId);
655
+ setSuggestions(result.suggestions);
656
+ setIsLoading(result.isLoading);
657
+ }
658
+ });
659
+ return () => {
660
+ subscription.unsubscribe();
661
+ };
662
+ }, [copilotkit, resolvedAgentId]);
663
+ return {
664
+ suggestions,
665
+ reloadSuggestions: useCallback(() => {
666
+ copilotkit.reloadSuggestions(resolvedAgentId);
667
+ }, [copilotkit, resolvedAgentId]),
668
+ clearSuggestions: useCallback(() => {
669
+ copilotkit.clearSuggestions(resolvedAgentId);
670
+ }, [copilotkit, resolvedAgentId]),
671
+ isLoading
672
+ };
673
+ }
674
+
675
+ //#endregion
676
+ //#region src/v2/hooks/use-configure-suggestions.tsx
677
+ function useConfigureSuggestions(config, deps) {
678
+ const { copilotkit } = useCopilotKit();
679
+ const chatConfig = useCopilotChatConfiguration();
680
+ const extraDeps = deps ?? [];
681
+ const resolvedConsumerAgentId = useMemo(() => chatConfig?.agentId ?? DEFAULT_AGENT_ID, [chatConfig?.agentId]);
682
+ const rawConsumerAgentId = useMemo(() => config ? config.consumerAgentId : void 0, [config]);
683
+ const normalizationCacheRef = useRef({
684
+ serialized: null,
685
+ config: null
686
+ });
687
+ const { normalizedConfig, serializedConfig } = useMemo(() => {
688
+ if (!config) {
689
+ normalizationCacheRef.current = {
690
+ serialized: null,
691
+ config: null
692
+ };
693
+ return {
694
+ normalizedConfig: null,
695
+ serializedConfig: null
696
+ };
697
+ }
698
+ if (config.available === "disabled") {
699
+ normalizationCacheRef.current = {
700
+ serialized: null,
701
+ config: null
702
+ };
703
+ return {
704
+ normalizedConfig: null,
705
+ serializedConfig: null
706
+ };
707
+ }
708
+ let built;
709
+ if (isDynamicConfig(config)) built = { ...config };
710
+ else {
711
+ const normalizedSuggestions = normalizeStaticSuggestions(config.suggestions);
712
+ built = {
713
+ ...config,
714
+ suggestions: normalizedSuggestions
715
+ };
716
+ }
717
+ const serialized = JSON.stringify(built);
718
+ const cache = normalizationCacheRef.current;
719
+ if (cache.serialized === serialized && cache.config) return {
720
+ normalizedConfig: cache.config,
721
+ serializedConfig: serialized
722
+ };
723
+ normalizationCacheRef.current = {
724
+ serialized,
725
+ config: built
726
+ };
727
+ return {
728
+ normalizedConfig: built,
729
+ serializedConfig: serialized
730
+ };
731
+ }, [
732
+ config,
733
+ resolvedConsumerAgentId,
734
+ ...extraDeps
735
+ ]);
736
+ const latestConfigRef = useRef(null);
737
+ latestConfigRef.current = normalizedConfig;
738
+ const previousSerializedConfigRef = useRef(null);
739
+ const targetAgentId = useMemo(() => {
740
+ if (!normalizedConfig) return resolvedConsumerAgentId;
741
+ const consumer = normalizedConfig.consumerAgentId;
742
+ if (!consumer || consumer === "*") return resolvedConsumerAgentId;
743
+ return consumer;
744
+ }, [normalizedConfig, resolvedConsumerAgentId]);
745
+ const isGlobalConfig = rawConsumerAgentId === void 0 || rawConsumerAgentId === "*";
746
+ const isDynamicConfigType = useMemo(() => !!normalizedConfig && "instructions" in normalizedConfig, [normalizedConfig]);
747
+ const requestReload = useCallback(() => {
748
+ if (!normalizedConfig) return;
749
+ if (isGlobalConfig) {
750
+ const seen = /* @__PURE__ */ new Set();
751
+ const agents = Object.values(copilotkit.agents ?? {});
752
+ for (const entry of agents) {
753
+ const agentId = entry.agentId;
754
+ if (!agentId) continue;
755
+ seen.add(agentId);
756
+ if (!entry.isRunning) copilotkit.reloadSuggestions(agentId);
757
+ }
758
+ if (targetAgentId && !seen.has(targetAgentId)) copilotkit.reloadSuggestions(targetAgentId);
759
+ return;
760
+ }
761
+ if (!targetAgentId) return;
762
+ copilotkit.reloadSuggestions(targetAgentId);
763
+ }, [
764
+ copilotkit,
765
+ isGlobalConfig,
766
+ normalizedConfig,
767
+ targetAgentId
768
+ ]);
769
+ useEffect(() => {
770
+ if (!serializedConfig || !latestConfigRef.current) return;
771
+ const id = copilotkit.addSuggestionsConfig(latestConfigRef.current);
772
+ requestReload();
773
+ return () => {
774
+ copilotkit.removeSuggestionsConfig(id);
775
+ };
776
+ }, [
777
+ copilotkit,
778
+ serializedConfig,
779
+ requestReload
780
+ ]);
781
+ useEffect(() => {
782
+ if (!normalizedConfig) {
783
+ previousSerializedConfigRef.current = null;
784
+ return;
785
+ }
786
+ if (serializedConfig && previousSerializedConfigRef.current === serializedConfig) return;
787
+ if (serializedConfig) previousSerializedConfigRef.current = serializedConfig;
788
+ requestReload();
789
+ }, [
790
+ normalizedConfig,
791
+ requestReload,
792
+ serializedConfig
793
+ ]);
794
+ useEffect(() => {
795
+ if (!normalizedConfig || extraDeps.length === 0) return;
796
+ requestReload();
797
+ }, [
798
+ extraDeps.length,
799
+ normalizedConfig,
800
+ requestReload,
801
+ ...extraDeps
802
+ ]);
803
+ useEffect(() => {
804
+ if (!normalizedConfig || !isDynamicConfigType) return;
805
+ if (!targetAgentId) return;
806
+ if (!!copilotkit.getAgent(targetAgentId)) return;
807
+ const subscription = copilotkit.subscribe({ onAgentsChanged: () => {
808
+ if (copilotkit.getAgent(targetAgentId)) {
809
+ requestReload();
810
+ subscription.unsubscribe();
811
+ }
812
+ } });
813
+ return () => {
814
+ subscription.unsubscribe();
815
+ };
816
+ }, [
817
+ copilotkit,
818
+ normalizedConfig,
819
+ isDynamicConfigType,
820
+ targetAgentId,
821
+ requestReload
822
+ ]);
823
+ }
824
+ function isDynamicConfig(config) {
825
+ return "instructions" in config;
826
+ }
827
+ function normalizeStaticSuggestions(suggestions) {
828
+ return suggestions.map((suggestion) => ({
829
+ ...suggestion,
830
+ isLoading: suggestion.isLoading ?? false
831
+ }));
832
+ }
833
+
834
+ //#endregion
835
+ //#region src/v2/hooks/use-agent-context.tsx
836
+ function useAgentContext(context) {
837
+ const { description, value } = context;
838
+ const { copilotkit } = useCopilotKit();
839
+ const stringValue = useMemo(() => {
840
+ if (typeof value === "string") return value;
841
+ return JSON.stringify(value);
842
+ }, [value]);
843
+ useLayoutEffect(() => {
844
+ if (!copilotkit) return;
845
+ const id = copilotkit.addContext({
846
+ description,
847
+ value: stringValue
848
+ });
849
+ return () => {
850
+ copilotkit.removeContext(id);
851
+ };
852
+ }, [
853
+ description,
854
+ stringValue,
855
+ copilotkit
856
+ ]);
857
+ }
858
+
859
+ //#endregion
860
+ //#region src/v2/hooks/use-threads.tsx
861
+ function useThreadStoreSelector(store, selector) {
862
+ return useSyncExternalStore(useCallback((onStoreChange) => {
863
+ const subscription = store.select(selector).subscribe(onStoreChange);
864
+ return () => subscription.unsubscribe();
865
+ }, [store, selector]), () => selector(store.getState()));
866
+ }
867
+ /**
868
+ * React hook for listing and managing Intelligence platform threads.
869
+ *
870
+ * On mount the hook fetches the thread list for the runtime-authenticated user
871
+ * and the given `agentId`. When the Intelligence platform exposes a WebSocket
872
+ * URL, it also opens a realtime subscription so the `threads` array stays
873
+ * current without polling — thread creates, renames, archives, and deletes
874
+ * from any client are reflected immediately.
875
+ *
876
+ * Mutation methods (`renameThread`, `archiveThread`, `deleteThread`) return
877
+ * promises that resolve once the platform confirms the operation and reject
878
+ * with an `Error` on failure.
879
+ *
880
+ * @param input - Agent identifier and optional list controls.
881
+ * @returns Thread list state and stable mutation callbacks.
882
+ *
883
+ * @example
884
+ * ```tsx
885
+ * import { useThreads } from "@copilotkit/react-core";
886
+ *
887
+ * function ThreadList() {
888
+ * const { threads, isLoading, renameThread, deleteThread } = useThreads({
889
+ * agentId: "agent-1",
890
+ * });
891
+ *
892
+ * if (isLoading) return <p>Loading…</p>;
893
+ *
894
+ * return (
895
+ * <ul>
896
+ * {threads.map((t) => (
897
+ * <li key={t.id}>
898
+ * {t.name ?? "Untitled"}
899
+ * <button onClick={() => renameThread(t.id, "New name")}>Rename</button>
900
+ * <button onClick={() => deleteThread(t.id)}>Delete</button>
901
+ * </li>
902
+ * ))}
903
+ * </ul>
904
+ * );
905
+ * }
906
+ * ```
907
+ */
908
+ function useThreads({ agentId, includeArchived, limit }) {
909
+ const { copilotkit } = useCopilotKit();
910
+ const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
911
+ const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
912
+ const threads = useMemo(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt, lastRunAt }) => ({
913
+ id,
914
+ agentId,
915
+ name,
916
+ archived,
917
+ createdAt,
918
+ updatedAt,
919
+ ...lastRunAt !== void 0 ? { lastRunAt } : {}
920
+ })), [coreThreads]);
921
+ const storeIsLoading = useThreadStoreSelector(store, ɵselectThreadsIsLoading);
922
+ const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
923
+ const hasMoreThreads = useThreadStoreSelector(store, ɵselectHasNextPage);
924
+ const isFetchingMoreThreads = useThreadStoreSelector(store, ɵselectIsFetchingNextPage);
925
+ const headersKey = useMemo(() => {
926
+ return JSON.stringify(Object.entries(copilotkit.headers ?? {}).sort(([left], [right]) => left.localeCompare(right)));
927
+ }, [copilotkit.headers]);
928
+ const runtimeError = useMemo(() => {
929
+ if (copilotkit.runtimeUrl) return null;
930
+ return /* @__PURE__ */ new Error("Runtime URL is not configured");
931
+ }, [copilotkit.runtimeUrl]);
932
+ const [hasDispatchedContext, setHasDispatchedContext] = useState(false);
933
+ const preConnectLoading = !!copilotkit.runtimeUrl && !hasDispatchedContext;
934
+ const isLoading = runtimeError ? false : preConnectLoading || storeIsLoading;
935
+ const error = runtimeError ?? storeError;
936
+ useEffect(() => {
937
+ store.start();
938
+ return () => {
939
+ store.stop();
940
+ };
941
+ }, [store]);
942
+ const runtimeStatus = copilotkit.runtimeConnectionStatus;
943
+ useEffect(() => {
944
+ copilotkit.registerThreadStore(agentId, store);
945
+ return () => {
946
+ copilotkit.unregisterThreadStore(agentId);
947
+ };
948
+ }, [
949
+ copilotkit,
950
+ agentId,
951
+ store
952
+ ]);
953
+ useEffect(() => {
954
+ if (!copilotkit.runtimeUrl) {
955
+ store.setContext(null);
956
+ return;
957
+ }
958
+ if (runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) return;
959
+ const context = {
960
+ runtimeUrl: copilotkit.runtimeUrl,
961
+ headers: { ...copilotkit.headers },
962
+ wsUrl: copilotkit.intelligence?.wsUrl,
963
+ agentId,
964
+ includeArchived,
965
+ limit
966
+ };
967
+ store.setContext(context);
968
+ setHasDispatchedContext(true);
969
+ }, [
970
+ store,
971
+ copilotkit.runtimeUrl,
972
+ runtimeStatus,
973
+ headersKey,
974
+ copilotkit.intelligence?.wsUrl,
975
+ agentId,
976
+ includeArchived,
977
+ limit
978
+ ]);
979
+ const renameThread = useCallback((threadId, name) => store.renameThread(threadId, name), [store]);
980
+ const archiveThread = useCallback((threadId) => store.archiveThread(threadId), [store]);
981
+ const deleteThread = useCallback((threadId) => store.deleteThread(threadId), [store]);
982
+ return {
983
+ threads,
984
+ isLoading,
985
+ error,
986
+ hasMoreThreads,
987
+ isFetchingMoreThreads,
988
+ fetchMoreThreads: useCallback(() => store.fetchNextPage(), [store]),
989
+ renameThread,
990
+ archiveThread,
991
+ deleteThread
992
+ };
993
+ }
994
+
995
+ //#endregion
996
+ export { CopilotChatConfigurationProvider, CopilotChatDefaultLabels, CopilotKitCoreReact, useAgent, useAgentContext, useComponent, useConfigureSuggestions, useCopilotChatConfiguration, useFrontendTool, useHumanInTheLoop, useInterrupt, useSuggestions, useThreads };
997
+ //# sourceMappingURL=headless.mjs.map