Command reference
oko sessions model
Moves a live session to another model between turns, losing no work: a harness takes its model when it starts, and text typed into a running one reaches the model as a prompt, not as a harness command.
Invocation#
oko sessions model ID PROVIDER/MODEL [--socket PATH] [--json]Inputs and options#
- ID is the session id, as `oko sessions agents` prints it; PROVIDER/MODEL is the model it continues on, such as openai-codex/gpt-6-astra.
Output and state effect#
- Waits, on the kernel's events for the session's transcript and not on a clock, until the session's turn has ended (its newest record is an answer that calls no tool, or a turn the provider ended). A session no live process holds is not waited for.
- Before ending anything it asks the broker whether it can start the replacement now: it must answer this build's protocol and say it admits a new terminal (the `admits` field of its version answer). It then asks Brama for the lease the replacement runs on (the new model's subscription, the session's transcript as the lease's session) and opens the shared database the delivery is recorded in; a refusal of either leaves the session's terminal running. Only then does it end each broker terminal holding the session, wait for its process to end, and continue the session as `oko sessions resume ID --model PROVIDER/MODEL` does, with the message models.json declares for a switch. Prints what resume prints, plus ended (each terminal ended, with its pid) and switchedTo; `oko sessions agents` then shows the new model from the transcript.
- When Brama cannot be asked or refuses a subscription lease with HTTP 403, a harness that requires Brama (Jeden) is refused. OMP can instead start on its own provider sign-in: Oko reads OMP's latest usage_history windows and enabled credentials and selects an account with measured free usage. Which model a limit window binds is the scope OMP reported for that limit id in the usage reports it caches (`usage_cache:report:*`): a `modelId` scope binds that model, a `tier` scope binds the models whose id names the tier as one of its `-` separated parts, and every other limit, or one no cached report scopes, binds every model of the provider; a cached report Oko cannot read refuses with its cache key and cause. OAuth identities are pinned through OMP's auth broker; a sole enabled API key with one measured account uses OMP's credential store directly, with inherited broker selection removed. Ambiguous API-key accounts are not assumed usable. The unleased result names the Brama cause, provider, model, account, measurement and live unleased count. A missing or unreadable usage database, or no usable measured account, refuses before the running terminal is ended. Oko records the unleased terminal for inventory; the count is not a substitute for measured capacity.
- Before a lease request Oko reads Stado's resolver waiting register. A connection held from Oko to Brama is named and no request stands behind it; OMP uses its measured account if one is usable, while Jeden refuses. A waiting register that cannot be read is a refusal naming the file. The same read precedes a provider refusal report. Real test: tests/sessions/policy/held_route.rs.
- Without a lease the operator's limit of sessions per subscription still holds: Oko counts the unleased sessions each OMP account already carries among the broker's live terminals (~/.oko/unleased-sessions.json, each terminal with its provider and account) and starts on the least-used account with measured free usage that carries fewer than the limit, skipping fuller ones. The limit and the operator's own words are declared beside the register's code (numeric-provenance.json); Brama is not asked for it, so it holds while Brama is down. When every usable account is full the start is refused before any terminal runs: “every <provider> account with measured free usage for <model> is at the operator's limit of sessions per subscription, so the session was not started: <account> already carries <n> unleased <provider> session(s), the operator's limit of <limit> per subscription ("<his words>")”. A register that cannot be read refuses the same way, before the start: “the unleased sessions cannot be counted, so the session was not started: <cause>”. Real tests: tests/sessions/model/unleased.rs.
- The same register stands before every request Oko sends to Brama, not only a lease: a lease, a chat or decision completion (the blocker reviewer `oko tasks state --blocked` and `oko blockers record` ask, the task judge, the lifecycle model), a model listing. While the resolver holds a connection on the adapter the request would go through longer than the refresh interval this machine's resolver declares (read from ~/.stado/resolver-state.json and Stado's registry copy ~/.stado/cache/registry-last-good.json), the request is not sent and ends with “<address> not asked: this machine's Stado resolver holds <n> connection(s) on <bind> unanswered longer than the <s>s refresh interval it declares: the oldest, <service> for consumer <consumer> on <bind>, has waited <n>s … to <endpoint> on <host>; `stado resolver status` lists every one”; each caller handles it as Brama unreachable (the blocker reviewer keeps the record unjudged and says so). A register that is there and cannot be read refuses the request naming the file. A request already sent before the connection it stands behind counts as held is not cut. A held service refuses before anything is asked of it, whichever consumer's connection the resolver holds: every consumer's adapter reaches the same service on the same host. Brama's address is the adapter ~/.stado/resolver-state.json publishes for consumer oko while the resolver is serving, and `stado service directory connect brama --consumer oko` is asked only when it publishes none, so no Brama request first waits on Stado reaching another host. With this machine's gateway (no --router), a Brama connection held past the refresh interval ends the command with “brama is not asked: this machine's Stado resolver holds <n> connection(s) to brama unanswered longer than the <s>s refresh interval it declares: …”. Every credential read through Skarbiec (Brama's bearer and signing secret, the shared database's key) takes Skarbiec's adapter the same way and ends with “skarbiec is not asked: … to skarbiec …” while a Skarbiec connection is held, so `oko blockers record`, `oko gateway send` and every other command that needs either service end naming the wait instead of standing behind it. Real test: `tests/proxy/held.rs`.
- A service that takes requests and answers none is refused the same way even while no single connection to it is old: a client the harness cuts off closes its connection, so such a service only ever shows young waits. The resolver records, per service, when it last answered and since when every request that ended has ended without a first byte (~/.stado/resolver-answers.json); when that run is longer than the refresh interval the resolver declares, a request to the service on that adapter is not sent and ends with “<address> not asked: this machine's Stado resolver records that every request to <service> ended without an answer since <time>, the latest at <time>: <n>s, longer than the <s>s refresh interval it declares, and it last answered at <time>; `stado resolver status` names the host it is routed to”. A record that is there and cannot be read refuses the request naming the file.
Refusals and safe exits#
- “oko sessions model takes <session> <provider/model>; it was given <n> word(s): …”, “oko sessions model takes the model to switch to; it was empty” and “oko sessions model does not take <flag>; it takes --socket” (exit 2).
- A harness outside Oko's broker that holds the session's ended turn — an OMP window in VS Code, a terminal of the operator's — is not refused and not ended: it is the operator's window. The session is continued in a broker terminal beside it (the resume takes the file over as for any live holder of an ended turn), and the answer lists it under leftRunning [{pid, outsideBroker, turnEnded}]; the model policy's decision names it (“the harness outside Oko's broker that held its ended turn (pid <pid>) is left running as the operator's window”).
- “Oko's PTY broker at <socket> (pid <pid>) is retiring and admits no new terminal; session <id> was not switched to <model> and its terminal was left running”, “… predates saying whether it admits a new terminal, so it cannot be trusted to start one; …” and “… speaks protocol <n>; protocol <m> is required to start a terminal; …” — the session's terminal is not ended.
- A Brama refusal other than HTTP 403 leaves the terminal running. If an OMP account with measured free usage cannot be selected, or the shared database cannot record delivery, the switch leaves the terminal running and names that cause. All refusals exit 1.
- The refusals of `oko sessions resume` once the terminal has ended. All of these exit 1.