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
After a transient server connection drop (sleep/wake, network blip), the chat panel's "Server connection dropped -- reconnecting..." banner stays visible indefinitely despite the session being live and responsive. The ribbon never recovers to the idle state without a manual window reload.
Approach
Remove the event.persisted guard from resumeStreamAfterPageShow so that start() is called on every pageshow event. The guard was designed for browser bfcache semantics, but VS Code webview iframes never fire pageshow with persisted = true, leaving the SSE stream permanently stopped after a pagehide.
Approaches Considered
Always restart on pageshow (chosen) -- minimal one-function change; safe because start() is idempotent (if (started) return)
Remove pagehide/pageshow handlers entirely -- simpler but loses the intentional "stop stream on hide" cleanup, which matters if the app is reused in a browser context
Switch to document.visibilitychange -- semantically better but equally uncertain about VS Code webview event model, more code
Scope
In: fix resumeStreamAfterPageShow logic, update its unit test
Out: proactive health-check heartbeat (separate concern, unnecessary for this fix)
Out: extension-side status bar changes (the bug is in the web app, not StatusBarManager)
Acceptance Criteria
After a simulated pagehide + pageshow (with persisted = false), the SSE stream restarts and streamStatus transitions back to "connected"
The existing resumeStreamAfterPageShow unit test passes with the updated expectation (both persisted: true and persisted: false call start())
The ConnectionBanner shows the green "Server reconnected" flash and then hides within 3 seconds after recovery
No behavior change when pageshow fires without a preceding pagehide (start() is idempotent -- no double streams)
Key Decisions
start() idempotency (if (started) return run at line 270) is the safety net -- no risk of duplicate SSE connections from unconditional calls
Keep the pagehide -> stop() handler (intentional cleanup for when the page is truly hidden); only the resume side changes
The _event parameter becomes unused but is retained for the signature contract (callers pass the event)
Prior Art / Patterns
packages/app/src/context/server-sdk.tsx lines 162-165 -- the function to change
packages/app/src/context/server-sdk.tsx lines 269-339 -- the reconnect loop (unchanged)
packages/app/src/context/server-sdk.tsx lines 347-349 -- the event listener registration (unchanged)
packages/app/src/context/server-sdk.test.ts lines 6-16 -- the test to update
packages/app/src/components/connection-banner.tsx -- the banner component (unchanged, already handles recovery)
Target repo for the fix: harmoniqs/opencode (the engine vendored into the extension)
Notes
The fix ships with the next engine build (opencode binary) vendored into the Amicode VSIX. After merge, bump the vendor pin in harmoniqs/amicode and cut a new extension build.
The pagehide/pageshow lifecycle was added for the opencode browser/terminal app where bfcache is real; in that context persisted = true fires correctly. The VS Code webview is the only embedding where it fails.
Important
Problem
After a transient server connection drop (sleep/wake, network blip), the chat panel's "Server connection dropped -- reconnecting..." banner stays visible indefinitely despite the session being live and responsive. The ribbon never recovers to the idle state without a manual window reload.
Approach
Remove the
event.persistedguard fromresumeStreamAfterPageShowso thatstart()is called on everypageshowevent. The guard was designed for browser bfcache semantics, but VS Code webview iframes never firepageshowwithpersisted = true, leaving the SSE stream permanently stopped after apagehide.Approaches Considered
start()is idempotent (if (started) return)document.visibilitychange-- semantically better but equally uncertain about VS Code webview event model, more codeScope
resumeStreamAfterPageShowlogic, update its unit testStatusBarManager)Acceptance Criteria
pagehide+pageshow(withpersisted = false), the SSE stream restarts andstreamStatustransitions back to"connected"resumeStreamAfterPageShowunit test passes with the updated expectation (bothpersisted: trueandpersisted: falsecallstart())ConnectionBannershows the green "Server reconnected" flash and then hides within 3 seconds after recoverypageshowfires without a precedingpagehide(start() is idempotent -- no double streams)Key Decisions
start()idempotency (if (started) return runat line 270) is the safety net -- no risk of duplicate SSE connections from unconditional callspagehide->stop()handler (intentional cleanup for when the page is truly hidden); only the resume side changes_eventparameter becomes unused but is retained for the signature contract (callers pass the event)Prior Art / Patterns
packages/app/src/context/server-sdk.tsxlines 162-165 -- the function to changepackages/app/src/context/server-sdk.tsxlines 269-339 -- the reconnect loop (unchanged)packages/app/src/context/server-sdk.tsxlines 347-349 -- the event listener registration (unchanged)packages/app/src/context/server-sdk.test.tslines 6-16 -- the test to updatepackages/app/src/components/connection-banner.tsx-- the banner component (unchanged, already handles recovery)Source
harmoniqs/opencode(the engine vendored into the extension)Notes
opencodebinary) vendored into the Amicode VSIX. After merge, bump the vendor pin inharmoniqs/amicodeand cut a new extension build.pagehide/pageshowlifecycle was added for the opencode browser/terminal app where bfcache is real; in that contextpersisted = truefires correctly. The VS Code webview is the only embedding where it fails.