1use axum::{
16 extract::{Query, State},
17 response::Json,
18};
19use serde::Deserialize;
20use serde_json::{json, Value};
21
22use crate::backend::messagebus::{BusMessage, MessageType, Priority};
23use crate::backend::reactive::InjectionRequest;
24use super::AppState;
25
26#[derive(Deserialize)]
29pub(super) struct RegisterRequest {
30 agent_id: String,
31}
32
33#[derive(Deserialize)]
34pub(super) struct SendRequest {
35 from: String,
36 to: String,
37 payload: String,
38 #[serde(default)]
39 priority: Option<String>,
40}
41
42#[derive(Deserialize)]
43pub(super) struct InjectRequest {
44 from: String,
45 target: String,
46 message: String,
47 #[serde(default)]
48 priority: Option<String>,
49}
50
51#[derive(Deserialize)]
52pub(super) struct BroadcastRequest {
53 from: String,
54 payload: String,
55 #[serde(default)]
56 priority: Option<String>,
57}
58
59#[derive(Deserialize)]
60pub(super) struct ReadMessagesQuery {
61 agent_id: String,
62 #[serde(default = "default_limit")]
63 limit: usize,
64}
65
66fn default_limit() -> usize {
67 100
68}
69
70#[derive(Deserialize)]
71pub(super) struct DeleteMessagesRequest {
72 agent_id: String,
73 message_ids: Vec<String>,
74}
75
76fn parse_priority(s: &Option<String>) -> Priority {
79 match s.as_deref() {
80 Some("high") => Priority::High,
81 Some("urgent") => Priority::Urgent,
82 _ => Priority::Normal,
83 }
84}
85
86pub(super) async fn handle_register(
90 State(state): State<AppState>,
91 Json(req): Json<RegisterRequest>,
92) -> Json<Value> {
93 state.messagebus.register_http(&req.agent_id);
96 Json(json!({
97 "status": "registered",
98 "agent_id": req.agent_id,
99 }))
100}
101
102pub(super) async fn handle_send(
104 State(state): State<AppState>,
105 caller: Option<axum::Extension<super::caller::Caller>>,
106 Json(req): Json<SendRequest>,
107) -> Json<Value> {
108 super::actor::check_actor(
109 &state,
110 caller.as_deref(),
111 super::actor::ActorSite::BusSend,
112 Some(&req.from),
113 );
114 let priority = parse_priority(&req.priority);
115 let msg = BusMessage::new(&req.from, &req.to, MessageType::Send, &req.payload, priority)
116 .with_from_uid(&super::caller::attributed_uid(caller.as_deref()));
117 let msg_id = msg.id.clone();
118
119 match state.messagebus.send(msg) {
120 Ok(()) => Json(json!({
121 "status": "sent",
122 "message_id": msg_id,
123 "to": req.to,
124 })),
125 Err(e) => Json(json!({
126 "status": "error",
127 "error": e,
128 })),
129 }
130}
131
132pub(super) async fn handle_inject(
137 State(state): State<AppState>,
138 caller: Option<axum::Extension<super::caller::Caller>>,
139 Json(req): Json<InjectRequest>,
140) -> Json<Value> {
141 super::actor::check_actor(
142 &state,
143 caller.as_deref(),
144 super::actor::ActorSite::BusInject,
145 Some(&req.from),
146 );
147 let mut reactive_req = InjectionRequest {
149 target_agent: req.target.clone(),
150 message: req.message.clone(),
151 source_agent: Some(req.from.clone()),
152 request_id: None,
153 priority: req.priority.clone(),
154 jekt_tier: None,
155 delivery_tier: Some("host".to_string()),
156 forward_hops: 0,
157 audit_source_uid: super::caller::attributed_uid(caller.as_deref()),
158 ..Default::default()
159 };
160 super::reactive::verify_jekt_signature(&state, &mut reactive_req);
167 let resp = state.reactive_handler.inject_message(reactive_req);
168 if resp.success {
169 return Json(json!({
170 "status": "injected",
171 "via": "pty",
172 "block_id": resp.block_id,
173 "target": req.target,
174 }));
175 }
176
177 let is_not_found = resp.error.as_deref().map(|e| e.contains("not found")).unwrap_or(false);
180 if !is_not_found {
181 return Json(json!({
182 "status": "error",
183 "error": resp.error,
184 }));
185 }
186
187 let priority = parse_priority(&req.priority);
188 let from_uid = super::caller::attributed_uid(caller.as_deref());
189 match state.messagebus.inject(&req.from, &from_uid, &req.target, &req.message, priority) {
190 Ok(msg_id) => Json(json!({
191 "status": "injected",
192 "via": "messagebus",
193 "message_id": msg_id,
194 "target": req.target,
195 })),
196 Err(e) => Json(json!({
197 "status": "error",
198 "error": e,
199 })),
200 }
201}
202
203pub(super) async fn handle_broadcast(
205 State(state): State<AppState>,
206 caller: Option<axum::Extension<super::caller::Caller>>,
207 Json(req): Json<BroadcastRequest>,
208) -> Json<Value> {
209 super::actor::check_actor(
210 &state,
211 caller.as_deref(),
212 super::actor::ActorSite::BusBroadcast,
213 Some(&req.from),
214 );
215 let priority = parse_priority(&req.priority);
216
217 let from_uid = super::caller::attributed_uid(caller.as_deref());
218 match state.messagebus.broadcast(&req.from, &from_uid, &req.payload, priority) {
219 Ok(delivered) => Json(json!({
220 "status": "broadcast",
221 "delivered": delivered,
222 })),
223 Err(e) => Json(json!({
224 "status": "error",
225 "error": e,
226 })),
227 }
228}
229
230pub(super) async fn handle_read_messages(
232 State(state): State<AppState>,
233 Query(query): Query<ReadMessagesQuery>,
234) -> Json<Value> {
235 let messages = state.messagebus.read_messages(&query.agent_id, query.limit);
236 Json(json!({
237 "agent_id": query.agent_id,
238 "messages": messages,
239 "count": messages.len(),
240 }))
241}
242
243pub(super) async fn handle_list_agents(
245 State(state): State<AppState>,
246) -> Json<Value> {
247 let agents = state.messagebus.list_agents();
248 Json(json!({
249 "agents": agents,
250 "total_count": agents.len(),
251 }))
252}
253
254pub(super) async fn handle_delete_messages(
256 State(state): State<AppState>,
257 Json(req): Json<DeleteMessagesRequest>,
258) -> Json<Value> {
259 let deleted = state.messagebus.delete_messages(&req.agent_id, &req.message_ids);
260 Json(json!({
261 "status": "deleted",
262 "deleted": deleted,
263 }))
264}