@copilotkit/runtime 1.75.1 → 1.76.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 (61) hide show
  1. package/dist/agent/converters/aisdk.cjs +68 -10
  2. package/dist/agent/converters/aisdk.cjs.map +1 -1
  3. package/dist/agent/converters/aisdk.d.cts.map +1 -1
  4. package/dist/agent/converters/aisdk.d.mts.map +1 -1
  5. package/dist/agent/converters/aisdk.mjs +67 -11
  6. package/dist/agent/converters/aisdk.mjs.map +1 -1
  7. package/dist/agent/converters/tanstack.cjs +8 -6
  8. package/dist/agent/converters/tanstack.cjs.map +1 -1
  9. package/dist/agent/converters/tanstack.d.cts.map +1 -1
  10. package/dist/agent/converters/tanstack.d.mts.map +1 -1
  11. package/dist/agent/converters/tanstack.mjs +9 -7
  12. package/dist/agent/converters/tanstack.mjs.map +1 -1
  13. package/dist/agent/converters/usage.cjs +8 -2
  14. package/dist/agent/converters/usage.cjs.map +1 -1
  15. package/dist/agent/converters/usage.d.cts +3 -1
  16. package/dist/agent/converters/usage.d.cts.map +1 -1
  17. package/dist/agent/converters/usage.d.mts +3 -1
  18. package/dist/agent/converters/usage.d.mts.map +1 -1
  19. package/dist/agent/converters/usage.mjs +8 -2
  20. package/dist/agent/converters/usage.mjs.map +1 -1
  21. package/dist/agent/index.cjs +164 -34
  22. package/dist/agent/index.cjs.map +1 -1
  23. package/dist/agent/index.d.cts +18 -6
  24. package/dist/agent/index.d.cts.map +1 -1
  25. package/dist/agent/index.d.mts +18 -6
  26. package/dist/agent/index.d.mts.map +1 -1
  27. package/dist/agent/index.mjs +166 -36
  28. package/dist/agent/index.mjs.map +1 -1
  29. package/dist/agent/mcp-tool-names.cjs +84 -0
  30. package/dist/agent/mcp-tool-names.cjs.map +1 -0
  31. package/dist/agent/mcp-tool-names.mjs +81 -0
  32. package/dist/agent/mcp-tool-names.mjs.map +1 -0
  33. package/dist/lib/runtime/copilot-runtime.cjs +20 -9
  34. package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
  35. package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
  36. package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
  37. package/dist/lib/runtime/copilot-runtime.mjs +20 -9
  38. package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
  39. package/dist/package.cjs +6 -7
  40. package/dist/package.mjs +6 -7
  41. package/dist/v2/runtime/handlers/intelligence/connect.cjs +1 -0
  42. package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
  43. package/dist/v2/runtime/handlers/intelligence/connect.mjs +1 -0
  44. package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
  45. package/dist/v2/runtime/handlers/shared/agent-utils.cjs +90 -4
  46. package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
  47. package/dist/v2/runtime/handlers/shared/agent-utils.mjs +90 -4
  48. package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
  49. package/dist/v2/runtime/runner/in-memory.cjs +16 -1
  50. package/dist/v2/runtime/runner/in-memory.cjs.map +1 -1
  51. package/dist/v2/runtime/runner/in-memory.d.cts.map +1 -1
  52. package/dist/v2/runtime/runner/in-memory.d.mts.map +1 -1
  53. package/dist/v2/runtime/runner/in-memory.mjs +16 -1
  54. package/dist/v2/runtime/runner/in-memory.mjs.map +1 -1
  55. package/dist/v2/runtime/runner/intelligence.cjs +5 -2
  56. package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
  57. package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
  58. package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
  59. package/dist/v2/runtime/runner/intelligence.mjs +6 -3
  60. package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
  61. package/package.json +7 -10
@@ -6,6 +6,7 @@ const require_aisdk = require('./converters/aisdk.cjs');
6
6
  const require_tanstack = require('./converters/tanstack.cjs');
7
7
  const require_registry = require('../v2/runtime/intelligence-platform/skill-registry/registry.cjs');
8
8
  const require_learned_skills = require('./learned-skills.cjs');
9
+ const require_mcp_tool_names = require('./mcp-tool-names.cjs');
9
10
  let _ai_sdk_openai = require("@ai-sdk/openai");
10
11
  let _copilotkit_shared = require("@copilotkit/shared");
11
12
  let _ai_sdk_google = require("@ai-sdk/google");
@@ -27,6 +28,64 @@ let _modelcontextprotocol_sdk_client_streamableHttp_js = require("@modelcontextp
27
28
  * @param apiKey - Optional API key to use instead of environment variables
28
29
  * @returns LanguageModel instance
29
30
  */
31
+ /**
32
+ * An AG-UI tool result as the AI SDK's tool result output.
33
+ *
34
+ * Providers hold one response per tool call, and the Google adapter emits one
35
+ * functionResponse per text entry of a content list, so all of a result's text
36
+ * is collected into a single entry: text parts run together, and a
37
+ * URL-referenced part contributes the URL it carries on a line of its own —
38
+ * the bytes are not here to hand over, and the reference is the content the
39
+ * tool actually returned. A result with no inline media is that text alone.
40
+ * One with inline media is a content list: the text entry first, when there is
41
+ * any, then each media part with its bytes and media type. A media-only result
42
+ * is media alone: a placeholder text would be content the tool never returned.
43
+ * Which adapters can place media inside a tool response is theirs to decide.
44
+ * A provider file handle (`file` source) is neither bytes nor a URL, and this
45
+ * path cannot hand it to the provider, so the part is dropped with a warning.
46
+ * A result whose parts were all dropped is the empty string, as AG-UI 1.0
47
+ * requires: the call must still be answered.
48
+ */
49
+ function warnDroppedFileSource(what) {
50
+ console.warn(`[CopilotKit] Dropping a ${what} that references a provider file handle: it is not a URL or inline data, so it cannot be sent to the model here.`);
51
+ }
52
+ function toolResultOutput(content) {
53
+ if (typeof content === "string") return {
54
+ type: "text",
55
+ value: content
56
+ };
57
+ const segments = [];
58
+ const media = [];
59
+ let open = false;
60
+ for (const part of content) if (part.type === "text") {
61
+ if (open) segments[segments.length - 1] += part.text;
62
+ else segments.push(part.text);
63
+ open = true;
64
+ } else if (part.source.type === "file") warnDroppedFileSource(`${part.type} part in a tool result`);
65
+ else if (part.source.type === "url") {
66
+ segments.push(part.source.value);
67
+ open = false;
68
+ } else {
69
+ media.push({
70
+ type: "media",
71
+ data: part.source.value,
72
+ mediaType: part.source.mimeType
73
+ });
74
+ open = false;
75
+ }
76
+ const text = segments.join("\n");
77
+ if (media.length === 0) return {
78
+ type: "text",
79
+ value: text
80
+ };
81
+ return {
82
+ type: "content",
83
+ value: [...text.length > 0 ? [{
84
+ type: "text",
85
+ text
86
+ }] : [], ...media]
87
+ };
88
+ }
30
89
  function resolveModel(spec, apiKey) {
31
90
  if (typeof spec !== "string") return spec;
32
91
  const parts = spec.replace("/", ":").trim().split(":");
@@ -151,6 +210,7 @@ function convertUserMessageContent(content) {
151
210
  image: source.value,
152
211
  mediaType: source.mimeType
153
212
  });
213
+ else if (source.type === "file") warnDroppedFileSource("image part");
154
214
  else if (source.type === "url") try {
155
215
  parts.push({
156
216
  type: "image",
@@ -172,6 +232,7 @@ function convertUserMessageContent(content) {
172
232
  data: source.value,
173
233
  mediaType: source.mimeType
174
234
  });
235
+ else if (source.type === "file") warnDroppedFileSource(`${part.type} part`);
175
236
  else if (source.type === "url") try {
176
237
  parts.push({
177
238
  type: "file",
@@ -277,10 +338,7 @@ function convertMessagesToVercelAISDKMessages(messages, options = {}) {
277
338
  type: "tool-result",
278
339
  toolCallId: message.toolCallId,
279
340
  toolName,
280
- output: {
281
- type: "text",
282
- value: message.content
283
- }
341
+ output: toolResultOutput(message.content)
284
342
  }]
285
343
  };
286
344
  result.push(toolMsg);
@@ -458,7 +516,8 @@ var BuiltInAgent = class BuiltInAgent extends _ag_ui_client.AbstractAgent {
458
516
  const startEvent = {
459
517
  type: _ag_ui_client.EventType.RUN_STARTED,
460
518
  threadId: input.threadId,
461
- runId: input.runId
519
+ runId: input.runId,
520
+ protocolVersion: _ag_ui_client.PROTOCOL_VERSION
462
521
  };
463
522
  subscriber.next(startEvent);
464
523
  const model = resolveModel(config.model, config.apiKey);
@@ -627,16 +686,19 @@ This is state from the application that you can edit by calling AGUISendStateSna
627
686
  }
628
687
  })
629
688
  };
630
- if (config.mcpClients && config.mcpClients.length > 0) for (const client of config.mcpClients) {
689
+ const mcpToolSources = [];
690
+ const userMcpClients = config.mcpClients ?? [];
691
+ for (const [index, client] of userMcpClients.entries()) {
631
692
  const mcpTools = await client.tools();
632
693
  abortController.signal.throwIfAborted();
633
- streamTextParams.tools = {
634
- ...streamTextParams.tools,
635
- ...mcpTools
636
- };
694
+ mcpToolSources.push({
695
+ label: `mcp${index + 1}`,
696
+ tools: mcpTools
697
+ });
637
698
  }
638
699
  const allMcpServers = [...config.mcpServers ?? []];
639
- if (allMcpServers.length > 0) for (const serverConfig of allMcpServers) {
700
+ if (allMcpServers.length > 0) for (const [index, serverConfig] of allMcpServers.entries()) {
701
+ const label = serverConfig.name ? require_mcp_tool_names.toToolNamePrefix(serverConfig.name) : `mcp${userMcpClients.length + index + 1}`;
640
702
  let transport;
641
703
  if (serverConfig.type === "http") transport = new _modelcontextprotocol_sdk_client_streamableHttp_js.StreamableHTTPClientTransport(new URL(serverConfig.url), serverConfig.options);
642
704
  else if (serverConfig.type === "sse") {
@@ -661,16 +723,17 @@ This is state from the application that you can edit by calling AGUISendStateSna
661
723
  try {
662
724
  const mcpTools = await mcpClient.tools();
663
725
  abortController.signal.throwIfAborted();
664
- streamTextParams.tools = {
665
- ...streamTextParams.tools,
666
- ...mcpTools
667
- };
726
+ mcpToolSources.push({
727
+ label,
728
+ tools: mcpTools
729
+ });
668
730
  } catch (err) {
669
731
  abortController.signal.throwIfAborted();
670
732
  console.error(`[CopilotKit] MCP server ${serverConfig.url} tools() failed — skipping its tools for this run:`, err);
671
733
  }
672
734
  }
673
735
  }
736
+ if (mcpToolSources.length > 0) streamTextParams.tools = require_mcp_tool_names.mergeMCPTools(streamTextParams.tools ?? {}, mcpToolSources);
674
737
  if (this.skillRegistry) {
675
738
  require_learned_skills.assertNoSkillToolConflicts(streamTextParams.tools ?? {});
676
739
  streamTextParams.tools = {
@@ -703,6 +766,7 @@ This is state from the application that you can edit by calling AGUISendStateSna
703
766
  }
704
767
  return state;
705
768
  };
769
+ const warnedUnknownPartTypes = /* @__PURE__ */ new Set();
706
770
  for await (const part of response.fullStream) {
707
771
  if (part.type !== "reasoning-delta") closeReasoningIfOpen();
708
772
  switch (part.type) {
@@ -710,7 +774,8 @@ This is state from the application that you can edit by calling AGUISendStateSna
710
774
  const abortEndEvent = {
711
775
  type: _ag_ui_client.EventType.RUN_FINISHED,
712
776
  threadId: input.threadId,
713
- runId: input.runId
777
+ runId: input.runId,
778
+ ...input.protocolVersion !== void 0 ? { outcome: { type: "cancelled" } } : {}
714
779
  };
715
780
  subscriber.next(abortEndEvent);
716
781
  terminalEventEmitted = true;
@@ -718,8 +783,7 @@ This is state from the application that you can edit by calling AGUISendStateSna
718
783
  break;
719
784
  }
720
785
  case "reasoning-start": {
721
- const providedId = "id" in part ? part.id : void 0;
722
- reasoningMessageId = !providedId || providedId === "0" || /^(txt|reasoning|msg)-0$/.test(providedId) ? (0, _copilotkit_shared.randomUUID)() : providedId;
786
+ reasoningMessageId = require_aisdk.resolveStreamPartMessageId("id" in part ? part.id : void 0);
723
787
  const reasoningStartEvent = {
724
788
  type: _ag_ui_client.EventType.REASONING_START,
725
789
  messageId: reasoningMessageId
@@ -775,11 +839,9 @@ This is state from the application that you can edit by calling AGUISendStateSna
775
839
  break;
776
840
  }
777
841
  case "tool-input-end": break;
778
- case "text-start": {
779
- const providedId = "id" in part ? part.id : void 0;
780
- messageId = !providedId || providedId === "0" || /^(txt|reasoning|msg)-0$/.test(providedId) ? (0, _copilotkit_shared.randomUUID)() : providedId;
842
+ case "text-start":
843
+ messageId = require_aisdk.resolveStreamPartMessageId("id" in part ? part.id : void 0);
781
844
  break;
782
- }
783
845
  case "text-delta": {
784
846
  const textDelta = "text" in part ? part.text : "";
785
847
  const textEvent = {
@@ -886,6 +948,24 @@ This is state from the application that you can edit by calling AGUISendStateSna
886
948
  subscriber.next(resultEvent);
887
949
  break;
888
950
  }
951
+ case "tool-error": {
952
+ const toolCallId = part.toolCallId;
953
+ const toolName = "toolName" in part && part.toolName || toolCallStates.get(toolCallId)?.toolName || "";
954
+ if (toolName && interruptToolNames.has(toolName)) {
955
+ toolCallStates.delete(toolCallId);
956
+ break;
957
+ }
958
+ toolCallStates.delete(toolCallId);
959
+ const resultEvent = {
960
+ type: _ag_ui_client.EventType.TOOL_CALL_RESULT,
961
+ role: "tool",
962
+ messageId: (0, _copilotkit_shared.randomUUID)(),
963
+ toolCallId,
964
+ content: `Error: ${require_aisdk.formatToolError(part.error)}`
965
+ };
966
+ subscriber.next(resultEvent);
967
+ break;
968
+ }
889
969
  case "finish": {
890
970
  const model = streamTextParams.model;
891
971
  const finishedEvent = {
@@ -921,6 +1001,24 @@ This is state from the application that you can edit by calling AGUISendStateSna
921
1001
  else subscriber.error(new Error(typeof err === "string" ? err : `AI SDK stream error`));
922
1002
  break;
923
1003
  }
1004
+ case "start":
1005
+ case "start-step":
1006
+ case "finish-step":
1007
+ case "text-end":
1008
+ case "source":
1009
+ case "file":
1010
+ case "tool-output-denied":
1011
+ case "tool-approval-request":
1012
+ case "raw": break;
1013
+ default: {
1014
+ const _exhaustive = part;
1015
+ const unknownType = String(_exhaustive.type);
1016
+ if (!warnedUnknownPartTypes.has(unknownType)) {
1017
+ warnedUnknownPartTypes.add(unknownType);
1018
+ console.warn(`[BuiltInAgent] Ignoring unhandled AI SDK stream part: ${unknownType}`);
1019
+ }
1020
+ break;
1021
+ }
924
1022
  }
925
1023
  }
926
1024
  if (!terminalEventEmitted) {
@@ -974,7 +1072,8 @@ This is state from the application that you can edit by calling AGUISendStateSna
974
1072
  const startEvent = {
975
1073
  type: _ag_ui_client.EventType.RUN_STARTED,
976
1074
  threadId: input.threadId,
977
- runId: input.runId
1075
+ runId: input.runId,
1076
+ protocolVersion: _ag_ui_client.PROTOCOL_VERSION
978
1077
  };
979
1078
  subscriber.next(startEvent);
980
1079
  const ctx = {
@@ -989,20 +1088,47 @@ This is state from the application that you can edit by calling AGUISendStateSna
989
1088
  throw new InterruptSignal(interrupts);
990
1089
  }
991
1090
  };
992
- const answeredToolCallIds = new Set(input.messages.filter((m) => m.role === "tool").map((m) => m.toolCallId).filter((id) => typeof id === "string"));
993
- const resumeToolMessages = (input.resume ?? []).filter((entry) => !answeredToolCallIds.has(entry.interruptId)).map((entry) => ({
994
- id: (0, _copilotkit_shared.randomUUID)(),
995
- role: "tool",
996
- toolCallId: entry.interruptId,
997
- content: JSON.stringify(entry.status === "cancelled" ? { status: "cancelled" } : entry.payload ?? { status: "resolved" })
998
- }));
999
- const factoryInput = resumeToolMessages.length > 0 && config.type !== "custom" ? {
1000
- ...input,
1001
- messages: [...input.messages, ...resumeToolMessages]
1002
- } : input;
1003
1091
  (async () => {
1004
1092
  const runFinishedDetails = {};
1005
1093
  try {
1094
+ const toolCallIds = new Set(input.messages.flatMap((message) => message.role === "assistant" ? (message.toolCalls ?? []).map((call) => call.id) : []));
1095
+ const answeredToolMessages = input.messages.filter((message) => message.role === "tool");
1096
+ const answeredToolCallIds = new Set(answeredToolMessages.map((message) => message.toolCallId));
1097
+ const nativeResume = config.type === "custom" ? [] : input.resume ?? [];
1098
+ const resumeToolMessages = [];
1099
+ const decisionsByCall = /* @__PURE__ */ new Map();
1100
+ for (const entry of nativeResume) {
1101
+ if (!toolCallIds.has(entry.interruptId)) continue;
1102
+ const content = JSON.stringify(entry.status === "cancelled" ? { status: "cancelled" } : entry.payload ?? { status: "resolved" });
1103
+ if (decisionsByCall.has(entry.interruptId)) {
1104
+ if (decisionsByCall.get(entry.interruptId) !== content) throw new Error(`Conflicting decisions for resumed tool call ${entry.interruptId}`);
1105
+ continue;
1106
+ }
1107
+ decisionsByCall.set(entry.interruptId, content);
1108
+ if (answeredToolCallIds.has(entry.interruptId)) continue;
1109
+ resumeToolMessages.push({
1110
+ id: (0, _copilotkit_shared.randomUUID)(),
1111
+ role: "tool",
1112
+ toolCallId: entry.interruptId,
1113
+ content
1114
+ });
1115
+ }
1116
+ const resumedIds = new Set(nativeResume.map((entry) => entry.interruptId));
1117
+ const resumedResults = new Map([...answeredToolMessages, ...resumeToolMessages].filter((message) => resumedIds.has(message.toolCallId)).map((message) => [message.toolCallId, message.content]));
1118
+ for (const message of resumeToolMessages) {
1119
+ const event = {
1120
+ type: _ag_ui_client.EventType.TOOL_CALL_RESULT,
1121
+ messageId: message.id,
1122
+ toolCallId: message.toolCallId,
1123
+ role: "tool",
1124
+ content: message.content
1125
+ };
1126
+ subscriber.next(event);
1127
+ }
1128
+ const factoryInput = resumeToolMessages.length > 0 && config.type !== "custom" ? {
1129
+ ...input,
1130
+ messages: [...input.messages, ...resumeToolMessages]
1131
+ } : input;
1006
1132
  const learnedSkills = await require_learned_skills.prepareLearnedSkills(this.skillRegistry, controller.signal);
1007
1133
  controller.signal.throwIfAborted();
1008
1134
  const factoryCtx = {
@@ -1029,6 +1155,10 @@ This is state from the application that you can edit by calling AGUISendStateSna
1029
1155
  }
1030
1156
  }
1031
1157
  for await (const event of events) {
1158
+ if (event.type === _ag_ui_client.EventType.TOOL_CALL_RESULT && "toolCallId" in event && typeof event.toolCallId === "string" && "content" in event && resumedResults.has(event.toolCallId)) {
1159
+ if (resumedResults.get(event.toolCallId) !== event.content) throw new Error(`Conflicting result for resumed tool call ${event.toolCallId}`);
1160
+ continue;
1161
+ }
1032
1162
  if (config.type === "custom" && event.type === _ag_ui_client.EventType.RUN_FINISHED) {
1033
1163
  customRunFinishedEvent = event;
1034
1164
  require_usage.collectStandardRunFinishedDetails(event, runFinishedDetails);