# Client contract changelog

Versions of the client contract (`manifest.json` `contractVersion`), separate
from the package version. See `README.md` for what counts as minor and major.

## 1.3.0

Added. Every new field is optional for a decoder: a run written before this
version reads with the new fields absent or null.

- Runs index 1.2.0 (`GET /v1/runs`, `GET /v1/runs/{id}`). Per run: `runType`
  (`analysis`, `development`, `autopilot`), `title` (the issue title Phase 0
  fetched, else the launch input), `progress` (0..1, the mean over the
  registered phases) and `currentTask` (`{subject, activeForm}` of the first
  in-progress task of the phase in progress). Per phase: `progress` and
  `tasks[]` (`{id, subject, activeForm, status}`), the host's own task list as
  `phase-tracker.sh task` mirrored it. `now` is read from `meta.Now`, where
  `phase-tracker.sh now` writes it. The index reads the unattended log root
  beside `LOGS_ROOT`, one record per task id. `processAlive` (boolean or
  null) says whether the session the run recorded is alive per
  `claude agents --json`; null when none is recorded or the list cannot be
  read. The resume route's `run-active` and the kill preview use the same
  answer.
- `POST /v1/launch` `mode: "background"` with `kind: "development" | "analysis"`
  (launch request, launch plan 1.1.0). The plan is the autopilot launch shape
  with `/multi-agent <input>` or `/multi-agent:analysis <input>`; the plan
  carries `kind`. The run never blocks on a terminal: a picker the request did
  not answer parks it with `waitingFor: "question"` and a `pendingQuestion`,
  answered through `POST /v1/runs/{id}/answer`. An analysis launch takes no
  answers, and `workspace: local` is refused in background mode
  (`run-questions.json` 1.1.0). `launch.json` 1.2.0 declares
  `hosts.claude.background` and `hosts.claude.resume`.
- `GET /v1/repos` (`repos.schema.json`): the repositories `POST /v1/launch?repo=`
  accepts, `{path, name, source: "autopilot" | "registered"}`, from the
  autopilot config and `launch-repos.json`; `path` is dropped under redact.
- `GET /v1/runs/{id}/log?tail=N` (`run-log.schema.json`): the last N lines
  (1-2000, default 200) of the run's `agent-log.md`, and whether more exist.
- `POST /v1/runs/{id}/resume` (`resume-request.schema.json`, returns
  `launch-plan.schema.json` with `mode: "resume"` and no request file). Only a
  run under the unattended log root resumes in the background; `answers`
  answers its pending question first.
- `POST /v1/runs/{id}/kill` and `POST /v1/gc` (`kill.schema.json`,
  `gc.schema.json`): two steps, preview then confirm, with a single-use confirm
  token valid for 120 seconds and bound to the route and run.
- `GET /v1/autopilot` (`autopilot-status.schema.json`) and
  `POST /v1/autopilot/off` (`autopilot-off.schema.json`).
- `contract-error.schema.json`: `run-active`, `not-resumable`,
  `confirm-expired`, `confirm-mismatch`, and `unattended-profile-missing`
  (409 from launch and resume when the unattended permission profile is not
  installed) with a `remedy` field naming the install command, and
  `workspace-not-trusted` (409 from launch and resume when Claude Code's
  config records the repository as not trusted; `remedy` says to open Claude
  Code there once and accept the trust prompt).
- Every launch shape in `launch.json` passes the prompt as claude's positional
  argument, without `-p`: `claude --bg` refuses `--print`.
- `manifest.json` `server.confirm` describes the token flow.

## 1.2.0

Added, plus one narrowing of the launch input, which no released pipeline
version served before this one.

- Phone routes under `/v1/phone/`: `GET /v1/phone/runs`, `GET /v1/phone/runs/{id}`,
  `POST /v1/phone/runs/{id}/answer` and `POST /v1/phone/launch`. Each request
  is signed with an enrolled device's Ed25519 key (`phone-signed-request.schema.json`);
  the bearer token does not open them and a signature opens no desktop route.
  Responses are always redacted, and launch is refused until it is enabled
  locally. `manifest.json` `phone` names the headers, the signed fields, the
  window and the scopes, so a client hardcodes none of them.
- `phone-devices.schema.json`: the device registry `phone-devices.mjs` writes.
- `contract-error.schema.json`: `unsigned`, `unknown-device`, `revoked-device`,
  `bad-signature`, `stale-request`, `replayed`, `replay-store-full`,
  `unknown-verb`, `forbidden-scope` and `launch-disabled`. A desktop request
  never receives one of them. `repo-not-allowed` (403) answers a launch on
  either route whose `repo` is neither a configured autopilot repo nor one
  registered with `launch-request.mjs register-repo`.
- `manifest.json` `phone.signature.futureSkewSeconds` (5): how far ahead of
  the server's clock a timestamp may be. `windowSeconds` (60) is how far
  behind.
- Launch input. `input` is a structured reference (Jira key, GitHub issue URL,
  `repo#N`, `#N`, Jira URL on the configured host) or, on the desktop channel
  only, free text that the plan renders as one quoted argument. A newline, a
  control or format character, a leading `-`, a flag-like token or a first
  token that is a pipeline op or mode keyword rejects the request
  (`400 invalid-request`); free text may use those words after its first
  token. A client that sent free text to `/v1/phone/launch`, or an input
  carrying one of those, now sends a structured reference instead.
- `launch-request.mjs plan --channel desktop|phone` (default `desktop`) applies
  that channel's input rule, so `plan --channel phone --redact` prints what
  `POST /v1/phone/launch` returns for the same request file.
- `manifest.json`: the `launch-spec` surface carries `launch.json`'s own
  version, 1.1.0.
- `runs-index` `group`: a run with `waitingFor` set is `waiting` at any phase.
  A run paused on a question at phase 0 is no longer grouped `question`, the
  group whose action is garbage-collecting an abandoned intake.

## 1.1.0

Added; nothing a 1.0 client decodes changed.

- HTTP carrier: `contract-server.mjs` serves every surface on `127.0.0.1`
  behind a per-start bearer token, byte-for-byte what the CLI prints.
- `runs-index` 1.1.0: each run carries `sessionId`, `waitingFor` and
  `pendingQuestion`, so a client can find the run it launched and answer the
  question it stopped on without reading `agent-state.json`.
- `runs-index`: `schemaVersion` is always a string or null (agent-state.schema.json
  already declares a string) and token counts are always integers. The
  producer now coerces both and the schema says so; the toolbox decodes exactly
  these types.
- `launch-request.mjs plan` and `POST /v1/launch`: the launch plan
  (`launch-plan.schema.json`). `launch-request.mjs questions` and `spec` print
  `run-questions.json` and `launch.json`.
- `answer-question.mjs --question <id>` and `POST /v1/runs/{id}/answer`
  (`answer-request.schema.json`): an answer names the question it answers.
- `credential-inventory.sh --json` carries `probed` in every envelope,
  including `empty` and `no-backend`.
- `contract-error.schema.json` for every non-2xx HTTP body.
- The kit itself: `manifest.json`, generated `types/index.d.ts`, fixtures and
  the frozen toolbox fields.

## 1.0.0

The first declared contract: `commands.mjs --json`, `run-questions.json`,
launch requests (`launch-request.mjs validate|resolve`), `answer-question.mjs`,
`issues.mjs --json`, `worktrees.mjs --json`, `runs-index.mjs --json` with
`--redact`, and `launch.json`.
