Skip to main content

tenzro_types/
agent.rs

1//! AI Agent types for Tenzro Network
2//!
3//! This module defines types for AI agents, their capabilities,
4//! configuration, and communication on the network.
5
6use crate::primitives::{Address, Hash, Timestamp};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10/// AI agent identity on Tenzro Network
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct AgentIdentity {
13    /// Unique agent identifier
14    pub agent_id: String,
15    /// Agent's on-chain address
16    pub address: Address,
17    /// Agent name
18    pub name: String,
19    /// Agent version
20    pub version: String,
21    /// Agent creator/owner
22    pub creator: Address,
23}
24
25impl AgentIdentity {
26    /// Creates a new agent identity
27    pub fn new(agent_id: String, address: Address, name: String, creator: Address) -> Self {
28        Self {
29            agent_id,
30            address,
31            name,
32            version: "1.0.0".to_string(),
33            creator,
34        }
35    }
36
37    /// Sets the version
38    pub fn with_version(mut self, version: String) -> Self {
39        self.version = version;
40        self
41    }
42}
43
44/// AI agent configuration
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct AgentConfig {
47    /// Agent identity
48    pub identity: AgentIdentity,
49    /// Agent capabilities
50    pub capabilities: Vec<Capability>,
51    /// Agent description
52    pub description: String,
53    /// Execution environment requirements
54    pub execution_requirements: ExecutionRequirements,
55    /// Agent permissions
56    pub permissions: AgentPermissions,
57    /// Resource limits
58    pub resource_limits: ResourceLimits,
59    /// Configuration metadata
60    pub metadata: HashMap<String, String>,
61}
62
63impl AgentConfig {
64    /// Creates a new agent configuration
65    pub fn new(identity: AgentIdentity, capabilities: Vec<Capability>) -> Self {
66        Self {
67            identity,
68            capabilities,
69            description: String::new(),
70            execution_requirements: ExecutionRequirements::default(),
71            permissions: AgentPermissions::default(),
72            resource_limits: ResourceLimits::default(),
73            metadata: HashMap::new(),
74        }
75    }
76
77    /// Adds a description
78    pub fn with_description(mut self, description: String) -> Self {
79        self.description = description;
80        self
81    }
82
83    /// Adds metadata
84    pub fn add_metadata(&mut self, key: String, value: String) {
85        self.metadata.insert(key, value);
86    }
87}
88
89/// Capabilities that an AI agent can perform
90#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
91#[serde(tag = "type", content = "config")]
92pub enum Capability {
93    /// Natural language processing
94    NaturalLanguageProcessing {
95        /// Supported languages
96        languages: Vec<String>,
97    },
98    /// Computer vision
99    ComputerVision {
100        /// Supported vision tasks
101        tasks: Vec<String>,
102    },
103    /// Code generation and analysis
104    CodeGeneration {
105        /// Supported programming languages
106        languages: Vec<String>,
107    },
108    /// Data analysis
109    DataAnalysis {
110        /// Supported data formats
111        formats: Vec<String>,
112    },
113    /// Blockchain interaction
114    BlockchainInteraction {
115        /// Supported chains
116        chains: Vec<String>,
117    },
118    /// Smart contract execution
119    SmartContractExecution,
120    /// External API integration
121    ExternalAPIIntegration {
122        /// Supported APIs
123        apis: Vec<String>,
124    },
125    /// Multi-agent coordination
126    MultiAgentCoordination,
127    /// Custom capability
128    Custom {
129        /// Capability name
130        name: String,
131        /// Capability parameters
132        parameters: HashMap<String, String>,
133    },
134}
135
136/// Execution environment requirements for an agent
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138pub struct ExecutionRequirements {
139    /// Requires TEE execution
140    pub requires_tee: bool,
141    /// Required TEE vendor (if any)
142    pub tee_vendor: Option<String>,
143    /// Minimum memory (bytes)
144    pub min_memory: u64,
145    /// Minimum CPU cores
146    pub min_cpu_cores: u32,
147    /// Requires GPU
148    pub requires_gpu: bool,
149    /// Network access required
150    pub requires_network: bool,
151}
152
153impl Default for ExecutionRequirements {
154    fn default() -> Self {
155        Self {
156            requires_tee: false,
157            tee_vendor: None,
158            min_memory: 1024 * 1024 * 512, // 512 MB
159            min_cpu_cores: 1,
160            requires_gpu: false,
161            requires_network: true,
162        }
163    }
164}
165
166/// Agent permissions
167#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
168pub struct AgentPermissions {
169    /// Can execute transactions
170    pub can_execute_transactions: bool,
171    /// Can access external APIs
172    pub can_access_external_apis: bool,
173    /// Can interact with other agents
174    pub can_interact_with_agents: bool,
175    /// Can store data on-chain
176    pub can_store_data: bool,
177    /// Maximum transaction value (in smallest TNZO unit)
178    pub max_transaction_value: u64,
179    /// Allowed contract addresses
180    pub allowed_contracts: Vec<Address>,
181}
182
183impl Default for AgentPermissions {
184    fn default() -> Self {
185        Self {
186            can_execute_transactions: false,
187            can_access_external_apis: true,
188            can_interact_with_agents: true,
189            can_store_data: true,
190            max_transaction_value: 0,
191            allowed_contracts: Vec::new(),
192        }
193    }
194}
195
196/// Resource limits for agent execution
197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
198pub struct ResourceLimits {
199    /// Maximum execution time (milliseconds)
200    pub max_execution_time: u64,
201    /// Maximum memory usage (bytes)
202    pub max_memory: u64,
203    /// Maximum CPU usage percentage
204    pub max_cpu_percent: u8,
205    /// Maximum storage (bytes)
206    pub max_storage: u64,
207    /// Maximum API calls per execution
208    pub max_api_calls: u32,
209}
210
211impl Default for ResourceLimits {
212    fn default() -> Self {
213        Self {
214            max_execution_time: 60_000, // 60 seconds
215            max_memory: 1024 * 1024 * 1024, // 1 GB
216            max_cpu_percent: 80,
217            max_storage: 1024 * 1024 * 100, // 100 MB
218            max_api_calls: 100,
219        }
220    }
221}
222
223/// A message between agents on Tenzro Network
224///
225/// Wave 3d hybrid signing: signed messages carry BOTH `signature` (Ed25519
226/// classical) and `pq_signature` (ML-DSA-65). When the message is signed,
227/// both legs are `Some(_)`. When the message is unsigned (trusted
228/// single-process tests), both legs are `None`. Mixing — one `Some`, one
229/// `None` — is rejected by the router.
230#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
231pub struct AgentMessage {
232    /// Message ID
233    pub message_id: String,
234    /// Sender agent
235    pub from: AgentIdentity,
236    /// Recipient agent
237    pub to: AgentIdentity,
238    /// Message type
239    pub message_type: AgentMessageType,
240    /// Message payload
241    pub payload: Vec<u8>,
242    /// Message timestamp
243    pub timestamp: Timestamp,
244    /// Classical Ed25519 message signature (None when unsigned)
245    pub signature: Option<Vec<u8>>,
246    /// Post-quantum ML-DSA-65 message signature (3309 bytes when signed,
247    /// None when unsigned). Must be present whenever `signature` is present.
248    pub pq_signature: Option<Vec<u8>>,
249    /// Reply-to message ID (if this is a reply)
250    pub reply_to: Option<String>,
251}
252
253impl AgentMessage {
254    /// Creates a new agent message
255    pub fn new(
256        from: AgentIdentity,
257        to: AgentIdentity,
258        message_type: AgentMessageType,
259        payload: Vec<u8>,
260    ) -> Self {
261        Self {
262            message_id: uuid::Uuid::new_v4().to_string(),
263            from,
264            to,
265            message_type,
266            payload,
267            timestamp: Timestamp::now(),
268            signature: None,
269            pq_signature: None,
270            reply_to: None,
271        }
272    }
273
274    /// Sets the message as a reply to another message
275    pub fn as_reply_to(mut self, message_id: String) -> Self {
276        self.reply_to = Some(message_id);
277        self
278    }
279
280    /// Adds a classical-only signature to the message.
281    ///
282    /// Wave 3d hybrid signing: this is the classical (Ed25519) leg only.
283    /// Production agent message production should call `with_hybrid_signature`
284    /// instead so both legs are populated; the router rejects messages with
285    /// only the classical leg present.
286    pub fn with_signature(mut self, signature: Vec<u8>) -> Self {
287        self.signature = Some(signature);
288        self
289    }
290
291    /// Adds a full hybrid (Ed25519 + ML-DSA-65) signature to the message.
292    pub fn with_hybrid_signature(
293        mut self,
294        classical: Vec<u8>,
295        pq: Vec<u8>,
296    ) -> Self {
297        self.signature = Some(classical);
298        self.pq_signature = Some(pq);
299        self
300    }
301
302    /// Returns the canonical bytes that should be signed.
303    ///
304    /// This deliberately **excludes** the `signature` field so that signing
305    /// followed by verifying yields a stable hash. The encoding is a simple
306    /// length-prefixed concatenation of the message-determining fields:
307    ///
308    /// ```text
309    /// signing_data = len(message_id) || message_id
310    ///              || from.agent_id_len || from.agent_id || from.address
311    ///              || to.agent_id_len   || to.agent_id   || to.address
312    ///              || message_type_tag (u8)
313    ///              || payload_len (u64 LE) || payload
314    ///              || timestamp_millis (i64 LE)
315    ///              || reply_to_present (u8) [|| reply_to_len || reply_to]
316    /// ```
317    ///
318    /// All `len()` fields are encoded as little-endian `u64` for
319    /// deterministic, length-prefixed framing. This avoids ambiguity attacks
320    /// where two different field arrangements collide into the same byte
321    /// stream (e.g. concatenating two strings without a length prefix).
322    pub fn signing_data(&self) -> Vec<u8> {
323        let mut buf = Vec::with_capacity(256 + self.payload.len());
324
325        // message_id
326        buf.extend_from_slice(&(self.message_id.len() as u64).to_le_bytes());
327        buf.extend_from_slice(self.message_id.as_bytes());
328
329        // from agent identity
330        buf.extend_from_slice(&(self.from.agent_id.len() as u64).to_le_bytes());
331        buf.extend_from_slice(self.from.agent_id.as_bytes());
332        buf.extend_from_slice(self.from.address.as_bytes());
333
334        // to agent identity
335        buf.extend_from_slice(&(self.to.agent_id.len() as u64).to_le_bytes());
336        buf.extend_from_slice(self.to.agent_id.as_bytes());
337        buf.extend_from_slice(self.to.address.as_bytes());
338
339        // message_type as a single tag byte
340        buf.push(self.message_type as u8);
341
342        // payload (length-prefixed)
343        buf.extend_from_slice(&(self.payload.len() as u64).to_le_bytes());
344        buf.extend_from_slice(&self.payload);
345
346        // timestamp
347        buf.extend_from_slice(&self.timestamp.0.to_le_bytes());
348
349        // optional reply_to (1-byte presence flag, then length-prefixed value)
350        match &self.reply_to {
351            Some(reply) => {
352                buf.push(1);
353                buf.extend_from_slice(&(reply.len() as u64).to_le_bytes());
354                buf.extend_from_slice(reply.as_bytes());
355            }
356            None => {
357                buf.push(0);
358            }
359        }
360
361        buf
362    }
363
364    /// Computes the SHA-256 hash of the message's canonical signing data.
365    ///
366    /// This is the value passed to a signer when producing the
367    /// `signature` field, and the value verified against the public key
368    /// when validating an inbound message. Because it is computed from
369    /// [`Self::signing_data`], the `signature` field itself is **not**
370    /// included in the hash, so signing is idempotent.
371    pub fn hash(&self) -> Hash {
372        use sha2::{Digest, Sha256};
373        let mut hasher = Sha256::new();
374        hasher.update(self.signing_data());
375        let digest = hasher.finalize();
376        let mut bytes = [0u8; 32];
377        bytes.copy_from_slice(&digest);
378        Hash::new(bytes)
379    }
380}
381
382/// Types of agent-to-agent messages
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
384pub enum AgentMessageType {
385    /// Request for task execution
386    TaskRequest,
387    /// Response to a task request
388    TaskResponse,
389    /// Query for information
390    Query,
391    /// Response to a query
392    QueryResponse,
393    /// Notification
394    Notification,
395    /// Coordination message for multi-agent tasks
396    Coordination,
397    /// Error message
398    Error,
399    /// Request to spawn a new sub-agent
400    SpawnRequest,
401    /// Confirmation that a sub-agent was successfully spawned
402    SpawnConfirmation,
403    /// Orchestrator dispatching a task to a swarm member
404    SwarmCoordination,
405    /// Swarm member returning result to orchestrator
406    SwarmResult,
407}
408
409/// A tool call made by an agent during autonomous execution
410#[derive(Debug, Clone, Serialize, Deserialize)]
411pub struct AgentToolCall {
412    /// Tool name: "spawn_agent" | "delegate_task" | "collect_results" | "complete"
413    pub tool_name: String,
414    /// Tool arguments as JSON
415    pub arguments: serde_json::Value,
416}
417
418/// Configuration for a swarm of agents
419#[derive(Debug, Clone, Serialize, Deserialize)]
420pub struct SwarmConfig {
421    /// Maximum number of member agents (default: 10)
422    pub max_members: usize,
423    /// Per-task timeout in seconds (default: 300)
424    pub task_timeout_secs: u64,
425    /// Whether to dispatch tasks in parallel (default: true)
426    pub parallel: bool,
427}
428
429impl Default for SwarmConfig {
430    fn default() -> Self {
431        Self {
432            max_members: 10,
433            task_timeout_secs: 300,
434            parallel: true,
435        }
436    }
437}
438
439/// A member of an agent swarm
440#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct SwarmMember {
442    /// Agent ID of this member
443    pub agent_id: String,
444    /// Role/name of this member in the swarm
445    pub role: String,
446    /// Current status of this member
447    pub status: SwarmMemberStatus,
448    /// Result produced by this member (if completed)
449    pub result: Option<String>,
450}
451
452/// Status of a swarm member
453#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
454pub enum SwarmMemberStatus {
455    /// Idle, waiting for a task
456    Idle,
457    /// Currently executing a task
458    Working,
459    /// Successfully completed its task
460    Completed,
461    /// Failed to complete its task
462    Failed(String),
463}