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 {
216 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
217 self
218 }
219
220 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
222 self.skill_registry = Some(registry);
223 self
224 }
225
226 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
231 self.enforce_active_skill_tool_restrictions = Some(enabled);
232 self
233 }
234
235 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
238 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
239 self
240 }
241
242 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
244 let registry = self
245 .skill_registry
246 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
247 if let Err(e) = registry.load_from_dir(&dir) {
248 tracing::warn!(
249 dir = %dir.as_ref().display(),
250 error = %e,
251 "Failed to load skills from directory — continuing without them"
252 );
253 }
254 self.skill_registry = Some(registry);
255 self
256 }
257
258 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
262 self.memory_store = Some(store);
263 self
264 }
265
266 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
272 self.file_memory_dir = Some(dir.into());
273 self
274 }
275
276 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
278 self.session_store = Some(store);
279 self
280 }
281
282 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
284 let dir = dir.into();
285 match tokio::runtime::Handle::try_current() {
286 Ok(handle) => {
287 match tokio::task::block_in_place(|| {
288 handle.block_on(crate::store::FileSessionStore::new(dir))
289 }) {
290 Ok(store) => {
291 self.session_store =
292 Some(Arc::new(store) as Arc<dyn crate::store::SessionStore>);
293 }
294 Err(e) => {
295 tracing::warn!("Failed to create file session store: {}", e);
296 }
297 }
298 }
299 Err(_) => {
300 tracing::warn!(
301 "No async runtime available for file session store — persistence disabled"
302 );
303 }
304 }
305 self
306 }
307
308 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
310 self.session_id = Some(id.into());
311 self
312 }
313
314 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
317 self.tenant_id = Some(tenant.into());
318 self
319 }
320
321 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
324 self.principal = Some(principal.into());
325 self
326 }
327
328 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
331 self.agent_template_id = Some(template_id.into());
332 self
333 }
334
335 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
338 self.correlation_id = Some(corr.into());
339 self
340 }
341
342 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
347 self.budget_guard = Some(guard);
348 self
349 }
350
351 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
357 self.host_env = Some(env);
358 self
359 }
360
361 pub fn with_retention_limits(
371 mut self,
372 limits: crate::retention::SessionRetentionLimits,
373 ) -> Self {
374 self.retention_limits = Some(limits);
375 self
376 }
377
378 pub fn with_rl_trajectory(mut self, config: crate::rl_trajectory::RlTrajectoryConfig) -> Self {
384 self.rl_trajectory = Some(config);
385 self
386 }
387
388 pub fn with_llm_logprobs(mut self, enabled: bool) -> Self {
390 self.llm_logprobs = Some(enabled);
391 self
392 }
393
394 pub fn with_llm_top_logprobs(mut self, top_logprobs: usize) -> Self {
396 self.llm_logprobs = Some(true);
397 self.llm_top_logprobs = Some(top_logprobs);
398 self
399 }
400
401 pub fn with_auto_save(mut self, enabled: bool) -> Self {
403 self.auto_save = enabled;
404 self
405 }
406
407 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
409 self.artifact_store_limits = Some(limits);
410 self
411 }
412
413 pub fn with_parse_retries(mut self, max: u32) -> Self {
419 self.max_parse_retries = Some(max);
420 self
421 }
422
423 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
429 self.tool_timeout_ms = Some(timeout_ms);
430 self
431 }
432
433 pub fn with_llm_api_timeout(mut self, timeout_ms: u64) -> Self {
439 self.llm_api_timeout_ms = Some(timeout_ms);
440 self
441 }
442
443 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
449 self.circuit_breaker_threshold = Some(threshold);
450 self
451 }
452
453 pub fn with_duplicate_tool_call_threshold(mut self, threshold: u32) -> Self {
459 self.duplicate_tool_call_threshold = Some(threshold.max(1));
460 self
461 }
462
463 pub fn with_resilience_defaults(self) -> Self {
469 self.with_parse_retries(2)
470 .with_tool_timeout(120_000)
471 .with_circuit_breaker(3)
472 }
473
474 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
482 self.sandbox_handle = Some(handle);
483 self
484 }
485
486 pub fn with_workspace_backend(
492 mut self,
493 services: Arc<crate::workspace::WorkspaceServices>,
494 ) -> Self {
495 self.workspace_services = Some(services);
496 self
497 }
498
499 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
504 self.auto_compact = enabled;
505 self
506 }
507
508 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
510 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
511 self
512 }
513
514 pub fn with_continuation(mut self, enabled: bool) -> Self {
519 self.continuation_enabled = Some(enabled);
520 self
521 }
522
523 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
525 self.max_continuation_turns = Some(turns);
526 self
527 }
528
529 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
534 self.mcp_manager = Some(manager);
535 self
536 }
537
538 pub fn with_temperature(mut self, temperature: f32) -> Self {
539 self.temperature = Some(temperature);
540 self
541 }
542
543 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
544 self.thinking_budget = Some(budget);
545 self
546 }
547
548 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
553 self.max_tool_rounds = Some(rounds);
554 self
555 }
556
557 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
559 self.max_parallel_tasks = Some(tasks.max(1));
560 self
561 }
562
563 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
565 self.auto_delegation = Some(config);
566 self
567 }
568
569 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
571 let mut config = self.auto_delegation.take().unwrap_or_default();
572 config.enabled = enabled;
573 self.auto_delegation = Some(config);
574 self
575 }
576
577 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
584 if let Some(config) = &mut self.auto_delegation {
585 config.allow_manual_delegation = enabled;
586 }
587 self.manual_delegation_enabled = Some(enabled);
588 self
589 }
590
591 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
595 if let Some(config) = &mut self.auto_delegation {
596 config.auto_parallel = enabled;
597 }
598 self.auto_parallel_delegation = Some(enabled);
599 self
600 }
601
602 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
607 self.prompt_slots = Some(slots);
608 self
609 }
610
611 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
617 self.hook_executor = Some(executor);
618 self
619 }
620}