enact-core 0.0.2

Core agent runtime for Enact - Graph-Native AI agents
Documentation
//! Long-running execution policy
//!
//! Defines policies for executions that can run for extended periods,
//! including checkpointing, memory management, and cost controls.

use serde::{Deserialize, Serialize};

/// Policy configuration for long-running agentic executions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LongRunningExecutionPolicy {
    /// Maximum number of dynamically discovered steps before intervention
    pub max_discovered_steps: Option<u32>,
    /// Maximum depth of discovery chains
    pub max_discovery_depth: Option<u32>,
    /// Alert threshold for cumulative cost in USD
    pub cost_alert_threshold_usd: Option<f64>,
    /// Maximum time without activity before idle timeout (seconds)
    pub idle_timeout_seconds: Option<u64>,
    /// Maximum repetitions of same methodology before loop detection
    pub max_same_step_repetitions: Option<u32>,
    /// Checkpointing strategy
    pub checkpointing: CheckpointPolicy,
    /// Memory management strategy
    pub memory: WorkingMemoryPolicy,
}

impl Default for LongRunningExecutionPolicy {
    fn default() -> Self {
        Self::standard()
    }
}

impl LongRunningExecutionPolicy {
    /// Standard preset
    pub fn standard() -> Self {
        Self {
            max_discovered_steps: Some(50),
            max_discovery_depth: Some(5),
            cost_alert_threshold_usd: Some(5.0),
            idle_timeout_seconds: Some(1800),
            max_same_step_repetitions: Some(3),
            checkpointing: CheckpointPolicy::default(),
            memory: WorkingMemoryPolicy::default(),
        }
    }

    /// Extended preset
    pub fn extended() -> Self {
        Self {
            max_discovered_steps: Some(300),
            max_discovery_depth: Some(10),
            cost_alert_threshold_usd: Some(50.0),
            idle_timeout_seconds: Some(14400),
            max_same_step_repetitions: Some(5),
            checkpointing: CheckpointPolicy {
                interval_steps: Some(10),
                on_discovery: true,
                ..Default::default()
            },
            memory: WorkingMemoryPolicy {
                strategy: ContextStrategy::Summarize {
                    summarize_after: 50,
                    keep_recent: 10,
                },
                max_tokens: Some(100_000),
                ..Default::default()
            },
        }
    }
}

/// Policy for checkpointing execution state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckpointPolicy {
    /// Checkpoint every N steps
    pub interval_steps: Option<u32>,
    /// Checkpoint every N seconds
    pub interval_seconds: Option<u64>,
    /// Checkpoint on every new step discovery
    pub on_discovery: bool,
    /// Maximum checkpoints to retain
    pub max_checkpoints: Option<u32>,
}

impl Default for CheckpointPolicy {
    fn default() -> Self {
        Self {
            interval_steps: Some(10),
            interval_seconds: Some(300),
            on_discovery: false,
            max_checkpoints: Some(50),
        }
    }
}

/// Policy for working memory management
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkingMemoryPolicy {
    /// Strategy to use when context fills up
    pub strategy: ContextStrategy,
    /// Maximum tokens allowed in working memory
    pub max_tokens: Option<u32>,
    /// Whether to auto-compact when near limit
    pub auto_compact: bool,
}

impl Default for WorkingMemoryPolicy {
    fn default() -> Self {
        Self {
            strategy: ContextStrategy::SlidingWindow { size: 20 },
            max_tokens: Some(32_000),
            auto_compact: true,
        }
    }
}

/// Strategy for managing context window
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ContextStrategy {
    /// Keep all history (risk of overflow)
    KeepAll,
    /// Keep only the last N messages
    SlidingWindow { size: u32 },
    /// Summarize older history, keep recent raw
    Summarize {
        summarize_after: u32,
        keep_recent: u32,
    },
    /// Move older history to episodic memory
    Archive {
        archive_after: u32,
        keep_recent: u32,
    },
}