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(
49 "session_checkpoint_export_sink",
50 &self.session_checkpoint_export_sink.is_some(),
51 )
52 .field("file_session_store_dir", &self.file_session_store_dir)
53 .field("session_id", &self.session_id)
54 .field("rl_trajectory", &self.rl_trajectory)
55 .field("llm_logprobs", &self.llm_logprobs)
56 .field("llm_top_logprobs", &self.llm_top_logprobs)
57 .field("auto_save", &self.auto_save)
58 .field("artifact_store_limits", &self.artifact_store_limits)
59 .field("immutable_content_adapter", &self.immutable_content_adapter)
60 .field(
61 "tool_result_transform_policy",
62 &self.tool_result_transform_policy,
63 )
64 .field("tool_presentation_profile", &self.tool_presentation_profile)
65 .field("max_parse_retries", &self.max_parse_retries)
66 .field("tool_timeout_ms", &self.tool_timeout_ms)
67 .field("llm_api_timeout_ms", &self.llm_api_timeout_ms)
68 .field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
69 .field(
70 "duplicate_tool_call_threshold",
71 &self.duplicate_tool_call_threshold,
72 )
73 .field("sandbox_handle", &self.sandbox_handle.is_some())
74 .field("workspace_services", &self.workspace_services.is_some())
75 .field("workspace_retrieval", &self.workspace_retrieval)
76 .field("auto_compact", &self.auto_compact)
77 .field("auto_compact_threshold", &self.auto_compact_threshold)
78 .field("max_context_tokens", &self.max_context_tokens)
79 .field("continuation_enabled", &self.continuation_enabled)
80 .field("max_continuation_turns", &self.max_continuation_turns)
81 .field("mcp_manager", &self.mcp_manager.is_some())
82 .field("temperature", &self.temperature)
83 .field("thinking_budget", &self.thinking_budget)
84 .field("max_tool_rounds", &self.max_tool_rounds)
85 .field("max_parallel_tasks", &self.max_parallel_tasks)
86 .field("auto_delegation", &self.auto_delegation)
87 .field("manual_delegation_enabled", &self.manual_delegation_enabled)
88 .field("auto_parallel_delegation", &self.auto_parallel_delegation)
89 .field("prompt_slots", &self.prompt_slots.is_some())
90 .finish()
91 }
92}
93
94impl SessionOptions {
95 pub fn new() -> Self {
96 Self::default()
97 }
98
99 pub fn with_model(mut self, model: impl Into<String>) -> Self {
100 self.model = Some(model.into());
101 self
102 }
103
104 pub fn with_task_priority(mut self, priority: crate::task_scheduler::TaskPriority) -> Self {
106 self.task_priority = priority;
107 self
108 }
109
110 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
111 self.agent_dirs.push(dir.into());
112 self
113 }
114
115 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
117 self.worker_agents.push(spec);
118 self
119 }
120
121 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
123 where
124 I: IntoIterator<Item = WorkerAgentSpec>,
125 {
126 self.worker_agents.extend(specs);
127 self
128 }
129
130 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
131 self.queue_config = Some(config);
132 self
133 }
134
135 pub fn with_default_security(mut self) -> Self {
137 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
138 self
139 }
140
141 pub fn with_security_provider(
143 mut self,
144 provider: Arc<dyn crate::security::SecurityProvider>,
145 ) -> Self {
146 self.security_provider = Some(provider);
147 self
148 }
149
150 pub fn with_llm_client(mut self, client: Arc<dyn crate::llm::LlmClient>) -> Self {
158 self.llm_client = Some(client);
159 self
160 }
161
162 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
164 let config = crate::context::FileSystemContextConfig::new(root_path);
165 self.context_providers
166 .push(Arc::new(crate::context::FileSystemContextProvider::new(
167 config,
168 )));
169 self
170 }
171
172 pub fn with_context_provider(
174 mut self,
175 provider: Arc<dyn crate::context::ContextProvider>,
176 ) -> Self {
177 self.context_providers.push(provider);
178 self
179 }
180
181 pub fn with_cognitive_context(
188 mut self,
189 context: crate::cognitive_context::CognitiveContextSession,
190 ) -> Self {
191 self.cognitive_context = Some(context);
192 self
193 }
194
195 pub fn with_confirmation_manager(
197 mut self,
198 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
199 ) -> Self {
200 self.confirmation_manager = Some(manager);
201 self
202 }
203
204 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
209 self.confirmation_policy = Some(policy);
210 self
211 }
212
213 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
215 self.permission_checker = Some(Arc::new(policy.clone()));
216 self.permission_policy = Some(policy);
217 self
218 }
219
220 pub fn with_permission_checker(
222 mut self,
223 checker: Arc<dyn crate::permissions::PermissionChecker>,
224 ) -> Self {
225 self.permission_checker = Some(checker);
226 self
227 }
228
229 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
231 self.planning_mode = mode;
232 self
233 }
234
235 pub fn with_planning(mut self, enabled: bool) -> Self {
237 self.planning_mode = if enabled {
238 PlanningMode::Enabled
239 } else {
240 PlanningMode::Disabled
241 };
242 self
243 }
244
245 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
247 self.goal_tracking = enabled;
248 self
249 }
250
251 pub fn with_builtin_skills(mut self) -> Self {
257 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
258 self
259 }
260
261 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
263 self.skill_registry = Some(registry);
264 self
265 }
266
267 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
272 self.enforce_active_skill_tool_restrictions = Some(enabled);
273 self
274 }
275
276 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
280 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
281 self
282 }
283
284 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
286 let registry = self
287 .skill_registry
288 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
289 if let Err(e) = registry.load_from_dir(&dir) {
290 tracing::warn!(
291 dir = %dir.as_ref().display(),
292 error = %e,
293 "Failed to load skills from directory — continuing without them"
294 );
295 }
296 self.skill_registry = Some(registry);
297 self
298 }
299
300 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
304 self.memory_store = Some(store);
305 self.file_memory_dir = None;
306 self
307 }
308
309 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
315 self.memory_store = None;
316 self.file_memory_dir = Some(dir.into());
317 self
318 }
319
320 pub fn with_memory_observer(
324 mut self,
325 observer: Arc<dyn crate::memory::MemoryObserver>,
326 ) -> Self {
327 self.memory_observers.push(observer);
328 self
329 }
330
331 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
333 self.session_store = Some(store);
334 self.file_session_store_dir = None;
335 self
336 }
337
338 pub fn with_session_checkpoint_export_sink(
344 mut self,
345 sink: Arc<dyn crate::session_checkpoint::SessionCheckpointExportSink>,
346 ) -> Self {
347 self.session_checkpoint_export_sink = Some(sink);
348 self
349 }
350
351 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
356 self.session_store = None;
357 self.file_session_store_dir = Some(dir.into());
358 self
359 }
360
361 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
363 self.session_id = Some(id.into());
364 self
365 }
366
367 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
370 self.tenant_id = Some(tenant.into());
371 self
372 }
373
374 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
377 self.principal = Some(principal.into());
378 self
379 }
380
381 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
384 self.agent_template_id = Some(template_id.into());
385 self
386 }
387
388 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
391 self.correlation_id = Some(corr.into());
392 self
393 }
394
395 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
400 self.budget_guard = Some(guard);
401 self
402 }
403
404 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
410 self.host_env = Some(env);
411 self
412 }
413
414 pub fn with_retention_limits(
424 mut self,
425 limits: crate::retention::SessionRetentionLimits,
426 ) -> Self {
427 self.retention_limits = Some(limits);
428 self
429 }
430
431 pub fn with_rl_trajectory(mut self, config: crate::rl_trajectory::RlTrajectoryConfig) -> Self {
437 self.rl_trajectory = Some(config);
438 self
439 }
440
441 pub fn with_llm_logprobs(mut self, enabled: bool) -> Self {
443 self.llm_logprobs = Some(enabled);
444 self
445 }
446
447 pub fn with_llm_top_logprobs(mut self, top_logprobs: usize) -> Self {
449 self.llm_logprobs = Some(true);
450 self.llm_top_logprobs = Some(top_logprobs);
451 self
452 }
453
454 pub fn with_auto_save(mut self, enabled: bool) -> Self {
456 self.auto_save = enabled;
457 self
458 }
459
460 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
462 self.artifact_store_limits = Some(limits);
463 self
464 }
465
466 pub fn with_immutable_content_adapter(
471 mut self,
472 adapter: crate::tools::ImmutableContentAdapterSession,
473 ) -> Self {
474 self.immutable_content_adapter = Some(adapter);
475 self
476 }
477
478 pub fn with_tool_result_transform_policy(
480 mut self,
481 policy: crate::tools::ToolResultTransformPolicyV1,
482 ) -> Self {
483 self.tool_result_transform_policy = Some(policy);
484 self
485 }
486
487 pub fn with_tool_presentation_profile(
489 mut self,
490 profile: crate::tools::ToolPresentationProfileV1,
491 ) -> Self {
492 self.tool_presentation_profile = Some(profile);
493 self
494 }
495
496 pub fn with_parse_retries(mut self, max: u32) -> Self {
502 self.max_parse_retries = Some(max);
503 self
504 }
505
506 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
512 self.tool_timeout_ms = Some(timeout_ms);
513 self
514 }
515
516 pub fn with_llm_api_timeout(mut self, timeout_ms: u64) -> Self {
522 self.llm_api_timeout_ms = Some(timeout_ms);
523 self
524 }
525
526 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
532 self.circuit_breaker_threshold = Some(threshold);
533 self
534 }
535
536 pub fn with_duplicate_tool_call_threshold(mut self, threshold: u32) -> Self {
542 self.duplicate_tool_call_threshold = Some(threshold.max(1));
543 self
544 }
545
546 pub fn with_resilience_defaults(self) -> Self {
552 self.with_parse_retries(2)
553 .with_tool_timeout(120_000)
554 .with_circuit_breaker(3)
555 }
556
557 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
565 self.sandbox_handle = Some(handle);
566 self
567 }
568
569 pub fn with_workspace_backend(
575 mut self,
576 services: Arc<crate::workspace::WorkspaceServices>,
577 ) -> Self {
578 self.workspace_services = Some(services);
579 self
580 }
581
582 pub fn with_workspace_retrieval(
588 mut self,
589 options: crate::workspace::WorkspaceRetrievalOptions,
590 ) -> Self {
591 self.workspace_retrieval = Some(options);
592 self
593 }
594
595 pub fn without_workspace_retrieval(mut self) -> Self {
602 self.workspace_retrieval = None;
603 self
604 }
605
606 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
611 self.auto_compact = enabled;
612 self
613 }
614
615 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
617 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
618 self
619 }
620
621 pub fn with_max_context_tokens(mut self, tokens: usize) -> Self {
623 self.max_context_tokens = Some(tokens);
624 self
625 }
626
627 pub fn with_continuation(mut self, enabled: bool) -> Self {
632 self.continuation_enabled = Some(enabled);
633 self
634 }
635
636 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
638 self.max_continuation_turns = Some(turns);
639 self
640 }
641
642 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
649 self.mcp_manager = Some(manager);
650 self
651 }
652
653 pub fn with_temperature(mut self, temperature: f32) -> Self {
654 self.temperature = Some(temperature);
655 self
656 }
657
658 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
659 self.thinking_budget = Some(budget);
660 self
661 }
662
663 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
668 self.max_tool_rounds = Some(rounds);
669 self
670 }
671
672 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
674 self.max_parallel_tasks = Some(tasks.max(1));
675 self
676 }
677
678 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
680 self.auto_delegation = Some(config);
681 self
682 }
683
684 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
686 let mut config = self.auto_delegation.take().unwrap_or_default();
687 config.enabled = enabled;
688 self.auto_delegation = Some(config);
689 self
690 }
691
692 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
699 if let Some(config) = &mut self.auto_delegation {
700 config.allow_manual_delegation = enabled;
701 }
702 self.manual_delegation_enabled = Some(enabled);
703 self
704 }
705
706 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
711 if let Some(config) = &mut self.auto_delegation {
712 config.auto_parallel = enabled;
713 }
714 self.auto_parallel_delegation = Some(enabled);
715 self
716 }
717
718 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
723 self.prompt_slots = Some(slots);
724 self
725 }
726
727 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
732 self.hook_executor = Some(executor);
733 self
734 }
735}