use async_trait::async_trait;
use serde_json::Value;
use roboticus_core::RiskLevel;
use super::{Tool, ToolContext, ToolResult, ToolError};
pub struct GetRuntimeContextTool;
#[async_trait]
impl Tool for GetRuntimeContextTool {
fn name(&self) -> &str {
"get_runtime_context"
}
fn description(&self) -> &str {
"Returns the current agent runtime context including session, channel, and workspace path"
}
fn risk_level(&self) -> RiskLevel {
RiskLevel::Safe
}
fn parameters_schema(&self) -> Value {
serde_json::json!({
"type": "object",
"properties": {},
"required": []
})
}
async fn execute(
&self,
_params: Value,
ctx: &ToolContext,
) -> std::result::Result<ToolResult, ToolError> {
let info = serde_json::json!({
"agent_id": ctx.agent_id,
"session_id": ctx.session_id,
"channel": ctx.channel,
"workspace_root": ctx.workspace_root.display().to_string(),
"authority": format!("{:?}", ctx.authority),
});
Ok(ToolResult {
output: serde_json::to_string_pretty(&info).unwrap_or_else(|_| "{}".into()),
metadata: Some(info),
})
}
}
pub struct GetMemoryStatsTool;
#[async_trait]
impl Tool for GetMemoryStatsTool {
fn name(&self) -> &str {
"get_memory_stats"
}
fn description(&self) -> &str {
"Returns memory retrieval tier allocations and configuration"
}
fn risk_level(&self) -> RiskLevel {
RiskLevel::Safe
}
fn parameters_schema(&self) -> Value {
serde_json::json!({
"type": "object",
"properties": {},
"required": []
})
}
async fn execute(
&self,
_params: Value,
_ctx: &ToolContext,
) -> std::result::Result<ToolResult, ToolError> {
let tiers = serde_json::json!({
"tiers": {
"working": { "budget_pct": 30, "description": "Active conversation context" },
"episodic": { "budget_pct": 25, "description": "Session digests and summaries" },
"semantic": { "budget_pct": 20, "description": "Vector-similarity recalled facts" },
"procedural": { "budget_pct": 15, "description": "How-to knowledge and procedures" },
"relationship": { "budget_pct": 10, "description": "Entity relationships and graph" },
},
"retrieval_method": "5-tier hybrid (FTS5 + vector cosine)",
});
Ok(ToolResult {
output: serde_json::to_string_pretty(&tiers).unwrap_or_else(|_| "{}".into()),
metadata: Some(tiers),
})
}
}
pub struct GetChannelHealthTool;
#[async_trait]
impl Tool for GetChannelHealthTool {
fn name(&self) -> &str {
"get_channel_health"
}
fn description(&self) -> &str {
"Returns the health status of the current delivery channel"
}
fn risk_level(&self) -> RiskLevel {
RiskLevel::Safe
}
fn parameters_schema(&self) -> Value {
serde_json::json!({
"type": "object",
"properties": {},
"required": []
})
}
async fn execute(
&self,
_params: Value,
ctx: &ToolContext,
) -> std::result::Result<ToolResult, ToolError> {
let channel = ctx.channel.as_deref().unwrap_or("unknown");
let health = serde_json::json!({
"channel": channel,
"status": "operational",
"note": "Detailed channel health metrics require a ChannelRouter reference; \
basic connectivity confirmed by successful tool invocation.",
});
Ok(ToolResult {
output: serde_json::to_string_pretty(&health).unwrap_or_else(|_| "{}".into()),
metadata: Some(health),
})
}
}
pub struct GetSubagentStatusTool;
#[async_trait]
impl Tool for GetSubagentStatusTool {
fn name(&self) -> &str {
"get_subagent_status"
}
fn description(&self) -> &str {
"Returns the status of registered subagents (specialists) and open tasks"
}
fn risk_level(&self) -> RiskLevel {
RiskLevel::Safe
}
fn parameters_schema(&self) -> Value {
serde_json::json!({
"type": "object",
"properties": {},
"required": []
})
}
async fn execute(
&self,
_params: Value,
ctx: &ToolContext,
) -> std::result::Result<ToolResult, ToolError> {
let db = match &ctx.db {
Some(db) => db,
None => {
let result = serde_json::json!({
"error": "database not available",
"subagents": [],
"tasks": [],
});
return Ok(ToolResult {
output: serde_json::to_string_pretty(&result).unwrap_or_else(|_| "{}".into()),
metadata: Some(result),
});
}
};
let subagents = roboticus_db::agents::list_sub_agents(db)
.unwrap_or_default()
.into_iter()
.map(|a| {
serde_json::json!({
"name": a.name,
"display_name": a.display_name,
"model": a.model,
"role": a.role,
"enabled": a.enabled,
"session_count": a.session_count,
})
})
.collect::<Vec<_>>();
let tasks = {
let conn = db.conn();
conn.prepare(
"SELECT id, title, status, priority, source, created_at \
FROM tasks WHERE status IN ('pending', 'in_progress') \
ORDER BY priority DESC, created_at ASC LIMIT 50",
)
.ok()
.map(|mut stmt| {
stmt.query_map([], |row| {
let source_raw: Option<String> = row.get(4)?;
Ok(serde_json::json!({
"id": row.get::<_, String>(0)?,
"title": row.get::<_, String>(1)?,
"status": row.get::<_, String>(2)?,
"priority": row.get::<_, i64>(3)?,
"source": roboticus_db::tasks::normalize_task_source_value(source_raw.as_deref()),
"created_at": row.get::<_, String>(5)?,
}))
})
.inspect_err(|e| tracing::warn!("failed to query tasks: {e}"))
.ok()
.map(|rows| rows.filter_map(|r| {
r.inspect_err(|e| tracing::warn!("skipping corrupted task row: {e}"))
.ok()
}).collect::<Vec<_>>())
.unwrap_or_default()
})
.unwrap_or_default()
};
let result = serde_json::json!({
"subagents": subagents,
"subagent_count": subagents.len(),
"tasks": tasks,
"open_task_count": tasks.len(),
});
Ok(ToolResult {
output: serde_json::to_string_pretty(&result).unwrap_or_else(|_| "{}".into()),
metadata: Some(result),
})
}
}