@ynhcj/xiaoyi 2.3.6 → 2.3.8
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 +26 -24
- package/dist/types.d.ts +3 -0
- package/dist/websocket.d.ts +9 -0
- package/dist/websocket.js +44 -10
- package/package.json +1 -1
package/dist/channel.js
CHANGED
|
@@ -214,8 +214,10 @@ exports.xiaoyiPlugin = {
|
|
|
214
214
|
const { getXiaoYiRuntime } = require("./runtime");
|
|
215
215
|
const runtime = getXiaoYiRuntime();
|
|
216
216
|
console.log(`XiaoYi: [Message Handler] Using runtime instance: ${runtime.getInstanceId()}`);
|
|
217
|
+
// For message/stream, sessionId is ONLY in params
|
|
218
|
+
const sessionId = message.params.sessionId;
|
|
217
219
|
// Store sessionId -> taskId mapping in runtime (use params.id as taskId)
|
|
218
|
-
runtime.setTaskIdForSession(
|
|
220
|
+
runtime.setTaskIdForSession(sessionId, message.params.id);
|
|
219
221
|
// Get PluginRuntime from our runtime wrapper
|
|
220
222
|
const pluginRuntime = runtime.getPluginRuntime();
|
|
221
223
|
if (!pluginRuntime) {
|
|
@@ -291,8 +293,8 @@ exports.xiaoyiPlugin = {
|
|
|
291
293
|
const msgContext = {
|
|
292
294
|
Body: bodyText,
|
|
293
295
|
From: senderId,
|
|
294
|
-
To:
|
|
295
|
-
SessionKey: `xiaoyi:${resolvedAccount.accountId}:${
|
|
296
|
+
To: sessionId,
|
|
297
|
+
SessionKey: `xiaoyi:${resolvedAccount.accountId}:${sessionId}`,
|
|
296
298
|
AccountId: resolvedAccount.accountId,
|
|
297
299
|
MessageSid: message.id, // Use top-level id as message sequence number
|
|
298
300
|
Timestamp: Date.now(), // Generate timestamp since new format doesn't include it
|
|
@@ -307,34 +309,34 @@ exports.xiaoyiPlugin = {
|
|
|
307
309
|
try {
|
|
308
310
|
console.log("\n" + "=".repeat(60));
|
|
309
311
|
console.log(`XiaoYi: [MESSAGE] Processing user message`);
|
|
310
|
-
console.log(` Session: ${
|
|
312
|
+
console.log(` Session: ${sessionId}`);
|
|
311
313
|
console.log(` Task ID: ${message.params.id}`);
|
|
312
314
|
console.log(` User input: ${bodyText.substring(0, 50)}${bodyText.length > 50 ? "..." : ""}`);
|
|
313
315
|
console.log(` Images: ${images.length}`);
|
|
314
316
|
console.log("=".repeat(60) + "\n");
|
|
315
|
-
const taskId = runtime.getTaskIdForSession(
|
|
317
|
+
const taskId = runtime.getTaskIdForSession(sessionId) || `task_${Date.now()}`;
|
|
316
318
|
const startTime = Date.now();
|
|
317
319
|
let accumulatedText = "";
|
|
318
320
|
// ==================== CREATE ABORT CONTROLLER ====================
|
|
319
321
|
// Create AbortController for this session to allow cancelation
|
|
320
|
-
const { controller: abortController, signal: abortSignal } = runtime.createAbortControllerForSession(
|
|
322
|
+
const { controller: abortController, signal: abortSignal } = runtime.createAbortControllerForSession(sessionId);
|
|
321
323
|
// ================================================================
|
|
322
324
|
// ==================== START TIMEOUT PROTECTION ====================
|
|
323
325
|
// Start 60-second timeout timer
|
|
324
326
|
const timeoutConfig = runtime.getTimeoutConfig();
|
|
325
|
-
console.log(`[TIMEOUT] Starting ${timeoutConfig.duration}ms timeout protection for session ${
|
|
326
|
-
runtime.setTimeoutForSession(
|
|
327
|
+
console.log(`[TIMEOUT] Starting ${timeoutConfig.duration}ms timeout protection for session ${sessionId}`);
|
|
328
|
+
runtime.setTimeoutForSession(sessionId, async () => {
|
|
327
329
|
// Timeout callback - send timeout message to user
|
|
328
330
|
const elapsed = Date.now() - startTime;
|
|
329
331
|
console.log("\n" + "=".repeat(60));
|
|
330
|
-
console.log(`[TIMEOUT] Timeout triggered for session ${
|
|
332
|
+
console.log(`[TIMEOUT] Timeout triggered for session ${sessionId}`);
|
|
331
333
|
console.log(` Elapsed: ${elapsed}ms`);
|
|
332
334
|
console.log(` Task ID: ${taskId}`);
|
|
333
335
|
console.log("=".repeat(60) + "\n");
|
|
334
336
|
const conn = runtime.getConnection();
|
|
335
337
|
if (conn) {
|
|
336
338
|
const timeoutResponse = {
|
|
337
|
-
sessionId:
|
|
339
|
+
sessionId: sessionId,
|
|
338
340
|
messageId: `timeout_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
339
341
|
timestamp: Date.now(),
|
|
340
342
|
agentId: resolvedAccount.config.agentId,
|
|
@@ -350,8 +352,8 @@ exports.xiaoyiPlugin = {
|
|
|
350
352
|
status: "success",
|
|
351
353
|
};
|
|
352
354
|
try {
|
|
353
|
-
await conn.sendResponse(timeoutResponse, taskId,
|
|
354
|
-
console.log(`[TIMEOUT] Timeout message sent successfully to session ${
|
|
355
|
+
await conn.sendResponse(timeoutResponse, taskId, sessionId, true, false);
|
|
356
|
+
console.log(`[TIMEOUT] Timeout message sent successfully to session ${sessionId}\n`);
|
|
355
357
|
}
|
|
356
358
|
catch (error) {
|
|
357
359
|
console.error(`[TIMEOUT] Failed to send timeout message:`, error);
|
|
@@ -371,10 +373,10 @@ exports.xiaoyiPlugin = {
|
|
|
371
373
|
const completeText = payload.text || "";
|
|
372
374
|
accumulatedText = completeText;
|
|
373
375
|
// Check if session was aborted
|
|
374
|
-
if (runtime.isSessionAborted(
|
|
376
|
+
if (runtime.isSessionAborted(sessionId)) {
|
|
375
377
|
console.log("\n" + "=".repeat(60));
|
|
376
378
|
console.log(`[ABORT] Response received AFTER abort`);
|
|
377
|
-
console.log(` Session: ${
|
|
379
|
+
console.log(` Session: ${sessionId}`);
|
|
378
380
|
console.log(` Elapsed: ${elapsed}ms`);
|
|
379
381
|
console.log(` Action: DISCARDING (session was canceled)`);
|
|
380
382
|
console.log("=".repeat(60) + "\n");
|
|
@@ -382,10 +384,10 @@ exports.xiaoyiPlugin = {
|
|
|
382
384
|
}
|
|
383
385
|
// ==================== CHECK TIMEOUT ====================
|
|
384
386
|
// If timeout already sent, discard this response
|
|
385
|
-
if (runtime.isSessionTimeout(
|
|
387
|
+
if (runtime.isSessionTimeout(sessionId)) {
|
|
386
388
|
console.log("\n" + "=".repeat(60));
|
|
387
389
|
console.log(`[TIMEOUT] Response received AFTER timeout`);
|
|
388
|
-
console.log(` Session: ${
|
|
390
|
+
console.log(` Session: ${sessionId}`);
|
|
389
391
|
console.log(` Elapsed: ${elapsed}ms`);
|
|
390
392
|
console.log(` Action: DISCARDING (timeout message already sent)`);
|
|
391
393
|
console.log("=".repeat(60) + "\n");
|
|
@@ -396,7 +398,7 @@ exports.xiaoyiPlugin = {
|
|
|
396
398
|
if (!completeText || completeText.length === 0) {
|
|
397
399
|
console.log("\n" + "=".repeat(60));
|
|
398
400
|
console.log(`[TIMEOUT] Empty response detected`);
|
|
399
|
-
console.log(` Session: ${
|
|
401
|
+
console.log(` Session: ${sessionId}`);
|
|
400
402
|
console.log(` Elapsed: ${elapsed}ms`);
|
|
401
403
|
console.log(` Action: KEEPING TIMEOUT (session conflict detected)`);
|
|
402
404
|
console.log("=".repeat(60) + "\n");
|
|
@@ -411,7 +413,7 @@ exports.xiaoyiPlugin = {
|
|
|
411
413
|
console.log("-".repeat(60) + "\n");
|
|
412
414
|
// Send final response to XiaoYi
|
|
413
415
|
const response = {
|
|
414
|
-
sessionId:
|
|
416
|
+
sessionId: sessionId,
|
|
415
417
|
messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
416
418
|
timestamp: Date.now(),
|
|
417
419
|
agentId: resolvedAccount.config.agentId,
|
|
@@ -428,14 +430,14 @@ exports.xiaoyiPlugin = {
|
|
|
428
430
|
};
|
|
429
431
|
const conn = runtime.getConnection();
|
|
430
432
|
if (conn) {
|
|
431
|
-
await conn.sendResponse(response, taskId,
|
|
433
|
+
await conn.sendResponse(response, taskId, sessionId, true, false);
|
|
432
434
|
console.log(`✓ XiaoYi: Response sent successfully\n`);
|
|
433
435
|
}
|
|
434
436
|
else {
|
|
435
437
|
console.error(`✗ XiaoYi: Connection not available\n`);
|
|
436
438
|
}
|
|
437
439
|
// Clear timeout as response was sent successfully
|
|
438
|
-
runtime.markSessionCompleted(
|
|
440
|
+
runtime.markSessionCompleted(sessionId);
|
|
439
441
|
},
|
|
440
442
|
onIdle: async () => {
|
|
441
443
|
const elapsed = Date.now() - startTime;
|
|
@@ -446,8 +448,8 @@ exports.xiaoyiPlugin = {
|
|
|
446
448
|
// Only clear timeout if we have a valid response
|
|
447
449
|
// If empty, keep timeout running to handle session conflict
|
|
448
450
|
if (accumulatedText.length > 0) {
|
|
449
|
-
runtime.markSessionCompleted(
|
|
450
|
-
runtime.clearAbortControllerForSession(
|
|
451
|
+
runtime.markSessionCompleted(sessionId);
|
|
452
|
+
runtime.clearAbortControllerForSession(sessionId);
|
|
451
453
|
console.log(`[TIMEOUT] Timeout cleared (valid response)\n`);
|
|
452
454
|
}
|
|
453
455
|
else {
|
|
@@ -465,9 +467,9 @@ exports.xiaoyiPlugin = {
|
|
|
465
467
|
catch (error) {
|
|
466
468
|
console.error("XiaoYi: [ERROR] Error dispatching message:", error);
|
|
467
469
|
// Clear timeout on error
|
|
468
|
-
runtime.clearSessionTimeout(
|
|
470
|
+
runtime.clearSessionTimeout(sessionId);
|
|
469
471
|
// Clear abort controller on error
|
|
470
|
-
runtime.clearAbortControllerForSession(
|
|
472
|
+
runtime.clearAbortControllerForSession(sessionId);
|
|
471
473
|
}
|
|
472
474
|
});
|
|
473
475
|
// Setup cancel handler
|
package/dist/types.d.ts
CHANGED
|
@@ -3,11 +3,14 @@ export interface A2ARequestMessage {
|
|
|
3
3
|
jsonrpc: "2.0";
|
|
4
4
|
id: string;
|
|
5
5
|
method: "message/stream";
|
|
6
|
+
deviceId?: string;
|
|
6
7
|
params: {
|
|
7
8
|
id: string;
|
|
8
9
|
sessionId: string;
|
|
9
10
|
agentLoginSessionId?: string;
|
|
10
11
|
message: {
|
|
12
|
+
kind?: string;
|
|
13
|
+
messageId?: string;
|
|
11
14
|
role: "user" | "agent";
|
|
12
15
|
parts: Array<{
|
|
13
16
|
kind: "text" | "file" | "data";
|
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 ONLY 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
|
*/
|
|
@@ -128,6 +136,7 @@ export declare class XiaoYiWebSocketManager extends EventEmitter {
|
|
|
128
136
|
private clearStableConnectionCheck;
|
|
129
137
|
/**
|
|
130
138
|
* Type guard for A2A request messages
|
|
139
|
+
* For message/stream, sessionId MUST be in params
|
|
131
140
|
*/
|
|
132
141
|
private isA2ARequestMessage;
|
|
133
142
|
/**
|
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 ONLY 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 ONLY 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,
|
|
@@ -766,6 +798,7 @@ class XiaoYiWebSocketManager extends events_1.EventEmitter {
|
|
|
766
798
|
}
|
|
767
799
|
/**
|
|
768
800
|
* Type guard for A2A request messages
|
|
801
|
+
* For message/stream, sessionId MUST be in params
|
|
769
802
|
*/
|
|
770
803
|
isA2ARequestMessage(data) {
|
|
771
804
|
return data &&
|
|
@@ -775,6 +808,7 @@ class XiaoYiWebSocketManager extends events_1.EventEmitter {
|
|
|
775
808
|
data.method === "message/stream" &&
|
|
776
809
|
data.params &&
|
|
777
810
|
typeof data.params.id === "string" &&
|
|
811
|
+
// For message/stream, sessionId MUST be in params
|
|
778
812
|
typeof data.params.sessionId === "string" &&
|
|
779
813
|
data.params.message &&
|
|
780
814
|
typeof data.params.message.role === "string" &&
|