crates/goose-agent/README.md
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.
Operation<S, E> — 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<S, E> — 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<Step<..>> 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 AgentEvents (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.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 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 crate.