agent_client_protocol/
tool_call.rs

1use std::{path::PathBuf, sync::Arc};
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::ContentBlock;
7
8#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
9#[serde(rename_all = "camelCase")]
10pub struct ToolCall {
11    #[serde(rename = "toolCallId")]
12    pub id: ToolCallId,
13    pub title: String,
14    pub kind: ToolKind,
15    pub status: ToolCallStatus,
16    #[serde(default, skip_serializing_if = "Vec::is_empty")]
17    pub content: Vec<ToolCallContent>,
18    #[serde(default, skip_serializing_if = "Vec::is_empty")]
19    pub locations: Vec<ToolCallLocation>,
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub raw_input: Option<serde_json::Value>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25#[serde(rename_all = "camelCase")]
26pub struct ToolCallUpdate {
27    #[serde(rename = "toolCallId")]
28    pub id: ToolCallId,
29    #[serde(flatten)]
30    pub fields: ToolCallUpdateFields,
31}
32
33#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
34#[serde(rename_all = "camelCase")]
35pub struct ToolCallUpdateFields {
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub kind: Option<ToolKind>,
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub status: Option<ToolCallStatus>,
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub title: Option<String>,
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub content: Option<Vec<ToolCallContent>>,
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub locations: Option<Vec<ToolCallLocation>>,
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    pub raw_input: Option<serde_json::Value>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
51#[serde(transparent)]
52pub struct ToolCallId(pub Arc<str>);
53
54#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
55#[serde(rename_all = "snake_case")]
56pub enum ToolKind {
57    Read,
58    Edit,
59    Delete,
60    Move,
61    Search,
62    Execute,
63    Think,
64    Fetch,
65    Other,
66}
67
68#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
69#[serde(rename_all = "snake_case")]
70pub enum ToolCallStatus {
71    /// The tool call hasn't started running yet because the input is either
72    /// streaming or we're awaiting approval.
73    Pending,
74    /// The tool call is currently running.
75    InProgress,
76    /// The tool call completed successfully.
77    Completed,
78    /// The tool call failed.
79    Failed,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
83#[serde(tag = "type", rename_all = "snake_case")]
84pub enum ToolCallContent {
85    Content {
86        content: ContentBlock,
87    },
88    Diff {
89        #[serde(flatten)]
90        diff: Diff,
91    },
92}
93
94impl<T: Into<ContentBlock>> From<T> for ToolCallContent {
95    fn from(content: T) -> Self {
96        ToolCallContent::Content {
97            content: content.into(),
98        }
99    }
100}
101
102impl From<Diff> for ToolCallContent {
103    fn from(diff: Diff) -> Self {
104        ToolCallContent::Diff { diff }
105    }
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
109#[serde(rename_all = "camelCase")]
110pub struct Diff {
111    pub path: PathBuf,
112    pub old_text: Option<String>,
113    pub new_text: String,
114}
115
116#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
117#[serde(tag = "type", rename_all = "camelCase")]
118pub struct ToolCallLocation {
119    pub path: PathBuf,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub line: Option<u32>,
122}