use crate::blueprint::{
ContentTransform, ContextTransform, EdgeTransform, ModelConfig, ModelEntry, RegionMapping,
StageMode, StuckConfig, TransitionCondition, TransitionEdge,
};
use crate::error::{Error, Result};
use crate::layout::{RegionDefinition, RegionSeed};
use crate::lifecycle::CompactionConfig;
use crate::{Blueprint, ContextLayout, EvictionStrategy, RegionKind, Stage};
pub fn parse_manifest(content: &str) -> Result<Blueprint> {
let parsed: toml::Value = toml::from_str(content)
.map_err(|e| Error::Other(format!("Failed to parse agent.leviath: {e}")))?;
let agent = parsed
.get("agent")
.ok_or_else(|| Error::Other("Missing [agent] section".to_string()))?;
let name = str_of(agent, "name").unwrap_or("unnamed").to_string();
let version = str_of(agent, "version").unwrap_or("0.1.0").to_string();
let description = str_of(agent, "description").unwrap_or("").to_string();
let max_child_depth = count_of(agent, "[agent]", "max_child_depth")?;
let entry_stage = str_of(agent, "entry_stage").map(|s| s.to_string());
let dynamic_tools = bool_of(agent, "dynamic_tools").unwrap_or(false);
let mut stages = Vec::new();
if let Some(stages_table) = table_of(&parsed, "stages") {
for (stage_name, stage_value) in stages_table {
stages.push(parse_stage(stage_name, stage_value)?);
}
}
if stages.is_empty() {
stages.push(Stage::new(
"main".to_string(),
ModelConfig::new("anthropic".to_string(), "claude-sonnet-4-6".to_string()),
));
}
let (mut regions, mut total_tokens) = match parsed
.get("context")
.and_then(|v| v.get("regions"))
.and_then(|v| v.as_table())
{
Some(regions_table) => parse_region_layout(regions_table)?,
None => (Vec::new(), 0usize),
};
if regions.is_empty() {
regions.push(RegionDefinition::new(
"system".to_string(),
RegionKind::Pinned,
8000,
));
regions.push(RegionDefinition::new(
"conversation".to_string(),
RegionKind::SlidingWindow {
max_items: 10,
eviction_strategy: EvictionStrategy::default(),
},
10000,
));
total_tokens = 18000;
}
let layout = ContextLayout::new(regions, total_tokens);
let mut blueprint = Blueprint::new(name, description, stages, layout);
blueprint.version = version;
blueprint.max_child_depth = max_child_depth;
blueprint.entry_stage = entry_stage;
blueprint.dynamic_tools = dynamic_tools;
if let Some(compaction_table) = table_of(&parsed, "compaction") {
blueprint.compaction_config = Some(parse_compaction_config(compaction_table)?);
}
if let Some(security_table) = table_of(&parsed, "security") {
blueprint.security = Some(parse_security_config(security_table));
}
if let Some(bth) = bool_of(agent, "batch_tool_hint") {
blueprint.batch_tool_hint = Some(bth);
}
if let Some(sh) = bool_of(agent, "shell_hint") {
blueprint.shell_hint = Some(sh);
}
if let Some(nudge_table) = table_of(agent, "nudge") {
blueprint.nudge = Some(parse_nudge_config("[agent.nudge]", nudge_table)?);
}
if let Some(output_table) = table_of(agent, "output") {
blueprint.output = Some(parse_output_spec("[agent.output]", output_table)?);
}
if let Some(sandbox_table) = table_of(&parsed, "sandbox") {
blueprint.sandbox = Some(parse_sandbox_config("", sandbox_table)?);
}
if let Some(rp_table) = table_of(&parsed, "read_paths") {
blueprint.read_paths = Some(parse_read_paths(rp_table)?);
}
if let Some(sc_table) = table_of(&parsed, "safe_commands") {
blueprint.safe_commands = Some(parse_safe_commands(sc_table)?);
}
if let Some(tp_table) = table_of(&parsed, "tool_permissions") {
blueprint
.metadata
.extend(tool_permission_metadata(tp_table)?);
}
if let Some(context_table) = table_of(&parsed, "context")
&& let Some(ft_table) = table_of(context_table, "file_tracking")
{
blueprint.file_tracking = Some(parse_file_tracking(ft_table)?);
}
if let Some(rd_table) = table_of(&parsed, "repetition_detection") {
blueprint.repetition_detection = Some(parse_repetition_detection(rd_table)?);
}
if let Some(transforms_arr) = array_of(&parsed, "transforms") {
blueprint
.transforms
.extend(transforms_arr.iter().map(parse_context_transform));
}
Ok(blueprint)
}
mod model;
mod read;
mod regions;
mod sections;
mod stage;
use model::*;
use read::*;
use regions::*;
use sections::*;
use stage::*;
#[cfg(test)]
const AGENT_KEYS: &[&str] = &[
"batch_tool_hint",
"description",
"dynamic_tools",
"entry_stage",
"max_child_depth",
"name",
"nudge",
"output",
"shell_hint",
"version",
];
#[cfg(test)]
mod tests;