@ynhcj/xiaoyi 2.4.4 → 2.4.5

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 +94 -32
  2. package/package.json +1 -1
package/dist/channel.js CHANGED
@@ -322,6 +322,7 @@ exports.xiaoyiPlugin = {
322
322
  const taskId = runtime.getTaskIdForSession(sessionId) || `task_${Date.now()}`;
323
323
  const startTime = Date.now();
324
324
  let accumulatedText = "";
325
+ let sentTextLength = 0; // Track sent text length for streaming
325
326
  // ==================== CREATE ABORT CONTROLLER ====================
326
327
  // Create AbortController for this session to allow cancelation
327
328
  const { controller: abortController, signal: abortSignal } = runtime.createAbortControllerForSession(sessionId);
@@ -365,15 +366,17 @@ exports.xiaoyiPlugin = {
365
366
  ctx: msgContext,
366
367
  cfg: config,
367
368
  dispatcherOptions: {
368
- deliver: async (payload) => {
369
+ deliver: async (payload, info) => {
369
370
  const elapsed = Date.now() - startTime;
370
371
  const completeText = payload.text || "";
371
372
  accumulatedText = completeText;
373
+ const kind = info.kind; // "tool" | "block" | "final"
372
374
  // Check if session was aborted
373
375
  if (runtime.isSessionAborted(sessionId)) {
374
376
  console.log("\n" + "=".repeat(60));
375
377
  console.log(`[ABORT] Response received AFTER abort`);
376
378
  console.log(` Session: ${sessionId}`);
379
+ console.log(` Kind: ${kind}`);
377
380
  console.log(` Elapsed: ${elapsed}ms`);
378
381
  console.log(` Action: DISCARDING (session was canceled)`);
379
382
  console.log("=".repeat(60) + "\n");
@@ -385,6 +388,7 @@ exports.xiaoyiPlugin = {
385
388
  console.log("\n" + "=".repeat(60));
386
389
  console.log(`[TIMEOUT] Response received AFTER timeout`);
387
390
  console.log(` Session: ${sessionId}`);
391
+ console.log(` Kind: ${kind}`);
388
392
  console.log(` Elapsed: ${elapsed}ms`);
389
393
  console.log(` Action: DISCARDING (timeout message already sent)`);
390
394
  console.log("=".repeat(60) + "\n");
@@ -394,47 +398,105 @@ exports.xiaoyiPlugin = {
394
398
  // If response is empty, don't clear timeout (let it trigger)
395
399
  if (!completeText || completeText.length === 0) {
396
400
  console.log("\n" + "=".repeat(60));
397
- console.log(`[TIMEOUT] Empty response detected`);
401
+ console.log(`[STREAM] Empty ${kind} response detected`);
398
402
  console.log(` Session: ${sessionId}`);
399
403
  console.log(` Elapsed: ${elapsed}ms`);
400
- console.log(` Action: KEEPING TIMEOUT (session conflict detected)`);
404
+ console.log(` Action: SKIPPING (empty response)`);
401
405
  console.log("=".repeat(60) + "\n");
402
406
  // Don't send anything, and don't clear timeout
403
407
  return;
404
408
  }
405
- console.log("\n" + "-".repeat(60));
406
- console.log(`XiaoYi: [DELIVER] AI response received`);
407
- console.log(` Elapsed: ${elapsed}ms`);
408
- console.log(` Length: ${completeText.length} chars`);
409
- console.log(` Preview: ${completeText.substring(0, 100)}...`);
410
- console.log("-".repeat(60) + "\n");
411
- // Send final response to XiaoYi
412
- const response = {
413
- sessionId: sessionId,
414
- messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
415
- timestamp: Date.now(),
416
- agentId: resolvedAccount.config.agentId,
417
- sender: {
418
- id: resolvedAccount.config.agentId,
419
- name: "OpenClaw Agent",
420
- type: "agent",
421
- },
422
- content: {
423
- type: "text",
424
- text: accumulatedText,
425
- },
426
- status: "success",
427
- };
428
409
  const conn = runtime.getConnection();
429
- if (conn) {
410
+ if (!conn) {
411
+ console.error(`✗ XiaoYi: Connection not available\n`);
412
+ return;
413
+ }
414
+ // ==================== STREAMING LOGIC ====================
415
+ // For "block" kind: send incremental text (streaming)
416
+ // For "final" kind: send complete text (final message)
417
+ if (kind === "block") {
418
+ // Calculate incremental text
419
+ const incrementalText = completeText.slice(sentTextLength);
420
+ if (incrementalText.length > 0) {
421
+ console.log("\n" + "-".repeat(60));
422
+ console.log(`XiaoYi: [STREAM] Incremental block received`);
423
+ console.log(` Session: ${sessionId}`);
424
+ console.log(` Elapsed: ${elapsed}ms`);
425
+ console.log(` Total length: ${completeText.length} chars`);
426
+ console.log(` Incremental: +${incrementalText.length} chars (sent: ${sentTextLength})`);
427
+ console.log(` Preview: "${incrementalText.substring(0, 50)}${incrementalText.length > 50 ? "..." : ""}"`);
428
+ console.log("-".repeat(60) + "\n");
429
+ const response = {
430
+ sessionId: sessionId,
431
+ messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
432
+ timestamp: Date.now(),
433
+ agentId: resolvedAccount.config.agentId,
434
+ sender: {
435
+ id: resolvedAccount.config.agentId,
436
+ name: "OpenClaw Agent",
437
+ type: "agent",
438
+ },
439
+ content: {
440
+ type: "text",
441
+ text: incrementalText,
442
+ },
443
+ status: "success",
444
+ };
445
+ // Send incremental text, NOT final, append mode
446
+ await conn.sendResponse(response, taskId, sessionId, false, true);
447
+ console.log(`✓ XiaoYi: Stream chunk sent (+${incrementalText.length} chars)\n`);
448
+ // Update sent length
449
+ sentTextLength = completeText.length;
450
+ }
451
+ else {
452
+ console.log("\n" + "-".repeat(60));
453
+ console.log(`XiaoYi: [STREAM] No new text to send`);
454
+ console.log(` Session: ${sessionId}`);
455
+ console.log(` Total length: ${completeText.length} chars`);
456
+ console.log(` Already sent: ${sentTextLength} chars`);
457
+ console.log("-".repeat(60) + "\n");
458
+ }
459
+ }
460
+ else if (kind === "final") {
461
+ console.log("\n" + "=".repeat(60));
462
+ console.log(`XiaoYi: [STREAM] Final response received`);
463
+ console.log(` Session: ${sessionId}`);
464
+ console.log(` Elapsed: ${elapsed}ms`);
465
+ console.log(` Total length: ${completeText.length} chars`);
466
+ console.log(` Preview: "${completeText.substring(0, 100)}..."`);
467
+ console.log("=".repeat(60) + "\n");
468
+ const response = {
469
+ sessionId: sessionId,
470
+ messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
471
+ timestamp: Date.now(),
472
+ agentId: resolvedAccount.config.agentId,
473
+ sender: {
474
+ id: resolvedAccount.config.agentId,
475
+ name: "OpenClaw Agent",
476
+ type: "agent",
477
+ },
478
+ content: {
479
+ type: "text",
480
+ text: completeText,
481
+ },
482
+ status: "success",
483
+ };
484
+ // Send complete text as FINAL, NOT append
430
485
  await conn.sendResponse(response, taskId, sessionId, true, false);
431
- console.log(`✓ XiaoYi: Response sent successfully\n`);
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
432
491
  }
433
- else {
434
- console.error(`✗ XiaoYi: Connection not available\n`);
492
+ else if (kind === "tool") {
493
+ // Tool results - typically not sent as messages
494
+ console.log("\n" + "-".repeat(60));
495
+ console.log(`XiaoYi: [STREAM] Tool result received (not forwarded)`);
496
+ console.log(` Session: ${sessionId}`);
497
+ console.log(` Elapsed: ${elapsed}ms`);
498
+ console.log("-".repeat(60) + "\n");
435
499
  }
436
- // Clear timeout as response was sent successfully
437
- runtime.markSessionCompleted(sessionId);
438
500
  },
439
501
  onIdle: async () => {
440
502
  const elapsed = Date.now() - startTime;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi",
3
- "version": "2.4.4",
3
+ "version": "2.4.5",
4
4
  "description": "XiaoYi channel plugin for OpenClaw",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",