tap_http/external_decision/
protocol.rs1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct JsonRpcRequest {
12 pub jsonrpc: String,
13 pub id: Value,
14 pub method: String,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub params: Option<Value>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct JsonRpcNotification {
22 pub jsonrpc: String,
23 pub method: String,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub params: Option<Value>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct JsonRpcResponse {
31 pub jsonrpc: String,
32 pub id: Value,
33 pub result: Value,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct JsonRpcErrorResponse {
39 pub jsonrpc: String,
40 pub id: Value,
41 pub error: JsonRpcError,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct JsonRpcError {
47 pub code: i64,
48 pub message: String,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub data: Option<Value>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(untagged)]
56pub enum IncomingMessage {
57 Request(JsonRpcRequest),
58 Notification(JsonRpcNotification),
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct InitializeParams {
68 pub version: String,
69 pub agent_dids: Vec<String>,
70 pub subscribe_mode: String,
71 pub capabilities: InitializeCapabilities,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct InitializeCapabilities {
76 pub tools: bool,
77 pub decisions: bool,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct DecisionRequestParams {
83 pub decision_id: i64,
84 pub transaction_id: String,
85 pub agent_did: String,
86 pub decision_type: String,
87 pub context: Value,
88 pub created_at: String,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct EventNotificationParams {
94 pub event_type: String,
95 pub agent_did: Option<String>,
96 pub data: Value,
97 pub timestamp: String,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct DecisionResponse {
107 pub action: String,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 pub detail: Option<Value>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct ReadyParams {
115 #[serde(default)]
116 pub version: Option<String>,
117 #[serde(default)]
118 pub name: Option<String>,
119}
120
121impl JsonRpcRequest {
126 pub fn new(id: impl Into<Value>, method: impl Into<String>, params: Option<Value>) -> Self {
127 Self {
128 jsonrpc: "2.0".to_string(),
129 id: id.into(),
130 method: method.into(),
131 params,
132 }
133 }
134}
135
136impl JsonRpcNotification {
137 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
138 Self {
139 jsonrpc: "2.0".to_string(),
140 method: method.into(),
141 params,
142 }
143 }
144}
145
146impl JsonRpcResponse {
147 pub fn new(id: Value, result: Value) -> Self {
148 Self {
149 jsonrpc: "2.0".to_string(),
150 id,
151 result,
152 }
153 }
154}
155
156impl JsonRpcErrorResponse {
157 pub fn new(id: Value, code: i64, message: impl Into<String>) -> Self {
158 Self {
159 jsonrpc: "2.0".to_string(),
160 id,
161 error: JsonRpcError {
162 code,
163 message: message.into(),
164 data: None,
165 },
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use serde_json::json;
174
175 #[test]
176 fn test_decision_request_serialization() {
177 let params = DecisionRequestParams {
178 decision_id: 42,
179 transaction_id: "txn-123".to_string(),
180 agent_did: "did:key:z6Mk...".to_string(),
181 decision_type: "authorization_required".to_string(),
182 context: json!({
183 "transaction_state": "Received",
184 "pending_agents": ["did:key:z6Mk..."],
185 }),
186 created_at: "2026-02-21T12:00:00Z".to_string(),
187 };
188
189 let req = JsonRpcRequest::new(
190 1,
191 "tap/decision",
192 Some(serde_json::to_value(¶ms).unwrap()),
193 );
194 let json = serde_json::to_string(&req).unwrap();
195 let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
196
197 assert_eq!(parsed.method, "tap/decision");
198 assert_eq!(parsed.id, json!(1));
199
200 let parsed_params: DecisionRequestParams =
201 serde_json::from_value(parsed.params.unwrap()).unwrap();
202 assert_eq!(parsed_params.decision_id, 42);
203 assert_eq!(parsed_params.transaction_id, "txn-123");
204 }
205
206 #[test]
207 fn test_decision_response_deserialization() {
208 let json = r#"{"action":"authorize","detail":{"settlement_address":"eip155:1:0xABC"}}"#;
209 let resp: DecisionResponse = serde_json::from_str(json).unwrap();
210 assert_eq!(resp.action, "authorize");
211 assert_eq!(resp.detail.unwrap()["settlement_address"], "eip155:1:0xABC");
212 }
213
214 #[test]
215 fn test_event_notification_serialization() {
216 let params = EventNotificationParams {
217 event_type: "message_received".to_string(),
218 agent_did: Some("did:key:z6Mk...".to_string()),
219 data: json!({"message_id": "msg-1", "message_type": "Transfer"}),
220 timestamp: "2026-02-21T12:00:00Z".to_string(),
221 };
222
223 let notif =
224 JsonRpcNotification::new("tap/event", Some(serde_json::to_value(¶ms).unwrap()));
225 let json = serde_json::to_string(¬if).unwrap();
226
227 let parsed: Value = serde_json::from_str(&json).unwrap();
229 assert!(parsed.get("id").is_none());
230 assert_eq!(parsed["method"], "tap/event");
231 }
232
233 #[test]
234 fn test_initialize_params_serialization() {
235 let params = InitializeParams {
236 version: "0.1.0".to_string(),
237 agent_dids: vec!["did:key:z6Mk1".to_string(), "did:key:z6Mk2".to_string()],
238 subscribe_mode: "decisions".to_string(),
239 capabilities: InitializeCapabilities {
240 tools: true,
241 decisions: true,
242 },
243 };
244
245 let json_str = serde_json::to_string(¶ms).unwrap();
246 let parsed: InitializeParams = serde_json::from_str(&json_str).unwrap();
247
248 assert_eq!(parsed.version, "0.1.0");
249 assert_eq!(parsed.agent_dids.len(), 2);
250 assert!(parsed.capabilities.tools);
251 }
252
253 #[test]
254 fn test_incoming_message_request() {
255 let json = r#"{"jsonrpc":"2.0","id":100,"method":"tools/call","params":{"name":"tap_authorize","arguments":{}}}"#;
256 let msg: IncomingMessage = serde_json::from_str(json).unwrap();
257 match msg {
258 IncomingMessage::Request(req) => {
259 assert_eq!(req.method, "tools/call");
260 assert_eq!(req.id, json!(100));
261 }
262 _ => panic!("Expected request"),
263 }
264 }
265
266 #[test]
267 fn test_incoming_message_notification() {
268 let json = r#"{"jsonrpc":"2.0","method":"tap/ready","params":{"name":"my-engine"}}"#;
269 let msg: IncomingMessage = serde_json::from_str(json).unwrap();
270 match msg {
271 IncomingMessage::Notification(notif) => {
272 assert_eq!(notif.method, "tap/ready");
273 }
274 _ => panic!("Expected notification"),
275 }
276 }
277
278 #[test]
279 fn test_json_rpc_error_response() {
280 let err = JsonRpcErrorResponse::new(json!(1), -32600, "Invalid request");
281 let json_str = serde_json::to_string(&err).unwrap();
282 let parsed: Value = serde_json::from_str(&json_str).unwrap();
283
284 assert_eq!(parsed["error"]["code"], -32600);
285 assert_eq!(parsed["error"]["message"], "Invalid request");
286 }
287
288 #[test]
289 fn test_ready_params_deserialization() {
290 let json = r#"{"version":"1.0.0","name":"my-compliance-engine"}"#;
291 let params: ReadyParams = serde_json::from_str(json).unwrap();
292 assert_eq!(params.version, Some("1.0.0".to_string()));
293 assert_eq!(params.name, Some("my-compliance-engine".to_string()));
294 }
295
296 #[test]
297 fn test_ready_params_minimal() {
298 let json = r#"{}"#;
299 let params: ReadyParams = serde_json::from_str(json).unwrap();
300 assert!(params.version.is_none());
301 assert!(params.name.is_none());
302 }
303}