fix(googlechat): preserve thread reply target through delivery#74235
fix(googlechat): preserve thread reply target through delivery#74235jai wants to merge 23 commits into
Conversation
|
Codex review: found issues before merge. Reviewed July 3, 2026, 4:10 PM ET / 20:10 UTC. Summary PR surface: Source +296, Tests +1374, Docs +1. Total +1671 across 27 files. Reproducibility: yes. Source inspection shows current main and v2026.6.11 still pass Google Chat replyToId values directly into thread delivery, and the PR discussion includes live before/after screenshots plus a production confirmation of message-shaped resources escaping the inbound thread. Review metrics: 2 noteworthy metrics.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof path suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land one narrow Google Chat thread-routing fix after maintainers choose this proof-backed source branch or the clean replacement PR; the landing branch should be current, thread-only, and free of auth-provider and CHANGELOG.md churn. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main and v2026.6.11 still pass Google Chat replyToId values directly into thread delivery, and the PR discussion includes live before/after screenshots plus a production confirmation of message-shaped resources escaping the inbound thread. Is this the best way to solve the issue? No, not as currently submitted. The thread-routing fix is plausible and well-proven, but the best merge shape is a rebased thread-only branch or the clean replacement PR after maintainers settle proof and canonical ownership. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 5361e5a0b455. Label changesLabel justifications:
Evidence reviewedPR surface: Source +296, Tests +1374, Docs +1. Total +1671 across 27 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Greptile SummaryThis PR fixes three independent code paths that caused Google Chat assistant replies to escape the inbound thread and re-root as new top-level messages. It adds a Only one minor finding:
Confidence Score: 4/5Safe to merge; all three bug-causing paths are closed by layered validation and the outbound thread resolver, with comprehensive unit and integration test coverage. No P0 or P1 issues found. The only finding is a P2 style concern about the thread-resource regex being duplicated between No files require special attention beyond the regex duplication noted in Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/googlechat/src/channel.adapters.ts
Line: 39-44
Comment:
**Duplicate thread resource regex**
`GOOGLE_CHAT_THREAD_RESOURCE_RE` here is the same pattern as the `isGoogleChatThreadResourceName` export in `api.ts`. The PR description calls `api.ts` the "centralized validator," but `channel.adapters.ts` cannot import it directly (lazy-runtime boundary) and so carries its own copy. This is a silent divergence risk: if the pattern ever needs to change (e.g. to account for a new resource name segment), the two copies must be updated in sync. Consider exposing the regex constant itself from a lightweight, import-safe utils module (not `api.ts`) so both callers can share it without pulling in network/auth dependencies.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(googlechat): harden thread reply bou..." | Re-trigger Greptile |
|
Codex review: needs maintainer review before merge. What this changes: The PR carries Google Chat inbound thread resource names through reply delivery and message-tool paths, normalizes outbound thread targets at the Google Chat boundary, adds a shared thread-resource validator, and expands core plus Google Chat regression coverage. Maintainer follow-up before merge: This is an open implementation PR for a real current-main Google Chat threading gap, but it touches both shared reply plumbing and the Google Chat plugin boundary; the next action is maintainer review/CI validation rather than an automated replacement fix PR. Review detailsBest possible solution: Review and land this PR, or request focused revisions, with the Google Chat resource-name logic kept inside the plugin boundary and regression coverage for final replies, block-streamed replies, message-tool sends, explicit reply tags, and malformed thread inputs. Related #39554 and #69422 should be reconciled after the fix lands. Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against dc5a85d6064d. |
cde54a5 to
b5c8b45
Compare
b64612c to
58bc421
Compare
|
Addressed the ClawSweeper P2 findings in
Local verification:
GitHub now reports the PR as mergeable; CI is queued for the new head SHA |
|
We are investigating potential issues seen while testing this change in a live OpenClaw gateway runtime. Holding this PR as draft until we finish root-cause analysis and confirm whether the behavior is caused by this patch or by an environment/config interaction. |
|
Update from live/code investigation:
Next safe validation I’d recommend before marking ready again: test the current PR head in an isolated/non-production gateway or a short-lived Scout clone, not the stale local hotpatch branch. |
|
Rechecked against latest Conclusion: the PR still addresses a separate Google Chat thread-routing gap and is not made obsolete by the latest main typing fix. What latest main fixed:
What latest main still does not fix:
Additional fix pushed in
Local verification after the media-path fix:
Merge simulation against latest |
db72afb to
04dac3d
Compare
|
Rebased and revalidated current head What changed since the prior head:
Local verification:
Deploy/live status:
CI for the pushed head is pending as of this update. The only inline review thread I see remains resolved/outdated. |
04dac3d to
770bcc8
Compare
|
Rebased and revalidated pushed head Local verification on this head:
Deploy/live status:
I am keeping the PR draft. Note: upstream |
770bcc8 to
2530254
Compare
|
Rebased and pushed head What changed since the prior pushed head:
Verification:
Deploy/live status:
Keeping the PR draft; not marking ready while live reply placement remains unverified. |
2530254 to
2c5e3d7
Compare
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Brave Lint Imp Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
|
Confirming this from a production deployment — we hit exactly this on OpenClaw Traced in our installed bundle to the same place this PR addresses: the continuation's reply target is built from the inbound message resource ( For anyone blocked before this lands: Would be great to see this merged — happy to test a build if useful. |
- Extract resolveAutoThreadingTargets so the implicit-reply rule has a single, unit-tested home shared by runReplyAgent and runAgentTurnWithFallback. - Cover the missing-thread fallback in createBlockReplyDeliveryHandler so non-Google-Chat channels keep threading on currentMessageId. - Add a DM-shaped Google Chat webhook test asserting MessageThreadId stays unset when the inbound message has no thread. - Expand sendGoogleChatMessage thread validation with negative cases for malformed resource names.
When the agent uses the message tool to reply mid-turn without an explicit threadId/replyTo, the send rooted a new top-level thread. The googlechat threading adapter now exposes the inbound thread resource as currentThreadTs so the shared message tool's outbound action can default to it; the action handler reads toolContext and uses the inbound thread when threadId/replyTo are omitted.
…er-utils Pre-existing test mock for ./agent-runner-utils.js missed the resolveAutoThreadingTargets export added when the auto-threading helper was hoisted into shared utils, so 51 agent-runner-execution test cases failed at module-load. Add the mock entry so the changed gate stays green.
… delivery When core reply plumbing surfaces a Google Chat message resource (spaces/X/messages/Y) as the implicit reply target, sendGoogleChatMessage rejects it as a thread name and the reply re-roots as a new top-level thread. Resolve the outbound thread at the channel boundary in deliverGoogleChatReply: keep an explicit thread-shaped replyToId, otherwise fall back to the inbound thread captured from message.thread. Core reply plumbing remains channel-agnostic.
Summary
message.thread.nameasMessageThreadId, expose it to reply/message-tool threading, and normalize Google Chat outbound thread targets before they reachthread.name.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Real behavior proof (required for external PRs)
Root Cause (if applicable)
spaces/.../messages/...) and threads (spaces/.../threads/...), and OpenClaw previously let message-shaped values flow into thread-routing seams.MessageThreadIdfrommessage.thread.name.threadId.[[reply_to_current]]resolves to the current message id; for Google Chat that value must be converted to the inbound thread before delivery.Regression Test Plan (if applicable)
src/auto-reply/reply/reply-plumbing.test.tssrc/auto-reply/reply/agent-runner-payloads.test.tssrc/auto-reply/reply/agent-runner-utils.test.tssrc/auto-reply/reply/reply-delivery.test.tsextensions/googlechat/src/monitor.webhook-routing.test.tsextensions/googlechat/src/monitor.reply-delivery.test.tsextensions/googlechat/src/channel.test.tsextensions/googlechat/src/actions.test.tsextensions/googlechat/src/targets.test.tsUser-visible / Behavior Changes
Google Chat assistant replies from an existing Chat thread now stay in that thread instead of creating a new top-level message.
Diagram (if applicable)
Security Impact (required)
NoNoNoNoNoYes, explain risk + mitigation: N/ARepro + Verification
Environment
a617644c81dd43bb7285d3001ca6d19750e62e68; live Google Chat verification on a Linux VPS-hosted runtime with the thread-placement patch appliedSteps
threadId.[[reply_to_current]]behavior where the core seam supplies the current message id.Expected
thread.nameas-is.Actual
Evidence
Screenshot evidence attached below:
Verification commands run on exact rebased head
a617644c81dd43bb7285d3001ca6d19750e62e68after the May 26 rebase:Additional broad test signal from pre-ready validation on a prior PR head:
Human Verification (required)
What you personally verified (not just CI), and how:
[[reply_to_current]]can still resolve to the core current message id; Google Chat rewrites message-shaped values at the channel boundary.replyToIdwins over the inbound thread.threadIduse the inbound thread only when the current target matches and reply mode allows implicit threading.Review Conversations
Compatibility / Migration
YesNoNoRisks and Mitigations
threadnow fail loudly instead of silently falling back.replyToId.spaces/.../threads/...passes asthread.name; everything else is dropped or falls back to the inbound thread.Screenshots (Bad, Good)
Bad (before patch)
Good (after patch)