Commit Graph

206 Commits

Author SHA1 Message Date
Josh Lehman
0a8e3604ba refactor: flip sessions and transcripts to sqlite storage (#98236)
* refactor(sessions): migrate runtime storage to sqlite

* test(sessions): fix sqlite CI regressions

* test(sessions): align remaining sqlite fixtures

* fix(codex): require sqlite trajectory recorder

* test(sessions): align orphan recovery sqlite fixture

* test(sessions): align sqlite rebase fixtures

* fix(sessions): finish current-main integration of the sqlite flip

Resolve the whole-store SDK removal across its owner boundary: drop the
loadSessionStore re-export and the registry whole-store wrappers, wire
hasTrackedActiveSessionRun into gateway chat, complete the
preserveLockedHarnessIds cleanup contract, flip the codex thread-history
import to storePath targets, and port remaining main-side tests from
file-store helpers to session accessor reads.

* chore: drop committed pebbles log, revert plugin-inspector bump, refresh generated docs

Remove the 1.8k-line .pebbles/events.jsonl work log from the branch, restore
the plugin-inspector advisory lane to main's pinned 0.3.10 so the supply-chain
bump gets its own review, and regenerate docs_map, the plugin SDK API baseline,
and the export-surface ratchet for the merged tree.

* feat(sessions): keep archived transcripts by default with zstd cold storage

Codex-style retention: deleting or resetting a session archives its
transcript as a zstd-compressed JSONL artifact (plain when the runtime
lacks node:zlib zstd) and keeps it until the disk budget evicts oldest
first. resetArchiveRetention now governs both deleted and reset archives
and defaults to keep; maxDiskBytes defaults to 2gb so retention stays
bounded, with archives evicted before live sessions. The cron reaper
follows the same knob instead of deleting archives on its own timer.

* fix(state): converge agent DB migration lineages and bound database growth

Merge coherence: run both structure-gated legacy memory-schema repairs
(flip-lineage drop, main-lineage identity rebuild) before the flip
migration so pre-flip v1/v2 and pre-merge flip v1/v4 databases all
converge, and hoist foreign_keys=OFF outside the schema transaction
where the pragma was silently ignored and the v1 sessions rebuild
cascade-deleted session_entries.

Growth guards: fresh agent DBs enable auto_vacuum=INCREMENTAL, WAL
maintenance releases freed pages in bounded passes (never a blocking
full VACUUM), and doctor reports state/agent DB bloat from freelist
stats.

* fix(codex): resolve the store path for thread-history import via the SDK

The supervision catalog passed the legacy sessionFile locator to the
storePath-targeted transcript mirror; resolve the agent store path with
the session-store SDK helper instead of a runtime-object seam so test
fakes and headless callers need no extra surface. Drop the obsolete
missing-session-id preprocessing case: sessions rows are NOT NULL on
session_id and upsert repairs id-less patches at write time.

* fix(sessions): fail safe on malformed disk-budget config and doctor stat errors

A malformed explicit maxDiskBytes disables the budget instead of
falling back to the destructive 2gb default the user never chose, and
the doctor bloat check skips databases whose paths stat-fail instead of
aborting doctor.

* fix(sessions): complete sqlite conflict translations

* test(sqlite): align hardening checks with maintenance

* test(sessions): inspect compressed transcript archives

* fix(tests): await session seeds and drop unused helpers flagged by CI lint

The five unawaited writeSessionStoreSeed calls raced their SQLite seeds
against the assertions, failing compact shards; the bloat probe drops a
useless initializer and the merged tests drop now-unused helpers.

* test(sessions): type legacy proof events directly

* test(sessions): align hardening contracts

* perf(sessions): read usage transcript sizes from SQL aggregates

Usage/cost scans walked every session and materialized every transcript
event just to re-stringify it for a byte estimate — the #86718 stall
class reborn on the DB. readTranscriptStatsSync sums stored JSON bytes
in SQLite without loading a single row.

* fix(sessions): re-root foreign-root transcript paths onto the current sessions dir

Restored backups, moved OPENCLAW_STATE_DIR, and rehearsal copies carry
absolute sessionFile paths from the old root; the containment fallback
kept those foreign paths, so migration read (and would archive) files in
the original root and reported local copies missing. Re-root the
canonical agents/<id>/sessions suffix onto the current dir when the file
exists there; genuine cross-root layouts still fall through unchanged.

* test(agents): seed harness admission through sqlite

* fix(sqlite): close agent db on pragma setup failure

* fix(doctor): compact and retrofit incremental auto-vacuum after session import

The migration is the sanctioned offline window: post-import compact
reclaims import churn and applies auto_vacuum=INCREMENTAL to databases
created before the fresh-DB pragma existed, so runtime maintenance can
release pages in bounded passes on every install.

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 14:50:37 -07:00
Peter Steinberger
fe3884a7ad fix(telegram): keep streamed final pages on word boundaries (#104608)
* fix(telegram): align draft final pagination

* chore: drop release-owned changelog entry

* style(telegram): format pagination parity test
2026-07-11 11:44:09 -07:00
Peter Steinberger
81a201df26 feat: add portable table presentation blocks (#103583)
* feat(presentation): add portable table blocks

* chore(presentation): refresh generated contracts

* fix(slack): preserve table fallback payloads

* docs(changelog): note portable message tables

* fix(presentation): preserve cross-channel fallback delivery

* chore(plugin-sdk): refresh rebased contracts

* test(slack): align accessibility expectations

* fix(presentation): harden cross-channel fallback delivery

* chore(plugin-sdk): refresh rebased contracts

* docs(changelog): defer portable table release note

* fix(presentation): satisfy extension lint

* chore(plugin-sdk): refresh surface budgets

* fix(telegram): preserve reactions after progress replies

* fix(slack): preserve rendered preview fallback text

* fix(feishu): preserve oversized presentation fallbacks

* docs(changelog): note portable message tables

* docs(changelog): defer portable table release note

* chore(plugin-sdk): refresh rebased contracts

---------

Co-authored-by: Peter Steinberger <steipete@openai.com>
2026-07-10 22:29:13 -07:00
ly-wang19
780f8d6f62 fix(telegram): clean up split reasoning previews (#97828)
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
2026-07-10 19:43:14 -07:00
chenyangjun-xy
b58029f4fd fix(telegram): preserve outbound prompt context projections (#102469)
* fix(telegram): preserve outbound prompt context projections

Co-authored-by: 陈杨俊0668000971 <chen.yangjun@xydigit.com>

* fix(telegram): satisfy legacy sender lint

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-10 18:25:54 -07:00
Peter Steinberger
3f402f2c48 fix(telegram): suppress replies superseded during adoption (#103965)
* fix(telegram): preserve supersession through async adoption

* fix(auto-reply): stop superseded queued turns after adoption

* test(telegram): use dispatcher delivery return type
2026-07-10 23:10:05 +01:00
Vincent Koc
c5bdd64442 fix(telegram): detach adopted turns from reply fence (#103952) 2026-07-10 14:36:46 -07:00
Vincent Koc
f79e885985 fix(telegram): harden spooled turn adoption (#103695)
* fix(telegram): harden spooled turn adoption

* fix(telegram): retain fence through adoption

* fix(auto-reply): await queued admission hooks

* fix(telegram): clean up adoption integration

* fix(telegram): break spool retry import cycle

* fix(telegram): serialize spool timeout with adoption
2026-07-10 07:08:35 -07:00
Yuval Dinodia
b7ab62c2cf fix(mattermost): preserve text-block boundaries in draft preview (#87322) (#87449)
* fix(mattermost): preserve text-block boundaries in draft preview (#87322)

* fix(mattermost): preserve block preview boundaries

* fix(mattermost): reset progress at tool boundaries

* docs(plugin-sdk): refresh API baseline

* fix(mattermost): group parallel tool previews

* fix(mattermost): stage tool previews before awaits

* fix(mattermost): satisfy tool boundary return contract

* fix(mattermost): serialize preview block generations

Co-authored-by: Yuval Dinodia <yetvald@gmail.com>

* fix(mattermost): preserve complete block previews

* test(agents): align streaming assertions

* test(telegram): align reply pipeline mock

* fix(plugin-sdk): keep reply prefix options compatible

* test(mattermost): exercise threaded final participation

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-10 14:32:11 +01:00
Ayaan Zaidi
d11d1d0ee6 fix(telegram): scope reply-fence abort authority to pre-adoption dispatch 2026-07-10 17:08:31 +05:30
Gorkem Erdogan
a6b907825a fix(telegram): attribute the bot's own chat-window messages as self (#102507)
* fix(telegram): label cached bot replies as self

Co-authored-by: Gorkem Erdogan <gorkem.erdogan@outlook.com>

* fix(telegram): preserve optional cached senders

* fix(telegram): recognize business bot senders

* test(telegram): isolate self-attribution fixtures

* fix(telegram): ignore fake channel senders

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-10 10:19:13 +01:00
Peter Steinberger
add2c586c2 feat(slack): support native chart presentations (#102635) 2026-07-10 04:05:23 +01:00
NIO
a3f00d32c9 fix(telegram): keep DM topic auto-rename user message UTF-16 safe (#101781)
* fix(telegram): keep DM topic auto-rename user message UTF-16 safe

Add surrogate-boundary regression for auto-topic label input truncation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(telegram): tighten topic label boundary proof

---------

Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-09 06:44:19 +01:00
snowzlmbot
2f98e1fdb5 fix(telegram): address rich fallback proof review 2026-07-07 14:14:03 +05:30
Momo
e0d23cfc5a fix(telegram): dedupe visible assistant prompt context (#100573)
Summary:
- Merged fix(telegram): dedupe visible assistant prompt context after ClawSweeper review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(telegram): align directive reply prompt timestamps

Validation:
- ClawSweeper review passed for head 5c4cc7245e.
- Required merge gates passed before the squash merge.

Prepared head SHA: 5c4cc7245e
Review: https://github.com/openclaw/openclaw/pull/100573#issuecomment-4890518598

Co-authored-by: momothemage <niuzhengnan@163.com>
Approved-by: momothemage
2026-07-06 08:11:08 +00:00
Vincent Koc
4212de9e08 fix(telegram): count thinking token progress in collapse summary 2026-07-05 05:23:16 +02:00
Ayaan Zaidi
b34c188f8f fix(agents): surface Claude CLI thinking token progress 2026-07-04 19:43:11 -07:00
zhang-guiping
0ad58848b3 fix(telegram): stop duplicate fallback when dispatch fails after final reply
Telegram no longer sends a generic "Something went wrong" fallback after a
final answer was already delivered and a later dispatch/cleanup step failed.
Failures with only partial or no visible output still send the error fallback
and stay retryable.

Related: #87299
Closes #90152
2026-07-03 09:37:48 -07:00
Dallin Romney
e7384d5f02 fix(ci): restore Telegram and SDK guard checks (#99355) 2026-07-02 21:18:25 -07:00
Ayaan Zaidi
3ad465d32b fix(telegram): persist ambient transcript rows
Persist room-event observations as durable bare user transcript rows and carry an ambient transcript watermark through session state so Telegram chat windows only include the unpersisted gap.

Fixes #99257
2026-07-02 19:01:22 -07:00
Peter Lindsey
dae86e6bec fix(telegram): guard the normal collapse-bar fallback send too
ClawSweeper review finding: F3 only wrapped the cleanup-fallback bar send.
The normal path applyProgressCollapseSummary awaited an unguarded durable
send when finalizeToPreview could not edit in place, and sendPayload throws
durable.error on delivery failure — so a cosmetic summary-bar flood-wait
could propagate and fail an otherwise-complete turn (merge-risk:
message-delivery).

Route BOTH cosmetic-bar sends through one shared guarded helper
postCosmeticSummaryBar (swallow + logVerbose), so neither the cleanup path
nor the finalizeToPreview-miss path can fail turn delivery. Add the missing
regression: no-live-message fallback bar send throwing keeps the turn alive
and the final answer delivered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
b43687d0b5 fix(telegram): align progress-window tests with rebased core types
Post-rebase onto main, check-test-types/lint failed on our touched test
files (runtime unaffected):
- core's onVerboseProgressVisibility now takes a thunk (isActive: () =>
  boolean); the dispatch test passed a bare boolean. Pass () => true.
- our TelegramDraftStream additions (finalizeToPreview,
  rotateToNewMessageDeferringDelete) are required members; the QA-e2e
  mock streams omitted them. Add both to every mock stream.
- drop a redundant 'as string' cast (oxlint no-unnecessary-type-assertion).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
9dfa7dc87a fix(telegram): red-team hardening for the progress collapse bar
Addresses five codex red-team findings on the streamed progress-window
collapse bar. Channel-side only; no core src/** changes.

F1 (transcript pollution): the collapse bar went through
sendPayload({durable:true}), which unconditionally passed the transcript
mirror to the deliverReplies fallback, writing the cosmetic digest
("💬 1 note · 🛠️ 1 tool call · ⏱️ 4s") into the session transcript so the
model read it back as its own prior turn. Added a mirrorTranscript option to
sendPayload; the bar now sends durably with mirrorTranscript:false. Discord
parity: its summary bar (monitor/reply-delivery.ts deliverDiscordReply →
sendDurableMessageBatch) has no transcript-mirror seam at all. Real finals
keep mirroring.

F4 (tool overcount): progressSummary.noteToolCall() fired for ANY start-phase
tool, but the compositor renders a line only for work tools
(isChannelProgressDraftWorkToolName) and only when toolProgress is on, so
codex/message_tool_only turns showed "🛠️ 1 tool call" with no tool line. The
count is now gated by the same public work-tool-name check plus
streamToolProgressEnabled; non-counting tool starts still close the
reasoning/commentary bursts as a boundary.

F2 (silent collapse drop): finalizeToPreview ignored a false return from the
in-place edit (flood-wait 429 / terminal error), so applyProgressCollapseSummary
assumed "edited", cleared state, posted no bar, and left the tall window.
finalizeToPreview now returns undefined when the edit did not apply, so the
dispatch falls back to the existing durable-post path.

F3 (cosmetic send fails the turn): the cleanup-time deliverProgressCollapseSummary
could throw (429/network) and propagate out of dispatch after the real final
already delivered. The bar send is now wrapped: failures log via logVerbose and
never fail the turn; the once-guard is preserved.

F5 (ghost-preview race): rotateToNewMessageDeferringDelete rewound while a FIRST
send was still in flight; the late send landed as superseded {retain:true}, which
the dispatch handler kept as an orphaned stale bubble. A reposition now records
the in-flight generation and deletes its late-landing message (deferred), while
forceNewMessage's retain-as-durable-chunk contract is unchanged.

Tests: added unit tests for each invariant (bar delivered but absent from the
transcript mirror; message-tool start → no 🛠️ count and toolProgress-off → no
count; failed edit → undefined; bar send throw → turn still succeeds; reposition
race → superseded send deleted not retained). Telegram suites green
(bot-message-dispatch 165, draft-stream 58, progress-summary 16); extensions
tsgo clean for telegram.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
21c63be655 fix(telegram): keep interim answer blocks out of the progress window
Progress mode's streaming window is a pure activity log (Discord parity): it
carries only 🧠/💬/🛠️ progress lines, and the answer appears once, as the final
message below the collapsed bar. Answer PARTIALS were already suppressed
(updateDraftFromPartial early-returns for the answer lane), but intermediate
answer BLOCKS (info.kind === "block", before the final) still streamed into the
window via deliverLaneText, so interim assistant prose flashed inside the
working bubble mid-turn.

Fix: in the deliver() answer-block branch, suppress plain interim answer blocks
in progress mode — buffer them (like the existing skipTextOnlyBlock path) so they
still feed the final/collapse, and skip the draft-stream delivery. Media,
approval, and button blocks are not plain interim prose and fall through to
normal delivery. With plain blocks now suppressed upstream, the block-branch's
progress rotation guard only guards those remaining media/button blocks; comment
updated to say so.

This is the product decision Peter approved (interim answer-block visibility in
the window); it completes the single-message model so the window truly only ever
shows progress lines.

Tests: added "never streams an interim answer block into the progress window
(Discord parity)" — an interim block never reaches update/updatePreview and never
appears in delivered replies, while the final answer is delivered below. 233
green across bot-message-dispatch, draft-stream, progress-summary;
extensions/telegram typechecks clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
5d59338254 fix(telegram): single stationary progress window (no mid-turn rotation churn)
Progress mode now keeps ONE window message per turn, edited through every lane
handover and edited into the bar only at collapse — matching Discord's
single-message model. Previously the tool-progress window was rotated/repositioned
to a fresh bubble on every interim answer chunk and on the tools->text handover,
even though progress mode never renders interim answer text into the window
(updateDraftFromPartial already returns early for the answer lane). Each rotation
spawned a new message; that churn was the on-off jump's underlying source.

Fix — progress-mode-gated so the rotation branches fire only where they must
(block/partial); block/partial and finalized-rotation paths are untouched:
- prepareAnswerLaneForText returns early in progress mode — no rotate/reposition
  for interim answer text that never displays. (bot-message-dispatch.ts:1288)
- prepareAnswerLaneForToolProgress no longer rotates in progress mode; the tool
  lane always edits the same message (rotate stays for block/partial where answer
  text streamed first). (bot-message-dispatch.ts:1064)
- the interim answer-block path no longer rotates the window to a new bubble in
  progress mode. (bot-message-dispatch.ts:2356)

Rotation/delete paths REMAIN (all post-first / deferred-delete per cc9c2b13a9)
only for: block mode, forceNewMessage after a finalized answer (new turn), error
teardown with nothing to summarize, and superseded-generation cleanup. Collapse
uses task-9 order (final first, then edit the one window into the bar in place);
zero deleteMessage in the happy path. The end-of-turn forceNewMessage-after-
collapse is unchanged (end-of-turn, not mid-turn churn).

LOC: net +~24 prod lines, all justified — three small streamMode==="progress"
guards plus the invariant comments AGENTS.md requires for intentional caller
differences. The rotation branches are shared with block/partial and cannot be
deleted, only gated.

Tests: removed the now-obsolete task-8 "repositions the tool-progress window"
test (that path no longer runs in progress mode); added "keeps a single
stationary window when text follows durable reasoning" and a multi-boundary turn
"uses one stationary window message across a multi-boundary turn
(commentary->tool->commentary->tool->final)" asserting one window message id used
throughout, edited N times, zero deletes, one bar edit last, final posted before
the bar edit. 231 green across bot-message-dispatch, draft-stream, progress-summary;
extensions/telegram typechecks clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
d1a24ba8cc fix(telegram): send final before collapsing the window (edit-shrink anchor loss)
Live off-off finding (no reposition involved): the final answer posted below the
viewport. Cause: collapseProgressWindowIntoSummary edited the tall window bubble
DOWN to a one-line bar BEFORE the final was sent; shrinking content under the
viewport breaks the Telegram client's at-bottom follow, so the freshly sent final
landed off-screen.

Fix — swap the order in deliverProgressModeFinalAnswer:
- Snapshot the bar line (resolveProgressCollapseSummaryLine, which also consumes
  the once-guard) BEFORE the final send, so the final's own delivery cannot
  perturb the counts/timer.
- Send the final answer FIRST (it lands at the bottom of the anchored viewport),
  THEN collapse the window above it. The bar is an edit of an already-posted
  message that sits above the final, so shrinking it is harmless.
- Failure path: if the final send skips/fails, still collapse to the bar (or tear
  the window down when there is nothing to summarize) so no stale window lingers.
- Mark answerLane.finalized / markProgressFinalDelivered LAST, after the collapse
  resets lane state, so end-of-turn cleanup sees a finalized lane (stop(), not a
  spurious clear()).

Renamed collapseProgressWindowIntoSummary -> applyProgressCollapseSummary (takes
the pre-resolved line; "edited" | "posted").

Tests: updated the collapse tests to assert the new order (final delivered before
finalizeToPreview); added "sends the final answer before collapsing the window
into the bar" (order + counter snapshot) and "still collapses the window when the
final answer send is skipped". 230 green across bot-message-dispatch, draft-stream,
progress-summary; extensions/telegram typechecks clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
3932f4abb4 fix(telegram): reposition streaming window post-first, delete-old-deferred
Peter isolated the on-off focus-jump precisely: when a durable 🧠 posts BELOW
the streaming window, the window (now above the reasoning) is repositioned to
stay newest by DELETING it and reposting below. Telegram cannot move messages,
so the delete-then-repost order scroll-jumps the client.

Root cause: rotateAnswerLaneAfterToolProgress rewound the tool-progress window
with stream.clear(), which deletes the old message IMMEDIATELY when it has been
on screen past the dwell — before the replacement message is sent. Delete-first,
post-second.

Fix — invert the order, preserving arrival order:
- draft-stream: new rotateToNewMessageDeferringDelete() rewinds the stream so the
  NEXT update creates a fresh message, and schedules the superseded message's
  delete for AFTER it (detached, floored at 1.5s so the new message lands first).
  Extracted the shared deferred-delete scheduler (scheduleDetachedDelete) used by
  both clear() and the reposition. (draft-stream.ts)
- dispatch: rotateAnswerLaneAfterToolProgress now repositions via that method
  instead of clear()+forceNewMessage, so no window reposition deletes before the
  replacement lands. (bot-message-dispatch.ts:1259)

Tests: draft-stream unit tests prove the sequencing (new message sent before the
old is deleted; delete deferred; no-op with no live message). Added a dispatch
repro (durable 🧠 then answer text mid-turn -> reposition, no clear). Updated the
predating tool-progress-rotation tests to assert the deferred-delete reposition
and that any deliverer-cleanup clear() runs only AFTER the rewind. 228 green
across bot-message-dispatch, draft-stream, progress-summary; extensions/telegram
typechecks clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
7933c8cdea fix(telegram): make progress-window collapse consistent across all modes
Live on-off finding: the collapse path was inconsistent. Edit-in-place worked
in off-off/stream-off, but in on-off (tool-progress-only window, reasoning
durable) finalizeToPreview returned undefined and the caller fell into a bare
clear() -> deleteMessage -> Telegram focus-jump; a sibling sub-branch dropped
the bar entirely. Two divergent outcomes from one fallback.

Root cause: a throttled tool-progress preview could still be pending (coalesced,
never sent) when the turn ended, so the window had no message id even though it
had "rendered". finalizeToPreview gave up and the dispatch fallback deleted the
(late-landing) window.

Fixes:
- draft-stream finalizeToPreview: settle the stream, then MATERIALIZE a still-
  pending preview (send it, as a final flush would) so the window message exists
  and can be edited in place. Only when no message could be established does it
  return undefined. (draft-stream.ts:597)
- dispatch collapse: one deterministic path returning "edited" | "posted" |
  "none". A bar is ALWAYS surfaced when one exists — edited in place, or posted
  durably with ZERO deleteMessage. clear()/delete now runs ONLY for the "none"
  case (error final or nothing to summarize), never when a bar exists, so no
  collapse path can focus-jump. Split into resolveProgressCollapseSummaryLine /
  collapseProgressWindowIntoSummary / resetAnswerLaneAfterCollapse /
  teardownProgressWindow for a readable branch. (bot-message-dispatch.ts:1949)

Tests: added on-off dispatch repro (tool-progress-only + durable reasoning +
mid-turn rotation + final -> edits into bar, no clear, exactly one bar), the
no-live-message durable-bar-without-delete case, and three draft-stream
finalizeToPreview cases (edit-in-place, pending-materialize, no-window).
225 green across bot-message-dispatch, draft-stream, progress-summary;
extensions/telegram typechecks clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
af0477b61d fix(telegram): mode-gate corrections, collapse-by-edit, durable 🧠 marker
Five progress-window fixes for the Telegram streaming lane, aligning it with
the Discord reference surface:

- FIX 2: /reasoning on + /verbose off no longer kills the progress window.
  forceBlockStreamingForReasoning is now scoped to non-progress modes, so
  durable reasoning removes only the 🧠 lane; commentary/tools still stream and
  the collapse bar still posts.
- FIX 3: durable thoughts render behind the 🧠 marker instead of the literal
  "Thinking" header. The core formatReasoningMessage output is rewritten
  channel-side in reasoning-lane-coordinator (no core change), keeping the
  italic body.
- FIX 4: /verbose on no longer duplicates tool calls. canPushStreamToolProgress
  now yields under verbose so the durable verbose lane owns every progress
  surface (invariant: persistent message XOR window).
- FIX 1: the progress window collapses by EDITING the existing message in place
  into the summary bar (draft-stream finalizeToPreview), then posts the final
  below — no delete + repost, which scroll-jumped the client. Falls back to
  clearing the window when there is no bar to collapse into.
- FIX 5: message_tool_only/codex finals that bypass the in-band answer path now
  post the collapse bar from a cleanup-time fallback (sawProgressFinal from the
  dispatch counts).

Tests: adapted predating dispatch/reasoning tests to the new 🧠 marker and
collapse-by-edit behavior; added coverage for FIX 2 (window alive under
/reasoning on), FIX 4 (no window tool dup under verbose), and FIX 5
(message_tool_only collapse bar). 220 green across bot-message-dispatch,
draft-stream, progress-summary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
16a07774a9 feat(telegram): collapse summary bar + multi-line lane rendering
Port the dev-beta2 progress-summary work onto the compositor-era dispatch:

- Post a one-line activity digest (🧠 N thoughts · 💬 N notes · 🛠️ N tool
  calls · ⏱️ Ns, Discord parity) as a durable message when the progress
  window collapses at end-of-turn, before the final answer. Counted
  per-burst channel-side; only window-streamed activity feeds the bar
  (durable rv/verbose items never do — persistent message XOR bar count),
  no bar for error finals or turns where the window never rendered.
- Render multi-line commentary with its line structure intact (per-line
  markdown conversion, joined as line breaks) instead of collapsing to
  one run-on line; per-line conversion also keeps block markdown (setext
  headings) from forming across lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
0ccc3b986b fix(telegram): distinguish and render streamed reasoning/commentary lanes
Streamed progress on Telegram rendered reasoning and commentary
indistinguishably and leaked the model's raw markdown: the compositor
refactor dropped the per-channel lane markers and the markdown rendering
that the pre-compositor path had.

- Pass the 🧠/💬 lane prefixes and commentaryItalics:false to the shared
  progress-draft compositor (mirroring Discord), so reasoning renders
  italic and commentary plain, each behind its own marker.
- Render lane bodies through renderTelegramHtmlText — the parse_mode=HTML
  safe converter — so **bold**, inline code, and _italic_ render as
  intended rather than leaking raw markers. Lane lines collapse to one
  line first: multi-line commentary otherwise forms block markdown (a
  `\n\n---\n\n` separator turns the paragraph above it into a setext
  <h2>), which Telegram rejects, dropping the whole preview (all lanes)
  to unformatted plain text.
- Clip long lane lines INSIDE the whole-line _…_ wrapper: clipping the
  assembled line chopped the closing underscore, silently degrading every
  long reasoning line (the common case) from italic to plain text.

Generic item payloads keep their monospace styling. Matters most for
deepseek, which narrates progress almost entirely through its
markdown-heavy reasoning lane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Ayaan Zaidi
63cbb35056 fix(telegram): unify group history prompt context
Move Telegram group history out of the message body fold and into the structured chat-window context used by the inbound metadata renderer. Preserve outbound self entries, post-self watermarks, recovered forum-topic routing, and richer cached reply/media fields while dropping stale recovered-topic windows.\n\nFixes #99218
2026-07-02 16:05:49 -07:00
Ayaan Zaidi
b07fd6f1b4 fix(telegram): keep group history always on 2026-07-02 09:22:59 -07:00
rabsef-bicrym
c8dba85b5d fix: keep Telegram outbound context on transcript time (#98769) 2026-07-01 22:15:10 -07:00
Marvinthebored
b3b51b0c91 fix(anthropic): surface Discord pre-tool commentary
Route Anthropic pre-tool narration through the commentary progress lane, preserve shared channel progress defaults, and keep Discord/Telegram reasoning gates explicit.

Thanks @Marvinthebored!
2026-06-30 18:12:07 -07:00
Peter Lee
e1724474d6 test(telegram): add regression test for forum topic message_thread_id with streamed reasoning (#94526)
* test(telegram): add regression test for forum topic message_thread_id with streamed reasoning

After thorough code tracing of all delivery paths (draft stream, durable,
non-durable, preview hooks), the current main branch already correctly
passes message_thread_id through all paths for the streaming reasoning +
final answer scenario in forum topics. The bug reported in #89352 may have
been resolved by earlier merged changes (media-path preservation,
draft/progress streaming, etc.).

This commit adds a focused regression test covering the combined
scenario to prevent future regressions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(telegram): assert forum thread at draft-stream boundary for both lanes

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 17:58:00 -07:00
Jason (Json)
455f813d6e fix(telegram): deliver durable reasoning when enabled
Preserve shared reasoning suppression by default while letting Telegram opt into durable reasoning payloads only when it has a deliverable reasoning lane. Covers persistent /reasoning on, separate reasoning stream lanes, and progress-stream suppression.\n\nVerification:\n- node scripts/run-vitest.mjs src/auto-reply/reply/dispatch-from-config.test.ts extensions/telegram/src/bot-message-dispatch.test.ts\n- git diff --check upstream/main...HEAD\n- .agents/skills/autoreview/scripts/autoreview --mode branch --base upstream/main --stream-engine-output\n- CI run 28411526182 green, including QA Smoke CI and check-test-types\n- Real behavior proof run 28411676681 passed\n\nPR: #97875
2026-06-29 17:31:06 -07:00
Ayaan Zaidi
52eb5946c7 fix(telegram): preserve literal reasoning tags 2026-06-28 15:49:03 -07:00
Josh Lehman
7e2b2d2987 refactor: migrate bundled session metadata reads (#96527) 2026-06-24 12:19:53 -07:00
张贵萍0668001030
8150b76b6f fix(telegram): preserve rich table styling 2026-06-24 06:41:38 -07:00
Josh Lehman
132d70bfb3 refactor: migrate bundled transcript target lookups (#89911) 2026-06-23 14:32:21 -07:00
Vincent Koc
013e33c6d3 fix(telegram): avoid duplicate progress headings 2026-06-22 21:43:47 +02:00
Moeed Ahmed
5cafe4b0cf fix(telegram): keep bot reply answers anchored to current message (#90475)
Co-authored-by: Moeed Ahmed <moeedahmed@Moeed-Mac-mini.local>
2026-06-22 19:17:07 +00:00
snowzlmbot
e37b0f8cd3 fix(telegram): render progress drafts with clean HTML transport
Render Telegram progress draft rows with clean plain previews while preserving Telegram HTML formatting through parse_mode.

The progress HTML path now stays transport-owned, including richMessages=false progress messages, while debug/plain/sanitized text remains readable without raw markup.

Thanks @snowzlmbot!
2026-06-22 05:54:44 +05:30
heichaowo
e00c1eebc4 fix(telegram): skip duplicate mirror replies (#95069)
Fix Telegram session transcript duplicate mirror writes after the primary assistant reply already exists.

Thanks @heichaowo!
2026-06-22 01:55:08 +05:30
snowzlmbot
6441e56465 fix(telegram): materialize streaming progress placeholders (#95183)
* fix(telegram): materialize streaming progress placeholders

* fix(telegram): cancel delayed progress drafts before final

* fix(telegram): satisfy progress placeholder lint

* fix(telegram): cancel delayed draft preview on clear

* refactor(telegram): simplify delayed preview flush

---------

Co-authored-by: snowzlmbot <snowzlmbot@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-06-21 19:48:41 +05:30
Ayaan Zaidi
d6d17709e8 fix(telegram): clear progress draft before tool output (#93002) (thanks @zhangguiping-xydt) 2026-06-21 19:14:57 +05:30
张贵萍0668001030
f4dee99574 fix(telegram): clear progress draft before tool artifacts 2026-06-21 19:14:57 +05:30
张贵萍0668001030
db33402af0 fix(telegram): clear progress draft before verbose tool output 2026-06-21 19:14:57 +05:30
Ayaan Zaidi
a8b5f5d551 fix(telegram): send progress previews as html text 2026-06-19 14:56:35 +05:30