1use std::collections::HashMap;
3use std::sync::Arc;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Message {
9 pub role: MessageRole,
10 pub content: String,
11 #[serde(skip_serializing_if = "Option::is_none", default)]
13 pub uuid: Option<String>,
14 #[serde(skip_serializing_if = "Option::is_none", default)]
15 pub attachments: Option<Vec<Attachment>>,
16 #[serde(skip_serializing_if = "Option::is_none", default)]
18 pub tool_call_id: Option<String>,
19 #[serde(skip_serializing_if = "Option::is_none", default)]
21 pub tool_calls: Option<Vec<ToolCall>>,
22 #[serde(skip_serializing_if = "Option::is_none", default)]
24 pub is_error: Option<bool>,
25 #[serde(skip_serializing_if = "Option::is_none", default)]
27 pub is_meta: Option<bool>,
28 #[serde(skip_serializing_if = "Option::is_none", default)]
30 pub is_api_error_message: Option<bool>,
31 #[serde(skip_serializing_if = "Option::is_none", default)]
33 pub error_details: Option<String>,
34}
35
36impl Default for Message {
37 fn default() -> Self {
38 Self {
39 role: MessageRole::User,
40 content: String::new(),
41 uuid: None,
42 attachments: None,
43 tool_call_id: None,
44 tool_calls: None,
45 is_error: None,
46 is_meta: None,
47 is_api_error_message: None,
48 error_details: None,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct ToolCall {
56 pub id: String,
57 #[serde(default = "default_tool_call_type")]
58 pub r#type: String,
59 pub name: String,
60 pub arguments: serde_json::Value,
61}
62
63fn default_tool_call_type() -> String {
64 "function".to_string()
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
68#[serde(rename_all = "lowercase")]
69pub enum MessageRole {
70 #[default]
71 User,
72 Assistant,
73 #[serde(rename = "tool")]
74 Tool,
75 System,
76}
77
78impl MessageRole {
79 pub fn as_str(&self) -> &'static str {
81 match self {
82 MessageRole::User => "user",
83 MessageRole::Assistant => "assistant",
84 MessageRole::Tool => "tool",
85 MessageRole::System => "system",
86 }
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
92#[serde(tag = "type")]
93pub enum Attachment {
94 File { path: String },
96 AlreadyReadFile { path: String, content: String },
98 PdfReference { path: String },
100 EditedTextFile { filename: String, snippet: String },
102 EditedImageFile { filename: String },
104 Directory {
106 path: String,
107 content: String,
108 display_path: String,
109 },
110 SelectedLinesInIde {
112 ide_name: String,
113 filename: String,
114 start_line: u32,
115 end_line: u32,
116 },
117 MemoryFile { path: String },
119 SkillListing { skills: Vec<SkillInfo> },
121 InvokedSkills { skills: Vec<InvokedSkill> },
123 TaskStatus {
125 task_id: String,
126 description: String,
127 status: String,
128 },
129 PlanFileReference { path: String },
131 McpResources { tools: Vec<String> },
133 DeferredTools { tools: Vec<String> },
135 AgentListing { agents: Vec<String> },
137 Custom {
139 name: String,
140 content: serde_json::Value,
141 },
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct SkillInfo {
146 pub name: String,
147 pub description: String,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct InvokedSkill {
152 pub name: String,
153 pub path: String,
154 pub content: String,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, Default)]
158pub struct TokenUsage {
159 pub input_tokens: u64,
160 pub output_tokens: u64,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub cache_creation_input_tokens: Option<u64>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub cache_read_input_tokens: Option<u64>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub iterations: Option<Vec<IterationUsage>>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, Default)]
171pub struct IterationUsage {
172 pub input_tokens: u64,
173 pub output_tokens: u64,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct ToolDefinition {
178 pub name: String,
179 pub description: String,
180 pub input_schema: ToolInputSchema,
181 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub annotations: Option<ToolAnnotations>,
184 #[serde(default, skip_serializing_if = "Option::is_none")]
186 pub should_defer: Option<bool>,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub always_load: Option<bool>,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
192 pub is_mcp: Option<bool>,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub search_hint: Option<String>,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
198 pub aliases: Option<Vec<String>>,
199 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub user_facing_name: Option<String>,
202 #[serde(rename = "interruptBehavior", default, skip_serializing_if = "Option::is_none")]
204 pub interrupt_behavior: Option<String>,
205}
206
207impl Default for ToolDefinition {
208 fn default() -> Self {
209 Self {
210 name: String::new(),
211 description: String::new(),
212 input_schema: ToolInputSchema::default(),
213 annotations: None,
214 should_defer: None,
215 always_load: None,
216 is_mcp: None,
217 search_hint: None,
218 aliases: None,
219 user_facing_name: None,
220 interrupt_behavior: None,
221 }
222 }
223}
224
225impl ToolDefinition {
226 pub fn new(name: &str, description: &str, input_schema: ToolInputSchema) -> Self {
228 Self {
229 name: name.to_string(),
230 description: description.to_string(),
231 input_schema,
232 annotations: None,
233 should_defer: None,
234 always_load: None,
235 is_mcp: None,
236 search_hint: None,
237 aliases: None,
238 user_facing_name: None,
239 interrupt_behavior: None,
240 }
241 }
242
243 pub fn with_deferred(mut self, should_defer: bool) -> Self {
245 self.should_defer = Some(should_defer);
246 self
247 }
248
249 pub fn with_always_load(mut self) -> Self {
251 self.always_load = Some(true);
252 self
253 }
254
255 pub fn with_mcp(mut self) -> Self {
257 self.is_mcp = Some(true);
258 self
259 }
260
261 pub fn with_search_hint(mut self, hint: &str) -> Self {
263 self.search_hint = Some(hint.to_string());
264 self
265 }
266
267 pub fn interrupt_behavior(&self) -> crate::tools::types::InterruptBehavior {
270 match self.interrupt_behavior.as_deref() {
271 Some("cancel") => crate::tools::types::InterruptBehavior::Cancel,
272 _ => crate::tools::types::InterruptBehavior::Block,
273 }
274 }
275
276 pub fn backfill_observable_input(&self, _input: &mut serde_json::Value) {
280 }
283
284 pub fn is_concurrency_safe(&self, _input: &serde_json::Value) -> bool {
286 self.annotations
287 .as_ref()
288 .and_then(|a| a.concurrency_safe)
289 .unwrap_or(false)
290 }
291
292 pub fn is_read_only(&self, _input: &serde_json::Value) -> bool {
294 if let Some(ref a) = self.annotations {
295 if let Some(ro) = a.read_only {
296 return ro;
297 }
298 }
299 matches!(
301 self.name.as_str(),
302 "Read" | "Glob" | "Grep" | "Search" | "WebFetch" | "WebSearch"
303 )
304 }
305
306 pub fn is_destructive(&self, input: &serde_json::Value) -> bool {
308 if let Some(ref a) = self.annotations {
309 if let Some(d) = a.destructive {
310 return d;
311 }
312 }
313 let input_str = input.to_string();
315 matches!(self.name.as_str(), "Bash" | "Write" | "Edit")
316 && (input_str.contains("rm -rf")
317 || input_str.contains("rm /")
318 || input_str.contains("dd if=")
319 || input_str.contains("format"))
320 }
321
322 pub fn is_idempotent(&self) -> bool {
324 self.annotations
325 .as_ref()
326 .and_then(|a| a.idempotent)
327 .unwrap_or(false)
328 }
329
330 pub fn get_use_summary(&self, input: &serde_json::Value) -> String {
332 match self.name.as_str() {
333 "Bash" => {
334 if let Some(cmd) = input.get("command").and_then(|v| v.as_str()) {
335 let truncated = if cmd.len() > 50 {
336 format!("{}...", &cmd[..50])
337 } else {
338 cmd.to_string()
339 };
340 format!("Bash: {}", truncated)
341 } else {
342 "Bash".to_string()
343 }
344 }
345 "Read" => {
346 if let Some(path) = input.get("path").and_then(|v| v.as_str()) {
347 format!("Read: {}", path)
348 } else {
349 "Read".to_string()
350 }
351 }
352 "Write" => {
353 if let Some(path) = input.get("path").and_then(|v| v.as_str()) {
354 format!("Write: {}", path)
355 } else {
356 "Write".to_string()
357 }
358 }
359 "Edit" => {
360 if let Some(path) = input.get("file_path").and_then(|v| v.as_str()) {
361 format!("Edit: {}", path)
362 } else {
363 "Edit".to_string()
364 }
365 }
366 "Glob" => {
367 if let Some(pattern) = input.get("pattern").and_then(|v| v.as_str()) {
368 format!("Glob: {}", pattern)
369 } else {
370 "Glob".to_string()
371 }
372 }
373 "Grep" => {
374 if let Some(pattern) = input.get("pattern").and_then(|v| v.as_str()) {
375 format!("Grep: {}", pattern)
376 } else {
377 "Grep".to_string()
378 }
379 }
380 _ => self.name.clone(),
381 }
382 }
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize, Default)]
387pub struct ToolAnnotations {
388 #[serde(rename = "concurrencySafe", skip_serializing_if = "Option::is_none")]
390 pub concurrency_safe: Option<bool>,
391 #[serde(rename = "readOnly", skip_serializing_if = "Option::is_none")]
393 pub read_only: Option<bool>,
394 #[serde(rename = "destructive", skip_serializing_if = "Option::is_none")]
396 pub destructive: Option<bool>,
397 #[serde(skip_serializing_if = "Option::is_none")]
399 pub idempotent: Option<bool>,
400 #[serde(rename = "openWorld", skip_serializing_if = "Option::is_none")]
402 pub open_world: Option<bool>,
403}
404
405impl ToolAnnotations {
406 pub fn read_only() -> Self {
408 Self {
409 read_only: Some(true),
410 ..Default::default()
411 }
412 }
413
414 pub fn destructive() -> Self {
416 Self {
417 destructive: Some(true),
418 ..Default::default()
419 }
420 }
421
422 pub fn concurrency_safe() -> Self {
424 Self {
425 concurrency_safe: Some(true),
426 ..Default::default()
427 }
428 }
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize, Default)]
432pub struct ToolInputSchema {
433 #[serde(rename = "type")]
434 pub schema_type: String,
435 pub properties: serde_json::Value,
436 pub required: Option<Vec<String>>,
437}
438
439#[derive(Debug, Clone, Deserialize)]
440pub struct ToolContext {
441 pub cwd: String,
442 #[serde(skip)]
443 pub abort_signal: std::sync::Arc<crate::utils::AbortSignal>,
444}
445
446impl Serialize for ToolContext {
448 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
449 where
450 S: serde::Serializer,
451 {
452 use serde::ser::SerializeStruct;
454 let mut state = serializer.serialize_struct("ToolContext", 1)?;
455 state.serialize_field("cwd", &self.cwd)?;
456 state.end()
457 }
458}
459
460impl Default for ToolContext {
461 fn default() -> Self {
462 Self {
463 cwd: String::new(),
464 abort_signal: std::sync::Arc::new(crate::utils::AbortSignal::new(0)),
465 }
466 }
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct ToolResult {
471 #[serde(rename = "type")]
472 pub result_type: String,
473 pub tool_use_id: String,
474 pub content: String,
475 #[serde(skip_serializing_if = "Option::is_none")]
476 pub is_error: Option<bool>,
477 #[serde(skip_serializing_if = "Option::is_none")]
478 pub was_persisted: Option<bool>,
479}
480
481impl Default for ToolResult {
482 fn default() -> Self {
483 Self {
484 result_type: String::new(),
485 tool_use_id: String::new(),
486 content: String::new(),
487 is_error: None,
488 was_persisted: None,
489 }
490 }
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
495#[serde(untagged)]
496pub enum ToolResultContent {
497 Text {
499 #[serde(rename = "type")]
500 content_type: String,
501 text: String,
502 },
503 ToolReference {
505 #[serde(rename = "type")]
506 content_type: String,
507 tool_name: String,
508 },
509}
510
511impl ToolResultContent {
512 pub fn text(text: &str) -> Self {
513 Self::Text {
514 content_type: "text".to_string(),
515 text: text.to_string(),
516 }
517 }
518
519 pub fn tool_reference(tool_name: &str) -> Self {
520 Self::ToolReference {
521 content_type: "tool_reference".to_string(),
522 tool_name: tool_name.to_string(),
523 }
524 }
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct ToolResultStructured {
530 #[serde(rename = "type")]
531 pub result_type: String,
532 pub tool_use_id: String,
533 pub content: Vec<ToolResultContent>,
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub is_error: Option<bool>,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
540#[serde(tag = "type")]
541pub enum ThinkingConfig {
542 Adaptive,
544 Enabled {
546 #[serde(rename = "budgetTokens")]
547 budget_tokens: u32,
548 },
549 Disabled,
551}
552
553impl Default for ThinkingConfig {
554 fn default() -> Self {
555 ThinkingConfig::Adaptive
557 }
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize)]
562pub enum ExitReason {
563 Completed,
565 MaxTurns { max_turns: u32, turn_count: u32 },
567 AbortedStreaming { reason: String },
569 AbortedTools { reason: String },
571 HookStopped,
573 StopHookPrevented,
575 PromptTooLong { error: Option<String> },
577 ImageError { error: String },
579 ModelError { error: String },
581 BlockingLimit,
583 TokenBudgetExhausted { reason: String },
585 MaxTokens,
587 MaxBudgetExceeded { max_budget_usd: f64 },
589}
590
591impl Default for ExitReason {
592 fn default() -> Self {
593 ExitReason::Completed
594 }
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599#[serde(tag = "type")]
600pub enum CompactProgressEvent {
601 #[serde(rename = "hooks_start")]
602 HooksStart {
603 #[serde(rename = "hookType")]
604 hook_type: CompactHookType,
605 },
606 #[serde(rename = "compact_start")]
607 CompactStart,
608 #[serde(rename = "compact_end")]
609 CompactEnd {
610 #[serde(skip_serializing_if = "Option::is_none")]
613 message: Option<String>,
614 },
615}
616
617#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
619#[serde(rename_all = "snake_case")]
620pub enum CompactHookType {
621 PreCompact,
622 PostCompact,
623 SessionStart,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
627pub struct QueryResult {
628 pub text: String,
629 pub usage: TokenUsage,
630 pub num_turns: u32,
631 pub duration_ms: u64,
632 pub exit_reason: ExitReason,
634}
635
636#[derive(Debug, Clone)]
638pub enum AgentEvent {
639 ToolStart {
641 tool_name: String,
642 tool_call_id: String,
643 input: serde_json::Value,
644 display_name: Option<String>,
647 summary: Option<String>,
649 activity_description: Option<String>,
651 },
652 ToolComplete {
654 tool_name: String,
655 tool_call_id: String,
656 result: ToolResult,
657 display_name: Option<String>,
659 rendered_result: Option<String>,
661 },
662 ToolError {
664 tool_name: String,
665 tool_call_id: String,
666 error: String,
667 },
668 Thinking { turn: u32 },
670 Done { result: QueryResult },
672 MessageStart { message_id: String },
674 ContentBlockStart { index: u32, block_type: String },
676 ContentBlockDelta { index: u32, delta: ContentDelta },
678 ContentBlockStop { index: u32 },
680 MessageStop,
682 RequestStart,
684 StreamRequestEnd,
687 RateLimitStatus {
689 is_rate_limited: bool,
691 retry_after_secs: Option<f64>,
693 },
694 MaxTurnsReached { max_turns: u32, turn_count: u32 },
696 Tombstone { message: String },
699 Compact { event: CompactProgressEvent },
702 TokenUsage {
705 usage: TokenUsage,
706 cost: f64,
707 },
708 ApiRetry {
712 attempt: u32,
714 max_retries: u32,
716 retry_delay_ms: u64,
718 error_status: Option<u16>,
720 error: String,
722 },
723}
724
725#[derive(Debug, Clone)]
727pub enum ContentDelta {
728 Text { text: String },
730 Thinking { text: String },
732 ToolUse {
734 id: String,
735 name: String,
736 input: serde_json::Value,
737 is_complete: bool,
738 },
739}
740
741#[derive(Debug, Clone, Serialize, Deserialize)]
747#[serde(untagged)]
748pub enum McpServerConfig {
749 Stdio(McpStdioConfig),
750 Sse(McpSseConfig),
751 Http(McpHttpConfig),
752}
753
754#[derive(Debug, Clone, Serialize, Deserialize)]
755#[serde(rename_all = "camelCase")]
756pub struct McpStdioConfig {
757 #[serde(default = "default_stdio_type")]
758 pub transport_type: Option<String>,
759 pub command: String,
760 pub args: Option<Vec<String>>,
761 pub env: Option<std::collections::HashMap<String, String>>,
762}
763
764fn default_stdio_type() -> Option<String> {
765 Some("stdio".to_string())
766}
767
768#[derive(Debug, Clone, Serialize, Deserialize)]
769#[serde(rename_all = "camelCase")]
770pub struct McpSseConfig {
771 pub transport_type: String,
772 pub url: String,
773 pub headers: Option<std::collections::HashMap<String, String>>,
774}
775
776#[derive(Debug, Clone, Serialize, Deserialize)]
777#[serde(rename_all = "camelCase")]
778pub struct McpHttpConfig {
779 pub transport_type: String,
780 pub url: String,
781 pub headers: Option<std::collections::HashMap<String, String>>,
782}
783
784#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
786#[serde(rename_all = "lowercase")]
787pub enum McpConnectionStatus {
788 Connected,
789 Disconnected,
790 Error,
791}
792
793#[derive(Debug, Clone, Serialize, Deserialize)]
795pub struct McpTool {
796 pub name: String,
797 pub description: Option<String>,
798 #[serde(rename = "inputSchema")]
799 pub input_schema: Option<serde_json::Value>,
800}
801
802#[derive(Debug, Clone, Serialize, Deserialize)]
808pub struct QueryChainTracking {
809 pub chain_id: String,
810 pub depth: u32,
811}
812
813#[derive(Debug, Clone, Serialize, Deserialize)]
816#[serde(tag = "result")]
817pub enum ValidationResult {
818 #[serde(rename = "true")]
820 Valid,
821 Invalid {
823 message: String,
825 #[serde(rename = "errorCode")]
827 error_code: i32,
828 },
829}
830
831impl ValidationResult {
832 pub fn valid() -> Self {
834 ValidationResult::Valid
835 }
836
837 pub fn invalid(message: String, error_code: i32) -> Self {
839 ValidationResult::Invalid {
840 message,
841 error_code,
842 }
843 }
844
845 pub fn is_valid(&self) -> bool {
847 matches!(self, ValidationResult::Valid)
848 }
849}
850
851#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
853#[serde(rename_all = "lowercase")]
854pub enum PermissionMode {
855 Default,
856 Auto,
857 #[serde(rename = "auto-accept")]
858 AutoAccept,
859 #[serde(rename = "auto-deny")]
860 AutoDeny,
861 Bypass,
862}
863
864#[derive(Debug, Clone, Serialize, Deserialize)]
866pub struct AdditionalWorkingDirectory {
867 pub path: String,
868 #[serde(rename = "permissionMode")]
869 pub permission_mode: Option<PermissionMode>,
870}
871
872#[derive(Debug, Clone, Serialize, Deserialize)]
874pub struct PermissionResult {
875 pub behavior: PermissionBehavior,
876 #[serde(rename = "updatedInput")]
877 pub updated_input: Option<serde_json::Value>,
878 #[serde(skip_serializing_if = "Option::is_none")]
879 pub message: Option<String>,
880}
881
882#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
884#[serde(rename_all = "kebab-case")]
885pub enum PermissionBehavior {
886 Allow,
887 Deny,
888 Ask,
889}
890
891pub type ToolPermissionRulesBySource = HashMap<String, Vec<String>>;
893
894#[derive(Debug, Clone, Serialize, Deserialize)]
896pub struct ToolPermissionContext {
897 pub mode: PermissionMode,
898 #[serde(rename = "additionalWorkingDirectories")]
899 pub additional_working_directories: HashMap<String, AdditionalWorkingDirectory>,
900 #[serde(rename = "alwaysAllowRules")]
901 pub always_allow_rules: ToolPermissionRulesBySource,
902 #[serde(rename = "alwaysDenyRules")]
903 pub always_deny_rules: ToolPermissionRulesBySource,
904 #[serde(rename = "alwaysAskRules")]
905 pub always_ask_rules: ToolPermissionRulesBySource,
906 #[serde(rename = "isBypassPermissionsModeAvailable")]
907 pub is_bypass_permissions_mode_available: bool,
908 #[serde(
909 rename = "isAutoModeAvailable",
910 skip_serializing_if = "Option::is_none"
911 )]
912 pub is_auto_mode_available: Option<bool>,
913 #[serde(
914 rename = "strippedDangerousRules",
915 skip_serializing_if = "Option::is_none"
916 )]
917 pub stripped_dangerous_rules: Option<ToolPermissionRulesBySource>,
918 #[serde(
919 rename = "shouldAvoidPermissionPrompts",
920 skip_serializing_if = "Option::is_none"
921 )]
922 pub should_avoid_permission_prompts: Option<bool>,
923 #[serde(
924 rename = "awaitAutomatedChecksBeforeDialog",
925 skip_serializing_if = "Option::is_none"
926 )]
927 pub await_automated_checks_before_dialog: Option<bool>,
928 #[serde(rename = "prePlanMode", skip_serializing_if = "Option::is_none")]
929 pub pre_plan_mode: Option<PermissionMode>,
930}
931
932impl Default for ToolPermissionContext {
933 fn default() -> Self {
934 Self {
935 mode: PermissionMode::Default,
936 additional_working_directories: HashMap::new(),
937 always_allow_rules: HashMap::new(),
938 always_deny_rules: HashMap::new(),
939 always_ask_rules: HashMap::new(),
940 is_bypass_permissions_mode_available: false,
941 is_auto_mode_available: None,
942 stripped_dangerous_rules: None,
943 should_avoid_permission_prompts: None,
944 await_automated_checks_before_dialog: None,
945 pre_plan_mode: None,
946 }
947 }
948}
949
950pub fn get_empty_tool_permission_context() -> ToolPermissionContext {
952 ToolPermissionContext::default()
953}
954
955#[derive(Debug, Clone, Serialize, Deserialize)]
957pub struct ToolInputJSONSchema {
958 #[serde(flatten)]
959 pub properties: serde_json::Value,
960 #[serde(rename = "type")]
961 pub schema_type: String,
962}
963
964#[derive(Debug, Clone, Serialize, Deserialize)]
970pub struct BashProgress {
971 #[serde(rename = "shell")]
972 pub shell: Option<String>,
973 #[serde(rename = "command")]
974 pub command: Option<String>,
975}
976
977#[derive(Debug, Clone, Serialize, Deserialize)]
979pub struct ReplProgress {
980 #[serde(rename = "input")]
981 pub input: Option<String>,
982 #[serde(rename = "toolName")]
983 pub tool_name: Option<String>,
984 #[serde(rename = "toolCallId")]
985 pub tool_call_id: Option<String>,
986}
987
988#[derive(Debug, Clone, Serialize, Deserialize)]
990pub struct McpProgress {
991 #[serde(rename = "serverName")]
992 pub server_name: String,
993 #[serde(rename = "toolName")]
994 pub tool_name: String,
995 #[serde(rename = "progress")]
996 pub progress: Option<serde_json::Value>,
997}
998
999#[derive(Debug, Clone, Serialize, Deserialize)]
1001pub struct WebSearchProgress {
1002 #[serde(rename = "query")]
1003 pub query: String,
1004 #[serde(rename = "currentStep")]
1005 pub current_step: Option<String>,
1006}
1007
1008#[derive(Debug, Clone, Serialize, Deserialize)]
1010pub struct TaskOutputProgress {
1011 #[serde(rename = "taskId")]
1012 pub task_id: String,
1013 #[serde(rename = "output")]
1014 pub output: Option<String>,
1015}
1016
1017#[derive(Debug, Clone, Serialize, Deserialize)]
1019pub struct SkillToolProgress {
1020 #[serde(rename = "skill")]
1021 pub skill: String,
1022 #[serde(rename = "step")]
1023 pub step: Option<String>,
1024}
1025
1026#[derive(Debug, Clone, Serialize, Deserialize)]
1028pub struct AgentToolProgress {
1029 #[serde(rename = "description")]
1030 pub description: String,
1031 #[serde(rename = "subagentType")]
1032 pub subagent_type: Option<String>,
1033}
1034
1035#[derive(Debug, Clone, Serialize, Deserialize)]
1037#[serde(tag = "type")]
1038pub enum ToolProgressData {
1039 #[serde(rename = "bash_progress")]
1040 BashProgress(BashProgress),
1041 #[serde(rename = "repl_progress")]
1042 ReplProgress(ReplProgress),
1043 #[serde(rename = "mcp_progress")]
1044 McpProgress(McpProgress),
1045 #[serde(rename = "web_search_progress")]
1046 WebSearchProgress(WebSearchProgress),
1047 #[serde(rename = "task_output_progress")]
1048 TaskOutputProgress(TaskOutputProgress),
1049 #[serde(rename = "skill_progress")]
1050 SkillProgress(SkillToolProgress),
1051 #[serde(rename = "agent_progress")]
1052 AgentProgress(AgentToolProgress),
1053}
1054
1055#[derive(Debug, Clone, Serialize, Deserialize)]
1057pub struct ToolProgress<P: Clone + serde::Serialize> {
1058 #[serde(rename = "toolUseID")]
1059 pub tool_use_id: String,
1060 pub data: P,
1061}
1062
1063pub fn filter_tool_progress_messages(
1065 progress_messages: &[serde_json::Value],
1066) -> Vec<serde_json::Value> {
1067 progress_messages
1068 .iter()
1069 .filter(|msg| {
1070 let data_type = msg.get("data").and_then(|d| d.get("type"));
1071 data_type.map(|t| t != "hook_progress").unwrap_or(true)
1072 })
1073 .cloned()
1074 .collect()
1075}