Skip to main content

feagi_agent/command_and_control/
agent_registration_message.rs

1use crate::{AgentCapabilities, AgentDescriptor, AuthToken, FeagiApiVersion};
2use feagi_io::traits_and_enums::shared::{
3    TransportProtocolEndpoint, TransportProtocolImplementation,
4};
5use feagi_io::AgentID;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum AgentRegistrationMessage {
12    ClientRequestRegistration(RegistrationRequest),
13    ServerRespondsRegistration(RegistrationResponse),
14    /// Client-initiated request to tear down an active registration/session.
15    ///
16    /// This supports voluntary deregistration (graceful disconnect) where the
17    /// client explicitly asks FEAGI to release all associated resources.
18    ClientRequestDeregistration(DeregistrationRequest),
19    /// Server response to a deregistration request.
20    ///
21    /// The server responds with either `Success` (resources released) or
22    /// `NotRegistered` if the session was already absent.
23    ServerRespondsDeregistration(DeregistrationResponse),
24}
25
26//region Registration Request
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
29pub struct RegistrationRequest {
30    agent_descriptor: AgentDescriptor,
31    auth_token: AuthToken,
32    requested_capabilities: Vec<AgentCapabilities>,
33    connection_protocol: TransportProtocolImplementation,
34    api_version: FeagiApiVersion,
35}
36
37impl RegistrationRequest {
38    /// No request should be bigger than this many bytes. If so, assume something malicious is going on
39    pub const MAX_REQUEST_SIZE: usize = 1024;
40
41    /// Create a new registration request.
42    ///
43    /// # Arguments
44    /// * `agent_descriptor` - Information identifying the agent
45    /// * `auth_token` - Authentication token for secure access
46    /// * `requested_capabilities` - List of capabilities the agent is requesting
47    /// * `connection_protocol` - The protocol the agent wants to use for communication
48    pub fn new(
49        agent_descriptor: AgentDescriptor,
50        auth_token: AuthToken,
51        requested_capabilities: Vec<AgentCapabilities>,
52        connection_protocol: TransportProtocolImplementation,
53    ) -> Self {
54        Self {
55            agent_descriptor,
56            auth_token,
57            requested_capabilities,
58            connection_protocol,
59            api_version: FeagiApiVersion::get_current_api_version(),
60        }
61    }
62
63    /// Get the reported API version
64    pub fn api_version(&self) -> &FeagiApiVersion {
65        &self.api_version
66    }
67
68    /// Get the agent descriptor.
69    pub fn agent_descriptor(&self) -> &AgentDescriptor {
70        &self.agent_descriptor
71    }
72
73    /// Get the authentication token.
74    pub fn auth_token(&self) -> &AuthToken {
75        &self.auth_token
76    }
77
78    /// Get the requested capabilities.
79    pub fn requested_capabilities(&self) -> &[AgentCapabilities] {
80        &self.requested_capabilities
81    }
82
83    /// Get the connection protocol.
84    pub fn connection_protocol(&self) -> &TransportProtocolImplementation {
85        &self.connection_protocol
86    }
87}
88
89//endregion
90
91//region Registration Response
92
93#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
94#[serde(rename_all = "snake_case")]
95pub enum RegistrationResponse {
96    FailedInvalidRequest, // This may not be sent back if the server ignores bad data
97    FailedInvalidAuth, // Usually the auth token, may be the agent too. Server may not send this if configured to ignore invalid auth
98    AlreadyRegistered,
99    Success(
100        AgentID,
101        HashMap<AgentCapabilities, TransportProtocolEndpoint>,
102    ),
103}
104
105//endregion
106
107//region Deregistration Request/Response
108
109/// Deregistration request sent by a registered client.
110///
111/// The optional `reason` is intended for observability and diagnostics and
112/// does not affect deregistration semantics.
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
114pub struct DeregistrationRequest {
115    reason: Option<String>,
116}
117
118impl DeregistrationRequest {
119    /// Create a request with an optional reason message.
120    pub fn new(reason: Option<String>) -> Self {
121        Self { reason }
122    }
123
124    /// Optional human-readable reason provided by the client.
125    pub fn reason(&self) -> Option<&str> {
126        self.reason.as_deref()
127    }
128}
129
130/// Deregistration response from the server.
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
132#[serde(rename_all = "snake_case")]
133pub enum DeregistrationResponse {
134    Success,
135    NotRegistered,
136}
137
138//endregion
139
140#[cfg(test)]
141mod tests {
142    use super::{AgentRegistrationMessage, DeregistrationRequest, DeregistrationResponse};
143
144    #[test]
145    fn deregistration_request_round_trip_serialization_preserves_reason() {
146        let request = AgentRegistrationMessage::ClientRequestDeregistration(
147            DeregistrationRequest::new(Some("shutdown".to_string())),
148        );
149        let encoded =
150            serde_json::to_string(&request).expect("deregistration request should serialize");
151        let decoded: AgentRegistrationMessage =
152            serde_json::from_str(&encoded).expect("deregistration request should deserialize");
153        assert_eq!(request, decoded);
154    }
155
156    #[test]
157    fn deregistration_response_round_trip_serialization_preserves_variant() {
158        let response = AgentRegistrationMessage::ServerRespondsDeregistration(
159            DeregistrationResponse::NotRegistered,
160        );
161        let encoded =
162            serde_json::to_string(&response).expect("deregistration response should serialize");
163        let decoded: AgentRegistrationMessage =
164            serde_json::from_str(&encoded).expect("deregistration response should deserialize");
165        assert_eq!(response, decoded);
166    }
167}