@yourgpt/copilot-sdk 1.2.5 → 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,7 +803,10 @@ ${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);
806
+ this.state.updateMessageById(
807
+ this.streamState.messageId,
808
+ () => finalMessage
809
+ );
791
810
  if (!finalMessage.content && (!finalMessage.toolCalls || finalMessage.toolCalls.length === 0)) {
792
811
  this.debug("warning", "Empty response - no content and no tool calls");
793
812
  }
@@ -869,6 +888,9 @@ var AbstractAgentLoop = class {
869
888
  this._iteration = 0;
870
889
  this._maxIterationsReached = false;
871
890
  this._isProcessing = false;
891
+ this._isCancelled = false;
892
+ // Cancellation support
893
+ this.abortController = null;
872
894
  // Registered tools
873
895
  this.registeredTools = /* @__PURE__ */ new Map();
874
896
  // Pending approvals - resolve with approval result including extraData
@@ -901,6 +923,12 @@ var AbstractAgentLoop = class {
901
923
  get isProcessing() {
902
924
  return this._isProcessing;
903
925
  }
926
+ get isCancelled() {
927
+ return this._isCancelled;
928
+ }
929
+ get signal() {
930
+ return this.abortController?.signal;
931
+ }
904
932
  get state() {
905
933
  return {
906
934
  toolExecutions: this._toolExecutions,
@@ -975,17 +1003,32 @@ var AbstractAgentLoop = class {
975
1003
  * Returns tool results for sending back to LLM
976
1004
  */
977
1005
  async executeToolCalls(toolCalls) {
1006
+ if (this._isCancelled) {
1007
+ return [];
1008
+ }
978
1009
  if (this._iteration >= this._maxIterations) {
979
1010
  this._maxIterationsReached = true;
980
1011
  this.callbacks.onMaxIterationsReached?.();
981
1012
  return [];
982
1013
  }
1014
+ this.abortController = new AbortController();
1015
+ this._isCancelled = false;
1016
+ this._isProcessing = true;
983
1017
  this.setIteration(this._iteration + 1);
984
1018
  const results = [];
985
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
+ }
986
1028
  const result = await this.executeSingleTool(toolCall);
987
1029
  results.push(result);
988
1030
  }
1031
+ this._isProcessing = false;
989
1032
  return results;
990
1033
  }
991
1034
  /**
@@ -1049,7 +1092,11 @@ var AbstractAgentLoop = class {
1049
1092
  if (!tool.handler) {
1050
1093
  throw new Error(`Tool "${toolCall.name}" has no handler`);
1051
1094
  }
1095
+ if (this._isCancelled || this.abortController?.signal.aborted) {
1096
+ throw new Error("Tool execution cancelled");
1097
+ }
1052
1098
  const result = await tool.handler(toolCall.args, {
1099
+ signal: this.abortController?.signal,
1053
1100
  data: { toolCallId: toolCall.id },
1054
1101
  approvalData
1055
1102
  });
@@ -1132,6 +1179,37 @@ var AbstractAgentLoop = class {
1132
1179
  this.setIteration(0);
1133
1180
  this._maxIterationsReached = false;
1134
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
+ }
1135
1213
  // ============================================
1136
1214
  // State Management
1137
1215
  // ============================================
@@ -1141,6 +1219,8 @@ var AbstractAgentLoop = class {
1141
1219
  reset() {
1142
1220
  this.clearToolExecutions();
1143
1221
  this.pendingApprovals.clear();
1222
+ this._isCancelled = false;
1223
+ this.abortController = null;
1144
1224
  }
1145
1225
  /**
1146
1226
  * Reset iteration counter only (allows continuing after max iterations)
@@ -1149,6 +1229,7 @@ var AbstractAgentLoop = class {
1149
1229
  resetIterations() {
1150
1230
  this.setIteration(0);
1151
1231
  this._maxIterationsReached = false;
1232
+ this._isCancelled = false;
1152
1233
  }
1153
1234
  /**
1154
1235
  * Update configuration
@@ -1289,6 +1370,25 @@ var ChatWithTools = class {
1289
1370
  get isStreaming() {
1290
1371
  return this.chat.isStreaming;
1291
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
+ }
1292
1392
  // ============================================
1293
1393
  // Tool Execution Getters
1294
1394
  // ============================================
@@ -1312,16 +1412,23 @@ var ChatWithTools = class {
1312
1412
  // ============================================
1313
1413
  /**
1314
1414
  * Send a message
1415
+ * Returns false if a request is already in progress
1315
1416
  */
1316
1417
  async sendMessage(content, attachments) {
1418
+ if (this.isLoading) {
1419
+ this.debug("sendMessage blocked - request already in progress");
1420
+ return false;
1421
+ }
1317
1422
  this.agentLoop.resetIterations();
1318
- await this.chat.sendMessage(content, attachments);
1423
+ return await this.chat.sendMessage(content, attachments);
1319
1424
  }
1320
1425
  /**
1321
- * Stop generation
1426
+ * Stop generation and cancel any running tools
1322
1427
  */
1323
1428
  stop() {
1429
+ this.agentLoop.cancel();
1324
1430
  this.chat.stop();
1431
+ this.debug("Stopped - cancelled tools and aborted stream");
1325
1432
  }
1326
1433
  /**
1327
1434
  * Clear all messages
@@ -3604,5 +3711,5 @@ function useChat(config) {
3604
3711
  }
3605
3712
 
3606
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 };
3607
- //# sourceMappingURL=chunk-6NJKNYYW.js.map
3608
- //# sourceMappingURL=chunk-6NJKNYYW.js.map
3714
+ //# sourceMappingURL=chunk-EYNSW3NR.js.map
3715
+ //# sourceMappingURL=chunk-EYNSW3NR.js.map