Skip to content

fix(gateway): filter delivery-mirror entries from chat.history#38075

Closed
MumuTW wants to merge 1 commit into
openclaw:mainfrom
MumuTW:fix/chat-history-delivery-mirror
Closed

fix(gateway): filter delivery-mirror entries from chat.history#38075
MumuTW wants to merge 1 commit into
openclaw:mainfrom
MumuTW:fix/chat-history-delivery-mirror

Conversation

@MumuTW

@MumuTW MumuTW commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Filter out internal delivery-mirror transcript entries from chat.history responses so webchat no longer displays duplicate assistant messages.
  • When a delivery channel (e.g. Telegram) is configured, each assistant reply is mirrored into the session transcript with model: "delivery-mirror". The chat.history endpoint was returning these alongside real provider messages, causing webchat to render the same reply twice.
  • Adds a guard in sanitizeChatHistoryMessages() that drops messages with model === "delivery-mirror", consistent with the existing pattern for filtering silent-reply messages.

Test plan

  • Configure a Telegram (or other) delivery channel alongside webchat
  • Send a prompt and receive a normal assistant reply
  • Verify webchat shows the reply only once (not duplicated)
  • Verify Telegram still receives the message normally
  • Verify chat.history API response no longer contains delivery-mirror entries

Fixes #38061

When a delivery channel (e.g. Telegram) is configured, each assistant
reply is mirrored into the session transcript as an internal
delivery-mirror entry (provider=openclaw, model=delivery-mirror).

The chat.history endpoint returned these mirror entries alongside
the real provider messages, causing webchat to display the same
assistant reply twice. Telegram was unaffected because it only
receives the actual delivery.

Filter out delivery-mirror messages in sanitizeChatHistoryMessages()
so that chat.history returns only the original assistant replies.

Fixes openclaw#38061

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime size: XS labels Mar 6, 2026
@greptile-apps

greptile-apps Bot commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR filters out internal delivery-mirror transcript entries from chat.history responses to prevent webchat from rendering duplicate assistant messages when a delivery channel (e.g. Telegram) is configured alongside webchat.

The implementation adds a small isDeliveryMirrorMessage guard in sanitizeChatHistoryMessages() that is consistent with the existing silent-reply filter pattern. The fix is logically sound and correctly operates on the post-sanitizeChatHistoryMessage version of each entry.

  • The only issue found is a misplaced JSDoc comment: the block that previously documented extractAssistantTextForSilentCheck now attaches to the newly inserted isDeliveryMirrorMessage function because the new function was inserted immediately after the closing */ of that comment.
  • No tests were added for the new filtering behaviour; given the project has a chat.directive-tags.test.ts and other unit-level tests for helpers in this file, a test covering the delivery-mirror filter path would be a useful addition.

Confidence Score: 4/5

  • This PR is safe to merge — the change is minimal, well-scoped, and the logic is correct.
  • The filtering logic mirrors the existing silent-reply pattern exactly, targets a clearly-defined model value that is set in a single place in the codebase (transcript.ts), and the only concern is a cosmetic JSDoc placement issue rather than a functional bug.
  • No files require special attention beyond the JSDoc placement note in src/gateway/server-methods/chat.ts.

Last reviewed commit: ec0aafb

Comment on lines +220 to +226
function isDeliveryMirrorMessage(message: unknown): boolean {
if (!message || typeof message !== "object") {
return false;
}
const entry = message as Record<string, unknown>;
return entry.role === "assistant" && entry.model === "delivery-mirror";
}

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.

Misplaced JSDoc comment

The existing JSDoc block (lines 214–219) that describes extractAssistantTextForSilentCheck ("Extract the visible text from an assistant history message for silent-token checks…") now directly precedes the newly inserted isDeliveryMirrorMessage function. In TypeScript/JSDoc tooling, a /** … */ comment attaches to the declaration that immediately follows it, so the wrong function now appears to be documented by that comment while extractAssistantTextForSilentCheck loses its documentation entirely.

The new function should be inserted before the JSDoc block (i.e., above line 214), or a separate JSDoc should be added for isDeliveryMirrorMessage so both functions retain accurate documentation.

Suggested change
function isDeliveryMirrorMessage(message: unknown): boolean {
if (!message || typeof message !== "object") {
return false;
}
const entry = message as Record<string, unknown>;
return entry.role === "assistant" && entry.model === "delivery-mirror";
}
/**
* Returns true when the message is an internal delivery-mirror transcript entry
* that should be excluded from `chat.history` responses.
*/
function isDeliveryMirrorMessage(message: unknown): boolean {
if (!message || typeof message !== "object") {
return false;
}
const entry = message as Record<string, unknown>;
return entry.role === "assistant" && entry.model === "delivery-mirror";
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-methods/chat.ts
Line: 220-226

Comment:
**Misplaced JSDoc comment**

The existing JSDoc block (lines 214–219) that describes `extractAssistantTextForSilentCheck` ("Extract the visible text from an assistant history message for silent-token checks…") now directly precedes the newly inserted `isDeliveryMirrorMessage` function. In TypeScript/JSDoc tooling, a `/** … */` comment attaches to the declaration that immediately follows it, so the wrong function now appears to be documented by that comment while `extractAssistantTextForSilentCheck` loses its documentation entirely.

The new function should be inserted *before* the JSDoc block (i.e., above line 214), or a separate JSDoc should be added for `isDeliveryMirrorMessage` so both functions retain accurate documentation.

```suggestion
/**
 * Returns true when the message is an internal delivery-mirror transcript entry
 * that should be excluded from `chat.history` responses.
 */
function isDeliveryMirrorMessage(message: unknown): boolean {
  if (!message || typeof message !== "object") {
    return false;
  }
  const entry = message as Record<string, unknown>;
  return entry.role === "assistant" && entry.model === "delivery-mirror";
}
```

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

@MumuTW

MumuTW commented Mar 23, 2026

Copy link
Copy Markdown
Contributor Author

Friendly bump — this filters delivery-mirror entries from chat.history. Any concerns?

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 21, 2026
@clawsweeper

clawsweeper Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Closing this as duplicate or superseded after Codex automated review.

PR #38075 is superseded by the merged write-side fix in #67185. Current main prevents the duplicate visible-reply case by reusing the latest matching real assistant transcript entry instead of appending a redundant delivery-mirror row, with regression coverage and a v2026.4.15 changelog entry.

Best possible solution:

Keep the shipped #67185 write-side dedupe as the canonical fix for duplicate visible replies, close this older chat.history-only PR as superseded, and use #40716 or a new focused issue only if maintainers still want historical cleanup or broader consumer-path filtering.

What I checked:

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

Codex Review notes: model gpt-5.5, reasoning high; reviewed against 46b9044c3f9d; fix evidence: release v2026.4.15, commit d842ec417924.

@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Apr 26, 2026
@clawsweeper clawsweeper Bot closed this Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui gateway Gateway runtime size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: [Bug]: Webchat shows duplicate assistant messages because chat.history returns delivery-mirror transcript entries as normal assistant messages

1 participant