vtcode 0.107.0

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use crate::agent::runloop::ui::build_inline_header_context;
use crate::agent::runloop::unified::session_setup::ide_context::{
    IdeContextBridge, status_line_editor_label, tui_header_summary,
};
use crate::agent::runloop::unified::{context_manager, palettes};
use crate::agent::runloop::welcome::SessionBootstrap;
use anyhow::Result;
use tracing::warn;
use vtcode_core::config::loader::VTCodeConfig;
use vtcode_core::config::types::AgentConfig as CoreAgentConfig;
use vtcode_core::llm::provider as uni;
use vtcode_core::utils::ansi::{AnsiRenderer, MessageStyle};
use vtcode_tui::app::{InlineHandle, InlineHeaderContext};

use super::persistent_memory::{
    apply_persistent_memory_header_guide, load_persistent_memory_status,
};

pub(super) struct HeaderContextInit<'a> {
    pub(super) config: &'a CoreAgentConfig,
    pub(super) vt_cfg: Option<&'a VTCodeConfig>,
    pub(super) session_bootstrap: &'a SessionBootstrap,
    pub(super) provider_client: &'a dyn uni::LLMProvider,
    pub(super) header_provider_label: String,
    pub(super) full_auto: bool,
}

pub(super) async fn initialize_header_context(
    renderer: &mut AnsiRenderer,
    handle: &InlineHandle,
    context_manager: &mut context_manager::ContextManager,
    ide_context_bridge: &mut Option<IdeContextBridge>,
    init: HeaderContextInit<'_>,
) -> Result<InlineHeaderContext> {
    let HeaderContextInit {
        config,
        vt_cfg,
        session_bootstrap,
        provider_client,
        header_provider_label,
        full_auto,
    } = init;

    let persistent_memory_status = load_persistent_memory_status(config, vt_cfg);
    if let Err(err) = persistent_memory_status.as_ref() {
        warn!(
            workspace = %config.workspace.display(),
            error = ?err,
            "Failed to load persistent memory status for TUI guide"
        );
        renderer.line(
            MessageStyle::Warning,
            "Persistent memory is enabled, but VT Code couldn't load the TUI memory guide.",
        )?;
    }
    let persistent_memory_status = persistent_memory_status.ok().flatten();

    if let Some(notice) = session_bootstrap.search_tools_notice.as_ref() {
        notice.render(renderer)?;
    }
    maybe_render_openai_priority_notice(renderer, config, vt_cfg)?;

    handle.set_theme(vtcode_core::ui::inline_theme_from_core_styles(
        &vtcode_core::ui::theme::active_styles(),
    ));
    palettes::apply_prompt_style(handle);

    let reasoning_label = vt_cfg
        .as_ref()
        .map(|cfg| cfg.agent.reasoning_effort.as_str().to_string())
        .unwrap_or_else(|| config.reasoning_effort.as_str().to_string());

    let mode_label = match (config.ui_surface, full_auto) {
        (vtcode_core::config::types::UiSurfacePreference::Inline, true) => "auto".to_string(),
        (vtcode_core::config::types::UiSurfacePreference::Inline, false) => "inline".to_string(),
        (vtcode_core::config::types::UiSurfacePreference::Alternate, _) => "alt".to_string(),
        (vtcode_core::config::types::UiSurfacePreference::Auto, true) => "auto".to_string(),
        (vtcode_core::config::types::UiSurfacePreference::Auto, false) => "std".to_string(),
    };

    let mut header_context = build_inline_header_context(
        config,
        vt_cfg,
        session_bootstrap,
        header_provider_label,
        config.model.clone(),
        provider_client.effective_context_size(&config.model),
        mode_label,
        reasoning_label,
    )
    .await?;
    if let Some(memory_status) = persistent_memory_status.as_ref() {
        apply_persistent_memory_header_guide(&mut header_context, memory_status);
    }

    let initial_editor_snapshot = if let Some(bridge) = ide_context_bridge.as_mut() {
        match bridge.refresh() {
            Ok((snapshot, _)) => snapshot,
            Err(err) => {
                warn!("Failed to refresh IDE context snapshot: {}", err);
                None
            }
        }
    } else {
        None
    };
    apply_ide_context_snapshot(
        context_manager,
        &mut header_context,
        handle,
        config.workspace.as_path(),
        vt_cfg,
        initial_editor_snapshot,
    );

    Ok(header_context)
}

fn maybe_render_openai_priority_notice(
    renderer: &mut AnsiRenderer,
    config: &CoreAgentConfig,
    vt_cfg: Option<&VTCodeConfig>,
) -> Result<()> {
    if !config.provider.eq_ignore_ascii_case("openai") {
        return Ok(());
    }

    let default_auth = vtcode_auth::OpenAIAuthConfig::default();
    let auth_cfg = vt_cfg.map(|cfg| &cfg.auth.openai).unwrap_or(&default_auth);
    let storage_mode = vt_cfg
        .map(|cfg| cfg.agent.credential_storage_mode)
        .unwrap_or_default();
    let api_key = vtcode_core::config::api_keys::get_api_key(
        "openai",
        &vtcode_core::config::api_keys::ApiKeySources::default(),
    )
    .ok();
    let overview =
        vtcode_config::auth::summarize_openai_credentials(auth_cfg, storage_mode, api_key)?;
    let Some(notice) = overview.notice.as_deref() else {
        return Ok(());
    };

    renderer.line(MessageStyle::Info, notice)?;
    if let Some(recommendation) = overview.recommendation.as_deref() {
        renderer.line(MessageStyle::Output, recommendation)?;
    }
    Ok(())
}

pub(crate) fn apply_ide_context_snapshot(
    context_manager: &mut context_manager::ContextManager,
    header_context: &mut InlineHeaderContext,
    handle: &InlineHandle,
    workspace: &std::path::Path,
    vt_cfg: Option<&VTCodeConfig>,
    snapshot: Option<vtcode_core::EditorContextSnapshot>,
) {
    let ide_context_config = vt_cfg.map(|cfg| &cfg.ide_context);
    context_manager.set_editor_context_snapshot(snapshot.clone(), ide_context_config);
    let effective_ide_context_config =
        context_manager.effective_ide_context_config_with_base(ide_context_config);
    header_context.editor_context = tui_header_summary(
        workspace,
        Some(&effective_ide_context_config),
        snapshot.as_ref(),
    );
    handle.set_header_context(header_context.clone());
}

pub(crate) fn ide_context_status_label(
    context_manager: &context_manager::ContextManager,
    workspace: &std::path::Path,
    vt_cfg: Option<&VTCodeConfig>,
    snapshot: Option<&vtcode_core::EditorContextSnapshot>,
    source: Option<&std::path::Path>,
) -> Option<String> {
    let effective_ide_context_config =
        context_manager.effective_ide_context_config_with_base(vt_cfg.map(|cfg| &cfg.ide_context));
    status_line_editor_label(
        workspace,
        Some(&effective_ide_context_config),
        snapshot,
        source,
    )
}

pub(crate) fn ide_context_status_label_from_bridge(
    context_manager: &context_manager::ContextManager,
    workspace: &std::path::Path,
    vt_cfg: Option<&VTCodeConfig>,
    ide_context_bridge: Option<&IdeContextBridge>,
) -> Option<String> {
    ide_context_bridge.and_then(|bridge| {
        ide_context_status_label(
            context_manager,
            workspace,
            vt_cfg,
            bridge.snapshot(),
            bridge.snapshot_source(),
        )
    })
}