…s in error
The 150 ms delta throttle in emitChatDelta withholds the most recent chunk
without scheduling it, leaving it in the run buffer for a later event to
deliver. emitChatTerminal exists to be that event: it flushes the buffer
before emitting the terminal frame.
On the error terminal it cannot, because the buffer is cleared at the top of
the branch, before finalizeLifecycleEvent runs. The flush then resolves empty
text and no-ops, so the withheld tail never reaches the chat channel, and a
terminal that classifies as a cancellation resolves its message to undefined.
The clear is redundant on the immediate branch: finalizeLifecycleEvent already
clears the buffer itself, after emitChatTerminal has flushed and resolved the
terminal message. Removing it there fixes both the missing delta and the
missing message. The deferred retry-grace branch still needs the clear so a
fallback attempt cannot merge onto the failed attempt's text, so flush before
clearing there, matching the pre-tool and pre-item flush sites in the same
function.
The end terminal was already correct, which is why this stayed invisible.
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Separate durable ClickClack room identity from replaceable exact-session attachments. Session archive, reset, deletion, and recreation retain the existing room and history while rotating local routing authority and preserving fail-closed exact-incarnation grants.
Pin ambiguous room recovery to the originating account and credential, and retain prior attachments when persistence fails.
Closes#119300
Co-authored-by: Chisel <chisel@psiclawops.dev>
* [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>
Require a short-lived, owner-bound launch ticket before minting a Control UI bootstrap token.
Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.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>
* fix(qa): reject unanswered and unsafe character conversations
* fix(qa): propagate classified persona delivery failures
* fix(qa): keep qualitative persona evaluations on live providers
* test(qa): avoid inefficient persona failure table spread