pub use crate::runtime::tool_call::lifecycle::ResumeDecisionAction;
use crate::runtime::tool_call::lifecycle::ToolCallResume;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ToolCallDecision {
pub target_id: String,
#[serde(flatten)]
pub resume: ToolCallResume,
}
impl ToolCallDecision {
pub fn resume(target_id: impl Into<String>, result: Value, updated_at: u64) -> Self {
Self {
target_id: target_id.into(),
resume: ToolCallResume {
decision_id: String::new(),
action: ResumeDecisionAction::Resume,
result,
reason: None,
updated_at,
},
}
}
pub fn cancel(
target_id: impl Into<String>,
result: Value,
reason: Option<String>,
updated_at: u64,
) -> Self {
Self {
target_id: target_id.into(),
resume: ToolCallResume {
decision_id: String::new(),
action: ResumeDecisionAction::Cancel,
result,
reason,
updated_at,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tool_call_decision_resume_constructor_sets_resume_action() {
let mut decision = ToolCallDecision::resume("fc_1", Value::Bool(true), 123);
decision.resume.decision_id = "decision_fc_1".to_string();
assert_eq!(decision.target_id, "fc_1");
assert_eq!(decision.resume.decision_id, "decision_fc_1");
assert!(matches!(
decision.resume.action,
ResumeDecisionAction::Resume
));
assert_eq!(decision.resume.result, Value::Bool(true));
assert!(decision.resume.reason.is_none());
assert_eq!(decision.resume.updated_at, 123);
}
#[test]
fn tool_call_decision_cancel_constructor_sets_cancel_action() {
let mut decision = ToolCallDecision::cancel(
"fc_2",
serde_json::json!({
"approved": false,
"reason": "denied by user"
}),
Some("denied by user".to_string()),
456,
);
decision.resume.decision_id = "decision_fc_2".to_string();
assert!(matches!(
decision.resume.action,
ResumeDecisionAction::Cancel
));
assert_eq!(decision.resume.reason.as_deref(), Some("denied by user"));
assert_eq!(decision.resume.updated_at, 456);
}
#[test]
fn tool_call_decision_serde_flatten_roundtrip() {
let decision = ToolCallDecision::resume("fc_1", Value::Bool(true), 42);
let json = serde_json::to_value(&decision).unwrap();
assert!(json.get("resume").is_none(), "resume should be flattened");
assert_eq!(json["target_id"], "fc_1");
assert_eq!(json["action"], "resume");
assert_eq!(json["result"], true);
let deserialized: ToolCallDecision = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, decision);
}
}