feat(core): enable the responses websocket by default - #48140
Merged
Conversation
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.
Summary
Turn the OpenAI Responses WebSocket channel on by default for every route that advertises
responsesWebsockets(OpenAI, Azure, xAI), after two days of live verification with an API key and a ChatGPT Codex credential, and give it a config switch shaped likecompaction.Behaviour changes
providers.<id>.websocket: false(or per model) keeps a provider on HTTP; the model policy overrides the provider policy, and providers withhttp.request/http.responsehooks stay on HTTP as before. The experimentalOPENCODE_EXPERIMENTAL_<P>_RESPONSES_WEBSOCKETopt-in is gone: it defaulted tofalse, was never documented, and config now owns the policy, so there is one switch. Docs: "WebSocket transport" in providers.mdx, one line in config.mdx.deliveryand always retry; WebSocket post-send failures wereambiguousand never did (fix(ai): harden websocket error contracts #40695 said "never replays after ambiguous delivery" — this reverses that), so a socket dying before the first frame was a hard step failure. Now onlyacceptedandrejecteddeliveries are final. Pre-output andstore: false; worst case is one duplicated request's tokens.previous_response_idasinvalid_request_errorwith nocode, soOpenResponsesContinuationnever classified itretry-full. An unclassifiedInvalidRequeston an incremental send is now retried full, read from the canonical classification the base driver already produced — classified failures such as context overflow keep their runner-owned recovery (an earlier draft re-parsed the raw event and would have turned overflow into a wasted full resend; covered by a test).wsreports an aborted handshake as anerrorevent on the next tick;waitOpenremoved its listeners beforeclose(), so with the new timeout every hung connect on the Node build would have raised an uncaught exception. Reproduced with node v22 + ws 8.21 and fixed; Bun is unaffected (itswsshim is the native EventTarget socket), which is also why this has no Bun test.Plumbing (
feat(core): add the websocket provider policy):Config.Provider/Config.Model→ config plugin →Provider.Info.websocket/Model.Info.websocket→projectModel(model.websocket ?? provider.websocket) →Resolved.websocket: boolean(default resolved once, at the resolver) → the gate inprepare, which is now one expression:webSocket: "session" && !hasHttpHooks && capability && policy. Capability ("the route can") stays plugin-owned; policy ("the user wants") is config-owned.Builds on #47973 (drop the socket after error frames) and #47806/#47974.
Evidence
gpt-5.4-mini) and ChatGPT Codex (gpt-5.5): tool-heavy steps, interrupt mid-step, model switch, idle gaps, automatic provider compactions,kill -9mid-turn + restart. Zero HTTP fallbacks, zero WARN/ERROR, every assistant message error-free, recall intact across compaction and restart. Earlier soaks covered 150 s / 400 s idle gaps (api.openai.com drops idle sockets at ~4.5 min, Codex kept one through 400 s; both reconnect transparently).false→ zero WebSocket activity; providerfalse+ modeltrue→ WebSocket for that model; modelfalse→ zero.Validation
TestClock; incremental unclassifiedInvalidRequest→retry-full, full → provider failure, incremental overflow keepscontext-overflow; Azure default-on with the policy disabling it; config policy inheritance and model override through the catalog; resolver defaultwebsocket: true.bun run test: core 5,292 passed / 35 skipped;@opencode/ai1,290 passed.bun run generateinpackages/client; typecheck clean for schema, ai, core, client, server, sdk, tui, session-ui.Resolved.websocket, connect-failure handling as statements, canonical classification, de-duplicated tests and docs).Not in this PR
gpt-5.4andgpt-5.4-minifor ChatGPT accounts whilecodexAllowedstill lists them — fix(core): drop gpt-5.4 models from the Codex allowlist #48141.