Repository navigation
chore(release): version package - #78
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
4 times, most recently
from
September 23, 2026 17:50
7d996e5 to
e17501d
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
September 23, 2026 19:06
e17501d to
6d9c898
Compare
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.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@mcp-b/do-runtime@0.9.0
Minor Changes
8a939e7: A root container now repairs, when it is next placed, an alarm its scheduler lost. An alarm outlet
that reaches its scheduler over RPC cannot fail before the actor's local commit, so a failed
request, or a worker killed at the wrong moment, could leave an alarm stored in the actor that
never fires. The new optional
AlarmOutlet.reconcile(stored)hook fixes this:createActorContainercalls it once, before construction, with the alarm the actor stored, and a rejection fails creation.
Until the actor is placed again, a lost alarm that was its only wake source stays dormant, so a
host that wants a prompt repair re-places a root whose container broke on a failed commit.
AlarmScheduler.hooks(id)implements it, now typedRequired<AlarmOutlet>: it sets the alarm onlywhen the scheduler has none for the actor, or has a later one. Opening a container therefore never
resets the ladder of, or queues a second delivery for, an alarm the scheduler already holds at or
before the stored time. Alarms stay at-least-once: an alarm whose success was reported but whose
deletion never committed runs again. Hosts that forward
scheduleRunto a scheduler in anotherworker should forward
AlarmOutlet.reconcilethe same way and drop any re-push ofgetAlarm()after placement. That re-push reset the retry ladder and redelivered a failing alarm immediately,
at retry count 0, on every alarm-triggered placement.
8a939e7: Remove package surface that nothing imports. This breaks a consumer that used any of it:
@mcp-b/do-runtime/server/alarm-schedulersubpath is gone. ImportAlarmScheduler, itstypes and its retry constants from
@mcp-b/do-runtime, which already exports every one.@mcp-b/do-runtime/conformancesubpath is gone. It exported this repository's conformanceharness types and
substrate()helper, which only its own lanes use.installWebSocketGlobalsormarkWebSocketUsed. The runtime callsboth itself:
container.globalsandinstallActorScopecarry the socket globals, andinstallWebSocketUpgradeGlobals()from@mcp-b/do-runtime/browsermarks upgraded sockets.@mcp-b/do-runtime/gateno longer exports__gate.doRuntimeAwaitTransformemits__gateAwait,__resumeAwaitand__gateAsyncIterable, which stay.8a939e7: Export
platformTimerandplatformFetchfrom@mcp-b/do-runtime: aTimerand aFetchPortover the platform's own
setTimeout,clearTimeoutandfetch, forports.timer,ports.fetchandAlarmScheduler. The package captures them when it loads, which is beforeany
installActorScopecan replace the globals with gated ones built on those ports. A host nolonger has to capture the timers at the top of its worker module before anything else runs. A
port that read the installed globals would recurse. An aborted
afterDelay, including one whosesignal was already aborted, never settles.
8a939e7: The sqlite-wasm backend now grows its OPFS SAH pool when it opens a database, restores a snapshot
or clones a facet's storage, so a pool that fills up no longer breaks the actors in it. Before each
of these it calls the pool's
reserveMinimumCapacity()so the pool holds every file plus arollback journal for each connection the backend has open. Previously a full pool (for example,
one holding facets that were never deleted) still let one more database open but left no slot for
its journal. Every later write then failed with
SQLITE_CANTOPEN, the actor's output gate broke,and re-placing the actor broke it again. A clone or restore could also use up the journal slots or
fail with
No available handles to import to., and a failed facet clone left the facet empty.initialCapacityis now only the size a new pool starts at. The backend never shrinks a pool. Ifthe browser refuses the storage, the operation fails with the driver's error.
OpfsSahPool.reserveMinimumCapacityis now a required member. The driver's pool already has it,but a pool wrapper must forward it.
Opens, restores and clones on one pool now run one at a time, so
open()resolves a little later.Close the storage or delete it only after its
open()calls have resolved. Anopen()stillwaiting its turn is not closed, and
importSnapshot()refuses if that open lands first.8a939e7: Add
browserHost({ include, asyncContext, facets })andworkersModuleAliases()to@mcp-b/do-runtime/vite: the Workers bundle contract every browser host wrote by hand. Register...browserHost({ include })in the application'splugins, in a browser-only config. Italiases
cloudflare:workers,cloudflare:emailand, by default, bare andnode:async_hooksto the package's own files, ahead of the application's own aliases and including for
dependencies Vite pre-bundles. Workers build as ES modules, keep class names (the Agents SDK
routes and persists sub-agents by
constructor.name), and rundoRuntimeAwaitTransform({ include, asyncContext: true }). The application plugins run thetransform only while serving, because unbundled development serves Worker modules through them,
so production page builds no longer lower non-actor code. With
facets: { registry, match }, Worker bundles turn code splitting off, matched chunks getfacetScopeBanner({ registry })through thebanneroutput hook, and the build fails when amatched chunk imports anything: an imported chunk's free
WebSocketPair, streams and timerswould bind to the root actor's scope.
workersModuleAliases()returns just the two platformaliases, for configurations that run no transform.
A second
asyncContexttransform in the same pipeline, such as a host's own besidebrowserHost(), no longer fails on the Oxc async-generator helper the first one corrected.ada5bd1: Ship the Worker half of the browser alarm protocol.
createBrowserAlarmProjector()supplies the
AlarmScheduler'sprojectWakeand anacknowledge()for theBrowserAlarmCoordinator'sdeliver(). Projections leave one at a time, and each drawsits generation only after the previous one was sent. A consumed wake is acknowledged only
after the latest projection is accepted, no delivery or cleanup is active, and the next
wake is absent or later. If the latest projection failed,
acknowledge()sends it againfirst, so a scheduler with nothing new to project cannot leave the wake retrying forever.
nextGeneration()must be durable across Worker restarts: the coordinator silently dropsany projection older than the generation it journaled, so an in-memory counter would stall
every wake after a restart.
parseBrowserAlarmProjection()is now exported.Add
connectMessagePortWebSocket()to@mcp-b/do-runtime/browser. It routes oneMessagePort socket through a Workers-style
fetchsuch as the Agents SDK'srouteAgentRequest(). A socket nothing routes closes with 1011, and a refused upgradecloses with 1008 and the refusal's text when it is printable ASCII of at most 123 bytes.
Previously every failure closed with a generic 1011 that dropped the Agent's reason.
serveMessagePortWebSockets()now takes(bridge, url) => Promise<void>, such as(bridge, url) => connectMessagePortWebSocket(bridge, url, route), instead of a functionresolving a URL to a socket. It reports a connection failure after closing the client,
and a socket that finishes connecting after
stop()now closes with"MessagePort transport closed" instead of "host stopped".
Gate a hibernatable socket that is a host transport, such as a
MessagePortWebSocketrehydrated after a Worker restart. The actor used to receive the transport itself, so its
send()andclose()could leave before a preceding storage write was confirmed. It nowreceives one stable socket per transport that waits for the output gate like a
WebSocketPairhalf; hibernation hosts still see the transport.OffscreenDocumentAdaptergains optionalready()andreplaceUnready()hooks. Thecoordinator runs readiness in the same single flight as creation, for new and existing
documents, and replaces a document that fails it at most once.
Fix two
MessagePortWebSockethangs. A throwingonmessage,onopenoronclosehandler skipped the
addEventListenerlisteners behind it; during the open flush it alsodropped the queued frames and held every later frame in the queue. Handler errors are now
reported the way
EventTargetreports a throwing listener. A throw while bridging aconnected socket, such as a second
accept(), left the brokered client connecting; it nowcloses with 1011.
ada5bd1: Add
installSqliteWasmHost()to@mcp-b/do-runtime/backends/sqlite-wasm. It installs an OPFS SAHpool through sqlite-wasm's
installOpfsSAHPoolVfs()with thename,directory,clearOnInitand
initialCapacityoptions, and returns the{ pool, capi }host the sqlite-wasm providertakes. Concurrent calls for one pool share a single install. Install pools through it:
rejects with a
NoModificationAllowedError. Installing while the previous worker was stillshutting down could delete the pool's directory and every database in it.
reopened. Previously, reopening could expose the interrupted transaction's writes in place of
committed rows and left the recovery journal behind, so snapshot export stayed refused.
ada5bd1: Export
ACTOR_SCOPE_GLOBALSfrom@mcp-b/do-runtimeandfacetScopeBanner()from@mcp-b/do-runtime/vite. The banner binds every nameinstallActorScope()writes to afacet bundle's own scope. Banners that bound only timers,
fetchandcryptoleftWebSocketPairon the root actor's global, so a facet's socket frames waited on the root'soutput gate and could leave before the facet's own write committed.
doRuntimeAwaitTransform()now resolves the imports it injects to the package's own files,ahead of any host alias for them, so hosts no longer alias
@mcp-b/do-runtime/gateor@mcp-b/do-runtime/browser/async-hooks. Add a@mcp-b/do-runtime/cloudflare-emailexport,a data-only
EmailMessagefor hosts to aliascloudflare:emailto.Patch Changes
8a939e7:
bridgeWebSocket(), and soconnectMessagePortWebSocket(), no longer throws inside the Workerwhen its MessagePort peer reports a close that
WebSocket.close()refuses: 1006, which a hostsends when a
chrome.runtime.Portdisconnects withlastError, as well as 1005, 1015, anyother reserved code, a code from 0 to 999 or from 5000 to 65535, and a reason over 123 UTF-8
bytes. The throw surfaced as an uncaught error on the Worker, which a host may treat as fatal
to the actor. The actor's socket now sees a dropped connection instead: the host's code and
reason,
wasClean: false, and no close handshake. Anaccept()ed socket is alreadyCLOSEDwhen its
closeevent fires, and a hibernatable actor receiveswebSocketClose(ws, code, reason, false)with the socketCLOSING, as after any peer close.A close that
close()accepts still completes a handshake as before.MessagePortWebSocketnow reports such a wire close with
wasClean: false, so a socket rehydrated from a rawMessagePortWebSocketreports these closes the same way, and so do clients made bycreateMessagePortWebSocketConstructor(). That includes 1005 (no status received), whereasworkerd treats a close frame without a status as clean. A wire close with a code outside
0-65535 is instead a protocol error: the
MessagePortWebSocketcloses itself with 1002, so abridged actor sees a clean 1002 close with a handshake. When a subclass calls the protected
disconnect()on a bridgedMessagePortWebSocketwith a valid code, the pair is now droppedinstead of completing a handshake.
ada5bd1: Validate both
withEnvAndExports()scopes before installing either. A non-objectexportsargument previously left the
envscope installed for the whole realm, so every laterenvread resolved against it.
Treat a
Timer.afterDelaythat rejects on abort, asnode:timers/promisesdoes, ascancellation. Replacing or deleting a waiting alarm previously raised an unhandled
AbortError, which terminates a Node host by default. TheTimercontract now states thatan aborted wait may stay pending or reject, but must not resolve.
Close the databases
createActorContainer()opened when an open-time check refuses, such asstorage written by a newer release. Each refused attempt previously leaked its handles: a
retry opened another connection to the same OPFS file, and on Node the provider's
exportSnapshot()refused from then on. AFacetHost.abortthat throws is now recorded like afailed facet deletion instead of becoming an unhandled rejection.
Report
rowsWrittenas 0 for statements without result columns that write nothing, in bothSQLite backends. DDL previously repeated the last write's count, because SQLite does not
reset
sqlite3_changes()for it.SqliteWasmActorStorage.copyFrom()now reads every sourcefile in the same task as its recovery-sidecar check, so a source that is still running cannot
open a write transaction between the two.
ada5bd1: Keep the input lock and the implicit transaction across transformed awaits that settle while
the actor still holds its lock.
doRuntimeAwaitTransform()previously resumed every awaitthrough a macrotask hop and a fresh input lock queued behind waiting events, including awaits
of storage calls and plain values. Another event could then run between
await storage.get()and the followingput(), so two concurrent read-modify-writes lost an update.The implicit transaction also committed at the await, so writes survived an abort that workerd
rolls back. A transformed
await ctx.blockConcurrencyWhile()let queued events run before itscaller resumed. Every consumer that transforms the Agents SDK was exposed on its storage
sequences.
A transformed await now continues in the same checkpoint when its promise settles while the
actor holds its lock: storage calls, plain values, and resumptions the runtime already admitted.
Settled awaits no longer pay a macrotask each. An await on foreign I/O still re-enters through
a fresh lock. When another actor's continuation owns the checkpoint, the await waits for a later
task without releasing its lock, and its implicit transaction commits at that hand-off. Code that
relied on a storage-only await to let other events in now blocks them, as on workerd. The Node
and browser conformance lanes now also run the suite with the probe compiled by the transform.