openclaw-linso 1.0.14 → 1.0.16

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.
@@ -418,6 +418,8 @@ async function handleIncomingMessage(deviceId, text, imageUrls, cfg, log) {
418
418
  const runId = `linso-${Date.now()}`;
419
419
  deviceActiveRunId.set(deviceId, runId); // 记录当前活跃 runId
420
420
  let delivered = false; // 是否已发送过内容
421
+ let deltaCount = 0; // 已发送的 delta 数量
422
+ let lastSentText = ""; // 上次发送的累计文本,用于计算真实增量
421
423
  const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
422
424
  deliver: async (payload, info) => {
423
425
  // 检查 stop 标志
@@ -431,6 +433,13 @@ async function handleIncomingMessage(deviceId, text, imageUrls, cfg, log) {
431
433
  log(`[Linso] deliver 跳过: kind=${info.kind} text=empty`);
432
434
  return;
433
435
  }
436
+ // 如果已经通过 onPartialReply 发过 delta,则 final 阶段发 stream_end 而非重复文本
437
+ if (info.kind === "final" && deltaCount > 0) {
438
+ log(`[Linso] deliver final skipped (streamed ${deltaCount} deltas): text="${payload.text.slice(0, 40)}"`);
439
+ delivered = true;
440
+ sendToClient(deviceId, { type: "stream_end", runId });
441
+ return;
442
+ }
434
443
  log(`[Linso] deliver called: kind=${info.kind} text="${payload.text.slice(0, 40)}"`);
435
444
  delivered = true;
436
445
  sendToClient(deviceId, {
@@ -465,7 +474,24 @@ async function handleIncomingMessage(deviceId, text, imageUrls, cfg, log) {
465
474
  ctx: inboundCtx,
466
475
  cfg: effectiveCfg,
467
476
  dispatcher,
468
- replyOptions,
477
+ replyOptions: {
478
+ ...replyOptions,
479
+ onPartialReply: (payload) => {
480
+ if (deviceStopFlags.get(deviceId))
481
+ return;
482
+ if (!payload.text)
483
+ return;
484
+ // payload.text 是累积完整文本,提取真实增量部分
485
+ const fullText = payload.text;
486
+ if (fullText.length <= lastSentText.length)
487
+ return; // 没有新内容
488
+ const increment = fullText.slice(lastSentText.length);
489
+ lastSentText = fullText;
490
+ deltaCount++;
491
+ delivered = true;
492
+ sendToClient(deviceId, { type: "delta", runId, text: increment });
493
+ },
494
+ },
469
495
  }),
470
496
  });
471
497
  log(`[Linso] dispatchResult: ${JSON.stringify(dispatchResult)} delivered=${delivered}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-linso",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Linso iOS App channel plugin for OpenClaw — connects via cloud Relay Server",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/monitor.ts CHANGED
@@ -495,6 +495,8 @@ async function handleIncomingMessage(
495
495
  const runId = `linso-${Date.now()}`;
496
496
  deviceActiveRunId.set(deviceId, runId); // 记录当前活跃 runId
497
497
  let delivered = false; // 是否已发送过内容
498
+ let deltaCount = 0; // 已发送的 delta 数量
499
+ let lastSentText = ""; // 上次发送的累计文本,用于计算真实增量
498
500
 
499
501
  const { dispatcher, replyOptions, markDispatchIdle } =
500
502
  core.channel.reply.createReplyDispatcherWithTyping({
@@ -509,6 +511,13 @@ async function handleIncomingMessage(
509
511
  log(`[Linso] deliver 跳过: kind=${info.kind} text=empty`);
510
512
  return;
511
513
  }
514
+ // 如果已经通过 onPartialReply 发过 delta,则 final 阶段发 stream_end 而非重复文本
515
+ if (info.kind === "final" && deltaCount > 0) {
516
+ log(`[Linso] deliver final skipped (streamed ${deltaCount} deltas): text="${payload.text.slice(0, 40)}"`);
517
+ delivered = true;
518
+ sendToClient(deviceId, { type: "stream_end", runId });
519
+ return;
520
+ }
512
521
  log(`[Linso] deliver called: kind=${info.kind} text="${payload.text.slice(0, 40)}"`);
513
522
  delivered = true;
514
523
  sendToClient(deviceId, {
@@ -546,7 +555,21 @@ async function handleIncomingMessage(
546
555
  ctx: inboundCtx,
547
556
  cfg: effectiveCfg,
548
557
  dispatcher,
549
- replyOptions,
558
+ replyOptions: {
559
+ ...replyOptions,
560
+ onPartialReply: (payload) => {
561
+ if (deviceStopFlags.get(deviceId)) return;
562
+ if (!payload.text) return;
563
+ // payload.text 是累积完整文本,提取真实增量部分
564
+ const fullText = payload.text;
565
+ if (fullText.length <= lastSentText.length) return; // 没有新内容
566
+ const increment = fullText.slice(lastSentText.length);
567
+ lastSentText = fullText;
568
+ deltaCount++;
569
+ delivered = true;
570
+ sendToClient(deviceId, { type: "delta", runId, text: increment });
571
+ },
572
+ },
550
573
  }),
551
574
  });
552
575