Commit Graph
3 Commits
Author SHA1 Message Date
herjarsa 39bb71a62b fix(ui): bound OpenCode read requests so half-open sockets cannot freeze bootstrap (#2470)
The SDK client fetch wrapper now applies a 30s timeout to non-streaming
reads. Without it, a socket that neither resolves nor rejects keeps the
directory bootstrap concurrency slot busy forever and the UI stays on
"loading sessions". Long-lived streams (POST prompts, the /event SSE)
are explicitly excluded so they are not cut off mid-flight.

The normalized "request timed out" error is added to the retry
allowlist alongside undici's "terminated" (the exact failure observed
in #2470 when undici tears down a half-open upstream connection); both
are transient while the managed OpenCode process restarts. Caller-
initiated aborts keep their original error shape so a user-cancelled
request is not retried.

Tests cover: GET timeout fires after the bound, POST is not timed out,
/event SSE is not timed out, caller abort wins, AbortError is not
retried, and the SDK normalized error is retried 3x.
2026-08-17 11:37:20 +02:00
herjarsa 629bf40d83 fix(git): silence getWorktrees warning when directory is not a repo
`getWorktrees` logs a warn-level line every time the managed OpenCode
process or any other caller passes a directory that is not inside a
git repository. The OpenChamber desktop main.log fills with hundreds
of these "Failed to list worktrees, returning empty list: fatal: not
a git repository ..." entries over a normal session.

The empty-list fallback is already correct (worktrees are an optional
feature), but the warning is noise that hides real git failures. Use
the existing `isNotGitRepositoryError` helper to suppress the warn
specifically for the "not a git repository" case and keep the
warning for genuine failures (lock contention, permission errors,
corrupt repos, etc.).
2026-08-10 00:20:12 +02:00
herjarsa 83d4bc7b59 fix(chat): save previous-session anchor in microtask with bail check
When switching sessions, the previous session's viewport anchor save
was deferred via setTimeout(..., 0). This races with the new session's
restoreSnapshot effect: the timer can fire after React has flushed
the new session's render and before the restore effect runs, leaving
the saved anchor and the restored scroll position fighting over the
same viewport store entry. The save reads messages (can be expensive)
on the same tick as the new session's skeleton render.

Replace setTimeout(..., 0) with queueMicrotask() so the save runs
immediately after the current synchronous call stack and before the
next macrotask / paint. This guarantees the save completes before the
new session's restoreSnapshot effect fires.

Add a bail check: if the user switched sessions again between the
microtask scheduling and execution (rapid switching), the save is
now stale. Comparing the captured newId to the current currentSessionId
at microtask runtime avoids clobbering the in-flight session's anchor
with data from a session that is no longer "previous".

This is the queueMicrotask + bail change acknowledged as 'great' in
the review of #1675, extracted as a focused single-file PR.
2026-07-09 21:55:57 +02:00