@yourgpt/copilot-sdk 1.2.4 → 1.2.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.
@@ -454,10 +454,21 @@ var AbstractChat = class {
454
454
  // ============================================
455
455
  // Public Actions
456
456
  // ============================================
457
+ /**
458
+ * Check if a request is currently in progress
459
+ */
460
+ get isBusy() {
461
+ return this.state.status === "submitted" || this.state.status === "streaming";
462
+ }
457
463
  /**
458
464
  * Send a message
465
+ * Returns false if a request is already in progress
459
466
  */
460
467
  async sendMessage(content, attachments) {
468
+ if (this.isBusy) {
469
+ this.debug("sendMessage", "Blocked - request already in progress");
470
+ return false;
471
+ }
461
472
  this.debug("sendMessage", { content, attachments });
462
473
  try {
463
474
  this.resolveUnresolvedToolCalls();
@@ -469,8 +480,10 @@ var AbstractChat = class {
469
480
  this.callbacks.onStatusChange?.("submitted");
470
481
  await Promise.resolve();
471
482
  await this.processRequest();
483
+ return true;
472
484
  } catch (error) {
473
485
  this.handleError(error);
486
+ return false;
474
487
  }
475
488
  }
476
489
  /**
@@ -771,7 +784,10 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
771
784
  }
772
785
  this.streamState = processStreamChunk(chunk, this.streamState);
773
786
  const updatedMessage = streamStateToMessage(this.streamState);
774
- this.state.updateMessageById(this.streamState.messageId, () => updatedMessage);
787
+ this.state.updateMessageById(
788
+ this.streamState.messageId,
789
+ () => updatedMessage
790
+ );
775
791
  if (chunk.type === "message:delta") {
776
792
  this.callbacks.onMessageDelta?.(assistantMessage.id, chunk.content);
777
793
  }
@@ -787,15 +803,20 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
787
803
  }
788
804
  this.debug("handleStreamResponse", `Processed ${chunkCount} chunks`);
789
805
  const finalMessage = streamStateToMessage(this.streamState);
790
- this.state.updateMessageById(this.streamState.messageId, () => finalMessage);
791
- this.state.status = "ready";
806
+ this.state.updateMessageById(
807
+ this.streamState.messageId,
808
+ () => finalMessage
809
+ );
792
810
  if (!finalMessage.content && (!finalMessage.toolCalls || finalMessage.toolCalls.length === 0)) {
793
811
  this.debug("warning", "Empty response - no content and no tool calls");
794
812
  }
795
813
  this.callbacks.onMessageFinish?.(finalMessage);
796
- this.callbacks.onStatusChange?.("ready");
797
814
  this.callbacks.onMessagesChange?.(this.state.messages);
798
- this.callbacks.onFinish?.(this.state.messages);
815
+ if (!toolCallsEmitted) {
816
+ this.state.status = "ready";
817
+ this.callbacks.onStatusChange?.("ready");
818
+ this.callbacks.onFinish?.(this.state.messages);
819
+ }
799
820
  this.emit("done", {});
800
821
  this.streamState = null;
801
822
  }
@@ -813,15 +834,15 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
813
834
  };
814
835
  this.state.pushMessage(message);
815
836
  }
816
- this.state.status = "ready";
817
- this.callbacks.onStatusChange?.("ready");
818
837
  this.callbacks.onMessagesChange?.(this.state.messages);
819
- this.callbacks.onFinish?.(this.state.messages);
820
- if (response.requiresAction && this.state.messages.length > 0) {
838
+ const hasToolCalls = response.requiresAction && this.state.messages.length > 0 && this.state.messages[this.state.messages.length - 1]?.toolCalls?.length;
839
+ if (hasToolCalls) {
821
840
  const lastMessage = this.state.messages[this.state.messages.length - 1];
822
- if (lastMessage?.toolCalls?.length) {
823
- this.emit("toolCalls", { toolCalls: lastMessage.toolCalls });
824
- }
841
+ this.emit("toolCalls", { toolCalls: lastMessage.toolCalls });
842
+ } else {
843
+ this.state.status = "ready";
844
+ this.callbacks.onStatusChange?.("ready");
845
+ this.callbacks.onFinish?.(this.state.messages);
825
846
  }
826
847
  this.emit("done", {});
827
848
  }
@@ -867,6 +888,9 @@ var AbstractAgentLoop = class {
867
888
  this._iteration = 0;
868
889
  this._maxIterationsReached = false;
869
890
  this._isProcessing = false;
891
+ this._isCancelled = false;
892
+ // Cancellation support
893
+ this.abortController = null;
870
894
  // Registered tools
871
895
  this.registeredTools = /* @__PURE__ */ new Map();
872
896
  // Pending approvals - resolve with approval result including extraData
@@ -899,6 +923,12 @@ var AbstractAgentLoop = class {
899
923
  get isProcessing() {
900
924
  return this._isProcessing;
901
925
  }
926
+ get isCancelled() {
927
+ return this._isCancelled;
928
+ }
929
+ get signal() {
930
+ return this.abortController?.signal;
931
+ }
902
932
  get state() {
903
933
  return {
904
934
  toolExecutions: this._toolExecutions,
@@ -973,17 +1003,32 @@ var AbstractAgentLoop = class {
973
1003
  * Returns tool results for sending back to LLM
974
1004
  */
975
1005
  async executeToolCalls(toolCalls) {
1006
+ if (this._isCancelled) {
1007
+ return [];
1008
+ }
976
1009
  if (this._iteration >= this._maxIterations) {
977
1010
  this._maxIterationsReached = true;
978
1011
  this.callbacks.onMaxIterationsReached?.();
979
1012
  return [];
980
1013
  }
1014
+ this.abortController = new AbortController();
1015
+ this._isCancelled = false;
1016
+ this._isProcessing = true;
981
1017
  this.setIteration(this._iteration + 1);
982
1018
  const results = [];
983
1019
  for (const toolCall of toolCalls) {
1020
+ if (this._isCancelled || this.abortController.signal.aborted) {
1021
+ results.push({
1022
+ toolCallId: toolCall.id,
1023
+ success: false,
1024
+ error: "Tool execution cancelled"
1025
+ });
1026
+ continue;
1027
+ }
984
1028
  const result = await this.executeSingleTool(toolCall);
985
1029
  results.push(result);
986
1030
  }
1031
+ this._isProcessing = false;
987
1032
  return results;
988
1033
  }
989
1034
  /**
@@ -1047,7 +1092,11 @@ var AbstractAgentLoop = class {
1047
1092
  if (!tool.handler) {
1048
1093
  throw new Error(`Tool "${toolCall.name}" has no handler`);
1049
1094
  }
1095
+ if (this._isCancelled || this.abortController?.signal.aborted) {
1096
+ throw new Error("Tool execution cancelled");
1097
+ }
1050
1098
  const result = await tool.handler(toolCall.args, {
1099
+ signal: this.abortController?.signal,
1051
1100
  data: { toolCallId: toolCall.id },
1052
1101
  approvalData
1053
1102
  });
@@ -1130,6 +1179,37 @@ var AbstractAgentLoop = class {
1130
1179
  this.setIteration(0);
1131
1180
  this._maxIterationsReached = false;
1132
1181
  }
1182
+ /**
1183
+ * Cancel all pending and executing tools
1184
+ * This will:
1185
+ * 1. Abort the current abort controller (signals tools to stop)
1186
+ * 2. Reject all pending approvals
1187
+ * 3. Mark executing tools as cancelled
1188
+ */
1189
+ cancel() {
1190
+ this._isCancelled = true;
1191
+ this._isProcessing = false;
1192
+ this.abortController?.abort();
1193
+ for (const [id, pending] of this.pendingApprovals) {
1194
+ pending.resolve({ approved: false });
1195
+ this.updateToolExecution(id, {
1196
+ status: "failed",
1197
+ approvalStatus: "rejected",
1198
+ error: "Cancelled by user",
1199
+ completedAt: /* @__PURE__ */ new Date()
1200
+ });
1201
+ }
1202
+ this.pendingApprovals.clear();
1203
+ for (const exec of this._toolExecutions) {
1204
+ if (exec.status === "executing" || exec.status === "pending") {
1205
+ this.updateToolExecution(exec.id, {
1206
+ status: "failed",
1207
+ error: "Cancelled by user",
1208
+ completedAt: /* @__PURE__ */ new Date()
1209
+ });
1210
+ }
1211
+ }
1212
+ }
1133
1213
  // ============================================
1134
1214
  // State Management
1135
1215
  // ============================================
@@ -1139,6 +1219,8 @@ var AbstractAgentLoop = class {
1139
1219
  reset() {
1140
1220
  this.clearToolExecutions();
1141
1221
  this.pendingApprovals.clear();
1222
+ this._isCancelled = false;
1223
+ this.abortController = null;
1142
1224
  }
1143
1225
  /**
1144
1226
  * Reset iteration counter only (allows continuing after max iterations)
@@ -1147,6 +1229,7 @@ var AbstractAgentLoop = class {
1147
1229
  resetIterations() {
1148
1230
  this.setIteration(0);
1149
1231
  this._maxIterationsReached = false;
1232
+ this._isCancelled = false;
1150
1233
  }
1151
1234
  /**
1152
1235
  * Update configuration
@@ -1287,6 +1370,25 @@ var ChatWithTools = class {
1287
1370
  get isStreaming() {
1288
1371
  return this.chat.isStreaming;
1289
1372
  }
1373
+ /**
1374
+ * Whether any operation is in progress (chat or tools)
1375
+ * Use this to show loading indicators and disable send button
1376
+ */
1377
+ get isLoading() {
1378
+ const chatBusy = this.status === "submitted" || this.status === "streaming";
1379
+ const toolsBusy = this.agentLoop.isProcessing;
1380
+ const hasPendingApprovals = this.agentLoop.pendingApprovalExecutions.length > 0;
1381
+ return chatBusy || toolsBusy || hasPendingApprovals;
1382
+ }
1383
+ /**
1384
+ * Check if a request is currently in progress (excludes pending approvals)
1385
+ * Use this to prevent sending new messages
1386
+ */
1387
+ get isBusy() {
1388
+ const chatBusy = this.status === "submitted" || this.status === "streaming";
1389
+ const toolsBusy = this.agentLoop.isProcessing;
1390
+ return chatBusy || toolsBusy;
1391
+ }
1290
1392
  // ============================================
1291
1393
  // Tool Execution Getters
1292
1394
  // ============================================
@@ -1310,16 +1412,23 @@ var ChatWithTools = class {
1310
1412
  // ============================================
1311
1413
  /**
1312
1414
  * Send a message
1415
+ * Returns false if a request is already in progress
1313
1416
  */
1314
1417
  async sendMessage(content, attachments) {
1418
+ if (this.isLoading) {
1419
+ this.debug("sendMessage blocked - request already in progress");
1420
+ return false;
1421
+ }
1315
1422
  this.agentLoop.resetIterations();
1316
- await this.chat.sendMessage(content, attachments);
1423
+ return await this.chat.sendMessage(content, attachments);
1317
1424
  }
1318
1425
  /**
1319
- * Stop generation
1426
+ * Stop generation and cancel any running tools
1320
1427
  */
1321
1428
  stop() {
1429
+ this.agentLoop.cancel();
1322
1430
  this.chat.stop();
1431
+ this.debug("Stopped - cancelled tools and aborted stream");
1323
1432
  }
1324
1433
  /**
1325
1434
  * Clear all messages
@@ -3602,5 +3711,5 @@ function useChat(config) {
3602
3711
  }
3603
3712
 
3604
3713
  export { AbstractAgentLoop, AbstractChat, CopilotProvider, ReactChat, ReactChatState, ReactThreadManager, ReactThreadManagerState, createPermissionStorage, createReactChat, createReactChatState, createReactThreadManager, createReactThreadManagerState, createSessionPermissionCache, formatKnowledgeResultsForAI, initialAgentLoopState, searchKnowledgeBase, useAIAction, useAIActions, useAIContext, useAIContexts, useAITools, useAgent, useCapabilities, useChat, useCopilot, useDevLogger, useFeatureSupport, useKnowledgeBase, useSuggestions, useSupportedMediaTypes, useThreadManager, useTool, useToolExecutor, useToolWithSchema, useTools, useToolsWithSchema };
3605
- //# sourceMappingURL=chunk-CVZ7LT5B.js.map
3606
- //# sourceMappingURL=chunk-CVZ7LT5B.js.map
3714
+ //# sourceMappingURL=chunk-EYNSW3NR.js.map
3715
+ //# sourceMappingURL=chunk-EYNSW3NR.js.map