Skip to main content

rootcx_types/
lib.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value as JsonValue;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct OsStatus {
6    pub runtime: RuntimeStatus,
7    pub postgres: PostgresStatus,
8    pub forge: ForgeStatus,
9}
10
11impl OsStatus {
12    pub fn offline() -> Self {
13        Self {
14            runtime: RuntimeStatus { version: String::new(), state: ServiceState::Offline },
15            postgres: PostgresStatus { state: ServiceState::Offline, port: None, data_dir: None },
16            forge: ForgeStatus { state: ServiceState::Offline, port: None },
17        }
18    }
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ForgeStatus {
23    pub state: ServiceState,
24    pub port: Option<u16>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct RuntimeStatus {
29    pub version: String,
30    pub state: ServiceState,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct PostgresStatus {
35    pub state: ServiceState,
36    pub port: Option<u16>,
37    pub data_dir: Option<String>,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(rename_all = "lowercase")]
42pub enum ServiceState {
43    Online,
44    Offline,
45    Starting,
46    Stopping,
47    Error,
48}
49
50#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "lowercase")]
52pub enum AppType {
53    #[default]
54    App,
55    Integration,
56    Agent,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct ActionDefinition {
62    pub id: String,
63    pub name: String,
64    #[serde(default)]
65    pub description: String,
66    #[serde(default)]
67    pub input_schema: Option<JsonValue>,
68    #[serde(default)]
69    pub output_schema: Option<JsonValue>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct AppManifest {
75    pub app_id: String,
76    pub name: String,
77    #[serde(default = "default_version")]
78    pub version: String,
79    #[serde(default)]
80    pub description: String,
81    #[serde(default, rename = "type")]
82    pub app_type: AppType,
83    #[serde(default)]
84    pub permissions: Option<PermissionsContract>,
85    #[serde(default)]
86    pub data_contract: Vec<EntityContract>,
87    #[serde(default, skip_serializing_if = "Vec::is_empty")]
88    pub actions: Vec<ActionDefinition>,
89    #[serde(default, skip_serializing_if = "Option::is_none")]
90    pub config_schema: Option<JsonValue>,
91    #[serde(default, skip_serializing_if = "Option::is_none")]
92    pub user_auth: Option<JsonValue>,
93    #[serde(default, skip_serializing_if = "Vec::is_empty")]
94    pub webhooks: Vec<WebhookDefinition>,
95    /// Free-form usage instructions surfaced to AI via list_integrations tool
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub instructions: Option<String>,
98    /// Trigger: auto-invoke this agent on entity events
99    #[serde(default, skip_serializing_if = "Option::is_none")]
100    pub trigger: Option<TriggerConfig>,
101    /// Declarative cron schedules synced on deploy
102    #[serde(default, skip_serializing_if = "Vec::is_empty")]
103    pub crons: Vec<CronDefinition>,
104    #[serde(default, skip_serializing_if = "Option::is_none")]
105    pub icon: Option<String>,
106    /// Public-access surface. Routes listed here bypass Identity. RPCs that
107    /// declare `scope` additionally require a share token whose context
108    /// matches the request body on the listed keys.
109    #[serde(default, skip_serializing_if = "Option::is_none")]
110    pub public: Option<PublicSurface>,
111}
112
113/// Declarative public-access surface for an app.
114///
115/// Anything listed here is reachable without an Authorization header.
116/// Anything **not** listed retains the default JWT-required behavior.
117///
118/// See `core/src/extensions/sharing/` for the runtime enforcement.
119#[derive(Debug, Clone, Default, Serialize, Deserialize)]
120#[serde(rename_all = "camelCase")]
121pub struct PublicSurface {
122    /// Custom RPCs exposed publicly. If `scope` is non-empty, the request
123    /// must carry a share token whose `context` matches the request body on
124    /// every listed key.
125    #[serde(default, skip_serializing_if = "Vec::is_empty")]
126    pub rpcs: Vec<PublicRpc>,
127    /// CRUD collections exposed publicly with the listed actions.
128    /// Allowed actions: "list", "read", "create", "update", "delete".
129    #[serde(default, skip_serializing_if = "Vec::is_empty")]
130    pub collections: Vec<PublicCollection>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134#[serde(rename_all = "camelCase")]
135pub struct PublicRpc {
136    pub name: String,
137    /// Keys to enforce-match between the share token's `context` and the
138    /// request body. Empty `scope` means anonymous access (no share token
139    /// required). Non-empty means a share token IS required and the listed
140    /// keys MUST match exactly.
141    #[serde(default, skip_serializing_if = "Vec::is_empty")]
142    pub scope: Vec<String>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub struct PublicCollection {
148    pub entity: String,
149    /// Subset of CRUD actions exposed: "list", "read", "create", "update", "delete".
150    pub actions: Vec<String>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154#[serde(rename_all = "camelCase")]
155pub struct CronDefinition {
156    pub name: String,
157    pub schedule: String,
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub timezone: Option<String>,
160    #[serde(default, skip_serializing_if = "Option::is_none")]
161    pub method: Option<String>,
162    #[serde(default, skip_serializing_if = "Option::is_none")]
163    pub payload: Option<JsonValue>,
164    #[serde(default = "default_overlap_policy")]
165    pub overlap_policy: String,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169#[serde(untagged)]
170pub enum WebhookDefinition {
171    Simple(String),
172    #[serde(rename_all = "camelCase")]
173    Full { name: String, #[serde(default = "default_post")] method: String },
174}
175
176fn default_post() -> String { "POST".into() }
177
178impl WebhookDefinition {
179    pub fn name(&self) -> &str {
180        match self {
181            Self::Simple(s) => s.as_str(),
182            Self::Full { name, .. } => name.as_str(),
183        }
184    }
185    pub fn method(&self) -> &str {
186        match self {
187            Self::Simple(_) => "POST",
188            Self::Full { method, .. } => method.as_str(),
189        }
190    }
191}
192
193fn default_overlap_policy() -> String { "skip".into() }
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196#[serde(rename_all = "camelCase")]
197pub struct TriggerConfig {
198    pub app_id: String,
199    pub entity: String,
200    pub on: Vec<String>,
201}
202
203fn default_version() -> String {
204    "0.0.1".to_string()
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct EntityContract {
210    pub entity_name: String,
211    pub fields: Vec<FieldContract>,
212    #[serde(default, skip_serializing_if = "Option::is_none")]
213    pub identity_kind: Option<String>,
214    #[serde(default, skip_serializing_if = "Option::is_none")]
215    pub identity_key: Option<String>,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
219#[serde(rename_all = "camelCase")]
220pub struct FieldContract {
221    pub name: String,
222    #[serde(rename = "type")]
223    pub field_type: String,
224    #[serde(default)]
225    pub required: bool,
226    #[serde(default)]
227    pub default_value: Option<JsonValue>,
228    #[serde(default)]
229    pub enum_values: Option<Vec<String>>,
230    #[serde(default)]
231    pub references: Option<FieldReference>,
232    #[serde(default)]
233    pub is_primary_key: Option<bool>,
234    #[serde(default, skip_serializing_if = "Option::is_none")]
235    pub on_delete: Option<OnDeletePolicy>,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
239#[serde(rename_all = "snake_case")]
240pub enum OnDeletePolicy {
241    Cascade,
242    Restrict,
243    SetNull,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct FieldReference {
248    pub entity: String,
249    pub field: String,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
253#[serde(rename_all = "camelCase")]
254pub struct InstalledApp {
255    pub id: String,
256    pub name: String,
257    pub version: String,
258    pub status: String,
259    #[serde(rename = "type", default)]
260    pub app_type: AppType,
261    pub entities: Vec<String>,
262    #[serde(default)]
263    pub has_frontend: bool,
264    #[serde(default, skip_serializing_if = "Option::is_none")]
265    pub icon: Option<String>,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270pub struct PermissionsContract {
271    #[serde(default)]
272    pub permissions: Vec<PermissionDeclaration>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct PermissionDeclaration {
277    pub key: String,
278    #[serde(default)]
279    pub description: String,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct SchemaChange {
284    pub entity: String,
285    pub change_type: String,
286    pub column: String,
287    pub detail: Option<String>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct SchemaVerification {
292    pub compliant: bool,
293    pub changes: Vec<SchemaChange>,
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
297#[serde(rename_all = "lowercase")]
298pub enum ProviderType {
299    Anthropic,
300    OpenAI,
301    Bedrock,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305#[serde(rename_all = "camelCase")]
306pub struct AgentDefinition {
307    pub name: String,
308    #[serde(default)]
309    pub description: Option<String>,
310    #[serde(default)]
311    pub system_prompt: Option<String>,
312    #[serde(default)]
313    pub memory: Option<AgentMemory>,
314    #[serde(default)]
315    pub limits: Option<AgentLimits>,
316    #[serde(default)]
317    pub supervision: Option<SupervisionConfig>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct AgentMemory {
322    pub enabled: bool,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
326#[serde(rename_all = "camelCase")]
327pub struct AgentLimits {
328    #[serde(default)]
329    pub max_turns: Option<u32>,
330    #[serde(default)]
331    pub max_context_tokens: Option<u64>,
332    #[serde(default)]
333    pub keep_recent_messages: Option<u32>,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
337#[serde(rename_all = "camelCase")]
338pub struct SupervisionConfig {
339    pub mode: SupervisionMode,
340    #[serde(default)]
341    pub policies: Vec<SupervisionPolicy>,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
345#[serde(rename_all = "lowercase")]
346pub enum SupervisionMode {
347    Autonomous,
348    Supervised,
349    Strict,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
353#[serde(rename_all = "camelCase")]
354pub struct SupervisionPolicy {
355    pub action: String,
356    #[serde(default)]
357    pub entity: Option<String>,
358    #[serde(default)]
359    pub requires: Option<String>,
360    #[serde(default)]
361    pub rate_limit: Option<RateLimit>,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct RateLimit {
366    pub max: u32,
367    pub window: String,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
371#[serde(rename_all = "camelCase")]
372pub struct McpServerConfig {
373    pub name: String,
374    pub transport: McpTransport,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
378#[serde(tag = "type", rename_all = "lowercase")]
379pub enum McpTransport {
380    Stdio { command: String, #[serde(default)] args: Vec<String> },
381    Http { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
382    #[deprecated = "use Http"]
383    Sse { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
384    Cli { install: String },
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize)]
388#[serde(rename_all = "camelCase")]
389pub struct ToolDescriptor {
390    pub name: String,
391    pub description: String,
392    pub input_schema: serde_json::Value,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
396#[serde(rename_all = "lowercase")]
397pub enum Role {
398    User,
399    Assistant,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
403#[serde(tag = "type", rename_all = "snake_case")]
404pub enum ContentBlock {
405    Text { text: String },
406    ToolUse { id: String, name: String, input: serde_json::Value },
407    ToolResult { tool_use_id: String, content: String, #[serde(default)] is_error: bool },
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize)]
411pub struct ChatMessage {
412    pub role: Role,
413    pub content: Vec<ContentBlock>,
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
417pub struct ToolDef {
418    pub name: String,
419    pub description: String,
420    pub input_schema: serde_json::Value,
421}
422