You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(cloud): #92 review, round three — sign-outs, BYOK in Settings, malformed renewals
1. A sign-out could be reported as an expiry. Chat and the agent both called a
gateway 401 "the session ended" whenever the window was no longer signed in.
Signing out while that 401's refresh is still out leaves the window signed out
as well — the superseded refresh finds no token — so someone who had just
pressed Sign out was told "Your session has expired". Both paths now ask one
function, isEndedSessionError, and it also requires an expiry to be waiting.
One function, because the rule was written twice and had the same hole twice.
2. Choosing BYOK in Settings left the card up. The webview dropped the card only
on a signed-in account message, so with the mode switched — or the cloud host
cleared — requests ran on the user's own key under a card still asking them to
sign in. The account message now carries `expired`, the host's own answer to
"is an expiry still waiting?", and the card goes when that is false; the
webview does not keep its own list of reasons. The way back is covered too:
returning to gateway mode puts the expiry back in force, so the settings
listener replays the card. And a session that ends while in BYOK mode no
longer shows a card at all — nothing the user was doing has stopped working.
3. A malformed 2xx counted as a renewal. classifyRefresh accepted any truthy
`access`, so `{ access: {} }` went to SecretStorage, which takes strings. I had
moved those writes out of the refresh's try in 5182a1f, so the rejection came
out of the check `ready` awaits and took the rest of initialization with it.
`{ access: 'a', refresh: {} }` replaced the access token first and threw
second. Both fields are validated before a reply is 'ok'. Separately,
refreshCloudToken can no longer reject: a store that will not write (a locked
keychain) is one more way for the attempt to fail, which is the containment
that commit lost.
Verified: 43 suites, 757 cases, 0 failing. sessionExpiredHost.test.js goes from 44
to 57 cases and session.test.js from 12 to 15. The agent cases run the REAL loop
from agent.js — loaded whole, with the provider call replaced — through a 401
whose refresh is refused, and through a sign-out while that refresh is out. The
suite takes the real fetch away, so nothing in it can reach a gateway even if a
refactor routes around the stand-ins. Its SecretStorage stand-in now rejects
non-strings, as the real one does. Each defect put back fails its own case: no
"expiry waiting" clause -> the chat and agent sign-outs; the hook with its own
copy of the rule -> the wiring guard; either token unvalidated -> session.test.js
and "nothing was written"; the refresh allowed to reject -> "the keychain is
locked" escapes; no `expired` on the account message, no replay on returning to
gateway, the webview dismissing on sign-in alone -> each its own case. In headless
Chrome against the real chat.html, the BYOK and cleared-host checks fail on the
previous commit and pass on this one. tsc --checkJs reports nothing new.
0 commit comments