web terminal: session-unknown fast-path via browser-visible 4404 close code#992
Merged
Conversation
…tor, id-reassignment finding)
amrmelsayed
added a commit
that referenced
this pull request
Jun 5, 2026
amrmelsayed
added a commit
that referenced
this pull request
Jun 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PIR Review: Web terminal session-unknown fast-path (browser-visible Tower close code)
Fixes #971
Summary
The web (dashboard) terminal previously blind-retried for the full 6-attempt backoff (~60 s) before giving up when a session no longer existed on Tower, because a browser can't read a failed WebSocket upgrade's HTTP status (it only sees close
1006). This change makes Tower, for browser clients only, accept the upgrade to an unknown session and immediately close with an app-range code (4404); the coreclassifyUpgradeErrorhelper now classifies4404aspermanent; and the dashboard'soncloseconsults the classifier and gives up immediately. The VSCode/Node path is untouched — it still receives the HTTP404upgrade rejection it relies on (#936), so there is no regression.Files Changed
packages/core/src/reconnect-policy.ts(+25 / -10) —WS_CLOSE_SESSION_UNKNOWN = 4404export;code === 4404 → permanentbranch; doc comment for the overloadedcodemeaningpackages/core/src/__tests__/reconnect-policy.test.ts(+14 / -0) —4404permanent + non-4404 WS-code transient casespackages/codev/src/agent-farm/servers/tower-websocket.ts(+37 / -4) —rejectUnknownSession()helper (Origin-based browser/Node discriminator); wired both session-not-found sitespackages/codev/src/agent-farm/__tests__/tower-websocket.test.ts(+62 / -2) — browser-path (4404) + Node-path (404) cases for both routespackages/dashboard/src/components/Terminal.tsx(+14 / -6) —onclose(event)fast-path onpermanentCommits
c7ad5b2e[PIR web terminal: adopt session-unknown fast-path once Tower emits a browser-visible WS close code #971] core: classify WS close code 4404 (session-unknown) as permanent8e4b62d2[PIR web terminal: adopt session-unknown fast-path once Tower emits a browser-visible WS close code #971] tower: emit browser-visible 4404 close for unknown sessionaa2fa0f2[PIR web terminal: adopt session-unknown fast-path once Tower emits a browser-visible WS close code #971] dashboard: fast-path give-up on permanent close (4404)[PIR #971]thread-log commits)Test Results
pnpm build: ✓ (core, codev, dashboard — tsc + vite)pnpm test: ✓ core reconnect-policy 19/19; tower-websocket 27/27; full codev suite 3224 passed / 13 skipped / 0 faileddev-approvalgate (kill a session Tower-side → web terminal gives up immediately, no full backoff; VSCode terminal fast-path unchanged).Architecture Updates
No
arch.mdchanges needed. The reconnect architecture (ReconnectPolicyin core,SessionManagerreconnection, ring-buffer replay) is already documented and unchanged; this PR extends the existingclassifyUpgradeErrorseam and adds a localized Origin-based discriminator in the upgrade handler — no module boundary or data-flow change.Lessons Learned Updates
Two durable entries added to
codev/resources/lessons-learned.md(both[From #971]):Origin-header discriminator for making Tower's session-unknown rejection browser-readable without regressing the Node path, and that the classifier stays a pure predicate (retry policy lives in callers).Things to Look At During PR Review
Originheader (tower-websocket.tsrejectUnknownSession). This is the crux of the no-regression guarantee: Nodewsclients send noOrigin, so they keep the HTTP404path; browsers always send it, so they get the4404close. A browser withoutOrigindegrades to blind retry (no worse than today).4404. The other404/400rejections (unhandled route, missing/invalid workspace path) stay as HTTP rejections — they're routing errors, not the stale-session case.400–499HTTP-range check in the classifier's object form (disjoint from WS close-code ranges1000–1015/3000–3999/4000–4999, so it can never misclassify aCloseEvent.code; preserves the existing object-codecontract/tests).1006(transient → one retry), the reconnect attempt hits4404→ give up. Matches the VSCode sequence.How to Test Locally
pir-971→ Review Diffafx dev pir-971Related Issues
classifyUpgradeErrorseam this PR makes live)