1use super::SessionOptions;
8use crate::prompts::{PlanningMode, SystemPromptSlots};
9use crate::queue::SessionQueueConfig;
10use crate::subagent::WorkerAgentSpec;
11use a3s_memory::MemoryStore;
12use std::collections::HashMap;
13use std::path::PathBuf;
14use std::sync::Arc;
15
16impl std::fmt::Debug for SessionOptions {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 f.debug_struct("SessionOptions")
19 .field("model", &self.model)
20 .field("task_priority", &self.task_priority)
21 .field("agent_dirs", &self.agent_dirs)
22 .field("worker_agents", &self.worker_agents.len())
23 .field("skill_dirs", &self.skill_dirs)
24 .field(
25 "command_env",
26 &self.command_env.as_ref().map(|env| env.len()),
27 )
28 .field("queue_config", &self.queue_config)
29 .field("search_config", &self.search_config)
30 .field("security_provider", &self.security_provider.is_some())
31 .field("llm_client", &self.llm_client.is_some())
32 .field("context_providers", &self.context_providers.len())
33 .field("cognitive_context", &self.cognitive_context)
34 .field("confirmation_manager", &self.confirmation_manager.is_some())
35 .field("permission_checker", &self.permission_checker.is_some())
36 .field("permission_policy", &self.permission_policy.is_some())
37 .field("planning_mode", &self.planning_mode)
38 .field("goal_tracking", &self.goal_tracking)
39 .field(
40 "skill_registry",
41 &self
42 .skill_registry
43 .as_ref()
44 .map(|r| format!("{} skills", r.len())),
45 )
46 .field("host_skills", &self.host_skills.len())
47 .field(
48 "enforce_active_skill_tool_restrictions",
49 &self.enforce_active_skill_tool_restrictions,
50 )
51 .field("memory_store", &self.memory_store.is_some())
52 .field("durable_memory", &self.durable_memory)
53 .field("memory_observers", &self.memory_observers.len())
54 .field("memory_maintenance", &self.memory_maintenance)
55 .field("file_memory_dir", &self.file_memory_dir)
56 .field("session_store", &self.session_store.is_some())
57 .field(
58 "session_checkpoint_export_sink",
59 &self.session_checkpoint_export_sink.is_some(),
60 )
61 .field("file_session_store_dir", &self.file_session_store_dir)
62 .field("session_id", &self.session_id)
63 .field("rl_trajectory", &self.rl_trajectory)
64 .field("llm_logprobs", &self.llm_logprobs)
65 .field("llm_top_logprobs", &self.llm_top_logprobs)
66 .field("auto_save", &self.auto_save)
67 .field("artifact_store_limits", &self.artifact_store_limits)
68 .field("immutable_content_adapter", &self.immutable_content_adapter)
69 .field(
70 "tool_result_transform_policy",
71 &self.tool_result_transform_policy,
72 )
73 .field("tool_presentation_profile", &self.tool_presentation_profile)
74 .field("max_parse_retries", &self.max_parse_retries)
75 .field("tool_timeout_ms", &self.tool_timeout_ms)
76 .field("llm_api_timeout_ms", &self.llm_api_timeout_ms)
77 .field("circuit_breaker_threshold", &self.circuit_breaker_threshold)
78 .field(
79 "duplicate_tool_call_threshold",
80 &self.duplicate_tool_call_threshold,
81 )
82 .field("sandbox_handle", &self.sandbox_handle.is_some())
83 .field(
84 "allow_process_host_sandbox",
85 &self.allow_process_host_sandbox,
86 )
87 .field("workspace_services", &self.workspace_services.is_some())
88 .field("workspace_retrieval", &self.workspace_retrieval)
89 .field("auto_compact", &self.auto_compact)
90 .field("auto_compact_threshold", &self.auto_compact_threshold)
91 .field("max_context_tokens", &self.max_context_tokens)
92 .field("continuation_enabled", &self.continuation_enabled)
93 .field("max_continuation_turns", &self.max_continuation_turns)
94 .field("mcp_manager", &self.mcp_manager.is_some())
95 .field("temperature", &self.temperature)
96 .field("thinking_budget", &self.thinking_budget)
97 .field("max_tool_rounds", &self.max_tool_rounds)
98 .field("max_parallel_tasks", &self.max_parallel_tasks)
99 .field("auto_delegation", &self.auto_delegation)
100 .field("manual_delegation_enabled", &self.manual_delegation_enabled)
101 .field("auto_parallel_delegation", &self.auto_parallel_delegation)
102 .field("prompt_slots", &self.prompt_slots.is_some())
103 .finish()
104 }
105}
106
107impl SessionOptions {
108 pub fn new() -> Self {
109 Self::default()
110 }
111
112 pub fn with_model(mut self, model: impl Into<String>) -> Self {
113 self.model = Some(model.into());
114 self
115 }
116
117 pub fn with_task_priority(mut self, priority: crate::task_scheduler::TaskPriority) -> Self {
119 self.task_priority = priority;
120 self
121 }
122
123 pub fn with_agent_dir(mut self, dir: impl Into<PathBuf>) -> Self {
124 self.agent_dirs.push(dir.into());
125 self
126 }
127
128 pub fn with_worker_agent(mut self, spec: WorkerAgentSpec) -> Self {
130 self.worker_agents.push(spec);
131 self
132 }
133
134 pub fn with_worker_agents<I>(mut self, specs: I) -> Self
136 where
137 I: IntoIterator<Item = WorkerAgentSpec>,
138 {
139 self.worker_agents.extend(specs);
140 self
141 }
142
143 pub fn with_queue_config(mut self, config: SessionQueueConfig) -> Self {
144 self.queue_config = Some(config);
145 self
146 }
147
148 pub fn with_search_config(mut self, config: crate::config::SearchConfig) -> Self {
154 self.search_config = Some(config);
155 self
156 }
157
158 pub fn with_default_security(mut self) -> Self {
160 self.security_provider = Some(Arc::new(crate::security::DefaultSecurityProvider::new()));
161 self
162 }
163
164 pub fn with_security_provider(
166 mut self,
167 provider: Arc<dyn crate::security::SecurityProvider>,
168 ) -> Self {
169 self.security_provider = Some(provider);
170 self
171 }
172
173 pub fn with_llm_client(mut self, client: Arc<dyn crate::llm::LlmClient>) -> Self {
181 self.llm_client = Some(client);
182 self
183 }
184
185 pub fn with_fs_context(mut self, root_path: impl Into<PathBuf>) -> Self {
187 let config = crate::context::FileSystemContextConfig::new(root_path);
188 self.context_providers
189 .push(Arc::new(crate::context::FileSystemContextProvider::new(
190 config,
191 )));
192 self
193 }
194
195 pub fn with_context_provider(
197 mut self,
198 provider: Arc<dyn crate::context::ContextProvider>,
199 ) -> Self {
200 self.context_providers.push(provider);
201 self
202 }
203
204 pub fn with_cognitive_context(
211 mut self,
212 context: crate::cognitive_context::CognitiveContextSession,
213 ) -> Self {
214 self.cognitive_context = Some(context);
215 self
216 }
217
218 pub fn with_confirmation_manager(
220 mut self,
221 manager: Arc<dyn crate::hitl::ConfirmationProvider>,
222 ) -> Self {
223 self.confirmation_manager = Some(manager);
224 self
225 }
226
227 pub fn with_confirmation_policy(mut self, policy: crate::hitl::ConfirmationPolicy) -> Self {
232 self.confirmation_policy = Some(policy);
233 self
234 }
235
236 pub fn with_permission_policy(mut self, policy: crate::permissions::PermissionPolicy) -> Self {
238 self.permission_checker = Some(Arc::new(policy.clone()));
239 self.permission_policy = Some(policy);
240 self
241 }
242
243 pub fn with_permission_checker(
245 mut self,
246 checker: Arc<dyn crate::permissions::PermissionChecker>,
247 ) -> Self {
248 self.permission_checker = Some(checker);
249 self
250 }
251
252 pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
254 self.planning_mode = mode;
255 self
256 }
257
258 pub fn with_planning(mut self, enabled: bool) -> Self {
260 self.planning_mode = if enabled {
261 PlanningMode::Enabled
262 } else {
263 PlanningMode::Disabled
264 };
265 self
266 }
267
268 pub fn with_goal_tracking(mut self, enabled: bool) -> Self {
270 self.goal_tracking = enabled;
271 self
272 }
273
274 pub fn with_builtin_skills(mut self) -> Self {
280 self.skill_registry = Some(Arc::new(crate::skills::SkillRegistry::with_builtins()));
281 self
282 }
283
284 pub fn with_skill_registry(mut self, registry: Arc<crate::skills::SkillRegistry>) -> Self {
286 self.skill_registry = Some(registry);
287 self
288 }
289
290 pub fn with_host_skill(mut self, skill: Arc<crate::skills::Skill>) -> Self {
294 self.host_skills.push(skill);
295 self
296 }
297
298 pub fn with_active_skill_tool_restrictions(mut self, enabled: bool) -> Self {
303 self.enforce_active_skill_tool_restrictions = Some(enabled);
304 self
305 }
306
307 pub fn with_skill_dirs(mut self, dirs: impl IntoIterator<Item = impl Into<PathBuf>>) -> Self {
311 self.skill_dirs.extend(dirs.into_iter().map(Into::into));
312 self
313 }
314
315 pub fn with_command_env(mut self, env: HashMap<String, String>) -> Self {
319 self.command_env = Some(env);
320 self
321 }
322
323 pub fn with_skills_from_dir(mut self, dir: impl AsRef<std::path::Path>) -> Self {
325 let registry = self
326 .skill_registry
327 .unwrap_or_else(|| Arc::new(crate::skills::SkillRegistry::new()));
328 if let Err(e) = registry.load_from_dir(&dir) {
329 tracing::warn!(
330 dir = %dir.as_ref().display(),
331 error = %e,
332 "Failed to load skills from directory — continuing without them"
333 );
334 }
335 self.skill_registry = Some(registry);
336 self
337 }
338
339 pub fn with_memory(mut self, store: Arc<dyn MemoryStore>) -> Self {
343 self.memory_store = Some(store);
344 self.file_memory_dir = None;
345 self
346 }
347
348 pub fn with_durable_memory(
357 mut self,
358 binding: crate::durable_memory::DurableMemorySession,
359 ) -> Self {
360 self.durable_memory = Some(binding);
361 self
362 }
363
364 pub fn with_file_memory(mut self, dir: impl Into<PathBuf>) -> Self {
370 self.memory_store = None;
371 self.file_memory_dir = Some(dir.into());
372 self
373 }
374
375 pub fn with_memory_observer(
379 mut self,
380 observer: Arc<dyn crate::memory::MemoryObserver>,
381 ) -> Self {
382 self.memory_observers.push(observer);
383 self
384 }
385
386 pub fn with_memory_maintenance(
391 mut self,
392 maintenance: crate::memory::MemoryMaintenanceOptions,
393 ) -> Self {
394 self.memory_maintenance = maintenance;
395 self
396 }
397
398 pub fn with_session_store(mut self, store: Arc<dyn crate::store::SessionStore>) -> Self {
400 self.session_store = Some(store);
401 self.file_session_store_dir = None;
402 self
403 }
404
405 pub fn with_session_checkpoint_export_sink(
411 mut self,
412 sink: Arc<dyn crate::session_checkpoint::SessionCheckpointExportSink>,
413 ) -> Self {
414 self.session_checkpoint_export_sink = Some(sink);
415 self
416 }
417
418 pub fn with_file_session_store(mut self, dir: impl Into<PathBuf>) -> Self {
423 self.session_store = None;
424 self.file_session_store_dir = Some(dir.into());
425 self
426 }
427
428 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
430 self.session_id = Some(id.into());
431 self
432 }
433
434 pub fn with_tenant_id(mut self, tenant: impl Into<String>) -> Self {
437 self.tenant_id = Some(tenant.into());
438 self
439 }
440
441 pub fn with_principal(mut self, principal: impl Into<String>) -> Self {
444 self.principal = Some(principal.into());
445 self
446 }
447
448 pub fn with_agent_template_id(mut self, template_id: impl Into<String>) -> Self {
451 self.agent_template_id = Some(template_id.into());
452 self
453 }
454
455 pub fn with_correlation_id(mut self, corr: impl Into<String>) -> Self {
458 self.correlation_id = Some(corr.into());
459 self
460 }
461
462 pub fn with_budget_guard(mut self, guard: Arc<dyn crate::budget::BudgetGuard>) -> Self {
467 self.budget_guard = Some(guard);
468 self
469 }
470
471 pub fn with_host_env(mut self, env: Arc<crate::host_env::HostEnv>) -> Self {
477 self.host_env = Some(env);
478 self
479 }
480
481 pub fn with_retention_limits(
488 mut self,
489 limits: crate::retention::SessionRetentionLimits,
490 ) -> Self {
491 self.retention_limits = Some(limits);
492 self
493 }
494
495 pub fn with_rl_trajectory(mut self, config: crate::rl_trajectory::RlTrajectoryConfig) -> Self {
501 self.rl_trajectory = Some(config);
502 self
503 }
504
505 pub fn with_llm_logprobs(mut self, enabled: bool) -> Self {
507 self.llm_logprobs = Some(enabled);
508 self
509 }
510
511 pub fn with_llm_top_logprobs(mut self, top_logprobs: usize) -> Self {
513 self.llm_logprobs = Some(true);
514 self.llm_top_logprobs = Some(top_logprobs);
515 self
516 }
517
518 pub fn with_auto_save(mut self, enabled: bool) -> Self {
520 self.auto_save = enabled;
521 self
522 }
523
524 pub fn with_artifact_store_limits(mut self, limits: crate::tools::ArtifactStoreLimits) -> Self {
526 self.artifact_store_limits = Some(limits);
527 self
528 }
529
530 pub fn with_immutable_content_adapter(
535 mut self,
536 adapter: crate::tools::ImmutableContentAdapterSession,
537 ) -> Self {
538 self.immutable_content_adapter = Some(adapter);
539 self
540 }
541
542 pub fn with_tool_result_transform_policy(
544 mut self,
545 policy: crate::tools::ToolResultTransformPolicyV1,
546 ) -> Self {
547 self.tool_result_transform_policy = Some(policy);
548 self
549 }
550
551 pub fn with_tool_presentation_profile(
553 mut self,
554 profile: crate::tools::ToolPresentationProfileV1,
555 ) -> Self {
556 self.tool_presentation_profile = Some(profile);
557 self
558 }
559
560 pub fn with_parse_retries(mut self, max: u32) -> Self {
566 self.max_parse_retries = Some(max);
567 self
568 }
569
570 pub fn with_tool_timeout(mut self, timeout_ms: u64) -> Self {
576 self.tool_timeout_ms = Some(timeout_ms);
577 self
578 }
579
580 pub fn with_llm_api_timeout(mut self, timeout_ms: u64) -> Self {
586 self.llm_api_timeout_ms = Some(timeout_ms);
587 self
588 }
589
590 pub fn with_circuit_breaker(mut self, threshold: u32) -> Self {
596 self.circuit_breaker_threshold = Some(threshold);
597 self
598 }
599
600 pub fn with_duplicate_tool_call_threshold(mut self, threshold: u32) -> Self {
606 self.duplicate_tool_call_threshold = Some(threshold.max(1));
607 self
608 }
609
610 pub fn with_resilience_defaults(self) -> Self {
617 self.with_parse_retries(2)
618 .with_tool_timeout(120_000)
619 .with_llm_api_timeout(120_000)
620 .with_circuit_breaker(3)
621 }
622
623 pub fn with_sandbox_handle(mut self, handle: Arc<dyn crate::sandbox::BashSandbox>) -> Self {
632 self.sandbox_handle = Some(handle);
633 self
634 }
635
636 pub fn with_allow_process_host_sandbox(mut self, allow: bool) -> Self {
644 self.allow_process_host_sandbox = allow;
645 self
646 }
647
648 pub fn with_workspace_backend(
654 mut self,
655 services: Arc<crate::workspace::WorkspaceServices>,
656 ) -> Self {
657 self.workspace_services = Some(services);
658 self
659 }
660
661 pub fn with_workspace_retrieval(
667 mut self,
668 options: crate::workspace::WorkspaceRetrievalOptions,
669 ) -> Self {
670 self.workspace_retrieval = Some(options);
671 self
672 }
673
674 pub fn without_workspace_retrieval(mut self) -> Self {
681 self.workspace_retrieval = None;
682 self
683 }
684
685 pub fn with_auto_compact(mut self, enabled: bool) -> Self {
690 self.auto_compact = enabled;
691 self
692 }
693
694 pub fn with_auto_compact_threshold(mut self, threshold: f32) -> Self {
696 self.auto_compact_threshold = Some(threshold.clamp(0.0, 1.0));
697 self
698 }
699
700 pub fn with_max_context_tokens(mut self, tokens: usize) -> Self {
702 self.max_context_tokens = Some(tokens);
703 self
704 }
705
706 pub fn with_continuation(mut self, enabled: bool) -> Self {
711 self.continuation_enabled = Some(enabled);
712 self
713 }
714
715 pub fn with_max_continuation_turns(mut self, turns: u32) -> Self {
717 self.max_continuation_turns = Some(turns);
718 self
719 }
720
721 pub fn with_mcp(mut self, manager: Arc<crate::mcp::manager::McpManager>) -> Self {
728 self.mcp_manager = Some(manager);
729 self
730 }
731
732 pub fn with_temperature(mut self, temperature: f32) -> Self {
733 self.temperature = Some(temperature);
734 self
735 }
736
737 pub fn with_thinking_budget(mut self, budget: usize) -> Self {
738 self.thinking_budget = Some(budget);
739 self
740 }
741
742 pub fn with_max_tool_rounds(mut self, rounds: usize) -> Self {
747 self.max_tool_rounds = Some(rounds);
748 self
749 }
750
751 pub fn with_max_parallel_tasks(mut self, tasks: usize) -> Self {
753 self.max_parallel_tasks = Some(tasks.max(1));
754 self
755 }
756
757 pub fn with_auto_delegation(mut self, config: crate::config::AutoDelegationConfig) -> Self {
759 self.auto_delegation = Some(config);
760 self
761 }
762
763 pub fn with_auto_delegation_enabled(mut self, enabled: bool) -> Self {
765 let mut config = self.auto_delegation.take().unwrap_or_default();
766 config.enabled = enabled;
767 self.auto_delegation = Some(config);
768 self
769 }
770
771 pub fn with_manual_delegation_enabled(mut self, enabled: bool) -> Self {
778 if let Some(config) = &mut self.auto_delegation {
779 config.allow_manual_delegation = enabled;
780 }
781 self.manual_delegation_enabled = Some(enabled);
782 self
783 }
784
785 pub fn with_auto_parallel_delegation(mut self, enabled: bool) -> Self {
790 if let Some(config) = &mut self.auto_delegation {
791 config.auto_parallel = enabled;
792 }
793 self.auto_parallel_delegation = Some(enabled);
794 self
795 }
796
797 pub fn with_prompt_slots(mut self, slots: SystemPromptSlots) -> Self {
802 self.prompt_slots = Some(slots);
803 self
804 }
805
806 pub fn with_output_language(mut self, language: impl Into<String>) -> Self {
811 let slots = self
812 .prompt_slots
813 .take()
814 .unwrap_or_default()
815 .with_output_language(language);
816 self.prompt_slots = Some(slots);
817 self
818 }
819
820 pub fn with_hook_executor(mut self, executor: Arc<dyn crate::hooks::HookExecutor>) -> Self {
825 self.hook_executor = Some(executor);
826 self
827 }
828
829 pub fn with_effect_isolation(mut self, enabled: bool) -> Self {
831 self.effect_isolation = enabled;
832 self
833 }
834
835 pub fn with_completion_waivers(
836 mut self,
837 waivers: Vec<crate::harness_loop::CompletionWaiverV1>,
838 ) -> Self {
839 self.completion_waivers = waivers;
840 self
841 }
842
843 pub fn with_plan_run(mut self, admission: crate::harness_loop::PlanRunAdmission) -> Self {
844 self.plan_run = admission;
845 self
846 }
847
848 pub fn with_path_rules(mut self, rules: Vec<crate::path_instructions::PathRule>) -> Self {
849 self.path_rules = rules;
850 self
851 }
852
853 pub fn with_verifier(mut self, enabled: bool) -> Self {
854 self.verifier_enabled = enabled;
855 self
856 }
857
858 pub fn with_completion_attestor(
864 mut self,
865 attestor: std::sync::Arc<dyn crate::completion_attestor::CompletionAttestor>,
866 ) -> Self {
867 self.completion_attestor = Some(attestor);
868 self
869 }
870
871 pub fn with_harness(mut self, harness: crate::meta_harness::HarnessComposeOptions) -> Self {
873 self.harness = Some(harness);
874 self
875 }
876
877 pub fn with_host_harness_registry(
879 mut self,
880 registry: std::sync::Arc<dyn crate::meta_harness::HostHarnessRegistry>,
881 ) -> Self {
882 self.host_harness_registry = Some(registry);
883 self
884 }
885
886 pub fn with_host_harness_assembler(
892 mut self,
893 assembler: std::sync::Arc<dyn crate::meta_harness::HostHarnessAssembler>,
894 ) -> Self {
895 self.host_harness_assembler = Some(assembler);
896 self
897 }
898
899 pub fn with_external_observations(
900 mut self,
901 observations: Vec<crate::external_observation::ExternalObservationV1>,
902 ) -> Self {
903 self.external_observations = observations;
904 self
905 }
906
907 pub fn with_outcome_ledger(mut self, ledger: crate::outcome_memory::OutcomeLedger) -> Self {
908 self.outcome_ledger = ledger;
909 self
910 }
911
912 pub fn with_read_only_session(mut self, read_only: bool) -> Self {
913 self.read_only_session = read_only;
914 self
915 }
916
917 pub(crate) fn session_id_hint(&self) -> String {
918 self.session_id
919 .clone()
920 .filter(|id| !id.trim().is_empty())
921 .unwrap_or_else(|| "session".to_string())
922 }
923
924 pub(crate) fn can_write_workspace(&self) -> bool {
925 !self.read_only_session
926 }
927}