Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
d75ab82
feat(i18n): add @bedrock-core/i18n package
drav0011 Aug 16, 2026
922daf8
feat(i18n): typed verb engine with bound display() and the DisplayTex…
drav0011 Aug 16, 2026
e7300cb
fix(i18n): keep arguments for locale-only plural variants
drav0011 Aug 16, 2026
dcc0edb
fix(rehearsal): install both repos before either builds
drav0011 Aug 16, 2026
443210b
docs: correct the package READMEs against the current API and protocol
drav0011 Aug 16, 2026
6caf823
docs: trim the package READMEs to what a reader deciding to install n…
drav0011 Aug 18, 2026
b8146e8
fix(release): publish with yarn so workspace:* is rewritten, and go p…
drav0011 Aug 18, 2026
109e3da
chore(release): version packages
github-actions[bot] Aug 19, 2026
dc70417
feat(bds-runner): run the addons' GameTests headlessly on a real BDS
drav0011 Aug 21, 2026
df12ff3
feat(sync): tag the wire format and pack small messages
drav0011 Aug 27, 2026
4643bb1
feat(sync): negotiate the protocol per peer instead of demanding a match
drav0011 Aug 27, 2026
71e3105
feat(test-addon-2): crafting table container screen
drav0011 Aug 27, 2026
b276760
feat(server-runtime): publish and read a compiled guide's reference
drav0011 Sep 3, 2026
438848a
chore(test-addons): compile the guides, publish the reference, link t…
drav0011 Sep 3, 2026
6f3d9db
chore(test-addons): compile the config editor
drav0011 Sep 3, 2026
213f0b5
docs(plan): design the server data layer as observable, shared, db an…
drav0011 Sep 5, 2026
364c9f7
docs(plan): shared namespaces as typed accessor trees
drav0011 Sep 5, 2026
5cfa30a
docs(plan): align data-flow label with the persisted() marker
drav0011 Sep 5, 2026
9bb5da2
docs(plan): record the dynamic-property ABI survey
drav0011 Sep 5, 2026
830186a
docs(plan): native observable measurements and the block-entity ABI o…
drav0011 Sep 6, 2026
8c93c74
docs(plan): a container slot hosts properties only for non-stackable …
drav0011 Sep 6, 2026
03cf57b
feat(runtime): publish an addon's list page as a reference; test addo…
drav0011 Sep 6, 2026
628b9e5
feat(observable): add the reactive primitive package
drav0011 Sep 6, 2026
55d75fe
feat(db): add the host resolver
drav0011 Sep 6, 2026
220ca17
feat(db): add collections
drav0011 Sep 6, 2026
7a3f755
feat(db): add the index, block cleanup and coalesced writes
drav0011 Sep 7, 2026
35ac54b
chore(test-addon-2): handlers take the event object; drop a stale con…
drav0011 Sep 7, 2026
4d96388
perf(db): tune for the engine, record the in-game measurements
drav0011 Sep 7, 2026
10d9c0b
feat(runtime): add core.shared and core.db; register() takes a manife…
drav0011 Sep 7, 2026
ed1e487
build(test-addons): run the filter stack through the core filter
drav0011 Sep 7, 2026
786ee95
refactor: move bds-runner to its own repository
drav0011 Sep 7, 2026
b2565a0
fix(runtime): guard engine handles that can be invalidated
drav0011 Sep 7, 2026
518470d
docs(plan): record the handle-validity rule and the harness split
drav0011 Sep 7, 2026
72ba18f
feat(config): make config nodes satisfy the observable contract
drav0011 Sep 7, 2026
13a58aa
docs(plan): phase 1 done
drav0011 Sep 7, 2026
a8852b3
feat(test-addon): answer S6, and stop the benchmarks running as corre…
drav0011 Sep 7, 2026
b386156
feat(db): announce documents to peers, and serve them over RPC
drav0011 Sep 8, 2026
90d4ef6
feat(observable): add last(), an event's most recent payload as a value
drav0011 Sep 8, 2026
ac567fe
refactor(server): three channels, and db kept local
drav0011 Sep 8, 2026
e428584
refactor(server-runtime): remove core.state
drav0011 Sep 8, 2026
fa82e68
chore(release): collapse the pending changesets
drav0011 Sep 8, 2026
c17fe55
build(server): make the repository root the @bedrock-core/server package
drav0011 Sep 8, 2026
df867bf
refactor(server-runtime): one Announcement behind every cross-addon feed
drav0011 Sep 8, 2026
1c065f7
docs: comments on every export, and design notes down to what is not …
drav0011 Sep 9, 2026
5da92c5
ci: sibling checkouts for the public repos, and the 0.2.0 runtime ver…
drav0011 Sep 9, 2026
444ff1f
feat(server-runtime): export the schema flatteners
drav0011 Sep 11, 2026
a89d2fe
refactor(test-addons): declare, and let the build do the rest
drav0011 Sep 11, 2026
4574206
chore: follow this repository's rename to server
drav0011 Sep 11, 2026
9725cd1
refactor(test-fixtures): split the GameTest fixtures from the example…
drav0011 Sep 11, 2026
7459044
chore: @minecraft/server 2.9.0, and the server packages by portal
drav0011 Sep 11, 2026
4184ca7
feat(server-runtime)!: core.screens replaces core.guides
drav0011 Sep 11, 2026
6fb64c3
chore: @minecraft/server 2.9.0 with vanilla-data beside it
drav0011 Sep 11, 2026
0c000ac
Add 'packages/i18n/' from commit '74590445039110b9cf60aa5ae6c2055e972…
drav0011 Sep 12, 2026
0051d2e
refactor: use the ui-compiler package name
drav0011 Sep 12, 2026
42219be
refactor: the runtime is a floor, and declarations replace its branches
drav0011 Sep 13, 2026
160b63a
refactor: config leaves the floor, and a slot holds what a package in…
drav0011 Sep 13, 2026
61e8712
feat(sync)!: presence is a value, not a stream
drav0011 Sep 14, 2026
0edc847
chore(test-fixtures): add a build profile that exports locally
drav0011 Sep 14, 2026
5035edd
test(fixtures): cover db and sync in the engine
drav0011 Sep 14, 2026
046a41e
build(deps): star ranges pinned once at the root, on @minecraft/serve…
drav0011 Sep 15, 2026
35e4387
ci: publish by hand on Node 24, with no sibling checkouts
drav0011 Sep 15, 2026
405c84f
test(fixtures): run the core filter from its published tag
drav0011 Sep 15, 2026
1982287
refactor(runtime)!: declarations are registerEvents and registerShared
drav0011 Sep 15, 2026
ad5158b
feat(i18n): overlay the realm's published strings, published by the f…
drav0011 Sep 15, 2026
f890ed4
chore(release): the meta's version is the runtime's, and one sweep pu…
drav0011 Sep 15, 2026
305b924
docs(runtime, observable): drop the package overviews from the entry …
drav0011 Sep 15, 2026
e747a9c
chore: manifest v3 fixtures, 26.50 stable pins, lint ignores generate…
drav0011 Sep 15, 2026
f8ca0a9
feat: @bedrock-core/server/i18n, and READMEs for the current packages
drav0011 Sep 16, 2026
ced92e3
feat(sync): one writer per namespace
drav0011 Sep 17, 2026
2f7f59d
docs(server-runtime): the Announcement comment lists only the feeds t…
drav0011 Sep 17, 2026
54cfadf
test: use published Regolith filter stack
drav0011 Sep 20, 2026
7632b20
chore(release): prepare server 0.2.0 publication
drav0011 Sep 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 37 additions & 32 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Changesets

This folder is managed by [Changesets](https://github.com/changesets/changesets).
It tracks pending version bumps + changelog entries for the publishable packages
(`@bedrock-core/server`, `@bedrock-core/server-runtime`, `@bedrock-core/sync`).
The monorepo root and the reference addons (`packages/test-addon*`) are `private`
and are ignored automatically.
It tracks pending version bumps and changelog entries for the publishable packages:
`@bedrock-core/server`, `@bedrock-core/server-runtime`, `@bedrock-core/sync`,
`@bedrock-core/db`, `@bedrock-core/observable` and `@bedrock-core/i18n`. The monorepo
root and the GameTest fixtures are `private` and are ignored automatically.

## Authoring a changeset

Expand All @@ -20,36 +20,41 @@ your change.

## How releasing works

Releasing is automatic. The **Release** workflow (`.github/workflows/publish.yml`)
runs on every push to `main` and, through `changesets/action`, does one of two
things:

- **Changesets are pending** → it opens (or refreshes) a **"Version Packages"** PR
built by `yarn version-packages`. That runs `changeset version` — consuming the
pending changesets, bumping the changed packages **and their dependents**
(`updateInternalDependencies: patch`, e.g. a `sync` bump patches
`server-runtime`) and writing CHANGELOGs — then
`scripts/sync-runtime-version.mjs`, which rewrites `RUNTIME_VERSION` so the
constant can never disagree with the tag that ships it. The PR is refreshed on
every further push while it stays open.
- **No changesets left** → merging that PR lands the bumps on `main`, and the run
that follows executes `yarn release` (`lint:libs`, `build:libs`, then
`changeset publish`): each changed package goes to npm, gets tagged
`@bedrock-core/<name>@<version>`, and gets a GitHub release.
The **Release** workflow (`.github/workflows/publish.yml`) has two jobs:

- **A push to `main` with changesets pending** opens (or refreshes) a **"Version
Packages"** PR built by `yarn version-packages`. That runs `changeset version`,
consuming the pending changesets, bumping the changed packages **and their
dependents** (`updateInternalDependencies: patch`) and writing CHANGELOGs, then
`scripts/sync-meta-version.mjs` and `scripts/sync-runtime-version.mjs`, which set
the root meta's version and rewrite `RUNTIME_VERSION`. Nothing is published from
a push.
- **A manual run** (Actions, Release, Run workflow) after that PR merges runs
`yarn release`: `lint:libs`, `build:libs`, `scripts/publish-tarballs.mjs`,
and `scripts/tag-packages.mjs`. Each changed package goes to npm
through trusted publishing (OIDC), with no npm token, and is tagged
`@bedrock-core/<name>@<version>`.

**`@bedrock-core/server`'s version IS `@bedrock-core/server-runtime`'s**,
character for character, prerelease tag included. The runtime is what the meta
is; `db`, `observable`, `sync` and `i18n` are support around it, so a consumer
reading either number is reading the same one. A release the runtime does not move
leaves the meta where it is. The root is not a valid changeset target: do not
select it.

`0.0.0` is what an unreleased package sits at, and `publish-tarballs.mjs` skips it.

Only the publishable libraries are installed for the release (`yarn workspaces
focus`), so the `portal:../ui` resolutions the test addons use are never resolved
and the job needs no sibling `ui` checkout.
focus`), and nothing is checked out beside this repository: every dependency
resolves from the registry. The root `resolutions` must carry versions rather than
`portal:` entries before a release can install; that swap is the release step.

The `workspace:` ranges the libraries use to depend on each other must come out of
the tarball as concrete versions. **Release rehearsal**
(`.github/workflows/rehearsal.yml`, `workflow_dispatch`) is what proves they do:
it publishes every package to a throwaway local registry and installs each one
from a clean consumer. Run it before any release you care about.
The `workspace:` ranges the libraries use to depend on each other come out of each
tarball as concrete versions, because `publish-tarballs.mjs` packs with Yarn.

Two repo settings the workflow depends on:
Two things the workflow depends on:

- *Allow GitHub Actions to create and approve pull requests* (Settings → Actions →
General) — without it the Version PR can't be opened.
- An `NPM_TOKEN` repository secret with publish rights on the `@bedrock-core`
scope.
- *Allow GitHub Actions to create and approve pull requests* (Settings, Actions,
General). Without it the Version PR cannot be opened.
- A trusted publisher on npmjs.com for each package, naming this repository and
`publish.yml`.
31 changes: 31 additions & 0 deletions .changeset/db.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
"@bedrock-core/db": minor
---

Add `@bedrock-core/db`: typed documents persisted on whatever dynamic properties a target can
hold.

**The host resolver** probes a target for where its documents can live — its own dynamic
properties through the engine's six-method or component ABI, or a world property keyed by its
identity when it holds nothing — behind one adapter over both ABIs, with a capability record per
host, refusals by name for an `ItemStack` and a stackable slot, and a per-type cache that never
caches a throw.

**Collections** key documents by target on the host the resolver finds. One JSON envelope per
document carries its version; migrations run lazily per document; `defaults` fill deep on read and
a `normalize` runs on every write; `patch` merges plain objects recursively and replaces arrays and
scalars, with `undefined` deleting a key. A document that cannot be read is quarantined under
`<key>#bad` rather than deleted. A budget check runs before the engine, with chunking on the direct
ABI, `accept` and `require` are checked at compile time against what the target type can do, and a
validity gate re-resolves the target on every operation.

**The chunked index** has a resumable `all()` that heals a replaced block, a `blockCleanup` custom
component for `onBreak`, and `coalesce` write-behind that parks on `entityLoad` and flushes on
`playerLeave` — wired to the engine only when a coalescing collection exists.

A collection is **local**. Nothing here is reachable from another realm: what crosses is what the
owning addon puts on one of the runtime's channels.

Tuned for the engine throughout: hosts, resolutions, document stores and handles are classes with
prototype methods, a handle resolves its target once per tick, the resolver trusts a cached type
decision, index chunks are written behind, and a single value skips the batch write.
18 changes: 18 additions & 0 deletions .changeset/i18n-overlay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@bedrock-core/i18n": minor
---

A library's strings can be overridden by the world it runs in.

A library that draws UI ships its own bundle, keyed under a namespace it shares with the rest of
its family. A running realm has more than that: every addon present has announced a bundle, and
one of them may carry the very same key — deliberately, to rename what the library calls something
("Addons" becomes "Mods"), or simply because it ships a locale the library does not.

`overlay(bound, published, bundle)` is that precedence, as verbs. `t()` prefers the published value
wherever it carries the key, so an override and an unshipped locale reach the strings a script
renders rather than only the keys a client paints. `resolve()` and `display()` become the world's,
so a key from any addon's bundle resolves — which is what a screen showing another addon's display
fields needs.

A realm with no published bundles gets the bound instance back untouched and allocates nothing.
11 changes: 11 additions & 0 deletions .changeset/observable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@bedrock-core/observable": minor
---

Add `@bedrock-core/observable`, the reactive primitive the stack notifies through: `observable`
with `get` / `set` / `subscribe`, `computed`, `effect` and `batch`, delivering synchronously with
listeners isolated from each other. `last(signal)` turns anything with `subscribe` — a Minecraft event
signal included — into an observable of its most recent payload, `undefined` until the first one
arrives, released with `dispose()`. The `/minecraft` entry bridges one of ours to a data-driven UI
observable with `toNative`, keeping the native in step for a form's lifetime and writing back only
when the control is client-writable.
37 changes: 37 additions & 0 deletions .changeset/presence-as-a-value.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
"@bedrock-core/sync": minor
"@bedrock-core/server-runtime": minor
---

Who is present is a value, not a stream: discovery, the registry and the host election are
observables.

**Breaking.** `discovery.peers` and `discovery.incompatiblePeers` are `ReadonlyObservable` lists
rather than array getters, so they read `.get()`, watch with `.subscribe()`, and compose with
`computed()` like a config leaf or a shared value:

```ts
sync.discovery.peers.subscribe(peers => redraw(peers));
core.registry.addons.subscribe(addons => redraw(addons));

const hostBanner = computed(() => `hosted by ${core.host.id.get()}`, [core.host.id]);
```

The lists republish only when the world actually changes. A heartbeat that repeats what a peer
already said refreshes its liveness and notifies nobody, which is what lets a listener sit on the
list without waking every five seconds per peer. `lastSeen` therefore left `PeerInfo` and
`IncompatiblePeer` — a tick that moves on every heartbeat cannot live inside an observable value —
and is asked for by id instead:

```ts
sync.discovery.lastSeen('drav0011_economy'); // tick, or undefined
```

`onPeerUp` / `onPeerDown` / `onRegister` / `onUnregister` stay: an arrival is a delta, and a
caller that wants the one peer that changed still wants an event. A TTL sweep publishes the list
once for the whole sweep, before any listener runs, so a handler never sees a half-swept world.

`Registry` keeps no directory of its own — `all()`, `get()` and `has()` read `addons`, so a cached
answer can no longer disagree with the live one — and `HostElection` is `computed` over that list,
which retires its `start()`. `core.host.hostId` is now `core.host.id`, an observable; `HostListener`
takes a `previousHostId: string`, since a derived value always has one.
5 changes: 5 additions & 0 deletions .changeset/screens-feed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bedrock-core/server-runtime": minor
---

**Breaking.** Guides left the runtime: the `guide` and `guideReference` options of `register()`, `core.guides`, `core.guides.manifest`, `GuidesRegistry`, `GuideReference` and `GuideManifest` are removed. A guide is a set of compiled screens from `@bedrock-core/guides`, reached by navigating to its key, and the screens an addon publishes are read through `screens(core)` from `@bedrock-core/navigation`.
50 changes: 50 additions & 0 deletions .changeset/server-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
"@bedrock-core/server-runtime": minor
---

Everything an addon owns is declared in `register()`, and everything that crosses a realm goes over
one of three channels.

**Breaking.** `register()` installs declarations, not plain objects: `shared: registerShared(keys)`,
`events: registerEvents(tree)`, and one field per app, such as `config: registerConfig(definition)`
from `@bedrock-core/config`. Config and guides are no longer part of the runtime. They are the
`@bedrock-core/config` and `@bedrock-core/guides` apps, which keep their state in a `RuntimeSlots`
slot the runtime fills and hands back (`core.fill` / `core.slot`); `core.config`, `core.guides` and
`core.pages` are gone.

**`core.shared`** — a flat `shared` shape comes back as a typed tree, one observable per key with
`get` / `set` / `subscribe` and the usual `(next, prev)` listener. Peers read
`core.shared.of<Def>(ns)`, materialized from the key names the owner announces under
`core-shared/shape`, and never write: a peer's tree has no `set`, in the type and at runtime. The
backend subscription is attached with the first listener and released with the last. The mirror
stores nothing — a value that must survive a restart is a db document the owner maps onto a key.

**`core.events`** — an `events` shape comes back as a typed tree with `emit` and `subscribe`, the
payload type declared by `event<T>()`. A peer reads `core.events.of<Def>(ns)`, which has
`subscribe` alone and answers before the owning addon exists, so a listener attached early hears
the first event announced. Nothing is replayed.

**`core.rpc`** — a question the owner answers. `authorize(target, actorId, operation)` is the one
rule such a handler applies: an operator reaches anything, anyone else only their own entity, and a
request with no acting player is an addon acting for itself.

**`core.db`** is this addon's `@bedrock-core/db`, keyed under its namespace, with the declaration
API (`schema`, the acceptors and combinators, the errors) re-exported so a collection is declared
from the runtime import alone. It is local: a peer reaches a document only through a method the
owner wrote.

**Every cross-addon feed is an `Announcement`** — one value under a `core-` key in the owner's
namespace, with `provide` / `own` / `of(ns)` / `namespaces` / `subscribe` and a guard on read.
`core.translations` is one over the bundle, its verbs at `i18n(ns)`; `core.features.flags`
announces every flag as one record under `core-feature/flags`; the shared shape sits at
`core.shared.shape`. `provideManifest`, `provideReference`, `referenceOf`, `bundleOf`,
`addonsWithGuides` and `has` are gone with it.

**The package exports what an addon writes against.** Registries are exported as types — the
runtime constructs them — and the schema, document and wire helpers (`flattenSchema`,
`defaultsOf`, `normalizeAgainst`, `coerce`, `CONFIG_COLLECTIONS`, `configMethod`,
`SHARED_SHAPE_KEY`, `validateManifest`, `addonNamespace`, `compareVersions`, `PROTOCOL_MIN` /
`PROTOCOL_MAX`) are no longer exported.

**`core.state` and `ScopedState` are removed.** A shared key covers every call they had; the raw
namespace, framework keys included, stays reachable at `core.node.state`.
6 changes: 6 additions & 0 deletions .changeset/sync-one-writer-per-namespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@bedrock-core/sync": minor
"@bedrock-core/server-runtime": minor
---

**Breaking.** A node writes one namespace, the one named by its id. `ownedNamespaces` and `strictOwnership` are removed from `createSync` / `SyncNode`, and `StateOptions` from the exports: every mirror only ever applied a namespace's writes from the node whose id it is, so the options could not widen that. A write to another node's namespace is dropped by every mirror and counted in `droppedForeign`, and a node answers snapshot requests for its own namespace.
16 changes: 16 additions & 0 deletions .changeset/sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@bedrock-core/sync": minor
---

**Owner-only state.** A mirror applies an entry for a namespace only from the namespace's owner —
the sending node for a delta, the recorded writer for a snapshot entry, so a snapshot relayed by a
third party still names the original writer. Anything else is dropped and counted in
`droppedForeign`. The rule holds for a node's own writes too, so a foreign write is visible locally
exactly when it is visible everywhere, which is never.

**`Events`, the subsystem for a happening.** `node.events.emit(name, payload)` broadcasts one
message; `on(namespace, name, handler)` subscribes to one sender's name. The namespace a handler
matches is the envelope's `src`, read from the transport rather than the payload, so a message
cannot claim to come from a node that did not send it. The sender dispatches to its own handlers
first, synchronously, before the message leaves; a handler that throws is caught and the others
still run. Nothing is stored and nothing is replayed.
49 changes: 49 additions & 0 deletions .changeset/tagged-wire-and-batching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
"@bedrock-core/sync": minor
"@bedrock-core/server-runtime": minor
---

Rework the script-event wire format, and negotiate the protocol per peer instead of demanding a
match.

A message now opens with a tag saying which shape follows: one envelope, a batch of envelopes, or
one frame of a chunked envelope. An envelope that fits in a message is sent whole instead of nested
inside a frame's `p` field, so it is no longer JSON-escaped to sit inside a JSON string — a small
message loses about a third of its length, and a round trip costs roughly half the CPU.

The outbound queue packs consecutive envelopes into one message up to the size cap. The engine
bounds script events per tick by count rather than by size, so a node that sends a burst in a single
tick — a run of `State.set` calls, a snapshot broadcast, an RPC fan-out — now spends a few of its
per-tick slots instead of one per envelope. Each addon has its own queue, so this packs one node's
own traffic and never several nodes' together.

Framing charges each character what JSON actually spends escaping it, rather than reserving two
characters for every one. Real payloads fill a frame instead of half of it: a 16KB envelope splits
into 10 frames where it previously took 18.

**`PROTOCOL_VERSION` is replaced by `PROTOCOL_MIN` and `PROTOCOL_MAX`.** A node advertises the
range it speaks in every announce and talks to each peer at the newest version both know, so a
world may hold addons built against different releases without partitioning. Gating on one exact
version would have made this bump — and every later one — a silent split: two meshes on a single
channel, each listing only its own half, each electing its own UI host, each timing out every RPC
to the other.

Consequently:

- A protocol-1 message is a bare frame with no tag, and is read as one. Announces and `whois` are
pinned to `PROTOCOL_MIN` so the message that establishes a version never assumes one.
- Broadcasts go out at the lowest version any live peer can read, and packing stops while a peer
that predates the batch tag is present. Both recover on their own once that peer expires.
- `PeerInfo` gains `protocol` and `caps`. Capabilities are advertised per node and narrowed by the
negotiated version, so a later addition can appear or degrade without a version bump.
- A node whose range does not overlap this build's is reported through
`Discovery.onIncompatible` / `Registry.onIncompatible` and listed by `Registry.incompatible()`,
with a warning naming both ranges. It is named rather than silently absent.
- `negotiateProtocol` and `capsFor` are exported for anyone writing an interoperating
implementation.

The support window is two versions wide. Raising `PROTOCOL_MIN` drops everything below it and is a
breaking change.

`MAX_MESSAGE`, the default per-message character budget, is now exported alongside the existing
`BusOptions.maxMessage` override.
5 changes: 0 additions & 5 deletions .copilot/config.json

This file was deleted.

Loading
Loading