1use crate::acp;
2use serde_json::{Value, json};
3use std::path::PathBuf;
4
5pub const TOOL_FAILURE_PREFIX: &str = "Tool execution failed";
6pub const TOOL_SUCCESS_LABEL: &str = "success";
7pub const TOOL_ERROR_LABEL: &str = "error";
8pub const TOOL_RESPONSE_KEY_STATUS: &str = "status";
9pub const TOOL_RESPONSE_KEY_TOOL: &str = "tool";
10pub const TOOL_RESPONSE_KEY_PATH: &str = "path";
11pub const TOOL_RESPONSE_KEY_CONTENT: &str = "content";
12pub const TOOL_RESPONSE_KEY_TRUNCATED: &str = "truncated";
13pub const TOOL_RESPONSE_KEY_MESSAGE: &str = "message";
14
15pub const TOOL_EXECUTION_CANCELLED_MESSAGE: &str =
16 "Tool execution cancelled at the client's request";
17pub const TOOL_PERMISSION_ALLOW_OPTION_ID: &str = "allow-once";
18pub const TOOL_PERMISSION_ALLOW_ALWAYS_OPTION_ID: &str = "allow-always";
19pub const TOOL_PERMISSION_DENY_OPTION_ID: &str = "reject-once";
20pub const TOOL_PERMISSION_DENY_ALWAYS_OPTION_ID: &str = "reject-always";
21pub const TOOL_PERMISSION_ALLOW_PREFIX: &str = "Allow";
22pub const TOOL_PERMISSION_DENY_PREFIX: &str = "Deny";
23pub const TOOL_PERMISSION_DENIED_MESSAGE: &str =
24 "Tool execution cancelled: permission denied by the user";
25pub const TOOL_PERMISSION_CANCELLED_MESSAGE: &str =
26 "Tool execution cancelled: permission request interrupted";
27pub const TOOL_PERMISSION_REQUEST_FAILURE_LOG: &str =
28 "Failed to request ACP tool permission, cancelling the tool invocation";
29pub const TOOL_PERMISSION_UNKNOWN_OPTION_LOG: &str =
30 "Received unsupported ACP permission option selection";
31pub const TOOL_PERMISSION_REQUEST_FAILURE_MESSAGE: &str =
32 "Tool execution cancelled: permission request failed";
33
34pub struct ToolExecutionReport {
35 pub status: acp::ToolCallStatus,
36 pub llm_response: String,
37 pub content: Vec<acp::ToolCallContent>,
38 pub locations: Vec<acp::ToolCallLocation>,
39 pub raw_output: Option<Value>,
40}
41
42impl ToolExecutionReport {
43 pub fn success(
44 content: Vec<acp::ToolCallContent>,
45 locations: Vec<acp::ToolCallLocation>,
46 payload: Value,
47 ) -> Self {
48 Self {
49 status: acp::ToolCallStatus::Completed,
50 llm_response: payload.to_string(),
51 content,
52 locations,
53 raw_output: Some(payload),
54 }
55 }
56
57 pub fn failure(tool_name: &str, message: &str) -> Self {
58 let payload = json!({
59 TOOL_RESPONSE_KEY_STATUS: TOOL_ERROR_LABEL,
60 TOOL_RESPONSE_KEY_TOOL: tool_name,
61 TOOL_RESPONSE_KEY_MESSAGE: message,
62 });
63 Self {
64 status: acp::ToolCallStatus::Failed,
65 llm_response: payload.to_string(),
66 content: vec![acp::ToolCallContent::from(format!(
67 "{TOOL_FAILURE_PREFIX}: {message}"
68 ))],
69 locations: Vec::new(),
70 raw_output: Some(payload),
71 }
72 }
73
74 pub fn cancelled(tool_name: &str) -> Self {
75 Self::failure(tool_name, TOOL_EXECUTION_CANCELLED_MESSAGE)
76 }
77}
78
79pub fn create_diff_content(
80 path: &str,
81 old_text: Option<&str>,
82 new_text: &str,
83) -> acp::ToolCallContent {
84 acp::ToolCallContent::Diff(
85 acp::Diff::new(PathBuf::from(path), new_text.to_string())
86 .old_text(old_text.map(|s| s.to_string())),
87 )
88}