Google Chat uses replyToId as persistent thread context (threadName),
similar to Slack (thread_ts) and Mattermost (rootId). The outbound
delivery core was consuming inherited replyToId after the first
successful send, orphaning subsequent payloads to the top level.
Add googlechat to the isThreadBasedChannel check so replyToId survives
across all payloads in a multi-payload response.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mattermost uses replyToId as rootId for thread context, same pattern as
Slack's thread_ts. Without this, multi-payload assistant turns only keep
the first message in-thread; subsequent payloads get posted unthreaded.
Generalizes the Slack-specific exemption to all thread-based channels.
- Prevent quote-author cache poisoning: only cache sender-derived authors from actual
envelope senders; add isStrictUuid for canonical UUID validation
- Preserve Slack thread context: skip reply-state consumption for Slack so thread_ts
persists across all payloads in a delivery
- Normalize blank/whitespace replyToId to undefined before computing explicit reply
semantics; null still means do-not-reply
- Add tests for isStrictUuid, blank replyToId normalization, and trimming
Outbound:
- Map [[reply_to:]] tag to signal-cli quote-timestamp + quote-author RPC params
- Validate reply IDs are decimal-only (reject hex/scientific notation)
- Track quote consumption across dispatcher callbacks (turnReplyConsumed flag)
- Strip replyToId from payload after first delivery; prevents duplicate quote bubbles
- Preserve explicit null as reply suppression signal (distinct from unset)
- Materialize resolved replyToId on sendPayload payloads for downstream correctness
- Handle sendPayload partial failure: only consume on valid messageId
Inbound:
- Surface ReplyToId, ReplyToBody, ReplyToSender, ReplyToIsQuote on MsgContext
- Resolve quoted authors from LRU cache (uuid: prefix normalization for signal-cli)
- Preserve quote metadata through debounce coalescing
- Index skipped group messages for later author resolution
- Drop wrong-person fallback for unresolvable explicit reply IDs
Type safety:
- Extend OutboundReplyPayload.replyToId to string | null | undefined
- Fix null propagation in googlechat, irc, nextcloud-talk, imessage, telegram adapters
- Fix Feishu SDK type errors (timeout not in call param types)
Requires patched signal-cli at /opt/signal-cli-0.14.1-patched/ until
upstream publishes lib v140 to JitPack.
* test: align extension runtime mocks with plugin-sdk
Update stale extension tests to mock the plugin-sdk runtime barrels that production code now imports, and harden the Signal tool-result harness around system-event assertions so the channels lane matches current extension boundaries.
Regeneration-Prompt: |
Verify the failing channels-lane tests against current origin/main in an isolated worktree before changing anything. If the failures reproduce on main, keep the fix test-only unless production behavior is clearly wrong. Recent extension refactors moved Telegram, WhatsApp, and Signal code onto plugin-sdk runtime barrels, so update stale tests that still mock old core module paths to intercept the seams production code now uses. For Signal reaction notifications, avoid brittle assertions that depend on shared queued system-event state when a direct harness spy on enqueue behavior is sufficient. Preserve scope: only touch the failing tests and their local harness, then rerun the reproduced targeted tests plus the full channels lane and repo check gate.
* test: fix extension test drift on main
* fix: lazy-load bundled web search plugin registry
* test: make matrix sweeper failure injection portable
* fix: split heavy matrix runtime-api seams
* fix: simplify bundled web search id lookup
* test: tolerate windows env key casing
Reuse pi-ai's Anthropic client injection seam for streaming, and add
the OpenClaw-side provider discovery, auth, model catalog, and tests
needed to expose anthropic-vertex cleanly.
Signed-off-by: sallyom <somalley@redhat.com>