# Changelog

All notable **user-facing** changes to `spt` — what a person running the CLI
notices or does differently. The `## [<version>]` section of each release
becomes that release's GitHub Release notes verbatim (see
`docs/RELEASE-RUNBOOK.md`). This project follows
[Keep a Changelog](https://keepachangelog.com) and semantic versioning
(pre-1.0: breaking changes bump the minor).

## [Unreleased]

## [0.16.0] - 2026-06-25

A minor release adding a one-lever adapter-update arc (a delegated post-step), a global `--json` for status queries, an incremental digest cursor, a persistent `spt rc` identity marker, and manifest substitution primitives — plus the removal of `spt send --reply-to`.

### Added

- **Composite adapter update — `[update.post]`.** A manifest can declare a delegated post-step that `spt adapter update` runs *after* the primary update avenue resolves, so one command both pulls the adapter's `.spt` (e.g. from a GitHub release) **and** runs an in-harness sync. The post-step runs unconditionally (even on an up-to-date no-op), reads a published JSON line on stdin describing the update, and its stdout decides the post-update notice. A post-step failure warns and falls back — it never rolls back the committed pull.
- **Global `--json` for status queries.** `endpoint list`/`whoami`, `daemon status`, `subnet status`/`show-code`, `endpoint description`/`role`, `adapter list`/`version`, `notif list`, `grant list`, `access list`, `shell list`, and `how-to` now accept a global `--json` flag emitting stable, explicit fields for scripted consumption. (Action commands ignore it.)
- **`spt endpoint digest` incremental cursor.** `--json` output gains `--last <N>` (the last N turns; `--last 1` is the latest turn), a stable per-entry `seq` that survives live re-projection and window slides, `--after <seq>` (only what is newer, with a full-refresh signal if the cursor fell out of the window), a `partial` flag on the in-progress turn, and a per-entry `ts` — so a consumer can process turn-ends incrementally instead of re-reading the whole window.
- **`spt rc` identity marker.** An attached controller now shows a persistent top status row — right-aligned `SUBNET : ENDPOINT_ID @ NODE` in cyan — so you always know which endpoint you are driving. It re-asserts across alt-screen, resize, and scroll-region resets.
- **Manifest substitution primitives.** Two adapter-static substitution keys — `{adapter_dir}` (the adapter's install dir, which survives updates) and `{adapter_name}` — are available wherever command/string substitution runs, and `[strings]` values are now substituted at `get-string` read time. This lets an adapter resolve a path to its own packed binary without spt-core ever executing it.
- **`[message-idle-translation-binary]` takes a `command`.** The idle-delivery translation binary can be declared with a `command` (program token plus args, with adapter-static substitution) instead of the bare `path`, so it can be invoked as a subcommand of a consolidated adapter binary. The spawn and stdin/stdout protocol are unchanged.
- **Empty-scope creation flow.** Running `spt endpoint run` (or bare `spt`) on a node with no endpoints at all now opens directly on the adapter-creation screen instead of an empty picker.

### Changed

- **`[message-idle-translation-binary].path` is deprecated** in favor of `command`. It still parses (and warns at registration); exactly one of `path`/`command` may be set.

### Removed

- **`spt send --reply-to` is removed.** The send target is now a required positional argument; reply correlation rides the structural `from` on the message envelope. (The flag was a target-fallback nicety with no wire effect.)

## [0.15.0] - 2026-06-24

A minor release adding per-message delivery controls to `spt send`, an opaque metadata payload, a resume-context pull command, and a Windows console-flash fix.

### Added

- **`spt send` delivery-window controls.** `--idle-only` delivers a message only while the target is idle (the idle/wake window), holding until then; `--active-only` delivers only through the target's own poll, without ever waking an idle target. (`--active-only` replaces the old `--deferred`, which still works as a hidden back-compat alias.)
- **`spt send` channel controls.** `--prefer-native` delivers through the target's translation binary when one is running and falls back to the normal channel otherwise; `--force-native` delivers only through the translation binary, with no fallback or spooling (reported undelivered if none is running).
- **`spt send --ephemeral`** drops a message that can't be delivered to a translation-binary target within its window, or that expires (TTL), instead of spooling it. (A harness-relay target with no live listener still spools — that evaporation case lands in a later release.)
- **`spt send --json-payload <JSON>`** attaches an opaque JSON metadata blob alongside the message body, carried verbatim for the receiving adapter to parse (it does not replace the body).
- **`spt api psyche-download <id>`** pulls an agent's resume context for an adapter to restore at session start, appending any not-yet-synthesized commune/signoff updates.

### Fixed

- **Inbound messages are no longer silently lost if the delivery worker faults mid-handoff** — they re-spool and surface on the next poll (closes the transient gap left after the v0.14.3 raw-inject removal).
- **The spt-hosted translation binary no longer flashes a console window on Windows.**

## [0.14.3] - 2026-06-23

A patch release hardening idle message delivery to spt-hosted endpoints.

### Fixed

- **Idle messages to an spt-hosted endpoint are no longer silently dropped when delivery can't complete.** If no working translation helper is available to submit an incoming message to an idle spt-hosted endpoint, the daemon now queues the message for poll-based delivery and honestly reports it as queued — instead of typing it into the endpoint's terminal without ever submitting it (which looked delivered but was not). Delivery via a working helper is unchanged.

## [0.14.2] - 2026-06-23

A patch release fixing idle message delivery to spt-hosted endpoints.

### Fixed

- **Messages delivered to an idle spt-hosted endpoint now submit instead of stalling half-typed.** The daemon now resolves an adapter's idle-delivery translation binary against the adapter's install directory, so it launches correctly; previously the helper failed to start and an incoming message was typed into the endpoint's terminal but never sent.

## [0.14.1] - 2026-06-23

A patch release: `spt adapter add` no longer clobbers an existing install and reports its outcome more clearly, and the interactive `spt endpoint run` picker now lets you choose a new endpoint's home subnet on multi-subnet nodes.

### Added

- **The interactive `spt endpoint run` picker offers a home subnet.** On a node that belongs to more than one subnet, choosing *Create new* now prompts for which subnet the new endpoint should home to, with your most-recently-used subnet first. (The non-interactive `--subnet` path from 0.14.0 is unchanged.)

### Changed

- **`spt adapter add` is non-destructive.** Re-adding an already-registered adapter is now refused, with guidance to use `spt adapter update` or `spt adapter remove` instead. A fresh install stages the new files and swaps them in only once it is complete, so a failed or repeated add can no longer clobber a working install or leave it half-written (previously this could surface as a cryptic "os error 2").
- **`spt adapter add` reports its outcome more clearly.** Its messages now distinguish an adapter that is installed and ready from one whose install is still pending.

## [0.14.0] - 2026-06-23

A release focused on how endpoints are created: each endpoint now picks its subnet once, when you create it, on nodes that belong to more than one subnet; and you can attach to an endpoint while it is still starting up, before it is ready to receive messages.

### Added

- **An endpoint chooses its subnet when you create it.** `spt endpoint run` homes a new endpoint to a single subnet for its lifetime. On a node that belongs to just one subnet this happens automatically. On a node in two or more subnets, `endpoint run` now settles the subnet up front: interactively it proposes your most-recently-used subnet and asks you to confirm; non-interactively it requires `--subnet <name>` and, if you omit it, refuses immediately with the list of available subnets instead of hanging. Previously a multi-subnet node could stall silently during endpoint bringup.
- **You can attach to an endpoint before it finishes starting.** Between the moment an endpoint is spawned and the moment it binds, it now accepts a connection: `spt rc <id>` (and `spt endpoint run --attach`) drops you into the live pre-bind session, so you can watch startup or clear a bringup prompt before the endpoint is ready. Such an endpoint is not message-addressable yet — it appears as a hollow `UNBOUND` row in the endpoint picker, `spt endpoint list`, and `spt whoami`, distinct from an offline endpoint.

## [0.13.2] - 2026-06-22

A release focused on adapter packaging and updates: one adapter package can cover several platforms, adapters update live without restarting your agents, installs can pull from private GitHub repositories, plus a few adapter-tooling conveniences.

### Added

- **One adapter package can cover multiple platforms.** A `.spt` adapter can now bundle binaries for several operating systems and CPU architectures alongside one shared manifest; installing extracts the shared files plus the binary for your platform. Existing single-platform packages keep working.
- **Adapters update without restarting your agents.** When an adapter updates, the daemon stops just that adapter's background binary, swaps it in place, reloads its manifest, and restarts it — running agents continue across the update.
- **Adapter installs and updates can use private GitHub repositories.** `spt adapter add --release` can fetch from a private repo through the GitHub CLI, with no access token to manage. New `--gh` / `--https` flags choose the transport (default: automatic).
- **Adapters can show a notice after they update.** An adapter may declare a short Markdown message shown once, only when an update is actually applied.
- **`spt adapter version <name>`** prints an installed adapter's version.
- **`spt adapter digest-proof` and `spt adapter translate-proof` can test an unpackaged adapter** via new `--dir` / `--manifest` options — proof a development or bare-file adapter before it's installed.

### Fixed

- **`spt --help` and the CLI reference no longer leak internal tracking codes.** Generated help and reference text are swept clean of internal identifiers.

## [0.13.1] - 2026-06-22

A patch release: an author-time proof tool for idle-delivery translation binaries, plus a correction to the translation-binary contract docs.

### Added

- **`spt adapter translate-proof <adapter> --event '<EVENT…>'`** — validate an adapter's `[message-idle-translation-binary]` without a live session. It spawns and feeds the declared binary exactly as the daemon does at idle delivery and prints the keystroke commands it emits (`{key}` / `{text}` / `{delay_ms}` / `{commit}`), failing a binary that emits nothing or never sends a terminating `{commit}`. The author-time mirror of `spt adapter digest-proof`.

### Fixed

- **The `[message-idle-translation-binary]` contract now documents `{commit}`.** The published contract had omitted the mandatory `{"commit":true}` sequence terminator (and its degenerate example would have faulted at the 5-second commit deadline); it now describes `{commit}`, the inject floor, and the commit-deadline behavior.

## [0.13.0] - 2026-06-21

A minor release: idle message delivery for spt-hosted endpoints now runs through an adapter translation binary, `spt rc` gains real paste, key, and mouse support on Windows and stays attached under heavy output, session resume actually resumes a prior session, and the daemon no longer flashes a console window.

### Added

- **Idle message delivery for spt-hosted endpoints now uses a translation binary.** An adapter can declare a `[message-idle-translation-binary]`; spt-core brings it up alongside the endpoint, where it polls the relay for incoming messages and optionally emits keypresses, delays, and text injection that spt-core applies to the endpoint's terminal. Idle delivery now flows through the relay poll for every endpoint, instead of spt-hosted endpoints falling back to direct PTY injection.
- **Windows paste in `spt rc`.** Ctrl+V and right-click now paste the local clipboard into the attached agent as a single bracketed paste.
- **Windows special keys in `spt rc`.** Arrow keys, Home/End, Delete, function keys and other special keys are translated to terminal sequences and reach the agent — previously only plain characters got through.
- **`spt rc` forwards mouse scroll** to the agent's terminal.

### Fixed

- **Pasting or typing into an spt-hosted endpoint no longer freezes the daemon.** A large paste or input burst could wedge the daemon's input path so every new attach died; input now runs on a dedicated per-session writer and never blocks the daemon (this also covers the effect-journal stall on interactive input).
- **Session resume actually resumes now.** Agent endpoints track session history and offer explicit session resume via `spt endpoint run` (`--resume <session>` or *Resume from history*); the adapter manifest can now declare a `[session.resume]` command so the relaunch reattaches the prior session instead of always starting a new one. The run picker now shows each row's working directory and local time.
- **`spt rc --view` viewers survive a high-output terminal.** A viewer no longer dies when the output backlog rolls over or it is briefly evicted — it snaps forward or skips to live instead of failing — and a slow controller can no longer starve a concurrent viewer.
- **`spt rc` no longer races a just-started agent.** Attach now waits for the endpoint to come online, instead of failing when you attach immediately after `spt endpoint run`.
- **The daemon no longer flashes or respawns a console window on Windows.**
- **Windows Backspace and Ctrl+Backspace now do the right thing in `spt rc`.** Backspace deletes a character and Ctrl+Backspace deletes the previous word (Windows-native), instead of both deleting only a character.

### Changed

- **The `spt endpoint run` picker is clearer.** It opens directly on an existing pick, auto-attaches, shows the controlling node name, and produces clean bring-up output.
- **Human-prose command output renders Markdown.** Prose output (how-to topics and similar) now shows styled headers and emphasis in a terminal and clean plain text when piped — matching the v0.12.1 `--help` fix.

## [0.12.1] - 2026-06-18

A patch release fixing the live-agent lifecycle in a real terminal: attaching to a running agent, keeping it alive when you close the terminal, and the daemon staying responsive now all work as intended. Also polishes `spt endpoint list`, the run picker, and `spt --help`.

### Fixed

- **Attaching to a running agent now shows its output.** `spt rc <id>` against an agent started with `spt endpoint run` now delivers the agent's terminal output immediately, instead of connecting to a blank screen.
- **Closing the terminal that started an agent no longer kills it.** When `spt endpoint run` launches the background daemon for you, closing that terminal tab or window now leaves the agent running and re-attachable with `spt rc <id>`.
- **A crashed agent with a disconnected viewer no longer freezes the daemon.** A dead agent process combined with an abruptly-closed `spt rc` could previously wedge the daemon so new agents wouldn't start; the daemon now stays responsive and marks the dead agent offline.

### Changed

- **`spt endpoint list` and `spt whoami` always include this machine's local agents** — your own just-started agent always shows up. **The `--local` flag has been removed**: local agents are now always merged into the listing.
- **The `spt endpoint run` picker offers the right action.** An already-running agent now offers **Attach** instead of a meaningless "Start now".
- **`spt --help` renders cleanly.** Help text no longer shows raw `**` and backtick characters — emphasis and command names display as styled text in a terminal, and as plain text when piped or redirected.

## [0.12.0] - 2026-06-18

A minor release fixing the live-agent lifecycle: running an agent, attaching to it, and stopping or restarting the daemon now behave correctly, and an agent's reported status reflects whether it is actually reachable.

### Added

- **`spt endpoint purge <id>`** — removes an offline endpoint's record and leftover files in one step (offline endpoints only).
- **Agents started with `spt ready` now appear in the `spt endpoint run` resume-from-history picker when offline.** Previously only live agents were offered there, so a message-listener agent couldn't be relaunched from history; now it can.

### Fixed

- **An agent's status now reflects whether it is actually reachable.** A daemon-hosted agent whose session has gone away is now marked offline on the next check instead of staying "online" indefinitely. Agents reached over a relay are unaffected.
- **Attaching to an agent no longer hangs on a dead or silent session.** `spt rc` now fails fast with a message instead of showing an endless blank screen, and stopping an endpoint marks it offline.
- **`spt daemon stop` now fully stops the daemon.** It finishes releasing its sockets before reporting success, and it cleans up the agent and Psyche processes it launched instead of leaving them running.
- **Stopping or signing off a single agent now also shuts down its Psyche.** Previously, stopping one agent (without stopping the whole daemon) left its Psyche process running until the next `spt daemon stop`; the Psyche is now reaped as soon as the agent is un-hosted.
- **Restarting the daemon no longer revives stale "online" agents** that are not actually running, and no longer leaves a duplicate Psyche behind.

## [0.11.0] - 2026-06-17

A minor release: messages now reach daemon-hosted agents, endpoint environment variables are populated, and several errors are clearer.

### Fixed

- **`spt send` now delivers to an agent whose terminal is hosted by the daemon.** When the target has no `spt api listen` relay (the daemon holds its terminal directly), `spt send` previously queued the message silently. It now injects the message into the agent's session and reports "Sent" only once delivery is confirmed — otherwise it queues it ("Queued") as before, and never reports a false "Sent".
- **`spt endpoint run` now fills in `[env]` values.** Placeholders such as `{id}` in an adapter's `[env]` entries (for example `SPT_ENDPOINT_ID`) were never substituted, so an agent launched without explicit flags came up with an empty endpoint id and never registered. The values are now substituted and set on the launched process.
- **A daemon-hosted terminal now reports a clear message when the daemon is stopped,** instead of failing with a raw "failed to fill whole buffer" that looked like a crash.
- **Clearer error when an adapter's manifest has not been extracted yet.** Using such an adapter now reports an actionable message (and logs it as skipped) instead of a raw "os error 2" and a silent drop.
- **A removed endpoint no longer lingers with a stale status** — it is now shown as offline.

## [0.10.0] - 2026-06-17

A minor release: richer, consistent agent status in the picker and `spt endpoint list`.

### Added

- **Four-state endpoint status in the picker.** Beyond offline and online, a live agent running only inside its harness (with no hosted terminal) now shows as "online — harness only", and an agent whose session is currently being driven by someone shows as "online + controlled" — so you can tell at a glance how an agent is reachable.

### Changed

- **Subnet entries now show a readable node label instead of a raw key prefix,** rendered the same way in both the picker and `spt endpoint list`.
- **`spt endpoint list` columns are now aligned** instead of ragged, and the subnet listing notes that it is the subnet view, so a local agent you just started isn't mistaken for missing.

### Fixed

- **The picker now loads each agent's project history** — it previously always showed empty.
- **A self-owned agent listed under both Local and Subnet no longer shows conflicting status.** The live local status is now authoritative for both listings.

## [0.9.1] - 2026-06-17

A patch hardening harness-adapter resolution and making a stale-daemon error actionable.

### Fixed

- **Going live or ready still resolves the adapter when the harness executable was renamed in place.** After an in-place update that leaves the running program renamed (e.g. `claude.exe.old.<timestamp>`), the daemon now matches it to its adapter by the name stem before the first dot, so bringup keeps working instead of failing to find the adapter.
- **A daemon left running from before 0.9.0 now reports an actionable error.** Seeding a session against an out-of-date daemon previously failed with a cryptic "failed to fill whole buffer". It now explains the cause and tells you to run `spt daemon stop` (the daemon restarts automatically on the next `spt api` command).

## [0.9.0] - 2026-06-17

A minor release: harness-hosted agents go live (or ready) without naming an adapter — the daemon resolves it from the running session.

### Added

- **`[adapter] host_binaries` manifest field** — declares which harness executables an adapter hosts; the daemon matches a live session to its adapter by the running binary.
- **`spt adapter use <adapter>[:profile]`** — sets the default adapter profile per harness binary. Durable (survives adapter updates); without it, the most-recently-registered matching adapter is used.

### Changed

- **Going live or ready under a harness no longer requires `--adapter`.** `spt api seed` records just the session (pid + id); `spt api listen` resolves the owning adapter automatically from the session's process — restoring the one-step legacy bringup. `--adapter` remains an optional override for adapter development.

## [0.8.4] - 2026-06-17

A patch fixing a Windows launch failure for harness/shell adapters whose start command is a script.

### Fixed

- **Windows: harness and shell sessions launched via a script command now start.** A start command that resolves to a Windows batch file (`.cmd`/`.bat`) or an extensionless CLI shim (e.g. the node `ccs` launcher) previously failed with "not a valid Win32 application" (os error 193) — the daemon tried to execute the non-PE file directly. spt-core now resolves the program through `PATHEXT` (preferring real executables) and runs script targets through their interpreter (`cmd.exe` / PowerShell). No change on macOS/Linux.

## [0.8.3] - 2026-06-16

A reliability patch: a dead or unresponsive subnet peer can no longer stall background sync.

### Fixed

- **A dead or unresponsive peer can no longer stall background sync.** The daemon now bounds every network operation it makes on a live agent's behalf, so a roster peer that has gone offline (or stopped responding mid-handshake) fails fast as an ordinary, recoverable error instead of hanging the daemon's peer-sync loop. Previously such a peer could freeze background synchronization for tens of minutes and force repeated internal restarts; now the peer is simply skipped and retried on the next cycle, and a healthy node's sync keeps flowing. Normal peers are unaffected (no added latency).

## [0.8.2] - 2026-06-16

A reliability patch for command-template argument handling and dead-on-launch Psyche reporting.

### Fixed

- Command-template substitution now fills each argument as a single element: a multi-word or quoted value (e.g. a Psyche prompt) is passed through intact instead of being split or injected across multiple arguments.
- A daemon-hosted Psyche that launches but exits immediately is now correctly reported as a failed host (the harness-reachable psyche-host-error signal added in v0.8.1), instead of leaving a phantom "online" entry backed by a dead process.

## [0.8.1] - 2026-06-16

A visibility fix: a daemon that can't host a live agent's Psyche now reports it, instead of leaving the agent looking online with no cause.

### Fixed

- **Harness-reachable psyche-host failure signal.** When the daemon cannot host a live agent's Psyche (for example, the adapter's psyche binary is missing from its install directory), the failure is now recorded on the agent's perch state and surfaced by `spt endpoint list` / `spt whoami` as a `psyche-host: FAILED (<reason>; <n> attempt(s); <ts>)` line. Previously this failure was silent — visible only on the daemon's internal stderr — leaving an agent reporting `online` with no Psyche and no visible cause. Liveness (`status`) is unaffected and remains authoritative; the new `psyche_host_error` field is additive and backward-compatible.

## [0.8.0] - 2026-06-16

Remote shells you can hand off and watch across the subnet, two new in-CLI how-to guides, plus adapter-distribution and install fixes.

### Added

- **Drive and watch a hosted shell across the subnet.** Building on the remote-terminal host from 0.7.0, a hosted session now has a single live driver and any number of read-only watchers, with explicit per-capability consent before a sensitive action runs. You can tunnel into a same-node session, and a gateway node can own the shell on behalf of a peer it fronts. (Cross-node tunnelling is not yet available.)
- **Two new `spt how-to` guides: `subnet` and `live`.** Pairing machines (create vs join, the 6-digit code, reaching remote agents) and running as a live agent (the persistent `spt api listen` relay, the Psyche seam) now have task-oriented in-binary topics instead of dead-ending.
- **Update an adapter from a GitHub release, optionally signature-checked.** An adapter's `[update]` feed can now be a GitHub release (`avenue = "gh_release"`); `spt adapter update` pulls the newer `.spt`. Declare a `signing_key` and verification is fail-closed — an unsigned or wrong-signature artifact is refused, not installed.
- **`spt api` resolves an adapter's manifest and install directory from `--adapter`.** Pass `--adapter <name[:profile]>` without `--manifest` and spt looks both up from the registry; `--manifest` becomes an override for an unregistered or local manifest.

### Fixed

- **A bundled adapter binary resolves from its install directory before `PATH`.** An adapter that ships its own helper binaries — a `[digest]` extractor, the Psyche-spawn command — now finds them in the adapter's install directory first, so a bare program name in a manifest works without you placing it on `PATH`.
- **The Windows at-logon task starts the daemon in the background.** It now launches detached (`spt daemon start`) instead of holding a foreground console window.

## [0.7.3] - 2026-06-15

Install an adapter straight from a GitHub release.

### Added

- **`spt adapter add --release <user/repo>` installs an adapter from a GitHub release.** Point it at a repo — optionally with `--tag <tag>` and `--asset <name>` — and spt downloads the published `.spt` archive, extracts it, and registers the adapter. This lets you ship an adapter that lives inside a larger repository, where cloning the whole tree with `--github` doesn't fit. It trusts HTTPS and GitHub for the download, and doesn't change how an already-installed adapter updates.

### Changed

- Clearer adapter-installation guidance: a spelled-out post-install activation step and the distribution-repo layout for `--github`.

## [0.7.2] - 2026-06-15

A digest-proof fix: `spt adapter digest-proof` now works with the documented `{session_id}` example templates.

### Fixed

- **`spt adapter digest-proof` now fills the same substitution keys the live extractor does.** A proof run previously supplied an empty key map, so any extractor template using `{session_id}` (the shape in the published examples) failed instead of producing a sample. It now populates `{id}` and `{session_id}` to match runtime, with an optional `--session` to pin a specific value.

## [0.7.1] - 2026-06-15

A consistency-and-clarity patch: messages now arrive in one envelope across every channel, and `spt update apply` confirms success in plain language.

### Changed

- **One message envelope across every channel.** Messages drained with `spt api poll` and `spt api worker-poll` now arrive in the same `<EVENT type="msg" from="…">…</EVENT>` envelope as the live `spt api listen` stream — one format to parse everywhere, and several queued messages are now self-delimiting. (Building an adapter? Parse the `<EVENT>` envelope on the poll channel; the older internal frame is gone.)

### Fixed

- **`spt update apply` now confirms the update applied, in plain language.** A successful apply prints `Updated spt-core to vX.Y.Z.` with a link to the changelog, instead of the earlier provisional "trial" wording that left a finished update looking unresolved. (The changelog link now points at the canonical `github.com` address.)

## [0.7.0] - 2026-06-14

Remote terminals land. You can now bring an agent up under spt's own terminal host and attach to it from your own machine or across the subnet — drive it, or just watch — with a real one-at-a-time controller and any number of read-only viewers.

### Added

- **`spt endpoint run` brings an agent up under spt and attaches you to it.** spt hosts the session's terminal itself; `spt endpoint run --adapter <a> --id <name>` starts it and drops you in. Detach (see `spt rc`) and it keeps running headless until you come back — from this machine or another node.
- **`spt rc <id>` — attach to a running session's terminal.** Scrollback replays, live output streams, your keystrokes drive it. Detach with **ctrl-b** then `d` (the session keeps running); `ctrl-b ctrl-b` sends a literal ctrl-b. Works the same whether the session is on this machine or across the subnet.
- **Controller / viewer model.** One person drives at a time (the *controller*); any number can **`spt rc <id> --view`** to watch read-only (no input, never resizes the session). The controller's window size drives the terminal.
- **`spt rc <id> --take` — take control.** If someone else is driving, `--take` kicks them (they get a loud "you were taken over by …" notice and are detached) and you become the controller. A plain `spt rc <id>` on a session someone else controls now **refuses with guidance** (it tells you to `--view` or `--take`) instead of silently stealing control.
- **`spt endpoint run` is now an interactive picker.** Run it bare (no `--adapter`/`--id`) and pick *Create new* (choose a harness adapter + profile, name the endpoint) or *Pick existing* (browse by project / local node / subnet with live status, type-to-filter, and a description pane), then attach / start / view / resume-from-history. The flagged form is unchanged for scripts. **Bare `spt`** (no subcommand) opens the same picker on an interactive terminal — a pipe, redirect, or CI run still prints help instead. A controlled endpoint in the picker shows **View** and **Kick and take control** (not a plain attach), pinned with `controlled by <node> (+N viewing)`. Press `s` to bake the current selection into a project-root `spt-<id>` launcher shortcut (an adapter can brand it, e.g. `cc-<id>`, via the new `[adapter] shortcut_basename` manifest field).
- **`spt subnet join` shows a QR code + setup code on success.** After joining, scan the QR (or read the `otpauth://` code) to re-provision an authenticator app for the subnet.
- **Privilege-gated commands self-elevate, cross-platform.** When a command needs elevation, spt re-launches itself the right way for your system — a Windows UAC prompt, a Linux desktop `pkexec`/terminal `sudo`, or inline `sudo` in a terminal — and prints the exact command to run by hand when it can't.
- `spt spt` — ???

### Changed

- **`spt whoami` now shows the full picture.** It is an alias for `spt endpoint list` — your own endpoint pinned first (with its description, if set), then the subnet roster — instead of just printing a bare id.

## [0.6.0] - 2026-06-13

The session digest grows up — its own adapter seam, it follows an agent across `/clear`, and it shows the context spt itself feeds the agent.

### Changed

- **The session digest gets its own adapter seam, follows an agent across `/clear`, and shows the context spt feeds it.** An adapter now declares a `[digest]` *extractor* that maps its native log to the digest's `{role, text, tool, ts}` contract — its **own** manifest section, separate from `[history]` (which stays full-fidelity for the echo-commune). The digest **spans** a `/clear` or `/compact`: it enumerates an endpoint's recent sessions and shows a `── /clear ──` divider instead of going blank at every reset. It also **interleaves spt's own injected context** (session-start Psyche download, echo mirror, incoming messages) with the agent's activity, in time order. New `spt adapter digest-proof <adapter> --sample <log>` runs your extractor against a real log and prints exactly what parsed, what rendered, and **every dropped line with the reason** — no more silent-empty digest.
  - **Breaking (adapter authors):** this **supersedes** the v0.5.0 guidance to emit the digest contract through your `[history]` normalizer. Declare a `[digest]` extractor (or push via `spt api digest-entry`) instead; one `[history]` normalizer can no longer serve both the opaque echo-commune and the contract-typed digest.

## [0.5.0] - 2026-06-13

Adapter customization and richer session surfaces — make an adapter your own without forking it, give an agent a durable role, mark who a message came from, and get an at-a-glance "what is this agent doing" view for any session.

### Added

- **Adapter profiles — customize an adapter without forking it.** `spt adapter create-profile <adapter> <name>` makes a named variant of an installed adapter (its own environment, prompts, and capabilities); launch it by addressing `adapter:name`. A profile you create locally **survives updating or re-adding** the underlying adapter, and `spt adapter list` shows each profile as its own spawnable option. `spt adapter delete-profile` removes one. (A profile may only *tighten* what the adapter allows — an attempt to loosen a capability is refused at registration.)
- **Adapter config values.** `spt adapter set-string <adapter> <key> <value>` and `spt adapter get-string <adapter> <key>` read and write an adapter's named settings — per-profile when you address a profile.
- **Keyword hints.** An adapter can teach its own commands in context: when a keyword it declares appears in one of your messages, a one-line tip surfaces — at most once per session, so it never nags.
- **`spt endpoint role` — a durable agent role.** Set a free-form statement of what an endpoint is for; it is shown to the agent **first**, at the start of every new session. `spt endpoint role` is the only thing that writes it — nothing automated ever overwrites your wording.
- **`spt api digest-entry`** lets a harness with no readable session log feed its activity directly, so even those sessions show a live digest.

### Changed

- **The live session digest now works for any session, not just terminal-hosted ones.** `spt endpoint digest <id>` builds its at-a-glance view from a session's normalized logs instead of scraping the raw terminal stream — so a session spt-core doesn't host in its own terminal (for example a Claude Code session) now shows a digest too. `--follow` still streams changes as they happen.
  - **Breaking (adapter authors):** the `[pty_digest]` manifest section is **removed**. The digest now rides your `[history]` source — emit your history records as `{"role": …, "text": …, "tool": …}` JSON (or push them with `spt api digest-entry`) and the digest builds itself. No digest-specific manifest section is needed.
- **Messages now carry who sent them — a person or an agent.** A message you send is delivered marked as user-sent, and the daemon re-stamps anything that falsely claims to be from a user. A human-backed **Gateway** endpoint is accepted as a first-class endpoint — addressable, able to own shells, and able to subscribe to digests — including from another machine on your subnet.

## [0.4.2] - 2026-06-11

### Fixed

- **(Linux) An update now takes effect immediately — no manual restart needed.** On Linux, applying an update replaced the program on disk but the background service kept running the *previous* version until you restarted it by hand, so a fix could sit installed-but-inactive without you realizing the update hadn't truly taken hold. The service now relaunches its worker onto the freshly applied version on its own — the seamless behavior Windows already had. As an added safeguard on every platform, an update that somehow comes up running the wrong version is now detected and rolled back automatically instead of being recorded as applied.

## [0.4.1] - 2026-06-11

### Fixed

- **An unreachable peer can no longer stall your node's background work.** If another machine on your subnet dropped off mid-exchange — a network drop, a sleep, a hard crash — the daemon's outbound loops (peer sync, notifications, update checks) could hang waiting on it, in the worst case for hours, until something restarted the service. Now a stalled exchange gives up on its own in under a minute and the background loops resume, so one dead peer never freezes the rest of your node.

## [0.4.0] - 2026-06-10

### Fixed

- **`spt update apply` now actually runs the new version — no manual restart needed.** Previously, applying an update replaced the program files on disk but the already-running background service kept executing the *old* code until you manually restarted it. A fix could sit installed-but-inactive, and you'd keep seeing the old behavior — sometimes for a long time without realizing the update hadn't truly taken effect. The background service now relaunches itself onto the freshly installed version automatically, so an update goes live the moment you apply it. The swap is seamless: terminal sessions and network connections the daemon is hosting stay alive across it — nothing you're running gets dropped.
- **A failed update now rolls back on its own.** If a newly applied version can't start cleanly, the service automatically returns to the last version that was working instead of leaving you with a daemon that won't come up. Your machine keeps running on a known-good build while you sort out the bad release.

### Changed

- **(Windows) The background service no longer flashes brief console windows** when it starts or restarts its internal worker process.

## [0.3.2] - 2026-06-09

### Fixed

- **`spt update fetch` can no longer end up installing another platform's binary.** In a mixed Windows/Linux fleet, fetching an update on one machine and letting another machine pull it peer-to-peer could hand that machine a build for the *wrong* operating system — leaving `spt` unable to start. `spt update fetch` now downloads the signed, multi-platform update set, so every machine installs (and re-shares to its peers) the build for its own platform. As an extra safeguard, `spt update apply` refuses any staged update whose target platform can't be confirmed to match this machine. If you have an update staged from 0.3.1, re-run `spt update fetch` on 0.3.2 to replace it with the platform-safe set.

### Changed

- **`spt update apply` prints a friendly confirmation** — for example `Updated spt-core to v0.3.2.` followed by a link to the changelog — instead of a terse internal status line.

## [0.3.1] - 2026-06-08

### Added

- **`spt update fetch`** — pull and stage the latest signed release straight from the project's GitHub releases, then `spt update apply` to install it. This bootstraps the first machine in a fleet (or any machine with no peer to update from), which previously could only receive an update from another machine that already had it. The download is verified against the same signed-release keys as peer-to-peer updates. Add `--tag vX.Y.Z` to fetch a specific version.

### Fixed

- **Messages sent from Windows no longer arrive garbled.** A message piped into `spt send` or `spt ring` from a Windows shell (whose text carries a carriage return) could corrupt how the message displayed on the receiving machine. The message codec now neutralizes carriage returns, and `send`/`ring` trim their input like `notify` already did.
- **A node is no longer stranded offline after a reboot.** If the daemon started before the machine's network was ready (common immediately after boot), it used to come up with no connection and stay that way until you manually restarted it — `spt daemon` would just report the peer pump as "STALLED". Now the daemon keeps retrying the network in the background and brings itself online once the network is up, with no restart needed. While it's waiting, `spt daemon` reports "no connection" honestly instead of a misleading stalled-pump message. (On Linux the installed service now also waits for the network at boot.)

## [0.3.0] - 2026-06-08

### Added

- **`spt subnet revoke <node>…`** — remove one or more machines from a subnet across the **whole fleet**, not just locally. It tells every member to drop the node within moments, then rotates the subnet's shared secret so the removed machine is locked out and must re-pair to come back. By default the rotation is batched at the end of a one-hour window — several revokes in that window share a single rotation, and any member that was briefly offline heals automatically across it. Pass `--force-rotate-seed` to rotate the secret immediately (the compromised-machine path; a member that's offline at that moment will have to re-pair rather than auto-heal). Name each target by hostname, key prefix, or full key. Requires running elevated. This is the fleet-wide counterpart to `spt subnet prune`, which only cleans a dead node off the local machine.
- **`spt daemon start`** — bring the daemon up in the background, idempotently. When `spt` is installed as a service (the Linux per-user service, or the Windows logon task), `start` and `stop` now drive *that* service instead of a stray hand-started daemon — so the two never fight each other for the connection. `spt daemon start` on an already-running daemon just says so and does nothing.

### Changed

- **Subnets are now a full mesh.** *(Breaking — see the upgrade note.)* Every machine in a subnet now connects directly to, and shows, **every other member** in `spt subnet status --nodes` — previously you mainly saw the machines you had paired with directly. Membership in the subnet is now what grants trust, replacing the separate per-peer trust list that earlier versions kept.
  - **Upgrading from 0.2.0:** there is no automatic migration of the old trust list, so after updating, **re-pair your machines into their subnets** (`spt subnet join`, or create + invite from a seed holder) to rebuild membership. Until a machine is re-paired, it won't be reachable in its subnets.
- **`spt daemon stop` is service-aware.** If a managed service owns the daemon, `stop` asks the service manager to stop it cleanly (so it doesn't immediately restart), instead of signalling the process directly. A hand-started daemon still stops the same way as before.
- **`spt daemon run` is now strictly foreground on every platform** — it stays attached to your terminal until you stop it (the form the installed service uses). For a background daemon, use `spt daemon start`. On Windows, running `daemon run` from an elevated shell now refuses with a hint rather than silently disappearing into the background.
- **`spt daemon status` shows what manages the daemon** — whether a service owns it (and is active) or it was started by hand.

### Performance

- **`spt subnet status --nodes` is much faster when several nodes are offline.** It now checks all the quiet nodes at once, so the view comes back in about the time of a single check instead of stacking the wait up node by node. (This matters more now that a subnet is a full mesh and you see every member.)

### Fixed

- **A peer's name no longer disappears when it goes offline.** Once you've seen another node's hostname in `spt subnet status --nodes`, it now stays shown even after that node goes offline — previously the name reverted to a bare key after the node went quiet for a while. The name is only forgotten when you explicitly `spt subnet prune` that node.

## [0.2.0] - 2026-06-08

### Added

- **`spt endpoint` command group.** A single home for everything you do to an endpoint: `spt endpoint fork`, `suspend`, `wake`, `shutdown`, `rename`, `stop`, and `digest` all live here now. (See **Changed** — this is where these moved from.)
- **`spt endpoint list`** — one combined view of every endpoint you can see, grouped by subnet, with your own endpoint pinned at the top.
  - `spt endpoint list --local` shows just this machine's endpoints.
  - `spt endpoint list --subnet <name>` filters to one subnet.
  - `spt endpoint list --detail` adds each endpoint's description blurb.
- **`spt endpoint description [set]`** — read or write an endpoint's description blurb (bare command shows it, `set` writes it).
- **`spt endpoint access`** — per-endpoint access control (`allow` / `revoke` / `open` / `list`), scoped to the individual endpoint.
- **`spt daemon` command group:**
  - `spt daemon status` (or bare `spt daemon`) — a node status view: whether the daemon is running, its background-sync health, your subnets, and your local endpoints.
  - `spt daemon stop` — cleanly stops the running daemon.
  - `spt daemon run` — runs the daemon in the foreground (previously a hidden command).
- **Pause and resume a subnet without stopping the daemon:**
  - `spt subnet detach <name>` — stop advertising and connecting for that subnet (peers see you go offline for it) while everything else keeps running.
  - `spt subnet attach <name>` — start serving it again.
  - Add `--save` to either to make that choice the default the next time the daemon starts.
  - `spt subnet status` now shows a per-subnet state for each subnet (serving / detached / no connection).
- **Leave a subnet:** `spt subnet leave <name>` removes the subnet and its trust completely from this node.
- **Clean up dead nodes:** `spt subnet prune <node>` removes a stale node's trust so this machine stops trying to reach it. You can name the node by its hostname label, a key prefix, or the full key; it refuses if the name is ambiguous or refers to yourself.
- **Node names in `spt subnet status --nodes`.** Each node now shows its hostname label and can be addressed as `@<hostname>` in commands. Nodes that aren't running any endpoints still show their hostname instead of a bare key.
- **Automatic re-pair cleanup.** If you reinstall or regenerate a node's identity and pair it again from the same machine under the same name, its old, now-dead identity is removed automatically during pairing — no manual prune needed.
- **Firewall setup on Windows.** The installer (when run elevated) now adds the inbound network rule `spt` needs so other nodes can reach you. If it wasn't added, `spt subnet status` and the "coming online" banner now tell you the rule is missing and print the exact command to add it.
- **Starts on boot.** The installer now registers `spt` to start automatically — at login on Windows, and as a per-user service on Linux — so your node is reachable after a reboot without running a command first.

### Changed

- **BREAKING — commands have been reorganized; old spellings no longer work (no aliases).** If a command isn't found, check its new home below. The agent-messaging commands you use most are unchanged: `spt send`, `spt ring`, `spt ready`, `spt whoami`, and `spt how-to` all stay where they are, as does top-level `spt notif`.
  - These endpoint commands moved **under `spt endpoint`**: `fork`, `suspend`, `wake`, `shutdown`, `rename`, `stop`, `digest`. For example, `spt fork …` is now `spt endpoint fork …`.
  - The old `resources` view is gone; its listing is now `spt endpoint list --detail` and its per-endpoint blurb is now `spt endpoint description`.
  - `spt notify …` moved to `spt subnet notify [message] [--target <subnet>]`. With no `--target`, it sends to your home subnet; if you have no home subnet and don't pass `--target`, it refuses rather than guessing.
  - Stopping/checking the daemon moved under `spt daemon` (`spt daemon stop`, `spt daemon status`).
- **`spt subnet status` tells the truth about a stopped daemon.** A node with no subnets now reads "this node is standalone" and no longer implies messaging works while the daemon is down. If the background sync has stalled, the status view says so instead of looking healthy.
- **Cleaner node listing.** In `spt subnet status --nodes`, a normally-named node now shows just its hostname (e.g. `KITSUBITO`) instead of `KITSUBITO (43a51d9a…)`; the extra key prefix only appears when two nodes share the same hostname and need telling apart.
- **`spt subnet` hints tidied.** The "hint:" lines now appear only on the bare `spt subnet` overview, not in `spt subnet status` (so the status view is clean to read).
- **Pairing works on machines with a wrong clock.** Pairing now checks network time and tolerates a node whose system clock is off by more than a minute (which previously made pairing fail silently). If network time can't be reached, it falls back to the local clock as before; it never changes your system clock.
- **Faster first sync after joining or restarting.** `spt` now remembers peers' last known addresses, so after a join — or after the daemon restarts — other nodes reappear in `spt subnet status --nodes` in seconds instead of taking up to a minute.
- **Endpoints going online/offline show up almost immediately.** When an endpoint starts or stops, peers now see the change in `spt subnet status --nodes` within seconds instead of waiting for the next sync cycle.

### Fixed

- **`spt subnet status --nodes` no longer hangs on a dead peer.** Checking a node that has gone away used to stall the command for ~30 seconds; it's now bounded to a couple of seconds, and the command prints "Checking remote nodes…" so the brief wait is expected.
- **Detached/unreachable peers now read as offline.** A peer you've detached from a subnet (or that has stopped serving it) is correctly shown offline in `spt subnet status --nodes`, instead of appearing online indefinitely just because its machine is up.
- **Messages from other agents now arrive properly formatted.** Incoming messages on the listener stream now include the full envelope with the sender's name, instead of showing as a raw, unwrapped line.
- **`sudo spt …` now works on Linux user installs.** When `spt` is installed to your user directory, elevation guidance that said to "run as administrator/root" used to dead-end with `sudo: spt: command not found`. The installer now also makes `spt` reachable under `sudo`, and on an interactive terminal `spt` re-runs itself with `sudo` automatically; otherwise it prints a command that actually works.
- **Elevated `spt` on Linux runs under your account, not root.** The first time you run an elevated `spt`, it asks once which account should own the daemon and its data, remembers that choice, and every later `sudo spt` runs the daemon and stores state under that account — never as root.
- **`spt daemon stop` on Windows now finds the daemon it started.** A daemon launched through Windows' elevation prompt could end up using the wrong home directory, so `spt daemon stop` reported "daemon not running" while a daemon kept running. It now keeps the right home directory across that elevation step.
- **Removed a confusing internal status line.** `spt` no longer prints the internal "DEELEVATED: running as uid …" notice during normal use.
- **Stale node rows clear out on their own.** Nodes that haven't been heard from in a while are now removed from the listing automatically, so old/dead entries stop cluttering `spt subnet status`.

## [0.1.1] - 2026-06-07

Maintenance fixes following the first public release. (This changelog was
introduced in 0.2.0; 0.1.1 and earlier are summarized here for completeness.)

## [0.1.0] - 2026-06-06

First public release of `spt`.
