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("session_store", &self.session_store.is_some())
44 .field("session_id", &self.session_id)
45 .field("rl_trajectory", &self.rl_trajectory)
46 .field("llm_logprobs", &self.llm_logprobs)
47 .field("llm_top_logprobs", &self.llm_top_logprobs)
48 .field("auto_save", &self.auto_save)
49 .field("artifact_store_limits", &self.artifact_store_limits)
50 .field("max_parse_retries", &self.max_parse_retries)
51 .field("tool_timeout_ms", &self.tool_timeout_ms)
52 .field("llm_api_timeout_ms", &self.llm_api_timeout_ms)
53 .field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
54 .field("sandbox_handle", &self.sandbox_handle.is_some())
55 .field("workspace_services", &self.workspace_services.is_some())
56 .field("auto_compact", &self.auto_compact)
57 .field("auto_compact_threshold", &self.auto_compact_threshold)
58 .field("continuation_enabled", &self.continuation_enabled)
59 .field("max_continuation_turns", &self.max_continuation_turns)
60 .field("mcp_manager", &self.mcp_manager.is_some())
61 .field("temperature", &self.temperature)
62 .field("thinking_budget", &self.thinking_budget)
63 .field("max_tool_rounds", &self.max_tool_rounds)
64 .field("max_parallel_tasks", &self.max_parallel_tasks)
65 .field("auto_delegation", &self.auto_delegation)
66 .field("manual_delegation_enabled", &self.manual_delegation_enabled)
67 .field("auto_parallel_delegation", &self.auto_parallel_delegation)
68 .field("prompt_slots", &self.prompt_slots.is_some())
69 .finish()
70 }
71}
72
73impl SessionOptions {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_model(mut self, model: impl Into<String>) -> Self {
79 self.model = Some(model.into());
80 self
81 }
82
83 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
84 self.agent_dirs.push(dir.into());
85 self
86 }
87
88 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
90 self.worker_agents.push(spec);
91 self
92 }
93
94 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
96 where
97 I: IntoIterator<Item = WorkerAgentSpec>,
98 {
99 self.worker_agents.extend(specs);
100 self
101 }
102
103 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
104 self.queue_config = Some(config);
105 self
106 }
107
108 pub fn with_default_security(mut self) -> Self {
110 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
111 self
112 }
113
114 pub fn with_security_provider(
116 mut self,
117 provider: Arc<dyn crate::security::SecurityProvider>,
118 ) -> Self {
119 self.security_provider = Some(provider);
120 self
121 }
122
123 pub fn with_llm_client(mut self, client: Arc<dyn crate::llm::LlmClient>) -> Self {
131 self.llm_client = Some(client);
132 self
133 }
134
135 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
137 let config = crate::context::FileSystemContextConfig::new(root_path);
138 self.context_providers
139 .push(Arc::new(crate::context::FileSystemContextProvider::new(
140 config,
141 )));
142 self
143 }
144
145 pub fn with_context_provider(
147 mut self,
148 provider: Arc<dyn crate::context::ContextProvider>,
149 ) -> Self {
150 self.context_providers.push(provider);
151 self
152 }
153
154 pub fn with_confirmation_manager(
156 mut self,
157 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
158 ) -> Self {
159 self.confirmation_manager = Some(manager);
160 self
161 }
162
163 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
168 self.confirmation_policy = Some(policy);
169 self
170 }
171
172 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
174 self.permission_checker = Some(Arc::new(policy.clone()));
175 self.permission_policy = Some(policy);
176 self
177 }
178
179 pub fn with_permission_checker(
181 mut self,
182 checker: Arc<dyn crate::permissions::PermissionChecker>,
183 ) -> Self {
184 self.permission_checker = Some(checker);
185 self
186 }
187
188 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
190 self.planning_mode = mode;
191 self
192 }
193
194 pub fn with_planning(mut self, enabled: bool) -> Self {
196 self.planning_mode = if enabled {
197 PlanningMode::Enabled
198 } else {
199 PlanningMode::Disabled
200 };
201 self
202 }
203
204 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
206 self.goal_tracking = enabled;
207 self
208 }
209
210 pub fn with_builtin_skills(mut self) -> Self {
212 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
213 self
214 }
215
216 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
218 self.skill_registry = Some(registry);
219 self
220 }
221
222 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
227 self.enforce_active_skill_tool_restrictions = Some(enabled);
228 self
229 }
230
231 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
234 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
235 self
236 }
237
238 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
240 let registry = self
241 .skill_registry
242 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
243 if let Err(e) = registry.load_from_dir(&dir) {
244 tracing::warn!(
245 dir = %dir.as_ref().display(),
246 error = %e,
247 "Failed to load skills from directory — continuing without them"
248 );
249 }
250 self.skill_registry = Some(registry);
251 self
252 }
253
254 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
258 self.memory_store = Some(store);
259 self
260 }
261
262 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
268 self.file_memory_dir = Some(dir.into());
269 self
270 }
271
272 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
274 self.session_store = Some(store);
275 self
276 }
277
278 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
280 let dir = dir.into();
281 match tokio::runtime::Handle::try_current() {
282 Ok(handle) => {
283 match tokio::task::block_in_place(|| {
284 handle.block_on(crate::store::FileSessionStore::new(dir))
285 }) {
286 Ok(store) => {
287 self.session_store =
288 Some(Arc::new(store) as Arc<dyn crate::store::SessionStore>);
289 }
290 Err(e) => {
291 tracing::warn!("Failed to create file session store: {}", e);
292 }
293 }
294 }
295 Err(_) => {
296 tracing::warn!(
297 "No async runtime available for file session store — persistence disabled"
298 );
299 }
300 }
301 self
302 }
303
304 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
306 self.session_id = Some(id.into());
307 self
308 }
309
310 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
313 self.tenant_id = Some(tenant.into());
314 self
315 }
316
317 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
320 self.principal = Some(principal.into());
321 self
322 }
323
324 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
327 self.agent_template_id = Some(template_id.into());
328 self
329 }
330
331 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
334 self.correlation_id = Some(corr.into());
335 self
336 }
337
338 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
343 self.budget_guard = Some(guard);
344 self
345 }
346
347 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
353 self.host_env = Some(env);
354 self
355 }
356
357 pub fn with_retention_limits(
367 mut self,
368 limits: crate::retention::SessionRetentionLimits,
369 ) -> Self {
370 self.retention_limits = Some(limits);
371 self
372 }
373
374 pub fn with_rl_trajectory(mut self, config: crate::rl_trajectory::RlTrajectoryConfig) -> Self {
380 self.rl_trajectory = Some(config);
381 self
382 }
383
384 pub fn with_llm_logprobs(mut self, enabled: bool) -> Self {
386 self.llm_logprobs = Some(enabled);
387 self
388 }
389
390 pub fn with_llm_top_logprobs(mut self, top_logprobs: usize) -> Self {
392 self.llm_logprobs = Some(true);
393 self.llm_top_logprobs = Some(top_logprobs);
394 self
395 }
396
397 pub fn with_auto_save(mut self, enabled: bool) -> Self {
399 self.auto_save = enabled;
400 self
401 }
402
403 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
405 self.artifact_store_limits = Some(limits);
406 self
407 }
408
409 pub fn with_parse_retries(mut self, max: u32) -> Self {
415 self.max_parse_retries = Some(max);
416 self
417 }
418
419 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
425 self.tool_timeout_ms = Some(timeout_ms);
426 self
427 }
428
429 pub fn with_llm_api_timeout(mut self, timeout_ms: u64) -> Self {
435 self.llm_api_timeout_ms = Some(timeout_ms);
436 self
437 }
438
439 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
445 self.circuit_breaker_threshold = Some(threshold);
446 self
447 }
448
449 pub fn with_resilience_defaults(self) -> Self {
455 self.with_parse_retries(2)
456 .with_tool_timeout(120_000)
457 .with_circuit_breaker(3)
458 }
459
460 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
468 self.sandbox_handle = Some(handle);
469 self
470 }
471
472 pub fn with_workspace_backend(
478 mut self,
479 services: Arc<crate::workspace::WorkspaceServices>,
480 ) -> Self {
481 self.workspace_services = Some(services);
482 self
483 }
484
485 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
490 self.auto_compact = enabled;
491 self
492 }
493
494 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
496 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
497 self
498 }
499
500 pub fn with_continuation(mut self, enabled: bool) -> Self {
505 self.continuation_enabled = Some(enabled);
506 self
507 }
508
509 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
511 self.max_continuation_turns = Some(turns);
512 self
513 }
514
515 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
520 self.mcp_manager = Some(manager);
521 self
522 }
523
524 pub fn with_temperature(mut self, temperature: f32) -> Self {
525 self.temperature = Some(temperature);
526 self
527 }
528
529 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
530 self.thinking_budget = Some(budget);
531 self
532 }
533
534 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
539 self.max_tool_rounds = Some(rounds);
540 self
541 }
542
543 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
545 self.max_parallel_tasks = Some(tasks.max(1));
546 self
547 }
548
549 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
551 self.auto_delegation = Some(config);
552 self
553 }
554
555 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
557 let mut config = self.auto_delegation.take().unwrap_or_default();
558 config.enabled = enabled;
559 self.auto_delegation = Some(config);
560 self
561 }
562
563 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
570 if let Some(config) = &mut self.auto_delegation {
571 config.allow_manual_delegation = enabled;
572 }
573 self.manual_delegation_enabled = Some(enabled);
574 self
575 }
576
577 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
581 if let Some(config) = &mut self.auto_delegation {
582 config.auto_parallel = enabled;
583 }
584 self.auto_parallel_delegation = Some(enabled);
585 self
586 }
587
588 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
593 self.prompt_slots = Some(slots);
594 self
595 }
596
597 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
603 self.hook_executor = Some(executor);
604 self
605 }
606}