@copilotkit/react-core 1.61.1 → 1.62.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-UY-H6Kx7.mjs → copilotkit-BtRkFsNR.mjs} +1227 -599
  2. package/dist/copilotkit-BtRkFsNR.mjs.map +1 -0
  3. package/dist/{copilotkit-BCJDP8qd.cjs → copilotkit-CdpDZi3i.cjs} +1219 -585
  4. package/dist/copilotkit-CdpDZi3i.cjs.map +1 -0
  5. package/dist/{copilotkit-CEdu_aie.d.cts → copilotkit-Cga6AHO0.d.cts} +497 -209
  6. package/dist/copilotkit-Cga6AHO0.d.cts.map +1 -0
  7. package/dist/{copilotkit-M1FiciGd.d.mts → copilotkit-DnLpJ2Cl.d.mts} +497 -209
  8. package/dist/copilotkit-DnLpJ2Cl.d.mts.map +1 -0
  9. package/dist/index.cjs +1 -1
  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 +1 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +360 -270
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/v2/context.d.cts +5 -1
  20. package/dist/v2/context.d.cts.map +1 -1
  21. package/dist/v2/context.d.mts +5 -1
  22. package/dist/v2/context.d.mts.map +1 -1
  23. package/dist/v2/headless.cjs +323 -104
  24. package/dist/v2/headless.cjs.map +1 -1
  25. package/dist/v2/headless.d.cts +187 -69
  26. package/dist/v2/headless.d.cts.map +1 -1
  27. package/dist/v2/headless.d.mts +187 -69
  28. package/dist/v2/headless.d.mts.map +1 -1
  29. package/dist/v2/headless.mjs +325 -106
  30. package/dist/v2/headless.mjs.map +1 -1
  31. package/dist/v2/index.cjs +2 -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 +1112 -484
  37. package/dist/v2/index.umd.js.map +1 -1
  38. package/package.json +8 -6
  39. package/dist/copilotkit-BCJDP8qd.cjs.map +0 -1
  40. package/dist/copilotkit-CEdu_aie.d.cts.map +0 -1
  41. package/dist/copilotkit-M1FiciGd.d.mts.map +0 -1
  42. package/dist/copilotkit-UY-H6Kx7.mjs.map +0 -1
package/dist/index.umd.js CHANGED
@@ -144,6 +144,25 @@ react_markdown = __toESM(react_markdown);
144
144
  modalHeaderTitle: "CopilotKit Chat",
145
145
  welcomeMessageText: "How can I help you today?"
146
146
  };
147
+ /**
148
+ * Mobile breakpoint below which the chat modal and the thread-list drawer are
149
+ * mutually exclusive. At or above this width both surfaces may coexist. This
150
+ * mirrors the `(max-width: 767px)` / `(min-width: 768px)` split already used by
151
+ * CopilotChatInput and CopilotSidebarView.
152
+ */
153
+ const MOBILE_MAX_WIDTH_PX = 767;
154
+ /**
155
+ * Reports whether the current viewport is in the mobile range (`<768px`), where
156
+ * the chat modal and drawer must not be open simultaneously. SSR-safe and
157
+ * defensive against environments without `matchMedia` (treated as desktop, so
158
+ * no mutual-exclusion constraint is applied).
159
+ *
160
+ * @returns `true` when the viewport is mobile-width, `false` otherwise.
161
+ */
162
+ function isMobileViewport() {
163
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
164
+ return window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH_PX}px)`).matches;
165
+ }
147
166
  const CopilotChatConfiguration = (0, react.createContext)(null);
148
167
  const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
149
168
  const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
@@ -154,12 +173,22 @@ react_markdown = __toESM(react_markdown);
154
173
  ...stableLabels
155
174
  }), [stableLabels, parentConfig?.labels]);
156
175
  const resolvedAgentId = agentId ?? parentConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
176
+ const threadIdPropIsAuthoritative = threadId !== void 0 && hasExplicitThreadId !== false;
177
+ const isThreadIdControlled = threadIdPropIsAuthoritative;
178
+ const [activeThreadOverride, setActiveThreadOverride] = (0, react.useState)(null);
157
179
  const resolvedThreadId = (0, react.useMemo)(() => {
158
- if (threadId) return threadId;
180
+ if (threadIdPropIsAuthoritative) return threadId;
181
+ if (activeThreadOverride) return activeThreadOverride.threadId;
159
182
  if (parentConfig?.threadId) return parentConfig.threadId;
183
+ if (threadId) return threadId;
160
184
  return (0, _copilotkit_shared.randomUUID)();
161
- }, [threadId, parentConfig?.threadId]);
162
- const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
185
+ }, [
186
+ threadIdPropIsAuthoritative,
187
+ threadId,
188
+ parentConfig?.threadId,
189
+ activeThreadOverride
190
+ ]);
191
+ const resolvedHasExplicitThreadId = (threadIdPropIsAuthoritative ? true : activeThreadOverride?.explicit ?? hasExplicitThreadId ?? false) || !!parentConfig?.hasExplicitThreadId;
163
192
  const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen ?? true);
164
193
  const hasExplicitDefault = isModalDefaultOpen !== void 0;
165
194
  const setAndSync = (0, react.useCallback)((open) => {
@@ -178,20 +207,113 @@ react_markdown = __toESM(react_markdown);
178
207
  }, [parentConfig?.isModalOpen, hasExplicitDefault]);
179
208
  const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
180
209
  const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
210
+ const [ownDrawerOpen, setOwnDrawerOpen] = (0, react.useState)(false);
211
+ const [ownDrawerCount, setOwnDrawerCount] = (0, react.useState)(0);
212
+ const modalCloseRef = (0, react.useRef)(() => {});
213
+ modalCloseRef.current = resolvedSetModalOpen;
214
+ const registeredModalClosersRef = (0, react.useRef)([]);
215
+ const ownRegisterModalCloser = (0, react.useCallback)((closeModal) => {
216
+ registeredModalClosersRef.current.push(closeModal);
217
+ return () => {
218
+ registeredModalClosersRef.current = registeredModalClosersRef.current.filter((entry) => entry !== closeModal);
219
+ };
220
+ }, []);
221
+ const ownSetDrawerOpen = (0, react.useCallback)((open) => {
222
+ setOwnDrawerOpen(open);
223
+ if (open && isMobileViewport()) {
224
+ const registered = registeredModalClosersRef.current;
225
+ (registered.length > 0 ? registered[registered.length - 1] : modalCloseRef.current)(false);
226
+ }
227
+ }, []);
228
+ const ownRegisterDrawer = (0, react.useCallback)(() => {
229
+ setOwnDrawerCount((count) => count + 1);
230
+ return () => {
231
+ setOwnDrawerCount((count) => Math.max(0, count - 1));
232
+ };
233
+ }, []);
234
+ const resolvedDrawerOpen = parentConfig ? parentConfig.drawerOpen : ownDrawerOpen;
235
+ const resolvedSetDrawerOpen = parentConfig ? parentConfig.setDrawerOpen : ownSetDrawerOpen;
236
+ const resolvedDrawerRegistered = parentConfig ? parentConfig.drawerRegistered : ownDrawerCount > 0;
237
+ const resolvedRegisterDrawer = parentConfig ? parentConfig.registerDrawer : ownRegisterDrawer;
238
+ const resolvedRegisterModalCloser = parentConfig ? parentConfig.ɵregisterModalCloser : ownRegisterModalCloser;
239
+ (0, react.useEffect)(() => {
240
+ if (!hasExplicitDefault) return;
241
+ return resolvedRegisterModalCloser(resolvedSetModalOpen);
242
+ }, [
243
+ hasExplicitDefault,
244
+ resolvedRegisterModalCloser,
245
+ resolvedSetModalOpen
246
+ ]);
247
+ const isThreadIdControlledRef = (0, react.useRef)(isThreadIdControlled);
248
+ isThreadIdControlledRef.current = isThreadIdControlled;
249
+ const ownSetActiveThreadId = (0, react.useCallback)((id, options) => {
250
+ setActiveThreadOverride({
251
+ threadId: id,
252
+ explicit: options?.explicit ?? true
253
+ });
254
+ }, []);
255
+ const ownStartNewThread = (0, react.useCallback)(() => {
256
+ setActiveThreadOverride({
257
+ threadId: (0, _copilotkit_shared.randomUUID)(),
258
+ explicit: false
259
+ });
260
+ }, []);
261
+ const parentSetActiveThreadId = parentConfig?.setActiveThreadId;
262
+ const parentStartNewThread = parentConfig?.startNewThread;
263
+ const resolvedSetActiveThreadId = (0, react.useCallback)((id, options) => {
264
+ if (isThreadIdControlledRef.current) {
265
+ console.warn("[CopilotKit] Ignoring setActiveThreadId(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
266
+ return;
267
+ }
268
+ if (parentSetActiveThreadId) {
269
+ parentSetActiveThreadId(id, options);
270
+ return;
271
+ }
272
+ ownSetActiveThreadId(id, options);
273
+ }, [parentSetActiveThreadId, ownSetActiveThreadId]);
274
+ const resolvedStartNewThread = (0, react.useCallback)(() => {
275
+ if (isThreadIdControlledRef.current) {
276
+ console.warn("[CopilotKit] Ignoring startNewThread(): threadId is controlled via the `threadId` prop on CopilotChatConfigurationProvider.");
277
+ return;
278
+ }
279
+ if (parentStartNewThread) {
280
+ parentStartNewThread();
281
+ return;
282
+ }
283
+ ownStartNewThread();
284
+ }, [parentStartNewThread, ownStartNewThread]);
285
+ const setModalOpenWithDrawerExclusion = (0, react.useCallback)((open) => {
286
+ if (open && isMobileViewport()) resolvedSetDrawerOpen(false);
287
+ resolvedSetModalOpen(open);
288
+ }, [resolvedSetModalOpen, resolvedSetDrawerOpen]);
181
289
  const configurationValue = (0, react.useMemo)(() => ({
182
290
  labels: mergedLabels,
183
291
  agentId: resolvedAgentId,
184
292
  threadId: resolvedThreadId,
185
293
  hasExplicitThreadId: resolvedHasExplicitThreadId,
186
294
  isModalOpen: resolvedIsModalOpen,
187
- setModalOpen: resolvedSetModalOpen
295
+ setModalOpen: setModalOpenWithDrawerExclusion,
296
+ drawerOpen: resolvedDrawerOpen,
297
+ setDrawerOpen: resolvedSetDrawerOpen,
298
+ drawerRegistered: resolvedDrawerRegistered,
299
+ registerDrawer: resolvedRegisterDrawer,
300
+ ɵregisterModalCloser: resolvedRegisterModalCloser,
301
+ setActiveThreadId: resolvedSetActiveThreadId,
302
+ startNewThread: resolvedStartNewThread
188
303
  }), [
189
304
  mergedLabels,
190
305
  resolvedAgentId,
191
306
  resolvedThreadId,
192
307
  resolvedHasExplicitThreadId,
193
308
  resolvedIsModalOpen,
194
- resolvedSetModalOpen
309
+ setModalOpenWithDrawerExclusion,
310
+ resolvedDrawerOpen,
311
+ resolvedSetDrawerOpen,
312
+ resolvedDrawerRegistered,
313
+ resolvedRegisterDrawer,
314
+ resolvedRegisterModalCloser,
315
+ resolvedSetActiveThreadId,
316
+ resolvedStartNewThread
195
317
  ]);
196
318
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
197
319
  value: configurationValue,
@@ -327,149 +449,6 @@ react_markdown = __toESM(react_markdown);
327
449
  getLimit: () => null
328
450
  });
329
451
 
330
- //#endregion
331
- //#region src/v2/types/defineToolCallRenderer.ts
332
- function defineToolCallRenderer(def) {
333
- const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
334
- return {
335
- name: def.name,
336
- args: argsSchema,
337
- render: def.render,
338
- ...def.agentId ? { agentId: def.agentId } : {}
339
- };
340
- }
341
-
342
- //#endregion
343
- //#region src/v2/hooks/use-default-render-tool.tsx
344
- /**
345
- * Module-level dedup set so an unknown status value only emits a console
346
- * warning the FIRST time we encounter it. Otherwise a stuck/unmapped status
347
- * would log on every re-render (potentially many per second).
348
- */
349
- const warnedUnknownStatuses = /* @__PURE__ */ new Set();
350
- /**
351
- * Map a {@link ToolCallStatus} enum value to the documented string-union
352
- * status the {@link DefaultRenderProps} contract exposes. Unknown / future
353
- * enum members log a warning (once per distinct value) and fall back to
354
- * `"inProgress"`.
355
- */
356
- function mapToolCallStatus(status) {
357
- switch (status) {
358
- case _copilotkit_core.ToolCallStatus.Complete: return "complete";
359
- case _copilotkit_core.ToolCallStatus.Executing: return "executing";
360
- case _copilotkit_core.ToolCallStatus.InProgress: return "inProgress";
361
- default: {
362
- const key = String(status);
363
- if (!warnedUnknownStatuses.has(key)) {
364
- warnedUnknownStatuses.add(key);
365
- console.warn(`[CopilotKit] Unknown ToolCallStatus "${key}" in default tool-call renderer; falling back to "inProgress".`);
366
- }
367
- return "inProgress";
368
- }
369
- }
370
- }
371
- /**
372
- * Guarded JSON.stringify used inside the expanded `<pre>` blocks. A circular
373
- * reference would otherwise crash the entire React tree on render.
374
- */
375
- function safeStringifyForPre(value) {
376
- try {
377
- return JSON.stringify(value, null, 2);
378
- } catch (err) {
379
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for default renderer; falling back to String():", err);
380
- try {
381
- return String(value);
382
- } catch (innerErr) {
383
- console.warn("[CopilotKit] safeStringifyForPre: value could not be stringified:", innerErr);
384
- return "[unserializable]";
385
- }
386
- }
387
- }
388
- function DefaultToolCallRenderer({ name, toolCallId, parameters, status, result }) {
389
- const [isExpanded, setIsExpanded] = (0, react.useState)(false);
390
- const isActive = status === "inProgress" || status === "executing";
391
- const isComplete = status === "complete";
392
- const statusLabel = isActive ? "Running" : isComplete ? "Done" : status;
393
- const dotClassName = isActive ? "cpk:bg-amber-500" : isComplete ? "cpk:bg-emerald-500" : "cpk:bg-zinc-400";
394
- 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";
395
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
396
- "data-testid": "copilot-tool-render",
397
- "data-tool-name": name,
398
- "data-tool-call-id": toolCallId,
399
- "data-status": status,
400
- "data-args": safeStringifyForAttr(parameters),
401
- "data-result": safeStringifyForAttr(result),
402
- className: "cpk:mt-2 cpk:pb-2",
403
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
404
- className: "cpk:rounded-xl cpk:border cpk:border-zinc-200/60 cpk:bg-white/70 cpk:p-4 cpk:shadow-sm cpk:backdrop-blur cpk:dark:border-zinc-800/60 cpk:dark:bg-zinc-900/50",
405
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
406
- type: "button",
407
- "aria-expanded": isExpanded,
408
- onClick: () => setIsExpanded(!isExpanded),
409
- className: "cpk:flex cpk:w-full cpk:cursor-pointer cpk:select-none cpk:items-center cpk:justify-between cpk:gap-2.5 cpk:border-none cpk:bg-transparent cpk:p-0 cpk:m-0 cpk:text-left cpk:text-inherit",
410
- style: { font: "inherit" },
411
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
412
- className: "cpk:flex cpk:min-w-0 cpk:items-center cpk:gap-2",
413
- children: [
414
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
415
- className: `cpk:h-3.5 cpk:w-3.5 cpk:flex-shrink-0 cpk:text-zinc-500 cpk:transition-transform cpk:dark:text-zinc-400 ${isExpanded ? "cpk:rotate-90" : ""}`,
416
- fill: "none",
417
- viewBox: "0 0 24 24",
418
- strokeWidth: 2,
419
- stroke: "currentColor",
420
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
421
- strokeLinecap: "round",
422
- strokeLinejoin: "round",
423
- d: "M8.25 4.5l7.5 7.5-7.5 7.5"
424
- })
425
- }),
426
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `cpk:inline-block cpk:h-2 cpk:w-2 cpk:flex-shrink-0 cpk:rounded-full ${dotClassName}` }),
427
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
428
- "data-testid": "copilot-tool-render-name",
429
- className: "cpk:truncate cpk:text-[13px] cpk:font-semibold cpk:text-zinc-900 cpk:dark:text-zinc-100",
430
- children: name
431
- })
432
- ]
433
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
434
- "data-testid": "copilot-tool-render-status",
435
- className: `cpk:inline-flex cpk:flex-shrink-0 cpk:items-center cpk:rounded-full cpk:px-2 cpk:py-0.5 cpk:text-[11px] cpk:font-medium ${badgeClassName}`,
436
- children: statusLabel
437
- })]
438
- }), isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
439
- className: "cpk:mt-3 cpk:grid cpk:gap-3",
440
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
441
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
442
- children: "Arguments"
443
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
444
- className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
445
- children: safeStringifyForPre(parameters ?? {})
446
- })] }), result !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
447
- className: "cpk:text-[10px] cpk:uppercase cpk:text-zinc-500 cpk:dark:text-zinc-400",
448
- children: "Result"
449
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
450
- className: "cpk:mt-1.5 cpk:max-h-[200px] cpk:overflow-auto cpk:rounded-md cpk:bg-zinc-100 cpk:p-2.5 cpk:text-[11px] cpk:leading-relaxed cpk:text-zinc-800 cpk:whitespace-pre-wrap cpk:break-words cpk:dark:bg-zinc-800/60 cpk:dark:text-zinc-200",
451
- children: typeof result === "string" ? result : safeStringifyForPre(result)
452
- })] })]
453
- })]
454
- })
455
- });
456
- }
457
- function safeStringifyForAttr(value) {
458
- if (value === void 0 || value === null) return "";
459
- if (typeof value === "string") return value;
460
- try {
461
- return JSON.stringify(value);
462
- } catch (err) {
463
- console.warn("[CopilotKit] Failed to JSON.stringify tool-call payload for data-* attribute; falling back to String():", err);
464
- try {
465
- return String(value);
466
- } catch (innerErr) {
467
- console.warn("[CopilotKit] safeStringifyForAttr: value could not be stringified:", innerErr);
468
- return "";
469
- }
470
- }
471
- }
472
-
473
452
  //#endregion
474
453
  //#region src/v2/hooks/use-render-tool-call.tsx
475
454
  /**
@@ -524,10 +503,13 @@ react_markdown = __toESM(react_markdown);
524
503
  }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
525
504
  return (0, react.useCallback)(({ toolCall, toolMessage }) => {
526
505
  const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
506
+ const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
507
+ if (!renderConfig) return null;
508
+ const RenderComponent = renderConfig.render;
527
509
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallRenderer, {
528
510
  toolCall,
529
511
  toolMessage,
530
- RenderComponent: (exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*"))?.render ?? defaultToolCallRenderAdapter,
512
+ RenderComponent,
531
513
  isExecuting: executingToolCallIds.has(toolCall.id)
532
514
  }, toolCall.id);
533
515
  }, [
@@ -536,15 +518,6 @@ react_markdown = __toESM(react_markdown);
536
518
  agentId
537
519
  ]);
538
520
  }
539
- function defaultToolCallRenderAdapter(props) {
540
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultToolCallRenderer, {
541
- name: props.name,
542
- toolCallId: props.toolCallId,
543
- parameters: props.args,
544
- status: mapToolCallStatus(props.status),
545
- result: props.result
546
- });
547
- }
548
521
 
549
522
  //#endregion
550
523
  //#region src/v2/components/CopilotKitInspector.tsx
@@ -1997,7 +1970,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
1997
1970
  ...A2UILifecycleFields
1998
1971
  }).passthrough();
1999
1972
  function createA2UIMessageRenderer(options) {
2000
- const { theme, catalog, loadingComponent, recovery } = options;
1973
+ const { theme, catalog, loadingComponent, recovery, onAction } = options;
2001
1974
  const showAfterMs = recovery?.showAfterMs ?? 2e3;
2002
1975
  const showAfterAttempts = recovery?.showAfterAttempts ?? 2;
2003
1976
  const optionDebugExposure = recovery?.debugExposure ?? "collapsed";
@@ -2073,6 +2046,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2073
2046
  agent,
2074
2047
  copilotkit,
2075
2048
  catalog,
2049
+ onAction,
2076
2050
  onReady: markSurfaceReady
2077
2051
  }, surfaceId))
2078
2052
  });
@@ -2093,29 +2067,60 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2093
2067
  };
2094
2068
  }
2095
2069
  /**
2070
+ * Orchestrates a single A2UI user action: runs the optional `onAction`
2071
+ * interceptor first, then forwards to the agent unless the interceptor
2072
+ * suppressed it (returned `null`). Exported for unit testing; the wiring lives
2073
+ * in {@link ReactSurfaceHost}.
2074
+ */
2075
+ async function runA2UIAction({ message, agent, copilotkit, onAction }) {
2076
+ if (!agent) return;
2077
+ const action = message.userAction;
2078
+ const forward = async (forwardAction) => {
2079
+ const a2uiAction = forwardAction !== void 0 ? {
2080
+ ...message,
2081
+ userAction: forwardAction
2082
+ } : message;
2083
+ try {
2084
+ copilotkit.setProperties({
2085
+ ...copilotkit.properties,
2086
+ a2uiAction
2087
+ });
2088
+ await copilotkit.runAgent({ agent });
2089
+ } finally {
2090
+ if (copilotkit.properties) {
2091
+ const { a2uiAction: _omit, ...rest } = copilotkit.properties;
2092
+ copilotkit.setProperties(rest);
2093
+ }
2094
+ }
2095
+ };
2096
+ if (onAction && action) {
2097
+ const result = await onAction(action, forward);
2098
+ if (result === null) return;
2099
+ if (result) {
2100
+ await forward(result);
2101
+ return;
2102
+ }
2103
+ }
2104
+ await forward();
2105
+ }
2106
+ /**
2096
2107
  * Renders a single A2UI surface using the React renderer.
2097
2108
  * Wraps A2UIProvider + A2UIRenderer and bridges actions back to CopilotKit.
2098
2109
  */
2099
- function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onReady }) {
2110
+ function ReactSurfaceHost({ surfaceId, operations, theme, agent, copilotkit, catalog, onAction, onReady }) {
2100
2111
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2101
2112
  className: "cpk:flex cpk:w-full cpk:flex-none cpk:flex-col cpk:gap-4",
2102
2113
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_copilotkit_a2ui_renderer.A2UIProvider, {
2103
- onAction: (0, react.useCallback)(async (message) => {
2104
- if (!agent) return;
2105
- message.userAction;
2106
- try {
2107
- copilotkit.setProperties({
2108
- ...copilotkit.properties,
2109
- a2uiAction: message
2110
- });
2111
- await copilotkit.runAgent({ agent });
2112
- } finally {
2113
- if (copilotkit.properties) {
2114
- const { a2uiAction, ...rest } = copilotkit.properties;
2115
- copilotkit.setProperties(rest);
2116
- }
2117
- }
2118
- }, [agent, copilotkit]),
2114
+ onAction: (0, react.useCallback)((message) => runA2UIAction({
2115
+ message,
2116
+ agent,
2117
+ copilotkit,
2118
+ onAction
2119
+ }), [
2120
+ agent,
2121
+ copilotkit,
2122
+ onAction
2123
+ ]),
2119
2124
  theme,
2120
2125
  catalog,
2121
2126
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SurfaceMessageProcessor, {
@@ -2186,6 +2191,18 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2186
2191
  return operation?.createSurface?.surfaceId ?? operation?.updateComponents?.surfaceId ?? operation?.updateDataModel?.surfaceId ?? operation?.deleteSurface?.surfaceId ?? null;
2187
2192
  }
2188
2193
 
2194
+ //#endregion
2195
+ //#region src/v2/types/defineToolCallRenderer.ts
2196
+ function defineToolCallRenderer(def) {
2197
+ const argsSchema = def.name === "*" && !def.args ? zod.z.any() : def.args;
2198
+ return {
2199
+ name: def.name,
2200
+ args: argsSchema,
2201
+ render: def.render,
2202
+ ...def.agentId ? { agentId: def.agentId } : {}
2203
+ };
2204
+ }
2205
+
2189
2206
  //#endregion
2190
2207
  //#region src/v2/a2ui/A2UIToolCallRenderer.tsx
2191
2208
  /**
@@ -2280,6 +2297,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2280
2297
 
2281
2298
  //#endregion
2282
2299
  //#region src/v2/providers/CopilotKitProvider.tsx
2300
+ const zodToJsonSchemaAdapter = (schema, options) => {
2301
+ const refStrategy = options?.$refStrategy;
2302
+ return (0, zod_to_json_schema.zodToJsonSchema)(schema, refStrategy === "root" || refStrategy === "relative" || refStrategy === "none" || refStrategy === "seen" ? { $refStrategy: refStrategy } : {});
2303
+ };
2283
2304
  const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
2284
2305
  const COPILOT_CLOUD_CHAT_URL$1 = "https://api.cloud.copilotkit.ai/copilotkit/v1";
2285
2306
  const EMPTY_HEADERS = Object.freeze({});
@@ -2310,6 +2331,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2310
2331
  const [runtimeA2UIEnabled, setRuntimeA2UIEnabled] = (0, react.useState)(false);
2311
2332
  const [runtimeOpenGenUIEnabled, setRuntimeOpenGenUIEnabled] = (0, react.useState)(false);
2312
2333
  const openGenUIActive = runtimeOpenGenUIEnabled || !!openGenerativeUI;
2334
+ const a2uiCatalogProvided = !!a2ui?.catalog;
2335
+ const a2uiActive = runtimeA2UIEnabled || a2uiCatalogProvided;
2313
2336
  const [runtimeLicenseStatus, setRuntimeLicenseStatus] = (0, react.useState)(void 0);
2314
2337
  (0, react.useEffect)(() => {
2315
2338
  if (typeof window === "undefined") return;
@@ -2340,7 +2363,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2340
2363
  content: OpenGenerativeUIContentSchema,
2341
2364
  render: OpenGenerativeUIActivityRenderer
2342
2365
  });
2343
- if (runtimeA2UIEnabled) renderers.unshift(createA2UIMessageRenderer({
2366
+ if (a2uiActive) renderers.unshift(createA2UIMessageRenderer({
2344
2367
  theme: a2ui?.theme ?? _copilotkit_a2ui_renderer.viewerTheme,
2345
2368
  catalog: a2ui?.catalog,
2346
2369
  loadingComponent: a2ui?.loadingComponent,
@@ -2348,7 +2371,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2348
2371
  }));
2349
2372
  return renderers;
2350
2373
  }, [
2351
- runtimeA2UIEnabled,
2374
+ a2uiActive,
2352
2375
  openGenUIActive,
2353
2376
  a2ui
2354
2377
  ]);
@@ -2535,7 +2558,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2535
2558
  copilotkit.setRuntimeTransport(useSingleEndpoint === true ? "single" : useSingleEndpoint === false ? "rest" : "auto");
2536
2559
  copilotkit.setHeaders(mergedHeaders);
2537
2560
  copilotkit.setCredentials(credentials);
2538
- copilotkit.setProperties(properties);
2561
+ copilotkit.setProperties(a2uiCatalogProvided ? {
2562
+ ...properties,
2563
+ a2uiCatalogAvailable: true
2564
+ } : properties);
2539
2565
  copilotkit.setAgents__unsafe_dev_only(mergedAgents);
2540
2566
  copilotkit.setDebug(debug);
2541
2567
  }, [
@@ -2544,6 +2570,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2544
2570
  mergedHeaders,
2545
2571
  credentials,
2546
2572
  properties,
2573
+ a2uiCatalogProvided,
2547
2574
  mergedAgents,
2548
2575
  useSingleEndpoint,
2549
2576
  debug
@@ -2591,7 +2618,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2591
2618
  return JSON.stringify(sandboxFunctionsList.map((fn) => ({
2592
2619
  name: fn.name,
2593
2620
  description: fn.description,
2594
- parameters: (0, _copilotkit_shared.schemaToJsonSchema)(fn.parameters, { zodToJsonSchema: zod_to_json_schema.zodToJsonSchema })
2621
+ parameters: (0, _copilotkit_shared.schemaToJsonSchema)(fn.parameters, { zodToJsonSchema: zodToJsonSchemaAdapter })
2595
2622
  })));
2596
2623
  }, [sandboxFunctionsList]);
2597
2624
  (0, react.useLayoutEffect)(() => {
@@ -2612,7 +2639,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2612
2639
  copilotkit,
2613
2640
  executingToolCallIds
2614
2641
  }), [copilotkit, executingToolCallIds]);
2615
- const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(null), []);
2642
+ const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(runtimeLicenseStatus), [runtimeLicenseStatus]);
2616
2643
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SandboxFunctionsContext.Provider, {
2617
2644
  value: sandboxFunctionsList,
2618
2645
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitContext.Provider, {
@@ -2620,8 +2647,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2620
2647
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(LicenseContext.Provider, {
2621
2648
  value: licenseContextValue,
2622
2649
  children: [
2623
- runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuiltInToolCallRenderer, {}),
2624
- runtimeA2UIEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UICatalogContext, {
2650
+ a2uiActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UIBuiltInToolCallRenderer, {}),
2651
+ a2uiActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(A2UICatalogContext, {
2625
2652
  catalog: a2ui?.catalog,
2626
2653
  includeSchema: a2ui?.includeSchema
2627
2654
  }),
@@ -2838,7 +2865,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2838
2865
  if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
2839
2866
  const cached = provisionalAgentCache.current.get(agentId);
2840
2867
  if (cached) {
2841
- cached.headers = { ...copilotkit.headers };
2868
+ copilotkit.applyHeadersToAgent(cached);
2842
2869
  return cached;
2843
2870
  }
2844
2871
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
@@ -2847,14 +2874,14 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2847
2874
  transport: copilotkit.runtimeTransport,
2848
2875
  runtimeMode: "pending"
2849
2876
  });
2850
- provisional.headers = { ...copilotkit.headers };
2877
+ copilotkit.applyHeadersToAgent(provisional);
2851
2878
  provisionalAgentCache.current.set(agentId, provisional);
2852
2879
  return provisional;
2853
2880
  }
2854
2881
  if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
2855
2882
  const cached = provisionalAgentCache.current.get(agentId);
2856
2883
  if (cached) {
2857
- cached.headers = { ...copilotkit.headers };
2884
+ copilotkit.applyHeadersToAgent(cached);
2858
2885
  return cached;
2859
2886
  }
2860
2887
  const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
@@ -2863,7 +2890,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2863
2890
  transport: copilotkit.runtimeTransport,
2864
2891
  runtimeMode: "pending"
2865
2892
  });
2866
- provisional.headers = { ...copilotkit.headers };
2893
+ copilotkit.applyHeadersToAgent(provisional);
2867
2894
  provisionalAgentCache.current.set(agentId, provisional);
2868
2895
  return provisional;
2869
2896
  }
@@ -2914,7 +2941,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2914
2941
  updateFlags
2915
2942
  ]);
2916
2943
  (0, react.useEffect)(() => {
2917
- if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...copilotkit.headers };
2944
+ if (agent instanceof _ag_ui_client.HttpAgent) copilotkit.applyHeadersToAgent(agent);
2918
2945
  }, [agent, JSON.stringify(copilotkit.headers)]);
2919
2946
  const chatConfig = useCopilotChatConfiguration();
2920
2947
  const configThreadId = chatConfig?.threadId;
@@ -3148,114 +3175,165 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3148
3175
  function isPromiseLike(value) {
3149
3176
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
3150
3177
  }
3178
+ /** Derive the legacy-compatible `event` for any pending interrupt. */
3179
+ function toLegacyEvent(pending) {
3180
+ if (pending.kind === "legacy") return pending.event;
3181
+ return {
3182
+ name: INTERRUPT_EVENT_NAME,
3183
+ value: pending.interrupts[0]
3184
+ };
3185
+ }
3151
3186
  /**
3152
- * Handles agent interrupts (`on_interrupt`) with optional filtering, preprocessing, and resume behavior.
3187
+ * Handles agent interrupts with optional filtering, preprocessing, and resume behavior.
3153
3188
  *
3154
- * The hook listens to custom events on the active agent, stores interrupt payloads per run,
3155
- * and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
3156
- * execution with user-provided data.
3189
+ * Supports both the AG-UI standard interrupt flow (`RUN_FINISHED` with
3190
+ * `outcome.type === "interrupt"`) and the legacy custom-event flow
3191
+ * (`on_interrupt`). For standard interrupts, `render` receives `interrupt`
3192
+ * (the primary one) and `interrupts` (the full open set); call `resolve(payload)`
3193
+ * to resume or `cancel()` to cancel. Resuming addresses the targeted interrupt
3194
+ * and, once every open interrupt is addressed, submits a single spec `resume`
3195
+ * array via `copilotkit.runAgent`.
3157
3196
  *
3158
- * - `renderInChat: true` (default): the element is published into `<CopilotChat>` and this hook returns `void`.
3159
- * - `renderInChat: false`: the hook returns the interrupt element so you can place it anywhere in your component tree.
3160
- *
3161
- * `event.value` is typed as `any` since the interrupt payload shape depends on your agent.
3162
- * Type-narrow it in your callbacks (e.g. `handler`, `enabled`, `render`) as needed.
3163
- *
3164
- * @typeParam TResult - Inferred from `handler` return type. Exposed as `result` in `render`.
3165
- * @param config - Interrupt configuration (renderer, optional handler/filter, and render mode).
3166
- * @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
3167
- * Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
3168
- * either the handler's resolved return value or `null` (including when no handler is provided,
3169
- * when filtering skips the interrupt, or when handler execution fails).
3170
- *
3171
- * @example
3172
- * ```tsx
3173
- * import { useInterrupt } from "@copilotkit/react-core/v2";
3174
- *
3175
- * function InterruptUI() {
3176
- * useInterrupt({
3177
- * render: ({ event, resolve }) => (
3178
- * <div>
3179
- * <p>{event.value.question}</p>
3180
- * <button onClick={() => resolve({ approved: true })}>Approve</button>
3181
- * <button onClick={() => resolve({ approved: false })}>Reject</button>
3182
- * </div>
3183
- * ),
3184
- * });
3185
- *
3186
- * return null;
3187
- * }
3188
- * ```
3197
+ * - `renderInChat: true` (default): the element is published into `<CopilotChat>`; returns `void`.
3198
+ * - `renderInChat: false`: the hook returns the interrupt element for manual placement.
3189
3199
  *
3190
3200
  * @example
3191
3201
  * ```tsx
3192
- * import { useInterrupt } from "@copilotkit/react-core/v2";
3193
- *
3194
- * function CustomPanel() {
3195
- * const interruptElement = useInterrupt({
3196
- * renderInChat: false,
3197
- * enabled: (event) => event.value.startsWith("approval:"),
3198
- * handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
3199
- * render: ({ event, result, resolve }) => (
3200
- * <aside>
3201
- * <strong>{result?.label ?? ""}</strong>
3202
- * <button onClick={() => resolve({ value: event.value })}>Continue</button>
3203
- * </aside>
3204
- * ),
3205
- * });
3206
- *
3207
- * return <>{interruptElement}</>;
3208
- * }
3202
+ * useInterrupt({
3203
+ * render: ({ interrupt, resolve, cancel }) => (
3204
+ * <div>
3205
+ * <p>{interrupt?.message}</p>
3206
+ * <button onClick={() => resolve({ approved: true })}>Approve</button>
3207
+ * <button onClick={() => cancel()}>Cancel</button>
3208
+ * </div>
3209
+ * ),
3210
+ * });
3209
3211
  * ```
3210
3212
  */
3211
3213
  function useInterrupt(config) {
3212
3214
  const { copilotkit } = useCopilotKit();
3213
3215
  const { agent } = useAgent({ agentId: config.agentId });
3214
- const [pendingEvent, setPendingEvent] = (0, react.useState)(null);
3215
- const pendingEventRef = (0, react.useRef)(pendingEvent);
3216
- pendingEventRef.current = pendingEvent;
3216
+ const [pending, setPending] = (0, react.useState)(null);
3217
+ const pendingRef = (0, react.useRef)(pending);
3218
+ pendingRef.current = pending;
3217
3219
  const [handlerResult, setHandlerResult] = (0, react.useState)(null);
3220
+ const responsesRef = (0, react.useRef)({});
3218
3221
  (0, react.useEffect)(() => {
3219
- let localInterrupt = null;
3222
+ let localLegacy = null;
3223
+ let localStandard = null;
3220
3224
  const subscription = agent.subscribe({
3221
3225
  onCustomEvent: ({ event }) => {
3222
- if (event.name === INTERRUPT_EVENT_NAME) localInterrupt = {
3226
+ if (event.name === INTERRUPT_EVENT_NAME) localLegacy = {
3223
3227
  name: event.name,
3224
3228
  value: event.value
3225
3229
  };
3226
3230
  },
3231
+ onRunFinishedEvent: (params) => {
3232
+ if (params.outcome === "interrupt") localStandard = params.interrupts;
3233
+ },
3227
3234
  onRunStartedEvent: () => {
3228
- localInterrupt = null;
3229
- setPendingEvent(null);
3235
+ localLegacy = null;
3236
+ localStandard = null;
3237
+ responsesRef.current = {};
3238
+ setPending(null);
3230
3239
  },
3231
3240
  onRunFinalized: () => {
3232
- if (localInterrupt) {
3233
- setPendingEvent(localInterrupt);
3234
- localInterrupt = null;
3235
- }
3241
+ if (localStandard && localStandard.length > 0) setPending({
3242
+ kind: "standard",
3243
+ interrupts: localStandard
3244
+ });
3245
+ else if (localLegacy) setPending({
3246
+ kind: "legacy",
3247
+ event: localLegacy
3248
+ });
3249
+ localLegacy = null;
3250
+ localStandard = null;
3236
3251
  },
3237
3252
  onRunFailed: () => {
3238
- localInterrupt = null;
3239
- setPendingEvent(null);
3253
+ localLegacy = null;
3254
+ localStandard = null;
3255
+ responsesRef.current = {};
3256
+ setPending(null);
3240
3257
  }
3241
3258
  });
3242
3259
  return () => subscription.unsubscribe();
3243
3260
  }, [agent]);
3244
- const resolve = (0, react.useCallback)(async (response) => {
3261
+ const submitStandardIfComplete = (0, react.useCallback)(async (interrupts) => {
3262
+ if (!interrupts.every((i) => responsesRef.current[i.id])) return;
3263
+ const expired = interrupts.find((i) => (0, _ag_ui_client.isInterruptExpired)(i));
3264
+ if (expired) {
3265
+ console.error(`[CopilotKit] useInterrupt: interrupt ${expired.id} expired at ${expired.expiresAt}; not resuming.`);
3266
+ responsesRef.current = {};
3267
+ setPending(null);
3268
+ return;
3269
+ }
3270
+ const resume = (0, _ag_ui_client.buildResumeArray)(interrupts, responsesRef.current);
3271
+ for (const i of interrupts) {
3272
+ if (!i.toolCallId) continue;
3273
+ const response = responsesRef.current[i.id];
3274
+ const content = response.status === "cancelled" ? { status: "cancelled" } : response.payload ?? { status: "resolved" };
3275
+ agent.addMessage({
3276
+ id: (0, _ag_ui_client.randomUUID)(),
3277
+ role: "tool",
3278
+ toolCallId: i.toolCallId,
3279
+ content: JSON.stringify(content)
3280
+ });
3281
+ }
3282
+ responsesRef.current = {};
3245
3283
  try {
3284
+ return await copilotkit.runAgent({
3285
+ agent,
3286
+ resume
3287
+ });
3288
+ } catch (err) {
3289
+ console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
3290
+ setPending(null);
3291
+ throw err;
3292
+ }
3293
+ }, [agent, copilotkit]);
3294
+ const resolve = (0, react.useCallback)(async (payload, interruptId) => {
3295
+ const current = pendingRef.current;
3296
+ if (!current) return;
3297
+ if (current.kind === "legacy") try {
3246
3298
  return await copilotkit.runAgent({
3247
3299
  agent,
3248
3300
  forwardedProps: { command: {
3249
- resume: response,
3250
- interruptEvent: pendingEventRef.current?.value
3301
+ resume: payload,
3302
+ interruptEvent: current.event.value
3251
3303
  } }
3252
3304
  });
3253
3305
  } catch (err) {
3254
3306
  console.error("[CopilotKit] useInterrupt resolve: runAgent rejected; clearing pending + rethrowing", err);
3255
- setPendingEvent(null);
3307
+ setPending(null);
3256
3308
  throw err;
3257
3309
  }
3258
- }, [agent, copilotkit]);
3310
+ if (current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
3311
+ const id = interruptId ?? current.interrupts[0]?.id;
3312
+ if (!id) return;
3313
+ responsesRef.current[id] = {
3314
+ status: "resolved",
3315
+ payload
3316
+ };
3317
+ return submitStandardIfComplete(current.interrupts);
3318
+ }, [
3319
+ agent,
3320
+ copilotkit,
3321
+ submitStandardIfComplete
3322
+ ]);
3323
+ const cancel = (0, react.useCallback)(async (interruptId) => {
3324
+ const current = pendingRef.current;
3325
+ if (!current) return;
3326
+ if (current.kind === "legacy") {
3327
+ console.warn("[CopilotKit] useInterrupt: cancel() is not supported for legacy on_interrupt interrupts; dismissing.");
3328
+ setPending(null);
3329
+ return;
3330
+ }
3331
+ if (current.interrupts.length > 1 && interruptId === void 0) console.warn(`[CopilotKit] useInterrupt: resolve()/cancel() called without an interruptId while ${current.interrupts.length} interrupts are open; defaulting to the first. Pass an interruptId to address a specific interrupt.`);
3332
+ const id = interruptId ?? current.interrupts[0]?.id;
3333
+ if (!id) return;
3334
+ responsesRef.current[id] = { status: "cancelled" };
3335
+ return submitStandardIfComplete(current.interrupts);
3336
+ }, [submitStandardIfComplete]);
3259
3337
  const renderRef = (0, react.useRef)(config.render);
3260
3338
  renderRef.current = config.render;
3261
3339
  const enabledRef = (0, react.useRef)(config.enabled);
@@ -3264,6 +3342,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3264
3342
  handlerRef.current = config.handler;
3265
3343
  const resolveRef = (0, react.useRef)(resolve);
3266
3344
  resolveRef.current = resolve;
3345
+ const cancelRef = (0, react.useRef)(cancel);
3346
+ cancelRef.current = cancel;
3267
3347
  const isEnabled = (event) => {
3268
3348
  const predicate = enabledRef.current;
3269
3349
  if (!predicate) return true;
@@ -3275,11 +3355,12 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3275
3355
  }
3276
3356
  };
3277
3357
  (0, react.useEffect)(() => {
3278
- if (!pendingEvent) {
3358
+ if (!pending) {
3279
3359
  setHandlerResult(null);
3280
3360
  return;
3281
3361
  }
3282
- if (!isEnabled(pendingEvent)) {
3362
+ const legacyEvent = toLegacyEvent(pending);
3363
+ if (!isEnabled(legacyEvent)) {
3283
3364
  setHandlerResult(null);
3284
3365
  return;
3285
3366
  }
@@ -3292,8 +3373,11 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3292
3373
  let maybePromise;
3293
3374
  try {
3294
3375
  maybePromise = handler({
3295
- event: pendingEvent,
3296
- resolve: resolveRef.current
3376
+ event: legacyEvent,
3377
+ interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
3378
+ interrupts: pending.kind === "standard" ? pending.interrupts : [],
3379
+ resolve: resolveRef.current,
3380
+ cancel: cancelRef.current
3297
3381
  });
3298
3382
  } catch (err) {
3299
3383
  console.error("[CopilotKit] useInterrupt handler threw; result will be null:", err);
@@ -3312,19 +3396,24 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3312
3396
  return () => {
3313
3397
  cancelled = true;
3314
3398
  };
3315
- }, [pendingEvent]);
3399
+ }, [pending]);
3316
3400
  const element = (0, react.useMemo)(() => {
3317
- if (!pendingEvent) return null;
3318
- if (!isEnabled(pendingEvent)) return null;
3401
+ if (!pending) return null;
3402
+ const legacyEvent = toLegacyEvent(pending);
3403
+ if (!isEnabled(legacyEvent)) return null;
3319
3404
  return renderRef.current({
3320
- event: pendingEvent,
3405
+ event: legacyEvent,
3406
+ interrupt: pending.kind === "standard" ? pending.interrupts[0] : null,
3407
+ interrupts: pending.kind === "standard" ? pending.interrupts : [],
3321
3408
  result: handlerResult,
3322
- resolve
3409
+ resolve,
3410
+ cancel
3323
3411
  });
3324
3412
  }, [
3325
- pendingEvent,
3413
+ pending,
3326
3414
  handlerResult,
3327
- resolve
3415
+ resolve,
3416
+ cancel
3328
3417
  ]);
3329
3418
  (0, react.useEffect)(() => {
3330
3419
  if (config.renderInChat === false) return;
@@ -4982,6 +5071,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4982
5071
  const showInspector = shouldShowDevConsole(props.enableInspector);
4983
5072
  const publicApiKey = props.publicApiKey || props.publicLicenseKey;
4984
5073
  const renderArr = (0, react.useMemo)(() => [{ render: CoAgentStateRenderBridge }], []);
5074
+ const { onError: _onError, ...v2Props } = props;
4985
5075
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToastProvider, {
4986
5076
  enabled,
4987
5077
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotErrorBoundary, {
@@ -4990,7 +5080,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4990
5080
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadsProvider, {
4991
5081
  threadId: props.threadId,
4992
5082
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitProvider, {
4993
- ...props,
5083
+ ...v2Props,
4994
5084
  showDevConsole: showInspector,
4995
5085
  renderCustomMessages: renderArr,
4996
5086
  useSingleEndpoint: props.useSingleEndpoint ?? true,