Skip to main content

vfa_tui/models/
agent.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use super::harness::{Harness, SourceType};
6use super::provider::Provider;
7
8/// Deserialize a field, treating an explicit JSON `null` as the type's default.
9///
10/// `#[serde(default)]` alone covers a *missing* key; the catalog also emits an
11/// explicit `null` for empty `companion_skills`, which this helper coalesces.
12fn null_to_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
13where
14    D: Deserializer<'de>,
15    T: Default + Deserialize<'de>,
16{
17    Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
18}
19
20/// The type discriminator for agent entries.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22pub enum AgentType {
23    #[serde(rename = "agent")]
24    Agent,
25}
26
27impl std::fmt::Display for AgentType {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            AgentType::Agent => write!(f, "agent"),
31        }
32    }
33}
34
35/// Execution tier classification for agents.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "kebab-case")]
38pub enum ExecutionTier {
39    StaticReview,
40    ReadOnlyRuntime,
41    MutatingRuntime,
42}
43
44impl std::fmt::Display for ExecutionTier {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        match self {
47            ExecutionTier::StaticReview => write!(f, "static-review"),
48            ExecutionTier::ReadOnlyRuntime => write!(f, "read-only-runtime"),
49            ExecutionTier::MutatingRuntime => write!(f, "mutating-runtime"),
50        }
51    }
52}
53
54/// Lifecycle stage for agents.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(rename_all = "kebab-case")]
57pub enum Lifecycle {
58    Experimental,
59    Beta,
60    Stable,
61    Deprecated,
62}
63
64impl std::fmt::Display for Lifecycle {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        match self {
67            Lifecycle::Experimental => write!(f, "experimental"),
68            Lifecycle::Beta => write!(f, "beta"),
69            Lifecycle::Stable => write!(f, "stable"),
70            Lifecycle::Deprecated => write!(f, "deprecated"),
71        }
72    }
73}
74
75/// A catalog agent entry.
76#[derive(Debug, Clone, Serialize, Deserialize)]
77#[serde(deny_unknown_fields)]
78pub struct Agent {
79    pub id: String,
80    pub name: String,
81    #[serde(rename = "type")]
82    pub entity_type: AgentType,
83    pub provider: Provider,
84    pub harnesses: Vec<Harness>,
85    pub summary: String,
86    pub source_type: SourceType,
87    pub official_docs: Vec<String>,
88    pub security_notes: String,
89    pub last_verified: String,
90    pub path: String,
91    #[serde(default, deserialize_with = "null_to_default")]
92    pub companion_skills: Vec<String>,
93    pub execution_tier: Option<ExecutionTier>,
94    pub lifecycle: Option<Lifecycle>,
95    #[serde(default)]
96    pub harness_variants: HashMap<String, String>,
97    pub author: Option<String>,
98    pub version: Option<String>,
99    #[serde(default)]
100    pub provider_coverage: Option<serde_json::Value>,
101}