use crate::types::AgentId;
use super::template::CapabilityTemplate;
use super::types::CSpace;
const ROLE_WORKER: &str = "worker";
const ROLE_STANDARD: &str = "standard";
const ROLE_OPERATOR: &str = "operator";
const ROLE_SUPERVISOR: &str = "supervisor";
pub fn resolve_cspace(
cspace_hint: Option<&str>,
persona_role: Option<&str>,
default_template: Option<&str>,
agent_id: AgentId,
) -> CSpace {
if let Some(hint) = cspace_hint {
let trimmed = hint.trim();
if !trimmed.is_empty() {
return resolve_from_template_name(trimmed, agent_id);
}
}
if let Some(role) = persona_role {
let trimmed = role.trim().to_lowercase();
if !trimmed.is_empty() {
return resolve_from_template_name(&trimmed, agent_id);
}
}
let fallback = default_template.unwrap_or(ROLE_WORKER);
resolve_from_template_name(fallback, agent_id)
}
fn resolve_from_template_name(name: &str, agent_id: AgentId) -> CSpace {
match name {
ROLE_WORKER => CapabilityTemplate::worker().build_for(agent_id),
ROLE_STANDARD => CapabilityTemplate::standard().build_for(agent_id),
ROLE_OPERATOR => CapabilityTemplate::operator().build_for(agent_id),
ROLE_SUPERVISOR => CapabilityTemplate::supervisor().build_for(agent_id),
_ => {
if name.starts_with('{') {
tracing::warn!(
"JSON cspace_hint not yet supported, falling back to worker: {}",
name
);
} else {
tracing::warn!(
"Unknown capability template '{}', falling back to worker",
name
);
}
CapabilityTemplate::worker().build_for(agent_id)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hint_takes_priority_over_role() {
let id = AgentId::new_v4();
let cs = resolve_cspace(Some("supervisor"), Some("worker"), None, id);
use super::super::types::{ResourceRef, Rights};
assert!(cs.can(
&ResourceRef::KernelDomain {
domain: "security".into()
},
Rights::ALL,
));
}
#[test]
fn role_used_when_no_hint() {
let id = AgentId::new_v4();
let cs = resolve_cspace(None, Some("operator"), None, id);
use super::super::types::{ResourceRef, Rights};
assert!(cs.can(&ResourceRef::A2a, Rights::EXECUTE));
}
#[test]
fn default_is_worker() {
let id = AgentId::new_v4();
let cs = resolve_cspace(None, None, None, id);
use super::super::types::{ResourceRef, Rights};
assert!(cs.can(
&ResourceRef::Exec {
mode: "shell".into()
},
Rights::EXECUTE
));
assert!(!cs.can(&ResourceRef::A2a, Rights::READ));
}
#[test]
fn custom_default_template() {
let id = AgentId::new_v4();
let cs = resolve_cspace(None, None, Some("standard"), id);
use super::super::types::{ResourceRef, Rights};
assert!(cs.can(
&ResourceRef::KernelDomain {
domain: "memory".into()
},
Rights::READ
));
}
#[test]
fn empty_hint_falls_through() {
let id = AgentId::new_v4();
let cs = resolve_cspace(Some(""), Some("operator"), None, id);
use super::super::types::{ResourceRef, Rights};
assert!(cs.can(&ResourceRef::A2a, Rights::EXECUTE));
}
#[test]
fn unknown_name_falls_back_to_worker() {
let id = AgentId::new_v4();
let cs = resolve_cspace(Some("nonexistent"), None, None, id);
use super::super::types::{ResourceRef, Rights};
assert!(cs.can(
&ResourceRef::Exec {
mode: "shell".into()
},
Rights::EXECUTE
));
}
#[test]
fn json_hint_falls_back_gracefully() {
let id = AgentId::new_v4();
let cs = resolve_cspace(Some(r#"{"custom": true}"#), None, None, id);
use super::super::types::ResourceRef;
assert!(cs.can(
&ResourceRef::Exec {
mode: "shell".into()
},
super::super::types::Rights::EXECUTE
));
}
}