Skip to content

Add hibernatable WebSockets - #37

Merged
MiguelsPizza merged 7 commits into
mainfrom
alex/hibernatable-websockets
Aug 28, 2026
Merged

Add hibernatable WebSockets#37
MiguelsPizza merged 7 commits into
mainfrom
alex/hibernatable-websockets

Conversation

@MiguelsPizza

Copy link
Copy Markdown
Contributor

Why

Rook currently pins Agents to non-hibernating sockets because the TypeScript runtime could not preserve a live transport across actor eviction. This ports the observable Cloudflare Durable Objects WebSocket hibernation contract, with workerd as the oracle, so embedders can evict idle actors without reconnecting their clients.

This release unblocks the Rook host cutover that depends on socket rehydration and explicit quiescence.

What changed

  • Implements WebSocketPair, all Durable Object WebSocket state methods, tags, structured-clone attachments, auto-responses, event timeouts, close/error/message dispatch, and actor-scoped globals.
  • Adds the small host contract for mirroring socket state and rehydrating it before the replacement constructor runs.
  • Exposes ActorContainer.quiescence() for eviction decisions and gate hooks for embedder observability.
  • Runs Node, browser, and workerd through one conformance contract.
  • Turns the extension example into the reference embedder: an existing AgentClient remains readable and writable across root-container eviction.
  • Fixes canceled host timers so they release runtime waitUntil bookkeeping even when the host cancels by leaving its delay promise unsettled.

Verification

  • Unit: 850/850
  • Workerd conformance: 69/69
  • Node conformance: 69/69
  • Browser conformance: 77/77
  • Typecheck: all six projects
  • Extension E2E: green, including same-client eviction round trip
  • Vibe-platform E2E: green
  • check:package: green
  • check:oracle: green

Release

The included Changeset publishes @mcp-b/do-runtime as the next minor (0.6.0).

Capture the pinned runtime's acceptance, attachment, dispatch, close, auto-response, timeout, tag, and eviction behavior before changing do-runtime. The shared host now exposes connection and eviction affordances so the same oracle spec can drive the Node and browser implementations.
Exercise host mirroring, constructor-visible rehydration, socket lifecycle, quiescence, and gate hook injection through the public container surface. These tests intentionally fail on the current throwing stubs before implementation begins.
Replace the fail-closed state stubs with runtime-owned WebSocketPair endpoints, attachment and tag storage, class-handler dispatch, auto responses, and close-state handling. Add the mirror-out HibernationHost and pre-constructor rehydration option so an embedder can rebuild a container without reconnecting a live client. Node and browser lane hosts now exercise that same lifecycle, including a real eviction-and-wake cycle.
Remove the hibernate:false overrides and the duplicate in-memory WebSocketPair now that the runtime supplies both. Keep only the embedder-owned Response 101 adapter, and teach the MessagePort bridge to announce socket halves that are already OPEN so Agents RPC does not wait forever for a synthetic open event.
Describe the host mirror and rehydration handoff, the non-blocking quiescence signal, and the measured pair and close semantics. Record the breaking API replacement as a minor changeset for the 0.5.0 release.
Represent socket acceptance and delivery as explicit states so registry-dependent behavior cannot exist without its registry. Route raw listeners through the current registry after a hibernation rebuild, and keep attachment serialization local after close instead of calling a host that no longer owns the socket. Reuse Workers platform types and one conformance mirror to remove duplicated adapters and unsafe casts.
The hibernation contract was covered by conformance embedders, but the copyable MV3 host still did not preserve an Agents connection when its root container was replaced. Share the in-memory mirror with the extension, rebuild placements from its snapshot, and prove the same AgentClient can receive and send state after eviction.

That lifecycle exposed a timer bookkeeping bug: a host may model cancel-by-drop with an unsettled delay promise, leaving a cleared interval permanently counted in waitUntil. Settle the runtime-owned cancellation race and pin it with a regression so quiescence remains a truthful eviction signal.

Document the same-Worker boundary explicitly: container replacement rehydrates, while Worker or offscreen destruction removes the raw transport and reconnects.
@MiguelsPizza
MiguelsPizza merged commit 0b382f9 into main Aug 28, 2026
1 check passed
@MiguelsPizza
MiguelsPizza deleted the alex/hibernatable-websockets branch August 28, 2026 22:14
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.

1 participant