Native lease context is selected through Statewire-Version: "2026-09-30".
Registered Statewire versions
| Version | Contract |
|---|---|
2026-09-30 | Lease context. A re-sent sequence number at or below the watermark re-emits its answer. |
2026-11-01 | Adds the attach results cursor: the hello carries the answers above it. A re-sent sequence number at or below the watermark still re-emits its answer. |
2026-11-02 | Every attach names its attacher; the host retires the attacher's earlier attaches before the hello. |
- TypeScript clients offer
2026-11-01through2026-11-02; the Python client still offers2026-09-30. - Python and TypeScript hosts serve
2026-09-30through2026-11-02by default. Both re-emit the answer of a re-sent sequence number under every version.
Contracts
Statewire-Versionselects the Statewire wire contract.Statewire-Protocolselects a set of additive application contracts covering state, lease context, commands, and results. Harness contributesharness-sdk.- Statewire and each named application protocol have independent inclusive support ranges.
- The client's transport supplies its Statewire range; application integrations supply their named protocol ranges.
- A client's range describes builds compatible with its single current implementation.
- A server's range covers its native contract and the older contracts served by its adapters.
- Each advertised application contract must be encodable over the host's advertised Statewire range.
- Server adapters extend support to older application contracts.
- A successful attach fixes one Statewire version and a set of named application protocol/version selections.
- All selected protocols' guarantees hold together. Extensions add compatible fields, commands, or behavior.
- A fork that invalidates a protocol's guarantees advertises its own identifier instead of the incompatible protocol.
Versions and ranges
- Versions are calendar dates in fixed-width
YYYY-MM-DDform. - Dates are validated and ordered chronologically. Lexicographic order of validated identifiers gives the same ordering.
- Version suffixes are reserved; this revision accepts plain dates.
- A range includes both endpoints and every intermediate date. The represented contract changes at its registered effective dates.
- Clients bump
versionon new builds and bumpmin-versionwhen a compatibility break raises their compatibility floor. - Compatible builds retain their existing minimum.
- Build versions are bundled metadata, rather than the wall-clock date of an attach.
- A client uses one implementation throughout its advertised range; the server owns compatibility conversion.
- New features can require a newer server within an otherwise compatible range. Developers coordinate deployment or tighten the offered minimum when those features are required.
Request fields
Both fields use HTTP Structured Fields, RFC 9651.
| Field | Structured type | Value | Parameters |
|---|---|---|---|
Statewire-Version | Item | String: maximum supported version | Optional string min-version |
Statewire-Protocol | List of Items | Protocol token or * | Named protocols require string version; optional string min-version and boolean optional |
- Both fields are required on attach.
- An omitted
min-versionequals the offered maximum: an exact-version offer. - Minimums must be at or below their maximums.
- Protocol identifiers are case-sensitive tokens; header names are case-insensitive.
- Commas separate cumulative protocol requirements; parameters belong to the preceding item.
- Each named protocol appears at most once.
- Protocols are required by default.
optionaldefaults to false; bare; optionalis equivalent to; optional=?1. - An unavailable or incompatible required protocol rejects the attach. An unavailable or incompatible optional protocol is omitted from the selected set.
*is a standalone item with zero parameters and selects the server's native protocol set and versions.- The protocol list must contain at least one item; a request containing only optional protocols may select an empty set when the host supports that composition.
- Malformed Structured Fields, wrong value types, missing required values, reversed ranges, and unsupported parameters are invalid negotiation input.
- Required values are validated before application state is sent or commands are admitted.
Statewire-Version: "2026-09-13"
Statewire-Protocol: harness-sdk; version="2026-09-13"Additive protocols, with optional Acme functionality:
Statewire-Version: "2026-09-13"
Statewire-Protocol: harness-sdk; version="2026-09-13", acme-chat; version="2026-09-05"; min-version="2026-09-01"; optionalGeneric inspector:
Statewire-Version: "2026-09-13"
Statewire-Protocol: *Selection
For client interval [Cmin, Cmax] and server interval [Smin, Smax]:
lower = max(Cmin, Smin)
upper = min(Cmax, Smax)
match = lower <= upper
selected = upper, when match is true- Select the Statewire version from the Statewire range intersection.
- Select a supported combination satisfying every required protocol range and the host's declared protocol dependencies.
- Select the newest common protocol versions subject to those composition constraints.
- Include compatible optional protocols where the composition supports them. Optional protocols must not prevent successful negotiation of the required protocols.
- A wildcard selects the native protocol set and versions.
- A missing Statewire intersection is an unsupported Statewire version.
- An unsatisfied required protocol or incompatible required combination is an unsupported application protocol or application version.
- Selection completes before activation of a compatibility view or admission of any initial statements.
- Client validation confirms the returned Statewire version, every required protocol, and every included optional protocol satisfy the offers. Named selections are unique and belong to the requested set; a wildcard accepts the server's native set.
- The server serves the selected contracts; the client validates that they lie within the range compatible with its current implementation.
- The selected versions are available to application code for feature checks.
- Clients check the selected set before using optional functionality.
Subprotocols
- A protocol registration lists subprotocols by child name. The wire name of a child is
<parent>/<child>. - Selecting a parent selects every child at the parent's version. Children have no range of their own.
- Each child declares its
record:"ephemeral"marks an ephemeral lane; a durable child omitsrecord. A parent is always durable. - Clients offer parents only. The
Statewire-Protocolresponse header lists parents; the hello lists parents and children, each child following its parent with the parent's version and the child'srecord. - Client validation accepts a hello entry named
<selected parent>/<child>without an offer. Any other unoffered name is a protocol error. protocolin statements, mounts, and answers carries the full name.mainis unique per subprotocol.- Compatibility adapters on the parent convert the children's documents and commands.
{"syn": {"version": "2026-09-30", "protocols": [
{"name": "harness-sdk", "version": "2026-09-13", "seq": 41},
{"name": "harness-sdk/interest", "version": "2026-09-13", "record": "ephemeral", "seq": 0}]}}Python registration:
Statewire.Protocol(
name="harness-sdk",
version="2026-09-13",
subprotocols=(Statewire.Subprotocol("interest", record="ephemeral"),),
)TypeScript registration:
const protocol: StatewireProtocol.Definition = {
name: "harness-sdk",
version: "2026-09-13",
subprotocols: [{ name: "interest", record: "ephemeral" }],
};Initial statements
- An attach may include
ctx,cmd, or both with exact-version offers, version ranges, or a wildcard application offer. - The server selects the contracts, parses and validates the whole frame and all context candidates, installs lease context, then admits the initial statements under those contracts. Ephemeral-lane statements are admitted before the snapshot; durable-lane statements run after the hello (attach statements).
- A supported negotiation proceeds to ordinary statement handling, including when the client advertises a newer maximum than the server supports.
- Commands using newer methods or arguments may receive normal command rejections under the selected contract.
- Initial commands depending on an omitted optional protocol may fail. Clients requiring that functionality mark the protocol required.
- Invalid wire frames use the frame violation rules.
- Command rejection preserves the selected connection contract and the ordinary sequence/receipt rules.
- Clients may submit initial statements before receiving the selected versions; developers account for features that require a newer server.
- Send-and-attach clients tighten the Statewire and/or application range for that attach when its initial statements require a newer server. An exact-version offer is the narrowest range.
POST /streamand the initial WebSocket message retain their existing statement-submission role.- Lease-free HTTP clients can send statements through additional
POST /streamattaches.
Context routing
- Each
ctxentry carries its selected protocol's full name,value, and optionalmeta. - The target protocol owns the context schema and command-handler access. Its context object is scoped to the admitting attach.
- Context is available for every selected protocol, independently of the command lane's durable or ephemeral record.
- Context routing does not introduce another application protocol or command lane.
- Applications may put interests, ambient model context, or other lease-scoped data in their context object.
- Hosts decode metadata before application validation and deliver context-change notifications for accepted replacements.
- Native context support is negotiated through
Statewire-Version. The selected application contract defines its context schema. Clients requiring native context offer a wire minimum that supports it.
Command routing
- Each command statement carries a required
protocolstring naming its target application protocol. - Protocol names are stable across reconnects and persisted commands.
- Each selected protocol owns one lane: its own sequence space, admission watermark, and replay queue.
record: "ephemeral"insyn.protocols[]marks a lane that ends with the attach. - Command identity is the client id, protocol, and sequence number. Admission records the selected contract.
- Command answers correlate by
(protocol, seq). The client processes application results only for commands it tracks locally and ignores results for unknown commands. - Answer payloads use the tracked command's protocol.
duracknowledgments advance their lane's watermark independently of result handling.
{
"cmd": [
{"seq": 12, "protocol": "harness-sdk", "method": "example", "params": []},
{"seq": 13, "protocol": "acme-chat", "method": "setTheme", "params": ["dark"]}
]
}Documents and state
- State uses the document operations
mount,unmount,add,replace, andremove. - Each mount declares its
protocol; subsequent operations address its numericdocID. - The mount's protocol must be selected for the attach. Its contract version comes from
syn.protocols. - State document IDs are unique across an attach's state document table, spanning all selected protocols.
- Producers must omit
docfor document0; the field is emitted only for nonzero document IDs, including mounts and unmounts. - Each mounted document owns its state root; mutation paths are relative to that root.
- State packets retain a single ordered
opsarray spanning documents and protocols. Operations apply before accompanying command answers, with changes published together. - Negotiated connections initialize state with explicit mounts, including single-protocol connections.
- The hello mounts the attach's document view beside
syn.
| Mount field | Meaning |
|---|---|
doc | Optional numeric wire identifier; omitted means document 0 |
protocol | Required string naming the document's application protocol |
key | Optional array of strings addressing the document within its protocol; a leading "." denotes a relative key |
main | Optional boolean identifying the protocol's main document; defaults to false |
value | Required initial document data |
meta | Optional document value metadata |
- Keys address mounted documents for local lookup and reading. Absolute or canonically resolved keys additionally identify shared data across connections.
- Shared document identity is
(protocol, absolute key)within a storage adapter's cache namespace. The same key under a different protocol identifies different data. - Keys support coordination and optimistic updates across connections owning representations of the same logical document.
- An omitted key leaves the document addressable through its wire ID and any main designation.
key: []is an ordinary absolute key. - Each protocol has at most one mounted main document within a document table. A document may declare both
mainandkey. - Main-document designation is local to the document table; it is independent of shared document identity.
- Harness exposes its main client state through the
harness-sdkdocument markedmain: true. - Mount ownership and main designation remain fixed until unmount. Unmount releases the document ID and its main designation.
{
"ops": [
{
"op": "mount",
"protocol": "harness-sdk",
"main": true,
"value": {"status": "idle"}
},
{
"op": "mount",
"doc": 1,
"protocol": "thread-status",
"key": ["thread-123", "status"],
"value": "idle"
}
]
}- This example assumes both protocols were negotiated. A thread and a thread list may mount the same
thread-statuskey over different connections with different numeric document IDs.
{
"ops": [
{"op": "replace", "doc": 1, "path": [], "value": "running"}
]
}Document views
- Documents mounted by the host are global: one value per
(protocol, key). - Each attach has a view: the main documents of its selected durable protocols, every document of its ephemeral protocols, and the shared keyed documents the host included for it.
- Ephemeral protocols' documents are created per attach by command handlers and end with the attach. They may carry keys.
- The hello mounts the whole view. Later flushes diff the previous and current view per attach and emit
mount,unmount,add,replace, andremove. - The client document cache treats equal
(protocol, key)on different attaches as one identity. - Each ephemeral document's encoded value is bounded by
MAX_EPHEMERAL_DOCUMENT_BYTES(64 KiB).
Synced documents
An ephemeral subprotocol may expose its main document as a client-owned synced document through two commands.
| Command | Params | Effect |
|---|---|---|
set | [value] | Replaces the document root; mounts the document on the first call |
patch | [ops] | Applies Statepatch operations to the document |
{"protocol": "acme-chat/preferences", "method": "set", "params": [{"locale": "de"}]}code | Rejection |
|---|---|
no-document | patch before any set |
oversized-document | The result exceeds MAX_EPHEMERAL_DOCUMENT_BYTES |
invalid-patch | Malformed operations |
- Handlers read the document through the admitting attach.
Relative keys and message documents
- A leading
"."identifies a key relative to the protocol's local document scope. Lookup is scoped to the attach and protocol. - Relative keys identify documents for mounting, lookup, and reading even when a canonical prefix is unavailable.
- When a canonical prefix is available for the protocol's local root, resolve the key by replacing its leading
"."with that prefix. Relative and absolute forms then identify the same shared document. - Unresolved relative keys remain local to their document scope. Identical unresolved keys on different state connections remain distinct, including within a shared cache namespace.
- The application integration resolves the thread-relative root to
[threadId]when the stable thread identity is available. - Harness message IDs are unique within a thread. A message's absolute key is
[threadId, ns, messageId],nsnaming the thread namespace; its thread-relative key is[".", ns, messageId]. - Messages may be mounted as separate documents outside the main state and read through their keys. Mounting and reading them is independent of establishing cross-connection cache identity.
- A thread without an ID uses relative message keys until its canonical identity is available.
- Canonical resolution preserves the mounted document and its numeric wire ID.
{
"ops": [
{
"op": "mount",
"doc": 1,
"protocol": "harness-sdk",
"key": [".", "main", "m1"],
"value": {}
}
]
}- With prefix
["thread-123"], this key resolves to["thread-123", "main", "m1"].
Client cache namespaces
- Storage adapters may expose optional client-configured cache namespaces.
- Effective shared cache identity is
(cache namespace, protocol, absolute key), after resolving any relative key. An omitted namespace uses the storage adapter's default scope. - Connections sharing a cache namespace can coordinate canonically keyed documents. Separate namespaces isolate identical protocol/key pairs from independent backends or data scopes.
- Namespace configuration belongs to the client storage adapter; negotiation and mount payloads carry only the wire protocol and document identity fields defined above.
Fixed bootstrap and successful hello
- WebSocket negotiates the subprotocol
statewire.v1. - The bootstrap fixes the initial JSON object, WebSocket initialization
headers, successful negotiation fields insyn, and negotiation failures infin. - HTTP uses the same successful
synand failedfinJSON shapes. - Each successful hello carries the selected values in the following fields.
| Field | Type | Meaning |
|---|---|---|
syn.version | String | Selected Statewire version |
syn.protocols | Array | Complete selected application protocol set |
syn.protocols[].name | String | Selected protocol identifier |
syn.protocols[].version | String | Selected protocol version |
syn.protocols[].seq | Integer | Lane watermark |
syn.protocols[].record | String | "ephemeral" on an ephemeral lane; absent on a durable lane |
{
"syn": {
"version": "2026-09-13",
"protocols": [
{"name": "harness-sdk", "version": "2026-09-13", "seq": 0},
{"name": "harness-sdk/interest", "version": "2026-09-13", "record": "ephemeral", "seq": 0}
]
},
"ops": [{"op": "mount", "protocol": "harness-sdk", "main": true, "value": {}}]
}- The bootstrap decoder parses JSON and validates the selection before the client's ordinary decoder processes the remaining hello fields and snapshot.
- JSON property order is immaterial.
- Missing or invalid selection fields are protocol errors.
versionandrecordare hello-only; latersynpackets carryprotocols[].{name, seq}watermarks and connection facts.- A successful hello reports only the selected versions.
syn.protocolsincludes required and accepted optional protocols and their subprotocols. Each parent's children follow it; order carries no other meaning.- HTTP selection is carried in
syn; successful response headers carry transport metadata such as content type and lease. - The client becomes connected after validating the hello and applying its snapshot.
StatewireHttp
GET /streamandPOST /streamcarry negotiation fields in HTTP request headers.- HTTP frame bodies carry statement lanes; initialization headers belong to the HTTP request.
- Successful attaches return
200withContent-Type: text/event-stream. - The first SSE data event contains the common hello, including selection in
syn. Statewire-Lease, when supplied, binds subsequentPOST /framesrequests to the selected contracts and captured authorization.- Follow-up frame requests use their lease's selection throughout the attach.
- Supplying negotiation fields on
POST /framesdoes not override the lease; only a new attach selects new contracts. - Hosts allowing cross-origin browser access allow the negotiation request headers; lease exposure follows the existing HTTP transport contract.
GET /stream
Statewire-Client-Id: 0123456789abcdef0123456789abcdef
Statewire-Version: "2026-09-13"
Statewire-Protocol: harness-sdk; version="2026-09-13"200 OK
Content-Type: text/event-stream
Statewire-Lease: example-lease
data: {"syn":{"version":"2026-09-13","protocols":[{"name":"harness-sdk","version":"2026-09-13","seq":0}]},"ops":[{"op":"mount","protocol":"harness-sdk","main":true,"value":{}}]}
StatewireWebsocket
- Open
/ws?client=<id>with subprotocolstatewire.v1. - The first client text message carries
headers. - Handshake headers take precedence over initialization-message headers. Duplicate initialization header names are invalid.
- The server's first successful application packet is the common hello with selection in
synand the snapshot beside it. - The socket binds subsequent frames to the selected contracts and captured authorization.
- Later frames carry statement lanes; negotiation remains fixed until detach.
- Existing initialization timeouts, heartbeats, and transport limits continue to apply.
{
"headers": {
"Statewire-Version": "\"2026-09-13\"",
"Statewire-Protocol": "harness-sdk; version=\"2026-09-13\""
}
}Negotiation failures
| Failure | HTTP before streaming | fin.reason | fin.code | WebSocket close |
|---|---|---|---|---|
| Invalid negotiation input | 400 | protocol-error | invalid-negotiation | 1008 |
| Statewire ranges do not overlap | 406 | protocol-error | unsupported-version | 1008 |
| Required protocols or their combination are unsupported | 406 | protocol-error | unsupported-protocol | 1008 |
- HTTP negotiation failures return
Content-Type: application/jsonwith afinobject as their body. - WebSocket sends the same
finobject before closing. fin.statuscarries the corresponding HTTP status on either transport.- Both
unsupported-versionandunsupported-protocolfailures include the server's supported ranges infin.payloadusing the shape below. - Advertised per-protocol ranges remain subject to the server's composition constraints.
- Authentication and authorization precede disclosure of supported application contracts; existing access-denial behavior continues to apply.
- Negotiation failures leave command admission watermarks and application state unchanged.
- After HTTP streaming begins, failures travel as stream
finpackets under the selected wire contract. protocol-errorstops automatic reconnection, exceptunsupported-versionandunsupported-protocol: the TypeScript and Python clients retry those with reconnect backoff for up to 10 minutes, then stop. Explicitly changing configuration can start a new attempt.- HTTP clients parse a structured bootstrap
finwhen present and retain ordinary HTTP-error handling for intermediary responses.
{
"fin": {
"reason": "protocol-error",
"code": "unsupported-protocol",
"status": 406,
"payload": {
"version": "2026-09-13",
"minVersion": "2026-09-13",
"protocols": [
{"name": "harness-sdk", "version": "2026-09-13", "minVersion": "2026-08-15"}
]
}
}
}Application adapters
- The Python host registers named protocols, each with its native version, minimum supported version, and adapters.
- The Statewire implementation declares its own wire-version range independently.
- Registration declares supported protocol compositions and version dependencies.
- State projections and command transformations preserve every selected protocol's guarantees. Overlapping transformations require explicit composition; ambiguous compositions fail registration.
- Application adapters register conversion functions at breaking version boundaries, with explicit older and newer endpoints.
- Registration validates ordering, contiguous advertised support, and an unambiguous conversion path to the native contract.
- Native handlers operate against the native state and command contract.
- State travels toward older contracts through
state_down; commands travel toward the native contract throughcommand_up; application results travel toward older contracts throughresult_down. - State projections cover the protocol's documents and their main/key associations. The host assigns attach-local document IDs to the projected documents.
- Application error payloads follow the same selected application contract.
- Application command discovery reflects the selected application contract.
- Converters are deterministic, preserve their input values, and perform application-data transformations without external side effects.
- Each command conversion preserves the statement's identity and produces one command invocation.
- Wire encoding, admission, sequence numbers, durability, and delivery remain Statewire responsibilities.
- Adapter definitions are static; the host owns live compatibility views.
Python registration:
class Thread(Statewire):
protocols = (
Statewire.Protocol(
name="harness-sdk",
version="2026-09-13",
min_version="2026-08-01",
adapters=(
Statewire.Adapter(
older="2026-08-01",
newer="2026-09-01",
state_down=state_before_september,
command_up=command_to_september,
result_down=result_before_september,
),
),
),
)neweris the effective date of a breaking contract transition;olderidentifies the preceding contract revision.- In this example, versions from August 1 through August 31 use the preceding state projection; September versions use the native projection.
- Compatible builds within a revision retain its conversion path.
state_downis optional. WithNone(the default) the adapter leaves state unchanged; an attach whose adapter path has nostate_downordocuments_downreads the native state.
TypeScript registration (command adapters only):
type Definition = Range & {
name: string;
requires?: Readonly<Record<string, Range>>;
subprotocols?: readonly { name: string; record?: "ephemeral" }[];
adapters?: readonly StatewireProtocol.Adapter[];
};
type Adapter = {
older: string;
newer: string;
commandUp: (command: Command) => Command;
};
type Command = { method: string; params: readonly unknown[] };- The adapters must form a contiguous chain starting at
minVersion, eachnewerat mostversion; otherwise registration throws. - The socket host lifts each command on the protocol's own lane through every adapter whose
neweris above the attach's negotiated version, oldest first, before dispatch. Subprotocol commands pass unchanged. - A
commandUpthat throws, or returns anything but one{ method, params }command, rejects the statement. - State, results, and discovery stay native.
Replay invariants
- Selected protocol sets and versions belong to attaches. Concurrent attaches and reconnects may select different sets and versions for the same client id.
- The client id retains each durable lane's sequence space, command execution identity, and retained answers. Ephemeral lanes belong to their attach.
- Document IDs are reassigned through mounts on each attach.
- Admission binds each new command to the submitting attach's selected protocol set and versions.
- Duplicate detection precedes application command conversion and dispatch; an admitted sequence number retains its original execution and contract.
- Native command answers are encoded separately for each receiving attach's selected application contracts.
- Retried statements preserve their original application meaning and sequence identity.
- Client storage records serialized queued commands per durable lane and the Statewire and named application versions/ranges used to produce them.
- Before replay, the client checks stored commands against its current implementation and required application contracts. Applications may migrate incompatible stored commands before sending them or discard those local entries.
- Stored-command migrations preserve command meaning and sequence identity. Compatible entries retain their serialized application values.
- Discarding local entries relinquishes their replay; it does not cancel commands already accepted by the server. Queue reconciliation preserves admission watermarks and sequence continuity.
- Live wire compatibility conversion remains a server responsibility; storage migration is an application upgrade policy.
- Server-admitted pending statements and retained command answers carry enough source-contract metadata to select their conversions after a host restart.
- Durable queued commands remain executable through the advertised compatibility paths after a server upgrade.
- Wire encodings may differ across attaches; command identity and application values retain their source-contract interpretation.
- Restoring a pending command must use its recorded contract, rather than the current server contract by assumption.
- Adapter changes must preserve the conversion of previously admitted commands.
- In-flight command work retains the conversion dependencies needed to return its answers.
Lazy compatibility views
- Each host instance shares one view per required state-conversion stage within a compatible protocol composition.
- Protocol selections with the same composed projection path share the projected state.
- Native application clients use the native state.
- The first older-version attach activates its path; intermediate views stay active while downstream views depend on them.
- View initialization and subscription use one consistent native-state boundary.
state_down(newer_state) -> older_stateinitializes each view and projects each committed batch while active.- Statewire diffs the new projection against the previous projection to produce that version's updates.
- Projection completes before corresponding command-applied notifications are delivered. An unchanged projection still delivers its applicable command notifications.
- Views are released after their attaches, downstream views, and in-flight work release their dependencies.
- A projection failure fails affected compatibility attaches; the host must never substitute native state for a failed projection.
Delivery scope
- Python host implementation first; matching transport clients and inspector support accompany adoption.
- Bootstrap syntax and selection decoding are shared across HTTP and WebSocket.
- Initial adoption upgrades clients and hosts together; required request fields, selected-version
synfields, command protocol routing, and protocol-bearing state mounts define the cutover. - Matching client and host versions are required for the initial cutover.
Client configuration
const client = new StatewireClient({
protocol: "harness-sdk",
applicationProtocols: [
{ name: "harness-sdk", version: "2026-09-30", minVersion: "2026-09-30" },
{ name: "acme-chat", version: "2026-09-13", optional: true },
],
transport: StatewireHttp({ url: "/threads/t1" }),
keyPrefixes: { "harness-sdk": ["t1"] },
});
client.selection;
client.documents?.get("harness-sdk");
client.documents?.get("harness-sdk", [".", "main", "m1"]);
client.invoke("acme-chat", "setTheme", ["dark"]);
client.setContext("acme-chat", { locale: "de" });protocolselects the main document and command proxy used by the client’s ordinary state and command helpers.applicationProtocolsoffers every required and optional contract."*"discovers the native set for an inspector.wireVersionsets the independent Statewire range. Bundled transports provide its initial version.selectionexposes the accepted versions after hello, as{version, protocols: [{name, version, record?}]}.invoke(protocol, method, params, options?)queues the command in that protocol's lane; a flush mints itsseq. A protocol outside the selection throws.optionsis{ hold?: boolean; localMeta?: unknown }.documents.get(protocol, key?, prefix?)reads a named document or the protocol’s main document.list()exposes the mounted table andsubscribe(listener)observes document changes.storage.documentsmounts aStatewireDocuments.Storageresource. Its synchronousload(identity)andsave(identity, {version, value})use[namespace, protocol, ...resolvedKey]identities.storage.sessionmounts aStatewireClient.SessionStorageresource holding{clientId, lanes: {[protocol]: {nextSeq, commands, outbox?, removed?, verdicts?, inbox?}}}(storage format 5; formats 1 to 4 upgrade on load). Its optionalmigrate(record, current)callback upgrades incompatible stored commands or returnsnullto discard the saved session and start a new client identity.LocalStorageSession({principal, threadId, migrate})forwards the same migration policy. Use the authenticated account's stable principal and remount when the account changes.
from statewire import Statewire, command
from statewire.client import StatewireClient
from statewire.protocol import Offer, VersionRange
from harness_sdk import HARNESS_PROTOCOL
class Thread(Statewire):
protocols = (HARNESS_PROTOCOL, Statewire.Protocol("acme-chat"))
async def lifespan(self):
self.state = {"status": "idle"}
self.message = self.mount(
"harness-sdk", {"text": "Hello"}, key=[".", "main", "m1"]
)
yield
@command("setTheme", protocol="acme-chat")
async def set_theme(self, theme, *, ctx):
return {"theme": theme, "locale": ctx.context["locale"]}
async with StatewireClient.sse(
"http://localhost:8000/threads/t1",
protocol="harness-sdk",
application_protocols=(
Offer("harness-sdk", VersionRange("2026-09-30")),
Offer("acme-chat", VersionRange("2026-09-13")),
),
) as client:
client.set_context("acme-chat", {"locale": "de"})
await client.send("setTheme", "dark", protocol="acme-chat")
message = client.document("harness-sdk", (".", "main", "m1"))- Python’s first registered protocol owns
self.stateas its main document.mount(protocol, value, key=..., main=...)returns a reactive state proxy;unmount(proxy)releases it. @command(..., protocol=...)routes a handler; omittedprotocoluses the first registration.ctx.callercarriesclient_id,auth, andis_connected.ctx.attachis bound to the admitting attach:document(protocol, key=None),mount(protocol, value, key=None, main=False)on an ephemeral protocol,unmount(protocol, key=None),include(protocol, key),exclude(protocol, key). In-process dispatch has no attach; readingctx.attachraises.synced_document(protocol)(Python) /syncedDocument(protocol)(TS) installs thesetandpatchhandlers of a synced document.send(method, *params, protocol=...)mintsseqin that protocol's lane. A protocol outside the selection raises.Statewire.Adapter.state_downconverts the main document;Noneleaves it unchanged. Optionaldocuments_downtransforms the protocol’s completedict[int, Document], including keys and main associations.command_up,result_down,error_down, andcommands_downcover commands, results, application rejection data, and discovery. Omitted converters preserve their input.Statewire.Protocol.requiresmaps dependency names toStatewire.VersionRangevalues.- TypeScript native hosts accept
{protocol: Harness.protocol}in their host options. Python hosts provide the compatibility adapter registry.