* [AI] fix(agents): keep exact NO_REPLY silent instead of mirroring messaging-tool text
Exact final NO_REPLY used to be rewritten to the latest messaging-tool text
(messagingToolSentTexts.at(-1)). sessions_send bodies are recorded in that
global list on success, but carry no channel route target, so an internal
escalation note could become the final user-facing payload and be delivered
to the originating user channel (#119383).
Remove the obsolete generic rewrite: the Doctor migration contract already
states exact NO_REPLY is never rewritten to visible fallback text, and both
the block-reply delivery path and the final payload normalizer treat exact
NO_REPLY as silence. Messaging-tool sent-text/target evidence recording is
unchanged so dedupe and lifecycle evidence keep working.
Fixes#119383
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* [AI] refactor(agents): document NO_REPLY invariant without report reference
ClawSweeper P3: the inline comment in handleMessageEnd referenced the
specific report (#119383). State the durable invariant instead: global
messaging-tool send evidence is not a user-route reply and must never be
mirrored into the final payload. Also apply oxfmt formatting to the new
regression test fixture lines.
Related to #119383
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(agents): strip echoed base64 image payloads before per-turn CLI output-limit accounting
* fix(agents): normalize Claude tool media before output metering
Complete the owner-boundary repair for #119488 and #119445.
Preserve contributor @ml12580 and reporter @orbitingflea attribution.
* fix(agents): frame coalesced CLI records before output limits
Resolve both exact-head P1 coalesced-line framing findings and preserve the shipped raw-line CLI backend hook.
Preserve contributor @ml12580 and reporter @orbitingflea attribution for #119488 and #119445.
* fix(agents): meter every bounded Claude output frame
Resolve exact-head blank-frame and padded-output accounting across both Claude parser owners while preserving coalesced media and the shipped raw-line plugin hook.
Preserve contributor @ml12580 and reporter @orbitingflea attribution for #119488 and #119445.
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(media): keep the ttl sweep out of the managed outgoing tree
attachments.ttlHours arms a maintenance sweep that prunes every direct
child of the media dir by mtime. That includes media/outgoing, which
is not scratch: it holds the only durable copy of generated media
referenced by chat-history SQLite rows, and the legacy records/*.json
files that the orphan reaper treats as its pre-SQLite migration
barrier. One sweep deleted history originals while their rows kept
pointing at them, and cleared the barrier so the next reap removed
legacy orphans it was written to protect.
Skip the outgoing subtree in pruneNonPlaybackMedia. It is owned by
cleanupManagedOutgoingMediaRecords, which reaps with the database in
the loop instead of by mtime alone.
Fixes#119088
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(gateway): run the managed outgoing reaper on the media cadence
ClawSweeper's P1: skipping `outgoing` in the ttl sweep removed the only
periodic cleanup for transient records and unindexed originals, since
the SQLite-aware reaper only fired after chat.history reads. Run it
right after each maintenance sweep instead, so transient managed media
still expires in sessions that never read history, and say so in the
ttlHours help text (P2).
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* ci: retrigger after a runner checkout flake
* style: run oxfmt over the maintenance timer edit
* fix(media): decouple managed cleanup from attachment ttl
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(gateway): drain media cleanup during shutdown
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* test(gateway): let media maintenance settle before cadence
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(gateway): publish media cleanup owner before start
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(gateway): bound media cleanup shutdown drain
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(gateway): fence media cleanup across restarts
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): retain history when session state is unavailable
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): retain history for unavailable fixed stores
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): keep target result type private
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): retain history when session database is missing
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): preserve fixed-store ownership during cleanup
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): use discovered owner for history lookup
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): retain history for unreadable session rows
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): honor explicit retired store ownership
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(gateway): share media cleanup fence across startup
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(gateway): fence media cleanup in close prelude
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): retain history for unreadable sibling stores
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(media): require complete ownership evidence before cleanup
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* test(media): end-to-end regression for GC on a dropped session_nodes
The exact case the review called out: a real seeded database loses its
session_nodes table, the global sweep runs, and the record plus its
original bytes must survive. A control case keeps the healthy path
honest: a dereferenced record still gets deleted when the store reads
fine. Fails against the pre-guard code, passes with it.
* refactor(sessions): isolate retention availability reads
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* fix(sessions): keep availability result type private
Co-authored-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
---------
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Route public poll votes through Telegram’s authorized inbound turn path while preserving chat/topic origin and durable ordering. Surface unsupported routing without duplicating sent polls.
Co-authored-by: clawSean <260045960+clawSean@users.noreply.github.com>
* fix(ui): preserve active session status layout
* fix(ui): preserve avatar paths and fork lineage
* fix(ui): preserve avatars under control UI base paths
* [AI] fix(pairing): normalize account key in legacy allowFrom filename parsing
parseAllowFromFilename extracted the account segment from filenames
without normalizing it via safeAccountKey, so non-canonical spellings
(e.g. HY_RIN_Bot vs hy_rin_bot) could never match configured accounts.
This left the file unresolved, producing a startup migration warning
that blocks gateway readiness on every restart.
Normalize accountKey via safeAccountKey before comparing so that raw
and canonical spellings resolve to the same account. Wrap in try/catch
so pathological filename segments skip safely instead of throwing.
Guard non-canonical DEFAULT segments: when safeAccountKey maps a
non-literal-default segment (e.g. "DEFAULT") to the canonical key
"default", skip the filter so the file remains unresolved instead of
being attributed to the implicit bundled default account and deleted.
Keep the implicit bundled-default fallback on the literal canonical
suffix (accountKey === DEFAULT_ACCOUNT_ID). Ambiguity detection
remains unchanged.
Related to #110187
* fix(pairing): constrain legacy filename recovery
Co-authored-by: WangYan <wang.yan29@xydigit.com>
* fix(pairing): preserve case-colliding legacy files
Co-authored-by: WangYan <wang.yan29@xydigit.com>
* fix(pairing): match legacy files to raw account ids
Co-authored-by: WangYan <wang.yan29@xydigit.com>
* fix(pairing): keep default suffix literal
Co-authored-by: WangYan <wang.yan29@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
…rash
An MCP server whose stdout frame exceeds the SDK ReadBuffer 10 MiB cap makes
ReadBuffer.append throw synchronously inside the child stdout data callback.
The handler had no try/catch, so the error escaped as an uncaughtException and
took down the host process, killing every concurrent session. The frame size is
driven by tool-result data a lower-privileged sender can influence, not by the
operator who chose the server.
Wrap append and processReadBuffer in the same try/catch the upstream SDK
transport uses: route the error to onerror and close the transport so the MCP
runtime handles it as a server disconnect. This matches the existing stdin
(#75438) and stderr (#99803) guards in this file; stdout was the missed path.
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>