Skip to content

Google Chat: respect replyToMode: "off" for DM threading#67055

Closed
banddude wants to merge 2 commits into
openclaw:mainfrom
banddude:fix/googlechat-respect-replytomode-off
Closed

Google Chat: respect replyToMode: "off" for DM threading#67055
banddude wants to merge 2 commits into
openclaw:mainfrom
banddude:fix/googlechat-respect-replytomode-off

Conversation

@banddude

Copy link
Copy Markdown

Summary

The Google Chat monitor unconditionally copies message.thread.name from inbound webhooks into ReplyToId and passes it through to all outbound sends (typing indicator, text replies, media replies). This causes all agent responses to land inside a thread, even when replyToMode is set to "off" in the channel config.

Changes

This checks account.config.replyToMode at all five thread propagation points in extensions/googlechat/src/monitor.ts and suppresses threading when the value is "off":

  1. Inbound context: ReplyToId and ReplyToIdFull (lines 258-259)
  2. Typing indicator: sendGoogleChatMessage thread param (line 303)
  3. Text reply: sendGoogleChatMessage thread param (line 437)
  4. Media reply: sendGoogleChatMessage thread param (line 466)

Testing

Tested on a live OpenClaw 2026.4.14 instance with Google Chat DMs. Before the patch, all replies landed in threads despite replyToMode: "off". After the patch, replies appear flat in the DM conversation as expected.

Fixes #52475
Related: #39554

The Google Chat monitor unconditionally copies message.thread.name from
inbound webhooks into ReplyToId and passes it through to all outbound
sends (typing indicator, text replies, media replies). This causes all
agent responses to land inside a thread, even when replyToMode is set
to "off" in the channel config.

This change checks account.config.replyToMode at all five thread
propagation points and suppresses threading when the config is "off":

- Inbound context: ReplyToId and ReplyToIdFull
- Typing indicator: sendGoogleChatMessage thread param
- Text reply: sendGoogleChatMessage thread param
- Media reply: sendGoogleChatMessage thread param

Fixes openclaw#52475
Related: openclaw#39554
@openclaw-barnacle openclaw-barnacle Bot added channel: googlechat Channel integration: googlechat size: XS labels Apr 15, 2026
@greptile-apps

greptile-apps Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes the Google Chat monitor unconditionally propagating message.thread?.name into all outbound sends regardless of replyToMode config. The change adds account.config.replyToMode === "off" guards at all five threading propagation points (inbound ReplyToId/ReplyToIdFull, typing indicator, text reply, media reply) in monitor.ts.

The fix is correctly scoped — other replyToMode values ("first", "all", "batched") are unaffected, and the undefined (unconfigured) case preserves the existing threading behavior. No new tests are added for the monitor pipeline's replyToMode: "off" path.

Confidence Score: 5/5

Safe to merge — the change is a focused, correct guard at all threading propagation points with no functional regressions for other replyToMode values.

All findings are P2 (a redundant defensive check worth a clarifying comment). The core logic is correct, covers every send site, and does not regress other modes. Confidence is 5.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/googlechat/src/monitor.ts
Line: 437

Comment:
**Delivery-time check is redundant but not harmful**

`payload.replyToId` flows directly from `ctxPayload.ReplyToId`, which is already set to `undefined` when `replyToMode === "off"` (line 258). The extra guard here — and the matching one on line 466 — won't fire in practice under normal pipeline flow. They're safe to keep as a defensive layer if `replyToId` could ever be injected by a pipeline stage downstream of the inbound context build, but worth a comment to clarify the intent:

```suggestion
            thread: account.config.replyToMode === "off" ? undefined : payload.replyToId, // defensive: payload.replyToId is already undefined when replyToMode is "off"
```

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

Reviews (1): Last reviewed commit: "Google Chat: respect replyToMode: "off" ..." | Re-trigger Greptile

space: spaceId,
text: chunk,
thread: payload.replyToId,
thread: account.config.replyToMode === "off" ? undefined : payload.replyToId,

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 Delivery-time check is redundant but not harmful

payload.replyToId flows directly from ctxPayload.ReplyToId, which is already set to undefined when replyToMode === "off" (line 258). The extra guard here — and the matching one on line 466 — won't fire in practice under normal pipeline flow. They're safe to keep as a defensive layer if replyToId could ever be injected by a pipeline stage downstream of the inbound context build, but worth a comment to clarify the intent:

Suggested change
thread: account.config.replyToMode === "off" ? undefined : payload.replyToId,
thread: account.config.replyToMode === "off" ? undefined : payload.replyToId, // defensive: payload.replyToId is already undefined when replyToMode is "off"
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/googlechat/src/monitor.ts
Line: 437

Comment:
**Delivery-time check is redundant but not harmful**

`payload.replyToId` flows directly from `ctxPayload.ReplyToId`, which is already set to `undefined` when `replyToMode === "off"` (line 258). The extra guard here — and the matching one on line 466 — won't fire in practice under normal pipeline flow. They're safe to keep as a defensive layer if `replyToId` could ever be injected by a pipeline stage downstream of the inbound context build, but worth a comment to clarify the intent:

```suggestion
            thread: account.config.replyToMode === "off" ? undefined : payload.replyToId, // defensive: payload.replyToId is already undefined when replyToMode is "off"
```

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

Verify that replyToMode: 'off' resolves correctly at both the
channel level and as a per-account override through the threading
adapter. Documents the config contract that monitor.ts now honors.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 909a4a6982

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

space: spaceId,
text: chunk,
thread: payload.replyToId,
thread: account.config.replyToMode === "off" ? undefined : payload.replyToId,

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 Badge Honor explicit reply tags when replyToMode is off

payload.replyToId here has already been processed by the shared reply-threading flow, which preserves explicit [[reply_to:*]] / [[reply_to_current]] directives even when the mode is off. Replacing it with account.config.replyToMode === "off" ? undefined : ... strips those explicit overrides, so intentionally threaded Google Chat replies are forced flat whenever replyToMode is off (and the same guard appears in the media send path).

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close as superseded: the branch has a concrete correctness blocker and no inspectable real-behavior proof, while the remaining Google Chat off-mode/threading work is tracked by #42510 and the newer active implementation path at #74235.

Canonical path: Keep the canonical issue open and land #74235 or an equivalent Google Chat-owned fix that suppresses implicit off-mode threads while preserving explicit reply targets.

So I’m closing this here and keeping the remaining discussion on the canonical linked item.

Review details

Best possible solution:

Keep the canonical issue open and land #74235 or an equivalent Google Chat-owned fix that suppresses implicit off-mode threads while preserving explicit reply targets.

Do we have a high-confidence way to reproduce the issue?

Yes, source-level. Configure a Google Chat account with replyToMode: "off" and process an inbound webhook with message.thread.name; current main still copies that thread into reply context and the typing placeholder path.

Is this the best way to solve the issue?

No. The ingress and typing changes point at the right bug, but the final-send guards bypass the shared explicit-reply contract; the safer path is the canonical Google Chat threading fix that filters implicit sources before delivery.

Security review:

Security review cleared: The diff only changes Google Chat threading conditionals and tests, with no dependency, workflow, permission, secret-handling, or code-execution changes.

What I checked:

  • Current main still injects implicit Google Chat thread context: Current main copies message.thread?.name into Google Chat replyToId/replyToIdFull and sends the typing placeholder with thread: message.thread?.name, so the reported off-mode behavior is still source-reproducible. (extensions/googlechat/src/monitor.ts:311, 2eee70e0a64b)
  • Current main final delivery preserves payload reply targets: Final Google Chat text and media delivery use thread: payload.replyToId, which is the boundary that should keep already-filtered explicit reply targets intact. (extensions/googlechat/src/monitor-reply-delivery.ts:73, 2eee70e0a64b)
  • Shared off-mode contract preserves explicit reply directives: createReplyToModeFilter strips implicit replies in off mode but preserves explicit reply tags when allowed for known channels; adjacent tests assert explicit Slack/Telegram tags survive off mode. (src/auto-reply/reply/reply-threading.ts:101, 2eee70e0a64b)
  • PR diff strips explicit final reply targets: The PR changes final text and media sends to account.config.replyToMode === "off" ? undefined : payload.replyToId, which would flatten explicit Google Chat reply directives after the shared filter preserved them. (extensions/googlechat/src/monitor.ts:437, 909a4a6982b4)
  • Live PR state is conflicting and lacks inspectable proof: Live gh pr view reports mergeable: CONFLICTING / mergeStateStatus: DIRTY; the body claims live Google Chat testing but has no screenshot, recording, terminal output, linked artifact, or redacted logs attached to this PR. (909a4a6982b4)
  • Canonical exact issue remains open: Live issue inspection shows Google Chat: replyToMode: "off" does not suppress thread replies #42510 is open and directly tracks Google Chat: replyToMode: "off" does not suppress thread replies, with prior review noting current main still needs reconciliation with active Google Chat threading PRs.

Likely related people:

  • steipete: GitHub commit metadata ties this handle to Google Chat reply-delivery extraction, shared API handling, and landing adjacent thread reply option support. (role: recent Google Chat area contributor and merger; confidence: high; commits: 4f91d81e1dbc, 6ecc18463753, cdbed3c9b1c4; files: extensions/googlechat/src/monitor.ts, extensions/googlechat/src/monitor-reply-delivery.ts, extensions/googlechat/src/api.ts)
  • Takhoffman: Commit metadata shows account-level Google Chat replyToMode handling adjacent to the config contract this PR depends on. (role: replyToMode config contributor; confidence: medium; commits: 8e9607c06428; files: extensions/googlechat/src/channel.adapters.ts, extensions/googlechat/src/channel.test.ts)
  • jimdawdy-hub: Local blame in this shallow/grafted checkout attributes the current central Google Chat and reply-threading lines to this broad current-main commit, so it is a routing clue rather than feature authorship proof. (role: current-main provenance contributor; confidence: low; commits: f97645428e4e; files: extensions/googlechat/src/monitor.ts, extensions/googlechat/src/monitor-reply-delivery.ts, extensions/googlechat/src/channel.adapters.ts)
  • novan: The Google Chat messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD support was landed from contributor work credited to this handle, directly adjacent to thread.name behavior. (role: adjacent Google Chat thread support contributor; confidence: medium; commits: cdbed3c9b1c4; files: extensions/googlechat/src/api.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 2eee70e0a64b.

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes:

The PR adds replyToMode: "off" guards to Google Chat monitor thread propagation and adds Google Chat config-resolution tests for channel-level and per-account off mode.

Maintainer follow-up before merge:

This is a real current-main bug and the PR is useful, but the remaining action is maintainer-guided revision or consolidation with nearby Google Chat threading PRs rather than an automated replacement branch.

Review details

Best possible solution:

Revise or fold the PR into the current Google Chat threading work: filter only implicit inbound ReplyToId/ReplyToIdFull and typing-placeholder thread propagation when the resolved Google Chat account has replyToMode: "off", keep final delivery honoring already-filtered payload.replyToId, and add a focused monitor regression for off-mode DM replies.

Acceptance criteria:

  • pnpm test extensions/googlechat/src/monitor.reply-delivery.test.ts extensions/googlechat/src/channel.test.ts
  • pnpm test src/auto-reply/reply/reply-plumbing.test.ts
  • pnpm exec oxfmt --check --threads=1 extensions/googlechat/src/monitor.ts extensions/googlechat/src/monitor-reply-delivery.ts extensions/googlechat/src/channel.test.ts
  • pnpm tsgo:extensions

What I checked:

  • Current main still seeds implicit inbound thread context: Google Chat inbound handling still passes message.thread?.name into ReplyToId and ReplyToIdFull without checking the resolved account replyToMode. (extensions/googlechat/src/monitor.ts:218, acae48b790fa)
  • Typing placeholder still threads before payload filtering: The monitor still sends the typing placeholder with thread: message.thread?.name, so typingIndicator: "message" can create an in-thread placeholder even when the account is configured with replyToMode: "off". (extensions/googlechat/src/monitor.ts:263, acae48b790fa)
  • Current delivery boundary is payload-driven: Current main has split final reply delivery into monitor-reply-delivery.ts, where text and media sends use thread: payload.replyToId; this is the boundary that should preserve explicit reply targets after inbound implicit context has been filtered. (extensions/googlechat/src/monitor-reply-delivery.ts:73, acae48b790fa)
  • Shared off-mode contract preserves explicit tags: The shared reply-to-mode filter strips implicit replyToId in off mode but allows explicit reply tags/directives for known channels; regression tests assert explicit tags survive off mode. (src/auto-reply/reply/reply-threading.ts:100, acae48b790fa)
  • PR branch strips explicit final reply targets: The PR head adds account.config.replyToMode === "off" ? undefined : payload.replyToId at final text/media send sites, and the later review comment correctly flags that this would flatten explicit Google Chat reply directives in off mode. (extensions/googlechat/src/monitor.ts:437, 909a4a6982b4)
  • Test coverage gap remains: Current tests cover forwarding payload.replyToId into Google Chat sends and per-account replyToMode resolution, but there is no Google Chat monitor regression proving off mode suppresses inbound ReplyToId and the typing placeholder thread. (extensions/googlechat/src/monitor.reply-delivery.test.ts:68, acae48b790fa)

Likely related people:

  • steipete: Recent path history and local blame route the central Google Chat monitor, split reply delivery, API sender, and reply-threading surfaces through steipete-maintained commits; this is routing evidence, not fault attribution. (role: recent maintainer and merger; confidence: high; commits: 4f91d81e1dbc, 6ecc18463753, 28291eba628e; files: extensions/googlechat/src/monitor.ts, extensions/googlechat/src/monitor-reply-delivery.ts, extensions/googlechat/src/api.ts)
  • vincentkoc: Recent history on the Google Chat plugin SDK import boundary and shared reply-threading type surfaces is relevant to preserving the channel/plugin contract while revising this PR. (role: adjacent plugin SDK and reply boundary maintainer; confidence: medium; commits: 2b67a3f76e90, 74e7b8d47b18; files: extensions/googlechat/src/monitor.ts, extensions/googlechat/src/api.ts, src/auto-reply/reply/reply-threading.ts)
  • novan: The existing Google Chat messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD support was landed from contributor PR fix(googlechat): handle thread replies and Add-on event types #30965 credited to novan, directly adjacent to the thread field behavior this PR changes. (role: adjacent Google Chat thread support contributor; confidence: medium; commits: cdbed3c9b1c4; files: extensions/googlechat/src/api.ts)

Remaining risk / open question:

Codex review notes: model gpt-5.5, reasoning high; reviewed against acae48b790fa.

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

Labels

channel: googlechat Channel integration: googlechat size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Google Chat: add option to disable automatic threading on replies

1 participant