Normandy.Agents.Turn.Inline (normandy v1.0.0)

View Source

Inline (synchronous) interpreter for the pure Normandy.Agents.Turn core.

Drives a turn to completion in the calling process: it feeds :start into Turn.step/2, performs each returned effect's side-effect, and feeds the resulting event back into step/2 until the turn reaches :stopped (returns {:ok, state}) or :failed (returns {:error, reason, state}).

This is the library / scripted-run shell. It does NOT (yet) replace BaseAgent.run/2; it exists to prove the FSM core runs a real turn against a real Dispatch chokepoint. Compaction at the :steering boundary is supported via the optional :compact dep (default no-op); streaming, guardrails, validation, persistence and approval shells come in later phases.

deps is a map of side-effecting functions:

  • :call_llmfn request -> {:ok, response} | {:error, reason} end (required)

  • :dispatchfn calls -> [%ToolResult{}] end (required)
  • :appendfn role, content -> any end (optional, defaults to no-op)
  • :emitfn name, meta -> any end (optional, defaults to no-op)
  • :convertfn raw, response_model -> converted end (optional, defaults to identity:
              returns `raw` unchanged)
  • :validatefn value -> validated end (optional, defaults to identity: returns value
              unchanged)
  • :guardfn value -> any end (optional, defaults to no-op: side-effecting, return
              is ignored and the input `value` is fed forward via `{:output_guarded, value}`)
  • :compactfn info -> any end (optional, defaults to no-op). Invoked at the
              `:steering` boundary; the inline shell does not thread memory, so
              a real impl must compact external state by side effect.

By design, step/2 always places the single blocking/terminal effect (:call_llm, :dispatch_tools, :finalize, :fail) last in its effect list, so the interpreter performs the leading :append_message / :emit_event effects in order and then acts on the terminal one.

Summary

Functions

run(state, deps)