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}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct ActionDefinition {
61 pub id: String,
62 pub name: String,
63 #[serde(default)]
64 pub description: String,
65 #[serde(default)]
66 pub input_schema: Option<JsonValue>,
67 #[serde(default)]
68 pub output_schema: Option<JsonValue>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72#[serde(rename_all = "camelCase")]
73pub struct AppManifest {
74 pub app_id: String,
75 pub name: String,
76 #[serde(default = "default_version")]
77 pub version: String,
78 #[serde(default)]
79 pub description: String,
80 #[serde(default, rename = "type")]
81 pub app_type: AppType,
82 #[serde(default)]
83 pub permissions: Option<PermissionsContract>,
84 #[serde(default)]
85 pub data_contract: Vec<EntityContract>,
86 #[serde(default, skip_serializing_if = "Vec::is_empty")]
87 pub actions: Vec<ActionDefinition>,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub config_schema: Option<JsonValue>,
90 #[serde(default, skip_serializing_if = "Option::is_none")]
91 pub user_auth: Option<JsonValue>,
92 #[serde(default, skip_serializing_if = "Vec::is_empty")]
93 pub webhooks: Vec<String>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub instructions: Option<String>,
97}
98
99fn default_version() -> String {
100 "0.0.1".to_string()
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct EntityContract {
106 pub entity_name: String,
107 pub fields: Vec<FieldContract>,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 pub identity_kind: Option<String>,
110 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub identity_key: Option<String>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct FieldContract {
117 pub name: String,
118 #[serde(rename = "type")]
119 pub field_type: String,
120 #[serde(default)]
121 pub required: bool,
122 #[serde(default)]
123 pub default_value: Option<JsonValue>,
124 #[serde(default)]
125 pub enum_values: Option<Vec<String>>,
126 #[serde(default)]
127 pub references: Option<FieldReference>,
128 #[serde(default)]
129 pub is_primary_key: Option<bool>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct FieldReference {
134 pub entity: String,
135 pub field: String,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct InstalledApp {
141 pub id: String,
142 pub name: String,
143 pub version: String,
144 pub status: String,
145 pub entities: Vec<String>,
146 #[serde(default)]
147 pub has_frontend: bool,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct PermissionsContract {
153 #[serde(default)]
154 pub permissions: Vec<PermissionDeclaration>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct PermissionDeclaration {
159 pub key: String,
160 #[serde(default)]
161 pub description: String,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct SchemaChange {
166 pub entity: String,
167 pub change_type: String,
168 pub column: String,
169 pub detail: Option<String>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct SchemaVerification {
174 pub compliant: bool,
175 pub changes: Vec<SchemaChange>,
176}
177
178pub const DEFAULT_MODEL: &str = "claude-sonnet-4-6";
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
181#[serde(rename_all = "lowercase")]
182pub enum ProviderType {
183 Anthropic,
184 OpenAI,
185 Bedrock,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct AiConfig {
190 pub provider: ProviderType,
191 pub model: String,
192 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub region: Option<String>,
194}
195
196impl Default for AiConfig {
197 fn default() -> Self {
198 Self { provider: ProviderType::Anthropic, model: DEFAULT_MODEL.into(), region: None }
199 }
200}
201
202impl AiConfig {
203 pub fn forge_model_string(&self) -> String {
204 match self.provider {
205 ProviderType::Bedrock => format!("amazon-bedrock/anthropic.{}", self.model),
206 ProviderType::Anthropic => format!("anthropic/{}", self.model),
207 ProviderType::OpenAI => format!("openai/{}", self.model),
208 }
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct AgentDefinition {
215 pub name: String,
216 #[serde(default)]
217 pub description: Option<String>,
218 #[serde(default)]
219 pub system_prompt: Option<String>,
220 #[serde(default)]
221 pub memory: Option<AgentMemory>,
222 #[serde(default)]
223 pub limits: Option<AgentLimits>,
224 #[serde(default)]
225 pub supervision: Option<SupervisionConfig>,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct AgentMemory {
230 pub enabled: bool,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
234#[serde(rename_all = "camelCase")]
235pub struct AgentLimits {
236 #[serde(default)]
237 pub max_turns: Option<u32>,
238 #[serde(default)]
239 pub max_context_tokens: Option<u64>,
240 #[serde(default)]
241 pub keep_recent_messages: Option<u32>,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(rename_all = "camelCase")]
246pub struct SupervisionConfig {
247 pub mode: SupervisionMode,
248 #[serde(default)]
249 pub policies: Vec<SupervisionPolicy>,
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
253#[serde(rename_all = "lowercase")]
254pub enum SupervisionMode {
255 Autonomous,
256 Supervised,
257 Strict,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
261#[serde(rename_all = "camelCase")]
262pub struct SupervisionPolicy {
263 pub action: String,
264 #[serde(default)]
265 pub entity: Option<String>,
266 #[serde(default)]
267 pub requires: Option<String>,
268 #[serde(default)]
269 pub rate_limit: Option<RateLimit>,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct RateLimit {
274 pub max: u32,
275 pub window: String,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279#[serde(rename_all = "camelCase")]
280pub struct McpServerConfig {
281 pub name: String,
282 pub transport: McpTransport,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286#[serde(tag = "type", rename_all = "lowercase")]
287pub enum McpTransport {
288 Stdio { command: String, #[serde(default)] args: Vec<String> },
289 Http { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
290 #[deprecated = "use Http"]
291 Sse { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
295#[serde(rename_all = "camelCase")]
296pub struct ToolDescriptor {
297 pub name: String,
298 pub description: String,
299 pub input_schema: serde_json::Value,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
303#[serde(rename_all = "lowercase")]
304pub enum Role {
305 User,
306 Assistant,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
310#[serde(tag = "type", rename_all = "snake_case")]
311pub enum ContentBlock {
312 Text { text: String },
313 ToolUse { id: String, name: String, input: serde_json::Value },
314 ToolResult { tool_use_id: String, content: String, #[serde(default)] is_error: bool },
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct ChatMessage {
319 pub role: Role,
320 pub content: Vec<ContentBlock>,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct ToolDef {
325 pub name: String,
326 pub description: String,
327 pub input_schema: serde_json::Value,
328}
329
330impl ProviderType {
331 pub fn secret_key_name(&self) -> &'static str {
332 match self {
333 Self::Anthropic => "ANTHROPIC_API_KEY",
334 Self::OpenAI => "OPENAI_API_KEY",
335 Self::Bedrock => "AWS_BEARER_TOKEN_BEDROCK",
336 }
337 }
338}