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(
55 "duplicate_tool_call_threshold",
56 &self.duplicate_tool_call_threshold,
57 )
58 .field("sandbox_handle", &self.sandbox_handle.is_some())
59 .field("workspace_services", &self.workspace_services.is_some())
60 .field("auto_compact", &self.auto_compact)
61 .field("auto_compact_threshold", &self.auto_compact_threshold)
62 .field("continuation_enabled", &self.continuation_enabled)
63 .field("max_continuation_turns", &self.max_continuation_turns)
64 .field("mcp_manager", &self.mcp_manager.is_some())
65 .field("temperature", &self.temperature)
66 .field("thinking_budget", &self.thinking_budget)
67 .field("max_tool_rounds", &self.max_tool_rounds)
68 .field("max_parallel_tasks", &self.max_parallel_tasks)
69 .field("auto_delegation", &self.auto_delegation)
70 .field("manual_delegation_enabled", &self.manual_delegation_enabled)
71 .field("auto_parallel_delegation", &self.auto_parallel_delegation)
72 .field("prompt_slots", &self.prompt_slots.is_some())
73 .finish()
74 }
75}
76
77impl SessionOptions {
78 pub fn new() -> Self {
79 Self::default()
80 }
81
82 pub fn with_model(mut self, model: impl Into<String>) -> Self {
83 self.model = Some(model.into());
84 self
85 }
86
87 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
88 self.agent_dirs.push(dir.into());
89 self
90 }
91
92 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
94 self.worker_agents.push(spec);
95 self
96 }
97
98 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
100 where
101 I: IntoIterator<Item = WorkerAgentSpec>,
102 {
103 self.worker_agents.extend(specs);
104 self
105 }
106
107 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
108 self.queue_config = Some(config);
109 self
110 }
111
112 pub fn with_default_security(mut self) -> Self {
114 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
115 self
116 }
117
118 pub fn with_security_provider(
120 mut self,
121 provider: Arc<dyn crate::security::SecurityProvider>,
122 ) -> Self {
123 self.security_provider = Some(provider);
124 self
125 }
126
127 pub fn with_llm_client(mut self, client: Arc<dyn crate::llm::LlmClient>) -> Self {
135 self.llm_client = Some(client);
136 self
137 }
138
139 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
141 let config = crate::context::FileSystemContextConfig::new(root_path);
142 self.context_providers
143 .push(Arc::new(crate::context::FileSystemContextProvider::new(
144 config,
145 )));
146 self
147 }
148
149 pub fn with_context_provider(
151 mut self,
152 provider: Arc<dyn crate::context::ContextProvider>,
153 ) -> Self {
154 self.context_providers.push(provider);
155 self
156 }
157
158 pub fn with_confirmation_manager(
160 mut self,
161 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
162 ) -> Self {
163 self.confirmation_manager = Some(manager);
164 self
165 }
166
167 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
172 self.confirmation_policy = Some(policy);
173 self
174 }
175
176 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
178 self.permission_checker = Some(Arc::new(policy.clone()));
179 self.permission_policy = Some(policy);
180 self
181 }
182
183 pub fn with_permission_checker(
185 mut self,
186 checker: Arc<dyn crate::permissions::PermissionChecker>,
187 ) -> Self {
188 self.permission_checker = Some(checker);
189 self
190 }
191
192 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
194 self.planning_mode = mode;
195 self
196 }
197
198 pub fn with_planning(mut self, enabled: bool) -> Self {
200 self.planning_mode = if enabled {
201 PlanningMode::Enabled
202 } else {
203 PlanningMode::Disabled
204 };
205 self
206 }
207
208 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
210 self.goal_tracking = enabled;
211 self
212 }
213
214 pub fn with_builtin_skills(mut self) -> Self {
220 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
221 self
222 }
223
224 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
226 self.skill_registry = Some(registry);
227 self
228 }
229
230 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
235 self.enforce_active_skill_tool_restrictions = Some(enabled);
236 self
237 }
238
239 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
242 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
243 self
244 }
245
246 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
248 let registry = self
249 .skill_registry
250 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
251 if let Err(e) = registry.load_from_dir(&dir) {
252 tracing::warn!(
253 dir = %dir.as_ref().display(),
254 error = %e,
255 "Failed to load skills from directory — continuing without them"
256 );
257 }
258 self.skill_registry = Some(registry);
259 self
260 }
261
262 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
266 self.memory_store = Some(store);
267 self
268 }
269
270 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
276 self.file_memory_dir = Some(dir.into());
277 self
278 }
279
280 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
282 self.session_store = Some(store);
283 self
284 }
285
286 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
288 let dir = dir.into();
289 match tokio::runtime::Handle::try_current() {
290 Ok(handle) => {
291 match tokio::task::block_in_place(|| {
292 handle.block_on(crate::store::FileSessionStore::new(dir))
293 }) {
294 Ok(store) => {
295 self.session_store =
296 Some(Arc::new(store) as Arc<dyn crate::store::SessionStore>);
297 }
298 Err(e) => {
299 tracing::warn!("Failed to create file session store: {}", e);
300 }
301 }
302 }
303 Err(_) => {
304 tracing::warn!(
305 "No async runtime available for file session store — persistence disabled"
306 );
307 }
308 }
309 self
310 }
311
312 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
314 self.session_id = Some(id.into());
315 self
316 }
317
318 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
321 self.tenant_id = Some(tenant.into());
322 self
323 }
324
325 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
328 self.principal = Some(principal.into());
329 self
330 }
331
332 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
335 self.agent_template_id = Some(template_id.into());
336 self
337 }
338
339 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
342 self.correlation_id = Some(corr.into());
343 self
344 }
345
346 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
351 self.budget_guard = Some(guard);
352 self
353 }
354
355 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
361 self.host_env = Some(env);
362 self
363 }
364
365 pub fn with_retention_limits(
375 mut self,
376 limits: crate::retention::SessionRetentionLimits,
377 ) -> Self {
378 self.retention_limits = Some(limits);
379 self
380 }
381
382 pub fn with_rl_trajectory(mut self, config: crate::rl_trajectory::RlTrajectoryConfig) -> Self {
388 self.rl_trajectory = Some(config);
389 self
390 }
391
392 pub fn with_llm_logprobs(mut self, enabled: bool) -> Self {
394 self.llm_logprobs = Some(enabled);
395 self
396 }
397
398 pub fn with_llm_top_logprobs(mut self, top_logprobs: usize) -> Self {
400 self.llm_logprobs = Some(true);
401 self.llm_top_logprobs = Some(top_logprobs);
402 self
403 }
404
405 pub fn with_auto_save(mut self, enabled: bool) -> Self {
407 self.auto_save = enabled;
408 self
409 }
410
411 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
413 self.artifact_store_limits = Some(limits);
414 self
415 }
416
417 pub fn with_parse_retries(mut self, max: u32) -> Self {
423 self.max_parse_retries = Some(max);
424 self
425 }
426
427 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
433 self.tool_timeout_ms = Some(timeout_ms);
434 self
435 }
436
437 pub fn with_llm_api_timeout(mut self, timeout_ms: u64) -> Self {
443 self.llm_api_timeout_ms = Some(timeout_ms);
444 self
445 }
446
447 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
453 self.circuit_breaker_threshold = Some(threshold);
454 self
455 }
456
457 pub fn with_duplicate_tool_call_threshold(mut self, threshold: u32) -> Self {
463 self.duplicate_tool_call_threshold = Some(threshold.max(1));
464 self
465 }
466
467 pub fn with_resilience_defaults(self) -> Self {
473 self.with_parse_retries(2)
474 .with_tool_timeout(120_000)
475 .with_circuit_breaker(3)
476 }
477
478 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
486 self.sandbox_handle = Some(handle);
487 self
488 }
489
490 pub fn with_workspace_backend(
496 mut self,
497 services: Arc<crate::workspace::WorkspaceServices>,
498 ) -> Self {
499 self.workspace_services = Some(services);
500 self
501 }
502
503 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
508 self.auto_compact = enabled;
509 self
510 }
511
512 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
514 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
515 self
516 }
517
518 pub fn with_continuation(mut self, enabled: bool) -> Self {
523 self.continuation_enabled = Some(enabled);
524 self
525 }
526
527 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
529 self.max_continuation_turns = Some(turns);
530 self
531 }
532
533 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
538 self.mcp_manager = Some(manager);
539 self
540 }
541
542 pub fn with_temperature(mut self, temperature: f32) -> Self {
543 self.temperature = Some(temperature);
544 self
545 }
546
547 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
548 self.thinking_budget = Some(budget);
549 self
550 }
551
552 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
557 self.max_tool_rounds = Some(rounds);
558 self
559 }
560
561 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
563 self.max_parallel_tasks = Some(tasks.max(1));
564 self
565 }
566
567 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
569 self.auto_delegation = Some(config);
570 self
571 }
572
573 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
575 let mut config = self.auto_delegation.take().unwrap_or_default();
576 config.enabled = enabled;
577 self.auto_delegation = Some(config);
578 self
579 }
580
581 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
588 if let Some(config) = &mut self.auto_delegation {
589 config.allow_manual_delegation = enabled;
590 }
591 self.manual_delegation_enabled = Some(enabled);
592 self
593 }
594
595 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
599 if let Some(config) = &mut self.auto_delegation {
600 config.auto_parallel = enabled;
601 }
602 self.auto_parallel_delegation = Some(enabled);
603 self
604 }
605
606 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
611 self.prompt_slots = Some(slots);
612 self
613 }
614
615 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
621 self.hook_executor = Some(executor);
622 self
623 }
624}