# `PtcRunner.SubAgent.Loop.State`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/sub_agent/loop/state.ex#L1)

Typed state struct for the SubAgent execution loop.

All loop state fields live in one flat struct, providing compile-time field
checking and clear documentation of the state shape. TextMode-specific fields
(`schema`, `json_mode`, `tool_schemas`, etc.) default to `nil`/`0`/`[]`.

## Field Groups

**Core** — required for every run:
`llm`, `context`, `turn`, `messages`, `start_time`, `work_turns_remaining`

**Budget** — turn/token budget tracking:
`remaining_turns`, `work_turns_remaining`, `retry_turns_remaining`,
`token_limit`, `budget_callback`, `on_budget_exceeded`

**Metrics** — accumulated token/request counts:
`total_input_tokens`, `total_output_tokens`, `total_cache_creation_tokens`,
`total_cache_read_tokens`, `llm_requests`, `system_prompt_tokens`, `turn_tokens`

**TextMode** — only used by text output mode:
`schema`, `json_mode`, `tool_schemas`, `normalized_tools_map`,
`api_name_map`, `total_tool_calls`, `all_tool_calls`

**Transient** — set per-turn, not carried across runs:
`current_turn_type`, `current_system_prompt`, `current_messages`,
`compaction_stats`

# `t`

```elixir
@type t() :: %PtcRunner.SubAgent.Loop.State{
  agent_id: term(),
  agent_name: String.t() | atom() | nil,
  all_tool_calls: [map()],
  api_name_map: map() | nil,
  budget_callback: (map() -&gt; :continue | :stop) | nil,
  cache: boolean(),
  child_steps: [map()],
  collect_messages: boolean(),
  collected_system_prompt: String.t() | nil,
  combined_mode: boolean(),
  compaction_stats: map() | nil,
  context: map(),
  continuation_guard:
    (PtcRunner.Turn.t(), t(), t() -&gt;
       :continue | {:stop, {:ok | :error, PtcRunner.Step.t()}})
    | nil,
  current_messages: [map()] | nil,
  current_system_prompt: String.t() | nil,
  current_turn_type: :normal | :must_return | :retry | nil,
  debug: boolean(),
  discovery_exec: (atom(), list() -&gt; term()) | nil,
  expanded_prompt: String.t() | nil,
  initial_messages: [map()] | nil,
  journal: map() | nil,
  json_mode: boolean() | nil,
  last_fail: term(),
  last_return_error: String.t() | nil,
  llm: (map() -&gt; {:ok, map()} | {:error, term()}) | atom(),
  llm_registry: map() | nil,
  llm_requests: non_neg_integer(),
  llm_retry: map() | nil,
  max_heap: pos_integer() | nil,
  memory: map() | nil,
  messages: [map()],
  mission_deadline: DateTime.t() | nil,
  nesting_depth: non_neg_integer(),
  normalized_tools: map() | nil,
  normalized_tools_map: map() | nil,
  on_budget_exceeded: :return_partial | :fail | nil,
  on_chunk: (String.t() -&gt; term()) | nil,
  original_prompt: String.t() | nil,
  progress_state: term(),
  received_field_descriptions: map() | nil,
  remaining_turns: integer(),
  retry_turns_remaining: integer(),
  runtime: struct() | pid() | nil,
  schema: map() | nil,
  start_time: integer(),
  summaries: map(),
  system_prompt_tokens: non_neg_integer(),
  token_limit: pos_integer() | nil,
  tool_cache: map() | nil,
  tool_schemas: [map()] | nil,
  tools_meta: %{optional(String.t()) =&gt; PtcRunner.Tool.t()} | nil,
  total_cache_creation_tokens: non_neg_integer(),
  total_cache_read_tokens: non_neg_integer(),
  total_input_tokens: non_neg_integer(),
  total_output_tokens: non_neg_integer(),
  total_tool_calls: non_neg_integer(),
  trace_context: map() | nil,
  trace_mode: boolean() | :on_error,
  turn: pos_integer(),
  turn_history: [term()],
  turn_tokens: map() | nil,
  turns: [PtcRunner.Turn.t()],
  work_turns_remaining: integer()
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
