@clawling/clawchat-plugin-openclaw 2026.5.12-38 → 2026.5.12-39

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.
@@ -259,7 +259,8 @@ export function createOpenclawClawlingReplyDispatcher(options) {
259
259
  const { cfg, runtime, account, client, target, replyCtx, inboundMessageId, store, log, } = options;
260
260
  const isGroupTarget = target.chatType === "group";
261
261
  const outputVisibility = effectiveOutputVisibility(account, target.chatId, target.chatType);
262
- const splitFullOutput = outputVisibility === "full" && !isGroupTarget;
262
+ const splitFullOutput = outputVisibility === "full";
263
+ const splitNormalBlockOutput = outputVisibility === "normal";
263
264
  const ownerDirectTarget = () => {
264
265
  const ownerUserId = account.ownerUserId?.trim();
265
266
  return ownerUserId ? { chatId: ownerUserId, chatType: "direct" } : null;
@@ -523,12 +524,12 @@ export function createOpenclawClawlingReplyDispatcher(options) {
523
524
  return result;
524
525
  };
525
526
  const emitFullSegment = async (text, urls = []) => {
526
- if (outputVisibility !== "full") {
527
+ if (outputVisibility !== "full" && !splitNormalBlockOutput) {
527
528
  appendBufferedText(text);
528
529
  appendBufferedUrls(urls);
529
530
  return;
530
531
  }
531
- if (!splitFullOutput) {
532
+ if (!splitFullOutput && !splitNormalBlockOutput) {
532
533
  appendBufferedText(text);
533
534
  appendBufferedUrls(urls);
534
535
  return;
@@ -596,6 +597,9 @@ export function createOpenclawClawlingReplyDispatcher(options) {
596
597
  if (outputVisibility === "full") {
597
598
  await emitFullSegment(text, urls);
598
599
  }
600
+ else if (splitNormalBlockOutput) {
601
+ await emitFullSegment(text, urls);
602
+ }
599
603
  else if (outputVisibility === "minimal" || outputVisibility === "normal") {
600
604
  appendBufferedText(text);
601
605
  appendBufferedUrls(urls);
@@ -627,6 +631,12 @@ export function createOpenclawClawlingReplyDispatcher(options) {
627
631
  }
628
632
  const finalText = richFragment && account.richInteractions ? mergeFinalText("") : mergeFinalText(text);
629
633
  const finalUrls = mergeFinalUrls(urls);
634
+ if (isClawChatNoopResponseText(finalText) &&
635
+ !richFragment &&
636
+ finalUrls.length === 0) {
637
+ log?.info?.(`[${account.accountId}] clawchat-plugin-openclaw final suppressed: no-reply token`);
638
+ return;
639
+ }
630
640
  const mediaFragments = await uploadMediaUrls(finalUrls);
631
641
  const result = await sendStatic(finalText, mediaFragments, richFragment && account.richInteractions ? [richFragment] : [], { recordMessage: true });
632
642
  if (result?.messageId)
@@ -638,12 +648,8 @@ export function createOpenclawClawlingReplyDispatcher(options) {
638
648
  onError: (error, info) => {
639
649
  const errorText = normalizeReplyErrorText(error);
640
650
  log?.error?.(`[${account.accountId}] clawchat-plugin-openclaw ${info.kind} reply failed: ${errorText}`);
641
- if (!isGroupTarget && outputVisibility === "full")
651
+ if (outputVisibility === "full")
642
652
  void emitFullRuntimeText("error", errorText);
643
- if (isGroupTarget) {
644
- log?.error?.(`[${account.accountId}] clawchat-plugin-openclaw group runtime failure suppressed from ClawChat clients group=${target.chatId}`);
645
- return;
646
- }
647
653
  },
648
654
  onIdle: async () => {
649
655
  emitTyping(false);
@@ -670,10 +676,10 @@ export function createOpenclawClawlingReplyDispatcher(options) {
670
676
  replyOptions: {
671
677
  ...base.replyOptions,
672
678
  sourceReplyDeliveryMode: "automatic",
673
- disableBlockStreaming: true,
679
+ disableBlockStreaming: !splitNormalBlockOutput,
674
680
  suppressDefaultToolProgressMessages: true,
675
- allowProgressCallbacksWhenSourceDeliverySuppressed: outputVisibility === "full" ? true : undefined,
676
- onReasoningStream: outputVisibility === "full"
681
+ allowProgressCallbacksWhenSourceDeliverySuppressed: splitFullOutput ? true : undefined,
682
+ onReasoningStream: splitFullOutput
677
683
  ? async (payload) => {
678
684
  if (consumeTerminalSend("reasoning"))
679
685
  return;
@@ -684,14 +690,14 @@ export function createOpenclawClawlingReplyDispatcher(options) {
684
690
  reasoningText = reasoningText ? `${reasoningText}\n${trimmed}` : trimmed;
685
691
  }
686
692
  : undefined,
687
- onToolStart: outputVisibility === "full"
693
+ onToolStart: splitFullOutput
688
694
  ? async (payload) => {
689
695
  if (consumeTerminalSend("tool-start"))
690
696
  return;
691
697
  await emitFullSegment(formatToolStartSummary(payload));
692
698
  }
693
699
  : undefined,
694
- onToolResult: outputVisibility === "full"
700
+ onToolResult: splitFullOutput
695
701
  ? async (payload) => {
696
702
  if (consumeTerminalSend("tool-result"))
697
703
  return;
@@ -701,7 +707,7 @@ export function createOpenclawClawlingReplyDispatcher(options) {
701
707
  await emitFullRuntimeText("tool result", text, resolveOutboundMediaUrls(payload).filter(Boolean));
702
708
  }
703
709
  : undefined,
704
- onItemEvent: outputVisibility === "full"
710
+ onItemEvent: splitFullOutput
705
711
  ? async (payload) => {
706
712
  if (consumeTerminalSend("item-event"))
707
713
  return;
@@ -710,35 +716,35 @@ export function createOpenclawClawlingReplyDispatcher(options) {
710
716
  await emitFullRuntimeText("progress", summarizeProgressPayload(payload));
711
717
  }
712
718
  : undefined,
713
- onPlanUpdate: outputVisibility === "full"
719
+ onPlanUpdate: splitFullOutput
714
720
  ? async (payload) => {
715
721
  if (consumeTerminalSend("plan-update"))
716
722
  return;
717
723
  await emitFullRuntimeText("plan", summarizeProgressPayload(payload));
718
724
  }
719
725
  : undefined,
720
- onCommandOutput: outputVisibility === "full"
726
+ onCommandOutput: splitFullOutput
721
727
  ? async (payload) => {
722
728
  if (consumeTerminalSend("command-output"))
723
729
  return;
724
730
  await emitFullSegment(formatCommandOutputSummary(payload));
725
731
  }
726
732
  : undefined,
727
- onPatchSummary: outputVisibility === "full"
733
+ onPatchSummary: splitFullOutput
728
734
  ? async (payload) => {
729
735
  if (consumeTerminalSend("patch-summary"))
730
736
  return;
731
737
  await emitFullSegment(formatPatchSummary(payload));
732
738
  }
733
739
  : undefined,
734
- onCompactionStart: outputVisibility === "full"
740
+ onCompactionStart: splitFullOutput
735
741
  ? async () => {
736
742
  if (consumeTerminalSend("compaction-start"))
737
743
  return;
738
744
  await emitFullSegment("[compaction] started");
739
745
  }
740
746
  : undefined,
741
- onCompactionEnd: outputVisibility === "full"
747
+ onCompactionEnd: splitFullOutput
742
748
  ? async () => {
743
749
  if (consumeTerminalSend("compaction-end"))
744
750
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawling/clawchat-plugin-openclaw",
3
- "version": "2026.5.12-38",
3
+ "version": "2026.5.12-39",
4
4
  "description": "OpenClaw ClawChat channel plugin",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -13,6 +13,7 @@ This skill guides agent behavior for ClawChat-aware tasks. Use the registered Cl
13
13
 
14
14
  - Use registered ClawChat plugin tools for account/profile, friends, users, moments, comments, reactions, avatar, media, and read-only conversation lookup.
15
15
  - If a requested ClawChat tool is unavailable or returns a config error, report that result and stop instead of bypassing the plugin.
16
+ - Use the `/clawchat-output` slash command when the user asks to change how much ClawChat runtime output is shown in the current conversation.
16
17
 
17
18
  ## OpenClaw CLI
18
19
 
@@ -30,6 +31,18 @@ Use `update --force` only when local ClawChat plugin or skill files look corrupt
30
31
 
31
32
  If `channels add` reports `Unknown channel: clawchat-plugin-openclaw`, use the runtime slash command `/clawchat-activate CODE` after the operator ensures the plugin is loaded.
32
33
 
34
+ ## Output Visibility
35
+
36
+ When the user asks to change ClawChat output verbosity, use the runtime slash command for the current conversation. Treat natural-language wording as aliases for the three supported modes:
37
+
38
+ | User wording | Command |
39
+ | --- | --- |
40
+ | quiet mode, silent mode, minimal output, final-only output, `minimal` | `/clawchat-output minimal` |
41
+ | conversation mode, normal mode, regular mode, default output, `normal` | `/clawchat-output normal` |
42
+ | dev mode, developer mode, verbose mode, full output, `full` | `/clawchat-output full` |
43
+
44
+ Do not edit config files directly for this request. If the slash command returns an error, report that error instead of claiming the mode changed.
45
+
33
46
  ## Plugin Tool Routing
34
47
 
35
48
  Tool descriptions are authoritative. These routing hints resolve common ambiguity:
@@ -388,7 +388,8 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
388
388
  } = options;
389
389
  const isGroupTarget = target.chatType === "group";
390
390
  const outputVisibility = effectiveOutputVisibility(account, target.chatId, target.chatType);
391
- const splitFullOutput = outputVisibility === "full" && !isGroupTarget;
391
+ const splitFullOutput = outputVisibility === "full";
392
+ const splitNormalBlockOutput = outputVisibility === "normal";
392
393
  const ownerDirectTarget = () => {
393
394
  const ownerUserId = account.ownerUserId?.trim();
394
395
  return ownerUserId ? { chatId: ownerUserId, chatType: "direct" as const } : null;
@@ -670,12 +671,12 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
670
671
  };
671
672
 
672
673
  const emitFullSegment = async (text: string, urls: string[] = []): Promise<void> => {
673
- if (outputVisibility !== "full") {
674
+ if (outputVisibility !== "full" && !splitNormalBlockOutput) {
674
675
  appendBufferedText(text);
675
676
  appendBufferedUrls(urls);
676
677
  return;
677
678
  }
678
- if (!splitFullOutput) {
679
+ if (!splitFullOutput && !splitNormalBlockOutput) {
679
680
  appendBufferedText(text);
680
681
  appendBufferedUrls(urls);
681
682
  return;
@@ -750,6 +751,8 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
750
751
  if (info?.kind === "block") {
751
752
  if (outputVisibility === "full") {
752
753
  await emitFullSegment(text, urls);
754
+ } else if (splitNormalBlockOutput) {
755
+ await emitFullSegment(text, urls);
753
756
  } else if (outputVisibility === "minimal" || outputVisibility === "normal") {
754
757
  appendBufferedText(text);
755
758
  appendBufferedUrls(urls);
@@ -782,6 +785,14 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
782
785
  }
783
786
  const finalText = richFragment && account.richInteractions ? mergeFinalText("") : mergeFinalText(text);
784
787
  const finalUrls = mergeFinalUrls(urls);
788
+ if (
789
+ isClawChatNoopResponseText(finalText) &&
790
+ !richFragment &&
791
+ finalUrls.length === 0
792
+ ) {
793
+ log?.info?.(`[${account.accountId}] clawchat-plugin-openclaw final suppressed: no-reply token`);
794
+ return;
795
+ }
785
796
  const mediaFragments = await uploadMediaUrls(finalUrls);
786
797
  const result = await sendStatic(
787
798
  finalText,
@@ -800,13 +811,7 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
800
811
  log?.error?.(
801
812
  `[${account.accountId}] clawchat-plugin-openclaw ${info.kind} reply failed: ${errorText}`,
802
813
  );
803
- if (!isGroupTarget && outputVisibility === "full") void emitFullRuntimeText("error", errorText);
804
- if (isGroupTarget) {
805
- log?.error?.(
806
- `[${account.accountId}] clawchat-plugin-openclaw group runtime failure suppressed from ClawChat clients group=${target.chatId}`,
807
- );
808
- return;
809
- }
814
+ if (outputVisibility === "full") void emitFullRuntimeText("error", errorText);
810
815
  },
811
816
  onIdle: async () => {
812
817
  emitTyping(false);
@@ -830,10 +835,10 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
830
835
  replyOptions: {
831
836
  ...base.replyOptions,
832
837
  sourceReplyDeliveryMode: "automatic",
833
- disableBlockStreaming: true,
838
+ disableBlockStreaming: !splitNormalBlockOutput,
834
839
  suppressDefaultToolProgressMessages: true,
835
- allowProgressCallbacksWhenSourceDeliverySuppressed: outputVisibility === "full" ? true : undefined,
836
- onReasoningStream: outputVisibility === "full"
840
+ allowProgressCallbacksWhenSourceDeliverySuppressed: splitFullOutput ? true : undefined,
841
+ onReasoningStream: splitFullOutput
837
842
  ? async (payload: ReplyPayload) => {
838
843
  if (consumeTerminalSend("reasoning")) return;
839
844
  const text = resolvePayloadText(payload);
@@ -842,13 +847,13 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
842
847
  if (trimmed) reasoningText = reasoningText ? `${reasoningText}\n${trimmed}` : trimmed;
843
848
  }
844
849
  : undefined,
845
- onToolStart: outputVisibility === "full"
850
+ onToolStart: splitFullOutput
846
851
  ? async (payload) => {
847
852
  if (consumeTerminalSend("tool-start")) return;
848
853
  await emitFullSegment(formatToolStartSummary(payload));
849
854
  }
850
855
  : undefined,
851
- onToolResult: outputVisibility === "full"
856
+ onToolResult: splitFullOutput
852
857
  ? async (payload: ReplyPayload) => {
853
858
  if (consumeTerminalSend("tool-result")) return;
854
859
  const text = resolvePayloadText(payload);
@@ -856,38 +861,38 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
856
861
  await emitFullRuntimeText("tool result", text, resolveOutboundMediaUrls(payload).filter(Boolean));
857
862
  }
858
863
  : undefined,
859
- onItemEvent: outputVisibility === "full"
864
+ onItemEvent: splitFullOutput
860
865
  ? async (payload: Record<string, unknown>) => {
861
866
  if (consumeTerminalSend("item-event")) return;
862
867
  if (isToolProgressItem(payload)) return;
863
868
  await emitFullRuntimeText("progress", summarizeProgressPayload(payload));
864
869
  }
865
870
  : undefined,
866
- onPlanUpdate: outputVisibility === "full"
871
+ onPlanUpdate: splitFullOutput
867
872
  ? async (payload: Record<string, unknown>) => {
868
873
  if (consumeTerminalSend("plan-update")) return;
869
874
  await emitFullRuntimeText("plan", summarizeProgressPayload(payload));
870
875
  }
871
876
  : undefined,
872
- onCommandOutput: outputVisibility === "full"
877
+ onCommandOutput: splitFullOutput
873
878
  ? async (payload: Record<string, unknown>) => {
874
879
  if (consumeTerminalSend("command-output")) return;
875
880
  await emitFullSegment(formatCommandOutputSummary(payload));
876
881
  }
877
882
  : undefined,
878
- onPatchSummary: outputVisibility === "full"
883
+ onPatchSummary: splitFullOutput
879
884
  ? async (payload: Record<string, unknown>) => {
880
885
  if (consumeTerminalSend("patch-summary")) return;
881
886
  await emitFullSegment(formatPatchSummary(payload));
882
887
  }
883
888
  : undefined,
884
- onCompactionStart: outputVisibility === "full"
889
+ onCompactionStart: splitFullOutput
885
890
  ? async () => {
886
891
  if (consumeTerminalSend("compaction-start")) return;
887
892
  await emitFullSegment("[compaction] started");
888
893
  }
889
894
  : undefined,
890
- onCompactionEnd: outputVisibility === "full"
895
+ onCompactionEnd: splitFullOutput
891
896
  ? async () => {
892
897
  if (consumeTerminalSend("compaction-end")) return;
893
898
  await emitFullSegment("[compaction] finished");