Skip to content

issue(telegram): preview streaming can suppress drafts that Web already shows #95004

Description

@snowzlmbot

Summary

Telegram preview streaming can suppress intermediate drafts even when the Web UI has corresponding draft/progress state. Users then see no Telegram draft for a long time, or only the final fixed message, which makes Telegram feel unreliable compared with Web.

This appears to come from multiple Telegram-specific gates in the draft stream path:

  1. partial/block answer previews use minInitialChars = 30, so short first previews are intentionally not materialized;
  2. progress streaming mode suppresses answer-lane partial deltas and only shows progress/tool lines;
  3. draft finalization paths depend on whether a Telegram preview has already materialized (messageId() / sendMayHaveLanded()), so fast completions or delayed sends can skip visible drafts.

Source-level reproduction

1. First Telegram draft below 30 chars is suppressed

Existing tests in extensions/telegram/src/draft-stream.test.ts assert this behavior:

describe("minInitialChars threshold", () => {
  it("does not send first message below threshold", async () => {
    const api = createMockApi();
    const stream = createDebouncedStream(api);

    stream.update("Processing");
    await stream.flush();

    expect(api.sendMessage).not.toHaveBeenCalled();
  });
});

This means a Telegram draft can be absent even though upstream streaming has already produced visible draft text.

2. Telegram dispatch config applies the threshold to non-progress modes

extensions/telegram/src/bot-message-dispatch.ts:

const DRAFT_MIN_INITIAL_CHARS = 30;
const draftMinInitialChars = streamMode === "progress" ? 0 : DRAFT_MIN_INITIAL_CHARS;

The default/partial path therefore debounces the first native Telegram draft until it has at least 30 characters.

3. Progress mode intentionally suppresses answer-lane draft text

extensions/telegram/src/bot-message-dispatch.ts suppresses answer-lane partial updates in progress mode:

if (lane === answerLane) {
  if (streamMode === "progress") {
    return;
  }
  resetAnswerToolProgressDraft();
  suppressProgressDraftState();
}
laneStream.update(nextText);

In progress mode, Telegram can show only tool/progress rows; if there are no progress rows or they are hidden/suppressed, Telegram shows no draft even though the assistant is streaming.

Actual behavior

  • Web can show draft/progress state for the same turn.
  • Telegram sometimes shows no draft until final delivery, especially for short/fast turns or progress-mode turns without durable progress rows.
  • Users perceive this as unstable streaming: sometimes a draft appears, sometimes it does not, and sometimes only the final message appears.

Expected behavior

Telegram should provide a predictable lightweight progress indication whenever upstream streaming/draft state exists, without creating noisy duplicate messages.

Possible acceptable outcomes:

  • materialize a short safe placeholder for short/fast turns, then edit it once content is available;
  • lower or bypass minInitialChars when the turn has already been active for a configured delay;
  • make progress mode show a stable answer/progress placeholder when no progress rows are available;
  • document the intentional tradeoff if Telegram is expected to differ from Web.

Impact

  • Telegram users do not get Web-parity feedback during long-running turns.
  • Tool-heavy or fast-completing turns feel inconsistent: draft visibility depends on timing, content length, and stream mode.
  • The final message can arrive after a long silent period, causing users to think the bot is stuck.

Verification target

A fix should add/adjust Telegram tests that cover at least one of:

node scripts/run-vitest.mjs run --config test/vitest/vitest.extension-telegram.config.ts telegram/src/draft-stream.test.ts -t "minInitialChars"
node scripts/run-vitest.mjs run --config test/vitest/vitest.extension-telegram.config.ts telegram/src/bot-message-dispatch.test.ts -t "progress"

The test should prove Telegram provides a predictable visible draft/placeholder when upstream draft/progress state exists, while still avoiding duplicate preview spam.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions