diff --git a/CHANGELOG.md b/CHANGELOG.md index 863fbc134b3..679f95dbac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Telegram: deliver verbose tool summaries inside forum topic sessions again, so threaded topic chats now match DM verbose behavior. (#43236) Thanks @frankbuild. - Agents/sandbox: honor `tools.sandbox.tools.alsoAllow`, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman. - Agents/sandbox: make blocked-tool guidance glob-aware again, redact/sanitize session-specific explain hints for safer copy-paste, and avoid leaking control-character session keys in those hints. (#54684) Thanks @ngutman. - Feishu: close WebSocket connections on monitor stop/abort so ghost connections no longer persist, preventing duplicate event processing and resource leaks across restart cycles. (#52844) Thanks @schumilin. diff --git a/src/auto-reply/reply/dispatch-from-config.test.ts b/src/auto-reply/reply/dispatch-from-config.test.ts index d93a46863b9..025d0f3ff03 100644 --- a/src/auto-reply/reply/dispatch-from-config.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.test.ts @@ -755,6 +755,34 @@ describe("dispatchReplyFromConfig", () => { expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1); }); + it("delivers tool summaries in forum topic sessions (group + IsForum)", async () => { + setNoAbort(); + const cfg = emptyConfig; + const dispatcher = createDispatcher(); + const ctx = buildTestCtx({ + Provider: "telegram", + ChatType: "group", + IsForum: true, + MessageThreadId: 99, + }); + + const replyResolver = async ( + _ctx: MsgContext, + opts?: GetReplyOptions, + _cfg?: OpenClawConfig, + ) => { + await opts?.onToolResult?.({ text: "🔧 exec: ls" }); + return { text: "done" } satisfies ReplyPayload; + }; + + await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver }); + expect(dispatcher.sendToolResult).toHaveBeenCalledWith( + expect.objectContaining({ text: "🔧 exec: ls" }), + ); + expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(1); + expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1); + }); + it("delivers deterministic exec approval tool payloads in groups", async () => { setNoAbort(); const cfg = emptyConfig; diff --git a/src/auto-reply/reply/dispatch-from-config.ts b/src/auto-reply/reply/dispatch-from-config.ts index 8f68e4ab784..9d9f54f60e7 100644 --- a/src/auto-reply/reply/dispatch-from-config.ts +++ b/src/auto-reply/reply/dispatch-from-config.ts @@ -587,7 +587,10 @@ export async function dispatchReplyFromConfig(params: { } } - const shouldSendToolSummaries = ctx.ChatType !== "group" && ctx.CommandSource !== "native"; + // Forum topics are threaded conversations within a group — verbose tool + // summaries should be delivered into the topic thread, same as DMs. + const shouldSendToolSummaries = + (ctx.ChatType !== "group" || ctx.IsForum === true) && ctx.CommandSource !== "native"; const acpDispatch = await dispatchAcpRuntime.tryDispatchAcpReply({ ctx, cfg,