@copilotkitnext/core 1.54.0-next.5 → 1.54.0-next.7

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.
package/dist/index.cjs CHANGED
@@ -749,6 +749,13 @@ var RunHandler = class {
749
749
  this.core = core;
750
750
  }
751
751
  /**
752
+ * Typed access to CopilotKitCore's internal ("friend") methods.
753
+ * Centralises the single unavoidable cast so call-sites stay clean.
754
+ */
755
+ get _internal() {
756
+ return this.core;
757
+ }
758
+ /**
752
759
  * Get all tools as a readonly array
753
760
  */
754
761
  get tools() {
@@ -806,9 +813,9 @@ var RunHandler = class {
806
813
  await agent.detachActiveRun();
807
814
  agent.setMessages([]);
808
815
  agent.setState({});
809
- if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...this.core.headers };
816
+ if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...this._internal.headers };
810
817
  const runAgentResult = await agent.connectAgent({
811
- forwardedProps: this.core.properties,
818
+ forwardedProps: this._internal.properties,
812
819
  tools: this.buildFrontendTools(agent.agentId)
813
820
  }, this.createAgentErrorSubscriber(agent));
814
821
  return this.processAgentResult({
@@ -819,7 +826,7 @@ var RunHandler = class {
819
826
  const connectError = error instanceof Error ? error : new Error(String(error));
820
827
  const context = {};
821
828
  if (agent.agentId) context.agentId = agent.agentId;
822
- await this.core.emitError({
829
+ await this._internal.emitError({
823
830
  error: connectError,
824
831
  code: CopilotKitCoreErrorCode.AGENT_CONNECT_FAILED,
825
832
  context
@@ -831,16 +838,16 @@ var RunHandler = class {
831
838
  * Run an agent
832
839
  */
833
840
  async runAgent({ agent, forwardedProps }) {
834
- if (agent.agentId) this.core.suggestionEngine.clearSuggestions(agent.agentId);
835
- if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...this.core.headers };
841
+ if (agent.agentId) this._internal.suggestionEngine.clearSuggestions(agent.agentId);
842
+ if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...this._internal.headers };
836
843
  try {
837
844
  const runAgentResult = await agent.runAgent({
838
845
  forwardedProps: {
839
- ...this.core.properties,
846
+ ...this._internal.properties,
840
847
  ...forwardedProps
841
848
  },
842
849
  tools: this.buildFrontendTools(agent.agentId),
843
- context: Object.values(this.core.context)
850
+ context: Object.values(this._internal.context)
844
851
  }, this.createAgentErrorSubscriber(agent));
845
852
  return this.processAgentResult({
846
853
  runAgentResult,
@@ -850,7 +857,7 @@ var RunHandler = class {
850
857
  const runError = error instanceof Error ? error : new Error(String(error));
851
858
  const context = {};
852
859
  if (agent.agentId) context.agentId = agent.agentId;
853
- await this.core.emitError({
860
+ await this._internal.emitError({
854
861
  error: runError,
855
862
  code: CopilotKitCoreErrorCode.AGENT_RUN_FAILED,
856
863
  context
@@ -884,78 +891,107 @@ var RunHandler = class {
884
891
  }
885
892
  }
886
893
  }
887
- if (needsFollowUp) return await this.runAgent({ agent });
888
- this.core.suggestionEngine.reloadSuggestions(agentId);
894
+ if (needsFollowUp) {
895
+ await this._internal.waitForPendingFrameworkUpdates();
896
+ return await this.runAgent({ agent });
897
+ }
898
+ this._internal.suggestionEngine.reloadSuggestions(agentId);
889
899
  return runAgentResult;
890
900
  }
891
901
  /**
892
- * Execute a specific tool
902
+ * Shared handler execution logic used by executeSpecificTool, executeWildcardTool, and runTool.
903
+ * Handles arg parsing, subscriber notifications, handler invocation, result stringification,
904
+ * and error handling.
893
905
  */
894
- async executeSpecificTool(tool, toolCall, message, agent, agentId) {
895
- if (tool?.agentId && tool.agentId !== agent.agentId) return false;
906
+ async executeToolHandler({ tool, toolCall, agent, agentId, handlerArgs, toolType, messageId }) {
896
907
  let toolCallResult = "";
897
908
  let errorMessage;
898
- if (tool?.handler) {
899
- let parsedArgs;
900
- try {
901
- parsedArgs = ensureObjectArgs(JSON.parse(toolCall.function.arguments), toolCall.function.name);
902
- } catch (error) {
903
- const parseError = error instanceof Error ? error : new Error(String(error));
904
- errorMessage = parseError.message;
905
- await this.core.emitError({
906
- error: parseError,
907
- code: CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED,
908
- context: {
909
- agentId,
910
- toolCallId: toolCall.id,
911
- toolName: toolCall.function.name,
912
- rawArguments: toolCall.function.arguments,
913
- toolType: "specific",
914
- messageId: message.id
915
- }
916
- });
917
- }
918
- await this.core.notifySubscribers((subscriber) => subscriber.onToolExecutionStart?.({
919
- copilotkit: this.core,
920
- toolCallId: toolCall.id,
921
- agentId,
922
- toolName: toolCall.function.name,
923
- args: parsedArgs
924
- }), "Subscriber onToolExecutionStart error:");
925
- if (!errorMessage) try {
926
- const result = await tool.handler(parsedArgs, {
927
- toolCall,
928
- agent
929
- });
930
- if (result === void 0 || result === null) toolCallResult = "";
931
- else if (typeof result === "string") toolCallResult = result;
932
- else toolCallResult = JSON.stringify(result);
933
- } catch (error) {
934
- const handlerError = error instanceof Error ? error : new Error(String(error));
935
- errorMessage = handlerError.message;
936
- await this.core.emitError({
937
- error: handlerError,
938
- code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED,
939
- context: {
940
- agentId,
941
- toolCallId: toolCall.id,
942
- toolName: toolCall.function.name,
943
- parsedArgs,
944
- toolType: "specific",
945
- messageId: message.id
946
- }
947
- });
948
- }
949
- if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
950
- await this.core.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
951
- copilotkit: this.core,
952
- toolCallId: toolCall.id,
953
- agentId,
954
- toolName: toolCall.function.name,
955
- result: errorMessage ? "" : toolCallResult,
956
- error: errorMessage
957
- }), "Subscriber onToolExecutionEnd error:");
909
+ let isArgumentError = false;
910
+ let parsedArgs;
911
+ try {
912
+ parsedArgs = ensureObjectArgs(typeof handlerArgs === "string" ? JSON.parse(handlerArgs) : handlerArgs, toolCall.function.name);
913
+ } catch (error) {
914
+ const parseError = error instanceof Error ? error : new Error(String(error));
915
+ errorMessage = parseError.message;
916
+ isArgumentError = true;
917
+ await this._internal.emitError({
918
+ error: parseError,
919
+ code: CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED,
920
+ context: {
921
+ agentId,
922
+ toolCallId: toolCall.id,
923
+ toolName: toolCall.function.name,
924
+ rawArguments: handlerArgs,
925
+ toolType,
926
+ ...messageId ? { messageId } : {}
927
+ }
928
+ });
929
+ }
930
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionStart?.({
931
+ copilotkit: this.core,
932
+ toolCallId: toolCall.id,
933
+ agentId,
934
+ toolName: toolCall.function.name,
935
+ args: parsedArgs
936
+ }), "Subscriber onToolExecutionStart error:");
937
+ if (!errorMessage) try {
938
+ const result = await tool.handler(parsedArgs, {
939
+ toolCall,
940
+ agent
941
+ });
942
+ if (result === void 0 || result === null) toolCallResult = "";
943
+ else if (typeof result === "string") toolCallResult = result;
944
+ else toolCallResult = JSON.stringify(result);
945
+ } catch (error) {
946
+ const handlerError = error instanceof Error ? error : new Error(String(error));
947
+ errorMessage = handlerError.message;
948
+ await this._internal.emitError({
949
+ error: handlerError,
950
+ code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED,
951
+ context: {
952
+ agentId,
953
+ toolCallId: toolCall.id,
954
+ toolName: toolCall.function.name,
955
+ parsedArgs,
956
+ toolType,
957
+ ...messageId ? { messageId } : {}
958
+ }
959
+ });
958
960
  }
961
+ if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
962
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
963
+ copilotkit: this.core,
964
+ toolCallId: toolCall.id,
965
+ agentId,
966
+ toolName: toolCall.function.name,
967
+ result: errorMessage ? "" : toolCallResult,
968
+ error: errorMessage
969
+ }), "Subscriber onToolExecutionEnd error:");
970
+ return {
971
+ result: toolCallResult,
972
+ error: errorMessage,
973
+ isArgumentError
974
+ };
975
+ }
976
+ /**
977
+ * Execute a specific tool
978
+ */
979
+ async executeSpecificTool(tool, toolCall, message, agent, agentId) {
980
+ if (tool?.agentId && tool.agentId !== agent.agentId) return false;
981
+ let handlerResult = {
982
+ result: "",
983
+ error: void 0,
984
+ isArgumentError: false
985
+ };
986
+ if (tool?.handler) handlerResult = await this.executeToolHandler({
987
+ tool,
988
+ toolCall,
989
+ agent,
990
+ agentId,
991
+ handlerArgs: toolCall.function.arguments,
992
+ toolType: "specific",
993
+ messageId: message.id
994
+ });
959
995
  {
960
996
  const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
961
997
  if (messageIndex === -1) return false;
@@ -963,15 +999,18 @@ var RunHandler = class {
963
999
  id: (0, _copilotkitnext_shared.randomUUID)(),
964
1000
  role: "tool",
965
1001
  toolCallId: toolCall.id,
966
- content: toolCallResult
1002
+ content: handlerResult.result
967
1003
  };
968
1004
  agent.messages.splice(messageIndex + 1, 0, toolMessage);
969
- if (!errorMessage && tool?.followUp !== false) return true;
1005
+ if (!handlerResult.error && tool?.followUp !== false) return true;
970
1006
  }
971
1007
  return false;
972
1008
  }
973
1009
  /**
974
- * Execute a wildcard tool
1010
+ * Execute a wildcard tool.
1011
+ * Wildcard tools receive args wrapped as `{toolName, args}`, which differs from
1012
+ * specific tools, so this method keeps its own arg-wrapping logic rather than
1013
+ * delegating to `executeToolHandler`.
975
1014
  */
976
1015
  async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId) {
977
1016
  if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) return false;
@@ -984,7 +1023,7 @@ var RunHandler = class {
984
1023
  } catch (error) {
985
1024
  const parseError = error instanceof Error ? error : new Error(String(error));
986
1025
  errorMessage = parseError.message;
987
- await this.core.emitError({
1026
+ await this._internal.emitError({
988
1027
  error: parseError,
989
1028
  code: CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED,
990
1029
  context: {
@@ -1001,7 +1040,7 @@ var RunHandler = class {
1001
1040
  toolName: toolCall.function.name,
1002
1041
  args: parsedArgs
1003
1042
  };
1004
- await this.core.notifySubscribers((subscriber) => subscriber.onToolExecutionStart?.({
1043
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionStart?.({
1005
1044
  copilotkit: this.core,
1006
1045
  toolCallId: toolCall.id,
1007
1046
  agentId,
@@ -1019,7 +1058,7 @@ var RunHandler = class {
1019
1058
  } catch (error) {
1020
1059
  const handlerError = error instanceof Error ? error : new Error(String(error));
1021
1060
  errorMessage = handlerError.message;
1022
- await this.core.emitError({
1061
+ await this._internal.emitError({
1023
1062
  error: handlerError,
1024
1063
  code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED,
1025
1064
  context: {
@@ -1033,7 +1072,7 @@ var RunHandler = class {
1033
1072
  });
1034
1073
  }
1035
1074
  if (errorMessage) toolCallResult = `Error: ${errorMessage}`;
1036
- await this.core.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
1075
+ await this._internal.notifySubscribers((subscriber) => subscriber.onToolExecutionEnd?.({
1037
1076
  copilotkit: this.core,
1038
1077
  toolCallId: toolCall.id,
1039
1078
  agentId,
@@ -1057,6 +1096,95 @@ var RunHandler = class {
1057
1096
  return false;
1058
1097
  }
1059
1098
  /**
1099
+ * Programmatically execute a registered frontend tool without going through an LLM turn.
1100
+ * The handler runs, render components show up in the UI, and both the tool call and
1101
+ * result messages are added to `agent.messages`.
1102
+ */
1103
+ async runTool(params) {
1104
+ const { name, agentId, parameters = {}, followUp = false } = params;
1105
+ const tool = this.getTool({
1106
+ toolName: name,
1107
+ agentId
1108
+ });
1109
+ if (!tool) {
1110
+ const error = /* @__PURE__ */ new Error(`Tool not found: ${name}`);
1111
+ await this._internal.emitError({
1112
+ error,
1113
+ code: CopilotKitCoreErrorCode.TOOL_NOT_FOUND,
1114
+ context: {
1115
+ toolName: name,
1116
+ agentId
1117
+ }
1118
+ });
1119
+ throw error;
1120
+ }
1121
+ const resolvedAgentId = agentId ?? "default";
1122
+ const agent = this._internal.getAgent(resolvedAgentId);
1123
+ if (!agent) {
1124
+ const error = /* @__PURE__ */ new Error(`Agent not found: ${resolvedAgentId}`);
1125
+ await this._internal.emitError({
1126
+ error,
1127
+ code: CopilotKitCoreErrorCode.AGENT_NOT_FOUND,
1128
+ context: { agentId: resolvedAgentId }
1129
+ });
1130
+ throw error;
1131
+ }
1132
+ const toolCallId = (0, _copilotkitnext_shared.randomUUID)();
1133
+ const assistantMessage = {
1134
+ id: (0, _copilotkitnext_shared.randomUUID)(),
1135
+ role: "assistant",
1136
+ content: "",
1137
+ toolCalls: [{
1138
+ id: toolCallId,
1139
+ type: "function",
1140
+ function: {
1141
+ name,
1142
+ arguments: JSON.stringify(parameters)
1143
+ }
1144
+ }]
1145
+ };
1146
+ agent.messages.push(assistantMessage);
1147
+ let handlerResult = {
1148
+ result: "",
1149
+ error: void 0,
1150
+ isArgumentError: false
1151
+ };
1152
+ if (tool.handler) handlerResult = await this.executeToolHandler({
1153
+ tool,
1154
+ toolCall: assistantMessage.toolCalls[0],
1155
+ agent,
1156
+ agentId: resolvedAgentId,
1157
+ handlerArgs: parameters,
1158
+ toolType: "runTool"
1159
+ });
1160
+ const toolResultMessage = {
1161
+ id: (0, _copilotkitnext_shared.randomUUID)(),
1162
+ role: "tool",
1163
+ toolCallId,
1164
+ content: handlerResult.result
1165
+ };
1166
+ const assistantIndex = agent.messages.findIndex((m) => m.id === assistantMessage.id);
1167
+ if (assistantIndex !== -1) agent.messages.splice(assistantIndex + 1, 0, toolResultMessage);
1168
+ else agent.messages.push(toolResultMessage);
1169
+ if (!handlerResult.error && followUp !== false) {
1170
+ if (typeof followUp === "string" && followUp !== "generate") {
1171
+ const userMessage = {
1172
+ id: (0, _copilotkitnext_shared.randomUUID)(),
1173
+ role: "user",
1174
+ content: followUp
1175
+ };
1176
+ agent.messages.push(userMessage);
1177
+ }
1178
+ await this._internal.waitForPendingFrameworkUpdates();
1179
+ await this.runAgent({ agent });
1180
+ }
1181
+ return {
1182
+ toolCallId,
1183
+ result: handlerResult.result,
1184
+ error: handlerResult.error
1185
+ };
1186
+ }
1187
+ /**
1060
1188
  * Build frontend tools for an agent
1061
1189
  */
1062
1190
  buildFrontendTools(agentId) {
@@ -1073,7 +1201,7 @@ var RunHandler = class {
1073
1201
  const emitAgentError = async (error, code, extraContext = {}) => {
1074
1202
  const context = { ...extraContext };
1075
1203
  if (agent.agentId) context.agentId = agent.agentId;
1076
- await this.core.emitError({
1204
+ await this._internal.emitError({
1077
1205
  error,
1078
1206
  code,
1079
1207
  context
@@ -1319,6 +1447,8 @@ let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode)
1319
1447
  CopilotKitCoreErrorCode["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
1320
1448
  CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
1321
1449
  CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
1450
+ CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
1451
+ CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
1322
1452
  CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
1323
1453
  CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
1324
1454
  CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
@@ -1534,6 +1664,14 @@ var CopilotKitCore = class {
1534
1664
  return this.runHandler.runAgent(params);
1535
1665
  }
1536
1666
  /**
1667
+ * Programmatically execute a registered frontend tool without going through an LLM turn.
1668
+ * The handler runs, render components show up in the UI, and both the tool call and
1669
+ * result messages are added to `agent.messages`.
1670
+ */
1671
+ async runTool(params) {
1672
+ return this.runHandler.runTool(params);
1673
+ }
1674
+ /**
1537
1675
  * State management (delegated to StateManager)
1538
1676
  */
1539
1677
  getStateByRun(agentId, threadId, runId) {
@@ -1551,6 +1689,24 @@ var CopilotKitCore = class {
1551
1689
  buildFrontendTools(agentId) {
1552
1690
  return this.runHandler.buildFrontendTools(agentId);
1553
1691
  }
1692
+ /**
1693
+ * Called before each follow-up agent run (after tool execution).
1694
+ *
1695
+ * When a frontend tool handler calls framework state setters (e.g. React's
1696
+ * setState), those updates are batched and deferred — they do not take effect
1697
+ * until the framework's scheduler runs (React uses MessageChannel).
1698
+ * useAgentContext registers context via useLayoutEffect, which runs
1699
+ * synchronously after React commits that deferred batch.
1700
+ *
1701
+ * Without yielding here, the follow-up runAgent reads the context store
1702
+ * synchronously while the deferred updates are still pending, producing stale
1703
+ * context for the next agent turn.
1704
+ *
1705
+ * Override in framework-specific subclasses to yield to the framework
1706
+ * scheduler before the follow-up run. The base implementation is a no-op
1707
+ * because non-React environments have no deferred state to flush.
1708
+ */
1709
+ async waitForPendingFrameworkUpdates() {}
1554
1710
  };
1555
1711
 
1556
1712
  //#endregion