mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-08-26 08:42:02 +00:00
## Problem On mobile, backgrounding CodeNomad while the AI is working suspends JS execution. The SSE connection eventually times out (~45s), and any events that arrived while suspended (a tool call finishing, a message completing) are lost. Returning to the app could leave the UI permanently stuck showing "running" even though the work had actually finished on the server — the only way out was aborting the request. ## Fix **1. `useForegroundRefresh` hook** (`packages/ui/src/lib/hooks/use-foreground-refresh.ts`) Subscribes to the SSE transport's `disconnected -> connected` transition (not `visibilitychange`) and re-fetches session status + force-reloads messages only when a reconnect follows a real disconnect. An earlier attempt triggered on `visibilitychange` after a fixed delay, but that force-reloaded even when the connection had stayed alive the whole time, clearing in-flight "sending" state and making it look like the AI never responded to a message the user had just sent. Also fixes a latent bug in `server-events.ts`: `onPing` could fire for a stale SSE generation after a reconnect, sending a pong tied to the wrong connection. Guarded with the existing `connectGeneration` counter. **2. Two `hydrateMessages` bugs** found while testing the hook against real mobile background/foreground cycles (`packages/ui/src/stores/message-v2/instance-store.ts`): - **Unnecessary full re-render on every reload.** The force-reload path always bumped every message's revision regardless of whether the server returned identical content, invalidating render caches and re-rendering the entire visible session on every reconnect. Several reconnects in a row produced perceptible lag. Fixed by only bumping revision when a message's parts or status actually changed — reload time dropped from ~280-320ms to ~10ms for unchanged content. - **Duplicated message bubble on a race.** If the user sends a message right as a reconnect happens, the optimistic "sending" bubble (client-side temp id) isn't in the REST snapshot yet and was silently dropped from the visible list — without being deleted from the store. When the real SSE echo for it later arrived, the code could no longer find it to swap cleanly, so it created a new record instead, and a subsequent reload could leave both the orphaned bubble and the new one visible at once. Fixed by preserving pending "sending" messages across a reload until they're actually confirmed. ## Validation - `npm run typecheck` (workspace `@codenomad/ui`): clean - `node --test src/stores/message-v2/instance-store.test.ts`: 4/4 pass (2 new tests covering the duplicate-message race) - Full UI test suite run file-by-file: 57/66 pass; the 9 that don't fail identically on a pristine `upstream/dev` checkout with none of this PR's changes applied (pre-existing solid-toast SSR issue in the test environment, unrelated to this change) - `npm run build` (workspace `@codenomad/ui`): succeeds - Manually verified over ~1.7 hours of real mobile background/foreground cycles (14s glances up to two 40+ minute backgrounds) — refresh fires only on genuine reconnects, completes in ~200-450ms, no stuck "running" state and no duplicated messages on return --------- Co-authored-by: Pascal André <pascalandr@gmail.com> |
||
|---|---|---|
| .. | ||
| build-and-upload.yml | ||
| comment-pr-artifacts.yml | ||
| dev-release.yml | ||
| manual-npm-publish.yml | ||
| pr-build.yml | ||
| release-ui.yml | ||
| release.yml | ||
| restrict-non-dev-prs.yml | ||
| reusable-release.yml | ||
| update-winget.yml | ||