use crate::config::{CustomProviderConfig, HookSettings};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use super::{
agent::{
CompactionSettings, InstructionsSettings, IntegrationsSettings, ModelsSettings,
SessionTitleSettings, SkillsSettings, SubagentsSettings, TuiSettings,
},
helpers::is_false,
providers::{ProviderStreamSettings, SelectedModelSettings, TtsrSettings},
services::{LspSettings, McpServersSettings},
tools::ToolSettings,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SettingsScope {
Global,
Project,
}
impl SettingsScope {
pub(crate) fn label(self) -> &'static str {
match self {
Self::Global => "Global",
Self::Project => "Project",
}
}
pub(crate) fn toggle(self) -> Self {
match self {
Self::Global => Self::Project,
Self::Project => Self::Global,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SettingsListKind {
Skills,
Tools,
Subagents,
Models,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum TextVerbosity {
#[default]
Low,
Medium,
High,
}
impl TextVerbosity {
pub(crate) fn as_api_str(self) -> &'static str {
match self {
Self::Low => "low",
Self::Medium => "medium",
Self::High => "high",
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct FastSettings {
#[serde(default, skip_serializing_if = "is_false")]
#[schemars(!skip_serializing_if, default)]
pub enabled: bool,
}
impl FastSettings {
pub(crate) fn is_default(&self) -> bool {
self == &Self::default()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct OpenAiResponsesSettings {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text_verbosity: Option<TextVerbosity>,
}
impl OpenAiResponsesSettings {
pub(crate) fn is_default(&self) -> bool {
self == &Self::default()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct OpenAiCodexSettings {
#[serde(default)]
pub text_verbosity: TextVerbosity,
}
impl OpenAiCodexSettings {
pub(crate) fn is_default(&self) -> bool {
self == &Self::default()
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub enum AnthropicCacheTtl {
#[default]
#[serde(rename = "5m")]
FiveMinutes,
#[serde(rename = "1h")]
OneHour,
}
pub const DEFAULT_SESSION_RETENTION_DAYS: u64 = 30;
fn default_session_retention_days() -> u64 {
DEFAULT_SESSION_RETENTION_DAYS
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct SessionSettings {
#[serde(default = "default_session_retention_days")]
pub retention_days: u64,
}
impl Default for SessionSettings {
fn default() -> Self {
Self {
retention_days: DEFAULT_SESSION_RETENTION_DAYS,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub(crate) struct AppearanceSettings {
#[serde(
default = "default_appearance_theme",
skip_serializing_if = "is_default_appearance_theme"
)]
pub(crate) theme: String,
#[serde(default, skip_serializing_if = "is_false")]
pub(crate) reduced_motion: bool,
}
impl Default for AppearanceSettings {
fn default() -> Self {
Self {
theme: crate::appearance::DEFAULT_THEME_ID.to_string(),
reduced_motion: false,
}
}
}
fn default_appearance_theme() -> String {
crate::appearance::DEFAULT_THEME_ID.to_string()
}
fn is_default_appearance_theme(theme: &String) -> bool {
theme == crate::appearance::DEFAULT_THEME_ID
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Settings {
pub selected_model: SelectedModelSettings,
pub fast: FastSettings,
pub openai_codex: OpenAiCodexSettings,
pub openai_responses: OpenAiResponsesSettings,
pub no_color: Option<bool>,
pub anthropic_cache_ttl: Option<AnthropicCacheTtl>,
pub file_autocomplete_respects_gitignore: bool,
pub context: Option<crate::context::ContextBudget>,
pub session_titles: SessionTitleSettings,
pub summarizer: super::agent::SummarizerSettings,
pub compaction: CompactionSettings,
pub tools: ToolSettings,
pub subagents: SubagentsSettings,
pub models: ModelsSettings,
pub hooks: HookSettings,
pub instructions: InstructionsSettings,
pub skills: SkillsSettings,
pub custom_providers: BTreeMap<String, CustomProviderConfig>,
pub mcp_servers: McpServersSettings,
pub lsp: LspSettings,
pub integrations: IntegrationsSettings,
pub tui: TuiSettings,
pub provider_stream: ProviderStreamSettings,
pub ttsr: TtsrSettings,
pub selected_primary_agent: Option<String>,
}
impl Default for Settings {
fn default() -> Self {
Self {
fast: FastSettings::default(),
openai_responses: OpenAiResponsesSettings::default(),
selected_model: SelectedModelSettings::default(),
openai_codex: OpenAiCodexSettings::default(),
no_color: None,
file_autocomplete_respects_gitignore: true,
anthropic_cache_ttl: None,
context: None,
session_titles: SessionTitleSettings::default(),
summarizer: super::agent::SummarizerSettings::default(),
compaction: CompactionSettings::default(),
tools: ToolSettings::default(),
subagents: SubagentsSettings::default(),
models: ModelsSettings::default(),
hooks: HookSettings::default(),
instructions: InstructionsSettings::default(),
skills: SkillsSettings::default(),
custom_providers: BTreeMap::new(),
mcp_servers: BTreeMap::new(),
lsp: LspSettings::default(),
integrations: IntegrationsSettings::default(),
tui: TuiSettings::default(),
provider_stream: ProviderStreamSettings::default(),
ttsr: TtsrSettings::default(),
selected_primary_agent: None,
}
}
}
impl Settings {
pub(crate) fn text_verbosity_for(&self, provider: &str) -> Option<TextVerbosity> {
if provider == crate::providers::OPENAI_CODEX_PROVIDER {
return Some(
self.openai_responses
.text_verbosity
.unwrap_or(self.openai_codex.text_verbosity),
);
}
self.custom_providers.get(provider).and_then(|custom| {
(custom.use_responses_endpoint && custom.supports_text_verbosity)
.then_some(self.openai_responses.text_verbosity)
.flatten()
})
}
}