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 UiPlaced,
64 UiScroll,
65 UiStatus,
66 UiToast,
67 Unknown(String),
69}
70
71impl SystemSubtype {
72 pub fn as_str(&self) -> &str {
73 match self {
74 Self::Init => "init",
75 Self::Status => "status",
76 Self::CompactBoundary => "compact_boundary",
77 Self::ThinkingTokens => "thinking_tokens",
78 Self::TaskStarted => "task_started",
79 Self::TaskProgress => "task_progress",
80 Self::TaskUpdated => "task_updated",
81 Self::TaskNotification => "task_notification",
82 Self::ApiRetry => "api_retry",
83 Self::ControlRequestProgress => "control_request_progress",
84 Self::ModelRefusalFallback => "model_refusal_fallback",
85 Self::ModelRefusalNoFallback => "model_refusal_no_fallback",
86 Self::LocalCommandOutput => "local_command_output",
87 Self::HookStarted => "hook_started",
88 Self::HookProgress => "hook_progress",
89 Self::HookResponse => "hook_response",
90 Self::PluginInstall => "plugin_install",
91 Self::BackgroundTasksChanged => "background_tasks_changed",
92 Self::SessionStateChanged => "session_state_changed",
93 Self::WorkerShuttingDown => "worker_shutting_down",
94 Self::CommandsChanged => "commands_changed",
95 Self::Notification => "notification",
96 Self::FilesPersisted => "files_persisted",
97 Self::MemoryRecall => "memory_recall",
98 Self::ElicitationComplete => "elicitation_complete",
99 Self::PermissionDenied => "permission_denied",
100 Self::MirrorError => "mirror_error",
101 Self::Informational => "informational",
102 Self::CodeChangePublished => "code_change_published",
103 Self::VcsStateChanged => "vcs_state_changed",
104 Self::FeedbackDraftQueued => "feedback_draft_queued",
105 Self::CloudSessionDelta => "cloud_session_delta",
106 Self::DevIntent => "dev_intent",
107 Self::TurnHandoffAvailable => "turn_handoff_available",
108 Self::TurnPreempted => "turn_preempted",
109 Self::PeerMessageHold => "peer_message_hold",
110 Self::PerTurnEffortChanged => "per_turn_effort_changed",
111 Self::SessionTitleChanged => "session_title_changed",
112 Self::InstructionSizeWarning => "instruction_size_warning",
113 Self::FileAttachmentsMissing => "file_attachments_missing",
114 Self::PermissionCheckStatus => "permission_check_status",
115 Self::InheritedAsksSettled => "inherited_asks_settled",
116 Self::UiFocus => "ui_focus",
117 Self::UiInvalidate => "ui_invalidate",
118 Self::UiLog => "ui_log",
119 Self::UiPanes => "ui_panes",
120 Self::UiPlaced => "ui_placed",
121 Self::UiScroll => "ui_scroll",
122 Self::UiStatus => "ui_status",
123 Self::UiToast => "ui_toast",
124 Self::Unknown(s) => s.as_str(),
125 }
126 }
127}
128
129impl fmt::Display for SystemSubtype {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 f.write_str(self.as_str())
132 }
133}
134
135impl From<&str> for SystemSubtype {
136 fn from(s: &str) -> Self {
137 match s {
138 "init" => Self::Init,
139 "status" => Self::Status,
140 "compact_boundary" => Self::CompactBoundary,
141 "thinking_tokens" => Self::ThinkingTokens,
142 "task_started" => Self::TaskStarted,
143 "task_progress" => Self::TaskProgress,
144 "task_updated" => Self::TaskUpdated,
145 "task_notification" => Self::TaskNotification,
146 "api_retry" => Self::ApiRetry,
147 "control_request_progress" => Self::ControlRequestProgress,
148 "model_refusal_fallback" => Self::ModelRefusalFallback,
149 "model_refusal_no_fallback" => Self::ModelRefusalNoFallback,
150 "local_command_output" => Self::LocalCommandOutput,
151 "hook_started" => Self::HookStarted,
152 "hook_progress" => Self::HookProgress,
153 "hook_response" => Self::HookResponse,
154 "plugin_install" => Self::PluginInstall,
155 "background_tasks_changed" => Self::BackgroundTasksChanged,
156 "session_state_changed" => Self::SessionStateChanged,
157 "worker_shutting_down" => Self::WorkerShuttingDown,
158 "commands_changed" => Self::CommandsChanged,
159 "notification" => Self::Notification,
160 "files_persisted" => Self::FilesPersisted,
161 "memory_recall" => Self::MemoryRecall,
162 "elicitation_complete" => Self::ElicitationComplete,
163 "permission_denied" => Self::PermissionDenied,
164 "mirror_error" => Self::MirrorError,
165 "informational" => Self::Informational,
166 "code_change_published" => Self::CodeChangePublished,
167 "vcs_state_changed" => Self::VcsStateChanged,
168 "feedback_draft_queued" => Self::FeedbackDraftQueued,
169 "cloud_session_delta" => Self::CloudSessionDelta,
170 "dev_intent" => Self::DevIntent,
171 "turn_handoff_available" => Self::TurnHandoffAvailable,
172 "turn_preempted" => Self::TurnPreempted,
173 "peer_message_hold" => Self::PeerMessageHold,
174 "per_turn_effort_changed" => Self::PerTurnEffortChanged,
175 "session_title_changed" => Self::SessionTitleChanged,
176 "instruction_size_warning" => Self::InstructionSizeWarning,
177 "file_attachments_missing" => Self::FileAttachmentsMissing,
178 "permission_check_status" => Self::PermissionCheckStatus,
179 "inherited_asks_settled" => Self::InheritedAsksSettled,
180 "ui_focus" => Self::UiFocus,
181 "ui_invalidate" => Self::UiInvalidate,
182 "ui_log" => Self::UiLog,
183 "ui_panes" => Self::UiPanes,
184 "ui_placed" => Self::UiPlaced,
185 "ui_scroll" => Self::UiScroll,
186 "ui_status" => Self::UiStatus,
187 "ui_toast" => Self::UiToast,
188 other => Self::Unknown(other.to_string()),
189 }
190 }
191}
192
193impl Serialize for SystemSubtype {
194 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
195 serializer.serialize_str(self.as_str())
196 }
197}
198
199impl<'de> Deserialize<'de> for SystemSubtype {
200 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
201 let s = String::deserialize(deserializer)?;
202 Ok(Self::from(s.as_str()))
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, Hash)]
211pub enum MessageRole {
212 User,
213 Assistant,
214 Unknown(String),
216}
217
218impl MessageRole {
219 pub fn as_str(&self) -> &str {
220 match self {
221 Self::User => "user",
222 Self::Assistant => "assistant",
223 Self::Unknown(s) => s.as_str(),
224 }
225 }
226}
227
228impl fmt::Display for MessageRole {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 f.write_str(self.as_str())
231 }
232}
233
234impl From<&str> for MessageRole {
235 fn from(s: &str) -> Self {
236 match s {
237 "user" => Self::User,
238 "assistant" => Self::Assistant,
239 other => Self::Unknown(other.to_string()),
240 }
241 }
242}
243
244impl Serialize for MessageRole {
245 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
246 serializer.serialize_str(self.as_str())
247 }
248}
249
250impl<'de> Deserialize<'de> for MessageRole {
251 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
252 let s = String::deserialize(deserializer)?;
253 Ok(Self::from(s.as_str()))
254 }
255}
256
257#[derive(Debug, Clone, PartialEq, Eq, Hash)]
259pub enum CompactionTrigger {
260 Auto,
262 Manual,
264 Unknown(String),
266}
267
268impl CompactionTrigger {
269 pub fn as_str(&self) -> &str {
270 match self {
271 Self::Auto => "auto",
272 Self::Manual => "manual",
273 Self::Unknown(s) => s.as_str(),
274 }
275 }
276}
277
278impl fmt::Display for CompactionTrigger {
279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280 f.write_str(self.as_str())
281 }
282}
283
284impl From<&str> for CompactionTrigger {
285 fn from(s: &str) -> Self {
286 match s {
287 "auto" => Self::Auto,
288 "manual" => Self::Manual,
289 other => Self::Unknown(other.to_string()),
290 }
291 }
292}
293
294impl Serialize for CompactionTrigger {
295 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
296 serializer.serialize_str(self.as_str())
297 }
298}
299
300impl<'de> Deserialize<'de> for CompactionTrigger {
301 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
302 let s = String::deserialize(deserializer)?;
303 Ok(Self::from(s.as_str()))
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
309pub enum StopReason {
310 EndTurn,
312 MaxTokens,
314 ToolUse,
316 Unknown(String),
318}
319
320impl StopReason {
321 pub fn as_str(&self) -> &str {
322 match self {
323 Self::EndTurn => "end_turn",
324 Self::MaxTokens => "max_tokens",
325 Self::ToolUse => "tool_use",
326 Self::Unknown(s) => s.as_str(),
327 }
328 }
329}
330
331impl fmt::Display for StopReason {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 f.write_str(self.as_str())
334 }
335}
336
337impl From<&str> for StopReason {
338 fn from(s: &str) -> Self {
339 match s {
340 "end_turn" => Self::EndTurn,
341 "max_tokens" => Self::MaxTokens,
342 "tool_use" => Self::ToolUse,
343 other => Self::Unknown(other.to_string()),
344 }
345 }
346}
347
348impl Serialize for StopReason {
349 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
350 serializer.serialize_str(self.as_str())
351 }
352}
353
354impl<'de> Deserialize<'de> for StopReason {
355 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
356 let s = String::deserialize(deserializer)?;
357 Ok(Self::from(s.as_str()))
358 }
359}
360
361#[derive(Debug, Clone, PartialEq, Eq, Hash)]
363pub enum ApiKeySource {
364 None,
366 User,
367 Project,
368 Org,
369 Temporary,
370 Oauth,
371 Unknown(String),
373}
374
375impl ApiKeySource {
376 pub fn as_str(&self) -> &str {
377 match self {
378 Self::None => "none",
379 Self::User => "user",
380 Self::Project => "project",
381 Self::Org => "org",
382 Self::Temporary => "temporary",
383 Self::Oauth => "oauth",
384 Self::Unknown(s) => s.as_str(),
385 }
386 }
387}
388
389impl fmt::Display for ApiKeySource {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 f.write_str(self.as_str())
392 }
393}
394
395impl From<&str> for ApiKeySource {
396 fn from(s: &str) -> Self {
397 match s {
398 "none" => Self::None,
399 "user" => Self::User,
400 "project" => Self::Project,
401 "org" => Self::Org,
402 "temporary" => Self::Temporary,
403 "oauth" => Self::Oauth,
404 other => Self::Unknown(other.to_string()),
405 }
406 }
407}
408
409impl Serialize for ApiKeySource {
410 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
411 serializer.serialize_str(self.as_str())
412 }
413}
414
415impl<'de> Deserialize<'de> for ApiKeySource {
416 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
417 let s = String::deserialize(deserializer)?;
418 Ok(Self::from(s.as_str()))
419 }
420}
421
422#[derive(Debug, Clone, PartialEq, Eq, Hash)]
424pub enum OutputStyle {
425 Default,
427 Unknown(String),
429}
430
431impl OutputStyle {
432 pub fn as_str(&self) -> &str {
433 match self {
434 Self::Default => "default",
435 Self::Unknown(s) => s.as_str(),
436 }
437 }
438}
439
440impl fmt::Display for OutputStyle {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 f.write_str(self.as_str())
443 }
444}
445
446impl From<&str> for OutputStyle {
447 fn from(s: &str) -> Self {
448 match s {
449 "default" => Self::Default,
450 other => Self::Unknown(other.to_string()),
451 }
452 }
453}
454
455impl Serialize for OutputStyle {
456 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
457 serializer.serialize_str(self.as_str())
458 }
459}
460
461impl<'de> Deserialize<'de> for OutputStyle {
462 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
463 let s = String::deserialize(deserializer)?;
464 Ok(Self::from(s.as_str()))
465 }
466}
467
468#[derive(Debug, Clone, PartialEq, Eq, Hash)]
470pub enum InitPermissionMode {
471 Default,
473 AcceptEdits,
474 BypassPermissions,
475 Plan,
476 DontAsk,
477 Auto,
478 Unknown(String),
480}
481
482impl InitPermissionMode {
483 pub fn as_str(&self) -> &str {
484 match self {
485 Self::Default => "default",
486 Self::AcceptEdits => "acceptEdits",
487 Self::BypassPermissions => "bypassPermissions",
488 Self::Plan => "plan",
489 Self::DontAsk => "dontAsk",
490 Self::Auto => "auto",
491 Self::Unknown(s) => s.as_str(),
492 }
493 }
494}
495
496impl fmt::Display for InitPermissionMode {
497 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
498 f.write_str(self.as_str())
499 }
500}
501
502impl From<&str> for InitPermissionMode {
503 fn from(s: &str) -> Self {
504 match s {
505 "default" => Self::Default,
506 "acceptEdits" => Self::AcceptEdits,
507 "bypassPermissions" => Self::BypassPermissions,
508 "plan" => Self::Plan,
509 "dontAsk" => Self::DontAsk,
510 "auto" => Self::Auto,
511 other => Self::Unknown(other.to_string()),
512 }
513 }
514}
515
516impl Serialize for InitPermissionMode {
517 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
518 serializer.serialize_str(self.as_str())
519 }
520}
521
522impl<'de> Deserialize<'de> for InitPermissionMode {
523 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
524 let s = String::deserialize(deserializer)?;
525 Ok(Self::from(s.as_str()))
526 }
527}
528
529#[derive(Debug, Clone, PartialEq, Eq, Hash)]
531pub enum StatusMessageStatus {
532 Compacting,
534 Requesting,
536 Unknown(String),
538}
539
540impl StatusMessageStatus {
541 pub fn as_str(&self) -> &str {
542 match self {
543 Self::Compacting => "compacting",
544 Self::Requesting => "requesting",
545 Self::Unknown(s) => s.as_str(),
546 }
547 }
548}
549
550impl fmt::Display for StatusMessageStatus {
551 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552 f.write_str(self.as_str())
553 }
554}
555
556impl From<&str> for StatusMessageStatus {
557 fn from(s: &str) -> Self {
558 match s {
559 "compacting" => Self::Compacting,
560 "requesting" => Self::Requesting,
561 other => Self::Unknown(other.to_string()),
562 }
563 }
564}
565
566impl Serialize for StatusMessageStatus {
567 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
568 serializer.serialize_str(self.as_str())
569 }
570}
571
572impl<'de> Deserialize<'de> for StatusMessageStatus {
573 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
574 let s = String::deserialize(deserializer)?;
575 Ok(Self::from(s.as_str()))
576 }
577}
578
579pub(crate) fn serialize_optional_uuid<S>(
581 uuid: &Option<Uuid>,
582 serializer: S,
583) -> Result<S::Ok, S::Error>
584where
585 S: Serializer,
586{
587 match uuid {
588 Some(id) => serializer.serialize_str(&id.to_string()),
589 None => serializer.serialize_none(),
590 }
591}
592
593pub(crate) fn deserialize_optional_uuid<'de, D>(deserializer: D) -> Result<Option<Uuid>, D::Error>
595where
596 D: Deserializer<'de>,
597{
598 let opt_str: Option<String> = Option::deserialize(deserializer)?;
599 match opt_str {
600 Some(s) => Uuid::parse_str(&s)
601 .map(Some)
602 .map_err(serde::de::Error::custom),
603 None => Ok(None),
604 }
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
610pub struct MessageOrigin {
611 pub kind: String,
612 #[serde(flatten)]
613 pub extra: serde_json::Map<String, Value>,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
618pub struct SummarizeMetadata {
619 pub messages_summarized: u64,
620 #[serde(default, skip_serializing_if = "Option::is_none")]
621 pub user_context: Option<String>,
622 #[serde(default, skip_serializing_if = "Option::is_none")]
623 pub direction: Option<String>,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
628pub struct McpMeta {
629 #[serde(default, skip_serializing_if = "Option::is_none", rename = "_meta")]
630 pub meta: Option<Value>,
631 #[serde(default, skip_serializing_if = "Option::is_none")]
632 pub structured_content: Option<Value>,
633 #[serde(default, skip_serializing_if = "Vec::is_empty")]
638 pub resource_links: Vec<ResourceLink>,
639}
640
641#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
645pub struct ResourceLink {
646 pub uri: String,
647 pub name: String,
648 #[serde(default, skip_serializing_if = "Option::is_none")]
649 pub title: Option<String>,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
651 pub description: Option<String>,
652 #[serde(default, skip_serializing_if = "Option::is_none", rename = "mimeType")]
653 pub mime_type: Option<String>,
654 #[serde(default, skip_serializing_if = "Option::is_none")]
655 pub size: Option<u64>,
656 #[serde(default, skip_serializing_if = "Option::is_none")]
657 pub annotations: Option<Value>,
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
662pub struct ToolResultMeta {
663 pub id: String,
665 #[serde(default, skip_serializing_if = "Option::is_none")]
672 pub non_execution_kind: Option<String>,
673 #[serde(default, skip_serializing_if = "Option::is_none")]
675 pub user_feedback: Option<String>,
676 #[serde(default, skip_serializing_if = "Option::is_none")]
681 pub remedy: Option<ToolResultRemedy>,
682 #[serde(default, skip_serializing_if = "Option::is_none")]
689 pub permission_decision: Option<ToolPermissionDecision>,
690}
691
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
696pub struct ToolPermissionDecision {
697 pub decision: PermissionDecisionOutcome,
699 pub source: String,
707 #[serde(default, skip_serializing_if = "Option::is_none")]
713 pub reason_type: Option<PermissionDecisionReasonType>,
714}
715
716#[derive(Debug, Clone, PartialEq, Eq, Hash)]
718pub enum PermissionDecisionOutcome {
719 Accept,
721 Reject,
723 Cancelled,
725 Unknown(String),
727}
728
729impl PermissionDecisionOutcome {
730 pub fn as_str(&self) -> &str {
731 match self {
732 Self::Accept => "accept",
733 Self::Reject => "reject",
734 Self::Cancelled => "cancelled",
735 Self::Unknown(s) => s.as_str(),
736 }
737 }
738}
739
740impl fmt::Display for PermissionDecisionOutcome {
741 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742 f.write_str(self.as_str())
743 }
744}
745
746impl From<&str> for PermissionDecisionOutcome {
747 fn from(s: &str) -> Self {
748 match s {
749 "accept" => Self::Accept,
750 "reject" => Self::Reject,
751 "cancelled" => Self::Cancelled,
752 other => Self::Unknown(other.to_string()),
753 }
754 }
755}
756
757impl Serialize for PermissionDecisionOutcome {
758 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
759 serializer.serialize_str(self.as_str())
760 }
761}
762
763impl<'de> Deserialize<'de> for PermissionDecisionOutcome {
764 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
765 let s = String::deserialize(deserializer)?;
766 Ok(Self::from(s.as_str()))
767 }
768}
769
770#[derive(Debug, Clone, PartialEq, Eq, Hash)]
773pub enum PermissionDecisionReasonType {
774 Rule,
776 Mode,
778 SubcommandResults,
780 PermissionPromptTool,
782 Hook,
784 AsyncAgent,
786 SandboxOverride,
788 WorkingDir,
790 SafetyCheck,
792 Classifier,
794 Other,
796 Unknown(String),
798}
799
800impl PermissionDecisionReasonType {
801 pub fn as_str(&self) -> &str {
802 match self {
803 Self::Rule => "rule",
804 Self::Mode => "mode",
805 Self::SubcommandResults => "subcommandResults",
806 Self::PermissionPromptTool => "permissionPromptTool",
807 Self::Hook => "hook",
808 Self::AsyncAgent => "asyncAgent",
809 Self::SandboxOverride => "sandboxOverride",
810 Self::WorkingDir => "workingDir",
811 Self::SafetyCheck => "safetyCheck",
812 Self::Classifier => "classifier",
813 Self::Other => "other",
814 Self::Unknown(s) => s.as_str(),
815 }
816 }
817}
818
819impl fmt::Display for PermissionDecisionReasonType {
820 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
821 f.write_str(self.as_str())
822 }
823}
824
825impl From<&str> for PermissionDecisionReasonType {
826 fn from(s: &str) -> Self {
827 match s {
828 "rule" => Self::Rule,
829 "mode" => Self::Mode,
830 "subcommandResults" => Self::SubcommandResults,
831 "permissionPromptTool" => Self::PermissionPromptTool,
832 "hook" => Self::Hook,
833 "asyncAgent" => Self::AsyncAgent,
834 "sandboxOverride" => Self::SandboxOverride,
835 "workingDir" => Self::WorkingDir,
836 "safetyCheck" => Self::SafetyCheck,
837 "classifier" => Self::Classifier,
838 "other" => Self::Other,
839 other => Self::Unknown(other.to_string()),
840 }
841 }
842}
843
844impl Serialize for PermissionDecisionReasonType {
845 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
846 serializer.serialize_str(self.as_str())
847 }
848}
849
850impl<'de> Deserialize<'de> for PermissionDecisionReasonType {
851 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
852 let s = String::deserialize(deserializer)?;
853 Ok(Self::from(s.as_str()))
854 }
855}
856
857#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
862pub struct ToolResultRemedy {
863 pub kind: ToolResultRemedyKind,
866 #[serde(default, skip_serializing_if = "Option::is_none")]
870 pub servers: Option<Vec<String>>,
871 #[serde(default, skip_serializing_if = "Option::is_none")]
874 pub provider: Option<RemedyLoginProvider>,
875 #[serde(default, skip_serializing_if = "Option::is_none")]
879 pub path: Option<String>,
880 #[serde(default, skip_serializing_if = "Option::is_none")]
885 pub managed: Option<bool>,
886 #[serde(default, skip_serializing_if = "Option::is_none")]
888 pub feature: Option<RemedyFeature>,
889 #[serde(default, skip_serializing_if = "Option::is_none")]
891 pub cause: Option<RemedyFeatureCause>,
892 #[serde(default, skip_serializing_if = "Option::is_none")]
894 pub policy_kind: Option<RemedyPolicyKind>,
895}
896
897#[derive(Debug, Clone, PartialEq, Eq, Hash)]
899pub enum ToolResultRemedyKind {
900 McpNeedsAuth,
902 McpDisabled,
904 McpRequiredMissing,
906 AuthExpired,
908 AuthOverridden,
910 AuthMissingScope,
912 DesignNeedsAuthorization,
914 SandboxViolation,
916 OutsideReadsBlocked,
918 MemoryPaused,
920 FeatureDisabled,
922 PolicyDenied,
925 SpawnArgLimit,
927 StagedForReview,
929 Unknown(String),
931}
932
933impl ToolResultRemedyKind {
934 pub fn as_str(&self) -> &str {
935 match self {
936 Self::McpNeedsAuth => "mcp_needs_auth",
937 Self::McpDisabled => "mcp_disabled",
938 Self::McpRequiredMissing => "mcp_required_missing",
939 Self::AuthExpired => "auth_expired",
940 Self::AuthOverridden => "auth_overridden",
941 Self::AuthMissingScope => "auth_missing_scope",
942 Self::DesignNeedsAuthorization => "design_needs_authorization",
943 Self::SandboxViolation => "sandbox_violation",
944 Self::OutsideReadsBlocked => "outside_reads_blocked",
945 Self::MemoryPaused => "memory_paused",
946 Self::FeatureDisabled => "feature_disabled",
947 Self::PolicyDenied => "policy_denied",
948 Self::SpawnArgLimit => "spawn_arg_limit",
949 Self::StagedForReview => "staged_for_review",
950 Self::Unknown(s) => s.as_str(),
951 }
952 }
953}
954
955impl fmt::Display for ToolResultRemedyKind {
956 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
957 f.write_str(self.as_str())
958 }
959}
960
961impl From<&str> for ToolResultRemedyKind {
962 fn from(s: &str) -> Self {
963 match s {
964 "mcp_needs_auth" => Self::McpNeedsAuth,
965 "mcp_disabled" => Self::McpDisabled,
966 "mcp_required_missing" => Self::McpRequiredMissing,
967 "auth_expired" => Self::AuthExpired,
968 "auth_overridden" => Self::AuthOverridden,
969 "auth_missing_scope" => Self::AuthMissingScope,
970 "design_needs_authorization" => Self::DesignNeedsAuthorization,
971 "sandbox_violation" => Self::SandboxViolation,
972 "outside_reads_blocked" => Self::OutsideReadsBlocked,
973 "memory_paused" => Self::MemoryPaused,
974 "feature_disabled" => Self::FeatureDisabled,
975 "policy_denied" => Self::PolicyDenied,
976 "spawn_arg_limit" => Self::SpawnArgLimit,
977 "staged_for_review" => Self::StagedForReview,
978 other => Self::Unknown(other.to_string()),
979 }
980 }
981}
982
983impl Serialize for ToolResultRemedyKind {
984 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
985 serializer.serialize_str(self.as_str())
986 }
987}
988
989impl<'de> Deserialize<'de> for ToolResultRemedyKind {
990 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
991 let s = String::deserialize(deserializer)?;
992 Ok(Self::from(s.as_str()))
993 }
994}
995
996#[derive(Debug, Clone, PartialEq, Eq, Hash)]
998pub enum RemedyLoginProvider {
999 ClaudeAi,
1001 ClaudeDesign,
1003 Unknown(String),
1005}
1006
1007impl RemedyLoginProvider {
1008 pub fn as_str(&self) -> &str {
1009 match self {
1010 Self::ClaudeAi => "claude_ai",
1011 Self::ClaudeDesign => "claude_design",
1012 Self::Unknown(s) => s.as_str(),
1013 }
1014 }
1015}
1016
1017impl fmt::Display for RemedyLoginProvider {
1018 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1019 f.write_str(self.as_str())
1020 }
1021}
1022
1023impl From<&str> for RemedyLoginProvider {
1024 fn from(s: &str) -> Self {
1025 match s {
1026 "claude_ai" => Self::ClaudeAi,
1027 "claude_design" => Self::ClaudeDesign,
1028 other => Self::Unknown(other.to_string()),
1029 }
1030 }
1031}
1032
1033impl Serialize for RemedyLoginProvider {
1034 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1035 serializer.serialize_str(self.as_str())
1036 }
1037}
1038
1039impl<'de> Deserialize<'de> for RemedyLoginProvider {
1040 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1041 let s = String::deserialize(deserializer)?;
1042 Ok(Self::from(s.as_str()))
1043 }
1044}
1045
1046#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1049pub enum RemedyFeature {
1050 Workflows,
1051 Artifacts,
1052 Unknown(String),
1054}
1055
1056impl RemedyFeature {
1057 pub fn as_str(&self) -> &str {
1058 match self {
1059 Self::Workflows => "workflows",
1060 Self::Artifacts => "artifacts",
1061 Self::Unknown(s) => s.as_str(),
1062 }
1063 }
1064}
1065
1066impl fmt::Display for RemedyFeature {
1067 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1068 f.write_str(self.as_str())
1069 }
1070}
1071
1072impl From<&str> for RemedyFeature {
1073 fn from(s: &str) -> Self {
1074 match s {
1075 "workflows" => Self::Workflows,
1076 "artifacts" => Self::Artifacts,
1077 other => Self::Unknown(other.to_string()),
1078 }
1079 }
1080}
1081
1082impl Serialize for RemedyFeature {
1083 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1084 serializer.serialize_str(self.as_str())
1085 }
1086}
1087
1088impl<'de> Deserialize<'de> for RemedyFeature {
1089 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1090 let s = String::deserialize(deserializer)?;
1091 Ok(Self::from(s.as_str()))
1092 }
1093}
1094
1095#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1098pub enum RemedyFeatureCause {
1099 ManagedSettings,
1102 OrgPolicy,
1104 Unavailable,
1106 UserSetting,
1109 UnsupportedRuntime,
1113 Unknown(String),
1115}
1116
1117impl RemedyFeatureCause {
1118 pub fn as_str(&self) -> &str {
1119 match self {
1120 Self::ManagedSettings => "managed_settings",
1121 Self::OrgPolicy => "org_policy",
1122 Self::Unavailable => "unavailable",
1123 Self::UserSetting => "user_setting",
1124 Self::UnsupportedRuntime => "unsupported_runtime",
1125 Self::Unknown(s) => s.as_str(),
1126 }
1127 }
1128}
1129
1130impl fmt::Display for RemedyFeatureCause {
1131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1132 f.write_str(self.as_str())
1133 }
1134}
1135
1136impl From<&str> for RemedyFeatureCause {
1137 fn from(s: &str) -> Self {
1138 match s {
1139 "managed_settings" => Self::ManagedSettings,
1140 "org_policy" => Self::OrgPolicy,
1141 "unavailable" => Self::Unavailable,
1142 "user_setting" => Self::UserSetting,
1143 "unsupported_runtime" => Self::UnsupportedRuntime,
1144 other => Self::Unknown(other.to_string()),
1145 }
1146 }
1147}
1148
1149impl Serialize for RemedyFeatureCause {
1150 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1151 serializer.serialize_str(self.as_str())
1152 }
1153}
1154
1155impl<'de> Deserialize<'de> for RemedyFeatureCause {
1156 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1157 let s = String::deserialize(deserializer)?;
1158 Ok(Self::from(s.as_str()))
1159 }
1160}
1161
1162#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1165pub enum RemedyPolicyKind {
1166 OrgDenied,
1168 Latched,
1171 Unknown(String),
1173}
1174
1175impl RemedyPolicyKind {
1176 pub fn as_str(&self) -> &str {
1177 match self {
1178 Self::OrgDenied => "org_denied",
1179 Self::Latched => "latched",
1180 Self::Unknown(s) => s.as_str(),
1181 }
1182 }
1183}
1184
1185impl fmt::Display for RemedyPolicyKind {
1186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1187 f.write_str(self.as_str())
1188 }
1189}
1190
1191impl From<&str> for RemedyPolicyKind {
1192 fn from(s: &str) -> Self {
1193 match s {
1194 "org_denied" => Self::OrgDenied,
1195 "latched" => Self::Latched,
1196 other => Self::Unknown(other.to_string()),
1197 }
1198 }
1199}
1200
1201impl Serialize for RemedyPolicyKind {
1202 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1203 serializer.serialize_str(self.as_str())
1204 }
1205}
1206
1207impl<'de> Deserialize<'de> for RemedyPolicyKind {
1208 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1209 let s = String::deserialize(deserializer)?;
1210 Ok(Self::from(s.as_str()))
1211 }
1212}
1213
1214#[derive(Debug, Clone, Serialize, Deserialize)]
1216pub struct UserMessage {
1217 pub message: MessageContent,
1218 #[serde(skip_serializing_if = "Option::is_none", alias = "sessionId")]
1219 #[serde(
1220 serialize_with = "serialize_optional_uuid",
1221 deserialize_with = "deserialize_optional_uuid"
1222 )]
1223 pub session_id: Option<Uuid>,
1224 #[serde(skip_serializing_if = "Option::is_none")]
1226 pub parent_tool_use_id: Option<String>,
1227 #[serde(skip_serializing_if = "Option::is_none")]
1229 pub uuid: Option<String>,
1230 #[serde(skip_serializing_if = "Option::is_none")]
1232 pub timestamp: Option<String>,
1233 #[serde(skip_serializing_if = "Option::is_none")]
1240 pub tool_use_result: Option<serde_json::Value>,
1241 #[serde(default, skip_serializing_if = "Option::is_none")]
1246 pub agent_id: Option<String>,
1247 #[serde(skip_serializing_if = "Option::is_none")]
1250 pub subagent_type: Option<String>,
1251 #[serde(skip_serializing_if = "Option::is_none")]
1253 pub task_description: Option<String>,
1254 #[serde(skip_serializing_if = "Option::is_none")]
1255 pub origin: Option<MessageOrigin>,
1256 #[serde(skip_serializing_if = "Option::is_none")]
1257 pub priority: Option<String>,
1258 #[serde(skip_serializing_if = "Option::is_none", rename = "isSynthetic")]
1259 pub is_synthetic: Option<bool>,
1260 #[serde(skip_serializing_if = "Option::is_none", rename = "shouldQuery")]
1261 pub should_query: Option<bool>,
1262 #[serde(default, skip_serializing_if = "Option::is_none")]
1263 pub is_meta: Option<bool>,
1264 #[serde(default, skip_serializing_if = "Option::is_none")]
1265 pub is_visible_in_transcript_only: Option<bool>,
1266 #[serde(default, skip_serializing_if = "Option::is_none")]
1267 pub is_virtual: Option<bool>,
1268 #[serde(default, skip_serializing_if = "Option::is_none")]
1269 pub is_compact_summary: Option<bool>,
1270 #[serde(skip_serializing_if = "Option::is_none")]
1271 pub summarize_metadata: Option<SummarizeMetadata>,
1272 #[serde(skip_serializing_if = "Option::is_none")]
1273 pub mcp_meta: Option<McpMeta>,
1274 #[serde(skip_serializing_if = "Option::is_none")]
1277 pub tool_result_meta: Option<Vec<ToolResultMeta>>,
1278 #[serde(skip_serializing_if = "Option::is_none")]
1279 pub source_tool_use_id: Option<String>,
1280 #[serde(skip_serializing_if = "Option::is_none")]
1281 pub source_tool_assistant_uuid: Option<String>,
1282 #[serde(skip_serializing_if = "Option::is_none")]
1283 pub image_paste_ids: Option<Vec<u64>>,
1284 #[serde(skip_serializing_if = "Option::is_none")]
1285 pub client_platform: Option<String>,
1286 #[serde(skip_serializing_if = "Option::is_none")]
1287 pub inbound_origin: Option<String>,
1288 #[serde(skip_serializing_if = "Option::is_none", rename = "isReplay")]
1289 pub is_replay: Option<bool>,
1290 #[serde(skip_serializing_if = "Option::is_none")]
1291 pub file_attachments: Option<Vec<Value>>,
1292 #[serde(default, skip_serializing_if = "Option::is_none")]
1294 pub seeded_summon: Option<bool>,
1295 #[serde(default, skip_serializing_if = "Option::is_none")]
1298 pub client_composed: Option<bool>,
1299 #[serde(default, skip_serializing_if = "Option::is_none")]
1308 pub text_not_typed: Option<bool>,
1309 #[serde(default, skip_serializing_if = "Option::is_none")]
1314 pub initiator: Option<String>,
1315 #[serde(default, skip_serializing_if = "Option::is_none")]
1321 pub pasted_content: Option<Vec<Value>>,
1322 #[serde(default, skip_serializing_if = "Option::is_none")]
1326 pub historical: Option<bool>,
1327 #[serde(default, skip_serializing_if = "Option::is_none")]
1333 pub server_received_wall_ms: Option<f64>,
1334 #[serde(default, skip_serializing_if = "Option::is_none")]
1338 pub refused_message_id: Option<String>,
1339 #[serde(default, skip_serializing_if = "Option::is_none")]
1346 pub requeue_if_interrupted_within_ms: Option<u64>,
1347}
1348
1349impl UserMessage {
1350 pub fn tool_use_result_as<T: serde::de::DeserializeOwned>(
1358 &self,
1359 ) -> Option<Result<T, serde_json::Error>> {
1360 self.tool_use_result
1361 .as_ref()
1362 .map(|v| serde_json::from_value(v.clone()))
1363 }
1364
1365 pub fn subagent_result(&self) -> Option<SubagentResult> {
1376 self.tool_use_result
1377 .as_ref()
1378 .and_then(|v| serde_json::from_value(v.clone()).ok())
1379 }
1380}
1381
1382#[derive(Debug, Clone, Serialize, Deserialize)]
1393pub struct SubagentResult {
1394 #[serde(skip_serializing_if = "Option::is_none")]
1396 pub status: Option<String>,
1397 #[serde(skip_serializing_if = "Option::is_none")]
1399 pub prompt: Option<String>,
1400 #[serde(rename = "agentId", skip_serializing_if = "Option::is_none")]
1402 pub agent_id: Option<String>,
1403 #[serde(rename = "agentType", skip_serializing_if = "Option::is_none")]
1405 pub agent_type: Option<String>,
1406 #[serde(
1408 default,
1409 deserialize_with = "deserialize_content_blocks",
1410 skip_serializing_if = "Vec::is_empty"
1411 )]
1412 pub content: Vec<ContentBlock>,
1413 #[serde(rename = "resolvedModel", skip_serializing_if = "Option::is_none")]
1415 pub resolved_model: Option<String>,
1416 #[serde(rename = "totalDurationMs", skip_serializing_if = "Option::is_none")]
1418 pub total_duration_ms: Option<u64>,
1419 #[serde(rename = "totalTokens", skip_serializing_if = "Option::is_none")]
1421 pub total_tokens: Option<u64>,
1422 #[serde(rename = "totalToolUseCount", skip_serializing_if = "Option::is_none")]
1424 pub total_tool_use_count: Option<u64>,
1425 #[serde(skip_serializing_if = "Option::is_none")]
1427 pub usage: Option<super::result::UsageInfo>,
1428 #[serde(rename = "toolStats", skip_serializing_if = "Option::is_none")]
1430 pub tool_stats: Option<SubagentToolStats>,
1431}
1432
1433#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1438#[serde(rename_all = "camelCase")]
1439pub struct SubagentToolStats {
1440 #[serde(default)]
1441 pub read_count: u64,
1442 #[serde(default)]
1443 pub search_count: u64,
1444 #[serde(default)]
1445 pub bash_count: u64,
1446 #[serde(default)]
1447 pub edit_file_count: u64,
1448 #[serde(default)]
1449 pub lines_added: u64,
1450 #[serde(default)]
1451 pub lines_removed: u64,
1452 #[serde(default)]
1453 pub other_tool_count: u64,
1454 #[serde(flatten)]
1455 pub extra: serde_json::Map<String, Value>,
1456}
1457
1458#[derive(Debug, Clone, Default, PartialEq, Eq)]
1485pub struct SubagentUsageRollup {
1486 pub subagent_tokens: u64,
1489 pub agent_count: u32,
1491 pub tool_uses: u64,
1493 pub duration_ms: u64,
1495 seen_agent_ids: std::collections::BTreeSet<String>,
1496}
1497
1498impl SubagentUsageRollup {
1499 pub fn observe(&mut self, output: &ClaudeOutput) -> bool {
1505 match output {
1506 ClaudeOutput::User(user) => self.observe_user(user),
1507 _ => false,
1508 }
1509 }
1510
1511 pub fn observe_user(&mut self, user: &UserMessage) -> bool {
1518 let Some(result) = user.subagent_result() else {
1519 return false;
1520 };
1521 if result.total_tokens.is_none() && result.agent_id.is_none() {
1522 return false;
1523 }
1524 if let Some(agent_id) = &result.agent_id {
1525 if !self.seen_agent_ids.insert(agent_id.clone()) {
1526 return false;
1527 }
1528 }
1529 self.agent_count += 1;
1530 self.subagent_tokens += result.total_tokens.unwrap_or(0);
1531 self.tool_uses += result.total_tool_use_count.unwrap_or(0);
1532 self.duration_ms += result.total_duration_ms.unwrap_or(0);
1533 true
1534 }
1535}
1536
1537#[derive(Debug, Clone, Serialize, Deserialize)]
1539pub struct MessageContent {
1540 pub role: MessageRole,
1541 #[serde(deserialize_with = "deserialize_content_blocks")]
1542 pub content: Vec<ContentBlock>,
1543}
1544
1545#[derive(Debug, Clone, Serialize, Deserialize)]
1547pub struct SystemMessage {
1548 pub subtype: SystemSubtype,
1549 #[serde(flatten)]
1550 pub data: Value, }
1552
1553impl SystemMessage {
1554 pub fn is_init(&self) -> bool {
1556 self.subtype == SystemSubtype::Init
1557 }
1558
1559 pub fn is_status(&self) -> bool {
1561 self.subtype == SystemSubtype::Status
1562 }
1563
1564 pub fn is_compact_boundary(&self) -> bool {
1566 self.subtype == SystemSubtype::CompactBoundary
1567 }
1568
1569 pub fn as_init(&self) -> Option<InitMessage> {
1571 if self.subtype != SystemSubtype::Init {
1572 return None;
1573 }
1574 serde_json::from_value(self.data.clone()).ok()
1575 }
1576
1577 pub fn as_status(&self) -> Option<StatusMessage> {
1579 if self.subtype != SystemSubtype::Status {
1580 return None;
1581 }
1582 serde_json::from_value(self.data.clone()).ok()
1583 }
1584
1585 pub fn as_compact_boundary(&self) -> Option<CompactBoundaryMessage> {
1587 if self.subtype != SystemSubtype::CompactBoundary {
1588 return None;
1589 }
1590 serde_json::from_value(self.data.clone()).ok()
1591 }
1592
1593 pub fn is_task_started(&self) -> bool {
1595 self.subtype == SystemSubtype::TaskStarted
1596 }
1597
1598 pub fn is_task_progress(&self) -> bool {
1600 self.subtype == SystemSubtype::TaskProgress
1601 }
1602
1603 pub fn is_task_notification(&self) -> bool {
1605 self.subtype == SystemSubtype::TaskNotification
1606 }
1607
1608 pub fn as_task_started(&self) -> Option<TaskStartedMessage> {
1610 if self.subtype != SystemSubtype::TaskStarted {
1611 return None;
1612 }
1613 serde_json::from_value(self.data.clone()).ok()
1614 }
1615
1616 pub fn as_task_progress(&self) -> Option<TaskProgressMessage> {
1618 if self.subtype != SystemSubtype::TaskProgress {
1619 return None;
1620 }
1621 serde_json::from_value(self.data.clone()).ok()
1622 }
1623
1624 pub fn as_task_notification(&self) -> Option<TaskNotificationMessage> {
1626 if self.subtype != SystemSubtype::TaskNotification {
1627 return None;
1628 }
1629 serde_json::from_value(self.data.clone()).ok()
1630 }
1631
1632 pub fn is_task_updated(&self) -> bool {
1634 self.subtype == SystemSubtype::TaskUpdated
1635 }
1636
1637 pub fn as_task_updated(&self) -> Option<TaskUpdatedMessage> {
1639 if self.subtype != SystemSubtype::TaskUpdated {
1640 return None;
1641 }
1642 serde_json::from_value(self.data.clone()).ok()
1643 }
1644
1645 pub fn is_thinking_tokens(&self) -> bool {
1647 self.subtype == SystemSubtype::ThinkingTokens
1648 }
1649
1650 pub fn as_thinking_tokens(&self) -> Option<ThinkingTokensMessage> {
1652 if self.subtype != SystemSubtype::ThinkingTokens {
1653 return None;
1654 }
1655 serde_json::from_value(self.data.clone()).ok()
1656 }
1657
1658 pub fn is_code_change_published(&self) -> bool {
1660 self.subtype == SystemSubtype::CodeChangePublished
1661 }
1662
1663 pub fn as_code_change_published(&self) -> Option<CodeChangePublishedMessage> {
1665 if self.subtype != SystemSubtype::CodeChangePublished {
1666 return None;
1667 }
1668 serde_json::from_value(self.data.clone()).ok()
1669 }
1670
1671 pub fn is_vcs_state_changed(&self) -> bool {
1673 self.subtype == SystemSubtype::VcsStateChanged
1674 }
1675
1676 pub fn as_vcs_state_changed(&self) -> Option<VcsStateChangedMessage> {
1678 if self.subtype != SystemSubtype::VcsStateChanged {
1679 return None;
1680 }
1681 serde_json::from_value(self.data.clone()).ok()
1682 }
1683
1684 pub fn is_feedback_draft_queued(&self) -> bool {
1686 self.subtype == SystemSubtype::FeedbackDraftQueued
1687 }
1688
1689 pub fn as_feedback_draft_queued(&self) -> Option<FeedbackDraftQueuedMessage> {
1691 if self.subtype != SystemSubtype::FeedbackDraftQueued {
1692 return None;
1693 }
1694 serde_json::from_value(self.data.clone()).ok()
1695 }
1696
1697 pub fn is_cloud_session_delta(&self) -> bool {
1699 self.subtype == SystemSubtype::CloudSessionDelta
1700 }
1701
1702 pub fn as_cloud_session_delta(&self) -> Option<CloudSessionDeltaMessage> {
1704 if self.subtype != SystemSubtype::CloudSessionDelta {
1705 return None;
1706 }
1707 serde_json::from_value(self.data.clone()).ok()
1708 }
1709
1710 pub fn is_dev_intent(&self) -> bool {
1712 self.subtype == SystemSubtype::DevIntent
1713 }
1714
1715 pub fn as_dev_intent(&self) -> Option<DevIntentMessage> {
1717 if self.subtype != SystemSubtype::DevIntent {
1718 return None;
1719 }
1720 serde_json::from_value(self.data.clone()).ok()
1721 }
1722
1723 pub fn is_turn_handoff_available(&self) -> bool {
1725 self.subtype == SystemSubtype::TurnHandoffAvailable
1726 }
1727
1728 pub fn as_turn_handoff_available(&self) -> Option<TurnHandoffAvailableMessage> {
1730 if self.subtype != SystemSubtype::TurnHandoffAvailable {
1731 return None;
1732 }
1733 serde_json::from_value(self.data.clone()).ok()
1734 }
1735
1736 pub fn is_turn_preempted(&self) -> bool {
1738 self.subtype == SystemSubtype::TurnPreempted
1739 }
1740
1741 pub fn as_turn_preempted(&self) -> Option<TurnPreemptedMessage> {
1743 if self.subtype != SystemSubtype::TurnPreempted {
1744 return None;
1745 }
1746 serde_json::from_value(self.data.clone()).ok()
1747 }
1748
1749 pub fn is_peer_message_hold(&self) -> bool {
1751 self.subtype == SystemSubtype::PeerMessageHold
1752 }
1753
1754 pub fn as_peer_message_hold(&self) -> Option<PeerMessageHoldMessage> {
1756 if self.subtype != SystemSubtype::PeerMessageHold {
1757 return None;
1758 }
1759 serde_json::from_value(self.data.clone()).ok()
1760 }
1761
1762 pub fn is_per_turn_effort_changed(&self) -> bool {
1764 self.subtype == SystemSubtype::PerTurnEffortChanged
1765 }
1766
1767 pub fn as_per_turn_effort_changed(&self) -> Option<PerTurnEffortChangedMessage> {
1769 if self.subtype != SystemSubtype::PerTurnEffortChanged {
1770 return None;
1771 }
1772 serde_json::from_value(self.data.clone()).ok()
1773 }
1774
1775 pub fn is_session_title_changed(&self) -> bool {
1777 self.subtype == SystemSubtype::SessionTitleChanged
1778 }
1779
1780 pub fn as_session_title_changed(&self) -> Option<SessionTitleChangedMessage> {
1782 if self.subtype != SystemSubtype::SessionTitleChanged {
1783 return None;
1784 }
1785 serde_json::from_value(self.data.clone()).ok()
1786 }
1787
1788 pub fn is_instruction_size_warning(&self) -> bool {
1790 self.subtype == SystemSubtype::InstructionSizeWarning
1791 }
1792
1793 pub fn as_instruction_size_warning(&self) -> Option<InstructionSizeWarningMessage> {
1795 if self.subtype != SystemSubtype::InstructionSizeWarning {
1796 return None;
1797 }
1798 serde_json::from_value(self.data.clone()).ok()
1799 }
1800
1801 pub fn is_permission_check_status(&self) -> bool {
1803 self.subtype == SystemSubtype::PermissionCheckStatus
1804 }
1805
1806 pub fn as_permission_check_status(&self) -> Option<PermissionCheckStatusMessage> {
1808 if self.subtype != SystemSubtype::PermissionCheckStatus {
1809 return None;
1810 }
1811 serde_json::from_value(self.data.clone()).ok()
1812 }
1813
1814 pub fn is_inherited_asks_settled(&self) -> bool {
1816 self.subtype == SystemSubtype::InheritedAsksSettled
1817 }
1818
1819 pub fn as_inherited_asks_settled(&self) -> Option<InheritedAsksSettledMessage> {
1821 if self.subtype != SystemSubtype::InheritedAsksSettled {
1822 return None;
1823 }
1824 serde_json::from_value(self.data.clone()).ok()
1825 }
1826
1827 pub fn is_file_attachments_missing(&self) -> bool {
1829 self.subtype == SystemSubtype::FileAttachmentsMissing
1830 }
1831
1832 pub fn as_file_attachments_missing(&self) -> Option<FileAttachmentsMissingMessage> {
1834 if self.subtype != SystemSubtype::FileAttachmentsMissing {
1835 return None;
1836 }
1837 serde_json::from_value(self.data.clone()).ok()
1838 }
1839
1840 pub fn is_ui_focus(&self) -> bool {
1842 self.subtype == SystemSubtype::UiFocus
1843 }
1844
1845 pub fn as_ui_focus(&self) -> Option<UiFocusMessage> {
1847 if self.subtype != SystemSubtype::UiFocus {
1848 return None;
1849 }
1850 serde_json::from_value(self.data.clone()).ok()
1851 }
1852
1853 pub fn is_ui_invalidate(&self) -> bool {
1855 self.subtype == SystemSubtype::UiInvalidate
1856 }
1857
1858 pub fn as_ui_invalidate(&self) -> Option<UiInvalidateMessage> {
1860 if self.subtype != SystemSubtype::UiInvalidate {
1861 return None;
1862 }
1863 serde_json::from_value(self.data.clone()).ok()
1864 }
1865
1866 pub fn is_ui_log(&self) -> bool {
1868 self.subtype == SystemSubtype::UiLog
1869 }
1870
1871 pub fn as_ui_log(&self) -> Option<UiLogMessage> {
1873 if self.subtype != SystemSubtype::UiLog {
1874 return None;
1875 }
1876 serde_json::from_value(self.data.clone()).ok()
1877 }
1878
1879 pub fn is_ui_panes(&self) -> bool {
1881 self.subtype == SystemSubtype::UiPanes
1882 }
1883
1884 pub fn as_ui_panes(&self) -> Option<UiPanesMessage> {
1886 if self.subtype != SystemSubtype::UiPanes {
1887 return None;
1888 }
1889 serde_json::from_value(self.data.clone()).ok()
1890 }
1891
1892 pub fn is_ui_placed(&self) -> bool {
1894 self.subtype == SystemSubtype::UiPlaced
1895 }
1896
1897 pub fn as_ui_placed(&self) -> Option<UiPlacedMessage> {
1899 if self.subtype != SystemSubtype::UiPlaced {
1900 return None;
1901 }
1902 serde_json::from_value(self.data.clone()).ok()
1903 }
1904
1905 pub fn is_ui_scroll(&self) -> bool {
1907 self.subtype == SystemSubtype::UiScroll
1908 }
1909
1910 pub fn as_ui_scroll(&self) -> Option<UiScrollMessage> {
1912 if self.subtype != SystemSubtype::UiScroll {
1913 return None;
1914 }
1915 serde_json::from_value(self.data.clone()).ok()
1916 }
1917
1918 pub fn is_ui_status(&self) -> bool {
1920 self.subtype == SystemSubtype::UiStatus
1921 }
1922
1923 pub fn as_ui_status(&self) -> Option<UiStatusMessage> {
1925 if self.subtype != SystemSubtype::UiStatus {
1926 return None;
1927 }
1928 serde_json::from_value(self.data.clone()).ok()
1929 }
1930
1931 pub fn is_ui_toast(&self) -> bool {
1933 self.subtype == SystemSubtype::UiToast
1934 }
1935
1936 pub fn as_ui_toast(&self) -> Option<UiToastMessage> {
1938 if self.subtype != SystemSubtype::UiToast {
1939 return None;
1940 }
1941 serde_json::from_value(self.data.clone()).ok()
1942 }
1943
1944 pub fn as_known_system_event(&self) -> Option<KnownSystemEvent> {
1946 macro_rules! parse {
1947 ($variant:ident, $ty:ty) => {
1948 serde_json::from_value::<$ty>(self.data.clone())
1949 .ok()
1950 .map(KnownSystemEvent::$variant)
1951 };
1952 }
1953
1954 match self.subtype {
1955 SystemSubtype::Init => parse!(Init, InitMessage),
1956 SystemSubtype::Status => parse!(Status, StatusMessage),
1957 SystemSubtype::CompactBoundary => parse!(CompactBoundary, CompactBoundaryMessage),
1958 SystemSubtype::ThinkingTokens => parse!(ThinkingTokens, ThinkingTokensMessage),
1959 SystemSubtype::TaskStarted => parse!(TaskStarted, TaskStartedMessage),
1960 SystemSubtype::TaskProgress => parse!(TaskProgress, TaskProgressMessage),
1961 SystemSubtype::TaskUpdated => parse!(TaskUpdated, TaskUpdatedMessage),
1962 SystemSubtype::TaskNotification => parse!(TaskNotification, TaskNotificationMessage),
1963 SystemSubtype::ApiRetry => parse!(ApiRetry, ApiRetryMessage),
1964 SystemSubtype::ControlRequestProgress => {
1965 parse!(ControlRequestProgress, ControlRequestProgressMessage)
1966 }
1967 SystemSubtype::ModelRefusalFallback => {
1968 parse!(ModelRefusalFallback, ModelRefusalFallbackMessage)
1969 }
1970 SystemSubtype::ModelRefusalNoFallback => {
1971 parse!(ModelRefusalNoFallback, ModelRefusalNoFallbackMessage)
1972 }
1973 SystemSubtype::LocalCommandOutput => {
1974 parse!(LocalCommandOutput, LocalCommandOutputMessage)
1975 }
1976 SystemSubtype::HookStarted => parse!(HookStarted, HookStartedMessage),
1977 SystemSubtype::HookProgress => parse!(HookProgress, HookProgressMessage),
1978 SystemSubtype::HookResponse => parse!(HookResponse, HookResponseMessage),
1979 SystemSubtype::PluginInstall => parse!(PluginInstall, PluginInstallMessage),
1980 SystemSubtype::BackgroundTasksChanged => {
1981 parse!(BackgroundTasksChanged, BackgroundTasksChangedMessage)
1982 }
1983 SystemSubtype::SessionStateChanged => {
1984 parse!(SessionStateChanged, SessionStateChangedMessage)
1985 }
1986 SystemSubtype::WorkerShuttingDown => {
1987 parse!(WorkerShuttingDown, WorkerShuttingDownMessage)
1988 }
1989 SystemSubtype::CommandsChanged => parse!(CommandsChanged, CommandsChangedMessage),
1990 SystemSubtype::Notification => parse!(Notification, NotificationMessage),
1991 SystemSubtype::FilesPersisted => parse!(FilesPersisted, FilesPersistedMessage),
1992 SystemSubtype::MemoryRecall => parse!(MemoryRecall, MemoryRecallMessage),
1993 SystemSubtype::ElicitationComplete => {
1994 parse!(ElicitationComplete, ElicitationCompleteMessage)
1995 }
1996 SystemSubtype::PermissionDenied => parse!(PermissionDenied, PermissionDeniedMessage),
1997 SystemSubtype::MirrorError => parse!(MirrorError, MirrorErrorMessage),
1998 SystemSubtype::Informational => parse!(Informational, InformationalMessage),
1999 SystemSubtype::CodeChangePublished => {
2000 parse!(CodeChangePublished, CodeChangePublishedMessage)
2001 }
2002 SystemSubtype::VcsStateChanged => parse!(VcsStateChanged, VcsStateChangedMessage),
2003 SystemSubtype::FeedbackDraftQueued => {
2004 parse!(FeedbackDraftQueued, FeedbackDraftQueuedMessage)
2005 }
2006 SystemSubtype::CloudSessionDelta => {
2007 parse!(CloudSessionDelta, CloudSessionDeltaMessage)
2008 }
2009 SystemSubtype::DevIntent => parse!(DevIntent, DevIntentMessage),
2010 SystemSubtype::TurnHandoffAvailable => {
2011 parse!(TurnHandoffAvailable, TurnHandoffAvailableMessage)
2012 }
2013 SystemSubtype::TurnPreempted => parse!(TurnPreempted, TurnPreemptedMessage),
2014 SystemSubtype::PeerMessageHold => parse!(PeerMessageHold, PeerMessageHoldMessage),
2015 SystemSubtype::PerTurnEffortChanged => {
2016 parse!(PerTurnEffortChanged, PerTurnEffortChangedMessage)
2017 }
2018 SystemSubtype::SessionTitleChanged => {
2019 parse!(SessionTitleChanged, SessionTitleChangedMessage)
2020 }
2021 SystemSubtype::InstructionSizeWarning => {
2022 parse!(InstructionSizeWarning, InstructionSizeWarningMessage)
2023 }
2024 SystemSubtype::FileAttachmentsMissing => {
2025 parse!(FileAttachmentsMissing, FileAttachmentsMissingMessage)
2026 }
2027 SystemSubtype::PermissionCheckStatus => {
2028 parse!(PermissionCheckStatus, PermissionCheckStatusMessage)
2029 }
2030 SystemSubtype::InheritedAsksSettled => {
2031 parse!(InheritedAsksSettled, InheritedAsksSettledMessage)
2032 }
2033 SystemSubtype::UiFocus => parse!(UiFocus, UiFocusMessage),
2034 SystemSubtype::UiInvalidate => parse!(UiInvalidate, UiInvalidateMessage),
2035 SystemSubtype::UiLog => parse!(UiLog, UiLogMessage),
2036 SystemSubtype::UiPanes => parse!(UiPanes, UiPanesMessage),
2037 SystemSubtype::UiPlaced => parse!(UiPlaced, UiPlacedMessage),
2038 SystemSubtype::UiScroll => parse!(UiScroll, UiScrollMessage),
2039 SystemSubtype::UiStatus => parse!(UiStatus, UiStatusMessage),
2040 SystemSubtype::UiToast => parse!(UiToast, UiToastMessage),
2041 SystemSubtype::Unknown(_) => None,
2042 }
2043 }
2044
2045 pub fn typed_value(&self) -> Option<Value> {
2054 fn reserialize<T: Serialize>(parsed: Option<T>) -> Option<Value> {
2055 parsed.and_then(|v| serde_json::to_value(v).ok())
2056 }
2057 match self.subtype {
2058 SystemSubtype::Init => reserialize(self.as_init()),
2059 SystemSubtype::Status => reserialize(self.as_status()),
2060 SystemSubtype::CompactBoundary => reserialize(self.as_compact_boundary()),
2061 SystemSubtype::ThinkingTokens => reserialize(self.as_thinking_tokens()),
2062 SystemSubtype::TaskStarted => reserialize(self.as_task_started()),
2063 SystemSubtype::TaskProgress => reserialize(self.as_task_progress()),
2064 SystemSubtype::TaskUpdated => reserialize(self.as_task_updated()),
2065 SystemSubtype::TaskNotification => reserialize(self.as_task_notification()),
2066 SystemSubtype::ApiRetry => reserialize(parse_system::<ApiRetryMessage>(self)),
2067 SystemSubtype::ControlRequestProgress => {
2068 reserialize(parse_system::<ControlRequestProgressMessage>(self))
2069 }
2070 SystemSubtype::ModelRefusalFallback => {
2071 reserialize(parse_system::<ModelRefusalFallbackMessage>(self))
2072 }
2073 SystemSubtype::ModelRefusalNoFallback => {
2074 reserialize(parse_system::<ModelRefusalNoFallbackMessage>(self))
2075 }
2076 SystemSubtype::LocalCommandOutput => {
2077 reserialize(parse_system::<LocalCommandOutputMessage>(self))
2078 }
2079 SystemSubtype::HookStarted => reserialize(parse_system::<HookStartedMessage>(self)),
2080 SystemSubtype::HookProgress => reserialize(parse_system::<HookProgressMessage>(self)),
2081 SystemSubtype::HookResponse => reserialize(parse_system::<HookResponseMessage>(self)),
2082 SystemSubtype::PluginInstall => reserialize(parse_system::<PluginInstallMessage>(self)),
2083 SystemSubtype::BackgroundTasksChanged => {
2084 reserialize(parse_system::<BackgroundTasksChangedMessage>(self))
2085 }
2086 SystemSubtype::SessionStateChanged => {
2087 reserialize(parse_system::<SessionStateChangedMessage>(self))
2088 }
2089 SystemSubtype::WorkerShuttingDown => {
2090 reserialize(parse_system::<WorkerShuttingDownMessage>(self))
2091 }
2092 SystemSubtype::CommandsChanged => {
2093 reserialize(parse_system::<CommandsChangedMessage>(self))
2094 }
2095 SystemSubtype::Notification => reserialize(parse_system::<NotificationMessage>(self)),
2096 SystemSubtype::FilesPersisted => {
2097 reserialize(parse_system::<FilesPersistedMessage>(self))
2098 }
2099 SystemSubtype::MemoryRecall => reserialize(parse_system::<MemoryRecallMessage>(self)),
2100 SystemSubtype::ElicitationComplete => {
2101 reserialize(parse_system::<ElicitationCompleteMessage>(self))
2102 }
2103 SystemSubtype::PermissionDenied => {
2104 reserialize(parse_system::<PermissionDeniedMessage>(self))
2105 }
2106 SystemSubtype::MirrorError => reserialize(parse_system::<MirrorErrorMessage>(self)),
2107 SystemSubtype::Informational => reserialize(parse_system::<InformationalMessage>(self)),
2108 SystemSubtype::CodeChangePublished => {
2109 reserialize(parse_system::<CodeChangePublishedMessage>(self))
2110 }
2111 SystemSubtype::VcsStateChanged => {
2112 reserialize(parse_system::<VcsStateChangedMessage>(self))
2113 }
2114 SystemSubtype::FeedbackDraftQueued => {
2115 reserialize(parse_system::<FeedbackDraftQueuedMessage>(self))
2116 }
2117 SystemSubtype::CloudSessionDelta => {
2118 reserialize(parse_system::<CloudSessionDeltaMessage>(self))
2119 }
2120 SystemSubtype::DevIntent => reserialize(parse_system::<DevIntentMessage>(self)),
2121 SystemSubtype::TurnHandoffAvailable => {
2122 reserialize(parse_system::<TurnHandoffAvailableMessage>(self))
2123 }
2124 SystemSubtype::TurnPreempted => reserialize(parse_system::<TurnPreemptedMessage>(self)),
2125 SystemSubtype::PeerMessageHold => {
2126 reserialize(parse_system::<PeerMessageHoldMessage>(self))
2127 }
2128 SystemSubtype::PerTurnEffortChanged => {
2129 reserialize(parse_system::<PerTurnEffortChangedMessage>(self))
2130 }
2131 SystemSubtype::SessionTitleChanged => {
2132 reserialize(parse_system::<SessionTitleChangedMessage>(self))
2133 }
2134 SystemSubtype::InstructionSizeWarning => {
2135 reserialize(parse_system::<InstructionSizeWarningMessage>(self))
2136 }
2137 SystemSubtype::FileAttachmentsMissing => {
2138 reserialize(parse_system::<FileAttachmentsMissingMessage>(self))
2139 }
2140 SystemSubtype::PermissionCheckStatus => {
2141 reserialize(parse_system::<PermissionCheckStatusMessage>(self))
2142 }
2143 SystemSubtype::InheritedAsksSettled => {
2144 reserialize(parse_system::<InheritedAsksSettledMessage>(self))
2145 }
2146 SystemSubtype::UiFocus => reserialize(parse_system::<UiFocusMessage>(self)),
2147 SystemSubtype::UiInvalidate => reserialize(parse_system::<UiInvalidateMessage>(self)),
2148 SystemSubtype::UiLog => reserialize(parse_system::<UiLogMessage>(self)),
2149 SystemSubtype::UiPanes => reserialize(parse_system::<UiPanesMessage>(self)),
2150 SystemSubtype::UiPlaced => reserialize(parse_system::<UiPlacedMessage>(self)),
2151 SystemSubtype::UiScroll => reserialize(parse_system::<UiScrollMessage>(self)),
2152 SystemSubtype::UiStatus => reserialize(parse_system::<UiStatusMessage>(self)),
2153 SystemSubtype::UiToast => reserialize(parse_system::<UiToastMessage>(self)),
2154 SystemSubtype::Unknown(_) => None,
2155 }
2156 }
2157}
2158
2159fn parse_system<T: serde::de::DeserializeOwned>(message: &SystemMessage) -> Option<T> {
2160 serde_json::from_value(message.data.clone()).ok()
2161}
2162
2163#[allow(clippy::large_enum_variant)]
2169#[derive(Debug, Clone, Serialize, Deserialize)]
2170pub enum KnownSystemEvent {
2171 Init(InitMessage),
2172 Status(StatusMessage),
2173 CompactBoundary(CompactBoundaryMessage),
2174 ThinkingTokens(ThinkingTokensMessage),
2175 TaskStarted(TaskStartedMessage),
2176 TaskProgress(TaskProgressMessage),
2177 TaskUpdated(TaskUpdatedMessage),
2178 TaskNotification(TaskNotificationMessage),
2179 ApiRetry(ApiRetryMessage),
2180 ControlRequestProgress(ControlRequestProgressMessage),
2181 ModelRefusalFallback(ModelRefusalFallbackMessage),
2182 ModelRefusalNoFallback(ModelRefusalNoFallbackMessage),
2183 LocalCommandOutput(LocalCommandOutputMessage),
2184 HookStarted(HookStartedMessage),
2185 HookProgress(HookProgressMessage),
2186 HookResponse(HookResponseMessage),
2187 PluginInstall(PluginInstallMessage),
2188 BackgroundTasksChanged(BackgroundTasksChangedMessage),
2189 SessionStateChanged(SessionStateChangedMessage),
2190 WorkerShuttingDown(WorkerShuttingDownMessage),
2191 CommandsChanged(CommandsChangedMessage),
2192 Notification(NotificationMessage),
2193 FilesPersisted(FilesPersistedMessage),
2194 MemoryRecall(MemoryRecallMessage),
2195 ElicitationComplete(ElicitationCompleteMessage),
2196 PermissionDenied(PermissionDeniedMessage),
2197 MirrorError(MirrorErrorMessage),
2198 Informational(InformationalMessage),
2199 CodeChangePublished(CodeChangePublishedMessage),
2200 VcsStateChanged(VcsStateChangedMessage),
2201 FeedbackDraftQueued(FeedbackDraftQueuedMessage),
2202 CloudSessionDelta(CloudSessionDeltaMessage),
2203 DevIntent(DevIntentMessage),
2204 TurnHandoffAvailable(TurnHandoffAvailableMessage),
2205 TurnPreempted(TurnPreemptedMessage),
2206 PeerMessageHold(PeerMessageHoldMessage),
2207 PerTurnEffortChanged(PerTurnEffortChangedMessage),
2208 SessionTitleChanged(SessionTitleChangedMessage),
2209 InstructionSizeWarning(InstructionSizeWarningMessage),
2210 FileAttachmentsMissing(FileAttachmentsMissingMessage),
2211 PermissionCheckStatus(PermissionCheckStatusMessage),
2212 InheritedAsksSettled(InheritedAsksSettledMessage),
2213 UiFocus(UiFocusMessage),
2214 UiInvalidate(UiInvalidateMessage),
2215 UiLog(UiLogMessage),
2216 UiPanes(UiPanesMessage),
2217 UiPlaced(UiPlacedMessage),
2218 UiScroll(UiScrollMessage),
2219 UiStatus(UiStatusMessage),
2220 UiToast(UiToastMessage),
2221}
2222
2223#[derive(Debug, Clone, Serialize, Deserialize)]
2224pub struct ApiRetryMessage {
2225 pub attempt: u64,
2226 pub max_retries: u64,
2227 pub retry_delay_ms: u64,
2228 pub error_status: Option<u16>,
2229 pub error: String,
2230 #[serde(default, skip_serializing_if = "Option::is_none")]
2235 pub no_response: Option<ApiRetryNoResponse>,
2236 #[serde(default, skip_serializing_if = "Option::is_none")]
2237 pub uuid: Option<String>,
2238 #[serde(default, skip_serializing_if = "Option::is_none")]
2239 pub session_id: Option<String>,
2240}
2241
2242#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2245pub struct ApiRetryNoResponse {
2246 pub waited_ms: u64,
2248 pub retry_wait_ms: u64,
2250}
2251
2252#[derive(Debug, Clone, Serialize, Deserialize)]
2253pub struct ControlRequestProgressMessage {
2254 pub request_id: String,
2255 pub status: String,
2256 #[serde(default, skip_serializing_if = "Option::is_none")]
2257 pub attempt: Option<u64>,
2258 #[serde(default, skip_serializing_if = "Option::is_none")]
2259 pub max_retries: Option<u64>,
2260 #[serde(default, skip_serializing_if = "Option::is_none")]
2261 pub retry_delay_ms: Option<u64>,
2262 #[serde(default, skip_serializing_if = "Option::is_none")]
2263 pub error_status: Option<u16>,
2264 #[serde(default, skip_serializing_if = "Option::is_none")]
2265 pub error: Option<String>,
2266 #[serde(default, skip_serializing_if = "Option::is_none")]
2267 pub uuid: Option<String>,
2268 #[serde(default, skip_serializing_if = "Option::is_none")]
2269 pub session_id: Option<String>,
2270}
2271
2272#[derive(Debug, Clone, Serialize, Deserialize)]
2273pub struct ModelRefusalFallbackMessage {
2274 pub trigger: String,
2275 pub direction: String,
2276 #[serde(default, skip_serializing_if = "Option::is_none")]
2282 pub scope: Option<RefusalFallbackScope>,
2283 pub original_model: String,
2284 pub fallback_model: String,
2285 pub request_id: Option<String>,
2286 #[serde(default, skip_serializing_if = "Option::is_none")]
2287 pub api_refusal_category: Option<String>,
2288 #[serde(default, skip_serializing_if = "Option::is_none")]
2293 pub saw_cyber_refusal: Option<bool>,
2294 #[serde(default, skip_serializing_if = "Option::is_none")]
2295 pub api_refusal_explanation: Option<String>,
2296 #[serde(default, skip_serializing_if = "Option::is_none")]
2297 pub retracted_message_uuids: Option<Vec<String>>,
2298 #[serde(default, skip_serializing_if = "Option::is_none")]
2299 pub refused_user_message_uuid: Option<String>,
2300 pub content: Value,
2301 #[serde(default, skip_serializing_if = "Option::is_none")]
2302 pub uuid: Option<String>,
2303 #[serde(default, skip_serializing_if = "Option::is_none")]
2304 pub session_id: Option<String>,
2305}
2306
2307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2311pub enum RefusalFallbackScope {
2312 Session,
2314 Local,
2317 Unknown(String),
2319}
2320
2321impl RefusalFallbackScope {
2322 pub fn as_str(&self) -> &str {
2323 match self {
2324 Self::Session => "session",
2325 Self::Local => "local",
2326 Self::Unknown(s) => s.as_str(),
2327 }
2328 }
2329}
2330
2331impl fmt::Display for RefusalFallbackScope {
2332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2333 f.write_str(self.as_str())
2334 }
2335}
2336
2337impl From<&str> for RefusalFallbackScope {
2338 fn from(s: &str) -> Self {
2339 match s {
2340 "session" => Self::Session,
2341 "local" => Self::Local,
2342 other => Self::Unknown(other.to_string()),
2343 }
2344 }
2345}
2346
2347impl Serialize for RefusalFallbackScope {
2348 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2349 serializer.serialize_str(self.as_str())
2350 }
2351}
2352
2353impl<'de> Deserialize<'de> for RefusalFallbackScope {
2354 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
2355 let s = String::deserialize(deserializer)?;
2356 Ok(Self::from(s.as_str()))
2357 }
2358}
2359
2360#[derive(Debug, Clone, Serialize, Deserialize)]
2361pub struct ModelRefusalNoFallbackMessage {
2362 pub original_model: String,
2363 pub request_id: Option<String>,
2364 #[serde(default, skip_serializing_if = "Option::is_none")]
2365 pub api_refusal_category: Option<String>,
2366 #[serde(default, skip_serializing_if = "Option::is_none")]
2367 pub api_refusal_explanation: Option<String>,
2368 pub content: Value,
2369 #[serde(default, skip_serializing_if = "Option::is_none")]
2370 pub uuid: Option<String>,
2371 #[serde(default, skip_serializing_if = "Option::is_none")]
2372 pub session_id: Option<String>,
2373}
2374
2375#[derive(Debug, Clone, Serialize, Deserialize)]
2376pub struct LocalCommandOutputMessage {
2377 pub content: String,
2378 #[serde(default, skip_serializing_if = "Option::is_none")]
2379 pub uuid: Option<String>,
2380 #[serde(default, skip_serializing_if = "Option::is_none")]
2381 pub session_id: Option<String>,
2382}
2383
2384#[derive(Debug, Clone, Serialize, Deserialize)]
2385pub struct HookStartedMessage {
2386 pub hook_id: String,
2387 pub hook_name: String,
2388 pub hook_event: String,
2389 #[serde(default, skip_serializing_if = "Option::is_none")]
2390 pub uuid: Option<String>,
2391 #[serde(default, skip_serializing_if = "Option::is_none")]
2392 pub session_id: Option<String>,
2393}
2394
2395#[derive(Debug, Clone, Serialize, Deserialize)]
2396pub struct HookProgressMessage {
2397 pub hook_id: String,
2398 pub hook_name: String,
2399 pub hook_event: String,
2400 #[serde(default, skip_serializing_if = "Option::is_none")]
2401 pub stdout: Option<String>,
2402 #[serde(default, skip_serializing_if = "Option::is_none")]
2403 pub stderr: Option<String>,
2404 #[serde(default, skip_serializing_if = "Option::is_none")]
2405 pub output: Option<String>,
2406 #[serde(default, skip_serializing_if = "Option::is_none")]
2407 pub uuid: Option<String>,
2408 #[serde(default, skip_serializing_if = "Option::is_none")]
2409 pub session_id: Option<String>,
2410}
2411
2412#[derive(Debug, Clone, Serialize, Deserialize)]
2413pub struct HookResponseMessage {
2414 pub hook_id: String,
2415 pub hook_name: String,
2416 pub hook_event: String,
2417 #[serde(default, skip_serializing_if = "Option::is_none")]
2418 pub stdout: Option<String>,
2419 #[serde(default, skip_serializing_if = "Option::is_none")]
2420 pub stderr: Option<String>,
2421 #[serde(default, skip_serializing_if = "Option::is_none")]
2422 pub output: Option<String>,
2423 #[serde(default, skip_serializing_if = "Option::is_none")]
2424 pub exit_code: Option<i32>,
2425 pub outcome: String,
2426 #[serde(default, skip_serializing_if = "Option::is_none")]
2427 pub uuid: Option<String>,
2428 #[serde(default, skip_serializing_if = "Option::is_none")]
2429 pub session_id: Option<String>,
2430}
2431
2432#[derive(Debug, Clone, Serialize, Deserialize)]
2433pub struct PluginInstallMessage {
2434 pub status: String,
2435 #[serde(default, skip_serializing_if = "Option::is_none")]
2436 pub name: Option<String>,
2437 #[serde(default, skip_serializing_if = "Option::is_none")]
2438 pub error: Option<String>,
2439 #[serde(default, skip_serializing_if = "Option::is_none")]
2440 pub uuid: Option<String>,
2441 #[serde(default, skip_serializing_if = "Option::is_none")]
2442 pub session_id: Option<String>,
2443}
2444
2445#[derive(Debug, Clone, Serialize, Deserialize)]
2446pub struct BackgroundTasksChangedMessage {
2447 pub tasks: Vec<BackgroundTaskInfo>,
2448 #[serde(default, skip_serializing_if = "Option::is_none")]
2449 pub uuid: Option<String>,
2450 #[serde(default, skip_serializing_if = "Option::is_none")]
2451 pub session_id: Option<String>,
2452}
2453
2454#[derive(Debug, Clone, Serialize, Deserialize)]
2455pub struct BackgroundTaskInfo {
2456 pub task_id: String,
2457 #[serde(default, skip_serializing_if = "Option::is_none")]
2460 pub run_id: Option<String>,
2461 pub task_type: String,
2462 #[serde(default, skip_serializing_if = "Option::is_none")]
2466 pub subagent_type: Option<String>,
2467 pub description: String,
2468 #[serde(default, skip_serializing_if = "Option::is_none")]
2471 pub parent_task_id: Option<String>,
2472 #[serde(default, skip_serializing_if = "Option::is_none")]
2475 pub ambient: Option<bool>,
2476 #[serde(default, skip_serializing_if = "Option::is_none")]
2479 pub awaited: Option<bool>,
2480}
2481
2482#[derive(Debug, Clone, Serialize, Deserialize)]
2483pub struct SessionStateChangedMessage {
2484 pub state: String,
2485 #[serde(default, skip_serializing_if = "Option::is_none")]
2486 pub uuid: Option<String>,
2487 #[serde(default, skip_serializing_if = "Option::is_none")]
2488 pub session_id: Option<String>,
2489}
2490
2491#[derive(Debug, Clone, Serialize, Deserialize)]
2492pub struct WorkerShuttingDownMessage {
2493 pub reason: String,
2494 #[serde(default, skip_serializing_if = "Option::is_none")]
2495 pub uuid: Option<String>,
2496 #[serde(default, skip_serializing_if = "Option::is_none")]
2497 pub session_id: Option<String>,
2498}
2499
2500#[derive(Debug, Clone, Serialize, Deserialize)]
2501pub struct CommandsChangedMessage {
2502 pub commands: Vec<CommandInfo>,
2503 #[serde(default, skip_serializing_if = "Option::is_none")]
2504 pub uuid: Option<String>,
2505 #[serde(default, skip_serializing_if = "Option::is_none")]
2506 pub session_id: Option<String>,
2507}
2508
2509#[derive(Debug, Clone, Serialize, Deserialize)]
2510pub struct CommandInfo {
2511 pub name: String,
2512 pub description: String,
2513 #[serde(rename = "argumentHint")]
2514 pub argument_hint: String,
2515 #[serde(default, skip_serializing_if = "Option::is_none")]
2516 pub aliases: Option<Vec<String>>,
2517 #[serde(default, skip_serializing_if = "Option::is_none")]
2523 pub builtin: Option<bool>,
2524}
2525
2526#[derive(Debug, Clone, Serialize, Deserialize)]
2527pub struct NotificationMessage {
2528 pub key: String,
2529 pub text: String,
2530 pub priority: String,
2531 #[serde(default, skip_serializing_if = "Option::is_none")]
2532 pub color: Option<String>,
2533 #[serde(default, skip_serializing_if = "Option::is_none")]
2534 pub timeout_ms: Option<u64>,
2535 #[serde(default, skip_serializing_if = "Option::is_none")]
2536 pub uuid: Option<String>,
2537 #[serde(default, skip_serializing_if = "Option::is_none")]
2538 pub session_id: Option<String>,
2539}
2540
2541#[derive(Debug, Clone, Serialize, Deserialize)]
2542pub struct FilesPersistedMessage {
2543 pub files: Vec<PersistedFile>,
2544 pub failed: Vec<FailedPersistedFile>,
2545 pub processed_at: String,
2546 #[serde(default, skip_serializing_if = "Option::is_none")]
2547 pub uuid: Option<String>,
2548 #[serde(default, skip_serializing_if = "Option::is_none")]
2549 pub session_id: Option<String>,
2550}
2551
2552#[derive(Debug, Clone, Serialize, Deserialize)]
2553pub struct PersistedFile {
2554 pub filename: String,
2555 pub file_id: String,
2556}
2557
2558#[derive(Debug, Clone, Serialize, Deserialize)]
2559pub struct FailedPersistedFile {
2560 pub filename: String,
2561 pub error: String,
2562}
2563
2564#[derive(Debug, Clone, Serialize, Deserialize)]
2565pub struct MemoryRecallMessage {
2566 pub mode: String,
2567 pub memories: Vec<MemoryRecallItem>,
2568 #[serde(default, skip_serializing_if = "Option::is_none")]
2569 pub uuid: Option<String>,
2570 #[serde(default, skip_serializing_if = "Option::is_none")]
2571 pub session_id: Option<String>,
2572}
2573
2574#[derive(Debug, Clone, Serialize, Deserialize)]
2575pub struct MemoryRecallItem {
2576 pub path: String,
2577 pub scope: String,
2578 #[serde(default, skip_serializing_if = "Option::is_none")]
2579 pub content: Option<String>,
2580}
2581
2582#[derive(Debug, Clone, Serialize, Deserialize)]
2583pub struct ElicitationCompleteMessage {
2584 pub mcp_server_name: String,
2585 pub elicitation_id: String,
2586 #[serde(default, skip_serializing_if = "Option::is_none")]
2587 pub uuid: Option<String>,
2588 #[serde(default, skip_serializing_if = "Option::is_none")]
2589 pub session_id: Option<String>,
2590}
2591
2592#[derive(Debug, Clone, Serialize, Deserialize)]
2593pub struct PermissionDeniedMessage {
2594 pub tool_name: String,
2595 pub tool_use_id: String,
2596 #[serde(default, skip_serializing_if = "Option::is_none")]
2597 pub agent_id: Option<String>,
2598 #[serde(default, skip_serializing_if = "Option::is_none")]
2599 pub decision_reason_type: Option<String>,
2600 #[serde(default, skip_serializing_if = "Option::is_none")]
2604 pub decision_reason_code: Option<PermissionDeniedReasonCode>,
2605 #[serde(default, skip_serializing_if = "Option::is_none")]
2606 pub decision_reason: Option<String>,
2607 pub message: String,
2608 #[serde(default, skip_serializing_if = "Option::is_none")]
2609 pub uuid: Option<String>,
2610 #[serde(default, skip_serializing_if = "Option::is_none")]
2611 pub session_id: Option<String>,
2612}
2613
2614#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2617pub enum PermissionDeniedReasonCode {
2618 ClassifierTranscriptTooLong,
2620 OutsideReadsBlocked,
2622 MemoryPaused,
2624 Unknown(String),
2626}
2627
2628impl PermissionDeniedReasonCode {
2629 pub fn as_str(&self) -> &str {
2630 match self {
2631 Self::ClassifierTranscriptTooLong => "classifier_transcript_too_long",
2632 Self::OutsideReadsBlocked => "outside_reads_blocked",
2633 Self::MemoryPaused => "memory_paused",
2634 Self::Unknown(s) => s.as_str(),
2635 }
2636 }
2637}
2638
2639impl fmt::Display for PermissionDeniedReasonCode {
2640 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2641 f.write_str(self.as_str())
2642 }
2643}
2644
2645impl From<&str> for PermissionDeniedReasonCode {
2646 fn from(s: &str) -> Self {
2647 match s {
2648 "classifier_transcript_too_long" => Self::ClassifierTranscriptTooLong,
2649 "outside_reads_blocked" => Self::OutsideReadsBlocked,
2650 "memory_paused" => Self::MemoryPaused,
2651 other => Self::Unknown(other.to_string()),
2652 }
2653 }
2654}
2655
2656impl Serialize for PermissionDeniedReasonCode {
2657 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2658 serializer.serialize_str(self.as_str())
2659 }
2660}
2661
2662impl<'de> Deserialize<'de> for PermissionDeniedReasonCode {
2663 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
2664 let s = String::deserialize(deserializer)?;
2665 Ok(Self::from(s.as_str()))
2666 }
2667}
2668
2669#[derive(Debug, Clone, Serialize, Deserialize)]
2670pub struct MirrorErrorMessage {
2671 pub error: String,
2672 pub key: MirrorErrorKey,
2673 #[serde(default, skip_serializing_if = "Option::is_none")]
2674 pub uuid: Option<String>,
2675 #[serde(default, skip_serializing_if = "Option::is_none")]
2676 pub session_id: Option<String>,
2677}
2678
2679#[derive(Debug, Clone, Serialize, Deserialize)]
2680pub struct MirrorErrorKey {
2681 #[serde(rename = "projectKey")]
2682 pub project_key: String,
2683 #[serde(rename = "sessionId")]
2684 pub session_id: String,
2685 #[serde(default, skip_serializing_if = "Option::is_none")]
2686 pub subpath: Option<String>,
2687}
2688
2689#[derive(Debug, Clone, Serialize, Deserialize)]
2690pub struct InformationalMessage {
2691 pub content: String,
2692 pub level: String,
2693 #[serde(default, skip_serializing_if = "Option::is_none")]
2694 pub tool_use_id: Option<String>,
2695 #[serde(default, skip_serializing_if = "Option::is_none")]
2696 pub prevent_continuation: Option<bool>,
2697 #[serde(default, skip_serializing_if = "Option::is_none")]
2700 pub tag: Option<String>,
2701 #[serde(default, skip_serializing_if = "Option::is_none")]
2707 pub prompt_submit_outcome: Option<PromptSubmitOutcome>,
2708 #[serde(default, skip_serializing_if = "Option::is_none")]
2709 pub uuid: Option<String>,
2710 #[serde(default, skip_serializing_if = "Option::is_none")]
2711 pub session_id: Option<String>,
2712}
2713
2714#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2717pub struct PromptSubmitOutcome {
2718 #[serde(default, skip_serializing_if = "Option::is_none")]
2722 pub kind: Option<PromptSubmitOutcomeKind>,
2723 #[serde(default, skip_serializing_if = "Option::is_none")]
2730 pub no_verdict_kind: Option<String>,
2731 pub prompt_kept: bool,
2734 #[serde(default, skip_serializing_if = "Option::is_none")]
2738 pub reason: Option<String>,
2739}
2740
2741#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2743pub enum PromptSubmitOutcomeKind {
2744 BlockedByHook,
2746 StoppedByHook,
2748 DroppedByHook,
2750 NotPassedOn,
2752 NoVerdict,
2755 Other(String),
2757}
2758
2759impl PromptSubmitOutcomeKind {
2760 pub fn as_str(&self) -> &str {
2761 match self {
2762 Self::BlockedByHook => "blocked_by_hook",
2763 Self::StoppedByHook => "stopped_by_hook",
2764 Self::DroppedByHook => "dropped_by_hook",
2765 Self::NotPassedOn => "not_passed_on",
2766 Self::NoVerdict => "no_verdict",
2767 Self::Other(s) => s.as_str(),
2768 }
2769 }
2770}
2771
2772impl fmt::Display for PromptSubmitOutcomeKind {
2773 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2774 f.write_str(self.as_str())
2775 }
2776}
2777
2778impl From<&str> for PromptSubmitOutcomeKind {
2779 fn from(s: &str) -> Self {
2780 match s {
2781 "blocked_by_hook" => Self::BlockedByHook,
2782 "stopped_by_hook" => Self::StoppedByHook,
2783 "dropped_by_hook" => Self::DroppedByHook,
2784 "not_passed_on" => Self::NotPassedOn,
2785 "no_verdict" => Self::NoVerdict,
2786 other => Self::Other(other.to_string()),
2787 }
2788 }
2789}
2790
2791impl Serialize for PromptSubmitOutcomeKind {
2792 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2793 serializer.serialize_str(self.as_str())
2794 }
2795}
2796
2797impl<'de> Deserialize<'de> for PromptSubmitOutcomeKind {
2798 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
2799 let s = String::deserialize(deserializer)?;
2800 Ok(Self::from(s.as_str()))
2801 }
2802}
2803
2804#[derive(Debug, Clone, Serialize, Deserialize)]
2806pub struct PluginInfo {
2807 pub name: String,
2809 pub path: String,
2811 #[serde(skip_serializing_if = "Option::is_none")]
2813 pub source: Option<String>,
2814 #[serde(default, skip_serializing_if = "Option::is_none")]
2816 pub version: Option<String>,
2817}
2818
2819#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2821pub struct PluginDiagnostic {
2822 pub plugin: String,
2823 #[serde(rename = "type")]
2824 pub diagnostic_type: String,
2825 pub message: String,
2826}
2827
2828#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2830pub struct MemoryPaths {
2831 #[serde(default, skip_serializing_if = "Option::is_none")]
2832 pub auto: Option<String>,
2833 #[serde(default, skip_serializing_if = "Option::is_none")]
2834 pub team: Option<String>,
2835 #[serde(flatten)]
2836 pub extra: serde_json::Map<String, Value>,
2837}
2838
2839#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2843pub struct McpServerError {
2844 pub name: String,
2845 #[serde(rename = "type")]
2847 pub error_type: String,
2848 pub message: String,
2849}
2850
2851#[derive(Debug, Clone, Serialize, Deserialize)]
2853pub struct InitMessage {
2854 pub session_id: String,
2856 #[serde(skip_serializing_if = "Option::is_none")]
2858 pub cwd: Option<String>,
2859 #[serde(skip_serializing_if = "Option::is_none")]
2861 pub model: Option<String>,
2862 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2864 pub tools: Vec<String>,
2865 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2867 pub mcp_servers: Vec<Value>,
2868 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2870 pub slash_commands: Vec<String>,
2871 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2874 pub terminal_slash_commands: Vec<String>,
2875 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2877 pub agents: Vec<String>,
2878 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2880 pub plugins: Vec<PluginInfo>,
2881 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2883 pub skills: Vec<Value>,
2884 #[serde(skip_serializing_if = "Option::is_none")]
2886 pub claude_code_version: Option<String>,
2887 #[serde(skip_serializing_if = "Option::is_none")]
2890 pub messaging_socket_path: Option<String>,
2891 #[serde(skip_serializing_if = "Option::is_none", rename = "apiKeySource")]
2893 pub api_key_source: Option<ApiKeySource>,
2894 #[serde(skip_serializing_if = "Option::is_none")]
2896 pub output_style: Option<OutputStyle>,
2897 #[serde(skip_serializing_if = "Option::is_none", rename = "permissionMode")]
2899 pub permission_mode: Option<InitPermissionMode>,
2900
2901 #[serde(skip_serializing_if = "Option::is_none")]
2903 pub uuid: Option<String>,
2904
2905 #[serde(skip_serializing_if = "Option::is_none")]
2907 pub memory_paths: Option<MemoryPaths>,
2908
2909 #[serde(default, skip_serializing_if = "Option::is_none")]
2914 pub scratchpad_path: Option<String>,
2915
2916 #[serde(skip_serializing_if = "Option::is_none")]
2918 pub fast_mode_state: Option<String>,
2919
2920 #[serde(default, skip_serializing_if = "Option::is_none")]
2922 pub fast_mode_disabled_reason: Option<super::result::FastModeDisabledReason>,
2923
2924 #[serde(default, skip_serializing_if = "Option::is_none")]
2927 pub mcp_server_errors: Option<Vec<McpServerError>>,
2928
2929 #[serde(default, skip_serializing_if = "Option::is_none")]
2931 pub analytics_disabled: Option<bool>,
2932
2933 #[serde(default, skip_serializing_if = "Option::is_none")]
2935 pub product_feedback_disabled: Option<bool>,
2936
2937 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2939 pub betas: Vec<String>,
2940
2941 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2943 pub capabilities: Vec<String>,
2944
2945 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2947 pub plugin_errors: Vec<PluginDiagnostic>,
2948
2949 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2951 pub plugin_warnings: Vec<PluginDiagnostic>,
2952
2953 #[serde(default, skip_serializing_if = "Option::is_none")]
2958 pub effort: Option<String>,
2959
2960 #[serde(default, skip_serializing_if = "Option::is_none")]
2965 pub cloud_session: Option<Value>,
2966
2967 #[serde(default, skip_serializing_if = "Option::is_none")]
2971 pub footer_indicator: Option<FooterIndicator>,
2972
2973 #[serde(default, skip_serializing_if = "Option::is_none")]
2977 pub worker_epoch: Option<u64>,
2978
2979 #[serde(default, skip_serializing_if = "Option::is_none")]
2983 pub powershell_path: Option<Option<String>>,
2984
2985 #[serde(default, skip_serializing_if = "Option::is_none")]
2989 pub startup_timing: Option<serde_json::Map<String, Value>>,
2990
2991 #[serde(default, skip_serializing_if = "Option::is_none")]
3001 pub per_turn_effort_active: Option<bool>,
3002
3003 #[serde(default, skip_serializing_if = "Option::is_none")]
3010 pub view_mode: Option<ViewMode>,
3011
3012 #[serde(default, skip_serializing_if = "Option::is_none")]
3019 pub original_cwd: Option<String>,
3020
3021 #[serde(default, skip_serializing_if = "Option::is_none")]
3030 pub additional_directories: Option<Vec<String>>,
3031}
3032
3033#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3037pub struct FooterIndicator {
3038 pub text: String,
3041}
3042
3043#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3046pub enum ViewMode {
3047 Focus,
3050 Default,
3052 Unknown(String),
3054}
3055
3056impl ViewMode {
3057 pub fn as_str(&self) -> &str {
3058 match self {
3059 Self::Focus => "focus",
3060 Self::Default => "default",
3061 Self::Unknown(s) => s.as_str(),
3062 }
3063 }
3064}
3065
3066impl fmt::Display for ViewMode {
3067 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3068 f.write_str(self.as_str())
3069 }
3070}
3071
3072impl From<&str> for ViewMode {
3073 fn from(s: &str) -> Self {
3074 match s {
3075 "focus" => Self::Focus,
3076 "default" => Self::Default,
3077 other => Self::Unknown(other.to_string()),
3078 }
3079 }
3080}
3081
3082impl Serialize for ViewMode {
3083 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3084 serializer.serialize_str(self.as_str())
3085 }
3086}
3087
3088impl<'de> Deserialize<'de> for ViewMode {
3089 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3090 let s = String::deserialize(deserializer)?;
3091 Ok(Self::from(s.as_str()))
3092 }
3093}
3094
3095#[derive(Debug, Clone, Serialize, Deserialize)]
3097pub struct StatusMessage {
3098 pub session_id: String,
3100 pub status: Option<StatusMessageStatus>,
3102 #[serde(skip_serializing_if = "Option::is_none")]
3104 pub uuid: Option<String>,
3105 #[serde(skip_serializing_if = "Option::is_none", rename = "permissionMode")]
3107 pub permission_mode: Option<InitPermissionMode>,
3108 #[serde(skip_serializing_if = "Option::is_none")]
3109 pub compact_result: Option<String>,
3110 #[serde(skip_serializing_if = "Option::is_none")]
3111 pub compact_error: Option<String>,
3112 #[serde(default, skip_serializing_if = "Option::is_none")]
3119 pub compact_output_chars: Option<u64>,
3120}
3121
3122#[derive(Debug, Clone, Serialize, Deserialize)]
3124pub struct CompactBoundaryMessage {
3125 pub session_id: String,
3127 pub compact_metadata: CompactMetadata,
3129 #[serde(
3133 default,
3134 skip_serializing_if = "Option::is_none",
3135 alias = "content",
3136 alias = "text"
3137 )]
3138 pub summary: Option<String>,
3139 #[serde(
3143 default,
3144 skip_serializing_if = "Option::is_none",
3145 alias = "message_count"
3146 )]
3147 pub leaf_message_count: Option<u32>,
3148 #[serde(default, skip_serializing_if = "Option::is_none")]
3150 pub duration_ms: Option<u64>,
3151 #[serde(skip_serializing_if = "Option::is_none")]
3153 pub uuid: Option<String>,
3154 #[serde(skip_serializing_if = "Option::is_none")]
3156 pub logical_parent_uuid: Option<Option<String>>,
3157 #[serde(default, skip_serializing_if = "Option::is_none")]
3161 pub historical: Option<bool>,
3162}
3163
3164#[derive(Debug, Clone, Serialize, Deserialize)]
3166pub struct CompactMetadata {
3167 pub pre_tokens: u64,
3169 pub trigger: CompactionTrigger,
3171 #[serde(default, skip_serializing_if = "Option::is_none")]
3172 pub post_tokens: Option<u64>,
3173 #[serde(default, skip_serializing_if = "Option::is_none")]
3174 pub cumulative_dropped_tokens: Option<u64>,
3175 #[serde(default, skip_serializing_if = "Option::is_none")]
3176 pub duration_ms: Option<u64>,
3177 #[serde(default, skip_serializing_if = "Option::is_none")]
3178 pub user_context: Option<String>,
3179 #[serde(default, skip_serializing_if = "Option::is_none")]
3180 pub messages_summarized: Option<u64>,
3181 #[serde(default, skip_serializing_if = "Option::is_none")]
3182 pub precomputed: Option<bool>,
3183 #[serde(default, skip_serializing_if = "Option::is_none")]
3184 pub pre_compact_discovered_tools: Option<Vec<String>>,
3185 #[serde(default, skip_serializing_if = "Option::is_none")]
3190 pub pre_compact_artifact_read_versions: Option<Vec<ArtifactReadVersion>>,
3191 #[serde(default, skip_serializing_if = "Option::is_none")]
3192 pub preserved_segment: Option<PreservedSegment>,
3193 #[serde(default, skip_serializing_if = "Option::is_none")]
3194 pub preserved_messages: Option<PreservedMessages>,
3195}
3196
3197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3200pub struct ArtifactReadVersion {
3201 pub slug: String,
3203 pub ver: String,
3205}
3206
3207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3208pub struct PreservedSegment {
3209 pub head_uuid: String,
3210 pub anchor_uuid: String,
3211 pub tail_uuid: String,
3212}
3213
3214#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3215pub struct PreservedMessages {
3216 pub anchor_uuid: String,
3217 pub uuids: Vec<String>,
3218 #[serde(default, skip_serializing_if = "Option::is_none")]
3219 pub all_uuids: Option<Vec<String>>,
3220}
3221
3222#[derive(Debug, Clone, Serialize, Deserialize)]
3228pub struct TaskUsage {
3229 pub duration_ms: u64,
3231 pub tool_uses: u64,
3233 pub total_tokens: u64,
3235}
3236
3237#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3239pub enum TaskType {
3240 LocalAgent,
3242 LocalBash,
3244 LocalWorkflow,
3246 Unknown(String),
3248}
3249
3250impl TaskType {
3251 pub fn as_str(&self) -> &str {
3252 match self {
3253 Self::LocalAgent => "local_agent",
3254 Self::LocalBash => "local_bash",
3255 Self::LocalWorkflow => "local_workflow",
3256 Self::Unknown(s) => s.as_str(),
3257 }
3258 }
3259}
3260
3261impl fmt::Display for TaskType {
3262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3263 f.write_str(self.as_str())
3264 }
3265}
3266
3267impl From<&str> for TaskType {
3268 fn from(s: &str) -> Self {
3269 match s {
3270 "local_agent" => Self::LocalAgent,
3271 "local_bash" => Self::LocalBash,
3272 "local_workflow" => Self::LocalWorkflow,
3273 other => Self::Unknown(other.to_string()),
3274 }
3275 }
3276}
3277
3278impl Serialize for TaskType {
3279 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3280 serializer.serialize_str(self.as_str())
3281 }
3282}
3283
3284impl<'de> Deserialize<'de> for TaskType {
3285 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3286 let s = String::deserialize(deserializer)?;
3287 Ok(Self::from(s.as_str()))
3288 }
3289}
3290
3291#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3293pub enum TaskStatus {
3294 Pending,
3295 Running,
3296 Completed,
3297 Failed,
3298 Killed,
3299 Paused,
3300 Stopped,
3301 Unknown(String),
3302}
3303
3304impl TaskStatus {
3305 pub fn as_str(&self) -> &str {
3306 match self {
3307 Self::Pending => "pending",
3308 Self::Running => "running",
3309 Self::Completed => "completed",
3310 Self::Failed => "failed",
3311 Self::Killed => "killed",
3312 Self::Paused => "paused",
3313 Self::Stopped => "stopped",
3314 Self::Unknown(s) => s.as_str(),
3315 }
3316 }
3317}
3318
3319impl fmt::Display for TaskStatus {
3320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3321 f.write_str(self.as_str())
3322 }
3323}
3324
3325impl From<&str> for TaskStatus {
3326 fn from(s: &str) -> Self {
3327 match s {
3328 "pending" => Self::Pending,
3329 "running" => Self::Running,
3330 "completed" => Self::Completed,
3331 "failed" => Self::Failed,
3332 "killed" => Self::Killed,
3333 "paused" => Self::Paused,
3334 "stopped" => Self::Stopped,
3335 other => Self::Unknown(other.to_string()),
3336 }
3337 }
3338}
3339
3340impl Serialize for TaskStatus {
3341 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3342 serializer.serialize_str(self.as_str())
3343 }
3344}
3345
3346impl<'de> Deserialize<'de> for TaskStatus {
3347 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3348 let s = String::deserialize(deserializer)?;
3349 Ok(Self::from(s.as_str()))
3350 }
3351}
3352
3353#[derive(Debug, Clone, Serialize, Deserialize)]
3355pub struct TaskStartedMessage {
3356 pub session_id: String,
3357 pub task_id: String,
3358 #[serde(default, skip_serializing_if = "Option::is_none")]
3364 pub run_id: Option<String>,
3365 #[serde(default, skip_serializing_if = "Option::is_none")]
3371 pub parent_task_id: Option<String>,
3372 #[serde(default, skip_serializing_if = "Option::is_none")]
3373 pub task_type: Option<TaskType>,
3374 #[serde(default, skip_serializing_if = "Option::is_none")]
3375 pub tool_use_id: Option<String>,
3376 pub description: String,
3377 #[serde(default, skip_serializing_if = "Option::is_none")]
3380 pub subagent_type: Option<String>,
3381 #[serde(default, skip_serializing_if = "Option::is_none")]
3387 pub is_backgrounded: Option<bool>,
3388 #[serde(default, skip_serializing_if = "Option::is_none")]
3396 pub awaited: Option<bool>,
3397 #[serde(default, skip_serializing_if = "Option::is_none")]
3401 pub spawn_depth: Option<u32>,
3402 #[serde(default, skip_serializing_if = "Option::is_none")]
3404 pub prompt: Option<String>,
3405 #[serde(default, skip_serializing_if = "Option::is_none")]
3406 pub workflow_name: Option<String>,
3407 #[serde(default, skip_serializing_if = "Option::is_none")]
3408 pub skip_transcript: Option<bool>,
3409 #[serde(default, skip_serializing_if = "Option::is_none")]
3414 pub ambient: Option<bool>,
3415 pub uuid: String,
3416}
3417
3418#[derive(Debug, Clone, Serialize, Deserialize)]
3422pub struct TaskUpdatedMessage {
3423 pub session_id: String,
3424 pub task_id: String,
3425 #[serde(default, skip_serializing_if = "Option::is_none")]
3428 pub run_id: Option<String>,
3429 pub patch: TaskPatch,
3430 pub uuid: String,
3431}
3432
3433#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3436pub struct TaskPatch {
3437 #[serde(default, skip_serializing_if = "Option::is_none")]
3438 pub status: Option<TaskStatus>,
3439 #[serde(default, skip_serializing_if = "Option::is_none")]
3442 pub end_time: Option<u64>,
3443 #[serde(default, skip_serializing_if = "Option::is_none")]
3444 pub description: Option<String>,
3445 #[serde(default, skip_serializing_if = "Option::is_none")]
3446 pub total_paused_ms: Option<u64>,
3447 #[serde(default, skip_serializing_if = "Option::is_none")]
3448 pub error: Option<String>,
3449 #[serde(default, skip_serializing_if = "Option::is_none")]
3450 pub is_backgrounded: Option<bool>,
3451}
3452
3453#[derive(Debug, Clone, Serialize, Deserialize)]
3456pub struct ThinkingTokensMessage {
3457 pub session_id: String,
3458 pub estimated_tokens: u64,
3460 pub estimated_tokens_delta: u64,
3462 #[serde(default, skip_serializing_if = "Option::is_none")]
3469 pub user_message_uuid: Option<String>,
3470 pub uuid: String,
3471}
3472
3473#[derive(Debug, Clone, Serialize, Deserialize)]
3476pub struct TaskProgressMessage {
3477 pub session_id: String,
3478 pub task_id: String,
3479 #[serde(default, skip_serializing_if = "Option::is_none")]
3482 pub run_id: Option<String>,
3483 #[serde(default, skip_serializing_if = "Option::is_none")]
3484 pub tool_use_id: Option<String>,
3485 pub description: String,
3486 #[serde(default, skip_serializing_if = "Option::is_none")]
3487 pub last_tool_name: Option<String>,
3488 pub usage: TaskUsage,
3489 #[serde(default, skip_serializing_if = "Option::is_none")]
3491 pub subagent_type: Option<String>,
3492 #[serde(default, skip_serializing_if = "Option::is_none")]
3493 pub summary: Option<String>,
3494 pub uuid: String,
3495}
3496
3497#[derive(Debug, Clone, Serialize, Deserialize)]
3500pub struct TaskNotificationMessage {
3501 pub session_id: String,
3502 pub task_id: String,
3503 #[serde(default, skip_serializing_if = "Option::is_none")]
3506 pub run_id: Option<String>,
3507 pub status: TaskStatus,
3508 #[serde(default, skip_serializing_if = "Option::is_none")]
3511 pub reason: Option<TaskEndReason>,
3512 pub summary: String,
3513 pub output_file: Option<String>,
3514 #[serde(skip_serializing_if = "Option::is_none")]
3515 pub tool_use_id: Option<String>,
3516 #[serde(skip_serializing_if = "Option::is_none")]
3517 pub usage: Option<TaskUsage>,
3518 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3524 pub resource_links: Vec<ResourceLink>,
3525 #[serde(default, skip_serializing_if = "Option::is_none")]
3532 pub handback: Option<TaskHandback>,
3533 #[serde(default, skip_serializing_if = "Option::is_none")]
3537 pub handback_report: Option<HandbackReport>,
3538 #[serde(default, skip_serializing_if = "Option::is_none")]
3539 pub skip_transcript: Option<bool>,
3540 #[serde(default, skip_serializing_if = "Option::is_none")]
3543 pub ambient: Option<bool>,
3544 #[serde(skip_serializing_if = "Option::is_none")]
3545 pub uuid: Option<String>,
3546}
3547
3548#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3551pub enum TaskHandback {
3552 Send,
3554 Flagged,
3556 Withheld,
3558 Unknown(String),
3560}
3561
3562impl TaskHandback {
3563 pub fn as_str(&self) -> &str {
3564 match self {
3565 Self::Send => "send",
3566 Self::Flagged => "flagged",
3567 Self::Withheld => "withheld",
3568 Self::Unknown(s) => s.as_str(),
3569 }
3570 }
3571}
3572
3573impl fmt::Display for TaskHandback {
3574 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3575 f.write_str(self.as_str())
3576 }
3577}
3578
3579impl From<&str> for TaskHandback {
3580 fn from(s: &str) -> Self {
3581 match s {
3582 "send" => Self::Send,
3583 "flagged" => Self::Flagged,
3584 "withheld" => Self::Withheld,
3585 other => Self::Unknown(other.to_string()),
3586 }
3587 }
3588}
3589
3590impl Serialize for TaskHandback {
3591 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3592 serializer.serialize_str(self.as_str())
3593 }
3594}
3595
3596impl<'de> Deserialize<'de> for TaskHandback {
3597 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3598 let s = String::deserialize(deserializer)?;
3599 Ok(Self::from(s.as_str()))
3600 }
3601}
3602
3603#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3606pub struct HandbackReport {
3607 pub text: String,
3610 #[serde(default, skip_serializing_if = "Option::is_none")]
3613 pub warning: Option<String>,
3614}
3615
3616#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3618pub enum AssistantErrorKind {
3619 AuthenticationFailed,
3620 OauthOrgNotAllowed,
3621 AccountOnHold,
3622 BillingError,
3623 RateLimit,
3624 Overloaded,
3625 InvalidRequest,
3626 ModelNotFound,
3627 ServerError,
3628 UnknownError,
3629 MaxOutputTokens,
3630 Unknown(String),
3631}
3632
3633impl AssistantErrorKind {
3634 pub fn as_str(&self) -> &str {
3635 match self {
3636 Self::AuthenticationFailed => "authentication_failed",
3637 Self::OauthOrgNotAllowed => "oauth_org_not_allowed",
3638 Self::AccountOnHold => "account_on_hold",
3639 Self::BillingError => "billing_error",
3640 Self::RateLimit => "rate_limit",
3641 Self::Overloaded => "overloaded",
3642 Self::InvalidRequest => "invalid_request",
3643 Self::ModelNotFound => "model_not_found",
3644 Self::ServerError => "server_error",
3645 Self::UnknownError => "unknown",
3646 Self::MaxOutputTokens => "max_output_tokens",
3647 Self::Unknown(s) => s.as_str(),
3648 }
3649 }
3650}
3651
3652impl fmt::Display for AssistantErrorKind {
3653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3654 f.write_str(self.as_str())
3655 }
3656}
3657
3658impl From<&str> for AssistantErrorKind {
3659 fn from(s: &str) -> Self {
3660 match s {
3661 "authentication_failed" => Self::AuthenticationFailed,
3662 "oauth_org_not_allowed" => Self::OauthOrgNotAllowed,
3663 "account_on_hold" => Self::AccountOnHold,
3664 "billing_error" => Self::BillingError,
3665 "rate_limit" => Self::RateLimit,
3666 "overloaded" => Self::Overloaded,
3667 "invalid_request" => Self::InvalidRequest,
3668 "model_not_found" => Self::ModelNotFound,
3669 "server_error" => Self::ServerError,
3670 "unknown" => Self::UnknownError,
3671 "max_output_tokens" => Self::MaxOutputTokens,
3672 other => Self::Unknown(other.to_string()),
3673 }
3674 }
3675}
3676
3677impl Serialize for AssistantErrorKind {
3678 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3679 serializer.serialize_str(self.as_str())
3680 }
3681}
3682
3683impl<'de> Deserialize<'de> for AssistantErrorKind {
3684 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3685 let s = String::deserialize(deserializer)?;
3686 Ok(Self::from(s.as_str()))
3687 }
3688}
3689
3690#[derive(Debug, Clone, Serialize, Deserialize)]
3697pub struct CodeChangePublishedMessage {
3698 pub provider: String,
3702 pub url: String,
3704 pub repo: String,
3707 pub identifier: String,
3709 #[serde(default, skip_serializing_if = "Option::is_none")]
3718 pub action: Option<String>,
3719 #[serde(default, skip_serializing_if = "Option::is_none")]
3727 pub branch: Option<String>,
3728 pub uuid: String,
3729 pub session_id: String,
3730}
3731
3732#[derive(Debug, Clone, Serialize, Deserialize)]
3737pub struct VcsStateChangedMessage {
3738 pub kind: VcsMutationKind,
3741 pub cwd: String,
3744 #[serde(default, skip_serializing_if = "Option::is_none")]
3749 pub branch: Option<String>,
3750 pub uuid: String,
3751 pub session_id: String,
3752}
3753
3754#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3756pub enum VcsMutationKind {
3757 Commit,
3758 Push,
3759 Merge,
3760 Rebase,
3761 Unknown(String),
3763}
3764
3765impl VcsMutationKind {
3766 pub fn as_str(&self) -> &str {
3767 match self {
3768 Self::Commit => "commit",
3769 Self::Push => "push",
3770 Self::Merge => "merge",
3771 Self::Rebase => "rebase",
3772 Self::Unknown(s) => s.as_str(),
3773 }
3774 }
3775}
3776
3777impl fmt::Display for VcsMutationKind {
3778 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3779 f.write_str(self.as_str())
3780 }
3781}
3782
3783impl From<&str> for VcsMutationKind {
3784 fn from(s: &str) -> Self {
3785 match s {
3786 "commit" => Self::Commit,
3787 "push" => Self::Push,
3788 "merge" => Self::Merge,
3789 "rebase" => Self::Rebase,
3790 other => Self::Unknown(other.to_string()),
3791 }
3792 }
3793}
3794
3795impl Serialize for VcsMutationKind {
3796 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3797 serializer.serialize_str(self.as_str())
3798 }
3799}
3800
3801impl<'de> Deserialize<'de> for VcsMutationKind {
3802 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3803 let s = String::deserialize(deserializer)?;
3804 Ok(Self::from(s.as_str()))
3805 }
3806}
3807
3808#[derive(Debug, Clone, Serialize, Deserialize)]
3810pub struct FeedbackDraftQueuedMessage {
3811 pub draft_id: String,
3812 pub draft_type: String,
3813 pub title: String,
3814 pub details_preview: String,
3815 #[serde(default, skip_serializing_if = "Option::is_none")]
3816 pub uuid: Option<String>,
3817 #[serde(default, skip_serializing_if = "Option::is_none")]
3818 pub session_id: Option<String>,
3819 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3820 pub extra: serde_json::Map<String, Value>,
3821}
3822
3823#[derive(Debug, Clone, Serialize, Deserialize)]
3831pub struct CloudSessionDeltaMessage {
3832 pub seq: u64,
3836 pub changed: Vec<String>,
3840 pub cloud_session: Value,
3844 pub uuid: String,
3845 pub session_id: String,
3846 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3847 pub extra: serde_json::Map<String, Value>,
3848}
3849
3850#[derive(Debug, Clone, Serialize, Deserialize)]
3866pub struct DevIntentMessage {
3867 pub kind: DevIntentKind,
3869 #[serde(default, skip_serializing_if = "Option::is_none")]
3875 pub trigger: Option<DevIntentTrigger>,
3876 pub uuid: String,
3877 pub session_id: String,
3878 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3879 pub extra: serde_json::Map<String, Value>,
3880}
3881
3882#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3886pub enum DevIntentKind {
3887 IosApp,
3893 AndroidApp,
3900 Unknown(String),
3902}
3903
3904impl DevIntentKind {
3905 pub fn as_str(&self) -> &str {
3906 match self {
3907 Self::IosApp => "ios_app",
3908 Self::AndroidApp => "android_app",
3909 Self::Unknown(s) => s.as_str(),
3910 }
3911 }
3912}
3913
3914impl fmt::Display for DevIntentKind {
3915 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3916 f.write_str(self.as_str())
3917 }
3918}
3919
3920impl From<&str> for DevIntentKind {
3921 fn from(s: &str) -> Self {
3922 match s {
3923 "ios_app" => Self::IosApp,
3924 "android_app" => Self::AndroidApp,
3925 other => Self::Unknown(other.to_string()),
3926 }
3927 }
3928}
3929
3930impl Serialize for DevIntentKind {
3931 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3932 serializer.serialize_str(self.as_str())
3933 }
3934}
3935
3936impl<'de> Deserialize<'de> for DevIntentKind {
3937 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3938 let s = String::deserialize(deserializer)?;
3939 Ok(Self::from(s.as_str()))
3940 }
3941}
3942
3943#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3947pub enum DevIntentTrigger {
3948 SwiftEdit,
3950 UikitImport,
3952 XcodeProject,
3955 IosCommand,
3958 KotlinEdit,
3960 JavaEdit,
3962 AndroidImport,
3964 AndroidManifest,
3966 GradlePlugin,
3968 AndroidCommand,
3971 ProjectScan,
3974 Unknown(String),
3976}
3977
3978impl DevIntentTrigger {
3979 pub fn as_str(&self) -> &str {
3980 match self {
3981 Self::SwiftEdit => "swift_edit",
3982 Self::UikitImport => "uikit_import",
3983 Self::XcodeProject => "xcode_project",
3984 Self::IosCommand => "ios_command",
3985 Self::KotlinEdit => "kotlin_edit",
3986 Self::JavaEdit => "java_edit",
3987 Self::AndroidImport => "android_import",
3988 Self::AndroidManifest => "android_manifest",
3989 Self::GradlePlugin => "gradle_plugin",
3990 Self::AndroidCommand => "android_command",
3991 Self::ProjectScan => "project_scan",
3992 Self::Unknown(s) => s.as_str(),
3993 }
3994 }
3995}
3996
3997impl fmt::Display for DevIntentTrigger {
3998 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3999 f.write_str(self.as_str())
4000 }
4001}
4002
4003impl From<&str> for DevIntentTrigger {
4004 fn from(s: &str) -> Self {
4005 match s {
4006 "swift_edit" => Self::SwiftEdit,
4007 "uikit_import" => Self::UikitImport,
4008 "xcode_project" => Self::XcodeProject,
4009 "ios_command" => Self::IosCommand,
4010 "kotlin_edit" => Self::KotlinEdit,
4011 "java_edit" => Self::JavaEdit,
4012 "android_import" => Self::AndroidImport,
4013 "android_manifest" => Self::AndroidManifest,
4014 "gradle_plugin" => Self::GradlePlugin,
4015 "android_command" => Self::AndroidCommand,
4016 "project_scan" => Self::ProjectScan,
4017 other => Self::Unknown(other.to_string()),
4018 }
4019 }
4020}
4021
4022impl Serialize for DevIntentTrigger {
4023 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4024 serializer.serialize_str(self.as_str())
4025 }
4026}
4027
4028impl<'de> Deserialize<'de> for DevIntentTrigger {
4029 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4030 let s = String::deserialize(deserializer)?;
4031 Ok(Self::from(s.as_str()))
4032 }
4033}
4034
4035#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4039pub enum TaskEndReason {
4040 WorkerRestart,
4043 Unknown(String),
4045}
4046
4047impl TaskEndReason {
4048 pub fn as_str(&self) -> &str {
4049 match self {
4050 Self::WorkerRestart => "worker_restart",
4051 Self::Unknown(s) => s.as_str(),
4052 }
4053 }
4054}
4055
4056impl fmt::Display for TaskEndReason {
4057 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4058 f.write_str(self.as_str())
4059 }
4060}
4061
4062impl From<&str> for TaskEndReason {
4063 fn from(s: &str) -> Self {
4064 match s {
4065 "worker_restart" => Self::WorkerRestart,
4066 other => Self::Unknown(other.to_string()),
4067 }
4068 }
4069}
4070
4071impl Serialize for TaskEndReason {
4072 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4073 serializer.serialize_str(self.as_str())
4074 }
4075}
4076
4077impl<'de> Deserialize<'de> for TaskEndReason {
4078 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4079 let s = String::deserialize(deserializer)?;
4080 Ok(Self::from(s.as_str()))
4081 }
4082}
4083
4084#[derive(Debug, Clone, Serialize, Deserialize)]
4093pub struct TurnHandoffAvailableMessage {
4094 pub v: u64,
4096 pub tools: Vec<String>,
4098 pub worker_epoch: u64,
4100 #[serde(default, skip_serializing_if = "Option::is_none")]
4104 pub relay_marker: Option<bool>,
4105 #[serde(default, skip_serializing_if = "Option::is_none")]
4113 pub staged_files: Option<bool>,
4114 #[serde(default, skip_serializing_if = "Option::is_none")]
4121 pub no_query_first: Option<bool>,
4122 #[serde(default, skip_serializing_if = "Option::is_none")]
4130 pub home_files: Option<bool>,
4131 #[serde(default, skip_serializing_if = "Option::is_none")]
4135 pub file_names: Option<bool>,
4136 #[serde(default, skip_serializing_if = "Option::is_none")]
4146 pub carried_writes: Option<bool>,
4147 #[serde(default, skip_serializing_if = "Option::is_none")]
4161 pub hydrates_carried_lines: Option<bool>,
4162 #[serde(default, skip_serializing_if = "Option::is_none")]
4168 pub shared_message_id: Option<bool>,
4169 #[serde(default, skip_serializing_if = "Option::is_none")]
4174 pub requires_rewind_request_id: Option<bool>,
4175 pub uuid: String,
4176 pub session_id: String,
4177 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4178 pub extra: serde_json::Map<String, Value>,
4179}
4180
4181#[derive(Debug, Clone, Serialize, Deserialize)]
4193pub struct TurnPreemptedMessage {
4194 pub reason: String,
4198 pub preempted_by_uuid: String,
4202 #[serde(default)]
4205 pub preempted_message_uuids: Vec<String>,
4206 pub uuid: String,
4207 pub session_id: String,
4208 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4209 pub extra: serde_json::Map<String, Value>,
4210}
4211
4212#[derive(Debug, Clone, Serialize, Deserialize)]
4220pub struct PeerMessageHoldMessage {
4221 pub state: PeerMessageHoldState,
4227 #[serde(default, skip_serializing_if = "Option::is_none")]
4234 pub message_uuid: Option<String>,
4235 pub lane: PeerMessageLane,
4237 pub from: String,
4242 #[serde(default, skip_serializing_if = "Option::is_none")]
4245 pub from_name: Option<String>,
4246 #[serde(default, skip_serializing_if = "Option::is_none")]
4248 pub cause: Option<PeerMessageHoldCause>,
4249 #[serde(default, skip_serializing_if = "Option::is_none")]
4251 pub outcome: Option<PeerMessageHoldOutcome>,
4252 pub uuid: String,
4253 pub session_id: String,
4254 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4255 pub extra: serde_json::Map<String, Value>,
4256}
4257
4258#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4260pub enum PeerMessageHoldState {
4261 Held,
4263 Released,
4265 Dropped,
4267 Unknown(String),
4269}
4270
4271impl PeerMessageHoldState {
4272 pub fn as_str(&self) -> &str {
4273 match self {
4274 Self::Held => "held",
4275 Self::Released => "released",
4276 Self::Dropped => "dropped",
4277 Self::Unknown(s) => s.as_str(),
4278 }
4279 }
4280}
4281
4282impl fmt::Display for PeerMessageHoldState {
4283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4284 f.write_str(self.as_str())
4285 }
4286}
4287
4288impl From<&str> for PeerMessageHoldState {
4289 fn from(s: &str) -> Self {
4290 match s {
4291 "held" => Self::Held,
4292 "released" => Self::Released,
4293 "dropped" => Self::Dropped,
4294 other => Self::Unknown(other.to_string()),
4295 }
4296 }
4297}
4298
4299impl Serialize for PeerMessageHoldState {
4300 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4301 serializer.serialize_str(self.as_str())
4302 }
4303}
4304
4305impl<'de> Deserialize<'de> for PeerMessageHoldState {
4306 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4307 let s = String::deserialize(deserializer)?;
4308 Ok(Self::from(s.as_str()))
4309 }
4310}
4311
4312#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4314pub enum PeerMessageLane {
4315 Bridge,
4317 Stdin,
4319 Socket,
4321 Unknown(String),
4323}
4324
4325impl PeerMessageLane {
4326 pub fn as_str(&self) -> &str {
4327 match self {
4328 Self::Bridge => "bridge",
4329 Self::Stdin => "stdin",
4330 Self::Socket => "socket",
4331 Self::Unknown(s) => s.as_str(),
4332 }
4333 }
4334}
4335
4336impl fmt::Display for PeerMessageLane {
4337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4338 f.write_str(self.as_str())
4339 }
4340}
4341
4342impl From<&str> for PeerMessageLane {
4343 fn from(s: &str) -> Self {
4344 match s {
4345 "bridge" => Self::Bridge,
4346 "stdin" => Self::Stdin,
4347 "socket" => Self::Socket,
4348 other => Self::Unknown(other.to_string()),
4349 }
4350 }
4351}
4352
4353impl Serialize for PeerMessageLane {
4354 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4355 serializer.serialize_str(self.as_str())
4356 }
4357}
4358
4359impl<'de> Deserialize<'de> for PeerMessageLane {
4360 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4361 let s = String::deserialize(deserializer)?;
4362 Ok(Self::from(s.as_str()))
4363 }
4364}
4365
4366#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4373pub enum PeerMessageHoldCause {
4374 ExplicitSetting,
4375 ManagedSetting,
4376 RepoSetting,
4377 InvalidSetting,
4378 BypassDefault,
4379 ModeUnknown,
4380 ModeMismatch,
4381 NoModeAsserted,
4382 Unknown(String),
4384}
4385
4386impl PeerMessageHoldCause {
4387 pub fn as_str(&self) -> &str {
4388 match self {
4389 Self::ExplicitSetting => "explicit-setting",
4390 Self::ManagedSetting => "managed-setting",
4391 Self::RepoSetting => "repo-setting",
4392 Self::InvalidSetting => "invalid-setting",
4393 Self::BypassDefault => "bypass-default",
4394 Self::ModeUnknown => "mode-unknown",
4395 Self::ModeMismatch => "mode-mismatch",
4396 Self::NoModeAsserted => "no-mode-asserted",
4397 Self::Unknown(s) => s.as_str(),
4398 }
4399 }
4400}
4401
4402impl fmt::Display for PeerMessageHoldCause {
4403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4404 f.write_str(self.as_str())
4405 }
4406}
4407
4408impl From<&str> for PeerMessageHoldCause {
4409 fn from(s: &str) -> Self {
4410 match s {
4411 "explicit-setting" => Self::ExplicitSetting,
4412 "managed-setting" => Self::ManagedSetting,
4413 "repo-setting" => Self::RepoSetting,
4414 "invalid-setting" => Self::InvalidSetting,
4415 "bypass-default" => Self::BypassDefault,
4416 "mode-unknown" => Self::ModeUnknown,
4417 "mode-mismatch" => Self::ModeMismatch,
4418 "no-mode-asserted" => Self::NoModeAsserted,
4419 other => Self::Unknown(other.to_string()),
4420 }
4421 }
4422}
4423
4424impl Serialize for PeerMessageHoldCause {
4425 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4426 serializer.serialize_str(self.as_str())
4427 }
4428}
4429
4430impl<'de> Deserialize<'de> for PeerMessageHoldCause {
4431 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4432 let s = String::deserialize(deserializer)?;
4433 Ok(Self::from(s.as_str()))
4434 }
4435}
4436
4437#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4440pub enum PeerMessageHoldOutcome {
4441 Expired,
4445 Refused,
4447 Dropped,
4450 Discarded,
4452 Unknown(String),
4454}
4455
4456impl PeerMessageHoldOutcome {
4457 pub fn as_str(&self) -> &str {
4458 match self {
4459 Self::Expired => "expired",
4460 Self::Refused => "refused",
4461 Self::Dropped => "dropped",
4462 Self::Discarded => "discarded",
4463 Self::Unknown(s) => s.as_str(),
4464 }
4465 }
4466}
4467
4468impl fmt::Display for PeerMessageHoldOutcome {
4469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4470 f.write_str(self.as_str())
4471 }
4472}
4473
4474impl From<&str> for PeerMessageHoldOutcome {
4475 fn from(s: &str) -> Self {
4476 match s {
4477 "expired" => Self::Expired,
4478 "refused" => Self::Refused,
4479 "dropped" => Self::Dropped,
4480 "discarded" => Self::Discarded,
4481 other => Self::Unknown(other.to_string()),
4482 }
4483 }
4484}
4485
4486impl Serialize for PeerMessageHoldOutcome {
4487 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4488 serializer.serialize_str(self.as_str())
4489 }
4490}
4491
4492impl<'de> Deserialize<'de> for PeerMessageHoldOutcome {
4493 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4494 let s = String::deserialize(deserializer)?;
4495 Ok(Self::from(s.as_str()))
4496 }
4497}
4498
4499#[derive(Debug, Clone, Serialize, Deserialize)]
4510pub struct PerTurnEffortChangedMessage {
4511 pub per_turn_effort_active: bool,
4513 pub uuid: String,
4514 pub session_id: String,
4515}
4516
4517#[derive(Debug, Clone, Serialize, Deserialize)]
4524pub struct InstructionSizeWarningMessage {
4525 pub total_chars: u64,
4528 pub total_limit_chars: u64,
4531 pub file_count: u64,
4534 #[serde(default, skip_serializing_if = "Option::is_none")]
4538 pub largest_chars: Option<u64>,
4539 pub uuid: String,
4540 pub session_id: String,
4541}
4542
4543#[derive(Debug, Clone, Serialize, Deserialize)]
4550pub struct FileAttachmentsMissingMessage {
4551 pub message_uuid: String,
4554 pub sent_count: u64,
4556 pub missing: Vec<MissingFileAttachment>,
4558 pub uuid: String,
4559 pub session_id: String,
4560}
4561
4562#[derive(Debug, Clone, Serialize, Deserialize)]
4572pub struct PermissionCheckStatusMessage {
4573 pub tool_use_id: String,
4574 #[serde(default, skip_serializing_if = "Option::is_none")]
4577 pub agent_id: Option<String>,
4578 pub status: PermissionCheckStatus,
4579 pub uuid: String,
4580 pub session_id: String,
4581}
4582
4583#[derive(Debug, Clone, Serialize, Deserialize)]
4588pub struct InheritedAsksSettledMessage {
4589 pub kept_request_ids: Vec<String>,
4592 pub uuid: String,
4593 pub session_id: String,
4594}
4595
4596#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4598pub enum PermissionCheckStatus {
4599 Checking,
4602 Done,
4604 Unknown(String),
4606}
4607
4608impl PermissionCheckStatus {
4609 pub fn as_str(&self) -> &str {
4610 match self {
4611 Self::Checking => "checking",
4612 Self::Done => "done",
4613 Self::Unknown(s) => s.as_str(),
4614 }
4615 }
4616}
4617
4618impl fmt::Display for PermissionCheckStatus {
4619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4620 f.write_str(self.as_str())
4621 }
4622}
4623
4624impl From<&str> for PermissionCheckStatus {
4625 fn from(s: &str) -> Self {
4626 match s {
4627 "checking" => Self::Checking,
4628 "done" => Self::Done,
4629 other => Self::Unknown(other.to_string()),
4630 }
4631 }
4632}
4633
4634impl Serialize for PermissionCheckStatus {
4635 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4636 serializer.serialize_str(self.as_str())
4637 }
4638}
4639
4640impl<'de> Deserialize<'de> for PermissionCheckStatus {
4641 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4642 let s = String::deserialize(deserializer)?;
4643 Ok(Self::from(s.as_str()))
4644 }
4645}
4646
4647#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4649pub struct MissingFileAttachment {
4650 pub file_uuid: String,
4652 pub reason: String,
4657}
4658
4659#[derive(Debug, Clone, Serialize, Deserialize)]
4669pub struct SessionTitleChangedMessage {
4670 pub title: String,
4675 pub uuid: String,
4676 pub session_id: String,
4677}
4678
4679#[derive(Debug, Clone, Serialize, Deserialize)]
4683pub struct UiLogMessage {
4684 pub plugin: String,
4686 pub text: String,
4690 pub uuid: String,
4691 pub session_id: String,
4692}
4693
4694#[derive(Debug, Clone, Serialize, Deserialize)]
4699pub struct UiToastMessage {
4700 pub plugin: String,
4702 pub text: String,
4706 pub timeout_ms: u64,
4709 pub uuid: String,
4710 pub session_id: String,
4711}
4712
4713#[derive(Debug, Clone, Serialize, Deserialize)]
4717pub struct UiStatusMessage {
4718 pub plugin: String,
4720 pub text: Option<String>,
4725 pub uuid: String,
4726 pub session_id: String,
4727}
4728
4729#[derive(Debug, Clone, Serialize, Deserialize)]
4734pub struct UiInvalidateMessage {
4735 pub event: String,
4738 #[serde(default, skip_serializing_if = "Option::is_none")]
4743 pub instances: Option<Vec<UiInvalidatedInstance>>,
4744 #[serde(default, skip_serializing_if = "Option::is_none")]
4752 pub prompts_screened: Option<bool>,
4753 pub uuid: String,
4754 pub session_id: String,
4755}
4756
4757#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4759pub struct UiInvalidatedInstance {
4760 pub surface: String,
4762 pub component: String,
4764 pub instance_id: String,
4767}
4768
4769#[derive(Debug, Clone, Serialize, Deserialize)]
4776pub struct UiPanesMessage {
4777 pub panes: Vec<UiPane>,
4780 pub shown_id: Option<String>,
4783 pub focused_id: Option<String>,
4786 pub focus_requested_id: Option<String>,
4790 pub uuid: String,
4791 pub session_id: String,
4792}
4793
4794#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4796pub struct UiPane {
4797 pub id: String,
4800 pub title: String,
4803 pub plugin: String,
4805 #[serde(default, skip_serializing_if = "Option::is_none")]
4808 pub close_on_escape: Option<bool>,
4809 #[serde(default, skip_serializing_if = "Option::is_none")]
4812 pub hold_toasts: Option<bool>,
4813 #[serde(default, skip_serializing_if = "Option::is_none")]
4818 pub background_color: Option<String>,
4819 #[serde(default, skip_serializing_if = "Option::is_none")]
4822 pub rows: Option<u64>,
4823 #[serde(default, skip_serializing_if = "Option::is_none")]
4826 pub columns: Option<u64>,
4827}
4828
4829#[derive(Debug, Clone, Serialize, Deserialize)]
4837pub struct UiPlacedMessage {
4838 pub rows: Vec<UiPlacedRow>,
4840 pub uuid: String,
4841 pub session_id: String,
4842}
4843
4844#[derive(Debug, Clone, Serialize, Deserialize)]
4846pub struct UiPlacedRow {
4847 pub plugin: String,
4849 pub id: String,
4851 pub request_id: String,
4854 #[serde(default, skip_serializing_if = "Option::is_none")]
4857 pub after_uuid: Option<String>,
4858}
4859
4860#[derive(Debug, Clone, Serialize, Deserialize)]
4864pub struct UiScrollMessage {
4865 pub client_id: String,
4867 pub component: UiSiteComponent,
4869 pub instance_id: String,
4871 pub offset: u64,
4874 #[serde(default, skip_serializing_if = "Option::is_none")]
4877 pub follow_end: Option<bool>,
4878 pub uuid: String,
4879 pub session_id: String,
4880}
4881
4882#[derive(Debug, Clone, Serialize, Deserialize)]
4887pub struct UiFocusMessage {
4888 pub client_id: String,
4890 pub component: UiSiteComponent,
4892 pub instance_id: String,
4894 pub plugin: String,
4896 pub key: String,
4898 pub uuid: String,
4899 pub session_id: String,
4900}
4901
4902#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4904pub enum UiSiteComponent {
4905 Pane,
4907 AbovePrompt,
4909 Unknown(String),
4911}
4912
4913impl UiSiteComponent {
4914 pub fn as_str(&self) -> &str {
4915 match self {
4916 Self::Pane => "Pane",
4917 Self::AbovePrompt => "AbovePrompt",
4918 Self::Unknown(s) => s.as_str(),
4919 }
4920 }
4921}
4922
4923impl fmt::Display for UiSiteComponent {
4924 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4925 f.write_str(self.as_str())
4926 }
4927}
4928
4929impl From<&str> for UiSiteComponent {
4930 fn from(s: &str) -> Self {
4931 match s {
4932 "Pane" => Self::Pane,
4933 "AbovePrompt" => Self::AbovePrompt,
4934 other => Self::Unknown(other.to_string()),
4935 }
4936 }
4937}
4938
4939impl Serialize for UiSiteComponent {
4940 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4941 serializer.serialize_str(self.as_str())
4942 }
4943}
4944
4945impl<'de> Deserialize<'de> for UiSiteComponent {
4946 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4947 let s = String::deserialize(deserializer)?;
4948 Ok(Self::from(s.as_str()))
4949 }
4950}
4951
4952#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4955pub struct LocalCommandRun {
4956 pub command: String,
4958 pub args: String,
4961 #[serde(default, skip_serializing_if = "Option::is_none")]
4966 pub plugin: Option<String>,
4967}
4968
4969#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4973pub struct LocalCommandOutcome {
4974 pub kind: LocalCommandOutcomeKind,
4975 #[serde(default, skip_serializing_if = "Option::is_none")]
4978 pub suggestion: Option<String>,
4979}
4980
4981#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4986pub enum LocalCommandOutcomeKind {
4987 UnavailableHeadless,
4989 Unknown,
4992 Failed,
4994 RestartRequired,
4996 Other(String),
4998}
4999
5000impl LocalCommandOutcomeKind {
5001 pub fn as_str(&self) -> &str {
5002 match self {
5003 Self::UnavailableHeadless => "unavailable_headless",
5004 Self::Unknown => "unknown",
5005 Self::Failed => "failed",
5006 Self::RestartRequired => "restart_required",
5007 Self::Other(s) => s.as_str(),
5008 }
5009 }
5010}
5011
5012impl fmt::Display for LocalCommandOutcomeKind {
5013 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5014 f.write_str(self.as_str())
5015 }
5016}
5017
5018impl From<&str> for LocalCommandOutcomeKind {
5019 fn from(s: &str) -> Self {
5020 match s {
5021 "unavailable_headless" => Self::UnavailableHeadless,
5022 "unknown" => Self::Unknown,
5023 "failed" => Self::Failed,
5024 "restart_required" => Self::RestartRequired,
5025 other => Self::Other(other.to_string()),
5026 }
5027 }
5028}
5029
5030impl Serialize for LocalCommandOutcomeKind {
5031 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5032 serializer.serialize_str(self.as_str())
5033 }
5034}
5035
5036impl<'de> Deserialize<'de> for LocalCommandOutcomeKind {
5037 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5038 let s = String::deserialize(deserializer)?;
5039 Ok(Self::from(s.as_str()))
5040 }
5041}
5042
5043#[derive(Debug, Clone, Serialize, Deserialize)]
5049pub struct UsageReport {
5050 pub session: super::control::UsageSession,
5052 pub rate_limits: Option<UsageReportRateLimits>,
5056 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5057 pub extra: serde_json::Map<String, Value>,
5058}
5059
5060#[derive(Debug, Clone, Serialize, Deserialize)]
5062pub struct UsageReportRateLimits {
5063 pub limits: Option<Vec<UsageReportLimit>>,
5071 #[serde(default, skip_serializing_if = "Option::is_none")]
5074 pub extra_usage: Option<UsageReportExtraUsage>,
5075 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5076 pub extra: serde_json::Map<String, Value>,
5077}
5078
5079#[derive(Debug, Clone, Serialize, Deserialize)]
5081pub struct UsageReportLimit {
5082 pub kind: String,
5085 pub group: String,
5088 pub percent: f64,
5090 pub resets_at: Option<String>,
5092 #[serde(default, skip_serializing_if = "Option::is_none")]
5095 pub scope: Option<UsageReportScope>,
5096 #[serde(default, skip_serializing_if = "Option::is_none")]
5100 pub severity: Option<String>,
5101 #[serde(default, skip_serializing_if = "Option::is_none")]
5103 pub is_active: Option<bool>,
5104 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5105 pub extra: serde_json::Map<String, Value>,
5106}
5107
5108#[derive(Debug, Clone, Serialize, Deserialize)]
5110pub struct UsageReportScope {
5111 #[serde(default, skip_serializing_if = "Option::is_none")]
5112 pub model: Option<UsageReportScopeLabel>,
5113 #[serde(default, skip_serializing_if = "Option::is_none")]
5114 pub surface: Option<UsageReportScopeLabel>,
5115 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5116 pub extra: serde_json::Map<String, Value>,
5117}
5118
5119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5121pub struct UsageReportScopeLabel {
5122 pub display_name: String,
5123}
5124
5125#[derive(Debug, Clone, Serialize, Deserialize)]
5129pub struct UsageReportExtraUsage {
5130 pub is_enabled: bool,
5132 pub monthly_limit: Option<f64>,
5133 pub used_credits: Option<f64>,
5134 pub utilization: Option<f64>,
5135 #[serde(default, skip_serializing_if = "Option::is_none")]
5136 pub currency: Option<String>,
5137 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5138 pub extra: serde_json::Map<String, Value>,
5139}
5140
5141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5144pub struct BatchToolUse {
5145 pub id: String,
5146 pub name: String,
5147}
5148
5149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5154pub struct ContextUsage {
5155 pub model: String,
5157 pub total_tokens: u64,
5160 pub raw_max_tokens: u64,
5163 pub percentage: u64,
5165 #[serde(default, skip_serializing_if = "Option::is_none")]
5167 pub over_limit: Option<ContextOverLimit>,
5168 #[serde(default)]
5170 pub categories: Vec<ContextCategory>,
5171 #[serde(default)]
5173 pub mcp_tools: Vec<ContextMcpTool>,
5174 #[serde(default)]
5176 pub memory_files: Vec<ContextMemoryFile>,
5177 #[serde(default)]
5179 pub agents: Vec<ContextAgent>,
5180 #[serde(default, skip_serializing_if = "Option::is_none")]
5182 pub skills: Option<Vec<ContextSkill>>,
5183}
5184
5185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5187pub struct ContextOverLimit {
5188 pub tokens_over: u64,
5189 pub kind: String,
5192}
5193
5194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5196pub struct ContextCategory {
5197 pub name: String,
5200 pub tokens: u64,
5201 pub kind: String,
5205}
5206
5207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5209pub struct ContextMcpTool {
5210 pub name: String,
5212 pub server_name: String,
5213 pub tokens: u64,
5214}
5215
5216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5218pub struct ContextMemoryFile {
5219 pub path: String,
5220 #[serde(rename = "type")]
5222 pub file_type: String,
5223 pub tokens: u64,
5224}
5225
5226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5228pub struct ContextAgent {
5229 pub agent_type: String,
5230 pub source: String,
5232 pub tokens: u64,
5233}
5234
5235#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5237pub struct ContextSkill {
5238 pub name: String,
5239 pub source: String,
5241 #[serde(default, skip_serializing_if = "Option::is_none")]
5242 pub plugin_name: Option<String>,
5243 pub tokens: u64,
5244}
5245
5246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5248pub struct ToolUseMeta {
5249 pub id: String,
5250 pub display_name: String,
5251 #[serde(default, skip_serializing_if = "Option::is_none")]
5252 pub server_display_name: Option<String>,
5253 #[serde(default, skip_serializing_if = "Option::is_none")]
5254 pub icon_url: Option<String>,
5255 #[serde(default, skip_serializing_if = "Option::is_none")]
5260 pub server_id: Option<String>,
5261 #[serde(default, skip_serializing_if = "Option::is_none")]
5269 pub server_id_stamp: Option<String>,
5270 #[serde(default, skip_serializing_if = "Option::is_none")]
5273 pub tool_name: Option<String>,
5274}
5275
5276#[derive(Debug, Clone, Serialize, Deserialize)]
5278pub struct AssistantMessage {
5279 pub message: AssistantMessageContent,
5280 #[serde(alias = "sessionId")]
5281 pub session_id: String,
5282 #[serde(skip_serializing_if = "Option::is_none")]
5283 pub uuid: Option<String>,
5284 #[serde(skip_serializing_if = "Option::is_none")]
5285 pub parent_tool_use_id: Option<String>,
5286 #[serde(skip_serializing_if = "Option::is_none")]
5288 pub request_id: Option<String>,
5289 #[serde(default, skip_serializing_if = "Option::is_none")]
5294 pub refused_message_id: Option<String>,
5295 #[serde(default, skip_serializing_if = "Option::is_none")]
5304 pub user_message_uuid: Option<String>,
5305 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5311 pub user_message_uuids: Vec<String>,
5312 #[serde(default, skip_serializing_if = "Option::is_none")]
5316 pub agent_id: Option<String>,
5317 #[serde(skip_serializing_if = "Option::is_none")]
5320 pub subagent_type: Option<String>,
5321 #[serde(skip_serializing_if = "Option::is_none")]
5323 pub task_description: Option<String>,
5324 #[serde(skip_serializing_if = "Option::is_none")]
5325 pub error: Option<AssistantErrorKind>,
5326 #[serde(default, skip_serializing_if = "Option::is_none")]
5330 pub aborted: Option<bool>,
5331 #[serde(default, skip_serializing_if = "Option::is_none")]
5336 pub resumed_from_incomplete_thinking: Option<bool>,
5337 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5338 pub supersedes: Vec<String>,
5339 #[serde(skip_serializing_if = "Option::is_none")]
5340 pub timestamp: Option<String>,
5341 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5342 pub tool_use_meta: Vec<ToolUseMeta>,
5343 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5348 pub batch_tool_uses: Vec<BatchToolUse>,
5349 #[serde(default, skip_serializing_if = "Option::is_none")]
5355 pub wire_tool_inputs: Option<serde_json::Map<String, Value>>,
5356 #[serde(default, skip_serializing_if = "Option::is_none")]
5362 pub wire_ingest_context: Option<serde_json::Map<String, Value>>,
5363 #[serde(default, skip_serializing_if = "Option::is_none")]
5367 pub historical: Option<bool>,
5368 #[serde(default, skip_serializing_if = "Option::is_none")]
5376 pub resume_reason: Option<String>,
5377 #[serde(default, skip_serializing_if = "Option::is_none")]
5383 pub local_command_source: Option<String>,
5384 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5393 pub narration_block_indexes: Vec<usize>,
5394 #[serde(default, skip_serializing_if = "Option::is_none")]
5398 pub narration_hint: Option<String>,
5399 #[serde(default, skip_serializing_if = "Option::is_none")]
5408 pub thinking_duration_ms: Option<u64>,
5409 #[serde(default, skip_serializing_if = "Option::is_none")]
5420 pub thinking_display: Option<String>,
5421 #[serde(default, skip_serializing_if = "Option::is_none")]
5425 pub context_usage: Option<ContextUsage>,
5426 #[serde(default, skip_serializing_if = "Option::is_none")]
5434 pub usage_report: Option<Box<UsageReport>>,
5435 #[serde(default, skip_serializing_if = "Option::is_none")]
5442 pub local_command_run: Option<LocalCommandRun>,
5443 #[serde(default, skip_serializing_if = "Option::is_none")]
5449 pub local_command_outcome: Option<LocalCommandOutcome>,
5450 #[serde(default, skip_serializing_if = "Option::is_none")]
5451 pub is_meta: Option<bool>,
5452 #[serde(default, skip_serializing_if = "Option::is_none")]
5453 pub is_virtual: Option<bool>,
5454 #[serde(default, skip_serializing_if = "Option::is_none")]
5455 pub is_api_error_message: Option<bool>,
5456 #[serde(skip_serializing_if = "Option::is_none")]
5457 pub api_error_status: Option<u16>,
5458 #[serde(skip_serializing_if = "Option::is_none")]
5496 pub api_error: Option<String>,
5497 #[serde(default, skip_serializing_if = "Option::is_none")]
5504 pub api_error_code: Option<String>,
5505 #[serde(default, skip_serializing_if = "Option::is_none")]
5516 pub api_error_origin: Option<String>,
5517 #[serde(default, skip_serializing_if = "Option::is_none")]
5520 pub api_error_params: Option<ApiErrorParams>,
5521 #[serde(skip_serializing_if = "Option::is_none")]
5522 pub error_details: Option<String>,
5523 #[serde(skip_serializing_if = "Option::is_none")]
5524 pub advisor_model: Option<String>,
5525 #[serde(skip_serializing_if = "Option::is_none")]
5526 pub attribution_agent: Option<String>,
5527 #[serde(skip_serializing_if = "Option::is_none")]
5528 pub attribution_skill: Option<String>,
5529 #[serde(skip_serializing_if = "Option::is_none")]
5530 pub attribution_plugin: Option<String>,
5531 #[serde(skip_serializing_if = "Option::is_none")]
5532 pub attribution_mcp_server: Option<String>,
5533 #[serde(skip_serializing_if = "Option::is_none")]
5534 pub attribution_mcp_tool: Option<String>,
5535}
5536
5537#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
5541pub struct ApiErrorParams {
5542 #[serde(default, skip_serializing_if = "Option::is_none")]
5544 pub effort: Option<String>,
5545 #[serde(default, skip_serializing_if = "Option::is_none")]
5548 pub provider: Option<ApiErrorProvider>,
5549 #[serde(default, skip_serializing_if = "Option::is_none")]
5552 pub remedy: Option<ApiErrorRemedy>,
5553 #[serde(default, skip_serializing_if = "Option::is_none")]
5556 pub media: Option<ApiErrorMedia>,
5557 #[serde(default, skip_serializing_if = "Option::is_none")]
5559 pub media_reason: Option<ApiErrorMediaReason>,
5560 #[serde(default, skip_serializing_if = "Option::is_none")]
5568 pub rate_limit_info: Option<RateLimitInfo>,
5569}
5570
5571#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5573pub enum ApiErrorMedia {
5574 Image,
5575 Document,
5576 Unknown(String),
5578}
5579
5580impl ApiErrorMedia {
5581 pub fn as_str(&self) -> &str {
5582 match self {
5583 Self::Image => "image",
5584 Self::Document => "document",
5585 Self::Unknown(s) => s.as_str(),
5586 }
5587 }
5588}
5589
5590impl fmt::Display for ApiErrorMedia {
5591 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5592 f.write_str(self.as_str())
5593 }
5594}
5595
5596impl From<&str> for ApiErrorMedia {
5597 fn from(s: &str) -> Self {
5598 match s {
5599 "image" => Self::Image,
5600 "document" => Self::Document,
5601 other => Self::Unknown(other.to_string()),
5602 }
5603 }
5604}
5605
5606impl Serialize for ApiErrorMedia {
5607 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5608 serializer.serialize_str(self.as_str())
5609 }
5610}
5611
5612impl<'de> Deserialize<'de> for ApiErrorMedia {
5613 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5614 let s = String::deserialize(deserializer)?;
5615 Ok(Self::from(s.as_str()))
5616 }
5617}
5618
5619#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5622pub enum ApiErrorMediaReason {
5623 Unprocessable,
5626 UnsupportedByModel,
5628 MediaBudget,
5631 Unknown(String),
5633}
5634
5635impl ApiErrorMediaReason {
5636 pub fn as_str(&self) -> &str {
5637 match self {
5638 Self::Unprocessable => "unprocessable",
5639 Self::UnsupportedByModel => "unsupported_by_model",
5640 Self::MediaBudget => "media_budget",
5641 Self::Unknown(s) => s.as_str(),
5642 }
5643 }
5644}
5645
5646impl fmt::Display for ApiErrorMediaReason {
5647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5648 f.write_str(self.as_str())
5649 }
5650}
5651
5652impl From<&str> for ApiErrorMediaReason {
5653 fn from(s: &str) -> Self {
5654 match s {
5655 "unprocessable" => Self::Unprocessable,
5656 "unsupported_by_model" => Self::UnsupportedByModel,
5657 "media_budget" => Self::MediaBudget,
5658 other => Self::Unknown(other.to_string()),
5659 }
5660 }
5661}
5662
5663impl Serialize for ApiErrorMediaReason {
5664 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5665 serializer.serialize_str(self.as_str())
5666 }
5667}
5668
5669impl<'de> Deserialize<'de> for ApiErrorMediaReason {
5670 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5671 let s = String::deserialize(deserializer)?;
5672 Ok(Self::from(s.as_str()))
5673 }
5674}
5675
5676#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5678pub enum ApiErrorProvider {
5679 Bedrock,
5680 AnthropicAws,
5681 Mantle,
5682 AnthropicGoogleCloud,
5683 Vertex,
5684 Foundry,
5685 Gateway,
5686 Unknown(String),
5688}
5689
5690impl ApiErrorProvider {
5691 pub fn as_str(&self) -> &str {
5692 match self {
5693 Self::Bedrock => "bedrock",
5694 Self::AnthropicAws => "anthropicAws",
5695 Self::Mantle => "mantle",
5696 Self::AnthropicGoogleCloud => "anthropicGoogleCloud",
5697 Self::Vertex => "vertex",
5698 Self::Foundry => "foundry",
5699 Self::Gateway => "gateway",
5700 Self::Unknown(s) => s.as_str(),
5701 }
5702 }
5703}
5704
5705impl fmt::Display for ApiErrorProvider {
5706 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5707 f.write_str(self.as_str())
5708 }
5709}
5710
5711impl From<&str> for ApiErrorProvider {
5712 fn from(s: &str) -> Self {
5713 match s {
5714 "bedrock" => Self::Bedrock,
5715 "anthropicAws" => Self::AnthropicAws,
5716 "mantle" => Self::Mantle,
5717 "anthropicGoogleCloud" => Self::AnthropicGoogleCloud,
5718 "vertex" => Self::Vertex,
5719 "foundry" => Self::Foundry,
5720 "gateway" => Self::Gateway,
5721 other => Self::Unknown(other.to_string()),
5722 }
5723 }
5724}
5725
5726impl Serialize for ApiErrorProvider {
5727 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5728 serializer.serialize_str(self.as_str())
5729 }
5730}
5731
5732impl<'de> Deserialize<'de> for ApiErrorProvider {
5733 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5734 let s = String::deserialize(deserializer)?;
5735 Ok(Self::from(s.as_str()))
5736 }
5737}
5738
5739#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5741pub enum ApiErrorRemedy {
5742 RefreshCommand,
5745 RefreshCredentials,
5747 Adc,
5750 GatewayToken,
5753 HostManaged,
5755 ModelAccess,
5758 Unknown(String),
5760}
5761
5762impl ApiErrorRemedy {
5763 pub fn as_str(&self) -> &str {
5764 match self {
5765 Self::RefreshCommand => "refresh_command",
5766 Self::RefreshCredentials => "refresh_credentials",
5767 Self::Adc => "adc",
5768 Self::GatewayToken => "gateway_token",
5769 Self::HostManaged => "host_managed",
5770 Self::ModelAccess => "model_access",
5771 Self::Unknown(s) => s.as_str(),
5772 }
5773 }
5774}
5775
5776impl fmt::Display for ApiErrorRemedy {
5777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5778 f.write_str(self.as_str())
5779 }
5780}
5781
5782impl From<&str> for ApiErrorRemedy {
5783 fn from(s: &str) -> Self {
5784 match s {
5785 "refresh_command" => Self::RefreshCommand,
5786 "refresh_credentials" => Self::RefreshCredentials,
5787 "adc" => Self::Adc,
5788 "gateway_token" => Self::GatewayToken,
5789 "host_managed" => Self::HostManaged,
5790 "model_access" => Self::ModelAccess,
5791 other => Self::Unknown(other.to_string()),
5792 }
5793 }
5794}
5795
5796impl Serialize for ApiErrorRemedy {
5797 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5798 serializer.serialize_str(self.as_str())
5799 }
5800}
5801
5802impl<'de> Deserialize<'de> for ApiErrorRemedy {
5803 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5804 let s = String::deserialize(deserializer)?;
5805 Ok(Self::from(s.as_str()))
5806 }
5807}
5808
5809#[derive(Debug, Clone, Serialize, Deserialize)]
5811pub struct AssistantMessageContent {
5812 pub id: String,
5813 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
5815 pub message_type: Option<String>,
5816 pub role: MessageRole,
5817 pub model: String,
5818 pub content: Vec<ContentBlock>,
5819 #[serde(skip_serializing_if = "Option::is_none")]
5820 pub stop_reason: Option<StopReason>,
5821 #[serde(skip_serializing_if = "Option::is_none")]
5822 pub stop_sequence: Option<String>,
5823 #[serde(skip_serializing_if = "Option::is_none")]
5824 pub usage: Option<AssistantUsage>,
5825 #[serde(skip_serializing_if = "Option::is_none")]
5827 pub stop_details: Option<Value>,
5828 #[serde(skip_serializing_if = "Option::is_none")]
5830 pub context_management: Option<Value>,
5831}
5832
5833#[derive(Debug, Clone, Serialize, Deserialize)]
5835pub struct AssistantUsage {
5836 #[serde(default)]
5838 pub input_tokens: u32,
5839
5840 #[serde(default)]
5842 pub output_tokens: u32,
5843
5844 #[serde(default)]
5846 pub cache_creation_input_tokens: u32,
5847
5848 #[serde(default)]
5850 pub cache_read_input_tokens: u32,
5851
5852 #[serde(skip_serializing_if = "Option::is_none")]
5854 pub service_tier: Option<String>,
5855
5856 #[serde(skip_serializing_if = "Option::is_none")]
5858 pub cache_creation: Option<CacheCreationDetails>,
5859
5860 #[serde(skip_serializing_if = "Option::is_none")]
5862 pub inference_geo: Option<String>,
5863
5864 #[serde(default, skip_serializing_if = "Option::is_none")]
5867 pub speed: Option<String>,
5868
5869 #[serde(default, skip_serializing_if = "Option::is_none")]
5872 pub fallback_credit: Option<Value>,
5873}
5874
5875#[derive(Debug, Clone, Serialize, Deserialize)]
5877pub struct CacheCreationDetails {
5878 #[serde(default)]
5880 pub ephemeral_1h_input_tokens: u32,
5881
5882 #[serde(default)]
5884 pub ephemeral_5m_input_tokens: u32,
5885}
5886
5887#[cfg(test)]
5888mod tests {
5889 use crate::io::ClaudeOutput;
5890
5891 #[test]
5892 fn test_subagent_usage_rollup_accumulates_task_results() {
5893 use super::SubagentUsageRollup;
5894
5895 let mut rollup = SubagentUsageRollup::default();
5896
5897 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}}"#;
5898 let output: ClaudeOutput = serde_json::from_str(task_result).unwrap();
5899 assert!(rollup.observe(&output));
5900 assert_eq!(rollup.subagent_tokens, 10201);
5901 assert_eq!(rollup.agent_count, 1);
5902 assert_eq!(rollup.tool_uses, 3);
5903 assert_eq!(rollup.duration_ms, 1853);
5904
5905 assert!(!rollup.observe(&output));
5907 assert_eq!(rollup.agent_count, 1);
5908 assert_eq!(rollup.subagent_tokens, 10201);
5909
5910 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}}"#;
5912 let output: ClaudeOutput = serde_json::from_str(second).unwrap();
5913 assert!(rollup.observe(&output));
5914 assert_eq!(rollup.agent_count, 2);
5915 assert_eq!(rollup.subagent_tokens, 10701);
5916 }
5917
5918 #[test]
5919 fn test_subagent_usage_rollup_ignores_non_task_results() {
5920 use super::SubagentUsageRollup;
5921
5922 let mut rollup = SubagentUsageRollup::default();
5923
5924 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}}"#;
5927 let output: ClaudeOutput = serde_json::from_str(tool_search).unwrap();
5928 assert!(!rollup.observe(&output));
5929
5930 let plain = r#"{"type":"user","message":{"role":"user","content":[]},"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1"}"#;
5932 let output: ClaudeOutput = serde_json::from_str(plain).unwrap();
5933 assert!(!rollup.observe(&output));
5934
5935 let system = r#"{"type":"system","subtype":"status","status":null,"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1"}"#;
5937 let output: ClaudeOutput = serde_json::from_str(system).unwrap();
5938 assert!(!rollup.observe(&output));
5939
5940 assert_eq!(rollup, SubagentUsageRollup::default());
5941 }
5942
5943 #[test]
5944 fn test_subagent_usage_rollup_over_captured_session() {
5945 use super::SubagentUsageRollup;
5946
5947 let mut rollup = SubagentUsageRollup::default();
5948 let fixture =
5949 include_str!("../../test_cases/subagent_sessions/general_purpose_compute.jsonl");
5950 for line in fixture.lines().filter(|l| !l.trim().is_empty()) {
5951 if let Ok(output) = serde_json::from_str::<ClaudeOutput>(line) {
5952 rollup.observe(&output);
5953 }
5954 }
5955 assert_eq!(rollup.agent_count, 1);
5956 assert_eq!(rollup.subagent_tokens, 10201);
5957 }
5958
5959 #[test]
5960 fn test_system_message_init() {
5961 let json = r#"{
5962 "type": "system",
5963 "subtype": "init",
5964 "session_id": "test-session-123",
5965 "cwd": "/home/user/project",
5966 "model": "claude-sonnet-4",
5967 "tools": ["Bash", "Read", "Write"],
5968 "mcp_servers": [],
5969 "slash_commands": ["compact", "cost", "review"],
5970 "agents": ["Bash", "Explore", "Plan"],
5971 "plugins": [{"name": "rust-analyzer-lsp", "path": "/home/user/.claude/plugins/rust-analyzer-lsp/1.0.0"}],
5972 "skills": [],
5973 "claude_code_version": "2.1.15",
5974 "apiKeySource": "none",
5975 "output_style": "default",
5976 "permissionMode": "default"
5977 }"#;
5978
5979 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
5980 if let ClaudeOutput::System(sys) = output {
5981 assert!(sys.is_init());
5982 assert!(!sys.is_status());
5983 assert!(!sys.is_compact_boundary());
5984
5985 let init = sys.as_init().expect("Should parse as init");
5986 assert_eq!(init.session_id, "test-session-123");
5987 assert_eq!(init.cwd, Some("/home/user/project".to_string()));
5988 assert_eq!(init.model, Some("claude-sonnet-4".to_string()));
5989 assert_eq!(init.tools, vec!["Bash", "Read", "Write"]);
5990 assert_eq!(init.slash_commands, vec!["compact", "cost", "review"]);
5991 assert_eq!(init.agents, vec!["Bash", "Explore", "Plan"]);
5992 assert_eq!(init.plugins.len(), 1);
5993 assert_eq!(init.plugins[0].name, "rust-analyzer-lsp");
5994 assert_eq!(init.claude_code_version, Some("2.1.15".to_string()));
5995 assert_eq!(init.api_key_source, Some(super::ApiKeySource::None));
5996 assert_eq!(init.output_style, Some(super::OutputStyle::Default));
5997 assert_eq!(
5998 init.permission_mode,
5999 Some(super::InitPermissionMode::Default)
6000 );
6001 } else {
6002 panic!("Expected System message");
6003 }
6004 }
6005
6006 #[test]
6007 fn test_system_message_init_from_real_capture() {
6008 let json = include_str!("../../test_cases/tool_use_captures/tool_msg_0.json");
6009 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6010 if let ClaudeOutput::System(sys) = output {
6011 let init = sys.as_init().expect("Should parse real init capture");
6012 assert_eq!(init.slash_commands.len(), 8);
6013 assert!(init.slash_commands.contains(&"compact".to_string()));
6014 assert!(init.slash_commands.contains(&"review".to_string()));
6015 assert_eq!(init.agents.len(), 5);
6016 assert!(init.agents.contains(&"Bash".to_string()));
6017 assert!(init.agents.contains(&"Explore".to_string()));
6018 assert_eq!(init.plugins.len(), 1);
6019 assert_eq!(init.plugins[0].name, "rust-analyzer-lsp");
6020 assert_eq!(init.claude_code_version, Some("2.1.15".to_string()));
6021 } else {
6022 panic!("Expected System message");
6023 }
6024 }
6025
6026 #[test]
6027 fn test_system_message_status() {
6028 let json = r#"{
6029 "type": "system",
6030 "subtype": "status",
6031 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6032 "status": "compacting",
6033 "uuid": "32eb9f9d-5ef7-47ff-8fce-bbe22fe7ed93"
6034 }"#;
6035
6036 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6037 if let ClaudeOutput::System(sys) = output {
6038 assert!(sys.is_status());
6039 assert!(!sys.is_init());
6040
6041 let status = sys.as_status().expect("Should parse as status");
6042 assert_eq!(status.session_id, "879c1a88-3756-4092-aa95-0020c4ed9692");
6043 assert_eq!(status.status, Some(super::StatusMessageStatus::Compacting));
6044 assert_eq!(
6045 status.uuid,
6046 Some("32eb9f9d-5ef7-47ff-8fce-bbe22fe7ed93".to_string())
6047 );
6048 } else {
6049 panic!("Expected System message");
6050 }
6051 }
6052
6053 #[test]
6054 fn test_system_message_status_null() {
6055 let json = r#"{
6056 "type": "system",
6057 "subtype": "status",
6058 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6059 "status": null,
6060 "uuid": "92d9637e-d00e-418e-acd2-a504e3861c6a"
6061 }"#;
6062
6063 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6064 if let ClaudeOutput::System(sys) = output {
6065 let status = sys.as_status().expect("Should parse as status");
6066 assert_eq!(status.status, None);
6067 } else {
6068 panic!("Expected System message");
6069 }
6070 }
6071
6072 #[test]
6073 fn test_system_message_task_started() {
6074 let json = r#"{
6075 "type": "system",
6076 "subtype": "task_started",
6077 "session_id": "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9",
6078 "task_id": "b6daf3f",
6079 "task_type": "local_bash",
6080 "tool_use_id": "toolu_011rfSTFumpJZdCCfzeD7jaS",
6081 "description": "Wait for CI on PR #12",
6082 "uuid": "c4243261-c128-4747-b8c3-5e1c7c10eeb8"
6083 }"#;
6084
6085 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6086 if let ClaudeOutput::System(sys) = output {
6087 assert!(sys.is_task_started());
6088 assert!(!sys.is_task_progress());
6089 assert!(!sys.is_task_notification());
6090
6091 let task = sys.as_task_started().expect("Should parse as task_started");
6092 assert_eq!(task.session_id, "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9");
6093 assert_eq!(task.task_id, "b6daf3f");
6094 assert_eq!(task.task_type, Some(super::TaskType::LocalBash));
6095 assert_eq!(
6096 task.tool_use_id.as_deref(),
6097 Some("toolu_011rfSTFumpJZdCCfzeD7jaS")
6098 );
6099 assert_eq!(task.description, "Wait for CI on PR #12");
6100 } else {
6101 panic!("Expected System message");
6102 }
6103 }
6104
6105 #[test]
6106 fn test_system_message_task_started_agent() {
6107 let json = r#"{
6108 "type": "system",
6109 "subtype": "task_started",
6110 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6111 "task_id": "a4a7e0906e5fc64cc",
6112 "task_type": "local_agent",
6113 "tool_use_id": "toolu_01SFz9FwZ1cYgCSy8vRM7wep",
6114 "description": "Explore Scene/ArrayScene duplication",
6115 "uuid": "85a39f5a-e4d4-47f7-9a6d-1125f1a8035f"
6116 }"#;
6117
6118 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6119 if let ClaudeOutput::System(sys) = output {
6120 let task = sys.as_task_started().expect("Should parse as task_started");
6121 assert_eq!(task.task_type, Some(super::TaskType::LocalAgent));
6122 assert_eq!(task.task_id, "a4a7e0906e5fc64cc");
6123 } else {
6124 panic!("Expected System message");
6125 }
6126 }
6127
6128 #[test]
6129 fn test_system_message_task_progress() {
6130 let json = r#"{
6131 "type": "system",
6132 "subtype": "task_progress",
6133 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6134 "task_id": "a4a7e0906e5fc64cc",
6135 "tool_use_id": "toolu_01SFz9FwZ1cYgCSy8vRM7wep",
6136 "description": "Reading src/jplephem/chebyshev.rs",
6137 "last_tool_name": "Read",
6138 "usage": {
6139 "duration_ms": 13996,
6140 "tool_uses": 9,
6141 "total_tokens": 38779
6142 },
6143 "uuid": "85a39f5a-e4d4-47f7-9a6d-1125f1a8035f"
6144 }"#;
6145
6146 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6147 if let ClaudeOutput::System(sys) = output {
6148 assert!(sys.is_task_progress());
6149 assert!(!sys.is_task_started());
6150
6151 let progress = sys
6152 .as_task_progress()
6153 .expect("Should parse as task_progress");
6154 assert_eq!(progress.task_id, "a4a7e0906e5fc64cc");
6155 assert_eq!(progress.description, "Reading src/jplephem/chebyshev.rs");
6156 assert_eq!(progress.last_tool_name.as_deref(), Some("Read"));
6157 assert_eq!(progress.usage.duration_ms, 13996);
6158 assert_eq!(progress.usage.tool_uses, 9);
6159 assert_eq!(progress.usage.total_tokens, 38779);
6160 } else {
6161 panic!("Expected System message");
6162 }
6163 }
6164
6165 #[test]
6166 fn test_system_message_task_notification_completed() {
6167 let json = r#"{
6168 "type": "system",
6169 "subtype": "task_notification",
6170 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6171 "task_id": "a0ba761e9dc9c316f",
6172 "tool_use_id": "toolu_01Ho6XVXFLVNjTQ9YqowdBXW",
6173 "status": "completed",
6174 "summary": "Agent \"Write Hipparcos data source doc\" completed",
6175 "output_file": "",
6176 "usage": {
6177 "duration_ms": 172300,
6178 "tool_uses": 11,
6179 "total_tokens": 42005
6180 },
6181 "uuid": "269f49b9-218d-4c8d-9f7e-3a5383a0c5b2"
6182 }"#;
6183
6184 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6185 if let ClaudeOutput::System(sys) = output {
6186 assert!(sys.is_task_notification());
6187
6188 let notif = sys
6189 .as_task_notification()
6190 .expect("Should parse as task_notification");
6191 assert_eq!(notif.status, super::TaskStatus::Completed);
6192 assert_eq!(
6193 notif.summary,
6194 "Agent \"Write Hipparcos data source doc\" completed"
6195 );
6196 assert_eq!(notif.output_file, Some("".to_string()));
6197 assert_eq!(
6198 notif.tool_use_id,
6199 Some("toolu_01Ho6XVXFLVNjTQ9YqowdBXW".to_string())
6200 );
6201 let usage = notif.usage.expect("Should have usage");
6202 assert_eq!(usage.duration_ms, 172300);
6203 assert_eq!(usage.tool_uses, 11);
6204 assert_eq!(usage.total_tokens, 42005);
6205 } else {
6206 panic!("Expected System message");
6207 }
6208 }
6209
6210 #[test]
6211 fn test_system_message_task_notification_failed_no_usage() {
6212 let json = r#"{
6213 "type": "system",
6214 "subtype": "task_notification",
6215 "session_id": "ea629737-3c36-48a8-a1c4-ad761ad35784",
6216 "task_id": "b98f6a3",
6217 "status": "failed",
6218 "summary": "Background command \"Run FSM calibration\" failed with exit code 1",
6219 "output_file": "/tmp/claude-1000/tasks/b98f6a3.output"
6220 }"#;
6221
6222 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6223 if let ClaudeOutput::System(sys) = output {
6224 let notif = sys
6225 .as_task_notification()
6226 .expect("Should parse as task_notification");
6227 assert_eq!(notif.status, super::TaskStatus::Failed);
6228 assert!(notif.tool_use_id.is_none());
6229 assert!(notif.usage.is_none());
6230 assert_eq!(
6231 notif.output_file,
6232 Some("/tmp/claude-1000/tasks/b98f6a3.output".to_string())
6233 );
6234 } else {
6235 panic!("Expected System message");
6236 }
6237 }
6238
6239 #[test]
6245 fn test_task_messages_roundtrip_through_value() {
6246 let cases = [
6247 r#"{"type":"system","subtype":"task_started","session_id":"s1",
6248 "task_id":"t1","task_type":"local_bash","tool_use_id":"tu1",
6249 "description":"Sleep 3s","uuid":"u1"}"#,
6250 r#"{"type":"system","subtype":"task_progress","session_id":"s1",
6251 "task_id":"t1","tool_use_id":"tu1","description":"Running ls",
6252 "last_tool_name":"Bash",
6253 "usage":{"duration_ms":100,"tool_uses":1,"total_tokens":500},
6254 "uuid":"u2"}"#,
6255 r#"{"type":"system","subtype":"task_notification","session_id":"s1",
6256 "task_id":"t1","tool_use_id":"tu1","status":"completed",
6257 "summary":"done","output_file":"",
6258 "usage":{"duration_ms":100,"tool_uses":1,"total_tokens":500},
6259 "uuid":"u3"}"#,
6260 ];
6261
6262 for json in cases {
6263 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6264 let value = serde_json::to_value(&output).unwrap();
6265 let reparsed: ClaudeOutput = serde_json::from_value(value).unwrap();
6266
6267 let ClaudeOutput::System(sys) = reparsed else {
6268 panic!("Expected System variant after round-trip");
6269 };
6270
6271 match sys.subtype {
6272 super::SystemSubtype::TaskStarted => {
6273 assert!(
6274 sys.as_task_started().is_some(),
6275 "as_task_started failed after round-trip"
6276 );
6277 }
6278 super::SystemSubtype::TaskProgress => {
6279 assert!(
6280 sys.as_task_progress().is_some(),
6281 "as_task_progress failed after round-trip"
6282 );
6283 }
6284 super::SystemSubtype::TaskNotification => {
6285 assert!(
6286 sys.as_task_notification().is_some(),
6287 "as_task_notification failed after round-trip"
6288 );
6289 }
6290 other => panic!("unexpected subtype after round-trip: {other:?}"),
6291 }
6292 }
6293 }
6294
6295 #[test]
6296 fn test_system_message_compact_boundary() {
6297 let json = r#"{
6298 "type": "system",
6299 "subtype": "compact_boundary",
6300 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6301 "compact_metadata": {
6302 "pre_tokens": 155285,
6303 "trigger": "auto"
6304 },
6305 "uuid": "a67780d5-74cb-48b1-9137-7a6e7cee45d7"
6306 }"#;
6307
6308 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6309 if let ClaudeOutput::System(sys) = output {
6310 assert!(sys.is_compact_boundary());
6311 assert!(!sys.is_init());
6312 assert!(!sys.is_status());
6313
6314 let compact = sys
6315 .as_compact_boundary()
6316 .expect("Should parse as compact_boundary");
6317 assert_eq!(compact.session_id, "879c1a88-3756-4092-aa95-0020c4ed9692");
6318 assert_eq!(compact.compact_metadata.pre_tokens, 155285);
6319 assert_eq!(
6320 compact.compact_metadata.trigger,
6321 super::CompactionTrigger::Auto
6322 );
6323 assert!(compact.summary.is_none());
6325 assert!(compact.leaf_message_count.is_none());
6326 assert!(compact.duration_ms.is_none());
6327 } else {
6328 panic!("Expected System message");
6329 }
6330 }
6331
6332 #[test]
6333 fn test_compact_boundary_with_summary_stats() {
6334 let json = r#"{
6336 "type": "system",
6337 "subtype": "compact_boundary",
6338 "session_id": "s1",
6339 "compact_metadata": { "pre_tokens": 1000, "trigger": "manual" },
6340 "summary": "Summarized the earlier exploration.",
6341 "leaf_message_count": 42,
6342 "duration_ms": 1234,
6343 "uuid": "u1"
6344 }"#;
6345 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6346 let ClaudeOutput::System(sys) = output else {
6347 panic!("Expected System message");
6348 };
6349 let compact = sys.as_compact_boundary().expect("compact_boundary");
6350 assert_eq!(
6351 compact.summary.as_deref(),
6352 Some("Summarized the earlier exploration.")
6353 );
6354 assert_eq!(compact.leaf_message_count, Some(42));
6355 assert_eq!(compact.duration_ms, Some(1234));
6356
6357 let json_alt = r#"{
6360 "type": "system",
6361 "subtype": "compact_boundary",
6362 "session_id": "s2",
6363 "compact_metadata": { "pre_tokens": 2000, "trigger": "auto" },
6364 "content": "alt-key summary",
6365 "message_count": 7
6366 }"#;
6367 let output: ClaudeOutput = serde_json::from_str(json_alt).unwrap();
6368 let ClaudeOutput::System(sys) = output else {
6369 panic!("Expected System message");
6370 };
6371 let compact = sys.as_compact_boundary().expect("compact_boundary");
6372 assert_eq!(compact.summary.as_deref(), Some("alt-key summary"));
6373 assert_eq!(compact.leaf_message_count, Some(7));
6374 }
6375
6376 #[test]
6377 fn test_init_message_with_new_fields() {
6378 let json = r#"{
6379 "type": "system",
6380 "subtype": "init",
6381 "session_id": "test-session",
6382 "cwd": "/home/user",
6383 "model": "claude-opus-4-7",
6384 "tools": ["Bash"],
6385 "mcp_servers": [],
6386 "permissionMode": "default",
6387 "apiKeySource": "none",
6388 "uuid": "44841a0d-182d-493a-86b5-79800d3d9665",
6389 "memory_paths": {"auto": "/home/user/.claude/projects/memory/"},
6390 "fast_mode_state": "off",
6391 "plugins": [{"name": "lsp", "path": "/plugins/lsp", "source": "lsp@official"}],
6392 "claude_code_version": "2.1.117"
6393 }"#;
6394
6395 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6396 if let ClaudeOutput::System(sys) = output {
6397 let init = sys.as_init().expect("Should parse as init");
6398 assert_eq!(
6399 init.uuid.as_deref(),
6400 Some("44841a0d-182d-493a-86b5-79800d3d9665")
6401 );
6402 assert!(init.memory_paths.is_some());
6403 assert_eq!(init.fast_mode_state.as_deref(), Some("off"));
6404 assert_eq!(init.plugins[0].source.as_deref(), Some("lsp@official"));
6405 assert_eq!(init.claude_code_version.as_deref(), Some("2.1.117"));
6406 } else {
6407 panic!("Expected System message");
6408 }
6409 }
6410
6411 #[test]
6412 fn test_assistant_message_with_new_fields() {
6413 let json = r#"{
6414 "type": "assistant",
6415 "message": {
6416 "id": "msg_1",
6417 "type": "message",
6418 "role": "assistant",
6419 "model": "claude-opus-4-7",
6420 "content": [{"type": "text", "text": "Hello"}],
6421 "stop_reason": "end_turn",
6422 "stop_details": null,
6423 "context_management": null,
6424 "usage": {
6425 "input_tokens": 100,
6426 "output_tokens": 10,
6427 "cache_creation_input_tokens": 50,
6428 "cache_read_input_tokens": 0,
6429 "service_tier": "standard",
6430 "inference_geo": "not_available"
6431 }
6432 },
6433 "session_id": "abc",
6434 "uuid": "msg-uuid-123"
6435 }"#;
6436
6437 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6438 if let ClaudeOutput::Assistant(asst) = output {
6439 assert_eq!(asst.message.stop_details, None);
6440 assert_eq!(asst.message.context_management, None);
6441 let usage = asst.message.usage.unwrap();
6442 assert_eq!(usage.inference_geo.as_deref(), Some("not_available"));
6443 } else {
6444 panic!("Expected Assistant message");
6445 }
6446 }
6447
6448 #[test]
6449 fn test_user_message_with_new_fields() {
6450 let json = r#"{
6451 "type": "user",
6452 "message": {
6453 "role": "user",
6454 "content": [{"type": "text", "text": "Hello"}]
6455 },
6456 "session_id": "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9",
6457 "parent_tool_use_id": "toolu_123",
6458 "uuid": "user-msg-456"
6459 }"#;
6460
6461 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6462 if let ClaudeOutput::User(user) = output {
6463 assert_eq!(user.parent_tool_use_id.as_deref(), Some("toolu_123"));
6464 assert_eq!(user.uuid.as_deref(), Some("user-msg-456"));
6465 } else {
6466 panic!("Expected User message");
6467 }
6468 }
6469
6470 #[test]
6476 fn test_user_message_preserves_tool_use_result_and_timestamp() {
6477 let json = r#"{
6478 "type":"user",
6479 "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"}]},
6480 "parent_tool_use_id":null,
6481 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6482 "uuid":"8ef6e997-a849-4d15-bed3-2837c3d3f4cd",
6483 "timestamp":"2026-05-12T23:12:04.121Z",
6484 "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"}}
6485 }"#;
6486
6487 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6488 let user = match output {
6489 ClaudeOutput::User(u) => u,
6490 other => panic!("Expected User message, got {:?}", other.message_type()),
6491 };
6492
6493 assert_eq!(user.timestamp.as_deref(), Some("2026-05-12T23:12:04.121Z"));
6494 let raw = user
6495 .tool_use_result
6496 .as_ref()
6497 .expect("tool_use_result must be captured");
6498 assert_eq!(raw["answers"]["Color"], "Blue");
6499 assert_eq!(raw["questions"][0]["header"], "Color");
6500
6501 let reser: serde_json::Value = serde_json::to_value(&user).unwrap();
6505 assert_eq!(reser["timestamp"], "2026-05-12T23:12:04.121Z");
6506 assert_eq!(reser["tool_use_result"]["answers"]["Color"], "Blue");
6507 assert_eq!(
6508 reser["tool_use_result"]["questions"][0]["question"],
6509 "Which color do you prefer?"
6510 );
6511
6512 let typed: crate::AskUserQuestionInput = user
6515 .tool_use_result_as::<crate::AskUserQuestionInput>()
6516 .expect("tool_use_result present")
6517 .expect("AskUserQuestionInput parses");
6518 assert_eq!(typed.questions.len(), 1);
6519 assert_eq!(typed.questions[0].header, "Color");
6520 let answers = typed.answers.expect("answers populated");
6521 assert_eq!(answers.get("Color").map(String::as_str), Some("Blue"));
6522 }
6523
6524 #[test]
6527 fn test_user_message_without_tool_use_result_omits_field() {
6528 let json = r#"{
6529 "type":"user",
6530 "message":{"role":"user","content":[{"type":"text","text":"hello"}]},
6531 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d"
6532 }"#;
6533
6534 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6535 let user = match output {
6536 ClaudeOutput::User(u) => u,
6537 _ => panic!("Expected User message"),
6538 };
6539 assert!(user.tool_use_result.is_none());
6540 assert!(user.timestamp.is_none());
6541
6542 let reser = serde_json::to_value(&user).unwrap();
6543 assert!(reser.get("tool_use_result").is_none());
6544 assert!(reser.get("timestamp").is_none());
6545 }
6546
6547 #[test]
6551 fn test_subagent_result_exposes_token_accounting() {
6552 let json = r#"{
6553 "type":"user",
6554 "message":{"role":"user","content":[{"tool_use_id":"toolu_01","type":"tool_result","content":[{"type":"text","text":"21"}]}]},
6555 "session_id":"d3fc5942-75e5-4aa1-a87d-b9484a176541",
6556 "tool_use_result":{
6557 "status":"completed",
6558 "prompt":"Count the .rs files.",
6559 "agentId":"ac4f0276e9d4b6232",
6560 "agentType":"Explore",
6561 "content":[{"type":"text","text":"21"}],
6562 "resolvedModel":"claude-haiku-4-5-20251001",
6563 "totalDurationMs":6869,
6564 "totalTokens":7834,
6565 "totalToolUseCount":1,
6566 "usage":{"input_tokens":6,"cache_creation_input_tokens":125,"cache_read_input_tokens":7699,"output_tokens":4,"service_tier":"standard"},
6567 "toolStats":{"readCount":0,"searchCount":0,"bashCount":1,"editFileCount":0,"linesAdded":0,"linesRemoved":0,"otherToolCount":0}
6568 }
6569 }"#;
6570
6571 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6572 let user = match output {
6573 ClaudeOutput::User(u) => u,
6574 _ => panic!("Expected User message"),
6575 };
6576
6577 let result = user.subagent_result().expect("subagent result parses");
6578 assert_eq!(result.agent_type.as_deref(), Some("Explore"));
6579 assert_eq!(
6580 result.resolved_model.as_deref(),
6581 Some("claude-haiku-4-5-20251001")
6582 );
6583 assert_eq!(result.total_tokens, Some(7834));
6584 assert_eq!(result.total_duration_ms, Some(6869));
6585 assert_eq!(result.total_tool_use_count, Some(1));
6586
6587 let usage = result.usage.expect("nested usage present");
6588 assert_eq!(usage.input_tokens, 6);
6589 assert_eq!(usage.cache_read_input_tokens, 7699);
6590
6591 let stats = result.tool_stats.expect("toolStats present");
6592 assert_eq!(stats.bash_count, 1);
6593 }
6594
6595 #[test]
6599 fn test_subagent_result_absent_for_non_task_result() {
6600 let json = r#"{
6601 "type":"user",
6602 "message":{"role":"user","content":[{"type":"text","text":"hi"}]},
6603 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6604 "tool_use_result":{"questions":[],"answers":{"Color":"Blue"}}
6605 }"#;
6606
6607 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6608 let user = match output {
6609 ClaudeOutput::User(u) => u,
6610 _ => panic!("Expected User message"),
6611 };
6612
6613 let result = user.subagent_result().expect("lenient parse");
6614 assert_eq!(result.total_tokens, None);
6615 assert_eq!(result.agent_type, None);
6616 }
6617
6618 #[test]
6619 fn test_init_fast_mode_reason_and_mcp_server_errors_fully_wrapped() {
6620 use serde_json::Value;
6621
6622 let raw: Value = serde_json::from_str(
6623 r#"{
6624 "type":"system","subtype":"init","session_id":"s1","uuid":"u1",
6625 "fast_mode_state":"off",
6626 "fast_mode_disabled_reason":"not_first_party",
6627 "mcp_server_errors":[{"name":"broken","type":"invalid_config","message":"url entry with no type"}]
6628 }"#,
6629 )
6630 .unwrap();
6631 crate::io::assert_fully_wrapped(&raw);
6632
6633 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6634 let ClaudeOutput::System(sys) = output else {
6635 panic!("expected System");
6636 };
6637 let init = sys.as_init().expect("parses as init");
6638 assert_eq!(
6639 init.fast_mode_disabled_reason,
6640 Some(crate::FastModeDisabledReason::NotFirstParty)
6641 );
6642 let errs = init.mcp_server_errors.unwrap();
6643 assert_eq!(errs.len(), 1);
6644 assert_eq!(errs[0].name, "broken");
6645 assert_eq!(errs[0].error_type, "invalid_config");
6646 }
6647
6648 #[test]
6649 fn test_code_change_published_fully_wrapped() {
6650 use super::{KnownSystemEvent, SystemSubtype};
6651 use serde_json::Value;
6652
6653 let raw: Value = serde_json::from_str(
6654 r#"{
6655 "type":"system","subtype":"code_change_published",
6656 "provider":"github","url":"https://github.com/owner/repo/pull/42",
6657 "repo":"owner/repo","identifier":"42",
6658 "uuid":"u1","session_id":"s1"
6659 }"#,
6660 )
6661 .unwrap();
6662 crate::io::assert_fully_wrapped(&raw);
6663
6664 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6665 let ClaudeOutput::System(sys) = output else {
6666 panic!("expected System");
6667 };
6668 assert_eq!(sys.subtype, SystemSubtype::CodeChangePublished);
6669 let Some(KnownSystemEvent::CodeChangePublished(msg)) = sys.as_known_system_event() else {
6670 panic!("expected CodeChangePublished event");
6671 };
6672 assert_eq!(msg.provider, "github");
6673 assert_eq!(msg.repo, "owner/repo");
6674 assert_eq!(msg.identifier, "42");
6675
6676 assert!(sys.is_code_change_published());
6677 assert!(!sys.is_vcs_state_changed());
6678 let direct = sys.as_code_change_published().expect("direct accessor");
6679 assert_eq!(direct.url, "https://github.com/owner/repo/pull/42");
6680 assert!(sys.as_vcs_state_changed().is_none());
6681 }
6682
6683 #[test]
6684 fn test_feedback_draft_queued_fully_wrapped() {
6685 use super::{KnownSystemEvent, SystemSubtype};
6686 use serde_json::Value;
6687
6688 let raw: Value = serde_json::from_str(
6689 r#"{
6690 "type":"system","subtype":"feedback_draft_queued",
6691 "draft_id":"draft-1","draft_type":"bug_report",
6692 "title":"Tool output was truncated",
6693 "details_preview":"The last command omitted its final lines",
6694 "uuid":"u1","session_id":"s1","future_field":"preserved"
6695 }"#,
6696 )
6697 .unwrap();
6698 crate::io::assert_fully_wrapped(&raw);
6699
6700 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6701 let ClaudeOutput::System(sys) = output else {
6702 panic!("expected System");
6703 };
6704 assert_eq!(sys.subtype, SystemSubtype::FeedbackDraftQueued);
6705 assert!(sys.is_feedback_draft_queued());
6706 assert!(!sys.is_vcs_state_changed());
6707
6708 let direct = sys
6709 .as_feedback_draft_queued()
6710 .expect("direct typed accessor");
6711 assert_eq!(direct.draft_id, "draft-1");
6712 assert_eq!(direct.draft_type, "bug_report");
6713 assert_eq!(direct.extra["future_field"], "preserved");
6714
6715 let Some(KnownSystemEvent::FeedbackDraftQueued(known)) = sys.as_known_system_event() else {
6716 panic!("expected FeedbackDraftQueued event");
6717 };
6718 assert_eq!(known.title, "Tool output was truncated");
6719 assert_eq!(
6720 sys.typed_value().expect("typed value")["future_field"],
6721 "preserved"
6722 );
6723 }
6724
6725 #[test]
6726 fn test_cloud_session_delta_fully_wrapped() {
6727 use super::{KnownSystemEvent, SystemSubtype};
6728 use serde_json::Value;
6729
6730 let raw: Value = serde_json::from_str(
6731 r#"{
6732 "type":"system","subtype":"cloud_session_delta",
6733 "seq":3,"changed":["serving","connection"],
6734 "cloud_session":{"id":"session_abc","view_url":"https://example.invalid/s/abc",
6735 "serving":{"state":"on"},"connection":{"state":"live"}},
6736 "uuid":"u1","session_id":"s1","future_field":"preserved"
6737 }"#,
6738 )
6739 .unwrap();
6740 crate::io::assert_fully_wrapped(&raw);
6741
6742 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6743 let ClaudeOutput::System(sys) = output else {
6744 panic!("expected System");
6745 };
6746 assert_eq!(sys.subtype, SystemSubtype::CloudSessionDelta);
6747 assert!(sys.is_cloud_session_delta());
6748 assert!(!sys.is_feedback_draft_queued());
6749
6750 let direct = sys.as_cloud_session_delta().expect("direct typed accessor");
6751 assert_eq!(direct.seq, 3);
6752 assert_eq!(direct.changed, vec!["serving", "connection"]);
6753 assert_eq!(direct.cloud_session["id"], "session_abc");
6754 assert_eq!(direct.extra["future_field"], "preserved");
6755
6756 let Some(KnownSystemEvent::CloudSessionDelta(known)) = sys.as_known_system_event() else {
6757 panic!("expected CloudSessionDelta event");
6758 };
6759 assert_eq!(known.session_id, "s1");
6760 assert_eq!(
6761 sys.typed_value().expect("typed value")["future_field"],
6762 "preserved"
6763 );
6764 }
6765
6766 #[test]
6767 fn test_vcs_state_changed_fully_wrapped() {
6768 use super::{KnownSystemEvent, VcsMutationKind};
6769 use serde_json::Value;
6770
6771 for kind in ["commit", "push", "merge", "rebase"] {
6772 let raw: Value = serde_json::from_str(&format!(
6773 r#"{{"type":"system","subtype":"vcs_state_changed","kind":"{}","cwd":"/repo","uuid":"u1","session_id":"s1"}}"#,
6774 kind
6775 ))
6776 .unwrap();
6777 crate::io::assert_fully_wrapped(&raw);
6778
6779 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6780 let ClaudeOutput::System(sys) = output else {
6781 panic!("expected System");
6782 };
6783 let Some(KnownSystemEvent::VcsStateChanged(msg)) = sys.as_known_system_event() else {
6784 panic!("expected VcsStateChanged event");
6785 };
6786 assert_eq!(msg.kind.as_str(), kind);
6787 assert!(!matches!(msg.kind, VcsMutationKind::Unknown(_)));
6788 }
6789
6790 let raw: Value = serde_json::from_str(
6792 r#"{"type":"system","subtype":"vcs_state_changed","kind":"tag","cwd":"/repo","uuid":"u2","session_id":"s2"}"#,
6793 )
6794 .unwrap();
6795 crate::io::assert_fully_wrapped(&raw);
6796 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6797 let ClaudeOutput::System(sys) = output else {
6798 panic!("expected System");
6799 };
6800 let Some(KnownSystemEvent::VcsStateChanged(msg)) = sys.as_known_system_event() else {
6801 panic!("expected VcsStateChanged event");
6802 };
6803 assert_eq!(msg.kind, VcsMutationKind::Unknown("tag".to_string()));
6804
6805 assert!(sys.is_vcs_state_changed());
6806 let direct = sys.as_vcs_state_changed().expect("direct accessor");
6807 assert_eq!(direct.cwd, "/repo");
6808 assert!(sys.as_code_change_published().is_none());
6809 }
6810
6811 #[test]
6812 fn test_assistant_aborted_and_resume_flags_roundtrip() {
6813 let json = r#"{
6814 "type":"assistant",
6815 "message":{"id":"msg_1","role":"assistant","model":"claude-3","content":[{"type":"text","text":"partial"}]},
6816 "session_id":"s1",
6817 "aborted":true,
6818 "resumed_from_incomplete_thinking":true
6819 }"#;
6820 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6821 let ClaudeOutput::Assistant(msg) = &output else {
6822 panic!("expected Assistant");
6823 };
6824 assert_eq!(msg.aborted, Some(true));
6825 assert_eq!(msg.resumed_from_incomplete_thinking, Some(true));
6826 let reserialized = serde_json::to_string(&output).unwrap();
6827 assert!(reserialized.contains("\"aborted\":true"));
6828 assert!(reserialized.contains("\"resumed_from_incomplete_thinking\":true"));
6829
6830 let json = r#"{
6832 "type":"assistant",
6833 "message":{"id":"msg_2","role":"assistant","model":"claude-3","content":[]},
6834 "session_id":"s2"
6835 }"#;
6836 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6837 let reserialized = serde_json::to_string(&output).unwrap();
6838 assert!(!reserialized.contains("aborted"));
6839 assert!(!reserialized.contains("resumed_from_incomplete_thinking"));
6840 }
6841
6842 #[test]
6843 fn test_user_tool_result_meta_roundtrip() {
6844 let json = r#"{
6845 "type":"user",
6846 "message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_1","content":"denied"}]},
6847 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6848 "tool_result_meta":[
6849 {"id":"toolu_1","non_execution_kind":"user-rejected","user_feedback":"use the staging db"},
6850 {"id":"toolu_2","non_execution_kind":"permission-rule"}
6851 ]
6852 }"#;
6853 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6854 let ClaudeOutput::User(user) = &output else {
6855 panic!("expected User");
6856 };
6857 let meta = user.tool_result_meta.as_ref().unwrap();
6858 assert_eq!(meta.len(), 2);
6859 assert_eq!(meta[0].non_execution_kind.as_deref(), Some("user-rejected"));
6860 assert_eq!(meta[0].user_feedback.as_deref(), Some("use the staging db"));
6861 assert_eq!(meta[1].user_feedback, None);
6862
6863 let reserialized = serde_json::to_string(&output).unwrap();
6864 assert!(reserialized.contains("\"non_execution_kind\":\"user-rejected\""));
6865 assert!(!reserialized.contains("\"user_feedback\":null"));
6866 }
6867
6868 #[test]
6872 fn model_refusal_fallback_scope_roundtrips_and_defaults() {
6873 use super::{ModelRefusalFallbackMessage, RefusalFallbackScope};
6874 let with_scope = serde_json::json!({
6875 "trigger": "refusal",
6876 "direction": "retry",
6877 "scope": "local",
6878 "original_model": "claude-fable-5",
6879 "fallback_model": "claude-opus-5",
6880 "request_id": null,
6881 "content": "Refused; retried on fallback model.",
6882 "uuid": "u1",
6883 "session_id": "s1"
6884 });
6885 let msg: ModelRefusalFallbackMessage = serde_json::from_value(with_scope.clone()).unwrap();
6886 assert_eq!(msg.scope, Some(RefusalFallbackScope::Local));
6887 assert_eq!(serde_json::to_value(&msg).unwrap(), with_scope);
6888
6889 let mut without = with_scope.clone();
6892 without.as_object_mut().unwrap().remove("scope");
6893 let msg: ModelRefusalFallbackMessage = serde_json::from_value(without.clone()).unwrap();
6894 assert_eq!(msg.scope, None);
6895 assert_eq!(serde_json::to_value(&msg).unwrap(), without);
6896
6897 assert_eq!(
6899 RefusalFallbackScope::from("workspace").as_str(),
6900 "workspace"
6901 );
6902 }
6903}