Queued messages had four related defects:
- A queued message flashed and then vanished at flush time: concurrent
transcript refreshes (the `complete` handler racing the watcher-triggered
update) could resolve out of order, letting a stale response overwrite
newer server messages after the optimistic row was pruned. Session slots
now carry a monotonic fetch ticket and discard stale fetch/refresh/
fetchMore responses.
- Switching sessions flushed the previous session's queued draft into the
newly viewed one (sending it with the wrong provider's settings, e.g. a
Claude model into a Codex session). The composer flush is now scoped to
its session, and a draft restored into an idle session sends after a
short grace period so a live-run ack can cancel it.
- The thinking banner never appeared for a queued turn: the chat handler's
session-keyed completeRun safety net could fire after a queued message
had already started the session's next run, emitting a spurious
`complete` that killed it. The safety net is now scoped to its own run
via completeRunIfCurrent (with a regression test).
- Queued messages only sent while their session was being viewed. Drafts
now persist their send options (model, effort, permissions) at queue
time, and a new app-level useQueuedMessageAutoSend hook dispatches a
non-viewed session's queued message as soon as its run completes, using
the storage key as the claim ticket to prevent double sends.
- validate chat image attachments server-side: only files inside the
~/.cloudcli/assets upload store may reach provider file reads
- harden asset serving with nosniff and attachment disposition for
SVGs to prevent stored XSS
- show the timestamp on image-only user messages
- serialize git stage/unstage calls and defer the status re-sync so
rapid toggles can't interleave or flicker
- use a literal hex fallback for commit ref badges (alpha suffix on a
var() string produced invalid CSS)
- stop binding a URL session to a guening
project instead
- add the missing attentionRequiredIndicator key to all sidebar locales
- clean up dangling conjunctions left the
de/ru/tr/ja/zh-CN/zh-TW READMEs
The sidebar could keep a provider-native id after backend remapping.
That left a duplicate non-working session visible until refresh.
Fresh sessions could also appear hours old.
SQLite CURRENT_TIMESTAMP is UTC without a timezone suffix.
Browser parsing then treated those values like local time.
Broadcast a canonical session_upserted event when the provider id is mapped.
Collapse provider-id aliases onto the stable app session id in the client.
Normalize session-row timestamps to ISO UTC when reading from the repository.
Add a running-session view to the sidebar, including header controls, running counts, empty states, and row-level processing indicators so active provider work is visible outside the current chat.
Hydrate running state after refresh through a status-only /api/providers/sessions/running endpoint backed by chatRunRegistry.listRunningRuns, then sync and poll the frontend processingSessions map from AppContent without attaching to chat streams or replaying messages.
Preserve fresh local processing entries during sync so newly sent messages are not cleared before the backend registry catches up, and clear completed sessions once the status endpoint no longer reports them.
Thread active session state through sidebar project/session components, show rotating loaders for processing sessions, and keep the running search mode expanded and filterable.
Fix optimistic local user-message dedupe so repeated prompts are only collapsed when a matching server echo appears from the same send window, preventing sent messages from disappearing until assistant completion.
Add registry test coverage for listing currently running app sessions.
Tests: npx eslint on changed files; npx tsc --noEmit -p tsconfig.json; npx tsc --noEmit -p server/tsconfig.json; npx tsx --tsconfig server/tsconfig.json --test server/modules/websocket/tests/chat-run-registry.test.ts.
The frontend previously juggled placeholder IDs, provider-native IDs, and session_created handoffs, which caused race conditions and provider-specific branching. This introduces app-allocated session IDs, a chat run registry with event replay, delta sidebar updates, and one kind-based websocket contract so the UI can treat every provider the same while JSONL remains the source of truth.