1use serde::{Deserialize, Serialize};
8use serde_json::json;
9
10pub const WAIT_OPERATIONS_TOOL_NAME: &str = "wait_operations";
11pub const INSPECT_OPERATIONS_TOOL_NAME: &str = "inspect_operations";
12pub const STOP_OPERATIONS_TOOL_NAME: &str = "stop_operations";
13pub const SEND_OPERATION_INPUT_TOOL_NAME: &str = "send_operation_input";
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16#[serde(rename_all = "snake_case")]
17pub enum AsyncOperationKind {
18 Ability,
19 SubAgent,
20 Shell,
21 Media,
22}
23
24impl AsyncOperationKind {
25 pub fn as_str(self) -> &'static str {
26 match self {
27 Self::Ability => "ability",
28 Self::SubAgent => "sub_agent",
29 Self::Shell => "shell",
30 Self::Media => "media",
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum AsyncOperationStatus {
38 Running,
39 WaitingForInput,
40 Completed,
41 Failed,
42 Stopped,
43}
44
45impl AsyncOperationStatus {
46 pub fn as_str(self) -> &'static str {
47 match self {
48 Self::Running => "running",
49 Self::WaitingForInput => "waiting_for_input",
50 Self::Completed => "completed",
51 Self::Failed => "failed",
52 Self::Stopped => "stopped",
53 }
54 }
55
56 pub fn can_receive_input(self) -> bool {
57 matches!(self, Self::Running | Self::WaitingForInput)
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
62#[serde(rename_all = "snake_case")]
63pub enum AsyncOperationSignalKind {
64 Started,
65 Progress,
66 NeedsInput,
67 Completed,
68 Failed,
69 Stopped,
70}
71
72impl AsyncOperationSignalKind {
73 pub fn as_str(self) -> &'static str {
74 match self {
75 Self::Started => "started",
76 Self::Progress => "progress",
77 Self::NeedsInput => "needs_input",
78 Self::Completed => "completed",
79 Self::Failed => "failed",
80 Self::Stopped => "stopped",
81 }
82 }
83}
84
85#[derive(Debug, Clone, Deserialize)]
86pub struct InspectOperationsArgs {
87 #[serde(default)]
88 pub operations: Vec<String>,
89 #[serde(default)]
90 pub kind: Option<AsyncOperationKind>,
91 #[serde(default)]
92 pub include_transcript: bool,
93 #[serde(default = "default_inspect_limit")]
94 pub limit: usize,
95}
96
97#[derive(Debug, Clone, Deserialize)]
98pub struct StopOperationsArgs {
99 #[serde(default)]
100 pub operations: Vec<String>,
101 #[serde(default)]
102 pub kind: Option<AsyncOperationKind>,
103 pub reason: Option<String>,
104}
105
106#[derive(Debug, Clone, Deserialize)]
107pub struct WaitOperationsArgs {
108 #[serde(default = "default_wait_seconds")]
109 pub seconds: u64,
110 #[serde(default)]
111 pub kind: Option<AsyncOperationKind>,
112 pub reason: Option<String>,
113}
114
115#[derive(Debug, Clone, Deserialize)]
116pub struct SendOperationInputArgs {
117 #[serde(default)]
118 pub operations: Vec<String>,
119 pub message: String,
120}
121
122pub fn inspect_operations_parameters_schema() -> serde_json::Value {
123 json!({
124 "type": "object",
125 "properties": {
126 "operations": {"type": "array", "items": {"type": "string"}},
127 "kind": operation_kind_schema(),
128 "include_transcript": {"type": "boolean"},
129 "limit": {"type": "number", "minimum": 1, "maximum": 50}
130 },
131 "additionalProperties": false
132 })
133}
134
135pub fn stop_operations_parameters_schema() -> serde_json::Value {
136 json!({
137 "type": "object",
138 "properties": {
139 "operations": {"type": "array", "items": {"type": "string"}},
140 "kind": operation_kind_schema(),
141 "reason": {"type": "string"}
142 },
143 "additionalProperties": false
144 })
145}
146
147pub fn wait_operations_parameters_schema() -> serde_json::Value {
148 json!({
149 "type": "object",
150 "properties": {
151 "seconds": {"type": "number", "minimum": 1, "maximum": 30},
152 "kind": operation_kind_schema(),
153 "reason": {"type": "string"}
154 },
155 "additionalProperties": false
156 })
157}
158
159pub fn send_operation_input_parameters_schema() -> serde_json::Value {
160 json!({
161 "type": "object",
162 "properties": {
163 "operations": {"type": "array", "items": {"type": "string"}},
164 "message": {"type": "string"}
165 },
166 "required": ["operations", "message"],
167 "additionalProperties": false
168 })
169}
170
171pub fn operation_kind_schema() -> serde_json::Value {
172 json!({
173 "type": "string",
174 "enum": ["ability", "sub_agent", "shell", "media"]
175 })
176}
177
178fn default_inspect_limit() -> usize {
179 30
180}
181
182fn default_wait_seconds() -> u64 {
183 10
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn async_operation_kind_uses_wire_names() {
192 assert_eq!(AsyncOperationKind::Ability.as_str(), "ability");
193 assert_eq!(AsyncOperationKind::SubAgent.as_str(), "sub_agent");
194 assert_eq!(AsyncOperationKind::Shell.as_str(), "shell");
195 assert_eq!(AsyncOperationKind::Media.as_str(), "media");
196 }
197
198 #[test]
199 fn wait_args_deserialize_with_defaults() {
200 let args: WaitOperationsArgs = serde_json::from_value(json!({})).unwrap();
201
202 assert_eq!(args.seconds, 10);
203 assert_eq!(args.kind, None);
204 }
205}