Skip to main content

aether_cli/
resolve.rs

1use aether_core::agent_spec::AgentSpec;
2use aether_project::{AetherSettings, AgentCatalog, SettingsError};
3use llm::{ProviderConnectionOverrides, ReasoningEffort};
4use std::path::Path;
5use thiserror::Error;
6
7const FALLBACK_MODEL: &str = "anthropic:claude-sonnet-4-5";
8
9#[derive(Clone, Debug, Default)]
10pub(crate) enum InitialSessionSelection {
11    #[default]
12    Default,
13    Agent(String),
14    Model {
15        model: String,
16        reasoning_effort: Option<ReasoningEffort>,
17    },
18}
19
20impl InitialSessionSelection {
21    pub(crate) fn agent(name: String) -> Self {
22        Self::Agent(name)
23    }
24
25    pub(crate) fn model(model: String, reasoning_effort: Option<ReasoningEffort>) -> Self {
26        Self::Model { model, reasoning_effort }
27    }
28}
29
30pub(crate) struct ResolvedAgentSelection {
31    pub(crate) spec: AgentSpec,
32    pub(crate) catalog: AgentCatalog,
33}
34
35#[derive(Debug, Error)]
36pub(crate) enum AgentSelectionError {
37    #[error(transparent)]
38    Settings(#[from] SettingsError),
39    #[error("{0}")]
40    Agent(SettingsError),
41    #[error("Model error: {0}")]
42    Model(String),
43}
44
45pub(crate) fn resolve_agent_from_settings(
46    cwd: &Path,
47    settings: AetherSettings,
48    provider_connections: ProviderConnectionOverrides,
49    selection: &InitialSessionSelection,
50) -> Result<ResolvedAgentSelection, AgentSelectionError> {
51    let catalog = AgentCatalog::from_settings_or_empty(cwd, settings)?.with_provider_connections(provider_connections);
52    resolve_agent_from_catalog(catalog, selection)
53}
54
55pub(crate) fn resolve_agent_from_catalog(
56    catalog: AgentCatalog,
57    selection: &InitialSessionSelection,
58) -> Result<ResolvedAgentSelection, AgentSelectionError> {
59    let spec = match selection {
60        InitialSessionSelection::Agent(name) => catalog.resolve(name).map_err(AgentSelectionError::Agent)?,
61        InitialSessionSelection::Model { model, reasoning_effort } => {
62            catalog.default_spec(&model.parse().map_err(AgentSelectionError::Model)?, *reasoning_effort)
63        }
64        InitialSessionSelection::Default => match catalog.default_agent() {
65            Some(spec) => spec.clone(),
66            None => catalog.default_spec(&FALLBACK_MODEL.parse().map_err(AgentSelectionError::Model)?, None),
67        },
68    };
69
70    Ok(ResolvedAgentSelection { spec, catalog })
71}
72
73pub fn resolve_agent_spec(
74    catalog: &AgentCatalog,
75    agent_name: Option<&str>,
76) -> Result<AgentSpec, crate::error::CliError> {
77    let selection =
78        agent_name.map_or(InitialSessionSelection::Default, |name| InitialSessionSelection::Agent(name.to_string()));
79    resolve_agent_from_catalog(catalog.clone(), &selection).map(|resolved| resolved.spec).map_err(|error| match error {
80        AgentSelectionError::Settings(error) | AgentSelectionError::Agent(error) => {
81            crate::error::CliError::AgentError(error.to_string())
82        }
83        AgentSelectionError::Model(error) => crate::error::CliError::ModelError(error),
84    })
85}