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