Skip to content

Native lease context is selected through Statewire-Version: "2026-09-30".

Registered Statewire versions

VersionContract
2026-09-30Lease context. A re-sent sequence number at or below the watermark re-emits its answer.
2026-11-01Adds 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-02Every attach names its attacher; the host retires the attacher's earlier attaches before the hello.
  • TypeScript clients offer 2026-11-01 through 2026-11-02; the Python client still offers 2026-09-30.
  • Python and TypeScript hosts serve 2026-09-30 through 2026-11-02 by default. Both re-emit the answer of a re-sent sequence number under every version.

Contracts

  • Statewire-Version selects the Statewire wire contract.
  • Statewire-Protocol selects a set of additive application contracts covering state, lease context, commands, and results. Harness contributes harness-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-DD form.
  • 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 version on new builds and bump min-version when 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.

FieldStructured typeValueParameters
Statewire-VersionItemString: maximum supported versionOptional string min-version
Statewire-ProtocolList of ItemsProtocol token or *Named protocols require string version; optional string min-version and boolean optional
  • Both fields are required on attach.
  • An omitted min-version equals 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. optional defaults to false; bare ; optional is 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"; optional

Generic 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 omits record. A parent is always durable.
  • Clients offer parents only. The Statewire-Protocol response header lists parents; the hello lists parents and children, each child following its parent with the parent's version and the child's record.
  • Client validation accepts a hello entry named <selected parent>/<child> without an offer. Any other unoffered name is a protocol error.
  • protocol in statements, mounts, and answers carries the full name. main is 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 /stream and the initial WebSocket message retain their existing statement-submission role.
  • Lease-free HTTP clients can send statements through additional POST /stream attaches.

Context routing

  • Each ctx entry carries its selected protocol's full name, value, and optional meta.
  • 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 protocol string 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" in syn.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. dur acknowledgments 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, and remove.
  • Each mount declares its protocol; subsequent operations address its numeric doc ID.
  • 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 doc for document 0; 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 ops array 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 fieldMeaning
docOptional numeric wire identifier; omitted means document 0
protocolRequired string naming the document's application protocol
keyOptional array of strings addressing the document within its protocol; a leading "." denotes a relative key
mainOptional boolean identifying the protocol's main document; defaults to false
valueRequired initial document data
metaOptional 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 main and key.
  • 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-sdk document marked main: 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-status key 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, and remove.
  • 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.

CommandParamsEffect
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"}]}
codeRejection
no-documentpatch before any set
oversized-documentThe result exceeds MAX_EPHEMERAL_DOCUMENT_BYTES
invalid-patchMalformed 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], ns naming 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 in syn, and negotiation failures in fin.
  • HTTP uses the same successful syn and failed fin JSON shapes.
  • Each successful hello carries the selected values in the following fields.
FieldTypeMeaning
syn.versionStringSelected Statewire version
syn.protocolsArrayComplete selected application protocol set
syn.protocols[].nameStringSelected protocol identifier
syn.protocols[].versionStringSelected protocol version
syn.protocols[].seqIntegerLane watermark
syn.protocols[].recordString"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.
  • version and record are hello-only; later syn packets carry protocols[].{name, seq} watermarks and connection facts.
  • A successful hello reports only the selected versions.
  • syn.protocols includes 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 /stream and POST /stream carry negotiation fields in HTTP request headers.
  • HTTP frame bodies carry statement lanes; initialization headers belong to the HTTP request.
  • Successful attaches return 200 with Content-Type: text/event-stream.
  • The first SSE data event contains the common hello, including selection in syn.
  • Statewire-Lease, when supplied, binds subsequent POST /frames requests to the selected contracts and captured authorization.
  • Follow-up frame requests use their lease's selection throughout the attach.
  • Supplying negotiation fields on POST /frames does 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 subprotocol statewire.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 syn and 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

FailureHTTP before streamingfin.reasonfin.codeWebSocket close
Invalid negotiation input400protocol-errorinvalid-negotiation1008
Statewire ranges do not overlap406protocol-errorunsupported-version1008
Required protocols or their combination are unsupported406protocol-errorunsupported-protocol1008
  • HTTP negotiation failures return Content-Type: application/json with a fin object as their body.
  • WebSocket sends the same fin object before closing.
  • fin.status carries the corresponding HTTP status on either transport.
  • Both unsupported-version and unsupported-protocol failures include the server's supported ranges in fin.payload using 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 fin packets under the selected wire contract.
  • protocol-error stops automatic reconnection, except unsupported-version and unsupported-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 fin when 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 through command_up; application results travel toward older contracts through result_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,
                ),
            ),
        ),
    )
  • newer is the effective date of a breaking contract transition; older identifies 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_down is optional. With None (the default) the adapter leaves state unchanged; an attach whose adapter path has no state_down or documents_down reads 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, each newer at most version; otherwise registration throws.
  • The socket host lifts each command on the protocol's own lane through every adapter whose newer is above the attach's negotiated version, oldest first, before dispatch. Subprotocol commands pass unchanged.
  • A commandUp that 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_state initializes 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 syn fields, 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" });
  • protocol selects the main document and command proxy used by the client’s ordinary state and command helpers.
  • applicationProtocols offers every required and optional contract. "*" discovers the native set for an inspector.
  • wireVersion sets the independent Statewire range. Bundled transports provide its initial version.
  • selection exposes 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 its seq. A protocol outside the selection throws. options is { hold?: boolean; localMeta?: unknown }.
  • documents.get(protocol, key?, prefix?) reads a named document or the protocol’s main document. list() exposes the mounted table and subscribe(listener) observes document changes.
  • storage.documents mounts a StatewireDocuments.Storage resource. Its synchronous load(identity) and save(identity, {version, value}) use [namespace, protocol, ...resolvedKey] identities.
  • storage.session mounts a StatewireClient.SessionStorage resource holding {clientId, lanes: {[protocol]: {nextSeq, commands, outbox?, removed?, verdicts?, inbox?}}} (storage format 5; formats 1 to 4 upgrade on load). Its optional migrate(record, current) callback upgrades incompatible stored commands or returns null to 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.state as its main document. mount(protocol, value, key=..., main=...) returns a reactive state proxy; unmount(proxy) releases it.
  • @command(..., protocol=...) routes a handler; omitted protocol uses the first registration.
  • ctx.caller carries client_id, auth, and is_connected. ctx.attach is 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; reading ctx.attach raises.
  • synced_document(protocol) (Python) / syncedDocument(protocol) (TS) installs the set and patch handlers of a synced document.
  • send(method, *params, protocol=...) mints seq in that protocol's lane. A protocol outside the selection raises.
  • Statewire.Adapter.state_down converts the main document; None leaves it unchanged. Optional documents_down transforms the protocol’s complete dict[int, Document], including keys and main associations.
  • command_up, result_down, error_down, and commands_down cover commands, results, application rejection data, and discovery. Omitted converters preserve their input.
  • Statewire.Protocol.requires maps dependency names to Statewire.VersionRange values.
  • TypeScript native hosts accept {protocol: Harness.protocol} in their host options. Python hosts provide the compatibility adapter registry.