@ynhcj/xiaoyi 2.4.5 → 2.4.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.
Files changed (2) hide show
  1. package/dist/channel.js +68 -11
  2. package/package.json +6 -6
package/dist/channel.js CHANGED
@@ -323,6 +323,7 @@ exports.xiaoyiPlugin = {
323
323
  const startTime = Date.now();
324
324
  let accumulatedText = "";
325
325
  let sentTextLength = 0; // Track sent text length for streaming
326
+ let hasSentFinal = false; // Track if final content has been sent (to prevent duplicate isFinal=true)
326
327
  // ==================== CREATE ABORT CONTROLLER ====================
327
328
  // Create AbortController for this session to allow cancelation
328
329
  const { controller: abortController, signal: abortSignal } = runtime.createAbortControllerForSession(sessionId);
@@ -371,6 +372,22 @@ exports.xiaoyiPlugin = {
371
372
  const completeText = payload.text || "";
372
373
  accumulatedText = completeText;
373
374
  const kind = info.kind; // "tool" | "block" | "final"
375
+ // ==================== DEBUG LOG - ENTRY POINT ====================
376
+ // Log EVERY call to deliver with full details
377
+ console.log("\n" + "█".repeat(70));
378
+ console.log(`📨 [DELIVER ENTRY] deliver() callback invoked`);
379
+ console.log(` Session: ${sessionId}`);
380
+ console.log(` Elapsed: ${elapsed}ms`);
381
+ console.log(` Kind: "${kind}"`);
382
+ console.log(` Text length: ${completeText.length} chars`);
383
+ console.log(` Sent so far: ${sentTextLength} chars`);
384
+ if (completeText.length > 0) {
385
+ console.log(` Text preview: "${completeText.substring(0, 80)}${completeText.length > 80 ? "..." : ""}"`);
386
+ }
387
+ console.log(` Full info object:`, JSON.stringify(info, null, 2));
388
+ console.log(` Full payload keys:`, Object.keys(payload));
389
+ console.log("█".repeat(70) + "\n");
390
+ // =================================================================
374
391
  // Check if session was aborted
375
392
  if (runtime.isSessionAborted(sessionId)) {
376
393
  console.log("\n" + "=".repeat(60));
@@ -463,8 +480,16 @@ exports.xiaoyiPlugin = {
463
480
  console.log(` Session: ${sessionId}`);
464
481
  console.log(` Elapsed: ${elapsed}ms`);
465
482
  console.log(` Total length: ${completeText.length} chars`);
483
+ console.log(` Has already sent final: ${hasSentFinal}`);
466
484
  console.log(` Preview: "${completeText.substring(0, 100)}..."`);
467
485
  console.log("=".repeat(60) + "\n");
486
+ // Check if we've already sent final content - prevent duplicate isFinal=true
487
+ if (hasSentFinal) {
488
+ console.log(`XiaoYi: [STREAM] Skipping duplicate final response (already sent)\n`);
489
+ // Don't send anything, but clear timeout to prevent timeout warnings
490
+ runtime.clearSessionTimeout(sessionId);
491
+ return;
492
+ }
468
493
  const response = {
469
494
  sessionId: sessionId,
470
495
  messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
@@ -481,13 +506,16 @@ exports.xiaoyiPlugin = {
481
506
  },
482
507
  status: "success",
483
508
  };
484
- // Send complete text as FINAL, NOT append
485
- await conn.sendResponse(response, taskId, sessionId, true, false);
486
- console.log(`✓ XiaoYi: Final response sent (${completeText.length} chars)\n`);
487
- // Clear timeout and abort controller on final
488
- runtime.markSessionCompleted(sessionId);
489
- // Note: Don't clear abort controller here, let onIdle handle it
490
- // This ensures we can still abort if there's any pending cleanup
509
+ // Send complete text but NOT as final yet - use append mode to keep stream alive
510
+ // The true isFinal=true will be sent in onIdle callback
511
+ await conn.sendResponse(response, taskId, sessionId, false, true);
512
+ console.log(`✓ XiaoYi: Final response content sent (${completeText.length} chars, stream kept alive)\n`);
513
+ // Mark that we've sent the final content
514
+ hasSentFinal = true;
515
+ // Clear timeout to prevent timeout warnings, but don't mark session as completed yet
516
+ runtime.clearSessionTimeout(sessionId);
517
+ // Note: We DON'T call markSessionCompleted here
518
+ // The session will be marked completed in onIdle when we send isFinal=true
491
519
  }
492
520
  else if (kind === "tool") {
493
521
  // Tool results - typically not sent as messages
@@ -504,15 +532,44 @@ exports.xiaoyiPlugin = {
504
532
  console.log(`XiaoYi: [IDLE] Processing complete`);
505
533
  console.log(` Total time: ${elapsed}ms`);
506
534
  console.log(` Final length: ${accumulatedText.length} chars`);
507
- // Only clear timeout if we have a valid response
508
- // If empty, keep timeout running to handle session conflict
535
+ console.log(` Has sent final: ${hasSentFinal}`);
536
+ // Only send final close message if we have valid content
509
537
  if (accumulatedText.length > 0) {
538
+ const conn = runtime.getConnection();
539
+ if (conn) {
540
+ try {
541
+ // Send the true final response with isFinal=true to properly close the stream
542
+ const finalResponse = {
543
+ sessionId: sessionId,
544
+ messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
545
+ timestamp: Date.now(),
546
+ agentId: resolvedAccount.config.agentId,
547
+ sender: {
548
+ id: resolvedAccount.config.agentId,
549
+ name: "OpenClaw Agent",
550
+ type: "agent",
551
+ },
552
+ content: {
553
+ type: "text",
554
+ text: accumulatedText,
555
+ },
556
+ status: "success",
557
+ };
558
+ // Send with isFinal=true to properly close the A2A communication stream
559
+ await conn.sendResponse(finalResponse, taskId, sessionId, true, false);
560
+ console.log(`✓ XiaoYi: True isFinal=true sent to close stream\n`);
561
+ }
562
+ catch (error) {
563
+ console.error(`✗ XiaoYi: Failed to send final close message:`, error);
564
+ }
565
+ }
566
+ // Now mark session as completed and clean up
510
567
  runtime.markSessionCompleted(sessionId);
511
568
  runtime.clearAbortControllerForSession(sessionId);
512
- console.log(`[TIMEOUT] Timeout cleared (valid response)\n`);
569
+ console.log(`[TIMEOUT] Session completed and cleaned up\n`);
513
570
  }
514
571
  else {
515
- console.log(`[TIMEOUT] Keeping timeout (no valid response)\n`);
572
+ console.log(`[TIMEOUT] No valid response, keeping timeout active\n`);
516
573
  }
517
574
  console.log("=".repeat(60) + "\n");
518
575
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi",
3
- "version": "2.4.5",
3
+ "version": "2.4.7",
4
4
  "description": "XiaoYi channel plugin for OpenClaw",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,6 +20,11 @@
20
20
  "extensions": ["xiaoyi.js"],
21
21
  "channels": ["xiaoyi"],
22
22
  "installDependencies": true,
23
+ "install": {
24
+ "npmSpec": "@ynhcj/xiaoyi",
25
+ "localPath": ".",
26
+ "defaultChoice": "npm"
27
+ },
23
28
  "channel": {
24
29
  "id": "xiaoyi",
25
30
  "label": "XiaoYi",
@@ -29,11 +34,6 @@
29
34
  "blurb": "小艺 A2A 协议支持,通过 WebSocket 连接。",
30
35
  "order": 80,
31
36
  "aliases": ["xy"]
32
- },
33
- "install": {
34
- "npmSpec": "@ynhcj/xiaoyi",
35
- "localPath": ".",
36
- "defaultChoice": "npm"
37
37
  }
38
38
  },
39
39
  "peerDependencies": {