@ynhcj/xiaoyi 2.3.5 → 2.3.6

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/channel.js CHANGED
@@ -315,6 +315,10 @@ exports.xiaoyiPlugin = {
315
315
  const taskId = runtime.getTaskIdForSession(message.params.sessionId) || `task_${Date.now()}`;
316
316
  const startTime = Date.now();
317
317
  let accumulatedText = "";
318
+ // ==================== CREATE ABORT CONTROLLER ====================
319
+ // Create AbortController for this session to allow cancelation
320
+ const { controller: abortController, signal: abortSignal } = runtime.createAbortControllerForSession(message.params.sessionId);
321
+ // ================================================================
318
322
  // ==================== START TIMEOUT PROTECTION ====================
319
323
  // Start 60-second timeout timer
320
324
  const timeoutConfig = runtime.getTimeoutConfig();
@@ -366,6 +370,16 @@ exports.xiaoyiPlugin = {
366
370
  const elapsed = Date.now() - startTime;
367
371
  const completeText = payload.text || "";
368
372
  accumulatedText = completeText;
373
+ // Check if session was aborted
374
+ if (runtime.isSessionAborted(message.params.sessionId)) {
375
+ console.log("\n" + "=".repeat(60));
376
+ console.log(`[ABORT] Response received AFTER abort`);
377
+ console.log(` Session: ${message.params.sessionId}`);
378
+ console.log(` Elapsed: ${elapsed}ms`);
379
+ console.log(` Action: DISCARDING (session was canceled)`);
380
+ console.log("=".repeat(60) + "\n");
381
+ return;
382
+ }
369
383
  // ==================== CHECK TIMEOUT ====================
370
384
  // If timeout already sent, discard this response
371
385
  if (runtime.isSessionTimeout(message.params.sessionId)) {
@@ -433,6 +447,7 @@ exports.xiaoyiPlugin = {
433
447
  // If empty, keep timeout running to handle session conflict
434
448
  if (accumulatedText.length > 0) {
435
449
  runtime.markSessionCompleted(message.params.sessionId);
450
+ runtime.clearAbortControllerForSession(message.params.sessionId);
436
451
  console.log(`[TIMEOUT] Timeout cleared (valid response)\n`);
437
452
  }
438
453
  else {
@@ -441,7 +456,9 @@ exports.xiaoyiPlugin = {
441
456
  console.log("=".repeat(60) + "\n");
442
457
  },
443
458
  },
444
- replyOptions: undefined,
459
+ replyOptions: {
460
+ abortSignal: abortSignal, // Pass abort signal to allow cancellation
461
+ },
445
462
  images: images.length > 0 ? images : undefined,
446
463
  });
447
464
  }
@@ -449,18 +466,29 @@ exports.xiaoyiPlugin = {
449
466
  console.error("XiaoYi: [ERROR] Error dispatching message:", error);
450
467
  // Clear timeout on error
451
468
  runtime.clearSessionTimeout(message.params.sessionId);
469
+ // Clear abort controller on error
470
+ runtime.clearAbortControllerForSession(message.params.sessionId);
452
471
  }
453
472
  });
454
473
  // Setup cancel handler
455
- // Note: The response is already sent in websocket.ts, this is just for logging
474
+ // When tasks/cancel is received, abort the current session's agent run
456
475
  connection.on("cancel", async (data) => {
476
+ const { sessionId } = data;
457
477
  console.log("\n" + "=".repeat(60));
458
- console.log(`XiaoYi: [CANCEL] Cancel event received (response already sent)`);
459
- console.log(` Session: ${data.sessionId}`);
478
+ console.log(`XiaoYi: [CANCEL] Cancel event received`);
479
+ console.log(` Session: ${sessionId}`);
460
480
  console.log(` Task ID: ${data.taskId || "N/A"}`);
461
481
  console.log("=".repeat(60) + "\n");
462
- // NOTE: The cancel response has already been sent to XiaoYi in websocket.ts.
463
- // Actual cancellation of the agent run depends on OpenClaw's internal mechanisms.
482
+ // Abort the session's agent run
483
+ const aborted = runtime.abortSession(sessionId);
484
+ if (aborted) {
485
+ console.log(`[CANCEL] Successfully triggered abort for session ${sessionId}`);
486
+ }
487
+ else {
488
+ console.log(`[CANCEL] No active agent run found for session ${sessionId}`);
489
+ }
490
+ // Clear timeout as the session is being canceled
491
+ runtime.markSessionCompleted(sessionId);
464
492
  });
465
493
  console.log("XiaoYi: Event handlers registered");
466
494
  console.log("XiaoYi: startAccount() completed - END");
package/dist/runtime.d.ts CHANGED
@@ -21,6 +21,7 @@ export declare class XiaoYiRuntime {
21
21
  private sessionTimeoutMap;
22
22
  private sessionTimeoutSent;
23
23
  private timeoutConfig;
24
+ private sessionAbortControllerMap;
24
25
  constructor();
25
26
  getInstanceId(): string;
26
27
  /**
@@ -97,6 +98,36 @@ export declare class XiaoYiRuntime {
97
98
  * Clear taskId for a session
98
99
  */
99
100
  clearTaskIdForSession(sessionId: string): void;
101
+ /**
102
+ * Create and register an AbortController for a session
103
+ * @param sessionId - Session ID
104
+ * @returns The AbortController and its signal
105
+ */
106
+ createAbortControllerForSession(sessionId: string): {
107
+ controller: AbortController;
108
+ signal: AbortSignal;
109
+ };
110
+ /**
111
+ * Abort a session's agent run
112
+ * @param sessionId - Session ID
113
+ * @returns true if a controller was found and aborted, false otherwise
114
+ */
115
+ abortSession(sessionId: string): boolean;
116
+ /**
117
+ * Check if a session has been aborted
118
+ * @param sessionId - Session ID
119
+ * @returns true if the session's abort signal was triggered
120
+ */
121
+ isSessionAborted(sessionId: string): boolean;
122
+ /**
123
+ * Clear the AbortController for a session (call when agent completes successfully)
124
+ * @param sessionId - Session ID
125
+ */
126
+ clearAbortControllerForSession(sessionId: string): void;
127
+ /**
128
+ * Clear all AbortControllers
129
+ */
130
+ clearAllAbortControllers(): void;
100
131
  }
101
132
  export declare function getXiaoYiRuntime(): XiaoYiRuntime;
102
133
  export declare function setXiaoYiRuntime(runtime: any): void;
package/dist/runtime.js CHANGED
@@ -26,6 +26,8 @@ class XiaoYiRuntime {
26
26
  this.sessionTimeoutMap = new Map();
27
27
  this.sessionTimeoutSent = new Set();
28
28
  this.timeoutConfig = DEFAULT_TIMEOUT_CONFIG;
29
+ // AbortController management for canceling agent runs
30
+ this.sessionAbortControllerMap = new Map();
29
31
  this.instanceId = `runtime_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
30
32
  console.log(`XiaoYi: Created new runtime instance: ${this.instanceId}`);
31
33
  }
@@ -100,6 +102,8 @@ class XiaoYiRuntime {
100
102
  this.sessionToTaskIdMap.clear();
101
103
  // Clear all timeouts
102
104
  this.clearAllTimeouts();
105
+ // Clear all abort controllers
106
+ this.clearAllAbortControllers();
103
107
  }
104
108
  /**
105
109
  * Set timeout configuration
@@ -220,6 +224,62 @@ class XiaoYiRuntime {
220
224
  clearTaskIdForSession(sessionId) {
221
225
  this.sessionToTaskIdMap.delete(sessionId);
222
226
  }
227
+ /**
228
+ * Create and register an AbortController for a session
229
+ * @param sessionId - Session ID
230
+ * @returns The AbortController and its signal
231
+ */
232
+ createAbortControllerForSession(sessionId) {
233
+ // Abort any existing controller for this session
234
+ this.abortSession(sessionId);
235
+ const controller = new AbortController();
236
+ this.sessionAbortControllerMap.set(sessionId, controller);
237
+ console.log(`[ABORT] Created AbortController for session ${sessionId}`);
238
+ return { controller, signal: controller.signal };
239
+ }
240
+ /**
241
+ * Abort a session's agent run
242
+ * @param sessionId - Session ID
243
+ * @returns true if a controller was found and aborted, false otherwise
244
+ */
245
+ abortSession(sessionId) {
246
+ const controller = this.sessionAbortControllerMap.get(sessionId);
247
+ if (controller) {
248
+ console.log(`[ABORT] Aborting session ${sessionId}`);
249
+ controller.abort();
250
+ this.sessionAbortControllerMap.delete(sessionId);
251
+ return true;
252
+ }
253
+ console.log(`[ABORT] No AbortController found for session ${sessionId}`);
254
+ return false;
255
+ }
256
+ /**
257
+ * Check if a session has been aborted
258
+ * @param sessionId - Session ID
259
+ * @returns true if the session's abort signal was triggered
260
+ */
261
+ isSessionAborted(sessionId) {
262
+ const controller = this.sessionAbortControllerMap.get(sessionId);
263
+ return controller ? controller.signal.aborted : false;
264
+ }
265
+ /**
266
+ * Clear the AbortController for a session (call when agent completes successfully)
267
+ * @param sessionId - Session ID
268
+ */
269
+ clearAbortControllerForSession(sessionId) {
270
+ const controller = this.sessionAbortControllerMap.get(sessionId);
271
+ if (controller) {
272
+ this.sessionAbortControllerMap.delete(sessionId);
273
+ console.log(`[ABORT] Cleared AbortController for session ${sessionId}`);
274
+ }
275
+ }
276
+ /**
277
+ * Clear all AbortControllers
278
+ */
279
+ clearAllAbortControllers() {
280
+ this.sessionAbortControllerMap.clear();
281
+ console.log("[ABORT] All AbortControllers cleared");
282
+ }
223
283
  }
224
284
  exports.XiaoYiRuntime = XiaoYiRuntime;
225
285
  // Global runtime instance - use global object to survive module reloads
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi",
3
- "version": "2.3.5",
3
+ "version": "2.3.6",
4
4
  "description": "XiaoYi channel plugin for OpenClaw",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",