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("task_priority", &self.task_priority)
20 .field("agent_dirs", &self.agent_dirs)
21 .field("worker_agents", &self.worker_agents.len())
22 .field("skill_dirs", &self.skill_dirs)
23 .field("queue_config", &self.queue_config)
24 .field("security_provider", &self.security_provider.is_some())
25 .field("llm_client", &self.llm_client.is_some())
26 .field("context_providers", &self.context_providers.len())
27 .field("cognitive_context", &self.cognitive_context)
28 .field("confirmation_manager", &self.confirmation_manager.is_some())
29 .field("permission_checker", &self.permission_checker.is_some())
30 .field("permission_policy", &self.permission_policy.is_some())
31 .field("planning_mode", &self.planning_mode)
32 .field("goal_tracking", &self.goal_tracking)
33 .field(
34 "skill_registry",
35 &self
36 .skill_registry
37 .as_ref()
38 .map(|r| format!("{} skills", r.len())),
39 )
40 .field(
41 "enforce_active_skill_tool_restrictions",
42 &self.enforce_active_skill_tool_restrictions,
43 )
44 .field("memory_store", &self.memory_store.is_some())
45 .field("memory_observers", &self.memory_observers.len())
46 .field("file_memory_dir", &self.file_memory_dir)
47 .field("session_store", &self.session_store.is_some())
48 .field("file_session_store_dir", &self.file_session_store_dir)
49 .field("session_id", &self.session_id)
50 .field("rl_trajectory", &self.rl_trajectory)
51 .field("llm_logprobs", &self.llm_logprobs)
52 .field("llm_top_logprobs", &self.llm_top_logprobs)
53 .field("auto_save", &self.auto_save)
54 .field("artifact_store_limits", &self.artifact_store_limits)
55 .field(
56 "tool_result_transform_policy",
57 &self.tool_result_transform_policy,
58 )
59 .field("max_parse_retries", &self.max_parse_retries)
60 .field("tool_timeout_ms", &self.tool_timeout_ms)
61 .field("llm_api_timeout_ms", &self.llm_api_timeout_ms)
62 .field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
63 .field(
64 "duplicate_tool_call_threshold",
65 &self.duplicate_tool_call_threshold,
66 )
67 .field("sandbox_handle", &self.sandbox_handle.is_some())
68 .field("workspace_services", &self.workspace_services.is_some())
69 .field("auto_compact", &self.auto_compact)
70 .field("auto_compact_threshold", &self.auto_compact_threshold)
71 .field("max_context_tokens", &self.max_context_tokens)
72 .field("continuation_enabled", &self.continuation_enabled)
73 .field("max_continuation_turns", &self.max_continuation_turns)
74 .field("mcp_manager", &self.mcp_manager.is_some())
75 .field("temperature", &self.temperature)
76 .field("thinking_budget", &self.thinking_budget)
77 .field("max_tool_rounds", &self.max_tool_rounds)
78 .field("max_parallel_tasks", &self.max_parallel_tasks)
79 .field("auto_delegation", &self.auto_delegation)
80 .field("manual_delegation_enabled", &self.manual_delegation_enabled)
81 .field("auto_parallel_delegation", &self.auto_parallel_delegation)
82 .field("prompt_slots", &self.prompt_slots.is_some())
83 .finish()
84 }
85}
86
87impl SessionOptions {
88 pub fn new() -> Self {
89 Self::default()
90 }
91
92 pub fn with_model(mut self, model: impl Into<String>) -> Self {
93 self.model = Some(model.into());
94 self
95 }
96
97 pub fn with_task_priority(mut self, priority: crate::task_scheduler::TaskPriority) -> Self {
99 self.task_priority = priority;
100 self
101 }
102
103 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
104 self.agent_dirs.push(dir.into());
105 self
106 }
107
108 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
110 self.worker_agents.push(spec);
111 self
112 }
113
114 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
116 where
117 I: IntoIterator<Item = WorkerAgentSpec>,
118 {
119 self.worker_agents.extend(specs);
120 self
121 }
122
123 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
124 self.queue_config = Some(config);
125 self
126 }
127
128 pub fn with_default_security(mut self) -> Self {
130 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
131 self
132 }
133
134 pub fn with_security_provider(
136 mut self,
137 provider: Arc<dyn crate::security::SecurityProvider>,
138 ) -> Self {
139 self.security_provider = Some(provider);
140 self
141 }
142
143 pub fn with_llm_client(mut self, client: Arc<dyn crate::llm::LlmClient>) -> Self {
151 self.llm_client = Some(client);
152 self
153 }
154
155 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
157 let config = crate::context::FileSystemContextConfig::new(root_path);
158 self.context_providers
159 .push(Arc::new(crate::context::FileSystemContextProvider::new(
160 config,
161 )));
162 self
163 }
164
165 pub fn with_context_provider(
167 mut self,
168 provider: Arc<dyn crate::context::ContextProvider>,
169 ) -> Self {
170 self.context_providers.push(provider);
171 self
172 }
173
174 pub fn with_cognitive_context(
181 mut self,
182 context: crate::cognitive_context::CognitiveContextSession,
183 ) -> Self {
184 self.cognitive_context = Some(context);
185 self
186 }
187
188 pub fn with_confirmation_manager(
190 mut self,
191 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
192 ) -> Self {
193 self.confirmation_manager = Some(manager);
194 self
195 }
196
197 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
202 self.confirmation_policy = Some(policy);
203 self
204 }
205
206 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
208 self.permission_checker = Some(Arc::new(policy.clone()));
209 self.permission_policy = Some(policy);
210 self
211 }
212
213 pub fn with_permission_checker(
215 mut self,
216 checker: Arc<dyn crate::permissions::PermissionChecker>,
217 ) -> Self {
218 self.permission_checker = Some(checker);
219 self
220 }
221
222 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
224 self.planning_mode = mode;
225 self
226 }
227
228 pub fn with_planning(mut self, enabled: bool) -> Self {
230 self.planning_mode = if enabled {
231 PlanningMode::Enabled
232 } else {
233 PlanningMode::Disabled
234 };
235 self
236 }
237
238 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
240 self.goal_tracking = enabled;
241 self
242 }
243
244 pub fn with_builtin_skills(mut self) -> Self {
250 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
251 self
252 }
253
254 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
256 self.skill_registry = Some(registry);
257 self
258 }
259
260 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
265 self.enforce_active_skill_tool_restrictions = Some(enabled);
266 self
267 }
268
269 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
273 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
274 self
275 }
276
277 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
279 let registry = self
280 .skill_registry
281 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
282 if let Err(e) = registry.load_from_dir(&dir) {
283 tracing::warn!(
284 dir = %dir.as_ref().display(),
285 error = %e,
286 "Failed to load skills from directory — continuing without them"
287 );
288 }
289 self.skill_registry = Some(registry);
290 self
291 }
292
293 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
297 self.memory_store = Some(store);
298 self.file_memory_dir = None;
299 self
300 }
301
302 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
308 self.memory_store = None;
309 self.file_memory_dir = Some(dir.into());
310 self
311 }
312
313 pub fn with_memory_observer(
317 mut self,
318 observer: Arc<dyn crate::memory::MemoryObserver>,
319 ) -> Self {
320 self.memory_observers.push(observer);
321 self
322 }
323
324 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
326 self.session_store = Some(store);
327 self.file_session_store_dir = None;
328 self
329 }
330
331 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
336 self.session_store = None;
337 self.file_session_store_dir = Some(dir.into());
338 self
339 }
340
341 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
343 self.session_id = Some(id.into());
344 self
345 }
346
347 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
350 self.tenant_id = Some(tenant.into());
351 self
352 }
353
354 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
357 self.principal = Some(principal.into());
358 self
359 }
360
361 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
364 self.agent_template_id = Some(template_id.into());
365 self
366 }
367
368 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
371 self.correlation_id = Some(corr.into());
372 self
373 }
374
375 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
380 self.budget_guard = Some(guard);
381 self
382 }
383
384 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
390 self.host_env = Some(env);
391 self
392 }
393
394 pub fn with_retention_limits(
404 mut self,
405 limits: crate::retention::SessionRetentionLimits,
406 ) -> Self {
407 self.retention_limits = Some(limits);
408 self
409 }
410
411 pub fn with_rl_trajectory(mut self, config: crate::rl_trajectory::RlTrajectoryConfig) -> Self {
417 self.rl_trajectory = Some(config);
418 self
419 }
420
421 pub fn with_llm_logprobs(mut self, enabled: bool) -> Self {
423 self.llm_logprobs = Some(enabled);
424 self
425 }
426
427 pub fn with_llm_top_logprobs(mut self, top_logprobs: usize) -> Self {
429 self.llm_logprobs = Some(true);
430 self.llm_top_logprobs = Some(top_logprobs);
431 self
432 }
433
434 pub fn with_auto_save(mut self, enabled: bool) -> Self {
436 self.auto_save = enabled;
437 self
438 }
439
440 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
442 self.artifact_store_limits = Some(limits);
443 self
444 }
445
446 pub fn with_tool_result_transform_policy(
448 mut self,
449 policy: crate::tools::ToolResultTransformPolicyV1,
450 ) -> Self {
451 self.tool_result_transform_policy = Some(policy);
452 self
453 }
454
455 pub fn with_parse_retries(mut self, max: u32) -> Self {
461 self.max_parse_retries = Some(max);
462 self
463 }
464
465 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
471 self.tool_timeout_ms = Some(timeout_ms);
472 self
473 }
474
475 pub fn with_llm_api_timeout(mut self, timeout_ms: u64) -> Self {
481 self.llm_api_timeout_ms = Some(timeout_ms);
482 self
483 }
484
485 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
491 self.circuit_breaker_threshold = Some(threshold);
492 self
493 }
494
495 pub fn with_duplicate_tool_call_threshold(mut self, threshold: u32) -> Self {
501 self.duplicate_tool_call_threshold = Some(threshold.max(1));
502 self
503 }
504
505 pub fn with_resilience_defaults(self) -> Self {
511 self.with_parse_retries(2)
512 .with_tool_timeout(120_000)
513 .with_circuit_breaker(3)
514 }
515
516 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
524 self.sandbox_handle = Some(handle);
525 self
526 }
527
528 pub fn with_workspace_backend(
534 mut self,
535 services: Arc<crate::workspace::WorkspaceServices>,
536 ) -> Self {
537 self.workspace_services = Some(services);
538 self
539 }
540
541 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
546 self.auto_compact = enabled;
547 self
548 }
549
550 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
552 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
553 self
554 }
555
556 pub fn with_max_context_tokens(mut self, tokens: usize) -> Self {
558 self.max_context_tokens = Some(tokens);
559 self
560 }
561
562 pub fn with_continuation(mut self, enabled: bool) -> Self {
567 self.continuation_enabled = Some(enabled);
568 self
569 }
570
571 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
573 self.max_continuation_turns = Some(turns);
574 self
575 }
576
577 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
584 self.mcp_manager = Some(manager);
585 self
586 }
587
588 pub fn with_temperature(mut self, temperature: f32) -> Self {
589 self.temperature = Some(temperature);
590 self
591 }
592
593 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
594 self.thinking_budget = Some(budget);
595 self
596 }
597
598 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
603 self.max_tool_rounds = Some(rounds);
604 self
605 }
606
607 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
609 self.max_parallel_tasks = Some(tasks.max(1));
610 self
611 }
612
613 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
615 self.auto_delegation = Some(config);
616 self
617 }
618
619 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
621 let mut config = self.auto_delegation.take().unwrap_or_default();
622 config.enabled = enabled;
623 self.auto_delegation = Some(config);
624 self
625 }
626
627 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
634 if let Some(config) = &mut self.auto_delegation {
635 config.allow_manual_delegation = enabled;
636 }
637 self.manual_delegation_enabled = Some(enabled);
638 self
639 }
640
641 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
646 if let Some(config) = &mut self.auto_delegation {
647 config.auto_parallel = enabled;
648 }
649 self.auto_parallel_delegation = Some(enabled);
650 self
651 }
652
653 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
658 self.prompt_slots = Some(slots);
659 self
660 }
661
662 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
667 self.hook_executor = Some(executor);
668 self
669 }
670}