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