Local endpoint contract, generation 1

Local CLI, UI, metadata supervision, autostart readiness and doctor negotiate a stable contract on the existing Unix socket. Package versions are diagnostic; they are not compared for compatibility. The framing remains four-byte big-endian lengths and bounded MessagePack named maps, msgpack-map-v1.

The first envelope contains local_hello:

{"message":{"type":"local_hello","generation":1,"codec":"msgpack-map-v1","client_version":"0.38.0","required":["local.v1"],"optional":[],"mode":"control"}}

Interactive mode retains the existing ClientMode representation with terminal size and optional selector. The reply is local_welcome, with generation, codec, server_version, capabilities, selected and extension_catalog. Control selection is null; interactive selection is required. The catalog is always present. Replies echo the handshake request ID (or omit it when the request did). Handshakes are strictly decoded, including rejection of trailing bytes and unknown DTO fields, and validated before any terminal is leased.

local.v1 is required and freezes the complete current local contract: all allowlisted requests, responses and their transitive payloads, defaults, bounds, correlation, ordering and behavior. This includes contextual targeting, agent automation, extension reload, shutdown, nested workspace metadata, separate Kitty images, scroll deltas and chunked screens. The exhaustive allowlists in src/protocol/local.rs force an explicit decision when adding message variants. They do not replace mode, resource-scope or other operational checks.

Unknown optional capabilities are ignored; unknown required capabilities fail. Names, version strings and capability lists use the same bounded validation as the remote contract. Generation, codec and capability errors use endpoint_error. A rejected connection closes without changing daemon lifecycle or leasing a terminal. Normal operations never retry using a version claimed by the peer.

Evolution

This first step establishes the stable baseline, not a generic translation engine or an unlimited promise to support every past release. Compatible app releases retain generation 1 and local.v1, so updating a client alone need not interrupt running terminals.

Only local.v1 is selectable today. The first optional local feature must extend the capability representation, selection validation and boundary allowlists; the handshake’s optional list is not permission to emit unrecognized behavior.

Transition from release-number locking

The legacy hello / welcome path still requires an exact package-minor PROTOCOL_VERSION match, for older clients from the current release. Existing daemons predating local_hello cannot negotiate this contract: they need one explicit shutdown and restart. Ordinary operations do not silently fall back. Explicit daemon shutdown retains legacy handshake / daemon-binary recovery; --force remains the last-resort recovery path. The navigator’s explicit unsafe legacy mismatch escape hatch does not override negotiated generation or capability failures.