diff --git a/CHANGELOG.md b/CHANGELOG.md index 849b72ed9fe..1f868844eb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,7 +91,7 @@ Docs: https://docs.openclaw.ai - Commands/config writes: enforce `configWrites` against both the originating account and the targeted account scope for `/config` and config-backed `/allowlist` edits, blocking sibling-account mutations while preserving gateway `operator.admin` flows. Thanks @tdjackey for reporting. - Security/system.run: fail closed for approval-backed interpreter/runtime commands when OpenClaw cannot bind exactly one concrete local file operand, while extending best-effort direct-file binding to additional runtime forms. Thanks @tdjackey for reporting. - Gateway/session reset auth: split conversation `/new` and `/reset` handling away from the admin-only `sessions.reset` control-plane RPC so write-scoped gateway callers can no longer reach the privileged reset path through `agent`. Thanks @tdjackey for reporting. -- Telegram/final preview delivery followup: keep ambiguous first preview sends without a returned `message_id` instead of falling back to a second final send, so slow-provider Telegram replies stop duplicating on the first preview-final seam. (#41932) thanks @hougangdev. +- Telegram/final preview delivery followup: keep ambiguous missing-`message_id` finals only when a preview was already visible, while first-preview/no-id cases still fall back so Telegram users do not lose the final reply. (#41932) thanks @hougangdev. ## 2026.3.8 diff --git a/src/telegram/lane-delivery-text-deliverer.ts b/src/telegram/lane-delivery-text-deliverer.ts index d5614057452..56e0d974240 100644 --- a/src/telegram/lane-delivery-text-deliverer.ts +++ b/src/telegram/lane-delivery-text-deliverer.ts @@ -365,13 +365,6 @@ export function createLaneTextDeliverer(params: CreateLaneTextDelivererParams) { context, }); if (typeof previewTargetAfterStop.previewMessageId !== "number") { - if (lane.stream?.sendMayHaveLanded?.()) { - params.log( - `telegram: ${laneName} first preview send may have landed despite missing message id; keeping to avoid duplicate`, - ); - params.markDelivered(); - return "retained"; - } return "fallback"; } return finalizePreview(previewTargetAfterStop.previewMessageId, true, false); diff --git a/src/telegram/lane-delivery.test.ts b/src/telegram/lane-delivery.test.ts index 1243ae4a266..3a165147d84 100644 --- a/src/telegram/lane-delivery.test.ts +++ b/src/telegram/lane-delivery.test.ts @@ -538,7 +538,7 @@ describe("createLaneTextDeliverer", () => { ); }); - it("retains when the first preview send may have landed without a message id", async () => { + it("falls back when the first preview send may have landed without a message id", async () => { const stream = createTestDraftStream(); stream.sendMayHaveLanded.mockReturnValue(true); const harness = createHarness({ answerStream: stream }); @@ -550,10 +550,9 @@ describe("createLaneTextDeliverer", () => { infoKind: "final", }); - expect(result).toBe("preview-retained"); - expect(harness.sendPayload).not.toHaveBeenCalled(); - expect(harness.log).toHaveBeenCalledWith( - expect.stringContaining("first preview send may have landed despite missing message id"), + expect(result).toBe("sent"); + expect(harness.sendPayload).toHaveBeenCalledWith( + expect.objectContaining({ text: "Hello final" }), ); });