fix(desktop): latch dead-session 4001s off approval.pending and goal polls - #98568
liuhao1024 wants to merge 1 commit into
Conversation
…polls (NousResearch#98554) A reaped runtime rejects every session-scoped RPC with 4001 forever. The process.list status-stack poll already latches the dead id off (NousResearch#94219), but approval.pending (replayPendingApproval, re-driven on every gateway event) and the goal-status slash poll (refreshSessionGoal) kept hammering a dead id for the life of the window. Extract the latch into a dependency-free store/session-gone.ts so all three pollers share one gone-set and one clear path (any runtime rebind resets all of them via the existing resetBackgroundPollingGuard callers). composer-status.ts re-exports the helpers so existing import sites are untouched. Transient failures (timeouts, transport blips) still retry and rethrow exactly as before.
PR #98568 — fix(desktop): latch dead-session 4001s off approval.pending and goal pollsSummary: Extracts the Non-blocking observations:
Verdict: Correct shared-latch extraction closing the #94219 fallout for the two remaining pollers. No blocking issues. |
|
Thanks for the thorough review — glad the shared-latch extraction reads correctly. I intentionally kept the strict |
|
Superseded by #99891. That salvage keeps latching
You're credited via |
What does this PR do?
When a runtime id is reaped,
tui_gateway's_sess_nowaitrejects every session-scoped RPC with4001 "session not found"— a terminal condition for that id. Theprocess.liststatus-stack poll already latches the dead id off (#94219), but two other pollers never received the guard:store/prompts.ts→replayPendingApproval(approval.pending): no try/catch and no latch; callers swallow the rejection and it is re-driven on every gateway event.store/goals.ts→refreshSessionGoal(slash.execgoal status): a barecatch {}with no latch, so it re-asks on every refresh trigger.The result is a window whose runtime was reaped streaming rejected RPCs for the rest of its life (measured in the issue: 1.6 rejections/sec sustained, worst single id 694 rejections), with a user-visible symptom of the composer losing focus mid-keystroke while the rejected-RPC error path runs.
This PR extracts the existing latch from
composer-status.tsinto a dependency-freestore/session-gone.tsand routes all three pollers through the same gone-set:resetBackgroundPollingGuardis already called on every fresh-runtime rebind (use-gateway-boot.ts,composer/status-stack/index.tsx,store/gateway.ts), so a rebind resumes all three pollers and none can latch off permanently../goals,./promptsor./composer-status, so there is no import cycle (composer-status.tsalready imports./goals).composer-status.tsre-exportsisSessionGoneForBackgroundPollingandresetBackgroundPollingGuardso existing import sites are untouched.Related Issue
Fixes #98554
Type of Change
Changes Made
apps/desktop/src/store/session-gone.ts(new): the shared gone-latch —isSessionGone,markSessionGone,isSessionGoneForBackgroundPolling,resetBackgroundPollingGuard, moved verbatim fromcomposer-status.ts.apps/desktop/src/store/composer-status.ts: inline latch replaced by the shared module; helpers re-exported so external importers are unchanged.apps/desktop/src/store/goals.ts:refreshSessionGoalskips latched ids and latches on a 4001 instead of silently retrying forever.apps/desktop/src/store/prompts.ts:replayPendingApprovalskips latched ids, catches 4001 to latch, and rethrows anything transient.apps/desktop/src/store/prompts.test.ts: regression tests — 4001 latches off re-drives, transient errors still throw and retry, a runtime rebind resumes polling.apps/desktop/src/store/goals.test.ts: regression tests — 4001 latches off refresh triggers, transient errors do not latch.How to Test
cd apps/desktop && npx vitest run src/store/prompts.test.ts src/store/goals.test.ts src/store/composer-status.test.tsObserved result: 47 passed (3 files), including the 5 new dead-session-guard tests.
npx vitest run(full desktop suite)Observed result: 8715 passed / 1 failed — the single failure is
electron/managed-ssh-update.test.ts("POSIX managed launcher executes the updater command…"), which fails identically on a cleanupstream/maincheckout (it mis-creates macOS/var/folderstemp paths as workspace-relative directories), i.e. pre-existing and unrelated to this change.npx tsc -p . --noEmit && npx tsc -p tsconfig.electron.json --noEmit && npx tsc -p tsconfig.e2e.json --noEmit— all clean.npx eslint src/ electron/— clean.Manual shape (from the issue): open a Desktop session, let its runtime be reaped (WS drop past the orphan-reap grace), leave the window open — after this fix the gateway log shows one rejection per dead id and then silence, and typing in that window is no longer interrupted.
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests pass — N/A (desktop TypeScript change; full desktop vitest suite run instead, see How to Test)Documentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/A