Normandy.Agents.Turn
(normandy v1.1.1)
View Source
The pure finite-state-machine core of an agent turn.
A turn is plain data (Normandy.Agents.Turn.State) advanced by step/2, a
pure function: step(state, event) -> {state', [effect]}. It performs no I/O.
Events come from the shell (LLM responses, tool results, errors); effects are
data the shell interprets (call the LLM, dispatch tools, append to memory,
emit telemetry, finalize, fail). Keeping the core pure makes every transition
unit/property testable without processes, and makes the state serializable for
the durable/suspendable shells added in later phases.
States
Seven statuses are defined. :awaiting_approval (suspend/resume for human
approval) is entered when a dispatched batch parks calls. :steering is a
resting state entered at every tool-batch boundary: the core emits a
{:maybe_compact, info} effect there and resumes on {:compaction_done, _},
which is where context-window compaction (Phase 5) runs in the shell.
Summary
Functions
Builds the initial :provisioning state for a turn.
Re-derives the effects to continue a turn from a persisted, non-terminal state (used by an eager shell after passivation/handoff). Pure.
Advances the turn one event. Pure: returns {state', [effect]} and does no I/O.
Functions
@spec new(keyword()) :: Normandy.Agents.Turn.State.t()
Builds the initial :provisioning state for a turn.
Options: :max_iterations (default 5), :response_model (the response model
for normal tool-loop LLM calls, e.g. %ToolCallResponse{}), :output_schema
(the response model for the forced final call when the iteration cap is hit).
Raises ArgumentError if :max_iterations is not an integer >= 1, enforcing
the pos_integer() contract on State.max_iterations (mirrors the check
BaseAgent.init/1 applies at the shell boundary).
@spec resume(Normandy.Agents.Turn.State.t()) :: {Normandy.Agents.Turn.State.t(), [tuple()]}
Re-derives the effects to continue a turn from a persisted, non-terminal state (used by an eager shell after passivation/handoff). Pure.
Only states the core persists are resumable: :steering (per-batch boundary)
re-issues compaction then continues; :awaiting_approval waits for a decision
(no effects). Terminal states yield no effects.
@spec step(Normandy.Agents.Turn.State.t(), term()) :: {Normandy.Agents.Turn.State.t(), [tuple()]}
Advances the turn one event. Pure: returns {state', [effect]} and does no I/O.