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