Skip to main content

perspt_agent/tools/families/
mod.rs

1//! First-party tool families shipped through the open execution plane.
2//!
3//! Each family is registered by the composition root exactly like a
4//! third-party one — catalog entries via `Psp9AgentRuntime::with_tool_family`
5//! and handlers via [`CandidateHandlerRegistry::register`] — so they double
6//! as the standing proof that the plane stays open. A future family is a
7//! crate depending on this handler contract; nothing in the loop, the
8//! candidate, or the node assembly names these tools.
9
10pub mod db;
11pub mod system;
12
13use anyhow::Result;
14use perspt_sdk::ToolEntry;
15
16use super::handlers::CandidateHandlerRegistry;
17
18/// Catalog entries for every shipped family.
19pub fn standard_family_entries() -> Vec<ToolEntry> {
20    let mut entries = system::entries();
21    entries.extend(db::entries());
22    entries
23}
24
25/// Register every shipped family's handlers.
26pub fn register_standard_families(registry: &mut CandidateHandlerRegistry) -> Result<()> {
27    system::register(registry)?;
28    db::register(registry)?;
29    Ok(())
30}
31
32/// One shared entry constructor for family modules.
33pub(crate) fn family_entry(
34    name: &str,
35    description: &str,
36    effect: perspt_sdk::EffectKind,
37    schema: serde_json::Value,
38    footprint: perspt_sdk::FootprintSpec,
39) -> ToolEntry {
40    ToolEntry {
41        name: name.into(),
42        description: description.into(),
43        discovery_summary: String::new(),
44        description_templates: None,
45        effect,
46        risk: perspt_sdk::RiskClass::Low,
47        schema,
48        footprint,
49        proposal_bindings: Vec::new(),
50        durable: false,
51        origin: perspt_sdk::ToolOrigin::Builtin,
52        hot: false,
53    }
54}
55
56/// Object schema helper matching the base catalog's shape.
57pub(crate) fn object_schema(fields: &[(&str, &str, &str, bool)]) -> serde_json::Value {
58    let mut properties = serde_json::Map::new();
59    let mut required = Vec::new();
60    for (name, kind, description, is_required) in fields {
61        properties.insert(
62            (*name).to_string(),
63            serde_json::json!({"type": kind, "description": description}),
64        );
65        if *is_required {
66            required.push(serde_json::Value::String((*name).to_string()));
67        }
68    }
69    serde_json::json!({
70        "type": "object",
71        "properties": properties,
72        "required": required,
73        "additionalProperties": false,
74    })
75}