tenzro_types/resource.rs
1//! Unified resource discovery + invocation type.
2//!
3//! Tenzro Network's resource registries (tools, skills, knowledge,
4//! workflow templates, agent templates, models) are each first-class
5//! catalogs with their own type model. The discovery layer collapses
6//! them into a single shape so an agent can ask "what resources match
7//! this filter?" once and pick from a single result set.
8//!
9//! The `ResourceDescriptor` is the cross-registry projection. It
10//! carries enough metadata for an agent to decide whether to use the
11//! resource — class, name, capabilities, price, reputation — and
12//! enough to invoke it via `tenzro_useResource(resource_id, params)`.
13
14use crate::primitives::Address;
15use serde::{Deserialize, Serialize};
16
17/// Which registry a resource lives in. Drives the dispatch target
18/// for `tenzro_useResource`.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
20#[serde(rename_all = "snake_case")]
21pub enum ResourceClass {
22 /// `CF_TOOLS` — invocable MCP servers, API tools, native ops.
23 Tool,
24 /// `CF_SKILLS` — declarative capability descriptors.
25 Skill,
26 /// `CF_KNOWLEDGE` — queryable data resources (vector DBs, feeds, etc).
27 Knowledge,
28 /// `CF_WORKFLOW_TEMPLATES` — reusable workflow blueprints.
29 WorkflowTemplate,
30 /// `CF_AGENT_TEMPLATES` — reusable agent specs.
31 AgentTemplate,
32 /// `CF_MODELS` — AI inference models.
33 Model,
34}
35
36impl ResourceClass {
37 pub fn as_str(&self) -> &'static str {
38 match self {
39 ResourceClass::Tool => "tool",
40 ResourceClass::Skill => "skill",
41 ResourceClass::Knowledge => "knowledge",
42 ResourceClass::WorkflowTemplate => "workflow_template",
43 ResourceClass::AgentTemplate => "agent_template",
44 ResourceClass::Model => "model",
45 }
46 }
47
48 pub fn parse_str(s: &str) -> Option<Self> {
49 match s {
50 "tool" => Some(ResourceClass::Tool),
51 "skill" => Some(ResourceClass::Skill),
52 "knowledge" => Some(ResourceClass::Knowledge),
53 "workflow_template" => Some(ResourceClass::WorkflowTemplate),
54 "agent_template" => Some(ResourceClass::AgentTemplate),
55 "model" => Some(ResourceClass::Model),
56 _ => None,
57 }
58 }
59}
60
61/// Cross-registry projection of a single resource.
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct ResourceDescriptor {
64 /// Which registry this resource lives in.
65 pub class: ResourceClass,
66
67 /// Unique id in the source registry. Caller passes this back to
68 /// `tenzro_useResource(resource_id, params)` for invocation.
69 pub resource_id: String,
70
71 pub name: String,
72 pub version: String,
73 pub description: String,
74 pub category: String,
75
76 /// Capability tags lifted from the source registry. Used for the
77 /// post-filter on `tenzro_listResources(capability_tags=...)`.
78 pub capabilities: Vec<String>,
79
80 pub creator_did: Option<String>,
81
82 /// Operator payout wallet. Tenants pay TNZO; the protocol splits
83 /// 5% to treasury, 95% to this wallet.
84 pub creator_wallet: Option<Address>,
85
86 /// Per-invocation cost in atto-TNZO. `0` = free.
87 pub price_per_call: u128,
88
89 /// `true` when the resource is `Active` in the source registry.
90 pub is_available: bool,
91
92 /// Last liveness heartbeat (seconds). Helps the discovery layer
93 /// surface staleness without filtering aggressively.
94 pub last_seen_at: u64,
95
96 /// Optional kind / sub-type from the source registry, projected
97 /// as a plain string. E.g. for `Tool` this is the transport mode
98 /// (`mcp` / `mcp-stdio` / `mcp-sse` / `api` / `native`); for
99 /// `Knowledge` this is the kind (`vector_index` / `feed` / ...).
100 pub subtype: Option<String>,
101
102 /// Reputation score (0..=1000). `None` when the source registry
103 /// doesn't track reputation (only Models do today).
104 pub reputation: Option<u64>,
105}
106
107/// Filter for `tenzro_listResources`.
108#[derive(Debug, Clone, Default, Serialize, Deserialize)]
109pub struct ResourceFilter {
110 /// Subset of resource classes to query. Empty = all classes.
111 #[serde(default)]
112 pub classes: Vec<String>,
113
114 /// Free-text query — matches name, description, capabilities.
115 #[serde(default)]
116 pub query: Option<String>,
117
118 /// Capability tags — AND-match. Empty = no filter.
119 #[serde(default)]
120 pub capability_tags: Vec<String>,
121
122 /// Optional category filter.
123 #[serde(default)]
124 pub category: Option<String>,
125
126 /// Cost ceiling in atto-TNZO. Resources whose `price_per_call`
127 /// exceeds this are filtered out. `None` = no ceiling.
128 #[serde(default)]
129 pub max_tnzo_price: Option<u128>,
130
131 /// Filter by creator DID.
132 #[serde(default)]
133 pub creator_did: Option<String>,
134
135 #[serde(default)]
136 pub limit: Option<usize>,
137
138 #[serde(default)]
139 pub offset: Option<usize>,
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn resource_class_roundtrip() {
148 for c in [
149 ResourceClass::Tool,
150 ResourceClass::Skill,
151 ResourceClass::Knowledge,
152 ResourceClass::WorkflowTemplate,
153 ResourceClass::AgentTemplate,
154 ResourceClass::Model,
155 ] {
156 assert_eq!(ResourceClass::parse_str(c.as_str()), Some(c));
157 }
158 assert_eq!(ResourceClass::parse_str("nope"), None);
159 }
160}