lha 1.0.6

Long-Horizon Agent command-line package that installs the lha binary.
Documentation
use std::sync::Arc;

use crate::product::agent::AuthManager;
use crate::product::agent::CodexThread;
use crate::product::agent::config::Config;
use crate::product::agent::features::Feature;
use crate::product::agent::memories::metrics;
use crate::product::agent::memories::runtime::MemoryStartupContext;
use crate::product::agent::models_manager::manager::ModelsManager;
use crate::product::agent::skills::SkillsManager;
use crate::product::protocol::ThreadId;
use crate::product::protocol::protocol::SessionSource;
use tracing::debug;
use tracing::warn;

#[allow(clippy::too_many_arguments)]
pub(crate) fn start_memories_startup_task(
    auth_manager: Arc<AuthManager>,
    models_manager: Arc<ModelsManager>,
    skills_manager: Arc<SkillsManager>,
    config: Config,
    thread_id: ThreadId,
    thread: Arc<CodexThread>,
    session_source: SessionSource,
) {
    if config.ephemeral {
        metrics::counter(metrics::STARTUP, 1, &[("status", "skipped_ephemeral")]);
        return;
    }
    if !config.features.enabled(Feature::MemoryTool) {
        metrics::counter(
            metrics::STARTUP,
            1,
            &[("status", "skipped_feature_disabled")],
        );
        return;
    }
    if session_source.is_non_root_agent() {
        metrics::counter(metrics::STARTUP, 1, &[("status", "skipped_non_root_agent")]);
        return;
    }
    if thread.state_db().is_none() {
        metrics::counter(
            metrics::STARTUP,
            1,
            &[("status", "skipped_state_db_unavailable")],
        );
        return;
    }

    let context = Arc::new(MemoryStartupContext::new(
        auth_manager,
        models_manager,
        skills_manager,
        thread_id,
        thread,
        config,
        session_source,
    ));
    tokio::spawn(async move {
        metrics::counter(metrics::STARTUP, 1, &[("status", "started")]);
        let memory_root = context.memory_root();
        if let Err(err) = crate::product::memories_write::ensure_layout(memory_root.as_path()).await
        {
            warn!("failed to initialize memories layout: {err}");
            metrics::counter(metrics::STARTUP, 1, &[("status", "layout_error")]);
            return;
        }
        if let Some(state_db) = context.state_db()
            && let Some(memories) = state_db.memories()
            && let Err(err) = memories
                .prune_stage1_outputs_for_retention(
                    context.config().memories.max_unused_days,
                    crate::product::memories_write::STAGE_ONE_PRUNE_BATCH_SIZE,
                )
                .await
        {
            warn!("failed to prune memory stage1 outputs: {err}");
        }

        debug!(
            min_rate_limit_remaining_percent =
                context.config().memories.min_rate_limit_remaining_percent,
            "memory startup rate-limit snapshot unavailable; continuing"
        );
        metrics::counter(
            metrics::STARTUP,
            1,
            &[("status", "rate_limit_snapshot_unavailable_continuing")],
        );

        crate::product::agent::memories::phase1::run(Arc::clone(&context)).await;
        crate::product::agent::memories::phase2::run(context).await;
        metrics::counter(metrics::STARTUP, 1, &[("status", "completed")]);
    });
}