sim_lib_agent_runner_core/
ceiling.rs1use sim_kernel::{CapabilityName, CapabilitySet, Result, Symbol, diminish};
2
3pub fn effective_ceiling(current: &CapabilitySet, names: &[Symbol]) -> Result<CapabilitySet> {
5 let allowed = names
6 .iter()
7 .map(capability_name)
8 .fold(CapabilitySet::new(), CapabilitySet::grant);
9 Ok(diminish(current, &allowed))
10}
11
12fn capability_name(symbol: &Symbol) -> CapabilityName {
13 match symbol.namespace.as_deref() {
14 Some("capability") => CapabilityName::new(symbol.name.to_string()),
15 _ => CapabilityName::new(symbol.as_qualified_str()),
16 }
17}
18
19#[cfg(test)]
20mod tests {
21 use super::effective_ceiling;
22 use sim_kernel::{CapabilityName, CapabilitySet, Symbol};
23
24 #[test]
25 fn effective_ceiling_never_grows() {
26 let ai_run = CapabilityName::new("ai/run");
27 let fs_read = CapabilityName::new("fs/read");
28 let current = CapabilitySet::new()
29 .grant(ai_run.clone())
30 .grant(fs_read.clone());
31
32 let narrowed = effective_ceiling(
33 ¤t,
34 &[
35 Symbol::qualified("ai", "run"),
36 Symbol::qualified("net", "write"),
37 ],
38 )
39 .unwrap();
40
41 assert!(narrowed.contains(&ai_run));
42 assert!(!narrowed.contains(&fs_read));
43 assert!(!narrowed.contains(&CapabilityName::new("net/write")));
44 }
45
46 #[test]
47 fn capability_namespace_resolves_to_bare_name() {
48 let read_eval = CapabilityName::new("read-eval");
49 let current = CapabilitySet::new().grant(read_eval.clone());
50
51 let narrowed =
52 effective_ceiling(¤t, &[Symbol::qualified("capability", "read-eval")]).unwrap();
53
54 assert!(narrowed.contains(&read_eval));
55 }
56}