1use 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("llm_client", &self.llm_client.is_some())
25 .field("context_providers", &self.context_providers.len())
26 .field("confirmation_manager", &self.confirmation_manager.is_some())
27 .field("permission_checker", &self.permission_checker.is_some())
28 .field("permission_policy", &self.permission_policy.is_some())
29 .field("planning_mode", &self.planning_mode)
30 .field("goal_tracking", &self.goal_tracking)
31 .field(
32 "skill_registry",
33 &self
34 .skill_registry
35 .as_ref()
36 .map(|r| format!("{} skills", r.len())),
37 )
38 .field(
39 "enforce_active_skill_tool_restrictions",
40 &self.enforce_active_skill_tool_restrictions,
41 )
42 .field("memory_store", &self.memory_store.is_some())
43 .field("memory_observers", &self.memory_observers.len())
44 .field("file_memory_dir", &self.file_memory_dir)
45 .field("session_store", &self.session_store.is_some())
46 .field("file_session_store_dir", &self.file_session_store_dir)
47 .field("session_id", &self.session_id)
48 .field("rl_trajectory", &self.rl_trajectory)
49 .field("llm_logprobs", &self.llm_logprobs)
50 .field("llm_top_logprobs", &self.llm_top_logprobs)
51 .field("auto_save", &self.auto_save)
52 .field("artifact_store_limits", &self.artifact_store_limits)
53 .field("max_parse_retries", &self.max_parse_retries)
54 .field("tool_timeout_ms", &self.tool_timeout_ms)
55 .field("llm_api_timeout_ms", &self.llm_api_timeout_ms)
56 .field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
57 .field(
58 "duplicate_tool_call_threshold",
59 &self.duplicate_tool_call_threshold,
60 )
61 .field("sandbox_handle", &self.sandbox_handle.is_some())
62 .field("workspace_services", &self.workspace_services.is_some())
63 .field("auto_compact", &self.auto_compact)
64 .field("auto_compact_threshold", &self.auto_compact_threshold)
65 .field("max_context_tokens", &self.max_context_tokens)
66 .field("continuation_enabled", &self.continuation_enabled)
67 .field("max_continuation_turns", &self.max_continuation_turns)
68 .field("mcp_manager", &self.mcp_manager.is_some())
69 .field("temperature", &self.temperature)
70 .field("thinking_budget", &self.thinking_budget)
71 .field("max_tool_rounds", &self.max_tool_rounds)
72 .field("max_parallel_tasks", &self.max_parallel_tasks)
73 .field("auto_delegation", &self.auto_delegation)
74 .field("manual_delegation_enabled", &self.manual_delegation_enabled)
75 .field("auto_parallel_delegation", &self.auto_parallel_delegation)
76 .field("prompt_slots", &self.prompt_slots.is_some())
77 .finish()
78 }
79}
80
81impl SessionOptions {
82 pub fn new() -> Self {
83 Self::default()
84 }
85
86 pub fn with_model(mut self, model: impl Into<String>) -> Self {
87 self.model = Some(model.into());
88 self
89 }
90
91 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
92 self.agent_dirs.push(dir.into());
93 self
94 }
95
96 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
98 self.worker_agents.push(spec);
99 self
100 }
101
102 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
104 where
105 I: IntoIterator<Item = WorkerAgentSpec>,
106 {
107 self.worker_agents.extend(specs);
108 self
109 }
110
111 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
112 self.queue_config = Some(config);
113 self
114 }
115
116 pub fn with_default_security(mut self) -> Self {
118 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
119 self
120 }
121
122 pub fn with_security_provider(
124 mut self,
125 provider: Arc<dyn crate::security::SecurityProvider>,
126 ) -> Self {
127 self.security_provider = Some(provider);
128 self
129 }
130
131 pub fn with_llm_client(mut self, client: Arc<dyn crate::llm::LlmClient>) -> Self {
139 self.llm_client = Some(client);
140 self
141 }
142
143 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
145 let config = crate::context::FileSystemContextConfig::new(root_path);
146 self.context_providers
147 .push(Arc::new(crate::context::FileSystemContextProvider::new(
148 config,
149 )));
150 self
151 }
152
153 pub fn with_context_provider(
155 mut self,
156 provider: Arc<dyn crate::context::ContextProvider>,
157 ) -> Self {
158 self.context_providers.push(provider);
159 self
160 }
161
162 pub fn with_confirmation_manager(
164 mut self,
165 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
166 ) -> Self {
167 self.confirmation_manager = Some(manager);
168 self
169 }
170
171 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
176 self.confirmation_policy = Some(policy);
177 self
178 }
179
180 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
182 self.permission_checker = Some(Arc::new(policy.clone()));
183 self.permission_policy = Some(policy);
184 self
185 }
186
187 pub fn with_permission_checker(
189 mut self,
190 checker: Arc<dyn crate::permissions::PermissionChecker>,
191 ) -> Self {
192 self.permission_checker = Some(checker);
193 self
194 }
195
196 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
198 self.planning_mode = mode;
199 self
200 }
201
202 pub fn with_planning(mut self, enabled: bool) -> Self {
204 self.planning_mode = if enabled {
205 PlanningMode::Enabled
206 } else {
207 PlanningMode::Disabled
208 };
209 self
210 }
211
212 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
214 self.goal_tracking = enabled;
215 self
216 }
217
218 pub fn with_builtin_skills(mut self) -> Self {
224 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
225 self
226 }
227
228 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
230 self.skill_registry = Some(registry);
231 self
232 }
233
234 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
239 self.enforce_active_skill_tool_restrictions = Some(enabled);
240 self
241 }
242
243 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
247 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
248 self
249 }
250
251 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
253 let registry = self
254 .skill_registry
255 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
256 if let Err(e) = registry.load_from_dir(&dir) {
257 tracing::warn!(
258 dir = %dir.as_ref().display(),
259 error = %e,
260 "Failed to load skills from directory — continuing without them"
261 );
262 }
263 self.skill_registry = Some(registry);
264 self
265 }
266
267 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
271 self.memory_store = Some(store);
272 self.file_memory_dir = None;
273 self
274 }
275
276 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
282 self.memory_store = None;
283 self.file_memory_dir = Some(dir.into());
284 self
285 }
286
287 pub fn with_memory_observer(
291 mut self,
292 observer: Arc<dyn crate::memory::MemoryObserver>,
293 ) -> Self {
294 self.memory_observers.push(observer);
295 self
296 }
297
298 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
300 self.session_store = Some(store);
301 self.file_session_store_dir = None;
302 self
303 }
304
305 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
310 self.session_store = None;
311 self.file_session_store_dir = Some(dir.into());
312 self
313 }
314
315 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
317 self.session_id = Some(id.into());
318 self
319 }
320
321 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
324 self.tenant_id = Some(tenant.into());
325 self
326 }
327
328 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
331 self.principal = Some(principal.into());
332 self
333 }
334
335 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
338 self.agent_template_id = Some(template_id.into());
339 self
340 }
341
342 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
345 self.correlation_id = Some(corr.into());
346 self
347 }
348
349 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
354 self.budget_guard = Some(guard);
355 self
356 }
357
358 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
364 self.host_env = Some(env);
365 self
366 }
367
368 pub fn with_retention_limits(
378 mut self,
379 limits: crate::retention::SessionRetentionLimits,
380 ) -> Self {
381 self.retention_limits = Some(limits);
382 self
383 }
384
385 pub fn with_rl_trajectory(mut self, config: crate::rl_trajectory::RlTrajectoryConfig) -> Self {
391 self.rl_trajectory = Some(config);
392 self
393 }
394
395 pub fn with_llm_logprobs(mut self, enabled: bool) -> Self {
397 self.llm_logprobs = Some(enabled);
398 self
399 }
400
401 pub fn with_llm_top_logprobs(mut self, top_logprobs: usize) -> Self {
403 self.llm_logprobs = Some(true);
404 self.llm_top_logprobs = Some(top_logprobs);
405 self
406 }
407
408 pub fn with_auto_save(mut self, enabled: bool) -> Self {
410 self.auto_save = enabled;
411 self
412 }
413
414 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
416 self.artifact_store_limits = Some(limits);
417 self
418 }
419
420 pub fn with_parse_retries(mut self, max: u32) -> Self {
426 self.max_parse_retries = Some(max);
427 self
428 }
429
430 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
436 self.tool_timeout_ms = Some(timeout_ms);
437 self
438 }
439
440 pub fn with_llm_api_timeout(mut self, timeout_ms: u64) -> Self {
446 self.llm_api_timeout_ms = Some(timeout_ms);
447 self
448 }
449
450 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
456 self.circuit_breaker_threshold = Some(threshold);
457 self
458 }
459
460 pub fn with_duplicate_tool_call_threshold(mut self, threshold: u32) -> Self {
466 self.duplicate_tool_call_threshold = Some(threshold.max(1));
467 self
468 }
469
470 pub fn with_resilience_defaults(self) -> Self {
476 self.with_parse_retries(2)
477 .with_tool_timeout(120_000)
478 .with_circuit_breaker(3)
479 }
480
481 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
489 self.sandbox_handle = Some(handle);
490 self
491 }
492
493 pub fn with_workspace_backend(
499 mut self,
500 services: Arc<crate::workspace::WorkspaceServices>,
501 ) -> Self {
502 self.workspace_services = Some(services);
503 self
504 }
505
506 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
511 self.auto_compact = enabled;
512 self
513 }
514
515 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
517 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
518 self
519 }
520
521 pub fn with_max_context_tokens(mut self, tokens: usize) -> Self {
523 self.max_context_tokens = Some(tokens);
524 self
525 }
526
527 pub fn with_continuation(mut self, enabled: bool) -> Self {
532 self.continuation_enabled = Some(enabled);
533 self
534 }
535
536 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
538 self.max_continuation_turns = Some(turns);
539 self
540 }
541
542 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
549 self.mcp_manager = Some(manager);
550 self
551 }
552
553 pub fn with_temperature(mut self, temperature: f32) -> Self {
554 self.temperature = Some(temperature);
555 self
556 }
557
558 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
559 self.thinking_budget = Some(budget);
560 self
561 }
562
563 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
568 self.max_tool_rounds = Some(rounds);
569 self
570 }
571
572 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
574 self.max_parallel_tasks = Some(tasks.max(1));
575 self
576 }
577
578 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
580 self.auto_delegation = Some(config);
581 self
582 }
583
584 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
586 let mut config = self.auto_delegation.take().unwrap_or_default();
587 config.enabled = enabled;
588 self.auto_delegation = Some(config);
589 self
590 }
591
592 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
599 if let Some(config) = &mut self.auto_delegation {
600 config.allow_manual_delegation = enabled;
601 }
602 self.manual_delegation_enabled = Some(enabled);
603 self
604 }
605
606 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
610 if let Some(config) = &mut self.auto_delegation {
611 config.auto_parallel = enabled;
612 }
613 self.auto_parallel_delegation = Some(enabled);
614 self
615 }
616
617 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
622 self.prompt_slots = Some(slots);
623 self
624 }
625
626 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
631 self.hook_executor = Some(executor);
632 self
633 }
634}