Turn Loop
agent.Step(ctx, "user input") is the core operation. It submits one user message and drives the loop until the model emits text with no further tool calls.
Step(ctx, "user input")│├─ session.Turn++├─ session.Append(user message)├─ session.Save()│└─ loop { │ ├─ Pruner check (see Context Management) │ ├─ model.Complete(Request{ │ Messages: session.ContextMessages(window), │ Tools: toolSpecs(all tools), │ Stream: { Token, Reasoning, ToolArgs, Usage } │ }) │ ├─ if no tool calls → return StepResult │ └─ for each tool call: ├─ find tool by name ├─ call Fn(ctx, args) ├─ append result to session └─ continue loop }StepResult
Section titled “StepResult”type StepResult struct { Assistant string // final assistant text ToolCalls []ToolCall // all tool calls made this turn Turn int // turn counter}agent.Run(ctx, inputFunc) calls Step for each line the input function returns.
type InputFunc func() (string, bool)Returns (line, true) per turn, ("", false) when exhausted. Interruptions are handled — the loop continues after ErrInterrupted.
Interrupt
Section titled “Interrupt”ok := agent.Interrupt() // cancel in-flight model callGoroutine-safe. Returns false if no turn is active. The interrupted Step returns ErrInterrupted.
Runtime controls
Section titled “Runtime controls”agent.SetModel(newModel) // swap model mid-sessionagent.SetPrunerModel(cheapModel) // swap or enable pruneragent.SetPruneMode(axon.PruneExtreme) // change pruning aggressiveness
agent.Reset() // wipe session, kill bg shells, rebuild toolspath, ok := agent.Undo() // revert last file edit atomicallydir, err := agent.Cd("/new/path") // change working directoryAll of these must not be called while Step is running.