# goose-agent The GDK's agent loop, unrolled into a state machine you assemble yourself. Instead of a fixed "call the model, run the tools, repeat" loop, an agent here is an ordered list of steps. Each step gets a chance to look at the conversation and either decline or produce effects. The machine walks the list, applies the first step that applies, and starts over from the top — so the whole agent's behavior is a function of the persisted conversation, not of in-memory loop state. ## The pieces - **`Operation`** — one step. Implement `run` to act on the conversation, plus any of `inference_tools`, `prompt_parts`, and `moim_parts` to contribute to the model request. Returns `OperationResult::NotApplicable` to pass, or `applied(..)` / `yielded(..)` to take the step. Helpers: `not_applicable()`, `applied()`, `yielded()`, `yielded_with()`. - **`Inference`** — the step that reaches the provider. Before calling it, the machine collects tools and prompt parts from *every* operation in the list into an `InferenceInput`. - **`StateMachine<'a, S, E>`** — holds `Vec>` and a `CancellationToken`. `step()` runs one pass, `apply()` writes effects back, `run()` loops until a step yields to the client or no step applies. - **`ConversationEffect`** — the default effect type: `AppendMessage`, `ReplaceConversation`, `PatchToolRequestMeta`, `SetMessageVisibility`. Bring your own by implementing `MachineEffect`. - **`Emitter`** — streams `AgentEvent`s (`Message`, `Usage`, `MessageUsage`, `McpNotification`, `HistoryReplaced`) to the client while a step runs, and carries the cancellation token. - **`SessionLoader`** / **`EffectHandler`** / **`MachineSession`** — the traits your runtime implements so the machine can load a session by id and persist effects. The machine reloads the session between passes; it never caches it. ## Reading the conversation Because steps re-derive their decisions from history, the crate ships the predicates they need: `messages_since_kickoff`, `last_effective_role`, `assistant_turn_count`, `ends_turn`, and `trailing_error`. When a step must remember that it already did something, it records that on the message itself via `Operation::set_message_meta` / `message_meta` rather than in memory. ## Cancellation Cancellation is cooperative. Once the token fires, remaining steps are treated as not-applicable and each step's `cancel` hook gets a chance to rewrite its result; anything applied while cancelled yields to the client. The reference assembly of these pieces is `goose::agents::state_machine` in the [`goose`](../goose) crate.