* fix(gateway): clear secrets runtime snapshot only after channel teardown
Shutdown cleared the secrets runtime snapshot in the close prelude, which
also drops the pinned runtime config. The channels close step then lazily
re-pins the raw config, so stopChannel's resolveAccount hit unresolved
SecretRefs and every shutdown of a gateway with an exec/file SecretRef
credential logged an unresolved-SecretRef WARN and completed with
warnings. The failed resolveAccount also skipped the per-account stop
bookkeeping (graceful wait, task/abort cleanup, stopped-runtime state)
and the plugin stopAccount hook.
Move the scrub into the close handler's finally block so it still always
runs, but only after every step that can read credentials.
Fixes#112681
* chore: drop release-owned changelog entry
---------
Co-authored-by: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com>
* perf(sqlite): cache synchronous prepared statements
* fix(sqlite): refresh cached statement metadata
* fix(sqlite): bound cached statement retention
* fix(sqlite): close statement cache lifecycle gaps
* test: suppress SQLite warning in retention child
* test(sqlite): make transcript-load counter statement-cache-aware and allowlist raw test SQL
* test(sessions): type the wrapped iterate against its overloaded signature
* test: share a statement-cache-aware SQLite execution counter across prepare-count tests
* fix(pr): validate head SHA with bash regex so fork-CI runners without ripgrep pass the artifacts-init guard
---------
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Chat kept optimistic thinking-level and fast-mode row patches only in the
host copy of the session list. Any mid-flight capability publish (for
example the loading flip at the start of the post-patch list refresh)
replaced that copy wholesale and reverted the patched row until the
refreshed canonical list landed. The reasoning slider visibly snapped
back to the inherited default during that window and keyboard commits
landing inside it were swallowed, which is the race behind the flaky
chat-composer-redesign thinking-slider e2e assertion on CI.
patchSessionRow now mirrors the optimistic fields into the published
capability snapshot via a new SessionCapability.patchRowLocal, matching
how model overrides already live capability-side, so intermediate
publishes carry the patch and the revert window is gone.
An untrusted channel plugin cannot open its durable ingress queue, but the shared
ingress monitor resolved the queue lazily, so start() armed the poll timer anyway and
every tick re-invoked the throwing factory. On a production gateway that produced one
INFO-level "slack ingress drain failed" line per second for 26+ hours while Slack
reported connected and healthy and every inbound event was silently dropped.
Open the queue before arming the poll timer so an unusable monitor fails channel start
through the caller instead of spinning. Also report the actual denied capability:
openChannelIngressQueue and openSyncKeyedStore both announced themselves as
openKeyedStore, and the message named neither the plugin nor its origin.
The health monitor treated an account sleeping between its own auto-restart
attempts as "stopped" and force-restarted it. That start booted nothing (the
channel supervisor still owned the account task), but the accompanying
resetRestartAttempts wiped the retry ladder, so a crash-looping channel
restarted at attempt 1 every cooldown and could never reach
"giving up after 10 restart attempts".
ChannelManager now reports isAutoRestartScheduled for the supervisor's backoff
sleep and replacement start, and the health monitor defers to it instead of
restarting, resetting attempts, or spending its cooldown/hourly budget. The
monitor stays the last restart owner once the supervisor gives up.
Live evidence (clawmac gateway logs, 2026-07-27/28): 198 "restarting
(reason: stopped)" lines, zero give-ups, attempt ladders truncated at 8/10.
Related: #110802, #111572
Preserve unsplit message whitespace while matching final chunk trimming to all preceding chunks. Covers the reported examples and the auto-reply delivery path. Fixes#64036.
e0a119dabf (#114775) collapsed the account-status `connected` tri-state into a
boolean for running accounts: `resolveChannelAccountState` resolved
`input.runtime.connected ?? false` and the projection emitted the field
unconditionally. Every channel that never publishes connectivity (17 of 27
channel plugins, including imessage, signal, sms, googlechat, line, msteams)
therefore reported `connected: false` while running.
`evaluateChannelHealth` reads `connected === false` on a running account as
`disconnected`, so the gateway health monitor stopped and started every
socketless channel once per cooldown window, forever.
Restores absent connectivity as "no transport signal" in the one place it was
lost. Explicit `true`/`false` from socket channels is unchanged.