::docs :: concepts

the loop & steering

How a turn runs: one model call per pass, the plan checklist, pausing and steering with Esc, ask_user, and the bounds that end every turn with an answer.

* saturn v2, the version this site describes, ships in a few days. until then the installer gives you v1.

A turn is one loop. The agent makes one model call per pass. If that call asks for tools, they face the gate, run, and their results come back for the next pass. The first message without tool calls is the answer.

the loop
ground → agent ─(no tool calls)─→ answer
           ↑          │ tool calls
           └── tools ← approval      (a fully rejected batch goes straight back to agent)

ground

builds the context once per turn: the working folder, your standing instructions (~/.saturn/SATURN.md, then the folder's SATURN.md), the knowledge-base manifest, your memory, today's date and time, and any attachments. No model call.

agent

one native tool-calling call. Its reply either carries tool calls or is the answer, which streams under ── response as it's written.

approval

asks the gate policy about each call. Anything it doesn't auto-approve stops the turn for you. See the approval gate.

tools

runs the approved calls, records any egress, and fences instruction-shaped content in the results before the model sees them.

The agent works in rounds. Calls in one pass run together, so it batches calls only when none needs another's result. "Read the file, then email whoever it names" reads the file first and writes the mail on the next pass, instead of guessing the address. A chat question costs one model call; a single lookup costs two.

::the plan checklist

On a task that needs several tool calls, the agent records a checklist with the plan tool and updates it as steps complete. The checklist renders live in the rail. It also shows in the gate's e explain view, in /trace why, in a replay, and in the headless --json plan field. The agent skips it for a single lookup or a chat answer.

The checklist records what the agent intends, not what happened. plan is read_only and changes nothing outside the turn, so it never faces the gate. The answer's account of what was done comes from the tool calls that actually ran.

::pause, steer, abort

you dowhat happens
Esc on an empty linethe turn pauses at the agent's next pass and shows the checklist, if there is one: [Enter] continue · type a correction to steer · q abort
type a correction, then Escthe correction is added to the conversation and the agent reads it on its next pass. The turn keeps going; nothing restarts.
q at the pausethe turn stops at your request
Ctrl+Ccancels the turn outright

A correction typed just before an Esc pause isn't lost: it lands when the turn resumes. Steering is the same key story as queuing: Enter defers a line to the next message, and Esc acts on it now.

::when it asks instead of guessing

When a value, choice, or confirmation is missing and no tool can supply it, the agent calls ask_user with one question. The turn pauses, you type an answer, and the answer comes back as that call's result. ask_user always runs alone: if the model batches other calls with it, those are sent back with "ask first". Asking changes nothing, so it never faces the gate. Headless there's nobody to ask, so the tool reports that no answer exists and the model has to say what is still unknown.

::bounds and honest endings

  • –runtime.max_iterations (default 16) caps the passes that may run tools. From that pass on, no tool call runs: the model answers from what it has and says what was not done.
  • –A call identical to one already made twice this turn, with nothing changed in between, is refused as a loop. A legitimate re-read still runs.
  • –A call you declined at the gate is never re-issued this turn.
  • –An unknown tool, missing or malformed arguments, or arguments that belong to a different tool are sent back to the model with the right shape, with no gate and no extra model call. A malformed reply is retried once.
  • –A messaging call naming a phone number or email address that appears in nothing you typed and nothing a tool returned is refused before it reaches you. See your mac.

::thinking, only where it helps

runtime.think defaults to adaptive: a pass thinks, within runtime.think_budget (4096 tokens), only when the tool round just before it had an error, which is where the model needs a new approach. A chat answer, a clean lookup, and the final pass of a capped turn run without thinking. off never thinks; on thinks on every pass but the capped one. The reasoning never enters the answer, and /trace why shows what a thinking pass thought.