use serde_json::{json, Value};
use std::sync::OnceLock;
pub fn cached_tool_schemas() -> &'static Vec<Value> {
static SCHEMAS: OnceLock<Vec<Value>> = OnceLock::new();
SCHEMAS.get_or_init(tool_schemas)
}
pub fn cached_prompt_schemas() -> &'static Vec<Value> {
static PROMPTS: OnceLock<Vec<Value>> = OnceLock::new();
PROMPTS.get_or_init(prompt_schemas)
}
fn tool_schemas() -> Vec<Value> {
vec![
json!({
"name": "memory_add_fact",
"description": "Ingest a fact into CAR's graph memory. Kind defaults to \"pattern\"; use \"constraint\" for hard rules.",
"inputSchema": {
"type": "object",
"properties": {
"subject": { "type": "string" },
"body": { "type": "string" },
"kind": { "type": "string", "enum": ["pattern", "constraint"] },
},
"required": ["subject", "body"],
},
}),
json!({
"name": "memory_query",
"description": "Query CAR graph memory using spreading activation. Returns top-k nodes with activation scores.",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"k": { "type": "integer", "minimum": 1, "maximum": 50 },
},
"required": ["query"],
},
}),
json!({
"name": "verify",
"description": "Statically verify an ActionProposal: detect dependency cycles, missing tools, and simulate final state. No execution, no side effects.",
"inputSchema": {
"type": "object",
"properties": {
"proposal": { "type": "object", "description": "A car_ir::ActionProposal JSON object" },
"max_actions": { "type": "integer", "minimum": 1, "maximum": 1000 },
},
"required": ["proposal"],
},
}),
json!({
"name": "skill_ingest",
"description": "Ingest a skill into CAR's graph memory. A skill is executable code associated with a trigger (persona + url pattern + task keywords) so skill_find can later retrieve it for matching tasks.",
"inputSchema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"code": { "type": "string", "description": "Skill body — code, recipe, or procedure text" },
"platform": { "type": "string" },
"persona": { "type": "string" },
"url_pattern": { "type": "string" },
"description": { "type": "string" },
"task_keywords": { "type": "array", "items": { "type": "string" } },
"supersedes": { "type": "string", "description": "Name of an older skill this replaces" },
},
"required": ["name", "code"],
},
}),
json!({
"name": "skill_list",
"description": "Enumerate all ingested skills. Optional domain filter returns only skills scoped Global or Domain(domain).",
"inputSchema": {
"type": "object",
"properties": {
"domain": { "type": "string" },
},
},
}),
json!({
"name": "skill_find",
"description": "Find top-k skills matching a persona/url/task triple, ranked by activation.",
"inputSchema": {
"type": "object",
"properties": {
"persona": { "type": "string" },
"url": { "type": "string" },
"task": { "type": "string" },
"k": { "type": "integer", "minimum": 1, "maximum": 20 },
},
"required": ["task"],
},
}),
]
}
fn prompt_schemas() -> Vec<Value> {
vec![json!({
"name": "car_context",
"description": "Assemble CAR's four-layer context (identity → constraints → facts → conversation → environment → known-unknowns) for a query. Returns the context as a single user message the host can prepend to its own prompt.",
"arguments": [
{ "name": "query", "description": "Task or question the context should be assembled for.", "required": true },
{ "name": "mode", "description": "\"full\" (default) or \"fast\" — fast skips embedding flush, skill lookup, PPR scoring.", "required": false },
],
})]
}