Skip to content

Commit 06d32db

Browse files
committed
feat(messages): add toolMessageLogging override
1 parent 1373ac6 commit 06d32db

7 files changed

Lines changed: 67 additions & 2 deletions

File tree

docs/gateway/configuration-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,6 +1762,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
17621762
responsePrefix: "🦞", // or "auto"
17631763
ackReaction: "👀",
17641764
ackReactionScope: "group-mentions", // group-mentions | group-all | direct | all
1765+
toolMessageLogging: true,
17651766
removeAckAfterReply: false,
17661767
queue: {
17671768
mode: "collect", // steer | followup | collect | steer-backlog | steer+backlog | queue | interrupt
@@ -1808,6 +1809,7 @@ Variables are case-insensitive. `{think}` is an alias for `{thinkingLevel}`.
18081809
- Per-channel overrides: `channels.<channel>.ackReaction`, `channels.<channel>.accounts.<id>.ackReaction`.
18091810
- Resolution order: account → channel → `messages.ackReaction` → identity fallback.
18101811
- Scope: `group-mentions` (default), `group-all`, `direct`, `all`.
1812+
- `toolMessageLogging`: when `false`, suppresses intermediate tool-result/status messages and only sends the final reply. Default behavior is automatic: enabled for DMs and forum-style threads, suppressed for regular group chats.
18111813
- `removeAckAfterReply`: removes ack after reply on Slack, Discord, and Telegram.
18121814
- `messages.statusReactions.enabled`: enables lifecycle status reactions on Slack, Discord, and Telegram.
18131815
On Slack and Discord, unset keeps status reactions enabled when ack reactions are active.

src/auto-reply/reply/dispatch-from-config.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,61 @@ describe("dispatchReplyFromConfig", () => {
10561056
expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);
10571057
});
10581058

1059+
it("suppresses DM tool summaries when messages.toolMessageLogging=false", async () => {
1060+
setNoAbort();
1061+
const cfg = {
1062+
...emptyConfig,
1063+
messages: { toolMessageLogging: false },
1064+
} satisfies OpenClawConfig;
1065+
const dispatcher = createDispatcher();
1066+
const ctx = buildTestCtx({
1067+
Provider: "telegram",
1068+
ChatType: "direct",
1069+
});
1070+
1071+
const replyResolver = async (
1072+
_ctx: MsgContext,
1073+
opts?: GetReplyOptions,
1074+
_cfg?: OpenClawConfig,
1075+
) => {
1076+
await opts?.onToolResult?.({ text: "🔧 exec: ls" });
1077+
return { text: "done" } satisfies ReplyPayload;
1078+
};
1079+
1080+
await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
1081+
expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
1082+
expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);
1083+
});
1084+
1085+
it("delivers regular group tool summaries when messages.toolMessageLogging=true", async () => {
1086+
setNoAbort();
1087+
const cfg = {
1088+
...emptyConfig,
1089+
messages: { toolMessageLogging: true },
1090+
} satisfies OpenClawConfig;
1091+
const dispatcher = createDispatcher();
1092+
const ctx = buildTestCtx({
1093+
Provider: "telegram",
1094+
ChatType: "group",
1095+
});
1096+
1097+
const replyResolver = async (
1098+
_ctx: MsgContext,
1099+
opts?: GetReplyOptions,
1100+
_cfg?: OpenClawConfig,
1101+
) => {
1102+
await opts?.onToolResult?.({ text: "🔧 exec: ls" });
1103+
return { text: "done" } satisfies ReplyPayload;
1104+
};
1105+
1106+
await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
1107+
expect(dispatcher.sendToolResult).toHaveBeenCalledWith(
1108+
expect.objectContaining({ text: "🔧 exec: ls" }),
1109+
);
1110+
expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(1);
1111+
expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);
1112+
});
1113+
10591114
it("delivers deterministic exec approval tool payloads in groups", async () => {
10601115
setNoAbort();
10611116
const cfg = emptyConfig;

src/auto-reply/reply/dispatch-from-config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,10 @@ export async function dispatchReplyFromConfig(params: {
558558
chatType: sessionStoreEntry.entry?.chatType,
559559
});
560560

561-
const shouldSendToolSummaries = ctx.ChatType !== "group" || ctx.IsForum === true;
562-
const shouldSendToolStartStatuses = ctx.ChatType !== "group" || ctx.IsForum === true;
561+
const defaultToolMessageLogging = ctx.ChatType !== "group" || ctx.IsForum === true;
562+
const shouldSendToolSummaries = cfg.messages?.toolMessageLogging ?? defaultToolMessageLogging;
563+
const shouldSendToolStartStatuses =
564+
cfg.messages?.toolMessageLogging ?? defaultToolMessageLogging;
563565
const sendFinalPayload = async (
564566
payload: ReplyPayload,
565567
): Promise<{ queuedFinal: boolean; routedFinalCount: number }> => {

src/config/schema.help.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,8 @@ export const FIELD_HELP: Record<string, string> = {
14951495
"Map provider -> channel id -> model override (values are provider/model or aliases).",
14961496
"messages.suppressToolErrors":
14971497
"When true, suppress ⚠️ tool-error warnings from being shown to the user. The agent already sees errors in context and can retry. Default: false.",
1498+
"messages.toolMessageLogging":
1499+
"When false, suppress intermediate tool-result/status messages and only send the final reply. Default behavior is automatic: enabled for DMs/forums, suppressed for regular group chats.",
14981500
"messages.ackReaction": "Emoji reaction used to acknowledge inbound messages (empty disables).",
14991501
"messages.ackReactionScope":
15001502
'When to send ack reactions ("group-mentions", "group-all", "direct", "all", "off", "none"). "off"/"none" disables ack reactions entirely.',

src/config/schema.labels.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,7 @@ export const FIELD_LABELS: Record<string, string> = {
730730
"messages.queue.drop": "Queue Drop Strategy",
731731
"messages.inbound": "Inbound Debounce",
732732
"messages.suppressToolErrors": "Suppress Tool Error Warnings",
733+
"messages.toolMessageLogging": "Tool Message Logging",
733734
"messages.ackReaction": "Ack Reaction Emoji",
734735
"messages.ackReactionScope": "Ack Reaction Scope",
735736
"messages.removeAckAfterReply": "Remove Ack Reaction After Reply",

src/config/types.messages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ export type MessagesConfig = {
118118
removeAckAfterReply?: boolean;
119119
/** Lifecycle status reactions configuration. */
120120
statusReactions?: StatusReactionsConfig;
121+
/** When false, suppress intermediate tool-result/status messages and only send the final reply. Default: auto (on for DMs/forums, off for regular groups). */
122+
toolMessageLogging?: boolean;
121123
/** When true, suppress ⚠️ tool-error warnings from being shown to the user. Default: false. */
122124
suppressToolErrors?: boolean;
123125
/** Text-to-speech settings for outbound replies. */

src/config/zod-schema.session.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ export const MessagesSchema = z
186186
})
187187
.strict()
188188
.optional(),
189+
toolMessageLogging: z.boolean().optional(),
189190
suppressToolErrors: z.boolean().optional(),
190191
tts: TtsConfigSchema,
191192
})

0 commit comments

Comments
 (0)