Skip to content

feat(realtime): add opt-in bounded connection recovery - #2968

Merged
markstuart-oai merged 10 commits into
mainfrom
markstuart/websocket-recovery-parity
Oct 18, 2026
Merged

markstuart-oai merged 10 commits into
mainfrom
markstuart/websocket-recovery-parity

Conversation

@markstuart-oai

@markstuart-oai markstuart-oai commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Why

A temporary Realtime connection failure currently requires the application to reconnect. Add an option for the SDK to reconnect while keeping the current behavior by default.

High-level description

Reuse the shared WebSocket controller for bounded reconnection in the ws and native WebSocket clients. Each attempt refreshes credentials and keeps the same connection object. A replacement socket starts a new server session; it does not restore state or resend attempted commands.

Reconnection stays off by default. Model sessions can enable reconnect: true; transcription sessions and connections to an existing call reject this option. Wait for the first open before sending. During recovery, callers can set a byte budget to retain unattempted commands, restore state, then explicitly flush or take those commands.

Preserve transport options, binary mode, redirect protection and cleanup. Use the final committed credential and preserve terminal handshake errors. The README and Realtime guide explain defaults, retry limits and state restoration. Existing Responses, Translation and Live behavior stays unchanged.

Validation

  • All 37 focused wire tests pass. Regression tests reproduced lost initial HTTP diagnostics and exhausted credential-provider errors before the fixes. The earlier 64-test wire and adapter suite also passed.
  • Build and isolated package checks pass: TypeScript 4.9/6, packaged source and source maps, CommonJS, ESM and browser bundles.
  • The final test-inclusive TypeScript check and both affected TLS/authentication tests pass after correcting a test fixture type error found by CI.
  • Changed-file lint/format and independent source/security review pass. The earlier cross-API suite passed 1,851 tests with one existing skip.
  • All CI checks pass on 1735c1704de7bb5ab670cd1f4c900faaa345c472. All review threads are resolved; re-review is requested.

Hosted service tests remain on hold. No package publication or merge is part of this PR.

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Castiron custom code

Evaluated main: 3a3b8521a9278d68120c9256a0de4075acb82d80.

Trusted main policy: 3a3b8521a9278d68120c9256a0de4075acb82d80.

✅ No new custom-code files detected.

47 mixed files remain; 0 existing customizations changed.

Compared e989fc070339 → b44832cf963a. Generated baselines verified.

47 existing customizations unchanged
  • api.md
  • src/client.ts
  • src/resources/admin/organization/usage.ts
  • src/resources/audio/transcriptions.ts
  • src/resources/audio/translations.ts
  • src/resources/beta/agents/agents.ts
  • src/resources/beta/agents/environments/environments.ts
  • src/resources/beta/agents/environments/files.ts
  • src/resources/beta/agents/sessions/artifacts.ts
  • src/resources/beta/agents/sessions/sessions.ts
  • src/resources/beta/assistants.ts
  • src/resources/beta/beta.ts
  • src/resources/beta/index.ts
  • src/resources/beta/responses/internal-base.ts
  • src/resources/beta/responses/responses.ts
  • src/resources/beta/responses/ws-base.ts
  • src/resources/beta/responses/ws.ts
  • src/resources/beta/threads/index.ts
  • src/resources/beta/threads/runs/index.ts
  • src/resources/beta/threads/runs/runs.ts
  • src/resources/beta/threads/threads.ts
  • src/resources/chat/completions/completions.ts
  • src/resources/chat/completions/index.ts
  • src/resources/conversations/index.ts
  • src/resources/embeddings.ts
  • src/resources/files.ts
  • src/resources/fine-tuning/checkpoints/permissions.ts
  • src/resources/images.ts
  • src/resources/live/forks/ws-base.ts
  • src/resources/live/forks/ws.ts
  • src/resources/live/sideband/ws-base.ts
  • src/resources/live/sideband/ws.ts
  • src/resources/live/ws-base.ts
  • src/resources/live/ws.ts
  • src/resources/responses/internal-base.ts
  • src/resources/responses/responses.ts
  • src/resources/responses/ws-base.ts
  • src/resources/responses/ws.ts
  • src/resources/skills/skills.ts
  • src/resources/skills/versions/versions.ts

7 more in the full report.

A changed generated baseline means this report cannot reliably identify which handwritten lines changed.

Inspect the custom-code diff

Download the exact patch produced by this run (requires repository access):

gh run download 39107274745 --repo openai/openai-node \
  --name castiron-custom-code-39107274745-1 --dir /tmp/castiron-custom-code-39107274745-1
git apply --stat /tmp/castiron-custom-code-39107274745-1/custom-code.patch
cat /tmp/castiron-custom-code-39107274745-1/custom-code.patch

Or reproduce it from an SDK checkout containing the vendored reporter:

git fetch --no-tags origin 3a3b8521a9278d68120c9256a0de4075acb82d80 b44832cf963afdea3c8fbda95485005c39f86dd6
python3 scripts/castiron/custom_code_report.py report \
  --base 3a3b8521a9278d68120c9256a0de4075acb82d80 \
  --head b44832cf963afdea3c8fbda95485005c39f86dd6 --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-b44832cf963a
cat /tmp/castiron-custom-code-b44832cf963a/custom-code.patch

This is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR.

Full report and patch

Comment thread src/realtime/ws.ts Fixed
@markstuart-oai
markstuart-oai marked this pull request as ready for review October 18, 2026 04:32
@markstuart-oai
markstuart-oai requested a review from a team as a code owner October 18, 2026 04:32
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T07:22:25.332240Z b44832c New commits
🔒 Security Review ✅ Completed 2026-10-18T07:24:06.025061Z b44832c New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@openai-sdks

openai-sdks Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

OkTest Summary

✅ 236/236 SDK tests passed in 7.254s for Node SDK PR #2968.

Test results — 42 files
Test Result Time
tests/chat-completions-complex-body.test.ts ✅ Passed 183ms
tests/chat-completions-create.test.ts ✅ Passed 140ms
tests/chat-completions-stream.test.ts ✅ Passed 99ms
tests/files-content-binary.test.ts ✅ Passed 142ms
tests/files-create-multipart.test.ts ✅ Passed 109ms
tests/files-list-pagination.test.ts ✅ Passed 122ms
tests/initialize-config.test.ts ✅ Passed 116ms
tests/instance-isolation.test.ts ✅ Passed 147ms
tests/models-list.test.ts ✅ Passed 92ms
tests/responses-background-lifecycle.test.ts ✅ Passed 222ms
tests/responses-body-method-errors.test.ts ✅ Passed 225ms
tests/responses-cancel-timeout.test.ts ✅ Passed 181ms
tests/responses-cancel.test.ts ✅ Passed 136ms
tests/responses-compact-retries.test.ts ✅ Passed 198ms
tests/responses-compact.test.ts ✅ Passed 241ms
tests/responses-create-advanced-stream.test.ts ✅ Passed 128ms
tests/responses-create-advanced.test.ts ✅ Passed 102ms
tests/responses-create-disconnect.test.ts ✅ Passed 1.029s
tests/responses-create-errors.test.ts ✅ Passed 130ms
tests/responses-create-malformed-api-responses.test.ts ✅ Passed 82ms
tests/responses-create-retries.test.ts ✅ Passed 148ms
tests/responses-create-stream-failures.test.ts ✅ Passed 84ms
tests/responses-create-stream-timeout.test.ts ✅ Passed 2.126s
tests/responses-create-stream-wire.test.ts ✅ Passed 1.186s
tests/responses-create-stream.test.ts ✅ Passed 72ms
tests/responses-create-terminal-states.test.ts ✅ Passed 177ms
tests/responses-create-timeout.test.ts ✅ Passed 194ms
tests/responses-create.test.ts ✅ Passed 147ms
tests/responses-delete.test.ts ✅ Passed 138ms
tests/responses-input-items-errors.test.ts ✅ Passed 131ms
tests/responses-input-items-list.test.ts ✅ Passed 163ms
tests/responses-input-items-options.test.ts ✅ Passed 113ms
tests/responses-input-tokens-count-timeout.test.ts ✅ Passed 207ms
tests/responses-input-tokens-count.test.ts ✅ Passed 194ms
tests/responses-malformed-inputs.test.ts ✅ Passed 784ms
tests/responses-not-found-errors.test.ts ✅ Passed 166ms
tests/responses-parse.test.ts ✅ Passed 115ms
tests/responses-retrieve-retries.test.ts ✅ Passed 177ms
tests/responses-retrieve.test.ts ✅ Passed 131ms
tests/responses-stored-method-errors.test.ts ✅ Passed 376ms
tests/retry-behavior.test.ts ✅ Passed 3.001s
tests/sdk-error-shape.test.ts ✅ Passed 201ms

View OkTest run #39107242663

SDK merge (1ffb82f170b1) · head (b44832cf963a) · base (3a3b8521a927) · OkTest (ebbcfaca8a5e)

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at c79d6ff. Two independent static reviews covered all ten changed files and supporting controller, queue, adapter, and credential code. No actionable findings. Recovery remains opt-in, refreshes credentials per attempt, retains never-attempted events for explicit flushing, and preserves legacy defaults. Reviewed close cancellation, stale socket ownership, retry bounds, authentication rejection, and browser/Node wiring. Existing feedback is addressed. Hosted checks inspected pass, including prior Node 22/24/26 results; a newer Node 22 run remains pending. No candidate code or tests executed locally.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c79d6ff5f8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/realtime/ws.ts Outdated
Comment thread src/internal/ws-connection-controller.ts Outdated
Comment thread src/internal/ws-connection-controller.ts
Comment thread src/internal/realtime-recovery.ts Outdated

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revising my earlier approval after independently validating newly posted feedback in two static passes. Request changes for the existing undefined-serialization finding; no duplicate inline comment added. An otherwise typed event with toJSON() returning undefined is admitted as zero bytes, then draining throws after permanentClosed is set and before close notifications fire. Reject undefined serialization at admission and cover close/takePending with this input. Hosted CI, including the Node 22 rerun and external OkTest, now passes. No candidate code or tests executed locally.

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved after two independent static re-reviews of the six-file delta and supporting queue/controller paths. Undefined JSON serialization is rejected before admission, addressing the prior close-notification failure. Both wrappers now use the captured reconnect URL, lifetime attempt reporting is consistent, and delay limits fit the timer range (jitter only reduces the capped delay). Regression coverage exercises both wrappers. No new actionable findings. Hosted Node 26 tests and lint pass; other tests/ecosystem checks remain pending. No candidate code or tests executed locally.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c1fb728e4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread README.md Outdated
Comment thread src/internal/ws-connection-controller.ts Outdated
Comment thread src/realtime/websocket.ts Outdated
Comment thread src/realtime/ws.ts Outdated
Comment thread src/internal/ws-connection-controller.ts

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing my decision to request changes after independently validating newly posted feedback with a second static reviewer. The previously fixed serialization/URL/attempt/timer issues remain fixed, but two lifecycle defects remain:

  • Initial handshake queue: with recovery and a nonzero queue budget, CONNECTING sends are retained, initial open skips manual flushing, and no reconnected event tells callers to flush. Reject initial-handshake admission or define and implement an initial-open drain path.
  • Close exception cleanup: invalid close arguments can leave the physical socket open while the controller announces final close and rejects sends. Ensure fallback transport disposal/detachment before finalizing.

Also reconcile the canonical docs/realtime.md recovery/queue statements with the new opt-in API (existing documentation feedback). These references avoid duplicate inline findings. Hosted SDK tests/OkTest pass; remaining checks are pending. No candidate code or tests executed.

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two independent static re-reviews confirm the three prior findings are fixed: initial-handshake sends are rejected without queue retention; invalid close falls back to transport termination/close and blocks post-close event dispatch; and the canonical guide now documents opt-in recovery and explicit state restoration/queue handling. The additional TLS/header snapshot and browser binary-mode fixes are consistent with the existing adapters. No new actionable findings in the incremental diff and relevant lifecycle context.

Hosted checks currently pass or are skipped. No candidate code or tests executed locally.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

buildRealtimeURL: () => url,

P2 Badge Rebuild custom URLs for async-factory reconnects

When a function-based credential requires OpenAIRealtimeWS.create(), this replacement discards the caller's buildRealtimeURL callback after the initial connection and installs a closure that returns the originally captured URL forever. Consequently, a builder that supplies a rotating endpoint or short-lived signed URL is reevaluated for reconnects made through the constructor or Azure factory but not through create(), and every replacement can retry the expired first URL despite refreshing its credential. Preserve the original builder for subsequent attempts while passing the captured URL only to the initial socket; the native create() factory has the same issue.

AGENTS.md reference: AGENTS.md:L52-L57

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/realtime/ws.ts Outdated
Comment thread src/realtime/ws.ts Outdated

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revising my earlier approval after independently validating two newly posted findings with a second reviewer. The previous three fixes remain verified.

  1. Use the deferred credential commit result. The base credential hook intentionally reads the cached accessor-transformed key after assigning a provider token. Initial creation uses that result, but deferred recovery captures the raw token and discards the potentially different value returned by commit(). Use the commit return value when opening the replacement in both wrappers.
  2. Preserve terminal handshake causes. Permanent HTTP status and TLS errors are reduced to WeakSet membership while replacement errors are suppressed. The final generic 1006 abort loses the detail callers need to fix authentication or certificate configuration. Retain and surface the terminal cause.

Referencing the existing findings without duplicate inline comments. Hosted checks pass or are skipped; no candidate code or tests executed locally.

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two independent static reviews of the complete three-file incremental diff confirm both outstanding findings are fixed. Both reconnect constructors use the value returned by credential.commit(), preserving transformed cached credentials. The ws adapter retains terminal HTTP/TLS diagnostics per socket and supplies the cause to the existing final error report before clearing it on close; transient replacement errors remain suppressed. Tests cover committed credentials in both adapters and a single terminal diagnostic for authentication/TLS failures. No new actionable findings.

Hosted CI is still running. No candidate code or tests executed locally.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6039f103b7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/realtime/ws.ts
Comment thread src/realtime/ws.ts

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseding my earlier approval after validating newly posted feedback at the same head. The two previously requested fixes remain correct.

Request changes for the existing credential refresh diagnostic finding: both wrappers await credential preparation, but a rejection enters the unbound catch in ws-connection-controller.ts and is discarded. Once retries are exhausted, onError receives only the generic original-close-code message. Retain the last preparation failure and supply it as the final SDK error cause; cover repeated provider failure for both wrappers while preserving close cancellation and transient retry suppression. Referencing the existing inline finding without duplicating it.

I do not adopt the separate claim that initial TLS failures emit no SDK error: the controller initial-socket error handler forwards err.message and err while not reconnecting, despite the wrapper handler returning.

Two independent static traces confirm the credential-refresh gap. Hosted CI is now complete with all checks passing or skipped. No candidate code or tests executed locally.

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two independent static reviews of the complete three-file incremental diff confirm the remaining credential-refresh diagnostic finding is fixed. The retry loop retains the latest preparation/open failure and supplies it on exhaustion or abort for the opt-in Realtime path; intentional close still suppresses failure reporting and legacy automatic-queue callers retain their error contract. The Node adapter also prefers a recorded terminal HTTP cause over the generic termination error on the existing error-reporting path, preserving the initial 401 without duplicate events. Regression coverage exercises both public wrappers with distinct successive credential failures and initial HTTP rejection. No new actionable findings.

Hosted Node 22 CI/test matrix remains pending; other substantive checks passed. No candidate code or tests executed locally.

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two independent static reviews of the complete incremental diff found no actionable issues. Reconnection now flushes pending automatic-queue commands and invokes the public reconnected hook before notifying stream observers; closing from that hook terminates the iterator without a later reconnected event. Socket listener rebinding remains before notification. The regression covers that ordering; wire-test synchronization remains meaningful. The shortened README points to the canonical guide, which retains the detailed recovery contract. Previous credential/error-diagnostic fixes remain intact.

Hosted CI remains in progress on this head. No candidate code or tests executed locally.

@markstuart-oai
markstuart-oai added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 64872e5 Oct 18, 2026
40 checks passed
@markstuart-oai
markstuart-oai deleted the markstuart/websocket-recovery-parity branch October 18, 2026 20:51
@openai-sdks openai-sdks Bot mentioned this pull request Oct 18, 2026
gh-actions-shared Bot pushed a commit to xf-qubit/openai-node that referenced this pull request Oct 19, 2026
Automated Release PR
---


##
[7.39.0](openai/openai-node@v7.38.0...v7.39.0)
(2026-10-18)


### Features

* **realtime:** add opt-in bounded connection recovery
([openai#2968](openai#2968))
([64872e5](openai@64872e5))


### Chores

* **api:** use neutral moderation examples
([openai#2969](openai#2969))
([3a3b852](openai@3a3b852))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants