#![forbid(unsafe_code)]
use async_trait::async_trait;
use serde_json::{Value, json};
use std::sync::Arc;
use wm_core::{Context, EffectRow, Gana, Resource, Tool, ToolStats};
use wm_governance::ResourceRules;
pub struct SandboxSetLimitsTool {
rules: Arc<ResourceRules>,
stats: ToolStats,
effects: EffectRow,
}
impl SandboxSetLimitsTool {
#[must_use]
pub fn new(rules: Arc<ResourceRules>) -> Self {
Self {
rules,
stats: ToolStats::default(),
effects: EffectRow {
writes: vec![Resource::DharmaRules],
..Default::default()
},
}
}
}
#[async_trait]
impl Tool for SandboxSetLimitsTool {
fn name(&self) -> &str {
"sandbox.set_limits"
}
fn gana(&self) -> Gana {
Gana::Room
}
fn effects(&self) -> &EffectRow {
&self.effects
}
fn description(&self) -> &str {
"Update sandbox resource limits at runtime: max_writes_per_minute, max_spawns_per_minute, max_network_per_minute, novelty_window, max_repeats, require_human_review. All fields optional — provided ones are applied, others keep current values."
}
async fn call(&self, _ctx: &mut Context, args: Value) -> wm_core::Result<Value> {
let mut cfg = self.rules.config();
let mut changed: Vec<&'static str> = Vec::new();
if let Some(v) = args.get("max_writes_per_minute").and_then(Value::as_u64) {
cfg.max_writes_per_minute = v as u32;
changed.push("max_writes_per_minute");
}
if let Some(v) = args.get("max_spawns_per_minute").and_then(Value::as_u64) {
cfg.max_spawns_per_minute = v as u32;
changed.push("max_spawns_per_minute");
}
if let Some(v) = args.get("max_network_per_minute").and_then(Value::as_u64) {
cfg.max_network_per_minute = v as u32;
changed.push("max_network_per_minute");
}
if let Some(v) = args.get("novelty_window").and_then(Value::as_u64) {
cfg.novelty_window = v as usize;
changed.push("novelty_window");
}
if let Some(v) = args.get("max_repeats").and_then(Value::as_u64) {
cfg.max_repeats = v as u32;
changed.push("max_repeats");
}
if let Some(v) = args.get("require_human_review").and_then(Value::as_bool) {
cfg.require_human_review = v;
changed.push("require_human_review");
}
self.rules.set_config(cfg);
let cfg = self.rules.config();
Ok(json!({
"status": "success",
"changed": changed,
"limits": {
"max_writes_per_minute": cfg.max_writes_per_minute,
"max_spawns_per_minute": cfg.max_spawns_per_minute,
"max_network_per_minute": cfg.max_network_per_minute,
"novelty_window": cfg.novelty_window,
"max_repeats": cfg.max_repeats,
"require_human_review": cfg.require_human_review,
},
}))
}
fn stats(&self) -> &ToolStats {
&self.stats
}
}
pub struct SandboxLimitsTool {
rules: Arc<ResourceRules>,
stats: ToolStats,
effects: EffectRow,
}
impl SandboxLimitsTool {
#[must_use]
pub fn new(rules: Arc<ResourceRules>) -> Self {
Self {
rules,
stats: ToolStats::default(),
effects: EffectRow::read_only(vec![Resource::DharmaRules]),
}
}
}
#[async_trait]
impl Tool for SandboxLimitsTool {
fn name(&self) -> &str {
"sandbox.limits"
}
fn gana(&self) -> Gana {
Gana::Room
}
fn effects(&self) -> &EffectRow {
&self.effects
}
fn description(&self) -> &str {
"Show the current sandbox resource limits"
}
async fn call(&self, _ctx: &mut Context, _args: Value) -> wm_core::Result<Value> {
let cfg = self.rules.config();
Ok(json!({
"status": "success",
"limits": {
"max_writes_per_minute": cfg.max_writes_per_minute,
"max_spawns_per_minute": cfg.max_spawns_per_minute,
"max_network_per_minute": cfg.max_network_per_minute,
"novelty_window": cfg.novelty_window,
"max_repeats": cfg.max_repeats,
"require_human_review": cfg.require_human_review,
},
}))
}
fn stats(&self) -> &ToolStats {
&self.stats
}
}
#[must_use]
pub fn register_sandbox(
registry: &wm_dispatch::ToolRegistry,
rules: Option<&Arc<ResourceRules>>,
) -> wm_dispatch::ToolRegistry {
match rules {
Some(rules) => registry
.register(Arc::new(SandboxSetLimitsTool::new(rules.clone())))
.register(Arc::new(SandboxLimitsTool::new(rules.clone()))),
None => registry.clone(),
}
}