Phase 12: Tool registry — governed business actions for AI agents

- ToolRegistry: named tools with parameter validation and audit logging
- 6 built-in staffing tools:
  search_candidates (skills, city, state, experience, availability)
  get_candidate (by ID)
  revenue_by_client (top N by billed revenue)
  recruiter_performance (placements, revenue per recruiter)
  cold_leads (called N+ times, never placed)
  open_jobs (by vertical, city)
- Each tool: name, description, params, permission level (read/write/admin)
- SQL template with validated parameter substitution
- Full audit trail: every invocation logged with agent, params, result
- Endpoints: GET /tools (list), GET /tools/{name} (schema),
  POST /tools/{name}/call (execute), GET /tools/audit (log)
- Per ADR-015: governed interface before raw SQL for agents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-03-27 09:31:42 -05:00
co-authored by Claude Opus 4.6
parent 6cd1daeb51
commit 6f0f92a9e4
6 changed files with 484 additions and 2 deletions
+11 -2
View File
@@ -1,5 +1,6 @@
mod auth;
mod observability;
mod tools;
use axum::{Router, extract::DefaultBodyLimit, routing::get};
use proto::lakehouse::catalog_service_server::CatalogServiceServer;
@@ -51,7 +52,7 @@ async fn main() {
.route("/health", get(health))
.nest("/storage", storaged::service::router(store.clone()))
.nest("/catalog", catalogd::service::router(registry.clone()))
.nest("/query", queryd::service::router(engine))
.nest("/query", queryd::service::router(engine.clone()))
.nest("/ai", aibridge::service::router(ai_client.clone()))
.nest("/ingest", ingestd::service::router(ingestd::service::IngestState {
store: store.clone(),
@@ -68,7 +69,15 @@ async fn main() {
}
}))
.nest("/workspaces", queryd::workspace_service::router(workspace_mgr))
.nest("/journal", journald::service::router(journal));
.nest("/journal", journald::service::router(journal))
.nest("/tools", tools::service::router({
let tool_reg = tools::registry::ToolRegistry::new_with_defaults();
tool_reg.register_defaults().await;
tools::ToolState {
registry: tool_reg,
query_fn: tools::QueryExecutor::new(engine.clone()),
}
}));
// Auth middleware (if enabled)
if config.auth.enabled {