use super::*;
#[derive(Debug)]
pub struct AgentSession {
pub(super) provider_id: String,
pub(super) model: String,
pub(super) system_prompt: String,
pub(super) context_budget: ContextBudget,
pub(super) thinking_level: crate::thinking::ThinkingLevel,
pub(super) text_verbosity: Option<TextVerbosity>,
pub(super) thinking_levels: Vec<crate::thinking::ThinkingLevel>,
pub(super) send_default_reasoning_summary: bool,
pub(super) reasoning_updates_enabled: bool,
pub(super) replay_cache: Arc<Mutex<ConversationReplayCache>>,
pub(super) completion_verification: CompletionVerificationSettings,
pub(super) show_jev_debug_messages: bool,
pub(super) skill_suggestion: Option<skill_suggestion::SkillSuggestionContext>,
}
impl Clone for AgentSession {
fn clone(&self) -> Self {
Self {
provider_id: self.provider_id.clone(),
model: self.model.clone(),
system_prompt: self.system_prompt.clone(),
context_budget: self.context_budget.clone(),
thinking_level: self.thinking_level,
text_verbosity: self.text_verbosity,
thinking_levels: self.thinking_levels.clone(),
send_default_reasoning_summary: self.send_default_reasoning_summary,
reasoning_updates_enabled: self.reasoning_updates_enabled,
replay_cache: Arc::new(Mutex::new(ConversationReplayCache::default())),
completion_verification: self.completion_verification.clone(),
show_jev_debug_messages: self.show_jev_debug_messages,
skill_suggestion: self.skill_suggestion.clone(),
}
}
}
impl PartialEq for AgentSession {
fn eq(&self, other: &Self) -> bool {
self.provider_id == other.provider_id
&& self.model == other.model
&& self.system_prompt == other.system_prompt
&& self.context_budget == other.context_budget
&& self.thinking_level == other.thinking_level
&& self.text_verbosity == other.text_verbosity
&& self.thinking_levels == other.thinking_levels
&& self.send_default_reasoning_summary == other.send_default_reasoning_summary
&& self.reasoning_updates_enabled == other.reasoning_updates_enabled
&& self.completion_verification == other.completion_verification
&& self.show_jev_debug_messages == other.show_jev_debug_messages
&& self.skill_suggestion == other.skill_suggestion
}
}
impl Eq for AgentSession {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct AgentSessionConfig {
pub(super) provider_id: String,
pub(super) model: String,
pub(super) context_budget: ContextBudget,
pub(super) thinking_level: crate::thinking::ThinkingLevel,
pub(super) text_verbosity: Option<TextVerbosity>,
pub(super) thinking_levels: Vec<crate::thinking::ThinkingLevel>,
pub(super) send_default_reasoning_summary: bool,
}
impl AgentSessionConfig {
pub(crate) fn new(provider_id: impl Into<String>, model: impl Into<String>) -> Self {
Self {
provider_id: normalize_agent_identifier(provider_id.into(), "provider"),
model: normalize_agent_identifier(model.into(), "model"),
context_budget: ContextBudget::default(),
thinking_level: crate::thinking::ThinkingLevel::Default,
text_verbosity: None,
thinking_levels: crate::thinking::default_thinking_levels(),
send_default_reasoning_summary: false,
}
}
pub(super) fn for_model(model: impl Into<String>) -> Self {
Self::new(crate::providers::OPENAI_CODEX_PROVIDER, model)
}
pub(crate) fn with_context_budget(mut self, budget: ContextBudget) -> Self {
self.context_budget = budget;
self
}
pub(crate) fn with_thinking_level(
mut self,
thinking_level: crate::thinking::ThinkingLevel,
) -> Self {
self.thinking_level = thinking_level;
self
}
pub(crate) fn with_text_verbosity(mut self, text_verbosity: Option<TextVerbosity>) -> Self {
self.text_verbosity = text_verbosity;
self
}
pub(crate) fn with_thinking_levels(
mut self,
thinking_levels: Vec<crate::thinking::ThinkingLevel>,
) -> Self {
self.thinking_levels = crate::thinking::normalize_thinking_levels(&thinking_levels);
self.thinking_level =
crate::thinking::resolve_thinking_level(&self.thinking_levels, self.thinking_level);
self
}
pub(crate) fn with_default_reasoning_summary(mut self, supported: bool) -> Self {
self.send_default_reasoning_summary = supported;
self
}
}
impl AgentSession {
pub(crate) fn new_with_prompt_dir_and_subagents(
model: impl Into<String>,
prompt_dir: Option<&Path>,
instructions: &[InstructionFile],
skills: &SkillDiscovery,
subagents_section: Option<&str>,
code_mode_enabled: bool,
) -> anyhow::Result<Self> {
let system_prompt = build_system_prompt_with_prompt_dir_and_subagents(
prompt_dir,
instructions,
skills,
subagents_section,
code_mode_enabled,
)?;
Ok(Self::from_system_prompt(
system_prompt,
AgentSessionConfig::for_model(model),
))
}
pub(super) fn from_system_prompt(system_prompt: String, config: AgentSessionConfig) -> Self {
Self {
provider_id: config.provider_id,
model: config.model,
system_prompt,
context_budget: config.context_budget,
thinking_level: config.thinking_level,
text_verbosity: config.text_verbosity,
thinking_levels: config.thinking_levels,
send_default_reasoning_summary: config.send_default_reasoning_summary,
reasoning_updates_enabled: false,
replay_cache: Arc::new(Mutex::new(ConversationReplayCache::default())),
completion_verification: CompletionVerificationSettings::default(),
show_jev_debug_messages: false,
skill_suggestion: None,
}
}
pub(crate) fn with_reasoning_updates(mut self, enabled: bool) -> Self {
self.reasoning_updates_enabled = enabled;
self
}
pub(crate) fn with_completion_verification(
mut self,
settings: CompletionVerificationSettings,
) -> Self {
self.completion_verification = settings;
self
}
pub(crate) fn with_jev_debug_messages(mut self, enabled: bool) -> Self {
self.show_jev_debug_messages = enabled;
self
}
pub(crate) fn with_skill_suggestion(
mut self,
context: Option<skill_suggestion::SkillSuggestionContext>,
) -> Self {
self.skill_suggestion = context;
self
}
pub(super) fn reasoning_selection_payload(&self) -> serde_json::Value {
json!({"provider": self.provider_id, "model": self.model, "effort": self.thinking_level})
}
pub(crate) fn with_config(mut self, config: AgentSessionConfig) -> Self {
self.model = config.model;
self.with_provider_id(config.provider_id)
.with_thinking_level(config.thinking_level)
.with_text_verbosity(config.text_verbosity)
.with_thinking_levels(config.thinking_levels)
.with_default_reasoning_summary(config.send_default_reasoning_summary)
.with_context_budget(config.context_budget)
}
pub(crate) fn provider_id(&self) -> &str {
&self.provider_id
}
pub(crate) fn model(&self) -> &str {
&self.model
}
pub(crate) fn thinking_level(&self) -> crate::thinking::ThinkingLevel {
self.thinking_level
}
pub(crate) fn with_appended_system_prompt(&self, section: &str) -> Self {
let mut cloned = self.clone();
let section = section.trim();
if !section.is_empty() {
cloned.system_prompt.push_str("\n\n");
cloned.system_prompt.push_str(section);
}
cloned
}
pub fn with_provider_id(mut self, provider_id: impl Into<String>) -> Self {
self.provider_id = normalize_agent_identifier(provider_id.into(), "provider");
self.reasoning_updates_enabled = false;
self
}
pub fn with_context_budget(mut self, budget: ContextBudget) -> Self {
self.context_budget = budget;
self
}
pub(crate) fn with_model(mut self, model: impl Into<String>) -> Self {
self.model = normalize_agent_identifier(model.into(), "model");
self.reasoning_updates_enabled = false;
self
}
pub(crate) fn with_thinking_level(
mut self,
thinking_level: crate::thinking::ThinkingLevel,
) -> Self {
self.thinking_level = thinking_level;
self
}
pub(crate) fn with_text_verbosity(mut self, text_verbosity: Option<TextVerbosity>) -> Self {
self.text_verbosity = text_verbosity;
self
}
pub(crate) fn with_thinking_levels(
mut self,
thinking_levels: Vec<crate::thinking::ThinkingLevel>,
) -> Self {
self.thinking_levels = crate::thinking::normalize_thinking_levels(&thinking_levels);
self.thinking_level =
crate::thinking::resolve_thinking_level(&self.thinking_levels, self.thinking_level);
self
}
pub(crate) fn with_default_reasoning_summary(mut self, supported: bool) -> Self {
self.send_default_reasoning_summary = supported;
self
}
pub(crate) fn with_reasoning_override(
mut self,
thinking_level: crate::thinking::ThinkingLevel,
) -> Self {
self.thinking_level =
crate::thinking::resolve_thinking_level(&self.thinking_levels, thinking_level);
self
}
pub(crate) fn with_provider_model_reasoning(
self,
provider_id: impl Into<String>,
model: impl Into<String>,
thinking_levels: Vec<crate::thinking::ThinkingLevel>,
thinking_level: Option<crate::thinking::ThinkingLevel>,
) -> Self {
let selected_thinking = thinking_level.unwrap_or(self.thinking_level);
let send_default_reasoning_summary = thinking_levels.len() > 1;
let effective_thinking =
crate::thinking::resolve_thinking_level(&thinking_levels, selected_thinking);
self.with_provider_id(provider_id)
.with_model(model)
.with_thinking_levels(thinking_levels)
.with_default_reasoning_summary(send_default_reasoning_summary)
.with_thinking_level(effective_thinking)
}
}