1use crate::policy::Capability;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ToolCall {
9 pub call_id: String,
10 pub tool_name: String,
11 pub input: serde_json::Value,
12 #[serde(default)]
13 pub requested_capabilities: Vec<Capability>,
14}
15
16impl ToolCall {
17 pub fn new(
18 tool_name: impl Into<String>,
19 input: serde_json::Value,
20 requested_capabilities: Vec<Capability>,
21 ) -> Self {
22 Self {
23 call_id: uuid::Uuid::new_v4().to_string(),
24 tool_name: tool_name.into(),
25 input,
26 requested_capabilities,
27 }
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33#[serde(tag = "status", rename_all = "snake_case")]
34pub enum ToolOutcome {
35 Success { output: serde_json::Value },
36 Failure { error: String },
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn tool_call_new() {
45 let tc = ToolCall::new("read_file", serde_json::json!({"path": "/tmp"}), vec![]);
46 assert_eq!(tc.tool_name, "read_file");
47 assert!(!tc.call_id.is_empty());
48 }
49
50 #[test]
51 fn tool_outcome_serde_roundtrip() {
52 let success = ToolOutcome::Success {
53 output: serde_json::json!({"data": 42}),
54 };
55 let json = serde_json::to_string(&success).unwrap();
56 assert!(json.contains("\"status\":\"success\""));
57 let back: ToolOutcome = serde_json::from_str(&json).unwrap();
58 assert!(matches!(back, ToolOutcome::Success { .. }));
59
60 let failure = ToolOutcome::Failure {
61 error: "not found".into(),
62 };
63 let json = serde_json::to_string(&failure).unwrap();
64 assert!(json.contains("\"status\":\"failure\""));
65 }
66}