@ynhcj/xiaoyi 2.3.5 → 2.3.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/channel.js +34 -6
- package/dist/runtime.d.ts +31 -0
- package/dist/runtime.js +60 -0
- package/dist/websocket.d.ts +8 -0
- package/dist/websocket.js +42 -10
- package/package.json +1 -1
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:
|
|
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
|
-
//
|
|
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
|
|
459
|
-
console.log(` Session: ${
|
|
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
|
-
//
|
|
463
|
-
|
|
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/dist/websocket.d.ts
CHANGED
|
@@ -50,6 +50,14 @@ export declare class XiaoYiWebSocketManager extends EventEmitter {
|
|
|
50
50
|
* Setup WebSocket event handlers for specific server
|
|
51
51
|
*/
|
|
52
52
|
private setupWebSocketHandlers;
|
|
53
|
+
/**
|
|
54
|
+
* Extract sessionId from message based on method type
|
|
55
|
+
* Different methods have sessionId in different locations:
|
|
56
|
+
* - message/stream: sessionId in params
|
|
57
|
+
* - tasks/cancel: sessionId at top level
|
|
58
|
+
* - clearContext: sessionId at top level
|
|
59
|
+
*/
|
|
60
|
+
private extractSessionId;
|
|
53
61
|
/**
|
|
54
62
|
* Handle incoming message from specific server
|
|
55
63
|
*/
|
package/dist/websocket.js
CHANGED
|
@@ -303,6 +303,26 @@ class XiaoYiWebSocketManager extends events_1.EventEmitter {
|
|
|
303
303
|
}
|
|
304
304
|
});
|
|
305
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Extract sessionId from message based on method type
|
|
308
|
+
* Different methods have sessionId in different locations:
|
|
309
|
+
* - message/stream: sessionId in params
|
|
310
|
+
* - tasks/cancel: sessionId at top level
|
|
311
|
+
* - clearContext: sessionId at top level
|
|
312
|
+
*/
|
|
313
|
+
extractSessionId(message) {
|
|
314
|
+
// For message/stream, sessionId is in params
|
|
315
|
+
if (message.method === "message/stream") {
|
|
316
|
+
return message.params?.sessionId;
|
|
317
|
+
}
|
|
318
|
+
// For tasks/cancel and clearContext, sessionId is at top level
|
|
319
|
+
if (message.method === "tasks/cancel" ||
|
|
320
|
+
message.method === "clearContext" ||
|
|
321
|
+
message.action === "clear") {
|
|
322
|
+
return message.sessionId;
|
|
323
|
+
}
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
306
326
|
/**
|
|
307
327
|
* Handle incoming message from specific server
|
|
308
328
|
*/
|
|
@@ -319,10 +339,12 @@ class XiaoYiWebSocketManager extends events_1.EventEmitter {
|
|
|
319
339
|
console.warn(`[${sourceServer}] Mismatched agentId: ${message.agentId}, expected: ${this.config.agentId}. Discarding.`);
|
|
320
340
|
return;
|
|
321
341
|
}
|
|
342
|
+
// Extract sessionId based on method type
|
|
343
|
+
const sessionId = this.extractSessionId(message);
|
|
322
344
|
// Record session → server mapping
|
|
323
|
-
if (
|
|
324
|
-
this.sessionServerMap.set(
|
|
325
|
-
console.log(`[MAP] Session ${
|
|
345
|
+
if (sessionId) {
|
|
346
|
+
this.sessionServerMap.set(sessionId, sourceServer);
|
|
347
|
+
console.log(`[MAP] Session ${sessionId} -> ${sourceServer}`);
|
|
326
348
|
}
|
|
327
349
|
// Handle special messages (clearContext, tasks/cancel)
|
|
328
350
|
if (message.method === "clearContext") {
|
|
@@ -476,16 +498,21 @@ class XiaoYiWebSocketManager extends events_1.EventEmitter {
|
|
|
476
498
|
* Handle clearContext method
|
|
477
499
|
*/
|
|
478
500
|
handleClearContext(message, sourceServer) {
|
|
479
|
-
|
|
480
|
-
|
|
501
|
+
const sessionId = this.extractSessionId(message);
|
|
502
|
+
if (!sessionId) {
|
|
503
|
+
console.error(`[${sourceServer}] Failed to extract sessionId from clearContext message`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
console.log(`[${sourceServer}] Received clearContext for session: ${sessionId}`);
|
|
507
|
+
this.sendClearContextResponse(message.id, sessionId, true, sourceServer)
|
|
481
508
|
.catch(error => console.error(`[${sourceServer}] Failed to send clearContext response:`, error));
|
|
482
509
|
this.emit("clear", {
|
|
483
|
-
sessionId:
|
|
510
|
+
sessionId: sessionId,
|
|
484
511
|
id: message.id,
|
|
485
512
|
serverId: sourceServer,
|
|
486
513
|
});
|
|
487
514
|
// Remove session mapping
|
|
488
|
-
this.sessionServerMap.delete(
|
|
515
|
+
this.sessionServerMap.delete(sessionId);
|
|
489
516
|
}
|
|
490
517
|
/**
|
|
491
518
|
* Handle clear message (legacy format)
|
|
@@ -505,16 +532,21 @@ class XiaoYiWebSocketManager extends events_1.EventEmitter {
|
|
|
505
532
|
* Handle tasks/cancel message
|
|
506
533
|
*/
|
|
507
534
|
handleTasksCancelMessage(message, sourceServer) {
|
|
535
|
+
const sessionId = this.extractSessionId(message);
|
|
536
|
+
if (!sessionId) {
|
|
537
|
+
console.error(`[${sourceServer}] Failed to extract sessionId from tasks/cancel message`);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
508
540
|
const effectiveTaskId = message.taskId || message.id;
|
|
509
541
|
console.log("\n" + "=".repeat(60));
|
|
510
542
|
console.log(`[${sourceServer}] Received cancel request`);
|
|
511
|
-
console.log(` Session: ${
|
|
543
|
+
console.log(` Session: ${sessionId}`);
|
|
512
544
|
console.log(` Task ID: ${effectiveTaskId}`);
|
|
513
545
|
console.log("=".repeat(60) + "\n");
|
|
514
|
-
this.sendTasksCancelResponse(message.id,
|
|
546
|
+
this.sendTasksCancelResponse(message.id, sessionId, true, sourceServer)
|
|
515
547
|
.catch(error => console.error(`[${sourceServer}] Failed to send cancel response:`, error));
|
|
516
548
|
this.emit("cancel", {
|
|
517
|
-
sessionId:
|
|
549
|
+
sessionId: sessionId,
|
|
518
550
|
taskId: effectiveTaskId,
|
|
519
551
|
id: message.id,
|
|
520
552
|
serverId: sourceServer,
|