Skip to content

fix(telegram): mirror outbound replies to session transcript#77484

Closed
syshiuen-create wants to merge 1 commit into
openclaw:mainfrom
syshiuen-create:fix/telegram-delivery-mirror-transcript
Closed

fix(telegram): mirror outbound replies to session transcript#77484
syshiuen-create wants to merge 1 commit into
openclaw:mainfrom
syshiuen-create:fix/telegram-delivery-mirror-transcript

Conversation

@syshiuen-create

Copy link
Copy Markdown

Summary

Telegram outbound assistant replies were never appended to the session
transcript: the .jsonl file the sessions.json sessionFile field
points at simply did not exist on disk. The session metadata side (token
counts, lastInteractionAt) updated correctly, so this is silent — the
agent has no record of what it just sent.

Root cause: extensions/telegram/src/bot/delivery.replies.ts::deliverReplies
dispatches via the Grammy SDK directly (bot.api.sendMessage /
sendPhoto / etc.), bypassing deliverOutboundPayloads where the
channel-mirror writer (appendAssistantMessageToSessionTranscript,
introduced in #1031) actually runs. Telegram is one of the channels that
never picked up that hook.

This mirrors the same fix shape as #53607 (discord) and #75529 (bluebubbles),
which patched the same gap for those channels. Addresses the missing-mirror
behavior reported in #75991 for telegram + claude-cli runtime combinations.

Changes

  • extensions/telegram/src/bot/delivery.replies.ts: add optional
    transcriptMirror?: { sessionKey, agentId? } param; after a successful
    delivery (and after maybePinFirstDeliveredMessage), call
    appendAssistantMessageToSessionTranscript(...) with reply.text +
    mediaList. Reuses the same helper deliverOutboundPayloads already
    calls so delivery-mirror entry shape, parentId DAG, and write-lock
    semantics stay identical to other channels.
  • extensions/telegram/src/bot-message-dispatch.ts: populate
    transcriptMirror in deliveryBaseOptions from
    ctxPayload.SessionKey + route.agentId. Silent NO_REPLY fallback
    explicitly opts out (transcriptMirror: undefined) so sentinels do
    not reach the transcript.
  • src/plugin-sdk/agent-harness-runtime.ts: re-export
    appendAssistantMessageToSessionTranscript so extension code can
    call it without reaching into core src/. The companion
    transcript.runtime.ts already exported the same function for
    internal consumers; adding it to the SDK boundary mirrors how
    appendSessionTranscriptMessage is already exposed alongside.
  • docs/.generated/plugin-sdk-api-baseline.sha256: regenerated via
    pnpm plugin-sdk:api:gen for the new SDK export.

Verification

Local (no Testbox):

  • pnpm tsgo:core
  • pnpm tsgo:extensions
  • pnpm check:architecture (import-cycles, madge, deprecated APIs, deprecated JSDoc) ✅
  • pnpm test extensions/telegram/src/bot/delivery.test.ts extensions/telegram/src/bot-message-dispatch.test.ts ✅ 159
    tests, 0 regressions
  • pnpm build
  • pnpm plugin-sdk:api:check ✅ after regen
  • Empirical: with the running gateway rebuilt and restarted, sending a
    message through both telegram bots produced <sessionFile>.jsonl files
    for the first time, each containing one model: "delivery-mirror"
    assistant entry with the reply text. Before the fix the jsonl files
    were never created. agent:main:hook:* and agent:main:main sessions
    were unaffected.

Risk / scope notes

  • New param is optional with a default-undefined branch; all 6 existing
    deliverReplies callers compile unchanged.
  • bot-native-commands.ts paths (3 additional deliverReplies callsites
    for inline-keyboard interactions) are intentionally not opted in here
    — happy to add in a follow-up if maintainers want them mirrored.
  • No webchat outbound change in this PR. Webchat appears to bypass
    deliverOutboundPayloads similarly; the email-inbox hook session
    shows the same symptom but via a different code path. Out of scope
    for this fix.
  • Inbound user-side append is not added: the channel-mirror system
    (the appendAssistantMessageToSessionTranscript family) appears to
    be assistant-only by design, and there is no public
    appendUserMessage* API to mirror to. If user-side mirroring is
    intended, that is a separate change.

Telegram's deliverReplies dispatches via Grammy SDK directly,
bypassing deliverOutboundPayloads where the channel-mirror writer
runs. Outbound assistant replies were never appended to the
session transcript, leaving Telegram .jsonl files empty (the
sessions.json sessionFile path was populated but the file was
never created on disk).

Add an optional transcriptMirror param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions.
Reuses the existing appendAssistantMessageToSessionTranscript
helper that deliverOutboundPayloads already calls, so the
delivery-mirror entry shape, parentId DAG handling, and write
lock semantics stay identical to other channels. Silent NO_REPLY
fallback explicitly opts out so sentinels do not reach the
transcript.

Mirrors the same fix shape as openclaw#53607 (discord) and openclaw#75529
(bluebubbles). Addresses the missing-mirror behavior reported in
openclaw#75991 for telegram + claude-cli runtime combinations.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it
without reaching into core src/. API baseline regenerated.
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: telegram Channel integration: telegram size: XS labels May 4, 2026
@clawsweeper

clawsweeper Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

Summary
The PR adds a Telegram transcript-mirror option from message dispatch into deliverReplies, re-exports the transcript mirror helper through the plugin SDK agent-harness runtime barrel, and updates the generated SDK API hash.

Reproducibility: yes. from source inspection: current main has no transcript append in deliverReplies, and existing Telegram tests show a final reply can be delivered by preview finalization without calling deliverReplies. I did not run live Telegram or mutate the checkout in this read-only pass.

Next step before merge
The remaining blockers are narrow and repairable: add mirroring for preview-finalized Telegram replies and add the required changelog entry.

Security
Cleared: The diff does not add dependencies, CI changes, token handling, or new network execution paths; the SDK export is an additive wrapper around existing transcript-write capability.

Review findings

  • [P2] Mirror preview-finalized Telegram replies — extensions/telegram/src/bot-message-dispatch.ts:716-718
  • [P3] Add the required changelog entry — extensions/telegram/src/bot/delivery.replies.ts:875
Review details

Best possible solution:

Mirror every successful Telegram final assistant delivery that reaches the user, including both fresh deliverReplies sends and preview-finalized edits, through the existing transcript helper with focused regression coverage and a changelog entry.

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

Yes, from source inspection: current main has no transcript append in deliverReplies, and existing Telegram tests show a final reply can be delivered by preview finalization without calling deliverReplies. I did not run live Telegram or mutate the checkout in this read-only pass.

Is this the best way to solve the issue?

No. The proposed direction is right for standard deliverReplies sends, but it is not complete because preview-finalized final replies remain outside the mirror path; the safer fix is a shared Telegram mirror hook that runs for both delivery outcomes.

Full review comments:

  • [P2] Mirror preview-finalized Telegram replies — extensions/telegram/src/bot-message-dispatch.ts:716-718
    This only passes the mirror context into deliverReplies, but final streamed replies can be delivered by editing the existing Telegram preview instead. The current test emits only the internal message:sent hook when a final answer stays in preview asserts deliverReplies is not called in that path, so those visible final answers would still never append a delivery-mirror transcript entry. Please mirror preview-finalized results too, or route both paths through one shared mirror helper.
    Confidence: 0.91
  • [P3] Add the required changelog entry — extensions/telegram/src/bot/delivery.replies.ts:875
    This is a user-facing Telegram fix, but the diff does not update CHANGELOG.md. Repo policy requires a changelog bullet for user-facing fix changes, so please add a single-line entry under the active Fixes section.
    Confidence: 0.95

Overall correctness: patch is incorrect
Overall confidence: 0.88

Acceptance criteria:

  • pnpm test extensions/telegram/src/bot/delivery.test.ts extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/lane-delivery.test.ts
  • pnpm tsgo:extensions
  • pnpm tsgo:core
  • pnpm plugin-sdk:api:check
  • pnpm exec oxfmt --check --threads=1 extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/bot/delivery.replies.ts src/plugin-sdk/agent-harness-runtime.ts CHANGELOG.md

What I checked:

  • Current main lacks the Telegram delivery mirror in deliverReplies: On current main, deliverReplies sends via Telegram SDK helpers and emits message-sent hooks, but it does not append a delivery-mirror assistant entry to the session transcript. (extensions/telegram/src/bot/delivery.replies.ts:669, a3f6f24b79a5)
  • The existing mirror helper writes delivery-mirror assistant messages: appendAssistantMessageToSessionTranscript resolves mirrored text/media and appends an assistant message with provider openclaw and model delivery-mirror. (src/config/sessions/transcript.ts:159, a3f6f24b79a5)
  • Preview-finalized replies bypass deliverReplies: The current Telegram dispatch test explicitly asserts that a final answer can stay in a preview, deliverReplies is not called, and only the internal message-sent hook fires. (extensions/telegram/src/bot-message-dispatch.test.ts:1219, a3f6f24b79a5)
  • Production preview path only emits the internal hook: When deliverLaneText returns preview-finalized, dispatchTelegramMessage calls emitPreviewFinalizedHook; that path does not call deliverReplies, so the PR's mirror parameter would not run there. (extensions/telegram/src/bot-message-dispatch.ts:1016, a3f6f24b79a5)
  • Changelog entry is missing: The PR file list changes Telegram runtime behavior and the SDK API hash, but it does not include CHANGELOG.md; current policy requires a changelog entry for user-facing fixes. (CHANGELOG.md:48, a3f6f24b79a5)

Likely related people:

  • BunsDev: The changelog credits @BunsDev for the recent Telegram tool-only preview cleanup, which is adjacent to the preview-finalized path that this PR still misses. (role: recent Telegram preview behavior contributor; confidence: medium; commits: 042d7b88230b; files: extensions/telegram/src/bot-message-dispatch.ts, extensions/telegram/src/lane-delivery-text-deliverer.ts)
  • Val Alexander: Local git log for extensions/telegram/src/bot-message-dispatch.ts shows commit 042d7b8823 fix(telegram): clean up tool-only previews authored by Val Alexander. (role: recent committer on Telegram preview cleanup; confidence: medium; commits: 042d7b88230b; files: extensions/telegram/src/bot-message-dispatch.ts)
  • TSavo: Related PR feat: Delivery mirroring - let agents remember what they sent #1031 and the changelog entry for delivery mirroring identify TSavo as the contributor tied to the original outbound transcript mirror behavior. (role: delivery mirror feature history; confidence: medium; commits: 4aa930e84d3b; files: src/config/sessions/transcript.ts, src/infra/outbound/deliver.ts)

Remaining risk / open question:

  • The PR head is reported as unmergeable against current main, so a repair may need a rebase or replacement branch before CI can give a final signal.
  • The patch changes a public SDK export; if that export remains, the API baseline, docs, and import-topology checks need to stay aligned.

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

adagues added a commit to adagues/openclaw that referenced this pull request May 8, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
adagues added a commit to adagues/openclaw that referenced this pull request May 8, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
obviyus pushed a commit to adagues/openclaw that referenced this pull request May 9, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
obviyus pushed a commit to adagues/openclaw that referenced this pull request May 9, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
obviyus pushed a commit to adagues/openclaw that referenced this pull request May 9, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
obviyus pushed a commit to adagues/openclaw that referenced this pull request May 9, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
obviyus pushed a commit to adagues/openclaw that referenced this pull request May 9, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
obviyus pushed a commit that referenced this pull request May 9, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses #75991 for telegram + CLI runtime combinations.
Supersedes #77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Telegram's deliverReplies dispatches via Grammy SDK directly, bypassing
deliverOutboundPayloads where the channel-mirror writer runs. Outbound
assistant replies were never appended to the session transcript, leaving
Telegram .jsonl files empty (the sessions.json sessionFile path was
populated but the file was never created on disk).

Add an optional transcriptMirror callback param to deliverReplies and
populate it from bot-message-dispatch's deliveryBaseOptions. Reuses the
existing appendAssistantMessageToSessionTranscript helper that
deliverOutboundPayloads already calls. Also mirrors preview-finalized
replies so the transcript captures all final assistant output.

Plugin SDK boundary expansion: re-export
appendAssistantMessageToSessionTranscript from
plugin-sdk/agent-harness-runtime so extension code can call it without
reaching into core src/. API baseline regenerated.

Addresses openclaw#75991 for telegram + CLI runtime combinations.
Supersedes openclaw#77484 (incorporates reviewer feedback: preview-
finalized mirror + changelog entry).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram docs Improvements or additions to documentation size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants