fix(message-tool): apply messages.responsePrefix to outbound sends#93639
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 26, 2026, 1:01 PM ET / 17:01 UTC. Summary PR surface: Source +37, Tests +140. Total +177 across 2 files. Reproducibility: yes. Source inspection shows normal replies prepend Review metrics: 1 noteworthy metric.
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:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land this partial send-path fix only if maintainers accept the upgrade-visible output change and keep #39913 open for selected-model template parity. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows normal replies prepend Is this the best way to solve the issue? Yes for the scoped partial fix. The shared outbound send layer is the right owner boundary for static and identity prefixes, but full provider/model/thinking template parity needs a separate maintainer scope decision because tool sends do not currently carry selected-model context. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 1cd6f81a46ae. Label changesLabel justifications:
Evidence reviewedPR surface: Source +37, Tests +140. Total +177 across 2 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
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
9160d4d to
1eca756
Compare
Mantis Telegram Desktop ProofSummary: Mantis captured native Telegram Desktop before/after GIFs showing the outbound Telegram message without and with the configured response prefix.
Motion-trimmed clips: Raw QA files: https://artifacts.openclaw.ai/mantis/telegram-desktop/pr-93639/run-27847236857-1/index.json |
1eca756 to
4ad95d8
Compare
…skip unresolved model tokens
4ad95d8 to
d656910
Compare
|
Rebased onto latest What changed
Tests (
Local: @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Updated the linked context: this PR now uses |




Summary
messages.responsePrefixbeing silently dropped onmessage(action=send)tool sends, so the disambiguation prefix now shows on tool-initiated messages the same way it already does on direct replies.handleSendAction(via the existingresolveResponsePrefix, honoring account/channel/global precedence andauto), interpolates it through the sameresolveResponsePrefixTemplateas the reply path so identity tokens like[{identity.name}]render, and prepends it to the outbound text, guarded by astartsWithcheck so re-runs stay idempotent.handleInternalSourceReplySendAction) unchanged — it keeps flowing through the normal reply normalization, whose ownstartsWithguard already prevents any double prefix.broadcastre-enters this samesendpath, so it inherits the prefix without extra code.Linked context
Refs #39913
This PR lands the static + identity-token portion of the fix:
message(action=send)now applies a literalmessages.responsePrefixand interpolates identity tokens like[{identity.name}](via the sameresolveResponsePrefixTemplatethe reply path uses). Prefixes containing{provider}/{model}/{full-model}/{thinking}are intentionally skipped on tool sends: a tool send performs no live model selection, so resolving those tokens is out of scope here and emitting them raw would leak a literal{model}into the delivered text. That selected-model template parity stays tracked by #39913, so this PR usesRefsrather thanCloses.Re-do of the closed, unmerged #92299 (the author closed it to free PR slots after the ClawSweeper run failed to complete — the rating was an infra non-completion, not a rejection of the fix). The June 15 Codex review on the issue confirms it is still reproducible on
mainand canonical: the normal reply path applies the prefix, but the sharedmessage(action="send")builder returns text without it. This PR scopes the fix to the genuine cross-target send path rather than the shared builder, to avoid touching source-reply semantics.AI-assisted: drafted and verified with Claude Code; all evidence below was run locally by me.
Real behavior proof
messages.responsePrefixapplied to direct replies but not tomessage(action=send)tool sends, so the prefix was intermittently "forgotten" on cross-chat sends.@my_zw_openclaw_bot) on a local gateway, Node 22, WSL2, sending to a real private chat. The CLIopenclaw message sendroutes through the exact patched path (runMessageActionthenhandleSendAction), which is the same code the agentmessagetool uses.src/infra/outbound/message-action-runner.tsbuildSendPayloadPartsbuilds the send payload text without resolving/prependingresponsePrefix; only the reply path (normalize-reply.ts:131-138) prepends it.messages.responsePrefix: "[Nexus]", restoringmessage-action-runner.tstoupstream/mainand running the new test reproduces the drop (terminal output):openclaw message send(real delivery, not a mock), the gateway runtime log line for the send, and Telegram's own returned Message object read back from the local telegram message-cache. Command console output:Gateway runtime log:
Delivered message text as Telegram returned it (I sent
responsePrefix live test 39913 TEXT; it arrived prefixed):openclaw message sendconsole output and the gateway runtime log confirm a real Telegram delivery whose text arrived as[Nexus] responsePrefix live test 39913 TEXT— the configured prefix is now applied to message-tool sends.responsePrefixoverrides and{provider}/{model}-style selected-model templates were exercised only via the helper unit (resolveResponsePrefix) and existing identity tests, not live — those model templates are intentionally skipped on tool sends (see Linked context). Only Telegram was sent live; other channels share the same channel-agnostichandleSendActionpath. An earlier live attempt with the account defaultmessages.tts.auto: "always"was delivered as a TTS voice message (the prefix is then spoken, not visible), so I settts.auto: "off"for the text-visible run.Tests and validation
pnpm tsgo:core(clean)node scripts/run-vitest.mjs run src/infra/outbound/message-action-runner.core-send.test.ts(13 passed, incl. 3 new)Risk checklist
Did user-visible behavior change?
Yes—message(action=send)outbound text now carriesmessages.responsePrefix. This is the documented intent of the prefix (AI/human disambiguation) and matches the existing reply path; sends with no configured prefix are unaffected.Did config, environment, or migration behavior change?
No— reuses the existingmessages.responsePrefixconfig and its account/channel/global precedence; no new keys, defaults, or migrations.