Skip to main content

API Changelog

The bossanova.v1 OrchestratorService (see the API Reference) uses Stripe-style, date-based versioning. Each dated version pins a stable response shape: when the server starts serving a new value or behavior, it also registers a down-convert transform so callers pinned to an older version keep receiving the shape they were built against.

The Bossanova-Version header

Clients select an API version by sending a Bossanova-Version request header containing a YYYY-MM-DD identifier (for example Bossanova-Version: 2026-07-11). The resolved version is echoed back on the response.

  • A request that negotiates the Current version runs zero transforms.
  • A request pinned to an older version has the ordered chain of down-convert transforms applied so it observes the behavior that shipped with its version.
  • A header-less caller pins to the Baseline — the oldest supported version — so it never silently shifts onto newer behavior. This matches the Stripe intent of "pin to the version you started on."

The current version is 2026-08-21. The engineering procedure for adding a new version and its transform lives in the repository at docs/api-versioning.md (view on GitHub).

Versions

Listed oldest to newest. Every version below is a member of the production registry served by the OrchestratorService.

2026-06-29 — Baseline

The launch baseline and the oldest supported version. It is the version assumed for header-less callers. No transforms run against a Baseline-shaped response beyond those introduced by later versions.

2026-07-04 — Orphaned session state

The OrchestratorService began serving SessionState value SESSION_STATE_ORPHANED on Session.state, a terminal state for a headless run that was killed by a daemon restart. Clients pinned to an older version — which were built before this value existed — are down-converted to SESSION_STATE_IMPLEMENTING_PLAN, the prior observable behavior.

2026-07-05 — Agent auth-failed attention reason

The OrchestratorService began serving AttentionReason value ATTENTION_REASON_AGENT_AUTH_FAILED on Session.attention_status.reason, a new attention reason surfaced when an agent's pane shows the login-required terminal shape (for example "Not logged in" / "Please run /login"). Older clients, which never saw this reason, are down-converted back to no attention reason.

2026-07-06 — Unmanaged account label and limited chat status

Two transforms shipped in this version:

  • Unmanaged account label — restores the prior "System default" account label for older clients where the server now serves "Unmanaged local credentials" for the unbound rotation account.
  • Limited chat status — maps CHAT_STATUS_LIMITED and the derived "usage-limited…" session display shape back to the prior idle-style behavior for older clients. In other words, the new chat status value is hidden from clients that predate it.

2026-07-11 — No-eligible-account rotation outcome

The OrchestratorService began serving RotationOutcome value ROTATION_OUTCOME_STATUS_ONLY_NO_ELIGIBLE_ACCOUNT on Session.rotation_events[].outcome, distinguishing "no active account to rotate to" from the prior "agent cannot rotate". Clients pinned to an older version are down-converted back to ROTATION_OUTCOME_STATUS_ONLY_NO_CAPABILITY, the prior observable value.

2026-07-18 — Errored session display recolor

The OrchestratorService began serving the errored-recolor display shape for orphaned/blocked sessions on Session.display_label / display_intent / display_spinner. An errored session now keeps its real underlying status label and spinner (a live "working" spinner, a pending "? question") but has its intent recolored to DISPLAY_INTENT_DANGER so the error stays visible; a legitimate terminal muted PR ("✓ merged" / "closed") is exempt and stays muted. Clients pinned to an older version were built against the prior shapes — a fixed "orphaned"/DANGER/no-spinner tuple for orphaned sessions and an un-recolored base cascade for blocked sessions — so they are down-converted back to those.

2026-07-23 — Respawn-in-place rotation outcomes

The OrchestratorService began serving two RotationOutcome values on Session.rotation_events[].outcome: ROTATION_OUTCOME_RESPAWNED_SAME_ACCOUNT, audited when a pane that failed authentication is stopped and respawned in place under the same account (the account itself probes healthy), and ROTATION_OUTCOME_RESPAWN_CAP_EXHAUSTED, audited when the per-chat respawn-in-place budget for the window is spent. Clients pinned to an older version are down-converted back to ROTATION_OUTCOME_STATUS_ONLY_NO_CAPABILITY, the prior observable value.

2026-08-03 — Agent-stalled attention reason

The OrchestratorService began serving AttentionReason value ATTENTION_REASON_AGENT_STALLED on Session.attention_status.reason, a new attention reason raised when a chat reports CHAT_STATUS_WORKING while its agent has made no semantic progress for longer than its phase's threshold — a silently dead turn behind a still-animating spinner. Like the auth-failed reason it is only raised where the session had no other attention, so clients pinned to an older version are down-converted back to no attention reason, the prior "keeps spinning" behavior.

2026-08-04 — Waiting chat status

The OrchestratorService began serving ChatStatus value CHAT_STATUS_WAITING on ChatStatusEntry.status, ChatStatusDelta.status and SessionStatusEntry.status, alongside a new waiting_reason string on each of those messages. A chat is waiting when it is blocked on an external event — a registered GitHub callback, a background poll tick — rather than computing, and waiting_reason carries a short human-readable explanation such as awaiting checks_passed_ready on owner/repo#123 (it is empty for every other status). Before this version such a chat reported CHAT_STATUS_WORKING, so clients pinned to an older version are down-converted back to CHAT_STATUS_WORKING with an empty waiting_reason.

2026-08-12 — Draft-PR failure ranks below live activity

The OrchestratorService stopped letting a session-level draft-PR-creation failure claim the session's primary display composite while a chat is live. The ? PR failed label used to outrank every branch except a pending question and a usage-limit banner, so a row could read ? PR failed while the chat underneath it read working — two contradictory presents on one row. That branch now sits immediately below the working branch, so Session.display_label, Session.display_intent and Session.display_spinner carry the live label (working, waiting, initializing, merging, archiving) whenever one applies, and still report ? PR failed when nothing is live. Clients pinned to an older version were built against the previous precedence, so they are down-converted back to ? PR failed.

The failure itself is not hidden from a current client: it is surfaced as a session warning hint, and that hint is deliberately exempt from the accompanying recessive treatment because it is the row's only carrier of the failure.

2026-08-16 — A cron gate that could not run is no longer reported as gated

The OrchestratorService began distinguishing a cron gate that could not be evaluated from one that ran and decided there was no work. Previously every blocked fire — a gate that timed out, could not be launched, or that the shell reported missing (exit 127) or not executable (exit 126) — was recorded as gated and derived CRON_JOB_STATUS_GATED, a warning-styled "waiting, healthy" value. A broken deployment therefore looked exactly like a quiet backlog.

A gate that could not run now serves CronJob.last_run_outcome gate_failed and derives CronJob.last_run_status CRON_JOB_STATUS_FAILED, and RunCronJobNow returns the matching gate_failed skip reason. A gate that ran and exited non-zero for any other code keeps gated / CRON_JOB_STATUS_GATED, and still blocks the fire. No new CronJobStatus enum value was added — the new outcome reuses the existing CRON_JOB_STATUS_FAILED, so this is a change in the value served rather than in the schema.

Clients pinned to an older version were built against the previous values, so they are down-converted back to outcome gated, status CRON_JOB_STATUS_GATED, and skip reason gated.

2026-08-20 — A session account switch stopped by its own deadline is no longer reported as ABORTED

The OrchestratorService began distinguishing a session account switch that ran out of time from one that failed for an unknown reason. Previously the daemon had no wire value for "a deadline stopped this", so a switch ended by its own daemon-side respawn budget came back with no typed error code and ProxySwitchSessionAccount answered ABORTED — the historical fallback for an unmodelled failure.

ABORTED was not merely vaguer; it was actively misleading. It invites a retry, and a request killed by its own deadline is exactly the request a caller must not retry: re-sending restarts a budget that has already proven too short, and on an at-least-once command stream it can stack a second switch on top of work the first one may have partially completed.

A switch ended by its daemon-side budget now answers DEADLINE_EXCEEDED. A new CommandResult.ErrorCode value, ERROR_CODE_DEADLINE_EXCEEDED, carries the distinction over the daemon stream. No procedure or message field was added — this is a change in the status code served on an existing procedure rather than in the schema.

The change is scoped to the account switch alone. Other commands that fail with an expired context are unaffected and still answer ABORTED. ProxySwitchSessionAccount's own relay timeout — the orchestrator giving up waiting for the daemon — has always answered DEADLINE_EXCEEDED and is unchanged at every version, including for older clients.

Clients pinned to an older version were built when this case read as ABORTED, so they are down-converted back to ABORTED with the failure message preserved.

2026-08-21 — Switch cancellation and result-ceiling behavior (Current)

The OrchestratorService began distinguishing a session account switch that the caller canceled from one that failed for an unknown reason. Previously the daemon had no wire value for "the caller canceled this", so a canceled switch came back with no typed error code and ProxySwitchSessionAccount answered ABORTED, the historical fallback for an unmodelled failure.

ABORTED invites a retry, which is the wrong signal for a request the caller abandoned. Retrying can stack duplicate switch work on top of the partially completed first attempt.

A switch canceled by its caller now answers CANCELED. A new CommandResult.ErrorCode value, ERROR_CODE_CANCELED, carries the distinction over the daemon stream. No procedure or message field was added — this is a change in the status code served on an existing procedure rather than in the schema.

The change is scoped to the account switch alone. Other daemon-scoped commands that return the same typed code still answer ABORTED, and ProxySwitchSessionAccount's own orchestrator-side cancellation path is unchanged for every version.

Clients pinned to an older version were built when this case read as ABORTED, so they are down-converted back to ABORTED with the failure message preserved.

ProxySwitchSessionAccount began using self-describing timeout text when bosso's own 120-second result ceiling expires before a daemon verdict arrives. The status code remains DEADLINE_EXCEEDED, but current clients now see that the account switch may still be running, and that the request did not cancel or tear down the switch. The caller should re-check the chat's account to see where it landed.

Previously this handler-owned result ceiling used the generic relay timeout text: command timed out after 2m0s. Clients pinned to an older version were built against that message, so they are down-converted back to the legacy text while keeping DEADLINE_EXCEEDED.

See also

  • API Reference — the generated OpenAPI reference for the OrchestratorService.
  • docs/api-versioning.md in the repository — the engineering procedure for adding a new API version and down-convert transform.