Commit Graph

63420 Commits

Author SHA1 Message Date
github-actions[bot]
eebbf6cba5 chore(i18n): refresh native fr locale 2026-07-03 03:13:56 +00:00
github-actions[bot]
3b9e0c8682 chore(i18n): refresh native ko locale 2026-07-03 03:13:48 +00:00
github-actions[bot]
a598c976d0 chore(i18n): refresh native es locale 2026-07-03 03:12:09 +00:00
github-actions[bot]
1617233ff1 chore(i18n): refresh native ja-JP locale 2026-07-03 03:12:05 +00:00
github-actions[bot]
116e9db2a1 chore(i18n): refresh native de locale 2026-07-03 03:10:26 +00:00
github-actions[bot]
c4966e86cb chore(i18n): refresh native pt-BR locale 2026-07-03 03:10:16 +00:00
github-actions[bot]
3b16c41daa chore(i18n): refresh native zh-CN locale 2026-07-03 03:08:51 +00:00
github-actions[bot]
4b8b60f8ab chore(i18n): refresh native zh-TW locale 2026-07-03 03:08:37 +00:00
joshavant
8b9cc3afc4 chore: sync native i18n inventory 2026-07-02 22:06:58 -05:00
joshavant
6aabf07dd7 docs: document android license maintenance 2026-07-02 22:06:58 -05:00
joshavant
166938b7df android: add licenses settings screen 2026-07-02 22:06:58 -05:00
Dallin Romney
8d535fb039 test(qa): cover Crabline Zalo transport (#99303) 2026-07-02 19:09:22 -07:00
Dallin Romney
59b08b4693 refactor(shared): consolidate remaining channel lazy loaders (#99302) 2026-07-02 19:08:11 -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
Dallin Romney
b47fe7a4b3 fix(memory-wiki): avoid implicit error coercion (#99307) 2026-07-02 18:56:46 -07:00
github-actions[bot]
591d099897 chore(i18n): refresh native sv locale 2026-07-03 01:53:25 +00:00
github-actions[bot]
499feb2e2f chore(i18n): refresh native ru locale 2026-07-03 01:52:14 +00:00
github-actions[bot]
24f639c66c chore(i18n): refresh native fa locale 2026-07-03 01:51:36 +00:00
github-actions[bot]
030d184254 chore(i18n): refresh native nl locale 2026-07-03 01:50:36 +00:00
github-actions[bot]
3821c36d58 chore(i18n): refresh native vi locale 2026-07-03 01:50:00 +00:00
github-actions[bot]
204526ac0a chore(i18n): refresh native th locale 2026-07-03 01:48:56 +00:00
github-actions[bot]
99cbc58953 chore(i18n): refresh native pl locale 2026-07-03 01:48:24 +00:00
github-actions[bot]
49ea27ecc2 chore(i18n): refresh native id locale 2026-07-03 01:47:11 +00:00
github-actions[bot]
b7bc8600e4 chore(i18n): refresh native uk locale 2026-07-03 01:46:42 +00:00
Dallin Romney
792060bb63 refactor(shared): consolidate Discord Slack and Telegram lazy loaders (#99298) 2026-07-02 18:45:48 -07:00
github-actions[bot]
359ea4c19b chore(i18n): refresh native tr locale 2026-07-03 01:45:32 +00:00
github-actions[bot]
e60cccf8a9 chore(i18n): refresh native it locale 2026-07-03 01:45:04 +00:00
Dallin Romney
84c9f05513 Allow alternate Zalo Bot API roots (#98768)
* Allow alternate Zalo Bot API roots

* Normalize Zalo provider timestamps
2026-07-02 18:44:15 -07:00
github-actions[bot]
ace162b95b chore(i18n): refresh native ar locale 2026-07-03 01:43:48 +00:00
github-actions[bot]
2c8b97cee9 chore(i18n): refresh native hi locale 2026-07-03 01:43:19 +00:00
Dallin Romney
b98c11a46e refactor(shared): consolidate gateway and stateful runtime lazy loaders (#99296) 2026-07-02 18:43:14 -07:00
github-actions[bot]
f917e893a6 chore(i18n): refresh native fr locale 2026-07-03 01:42:05 +00:00
Ayaan Zaidi
8ca6a6f8ef fix(memory-wiki): fail closed on source page read errors 2026-07-02 18:42:00 -07:00
github-actions[bot]
436dd39343 chore(i18n): refresh native ko locale 2026-07-03 01:41:34 +00:00
github-actions[bot]
8f495e2f80 chore(i18n): refresh native ja-JP locale 2026-07-03 01:40:20 +00:00
github-actions[bot]
a9b0de8037 chore(i18n): refresh native es locale 2026-07-03 01:39:50 +00:00
Dallin Romney
aae57adc80 fix(qa): stagger isolated worker startup (#99294) 2026-07-02 18:39:02 -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
b95fe7f00f fix(telegram): keep the streaming preview on screen >=4s before deleting
The streaming "gerund" progress box was deleteMessage'd immediately at
teardown, so on fast turns it flashed and vanished before it could be read,
and the delete could race a just-persisted message (intermittently dropping
the first verbose commentary). Add MIN_PREVIEW_DWELL_MS (4000ms) and schedule
the delete DETACHED via setTimeout for max(0, 4000 - timeVisible), measured
from when the box first appeared. The delete never awaits, so turn teardown
is never stalled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 18:38:39 -07:00
Peter Lindsey
f5f3a2a571 fix(telegram): convert <br> joins to newlines for parse_mode=HTML previews
Progress drafts join their rendered lines with <br>, but the Bot API
parse_mode=HTML entity set has no <br> tag — line breaks must be literal
newlines. Telegram therefore rejected every multi-line preview edit with
"can't parse entities", and the parse-error fallback silently downgraded
the whole streamed progress draft (all lanes) to unformatted plain text.
Convert <br> to newlines in the parse-mode transport branch, matching what
the rich-message branch already does.

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