Expand description
The effect runner: dispatches Cmd values into tokio tasks.
There are exactly two places in the codebase that spawn a tokio
task: this module and tests. Everywhere else asks the
reducer to return a Cmd, and the runner handles it. That
centralization is what makes structured concurrency per turn
actually work — nothing can accidentally spawn a detached task
that outlives the turn it was started for.
Architecture:
main loop ── reducer ── Cmd ── dispatch ── EffectRunner
├── TurnScope(turn A) ── JoinSet
├── TurnScope(turn B) ── JoinSet
└── detached effects (Save, Exit, …)
↓
Msg via mpsc::Sender<Msg>
↓
main loop (next iteration)The runner dispatches every Cmd variant to a real handler —
model streaming (CallModel → ModelProvider::chat), tool
execution (ExecuteTool → ToolExecutor::execute), persistence
(SaveConversation, LoadConversation, PersistLastModel,
PersistReasoningFor, RefreshInstructions), MCP lifecycle
(InitMcpServers, StopMcpServer), local side-effects
(WriteImageToTemp, OpenInSystem, PullOllamaModel,
SetTerminalTitle, DismissStatusAfter). Cancellation flows
through Cmd::CancelScope(TurnId) → the scope’s
CancellationToken.
Structs§
- Effect
Runner - The runner. One instance per process, constructed by
app::runand consumed when the main loop exits. - Turn
Scope - One turn’s cancellable scope. Construct once per
SubmitPrompt; abandon (drop) at the end of the turn.
Constants§
- DEFAULT_
MAX_ ATTEMPTS - Total attempts (initial + retries). 3 attempts means up to 2 retries on top of the first request, costing at most ~1.5s of extra latency on the worst path (500ms + 1000ms backoff).
- MSG_
CHANNEL_ CAPACITY - Bounded channel capacity for the effect → reducer stream. 512 is generous — a single streaming chunk fits comfortably, and the main loop drains at ~60 Hz so backlog rarely grows. Bigger wastes RAM; smaller introduces spurious backpressure on bursty tool output.
Functions§
- retry_
transient_ http - Retry a closure whose output is
Result<reqwest::Response>whenever the response was a transient upstream failure (5xx / 429 / connection failed). Returns the first non-transient response, or the last result after attempts are exhausted.
Type Aliases§
- MsgSender
- Single channel back to the reducer.
EffectRunnerholds the sender; every spawned task clones this so it can emitMsgas work progresses. Bounded capacity applies natural backpressure — if the main loop can’t keep up, the provider’s streaming send.awaits and the whole pipeline throttles.