A complete record of one agent-loop execution, stored as LoopRecord. Loops are the iterations within a Session. Each Loop contains Turns (steps), tracks its model/provider configuration, accumulates usage, and links to parent/child loops for tree navigation.
Loops are created by agent_loop (origin loops) or agent_loop_continue (continuation loops). The SessionRecorder materializes LoopRecord structs from the AgentStart / AgentEnd event pairs.
Unique identifier. Format: "{session_id}.{config_segment}.{N}". The config_segment encodes which model/provider produced this loop. N is a monotonic counter per (session, config).
session_id
String
[EXISTS]
Session this loop belongs to.
agent_id
String
[EXISTS]
Agent that ran this loop.
status
LoopStatus
[EXISTS]
Lifecycle state: Pending, Running, Completed, Rejected, Aborted. See Status section below.
continuation_kind
ContinuationKind
[EXISTS]
How this loop relates to its parent. Initial for origin loops (agent_loop). Default for regular continuations. Rerun for retries. Branch for branch explorations. Compaction for standalone compaction passes.
parent_loop_id
Option<String>
[EXISTS]
The loop that directly preceded this one. None for origin loops. For sub-agent loops, points to the tool-call loop in a different session.
started_at
DateTime<Utc>
[EXISTS]
Timestamp from AgentStart.
ended_at
Option<DateTime<Utc>>
[EXISTS]
Timestamp from AgentEnd. None while running or pending.
rejection
Option<String>
[EXISTS]
Set when AgentEnd.rejection is Some (input filter blocked the run).
metadata
Option<serde_json::Value>
[EXISTS]
Opaque caller-supplied metadata from AgentStart (e.g., request id, trace ID).
The model/provider identity is captured as a lightweight snapshot, not the full config (which contains secrets and non-serializable closures).
Field
Type
Status
Description
config
Option<LoopConfigSnapshot>
[EXISTS]
Populated from AgentStart.config_snapshot or the first Message::Assistant seen. None if loop ended before any assistant message and no snapshot was provided.
config.model
String
[EXISTS]
Model id string (e.g., "claude-opus-4-6", "gpt-4o").
config.provider
String
[EXISTS]
Provider name (e.g., "anthropic", "openai").
config.config_id
Option<String>
[EXISTS]
Stable config identity from AgentLoopConfig.config_id. Matches the config_segment in loop_id.
config.name
Option<String>
[EXISTS]
Model display name.
config.api
Option<ApiProtocol>
[EXISTS]
Which API protocol was used (e.g., AnthropicMessages, OpenAiCompletions).
config.base_url
Option<String>
[EXISTS]
Provider base URL.
config.reasoning
Option<bool>
[EXISTS]
Whether this model supports reasoning/thinking.
config.context_window
Option<u32>
[EXISTS]
Context window size in tokens.
config.max_tokens
Option<u32>
[EXISTS]
Max output tokens per response.
config.thinking_level
Option<ThinkingLevel>
[EXISTS]
Reasoning depth level for this loop. Formerly a Session-level attribute; now per-loop.
config.temperature
Option<f32>
[EXISTS]
Sampling temperature. Formerly a Session-level attribute; now per-loop.
Model fallback hierarchy: Loop (AgentLoopConfig.model_config) -> Agent default (BasicAgent.model_config).
Model fallback is Loop -> Agent default. Session no longer carries model/thinking/temperature fields; these are tracked per-loop in LoopConfigSnapshot.
Turns as a struct are materialized on LoopRecord.turns as Vec<Turn>. Built by SessionRecorder from TurnStart/TurnEnd event pairs. The flat messages field is kept independently for compaction and context building. Old sessions without turns deserialize with an empty vec.
LoopConfigSnapshot intentionally does not store the full AgentLoopConfig because it contains API keys and non-serializable hook closures. The snapshot captures model identity plus key parameters (thinking_level, temperature, context_window, max_tokens, etc.) for cost attribution, replay identification, parallel branch differentiation, and per-loop config tracking.