orcs_types/lib.rs
1//! Core types for ORCS CLI.
2//!
3//! This crate provides foundational identifier types for the ORCS
4//! (Orchestrated Runtime for Collaborative Systems) architecture.
5//!
6//! # Crate Architecture
7//!
8//! ```text
9//! ┌─────────────────────────────────────────────────────────────┐
10//! │ Plugin SDK Layer │
11//! │ (External, SemVer stable, safe to depend on) │
12//! ├─────────────────────────────────────────────────────────────┤
13//! │ orcs-types : ID types, Principal, ErrorCode ◄── HERE │
14//! │ orcs-event : Signal, Request, Event │
15//! │ orcs-component : Component trait (WIT target) │
16//! └─────────────────────────────────────────────────────────────┘
17//! ↓
18//! ┌─────────────────────────────────────────────────────────────┐
19//! │ Runtime Layer │
20//! │ (Internal implementation, NOT for plugins) │
21//! ├─────────────────────────────────────────────────────────────┤
22//! │ orcs-runtime : auth, channel, engine │
23//! └─────────────────────────────────────────────────────────────┘
24//! ↓
25//! ┌─────────────────────────────────────────────────────────────┐
26//! │ Frontend Layer │
27//! │ (CLI, GUI, Network interfaces) │
28//! ├─────────────────────────────────────────────────────────────┤
29//! │ orcs-cli : Command-line interface │
30//! └─────────────────────────────────────────────────────────────┘
31//! ```
32//!
33//! # Why Plugin SDK?
34//!
35//! This crate is part of the **Plugin SDK** layer because:
36//!
37//! - **SemVer stable**: API changes follow semantic versioning
38//! - **Minimal dependencies**: Only what plugins truly need
39//! - **WIT compatible**: Types can be exported to WASM components
40//! - **Implementation freedom**: Runtime can change without breaking plugins
41//!
42//! # Event Architecture
43//!
44//! ORCS uses an EventBus-based architecture where all communication
45//! between Components flows through unified message types:
46//!
47//! - **Request/Response**: Synchronous queries between Components
48//! - **Event**: Asynchronous broadcasts (no response expected)
49//! - **Signal**: Human control interrupts (highest priority)
50//!
51//! # Identifier Design
52//!
53//! All identifiers are UUID-based for:
54//!
55//! - **Network compatibility**: Safe to transmit across processes/machines
56//! - **Cloud readiness**: Globally unique without coordination
57//! - **Serialization**: First-class serde support
58//!
59//! # Example
60//!
61//! ```
62//! use orcs_types::{ComponentId, ChannelId, RequestId, EventId};
63//!
64//! // Builtin components have deterministic UUIDs
65//! let llm = ComponentId::builtin("llm");
66//! let llm2 = ComponentId::builtin("llm");
67//! assert_eq!(llm, llm2); // Same UUID
68//!
69//! // Custom components get random UUIDs
70//! let plugin = ComponentId::new("plugin", "my-tool");
71//!
72//! // Channel for parallel execution context
73//! let channel = ChannelId::new();
74//!
75//! // Request/Response tracking
76//! let req_id = RequestId::new();
77//!
78//! // Event broadcast tracking
79//! let evt_id = EventId::new();
80//! ```
81
82mod construct;
83mod error;
84mod id;
85pub mod intent;
86mod principal;
87mod scope;
88
89pub use construct::TryNew;
90pub use error::{assert_error_code, assert_error_codes, ErrorCode};
91pub use id::{ChannelId, ComponentId, EventId, PrincipalId, RequestId};
92pub use intent::{
93 ActionIntent, Confidence, ContentBlock, IntentDef, IntentMeta, IntentResolver, IntentResult,
94 IntentSource, MessageContent, Priority, Role, StopReason,
95};
96pub use principal::Principal;
97pub use scope::SignalScope;
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn component_id_creation() {
105 let id = ComponentId::new("test", "component");
106 assert_eq!(id.namespace, "test");
107 assert_eq!(id.name, "component");
108 assert_eq!(id.fqn(), "test::component");
109 }
110
111 #[test]
112 fn component_id_builtin_deterministic() {
113 let id1 = ComponentId::builtin("llm");
114 let id2 = ComponentId::builtin("llm");
115 assert_eq!(id1.namespace, "builtin");
116 assert_eq!(id1.name, "llm");
117 // Same name produces same UUID (deterministic)
118 assert_eq!(id1.uuid, id2.uuid);
119 assert_eq!(id1, id2);
120 }
121
122 #[test]
123 fn component_id_builtin_different_names() {
124 let id1 = ComponentId::builtin("llm");
125 let id2 = ComponentId::builtin("tool");
126 // Different names produce different UUIDs
127 assert_ne!(id1.uuid, id2.uuid);
128 }
129
130 #[test]
131 fn component_id_display() {
132 let id = ComponentId::builtin("test");
133 let display = format!("{id}");
134 assert!(display.starts_with("builtin::test@"));
135 assert!(display.contains(&id.uuid.to_string()));
136 }
137
138 #[test]
139 fn component_id_new_random() {
140 let id1 = ComponentId::new("test", "comp");
141 let id2 = ComponentId::new("test", "comp");
142 // new() produces random UUIDs
143 assert_ne!(id1.uuid, id2.uuid);
144 assert_eq!(id1.fqn(), id2.fqn());
145 }
146
147 #[test]
148 fn component_id_fqn_eq() {
149 let id1 = ComponentId::new("test", "comp");
150 let id2 = ComponentId::new("test", "comp");
151 let id3 = ComponentId::new("test", "other");
152 // Different UUIDs but same FQN
153 assert_ne!(id1, id2);
154 assert!(id1.fqn_eq(&id2));
155 assert!(!id1.fqn_eq(&id3));
156 }
157
158 #[test]
159 fn component_id_matches() {
160 let id = ComponentId::builtin("llm");
161 assert!(id.matches("builtin", "llm"));
162 assert!(!id.matches("builtin", "tool"));
163 assert!(!id.matches("custom", "llm"));
164 }
165
166 #[test]
167 fn component_id_is_builtin() {
168 let builtin = ComponentId::builtin("llm");
169 let custom = ComponentId::new("custom", "llm");
170 assert!(builtin.is_builtin());
171 assert!(!custom.is_builtin());
172 }
173
174 #[test]
175 fn channel_id_uniqueness() {
176 let id1 = ChannelId::new();
177 let id2 = ChannelId::new();
178 assert_ne!(id1, id2);
179 }
180
181 // NOTE: ChannelId does not implement Default intentionally.
182 // See id.rs for rationale.
183
184 #[test]
185 fn channel_id_display() {
186 let id = ChannelId::new();
187 let display = format!("{id}");
188 assert!(display.starts_with("ch:"));
189 assert!(display.contains(&id.uuid().to_string()));
190 }
191
192 #[test]
193 fn channel_id_uuid() {
194 let id = ChannelId::new();
195 assert_eq!(id.uuid(), id.0);
196 }
197
198 #[test]
199 fn request_id_display() {
200 let id = RequestId::new();
201 let display = format!("{id}");
202 assert!(display.starts_with("req:"));
203 assert!(display.contains(&id.uuid().to_string()));
204 }
205
206 // NOTE: RequestId does not implement Default intentionally.
207 // See id.rs for rationale.
208
209 #[test]
210 fn event_id_creation() {
211 let id = EventId::new();
212 let display = format!("{id}");
213 assert!(display.starts_with("evt:"));
214 assert!(display.contains(&id.uuid().to_string()));
215 }
216
217 #[test]
218 fn event_id_default() {
219 let id1 = EventId::default();
220 let id2 = EventId::default();
221 assert_ne!(id1, id2);
222 }
223
224 #[test]
225 fn event_id_uniqueness() {
226 let id1 = EventId::new();
227 let id2 = EventId::new();
228 assert_ne!(id1, id2);
229 }
230
231 #[test]
232 fn principal_id_creation() {
233 let id = PrincipalId::new();
234 let display = format!("{id}");
235 assert!(display.starts_with("principal:"));
236 }
237
238 #[test]
239 fn principal_id_uniqueness() {
240 let id1 = PrincipalId::new();
241 let id2 = PrincipalId::new();
242 assert_ne!(id1, id2);
243 }
244
245 #[test]
246 fn principal_id_default() {
247 let id1 = PrincipalId::default();
248 let id2 = PrincipalId::default();
249 assert_ne!(id1, id2);
250 }
251
252 #[test]
253 fn principal_id_uuid() {
254 let id = PrincipalId::new();
255 assert_eq!(id.uuid(), id.0);
256 }
257}