The close handler ran cron.stopAndDrain() and heartbeatRunner.stop() bare, while every sibling
teardown uses shutdownStep() (catch/warn/continue). stopAndDrain() re-throws stream-watcher stop
failures by design, so the rejection skipped the remaining teardown -- wss.close(), httpServer.close(),
client closes, interval clears -- leaving the port bound and timers live; the next listen() then hits
EADDRINUSE. Wrap both calls in shutdownStep. Adds a regression test.
Co-authored-by: MatthewSynthia <matthewsynthia@users.noreply.github.com>
Honor existing per-turn timeout and retry controls across OpenAI Responses, Azure Responses, and OpenAI-compatible Chat Completions. Add real SDK HTTP regressions for all three transports.\n\nRefs: #114203
Fixes#101420. Preserve Matrix-native JSON fields while projecting readable message and pin tables. Apply only valid same-sender edits, honor homeserver replacement aggregates, and preserve cross-page updates, redactions, and original thread relationships.
* perf(ui): keep chat render state alive across session switches
Retain bounded per-session chat items and virtualizer measurements, avoid redundant tool-card walks, and reduce markdown cache churn during rapid switching.
User-visible behavior: switching back to a session now restores its prior reading position while end-anchored sessions continue following the latest message.
* chore(ui): drop dead resetChatThreadPresentationState barrel re-export
* refactor(ui): move gateway host panel and connection status to the Gateway page
* test(ui): update e2e expectations for the Gateway settings page move
* 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.