@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 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(message.params.sessionId, message.params.id);
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: message.params.sessionId,
295
- SessionKey: `xiaoyi:${resolvedAccount.accountId}:${message.params.sessionId}`,
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: ${message.params.sessionId}`);
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(message.params.sessionId) || `task_${Date.now()}`;
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(message.params.sessionId);
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 ${message.params.sessionId}`);
326
- runtime.setTimeoutForSession(message.params.sessionId, async () => {
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 ${message.params.sessionId}`);
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: message.params.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, message.params.sessionId, true, false);
354
- console.log(`[TIMEOUT] Timeout message sent successfully to session ${message.params.sessionId}\n`);
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(message.params.sessionId)) {
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: ${message.params.sessionId}`);
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(message.params.sessionId)) {
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: ${message.params.sessionId}`);
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: ${message.params.sessionId}`);
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: message.params.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, message.params.sessionId, true, false);
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(message.params.sessionId);
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(message.params.sessionId);
450
- runtime.clearAbortControllerForSession(message.params.sessionId);
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(message.params.sessionId);
470
+ runtime.clearSessionTimeout(sessionId);
469
471
  // Clear abort controller on error
470
- runtime.clearAbortControllerForSession(message.params.sessionId);
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";
@@ -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 (message.params.sessionId) {
324
- this.sessionServerMap.set(message.params.sessionId, sourceServer);
325
- console.log(`[MAP] Session ${message.params.sessionId} -> ${sourceServer}`);
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
- console.log(`[${sourceServer}] Received clearContext for session: ${message.params.sessionId}`);
480
- this.sendClearContextResponse(message.id, message.params.sessionId, true, sourceServer)
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: message.params.sessionId,
510
+ sessionId: sessionId,
484
511
  id: message.id,
485
512
  serverId: sourceServer,
486
513
  });
487
514
  // Remove session mapping
488
- this.sessionServerMap.delete(message.params.sessionId);
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: ${message.sessionId}`);
543
+ console.log(` Session: ${sessionId}`);
512
544
  console.log(` Task ID: ${effectiveTaskId}`);
513
545
  console.log("=".repeat(60) + "\n");
514
- this.sendTasksCancelResponse(message.id, message.sessionId, true, sourceServer)
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: message.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" &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi",
3
- "version": "2.3.6",
3
+ "version": "2.3.8",
4
4
  "description": "XiaoYi channel plugin for OpenClaw",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",