use std::time::Instant;
use anyhow::{bail, Result};
use serde_json::Value;
use atlas::invocation::{SkillContext, SkillInput};
use atlas::registry::SkillRegistry;
use crate::permission::{PermissionDecision, PermissionEngine};
use crate::registry::ToolRegistry;
pub struct ToolGateway {
pub manifests: ToolRegistry,
pub skills: SkillRegistry,
pub permission: PermissionEngine,
}
impl ToolGateway {
pub fn new(
manifests: ToolRegistry,
skills: SkillRegistry,
permission: PermissionEngine,
) -> Self {
Self {
manifests,
skills,
permission,
}
}
pub fn dispatch(&self, tool_name: &str, input: Value, caller: &str) -> Result<Value> {
let manifest = self
.manifests
.get(tool_name)
.ok_or_else(|| anyhow::anyhow!("unknown tool: {tool_name}"))?;
match self.permission.check(manifest.scope, caller) {
PermissionDecision::Allow => {}
PermissionDecision::Deny(reason) => {
bail!("permission denied for '{tool_name}': {reason}")
}
}
let skill = self.skills.get(tool_name).ok_or_else(|| {
anyhow::anyhow!("tool '{tool_name}' has no registered skill implementation")
})?;
let cwd = std::env::current_dir()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| ".".into());
let ctx = SkillContext::new(caller, cwd);
let skill_input = SkillInput::new(input);
let t = Instant::now();
let outcome = skill
.execute(skill_input, &ctx)
.map_err(|e| anyhow::anyhow!("skill '{tool_name}' failed: {e}"))?;
let elapsed_ms = t.elapsed().as_millis() as u64;
Ok(serde_json::json!({
"tool": tool_name,
"status": "ok",
"duration_ms": elapsed_ms,
"tokens_used": outcome.tokens_used,
"result": outcome.result,
}))
}
pub fn list_tools(&self) -> Value {
let tools: Vec<Value> = self
.manifests
.all()
.iter()
.map(|m| {
serde_json::json!({
"name": m.name,
"description": m.description,
"inputSchema": m.input_schema,
})
})
.collect();
serde_json::json!({ "tools": tools })
}
}