1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use serde_json::Value;
3use std::fmt;
4use uuid::Uuid;
5
6use super::claude_output::ClaudeOutput;
7use super::content_blocks::{deserialize_content_blocks, ContentBlock};
8use super::rate_limit::RateLimitInfo;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub enum SystemSubtype {
17 Init,
18 Status,
19 CompactBoundary,
20 ThinkingTokens,
21 TaskStarted,
22 TaskProgress,
23 TaskUpdated,
24 TaskNotification,
25 ApiRetry,
26 ControlRequestProgress,
27 ModelRefusalFallback,
28 ModelRefusalNoFallback,
29 LocalCommandOutput,
30 HookStarted,
31 HookProgress,
32 HookResponse,
33 PluginInstall,
34 BackgroundTasksChanged,
35 SessionStateChanged,
36 WorkerShuttingDown,
37 CommandsChanged,
38 Notification,
39 FilesPersisted,
40 MemoryRecall,
41 ElicitationComplete,
42 PermissionDenied,
43 MirrorError,
44 Informational,
45 CodeChangePublished,
46 VcsStateChanged,
47 FeedbackDraftQueued,
48 CloudSessionDelta,
49 DevIntent,
50 TurnHandoffAvailable,
51 TurnPreempted,
52 PeerMessageHold,
53 PerTurnEffortChanged,
54 SessionTitleChanged,
55 InstructionSizeWarning,
56 FileAttachmentsMissing,
57 PermissionCheckStatus,
58 InheritedAsksSettled,
59 UiFocus,
60 UiInvalidate,
61 UiLog,
62 UiPanes,
63 UiScroll,
64 UiStatus,
65 UiToast,
66 Unknown(String),
68}
69
70impl SystemSubtype {
71 pub fn as_str(&self) -> &str {
72 match self {
73 Self::Init => "init",
74 Self::Status => "status",
75 Self::CompactBoundary => "compact_boundary",
76 Self::ThinkingTokens => "thinking_tokens",
77 Self::TaskStarted => "task_started",
78 Self::TaskProgress => "task_progress",
79 Self::TaskUpdated => "task_updated",
80 Self::TaskNotification => "task_notification",
81 Self::ApiRetry => "api_retry",
82 Self::ControlRequestProgress => "control_request_progress",
83 Self::ModelRefusalFallback => "model_refusal_fallback",
84 Self::ModelRefusalNoFallback => "model_refusal_no_fallback",
85 Self::LocalCommandOutput => "local_command_output",
86 Self::HookStarted => "hook_started",
87 Self::HookProgress => "hook_progress",
88 Self::HookResponse => "hook_response",
89 Self::PluginInstall => "plugin_install",
90 Self::BackgroundTasksChanged => "background_tasks_changed",
91 Self::SessionStateChanged => "session_state_changed",
92 Self::WorkerShuttingDown => "worker_shutting_down",
93 Self::CommandsChanged => "commands_changed",
94 Self::Notification => "notification",
95 Self::FilesPersisted => "files_persisted",
96 Self::MemoryRecall => "memory_recall",
97 Self::ElicitationComplete => "elicitation_complete",
98 Self::PermissionDenied => "permission_denied",
99 Self::MirrorError => "mirror_error",
100 Self::Informational => "informational",
101 Self::CodeChangePublished => "code_change_published",
102 Self::VcsStateChanged => "vcs_state_changed",
103 Self::FeedbackDraftQueued => "feedback_draft_queued",
104 Self::CloudSessionDelta => "cloud_session_delta",
105 Self::DevIntent => "dev_intent",
106 Self::TurnHandoffAvailable => "turn_handoff_available",
107 Self::TurnPreempted => "turn_preempted",
108 Self::PeerMessageHold => "peer_message_hold",
109 Self::PerTurnEffortChanged => "per_turn_effort_changed",
110 Self::SessionTitleChanged => "session_title_changed",
111 Self::InstructionSizeWarning => "instruction_size_warning",
112 Self::FileAttachmentsMissing => "file_attachments_missing",
113 Self::PermissionCheckStatus => "permission_check_status",
114 Self::InheritedAsksSettled => "inherited_asks_settled",
115 Self::UiFocus => "ui_focus",
116 Self::UiInvalidate => "ui_invalidate",
117 Self::UiLog => "ui_log",
118 Self::UiPanes => "ui_panes",
119 Self::UiScroll => "ui_scroll",
120 Self::UiStatus => "ui_status",
121 Self::UiToast => "ui_toast",
122 Self::Unknown(s) => s.as_str(),
123 }
124 }
125}
126
127impl fmt::Display for SystemSubtype {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.write_str(self.as_str())
130 }
131}
132
133impl From<&str> for SystemSubtype {
134 fn from(s: &str) -> Self {
135 match s {
136 "init" => Self::Init,
137 "status" => Self::Status,
138 "compact_boundary" => Self::CompactBoundary,
139 "thinking_tokens" => Self::ThinkingTokens,
140 "task_started" => Self::TaskStarted,
141 "task_progress" => Self::TaskProgress,
142 "task_updated" => Self::TaskUpdated,
143 "task_notification" => Self::TaskNotification,
144 "api_retry" => Self::ApiRetry,
145 "control_request_progress" => Self::ControlRequestProgress,
146 "model_refusal_fallback" => Self::ModelRefusalFallback,
147 "model_refusal_no_fallback" => Self::ModelRefusalNoFallback,
148 "local_command_output" => Self::LocalCommandOutput,
149 "hook_started" => Self::HookStarted,
150 "hook_progress" => Self::HookProgress,
151 "hook_response" => Self::HookResponse,
152 "plugin_install" => Self::PluginInstall,
153 "background_tasks_changed" => Self::BackgroundTasksChanged,
154 "session_state_changed" => Self::SessionStateChanged,
155 "worker_shutting_down" => Self::WorkerShuttingDown,
156 "commands_changed" => Self::CommandsChanged,
157 "notification" => Self::Notification,
158 "files_persisted" => Self::FilesPersisted,
159 "memory_recall" => Self::MemoryRecall,
160 "elicitation_complete" => Self::ElicitationComplete,
161 "permission_denied" => Self::PermissionDenied,
162 "mirror_error" => Self::MirrorError,
163 "informational" => Self::Informational,
164 "code_change_published" => Self::CodeChangePublished,
165 "vcs_state_changed" => Self::VcsStateChanged,
166 "feedback_draft_queued" => Self::FeedbackDraftQueued,
167 "cloud_session_delta" => Self::CloudSessionDelta,
168 "dev_intent" => Self::DevIntent,
169 "turn_handoff_available" => Self::TurnHandoffAvailable,
170 "turn_preempted" => Self::TurnPreempted,
171 "peer_message_hold" => Self::PeerMessageHold,
172 "per_turn_effort_changed" => Self::PerTurnEffortChanged,
173 "session_title_changed" => Self::SessionTitleChanged,
174 "instruction_size_warning" => Self::InstructionSizeWarning,
175 "file_attachments_missing" => Self::FileAttachmentsMissing,
176 "permission_check_status" => Self::PermissionCheckStatus,
177 "inherited_asks_settled" => Self::InheritedAsksSettled,
178 "ui_focus" => Self::UiFocus,
179 "ui_invalidate" => Self::UiInvalidate,
180 "ui_log" => Self::UiLog,
181 "ui_panes" => Self::UiPanes,
182 "ui_scroll" => Self::UiScroll,
183 "ui_status" => Self::UiStatus,
184 "ui_toast" => Self::UiToast,
185 other => Self::Unknown(other.to_string()),
186 }
187 }
188}
189
190impl Serialize for SystemSubtype {
191 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
192 serializer.serialize_str(self.as_str())
193 }
194}
195
196impl<'de> Deserialize<'de> for SystemSubtype {
197 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
198 let s = String::deserialize(deserializer)?;
199 Ok(Self::from(s.as_str()))
200 }
201}
202
203#[derive(Debug, Clone, PartialEq, Eq, Hash)]
208pub enum MessageRole {
209 User,
210 Assistant,
211 Unknown(String),
213}
214
215impl MessageRole {
216 pub fn as_str(&self) -> &str {
217 match self {
218 Self::User => "user",
219 Self::Assistant => "assistant",
220 Self::Unknown(s) => s.as_str(),
221 }
222 }
223}
224
225impl fmt::Display for MessageRole {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 f.write_str(self.as_str())
228 }
229}
230
231impl From<&str> for MessageRole {
232 fn from(s: &str) -> Self {
233 match s {
234 "user" => Self::User,
235 "assistant" => Self::Assistant,
236 other => Self::Unknown(other.to_string()),
237 }
238 }
239}
240
241impl Serialize for MessageRole {
242 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
243 serializer.serialize_str(self.as_str())
244 }
245}
246
247impl<'de> Deserialize<'de> for MessageRole {
248 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
249 let s = String::deserialize(deserializer)?;
250 Ok(Self::from(s.as_str()))
251 }
252}
253
254#[derive(Debug, Clone, PartialEq, Eq, Hash)]
256pub enum CompactionTrigger {
257 Auto,
259 Manual,
261 Unknown(String),
263}
264
265impl CompactionTrigger {
266 pub fn as_str(&self) -> &str {
267 match self {
268 Self::Auto => "auto",
269 Self::Manual => "manual",
270 Self::Unknown(s) => s.as_str(),
271 }
272 }
273}
274
275impl fmt::Display for CompactionTrigger {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 f.write_str(self.as_str())
278 }
279}
280
281impl From<&str> for CompactionTrigger {
282 fn from(s: &str) -> Self {
283 match s {
284 "auto" => Self::Auto,
285 "manual" => Self::Manual,
286 other => Self::Unknown(other.to_string()),
287 }
288 }
289}
290
291impl Serialize for CompactionTrigger {
292 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
293 serializer.serialize_str(self.as_str())
294 }
295}
296
297impl<'de> Deserialize<'de> for CompactionTrigger {
298 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
299 let s = String::deserialize(deserializer)?;
300 Ok(Self::from(s.as_str()))
301 }
302}
303
304#[derive(Debug, Clone, PartialEq, Eq, Hash)]
306pub enum StopReason {
307 EndTurn,
309 MaxTokens,
311 ToolUse,
313 Unknown(String),
315}
316
317impl StopReason {
318 pub fn as_str(&self) -> &str {
319 match self {
320 Self::EndTurn => "end_turn",
321 Self::MaxTokens => "max_tokens",
322 Self::ToolUse => "tool_use",
323 Self::Unknown(s) => s.as_str(),
324 }
325 }
326}
327
328impl fmt::Display for StopReason {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 f.write_str(self.as_str())
331 }
332}
333
334impl From<&str> for StopReason {
335 fn from(s: &str) -> Self {
336 match s {
337 "end_turn" => Self::EndTurn,
338 "max_tokens" => Self::MaxTokens,
339 "tool_use" => Self::ToolUse,
340 other => Self::Unknown(other.to_string()),
341 }
342 }
343}
344
345impl Serialize for StopReason {
346 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
347 serializer.serialize_str(self.as_str())
348 }
349}
350
351impl<'de> Deserialize<'de> for StopReason {
352 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
353 let s = String::deserialize(deserializer)?;
354 Ok(Self::from(s.as_str()))
355 }
356}
357
358#[derive(Debug, Clone, PartialEq, Eq, Hash)]
360pub enum ApiKeySource {
361 None,
363 User,
364 Project,
365 Org,
366 Temporary,
367 Oauth,
368 Unknown(String),
370}
371
372impl ApiKeySource {
373 pub fn as_str(&self) -> &str {
374 match self {
375 Self::None => "none",
376 Self::User => "user",
377 Self::Project => "project",
378 Self::Org => "org",
379 Self::Temporary => "temporary",
380 Self::Oauth => "oauth",
381 Self::Unknown(s) => s.as_str(),
382 }
383 }
384}
385
386impl fmt::Display for ApiKeySource {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 f.write_str(self.as_str())
389 }
390}
391
392impl From<&str> for ApiKeySource {
393 fn from(s: &str) -> Self {
394 match s {
395 "none" => Self::None,
396 "user" => Self::User,
397 "project" => Self::Project,
398 "org" => Self::Org,
399 "temporary" => Self::Temporary,
400 "oauth" => Self::Oauth,
401 other => Self::Unknown(other.to_string()),
402 }
403 }
404}
405
406impl Serialize for ApiKeySource {
407 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
408 serializer.serialize_str(self.as_str())
409 }
410}
411
412impl<'de> Deserialize<'de> for ApiKeySource {
413 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
414 let s = String::deserialize(deserializer)?;
415 Ok(Self::from(s.as_str()))
416 }
417}
418
419#[derive(Debug, Clone, PartialEq, Eq, Hash)]
421pub enum OutputStyle {
422 Default,
424 Unknown(String),
426}
427
428impl OutputStyle {
429 pub fn as_str(&self) -> &str {
430 match self {
431 Self::Default => "default",
432 Self::Unknown(s) => s.as_str(),
433 }
434 }
435}
436
437impl fmt::Display for OutputStyle {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 f.write_str(self.as_str())
440 }
441}
442
443impl From<&str> for OutputStyle {
444 fn from(s: &str) -> Self {
445 match s {
446 "default" => Self::Default,
447 other => Self::Unknown(other.to_string()),
448 }
449 }
450}
451
452impl Serialize for OutputStyle {
453 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
454 serializer.serialize_str(self.as_str())
455 }
456}
457
458impl<'de> Deserialize<'de> for OutputStyle {
459 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
460 let s = String::deserialize(deserializer)?;
461 Ok(Self::from(s.as_str()))
462 }
463}
464
465#[derive(Debug, Clone, PartialEq, Eq, Hash)]
467pub enum InitPermissionMode {
468 Default,
470 AcceptEdits,
471 BypassPermissions,
472 Plan,
473 DontAsk,
474 Auto,
475 Unknown(String),
477}
478
479impl InitPermissionMode {
480 pub fn as_str(&self) -> &str {
481 match self {
482 Self::Default => "default",
483 Self::AcceptEdits => "acceptEdits",
484 Self::BypassPermissions => "bypassPermissions",
485 Self::Plan => "plan",
486 Self::DontAsk => "dontAsk",
487 Self::Auto => "auto",
488 Self::Unknown(s) => s.as_str(),
489 }
490 }
491}
492
493impl fmt::Display for InitPermissionMode {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 f.write_str(self.as_str())
496 }
497}
498
499impl From<&str> for InitPermissionMode {
500 fn from(s: &str) -> Self {
501 match s {
502 "default" => Self::Default,
503 "acceptEdits" => Self::AcceptEdits,
504 "bypassPermissions" => Self::BypassPermissions,
505 "plan" => Self::Plan,
506 "dontAsk" => Self::DontAsk,
507 "auto" => Self::Auto,
508 other => Self::Unknown(other.to_string()),
509 }
510 }
511}
512
513impl Serialize for InitPermissionMode {
514 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
515 serializer.serialize_str(self.as_str())
516 }
517}
518
519impl<'de> Deserialize<'de> for InitPermissionMode {
520 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
521 let s = String::deserialize(deserializer)?;
522 Ok(Self::from(s.as_str()))
523 }
524}
525
526#[derive(Debug, Clone, PartialEq, Eq, Hash)]
528pub enum StatusMessageStatus {
529 Compacting,
531 Requesting,
533 Unknown(String),
535}
536
537impl StatusMessageStatus {
538 pub fn as_str(&self) -> &str {
539 match self {
540 Self::Compacting => "compacting",
541 Self::Requesting => "requesting",
542 Self::Unknown(s) => s.as_str(),
543 }
544 }
545}
546
547impl fmt::Display for StatusMessageStatus {
548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549 f.write_str(self.as_str())
550 }
551}
552
553impl From<&str> for StatusMessageStatus {
554 fn from(s: &str) -> Self {
555 match s {
556 "compacting" => Self::Compacting,
557 "requesting" => Self::Requesting,
558 other => Self::Unknown(other.to_string()),
559 }
560 }
561}
562
563impl Serialize for StatusMessageStatus {
564 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
565 serializer.serialize_str(self.as_str())
566 }
567}
568
569impl<'de> Deserialize<'de> for StatusMessageStatus {
570 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
571 let s = String::deserialize(deserializer)?;
572 Ok(Self::from(s.as_str()))
573 }
574}
575
576pub(crate) fn serialize_optional_uuid<S>(
578 uuid: &Option<Uuid>,
579 serializer: S,
580) -> Result<S::Ok, S::Error>
581where
582 S: Serializer,
583{
584 match uuid {
585 Some(id) => serializer.serialize_str(&id.to_string()),
586 None => serializer.serialize_none(),
587 }
588}
589
590pub(crate) fn deserialize_optional_uuid<'de, D>(deserializer: D) -> Result<Option<Uuid>, D::Error>
592where
593 D: Deserializer<'de>,
594{
595 let opt_str: Option<String> = Option::deserialize(deserializer)?;
596 match opt_str {
597 Some(s) => Uuid::parse_str(&s)
598 .map(Some)
599 .map_err(serde::de::Error::custom),
600 None => Ok(None),
601 }
602}
603
604#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
607pub struct MessageOrigin {
608 pub kind: String,
609 #[serde(flatten)]
610 pub extra: serde_json::Map<String, Value>,
611}
612
613#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
615pub struct SummarizeMetadata {
616 pub messages_summarized: u64,
617 #[serde(default, skip_serializing_if = "Option::is_none")]
618 pub user_context: Option<String>,
619 #[serde(default, skip_serializing_if = "Option::is_none")]
620 pub direction: Option<String>,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
625pub struct McpMeta {
626 #[serde(default, skip_serializing_if = "Option::is_none", rename = "_meta")]
627 pub meta: Option<Value>,
628 #[serde(default, skip_serializing_if = "Option::is_none")]
629 pub structured_content: Option<Value>,
630 #[serde(default, skip_serializing_if = "Vec::is_empty")]
635 pub resource_links: Vec<ResourceLink>,
636}
637
638#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
642pub struct ResourceLink {
643 pub uri: String,
644 pub name: String,
645 #[serde(default, skip_serializing_if = "Option::is_none")]
646 pub title: Option<String>,
647 #[serde(default, skip_serializing_if = "Option::is_none")]
648 pub description: Option<String>,
649 #[serde(default, skip_serializing_if = "Option::is_none", rename = "mimeType")]
650 pub mime_type: Option<String>,
651 #[serde(default, skip_serializing_if = "Option::is_none")]
652 pub size: Option<u64>,
653 #[serde(default, skip_serializing_if = "Option::is_none")]
654 pub annotations: Option<Value>,
655}
656
657#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
659pub struct ToolResultMeta {
660 pub id: String,
662 #[serde(default, skip_serializing_if = "Option::is_none")]
669 pub non_execution_kind: Option<String>,
670 #[serde(default, skip_serializing_if = "Option::is_none")]
672 pub user_feedback: Option<String>,
673 #[serde(default, skip_serializing_if = "Option::is_none")]
678 pub remedy: Option<ToolResultRemedy>,
679 #[serde(default, skip_serializing_if = "Option::is_none")]
686 pub permission_decision: Option<ToolPermissionDecision>,
687}
688
689#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
693pub struct ToolPermissionDecision {
694 pub decision: PermissionDecisionOutcome,
696 pub source: String,
704 #[serde(default, skip_serializing_if = "Option::is_none")]
710 pub reason_type: Option<PermissionDecisionReasonType>,
711}
712
713#[derive(Debug, Clone, PartialEq, Eq, Hash)]
715pub enum PermissionDecisionOutcome {
716 Accept,
718 Reject,
720 Cancelled,
722 Unknown(String),
724}
725
726impl PermissionDecisionOutcome {
727 pub fn as_str(&self) -> &str {
728 match self {
729 Self::Accept => "accept",
730 Self::Reject => "reject",
731 Self::Cancelled => "cancelled",
732 Self::Unknown(s) => s.as_str(),
733 }
734 }
735}
736
737impl fmt::Display for PermissionDecisionOutcome {
738 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
739 f.write_str(self.as_str())
740 }
741}
742
743impl From<&str> for PermissionDecisionOutcome {
744 fn from(s: &str) -> Self {
745 match s {
746 "accept" => Self::Accept,
747 "reject" => Self::Reject,
748 "cancelled" => Self::Cancelled,
749 other => Self::Unknown(other.to_string()),
750 }
751 }
752}
753
754impl Serialize for PermissionDecisionOutcome {
755 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
756 serializer.serialize_str(self.as_str())
757 }
758}
759
760impl<'de> Deserialize<'de> for PermissionDecisionOutcome {
761 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
762 let s = String::deserialize(deserializer)?;
763 Ok(Self::from(s.as_str()))
764 }
765}
766
767#[derive(Debug, Clone, PartialEq, Eq, Hash)]
770pub enum PermissionDecisionReasonType {
771 Rule,
773 Mode,
775 SubcommandResults,
777 PermissionPromptTool,
779 Hook,
781 AsyncAgent,
783 SandboxOverride,
785 WorkingDir,
787 SafetyCheck,
789 Classifier,
791 Other,
793 Unknown(String),
795}
796
797impl PermissionDecisionReasonType {
798 pub fn as_str(&self) -> &str {
799 match self {
800 Self::Rule => "rule",
801 Self::Mode => "mode",
802 Self::SubcommandResults => "subcommandResults",
803 Self::PermissionPromptTool => "permissionPromptTool",
804 Self::Hook => "hook",
805 Self::AsyncAgent => "asyncAgent",
806 Self::SandboxOverride => "sandboxOverride",
807 Self::WorkingDir => "workingDir",
808 Self::SafetyCheck => "safetyCheck",
809 Self::Classifier => "classifier",
810 Self::Other => "other",
811 Self::Unknown(s) => s.as_str(),
812 }
813 }
814}
815
816impl fmt::Display for PermissionDecisionReasonType {
817 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
818 f.write_str(self.as_str())
819 }
820}
821
822impl From<&str> for PermissionDecisionReasonType {
823 fn from(s: &str) -> Self {
824 match s {
825 "rule" => Self::Rule,
826 "mode" => Self::Mode,
827 "subcommandResults" => Self::SubcommandResults,
828 "permissionPromptTool" => Self::PermissionPromptTool,
829 "hook" => Self::Hook,
830 "asyncAgent" => Self::AsyncAgent,
831 "sandboxOverride" => Self::SandboxOverride,
832 "workingDir" => Self::WorkingDir,
833 "safetyCheck" => Self::SafetyCheck,
834 "classifier" => Self::Classifier,
835 "other" => Self::Other,
836 other => Self::Unknown(other.to_string()),
837 }
838 }
839}
840
841impl Serialize for PermissionDecisionReasonType {
842 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
843 serializer.serialize_str(self.as_str())
844 }
845}
846
847impl<'de> Deserialize<'de> for PermissionDecisionReasonType {
848 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
849 let s = String::deserialize(deserializer)?;
850 Ok(Self::from(s.as_str()))
851 }
852}
853
854#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
859pub struct ToolResultRemedy {
860 pub kind: ToolResultRemedyKind,
863 #[serde(default, skip_serializing_if = "Option::is_none")]
867 pub servers: Option<Vec<String>>,
868 #[serde(default, skip_serializing_if = "Option::is_none")]
871 pub provider: Option<RemedyLoginProvider>,
872 #[serde(default, skip_serializing_if = "Option::is_none")]
876 pub path: Option<String>,
877 #[serde(default, skip_serializing_if = "Option::is_none")]
882 pub managed: Option<bool>,
883 #[serde(default, skip_serializing_if = "Option::is_none")]
885 pub feature: Option<RemedyFeature>,
886 #[serde(default, skip_serializing_if = "Option::is_none")]
888 pub cause: Option<RemedyFeatureCause>,
889 #[serde(default, skip_serializing_if = "Option::is_none")]
891 pub policy_kind: Option<RemedyPolicyKind>,
892}
893
894#[derive(Debug, Clone, PartialEq, Eq, Hash)]
896pub enum ToolResultRemedyKind {
897 McpNeedsAuth,
899 McpDisabled,
901 McpRequiredMissing,
903 AuthExpired,
905 AuthOverridden,
907 AuthMissingScope,
909 DesignNeedsAuthorization,
911 SandboxViolation,
913 OutsideReadsBlocked,
915 MemoryPaused,
917 FeatureDisabled,
919 PolicyDenied,
922 SpawnArgLimit,
924 StagedForReview,
926 Unknown(String),
928}
929
930impl ToolResultRemedyKind {
931 pub fn as_str(&self) -> &str {
932 match self {
933 Self::McpNeedsAuth => "mcp_needs_auth",
934 Self::McpDisabled => "mcp_disabled",
935 Self::McpRequiredMissing => "mcp_required_missing",
936 Self::AuthExpired => "auth_expired",
937 Self::AuthOverridden => "auth_overridden",
938 Self::AuthMissingScope => "auth_missing_scope",
939 Self::DesignNeedsAuthorization => "design_needs_authorization",
940 Self::SandboxViolation => "sandbox_violation",
941 Self::OutsideReadsBlocked => "outside_reads_blocked",
942 Self::MemoryPaused => "memory_paused",
943 Self::FeatureDisabled => "feature_disabled",
944 Self::PolicyDenied => "policy_denied",
945 Self::SpawnArgLimit => "spawn_arg_limit",
946 Self::StagedForReview => "staged_for_review",
947 Self::Unknown(s) => s.as_str(),
948 }
949 }
950}
951
952impl fmt::Display for ToolResultRemedyKind {
953 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954 f.write_str(self.as_str())
955 }
956}
957
958impl From<&str> for ToolResultRemedyKind {
959 fn from(s: &str) -> Self {
960 match s {
961 "mcp_needs_auth" => Self::McpNeedsAuth,
962 "mcp_disabled" => Self::McpDisabled,
963 "mcp_required_missing" => Self::McpRequiredMissing,
964 "auth_expired" => Self::AuthExpired,
965 "auth_overridden" => Self::AuthOverridden,
966 "auth_missing_scope" => Self::AuthMissingScope,
967 "design_needs_authorization" => Self::DesignNeedsAuthorization,
968 "sandbox_violation" => Self::SandboxViolation,
969 "outside_reads_blocked" => Self::OutsideReadsBlocked,
970 "memory_paused" => Self::MemoryPaused,
971 "feature_disabled" => Self::FeatureDisabled,
972 "policy_denied" => Self::PolicyDenied,
973 "spawn_arg_limit" => Self::SpawnArgLimit,
974 "staged_for_review" => Self::StagedForReview,
975 other => Self::Unknown(other.to_string()),
976 }
977 }
978}
979
980impl Serialize for ToolResultRemedyKind {
981 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
982 serializer.serialize_str(self.as_str())
983 }
984}
985
986impl<'de> Deserialize<'de> for ToolResultRemedyKind {
987 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
988 let s = String::deserialize(deserializer)?;
989 Ok(Self::from(s.as_str()))
990 }
991}
992
993#[derive(Debug, Clone, PartialEq, Eq, Hash)]
995pub enum RemedyLoginProvider {
996 ClaudeAi,
998 ClaudeDesign,
1000 Unknown(String),
1002}
1003
1004impl RemedyLoginProvider {
1005 pub fn as_str(&self) -> &str {
1006 match self {
1007 Self::ClaudeAi => "claude_ai",
1008 Self::ClaudeDesign => "claude_design",
1009 Self::Unknown(s) => s.as_str(),
1010 }
1011 }
1012}
1013
1014impl fmt::Display for RemedyLoginProvider {
1015 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1016 f.write_str(self.as_str())
1017 }
1018}
1019
1020impl From<&str> for RemedyLoginProvider {
1021 fn from(s: &str) -> Self {
1022 match s {
1023 "claude_ai" => Self::ClaudeAi,
1024 "claude_design" => Self::ClaudeDesign,
1025 other => Self::Unknown(other.to_string()),
1026 }
1027 }
1028}
1029
1030impl Serialize for RemedyLoginProvider {
1031 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1032 serializer.serialize_str(self.as_str())
1033 }
1034}
1035
1036impl<'de> Deserialize<'de> for RemedyLoginProvider {
1037 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1038 let s = String::deserialize(deserializer)?;
1039 Ok(Self::from(s.as_str()))
1040 }
1041}
1042
1043#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1046pub enum RemedyFeature {
1047 Workflows,
1048 Artifacts,
1049 Unknown(String),
1051}
1052
1053impl RemedyFeature {
1054 pub fn as_str(&self) -> &str {
1055 match self {
1056 Self::Workflows => "workflows",
1057 Self::Artifacts => "artifacts",
1058 Self::Unknown(s) => s.as_str(),
1059 }
1060 }
1061}
1062
1063impl fmt::Display for RemedyFeature {
1064 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1065 f.write_str(self.as_str())
1066 }
1067}
1068
1069impl From<&str> for RemedyFeature {
1070 fn from(s: &str) -> Self {
1071 match s {
1072 "workflows" => Self::Workflows,
1073 "artifacts" => Self::Artifacts,
1074 other => Self::Unknown(other.to_string()),
1075 }
1076 }
1077}
1078
1079impl Serialize for RemedyFeature {
1080 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1081 serializer.serialize_str(self.as_str())
1082 }
1083}
1084
1085impl<'de> Deserialize<'de> for RemedyFeature {
1086 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1087 let s = String::deserialize(deserializer)?;
1088 Ok(Self::from(s.as_str()))
1089 }
1090}
1091
1092#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1095pub enum RemedyFeatureCause {
1096 ManagedSettings,
1099 OrgPolicy,
1101 Unavailable,
1103 UserSetting,
1106 UnsupportedRuntime,
1110 Unknown(String),
1112}
1113
1114impl RemedyFeatureCause {
1115 pub fn as_str(&self) -> &str {
1116 match self {
1117 Self::ManagedSettings => "managed_settings",
1118 Self::OrgPolicy => "org_policy",
1119 Self::Unavailable => "unavailable",
1120 Self::UserSetting => "user_setting",
1121 Self::UnsupportedRuntime => "unsupported_runtime",
1122 Self::Unknown(s) => s.as_str(),
1123 }
1124 }
1125}
1126
1127impl fmt::Display for RemedyFeatureCause {
1128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1129 f.write_str(self.as_str())
1130 }
1131}
1132
1133impl From<&str> for RemedyFeatureCause {
1134 fn from(s: &str) -> Self {
1135 match s {
1136 "managed_settings" => Self::ManagedSettings,
1137 "org_policy" => Self::OrgPolicy,
1138 "unavailable" => Self::Unavailable,
1139 "user_setting" => Self::UserSetting,
1140 "unsupported_runtime" => Self::UnsupportedRuntime,
1141 other => Self::Unknown(other.to_string()),
1142 }
1143 }
1144}
1145
1146impl Serialize for RemedyFeatureCause {
1147 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1148 serializer.serialize_str(self.as_str())
1149 }
1150}
1151
1152impl<'de> Deserialize<'de> for RemedyFeatureCause {
1153 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1154 let s = String::deserialize(deserializer)?;
1155 Ok(Self::from(s.as_str()))
1156 }
1157}
1158
1159#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1162pub enum RemedyPolicyKind {
1163 OrgDenied,
1165 Latched,
1168 Unknown(String),
1170}
1171
1172impl RemedyPolicyKind {
1173 pub fn as_str(&self) -> &str {
1174 match self {
1175 Self::OrgDenied => "org_denied",
1176 Self::Latched => "latched",
1177 Self::Unknown(s) => s.as_str(),
1178 }
1179 }
1180}
1181
1182impl fmt::Display for RemedyPolicyKind {
1183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1184 f.write_str(self.as_str())
1185 }
1186}
1187
1188impl From<&str> for RemedyPolicyKind {
1189 fn from(s: &str) -> Self {
1190 match s {
1191 "org_denied" => Self::OrgDenied,
1192 "latched" => Self::Latched,
1193 other => Self::Unknown(other.to_string()),
1194 }
1195 }
1196}
1197
1198impl Serialize for RemedyPolicyKind {
1199 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1200 serializer.serialize_str(self.as_str())
1201 }
1202}
1203
1204impl<'de> Deserialize<'de> for RemedyPolicyKind {
1205 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1206 let s = String::deserialize(deserializer)?;
1207 Ok(Self::from(s.as_str()))
1208 }
1209}
1210
1211#[derive(Debug, Clone, Serialize, Deserialize)]
1213pub struct UserMessage {
1214 pub message: MessageContent,
1215 #[serde(skip_serializing_if = "Option::is_none", alias = "sessionId")]
1216 #[serde(
1217 serialize_with = "serialize_optional_uuid",
1218 deserialize_with = "deserialize_optional_uuid"
1219 )]
1220 pub session_id: Option<Uuid>,
1221 #[serde(skip_serializing_if = "Option::is_none")]
1223 pub parent_tool_use_id: Option<String>,
1224 #[serde(skip_serializing_if = "Option::is_none")]
1226 pub uuid: Option<String>,
1227 #[serde(skip_serializing_if = "Option::is_none")]
1229 pub timestamp: Option<String>,
1230 #[serde(skip_serializing_if = "Option::is_none")]
1237 pub tool_use_result: Option<serde_json::Value>,
1238 #[serde(default, skip_serializing_if = "Option::is_none")]
1243 pub agent_id: Option<String>,
1244 #[serde(skip_serializing_if = "Option::is_none")]
1247 pub subagent_type: Option<String>,
1248 #[serde(skip_serializing_if = "Option::is_none")]
1250 pub task_description: Option<String>,
1251 #[serde(skip_serializing_if = "Option::is_none")]
1252 pub origin: Option<MessageOrigin>,
1253 #[serde(skip_serializing_if = "Option::is_none")]
1254 pub priority: Option<String>,
1255 #[serde(skip_serializing_if = "Option::is_none", rename = "isSynthetic")]
1256 pub is_synthetic: Option<bool>,
1257 #[serde(skip_serializing_if = "Option::is_none", rename = "shouldQuery")]
1258 pub should_query: Option<bool>,
1259 #[serde(default, skip_serializing_if = "Option::is_none")]
1260 pub is_meta: Option<bool>,
1261 #[serde(default, skip_serializing_if = "Option::is_none")]
1262 pub is_visible_in_transcript_only: Option<bool>,
1263 #[serde(default, skip_serializing_if = "Option::is_none")]
1264 pub is_virtual: Option<bool>,
1265 #[serde(default, skip_serializing_if = "Option::is_none")]
1266 pub is_compact_summary: Option<bool>,
1267 #[serde(skip_serializing_if = "Option::is_none")]
1268 pub summarize_metadata: Option<SummarizeMetadata>,
1269 #[serde(skip_serializing_if = "Option::is_none")]
1270 pub mcp_meta: Option<McpMeta>,
1271 #[serde(skip_serializing_if = "Option::is_none")]
1274 pub tool_result_meta: Option<Vec<ToolResultMeta>>,
1275 #[serde(skip_serializing_if = "Option::is_none")]
1276 pub source_tool_use_id: Option<String>,
1277 #[serde(skip_serializing_if = "Option::is_none")]
1278 pub source_tool_assistant_uuid: Option<String>,
1279 #[serde(skip_serializing_if = "Option::is_none")]
1280 pub image_paste_ids: Option<Vec<u64>>,
1281 #[serde(skip_serializing_if = "Option::is_none")]
1282 pub client_platform: Option<String>,
1283 #[serde(skip_serializing_if = "Option::is_none")]
1284 pub inbound_origin: Option<String>,
1285 #[serde(skip_serializing_if = "Option::is_none", rename = "isReplay")]
1286 pub is_replay: Option<bool>,
1287 #[serde(skip_serializing_if = "Option::is_none")]
1288 pub file_attachments: Option<Vec<Value>>,
1289 #[serde(default, skip_serializing_if = "Option::is_none")]
1291 pub seeded_summon: Option<bool>,
1292 #[serde(default, skip_serializing_if = "Option::is_none")]
1295 pub client_composed: Option<bool>,
1296 #[serde(default, skip_serializing_if = "Option::is_none")]
1305 pub text_not_typed: Option<bool>,
1306 #[serde(default, skip_serializing_if = "Option::is_none")]
1311 pub initiator: Option<String>,
1312 #[serde(default, skip_serializing_if = "Option::is_none")]
1318 pub pasted_content: Option<Vec<Value>>,
1319 #[serde(default, skip_serializing_if = "Option::is_none")]
1323 pub historical: Option<bool>,
1324 #[serde(default, skip_serializing_if = "Option::is_none")]
1330 pub server_received_wall_ms: Option<f64>,
1331 #[serde(default, skip_serializing_if = "Option::is_none")]
1335 pub refused_message_id: Option<String>,
1336 #[serde(default, skip_serializing_if = "Option::is_none")]
1343 pub requeue_if_interrupted_within_ms: Option<u64>,
1344}
1345
1346impl UserMessage {
1347 pub fn tool_use_result_as<T: serde::de::DeserializeOwned>(
1355 &self,
1356 ) -> Option<Result<T, serde_json::Error>> {
1357 self.tool_use_result
1358 .as_ref()
1359 .map(|v| serde_json::from_value(v.clone()))
1360 }
1361
1362 pub fn subagent_result(&self) -> Option<SubagentResult> {
1373 self.tool_use_result
1374 .as_ref()
1375 .and_then(|v| serde_json::from_value(v.clone()).ok())
1376 }
1377}
1378
1379#[derive(Debug, Clone, Serialize, Deserialize)]
1390pub struct SubagentResult {
1391 #[serde(skip_serializing_if = "Option::is_none")]
1393 pub status: Option<String>,
1394 #[serde(skip_serializing_if = "Option::is_none")]
1396 pub prompt: Option<String>,
1397 #[serde(rename = "agentId", skip_serializing_if = "Option::is_none")]
1399 pub agent_id: Option<String>,
1400 #[serde(rename = "agentType", skip_serializing_if = "Option::is_none")]
1402 pub agent_type: Option<String>,
1403 #[serde(
1405 default,
1406 deserialize_with = "deserialize_content_blocks",
1407 skip_serializing_if = "Vec::is_empty"
1408 )]
1409 pub content: Vec<ContentBlock>,
1410 #[serde(rename = "resolvedModel", skip_serializing_if = "Option::is_none")]
1412 pub resolved_model: Option<String>,
1413 #[serde(rename = "totalDurationMs", skip_serializing_if = "Option::is_none")]
1415 pub total_duration_ms: Option<u64>,
1416 #[serde(rename = "totalTokens", skip_serializing_if = "Option::is_none")]
1418 pub total_tokens: Option<u64>,
1419 #[serde(rename = "totalToolUseCount", skip_serializing_if = "Option::is_none")]
1421 pub total_tool_use_count: Option<u64>,
1422 #[serde(skip_serializing_if = "Option::is_none")]
1424 pub usage: Option<super::result::UsageInfo>,
1425 #[serde(rename = "toolStats", skip_serializing_if = "Option::is_none")]
1427 pub tool_stats: Option<SubagentToolStats>,
1428}
1429
1430#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1435#[serde(rename_all = "camelCase")]
1436pub struct SubagentToolStats {
1437 #[serde(default)]
1438 pub read_count: u64,
1439 #[serde(default)]
1440 pub search_count: u64,
1441 #[serde(default)]
1442 pub bash_count: u64,
1443 #[serde(default)]
1444 pub edit_file_count: u64,
1445 #[serde(default)]
1446 pub lines_added: u64,
1447 #[serde(default)]
1448 pub lines_removed: u64,
1449 #[serde(default)]
1450 pub other_tool_count: u64,
1451 #[serde(flatten)]
1452 pub extra: serde_json::Map<String, Value>,
1453}
1454
1455#[derive(Debug, Clone, Default, PartialEq, Eq)]
1482pub struct SubagentUsageRollup {
1483 pub subagent_tokens: u64,
1486 pub agent_count: u32,
1488 pub tool_uses: u64,
1490 pub duration_ms: u64,
1492 seen_agent_ids: std::collections::BTreeSet<String>,
1493}
1494
1495impl SubagentUsageRollup {
1496 pub fn observe(&mut self, output: &ClaudeOutput) -> bool {
1502 match output {
1503 ClaudeOutput::User(user) => self.observe_user(user),
1504 _ => false,
1505 }
1506 }
1507
1508 pub fn observe_user(&mut self, user: &UserMessage) -> bool {
1515 let Some(result) = user.subagent_result() else {
1516 return false;
1517 };
1518 if result.total_tokens.is_none() && result.agent_id.is_none() {
1519 return false;
1520 }
1521 if let Some(agent_id) = &result.agent_id {
1522 if !self.seen_agent_ids.insert(agent_id.clone()) {
1523 return false;
1524 }
1525 }
1526 self.agent_count += 1;
1527 self.subagent_tokens += result.total_tokens.unwrap_or(0);
1528 self.tool_uses += result.total_tool_use_count.unwrap_or(0);
1529 self.duration_ms += result.total_duration_ms.unwrap_or(0);
1530 true
1531 }
1532}
1533
1534#[derive(Debug, Clone, Serialize, Deserialize)]
1536pub struct MessageContent {
1537 pub role: MessageRole,
1538 #[serde(deserialize_with = "deserialize_content_blocks")]
1539 pub content: Vec<ContentBlock>,
1540}
1541
1542#[derive(Debug, Clone, Serialize, Deserialize)]
1544pub struct SystemMessage {
1545 pub subtype: SystemSubtype,
1546 #[serde(flatten)]
1547 pub data: Value, }
1549
1550impl SystemMessage {
1551 pub fn is_init(&self) -> bool {
1553 self.subtype == SystemSubtype::Init
1554 }
1555
1556 pub fn is_status(&self) -> bool {
1558 self.subtype == SystemSubtype::Status
1559 }
1560
1561 pub fn is_compact_boundary(&self) -> bool {
1563 self.subtype == SystemSubtype::CompactBoundary
1564 }
1565
1566 pub fn as_init(&self) -> Option<InitMessage> {
1568 if self.subtype != SystemSubtype::Init {
1569 return None;
1570 }
1571 serde_json::from_value(self.data.clone()).ok()
1572 }
1573
1574 pub fn as_status(&self) -> Option<StatusMessage> {
1576 if self.subtype != SystemSubtype::Status {
1577 return None;
1578 }
1579 serde_json::from_value(self.data.clone()).ok()
1580 }
1581
1582 pub fn as_compact_boundary(&self) -> Option<CompactBoundaryMessage> {
1584 if self.subtype != SystemSubtype::CompactBoundary {
1585 return None;
1586 }
1587 serde_json::from_value(self.data.clone()).ok()
1588 }
1589
1590 pub fn is_task_started(&self) -> bool {
1592 self.subtype == SystemSubtype::TaskStarted
1593 }
1594
1595 pub fn is_task_progress(&self) -> bool {
1597 self.subtype == SystemSubtype::TaskProgress
1598 }
1599
1600 pub fn is_task_notification(&self) -> bool {
1602 self.subtype == SystemSubtype::TaskNotification
1603 }
1604
1605 pub fn as_task_started(&self) -> Option<TaskStartedMessage> {
1607 if self.subtype != SystemSubtype::TaskStarted {
1608 return None;
1609 }
1610 serde_json::from_value(self.data.clone()).ok()
1611 }
1612
1613 pub fn as_task_progress(&self) -> Option<TaskProgressMessage> {
1615 if self.subtype != SystemSubtype::TaskProgress {
1616 return None;
1617 }
1618 serde_json::from_value(self.data.clone()).ok()
1619 }
1620
1621 pub fn as_task_notification(&self) -> Option<TaskNotificationMessage> {
1623 if self.subtype != SystemSubtype::TaskNotification {
1624 return None;
1625 }
1626 serde_json::from_value(self.data.clone()).ok()
1627 }
1628
1629 pub fn is_task_updated(&self) -> bool {
1631 self.subtype == SystemSubtype::TaskUpdated
1632 }
1633
1634 pub fn as_task_updated(&self) -> Option<TaskUpdatedMessage> {
1636 if self.subtype != SystemSubtype::TaskUpdated {
1637 return None;
1638 }
1639 serde_json::from_value(self.data.clone()).ok()
1640 }
1641
1642 pub fn is_thinking_tokens(&self) -> bool {
1644 self.subtype == SystemSubtype::ThinkingTokens
1645 }
1646
1647 pub fn as_thinking_tokens(&self) -> Option<ThinkingTokensMessage> {
1649 if self.subtype != SystemSubtype::ThinkingTokens {
1650 return None;
1651 }
1652 serde_json::from_value(self.data.clone()).ok()
1653 }
1654
1655 pub fn is_code_change_published(&self) -> bool {
1657 self.subtype == SystemSubtype::CodeChangePublished
1658 }
1659
1660 pub fn as_code_change_published(&self) -> Option<CodeChangePublishedMessage> {
1662 if self.subtype != SystemSubtype::CodeChangePublished {
1663 return None;
1664 }
1665 serde_json::from_value(self.data.clone()).ok()
1666 }
1667
1668 pub fn is_vcs_state_changed(&self) -> bool {
1670 self.subtype == SystemSubtype::VcsStateChanged
1671 }
1672
1673 pub fn as_vcs_state_changed(&self) -> Option<VcsStateChangedMessage> {
1675 if self.subtype != SystemSubtype::VcsStateChanged {
1676 return None;
1677 }
1678 serde_json::from_value(self.data.clone()).ok()
1679 }
1680
1681 pub fn is_feedback_draft_queued(&self) -> bool {
1683 self.subtype == SystemSubtype::FeedbackDraftQueued
1684 }
1685
1686 pub fn as_feedback_draft_queued(&self) -> Option<FeedbackDraftQueuedMessage> {
1688 if self.subtype != SystemSubtype::FeedbackDraftQueued {
1689 return None;
1690 }
1691 serde_json::from_value(self.data.clone()).ok()
1692 }
1693
1694 pub fn is_cloud_session_delta(&self) -> bool {
1696 self.subtype == SystemSubtype::CloudSessionDelta
1697 }
1698
1699 pub fn as_cloud_session_delta(&self) -> Option<CloudSessionDeltaMessage> {
1701 if self.subtype != SystemSubtype::CloudSessionDelta {
1702 return None;
1703 }
1704 serde_json::from_value(self.data.clone()).ok()
1705 }
1706
1707 pub fn is_dev_intent(&self) -> bool {
1709 self.subtype == SystemSubtype::DevIntent
1710 }
1711
1712 pub fn as_dev_intent(&self) -> Option<DevIntentMessage> {
1714 if self.subtype != SystemSubtype::DevIntent {
1715 return None;
1716 }
1717 serde_json::from_value(self.data.clone()).ok()
1718 }
1719
1720 pub fn is_turn_handoff_available(&self) -> bool {
1722 self.subtype == SystemSubtype::TurnHandoffAvailable
1723 }
1724
1725 pub fn as_turn_handoff_available(&self) -> Option<TurnHandoffAvailableMessage> {
1727 if self.subtype != SystemSubtype::TurnHandoffAvailable {
1728 return None;
1729 }
1730 serde_json::from_value(self.data.clone()).ok()
1731 }
1732
1733 pub fn is_turn_preempted(&self) -> bool {
1735 self.subtype == SystemSubtype::TurnPreempted
1736 }
1737
1738 pub fn as_turn_preempted(&self) -> Option<TurnPreemptedMessage> {
1740 if self.subtype != SystemSubtype::TurnPreempted {
1741 return None;
1742 }
1743 serde_json::from_value(self.data.clone()).ok()
1744 }
1745
1746 pub fn is_peer_message_hold(&self) -> bool {
1748 self.subtype == SystemSubtype::PeerMessageHold
1749 }
1750
1751 pub fn as_peer_message_hold(&self) -> Option<PeerMessageHoldMessage> {
1753 if self.subtype != SystemSubtype::PeerMessageHold {
1754 return None;
1755 }
1756 serde_json::from_value(self.data.clone()).ok()
1757 }
1758
1759 pub fn is_per_turn_effort_changed(&self) -> bool {
1761 self.subtype == SystemSubtype::PerTurnEffortChanged
1762 }
1763
1764 pub fn as_per_turn_effort_changed(&self) -> Option<PerTurnEffortChangedMessage> {
1766 if self.subtype != SystemSubtype::PerTurnEffortChanged {
1767 return None;
1768 }
1769 serde_json::from_value(self.data.clone()).ok()
1770 }
1771
1772 pub fn is_session_title_changed(&self) -> bool {
1774 self.subtype == SystemSubtype::SessionTitleChanged
1775 }
1776
1777 pub fn as_session_title_changed(&self) -> Option<SessionTitleChangedMessage> {
1779 if self.subtype != SystemSubtype::SessionTitleChanged {
1780 return None;
1781 }
1782 serde_json::from_value(self.data.clone()).ok()
1783 }
1784
1785 pub fn is_instruction_size_warning(&self) -> bool {
1787 self.subtype == SystemSubtype::InstructionSizeWarning
1788 }
1789
1790 pub fn as_instruction_size_warning(&self) -> Option<InstructionSizeWarningMessage> {
1792 if self.subtype != SystemSubtype::InstructionSizeWarning {
1793 return None;
1794 }
1795 serde_json::from_value(self.data.clone()).ok()
1796 }
1797
1798 pub fn is_permission_check_status(&self) -> bool {
1800 self.subtype == SystemSubtype::PermissionCheckStatus
1801 }
1802
1803 pub fn as_permission_check_status(&self) -> Option<PermissionCheckStatusMessage> {
1805 if self.subtype != SystemSubtype::PermissionCheckStatus {
1806 return None;
1807 }
1808 serde_json::from_value(self.data.clone()).ok()
1809 }
1810
1811 pub fn is_inherited_asks_settled(&self) -> bool {
1813 self.subtype == SystemSubtype::InheritedAsksSettled
1814 }
1815
1816 pub fn as_inherited_asks_settled(&self) -> Option<InheritedAsksSettledMessage> {
1818 if self.subtype != SystemSubtype::InheritedAsksSettled {
1819 return None;
1820 }
1821 serde_json::from_value(self.data.clone()).ok()
1822 }
1823
1824 pub fn is_file_attachments_missing(&self) -> bool {
1826 self.subtype == SystemSubtype::FileAttachmentsMissing
1827 }
1828
1829 pub fn as_file_attachments_missing(&self) -> Option<FileAttachmentsMissingMessage> {
1831 if self.subtype != SystemSubtype::FileAttachmentsMissing {
1832 return None;
1833 }
1834 serde_json::from_value(self.data.clone()).ok()
1835 }
1836
1837 pub fn is_ui_focus(&self) -> bool {
1839 self.subtype == SystemSubtype::UiFocus
1840 }
1841
1842 pub fn as_ui_focus(&self) -> Option<UiFocusMessage> {
1844 if self.subtype != SystemSubtype::UiFocus {
1845 return None;
1846 }
1847 serde_json::from_value(self.data.clone()).ok()
1848 }
1849
1850 pub fn is_ui_invalidate(&self) -> bool {
1852 self.subtype == SystemSubtype::UiInvalidate
1853 }
1854
1855 pub fn as_ui_invalidate(&self) -> Option<UiInvalidateMessage> {
1857 if self.subtype != SystemSubtype::UiInvalidate {
1858 return None;
1859 }
1860 serde_json::from_value(self.data.clone()).ok()
1861 }
1862
1863 pub fn is_ui_log(&self) -> bool {
1865 self.subtype == SystemSubtype::UiLog
1866 }
1867
1868 pub fn as_ui_log(&self) -> Option<UiLogMessage> {
1870 if self.subtype != SystemSubtype::UiLog {
1871 return None;
1872 }
1873 serde_json::from_value(self.data.clone()).ok()
1874 }
1875
1876 pub fn is_ui_panes(&self) -> bool {
1878 self.subtype == SystemSubtype::UiPanes
1879 }
1880
1881 pub fn as_ui_panes(&self) -> Option<UiPanesMessage> {
1883 if self.subtype != SystemSubtype::UiPanes {
1884 return None;
1885 }
1886 serde_json::from_value(self.data.clone()).ok()
1887 }
1888
1889 pub fn is_ui_scroll(&self) -> bool {
1891 self.subtype == SystemSubtype::UiScroll
1892 }
1893
1894 pub fn as_ui_scroll(&self) -> Option<UiScrollMessage> {
1896 if self.subtype != SystemSubtype::UiScroll {
1897 return None;
1898 }
1899 serde_json::from_value(self.data.clone()).ok()
1900 }
1901
1902 pub fn is_ui_status(&self) -> bool {
1904 self.subtype == SystemSubtype::UiStatus
1905 }
1906
1907 pub fn as_ui_status(&self) -> Option<UiStatusMessage> {
1909 if self.subtype != SystemSubtype::UiStatus {
1910 return None;
1911 }
1912 serde_json::from_value(self.data.clone()).ok()
1913 }
1914
1915 pub fn is_ui_toast(&self) -> bool {
1917 self.subtype == SystemSubtype::UiToast
1918 }
1919
1920 pub fn as_ui_toast(&self) -> Option<UiToastMessage> {
1922 if self.subtype != SystemSubtype::UiToast {
1923 return None;
1924 }
1925 serde_json::from_value(self.data.clone()).ok()
1926 }
1927
1928 pub fn as_known_system_event(&self) -> Option<KnownSystemEvent> {
1930 macro_rules! parse {
1931 ($variant:ident, $ty:ty) => {
1932 serde_json::from_value::<$ty>(self.data.clone())
1933 .ok()
1934 .map(KnownSystemEvent::$variant)
1935 };
1936 }
1937
1938 match self.subtype {
1939 SystemSubtype::Init => parse!(Init, InitMessage),
1940 SystemSubtype::Status => parse!(Status, StatusMessage),
1941 SystemSubtype::CompactBoundary => parse!(CompactBoundary, CompactBoundaryMessage),
1942 SystemSubtype::ThinkingTokens => parse!(ThinkingTokens, ThinkingTokensMessage),
1943 SystemSubtype::TaskStarted => parse!(TaskStarted, TaskStartedMessage),
1944 SystemSubtype::TaskProgress => parse!(TaskProgress, TaskProgressMessage),
1945 SystemSubtype::TaskUpdated => parse!(TaskUpdated, TaskUpdatedMessage),
1946 SystemSubtype::TaskNotification => parse!(TaskNotification, TaskNotificationMessage),
1947 SystemSubtype::ApiRetry => parse!(ApiRetry, ApiRetryMessage),
1948 SystemSubtype::ControlRequestProgress => {
1949 parse!(ControlRequestProgress, ControlRequestProgressMessage)
1950 }
1951 SystemSubtype::ModelRefusalFallback => {
1952 parse!(ModelRefusalFallback, ModelRefusalFallbackMessage)
1953 }
1954 SystemSubtype::ModelRefusalNoFallback => {
1955 parse!(ModelRefusalNoFallback, ModelRefusalNoFallbackMessage)
1956 }
1957 SystemSubtype::LocalCommandOutput => {
1958 parse!(LocalCommandOutput, LocalCommandOutputMessage)
1959 }
1960 SystemSubtype::HookStarted => parse!(HookStarted, HookStartedMessage),
1961 SystemSubtype::HookProgress => parse!(HookProgress, HookProgressMessage),
1962 SystemSubtype::HookResponse => parse!(HookResponse, HookResponseMessage),
1963 SystemSubtype::PluginInstall => parse!(PluginInstall, PluginInstallMessage),
1964 SystemSubtype::BackgroundTasksChanged => {
1965 parse!(BackgroundTasksChanged, BackgroundTasksChangedMessage)
1966 }
1967 SystemSubtype::SessionStateChanged => {
1968 parse!(SessionStateChanged, SessionStateChangedMessage)
1969 }
1970 SystemSubtype::WorkerShuttingDown => {
1971 parse!(WorkerShuttingDown, WorkerShuttingDownMessage)
1972 }
1973 SystemSubtype::CommandsChanged => parse!(CommandsChanged, CommandsChangedMessage),
1974 SystemSubtype::Notification => parse!(Notification, NotificationMessage),
1975 SystemSubtype::FilesPersisted => parse!(FilesPersisted, FilesPersistedMessage),
1976 SystemSubtype::MemoryRecall => parse!(MemoryRecall, MemoryRecallMessage),
1977 SystemSubtype::ElicitationComplete => {
1978 parse!(ElicitationComplete, ElicitationCompleteMessage)
1979 }
1980 SystemSubtype::PermissionDenied => parse!(PermissionDenied, PermissionDeniedMessage),
1981 SystemSubtype::MirrorError => parse!(MirrorError, MirrorErrorMessage),
1982 SystemSubtype::Informational => parse!(Informational, InformationalMessage),
1983 SystemSubtype::CodeChangePublished => {
1984 parse!(CodeChangePublished, CodeChangePublishedMessage)
1985 }
1986 SystemSubtype::VcsStateChanged => parse!(VcsStateChanged, VcsStateChangedMessage),
1987 SystemSubtype::FeedbackDraftQueued => {
1988 parse!(FeedbackDraftQueued, FeedbackDraftQueuedMessage)
1989 }
1990 SystemSubtype::CloudSessionDelta => {
1991 parse!(CloudSessionDelta, CloudSessionDeltaMessage)
1992 }
1993 SystemSubtype::DevIntent => parse!(DevIntent, DevIntentMessage),
1994 SystemSubtype::TurnHandoffAvailable => {
1995 parse!(TurnHandoffAvailable, TurnHandoffAvailableMessage)
1996 }
1997 SystemSubtype::TurnPreempted => parse!(TurnPreempted, TurnPreemptedMessage),
1998 SystemSubtype::PeerMessageHold => parse!(PeerMessageHold, PeerMessageHoldMessage),
1999 SystemSubtype::PerTurnEffortChanged => {
2000 parse!(PerTurnEffortChanged, PerTurnEffortChangedMessage)
2001 }
2002 SystemSubtype::SessionTitleChanged => {
2003 parse!(SessionTitleChanged, SessionTitleChangedMessage)
2004 }
2005 SystemSubtype::InstructionSizeWarning => {
2006 parse!(InstructionSizeWarning, InstructionSizeWarningMessage)
2007 }
2008 SystemSubtype::FileAttachmentsMissing => {
2009 parse!(FileAttachmentsMissing, FileAttachmentsMissingMessage)
2010 }
2011 SystemSubtype::PermissionCheckStatus => {
2012 parse!(PermissionCheckStatus, PermissionCheckStatusMessage)
2013 }
2014 SystemSubtype::InheritedAsksSettled => {
2015 parse!(InheritedAsksSettled, InheritedAsksSettledMessage)
2016 }
2017 SystemSubtype::UiFocus => parse!(UiFocus, UiFocusMessage),
2018 SystemSubtype::UiInvalidate => parse!(UiInvalidate, UiInvalidateMessage),
2019 SystemSubtype::UiLog => parse!(UiLog, UiLogMessage),
2020 SystemSubtype::UiPanes => parse!(UiPanes, UiPanesMessage),
2021 SystemSubtype::UiScroll => parse!(UiScroll, UiScrollMessage),
2022 SystemSubtype::UiStatus => parse!(UiStatus, UiStatusMessage),
2023 SystemSubtype::UiToast => parse!(UiToast, UiToastMessage),
2024 SystemSubtype::Unknown(_) => None,
2025 }
2026 }
2027
2028 pub fn typed_value(&self) -> Option<Value> {
2037 fn reserialize<T: Serialize>(parsed: Option<T>) -> Option<Value> {
2038 parsed.and_then(|v| serde_json::to_value(v).ok())
2039 }
2040 match self.subtype {
2041 SystemSubtype::Init => reserialize(self.as_init()),
2042 SystemSubtype::Status => reserialize(self.as_status()),
2043 SystemSubtype::CompactBoundary => reserialize(self.as_compact_boundary()),
2044 SystemSubtype::ThinkingTokens => reserialize(self.as_thinking_tokens()),
2045 SystemSubtype::TaskStarted => reserialize(self.as_task_started()),
2046 SystemSubtype::TaskProgress => reserialize(self.as_task_progress()),
2047 SystemSubtype::TaskUpdated => reserialize(self.as_task_updated()),
2048 SystemSubtype::TaskNotification => reserialize(self.as_task_notification()),
2049 SystemSubtype::ApiRetry => reserialize(parse_system::<ApiRetryMessage>(self)),
2050 SystemSubtype::ControlRequestProgress => {
2051 reserialize(parse_system::<ControlRequestProgressMessage>(self))
2052 }
2053 SystemSubtype::ModelRefusalFallback => {
2054 reserialize(parse_system::<ModelRefusalFallbackMessage>(self))
2055 }
2056 SystemSubtype::ModelRefusalNoFallback => {
2057 reserialize(parse_system::<ModelRefusalNoFallbackMessage>(self))
2058 }
2059 SystemSubtype::LocalCommandOutput => {
2060 reserialize(parse_system::<LocalCommandOutputMessage>(self))
2061 }
2062 SystemSubtype::HookStarted => reserialize(parse_system::<HookStartedMessage>(self)),
2063 SystemSubtype::HookProgress => reserialize(parse_system::<HookProgressMessage>(self)),
2064 SystemSubtype::HookResponse => reserialize(parse_system::<HookResponseMessage>(self)),
2065 SystemSubtype::PluginInstall => reserialize(parse_system::<PluginInstallMessage>(self)),
2066 SystemSubtype::BackgroundTasksChanged => {
2067 reserialize(parse_system::<BackgroundTasksChangedMessage>(self))
2068 }
2069 SystemSubtype::SessionStateChanged => {
2070 reserialize(parse_system::<SessionStateChangedMessage>(self))
2071 }
2072 SystemSubtype::WorkerShuttingDown => {
2073 reserialize(parse_system::<WorkerShuttingDownMessage>(self))
2074 }
2075 SystemSubtype::CommandsChanged => {
2076 reserialize(parse_system::<CommandsChangedMessage>(self))
2077 }
2078 SystemSubtype::Notification => reserialize(parse_system::<NotificationMessage>(self)),
2079 SystemSubtype::FilesPersisted => {
2080 reserialize(parse_system::<FilesPersistedMessage>(self))
2081 }
2082 SystemSubtype::MemoryRecall => reserialize(parse_system::<MemoryRecallMessage>(self)),
2083 SystemSubtype::ElicitationComplete => {
2084 reserialize(parse_system::<ElicitationCompleteMessage>(self))
2085 }
2086 SystemSubtype::PermissionDenied => {
2087 reserialize(parse_system::<PermissionDeniedMessage>(self))
2088 }
2089 SystemSubtype::MirrorError => reserialize(parse_system::<MirrorErrorMessage>(self)),
2090 SystemSubtype::Informational => reserialize(parse_system::<InformationalMessage>(self)),
2091 SystemSubtype::CodeChangePublished => {
2092 reserialize(parse_system::<CodeChangePublishedMessage>(self))
2093 }
2094 SystemSubtype::VcsStateChanged => {
2095 reserialize(parse_system::<VcsStateChangedMessage>(self))
2096 }
2097 SystemSubtype::FeedbackDraftQueued => {
2098 reserialize(parse_system::<FeedbackDraftQueuedMessage>(self))
2099 }
2100 SystemSubtype::CloudSessionDelta => {
2101 reserialize(parse_system::<CloudSessionDeltaMessage>(self))
2102 }
2103 SystemSubtype::DevIntent => reserialize(parse_system::<DevIntentMessage>(self)),
2104 SystemSubtype::TurnHandoffAvailable => {
2105 reserialize(parse_system::<TurnHandoffAvailableMessage>(self))
2106 }
2107 SystemSubtype::TurnPreempted => reserialize(parse_system::<TurnPreemptedMessage>(self)),
2108 SystemSubtype::PeerMessageHold => {
2109 reserialize(parse_system::<PeerMessageHoldMessage>(self))
2110 }
2111 SystemSubtype::PerTurnEffortChanged => {
2112 reserialize(parse_system::<PerTurnEffortChangedMessage>(self))
2113 }
2114 SystemSubtype::SessionTitleChanged => {
2115 reserialize(parse_system::<SessionTitleChangedMessage>(self))
2116 }
2117 SystemSubtype::InstructionSizeWarning => {
2118 reserialize(parse_system::<InstructionSizeWarningMessage>(self))
2119 }
2120 SystemSubtype::FileAttachmentsMissing => {
2121 reserialize(parse_system::<FileAttachmentsMissingMessage>(self))
2122 }
2123 SystemSubtype::PermissionCheckStatus => {
2124 reserialize(parse_system::<PermissionCheckStatusMessage>(self))
2125 }
2126 SystemSubtype::InheritedAsksSettled => {
2127 reserialize(parse_system::<InheritedAsksSettledMessage>(self))
2128 }
2129 SystemSubtype::UiFocus => reserialize(parse_system::<UiFocusMessage>(self)),
2130 SystemSubtype::UiInvalidate => reserialize(parse_system::<UiInvalidateMessage>(self)),
2131 SystemSubtype::UiLog => reserialize(parse_system::<UiLogMessage>(self)),
2132 SystemSubtype::UiPanes => reserialize(parse_system::<UiPanesMessage>(self)),
2133 SystemSubtype::UiScroll => reserialize(parse_system::<UiScrollMessage>(self)),
2134 SystemSubtype::UiStatus => reserialize(parse_system::<UiStatusMessage>(self)),
2135 SystemSubtype::UiToast => reserialize(parse_system::<UiToastMessage>(self)),
2136 SystemSubtype::Unknown(_) => None,
2137 }
2138 }
2139}
2140
2141fn parse_system<T: serde::de::DeserializeOwned>(message: &SystemMessage) -> Option<T> {
2142 serde_json::from_value(message.data.clone()).ok()
2143}
2144
2145#[allow(clippy::large_enum_variant)]
2151#[derive(Debug, Clone, Serialize, Deserialize)]
2152pub enum KnownSystemEvent {
2153 Init(InitMessage),
2154 Status(StatusMessage),
2155 CompactBoundary(CompactBoundaryMessage),
2156 ThinkingTokens(ThinkingTokensMessage),
2157 TaskStarted(TaskStartedMessage),
2158 TaskProgress(TaskProgressMessage),
2159 TaskUpdated(TaskUpdatedMessage),
2160 TaskNotification(TaskNotificationMessage),
2161 ApiRetry(ApiRetryMessage),
2162 ControlRequestProgress(ControlRequestProgressMessage),
2163 ModelRefusalFallback(ModelRefusalFallbackMessage),
2164 ModelRefusalNoFallback(ModelRefusalNoFallbackMessage),
2165 LocalCommandOutput(LocalCommandOutputMessage),
2166 HookStarted(HookStartedMessage),
2167 HookProgress(HookProgressMessage),
2168 HookResponse(HookResponseMessage),
2169 PluginInstall(PluginInstallMessage),
2170 BackgroundTasksChanged(BackgroundTasksChangedMessage),
2171 SessionStateChanged(SessionStateChangedMessage),
2172 WorkerShuttingDown(WorkerShuttingDownMessage),
2173 CommandsChanged(CommandsChangedMessage),
2174 Notification(NotificationMessage),
2175 FilesPersisted(FilesPersistedMessage),
2176 MemoryRecall(MemoryRecallMessage),
2177 ElicitationComplete(ElicitationCompleteMessage),
2178 PermissionDenied(PermissionDeniedMessage),
2179 MirrorError(MirrorErrorMessage),
2180 Informational(InformationalMessage),
2181 CodeChangePublished(CodeChangePublishedMessage),
2182 VcsStateChanged(VcsStateChangedMessage),
2183 FeedbackDraftQueued(FeedbackDraftQueuedMessage),
2184 CloudSessionDelta(CloudSessionDeltaMessage),
2185 DevIntent(DevIntentMessage),
2186 TurnHandoffAvailable(TurnHandoffAvailableMessage),
2187 TurnPreempted(TurnPreemptedMessage),
2188 PeerMessageHold(PeerMessageHoldMessage),
2189 PerTurnEffortChanged(PerTurnEffortChangedMessage),
2190 SessionTitleChanged(SessionTitleChangedMessage),
2191 InstructionSizeWarning(InstructionSizeWarningMessage),
2192 FileAttachmentsMissing(FileAttachmentsMissingMessage),
2193 PermissionCheckStatus(PermissionCheckStatusMessage),
2194 InheritedAsksSettled(InheritedAsksSettledMessage),
2195 UiFocus(UiFocusMessage),
2196 UiInvalidate(UiInvalidateMessage),
2197 UiLog(UiLogMessage),
2198 UiPanes(UiPanesMessage),
2199 UiScroll(UiScrollMessage),
2200 UiStatus(UiStatusMessage),
2201 UiToast(UiToastMessage),
2202}
2203
2204#[derive(Debug, Clone, Serialize, Deserialize)]
2205pub struct ApiRetryMessage {
2206 pub attempt: u64,
2207 pub max_retries: u64,
2208 pub retry_delay_ms: u64,
2209 pub error_status: Option<u16>,
2210 pub error: String,
2211 #[serde(default, skip_serializing_if = "Option::is_none")]
2216 pub no_response: Option<ApiRetryNoResponse>,
2217 #[serde(default, skip_serializing_if = "Option::is_none")]
2218 pub uuid: Option<String>,
2219 #[serde(default, skip_serializing_if = "Option::is_none")]
2220 pub session_id: Option<String>,
2221}
2222
2223#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2226pub struct ApiRetryNoResponse {
2227 pub waited_ms: u64,
2229 pub retry_wait_ms: u64,
2231}
2232
2233#[derive(Debug, Clone, Serialize, Deserialize)]
2234pub struct ControlRequestProgressMessage {
2235 pub request_id: String,
2236 pub status: String,
2237 #[serde(default, skip_serializing_if = "Option::is_none")]
2238 pub attempt: Option<u64>,
2239 #[serde(default, skip_serializing_if = "Option::is_none")]
2240 pub max_retries: Option<u64>,
2241 #[serde(default, skip_serializing_if = "Option::is_none")]
2242 pub retry_delay_ms: Option<u64>,
2243 #[serde(default, skip_serializing_if = "Option::is_none")]
2244 pub error_status: Option<u16>,
2245 #[serde(default, skip_serializing_if = "Option::is_none")]
2246 pub error: Option<String>,
2247 #[serde(default, skip_serializing_if = "Option::is_none")]
2248 pub uuid: Option<String>,
2249 #[serde(default, skip_serializing_if = "Option::is_none")]
2250 pub session_id: Option<String>,
2251}
2252
2253#[derive(Debug, Clone, Serialize, Deserialize)]
2254pub struct ModelRefusalFallbackMessage {
2255 pub trigger: String,
2256 pub direction: String,
2257 #[serde(default, skip_serializing_if = "Option::is_none")]
2263 pub scope: Option<RefusalFallbackScope>,
2264 pub original_model: String,
2265 pub fallback_model: String,
2266 pub request_id: Option<String>,
2267 #[serde(default, skip_serializing_if = "Option::is_none")]
2268 pub api_refusal_category: Option<String>,
2269 #[serde(default, skip_serializing_if = "Option::is_none")]
2274 pub saw_cyber_refusal: Option<bool>,
2275 #[serde(default, skip_serializing_if = "Option::is_none")]
2276 pub api_refusal_explanation: Option<String>,
2277 #[serde(default, skip_serializing_if = "Option::is_none")]
2278 pub retracted_message_uuids: Option<Vec<String>>,
2279 #[serde(default, skip_serializing_if = "Option::is_none")]
2280 pub refused_user_message_uuid: Option<String>,
2281 pub content: Value,
2282 #[serde(default, skip_serializing_if = "Option::is_none")]
2283 pub uuid: Option<String>,
2284 #[serde(default, skip_serializing_if = "Option::is_none")]
2285 pub session_id: Option<String>,
2286}
2287
2288#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2292pub enum RefusalFallbackScope {
2293 Session,
2295 Local,
2298 Unknown(String),
2300}
2301
2302impl RefusalFallbackScope {
2303 pub fn as_str(&self) -> &str {
2304 match self {
2305 Self::Session => "session",
2306 Self::Local => "local",
2307 Self::Unknown(s) => s.as_str(),
2308 }
2309 }
2310}
2311
2312impl fmt::Display for RefusalFallbackScope {
2313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2314 f.write_str(self.as_str())
2315 }
2316}
2317
2318impl From<&str> for RefusalFallbackScope {
2319 fn from(s: &str) -> Self {
2320 match s {
2321 "session" => Self::Session,
2322 "local" => Self::Local,
2323 other => Self::Unknown(other.to_string()),
2324 }
2325 }
2326}
2327
2328impl Serialize for RefusalFallbackScope {
2329 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2330 serializer.serialize_str(self.as_str())
2331 }
2332}
2333
2334impl<'de> Deserialize<'de> for RefusalFallbackScope {
2335 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
2336 let s = String::deserialize(deserializer)?;
2337 Ok(Self::from(s.as_str()))
2338 }
2339}
2340
2341#[derive(Debug, Clone, Serialize, Deserialize)]
2342pub struct ModelRefusalNoFallbackMessage {
2343 pub original_model: String,
2344 pub request_id: Option<String>,
2345 #[serde(default, skip_serializing_if = "Option::is_none")]
2346 pub api_refusal_category: Option<String>,
2347 #[serde(default, skip_serializing_if = "Option::is_none")]
2348 pub api_refusal_explanation: Option<String>,
2349 pub content: Value,
2350 #[serde(default, skip_serializing_if = "Option::is_none")]
2351 pub uuid: Option<String>,
2352 #[serde(default, skip_serializing_if = "Option::is_none")]
2353 pub session_id: Option<String>,
2354}
2355
2356#[derive(Debug, Clone, Serialize, Deserialize)]
2357pub struct LocalCommandOutputMessage {
2358 pub content: String,
2359 #[serde(default, skip_serializing_if = "Option::is_none")]
2360 pub uuid: Option<String>,
2361 #[serde(default, skip_serializing_if = "Option::is_none")]
2362 pub session_id: Option<String>,
2363}
2364
2365#[derive(Debug, Clone, Serialize, Deserialize)]
2366pub struct HookStartedMessage {
2367 pub hook_id: String,
2368 pub hook_name: String,
2369 pub hook_event: String,
2370 #[serde(default, skip_serializing_if = "Option::is_none")]
2371 pub uuid: Option<String>,
2372 #[serde(default, skip_serializing_if = "Option::is_none")]
2373 pub session_id: Option<String>,
2374}
2375
2376#[derive(Debug, Clone, Serialize, Deserialize)]
2377pub struct HookProgressMessage {
2378 pub hook_id: String,
2379 pub hook_name: String,
2380 pub hook_event: String,
2381 #[serde(default, skip_serializing_if = "Option::is_none")]
2382 pub stdout: Option<String>,
2383 #[serde(default, skip_serializing_if = "Option::is_none")]
2384 pub stderr: Option<String>,
2385 #[serde(default, skip_serializing_if = "Option::is_none")]
2386 pub output: Option<String>,
2387 #[serde(default, skip_serializing_if = "Option::is_none")]
2388 pub uuid: Option<String>,
2389 #[serde(default, skip_serializing_if = "Option::is_none")]
2390 pub session_id: Option<String>,
2391}
2392
2393#[derive(Debug, Clone, Serialize, Deserialize)]
2394pub struct HookResponseMessage {
2395 pub hook_id: String,
2396 pub hook_name: String,
2397 pub hook_event: String,
2398 #[serde(default, skip_serializing_if = "Option::is_none")]
2399 pub stdout: Option<String>,
2400 #[serde(default, skip_serializing_if = "Option::is_none")]
2401 pub stderr: Option<String>,
2402 #[serde(default, skip_serializing_if = "Option::is_none")]
2403 pub output: Option<String>,
2404 #[serde(default, skip_serializing_if = "Option::is_none")]
2405 pub exit_code: Option<i32>,
2406 pub outcome: String,
2407 #[serde(default, skip_serializing_if = "Option::is_none")]
2408 pub uuid: Option<String>,
2409 #[serde(default, skip_serializing_if = "Option::is_none")]
2410 pub session_id: Option<String>,
2411}
2412
2413#[derive(Debug, Clone, Serialize, Deserialize)]
2414pub struct PluginInstallMessage {
2415 pub status: String,
2416 #[serde(default, skip_serializing_if = "Option::is_none")]
2417 pub name: Option<String>,
2418 #[serde(default, skip_serializing_if = "Option::is_none")]
2419 pub error: Option<String>,
2420 #[serde(default, skip_serializing_if = "Option::is_none")]
2421 pub uuid: Option<String>,
2422 #[serde(default, skip_serializing_if = "Option::is_none")]
2423 pub session_id: Option<String>,
2424}
2425
2426#[derive(Debug, Clone, Serialize, Deserialize)]
2427pub struct BackgroundTasksChangedMessage {
2428 pub tasks: Vec<BackgroundTaskInfo>,
2429 #[serde(default, skip_serializing_if = "Option::is_none")]
2430 pub uuid: Option<String>,
2431 #[serde(default, skip_serializing_if = "Option::is_none")]
2432 pub session_id: Option<String>,
2433}
2434
2435#[derive(Debug, Clone, Serialize, Deserialize)]
2436pub struct BackgroundTaskInfo {
2437 pub task_id: String,
2438 #[serde(default, skip_serializing_if = "Option::is_none")]
2441 pub run_id: Option<String>,
2442 pub task_type: String,
2443 #[serde(default, skip_serializing_if = "Option::is_none")]
2447 pub subagent_type: Option<String>,
2448 pub description: String,
2449 #[serde(default, skip_serializing_if = "Option::is_none")]
2452 pub parent_task_id: Option<String>,
2453 #[serde(default, skip_serializing_if = "Option::is_none")]
2456 pub ambient: Option<bool>,
2457 #[serde(default, skip_serializing_if = "Option::is_none")]
2460 pub awaited: Option<bool>,
2461}
2462
2463#[derive(Debug, Clone, Serialize, Deserialize)]
2464pub struct SessionStateChangedMessage {
2465 pub state: String,
2466 #[serde(default, skip_serializing_if = "Option::is_none")]
2467 pub uuid: Option<String>,
2468 #[serde(default, skip_serializing_if = "Option::is_none")]
2469 pub session_id: Option<String>,
2470}
2471
2472#[derive(Debug, Clone, Serialize, Deserialize)]
2473pub struct WorkerShuttingDownMessage {
2474 pub reason: String,
2475 #[serde(default, skip_serializing_if = "Option::is_none")]
2476 pub uuid: Option<String>,
2477 #[serde(default, skip_serializing_if = "Option::is_none")]
2478 pub session_id: Option<String>,
2479}
2480
2481#[derive(Debug, Clone, Serialize, Deserialize)]
2482pub struct CommandsChangedMessage {
2483 pub commands: Vec<CommandInfo>,
2484 #[serde(default, skip_serializing_if = "Option::is_none")]
2485 pub uuid: Option<String>,
2486 #[serde(default, skip_serializing_if = "Option::is_none")]
2487 pub session_id: Option<String>,
2488}
2489
2490#[derive(Debug, Clone, Serialize, Deserialize)]
2491pub struct CommandInfo {
2492 pub name: String,
2493 pub description: String,
2494 #[serde(rename = "argumentHint")]
2495 pub argument_hint: String,
2496 #[serde(default, skip_serializing_if = "Option::is_none")]
2497 pub aliases: Option<Vec<String>>,
2498 #[serde(default, skip_serializing_if = "Option::is_none")]
2504 pub builtin: Option<bool>,
2505}
2506
2507#[derive(Debug, Clone, Serialize, Deserialize)]
2508pub struct NotificationMessage {
2509 pub key: String,
2510 pub text: String,
2511 pub priority: String,
2512 #[serde(default, skip_serializing_if = "Option::is_none")]
2513 pub color: Option<String>,
2514 #[serde(default, skip_serializing_if = "Option::is_none")]
2515 pub timeout_ms: Option<u64>,
2516 #[serde(default, skip_serializing_if = "Option::is_none")]
2517 pub uuid: Option<String>,
2518 #[serde(default, skip_serializing_if = "Option::is_none")]
2519 pub session_id: Option<String>,
2520}
2521
2522#[derive(Debug, Clone, Serialize, Deserialize)]
2523pub struct FilesPersistedMessage {
2524 pub files: Vec<PersistedFile>,
2525 pub failed: Vec<FailedPersistedFile>,
2526 pub processed_at: String,
2527 #[serde(default, skip_serializing_if = "Option::is_none")]
2528 pub uuid: Option<String>,
2529 #[serde(default, skip_serializing_if = "Option::is_none")]
2530 pub session_id: Option<String>,
2531}
2532
2533#[derive(Debug, Clone, Serialize, Deserialize)]
2534pub struct PersistedFile {
2535 pub filename: String,
2536 pub file_id: String,
2537}
2538
2539#[derive(Debug, Clone, Serialize, Deserialize)]
2540pub struct FailedPersistedFile {
2541 pub filename: String,
2542 pub error: String,
2543}
2544
2545#[derive(Debug, Clone, Serialize, Deserialize)]
2546pub struct MemoryRecallMessage {
2547 pub mode: String,
2548 pub memories: Vec<MemoryRecallItem>,
2549 #[serde(default, skip_serializing_if = "Option::is_none")]
2550 pub uuid: Option<String>,
2551 #[serde(default, skip_serializing_if = "Option::is_none")]
2552 pub session_id: Option<String>,
2553}
2554
2555#[derive(Debug, Clone, Serialize, Deserialize)]
2556pub struct MemoryRecallItem {
2557 pub path: String,
2558 pub scope: String,
2559 #[serde(default, skip_serializing_if = "Option::is_none")]
2560 pub content: Option<String>,
2561}
2562
2563#[derive(Debug, Clone, Serialize, Deserialize)]
2564pub struct ElicitationCompleteMessage {
2565 pub mcp_server_name: String,
2566 pub elicitation_id: String,
2567 #[serde(default, skip_serializing_if = "Option::is_none")]
2568 pub uuid: Option<String>,
2569 #[serde(default, skip_serializing_if = "Option::is_none")]
2570 pub session_id: Option<String>,
2571}
2572
2573#[derive(Debug, Clone, Serialize, Deserialize)]
2574pub struct PermissionDeniedMessage {
2575 pub tool_name: String,
2576 pub tool_use_id: String,
2577 #[serde(default, skip_serializing_if = "Option::is_none")]
2578 pub agent_id: Option<String>,
2579 #[serde(default, skip_serializing_if = "Option::is_none")]
2580 pub decision_reason_type: Option<String>,
2581 #[serde(default, skip_serializing_if = "Option::is_none")]
2585 pub decision_reason_code: Option<PermissionDeniedReasonCode>,
2586 #[serde(default, skip_serializing_if = "Option::is_none")]
2587 pub decision_reason: Option<String>,
2588 pub message: String,
2589 #[serde(default, skip_serializing_if = "Option::is_none")]
2590 pub uuid: Option<String>,
2591 #[serde(default, skip_serializing_if = "Option::is_none")]
2592 pub session_id: Option<String>,
2593}
2594
2595#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2598pub enum PermissionDeniedReasonCode {
2599 ClassifierTranscriptTooLong,
2601 OutsideReadsBlocked,
2603 MemoryPaused,
2605 Unknown(String),
2607}
2608
2609impl PermissionDeniedReasonCode {
2610 pub fn as_str(&self) -> &str {
2611 match self {
2612 Self::ClassifierTranscriptTooLong => "classifier_transcript_too_long",
2613 Self::OutsideReadsBlocked => "outside_reads_blocked",
2614 Self::MemoryPaused => "memory_paused",
2615 Self::Unknown(s) => s.as_str(),
2616 }
2617 }
2618}
2619
2620impl fmt::Display for PermissionDeniedReasonCode {
2621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2622 f.write_str(self.as_str())
2623 }
2624}
2625
2626impl From<&str> for PermissionDeniedReasonCode {
2627 fn from(s: &str) -> Self {
2628 match s {
2629 "classifier_transcript_too_long" => Self::ClassifierTranscriptTooLong,
2630 "outside_reads_blocked" => Self::OutsideReadsBlocked,
2631 "memory_paused" => Self::MemoryPaused,
2632 other => Self::Unknown(other.to_string()),
2633 }
2634 }
2635}
2636
2637impl Serialize for PermissionDeniedReasonCode {
2638 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2639 serializer.serialize_str(self.as_str())
2640 }
2641}
2642
2643impl<'de> Deserialize<'de> for PermissionDeniedReasonCode {
2644 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
2645 let s = String::deserialize(deserializer)?;
2646 Ok(Self::from(s.as_str()))
2647 }
2648}
2649
2650#[derive(Debug, Clone, Serialize, Deserialize)]
2651pub struct MirrorErrorMessage {
2652 pub error: String,
2653 pub key: MirrorErrorKey,
2654 #[serde(default, skip_serializing_if = "Option::is_none")]
2655 pub uuid: Option<String>,
2656 #[serde(default, skip_serializing_if = "Option::is_none")]
2657 pub session_id: Option<String>,
2658}
2659
2660#[derive(Debug, Clone, Serialize, Deserialize)]
2661pub struct MirrorErrorKey {
2662 #[serde(rename = "projectKey")]
2663 pub project_key: String,
2664 #[serde(rename = "sessionId")]
2665 pub session_id: String,
2666 #[serde(default, skip_serializing_if = "Option::is_none")]
2667 pub subpath: Option<String>,
2668}
2669
2670#[derive(Debug, Clone, Serialize, Deserialize)]
2671pub struct InformationalMessage {
2672 pub content: String,
2673 pub level: String,
2674 #[serde(default, skip_serializing_if = "Option::is_none")]
2675 pub tool_use_id: Option<String>,
2676 #[serde(default, skip_serializing_if = "Option::is_none")]
2677 pub prevent_continuation: Option<bool>,
2678 #[serde(default, skip_serializing_if = "Option::is_none")]
2681 pub tag: Option<String>,
2682 #[serde(default, skip_serializing_if = "Option::is_none")]
2688 pub prompt_submit_outcome: Option<PromptSubmitOutcome>,
2689 #[serde(default, skip_serializing_if = "Option::is_none")]
2690 pub uuid: Option<String>,
2691 #[serde(default, skip_serializing_if = "Option::is_none")]
2692 pub session_id: Option<String>,
2693}
2694
2695#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2698pub struct PromptSubmitOutcome {
2699 #[serde(default, skip_serializing_if = "Option::is_none")]
2703 pub kind: Option<PromptSubmitOutcomeKind>,
2704 #[serde(default, skip_serializing_if = "Option::is_none")]
2711 pub no_verdict_kind: Option<String>,
2712 pub prompt_kept: bool,
2715 #[serde(default, skip_serializing_if = "Option::is_none")]
2719 pub reason: Option<String>,
2720}
2721
2722#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2724pub enum PromptSubmitOutcomeKind {
2725 BlockedByHook,
2727 StoppedByHook,
2729 DroppedByHook,
2731 NotPassedOn,
2733 NoVerdict,
2736 Other(String),
2738}
2739
2740impl PromptSubmitOutcomeKind {
2741 pub fn as_str(&self) -> &str {
2742 match self {
2743 Self::BlockedByHook => "blocked_by_hook",
2744 Self::StoppedByHook => "stopped_by_hook",
2745 Self::DroppedByHook => "dropped_by_hook",
2746 Self::NotPassedOn => "not_passed_on",
2747 Self::NoVerdict => "no_verdict",
2748 Self::Other(s) => s.as_str(),
2749 }
2750 }
2751}
2752
2753impl fmt::Display for PromptSubmitOutcomeKind {
2754 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2755 f.write_str(self.as_str())
2756 }
2757}
2758
2759impl From<&str> for PromptSubmitOutcomeKind {
2760 fn from(s: &str) -> Self {
2761 match s {
2762 "blocked_by_hook" => Self::BlockedByHook,
2763 "stopped_by_hook" => Self::StoppedByHook,
2764 "dropped_by_hook" => Self::DroppedByHook,
2765 "not_passed_on" => Self::NotPassedOn,
2766 "no_verdict" => Self::NoVerdict,
2767 other => Self::Other(other.to_string()),
2768 }
2769 }
2770}
2771
2772impl Serialize for PromptSubmitOutcomeKind {
2773 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2774 serializer.serialize_str(self.as_str())
2775 }
2776}
2777
2778impl<'de> Deserialize<'de> for PromptSubmitOutcomeKind {
2779 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
2780 let s = String::deserialize(deserializer)?;
2781 Ok(Self::from(s.as_str()))
2782 }
2783}
2784
2785#[derive(Debug, Clone, Serialize, Deserialize)]
2787pub struct PluginInfo {
2788 pub name: String,
2790 pub path: String,
2792 #[serde(skip_serializing_if = "Option::is_none")]
2794 pub source: Option<String>,
2795 #[serde(default, skip_serializing_if = "Option::is_none")]
2797 pub version: Option<String>,
2798}
2799
2800#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2802pub struct PluginDiagnostic {
2803 pub plugin: String,
2804 #[serde(rename = "type")]
2805 pub diagnostic_type: String,
2806 pub message: String,
2807}
2808
2809#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2811pub struct MemoryPaths {
2812 #[serde(default, skip_serializing_if = "Option::is_none")]
2813 pub auto: Option<String>,
2814 #[serde(default, skip_serializing_if = "Option::is_none")]
2815 pub team: Option<String>,
2816 #[serde(flatten)]
2817 pub extra: serde_json::Map<String, Value>,
2818}
2819
2820#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2824pub struct McpServerError {
2825 pub name: String,
2826 #[serde(rename = "type")]
2828 pub error_type: String,
2829 pub message: String,
2830}
2831
2832#[derive(Debug, Clone, Serialize, Deserialize)]
2834pub struct InitMessage {
2835 pub session_id: String,
2837 #[serde(skip_serializing_if = "Option::is_none")]
2839 pub cwd: Option<String>,
2840 #[serde(skip_serializing_if = "Option::is_none")]
2842 pub model: Option<String>,
2843 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2845 pub tools: Vec<String>,
2846 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2848 pub mcp_servers: Vec<Value>,
2849 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2851 pub slash_commands: Vec<String>,
2852 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2855 pub terminal_slash_commands: Vec<String>,
2856 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2858 pub agents: Vec<String>,
2859 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2861 pub plugins: Vec<PluginInfo>,
2862 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2864 pub skills: Vec<Value>,
2865 #[serde(skip_serializing_if = "Option::is_none")]
2867 pub claude_code_version: Option<String>,
2868 #[serde(skip_serializing_if = "Option::is_none")]
2871 pub messaging_socket_path: Option<String>,
2872 #[serde(skip_serializing_if = "Option::is_none", rename = "apiKeySource")]
2874 pub api_key_source: Option<ApiKeySource>,
2875 #[serde(skip_serializing_if = "Option::is_none")]
2877 pub output_style: Option<OutputStyle>,
2878 #[serde(skip_serializing_if = "Option::is_none", rename = "permissionMode")]
2880 pub permission_mode: Option<InitPermissionMode>,
2881
2882 #[serde(skip_serializing_if = "Option::is_none")]
2884 pub uuid: Option<String>,
2885
2886 #[serde(skip_serializing_if = "Option::is_none")]
2888 pub memory_paths: Option<MemoryPaths>,
2889
2890 #[serde(default, skip_serializing_if = "Option::is_none")]
2895 pub scratchpad_path: Option<String>,
2896
2897 #[serde(skip_serializing_if = "Option::is_none")]
2899 pub fast_mode_state: Option<String>,
2900
2901 #[serde(default, skip_serializing_if = "Option::is_none")]
2903 pub fast_mode_disabled_reason: Option<super::result::FastModeDisabledReason>,
2904
2905 #[serde(default, skip_serializing_if = "Option::is_none")]
2908 pub mcp_server_errors: Option<Vec<McpServerError>>,
2909
2910 #[serde(default, skip_serializing_if = "Option::is_none")]
2912 pub analytics_disabled: Option<bool>,
2913
2914 #[serde(default, skip_serializing_if = "Option::is_none")]
2916 pub product_feedback_disabled: Option<bool>,
2917
2918 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2920 pub betas: Vec<String>,
2921
2922 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2924 pub capabilities: Vec<String>,
2925
2926 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2928 pub plugin_errors: Vec<PluginDiagnostic>,
2929
2930 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2932 pub plugin_warnings: Vec<PluginDiagnostic>,
2933
2934 #[serde(default, skip_serializing_if = "Option::is_none")]
2939 pub effort: Option<String>,
2940
2941 #[serde(default, skip_serializing_if = "Option::is_none")]
2946 pub cloud_session: Option<Value>,
2947
2948 #[serde(default, skip_serializing_if = "Option::is_none")]
2952 pub footer_indicator: Option<FooterIndicator>,
2953
2954 #[serde(default, skip_serializing_if = "Option::is_none")]
2958 pub worker_epoch: Option<u64>,
2959
2960 #[serde(default, skip_serializing_if = "Option::is_none")]
2964 pub powershell_path: Option<Option<String>>,
2965
2966 #[serde(default, skip_serializing_if = "Option::is_none")]
2970 pub startup_timing: Option<serde_json::Map<String, Value>>,
2971
2972 #[serde(default, skip_serializing_if = "Option::is_none")]
2982 pub per_turn_effort_active: Option<bool>,
2983
2984 #[serde(default, skip_serializing_if = "Option::is_none")]
2991 pub view_mode: Option<ViewMode>,
2992
2993 #[serde(default, skip_serializing_if = "Option::is_none")]
3000 pub original_cwd: Option<String>,
3001
3002 #[serde(default, skip_serializing_if = "Option::is_none")]
3011 pub additional_directories: Option<Vec<String>>,
3012}
3013
3014#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3018pub struct FooterIndicator {
3019 pub text: String,
3022}
3023
3024#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3027pub enum ViewMode {
3028 Focus,
3031 Default,
3033 Unknown(String),
3035}
3036
3037impl ViewMode {
3038 pub fn as_str(&self) -> &str {
3039 match self {
3040 Self::Focus => "focus",
3041 Self::Default => "default",
3042 Self::Unknown(s) => s.as_str(),
3043 }
3044 }
3045}
3046
3047impl fmt::Display for ViewMode {
3048 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3049 f.write_str(self.as_str())
3050 }
3051}
3052
3053impl From<&str> for ViewMode {
3054 fn from(s: &str) -> Self {
3055 match s {
3056 "focus" => Self::Focus,
3057 "default" => Self::Default,
3058 other => Self::Unknown(other.to_string()),
3059 }
3060 }
3061}
3062
3063impl Serialize for ViewMode {
3064 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3065 serializer.serialize_str(self.as_str())
3066 }
3067}
3068
3069impl<'de> Deserialize<'de> for ViewMode {
3070 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3071 let s = String::deserialize(deserializer)?;
3072 Ok(Self::from(s.as_str()))
3073 }
3074}
3075
3076#[derive(Debug, Clone, Serialize, Deserialize)]
3078pub struct StatusMessage {
3079 pub session_id: String,
3081 pub status: Option<StatusMessageStatus>,
3083 #[serde(skip_serializing_if = "Option::is_none")]
3085 pub uuid: Option<String>,
3086 #[serde(skip_serializing_if = "Option::is_none", rename = "permissionMode")]
3088 pub permission_mode: Option<InitPermissionMode>,
3089 #[serde(skip_serializing_if = "Option::is_none")]
3090 pub compact_result: Option<String>,
3091 #[serde(skip_serializing_if = "Option::is_none")]
3092 pub compact_error: Option<String>,
3093 #[serde(default, skip_serializing_if = "Option::is_none")]
3100 pub compact_output_chars: Option<u64>,
3101}
3102
3103#[derive(Debug, Clone, Serialize, Deserialize)]
3105pub struct CompactBoundaryMessage {
3106 pub session_id: String,
3108 pub compact_metadata: CompactMetadata,
3110 #[serde(
3114 default,
3115 skip_serializing_if = "Option::is_none",
3116 alias = "content",
3117 alias = "text"
3118 )]
3119 pub summary: Option<String>,
3120 #[serde(
3124 default,
3125 skip_serializing_if = "Option::is_none",
3126 alias = "message_count"
3127 )]
3128 pub leaf_message_count: Option<u32>,
3129 #[serde(default, skip_serializing_if = "Option::is_none")]
3131 pub duration_ms: Option<u64>,
3132 #[serde(skip_serializing_if = "Option::is_none")]
3134 pub uuid: Option<String>,
3135 #[serde(skip_serializing_if = "Option::is_none")]
3137 pub logical_parent_uuid: Option<Option<String>>,
3138 #[serde(default, skip_serializing_if = "Option::is_none")]
3142 pub historical: Option<bool>,
3143}
3144
3145#[derive(Debug, Clone, Serialize, Deserialize)]
3147pub struct CompactMetadata {
3148 pub pre_tokens: u64,
3150 pub trigger: CompactionTrigger,
3152 #[serde(default, skip_serializing_if = "Option::is_none")]
3153 pub post_tokens: Option<u64>,
3154 #[serde(default, skip_serializing_if = "Option::is_none")]
3155 pub cumulative_dropped_tokens: Option<u64>,
3156 #[serde(default, skip_serializing_if = "Option::is_none")]
3157 pub duration_ms: Option<u64>,
3158 #[serde(default, skip_serializing_if = "Option::is_none")]
3159 pub user_context: Option<String>,
3160 #[serde(default, skip_serializing_if = "Option::is_none")]
3161 pub messages_summarized: Option<u64>,
3162 #[serde(default, skip_serializing_if = "Option::is_none")]
3163 pub precomputed: Option<bool>,
3164 #[serde(default, skip_serializing_if = "Option::is_none")]
3165 pub pre_compact_discovered_tools: Option<Vec<String>>,
3166 #[serde(default, skip_serializing_if = "Option::is_none")]
3171 pub pre_compact_artifact_read_versions: Option<Vec<ArtifactReadVersion>>,
3172 #[serde(default, skip_serializing_if = "Option::is_none")]
3173 pub preserved_segment: Option<PreservedSegment>,
3174 #[serde(default, skip_serializing_if = "Option::is_none")]
3175 pub preserved_messages: Option<PreservedMessages>,
3176}
3177
3178#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3181pub struct ArtifactReadVersion {
3182 pub slug: String,
3184 pub ver: String,
3186}
3187
3188#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3189pub struct PreservedSegment {
3190 pub head_uuid: String,
3191 pub anchor_uuid: String,
3192 pub tail_uuid: String,
3193}
3194
3195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3196pub struct PreservedMessages {
3197 pub anchor_uuid: String,
3198 pub uuids: Vec<String>,
3199 #[serde(default, skip_serializing_if = "Option::is_none")]
3200 pub all_uuids: Option<Vec<String>>,
3201}
3202
3203#[derive(Debug, Clone, Serialize, Deserialize)]
3209pub struct TaskUsage {
3210 pub duration_ms: u64,
3212 pub tool_uses: u64,
3214 pub total_tokens: u64,
3216}
3217
3218#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3220pub enum TaskType {
3221 LocalAgent,
3223 LocalBash,
3225 LocalWorkflow,
3227 Unknown(String),
3229}
3230
3231impl TaskType {
3232 pub fn as_str(&self) -> &str {
3233 match self {
3234 Self::LocalAgent => "local_agent",
3235 Self::LocalBash => "local_bash",
3236 Self::LocalWorkflow => "local_workflow",
3237 Self::Unknown(s) => s.as_str(),
3238 }
3239 }
3240}
3241
3242impl fmt::Display for TaskType {
3243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3244 f.write_str(self.as_str())
3245 }
3246}
3247
3248impl From<&str> for TaskType {
3249 fn from(s: &str) -> Self {
3250 match s {
3251 "local_agent" => Self::LocalAgent,
3252 "local_bash" => Self::LocalBash,
3253 "local_workflow" => Self::LocalWorkflow,
3254 other => Self::Unknown(other.to_string()),
3255 }
3256 }
3257}
3258
3259impl Serialize for TaskType {
3260 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3261 serializer.serialize_str(self.as_str())
3262 }
3263}
3264
3265impl<'de> Deserialize<'de> for TaskType {
3266 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3267 let s = String::deserialize(deserializer)?;
3268 Ok(Self::from(s.as_str()))
3269 }
3270}
3271
3272#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3274pub enum TaskStatus {
3275 Pending,
3276 Running,
3277 Completed,
3278 Failed,
3279 Killed,
3280 Paused,
3281 Stopped,
3282 Unknown(String),
3283}
3284
3285impl TaskStatus {
3286 pub fn as_str(&self) -> &str {
3287 match self {
3288 Self::Pending => "pending",
3289 Self::Running => "running",
3290 Self::Completed => "completed",
3291 Self::Failed => "failed",
3292 Self::Killed => "killed",
3293 Self::Paused => "paused",
3294 Self::Stopped => "stopped",
3295 Self::Unknown(s) => s.as_str(),
3296 }
3297 }
3298}
3299
3300impl fmt::Display for TaskStatus {
3301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3302 f.write_str(self.as_str())
3303 }
3304}
3305
3306impl From<&str> for TaskStatus {
3307 fn from(s: &str) -> Self {
3308 match s {
3309 "pending" => Self::Pending,
3310 "running" => Self::Running,
3311 "completed" => Self::Completed,
3312 "failed" => Self::Failed,
3313 "killed" => Self::Killed,
3314 "paused" => Self::Paused,
3315 "stopped" => Self::Stopped,
3316 other => Self::Unknown(other.to_string()),
3317 }
3318 }
3319}
3320
3321impl Serialize for TaskStatus {
3322 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3323 serializer.serialize_str(self.as_str())
3324 }
3325}
3326
3327impl<'de> Deserialize<'de> for TaskStatus {
3328 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3329 let s = String::deserialize(deserializer)?;
3330 Ok(Self::from(s.as_str()))
3331 }
3332}
3333
3334#[derive(Debug, Clone, Serialize, Deserialize)]
3336pub struct TaskStartedMessage {
3337 pub session_id: String,
3338 pub task_id: String,
3339 #[serde(default, skip_serializing_if = "Option::is_none")]
3345 pub run_id: Option<String>,
3346 #[serde(default, skip_serializing_if = "Option::is_none")]
3352 pub parent_task_id: Option<String>,
3353 #[serde(default, skip_serializing_if = "Option::is_none")]
3354 pub task_type: Option<TaskType>,
3355 #[serde(default, skip_serializing_if = "Option::is_none")]
3356 pub tool_use_id: Option<String>,
3357 pub description: String,
3358 #[serde(default, skip_serializing_if = "Option::is_none")]
3361 pub subagent_type: Option<String>,
3362 #[serde(default, skip_serializing_if = "Option::is_none")]
3368 pub is_backgrounded: Option<bool>,
3369 #[serde(default, skip_serializing_if = "Option::is_none")]
3377 pub awaited: Option<bool>,
3378 #[serde(default, skip_serializing_if = "Option::is_none")]
3382 pub spawn_depth: Option<u32>,
3383 #[serde(default, skip_serializing_if = "Option::is_none")]
3385 pub prompt: Option<String>,
3386 #[serde(default, skip_serializing_if = "Option::is_none")]
3387 pub workflow_name: Option<String>,
3388 #[serde(default, skip_serializing_if = "Option::is_none")]
3389 pub skip_transcript: Option<bool>,
3390 #[serde(default, skip_serializing_if = "Option::is_none")]
3395 pub ambient: Option<bool>,
3396 pub uuid: String,
3397}
3398
3399#[derive(Debug, Clone, Serialize, Deserialize)]
3403pub struct TaskUpdatedMessage {
3404 pub session_id: String,
3405 pub task_id: String,
3406 #[serde(default, skip_serializing_if = "Option::is_none")]
3409 pub run_id: Option<String>,
3410 pub patch: TaskPatch,
3411 pub uuid: String,
3412}
3413
3414#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3417pub struct TaskPatch {
3418 #[serde(default, skip_serializing_if = "Option::is_none")]
3419 pub status: Option<TaskStatus>,
3420 #[serde(default, skip_serializing_if = "Option::is_none")]
3423 pub end_time: Option<u64>,
3424 #[serde(default, skip_serializing_if = "Option::is_none")]
3425 pub description: Option<String>,
3426 #[serde(default, skip_serializing_if = "Option::is_none")]
3427 pub total_paused_ms: Option<u64>,
3428 #[serde(default, skip_serializing_if = "Option::is_none")]
3429 pub error: Option<String>,
3430 #[serde(default, skip_serializing_if = "Option::is_none")]
3431 pub is_backgrounded: Option<bool>,
3432}
3433
3434#[derive(Debug, Clone, Serialize, Deserialize)]
3437pub struct ThinkingTokensMessage {
3438 pub session_id: String,
3439 pub estimated_tokens: u64,
3441 pub estimated_tokens_delta: u64,
3443 #[serde(default, skip_serializing_if = "Option::is_none")]
3450 pub user_message_uuid: Option<String>,
3451 pub uuid: String,
3452}
3453
3454#[derive(Debug, Clone, Serialize, Deserialize)]
3457pub struct TaskProgressMessage {
3458 pub session_id: String,
3459 pub task_id: String,
3460 #[serde(default, skip_serializing_if = "Option::is_none")]
3463 pub run_id: Option<String>,
3464 #[serde(default, skip_serializing_if = "Option::is_none")]
3465 pub tool_use_id: Option<String>,
3466 pub description: String,
3467 #[serde(default, skip_serializing_if = "Option::is_none")]
3468 pub last_tool_name: Option<String>,
3469 pub usage: TaskUsage,
3470 #[serde(default, skip_serializing_if = "Option::is_none")]
3472 pub subagent_type: Option<String>,
3473 #[serde(default, skip_serializing_if = "Option::is_none")]
3474 pub summary: Option<String>,
3475 pub uuid: String,
3476}
3477
3478#[derive(Debug, Clone, Serialize, Deserialize)]
3481pub struct TaskNotificationMessage {
3482 pub session_id: String,
3483 pub task_id: String,
3484 #[serde(default, skip_serializing_if = "Option::is_none")]
3487 pub run_id: Option<String>,
3488 pub status: TaskStatus,
3489 #[serde(default, skip_serializing_if = "Option::is_none")]
3492 pub reason: Option<TaskEndReason>,
3493 pub summary: String,
3494 pub output_file: Option<String>,
3495 #[serde(skip_serializing_if = "Option::is_none")]
3496 pub tool_use_id: Option<String>,
3497 #[serde(skip_serializing_if = "Option::is_none")]
3498 pub usage: Option<TaskUsage>,
3499 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3505 pub resource_links: Vec<ResourceLink>,
3506 #[serde(default, skip_serializing_if = "Option::is_none")]
3513 pub handback: Option<TaskHandback>,
3514 #[serde(default, skip_serializing_if = "Option::is_none")]
3518 pub handback_report: Option<HandbackReport>,
3519 #[serde(default, skip_serializing_if = "Option::is_none")]
3520 pub skip_transcript: Option<bool>,
3521 #[serde(default, skip_serializing_if = "Option::is_none")]
3524 pub ambient: Option<bool>,
3525 #[serde(skip_serializing_if = "Option::is_none")]
3526 pub uuid: Option<String>,
3527}
3528
3529#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3532pub enum TaskHandback {
3533 Send,
3535 Flagged,
3537 Withheld,
3539 Unknown(String),
3541}
3542
3543impl TaskHandback {
3544 pub fn as_str(&self) -> &str {
3545 match self {
3546 Self::Send => "send",
3547 Self::Flagged => "flagged",
3548 Self::Withheld => "withheld",
3549 Self::Unknown(s) => s.as_str(),
3550 }
3551 }
3552}
3553
3554impl fmt::Display for TaskHandback {
3555 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3556 f.write_str(self.as_str())
3557 }
3558}
3559
3560impl From<&str> for TaskHandback {
3561 fn from(s: &str) -> Self {
3562 match s {
3563 "send" => Self::Send,
3564 "flagged" => Self::Flagged,
3565 "withheld" => Self::Withheld,
3566 other => Self::Unknown(other.to_string()),
3567 }
3568 }
3569}
3570
3571impl Serialize for TaskHandback {
3572 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3573 serializer.serialize_str(self.as_str())
3574 }
3575}
3576
3577impl<'de> Deserialize<'de> for TaskHandback {
3578 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3579 let s = String::deserialize(deserializer)?;
3580 Ok(Self::from(s.as_str()))
3581 }
3582}
3583
3584#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3587pub struct HandbackReport {
3588 pub text: String,
3591 #[serde(default, skip_serializing_if = "Option::is_none")]
3594 pub warning: Option<String>,
3595}
3596
3597#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3599pub enum AssistantErrorKind {
3600 AuthenticationFailed,
3601 OauthOrgNotAllowed,
3602 AccountOnHold,
3603 BillingError,
3604 RateLimit,
3605 Overloaded,
3606 InvalidRequest,
3607 ModelNotFound,
3608 ServerError,
3609 UnknownError,
3610 MaxOutputTokens,
3611 Unknown(String),
3612}
3613
3614impl AssistantErrorKind {
3615 pub fn as_str(&self) -> &str {
3616 match self {
3617 Self::AuthenticationFailed => "authentication_failed",
3618 Self::OauthOrgNotAllowed => "oauth_org_not_allowed",
3619 Self::AccountOnHold => "account_on_hold",
3620 Self::BillingError => "billing_error",
3621 Self::RateLimit => "rate_limit",
3622 Self::Overloaded => "overloaded",
3623 Self::InvalidRequest => "invalid_request",
3624 Self::ModelNotFound => "model_not_found",
3625 Self::ServerError => "server_error",
3626 Self::UnknownError => "unknown",
3627 Self::MaxOutputTokens => "max_output_tokens",
3628 Self::Unknown(s) => s.as_str(),
3629 }
3630 }
3631}
3632
3633impl fmt::Display for AssistantErrorKind {
3634 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3635 f.write_str(self.as_str())
3636 }
3637}
3638
3639impl From<&str> for AssistantErrorKind {
3640 fn from(s: &str) -> Self {
3641 match s {
3642 "authentication_failed" => Self::AuthenticationFailed,
3643 "oauth_org_not_allowed" => Self::OauthOrgNotAllowed,
3644 "account_on_hold" => Self::AccountOnHold,
3645 "billing_error" => Self::BillingError,
3646 "rate_limit" => Self::RateLimit,
3647 "overloaded" => Self::Overloaded,
3648 "invalid_request" => Self::InvalidRequest,
3649 "model_not_found" => Self::ModelNotFound,
3650 "server_error" => Self::ServerError,
3651 "unknown" => Self::UnknownError,
3652 "max_output_tokens" => Self::MaxOutputTokens,
3653 other => Self::Unknown(other.to_string()),
3654 }
3655 }
3656}
3657
3658impl Serialize for AssistantErrorKind {
3659 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3660 serializer.serialize_str(self.as_str())
3661 }
3662}
3663
3664impl<'de> Deserialize<'de> for AssistantErrorKind {
3665 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3666 let s = String::deserialize(deserializer)?;
3667 Ok(Self::from(s.as_str()))
3668 }
3669}
3670
3671#[derive(Debug, Clone, Serialize, Deserialize)]
3678pub struct CodeChangePublishedMessage {
3679 pub provider: String,
3683 pub url: String,
3685 pub repo: String,
3688 pub identifier: String,
3690 #[serde(default, skip_serializing_if = "Option::is_none")]
3699 pub action: Option<String>,
3700 #[serde(default, skip_serializing_if = "Option::is_none")]
3708 pub branch: Option<String>,
3709 pub uuid: String,
3710 pub session_id: String,
3711}
3712
3713#[derive(Debug, Clone, Serialize, Deserialize)]
3718pub struct VcsStateChangedMessage {
3719 pub kind: VcsMutationKind,
3722 pub cwd: String,
3725 #[serde(default, skip_serializing_if = "Option::is_none")]
3730 pub branch: Option<String>,
3731 pub uuid: String,
3732 pub session_id: String,
3733}
3734
3735#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3737pub enum VcsMutationKind {
3738 Commit,
3739 Push,
3740 Merge,
3741 Rebase,
3742 Unknown(String),
3744}
3745
3746impl VcsMutationKind {
3747 pub fn as_str(&self) -> &str {
3748 match self {
3749 Self::Commit => "commit",
3750 Self::Push => "push",
3751 Self::Merge => "merge",
3752 Self::Rebase => "rebase",
3753 Self::Unknown(s) => s.as_str(),
3754 }
3755 }
3756}
3757
3758impl fmt::Display for VcsMutationKind {
3759 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3760 f.write_str(self.as_str())
3761 }
3762}
3763
3764impl From<&str> for VcsMutationKind {
3765 fn from(s: &str) -> Self {
3766 match s {
3767 "commit" => Self::Commit,
3768 "push" => Self::Push,
3769 "merge" => Self::Merge,
3770 "rebase" => Self::Rebase,
3771 other => Self::Unknown(other.to_string()),
3772 }
3773 }
3774}
3775
3776impl Serialize for VcsMutationKind {
3777 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3778 serializer.serialize_str(self.as_str())
3779 }
3780}
3781
3782impl<'de> Deserialize<'de> for VcsMutationKind {
3783 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3784 let s = String::deserialize(deserializer)?;
3785 Ok(Self::from(s.as_str()))
3786 }
3787}
3788
3789#[derive(Debug, Clone, Serialize, Deserialize)]
3791pub struct FeedbackDraftQueuedMessage {
3792 pub draft_id: String,
3793 pub draft_type: String,
3794 pub title: String,
3795 pub details_preview: String,
3796 #[serde(default, skip_serializing_if = "Option::is_none")]
3797 pub uuid: Option<String>,
3798 #[serde(default, skip_serializing_if = "Option::is_none")]
3799 pub session_id: Option<String>,
3800 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3801 pub extra: serde_json::Map<String, Value>,
3802}
3803
3804#[derive(Debug, Clone, Serialize, Deserialize)]
3812pub struct CloudSessionDeltaMessage {
3813 pub seq: u64,
3817 pub changed: Vec<String>,
3821 pub cloud_session: Value,
3825 pub uuid: String,
3826 pub session_id: String,
3827 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3828 pub extra: serde_json::Map<String, Value>,
3829}
3830
3831#[derive(Debug, Clone, Serialize, Deserialize)]
3847pub struct DevIntentMessage {
3848 pub kind: DevIntentKind,
3850 #[serde(default, skip_serializing_if = "Option::is_none")]
3856 pub trigger: Option<DevIntentTrigger>,
3857 pub uuid: String,
3858 pub session_id: String,
3859 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3860 pub extra: serde_json::Map<String, Value>,
3861}
3862
3863#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3867pub enum DevIntentKind {
3868 IosApp,
3874 AndroidApp,
3881 Unknown(String),
3883}
3884
3885impl DevIntentKind {
3886 pub fn as_str(&self) -> &str {
3887 match self {
3888 Self::IosApp => "ios_app",
3889 Self::AndroidApp => "android_app",
3890 Self::Unknown(s) => s.as_str(),
3891 }
3892 }
3893}
3894
3895impl fmt::Display for DevIntentKind {
3896 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3897 f.write_str(self.as_str())
3898 }
3899}
3900
3901impl From<&str> for DevIntentKind {
3902 fn from(s: &str) -> Self {
3903 match s {
3904 "ios_app" => Self::IosApp,
3905 "android_app" => Self::AndroidApp,
3906 other => Self::Unknown(other.to_string()),
3907 }
3908 }
3909}
3910
3911impl Serialize for DevIntentKind {
3912 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3913 serializer.serialize_str(self.as_str())
3914 }
3915}
3916
3917impl<'de> Deserialize<'de> for DevIntentKind {
3918 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3919 let s = String::deserialize(deserializer)?;
3920 Ok(Self::from(s.as_str()))
3921 }
3922}
3923
3924#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3928pub enum DevIntentTrigger {
3929 SwiftEdit,
3931 UikitImport,
3933 XcodeProject,
3936 IosCommand,
3939 KotlinEdit,
3941 JavaEdit,
3943 AndroidImport,
3945 AndroidManifest,
3947 GradlePlugin,
3949 AndroidCommand,
3952 ProjectScan,
3955 Unknown(String),
3957}
3958
3959impl DevIntentTrigger {
3960 pub fn as_str(&self) -> &str {
3961 match self {
3962 Self::SwiftEdit => "swift_edit",
3963 Self::UikitImport => "uikit_import",
3964 Self::XcodeProject => "xcode_project",
3965 Self::IosCommand => "ios_command",
3966 Self::KotlinEdit => "kotlin_edit",
3967 Self::JavaEdit => "java_edit",
3968 Self::AndroidImport => "android_import",
3969 Self::AndroidManifest => "android_manifest",
3970 Self::GradlePlugin => "gradle_plugin",
3971 Self::AndroidCommand => "android_command",
3972 Self::ProjectScan => "project_scan",
3973 Self::Unknown(s) => s.as_str(),
3974 }
3975 }
3976}
3977
3978impl fmt::Display for DevIntentTrigger {
3979 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3980 f.write_str(self.as_str())
3981 }
3982}
3983
3984impl From<&str> for DevIntentTrigger {
3985 fn from(s: &str) -> Self {
3986 match s {
3987 "swift_edit" => Self::SwiftEdit,
3988 "uikit_import" => Self::UikitImport,
3989 "xcode_project" => Self::XcodeProject,
3990 "ios_command" => Self::IosCommand,
3991 "kotlin_edit" => Self::KotlinEdit,
3992 "java_edit" => Self::JavaEdit,
3993 "android_import" => Self::AndroidImport,
3994 "android_manifest" => Self::AndroidManifest,
3995 "gradle_plugin" => Self::GradlePlugin,
3996 "android_command" => Self::AndroidCommand,
3997 "project_scan" => Self::ProjectScan,
3998 other => Self::Unknown(other.to_string()),
3999 }
4000 }
4001}
4002
4003impl Serialize for DevIntentTrigger {
4004 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4005 serializer.serialize_str(self.as_str())
4006 }
4007}
4008
4009impl<'de> Deserialize<'de> for DevIntentTrigger {
4010 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4011 let s = String::deserialize(deserializer)?;
4012 Ok(Self::from(s.as_str()))
4013 }
4014}
4015
4016#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4020pub enum TaskEndReason {
4021 WorkerRestart,
4024 Unknown(String),
4026}
4027
4028impl TaskEndReason {
4029 pub fn as_str(&self) -> &str {
4030 match self {
4031 Self::WorkerRestart => "worker_restart",
4032 Self::Unknown(s) => s.as_str(),
4033 }
4034 }
4035}
4036
4037impl fmt::Display for TaskEndReason {
4038 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4039 f.write_str(self.as_str())
4040 }
4041}
4042
4043impl From<&str> for TaskEndReason {
4044 fn from(s: &str) -> Self {
4045 match s {
4046 "worker_restart" => Self::WorkerRestart,
4047 other => Self::Unknown(other.to_string()),
4048 }
4049 }
4050}
4051
4052impl Serialize for TaskEndReason {
4053 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4054 serializer.serialize_str(self.as_str())
4055 }
4056}
4057
4058impl<'de> Deserialize<'de> for TaskEndReason {
4059 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4060 let s = String::deserialize(deserializer)?;
4061 Ok(Self::from(s.as_str()))
4062 }
4063}
4064
4065#[derive(Debug, Clone, Serialize, Deserialize)]
4074pub struct TurnHandoffAvailableMessage {
4075 pub v: u64,
4077 pub tools: Vec<String>,
4079 pub worker_epoch: u64,
4081 #[serde(default, skip_serializing_if = "Option::is_none")]
4085 pub relay_marker: Option<bool>,
4086 #[serde(default, skip_serializing_if = "Option::is_none")]
4094 pub staged_files: Option<bool>,
4095 #[serde(default, skip_serializing_if = "Option::is_none")]
4102 pub no_query_first: Option<bool>,
4103 #[serde(default, skip_serializing_if = "Option::is_none")]
4111 pub home_files: Option<bool>,
4112 #[serde(default, skip_serializing_if = "Option::is_none")]
4116 pub file_names: Option<bool>,
4117 #[serde(default, skip_serializing_if = "Option::is_none")]
4127 pub carried_writes: Option<bool>,
4128 #[serde(default, skip_serializing_if = "Option::is_none")]
4142 pub hydrates_carried_lines: Option<bool>,
4143 #[serde(default, skip_serializing_if = "Option::is_none")]
4149 pub shared_message_id: Option<bool>,
4150 #[serde(default, skip_serializing_if = "Option::is_none")]
4155 pub requires_rewind_request_id: Option<bool>,
4156 pub uuid: String,
4157 pub session_id: String,
4158 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4159 pub extra: serde_json::Map<String, Value>,
4160}
4161
4162#[derive(Debug, Clone, Serialize, Deserialize)]
4174pub struct TurnPreemptedMessage {
4175 pub reason: String,
4179 pub preempted_by_uuid: String,
4183 #[serde(default)]
4186 pub preempted_message_uuids: Vec<String>,
4187 pub uuid: String,
4188 pub session_id: String,
4189 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4190 pub extra: serde_json::Map<String, Value>,
4191}
4192
4193#[derive(Debug, Clone, Serialize, Deserialize)]
4201pub struct PeerMessageHoldMessage {
4202 pub state: PeerMessageHoldState,
4208 #[serde(default, skip_serializing_if = "Option::is_none")]
4215 pub message_uuid: Option<String>,
4216 pub lane: PeerMessageLane,
4218 pub from: String,
4223 #[serde(default, skip_serializing_if = "Option::is_none")]
4226 pub from_name: Option<String>,
4227 #[serde(default, skip_serializing_if = "Option::is_none")]
4229 pub cause: Option<PeerMessageHoldCause>,
4230 #[serde(default, skip_serializing_if = "Option::is_none")]
4232 pub outcome: Option<PeerMessageHoldOutcome>,
4233 pub uuid: String,
4234 pub session_id: String,
4235 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4236 pub extra: serde_json::Map<String, Value>,
4237}
4238
4239#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4241pub enum PeerMessageHoldState {
4242 Held,
4244 Released,
4246 Dropped,
4248 Unknown(String),
4250}
4251
4252impl PeerMessageHoldState {
4253 pub fn as_str(&self) -> &str {
4254 match self {
4255 Self::Held => "held",
4256 Self::Released => "released",
4257 Self::Dropped => "dropped",
4258 Self::Unknown(s) => s.as_str(),
4259 }
4260 }
4261}
4262
4263impl fmt::Display for PeerMessageHoldState {
4264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4265 f.write_str(self.as_str())
4266 }
4267}
4268
4269impl From<&str> for PeerMessageHoldState {
4270 fn from(s: &str) -> Self {
4271 match s {
4272 "held" => Self::Held,
4273 "released" => Self::Released,
4274 "dropped" => Self::Dropped,
4275 other => Self::Unknown(other.to_string()),
4276 }
4277 }
4278}
4279
4280impl Serialize for PeerMessageHoldState {
4281 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4282 serializer.serialize_str(self.as_str())
4283 }
4284}
4285
4286impl<'de> Deserialize<'de> for PeerMessageHoldState {
4287 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4288 let s = String::deserialize(deserializer)?;
4289 Ok(Self::from(s.as_str()))
4290 }
4291}
4292
4293#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4295pub enum PeerMessageLane {
4296 Bridge,
4298 Stdin,
4300 Socket,
4302 Unknown(String),
4304}
4305
4306impl PeerMessageLane {
4307 pub fn as_str(&self) -> &str {
4308 match self {
4309 Self::Bridge => "bridge",
4310 Self::Stdin => "stdin",
4311 Self::Socket => "socket",
4312 Self::Unknown(s) => s.as_str(),
4313 }
4314 }
4315}
4316
4317impl fmt::Display for PeerMessageLane {
4318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4319 f.write_str(self.as_str())
4320 }
4321}
4322
4323impl From<&str> for PeerMessageLane {
4324 fn from(s: &str) -> Self {
4325 match s {
4326 "bridge" => Self::Bridge,
4327 "stdin" => Self::Stdin,
4328 "socket" => Self::Socket,
4329 other => Self::Unknown(other.to_string()),
4330 }
4331 }
4332}
4333
4334impl Serialize for PeerMessageLane {
4335 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4336 serializer.serialize_str(self.as_str())
4337 }
4338}
4339
4340impl<'de> Deserialize<'de> for PeerMessageLane {
4341 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4342 let s = String::deserialize(deserializer)?;
4343 Ok(Self::from(s.as_str()))
4344 }
4345}
4346
4347#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4354pub enum PeerMessageHoldCause {
4355 ExplicitSetting,
4356 ManagedSetting,
4357 RepoSetting,
4358 InvalidSetting,
4359 BypassDefault,
4360 ModeUnknown,
4361 ModeMismatch,
4362 NoModeAsserted,
4363 Unknown(String),
4365}
4366
4367impl PeerMessageHoldCause {
4368 pub fn as_str(&self) -> &str {
4369 match self {
4370 Self::ExplicitSetting => "explicit-setting",
4371 Self::ManagedSetting => "managed-setting",
4372 Self::RepoSetting => "repo-setting",
4373 Self::InvalidSetting => "invalid-setting",
4374 Self::BypassDefault => "bypass-default",
4375 Self::ModeUnknown => "mode-unknown",
4376 Self::ModeMismatch => "mode-mismatch",
4377 Self::NoModeAsserted => "no-mode-asserted",
4378 Self::Unknown(s) => s.as_str(),
4379 }
4380 }
4381}
4382
4383impl fmt::Display for PeerMessageHoldCause {
4384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4385 f.write_str(self.as_str())
4386 }
4387}
4388
4389impl From<&str> for PeerMessageHoldCause {
4390 fn from(s: &str) -> Self {
4391 match s {
4392 "explicit-setting" => Self::ExplicitSetting,
4393 "managed-setting" => Self::ManagedSetting,
4394 "repo-setting" => Self::RepoSetting,
4395 "invalid-setting" => Self::InvalidSetting,
4396 "bypass-default" => Self::BypassDefault,
4397 "mode-unknown" => Self::ModeUnknown,
4398 "mode-mismatch" => Self::ModeMismatch,
4399 "no-mode-asserted" => Self::NoModeAsserted,
4400 other => Self::Unknown(other.to_string()),
4401 }
4402 }
4403}
4404
4405impl Serialize for PeerMessageHoldCause {
4406 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4407 serializer.serialize_str(self.as_str())
4408 }
4409}
4410
4411impl<'de> Deserialize<'de> for PeerMessageHoldCause {
4412 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4413 let s = String::deserialize(deserializer)?;
4414 Ok(Self::from(s.as_str()))
4415 }
4416}
4417
4418#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4421pub enum PeerMessageHoldOutcome {
4422 Expired,
4426 Refused,
4428 Dropped,
4431 Discarded,
4433 Unknown(String),
4435}
4436
4437impl PeerMessageHoldOutcome {
4438 pub fn as_str(&self) -> &str {
4439 match self {
4440 Self::Expired => "expired",
4441 Self::Refused => "refused",
4442 Self::Dropped => "dropped",
4443 Self::Discarded => "discarded",
4444 Self::Unknown(s) => s.as_str(),
4445 }
4446 }
4447}
4448
4449impl fmt::Display for PeerMessageHoldOutcome {
4450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4451 f.write_str(self.as_str())
4452 }
4453}
4454
4455impl From<&str> for PeerMessageHoldOutcome {
4456 fn from(s: &str) -> Self {
4457 match s {
4458 "expired" => Self::Expired,
4459 "refused" => Self::Refused,
4460 "dropped" => Self::Dropped,
4461 "discarded" => Self::Discarded,
4462 other => Self::Unknown(other.to_string()),
4463 }
4464 }
4465}
4466
4467impl Serialize for PeerMessageHoldOutcome {
4468 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4469 serializer.serialize_str(self.as_str())
4470 }
4471}
4472
4473impl<'de> Deserialize<'de> for PeerMessageHoldOutcome {
4474 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4475 let s = String::deserialize(deserializer)?;
4476 Ok(Self::from(s.as_str()))
4477 }
4478}
4479
4480#[derive(Debug, Clone, Serialize, Deserialize)]
4491pub struct PerTurnEffortChangedMessage {
4492 pub per_turn_effort_active: bool,
4494 pub uuid: String,
4495 pub session_id: String,
4496}
4497
4498#[derive(Debug, Clone, Serialize, Deserialize)]
4505pub struct InstructionSizeWarningMessage {
4506 pub total_chars: u64,
4509 pub total_limit_chars: u64,
4512 pub file_count: u64,
4515 #[serde(default, skip_serializing_if = "Option::is_none")]
4519 pub largest_chars: Option<u64>,
4520 pub uuid: String,
4521 pub session_id: String,
4522}
4523
4524#[derive(Debug, Clone, Serialize, Deserialize)]
4531pub struct FileAttachmentsMissingMessage {
4532 pub message_uuid: String,
4535 pub sent_count: u64,
4537 pub missing: Vec<MissingFileAttachment>,
4539 pub uuid: String,
4540 pub session_id: String,
4541}
4542
4543#[derive(Debug, Clone, Serialize, Deserialize)]
4553pub struct PermissionCheckStatusMessage {
4554 pub tool_use_id: String,
4555 #[serde(default, skip_serializing_if = "Option::is_none")]
4558 pub agent_id: Option<String>,
4559 pub status: PermissionCheckStatus,
4560 pub uuid: String,
4561 pub session_id: String,
4562}
4563
4564#[derive(Debug, Clone, Serialize, Deserialize)]
4569pub struct InheritedAsksSettledMessage {
4570 pub kept_request_ids: Vec<String>,
4573 pub uuid: String,
4574 pub session_id: String,
4575}
4576
4577#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4579pub enum PermissionCheckStatus {
4580 Checking,
4583 Done,
4585 Unknown(String),
4587}
4588
4589impl PermissionCheckStatus {
4590 pub fn as_str(&self) -> &str {
4591 match self {
4592 Self::Checking => "checking",
4593 Self::Done => "done",
4594 Self::Unknown(s) => s.as_str(),
4595 }
4596 }
4597}
4598
4599impl fmt::Display for PermissionCheckStatus {
4600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4601 f.write_str(self.as_str())
4602 }
4603}
4604
4605impl From<&str> for PermissionCheckStatus {
4606 fn from(s: &str) -> Self {
4607 match s {
4608 "checking" => Self::Checking,
4609 "done" => Self::Done,
4610 other => Self::Unknown(other.to_string()),
4611 }
4612 }
4613}
4614
4615impl Serialize for PermissionCheckStatus {
4616 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4617 serializer.serialize_str(self.as_str())
4618 }
4619}
4620
4621impl<'de> Deserialize<'de> for PermissionCheckStatus {
4622 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4623 let s = String::deserialize(deserializer)?;
4624 Ok(Self::from(s.as_str()))
4625 }
4626}
4627
4628#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4630pub struct MissingFileAttachment {
4631 pub file_uuid: String,
4633 pub reason: String,
4638}
4639
4640#[derive(Debug, Clone, Serialize, Deserialize)]
4650pub struct SessionTitleChangedMessage {
4651 pub title: String,
4656 pub uuid: String,
4657 pub session_id: String,
4658}
4659
4660#[derive(Debug, Clone, Serialize, Deserialize)]
4664pub struct UiLogMessage {
4665 pub plugin: String,
4667 pub text: String,
4671 pub uuid: String,
4672 pub session_id: String,
4673}
4674
4675#[derive(Debug, Clone, Serialize, Deserialize)]
4680pub struct UiToastMessage {
4681 pub plugin: String,
4683 pub text: String,
4687 pub timeout_ms: u64,
4690 pub uuid: String,
4691 pub session_id: String,
4692}
4693
4694#[derive(Debug, Clone, Serialize, Deserialize)]
4698pub struct UiStatusMessage {
4699 pub plugin: String,
4701 pub text: Option<String>,
4706 pub uuid: String,
4707 pub session_id: String,
4708}
4709
4710#[derive(Debug, Clone, Serialize, Deserialize)]
4715pub struct UiInvalidateMessage {
4716 pub event: String,
4719 #[serde(default, skip_serializing_if = "Option::is_none")]
4724 pub instances: Option<Vec<UiInvalidatedInstance>>,
4725 #[serde(default, skip_serializing_if = "Option::is_none")]
4733 pub prompts_screened: Option<bool>,
4734 pub uuid: String,
4735 pub session_id: String,
4736}
4737
4738#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4740pub struct UiInvalidatedInstance {
4741 pub surface: String,
4743 pub component: String,
4745 pub instance_id: String,
4748}
4749
4750#[derive(Debug, Clone, Serialize, Deserialize)]
4757pub struct UiPanesMessage {
4758 pub panes: Vec<UiPane>,
4761 pub shown_id: Option<String>,
4764 pub focused_id: Option<String>,
4767 pub focus_requested_id: Option<String>,
4771 pub uuid: String,
4772 pub session_id: String,
4773}
4774
4775#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4777pub struct UiPane {
4778 pub id: String,
4781 pub title: String,
4784 pub plugin: String,
4786 #[serde(default, skip_serializing_if = "Option::is_none")]
4789 pub close_on_escape: Option<bool>,
4790 #[serde(default, skip_serializing_if = "Option::is_none")]
4793 pub hold_toasts: Option<bool>,
4794 #[serde(default, skip_serializing_if = "Option::is_none")]
4799 pub background_color: Option<String>,
4800 #[serde(default, skip_serializing_if = "Option::is_none")]
4803 pub rows: Option<u64>,
4804 #[serde(default, skip_serializing_if = "Option::is_none")]
4807 pub columns: Option<u64>,
4808}
4809
4810#[derive(Debug, Clone, Serialize, Deserialize)]
4814pub struct UiScrollMessage {
4815 pub client_id: String,
4817 pub component: UiSiteComponent,
4819 pub instance_id: String,
4821 pub offset: u64,
4824 #[serde(default, skip_serializing_if = "Option::is_none")]
4827 pub follow_end: Option<bool>,
4828 pub uuid: String,
4829 pub session_id: String,
4830}
4831
4832#[derive(Debug, Clone, Serialize, Deserialize)]
4837pub struct UiFocusMessage {
4838 pub client_id: String,
4840 pub component: UiSiteComponent,
4842 pub instance_id: String,
4844 pub plugin: String,
4846 pub key: String,
4848 pub uuid: String,
4849 pub session_id: String,
4850}
4851
4852#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4854pub enum UiSiteComponent {
4855 Pane,
4857 AbovePrompt,
4859 Unknown(String),
4861}
4862
4863impl UiSiteComponent {
4864 pub fn as_str(&self) -> &str {
4865 match self {
4866 Self::Pane => "Pane",
4867 Self::AbovePrompt => "AbovePrompt",
4868 Self::Unknown(s) => s.as_str(),
4869 }
4870 }
4871}
4872
4873impl fmt::Display for UiSiteComponent {
4874 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4875 f.write_str(self.as_str())
4876 }
4877}
4878
4879impl From<&str> for UiSiteComponent {
4880 fn from(s: &str) -> Self {
4881 match s {
4882 "Pane" => Self::Pane,
4883 "AbovePrompt" => Self::AbovePrompt,
4884 other => Self::Unknown(other.to_string()),
4885 }
4886 }
4887}
4888
4889impl Serialize for UiSiteComponent {
4890 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4891 serializer.serialize_str(self.as_str())
4892 }
4893}
4894
4895impl<'de> Deserialize<'de> for UiSiteComponent {
4896 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4897 let s = String::deserialize(deserializer)?;
4898 Ok(Self::from(s.as_str()))
4899 }
4900}
4901
4902#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4905pub struct LocalCommandRun {
4906 pub command: String,
4908 pub args: String,
4911 #[serde(default, skip_serializing_if = "Option::is_none")]
4916 pub plugin: Option<String>,
4917}
4918
4919#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4923pub struct LocalCommandOutcome {
4924 pub kind: LocalCommandOutcomeKind,
4925 #[serde(default, skip_serializing_if = "Option::is_none")]
4928 pub suggestion: Option<String>,
4929}
4930
4931#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4936pub enum LocalCommandOutcomeKind {
4937 UnavailableHeadless,
4939 Unknown,
4942 Failed,
4944 RestartRequired,
4946 Other(String),
4948}
4949
4950impl LocalCommandOutcomeKind {
4951 pub fn as_str(&self) -> &str {
4952 match self {
4953 Self::UnavailableHeadless => "unavailable_headless",
4954 Self::Unknown => "unknown",
4955 Self::Failed => "failed",
4956 Self::RestartRequired => "restart_required",
4957 Self::Other(s) => s.as_str(),
4958 }
4959 }
4960}
4961
4962impl fmt::Display for LocalCommandOutcomeKind {
4963 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4964 f.write_str(self.as_str())
4965 }
4966}
4967
4968impl From<&str> for LocalCommandOutcomeKind {
4969 fn from(s: &str) -> Self {
4970 match s {
4971 "unavailable_headless" => Self::UnavailableHeadless,
4972 "unknown" => Self::Unknown,
4973 "failed" => Self::Failed,
4974 "restart_required" => Self::RestartRequired,
4975 other => Self::Other(other.to_string()),
4976 }
4977 }
4978}
4979
4980impl Serialize for LocalCommandOutcomeKind {
4981 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4982 serializer.serialize_str(self.as_str())
4983 }
4984}
4985
4986impl<'de> Deserialize<'de> for LocalCommandOutcomeKind {
4987 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4988 let s = String::deserialize(deserializer)?;
4989 Ok(Self::from(s.as_str()))
4990 }
4991}
4992
4993#[derive(Debug, Clone, Serialize, Deserialize)]
4999pub struct UsageReport {
5000 pub session: super::control::UsageSession,
5002 pub rate_limits: Option<UsageReportRateLimits>,
5006 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5007 pub extra: serde_json::Map<String, Value>,
5008}
5009
5010#[derive(Debug, Clone, Serialize, Deserialize)]
5012pub struct UsageReportRateLimits {
5013 pub limits: Option<Vec<UsageReportLimit>>,
5021 #[serde(default, skip_serializing_if = "Option::is_none")]
5024 pub extra_usage: Option<UsageReportExtraUsage>,
5025 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5026 pub extra: serde_json::Map<String, Value>,
5027}
5028
5029#[derive(Debug, Clone, Serialize, Deserialize)]
5031pub struct UsageReportLimit {
5032 pub kind: String,
5035 pub group: String,
5038 pub percent: f64,
5040 pub resets_at: Option<String>,
5042 #[serde(default, skip_serializing_if = "Option::is_none")]
5045 pub scope: Option<UsageReportScope>,
5046 #[serde(default, skip_serializing_if = "Option::is_none")]
5050 pub severity: Option<String>,
5051 #[serde(default, skip_serializing_if = "Option::is_none")]
5053 pub is_active: Option<bool>,
5054 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5055 pub extra: serde_json::Map<String, Value>,
5056}
5057
5058#[derive(Debug, Clone, Serialize, Deserialize)]
5060pub struct UsageReportScope {
5061 #[serde(default, skip_serializing_if = "Option::is_none")]
5062 pub model: Option<UsageReportScopeLabel>,
5063 #[serde(default, skip_serializing_if = "Option::is_none")]
5064 pub surface: Option<UsageReportScopeLabel>,
5065 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5066 pub extra: serde_json::Map<String, Value>,
5067}
5068
5069#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5071pub struct UsageReportScopeLabel {
5072 pub display_name: String,
5073}
5074
5075#[derive(Debug, Clone, Serialize, Deserialize)]
5079pub struct UsageReportExtraUsage {
5080 pub is_enabled: bool,
5082 pub monthly_limit: Option<f64>,
5083 pub used_credits: Option<f64>,
5084 pub utilization: Option<f64>,
5085 #[serde(default, skip_serializing_if = "Option::is_none")]
5086 pub currency: Option<String>,
5087 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5088 pub extra: serde_json::Map<String, Value>,
5089}
5090
5091#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5094pub struct BatchToolUse {
5095 pub id: String,
5096 pub name: String,
5097}
5098
5099#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5104pub struct ContextUsage {
5105 pub model: String,
5107 pub total_tokens: u64,
5110 pub raw_max_tokens: u64,
5113 pub percentage: u64,
5115 #[serde(default, skip_serializing_if = "Option::is_none")]
5117 pub over_limit: Option<ContextOverLimit>,
5118 #[serde(default)]
5120 pub categories: Vec<ContextCategory>,
5121 #[serde(default)]
5123 pub mcp_tools: Vec<ContextMcpTool>,
5124 #[serde(default)]
5126 pub memory_files: Vec<ContextMemoryFile>,
5127 #[serde(default)]
5129 pub agents: Vec<ContextAgent>,
5130 #[serde(default, skip_serializing_if = "Option::is_none")]
5132 pub skills: Option<Vec<ContextSkill>>,
5133}
5134
5135#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5137pub struct ContextOverLimit {
5138 pub tokens_over: u64,
5139 pub kind: String,
5142}
5143
5144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5146pub struct ContextCategory {
5147 pub name: String,
5150 pub tokens: u64,
5151 pub kind: String,
5155}
5156
5157#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5159pub struct ContextMcpTool {
5160 pub name: String,
5162 pub server_name: String,
5163 pub tokens: u64,
5164}
5165
5166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5168pub struct ContextMemoryFile {
5169 pub path: String,
5170 #[serde(rename = "type")]
5172 pub file_type: String,
5173 pub tokens: u64,
5174}
5175
5176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5178pub struct ContextAgent {
5179 pub agent_type: String,
5180 pub source: String,
5182 pub tokens: u64,
5183}
5184
5185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5187pub struct ContextSkill {
5188 pub name: String,
5189 pub source: String,
5191 #[serde(default, skip_serializing_if = "Option::is_none")]
5192 pub plugin_name: Option<String>,
5193 pub tokens: u64,
5194}
5195
5196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5198pub struct ToolUseMeta {
5199 pub id: String,
5200 pub display_name: String,
5201 #[serde(default, skip_serializing_if = "Option::is_none")]
5202 pub server_display_name: Option<String>,
5203 #[serde(default, skip_serializing_if = "Option::is_none")]
5204 pub icon_url: Option<String>,
5205 #[serde(default, skip_serializing_if = "Option::is_none")]
5210 pub server_id: Option<String>,
5211 #[serde(default, skip_serializing_if = "Option::is_none")]
5219 pub server_id_stamp: Option<String>,
5220 #[serde(default, skip_serializing_if = "Option::is_none")]
5223 pub tool_name: Option<String>,
5224}
5225
5226#[derive(Debug, Clone, Serialize, Deserialize)]
5228pub struct AssistantMessage {
5229 pub message: AssistantMessageContent,
5230 #[serde(alias = "sessionId")]
5231 pub session_id: String,
5232 #[serde(skip_serializing_if = "Option::is_none")]
5233 pub uuid: Option<String>,
5234 #[serde(skip_serializing_if = "Option::is_none")]
5235 pub parent_tool_use_id: Option<String>,
5236 #[serde(skip_serializing_if = "Option::is_none")]
5238 pub request_id: Option<String>,
5239 #[serde(default, skip_serializing_if = "Option::is_none")]
5244 pub refused_message_id: Option<String>,
5245 #[serde(default, skip_serializing_if = "Option::is_none")]
5254 pub user_message_uuid: Option<String>,
5255 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5261 pub user_message_uuids: Vec<String>,
5262 #[serde(default, skip_serializing_if = "Option::is_none")]
5266 pub agent_id: Option<String>,
5267 #[serde(skip_serializing_if = "Option::is_none")]
5270 pub subagent_type: Option<String>,
5271 #[serde(skip_serializing_if = "Option::is_none")]
5273 pub task_description: Option<String>,
5274 #[serde(skip_serializing_if = "Option::is_none")]
5275 pub error: Option<AssistantErrorKind>,
5276 #[serde(default, skip_serializing_if = "Option::is_none")]
5280 pub aborted: Option<bool>,
5281 #[serde(default, skip_serializing_if = "Option::is_none")]
5286 pub resumed_from_incomplete_thinking: Option<bool>,
5287 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5288 pub supersedes: Vec<String>,
5289 #[serde(skip_serializing_if = "Option::is_none")]
5290 pub timestamp: Option<String>,
5291 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5292 pub tool_use_meta: Vec<ToolUseMeta>,
5293 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5298 pub batch_tool_uses: Vec<BatchToolUse>,
5299 #[serde(default, skip_serializing_if = "Option::is_none")]
5305 pub wire_tool_inputs: Option<serde_json::Map<String, Value>>,
5306 #[serde(default, skip_serializing_if = "Option::is_none")]
5312 pub wire_ingest_context: Option<serde_json::Map<String, Value>>,
5313 #[serde(default, skip_serializing_if = "Option::is_none")]
5317 pub historical: Option<bool>,
5318 #[serde(default, skip_serializing_if = "Option::is_none")]
5326 pub resume_reason: Option<String>,
5327 #[serde(default, skip_serializing_if = "Option::is_none")]
5333 pub local_command_source: Option<String>,
5334 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5343 pub narration_block_indexes: Vec<usize>,
5344 #[serde(default, skip_serializing_if = "Option::is_none")]
5348 pub narration_hint: Option<String>,
5349 #[serde(default, skip_serializing_if = "Option::is_none")]
5358 pub thinking_duration_ms: Option<u64>,
5359 #[serde(default, skip_serializing_if = "Option::is_none")]
5370 pub thinking_display: Option<String>,
5371 #[serde(default, skip_serializing_if = "Option::is_none")]
5375 pub context_usage: Option<ContextUsage>,
5376 #[serde(default, skip_serializing_if = "Option::is_none")]
5384 pub usage_report: Option<Box<UsageReport>>,
5385 #[serde(default, skip_serializing_if = "Option::is_none")]
5392 pub local_command_run: Option<LocalCommandRun>,
5393 #[serde(default, skip_serializing_if = "Option::is_none")]
5399 pub local_command_outcome: Option<LocalCommandOutcome>,
5400 #[serde(default, skip_serializing_if = "Option::is_none")]
5401 pub is_meta: Option<bool>,
5402 #[serde(default, skip_serializing_if = "Option::is_none")]
5403 pub is_virtual: Option<bool>,
5404 #[serde(default, skip_serializing_if = "Option::is_none")]
5405 pub is_api_error_message: Option<bool>,
5406 #[serde(skip_serializing_if = "Option::is_none")]
5407 pub api_error_status: Option<u16>,
5408 #[serde(skip_serializing_if = "Option::is_none")]
5437 pub api_error: Option<String>,
5438 #[serde(default, skip_serializing_if = "Option::is_none")]
5445 pub api_error_code: Option<String>,
5446 #[serde(default, skip_serializing_if = "Option::is_none")]
5457 pub api_error_origin: Option<String>,
5458 #[serde(default, skip_serializing_if = "Option::is_none")]
5461 pub api_error_params: Option<ApiErrorParams>,
5462 #[serde(skip_serializing_if = "Option::is_none")]
5463 pub error_details: Option<String>,
5464 #[serde(skip_serializing_if = "Option::is_none")]
5465 pub advisor_model: Option<String>,
5466 #[serde(skip_serializing_if = "Option::is_none")]
5467 pub attribution_agent: Option<String>,
5468 #[serde(skip_serializing_if = "Option::is_none")]
5469 pub attribution_skill: Option<String>,
5470 #[serde(skip_serializing_if = "Option::is_none")]
5471 pub attribution_plugin: Option<String>,
5472 #[serde(skip_serializing_if = "Option::is_none")]
5473 pub attribution_mcp_server: Option<String>,
5474 #[serde(skip_serializing_if = "Option::is_none")]
5475 pub attribution_mcp_tool: Option<String>,
5476}
5477
5478#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
5482pub struct ApiErrorParams {
5483 #[serde(default, skip_serializing_if = "Option::is_none")]
5485 pub effort: Option<String>,
5486 #[serde(default, skip_serializing_if = "Option::is_none")]
5489 pub provider: Option<ApiErrorProvider>,
5490 #[serde(default, skip_serializing_if = "Option::is_none")]
5493 pub remedy: Option<ApiErrorRemedy>,
5494 #[serde(default, skip_serializing_if = "Option::is_none")]
5497 pub media: Option<ApiErrorMedia>,
5498 #[serde(default, skip_serializing_if = "Option::is_none")]
5500 pub media_reason: Option<ApiErrorMediaReason>,
5501 #[serde(default, skip_serializing_if = "Option::is_none")]
5509 pub rate_limit_info: Option<RateLimitInfo>,
5510}
5511
5512#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5514pub enum ApiErrorMedia {
5515 Image,
5516 Document,
5517 Unknown(String),
5519}
5520
5521impl ApiErrorMedia {
5522 pub fn as_str(&self) -> &str {
5523 match self {
5524 Self::Image => "image",
5525 Self::Document => "document",
5526 Self::Unknown(s) => s.as_str(),
5527 }
5528 }
5529}
5530
5531impl fmt::Display for ApiErrorMedia {
5532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5533 f.write_str(self.as_str())
5534 }
5535}
5536
5537impl From<&str> for ApiErrorMedia {
5538 fn from(s: &str) -> Self {
5539 match s {
5540 "image" => Self::Image,
5541 "document" => Self::Document,
5542 other => Self::Unknown(other.to_string()),
5543 }
5544 }
5545}
5546
5547impl Serialize for ApiErrorMedia {
5548 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5549 serializer.serialize_str(self.as_str())
5550 }
5551}
5552
5553impl<'de> Deserialize<'de> for ApiErrorMedia {
5554 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5555 let s = String::deserialize(deserializer)?;
5556 Ok(Self::from(s.as_str()))
5557 }
5558}
5559
5560#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5563pub enum ApiErrorMediaReason {
5564 Unprocessable,
5567 UnsupportedByModel,
5569 MediaBudget,
5572 Unknown(String),
5574}
5575
5576impl ApiErrorMediaReason {
5577 pub fn as_str(&self) -> &str {
5578 match self {
5579 Self::Unprocessable => "unprocessable",
5580 Self::UnsupportedByModel => "unsupported_by_model",
5581 Self::MediaBudget => "media_budget",
5582 Self::Unknown(s) => s.as_str(),
5583 }
5584 }
5585}
5586
5587impl fmt::Display for ApiErrorMediaReason {
5588 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5589 f.write_str(self.as_str())
5590 }
5591}
5592
5593impl From<&str> for ApiErrorMediaReason {
5594 fn from(s: &str) -> Self {
5595 match s {
5596 "unprocessable" => Self::Unprocessable,
5597 "unsupported_by_model" => Self::UnsupportedByModel,
5598 "media_budget" => Self::MediaBudget,
5599 other => Self::Unknown(other.to_string()),
5600 }
5601 }
5602}
5603
5604impl Serialize for ApiErrorMediaReason {
5605 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5606 serializer.serialize_str(self.as_str())
5607 }
5608}
5609
5610impl<'de> Deserialize<'de> for ApiErrorMediaReason {
5611 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5612 let s = String::deserialize(deserializer)?;
5613 Ok(Self::from(s.as_str()))
5614 }
5615}
5616
5617#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5619pub enum ApiErrorProvider {
5620 Bedrock,
5621 AnthropicAws,
5622 Mantle,
5623 AnthropicGoogleCloud,
5624 Vertex,
5625 Foundry,
5626 Gateway,
5627 Unknown(String),
5629}
5630
5631impl ApiErrorProvider {
5632 pub fn as_str(&self) -> &str {
5633 match self {
5634 Self::Bedrock => "bedrock",
5635 Self::AnthropicAws => "anthropicAws",
5636 Self::Mantle => "mantle",
5637 Self::AnthropicGoogleCloud => "anthropicGoogleCloud",
5638 Self::Vertex => "vertex",
5639 Self::Foundry => "foundry",
5640 Self::Gateway => "gateway",
5641 Self::Unknown(s) => s.as_str(),
5642 }
5643 }
5644}
5645
5646impl fmt::Display for ApiErrorProvider {
5647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5648 f.write_str(self.as_str())
5649 }
5650}
5651
5652impl From<&str> for ApiErrorProvider {
5653 fn from(s: &str) -> Self {
5654 match s {
5655 "bedrock" => Self::Bedrock,
5656 "anthropicAws" => Self::AnthropicAws,
5657 "mantle" => Self::Mantle,
5658 "anthropicGoogleCloud" => Self::AnthropicGoogleCloud,
5659 "vertex" => Self::Vertex,
5660 "foundry" => Self::Foundry,
5661 "gateway" => Self::Gateway,
5662 other => Self::Unknown(other.to_string()),
5663 }
5664 }
5665}
5666
5667impl Serialize for ApiErrorProvider {
5668 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5669 serializer.serialize_str(self.as_str())
5670 }
5671}
5672
5673impl<'de> Deserialize<'de> for ApiErrorProvider {
5674 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5675 let s = String::deserialize(deserializer)?;
5676 Ok(Self::from(s.as_str()))
5677 }
5678}
5679
5680#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5682pub enum ApiErrorRemedy {
5683 RefreshCommand,
5686 RefreshCredentials,
5688 Adc,
5691 GatewayToken,
5694 HostManaged,
5696 ModelAccess,
5699 Unknown(String),
5701}
5702
5703impl ApiErrorRemedy {
5704 pub fn as_str(&self) -> &str {
5705 match self {
5706 Self::RefreshCommand => "refresh_command",
5707 Self::RefreshCredentials => "refresh_credentials",
5708 Self::Adc => "adc",
5709 Self::GatewayToken => "gateway_token",
5710 Self::HostManaged => "host_managed",
5711 Self::ModelAccess => "model_access",
5712 Self::Unknown(s) => s.as_str(),
5713 }
5714 }
5715}
5716
5717impl fmt::Display for ApiErrorRemedy {
5718 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5719 f.write_str(self.as_str())
5720 }
5721}
5722
5723impl From<&str> for ApiErrorRemedy {
5724 fn from(s: &str) -> Self {
5725 match s {
5726 "refresh_command" => Self::RefreshCommand,
5727 "refresh_credentials" => Self::RefreshCredentials,
5728 "adc" => Self::Adc,
5729 "gateway_token" => Self::GatewayToken,
5730 "host_managed" => Self::HostManaged,
5731 "model_access" => Self::ModelAccess,
5732 other => Self::Unknown(other.to_string()),
5733 }
5734 }
5735}
5736
5737impl Serialize for ApiErrorRemedy {
5738 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5739 serializer.serialize_str(self.as_str())
5740 }
5741}
5742
5743impl<'de> Deserialize<'de> for ApiErrorRemedy {
5744 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5745 let s = String::deserialize(deserializer)?;
5746 Ok(Self::from(s.as_str()))
5747 }
5748}
5749
5750#[derive(Debug, Clone, Serialize, Deserialize)]
5752pub struct AssistantMessageContent {
5753 pub id: String,
5754 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
5756 pub message_type: Option<String>,
5757 pub role: MessageRole,
5758 pub model: String,
5759 pub content: Vec<ContentBlock>,
5760 #[serde(skip_serializing_if = "Option::is_none")]
5761 pub stop_reason: Option<StopReason>,
5762 #[serde(skip_serializing_if = "Option::is_none")]
5763 pub stop_sequence: Option<String>,
5764 #[serde(skip_serializing_if = "Option::is_none")]
5765 pub usage: Option<AssistantUsage>,
5766 #[serde(skip_serializing_if = "Option::is_none")]
5768 pub stop_details: Option<Value>,
5769 #[serde(skip_serializing_if = "Option::is_none")]
5771 pub context_management: Option<Value>,
5772}
5773
5774#[derive(Debug, Clone, Serialize, Deserialize)]
5776pub struct AssistantUsage {
5777 #[serde(default)]
5779 pub input_tokens: u32,
5780
5781 #[serde(default)]
5783 pub output_tokens: u32,
5784
5785 #[serde(default)]
5787 pub cache_creation_input_tokens: u32,
5788
5789 #[serde(default)]
5791 pub cache_read_input_tokens: u32,
5792
5793 #[serde(skip_serializing_if = "Option::is_none")]
5795 pub service_tier: Option<String>,
5796
5797 #[serde(skip_serializing_if = "Option::is_none")]
5799 pub cache_creation: Option<CacheCreationDetails>,
5800
5801 #[serde(skip_serializing_if = "Option::is_none")]
5803 pub inference_geo: Option<String>,
5804
5805 #[serde(default, skip_serializing_if = "Option::is_none")]
5808 pub speed: Option<String>,
5809
5810 #[serde(default, skip_serializing_if = "Option::is_none")]
5813 pub fallback_credit: Option<Value>,
5814}
5815
5816#[derive(Debug, Clone, Serialize, Deserialize)]
5818pub struct CacheCreationDetails {
5819 #[serde(default)]
5821 pub ephemeral_1h_input_tokens: u32,
5822
5823 #[serde(default)]
5825 pub ephemeral_5m_input_tokens: u32,
5826}
5827
5828#[cfg(test)]
5829mod tests {
5830 use crate::io::ClaudeOutput;
5831
5832 #[test]
5833 fn test_subagent_usage_rollup_accumulates_task_results() {
5834 use super::SubagentUsageRollup;
5835
5836 let mut rollup = SubagentUsageRollup::default();
5837
5838 let task_result = r#"{"type":"user","message":{"role":"user","content":[]},"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1","tool_use_result":{"status":"completed","prompt":"Compute 6 times 7.","agentId":"ab52f22445470d454","agentType":"general-purpose","resolvedModel":"claude-sonnet-4-6","totalDurationMs":1853,"totalTokens":10201,"totalToolUseCount":3}}"#;
5839 let output: ClaudeOutput = serde_json::from_str(task_result).unwrap();
5840 assert!(rollup.observe(&output));
5841 assert_eq!(rollup.subagent_tokens, 10201);
5842 assert_eq!(rollup.agent_count, 1);
5843 assert_eq!(rollup.tool_uses, 3);
5844 assert_eq!(rollup.duration_ms, 1853);
5845
5846 assert!(!rollup.observe(&output));
5848 assert_eq!(rollup.agent_count, 1);
5849 assert_eq!(rollup.subagent_tokens, 10201);
5850
5851 let second = r#"{"type":"user","message":{"role":"user","content":[]},"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1","tool_use_result":{"status":"completed","agentId":"ffff00001111","totalDurationMs":100,"totalTokens":500,"totalToolUseCount":1}}"#;
5853 let output: ClaudeOutput = serde_json::from_str(second).unwrap();
5854 assert!(rollup.observe(&output));
5855 assert_eq!(rollup.agent_count, 2);
5856 assert_eq!(rollup.subagent_tokens, 10701);
5857 }
5858
5859 #[test]
5860 fn test_subagent_usage_rollup_ignores_non_task_results() {
5861 use super::SubagentUsageRollup;
5862
5863 let mut rollup = SubagentUsageRollup::default();
5864
5865 let tool_search = r#"{"type":"user","message":{"role":"user","content":[]},"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1","tool_use_result":{"matches":["TaskCreate"],"query":"select:TaskCreate","total_deferred_tools":27}}"#;
5868 let output: ClaudeOutput = serde_json::from_str(tool_search).unwrap();
5869 assert!(!rollup.observe(&output));
5870
5871 let plain = r#"{"type":"user","message":{"role":"user","content":[]},"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1"}"#;
5873 let output: ClaudeOutput = serde_json::from_str(plain).unwrap();
5874 assert!(!rollup.observe(&output));
5875
5876 let system = r#"{"type":"system","subtype":"status","status":null,"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1"}"#;
5878 let output: ClaudeOutput = serde_json::from_str(system).unwrap();
5879 assert!(!rollup.observe(&output));
5880
5881 assert_eq!(rollup, SubagentUsageRollup::default());
5882 }
5883
5884 #[test]
5885 fn test_subagent_usage_rollup_over_captured_session() {
5886 use super::SubagentUsageRollup;
5887
5888 let mut rollup = SubagentUsageRollup::default();
5889 let fixture =
5890 include_str!("../../test_cases/subagent_sessions/general_purpose_compute.jsonl");
5891 for line in fixture.lines().filter(|l| !l.trim().is_empty()) {
5892 if let Ok(output) = serde_json::from_str::<ClaudeOutput>(line) {
5893 rollup.observe(&output);
5894 }
5895 }
5896 assert_eq!(rollup.agent_count, 1);
5897 assert_eq!(rollup.subagent_tokens, 10201);
5898 }
5899
5900 #[test]
5901 fn test_system_message_init() {
5902 let json = r#"{
5903 "type": "system",
5904 "subtype": "init",
5905 "session_id": "test-session-123",
5906 "cwd": "/home/user/project",
5907 "model": "claude-sonnet-4",
5908 "tools": ["Bash", "Read", "Write"],
5909 "mcp_servers": [],
5910 "slash_commands": ["compact", "cost", "review"],
5911 "agents": ["Bash", "Explore", "Plan"],
5912 "plugins": [{"name": "rust-analyzer-lsp", "path": "/home/user/.claude/plugins/rust-analyzer-lsp/1.0.0"}],
5913 "skills": [],
5914 "claude_code_version": "2.1.15",
5915 "apiKeySource": "none",
5916 "output_style": "default",
5917 "permissionMode": "default"
5918 }"#;
5919
5920 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
5921 if let ClaudeOutput::System(sys) = output {
5922 assert!(sys.is_init());
5923 assert!(!sys.is_status());
5924 assert!(!sys.is_compact_boundary());
5925
5926 let init = sys.as_init().expect("Should parse as init");
5927 assert_eq!(init.session_id, "test-session-123");
5928 assert_eq!(init.cwd, Some("/home/user/project".to_string()));
5929 assert_eq!(init.model, Some("claude-sonnet-4".to_string()));
5930 assert_eq!(init.tools, vec!["Bash", "Read", "Write"]);
5931 assert_eq!(init.slash_commands, vec!["compact", "cost", "review"]);
5932 assert_eq!(init.agents, vec!["Bash", "Explore", "Plan"]);
5933 assert_eq!(init.plugins.len(), 1);
5934 assert_eq!(init.plugins[0].name, "rust-analyzer-lsp");
5935 assert_eq!(init.claude_code_version, Some("2.1.15".to_string()));
5936 assert_eq!(init.api_key_source, Some(super::ApiKeySource::None));
5937 assert_eq!(init.output_style, Some(super::OutputStyle::Default));
5938 assert_eq!(
5939 init.permission_mode,
5940 Some(super::InitPermissionMode::Default)
5941 );
5942 } else {
5943 panic!("Expected System message");
5944 }
5945 }
5946
5947 #[test]
5948 fn test_system_message_init_from_real_capture() {
5949 let json = include_str!("../../test_cases/tool_use_captures/tool_msg_0.json");
5950 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
5951 if let ClaudeOutput::System(sys) = output {
5952 let init = sys.as_init().expect("Should parse real init capture");
5953 assert_eq!(init.slash_commands.len(), 8);
5954 assert!(init.slash_commands.contains(&"compact".to_string()));
5955 assert!(init.slash_commands.contains(&"review".to_string()));
5956 assert_eq!(init.agents.len(), 5);
5957 assert!(init.agents.contains(&"Bash".to_string()));
5958 assert!(init.agents.contains(&"Explore".to_string()));
5959 assert_eq!(init.plugins.len(), 1);
5960 assert_eq!(init.plugins[0].name, "rust-analyzer-lsp");
5961 assert_eq!(init.claude_code_version, Some("2.1.15".to_string()));
5962 } else {
5963 panic!("Expected System message");
5964 }
5965 }
5966
5967 #[test]
5968 fn test_system_message_status() {
5969 let json = r#"{
5970 "type": "system",
5971 "subtype": "status",
5972 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
5973 "status": "compacting",
5974 "uuid": "32eb9f9d-5ef7-47ff-8fce-bbe22fe7ed93"
5975 }"#;
5976
5977 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
5978 if let ClaudeOutput::System(sys) = output {
5979 assert!(sys.is_status());
5980 assert!(!sys.is_init());
5981
5982 let status = sys.as_status().expect("Should parse as status");
5983 assert_eq!(status.session_id, "879c1a88-3756-4092-aa95-0020c4ed9692");
5984 assert_eq!(status.status, Some(super::StatusMessageStatus::Compacting));
5985 assert_eq!(
5986 status.uuid,
5987 Some("32eb9f9d-5ef7-47ff-8fce-bbe22fe7ed93".to_string())
5988 );
5989 } else {
5990 panic!("Expected System message");
5991 }
5992 }
5993
5994 #[test]
5995 fn test_system_message_status_null() {
5996 let json = r#"{
5997 "type": "system",
5998 "subtype": "status",
5999 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6000 "status": null,
6001 "uuid": "92d9637e-d00e-418e-acd2-a504e3861c6a"
6002 }"#;
6003
6004 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6005 if let ClaudeOutput::System(sys) = output {
6006 let status = sys.as_status().expect("Should parse as status");
6007 assert_eq!(status.status, None);
6008 } else {
6009 panic!("Expected System message");
6010 }
6011 }
6012
6013 #[test]
6014 fn test_system_message_task_started() {
6015 let json = r#"{
6016 "type": "system",
6017 "subtype": "task_started",
6018 "session_id": "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9",
6019 "task_id": "b6daf3f",
6020 "task_type": "local_bash",
6021 "tool_use_id": "toolu_011rfSTFumpJZdCCfzeD7jaS",
6022 "description": "Wait for CI on PR #12",
6023 "uuid": "c4243261-c128-4747-b8c3-5e1c7c10eeb8"
6024 }"#;
6025
6026 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6027 if let ClaudeOutput::System(sys) = output {
6028 assert!(sys.is_task_started());
6029 assert!(!sys.is_task_progress());
6030 assert!(!sys.is_task_notification());
6031
6032 let task = sys.as_task_started().expect("Should parse as task_started");
6033 assert_eq!(task.session_id, "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9");
6034 assert_eq!(task.task_id, "b6daf3f");
6035 assert_eq!(task.task_type, Some(super::TaskType::LocalBash));
6036 assert_eq!(
6037 task.tool_use_id.as_deref(),
6038 Some("toolu_011rfSTFumpJZdCCfzeD7jaS")
6039 );
6040 assert_eq!(task.description, "Wait for CI on PR #12");
6041 } else {
6042 panic!("Expected System message");
6043 }
6044 }
6045
6046 #[test]
6047 fn test_system_message_task_started_agent() {
6048 let json = r#"{
6049 "type": "system",
6050 "subtype": "task_started",
6051 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6052 "task_id": "a4a7e0906e5fc64cc",
6053 "task_type": "local_agent",
6054 "tool_use_id": "toolu_01SFz9FwZ1cYgCSy8vRM7wep",
6055 "description": "Explore Scene/ArrayScene duplication",
6056 "uuid": "85a39f5a-e4d4-47f7-9a6d-1125f1a8035f"
6057 }"#;
6058
6059 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6060 if let ClaudeOutput::System(sys) = output {
6061 let task = sys.as_task_started().expect("Should parse as task_started");
6062 assert_eq!(task.task_type, Some(super::TaskType::LocalAgent));
6063 assert_eq!(task.task_id, "a4a7e0906e5fc64cc");
6064 } else {
6065 panic!("Expected System message");
6066 }
6067 }
6068
6069 #[test]
6070 fn test_system_message_task_progress() {
6071 let json = r#"{
6072 "type": "system",
6073 "subtype": "task_progress",
6074 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6075 "task_id": "a4a7e0906e5fc64cc",
6076 "tool_use_id": "toolu_01SFz9FwZ1cYgCSy8vRM7wep",
6077 "description": "Reading src/jplephem/chebyshev.rs",
6078 "last_tool_name": "Read",
6079 "usage": {
6080 "duration_ms": 13996,
6081 "tool_uses": 9,
6082 "total_tokens": 38779
6083 },
6084 "uuid": "85a39f5a-e4d4-47f7-9a6d-1125f1a8035f"
6085 }"#;
6086
6087 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6088 if let ClaudeOutput::System(sys) = output {
6089 assert!(sys.is_task_progress());
6090 assert!(!sys.is_task_started());
6091
6092 let progress = sys
6093 .as_task_progress()
6094 .expect("Should parse as task_progress");
6095 assert_eq!(progress.task_id, "a4a7e0906e5fc64cc");
6096 assert_eq!(progress.description, "Reading src/jplephem/chebyshev.rs");
6097 assert_eq!(progress.last_tool_name.as_deref(), Some("Read"));
6098 assert_eq!(progress.usage.duration_ms, 13996);
6099 assert_eq!(progress.usage.tool_uses, 9);
6100 assert_eq!(progress.usage.total_tokens, 38779);
6101 } else {
6102 panic!("Expected System message");
6103 }
6104 }
6105
6106 #[test]
6107 fn test_system_message_task_notification_completed() {
6108 let json = r#"{
6109 "type": "system",
6110 "subtype": "task_notification",
6111 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6112 "task_id": "a0ba761e9dc9c316f",
6113 "tool_use_id": "toolu_01Ho6XVXFLVNjTQ9YqowdBXW",
6114 "status": "completed",
6115 "summary": "Agent \"Write Hipparcos data source doc\" completed",
6116 "output_file": "",
6117 "usage": {
6118 "duration_ms": 172300,
6119 "tool_uses": 11,
6120 "total_tokens": 42005
6121 },
6122 "uuid": "269f49b9-218d-4c8d-9f7e-3a5383a0c5b2"
6123 }"#;
6124
6125 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6126 if let ClaudeOutput::System(sys) = output {
6127 assert!(sys.is_task_notification());
6128
6129 let notif = sys
6130 .as_task_notification()
6131 .expect("Should parse as task_notification");
6132 assert_eq!(notif.status, super::TaskStatus::Completed);
6133 assert_eq!(
6134 notif.summary,
6135 "Agent \"Write Hipparcos data source doc\" completed"
6136 );
6137 assert_eq!(notif.output_file, Some("".to_string()));
6138 assert_eq!(
6139 notif.tool_use_id,
6140 Some("toolu_01Ho6XVXFLVNjTQ9YqowdBXW".to_string())
6141 );
6142 let usage = notif.usage.expect("Should have usage");
6143 assert_eq!(usage.duration_ms, 172300);
6144 assert_eq!(usage.tool_uses, 11);
6145 assert_eq!(usage.total_tokens, 42005);
6146 } else {
6147 panic!("Expected System message");
6148 }
6149 }
6150
6151 #[test]
6152 fn test_system_message_task_notification_failed_no_usage() {
6153 let json = r#"{
6154 "type": "system",
6155 "subtype": "task_notification",
6156 "session_id": "ea629737-3c36-48a8-a1c4-ad761ad35784",
6157 "task_id": "b98f6a3",
6158 "status": "failed",
6159 "summary": "Background command \"Run FSM calibration\" failed with exit code 1",
6160 "output_file": "/tmp/claude-1000/tasks/b98f6a3.output"
6161 }"#;
6162
6163 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6164 if let ClaudeOutput::System(sys) = output {
6165 let notif = sys
6166 .as_task_notification()
6167 .expect("Should parse as task_notification");
6168 assert_eq!(notif.status, super::TaskStatus::Failed);
6169 assert!(notif.tool_use_id.is_none());
6170 assert!(notif.usage.is_none());
6171 assert_eq!(
6172 notif.output_file,
6173 Some("/tmp/claude-1000/tasks/b98f6a3.output".to_string())
6174 );
6175 } else {
6176 panic!("Expected System message");
6177 }
6178 }
6179
6180 #[test]
6186 fn test_task_messages_roundtrip_through_value() {
6187 let cases = [
6188 r#"{"type":"system","subtype":"task_started","session_id":"s1",
6189 "task_id":"t1","task_type":"local_bash","tool_use_id":"tu1",
6190 "description":"Sleep 3s","uuid":"u1"}"#,
6191 r#"{"type":"system","subtype":"task_progress","session_id":"s1",
6192 "task_id":"t1","tool_use_id":"tu1","description":"Running ls",
6193 "last_tool_name":"Bash",
6194 "usage":{"duration_ms":100,"tool_uses":1,"total_tokens":500},
6195 "uuid":"u2"}"#,
6196 r#"{"type":"system","subtype":"task_notification","session_id":"s1",
6197 "task_id":"t1","tool_use_id":"tu1","status":"completed",
6198 "summary":"done","output_file":"",
6199 "usage":{"duration_ms":100,"tool_uses":1,"total_tokens":500},
6200 "uuid":"u3"}"#,
6201 ];
6202
6203 for json in cases {
6204 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6205 let value = serde_json::to_value(&output).unwrap();
6206 let reparsed: ClaudeOutput = serde_json::from_value(value).unwrap();
6207
6208 let ClaudeOutput::System(sys) = reparsed else {
6209 panic!("Expected System variant after round-trip");
6210 };
6211
6212 match sys.subtype {
6213 super::SystemSubtype::TaskStarted => {
6214 assert!(
6215 sys.as_task_started().is_some(),
6216 "as_task_started failed after round-trip"
6217 );
6218 }
6219 super::SystemSubtype::TaskProgress => {
6220 assert!(
6221 sys.as_task_progress().is_some(),
6222 "as_task_progress failed after round-trip"
6223 );
6224 }
6225 super::SystemSubtype::TaskNotification => {
6226 assert!(
6227 sys.as_task_notification().is_some(),
6228 "as_task_notification failed after round-trip"
6229 );
6230 }
6231 other => panic!("unexpected subtype after round-trip: {other:?}"),
6232 }
6233 }
6234 }
6235
6236 #[test]
6237 fn test_system_message_compact_boundary() {
6238 let json = r#"{
6239 "type": "system",
6240 "subtype": "compact_boundary",
6241 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6242 "compact_metadata": {
6243 "pre_tokens": 155285,
6244 "trigger": "auto"
6245 },
6246 "uuid": "a67780d5-74cb-48b1-9137-7a6e7cee45d7"
6247 }"#;
6248
6249 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6250 if let ClaudeOutput::System(sys) = output {
6251 assert!(sys.is_compact_boundary());
6252 assert!(!sys.is_init());
6253 assert!(!sys.is_status());
6254
6255 let compact = sys
6256 .as_compact_boundary()
6257 .expect("Should parse as compact_boundary");
6258 assert_eq!(compact.session_id, "879c1a88-3756-4092-aa95-0020c4ed9692");
6259 assert_eq!(compact.compact_metadata.pre_tokens, 155285);
6260 assert_eq!(
6261 compact.compact_metadata.trigger,
6262 super::CompactionTrigger::Auto
6263 );
6264 assert!(compact.summary.is_none());
6266 assert!(compact.leaf_message_count.is_none());
6267 assert!(compact.duration_ms.is_none());
6268 } else {
6269 panic!("Expected System message");
6270 }
6271 }
6272
6273 #[test]
6274 fn test_compact_boundary_with_summary_stats() {
6275 let json = r#"{
6277 "type": "system",
6278 "subtype": "compact_boundary",
6279 "session_id": "s1",
6280 "compact_metadata": { "pre_tokens": 1000, "trigger": "manual" },
6281 "summary": "Summarized the earlier exploration.",
6282 "leaf_message_count": 42,
6283 "duration_ms": 1234,
6284 "uuid": "u1"
6285 }"#;
6286 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6287 let ClaudeOutput::System(sys) = output else {
6288 panic!("Expected System message");
6289 };
6290 let compact = sys.as_compact_boundary().expect("compact_boundary");
6291 assert_eq!(
6292 compact.summary.as_deref(),
6293 Some("Summarized the earlier exploration.")
6294 );
6295 assert_eq!(compact.leaf_message_count, Some(42));
6296 assert_eq!(compact.duration_ms, Some(1234));
6297
6298 let json_alt = r#"{
6301 "type": "system",
6302 "subtype": "compact_boundary",
6303 "session_id": "s2",
6304 "compact_metadata": { "pre_tokens": 2000, "trigger": "auto" },
6305 "content": "alt-key summary",
6306 "message_count": 7
6307 }"#;
6308 let output: ClaudeOutput = serde_json::from_str(json_alt).unwrap();
6309 let ClaudeOutput::System(sys) = output else {
6310 panic!("Expected System message");
6311 };
6312 let compact = sys.as_compact_boundary().expect("compact_boundary");
6313 assert_eq!(compact.summary.as_deref(), Some("alt-key summary"));
6314 assert_eq!(compact.leaf_message_count, Some(7));
6315 }
6316
6317 #[test]
6318 fn test_init_message_with_new_fields() {
6319 let json = r#"{
6320 "type": "system",
6321 "subtype": "init",
6322 "session_id": "test-session",
6323 "cwd": "/home/user",
6324 "model": "claude-opus-4-7",
6325 "tools": ["Bash"],
6326 "mcp_servers": [],
6327 "permissionMode": "default",
6328 "apiKeySource": "none",
6329 "uuid": "44841a0d-182d-493a-86b5-79800d3d9665",
6330 "memory_paths": {"auto": "/home/user/.claude/projects/memory/"},
6331 "fast_mode_state": "off",
6332 "plugins": [{"name": "lsp", "path": "/plugins/lsp", "source": "lsp@official"}],
6333 "claude_code_version": "2.1.117"
6334 }"#;
6335
6336 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6337 if let ClaudeOutput::System(sys) = output {
6338 let init = sys.as_init().expect("Should parse as init");
6339 assert_eq!(
6340 init.uuid.as_deref(),
6341 Some("44841a0d-182d-493a-86b5-79800d3d9665")
6342 );
6343 assert!(init.memory_paths.is_some());
6344 assert_eq!(init.fast_mode_state.as_deref(), Some("off"));
6345 assert_eq!(init.plugins[0].source.as_deref(), Some("lsp@official"));
6346 assert_eq!(init.claude_code_version.as_deref(), Some("2.1.117"));
6347 } else {
6348 panic!("Expected System message");
6349 }
6350 }
6351
6352 #[test]
6353 fn test_assistant_message_with_new_fields() {
6354 let json = r#"{
6355 "type": "assistant",
6356 "message": {
6357 "id": "msg_1",
6358 "type": "message",
6359 "role": "assistant",
6360 "model": "claude-opus-4-7",
6361 "content": [{"type": "text", "text": "Hello"}],
6362 "stop_reason": "end_turn",
6363 "stop_details": null,
6364 "context_management": null,
6365 "usage": {
6366 "input_tokens": 100,
6367 "output_tokens": 10,
6368 "cache_creation_input_tokens": 50,
6369 "cache_read_input_tokens": 0,
6370 "service_tier": "standard",
6371 "inference_geo": "not_available"
6372 }
6373 },
6374 "session_id": "abc",
6375 "uuid": "msg-uuid-123"
6376 }"#;
6377
6378 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6379 if let ClaudeOutput::Assistant(asst) = output {
6380 assert_eq!(asst.message.stop_details, None);
6381 assert_eq!(asst.message.context_management, None);
6382 let usage = asst.message.usage.unwrap();
6383 assert_eq!(usage.inference_geo.as_deref(), Some("not_available"));
6384 } else {
6385 panic!("Expected Assistant message");
6386 }
6387 }
6388
6389 #[test]
6390 fn test_user_message_with_new_fields() {
6391 let json = r#"{
6392 "type": "user",
6393 "message": {
6394 "role": "user",
6395 "content": [{"type": "text", "text": "Hello"}]
6396 },
6397 "session_id": "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9",
6398 "parent_tool_use_id": "toolu_123",
6399 "uuid": "user-msg-456"
6400 }"#;
6401
6402 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6403 if let ClaudeOutput::User(user) = output {
6404 assert_eq!(user.parent_tool_use_id.as_deref(), Some("toolu_123"));
6405 assert_eq!(user.uuid.as_deref(), Some("user-msg-456"));
6406 } else {
6407 panic!("Expected User message");
6408 }
6409 }
6410
6411 #[test]
6417 fn test_user_message_preserves_tool_use_result_and_timestamp() {
6418 let json = r#"{
6419 "type":"user",
6420 "message":{"role":"user","content":[{"type":"tool_result","content":"User has answered your questions: . You can now continue with the user's answers in mind.","tool_use_id":"toolu_01331duMqP2PrRaqR2yWa8e4"}]},
6421 "parent_tool_use_id":null,
6422 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6423 "uuid":"8ef6e997-a849-4d15-bed3-2837c3d3f4cd",
6424 "timestamp":"2026-05-12T23:12:04.121Z",
6425 "tool_use_result":{"questions":[{"question":"Which color do you prefer?","header":"Color","options":[{"label":"Red","description":"A warm color"},{"label":"Blue","description":"A cool color"}],"multiSelect":false}],"answers":{"Color":"Blue"}}
6426 }"#;
6427
6428 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6429 let user = match output {
6430 ClaudeOutput::User(u) => u,
6431 other => panic!("Expected User message, got {:?}", other.message_type()),
6432 };
6433
6434 assert_eq!(user.timestamp.as_deref(), Some("2026-05-12T23:12:04.121Z"));
6435 let raw = user
6436 .tool_use_result
6437 .as_ref()
6438 .expect("tool_use_result must be captured");
6439 assert_eq!(raw["answers"]["Color"], "Blue");
6440 assert_eq!(raw["questions"][0]["header"], "Color");
6441
6442 let reser: serde_json::Value = serde_json::to_value(&user).unwrap();
6446 assert_eq!(reser["timestamp"], "2026-05-12T23:12:04.121Z");
6447 assert_eq!(reser["tool_use_result"]["answers"]["Color"], "Blue");
6448 assert_eq!(
6449 reser["tool_use_result"]["questions"][0]["question"],
6450 "Which color do you prefer?"
6451 );
6452
6453 let typed: crate::AskUserQuestionInput = user
6456 .tool_use_result_as::<crate::AskUserQuestionInput>()
6457 .expect("tool_use_result present")
6458 .expect("AskUserQuestionInput parses");
6459 assert_eq!(typed.questions.len(), 1);
6460 assert_eq!(typed.questions[0].header, "Color");
6461 let answers = typed.answers.expect("answers populated");
6462 assert_eq!(answers.get("Color").map(String::as_str), Some("Blue"));
6463 }
6464
6465 #[test]
6468 fn test_user_message_without_tool_use_result_omits_field() {
6469 let json = r#"{
6470 "type":"user",
6471 "message":{"role":"user","content":[{"type":"text","text":"hello"}]},
6472 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d"
6473 }"#;
6474
6475 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6476 let user = match output {
6477 ClaudeOutput::User(u) => u,
6478 _ => panic!("Expected User message"),
6479 };
6480 assert!(user.tool_use_result.is_none());
6481 assert!(user.timestamp.is_none());
6482
6483 let reser = serde_json::to_value(&user).unwrap();
6484 assert!(reser.get("tool_use_result").is_none());
6485 assert!(reser.get("timestamp").is_none());
6486 }
6487
6488 #[test]
6492 fn test_subagent_result_exposes_token_accounting() {
6493 let json = r#"{
6494 "type":"user",
6495 "message":{"role":"user","content":[{"tool_use_id":"toolu_01","type":"tool_result","content":[{"type":"text","text":"21"}]}]},
6496 "session_id":"d3fc5942-75e5-4aa1-a87d-b9484a176541",
6497 "tool_use_result":{
6498 "status":"completed",
6499 "prompt":"Count the .rs files.",
6500 "agentId":"ac4f0276e9d4b6232",
6501 "agentType":"Explore",
6502 "content":[{"type":"text","text":"21"}],
6503 "resolvedModel":"claude-haiku-4-5-20251001",
6504 "totalDurationMs":6869,
6505 "totalTokens":7834,
6506 "totalToolUseCount":1,
6507 "usage":{"input_tokens":6,"cache_creation_input_tokens":125,"cache_read_input_tokens":7699,"output_tokens":4,"service_tier":"standard"},
6508 "toolStats":{"readCount":0,"searchCount":0,"bashCount":1,"editFileCount":0,"linesAdded":0,"linesRemoved":0,"otherToolCount":0}
6509 }
6510 }"#;
6511
6512 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6513 let user = match output {
6514 ClaudeOutput::User(u) => u,
6515 _ => panic!("Expected User message"),
6516 };
6517
6518 let result = user.subagent_result().expect("subagent result parses");
6519 assert_eq!(result.agent_type.as_deref(), Some("Explore"));
6520 assert_eq!(
6521 result.resolved_model.as_deref(),
6522 Some("claude-haiku-4-5-20251001")
6523 );
6524 assert_eq!(result.total_tokens, Some(7834));
6525 assert_eq!(result.total_duration_ms, Some(6869));
6526 assert_eq!(result.total_tool_use_count, Some(1));
6527
6528 let usage = result.usage.expect("nested usage present");
6529 assert_eq!(usage.input_tokens, 6);
6530 assert_eq!(usage.cache_read_input_tokens, 7699);
6531
6532 let stats = result.tool_stats.expect("toolStats present");
6533 assert_eq!(stats.bash_count, 1);
6534 }
6535
6536 #[test]
6540 fn test_subagent_result_absent_for_non_task_result() {
6541 let json = r#"{
6542 "type":"user",
6543 "message":{"role":"user","content":[{"type":"text","text":"hi"}]},
6544 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6545 "tool_use_result":{"questions":[],"answers":{"Color":"Blue"}}
6546 }"#;
6547
6548 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6549 let user = match output {
6550 ClaudeOutput::User(u) => u,
6551 _ => panic!("Expected User message"),
6552 };
6553
6554 let result = user.subagent_result().expect("lenient parse");
6555 assert_eq!(result.total_tokens, None);
6556 assert_eq!(result.agent_type, None);
6557 }
6558
6559 #[test]
6560 fn test_init_fast_mode_reason_and_mcp_server_errors_fully_wrapped() {
6561 use serde_json::Value;
6562
6563 let raw: Value = serde_json::from_str(
6564 r#"{
6565 "type":"system","subtype":"init","session_id":"s1","uuid":"u1",
6566 "fast_mode_state":"off",
6567 "fast_mode_disabled_reason":"not_first_party",
6568 "mcp_server_errors":[{"name":"broken","type":"invalid_config","message":"url entry with no type"}]
6569 }"#,
6570 )
6571 .unwrap();
6572 crate::io::assert_fully_wrapped(&raw);
6573
6574 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6575 let ClaudeOutput::System(sys) = output else {
6576 panic!("expected System");
6577 };
6578 let init = sys.as_init().expect("parses as init");
6579 assert_eq!(
6580 init.fast_mode_disabled_reason,
6581 Some(crate::FastModeDisabledReason::NotFirstParty)
6582 );
6583 let errs = init.mcp_server_errors.unwrap();
6584 assert_eq!(errs.len(), 1);
6585 assert_eq!(errs[0].name, "broken");
6586 assert_eq!(errs[0].error_type, "invalid_config");
6587 }
6588
6589 #[test]
6590 fn test_code_change_published_fully_wrapped() {
6591 use super::{KnownSystemEvent, SystemSubtype};
6592 use serde_json::Value;
6593
6594 let raw: Value = serde_json::from_str(
6595 r#"{
6596 "type":"system","subtype":"code_change_published",
6597 "provider":"github","url":"https://github.com/owner/repo/pull/42",
6598 "repo":"owner/repo","identifier":"42",
6599 "uuid":"u1","session_id":"s1"
6600 }"#,
6601 )
6602 .unwrap();
6603 crate::io::assert_fully_wrapped(&raw);
6604
6605 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6606 let ClaudeOutput::System(sys) = output else {
6607 panic!("expected System");
6608 };
6609 assert_eq!(sys.subtype, SystemSubtype::CodeChangePublished);
6610 let Some(KnownSystemEvent::CodeChangePublished(msg)) = sys.as_known_system_event() else {
6611 panic!("expected CodeChangePublished event");
6612 };
6613 assert_eq!(msg.provider, "github");
6614 assert_eq!(msg.repo, "owner/repo");
6615 assert_eq!(msg.identifier, "42");
6616
6617 assert!(sys.is_code_change_published());
6618 assert!(!sys.is_vcs_state_changed());
6619 let direct = sys.as_code_change_published().expect("direct accessor");
6620 assert_eq!(direct.url, "https://github.com/owner/repo/pull/42");
6621 assert!(sys.as_vcs_state_changed().is_none());
6622 }
6623
6624 #[test]
6625 fn test_feedback_draft_queued_fully_wrapped() {
6626 use super::{KnownSystemEvent, SystemSubtype};
6627 use serde_json::Value;
6628
6629 let raw: Value = serde_json::from_str(
6630 r#"{
6631 "type":"system","subtype":"feedback_draft_queued",
6632 "draft_id":"draft-1","draft_type":"bug_report",
6633 "title":"Tool output was truncated",
6634 "details_preview":"The last command omitted its final lines",
6635 "uuid":"u1","session_id":"s1","future_field":"preserved"
6636 }"#,
6637 )
6638 .unwrap();
6639 crate::io::assert_fully_wrapped(&raw);
6640
6641 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6642 let ClaudeOutput::System(sys) = output else {
6643 panic!("expected System");
6644 };
6645 assert_eq!(sys.subtype, SystemSubtype::FeedbackDraftQueued);
6646 assert!(sys.is_feedback_draft_queued());
6647 assert!(!sys.is_vcs_state_changed());
6648
6649 let direct = sys
6650 .as_feedback_draft_queued()
6651 .expect("direct typed accessor");
6652 assert_eq!(direct.draft_id, "draft-1");
6653 assert_eq!(direct.draft_type, "bug_report");
6654 assert_eq!(direct.extra["future_field"], "preserved");
6655
6656 let Some(KnownSystemEvent::FeedbackDraftQueued(known)) = sys.as_known_system_event() else {
6657 panic!("expected FeedbackDraftQueued event");
6658 };
6659 assert_eq!(known.title, "Tool output was truncated");
6660 assert_eq!(
6661 sys.typed_value().expect("typed value")["future_field"],
6662 "preserved"
6663 );
6664 }
6665
6666 #[test]
6667 fn test_cloud_session_delta_fully_wrapped() {
6668 use super::{KnownSystemEvent, SystemSubtype};
6669 use serde_json::Value;
6670
6671 let raw: Value = serde_json::from_str(
6672 r#"{
6673 "type":"system","subtype":"cloud_session_delta",
6674 "seq":3,"changed":["serving","connection"],
6675 "cloud_session":{"id":"session_abc","view_url":"https://example.invalid/s/abc",
6676 "serving":{"state":"on"},"connection":{"state":"live"}},
6677 "uuid":"u1","session_id":"s1","future_field":"preserved"
6678 }"#,
6679 )
6680 .unwrap();
6681 crate::io::assert_fully_wrapped(&raw);
6682
6683 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6684 let ClaudeOutput::System(sys) = output else {
6685 panic!("expected System");
6686 };
6687 assert_eq!(sys.subtype, SystemSubtype::CloudSessionDelta);
6688 assert!(sys.is_cloud_session_delta());
6689 assert!(!sys.is_feedback_draft_queued());
6690
6691 let direct = sys.as_cloud_session_delta().expect("direct typed accessor");
6692 assert_eq!(direct.seq, 3);
6693 assert_eq!(direct.changed, vec!["serving", "connection"]);
6694 assert_eq!(direct.cloud_session["id"], "session_abc");
6695 assert_eq!(direct.extra["future_field"], "preserved");
6696
6697 let Some(KnownSystemEvent::CloudSessionDelta(known)) = sys.as_known_system_event() else {
6698 panic!("expected CloudSessionDelta event");
6699 };
6700 assert_eq!(known.session_id, "s1");
6701 assert_eq!(
6702 sys.typed_value().expect("typed value")["future_field"],
6703 "preserved"
6704 );
6705 }
6706
6707 #[test]
6708 fn test_vcs_state_changed_fully_wrapped() {
6709 use super::{KnownSystemEvent, VcsMutationKind};
6710 use serde_json::Value;
6711
6712 for kind in ["commit", "push", "merge", "rebase"] {
6713 let raw: Value = serde_json::from_str(&format!(
6714 r#"{{"type":"system","subtype":"vcs_state_changed","kind":"{}","cwd":"/repo","uuid":"u1","session_id":"s1"}}"#,
6715 kind
6716 ))
6717 .unwrap();
6718 crate::io::assert_fully_wrapped(&raw);
6719
6720 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6721 let ClaudeOutput::System(sys) = output else {
6722 panic!("expected System");
6723 };
6724 let Some(KnownSystemEvent::VcsStateChanged(msg)) = sys.as_known_system_event() else {
6725 panic!("expected VcsStateChanged event");
6726 };
6727 assert_eq!(msg.kind.as_str(), kind);
6728 assert!(!matches!(msg.kind, VcsMutationKind::Unknown(_)));
6729 }
6730
6731 let raw: Value = serde_json::from_str(
6733 r#"{"type":"system","subtype":"vcs_state_changed","kind":"tag","cwd":"/repo","uuid":"u2","session_id":"s2"}"#,
6734 )
6735 .unwrap();
6736 crate::io::assert_fully_wrapped(&raw);
6737 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6738 let ClaudeOutput::System(sys) = output else {
6739 panic!("expected System");
6740 };
6741 let Some(KnownSystemEvent::VcsStateChanged(msg)) = sys.as_known_system_event() else {
6742 panic!("expected VcsStateChanged event");
6743 };
6744 assert_eq!(msg.kind, VcsMutationKind::Unknown("tag".to_string()));
6745
6746 assert!(sys.is_vcs_state_changed());
6747 let direct = sys.as_vcs_state_changed().expect("direct accessor");
6748 assert_eq!(direct.cwd, "/repo");
6749 assert!(sys.as_code_change_published().is_none());
6750 }
6751
6752 #[test]
6753 fn test_assistant_aborted_and_resume_flags_roundtrip() {
6754 let json = r#"{
6755 "type":"assistant",
6756 "message":{"id":"msg_1","role":"assistant","model":"claude-3","content":[{"type":"text","text":"partial"}]},
6757 "session_id":"s1",
6758 "aborted":true,
6759 "resumed_from_incomplete_thinking":true
6760 }"#;
6761 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6762 let ClaudeOutput::Assistant(msg) = &output else {
6763 panic!("expected Assistant");
6764 };
6765 assert_eq!(msg.aborted, Some(true));
6766 assert_eq!(msg.resumed_from_incomplete_thinking, Some(true));
6767 let reserialized = serde_json::to_string(&output).unwrap();
6768 assert!(reserialized.contains("\"aborted\":true"));
6769 assert!(reserialized.contains("\"resumed_from_incomplete_thinking\":true"));
6770
6771 let json = r#"{
6773 "type":"assistant",
6774 "message":{"id":"msg_2","role":"assistant","model":"claude-3","content":[]},
6775 "session_id":"s2"
6776 }"#;
6777 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6778 let reserialized = serde_json::to_string(&output).unwrap();
6779 assert!(!reserialized.contains("aborted"));
6780 assert!(!reserialized.contains("resumed_from_incomplete_thinking"));
6781 }
6782
6783 #[test]
6784 fn test_user_tool_result_meta_roundtrip() {
6785 let json = r#"{
6786 "type":"user",
6787 "message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_1","content":"denied"}]},
6788 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6789 "tool_result_meta":[
6790 {"id":"toolu_1","non_execution_kind":"user-rejected","user_feedback":"use the staging db"},
6791 {"id":"toolu_2","non_execution_kind":"permission-rule"}
6792 ]
6793 }"#;
6794 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6795 let ClaudeOutput::User(user) = &output else {
6796 panic!("expected User");
6797 };
6798 let meta = user.tool_result_meta.as_ref().unwrap();
6799 assert_eq!(meta.len(), 2);
6800 assert_eq!(meta[0].non_execution_kind.as_deref(), Some("user-rejected"));
6801 assert_eq!(meta[0].user_feedback.as_deref(), Some("use the staging db"));
6802 assert_eq!(meta[1].user_feedback, None);
6803
6804 let reserialized = serde_json::to_string(&output).unwrap();
6805 assert!(reserialized.contains("\"non_execution_kind\":\"user-rejected\""));
6806 assert!(!reserialized.contains("\"user_feedback\":null"));
6807 }
6808
6809 #[test]
6813 fn model_refusal_fallback_scope_roundtrips_and_defaults() {
6814 use super::{ModelRefusalFallbackMessage, RefusalFallbackScope};
6815 let with_scope = serde_json::json!({
6816 "trigger": "refusal",
6817 "direction": "retry",
6818 "scope": "local",
6819 "original_model": "claude-fable-5",
6820 "fallback_model": "claude-opus-5",
6821 "request_id": null,
6822 "content": "Refused; retried on fallback model.",
6823 "uuid": "u1",
6824 "session_id": "s1"
6825 });
6826 let msg: ModelRefusalFallbackMessage = serde_json::from_value(with_scope.clone()).unwrap();
6827 assert_eq!(msg.scope, Some(RefusalFallbackScope::Local));
6828 assert_eq!(serde_json::to_value(&msg).unwrap(), with_scope);
6829
6830 let mut without = with_scope.clone();
6833 without.as_object_mut().unwrap().remove("scope");
6834 let msg: ModelRefusalFallbackMessage = serde_json::from_value(without.clone()).unwrap();
6835 assert_eq!(msg.scope, None);
6836 assert_eq!(serde_json::to_value(&msg).unwrap(), without);
6837
6838 assert_eq!(
6840 RefusalFallbackScope::from("workspace").as_str(),
6841 "workspace"
6842 );
6843 }
6844}