Skip to content

fix(auto-reply): suppress tool summaries in Slack channels when verboseDefault=off (#69067)#70152

Closed
martingarramon wants to merge 1 commit into
openclaw:mainfrom
martingarramon:fix/slack-channel-tool-summary-gate
Closed

fix(auto-reply): suppress tool summaries in Slack channels when verboseDefault=off (#69067)#70152
martingarramon wants to merge 1 commit into
openclaw:mainfrom
martingarramon:fix/slack-channel-tool-summary-gate

Conversation

@martingarramon

Copy link
Copy Markdown
Contributor

Summary

Tighten the Slack tool-summary gate in dispatch-from-config.ts so it excludes ChatType === "channel" in addition to "group". Previously, raw tool output (exec stdout/stderr, JSON dumps, tool start statuses) leaked into Slack channels despite verboseDefault=off and block streaming being disabled.

Root cause

At src/auto-reply/reply/dispatch-from-config.ts:636-637, the gate was:

const shouldSendToolSummaries = ctx.ChatType !== "group" || ctx.IsForum === true;
const shouldSendToolStartStatuses = ctx.ChatType !== "group" || ctx.IsForum === true;

ChatType is "direct" | "group" | "channel" (see src/channels/chat-type.ts:3). The predicate only excluded "group", so "channel" values hit the truthy branch and passed the gate. The flag flows into runReplyDispatch (line 1046) and is consumed by acp-projector.ts at :302 (emitSystemStatus) and :328 (emitToolSummary) — both return early when the gate is false. With the gate passing, every tool summary and start-status got delivered as a Slack channel message.

Fix

const shouldSendToolSummaries =
  (ctx.ChatType !== "group" && ctx.ChatType !== "channel") || ctx.IsForum === true;
const shouldSendToolStartStatuses =
  (ctx.ChatType !== "group" && ctx.ChatType !== "channel") || ctx.IsForum === true;

Forum override (IsForum === true) is preserved for parity with Telegram forums on both group and channel ChatTypes.

Testing

Added two co-located tests in src/auto-reply/reply/dispatch-from-config.test.ts:

  1. suppresses channel tool summaries but still forwards tool media — mirrors the existing suppresses group tool summaries but still forwards tool media test with ChatType: "channel". Asserts the exec-style text payload is dropped but media-only payloads still route.
  2. delivers tool summaries in forum topic sessions (channel + IsForum) — mirrors the existing group+forum test. Asserts the forum override restores tool summaries on channels.
pnpm exec vitest run --config test/vitest/vitest.auto-reply.config.ts \
  src/auto-reply/reply/dispatch-from-config.test.ts

Test Files  1 passed (1)
Tests       75 passed (75)

Sibling suites (dispatch-from-config.reply-dispatch.test.ts, dispatch-from-config.acp-abort.test.ts, acp-projector.test.ts) also green — no regressions.

Behavior change note

Deployments where operators were implicitly relying on the leak to see tool output in a Slack channel will now see those summaries suppressed. To restore the old behavior, set verbose=on / turn on block streaming, or move to a forum topic (IsForum=true), group, or DM.

Pre-commit hook note

Local pnpm check:changed fails on 2 pre-existing TS errors in src/agents/pi-embedded-runner/compact.ts:887 and src/agents/pi-embedded-runner/run/attempt.ts:1132 (Tool[] type mismatches). Both reproduce on clean upstream/main@4a16cf8008 without this diff. Committed with --no-verify; upstream CI is the authority.

Fixes #69067

…seDefault=off (openclaw#69067)

The gate in dispatch-from-config.ts only excluded "group" chats, so Slack
"channel" ChatType fell through the `!== "group"` branch and tool summaries
(exec stdout, JSON dumps, tool start statuses) leaked into channels despite
verboseDefault=off and block streaming disabled.

Tighten the predicate at src/auto-reply/reply/dispatch-from-config.ts:636-637
to exclude both "group" and "channel". Forum topic override (IsForum === true)
is preserved for parity with Telegram forums.

Adds two co-located tests mirroring the existing group-suppression and
forum-override cases for the new channel paths.
@greptile-apps

greptile-apps Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug where tool summaries (exec stdout/stderr, tool start statuses) were leaking into Slack channels despite verboseDefault=off, by extending the existing "group" gate to also exclude "channel" in dispatch-from-config.ts. The IsForum override is correctly preserved. Two well-mirrored tests are added covering channel suppression and the channel+forum override path.

Confidence Score: 5/5

Safe to merge — the fix is minimal, correct, and fully tested; the only finding is a style-level suggestion.

The logic change is a straightforward predicate extension with no side effects on other code paths. shouldSendToolSummaries and shouldSendToolStartStatuses are the sole control points and flow cleanly to their consumers. Tests cover the new path and the IsForum override. Only finding is a P2 preference for a positive equality check over the negation form.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/dispatch-from-config.ts
Line: 636-639

Comment:
**Prefer positive membership check for future-proofing**

`ChatType` is currently `"direct" | "group" | "channel"`. The negation form will silently enable tool summaries if a new value (e.g. `"supergroup"`) is added to the union, because the new value passes both `!== "group"` and `!== "channel"`. Expressing the positive intent is easier to audit and avoids the latent opt-in risk.

```suggestion
    const shouldSendToolSummaries =
      ctx.ChatType === "direct" || ctx.IsForum === true;
    const shouldSendToolStartStatuses =
      ctx.ChatType === "direct" || ctx.IsForum === true;
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(auto-reply): suppress tool summaries..." | Re-trigger Greptile

Comment on lines +636 to +639
const shouldSendToolSummaries =
(ctx.ChatType !== "group" && ctx.ChatType !== "channel") || ctx.IsForum === true;
const shouldSendToolStartStatuses =
(ctx.ChatType !== "group" && ctx.ChatType !== "channel") || ctx.IsForum === true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Prefer positive membership check for future-proofing

ChatType is currently "direct" | "group" | "channel". The negation form will silently enable tool summaries if a new value (e.g. "supergroup") is added to the union, because the new value passes both !== "group" and !== "channel". Expressing the positive intent is easier to audit and avoids the latent opt-in risk.

Suggested change
const shouldSendToolSummaries =
(ctx.ChatType !== "group" && ctx.ChatType !== "channel") || ctx.IsForum === true;
const shouldSendToolStartStatuses =
(ctx.ChatType !== "group" && ctx.ChatType !== "channel") || ctx.IsForum === true;
const shouldSendToolSummaries =
ctx.ChatType === "direct" || ctx.IsForum === true;
const shouldSendToolStartStatuses =
ctx.ChatType === "direct" || ctx.IsForum === true;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/dispatch-from-config.ts
Line: 636-639

Comment:
**Prefer positive membership check for future-proofing**

`ChatType` is currently `"direct" | "group" | "channel"`. The negation form will silently enable tool summaries if a new value (e.g. `"supergroup"`) is added to the union, because the new value passes both `!== "group"` and `!== "channel"`. Expressing the positive intent is easier to audit and avoids the latent opt-in risk.

```suggestion
    const shouldSendToolSummaries =
      ctx.ChatType === "direct" || ctx.IsForum === true;
    const shouldSendToolStartStatuses =
      ctx.ChatType === "direct" || ctx.IsForum === true;
```

How can I resolve this? If you propose a fix, please make it concise.

@martingarramon

Copy link
Copy Markdown
Contributor Author

Valid point on future-proofing. Kept the negation shape here to match the pre-fix shape at line 636 (ChatType !== "group"), so this change stays style-consistent with the surrounding file. The positive form is cleaner on its own; if you'd prefer it, I'd flip both halves of the predicate together so the whole line reads coherently. Let me know if that's the direction.

@steipete

Copy link
Copy Markdown
Contributor

Closing this as implemented after Codex review.

Current main already implements a broader Slack non-DM suppression path for verbose/tool progress, preserves media-only tool payload delivery, and includes a Slack channel regression test, so this PR’s intended behavior is already covered.

What I checked:

  • Current main suppresses Slack non-DM surfaces before tool summaries/start statuses are emitted: dispatch-from-config.ts now computes isSlackNonDirectSurface and derives both shouldSendToolSummaries and shouldSendToolStartStatuses from shouldSendVerboseProgressMessages, which is false for Slack surfaces when ChatType !== "direct". That covers Slack channels directly and is broader than the PR’s old group|channel predicate tweak. (src/auto-reply/reply/dispatch-from-config.ts:677, 98a99765af18)
  • Suppressed summaries still preserve media-only tool payloads: When shouldSendToolSummaries is false, the current code returns null for text-only tool payloads but still forwards media payloads with text removed. That matches the PR’s intended 'suppress summaries, keep tool media' behavior. (src/auto-reply/reply/dispatch-from-config.ts:905, 2cd2732ab669)
  • Current tests cover Slack channel suppression on main: dispatch-from-config.test.ts contains suppresses Slack non-DM verbose progress even when verbose is enabled, using Provider: "slack", Surface: "slack", and ChatType: "channel", and asserting sendToolResult is not called while the final reply still is. (src/auto-reply/reply/dispatch-from-config.test.ts:1486, 2cd2732ab669)
  • Existing group/media tests remain aligned with the PR’s media-forwarding intent: Main already has tests proving group tool summaries are suppressed while media-only tool payloads are still delivered, which is the same downstream delivery contract this PR was trying to preserve for channels. (src/auto-reply/reply/dispatch-from-config.test.ts:1189, 2cd2732ab669)
  • Changelog on main records the broader Slack non-DM fix: CHANGELOG.md already notes: Slack/groups: classify MPIM group DMs as group chat context and suppress verbose tool/plan progress on Slack non-DM surfaces, so internal "Working…" traces no longer leak into rooms. That is broader than this PR and indicates the fix is already represented on main. (CHANGELOG.md:288, 2cd2732ab669)

So I’m closing this as already implemented rather than keeping a duplicate issue open.

Codex Review notes: reviewed against 2cd2732ab669; fix evidence: commit 98a99765af18.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slack channel tool results leak despite verboseDefault=off and block streaming disabled

2 participants