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("auto_save", &self.auto_save)
46 .field("artifact_store_limits", &self.artifact_store_limits)
47 .field("max_parse_retries", &self.max_parse_retries)
48 .field("tool_timeout_ms", &self.tool_timeout_ms)
49 .field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
50 .field("sandbox_handle", &self.sandbox_handle.is_some())
51 .field("workspace_services", &self.workspace_services.is_some())
52 .field("auto_compact", &self.auto_compact)
53 .field("auto_compact_threshold", &self.auto_compact_threshold)
54 .field("continuation_enabled", &self.continuation_enabled)
55 .field("max_continuation_turns", &self.max_continuation_turns)
56 .field("mcp_manager", &self.mcp_manager.is_some())
57 .field("temperature", &self.temperature)
58 .field("thinking_budget", &self.thinking_budget)
59 .field("max_tool_rounds", &self.max_tool_rounds)
60 .field("max_parallel_tasks", &self.max_parallel_tasks)
61 .field("auto_delegation", &self.auto_delegation)
62 .field("manual_delegation_enabled", &self.manual_delegation_enabled)
63 .field("auto_parallel_delegation", &self.auto_parallel_delegation)
64 .field("prompt_slots", &self.prompt_slots.is_some())
65 .finish()
66 }
67}
68
69impl SessionOptions {
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 pub fn with_model(mut self, model: impl Into<String>) -> Self {
75 self.model = Some(model.into());
76 self
77 }
78
79 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
80 self.agent_dirs.push(dir.into());
81 self
82 }
83
84 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
86 self.worker_agents.push(spec);
87 self
88 }
89
90 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
92 where
93 I: IntoIterator<Item = WorkerAgentSpec>,
94 {
95 self.worker_agents.extend(specs);
96 self
97 }
98
99 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
100 self.queue_config = Some(config);
101 self
102 }
103
104 pub fn with_default_security(mut self) -> Self {
106 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
107 self
108 }
109
110 pub fn with_security_provider(
112 mut self,
113 provider: Arc<dyn crate::security::SecurityProvider>,
114 ) -> Self {
115 self.security_provider = Some(provider);
116 self
117 }
118
119 pub fn with_llm_client(mut self, client: Arc<dyn crate::llm::LlmClient>) -> Self {
127 self.llm_client = Some(client);
128 self
129 }
130
131 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
133 let config = crate::context::FileSystemContextConfig::new(root_path);
134 self.context_providers
135 .push(Arc::new(crate::context::FileSystemContextProvider::new(
136 config,
137 )));
138 self
139 }
140
141 pub fn with_context_provider(
143 mut self,
144 provider: Arc<dyn crate::context::ContextProvider>,
145 ) -> Self {
146 self.context_providers.push(provider);
147 self
148 }
149
150 pub fn with_confirmation_manager(
152 mut self,
153 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
154 ) -> Self {
155 self.confirmation_manager = Some(manager);
156 self
157 }
158
159 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
164 self.confirmation_policy = Some(policy);
165 self
166 }
167
168 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
170 self.permission_checker = Some(Arc::new(policy.clone()));
171 self.permission_policy = Some(policy);
172 self
173 }
174
175 pub fn with_permission_checker(
177 mut self,
178 checker: Arc<dyn crate::permissions::PermissionChecker>,
179 ) -> Self {
180 self.permission_checker = Some(checker);
181 self
182 }
183
184 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
186 self.planning_mode = mode;
187 self
188 }
189
190 pub fn with_planning(mut self, enabled: bool) -> Self {
192 self.planning_mode = if enabled {
193 PlanningMode::Enabled
194 } else {
195 PlanningMode::Disabled
196 };
197 self
198 }
199
200 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
202 self.goal_tracking = enabled;
203 self
204 }
205
206 pub fn with_builtin_skills(mut self) -> Self {
208 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
209 self
210 }
211
212 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
214 self.skill_registry = Some(registry);
215 self
216 }
217
218 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
223 self.enforce_active_skill_tool_restrictions = Some(enabled);
224 self
225 }
226
227 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
230 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
231 self
232 }
233
234 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
236 let registry = self
237 .skill_registry
238 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
239 if let Err(e) = registry.load_from_dir(&dir) {
240 tracing::warn!(
241 dir = %dir.as_ref().display(),
242 error = %e,
243 "Failed to load skills from directory — continuing without them"
244 );
245 }
246 self.skill_registry = Some(registry);
247 self
248 }
249
250 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
252 self.memory_store = Some(store);
253 self
254 }
255
256 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
262 self.file_memory_dir = Some(dir.into());
263 self
264 }
265
266 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
268 self.session_store = Some(store);
269 self
270 }
271
272 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
274 let dir = dir.into();
275 match tokio::runtime::Handle::try_current() {
276 Ok(handle) => {
277 match tokio::task::block_in_place(|| {
278 handle.block_on(crate::store::FileSessionStore::new(dir))
279 }) {
280 Ok(store) => {
281 self.session_store =
282 Some(Arc::new(store) as Arc<dyn crate::store::SessionStore>);
283 }
284 Err(e) => {
285 tracing::warn!("Failed to create file session store: {}", e);
286 }
287 }
288 }
289 Err(_) => {
290 tracing::warn!(
291 "No async runtime available for file session store — persistence disabled"
292 );
293 }
294 }
295 self
296 }
297
298 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
300 self.session_id = Some(id.into());
301 self
302 }
303
304 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
307 self.tenant_id = Some(tenant.into());
308 self
309 }
310
311 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
314 self.principal = Some(principal.into());
315 self
316 }
317
318 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
321 self.agent_template_id = Some(template_id.into());
322 self
323 }
324
325 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
328 self.correlation_id = Some(corr.into());
329 self
330 }
331
332 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
337 self.budget_guard = Some(guard);
338 self
339 }
340
341 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
347 self.host_env = Some(env);
348 self
349 }
350
351 pub fn with_retention_limits(
361 mut self,
362 limits: crate::retention::SessionRetentionLimits,
363 ) -> Self {
364 self.retention_limits = Some(limits);
365 self
366 }
367
368 pub fn with_auto_save(mut self, enabled: bool) -> Self {
370 self.auto_save = enabled;
371 self
372 }
373
374 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
376 self.artifact_store_limits = Some(limits);
377 self
378 }
379
380 pub fn with_parse_retries(mut self, max: u32) -> Self {
386 self.max_parse_retries = Some(max);
387 self
388 }
389
390 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
396 self.tool_timeout_ms = Some(timeout_ms);
397 self
398 }
399
400 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
406 self.circuit_breaker_threshold = Some(threshold);
407 self
408 }
409
410 pub fn with_resilience_defaults(self) -> Self {
416 self.with_parse_retries(2)
417 .with_tool_timeout(120_000)
418 .with_circuit_breaker(3)
419 }
420
421 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
429 self.sandbox_handle = Some(handle);
430 self
431 }
432
433 pub fn with_workspace_backend(
439 mut self,
440 services: Arc<crate::workspace::WorkspaceServices>,
441 ) -> Self {
442 self.workspace_services = Some(services);
443 self
444 }
445
446 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
451 self.auto_compact = enabled;
452 self
453 }
454
455 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
457 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
458 self
459 }
460
461 pub fn with_continuation(mut self, enabled: bool) -> Self {
466 self.continuation_enabled = Some(enabled);
467 self
468 }
469
470 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
472 self.max_continuation_turns = Some(turns);
473 self
474 }
475
476 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
481 self.mcp_manager = Some(manager);
482 self
483 }
484
485 pub fn with_temperature(mut self, temperature: f32) -> Self {
486 self.temperature = Some(temperature);
487 self
488 }
489
490 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
491 self.thinking_budget = Some(budget);
492 self
493 }
494
495 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
500 self.max_tool_rounds = Some(rounds);
501 self
502 }
503
504 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
506 self.max_parallel_tasks = Some(tasks.max(1));
507 self
508 }
509
510 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
512 self.auto_delegation = Some(config);
513 self
514 }
515
516 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
518 let mut config = self.auto_delegation.take().unwrap_or_default();
519 config.enabled = enabled;
520 self.auto_delegation = Some(config);
521 self
522 }
523
524 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
531 if let Some(config) = &mut self.auto_delegation {
532 config.allow_manual_delegation = enabled;
533 }
534 self.manual_delegation_enabled = Some(enabled);
535 self
536 }
537
538 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
542 if let Some(config) = &mut self.auto_delegation {
543 config.auto_parallel = enabled;
544 }
545 self.auto_parallel_delegation = Some(enabled);
546 self
547 }
548
549 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
554 self.prompt_slots = Some(slots);
555 self
556 }
557
558 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
564 self.hook_executor = Some(executor);
565 self
566 }
567}