Skip to main content

runifold_agent/
descriptor.rs

1use std::collections::BTreeMap;
2
3use runifold_core::{CapabilityDescriptor, CapabilityId, CapabilityKind, EffectClass, RiskLevel};
4use runifold_model::ToolSpec;
5use serde::{Deserialize, Serialize};
6use serde_json::{Value, json};
7
8/// Versioned contract for invoking an agent through a gateway.
9#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
10pub struct AgentDescriptor {
11    /// Stable capability identity.
12    pub id: CapabilityId,
13    /// Model-facing delegation name.
14    pub name: String,
15    /// Semantic contract version.
16    pub version: String,
17    /// Model-facing description of when this agent should be used.
18    pub description: String,
19    /// Coarse risk classification for policy engines.
20    pub risk: RiskLevel,
21    /// Host-only namespaced metadata.
22    pub metadata: BTreeMap<String, Value>,
23}
24
25impl AgentDescriptor {
26    /// Creates an agent contract with the canonical delegation schema.
27    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
28        Self {
29            id: CapabilityId::new(),
30            name: name.into(),
31            version: "1".into(),
32            description: description.into(),
33            risk: RiskLevel::Medium,
34            metadata: BTreeMap::new(),
35        }
36    }
37
38    /// Converts this descriptor into a grantable agent capability.
39    pub fn capability(&self) -> CapabilityDescriptor {
40        CapabilityDescriptor {
41            id: self.id,
42            name: self.name.clone(),
43            version: self.version.clone(),
44            kind: CapabilityKind::Agent,
45            input_schema: input_schema(),
46            output_schema: output_schema(),
47            effect: EffectClass::Unknown,
48            risk: self.risk,
49            metadata: self.metadata.clone(),
50        }
51    }
52
53    /// Converts this descriptor into the callable shape exposed to a model.
54    pub fn model_spec(&self) -> ToolSpec {
55        ToolSpec {
56            name: self.name.clone(),
57            description: self.description.clone(),
58            input_schema: input_schema(),
59            output_schema: Some(output_schema()),
60            metadata: self.metadata.clone(),
61        }
62    }
63}
64
65fn input_schema() -> Value {
66    json!({
67        "type": "object",
68        "properties": {
69            "input": {
70                "type": "string",
71                "description": "Task or question delegated to the agent"
72            }
73        },
74        "required": ["input"],
75        "additionalProperties": false
76    })
77}
78
79fn output_schema() -> Value {
80    json!({
81        "type": "object",
82        "properties": {
83            "agent": {"type": "string"},
84            "content": {"type": "array"},
85            "turns": {"type": "integer", "minimum": 0},
86            "tool_calls": {"type": "integer", "minimum": 0},
87            "delegations": {"type": "integer", "minimum": 0}
88        },
89        "required": ["agent", "content", "turns", "tool_calls", "delegations"],
90        "additionalProperties": false
91    })
92}