a3s_code_core/agent_api/
session_options.rs1use super::SessionOptions;
8use crate::prompts::{PlanningMode, SystemPromptSlots};
9use crate::queue::SessionQueueConfig;
10use crate::subagent::WorkerAgentSpec;
11use a3s_memory::MemoryStore;
12use std::path::PathBuf;
13use std::sync::Arc;
14
15impl std::fmt::Debug for SessionOptions {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 f.debug_struct("SessionOptions")
18 .field("model", &self.model)
19 .field("agent_dirs", &self.agent_dirs)
20 .field("worker_agents", &self.worker_agents.len())
21 .field("skill_dirs", &self.skill_dirs)
22 .field("queue_config", &self.queue_config)
23 .field("security_provider", &self.security_provider.is_some())
24 .field("context_providers", &self.context_providers.len())
25 .field("confirmation_manager", &self.confirmation_manager.is_some())
26 .field("permission_checker", &self.permission_checker.is_some())
27 .field("permission_policy", &self.permission_policy.is_some())
28 .field("planning_mode", &self.planning_mode)
29 .field("goal_tracking", &self.goal_tracking)
30 .field(
31 "skill_registry",
32 &self
33 .skill_registry
34 .as_ref()
35 .map(|r| format!("{} skills", r.len())),
36 )
37 .field("memory_store", &self.memory_store.is_some())
38 .field("session_store", &self.session_store.is_some())
39 .field("session_id", &self.session_id)
40 .field("auto_save", &self.auto_save)
41 .field("artifact_store_limits", &self.artifact_store_limits)
42 .field("max_parse_retries", &self.max_parse_retries)
43 .field("tool_timeout_ms", &self.tool_timeout_ms)
44 .field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
45 .field("sandbox_handle", &self.sandbox_handle.is_some())
46 .field("auto_compact", &self.auto_compact)
47 .field("auto_compact_threshold", &self.auto_compact_threshold)
48 .field("continuation_enabled", &self.continuation_enabled)
49 .field("max_continuation_turns", &self.max_continuation_turns)
50 .field("mcp_manager", &self.mcp_manager.is_some())
51 .field("temperature", &self.temperature)
52 .field("thinking_budget", &self.thinking_budget)
53 .field("max_tool_rounds", &self.max_tool_rounds)
54 .field("prompt_slots", &self.prompt_slots.is_some())
55 .finish()
56 }
57}
58
59impl SessionOptions {
60 pub fn new() -> Self {
61 Self::default()
62 }
63
64 pub fn with_model(mut self, model: impl Into<String>) -> Self {
65 self.model = Some(model.into());
66 self
67 }
68
69 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
70 self.agent_dirs.push(dir.into());
71 self
72 }
73
74 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
76 self.worker_agents.push(spec);
77 self
78 }
79
80 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
82 where
83 I: IntoIterator<Item = WorkerAgentSpec>,
84 {
85 self.worker_agents.extend(specs);
86 self
87 }
88
89 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
90 self.queue_config = Some(config);
91 self
92 }
93
94 pub fn with_default_security(mut self) -> Self {
96 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
97 self
98 }
99
100 pub fn with_security_provider(
102 mut self,
103 provider: Arc<dyn crate::security::SecurityProvider>,
104 ) -> Self {
105 self.security_provider = Some(provider);
106 self
107 }
108
109 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
111 let config = crate::context::FileSystemContextConfig::new(root_path);
112 self.context_providers
113 .push(Arc::new(crate::context::FileSystemContextProvider::new(
114 config,
115 )));
116 self
117 }
118
119 pub fn with_context_provider(
121 mut self,
122 provider: Arc<dyn crate::context::ContextProvider>,
123 ) -> Self {
124 self.context_providers.push(provider);
125 self
126 }
127
128 pub fn with_confirmation_manager(
130 mut self,
131 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
132 ) -> Self {
133 self.confirmation_manager = Some(manager);
134 self
135 }
136
137 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
142 self.confirmation_policy = Some(policy);
143 self
144 }
145
146 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
148 self.permission_checker = Some(Arc::new(policy.clone()));
149 self.permission_policy = Some(policy);
150 self
151 }
152
153 pub fn with_permission_checker(
155 mut self,
156 checker: Arc<dyn crate::permissions::PermissionChecker>,
157 ) -> Self {
158 self.permission_checker = Some(checker);
159 self
160 }
161
162 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
164 self.planning_mode = mode;
165 self
166 }
167
168 pub fn with_planning(mut self, enabled: bool) -> Self {
170 self.planning_mode = if enabled {
171 PlanningMode::Enabled
172 } else {
173 PlanningMode::Disabled
174 };
175 self
176 }
177
178 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
180 self.goal_tracking = enabled;
181 self
182 }
183
184 pub fn with_builtin_skills(mut self) -> Self {
186 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
187 self
188 }
189
190 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
192 self.skill_registry = Some(registry);
193 self
194 }
195
196 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
199 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
200 self
201 }
202
203 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
205 let registry = self
206 .skill_registry
207 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
208 if let Err(e) = registry.load_from_dir(&dir) {
209 tracing::warn!(
210 dir = %dir.as_ref().display(),
211 error = %e,
212 "Failed to load skills from directory — continuing without them"
213 );
214 }
215 self.skill_registry = Some(registry);
216 self
217 }
218
219 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
221 self.memory_store = Some(store);
222 self
223 }
224
225 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
231 self.file_memory_dir = Some(dir.into());
232 self
233 }
234
235 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
237 self.session_store = Some(store);
238 self
239 }
240
241 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
243 let dir = dir.into();
244 match tokio::runtime::Handle::try_current() {
245 Ok(handle) => {
246 match tokio::task::block_in_place(|| {
247 handle.block_on(crate::store::FileSessionStore::new(dir))
248 }) {
249 Ok(store) => {
250 self.session_store =
251 Some(Arc::new(store) as Arc<dyn crate::store::SessionStore>);
252 }
253 Err(e) => {
254 tracing::warn!("Failed to create file session store: {}", e);
255 }
256 }
257 }
258 Err(_) => {
259 tracing::warn!(
260 "No async runtime available for file session store — persistence disabled"
261 );
262 }
263 }
264 self
265 }
266
267 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
269 self.session_id = Some(id.into());
270 self
271 }
272
273 pub fn with_auto_save(mut self, enabled: bool) -> Self {
275 self.auto_save = enabled;
276 self
277 }
278
279 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
281 self.artifact_store_limits = Some(limits);
282 self
283 }
284
285 pub fn with_parse_retries(mut self, max: u32) -> Self {
291 self.max_parse_retries = Some(max);
292 self
293 }
294
295 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
301 self.tool_timeout_ms = Some(timeout_ms);
302 self
303 }
304
305 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
311 self.circuit_breaker_threshold = Some(threshold);
312 self
313 }
314
315 pub fn with_resilience_defaults(self) -> Self {
321 self.with_parse_retries(2)
322 .with_tool_timeout(120_000)
323 .with_circuit_breaker(3)
324 }
325
326 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
334 self.sandbox_handle = Some(handle);
335 self
336 }
337
338 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
343 self.auto_compact = enabled;
344 self
345 }
346
347 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
349 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
350 self
351 }
352
353 pub fn with_continuation(mut self, enabled: bool) -> Self {
358 self.continuation_enabled = Some(enabled);
359 self
360 }
361
362 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
364 self.max_continuation_turns = Some(turns);
365 self
366 }
367
368 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
373 self.mcp_manager = Some(manager);
374 self
375 }
376
377 pub fn with_temperature(mut self, temperature: f32) -> Self {
378 self.temperature = Some(temperature);
379 self
380 }
381
382 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
383 self.thinking_budget = Some(budget);
384 self
385 }
386
387 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
392 self.max_tool_rounds = Some(rounds);
393 self
394 }
395
396 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
401 self.prompt_slots = Some(slots);
402 self
403 }
404
405 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
411 self.hook_executor = Some(executor);
412 self
413 }
414}