@copilotkit/react-core 1.57.0-canary.1778082736 → 1.57.0-canary.1778116520

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 (84) hide show
  1. package/dist/{copilotkit-B_k0HSNz.cjs → copilotkit-BLlkMAjx.cjs} +368 -502
  2. package/dist/copilotkit-BLlkMAjx.cjs.map +1 -0
  3. package/dist/{copilotkit-BNlJq5UO.d.mts → copilotkit-BYnbIBN5.d.mts} +10 -62
  4. package/dist/copilotkit-BYnbIBN5.d.mts.map +1 -0
  5. package/dist/{copilotkit-ak8sGvQr.mjs → copilotkit-N0YiBG5S.mjs} +369 -497
  6. package/dist/copilotkit-N0YiBG5S.mjs.map +1 -0
  7. package/dist/{copilotkit-DgC5oCFO.d.cts → copilotkit-vx_R9p-O.d.cts} +10 -62
  8. package/dist/copilotkit-vx_R9p-O.d.cts.map +1 -0
  9. package/dist/index.cjs +5 -2
  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 +5 -2
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/index.umd.js +409 -347
  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 +1085 -0
  26. package/dist/v2/headless.cjs.map +1 -0
  27. package/dist/v2/headless.d.cts +607 -0
  28. package/dist/v2/headless.d.cts.map +1 -0
  29. package/dist/v2/headless.d.mts +514 -0
  30. package/dist/v2/headless.d.mts.map +1 -0
  31. package/dist/v2/headless.mjs +1039 -0
  32. package/dist/v2/headless.mjs.map +1 -0
  33. package/dist/v2/index.cjs +1 -2
  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 +387 -523
  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 +6 -5
  42. package/src/hooks/use-copilot-chat_internal.ts +1 -0
  43. package/src/v2/components/MCPAppsActivityRenderer.tsx +9 -3
  44. package/src/v2/components/chat/CopilotChat.tsx +1 -2
  45. package/src/v2/components/chat/CopilotChatMessageView.tsx +9 -24
  46. package/src/v2/components/chat/CopilotChatView.tsx +2 -2
  47. package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +3 -1
  48. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +25 -29
  49. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +102 -0
  50. package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.tsx +60 -5
  51. package/src/v2/components/index.ts +0 -1
  52. package/src/v2/context.ts +62 -0
  53. package/src/v2/headless.ts +42 -0
  54. package/src/v2/hooks/__tests__/standard-schema.test.tsx +2 -2
  55. package/src/v2/hooks/__tests__/use-agent-context.test.tsx +3 -3
  56. package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +3 -3
  57. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +333 -0
  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 +118 -9
  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 +11 -3
  70. package/src/v2/hooks/use-render-custom-messages.tsx +6 -1
  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 +0 -118
  77. package/tsdown.config.ts +75 -0
  78. package/dist/copilotkit-BNlJq5UO.d.mts.map +0 -1
  79. package/dist/copilotkit-B_k0HSNz.cjs.map +0 -1
  80. package/dist/copilotkit-DgC5oCFO.d.cts.map +0 -1
  81. package/dist/copilotkit-ak8sGvQr.mjs.map +0 -1
  82. package/src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx +0 -286
  83. package/src/v2/components/intelligence-indicator/__tests__/IntelligenceIndicator.e2e.test.tsx +0 -464
  84. package/src/v2/components/intelligence-indicator/index.ts +0 -2
@@ -1294,6 +1294,197 @@ function useKatexStyles() {
1294
1294
  }, []);
1295
1295
  }
1296
1296
 
1297
+ //#endregion
1298
+ //#region src/v2/lib/react-core.ts
1299
+ var CopilotKitCoreReact = class extends CopilotKitCore {
1300
+ constructor(config) {
1301
+ super(config);
1302
+ this._renderToolCalls = [];
1303
+ this._hookRenderToolCalls = /* @__PURE__ */ new Map();
1304
+ this._cachedMergedRenderToolCalls = null;
1305
+ this._renderCustomMessages = [];
1306
+ this._renderActivityMessages = [];
1307
+ this._interruptElement = null;
1308
+ this._renderToolCalls = config.renderToolCalls ?? [];
1309
+ this._renderCustomMessages = config.renderCustomMessages ?? [];
1310
+ this._renderActivityMessages = config.renderActivityMessages ?? [];
1311
+ }
1312
+ get renderCustomMessages() {
1313
+ return this._renderCustomMessages;
1314
+ }
1315
+ get renderActivityMessages() {
1316
+ return this._renderActivityMessages;
1317
+ }
1318
+ get renderToolCalls() {
1319
+ if (this._hookRenderToolCalls.size === 0) return this._renderToolCalls;
1320
+ if (this._cachedMergedRenderToolCalls) return this._cachedMergedRenderToolCalls;
1321
+ const merged = /* @__PURE__ */ new Map();
1322
+ for (const rc of this._renderToolCalls) merged.set(`${rc.agentId ?? ""}:${rc.name}`, rc);
1323
+ for (const [key, rc] of this._hookRenderToolCalls) merged.set(key, rc);
1324
+ this._cachedMergedRenderToolCalls = Array.from(merged.values());
1325
+ return this._cachedMergedRenderToolCalls;
1326
+ }
1327
+ setRenderActivityMessages(renderers) {
1328
+ this._renderActivityMessages = renderers;
1329
+ }
1330
+ setRenderCustomMessages(renderers) {
1331
+ this._renderCustomMessages = renderers;
1332
+ }
1333
+ setRenderToolCalls(renderToolCalls) {
1334
+ this._renderToolCalls = renderToolCalls;
1335
+ this._cachedMergedRenderToolCalls = null;
1336
+ this._notifyRenderToolCallsChanged();
1337
+ }
1338
+ addHookRenderToolCall(entry) {
1339
+ const key = `${entry.agentId ?? ""}:${entry.name}`;
1340
+ this._hookRenderToolCalls.set(key, entry);
1341
+ this._cachedMergedRenderToolCalls = null;
1342
+ this._notifyRenderToolCallsChanged();
1343
+ }
1344
+ removeHookRenderToolCall(name, agentId) {
1345
+ const key = `${agentId ?? ""}:${name}`;
1346
+ if (this._hookRenderToolCalls.delete(key)) {
1347
+ this._cachedMergedRenderToolCalls = null;
1348
+ this._notifyRenderToolCallsChanged();
1349
+ }
1350
+ }
1351
+ _notifyRenderToolCallsChanged() {
1352
+ this.notifySubscribers((subscriber) => {
1353
+ const reactSubscriber = subscriber;
1354
+ if (reactSubscriber.onRenderToolCallsChanged) reactSubscriber.onRenderToolCallsChanged({
1355
+ copilotkit: this,
1356
+ renderToolCalls: this.renderToolCalls
1357
+ });
1358
+ }, "Subscriber onRenderToolCallsChanged error:");
1359
+ }
1360
+ get interruptElement() {
1361
+ return this._interruptElement;
1362
+ }
1363
+ setInterruptElement(element) {
1364
+ this._interruptElement = element;
1365
+ this.notifySubscribers((subscriber) => {
1366
+ subscriber.onInterruptElementChanged?.({
1367
+ copilotkit: this,
1368
+ interruptElement: this._interruptElement
1369
+ });
1370
+ }, "Subscriber onInterruptElementChanged error:");
1371
+ }
1372
+ subscribe(subscriber) {
1373
+ return super.subscribe(subscriber);
1374
+ }
1375
+ /**
1376
+ * Wait for pending React state updates before the follow-up agent run.
1377
+ *
1378
+ * When a frontend tool handler calls setState(), React 18 batches the update
1379
+ * and schedules a commit via its internal scheduler (MessageChannel). The
1380
+ * useAgentContext hook registers context via useLayoutEffect, which runs
1381
+ * synchronously after React commits that batch.
1382
+ *
1383
+ * Awaiting a zero-delay timeout yields to the macrotask queue. React's
1384
+ * MessageChannel task runs first, committing the pending state and running
1385
+ * useLayoutEffect (which updates the context store). The follow-up runAgent
1386
+ * call then reads fresh context.
1387
+ */
1388
+ async waitForPendingFrameworkUpdates() {
1389
+ await new Promise((resolve) => setTimeout(resolve, 0));
1390
+ }
1391
+ };
1392
+
1393
+ //#endregion
1394
+ //#region src/v2/context.ts
1395
+ const CopilotKitContext = createContext(null);
1396
+ const useCopilotKit = () => {
1397
+ const context = useContext(CopilotKitContext);
1398
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
1399
+ if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider");
1400
+ useEffect(() => {
1401
+ const subscription = context.copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
1402
+ forceUpdate();
1403
+ } });
1404
+ return () => {
1405
+ subscription.unsubscribe();
1406
+ };
1407
+ }, []);
1408
+ return context;
1409
+ };
1410
+ const LicenseContext = createContext({
1411
+ status: null,
1412
+ license: null,
1413
+ checkFeature: () => true,
1414
+ getLimit: () => null
1415
+ });
1416
+ const useLicenseContext = () => useContext(LicenseContext);
1417
+
1418
+ //#endregion
1419
+ //#region src/v2/hooks/use-render-tool-call.tsx
1420
+ /**
1421
+ * Memoized component that renders a single tool call.
1422
+ * This prevents unnecessary re-renders when parent components update
1423
+ * but the tool call data hasn't changed.
1424
+ */
1425
+ const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
1426
+ const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
1427
+ const toolName = toolCall.function.name;
1428
+ if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
1429
+ name: toolName,
1430
+ toolCallId: toolCall.id,
1431
+ args,
1432
+ status: ToolCallStatus.Complete,
1433
+ result: toolMessage.content
1434
+ });
1435
+ else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
1436
+ name: toolName,
1437
+ toolCallId: toolCall.id,
1438
+ args,
1439
+ status: ToolCallStatus.Executing,
1440
+ result: void 0
1441
+ });
1442
+ else return /* @__PURE__ */ jsx(RenderComponent, {
1443
+ name: toolName,
1444
+ toolCallId: toolCall.id,
1445
+ args,
1446
+ status: ToolCallStatus.InProgress,
1447
+ result: void 0
1448
+ });
1449
+ }, (prevProps, nextProps) => {
1450
+ if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
1451
+ if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
1452
+ if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
1453
+ if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
1454
+ if (prevProps.isExecuting !== nextProps.isExecuting) return false;
1455
+ if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
1456
+ return true;
1457
+ });
1458
+ /**
1459
+ * Hook that returns a function to render tool calls based on the render functions
1460
+ * defined in CopilotKitProvider.
1461
+ *
1462
+ * @returns A function that takes a tool call and optional tool message and returns the rendered component
1463
+ */
1464
+ function useRenderToolCall() {
1465
+ const { copilotkit, executingToolCallIds } = useCopilotKit();
1466
+ const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
1467
+ const renderToolCalls = useSyncExternalStore((callback) => {
1468
+ return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
1469
+ }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
1470
+ return useCallback(({ toolCall, toolMessage }) => {
1471
+ const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
1472
+ const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
1473
+ if (!renderConfig) return null;
1474
+ const RenderComponent = renderConfig.render;
1475
+ return /* @__PURE__ */ jsx(ToolCallRenderer, {
1476
+ toolCall,
1477
+ toolMessage,
1478
+ RenderComponent,
1479
+ isExecuting: executingToolCallIds.has(toolCall.id)
1480
+ }, toolCall.id);
1481
+ }, [
1482
+ renderToolCalls,
1483
+ executingToolCallIds,
1484
+ agentId
1485
+ ]);
1486
+ }
1487
+
1297
1488
  //#endregion
1298
1489
  //#region src/v2/components/CopilotKitInspector.tsx
1299
1490
  const CopilotKitInspector = ({ core, ...rest }) => {
@@ -1638,6 +1829,7 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
1638
1829
  promise: null,
1639
1830
  resourceUri: null
1640
1831
  });
1832
+ const { resourceUri, serverHash, serverId } = content;
1641
1833
  const sendToIframe = useCallback((msg) => {
1642
1834
  if (iframeRef.current?.contentWindow) {
1643
1835
  console.log("[MCPAppsRenderer] Sending to iframe:", msg);
@@ -1669,7 +1861,6 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
1669
1861
  });
1670
1862
  }, [sendToIframe]);
1671
1863
  useEffect(() => {
1672
- const { resourceUri, serverHash, serverId } = content;
1673
1864
  if (fetchStateRef.current.inProgress && fetchStateRef.current.resourceUri === resourceUri) {
1674
1865
  fetchStateRef.current.promise?.then((resource) => {
1675
1866
  if (resource) {
@@ -1716,7 +1907,12 @@ const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agen
1716
1907
  setError(err instanceof Error ? err : new Error(String(err)));
1717
1908
  setIsLoading(false);
1718
1909
  });
1719
- }, [agent, content]);
1910
+ }, [
1911
+ agent,
1912
+ resourceUri,
1913
+ serverHash,
1914
+ serverId
1915
+ ]);
1720
1916
  useEffect(() => {
1721
1917
  if (isLoading || !fetchedResource) return;
1722
1918
  const container = containerRef.current;
@@ -2937,102 +3133,6 @@ function A2UICatalogContext({ catalog, includeSchema }) {
2937
3133
  return null;
2938
3134
  }
2939
3135
 
2940
- //#endregion
2941
- //#region src/v2/lib/react-core.ts
2942
- var CopilotKitCoreReact = class extends CopilotKitCore {
2943
- constructor(config) {
2944
- super(config);
2945
- this._renderToolCalls = [];
2946
- this._hookRenderToolCalls = /* @__PURE__ */ new Map();
2947
- this._cachedMergedRenderToolCalls = null;
2948
- this._renderCustomMessages = [];
2949
- this._renderActivityMessages = [];
2950
- this._interruptElement = null;
2951
- this._renderToolCalls = config.renderToolCalls ?? [];
2952
- this._renderCustomMessages = config.renderCustomMessages ?? [];
2953
- this._renderActivityMessages = config.renderActivityMessages ?? [];
2954
- }
2955
- get renderCustomMessages() {
2956
- return this._renderCustomMessages;
2957
- }
2958
- get renderActivityMessages() {
2959
- return this._renderActivityMessages;
2960
- }
2961
- get renderToolCalls() {
2962
- if (this._hookRenderToolCalls.size === 0) return this._renderToolCalls;
2963
- if (this._cachedMergedRenderToolCalls) return this._cachedMergedRenderToolCalls;
2964
- const merged = /* @__PURE__ */ new Map();
2965
- for (const rc of this._renderToolCalls) merged.set(`${rc.agentId ?? ""}:${rc.name}`, rc);
2966
- for (const [key, rc] of this._hookRenderToolCalls) merged.set(key, rc);
2967
- this._cachedMergedRenderToolCalls = Array.from(merged.values());
2968
- return this._cachedMergedRenderToolCalls;
2969
- }
2970
- setRenderActivityMessages(renderers) {
2971
- this._renderActivityMessages = renderers;
2972
- }
2973
- setRenderCustomMessages(renderers) {
2974
- this._renderCustomMessages = renderers;
2975
- }
2976
- setRenderToolCalls(renderToolCalls) {
2977
- this._renderToolCalls = renderToolCalls;
2978
- this._cachedMergedRenderToolCalls = null;
2979
- this._notifyRenderToolCallsChanged();
2980
- }
2981
- addHookRenderToolCall(entry) {
2982
- const key = `${entry.agentId ?? ""}:${entry.name}`;
2983
- this._hookRenderToolCalls.set(key, entry);
2984
- this._cachedMergedRenderToolCalls = null;
2985
- this._notifyRenderToolCallsChanged();
2986
- }
2987
- removeHookRenderToolCall(name, agentId) {
2988
- const key = `${agentId ?? ""}:${name}`;
2989
- if (this._hookRenderToolCalls.delete(key)) {
2990
- this._cachedMergedRenderToolCalls = null;
2991
- this._notifyRenderToolCallsChanged();
2992
- }
2993
- }
2994
- _notifyRenderToolCallsChanged() {
2995
- this.notifySubscribers((subscriber) => {
2996
- const reactSubscriber = subscriber;
2997
- if (reactSubscriber.onRenderToolCallsChanged) reactSubscriber.onRenderToolCallsChanged({
2998
- copilotkit: this,
2999
- renderToolCalls: this.renderToolCalls
3000
- });
3001
- }, "Subscriber onRenderToolCallsChanged error:");
3002
- }
3003
- get interruptElement() {
3004
- return this._interruptElement;
3005
- }
3006
- setInterruptElement(element) {
3007
- this._interruptElement = element;
3008
- this.notifySubscribers((subscriber) => {
3009
- subscriber.onInterruptElementChanged?.({
3010
- copilotkit: this,
3011
- interruptElement: this._interruptElement
3012
- });
3013
- }, "Subscriber onInterruptElementChanged error:");
3014
- }
3015
- subscribe(subscriber) {
3016
- return super.subscribe(subscriber);
3017
- }
3018
- /**
3019
- * Wait for pending React state updates before the follow-up agent run.
3020
- *
3021
- * When a frontend tool handler calls setState(), React 18 batches the update
3022
- * and schedules a commit via its internal scheduler (MessageChannel). The
3023
- * useAgentContext hook registers context via useLayoutEffect, which runs
3024
- * synchronously after React commits that batch.
3025
- *
3026
- * Awaiting a zero-delay timeout yields to the macrotask queue. React's
3027
- * MessageChannel task runs first, committing the pending state and running
3028
- * useLayoutEffect (which updates the context store). The follow-up runAgent
3029
- * call then reads fresh context.
3030
- */
3031
- async waitForPendingFrameworkUpdates() {
3032
- await new Promise((resolve) => setTimeout(resolve, 0));
3033
- }
3034
- };
3035
-
3036
3136
  //#endregion
3037
3137
  //#region src/v2/providers/CopilotKitProvider.tsx
3038
3138
  const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
@@ -3048,12 +3148,6 @@ const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follo
3048
3148
  - Minimal transitions (150ms) for hover/focus states only. No decorative animations.
3049
3149
  - Keep the UI focused and dense — avoid excessive padding. Use compact spacing (8–12px gaps, 10–14px padding in controls).`;
3050
3150
  const GENERATE_SANDBOXED_UI_DESCRIPTION = "Generate sandboxed UI. IMPORTANT: The generated code runs in a sandboxed iframe WITHOUT same-origin access. Do NOT use localStorage, sessionStorage, document.cookie, IndexedDB, or fetch/XMLHttpRequest to same-origin URLs. To communicate with the host application, use Websandbox.connection.remote.<functionName>(args) which returns a Promise.\n\nYou CAN use external libraries from CDNs by including <script> or <link> tags in the HTML <head> (e.g., Chart.js, D3, Three.js, x-data-spreadsheet, etc.). CDN resources load normally inside the sandbox.\n\nPARAMETER ORDER IS CRITICAL — generate parameters in exactly this order:\n1. initialHeight + placeholderMessages (shown to user while generating)\n2. css (all styles FIRST — the user sees a placeholder until CSS is complete)\n3. html (streams in live — the user watches the UI build as HTML is generated)\n4. jsFunctions (reusable helper functions)\n5. jsExpressions (applied one-by-one — the user sees each expression take effect)";
3051
- const CopilotKitContext = createContext({
3052
- copilotkit: null,
3053
- executingToolCallIds: /* @__PURE__ */ new Set()
3054
- });
3055
- const LicenseContext = createContext(createLicenseContextValue(null));
3056
- const useLicenseContext = () => useContext(LicenseContext);
3057
3151
  function useStableArrayProp(prop, warningMessage, isMeaningfulChange) {
3058
3152
  const empty = useMemo(() => [], []);
3059
3153
  const value = prop ?? empty;
@@ -3275,13 +3369,9 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = {}, c
3275
3369
  onErrorRef.current = onError;
3276
3370
  }, [onError]);
3277
3371
  useEffect(() => {
3278
- if (!onErrorRef.current) return;
3279
3372
  const subscription = copilotkit.subscribe({ onError: (event) => {
3280
- onErrorRef.current?.({
3281
- error: event.error,
3282
- code: event.code,
3283
- context: event.context
3284
- });
3373
+ if (onErrorRef.current) onErrorRef.current(event);
3374
+ else console.error(`[CopilotKit] Error (${event.code}):`, event.error, event.context ?? {});
3285
3375
  } });
3286
3376
  return () => {
3287
3377
  subscription.unsubscribe();
@@ -3396,89 +3486,167 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = {}, c
3396
3486
  })
3397
3487
  });
3398
3488
  };
3399
- const useCopilotKit = () => {
3400
- const context = useContext(CopilotKitContext);
3489
+
3490
+ //#endregion
3491
+ //#region src/v2/hooks/use-agent.tsx
3492
+ let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
3493
+ UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
3494
+ UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
3495
+ UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
3496
+ return UseAgentUpdate;
3497
+ }({});
3498
+ const ALL_UPDATES = [
3499
+ UseAgentUpdate.OnMessagesChanged,
3500
+ UseAgentUpdate.OnStateChanged,
3501
+ UseAgentUpdate.OnRunStatusChanged
3502
+ ];
3503
+ /**
3504
+ * Clone a registry agent for per-thread isolation.
3505
+ * Copies agent configuration (transport, headers, etc.) but resets conversation
3506
+ * state (messages, threadId, state) so each thread starts fresh.
3507
+ */
3508
+ function cloneForThread(source, threadId, headers) {
3509
+ const clone = source.clone();
3510
+ if (clone === source) throw new Error(`useAgent: ${source.constructor.name}.clone() returned the same instance. clone() must return a new, independent object.`);
3511
+ clone.threadId = threadId;
3512
+ clone.setMessages([]);
3513
+ clone.setState({});
3514
+ if (clone instanceof HttpAgent) clone.headers = { ...headers };
3515
+ return clone;
3516
+ }
3517
+ /**
3518
+ * Module-level WeakMap: registryAgent → (threadId → clone).
3519
+ * Shared across all useAgent() calls so that every component using the same
3520
+ * (agentId, threadId) pair receives the same agent instance. Using WeakMap
3521
+ * ensures the clone map is garbage-collected when the registry agent is
3522
+ * replaced (e.g. after reconnect or hot-reload).
3523
+ */
3524
+ const globalThreadCloneMap = /* @__PURE__ */ new WeakMap();
3525
+ /**
3526
+ * Look up an existing per-thread clone without creating one.
3527
+ * Returns undefined when no clone has been created yet for this pair.
3528
+ */
3529
+ function getThreadClone(registryAgent, threadId) {
3530
+ if (!registryAgent || !threadId) return void 0;
3531
+ return globalThreadCloneMap.get(registryAgent)?.get(threadId);
3532
+ }
3533
+ function getOrCreateThreadClone(existing, threadId, headers) {
3534
+ let byThread = globalThreadCloneMap.get(existing);
3535
+ if (!byThread) {
3536
+ byThread = /* @__PURE__ */ new Map();
3537
+ globalThreadCloneMap.set(existing, byThread);
3538
+ }
3539
+ const cached = byThread.get(threadId);
3540
+ if (cached) return cached;
3541
+ const clone = cloneForThread(existing, threadId, headers);
3542
+ byThread.set(threadId, clone);
3543
+ return clone;
3544
+ }
3545
+ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3546
+ agentId ??= DEFAULT_AGENT_ID;
3547
+ const { copilotkit } = useCopilotKit();
3548
+ const providerThrottleMs = copilotkit.defaultThrottleMs;
3549
+ const chatConfig = useCopilotChatConfiguration();
3550
+ threadId ??= chatConfig?.threadId;
3401
3551
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
3402
- if (!context) throw new Error("useCopilotKit must be used within CopilotKitProvider");
3552
+ const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
3553
+ const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
3554
+ const agent = useMemo(() => {
3555
+ const cacheKey = threadId ? `${agentId}:${threadId}` : agentId;
3556
+ const existing = copilotkit.getAgent(agentId);
3557
+ if (existing) {
3558
+ provisionalAgentCache.current.delete(cacheKey);
3559
+ provisionalAgentCache.current.delete(agentId);
3560
+ if (!threadId) return existing;
3561
+ return getOrCreateThreadClone(existing, threadId, copilotkit.headers);
3562
+ }
3563
+ const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
3564
+ const status = copilotkit.runtimeConnectionStatus;
3565
+ if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
3566
+ const cached = provisionalAgentCache.current.get(cacheKey);
3567
+ if (cached) {
3568
+ cached.headers = { ...copilotkit.headers };
3569
+ return cached;
3570
+ }
3571
+ const provisional = new ProxiedCopilotRuntimeAgent({
3572
+ runtimeUrl: copilotkit.runtimeUrl,
3573
+ agentId,
3574
+ transport: copilotkit.runtimeTransport,
3575
+ runtimeMode: "pending"
3576
+ });
3577
+ provisional.headers = { ...copilotkit.headers };
3578
+ if (threadId) provisional.threadId = threadId;
3579
+ provisionalAgentCache.current.set(cacheKey, provisional);
3580
+ return provisional;
3581
+ }
3582
+ if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
3583
+ const cached = provisionalAgentCache.current.get(cacheKey);
3584
+ if (cached) {
3585
+ cached.headers = { ...copilotkit.headers };
3586
+ return cached;
3587
+ }
3588
+ const provisional = new ProxiedCopilotRuntimeAgent({
3589
+ runtimeUrl: copilotkit.runtimeUrl,
3590
+ agentId,
3591
+ transport: copilotkit.runtimeTransport,
3592
+ runtimeMode: "pending"
3593
+ });
3594
+ provisional.headers = { ...copilotkit.headers };
3595
+ if (threadId) provisional.threadId = threadId;
3596
+ provisionalAgentCache.current.set(cacheKey, provisional);
3597
+ return provisional;
3598
+ }
3599
+ const knownAgents = Object.keys(copilotkit.agents ?? {});
3600
+ const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
3601
+ 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.");
3602
+ }, [
3603
+ agentId,
3604
+ threadId,
3605
+ copilotkit.agents,
3606
+ copilotkit.runtimeConnectionStatus,
3607
+ copilotkit.runtimeUrl,
3608
+ copilotkit.runtimeTransport,
3609
+ JSON.stringify(copilotkit.headers)
3610
+ ]);
3403
3611
  useEffect(() => {
3404
- const subscription = context.copilotkit.subscribe({ onRuntimeConnectionStatusChanged: () => {
3405
- forceUpdate();
3406
- } });
3612
+ if (updateFlags.length === 0) return;
3613
+ let active = true;
3614
+ const handlers = {};
3615
+ let batchScheduled = false;
3616
+ const batchedForceUpdate = () => {
3617
+ if (!active) return;
3618
+ if (!batchScheduled) {
3619
+ batchScheduled = true;
3620
+ queueMicrotask(() => {
3621
+ batchScheduled = false;
3622
+ if (active) forceUpdate();
3623
+ });
3624
+ }
3625
+ };
3626
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = batchedForceUpdate;
3627
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
3628
+ if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3629
+ handlers.onRunInitialized = batchedForceUpdate;
3630
+ handlers.onRunFinalized = batchedForceUpdate;
3631
+ handlers.onRunFailed = batchedForceUpdate;
3632
+ handlers.onRunErrorEvent = batchedForceUpdate;
3633
+ }
3634
+ const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
3407
3635
  return () => {
3636
+ active = false;
3408
3637
  subscription.unsubscribe();
3409
3638
  };
3410
- }, []);
3411
- return context;
3412
- };
3413
-
3414
- //#endregion
3415
- //#region src/v2/hooks/use-render-tool-call.tsx
3416
- /**
3417
- * Memoized component that renders a single tool call.
3418
- * This prevents unnecessary re-renders when parent components update
3419
- * but the tool call data hasn't changed.
3420
- */
3421
- const ToolCallRenderer = React.memo(function ToolCallRenderer({ toolCall, toolMessage, RenderComponent, isExecuting }) {
3422
- const args = useMemo(() => partialJSONParse(toolCall.function.arguments), [toolCall.function.arguments]);
3423
- const toolName = toolCall.function.name;
3424
- if (toolMessage) return /* @__PURE__ */ jsx(RenderComponent, {
3425
- name: toolName,
3426
- toolCallId: toolCall.id,
3427
- args,
3428
- status: ToolCallStatus.Complete,
3429
- result: toolMessage.content
3430
- });
3431
- else if (isExecuting) return /* @__PURE__ */ jsx(RenderComponent, {
3432
- name: toolName,
3433
- toolCallId: toolCall.id,
3434
- args,
3435
- status: ToolCallStatus.Executing,
3436
- result: void 0
3437
- });
3438
- else return /* @__PURE__ */ jsx(RenderComponent, {
3439
- name: toolName,
3440
- toolCallId: toolCall.id,
3441
- args,
3442
- status: ToolCallStatus.InProgress,
3443
- result: void 0
3444
- });
3445
- }, (prevProps, nextProps) => {
3446
- if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;
3447
- if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name) return false;
3448
- if (prevProps.toolCall.function.arguments !== nextProps.toolCall.function.arguments) return false;
3449
- if (prevProps.toolMessage?.content !== nextProps.toolMessage?.content) return false;
3450
- if (prevProps.isExecuting !== nextProps.isExecuting) return false;
3451
- if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;
3452
- return true;
3453
- });
3454
- /**
3455
- * Hook that returns a function to render tool calls based on the render functions
3456
- * defined in CopilotKitProvider.
3457
- *
3458
- * @returns A function that takes a tool call and optional tool message and returns the rendered component
3459
- */
3460
- function useRenderToolCall() {
3461
- const { copilotkit, executingToolCallIds } = useCopilotKit();
3462
- const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
3463
- const renderToolCalls = useSyncExternalStore((callback) => {
3464
- return copilotkit.subscribe({ onRenderToolCallsChanged: callback }).unsubscribe;
3465
- }, () => copilotkit.renderToolCalls, () => copilotkit.renderToolCalls);
3466
- return useCallback(({ toolCall, toolMessage }) => {
3467
- const exactMatches = renderToolCalls.filter((rc) => rc.name === toolCall.function.name);
3468
- const renderConfig = exactMatches.find((rc) => rc.agentId === agentId) || exactMatches.find((rc) => !rc.agentId) || exactMatches[0] || renderToolCalls.find((rc) => rc.name === "*");
3469
- if (!renderConfig) return null;
3470
- const RenderComponent = renderConfig.render;
3471
- return /* @__PURE__ */ jsx(ToolCallRenderer, {
3472
- toolCall,
3473
- toolMessage,
3474
- RenderComponent,
3475
- isExecuting: executingToolCallIds.has(toolCall.id)
3476
- }, toolCall.id);
3477
3639
  }, [
3478
- renderToolCalls,
3479
- executingToolCallIds,
3480
- agentId
3640
+ agent,
3641
+ forceUpdate,
3642
+ throttleMs,
3643
+ providerThrottleMs,
3644
+ updateFlags
3481
3645
  ]);
3646
+ useEffect(() => {
3647
+ if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
3648
+ }, [agent, JSON.stringify(copilotkit.headers)]);
3649
+ return { agent };
3482
3650
  }
3483
3651
 
3484
3652
  //#endregion
@@ -3498,7 +3666,8 @@ function useRenderCustomMessages() {
3498
3666
  const { message, position } = params;
3499
3667
  const resolvedRunId = copilotkit.getRunIdForMessage(agentId, threadId, message.id) ?? copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
3500
3668
  const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
3501
- const agent = copilotkit.getAgent(agentId);
3669
+ const registryAgent = copilotkit.getAgent(agentId);
3670
+ const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
3502
3671
  if (!agent) return null;
3503
3672
  const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
3504
3673
  const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
@@ -3530,7 +3699,8 @@ function useRenderCustomMessages() {
3530
3699
  //#region src/v2/hooks/use-render-activity-message.tsx
3531
3700
  function useRenderActivityMessage() {
3532
3701
  const { copilotkit } = useCopilotKit();
3533
- const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
3702
+ const config = useCopilotChatConfiguration();
3703
+ const agentId = config?.agentId ?? DEFAULT_AGENT_ID;
3534
3704
  const renderers = copilotkit.renderActivityMessages;
3535
3705
  const findRenderer = useCallback((activityType) => {
3536
3706
  if (!renderers.length) return null;
@@ -3546,7 +3716,8 @@ function useRenderActivityMessage() {
3546
3716
  return null;
3547
3717
  }
3548
3718
  const Component = renderer.render;
3549
- const agent = copilotkit.getAgent(agentId);
3719
+ const registryAgent = copilotkit.getAgent(agentId);
3720
+ const agent = getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
3550
3721
  return /* @__PURE__ */ jsx(Component, {
3551
3722
  activityType: message.activityType,
3552
3723
  content: parseResult.data,
@@ -3555,6 +3726,7 @@ function useRenderActivityMessage() {
3555
3726
  }, message.id);
3556
3727
  }, [
3557
3728
  agentId,
3729
+ config?.threadId,
3558
3730
  copilotkit,
3559
3731
  findRenderer
3560
3732
  ]);
@@ -3593,8 +3765,7 @@ function useFrontendTool(tool, deps) {
3593
3765
  tool.name,
3594
3766
  tool.available,
3595
3767
  copilotkit,
3596
- extraDeps.length,
3597
- ...extraDeps
3768
+ JSON.stringify(extraDeps)
3598
3769
  ]);
3599
3770
  }
3600
3771
 
@@ -3733,8 +3904,7 @@ function useRenderTool(config, deps) {
3733
3904
  }, [
3734
3905
  config.name,
3735
3906
  copilotkit,
3736
- extraDeps.length,
3737
- ...extraDeps
3907
+ JSON.stringify(extraDeps)
3738
3908
  ]);
3739
3909
  }
3740
3910
 
@@ -3994,118 +4164,6 @@ function useHumanInTheLoop(tool, deps) {
3994
4164
  ]);
3995
4165
  }
3996
4166
 
3997
- //#endregion
3998
- //#region src/v2/hooks/use-agent.tsx
3999
- let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
4000
- UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
4001
- UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
4002
- UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
4003
- return UseAgentUpdate;
4004
- }({});
4005
- const ALL_UPDATES = [
4006
- UseAgentUpdate.OnMessagesChanged,
4007
- UseAgentUpdate.OnStateChanged,
4008
- UseAgentUpdate.OnRunStatusChanged
4009
- ];
4010
- function useAgent({ agentId, updates, throttleMs } = {}) {
4011
- agentId ??= DEFAULT_AGENT_ID;
4012
- const { copilotkit } = useCopilotKit();
4013
- const providerThrottleMs = copilotkit.defaultThrottleMs;
4014
- const [, forceUpdate] = useReducer((x) => x + 1, 0);
4015
- const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
4016
- const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
4017
- const agent = useMemo(() => {
4018
- const existing = copilotkit.getAgent(agentId);
4019
- if (existing) {
4020
- provisionalAgentCache.current.delete(agentId);
4021
- return existing;
4022
- }
4023
- const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
4024
- const status = copilotkit.runtimeConnectionStatus;
4025
- if (isRuntimeConfigured && (status === CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
4026
- const cached = provisionalAgentCache.current.get(agentId);
4027
- if (cached) {
4028
- cached.headers = { ...copilotkit.headers };
4029
- return cached;
4030
- }
4031
- const provisional = new ProxiedCopilotRuntimeAgent({
4032
- runtimeUrl: copilotkit.runtimeUrl,
4033
- agentId,
4034
- transport: copilotkit.runtimeTransport,
4035
- runtimeMode: "pending"
4036
- });
4037
- provisional.headers = { ...copilotkit.headers };
4038
- provisionalAgentCache.current.set(agentId, provisional);
4039
- return provisional;
4040
- }
4041
- if (isRuntimeConfigured && status === CopilotKitCoreRuntimeConnectionStatus.Error) {
4042
- const cached = provisionalAgentCache.current.get(agentId);
4043
- if (cached) {
4044
- cached.headers = { ...copilotkit.headers };
4045
- return cached;
4046
- }
4047
- const provisional = new ProxiedCopilotRuntimeAgent({
4048
- runtimeUrl: copilotkit.runtimeUrl,
4049
- agentId,
4050
- transport: copilotkit.runtimeTransport,
4051
- runtimeMode: "pending"
4052
- });
4053
- provisional.headers = { ...copilotkit.headers };
4054
- provisionalAgentCache.current.set(agentId, provisional);
4055
- return provisional;
4056
- }
4057
- const knownAgents = Object.keys(copilotkit.agents ?? {});
4058
- const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
4059
- 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.");
4060
- }, [
4061
- agentId,
4062
- copilotkit.agents,
4063
- copilotkit.runtimeConnectionStatus,
4064
- copilotkit.runtimeUrl,
4065
- copilotkit.runtimeTransport,
4066
- JSON.stringify(copilotkit.headers)
4067
- ]);
4068
- useEffect(() => {
4069
- if (updateFlags.length === 0) return;
4070
- let active = true;
4071
- const handlers = {};
4072
- let batchScheduled = false;
4073
- const batchedForceUpdate = () => {
4074
- if (!active) return;
4075
- if (!batchScheduled) {
4076
- batchScheduled = true;
4077
- queueMicrotask(() => {
4078
- batchScheduled = false;
4079
- if (active) forceUpdate();
4080
- });
4081
- }
4082
- };
4083
- if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = forceUpdate;
4084
- if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
4085
- if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
4086
- handlers.onRunInitialized = batchedForceUpdate;
4087
- handlers.onRunFinalized = batchedForceUpdate;
4088
- handlers.onRunFailed = batchedForceUpdate;
4089
- handlers.onRunErrorEvent = batchedForceUpdate;
4090
- }
4091
- const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
4092
- return () => {
4093
- active = false;
4094
- subscription.unsubscribe();
4095
- };
4096
- }, [
4097
- agent,
4098
- forceUpdate,
4099
- throttleMs,
4100
- providerThrottleMs,
4101
- updateFlags
4102
- ]);
4103
- useEffect(() => {
4104
- if (agent instanceof HttpAgent) agent.headers = { ...copilotkit.headers };
4105
- }, [agent, JSON.stringify(copilotkit.headers)]);
4106
- return { agent };
4107
- }
4108
-
4109
4167
  //#endregion
4110
4168
  //#region src/v2/hooks/use-capabilities.tsx
4111
4169
  /**
@@ -5571,190 +5629,6 @@ CopilotChatSuggestionView.displayName = "CopilotChatSuggestionView";
5571
5629
  */
5572
5630
  const ScrollElementContext = React.createContext(null);
5573
5631
 
5574
- //#endregion
5575
- //#region src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx
5576
- /**
5577
- * Grace window before showing the spinner. A matching tool call must
5578
- * remain unresolved (no `tool`-role result message in `agent.messages`)
5579
- * for at least this long before the pill appears. This filters out
5580
- * history-replay flashes — during `connectAgent` replay, tool calls and
5581
- * their results arrive back-to-back in sub-millisecond bursts, so the
5582
- * timer is cancelled before it fires. Live runs cross the threshold
5583
- * easily because the tool actually has to execute.
5584
- */
5585
- const PENDING_THRESHOLD_MS = 100;
5586
- /** Hold the checkmark briefly before fading out. */
5587
- const CHECK_HOLD_MS = 800;
5588
- /**
5589
- * Duration of the fade-out animation. Must match
5590
- * `cpk-intelligence-pill-fade-out` keyframes in `v2/styles/globals.css`.
5591
- */
5592
- const FADE_OUT_ANIMATION_MS = 480;
5593
- /**
5594
- * Tool-name regex patterns that trigger the indicator. Currently
5595
- * hardcoded to the Intelligence MCP server's canonical tool name. If
5596
- * we add per-instance customization later (e.g. a `CopilotKitProvider`
5597
- * prop or a runtime-info field), this constant becomes the fallback.
5598
- */
5599
- const DEFAULT_TOOL_PATTERNS = [/^copilotkit_knowledge_base_shell$/];
5600
- const isMatchingToolCallName = (name) => typeof name === "string" && DEFAULT_TOOL_PATTERNS.some((p) => p.test(name));
5601
- /**
5602
- * "Tool-call-like" messages do NOT count as a real follow-up: tool
5603
- * result messages, assistant messages that carry tool calls, and
5604
- * empty-content assistant messages (which some providers emit as a
5605
- * standalone wrapper around a batch of tool calls). A real follow-up
5606
- * is anything else — most importantly an assistant message with prose
5607
- * content, or a fresh user message.
5608
- */
5609
- const isToolCallLikeMessage = (m) => {
5610
- if (m.role === "tool") return true;
5611
- if (m.role === "assistant") {
5612
- if ((Array.isArray(m.toolCalls) ? m.toolCalls : []).length > 0) return true;
5613
- const content = m.content;
5614
- return typeof content !== "string" || content.trim().length === 0;
5615
- }
5616
- return false;
5617
- };
5618
- /**
5619
- * The "Using CopilotKit Intelligence" pill. Auto-mounted by
5620
- * `CopilotChatMessageView` for every message slot when
5621
- * `copilotkit.intelligence` is configured — callers do not register
5622
- * this themselves. Self-gates so only the canonical message renders a
5623
- * pill.
5624
- *
5625
- * Render gates (all must hold):
5626
- * 1. `copilotkit.intelligence !== undefined`
5627
- * 2. The message is an assistant message with at least one tool call
5628
- * whose name matches {@link DEFAULT_TOOL_PATTERNS}
5629
- * 3. The message is the *latest* such matching-assistant message in
5630
- * `agent.messages` — tool-result messages and prose-only assistant
5631
- * messages don't invalidate the slot, so the pill stays
5632
- * continuously through a multi-step tool chain.
5633
- * 4. The phase machine is past `idle` (the pending-grace timer fired)
5634
- * and not yet `hidden`.
5635
- *
5636
- * Phase machine (per-instance, all timers local):
5637
- * - Starts in `idle` — nothing rendered.
5638
- * - `idle → spinner` once a matching tool call has been pending
5639
- * (no `tool`-role result with a matching `toolCallId`) for
5640
- * {@link PENDING_THRESHOLD_MS}. Replay flashes (tool call + result
5641
- * in the same tick) never cross this threshold.
5642
- * - `spinner → check` as soon as EITHER `agent.isRunning` flips
5643
- * false OR a non-tool-call-like message appears later in
5644
- * `agent.messages` (i.e. the agent has produced a "real"
5645
- * follow-up — prose answer or a new user turn).
5646
- * - `check → fading` after {@link CHECK_HOLD_MS}.
5647
- * - `fading → hidden` after {@link FADE_OUT_ANIMATION_MS}.
5648
- *
5649
- * Once `hidden`, the phase is sticky — a finished pill never re-spawns
5650
- * on the same message. New runs mount fresh indicator instances on
5651
- * their own assistant messages.
5652
- *
5653
- * The "exactly one pill at a time" guarantee is structural: only one
5654
- * message satisfies the latest-matching-assistant gate at any moment.
5655
- */
5656
- function IntelligenceIndicator(props) {
5657
- const { message, agentId, label = "Using CopilotKit Intelligence" } = props;
5658
- const { copilotkit } = useCopilotKit();
5659
- const config = useCopilotChatConfiguration();
5660
- const { agent } = useAgent({
5661
- agentId,
5662
- updates: [UseAgentUpdate.OnRunStatusChanged, UseAgentUpdate.OnMessagesChanged]
5663
- });
5664
- const matchingToolCallIds = useMemo(() => {
5665
- if (message.role !== "assistant") return [];
5666
- const tcs = Array.isArray(message.toolCalls) ? message.toolCalls : [];
5667
- const ids = [];
5668
- for (const tc of tcs) if (isMatchingToolCallName(tc?.function?.name) && tc?.id) ids.push(tc.id);
5669
- return ids;
5670
- }, [message]);
5671
- const hasPending = useMemo(() => {
5672
- if (matchingToolCallIds.length === 0) return false;
5673
- const resolved = /* @__PURE__ */ new Set();
5674
- for (const m of agent.messages) if (m.role === "tool" && m.toolCallId) resolved.add(m.toolCallId);
5675
- return matchingToolCallIds.some((id) => !resolved.has(id));
5676
- }, [matchingToolCallIds, agent.messages]);
5677
- const sawRealFollowup = useMemo(() => {
5678
- const idx = agent.messages.findIndex((m) => m.id === message.id);
5679
- if (idx < 0) return false;
5680
- for (let i = idx + 1; i < agent.messages.length; i += 1) if (!isToolCallLikeMessage(agent.messages[i])) return true;
5681
- return false;
5682
- }, [agent.messages, message.id]);
5683
- const [phase, setPhase] = useState("idle");
5684
- useEffect(() => {
5685
- if (phase !== "idle") return void 0;
5686
- if (!hasPending) return void 0;
5687
- const t = setTimeout(() => setPhase("spinner"), PENDING_THRESHOLD_MS);
5688
- return () => clearTimeout(t);
5689
- }, [phase, hasPending]);
5690
- useEffect(() => {
5691
- if (phase !== "spinner") return void 0;
5692
- if (!agent.isRunning || sawRealFollowup) setPhase("check");
5693
- }, [
5694
- phase,
5695
- agent.isRunning,
5696
- sawRealFollowup
5697
- ]);
5698
- useEffect(() => {
5699
- if (phase !== "check") return void 0;
5700
- const t = setTimeout(() => setPhase("fading"), CHECK_HOLD_MS);
5701
- return () => clearTimeout(t);
5702
- }, [phase]);
5703
- useEffect(() => {
5704
- if (phase !== "fading") return void 0;
5705
- const t = setTimeout(() => setPhase("hidden"), FADE_OUT_ANIMATION_MS);
5706
- return () => clearTimeout(t);
5707
- }, [phase]);
5708
- if (copilotkit.intelligence === void 0) return null;
5709
- if (!config) return null;
5710
- if (phase === "idle" || phase === "hidden") return null;
5711
- if (message.role !== "assistant") return null;
5712
- if (!(Array.isArray(message.toolCalls) ? message.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name))) return null;
5713
- let latestMatchingAssistantId;
5714
- for (let i = agent.messages.length - 1; i >= 0; i -= 1) {
5715
- const m = agent.messages[i];
5716
- if (m.role !== "assistant") continue;
5717
- if ((Array.isArray(m.toolCalls) ? m.toolCalls : []).some((tc) => isMatchingToolCallName(tc?.function?.name))) {
5718
- latestMatchingAssistantId = m.id;
5719
- break;
5720
- }
5721
- }
5722
- if (latestMatchingAssistantId !== message.id) return null;
5723
- const showSpinner = phase === "spinner";
5724
- const isFading = phase === "fading";
5725
- return /* @__PURE__ */ jsxs("span", {
5726
- className: "cpk-intelligence-pill" + (isFading ? " cpk-intelligence-pill--fading" : ""),
5727
- role: "status",
5728
- "aria-live": "polite",
5729
- "aria-hidden": isFading || void 0,
5730
- "data-testid": `cpk-intelligence-pill-${message.id}`,
5731
- title: label,
5732
- children: [/* @__PURE__ */ jsxs("svg", {
5733
- className: "cpk-intelligence-pill__icon",
5734
- viewBox: "0 0 24 24",
5735
- width: "14",
5736
- height: "14",
5737
- "aria-hidden": "true",
5738
- children: [/* @__PURE__ */ jsx("circle", {
5739
- cx: "12",
5740
- cy: "12",
5741
- r: "9",
5742
- fill: "none",
5743
- strokeWidth: "2.5",
5744
- strokeLinecap: "round",
5745
- className: "cpk-intelligence-pill__ring" + (showSpinner ? "" : " cpk-intelligence-pill__ring--done")
5746
- }), /* @__PURE__ */ jsx("path", {
5747
- d: "M8 12.5l3 3 5-6",
5748
- fill: "none",
5749
- strokeWidth: "2.5",
5750
- strokeLinecap: "round",
5751
- strokeLinejoin: "round",
5752
- className: "cpk-intelligence-pill__check" + (showSpinner ? "" : " cpk-intelligence-pill__check--shown")
5753
- })]
5754
- }), /* @__PURE__ */ jsx("span", { children: label })]
5755
- });
5756
- }
5757
-
5758
5632
  //#endregion
5759
5633
  //#region src/v2/components/chat/CopilotChatMessageView.tsx
5760
5634
  /**
@@ -5912,12 +5786,14 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
5912
5786
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
5913
5787
  useEffect(() => {
5914
5788
  if (!config?.agentId) return;
5915
- const agent = copilotkit.getAgent(config.agentId);
5789
+ const registryAgent = copilotkit.getAgent(config.agentId);
5790
+ const agent = getThreadClone(registryAgent, config.threadId) ?? registryAgent;
5916
5791
  if (!agent) return;
5917
5792
  const subscription = agent.subscribe({ onStateChanged: forceUpdate });
5918
5793
  return () => subscription.unsubscribe();
5919
5794
  }, [
5920
5795
  config?.agentId,
5796
+ config?.threadId,
5921
5797
  copilotkit,
5922
5798
  forceUpdate
5923
5799
  ]);
@@ -6000,10 +5876,6 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
6000
5876
  renderCustomMessage,
6001
5877
  stateSnapshot
6002
5878
  }, `${message.id}-custom-after`));
6003
- if (copilotkit.intelligence !== void 0 && message.role === "assistant") elements.push(/* @__PURE__ */ jsx(IntelligenceIndicator, {
6004
- message,
6005
- agentId: config?.agentId ?? DEFAULT_AGENT_ID
6006
- }, `${message.id}-intelligence`));
6007
5879
  return elements.filter(Boolean);
6008
5880
  };
6009
5881
  const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
@@ -6952,6 +6824,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6952
6824
  const hasExplicitThreadId = !!threadId || !!existingConfig?.hasExplicitThreadId;
6953
6825
  const { agent } = useAgent({
6954
6826
  agentId: resolvedAgentId,
6827
+ threadId: resolvedThreadId,
6955
6828
  throttleMs
6956
6829
  });
6957
6830
  const { copilotkit } = useCopilotKit();
@@ -6993,7 +6866,6 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6993
6866
  let detached = false;
6994
6867
  const connectAbortController = new AbortController();
6995
6868
  if (agent instanceof HttpAgent) agent.abortController = connectAbortController;
6996
- agent.threadId = resolvedThreadId;
6997
6869
  const connect = async (agent) => {
6998
6870
  try {
6999
6871
  await copilotkit.connectAgent({ agent });
@@ -9934,5 +9806,5 @@ function validateProps(props) {
9934
9806
  }
9935
9807
 
9936
9808
  //#endregion
9937
- export { useRenderToolCall as $, IntelligenceIndicator as A, useInterrupt as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAttachmentRenderer as F, useAgent as G, useSuggestions as H, CopilotChatAssistantMessage_default as I, useRenderTool as J, useHumanInTheLoop as K, CopilotChatToolCallsView as L, CopilotChatSuggestionPill as M, CopilotChatReasoningMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatUserMessage_default as P, useRenderCustomMessages as Q, useAttachments as R, CopilotModalHeader as S, DefaultOpenIcon as T, useCapabilities as U, useConfigureSuggestions as V, UseAgentUpdate as W, useFrontendTool as X, useComponent as Y, useRenderActivityMessage as Z, WildcardToolCallRender as _, ThreadsProvider as a, createA2UIMessageRenderer as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, MCPAppsActivityContentSchema as ct, shouldShowDevConsole as d, CopilotKitInspector as dt, CopilotKitProvider as et, useToast as f, CopilotChatInput_default as ft, useCopilotContext as g, useCopilotChatConfiguration as gt, CopilotContext as h, CopilotChatConfigurationProvider as ht, ThreadsContext as i, defineToolCallRenderer as it, CopilotChatSuggestionView as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, MCPAppsActivityRenderer as lt, useCopilotMessagesContext as m, CopilotChatAudioRecorder as mt, defaultCopilotContextCategories as n, CopilotKitCoreReact as nt, useThreads as o, SandboxFunctionsContext as ot, CopilotMessagesContext as p, AudioRecorderError as pt, useDefaultRenderTool as q, CoAgentStateRenderBridge as r, useAgentContext as rt, CoAgentStateRendersContext as s, useSandboxFunctions as st, CopilotKit as t, useCopilotKit as tt, useAsyncCallback as u, MCPAppsActivityType as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useThreads$1 as z };
9938
- //# sourceMappingURL=copilotkit-ak8sGvQr.mjs.map
9809
+ export { useAgentContext as $, CopilotChatSuggestionView as A, useConfigureSuggestions as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAssistantMessage_default as F, useRenderTool as G, useCapabilities as H, CopilotChatToolCallsView as I, useRenderActivityMessage as J, useComponent as K, useAttachments as L, CopilotChatReasoningMessage_default as M, CopilotChatUserMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatAttachmentRenderer as P, CopilotKitProvider as Q, useThreads$1 as R, CopilotModalHeader as S, DefaultOpenIcon as T, useHumanInTheLoop as U, useSuggestions as V, useDefaultRenderTool as W, UseAgentUpdate as X, useRenderCustomMessages as Y, useAgent as Z, WildcardToolCallRender as _, ThreadsProvider as a, MCPAppsActivityRenderer as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, useRenderToolCall as ct, shouldShowDevConsole as d, CopilotChatInput_default as dt, defineToolCallRenderer as et, useToast as f, AudioRecorderError as ft, useCopilotContext as g, CopilotContext as h, useCopilotChatConfiguration as ht, ThreadsContext as i, MCPAppsActivityContentSchema as it, CopilotChatSuggestionPill as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, useCopilotKit as lt, useCopilotMessagesContext as m, CopilotChatConfigurationProvider as mt, defaultCopilotContextCategories as n, SandboxFunctionsContext as nt, useThreads as o, MCPAppsActivityType as ot, CopilotMessagesContext as p, CopilotChatAudioRecorder as pt, useFrontendTool as q, CoAgentStateRenderBridge as r, useSandboxFunctions as rt, CoAgentStateRendersContext as s, CopilotKitInspector as st, CopilotKit as t, createA2UIMessageRenderer as tt, useAsyncCallback as u, CopilotKitCoreReact as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useInterrupt as z };
9810
+ //# sourceMappingURL=copilotkit-N0YiBG5S.mjs.map