@copilotkit/react-core 1.69.3 → 1.70.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{copilotkit-CrNUgJiq.d.cts → copilotkit-CtF2clxZ.d.cts} +50 -23
  2. package/dist/copilotkit-CtF2clxZ.d.cts.map +1 -0
  3. package/dist/{copilotkit-BPeh62mW.cjs → copilotkit-CxLT6zFx.cjs} +214 -230
  4. package/dist/copilotkit-CxLT6zFx.cjs.map +1 -0
  5. package/dist/{copilotkit-JdHy0xdu.mjs → copilotkit-DiUK2Bhq.mjs} +209 -231
  6. package/dist/copilotkit-DiUK2Bhq.mjs.map +1 -0
  7. package/dist/{copilotkit-DgO31tBq.d.mts → copilotkit-DsWuxPUQ.d.mts} +50 -23
  8. package/dist/copilotkit-DsWuxPUQ.d.mts.map +1 -0
  9. package/dist/index.cjs +97 -24
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +18 -3
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts +18 -3
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +97 -24
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +275 -239
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/v2/headless.cjs +16 -8
  20. package/dist/v2/headless.cjs.map +1 -1
  21. package/dist/v2/headless.d.cts.map +1 -1
  22. package/dist/v2/headless.d.mts.map +1 -1
  23. package/dist/v2/headless.mjs +16 -8
  24. package/dist/v2/headless.mjs.map +1 -1
  25. package/dist/v2/index.cjs +2 -1
  26. package/dist/v2/index.d.cts +2 -2
  27. package/dist/v2/index.d.mts +2 -2
  28. package/dist/v2/index.mjs +2 -2
  29. package/dist/v2/index.umd.js +208 -229
  30. package/dist/v2/index.umd.js.map +1 -1
  31. package/package.json +13 -10
  32. package/skills/react-core/SKILL.md +1 -1
  33. package/skills/react-core/references/threads.md +1 -1
  34. package/dist/copilotkit-BPeh62mW.cjs.map +0 -1
  35. package/dist/copilotkit-CrNUgJiq.d.cts.map +0 -1
  36. package/dist/copilotkit-DgO31tBq.d.mts.map +0 -1
  37. package/dist/copilotkit-JdHy0xdu.mjs.map +0 -1
package/dist/index.umd.js CHANGED
@@ -688,7 +688,6 @@ react_markdown = __toESM(react_markdown);
688
688
  newMessages: []
689
689
  };
690
690
  }
691
- const PROTOCOL_VERSION = "2025-06-18";
692
691
  function buildSandboxHTML(extraCspDomains) {
693
692
  const baseScriptSrc = "'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob: data: http://localhost:* https://localhost:*";
694
693
  const baseFrameSrc = "* blob: data: http://localhost:* https://localhost:*";
@@ -800,6 +799,13 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
800
799
  }
801
800
  };
802
801
  const mcpAppsRequestQueue = new MCPAppsRequestQueue();
802
+ const MCP_OPEN_LINK_BLOCKED_SCHEMES = new Set([
803
+ "javascript:",
804
+ "data:",
805
+ "vbscript:",
806
+ "blob:",
807
+ "file:"
808
+ ]);
803
809
  /**
804
810
  * Activity type for MCP Apps events - must match the middleware's MCPAppsActivityType
805
811
  */
@@ -815,18 +821,32 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
815
821
  serverId: zod.z.string().optional(),
816
822
  toolInput: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
817
823
  });
818
- function isRequest(msg) {
819
- return "id" in msg && "method" in msg;
820
- }
821
- function isNotification(msg) {
822
- return !("id" in msg) && "method" in msg;
823
- }
824
824
  /**
825
825
  * MCP Apps Extension Activity Renderer
826
826
  *
827
827
  * Renders MCP Apps UI in a sandboxed iframe with full protocol support.
828
828
  * Fetches resource content on-demand via proxied MCP requests.
829
829
  */
830
+ /**
831
+ * Permissive `ui/message` schema. ext-apps restricts the request to
832
+ * `role: "user"` with no `followUp`, but CopilotKit intentionally extends
833
+ * `ui/message` with `role` ("user" | "assistant") and `followUp` (documented
834
+ * behavior with dedicated tests). We register our own handler (instead of the
835
+ * bridge's strict `onmessage`) so those extensions survive the migration.
836
+ *
837
+ * Going forward, widgets SHOULD pass the extensions under
838
+ * `params._meta.copilotkit`; the top-level `role`/`followUp` fields are the
839
+ * legacy channel, kept for backward compatibility and slated for deprecation.
840
+ */
841
+ const CopilotKitUiMessageSchema = zod.z.object({
842
+ method: zod.z.literal("ui/message"),
843
+ params: zod.z.object({
844
+ role: zod.z.string().optional(),
845
+ content: zod.z.array(zod.z.any()).optional(),
846
+ followUp: zod.z.boolean().optional(),
847
+ _meta: zod.z.record(zod.z.string(), zod.z.any()).optional()
848
+ }).passthrough()
849
+ });
830
850
  const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agent }) {
831
851
  const { copilotkit } = useCopilotKit();
832
852
  const containerRef = (0, react.useRef)(null);
@@ -840,41 +860,12 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
840
860
  contentRef.current = content;
841
861
  const agentRef = (0, react.useRef)(agent);
842
862
  agentRef.current = agent;
863
+ const bridgeRef = (0, react.useRef)(null);
843
864
  const fetchStateRef = (0, react.useRef)({
844
865
  inProgress: false,
845
866
  promise: null,
846
867
  resourceUri: null
847
868
  });
848
- const sendToIframe = (0, react.useCallback)((msg) => {
849
- if (iframeRef.current?.contentWindow) {
850
- console.log("[MCPAppsRenderer] Sending to iframe:", msg);
851
- iframeRef.current.contentWindow.postMessage(msg, "*");
852
- }
853
- }, []);
854
- const sendResponse = (0, react.useCallback)((id, result) => {
855
- sendToIframe({
856
- jsonrpc: "2.0",
857
- id,
858
- result
859
- });
860
- }, [sendToIframe]);
861
- const sendErrorResponse = (0, react.useCallback)((id, code, message) => {
862
- sendToIframe({
863
- jsonrpc: "2.0",
864
- id,
865
- error: {
866
- code,
867
- message
868
- }
869
- });
870
- }, [sendToIframe]);
871
- const sendNotification = (0, react.useCallback)((method, params) => {
872
- sendToIframe({
873
- jsonrpc: "2.0",
874
- method,
875
- params: params || {}
876
- });
877
- }, [sendToIframe]);
878
869
  (0, react.useEffect)(() => {
879
870
  const { resourceUri, serverHash, serverId } = content;
880
871
  if (fetchStateRef.current.inProgress && fetchStateRef.current.resourceUri === resourceUri) {
@@ -929,11 +920,15 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
929
920
  const container = containerRef.current;
930
921
  if (!container) return;
931
922
  let mounted = true;
932
- let messageHandler = null;
933
- let initialListener = null;
923
+ let bridge = null;
934
924
  let createdIframe = null;
935
925
  const setup = async () => {
936
926
  try {
927
+ const bridgeModule = await import("@modelcontextprotocol/ext-apps/app-bridge").catch((importErr) => {
928
+ throw new Error("MCP Apps require '@modelcontextprotocol/ext-apps' and its peer '@modelcontextprotocol/sdk'. Install them with: npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk", { cause: importErr });
929
+ });
930
+ if (!mounted) return;
931
+ const { AppBridge, PostMessageTransport } = bridgeModule;
937
932
  const iframe = document.createElement("iframe");
938
933
  createdIframe = iframe;
939
934
  iframe.style.width = "100%";
@@ -944,151 +939,108 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
944
939
  iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
945
940
  iframe.setAttribute("data-testid", "mcp-app-iframe");
946
941
  iframe.setAttribute("title", "Interactive MCP application");
947
- const sandboxReady = new Promise((resolve) => {
948
- initialListener = (event) => {
949
- if (event.source === iframe.contentWindow) {
950
- if (event.data?.method === "ui/notifications/sandbox-proxy-ready") {
951
- if (initialListener) {
952
- window.removeEventListener("message", initialListener);
953
- initialListener = null;
954
- }
955
- resolve();
956
- }
957
- }
958
- };
959
- window.addEventListener("message", initialListener);
960
- });
961
- if (!mounted) {
962
- if (initialListener) {
963
- window.removeEventListener("message", initialListener);
964
- initialListener = null;
965
- }
966
- return;
967
- }
968
942
  const cspDomains = fetchedResource._meta?.ui?.csp?.resourceDomains;
969
943
  iframe.srcdoc = buildSandboxHTML(cspDomains);
970
944
  iframeRef.current = iframe;
971
945
  container.appendChild(iframe);
972
- await sandboxReady;
973
- if (!mounted) return;
974
- console.log("[MCPAppsRenderer] Sandbox proxy ready");
975
- messageHandler = async (event) => {
976
- if (event.source !== iframe.contentWindow) return;
977
- const msg = event.data;
978
- if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0") return;
979
- console.log("[MCPAppsRenderer] Received from iframe:", msg);
980
- if (isRequest(msg)) switch (msg.method) {
981
- case "ui/initialize":
982
- sendResponse(msg.id, {
983
- protocolVersion: PROTOCOL_VERSION,
984
- hostInfo: {
985
- name: "CopilotKit MCP Apps Host",
986
- version: "1.0.0"
987
- },
988
- hostCapabilities: {
989
- openLinks: {},
990
- logging: {}
991
- },
992
- hostContext: {
993
- theme: "light",
994
- platform: "web"
995
- }
996
- });
997
- break;
998
- case "ui/message": {
999
- const currentAgent = agentRef.current;
1000
- if (!currentAgent) {
1001
- console.warn("[MCPAppsRenderer] ui/message: No agent available");
1002
- sendResponse(msg.id, { isError: false });
1003
- break;
1004
- }
1005
- try {
1006
- const params = msg.params;
1007
- const role = params.role || "user";
1008
- const textContent = params.content?.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n") || "";
1009
- if (textContent) currentAgent.addMessage({
1010
- id: crypto.randomUUID(),
1011
- role,
1012
- content: textContent
1013
- });
1014
- sendResponse(msg.id, { isError: false });
1015
- if ((params.followUp ?? role === "user") && textContent) {
1016
- const capturedThreadId = currentAgent.threadId || "default";
1017
- mcpAppsRequestQueue.enqueue(currentAgent, () => ɵrunMcpFollowUp({
1018
- host: copilotkit,
1019
- agent: currentAgent,
1020
- capturedThreadId
1021
- })).catch((err) => console.error("[MCPAppsRenderer] ui/message agent run failed:", err));
1022
- }
1023
- } catch (err) {
1024
- console.error("[MCPAppsRenderer] ui/message error:", err);
1025
- sendResponse(msg.id, { isError: true });
1026
- }
1027
- break;
1028
- }
1029
- case "ui/open-link": {
1030
- const url = msg.params?.url;
1031
- if (url) {
1032
- window.open(url, "_blank", "noopener,noreferrer");
1033
- sendResponse(msg.id, { isError: false });
1034
- } else sendErrorResponse(msg.id, -32602, "Missing url parameter");
1035
- break;
1036
- }
1037
- case "tools/call": {
1038
- const { serverHash, serverId } = contentRef.current;
1039
- const currentAgent = agentRef.current;
1040
- if (!serverHash) {
1041
- sendErrorResponse(msg.id, -32603, "No server hash available for proxying");
1042
- break;
1043
- }
1044
- if (!currentAgent) {
1045
- sendErrorResponse(msg.id, -32603, "No agent available for proxying");
1046
- break;
1047
- }
1048
- try {
1049
- const runResult = await mcpAppsRequestQueue.enqueue(currentAgent, () => currentAgent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
1050
- serverHash,
1051
- serverId,
1052
- method: "tools/call",
1053
- params: msg.params
1054
- } } }));
1055
- sendResponse(msg.id, runResult.result || {});
1056
- } catch (err) {
1057
- console.error("[MCPAppsRenderer] tools/call error:", err);
1058
- sendErrorResponse(msg.id, -32603, String(err));
1059
- }
1060
- break;
1061
- }
1062
- default: sendErrorResponse(msg.id, -32601, `Method not found: ${msg.method}`);
1063
- }
1064
- if (isNotification(msg)) switch (msg.method) {
1065
- case "ui/notifications/initialized":
1066
- console.log("[MCPAppsRenderer] Inner iframe initialized");
1067
- if (mounted) setIframeReady(true);
1068
- break;
1069
- case "ui/notifications/size-changed": {
1070
- const { width, height } = msg.params || {};
1071
- console.log("[MCPAppsRenderer] Size change:", {
1072
- width,
1073
- height
1074
- });
1075
- if (mounted) setIframeSize({
1076
- width: typeof width === "number" ? width : void 0,
1077
- height: typeof height === "number" ? height : void 0
1078
- });
1079
- break;
1080
- }
1081
- case "notifications/message":
1082
- console.log("[MCPAppsRenderer] App log:", msg.params);
1083
- break;
1084
- }
1085
- };
1086
- window.addEventListener("message", messageHandler);
946
+ const win = iframe.contentWindow;
947
+ if (!win) throw new Error("Sandbox iframe has no contentWindow");
1087
948
  let html;
1088
949
  if (fetchedResource.text) html = fetchedResource.text;
1089
950
  else if (fetchedResource.blob) html = atob(fetchedResource.blob);
1090
951
  else throw new Error("Resource has no text or blob content");
1091
- sendNotification("ui/notifications/sandbox-resource-ready", { html });
952
+ bridge = new AppBridge(null, {
953
+ name: "CopilotKit MCP Apps Host",
954
+ version: "1.0.0"
955
+ }, {
956
+ openLinks: {},
957
+ logging: {},
958
+ message: { text: {} }
959
+ }, { hostContext: {
960
+ theme: "light",
961
+ platform: "web"
962
+ } });
963
+ bridge.onsandboxready = () => {
964
+ bridge?.sendSandboxResourceReady({ html });
965
+ };
966
+ bridge.setRequestHandler(CopilotKitUiMessageSchema, async (req) => {
967
+ const currentAgent = agentRef.current;
968
+ if (!currentAgent) {
969
+ console.warn("[MCPAppsRenderer] ui/message: No agent available");
970
+ return { isError: false };
971
+ }
972
+ try {
973
+ const params = req.params;
974
+ const ck = params._meta?.copilotkit ?? {};
975
+ const role = ck.role || params.role || "user";
976
+ const textContent = params.content?.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n") || "";
977
+ if (textContent) currentAgent.addMessage({
978
+ id: crypto.randomUUID(),
979
+ role,
980
+ content: textContent
981
+ });
982
+ if ((ck.followUp ?? params.followUp ?? role === "user") && textContent) {
983
+ const capturedThreadId = currentAgent.threadId || "default";
984
+ mcpAppsRequestQueue.enqueue(currentAgent, () => ɵrunMcpFollowUp({
985
+ host: copilotkit,
986
+ agent: currentAgent,
987
+ capturedThreadId
988
+ })).catch((err) => console.error("[MCPAppsRenderer] ui/message agent run failed:", err));
989
+ }
990
+ return { isError: false };
991
+ } catch (err) {
992
+ console.error("[MCPAppsRenderer] ui/message error:", err);
993
+ return { isError: true };
994
+ }
995
+ });
996
+ bridge.onopenlink = async ({ url }) => {
997
+ let parsed;
998
+ try {
999
+ parsed = new URL(url);
1000
+ } catch {
1001
+ console.warn("[MCPAppsRenderer] ui/open-link rejected: unparseable url");
1002
+ return { isError: true };
1003
+ }
1004
+ if (MCP_OPEN_LINK_BLOCKED_SCHEMES.has(parsed.protocol)) {
1005
+ console.warn("[MCPAppsRenderer] ui/open-link rejected: blocked scheme", parsed.protocol);
1006
+ return { isError: true };
1007
+ }
1008
+ window.open(url, "_blank", "noopener,noreferrer");
1009
+ return { isError: false };
1010
+ };
1011
+ bridge.oncalltool = async (params) => {
1012
+ const { serverHash, serverId } = contentRef.current;
1013
+ const currentAgent = agentRef.current;
1014
+ if (!serverHash) throw new Error("No server hash available for proxying");
1015
+ if (!currentAgent) throw new Error("No agent available for proxying");
1016
+ return (await mcpAppsRequestQueue.enqueue(currentAgent, () => currentAgent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
1017
+ serverHash,
1018
+ serverId,
1019
+ method: "tools/call",
1020
+ params
1021
+ } } }))).result || { content: [] };
1022
+ };
1023
+ bridge.onsizechange = (p) => {
1024
+ if (!mounted) return;
1025
+ const { width, height } = p || {};
1026
+ setIframeSize({
1027
+ width: typeof width === "number" ? width : void 0,
1028
+ height: typeof height === "number" ? height : void 0
1029
+ });
1030
+ };
1031
+ bridge.oninitialized = () => {
1032
+ if (mounted) setIframeReady(true);
1033
+ };
1034
+ bridge.onloggingmessage = (p) => {
1035
+ console.log("[MCPAppsRenderer] App log:", p);
1036
+ };
1037
+ const transport = new PostMessageTransport(win, win);
1038
+ await bridge.connect(transport);
1039
+ if (!mounted) {
1040
+ await bridge.close();
1041
+ return;
1042
+ }
1043
+ bridgeRef.current = bridge;
1092
1044
  } catch (err) {
1093
1045
  console.error("[MCPAppsRenderer] Setup error:", err);
1094
1046
  if (mounted) setError(err instanceof Error ? err : new Error(String(err)));
@@ -1097,11 +1049,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
1097
1049
  setup();
1098
1050
  return () => {
1099
1051
  mounted = false;
1100
- if (initialListener) {
1101
- window.removeEventListener("message", initialListener);
1102
- initialListener = null;
1103
- }
1104
- if (messageHandler) window.removeEventListener("message", messageHandler);
1052
+ bridgeRef.current = null;
1053
+ bridge?.close();
1105
1054
  if (createdIframe) {
1106
1055
  createdIframe.remove();
1107
1056
  createdIframe = null;
@@ -1111,9 +1060,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
1111
1060
  }, [
1112
1061
  isLoading,
1113
1062
  fetchedResource,
1114
- sendNotification,
1115
- sendResponse,
1116
- sendErrorResponse
1063
+ copilotkit
1117
1064
  ]);
1118
1065
  (0, react.useEffect)(() => {
1119
1066
  if (iframeRef.current) {
@@ -1125,25 +1072,11 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
1125
1072
  }
1126
1073
  }, [iframeSize]);
1127
1074
  (0, react.useEffect)(() => {
1128
- if (iframeReady && content.toolInput) {
1129
- console.log("[MCPAppsRenderer] Sending tool input:", content.toolInput);
1130
- sendNotification("ui/notifications/tool-input", { arguments: content.toolInput });
1131
- }
1132
- }, [
1133
- iframeReady,
1134
- content.toolInput,
1135
- sendNotification
1136
- ]);
1075
+ if (iframeReady && content.toolInput) bridgeRef.current?.sendToolInput({ arguments: content.toolInput });
1076
+ }, [iframeReady, content.toolInput]);
1137
1077
  (0, react.useEffect)(() => {
1138
- if (iframeReady && content.result) {
1139
- console.log("[MCPAppsRenderer] Sending tool result:", content.result);
1140
- sendNotification("ui/notifications/tool-result", content.result);
1141
- }
1142
- }, [
1143
- iframeReady,
1144
- content.result,
1145
- sendNotification
1146
- ]);
1078
+ if (iframeReady && content.result) bridgeRef.current?.sendToolResult(content.result);
1079
+ }, [iframeReady, content.result]);
1147
1080
  const borderStyle = fetchedResource?._meta?.ui?.prefersBorder === true ? {
1148
1081
  borderRadius: "8px",
1149
1082
  backgroundColor: "#f9f9f9",
@@ -2363,6 +2296,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2363
2296
  const a2uiCatalogProvided = !!a2ui?.catalog;
2364
2297
  const a2uiActive = runtimeA2UIEnabled || a2uiCatalogProvided;
2365
2298
  const [runtimeLicenseStatus, setRuntimeLicenseStatus] = (0, react.useState)(void 0);
2299
+ const [runtimeEntitlements, setRuntimeEntitlements] = (0, react.useState)(void 0);
2300
+ const [runtimeEntitlementRetryPending, setRuntimeEntitlementRetryPending] = (0, react.useState)(false);
2366
2301
  const requestInspectorOpen = (0, react.useCallback)((request) => {
2367
2302
  setInspectorOpenRequest({ ...request });
2368
2303
  }, []);
@@ -2553,6 +2488,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2553
2488
  setRuntimeA2UIEnabled(copilotkit.a2uiEnabled);
2554
2489
  setRuntimeOpenGenUIEnabled(copilotkit.openGenerativeUIEnabled);
2555
2490
  setRuntimeLicenseStatus(copilotkit.licenseStatus);
2491
+ setRuntimeEntitlements(copilotkit.runtimeEntitlements);
2492
+ setRuntimeEntitlementRetryPending(copilotkit.runtimeEntitlementRetryPending);
2556
2493
  };
2557
2494
  const subscription = copilotkit.subscribe({ onRuntimeConnectionStatusChanged: syncRuntimeInfo });
2558
2495
  syncRuntimeInfo();
@@ -2693,7 +2630,26 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2693
2630
  copilotkit,
2694
2631
  executingToolCallIds
2695
2632
  }), [copilotkit, executingToolCallIds]);
2696
- const licenseContextValue = (0, react.useMemo)(() => (0, _copilotkit_shared.createLicenseContextValue)(runtimeLicenseStatus), [runtimeLicenseStatus]);
2633
+ const retryableRuntimeEntitlementFailure = runtimeEntitlements?.status !== "ready" && runtimeEntitlements?.error.retryable === true;
2634
+ const hasNonReadyRuntimeEntitlement = runtimeEntitlements !== void 0 && runtimeEntitlements.status !== "ready";
2635
+ const hasLegacyRuntimeEntitlementFallback = runtimeLicenseStatus === "valid" || runtimeLicenseStatus === "expiring";
2636
+ const runtimeEntitlementRetryInProgress = retryableRuntimeEntitlementFailure && runtimeEntitlementRetryPending && !hasLegacyRuntimeEntitlementFallback;
2637
+ const runtimeEntitlementFailureSettled = hasNonReadyRuntimeEntitlement && !runtimeEntitlementRetryInProgress && !hasLegacyRuntimeEntitlementFallback;
2638
+ const licenseContextValue = (0, react.useMemo)(() => {
2639
+ const runtimeLicenseContext = (0, _copilotkit_shared.createLicenseContextValue)(runtimeEntitlementRetryInProgress ? void 0 : runtimeLicenseStatus, runtimeEntitlements);
2640
+ if (!runtimeEntitlementFailureSettled) return runtimeLicenseContext;
2641
+ return {
2642
+ ...runtimeLicenseContext,
2643
+ checkFeature: () => false,
2644
+ getLimit: () => null
2645
+ };
2646
+ }, [
2647
+ runtimeEntitlementFailureSettled,
2648
+ runtimeEntitlementRetryInProgress,
2649
+ runtimeEntitlements,
2650
+ runtimeLicenseStatus
2651
+ ]);
2652
+ const runtimeLicenseWarningStatus = runtimeEntitlementRetryInProgress ? void 0 : licenseContextValue.status ?? void 0;
2697
2653
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SandboxFunctionsContext.Provider, {
2698
2654
  value: sandboxFunctionsList,
2699
2655
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitContext.Provider, {
@@ -2713,10 +2669,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2713
2669
  openRequest: inspectorOpenRequest
2714
2670
  }) : null]
2715
2671
  }),
2716
- runtimeLicenseStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "no_license" }),
2717
- runtimeLicenseStatus === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expired" }),
2718
- runtimeLicenseStatus === "invalid" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "invalid" }),
2719
- runtimeLicenseStatus === "expiring" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expiring" })
2672
+ runtimeLicenseWarningStatus === "none" && !resolvedPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "no_license" }),
2673
+ runtimeLicenseWarningStatus === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expired" }),
2674
+ runtimeLicenseWarningStatus === "invalid" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "invalid" }),
2675
+ runtimeLicenseWarningStatus === "expiring" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LicenseWarningBanner, { type: "expiring" })
2720
2676
  ]
2721
2677
  })
2722
2678
  })
@@ -2774,7 +2730,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2774
2730
  function useFrontendTool$1(tool, deps) {
2775
2731
  const { copilotkit } = useCopilotKit();
2776
2732
  const extraDeps = deps ?? EMPTY_DEPS;
2777
- (0, react.useEffect)(() => {
2733
+ (0, react.useLayoutEffect)(() => {
2778
2734
  const name = tool.name;
2779
2735
  if (copilotkit.getTool({
2780
2736
  toolName: name,
@@ -2797,12 +2753,15 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2797
2753
  tool.name,
2798
2754
  tool.available,
2799
2755
  copilotkit,
2800
- JSON.stringify(extraDeps)
2756
+ JSON.stringify(extraDeps),
2757
+ JSON.stringify(tool.webmcp ?? null)
2801
2758
  ]);
2802
2759
  }
2803
2760
 
2804
2761
  //#endregion
2805
2762
  //#region src/v2/hooks/use-human-in-the-loop.tsx
2763
+ /** Registration name of a catch-all tool: handles any otherwise-unhandled call. */
2764
+ const WILDCARD_TOOL_NAME = "*";
2806
2765
  function useHumanInTheLoop$1(tool, deps) {
2807
2766
  const { copilotkit } = useCopilotKit();
2808
2767
  const resolvePromiseRef = (0, react.useRef)(null);
@@ -2838,10 +2797,11 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2838
2797
  }, []);
2839
2798
  const RenderComponent = (0, react.useCallback)((props) => {
2840
2799
  const ToolComponent = tool.render;
2800
+ const name = tool.name === WILDCARD_TOOL_NAME ? props.name : tool.name;
2841
2801
  if (props.status === _copilotkit_core.ToolCallStatus.InProgress) {
2842
2802
  const enhancedProps = {
2843
2803
  ...props,
2844
- name: tool.name,
2804
+ name,
2845
2805
  description: tool.description || "",
2846
2806
  agentId: tool.agentId,
2847
2807
  respond: void 0
@@ -2850,7 +2810,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2850
2810
  } else if (props.status === _copilotkit_core.ToolCallStatus.Executing) {
2851
2811
  const enhancedProps = {
2852
2812
  ...props,
2853
- name: tool.name,
2813
+ name,
2854
2814
  description: tool.description || "",
2855
2815
  agentId: tool.agentId,
2856
2816
  respond
@@ -2859,7 +2819,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2859
2819
  } else if (props.status === _copilotkit_core.ToolCallStatus.Complete) {
2860
2820
  const enhancedProps = {
2861
2821
  ...props,
2862
- name: tool.name,
2822
+ name,
2863
2823
  description: tool.description || "",
2864
2824
  agentId: tool.agentId,
2865
2825
  respond: void 0
@@ -2879,7 +2839,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2879
2839
  handler,
2880
2840
  render: RenderComponent
2881
2841
  }, deps);
2882
- (0, react.useEffect)(() => {
2842
+ (0, react.useLayoutEffect)(() => {
2883
2843
  return () => {
2884
2844
  copilotkit.removeHookRenderToolCall(tool.name, tool.agentId);
2885
2845
  };
@@ -3299,8 +3259,12 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3299
3259
  }
3300
3260
 
3301
3261
  //#endregion
3302
- //#region src/v2/hooks/use-interrupt.tsx
3262
+ //#region src/v2/types/interrupt.ts
3263
+ /** Name of the legacy custom event agents emit to signal an interrupt. */
3303
3264
  const INTERRUPT_EVENT_NAME = "on_interrupt";
3265
+
3266
+ //#endregion
3267
+ //#region src/v2/hooks/use-interrupt.tsx
3304
3268
  function isPromiseLike(value) {
3305
3269
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof Reflect.get(value, "then") === "function";
3306
3270
  }
@@ -4507,7 +4471,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4507
4471
  switch (error.code) {
4508
4472
  case _copilotkit_shared.CopilotKitErrorCode.MISSING_PUBLIC_API_KEY_ERROR: return { primary: {
4509
4473
  label: "Show me how",
4510
- onClick: () => window.open("https://docs.copilotkit.ai/premium/overview#getting-access", "_blank", "noopener,noreferrer")
4474
+ onClick: () => window.open("https://docs.copilotkit.ai/intelligence/overview#plans-and-access", "_blank", "noopener,noreferrer")
4511
4475
  } };
4512
4476
  case _copilotkit_shared.CopilotKitErrorCode.UPGRADE_REQUIRED_ERROR: return { primary: {
4513
4477
  label: "Upgrade",
@@ -5753,13 +5717,18 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5753
5717
  const renderToolCall = useRenderToolCall$1();
5754
5718
  return (0, react.useCallback)((message, messages) => {
5755
5719
  if (!message?.toolCalls?.length) return null;
5756
- const toolCall = message.toolCalls[0];
5757
- if (!toolCall) return null;
5758
- const toolMessage = messages?.find((m) => m.role === "tool" && m.toolCallId === toolCall.id);
5759
- return () => renderToolCall({
5760
- toolCall,
5761
- toolMessage
5762
- });
5720
+ const toolCalls = message.toolCalls;
5721
+ return () => {
5722
+ const renderedToolCalls = toolCalls.map((toolCall) => {
5723
+ const toolMessage = messages?.find((m) => m.role === "tool" && m.toolCallId === toolCall.id);
5724
+ return renderToolCall({
5725
+ toolCall,
5726
+ toolMessage
5727
+ });
5728
+ }).filter((renderedToolCall) => renderedToolCall !== null);
5729
+ if (!renderedToolCalls.length) return null;
5730
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: renderedToolCalls });
5731
+ };
5763
5732
  }, [renderToolCall]);
5764
5733
  }
5765
5734
 
@@ -6094,7 +6063,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6094
6063
  * `useCopilotChatHeadless_c` is for building fully custom UI (headless UI) implementations.
6095
6064
  *
6096
6065
  * <Callout title="This is a CopilotKit Intelligence feature">
6097
- * Read more about <a href="/premium/overview">CopilotKit Intelligence</a>.
6066
+ * Read more about <a href="/intelligence/overview">CopilotKit Intelligence</a>.
6098
6067
  *
6099
6068
  * Usage is generous and **free** to get started.
6100
6069
  * </Callout>
@@ -6325,7 +6294,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6325
6294
  * See https://docs.copilotkit.ai/reference/v2/hooks/useFrontendTool
6326
6295
  */
6327
6296
  function useFrontendTool(tool, dependencies) {
6328
- const { name, description, parameters, render, followUp, available } = tool;
6297
+ const { name, description, parameters, render, followUp, available, webmcp } = tool;
6329
6298
  const zodParameters = (0, _copilotkit_shared.getZodParameters)(parameters);
6330
6299
  const renderRef = (0, react.useRef)(render);
6331
6300
  (0, react.useEffect)(() => {
@@ -6356,7 +6325,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6356
6325
  handler: tool.handler ? (args) => handlerRef.current?.(args) : void 0,
6357
6326
  followUp,
6358
6327
  render: normalizedRender,
6359
- available: available === void 0 ? void 0 : available !== "disabled"
6328
+ available: available === void 0 ? void 0 : available !== "disabled",
6329
+ webmcp
6360
6330
  });
6361
6331
  }
6362
6332
 
@@ -6454,20 +6424,24 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6454
6424
  if (!render) return null;
6455
6425
  const rendered = render((() => {
6456
6426
  const mappedArgs = args.args;
6427
+ const toolCallName = args.name;
6457
6428
  switch (args.status) {
6458
6429
  case _copilotkit_core.ToolCallStatus.InProgress: return {
6430
+ name: toolCallName,
6459
6431
  args: mappedArgs,
6460
6432
  respond: args.respond,
6461
6433
  status: args.status,
6462
6434
  handler: void 0
6463
6435
  };
6464
6436
  case _copilotkit_core.ToolCallStatus.Executing: return {
6437
+ name: toolCallName,
6465
6438
  args: mappedArgs,
6466
6439
  respond: args.respond,
6467
6440
  status: args.status,
6468
6441
  handler: () => {}
6469
6442
  };
6470
6443
  case _copilotkit_core.ToolCallStatus.Complete: return {
6444
+ name: toolCallName,
6471
6445
  args: mappedArgs,
6472
6446
  respond: args.respond,
6473
6447
  status: args.status,
@@ -6627,10 +6601,21 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6627
6601
  * This hooks enables you to dynamically generate UI elements and render them in the copilot chat. For more information, check out the [Generative UI](/guides/generative-ui) page.
6628
6602
  */
6629
6603
  function getActionConfig(action) {
6630
- if (action.name === "*") return {
6631
- type: "render",
6632
- action
6633
- };
6604
+ if (action.name === "*") {
6605
+ const catchAll = action;
6606
+ const waitRender = catchAll.renderAndWaitForResponse ?? catchAll.renderAndWait;
6607
+ if (waitRender) return {
6608
+ type: "hitl",
6609
+ action: {
6610
+ ...catchAll,
6611
+ render: waitRender
6612
+ }
6613
+ };
6614
+ return {
6615
+ type: "render",
6616
+ action
6617
+ };
6618
+ }
6634
6619
  if ("renderAndWaitForResponse" in action || "renderAndWait" in action) {
6635
6620
  let render = action.render;
6636
6621
  if (!render && "renderAndWaitForResponse" in action) render = action.renderAndWaitForResponse;
@@ -6941,37 +6926,87 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6941
6926
 
6942
6927
  //#endregion
6943
6928
  //#region src/v1-deprecated/hooks/use-agent-nodename.ts
6944
- /**
6929
+ const initialAgentNodeNameState = {
6930
+ nodeName: "start",
6931
+ lastActiveNodeName: "start",
6932
+ hasLegacyInterrupt: false
6933
+ };
6934
+ const transitionAgentNodeName = (state, event) => {
6935
+ switch (event.type) {
6936
+ case "reset":
6937
+ case "runStarted": return initialAgentNodeNameState;
6938
+ case "stepStarted": return {
6939
+ ...state,
6940
+ nodeName: event.nodeName,
6941
+ lastActiveNodeName: event.nodeName
6942
+ };
6943
+ case "legacyInterruptReceived": return {
6944
+ ...state,
6945
+ hasLegacyInterrupt: true
6946
+ };
6947
+ case "runFinished":
6948
+ if (event.outcome === "interrupt" || state.hasLegacyInterrupt) return {
6949
+ ...state,
6950
+ nodeName: state.lastActiveNodeName,
6951
+ hasLegacyInterrupt: false
6952
+ };
6953
+ return {
6954
+ ...state,
6955
+ nodeName: "end",
6956
+ hasLegacyInterrupt: false
6957
+ };
6958
+ case "runError": return {
6959
+ ...state,
6960
+ nodeName: "end",
6961
+ hasLegacyInterrupt: false
6962
+ };
6963
+ }
6964
+ };
6965
+ /**
6945
6966
  * Tracks the node the agent is currently executing.
6946
6967
  *
6947
6968
  * Backed by state rather than a ref: mutating a ref schedules no render, so
6948
6969
  * consumers such as `useCoAgent().nodeName` kept reporting whichever node was
6949
- * current at their last render and never updated on their own.
6970
+ * current at their last render and never updated on their own. Interrupt-aware
6971
+ * transitions keep the last active node available while an interrupt is pending.
6950
6972
  */
6951
6973
  function useAgentNodeName(agentName) {
6952
6974
  const { agent } = useAgent({ agentId: agentName });
6953
- const [nodeName, setNodeName] = (0, react.useState)("start");
6975
+ const [nodeNameState, setNodeNameState] = (0, react.useState)(initialAgentNodeNameState);
6954
6976
  (0, react.useEffect)(() => {
6977
+ const transition = (event) => {
6978
+ setNodeNameState((state) => transitionAgentNodeName(state, event));
6979
+ };
6980
+ transition({ type: "reset" });
6955
6981
  if (!agent) return;
6956
6982
  const subscription = agent.subscribe({
6957
6983
  onStepStartedEvent: ({ event }) => {
6958
- setNodeName(event.stepName);
6984
+ transition({
6985
+ type: "stepStarted",
6986
+ nodeName: event.stepName
6987
+ });
6959
6988
  },
6960
6989
  onRunStartedEvent: () => {
6961
- setNodeName("start");
6990
+ transition({ type: "runStarted" });
6962
6991
  },
6963
- onRunFinishedEvent: () => {
6964
- setNodeName("end");
6992
+ onRunFinishedEvent: ({ outcome }) => {
6993
+ transition({
6994
+ type: "runFinished",
6995
+ outcome
6996
+ });
6965
6997
  },
6966
6998
  onRunErrorEvent: () => {
6967
- setNodeName("end");
6999
+ transition({ type: "runError" });
7000
+ },
7001
+ onCustomEvent: ({ event }) => {
7002
+ if (event.name === INTERRUPT_EVENT_NAME) transition({ type: "legacyInterruptReceived" });
6968
7003
  }
6969
7004
  });
6970
7005
  return () => {
6971
7006
  subscription.unsubscribe();
6972
7007
  };
6973
- }, [agent]);
6974
- return nodeName;
7008
+ }, [agent, agentName]);
7009
+ return nodeNameState.nodeName;
6975
7010
  }
6976
7011
 
6977
7012
  //#endregion
@@ -7174,6 +7209,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7174
7209
  agent?.threadId,
7175
7210
  agent?.isRunning,
7176
7211
  agent?.agentId,
7212
+ nodeName,
7177
7213
  handleStateUpdate,
7178
7214
  options.name
7179
7215
  ]);