swink_agent/loop_/config.rs
1//! Configuration for the agent loop.
2
3use std::sync::Arc;
4
5use crate::agent_options::{ApproveToolFn, GetApiKeyFn};
6use crate::async_context_transformer::AsyncContextTransformer;
7use crate::fallback::ModelFallback;
8use crate::message_provider::MessageProvider;
9use crate::retry::RetryStrategy;
10use crate::stream::{StreamFn, StreamOptions};
11use crate::tool::{AgentTool, ApprovalMode};
12use crate::tool_execution_policy::ToolExecutionPolicy;
13use crate::types::ModelSpec;
14
15use super::ConvertToLlmFn;
16
17// ─── AgentLoopConfig ─────────────────────────────────────────────────────────
18
19/// Configuration for the agent loop.
20///
21/// Carries the model spec, stream options, retry strategy, stream function,
22/// tools, and all the hooks that the loop calls at various points.
23pub struct AgentLoopConfig {
24 /// Optional agent name used for transfer chain safety enforcement.
25 ///
26 /// When set, the loop pushes this name onto the [`TransferChain`](crate::transfer::TransferChain)
27 /// at startup so circular transfers back to this agent are detected.
28 pub agent_name: Option<String>,
29 /// Optional transfer chain carried from a previous handoff.
30 ///
31 /// When set, the loop resumes transfer safety checks from this chain.
32 pub transfer_chain: Option<crate::transfer::TransferChain>,
33
34 /// Model specification passed through to `StreamFn`.
35 pub model: ModelSpec,
36
37 /// Stream options passed through to `StreamFn`.
38 pub stream_options: StreamOptions,
39
40 /// Retry strategy applied to model calls.
41 pub retry_strategy: Box<dyn RetryStrategy>,
42
43 /// The pluggable streaming function that calls the LLM provider.
44 pub stream_fn: Arc<dyn StreamFn>,
45
46 /// Available tools for the agent to call.
47 pub tools: Vec<Arc<dyn AgentTool>>,
48
49 /// Converts an `AgentMessage` to an `LlmMessage` for the provider.
50 /// Returns `None` to filter out custom or UI-only messages.
51 pub convert_to_llm: Box<ConvertToLlmFn>,
52
53 /// Optional hook called before `convert_to_llm`; used for context pruning,
54 /// token budget enforcement, or external context injection.
55 /// When the overflow signal is set, the transformer should prune more
56 /// aggressively.
57 pub transform_context: Option<Arc<dyn crate::context_transformer::ContextTransformer>>,
58
59 /// Optional async callback for dynamic API key resolution.
60 pub get_api_key: Option<Box<GetApiKeyFn>>,
61
62 /// Optional provider polled for steering and follow-up messages.
63 ///
64 /// [`MessageProvider::poll_steering`] is called after each tool execution batch.
65 /// [`MessageProvider::poll_follow_up`] is called when the agent would otherwise stop.
66 pub message_provider: Option<Arc<dyn MessageProvider>>,
67
68 /// Shared snapshot of loop-local pending messages for pause checkpoints.
69 #[allow(private_interfaces)]
70 pub pending_message_snapshot: Arc<crate::pause_state::PendingMessageSnapshot>,
71
72 /// Shared snapshot of the loop's full `context_messages` for pause checkpoints.
73 ///
74 /// Updated after each turn's pending-message drain so that `Agent::pause()`
75 /// can reconstruct the complete message history even for messages that have
76 /// been moved out of the shared pending queue and into loop-local context.
77 #[allow(private_interfaces)]
78 pub loop_context_snapshot: Arc<crate::pause_state::LoopContextSnapshot>,
79
80 /// Optional async callback for approving/rejecting tool calls before execution.
81 /// When `Some` and `approval_mode` is `Enabled`, each tool call is sent through
82 /// this callback before dispatch. Rejected tools return an error result to the LLM.
83 pub approve_tool: Option<Box<ApproveToolFn>>,
84
85 /// Controls whether the approval gate is active. Defaults to `Enabled`.
86 pub approval_mode: ApprovalMode,
87
88 /// Pre-turn policies evaluated before each LLM call.
89 pub pre_turn_policies: Vec<Arc<dyn crate::policy::PreTurnPolicy>>,
90
91 /// Pre-dispatch policies evaluated per tool call, before approval.
92 pub pre_dispatch_policies: Vec<Arc<dyn crate::policy::PreDispatchPolicy>>,
93
94 /// Post-turn policies evaluated after each completed turn.
95 pub post_turn_policies: Vec<Arc<dyn crate::policy::PostTurnPolicy>>,
96
97 /// Post-loop policies evaluated after the inner loop exits.
98 pub post_loop_policies: Vec<Arc<dyn crate::policy::PostLoopPolicy>>,
99
100 /// Optional async context transformer (runs before the sync transformer).
101 ///
102 /// Enables async operations like fetching summaries or RAG retrieval
103 /// before context compaction.
104 pub async_transform_context: Option<Arc<dyn AsyncContextTransformer>>,
105
106 /// Optional metrics collector invoked at the end of each turn with
107 /// per-turn timing, token usage, and cost data.
108 pub metrics_collector: Option<Arc<dyn crate::metrics::MetricsCollector>>,
109
110 /// Optional model fallback chain tried when the primary model exhausts
111 /// its retry budget on a retryable error.
112 pub fallback: Option<ModelFallback>,
113
114 /// Controls how tool calls within a turn are dispatched.
115 ///
116 /// Defaults to [`ToolExecutionPolicy::Concurrent`] for backward
117 /// compatibility.
118 pub tool_execution_policy: ToolExecutionPolicy,
119
120 /// Session key-value state store shared with tools and policies.
121 pub session_state: Arc<std::sync::RwLock<crate::SessionState>>,
122
123 /// Optional credential resolver for tool authentication.
124 pub credential_resolver: Option<Arc<dyn crate::credential::CredentialResolver>>,
125
126 /// Optional context caching configuration.
127 pub cache_config: Option<crate::context_cache::CacheConfig>,
128
129 /// Mutable cache state tracking turns since last write.
130 pub cache_state: std::sync::Mutex<crate::context_cache::CacheState>,
131
132 /// Optional dynamic system prompt closure (called fresh each turn).
133 ///
134 /// Its output is injected as a user-role message after the system prompt
135 /// to avoid invalidating provider-side caches.
136 pub dynamic_system_prompt: Option<Arc<dyn Fn() -> String + Send + Sync>>,
137}
138
139impl std::fmt::Debug for AgentLoopConfig {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 f.debug_struct("AgentLoopConfig")
142 .field("model", &self.model)
143 .field("stream_options", &self.stream_options)
144 .field("tools", &format_args!("[{} tool(s)]", self.tools.len()))
145 .field(
146 "pre_turn_policies",
147 &format_args!("[{} policy(ies)]", self.pre_turn_policies.len()),
148 )
149 .field(
150 "pre_dispatch_policies",
151 &format_args!("[{} policy(ies)]", self.pre_dispatch_policies.len()),
152 )
153 .field(
154 "post_turn_policies",
155 &format_args!("[{} policy(ies)]", self.post_turn_policies.len()),
156 )
157 .field(
158 "post_loop_policies",
159 &format_args!("[{} policy(ies)]", self.post_loop_policies.len()),
160 )
161 .field("tool_execution_policy", &self.tool_execution_policy)
162 .finish_non_exhaustive()
163 }
164}