use super::*;
pub(super) fn parse_compaction_config(table: &toml::value::Table) -> CompactionConfig {
let mut cc = CompactionConfig::default();
if let Some(provider) = table.get("provider").and_then(|v| v.as_str()) {
cc.provider = provider.to_string();
}
if let Some(model) = table.get("model").and_then(|v| v.as_str()) {
cc.model = model.to_string();
}
if let Some(sp) = table.get("system_prompt").and_then(|v| v.as_str()) {
cc.system_prompt = Some(sp.to_string());
}
if let Some(mst) = table.get("max_summary_tokens").and_then(|v| v.as_integer()) {
cc.max_summary_tokens = mst as usize;
}
if let Some(temp) = table.get("temperature").and_then(|v| v.as_float()) {
cc.temperature = temp as f32;
}
cc
}
pub(super) fn parse_read_paths(
table: &toml::value::Table,
) -> Result<crate::blueprint::ReadPathsConfig> {
let mut allow = Vec::new();
if let Some(entries) = table.get("allow").and_then(|v| v.as_array()) {
for entry in entries {
let Some(raw) = entry.as_str() else {
return Err(Error::Other(format!(
"[read_paths] allow entries must be strings, got: {entry}"
)));
};
crate::read_paths::validate_entry_syntax(raw).map_err(Error::Other)?;
allow.push(raw.to_string());
}
}
Ok(crate::blueprint::ReadPathsConfig { allow })
}
pub(super) fn parse_safe_commands(
table: &toml::value::Table,
) -> Result<crate::blueprint::SafeCommandsConfig> {
let strings = |field: &str| -> Result<Vec<String>> {
let Some(entries) = table.get(field).and_then(|v| v.as_array()) else {
return Ok(Vec::new());
};
entries
.iter()
.map(|entry| {
entry.as_str().map(str::to_string).ok_or_else(|| {
Error::Other(format!(
"[safe_commands] {field} entries must be strings, got: {entry}"
))
})
})
.collect()
};
Ok(crate::blueprint::SafeCommandsConfig {
tools: strings("tools")?,
shell: strings("shell")?,
})
}
pub(super) fn tool_permission_metadata(
table: &toml::value::Table,
) -> Result<Vec<(String, serde_json::Value)>> {
table
.iter()
.map(|(tool_name, policy_val)| {
let policy = policy_val.as_str().ok_or_else(|| {
Error::Other(format!(
"[tool_permissions]: {tool_name} must be one of {}",
TOOL_POLICIES.join(", ")
))
})?;
validate_tool_policy("[tool_permissions]", tool_name, policy)?;
Ok((
format!("tool_perm:{}", tool_name),
serde_json::Value::String(policy.to_string()),
))
})
.collect()
}
pub(super) fn parse_file_tracking(table: &toml::value::Table) -> crate::FileTrackingConfig {
crate::FileTrackingConfig {
region: table
.get("region")
.and_then(|v| v.as_str())
.unwrap_or("files")
.to_string(),
track_reads: table
.get("track_reads")
.and_then(|v| v.as_bool())
.unwrap_or(true),
track_writes: table
.get("track_writes")
.and_then(|v| v.as_bool())
.unwrap_or(true),
max_file_tokens: table
.get("max_file_tokens")
.and_then(|v| v.as_integer())
.map(|v| v as usize),
}
}
pub(super) fn parse_repetition_detection(
table: &toml::value::Table,
) -> crate::RepetitionDetectionConfig {
crate::RepetitionDetectionConfig {
max_repeat_calls: table
.get("max_repeat_calls")
.and_then(|v| v.as_integer())
.map(|v| v as usize),
max_readonly_streak: table
.get("max_readonly_streak")
.and_then(|v| v.as_integer())
.map(|v| v as usize),
enabled: table.get("enabled").and_then(|v| v.as_bool()),
}
}
pub(super) fn parse_output_spec(table: &toml::value::Table) -> crate::output::OutputSpec {
let string_field = |key: &str| {
table
.get(key)
.and_then(|v| v.as_str())
.map(|s| s.trim().to_string())
};
crate::output::OutputSpec {
format: string_field("format"),
instructions: string_field("instructions"),
example: string_field("example"),
schema: table
.get("schema")
.and_then(|v| serde_json::to_value(v).ok()),
validator: string_field("validator"),
}
}
pub(super) fn parse_security_config(security_table: &toml::value::Table) -> crate::SecurityConfig {
let mut sc = crate::SecurityConfig::default();
if let Some(tt) = security_table
.get("taint_tracking")
.and_then(|v| v.as_bool())
{
sc.taint_tracking = tt;
}
sc
}
pub(super) fn parse_sandbox_config(
table: &toml::value::Table,
) -> Result<crate::sandbox::ToolSandboxConfig> {
use crate::sandbox::{OnUnavailable, SandboxKind, ToolSandboxConfig};
let mut sc = ToolSandboxConfig::default();
if let Some(kind) = table.get("kind").and_then(|v| v.as_str()) {
sc.kind = match kind {
"none" => SandboxKind::None,
"namespace" => SandboxKind::Namespace,
"container" => SandboxKind::Container,
other => {
return Err(Error::Other(format!(
"sandbox has unknown kind '{other}' \
(valid: none, namespace, container)"
)));
}
};
}
if let Some(image) = table.get("image").and_then(|v| v.as_str()) {
sc.image = Some(image.to_string());
}
if let Some(engine) = table.get("engine").and_then(|v| v.as_str()) {
sc.engine = Some(engine.to_string());
}
if let Some(network) = table.get("network").and_then(|v| v.as_bool()) {
sc.network = network;
}
if let Some(persist) = table.get("persist").and_then(|v| v.as_bool()) {
sc.persist = persist;
}
if let Some(mounts) = table.get("mount").and_then(|v| v.as_array()) {
sc.mounts = mounts
.iter()
.filter_map(|m| m.as_str().map(str::to_string))
.collect();
}
if let Some(ou) = table.get("on_unavailable").and_then(|v| v.as_str()) {
sc.on_unavailable = match ou {
"error" => OnUnavailable::Error,
"warn" => OnUnavailable::Warn,
other => {
return Err(Error::Other(format!(
"sandbox has unknown on_unavailable '{other}' \
(valid: error, warn)"
)));
}
};
}
Ok(sc)
}