roboticus-core 0.11.2

Shared types, config parsing, personality system, and error types for the Roboticus agent runtime
Documentation
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct ConfigMigrationReport {
    pub renamed_server_host_to_bind: bool,
    pub routing_mode_heuristic_rewritten: bool,
    pub deny_on_empty_allowlist_hardened: bool,
    pub removed_credit_cooldown_seconds: bool,
}

impl ConfigMigrationReport {
    pub fn changed(&self) -> bool {
        self.renamed_server_host_to_bind
            || self.routing_mode_heuristic_rewritten
            || self.deny_on_empty_allowlist_hardened
            || self.removed_credit_cooldown_seconds
    }
}

pub fn migrate_removed_legacy_config(raw: &str) -> Result<Option<(String, ConfigMigrationReport)>> {
    let mut doc: toml::Value = toml::from_str(raw)?;
    let mut report = ConfigMigrationReport::default();
    let root = match doc.as_table_mut() {
        Some(root) => root,
        None => return Ok(None),
    };

    if let Some(server) = root.get_mut("server").and_then(|v| v.as_table_mut())
        && let Some(host) = server.remove("host")
    {
        if !server.contains_key("bind") {
            server.insert("bind".to_string(), host);
        }
        report.renamed_server_host_to_bind = true;
    }

    if let Some(models) = root.get_mut("models").and_then(|v| v.as_table_mut())
        && let Some(routing) = models.get_mut("routing").and_then(|v| v.as_table_mut())
        && let Some(mode) = routing.get_mut("mode")
        && let Some(mode_str) = mode.as_str()
        && mode_str == "heuristic"
    {
        *mode = toml::Value::String("metascore".into());
        report.routing_mode_heuristic_rewritten = true;
    }

    if let Some(security) = root.get_mut("security").and_then(|v| v.as_table_mut())
        && let Some(deny) = security.get_mut("deny_on_empty_allowlist")
        && deny.as_bool() == Some(false)
    {
        *deny = toml::Value::Boolean(true);
        report.deny_on_empty_allowlist_hardened = true;
    }

    if let Some(circuit_breaker) = root.get_mut("circuit_breaker").and_then(|v| v.as_table_mut())
        && circuit_breaker.remove("credit_cooldown_seconds").is_some()
    {
        report.removed_credit_cooldown_seconds = true;
    }

    if !report.changed() {
        return Ok(None);
    }

    Ok(Some((toml::to_string_pretty(&doc)?, report)))
}