Skip to content

fix(desktop): queue useSyncExternalStore stability, missing imports, promote mutation - #68697

Closed
re-ITRT wants to merge 3 commits into
NousResearch:mainfrom
re-ITRT:fix/desktop-queue-stability
Closed

fix(desktop): queue useSyncExternalStore stability, missing imports, promote mutation#68697
re-ITRT wants to merge 3 commits into
NousResearch:mainfrom
re-ITRT:fix/desktop-queue-stability

Conversation

@re-ITRT

@re-ITRT re-ITRT commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Three fixes in the queue-manager area, all related to the QueueManager refactor (#61573).

Changes

1. useSyncExternalStore infinite re-render (error #185)

  • queue-manager.ts: Stable EMPTY_QUEUE reference for getAll() empty returns (was ?? [] — new reference every call defeats React's Object.is bail-out)
  • composer-queue.ts: Module-level EMPTY constant for stable empty array ref
  • use-composer-queue.ts: useCallback wrappers for useSyncExternalStore subscribe/getSnapshot, useRef([]).current for stable empty array in falsy branch
  • use-composer-queue.ts: Fixed QueueManager.subscribe this binding via useCallback

2. Missing imports in use-composer-queue.ts

Added MAX_AUTO_DRAIN_ATTEMPTS, shouldAutoDrain, notify — these symbols were used in the code body but never imported (leftover from the refactor)

3. Syntax error in use-composer-queue.ts

queueCurrentDraft useCallback was missing its closing }); removed duplicate prevQueueKeyRef/etc. declarations that leaked after the missing brace

4. Pre-existing issues caught during investigation

  • queue-manager.ts: Fixed `` type cast from Set<string> → `includes()` (runtime type is `readonly string[]`)
  • queue-manager.ts: promote() now creates a new array reference instead of splice/unshift mutating in place (React wouldn't detect the change)

re-ITRT added 3 commits July 21, 2026 22:42
…Research#61573)

Replace the nanostore-based + useEffect-driven queue system with a
standalone QueueManager that survives component mount/unmount cycles.

Why:
- Queue auto-drain relied on React effects that died when ChatBar
  unmounted (e.g. navigating to Messaging tab).  The drain would then
  inherit a stale foreground runtime id via activeSessionIdRef and
  deliver the queued message to the wrong session.
- Resolving session identity at drain time from shared mutable refs
  (runtimeIdByStoredSessionIdRef, activeSessionIdRef) was fragile and
  produced unpredictable cross-session routing.

What changed:
- New file src/lib/queue-manager.ts: singleton, RAF-batched subscriber
  notifications, retry with backoff (3 tries x 2s), 30s polling
  fallback, localStorage persistence, running-guard on session.resume.
- Deleted use-background-queue-drain.ts (effect) and its test.
- composer-queue.ts delegates all operations to QueueManager.
- use-composer-queue.ts: replace nanostore useSessionSlice with
  useSyncExternalStore(QueueManager.subscribe); remove auto-drain
  effect (QueueManager handles drain via ).
- wiring.tsx: init()/destroy() QueueManager when gateway opens/closes
  instead of calling useBackgroundQueueDrain hook.

Architecture:
    Gateway WS events ->  -> QueueManager.subscribe
                                                    |
                                              tryDrain(sid)
                                                    |
                                        session.resume -> prompt.submit
QueueManager lives at the wiring level, not inside any component
tree, so it keeps draining regardless of sidebar navigation.
- queue-manager: stable EMPTY_QUEUE reference for getAll empty returns
- queue-manager:  type cast (Set→includes) at both call sites
- queue-manager: promote() creates new array instead of mutating in place
- composer-queue: module-level EMPTY constant for stable empty array ref

All three address the same root cause: useSyncExternalStore snapshot
stability.  Returning a fresh '?? []' every call defeats React's
Object.is bail-out and causes 'Maximum update depth exceeded' (error
NousResearch#185) when the session key changes (e.g. navigating to Messaging
tab).
…s, stabilize subscribe/getSnapshot

- Close unclosed useCallback for queueCurrentDraft (syntax error)
- Remove duplicate ref declarations that leaked after the missing brace
- Add missing imports: MAX_AUTO_DRAIN_ATTEMPTS, shouldAutoDrain, notify
- Wrap QueueManager.subscribe in useCallback for stable identity
- Wrap getSnapshot in useCallback, use useRef([]).current for stable empty array
- Restore original dep array scope.attachments
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jul 21, 2026
@re-ITRT

re-ITRT commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

错误的操作导致了这个pr申请,抱歉

@re-ITRT re-ITRT closed this Jul 21, 2026
@re-ITRT

re-ITRT commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

This PR was created by an automated process error. Sorry for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants