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