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")]
3011 pub ultrathink_available: Option<bool>,
3012
3013 #[serde(default, skip_serializing_if = "Option::is_none")]
3020 pub view_mode: Option<ViewMode>,
3021
3022 #[serde(default, skip_serializing_if = "Option::is_none")]
3029 pub original_cwd: Option<String>,
3030
3031 #[serde(default, skip_serializing_if = "Option::is_none")]
3040 pub additional_directories: Option<Vec<String>>,
3041}
3042
3043#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3047pub struct FooterIndicator {
3048 pub text: String,
3051}
3052
3053#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3056pub enum ViewMode {
3057 Focus,
3060 Default,
3062 Unknown(String),
3064}
3065
3066impl ViewMode {
3067 pub fn as_str(&self) -> &str {
3068 match self {
3069 Self::Focus => "focus",
3070 Self::Default => "default",
3071 Self::Unknown(s) => s.as_str(),
3072 }
3073 }
3074}
3075
3076impl fmt::Display for ViewMode {
3077 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3078 f.write_str(self.as_str())
3079 }
3080}
3081
3082impl From<&str> for ViewMode {
3083 fn from(s: &str) -> Self {
3084 match s {
3085 "focus" => Self::Focus,
3086 "default" => Self::Default,
3087 other => Self::Unknown(other.to_string()),
3088 }
3089 }
3090}
3091
3092impl Serialize for ViewMode {
3093 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3094 serializer.serialize_str(self.as_str())
3095 }
3096}
3097
3098impl<'de> Deserialize<'de> for ViewMode {
3099 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3100 let s = String::deserialize(deserializer)?;
3101 Ok(Self::from(s.as_str()))
3102 }
3103}
3104
3105#[derive(Debug, Clone, Serialize, Deserialize)]
3107pub struct StatusMessage {
3108 pub session_id: String,
3110 pub status: Option<StatusMessageStatus>,
3112 #[serde(skip_serializing_if = "Option::is_none")]
3114 pub uuid: Option<String>,
3115 #[serde(skip_serializing_if = "Option::is_none", rename = "permissionMode")]
3117 pub permission_mode: Option<InitPermissionMode>,
3118 #[serde(skip_serializing_if = "Option::is_none")]
3119 pub compact_result: Option<String>,
3120 #[serde(skip_serializing_if = "Option::is_none")]
3121 pub compact_error: Option<String>,
3122 #[serde(default, skip_serializing_if = "Option::is_none")]
3129 pub compact_output_chars: Option<u64>,
3130}
3131
3132#[derive(Debug, Clone, Serialize, Deserialize)]
3134pub struct CompactBoundaryMessage {
3135 pub session_id: String,
3137 pub compact_metadata: CompactMetadata,
3139 #[serde(
3143 default,
3144 skip_serializing_if = "Option::is_none",
3145 alias = "content",
3146 alias = "text"
3147 )]
3148 pub summary: Option<String>,
3149 #[serde(
3153 default,
3154 skip_serializing_if = "Option::is_none",
3155 alias = "message_count"
3156 )]
3157 pub leaf_message_count: Option<u32>,
3158 #[serde(default, skip_serializing_if = "Option::is_none")]
3160 pub duration_ms: Option<u64>,
3161 #[serde(skip_serializing_if = "Option::is_none")]
3163 pub uuid: Option<String>,
3164 #[serde(skip_serializing_if = "Option::is_none")]
3166 pub logical_parent_uuid: Option<Option<String>>,
3167 #[serde(default, skip_serializing_if = "Option::is_none")]
3171 pub historical: Option<bool>,
3172}
3173
3174#[derive(Debug, Clone, Serialize, Deserialize)]
3176pub struct CompactMetadata {
3177 pub pre_tokens: u64,
3179 pub trigger: CompactionTrigger,
3181 #[serde(default, skip_serializing_if = "Option::is_none")]
3182 pub post_tokens: Option<u64>,
3183 #[serde(default, skip_serializing_if = "Option::is_none")]
3184 pub cumulative_dropped_tokens: Option<u64>,
3185 #[serde(default, skip_serializing_if = "Option::is_none")]
3186 pub duration_ms: Option<u64>,
3187 #[serde(default, skip_serializing_if = "Option::is_none")]
3188 pub user_context: Option<String>,
3189 #[serde(default, skip_serializing_if = "Option::is_none")]
3190 pub messages_summarized: Option<u64>,
3191 #[serde(default, skip_serializing_if = "Option::is_none")]
3192 pub precomputed: Option<bool>,
3193 #[serde(default, skip_serializing_if = "Option::is_none")]
3194 pub pre_compact_discovered_tools: Option<Vec<String>>,
3195 #[serde(default, skip_serializing_if = "Option::is_none")]
3200 pub pre_compact_artifact_read_versions: Option<Vec<ArtifactReadVersion>>,
3201 #[serde(default, skip_serializing_if = "Option::is_none")]
3202 pub preserved_segment: Option<PreservedSegment>,
3203 #[serde(default, skip_serializing_if = "Option::is_none")]
3204 pub preserved_messages: Option<PreservedMessages>,
3205}
3206
3207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3210pub struct ArtifactReadVersion {
3211 pub slug: String,
3213 pub ver: String,
3215}
3216
3217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3218pub struct PreservedSegment {
3219 pub head_uuid: String,
3220 pub anchor_uuid: String,
3221 pub tail_uuid: String,
3222}
3223
3224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3225pub struct PreservedMessages {
3226 pub anchor_uuid: String,
3227 pub uuids: Vec<String>,
3228 #[serde(default, skip_serializing_if = "Option::is_none")]
3229 pub all_uuids: Option<Vec<String>>,
3230}
3231
3232#[derive(Debug, Clone, Serialize, Deserialize)]
3238pub struct TaskUsage {
3239 pub duration_ms: u64,
3241 pub tool_uses: u64,
3243 pub total_tokens: u64,
3245}
3246
3247#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3249pub enum TaskType {
3250 LocalAgent,
3252 LocalBash,
3254 LocalWorkflow,
3256 Unknown(String),
3258}
3259
3260impl TaskType {
3261 pub fn as_str(&self) -> &str {
3262 match self {
3263 Self::LocalAgent => "local_agent",
3264 Self::LocalBash => "local_bash",
3265 Self::LocalWorkflow => "local_workflow",
3266 Self::Unknown(s) => s.as_str(),
3267 }
3268 }
3269}
3270
3271impl fmt::Display for TaskType {
3272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3273 f.write_str(self.as_str())
3274 }
3275}
3276
3277impl From<&str> for TaskType {
3278 fn from(s: &str) -> Self {
3279 match s {
3280 "local_agent" => Self::LocalAgent,
3281 "local_bash" => Self::LocalBash,
3282 "local_workflow" => Self::LocalWorkflow,
3283 other => Self::Unknown(other.to_string()),
3284 }
3285 }
3286}
3287
3288impl Serialize for TaskType {
3289 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3290 serializer.serialize_str(self.as_str())
3291 }
3292}
3293
3294impl<'de> Deserialize<'de> for TaskType {
3295 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3296 let s = String::deserialize(deserializer)?;
3297 Ok(Self::from(s.as_str()))
3298 }
3299}
3300
3301#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3303pub enum TaskStatus {
3304 Pending,
3305 Running,
3306 Completed,
3307 Failed,
3308 Killed,
3309 Paused,
3310 Stopped,
3311 Unknown(String),
3312}
3313
3314impl TaskStatus {
3315 pub fn as_str(&self) -> &str {
3316 match self {
3317 Self::Pending => "pending",
3318 Self::Running => "running",
3319 Self::Completed => "completed",
3320 Self::Failed => "failed",
3321 Self::Killed => "killed",
3322 Self::Paused => "paused",
3323 Self::Stopped => "stopped",
3324 Self::Unknown(s) => s.as_str(),
3325 }
3326 }
3327}
3328
3329impl fmt::Display for TaskStatus {
3330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3331 f.write_str(self.as_str())
3332 }
3333}
3334
3335impl From<&str> for TaskStatus {
3336 fn from(s: &str) -> Self {
3337 match s {
3338 "pending" => Self::Pending,
3339 "running" => Self::Running,
3340 "completed" => Self::Completed,
3341 "failed" => Self::Failed,
3342 "killed" => Self::Killed,
3343 "paused" => Self::Paused,
3344 "stopped" => Self::Stopped,
3345 other => Self::Unknown(other.to_string()),
3346 }
3347 }
3348}
3349
3350impl Serialize for TaskStatus {
3351 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3352 serializer.serialize_str(self.as_str())
3353 }
3354}
3355
3356impl<'de> Deserialize<'de> for TaskStatus {
3357 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3358 let s = String::deserialize(deserializer)?;
3359 Ok(Self::from(s.as_str()))
3360 }
3361}
3362
3363#[derive(Debug, Clone, Serialize, Deserialize)]
3365pub struct TaskStartedMessage {
3366 pub session_id: String,
3367 pub task_id: String,
3368 #[serde(default, skip_serializing_if = "Option::is_none")]
3374 pub run_id: Option<String>,
3375 #[serde(default, skip_serializing_if = "Option::is_none")]
3381 pub parent_task_id: Option<String>,
3382 #[serde(default, skip_serializing_if = "Option::is_none")]
3383 pub task_type: Option<TaskType>,
3384 #[serde(default, skip_serializing_if = "Option::is_none")]
3385 pub tool_use_id: Option<String>,
3386 pub description: String,
3387 #[serde(default, skip_serializing_if = "Option::is_none")]
3390 pub subagent_type: Option<String>,
3391 #[serde(default, skip_serializing_if = "Option::is_none")]
3397 pub is_backgrounded: Option<bool>,
3398 #[serde(default, skip_serializing_if = "Option::is_none")]
3406 pub awaited: Option<bool>,
3407 #[serde(default, skip_serializing_if = "Option::is_none")]
3411 pub spawn_depth: Option<u32>,
3412 #[serde(default, skip_serializing_if = "Option::is_none")]
3414 pub prompt: Option<String>,
3415 #[serde(default, skip_serializing_if = "Option::is_none")]
3416 pub workflow_name: Option<String>,
3417 #[serde(default, skip_serializing_if = "Option::is_none")]
3418 pub skip_transcript: Option<bool>,
3419 #[serde(default, skip_serializing_if = "Option::is_none")]
3424 pub ambient: Option<bool>,
3425 pub uuid: String,
3426}
3427
3428#[derive(Debug, Clone, Serialize, Deserialize)]
3432pub struct TaskUpdatedMessage {
3433 pub session_id: String,
3434 pub task_id: String,
3435 #[serde(default, skip_serializing_if = "Option::is_none")]
3438 pub run_id: Option<String>,
3439 pub patch: TaskPatch,
3440 pub uuid: String,
3441}
3442
3443#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3446pub struct TaskPatch {
3447 #[serde(default, skip_serializing_if = "Option::is_none")]
3448 pub status: Option<TaskStatus>,
3449 #[serde(default, skip_serializing_if = "Option::is_none")]
3452 pub end_time: Option<u64>,
3453 #[serde(default, skip_serializing_if = "Option::is_none")]
3454 pub description: Option<String>,
3455 #[serde(default, skip_serializing_if = "Option::is_none")]
3456 pub total_paused_ms: Option<u64>,
3457 #[serde(default, skip_serializing_if = "Option::is_none")]
3458 pub error: Option<String>,
3459 #[serde(default, skip_serializing_if = "Option::is_none")]
3460 pub is_backgrounded: Option<bool>,
3461}
3462
3463#[derive(Debug, Clone, Serialize, Deserialize)]
3466pub struct ThinkingTokensMessage {
3467 pub session_id: String,
3468 pub estimated_tokens: u64,
3470 pub estimated_tokens_delta: u64,
3472 #[serde(default, skip_serializing_if = "Option::is_none")]
3479 pub user_message_uuid: Option<String>,
3480 pub uuid: String,
3481}
3482
3483#[derive(Debug, Clone, Serialize, Deserialize)]
3486pub struct TaskProgressMessage {
3487 pub session_id: String,
3488 pub task_id: String,
3489 #[serde(default, skip_serializing_if = "Option::is_none")]
3492 pub run_id: Option<String>,
3493 #[serde(default, skip_serializing_if = "Option::is_none")]
3494 pub tool_use_id: Option<String>,
3495 pub description: String,
3496 #[serde(default, skip_serializing_if = "Option::is_none")]
3497 pub last_tool_name: Option<String>,
3498 pub usage: TaskUsage,
3499 #[serde(default, skip_serializing_if = "Option::is_none")]
3501 pub subagent_type: Option<String>,
3502 #[serde(default, skip_serializing_if = "Option::is_none")]
3503 pub summary: Option<String>,
3504 pub uuid: String,
3505}
3506
3507#[derive(Debug, Clone, Serialize, Deserialize)]
3510pub struct TaskNotificationMessage {
3511 pub session_id: String,
3512 pub task_id: String,
3513 #[serde(default, skip_serializing_if = "Option::is_none")]
3516 pub run_id: Option<String>,
3517 pub status: TaskStatus,
3518 #[serde(default, skip_serializing_if = "Option::is_none")]
3521 pub reason: Option<TaskEndReason>,
3522 pub summary: String,
3523 pub output_file: Option<String>,
3524 #[serde(skip_serializing_if = "Option::is_none")]
3525 pub tool_use_id: Option<String>,
3526 #[serde(skip_serializing_if = "Option::is_none")]
3527 pub usage: Option<TaskUsage>,
3528 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3534 pub resource_links: Vec<ResourceLink>,
3535 #[serde(default, skip_serializing_if = "Option::is_none")]
3542 pub handback: Option<TaskHandback>,
3543 #[serde(default, skip_serializing_if = "Option::is_none")]
3547 pub handback_report: Option<HandbackReport>,
3548 #[serde(default, skip_serializing_if = "Option::is_none")]
3549 pub skip_transcript: Option<bool>,
3550 #[serde(default, skip_serializing_if = "Option::is_none")]
3553 pub ambient: Option<bool>,
3554 #[serde(skip_serializing_if = "Option::is_none")]
3555 pub uuid: Option<String>,
3556}
3557
3558#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3561pub enum TaskHandback {
3562 Send,
3564 Flagged,
3566 Withheld,
3568 Unknown(String),
3570}
3571
3572impl TaskHandback {
3573 pub fn as_str(&self) -> &str {
3574 match self {
3575 Self::Send => "send",
3576 Self::Flagged => "flagged",
3577 Self::Withheld => "withheld",
3578 Self::Unknown(s) => s.as_str(),
3579 }
3580 }
3581}
3582
3583impl fmt::Display for TaskHandback {
3584 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3585 f.write_str(self.as_str())
3586 }
3587}
3588
3589impl From<&str> for TaskHandback {
3590 fn from(s: &str) -> Self {
3591 match s {
3592 "send" => Self::Send,
3593 "flagged" => Self::Flagged,
3594 "withheld" => Self::Withheld,
3595 other => Self::Unknown(other.to_string()),
3596 }
3597 }
3598}
3599
3600impl Serialize for TaskHandback {
3601 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3602 serializer.serialize_str(self.as_str())
3603 }
3604}
3605
3606impl<'de> Deserialize<'de> for TaskHandback {
3607 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3608 let s = String::deserialize(deserializer)?;
3609 Ok(Self::from(s.as_str()))
3610 }
3611}
3612
3613#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3616pub struct HandbackReport {
3617 pub text: String,
3620 #[serde(default, skip_serializing_if = "Option::is_none")]
3623 pub warning: Option<String>,
3624}
3625
3626#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3628pub enum AssistantErrorKind {
3629 AuthenticationFailed,
3630 OauthOrgNotAllowed,
3631 AccountOnHold,
3632 BillingError,
3633 RateLimit,
3634 Overloaded,
3635 InvalidRequest,
3636 ModelNotFound,
3637 ServerError,
3638 UnknownError,
3639 MaxOutputTokens,
3640 Unknown(String),
3641}
3642
3643impl AssistantErrorKind {
3644 pub fn as_str(&self) -> &str {
3645 match self {
3646 Self::AuthenticationFailed => "authentication_failed",
3647 Self::OauthOrgNotAllowed => "oauth_org_not_allowed",
3648 Self::AccountOnHold => "account_on_hold",
3649 Self::BillingError => "billing_error",
3650 Self::RateLimit => "rate_limit",
3651 Self::Overloaded => "overloaded",
3652 Self::InvalidRequest => "invalid_request",
3653 Self::ModelNotFound => "model_not_found",
3654 Self::ServerError => "server_error",
3655 Self::UnknownError => "unknown",
3656 Self::MaxOutputTokens => "max_output_tokens",
3657 Self::Unknown(s) => s.as_str(),
3658 }
3659 }
3660}
3661
3662impl fmt::Display for AssistantErrorKind {
3663 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3664 f.write_str(self.as_str())
3665 }
3666}
3667
3668impl From<&str> for AssistantErrorKind {
3669 fn from(s: &str) -> Self {
3670 match s {
3671 "authentication_failed" => Self::AuthenticationFailed,
3672 "oauth_org_not_allowed" => Self::OauthOrgNotAllowed,
3673 "account_on_hold" => Self::AccountOnHold,
3674 "billing_error" => Self::BillingError,
3675 "rate_limit" => Self::RateLimit,
3676 "overloaded" => Self::Overloaded,
3677 "invalid_request" => Self::InvalidRequest,
3678 "model_not_found" => Self::ModelNotFound,
3679 "server_error" => Self::ServerError,
3680 "unknown" => Self::UnknownError,
3681 "max_output_tokens" => Self::MaxOutputTokens,
3682 other => Self::Unknown(other.to_string()),
3683 }
3684 }
3685}
3686
3687impl Serialize for AssistantErrorKind {
3688 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3689 serializer.serialize_str(self.as_str())
3690 }
3691}
3692
3693impl<'de> Deserialize<'de> for AssistantErrorKind {
3694 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3695 let s = String::deserialize(deserializer)?;
3696 Ok(Self::from(s.as_str()))
3697 }
3698}
3699
3700#[derive(Debug, Clone, Serialize, Deserialize)]
3707pub struct CodeChangePublishedMessage {
3708 pub provider: String,
3712 pub url: String,
3714 pub repo: String,
3717 pub identifier: String,
3719 #[serde(default, skip_serializing_if = "Option::is_none")]
3728 pub action: Option<String>,
3729 #[serde(default, skip_serializing_if = "Option::is_none")]
3737 pub branch: Option<String>,
3738 pub uuid: String,
3739 pub session_id: String,
3740}
3741
3742#[derive(Debug, Clone, Serialize, Deserialize)]
3747pub struct VcsStateChangedMessage {
3748 pub kind: VcsMutationKind,
3751 pub cwd: String,
3754 #[serde(default, skip_serializing_if = "Option::is_none")]
3759 pub branch: Option<String>,
3760 pub uuid: String,
3761 pub session_id: String,
3762}
3763
3764#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3766pub enum VcsMutationKind {
3767 Commit,
3768 Push,
3769 Merge,
3770 Rebase,
3771 Unknown(String),
3773}
3774
3775impl VcsMutationKind {
3776 pub fn as_str(&self) -> &str {
3777 match self {
3778 Self::Commit => "commit",
3779 Self::Push => "push",
3780 Self::Merge => "merge",
3781 Self::Rebase => "rebase",
3782 Self::Unknown(s) => s.as_str(),
3783 }
3784 }
3785}
3786
3787impl fmt::Display for VcsMutationKind {
3788 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3789 f.write_str(self.as_str())
3790 }
3791}
3792
3793impl From<&str> for VcsMutationKind {
3794 fn from(s: &str) -> Self {
3795 match s {
3796 "commit" => Self::Commit,
3797 "push" => Self::Push,
3798 "merge" => Self::Merge,
3799 "rebase" => Self::Rebase,
3800 other => Self::Unknown(other.to_string()),
3801 }
3802 }
3803}
3804
3805impl Serialize for VcsMutationKind {
3806 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3807 serializer.serialize_str(self.as_str())
3808 }
3809}
3810
3811impl<'de> Deserialize<'de> for VcsMutationKind {
3812 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3813 let s = String::deserialize(deserializer)?;
3814 Ok(Self::from(s.as_str()))
3815 }
3816}
3817
3818#[derive(Debug, Clone, Serialize, Deserialize)]
3820pub struct FeedbackDraftQueuedMessage {
3821 pub draft_id: String,
3822 pub draft_type: String,
3823 pub title: String,
3824 pub details_preview: String,
3825 #[serde(default, skip_serializing_if = "Option::is_none")]
3826 pub uuid: Option<String>,
3827 #[serde(default, skip_serializing_if = "Option::is_none")]
3828 pub session_id: Option<String>,
3829 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3830 pub extra: serde_json::Map<String, Value>,
3831}
3832
3833#[derive(Debug, Clone, Serialize, Deserialize)]
3841pub struct CloudSessionDeltaMessage {
3842 pub seq: u64,
3846 pub changed: Vec<String>,
3850 pub cloud_session: Value,
3854 pub uuid: String,
3855 pub session_id: String,
3856 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3857 pub extra: serde_json::Map<String, Value>,
3858}
3859
3860#[derive(Debug, Clone, Serialize, Deserialize)]
3876pub struct DevIntentMessage {
3877 pub kind: DevIntentKind,
3879 #[serde(default, skip_serializing_if = "Option::is_none")]
3885 pub trigger: Option<DevIntentTrigger>,
3886 pub uuid: String,
3887 pub session_id: String,
3888 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
3889 pub extra: serde_json::Map<String, Value>,
3890}
3891
3892#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3896pub enum DevIntentKind {
3897 IosApp,
3903 AndroidApp,
3910 Unknown(String),
3912}
3913
3914impl DevIntentKind {
3915 pub fn as_str(&self) -> &str {
3916 match self {
3917 Self::IosApp => "ios_app",
3918 Self::AndroidApp => "android_app",
3919 Self::Unknown(s) => s.as_str(),
3920 }
3921 }
3922}
3923
3924impl fmt::Display for DevIntentKind {
3925 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3926 f.write_str(self.as_str())
3927 }
3928}
3929
3930impl From<&str> for DevIntentKind {
3931 fn from(s: &str) -> Self {
3932 match s {
3933 "ios_app" => Self::IosApp,
3934 "android_app" => Self::AndroidApp,
3935 other => Self::Unknown(other.to_string()),
3936 }
3937 }
3938}
3939
3940impl Serialize for DevIntentKind {
3941 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3942 serializer.serialize_str(self.as_str())
3943 }
3944}
3945
3946impl<'de> Deserialize<'de> for DevIntentKind {
3947 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
3948 let s = String::deserialize(deserializer)?;
3949 Ok(Self::from(s.as_str()))
3950 }
3951}
3952
3953#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3957pub enum DevIntentTrigger {
3958 SwiftEdit,
3960 UikitImport,
3962 XcodeProject,
3965 IosCommand,
3968 KotlinEdit,
3970 JavaEdit,
3972 AndroidImport,
3974 AndroidManifest,
3976 GradlePlugin,
3978 AndroidCommand,
3981 ProjectScan,
3984 Unknown(String),
3986}
3987
3988impl DevIntentTrigger {
3989 pub fn as_str(&self) -> &str {
3990 match self {
3991 Self::SwiftEdit => "swift_edit",
3992 Self::UikitImport => "uikit_import",
3993 Self::XcodeProject => "xcode_project",
3994 Self::IosCommand => "ios_command",
3995 Self::KotlinEdit => "kotlin_edit",
3996 Self::JavaEdit => "java_edit",
3997 Self::AndroidImport => "android_import",
3998 Self::AndroidManifest => "android_manifest",
3999 Self::GradlePlugin => "gradle_plugin",
4000 Self::AndroidCommand => "android_command",
4001 Self::ProjectScan => "project_scan",
4002 Self::Unknown(s) => s.as_str(),
4003 }
4004 }
4005}
4006
4007impl fmt::Display for DevIntentTrigger {
4008 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4009 f.write_str(self.as_str())
4010 }
4011}
4012
4013impl From<&str> for DevIntentTrigger {
4014 fn from(s: &str) -> Self {
4015 match s {
4016 "swift_edit" => Self::SwiftEdit,
4017 "uikit_import" => Self::UikitImport,
4018 "xcode_project" => Self::XcodeProject,
4019 "ios_command" => Self::IosCommand,
4020 "kotlin_edit" => Self::KotlinEdit,
4021 "java_edit" => Self::JavaEdit,
4022 "android_import" => Self::AndroidImport,
4023 "android_manifest" => Self::AndroidManifest,
4024 "gradle_plugin" => Self::GradlePlugin,
4025 "android_command" => Self::AndroidCommand,
4026 "project_scan" => Self::ProjectScan,
4027 other => Self::Unknown(other.to_string()),
4028 }
4029 }
4030}
4031
4032impl Serialize for DevIntentTrigger {
4033 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4034 serializer.serialize_str(self.as_str())
4035 }
4036}
4037
4038impl<'de> Deserialize<'de> for DevIntentTrigger {
4039 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4040 let s = String::deserialize(deserializer)?;
4041 Ok(Self::from(s.as_str()))
4042 }
4043}
4044
4045#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4049pub enum TaskEndReason {
4050 WorkerRestart,
4053 Unknown(String),
4055}
4056
4057impl TaskEndReason {
4058 pub fn as_str(&self) -> &str {
4059 match self {
4060 Self::WorkerRestart => "worker_restart",
4061 Self::Unknown(s) => s.as_str(),
4062 }
4063 }
4064}
4065
4066impl fmt::Display for TaskEndReason {
4067 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4068 f.write_str(self.as_str())
4069 }
4070}
4071
4072impl From<&str> for TaskEndReason {
4073 fn from(s: &str) -> Self {
4074 match s {
4075 "worker_restart" => Self::WorkerRestart,
4076 other => Self::Unknown(other.to_string()),
4077 }
4078 }
4079}
4080
4081impl Serialize for TaskEndReason {
4082 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4083 serializer.serialize_str(self.as_str())
4084 }
4085}
4086
4087impl<'de> Deserialize<'de> for TaskEndReason {
4088 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4089 let s = String::deserialize(deserializer)?;
4090 Ok(Self::from(s.as_str()))
4091 }
4092}
4093
4094#[derive(Debug, Clone, Serialize, Deserialize)]
4103pub struct TurnHandoffAvailableMessage {
4104 pub v: u64,
4106 pub tools: Vec<String>,
4108 pub worker_epoch: u64,
4110 #[serde(default, skip_serializing_if = "Option::is_none")]
4114 pub relay_marker: Option<bool>,
4115 #[serde(default, skip_serializing_if = "Option::is_none")]
4123 pub staged_files: Option<bool>,
4124 #[serde(default, skip_serializing_if = "Option::is_none")]
4131 pub no_query_first: Option<bool>,
4132 #[serde(default, skip_serializing_if = "Option::is_none")]
4140 pub home_files: Option<bool>,
4141 #[serde(default, skip_serializing_if = "Option::is_none")]
4145 pub file_names: Option<bool>,
4146 #[serde(default, skip_serializing_if = "Option::is_none")]
4156 pub carried_writes: Option<bool>,
4157 #[serde(default, skip_serializing_if = "Option::is_none")]
4171 pub hydrates_carried_lines: Option<bool>,
4172 #[serde(default, skip_serializing_if = "Option::is_none")]
4178 pub shared_message_id: Option<bool>,
4179 #[serde(default, skip_serializing_if = "Option::is_none")]
4184 pub requires_rewind_request_id: Option<bool>,
4185 pub uuid: String,
4186 pub session_id: String,
4187 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4188 pub extra: serde_json::Map<String, Value>,
4189}
4190
4191#[derive(Debug, Clone, Serialize, Deserialize)]
4203pub struct TurnPreemptedMessage {
4204 pub reason: String,
4208 pub preempted_by_uuid: String,
4212 #[serde(default)]
4215 pub preempted_message_uuids: Vec<String>,
4216 pub uuid: String,
4217 pub session_id: String,
4218 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4219 pub extra: serde_json::Map<String, Value>,
4220}
4221
4222#[derive(Debug, Clone, Serialize, Deserialize)]
4230pub struct PeerMessageHoldMessage {
4231 pub state: PeerMessageHoldState,
4237 #[serde(default, skip_serializing_if = "Option::is_none")]
4244 pub message_uuid: Option<String>,
4245 pub lane: PeerMessageLane,
4247 pub from: String,
4252 #[serde(default, skip_serializing_if = "Option::is_none")]
4255 pub from_name: Option<String>,
4256 #[serde(default, skip_serializing_if = "Option::is_none")]
4258 pub cause: Option<PeerMessageHoldCause>,
4259 #[serde(default, skip_serializing_if = "Option::is_none")]
4261 pub outcome: Option<PeerMessageHoldOutcome>,
4262 pub uuid: String,
4263 pub session_id: String,
4264 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
4265 pub extra: serde_json::Map<String, Value>,
4266}
4267
4268#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4270pub enum PeerMessageHoldState {
4271 Held,
4273 Released,
4275 Dropped,
4277 Unknown(String),
4279}
4280
4281impl PeerMessageHoldState {
4282 pub fn as_str(&self) -> &str {
4283 match self {
4284 Self::Held => "held",
4285 Self::Released => "released",
4286 Self::Dropped => "dropped",
4287 Self::Unknown(s) => s.as_str(),
4288 }
4289 }
4290}
4291
4292impl fmt::Display for PeerMessageHoldState {
4293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4294 f.write_str(self.as_str())
4295 }
4296}
4297
4298impl From<&str> for PeerMessageHoldState {
4299 fn from(s: &str) -> Self {
4300 match s {
4301 "held" => Self::Held,
4302 "released" => Self::Released,
4303 "dropped" => Self::Dropped,
4304 other => Self::Unknown(other.to_string()),
4305 }
4306 }
4307}
4308
4309impl Serialize for PeerMessageHoldState {
4310 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4311 serializer.serialize_str(self.as_str())
4312 }
4313}
4314
4315impl<'de> Deserialize<'de> for PeerMessageHoldState {
4316 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4317 let s = String::deserialize(deserializer)?;
4318 Ok(Self::from(s.as_str()))
4319 }
4320}
4321
4322#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4324pub enum PeerMessageLane {
4325 Bridge,
4327 Stdin,
4329 Socket,
4331 Unknown(String),
4333}
4334
4335impl PeerMessageLane {
4336 pub fn as_str(&self) -> &str {
4337 match self {
4338 Self::Bridge => "bridge",
4339 Self::Stdin => "stdin",
4340 Self::Socket => "socket",
4341 Self::Unknown(s) => s.as_str(),
4342 }
4343 }
4344}
4345
4346impl fmt::Display for PeerMessageLane {
4347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4348 f.write_str(self.as_str())
4349 }
4350}
4351
4352impl From<&str> for PeerMessageLane {
4353 fn from(s: &str) -> Self {
4354 match s {
4355 "bridge" => Self::Bridge,
4356 "stdin" => Self::Stdin,
4357 "socket" => Self::Socket,
4358 other => Self::Unknown(other.to_string()),
4359 }
4360 }
4361}
4362
4363impl Serialize for PeerMessageLane {
4364 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4365 serializer.serialize_str(self.as_str())
4366 }
4367}
4368
4369impl<'de> Deserialize<'de> for PeerMessageLane {
4370 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4371 let s = String::deserialize(deserializer)?;
4372 Ok(Self::from(s.as_str()))
4373 }
4374}
4375
4376#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4383pub enum PeerMessageHoldCause {
4384 ExplicitSetting,
4385 ManagedSetting,
4386 RepoSetting,
4387 InvalidSetting,
4388 BypassDefault,
4389 ModeUnknown,
4390 ModeMismatch,
4391 NoModeAsserted,
4392 Unknown(String),
4394}
4395
4396impl PeerMessageHoldCause {
4397 pub fn as_str(&self) -> &str {
4398 match self {
4399 Self::ExplicitSetting => "explicit-setting",
4400 Self::ManagedSetting => "managed-setting",
4401 Self::RepoSetting => "repo-setting",
4402 Self::InvalidSetting => "invalid-setting",
4403 Self::BypassDefault => "bypass-default",
4404 Self::ModeUnknown => "mode-unknown",
4405 Self::ModeMismatch => "mode-mismatch",
4406 Self::NoModeAsserted => "no-mode-asserted",
4407 Self::Unknown(s) => s.as_str(),
4408 }
4409 }
4410}
4411
4412impl fmt::Display for PeerMessageHoldCause {
4413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4414 f.write_str(self.as_str())
4415 }
4416}
4417
4418impl From<&str> for PeerMessageHoldCause {
4419 fn from(s: &str) -> Self {
4420 match s {
4421 "explicit-setting" => Self::ExplicitSetting,
4422 "managed-setting" => Self::ManagedSetting,
4423 "repo-setting" => Self::RepoSetting,
4424 "invalid-setting" => Self::InvalidSetting,
4425 "bypass-default" => Self::BypassDefault,
4426 "mode-unknown" => Self::ModeUnknown,
4427 "mode-mismatch" => Self::ModeMismatch,
4428 "no-mode-asserted" => Self::NoModeAsserted,
4429 other => Self::Unknown(other.to_string()),
4430 }
4431 }
4432}
4433
4434impl Serialize for PeerMessageHoldCause {
4435 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4436 serializer.serialize_str(self.as_str())
4437 }
4438}
4439
4440impl<'de> Deserialize<'de> for PeerMessageHoldCause {
4441 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4442 let s = String::deserialize(deserializer)?;
4443 Ok(Self::from(s.as_str()))
4444 }
4445}
4446
4447#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4450pub enum PeerMessageHoldOutcome {
4451 Expired,
4455 Refused,
4457 Dropped,
4460 Discarded,
4462 Unknown(String),
4464}
4465
4466impl PeerMessageHoldOutcome {
4467 pub fn as_str(&self) -> &str {
4468 match self {
4469 Self::Expired => "expired",
4470 Self::Refused => "refused",
4471 Self::Dropped => "dropped",
4472 Self::Discarded => "discarded",
4473 Self::Unknown(s) => s.as_str(),
4474 }
4475 }
4476}
4477
4478impl fmt::Display for PeerMessageHoldOutcome {
4479 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4480 f.write_str(self.as_str())
4481 }
4482}
4483
4484impl From<&str> for PeerMessageHoldOutcome {
4485 fn from(s: &str) -> Self {
4486 match s {
4487 "expired" => Self::Expired,
4488 "refused" => Self::Refused,
4489 "dropped" => Self::Dropped,
4490 "discarded" => Self::Discarded,
4491 other => Self::Unknown(other.to_string()),
4492 }
4493 }
4494}
4495
4496impl Serialize for PeerMessageHoldOutcome {
4497 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4498 serializer.serialize_str(self.as_str())
4499 }
4500}
4501
4502impl<'de> Deserialize<'de> for PeerMessageHoldOutcome {
4503 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4504 let s = String::deserialize(deserializer)?;
4505 Ok(Self::from(s.as_str()))
4506 }
4507}
4508
4509#[derive(Debug, Clone, Serialize, Deserialize)]
4520pub struct PerTurnEffortChangedMessage {
4521 pub per_turn_effort_active: bool,
4523 pub uuid: String,
4524 pub session_id: String,
4525}
4526
4527#[derive(Debug, Clone, Serialize, Deserialize)]
4534pub struct InstructionSizeWarningMessage {
4535 pub total_chars: u64,
4538 pub total_limit_chars: u64,
4541 pub file_count: u64,
4544 #[serde(default, skip_serializing_if = "Option::is_none")]
4548 pub largest_chars: Option<u64>,
4549 pub uuid: String,
4550 pub session_id: String,
4551}
4552
4553#[derive(Debug, Clone, Serialize, Deserialize)]
4560pub struct FileAttachmentsMissingMessage {
4561 pub message_uuid: String,
4564 pub sent_count: u64,
4566 pub missing: Vec<MissingFileAttachment>,
4568 pub uuid: String,
4569 pub session_id: String,
4570}
4571
4572#[derive(Debug, Clone, Serialize, Deserialize)]
4582pub struct PermissionCheckStatusMessage {
4583 pub tool_use_id: String,
4584 #[serde(default, skip_serializing_if = "Option::is_none")]
4587 pub agent_id: Option<String>,
4588 pub status: PermissionCheckStatus,
4589 pub uuid: String,
4590 pub session_id: String,
4591}
4592
4593#[derive(Debug, Clone, Serialize, Deserialize)]
4598pub struct InheritedAsksSettledMessage {
4599 pub kept_request_ids: Vec<String>,
4602 pub uuid: String,
4603 pub session_id: String,
4604}
4605
4606#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4608pub enum PermissionCheckStatus {
4609 Checking,
4612 Done,
4614 Unknown(String),
4616}
4617
4618impl PermissionCheckStatus {
4619 pub fn as_str(&self) -> &str {
4620 match self {
4621 Self::Checking => "checking",
4622 Self::Done => "done",
4623 Self::Unknown(s) => s.as_str(),
4624 }
4625 }
4626}
4627
4628impl fmt::Display for PermissionCheckStatus {
4629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4630 f.write_str(self.as_str())
4631 }
4632}
4633
4634impl From<&str> for PermissionCheckStatus {
4635 fn from(s: &str) -> Self {
4636 match s {
4637 "checking" => Self::Checking,
4638 "done" => Self::Done,
4639 other => Self::Unknown(other.to_string()),
4640 }
4641 }
4642}
4643
4644impl Serialize for PermissionCheckStatus {
4645 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4646 serializer.serialize_str(self.as_str())
4647 }
4648}
4649
4650impl<'de> Deserialize<'de> for PermissionCheckStatus {
4651 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4652 let s = String::deserialize(deserializer)?;
4653 Ok(Self::from(s.as_str()))
4654 }
4655}
4656
4657#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4659pub struct MissingFileAttachment {
4660 pub file_uuid: String,
4662 pub reason: String,
4667}
4668
4669#[derive(Debug, Clone, Serialize, Deserialize)]
4679pub struct SessionTitleChangedMessage {
4680 pub title: String,
4685 pub uuid: String,
4686 pub session_id: String,
4687}
4688
4689#[derive(Debug, Clone, Serialize, Deserialize)]
4693pub struct UiLogMessage {
4694 pub plugin: String,
4696 pub text: String,
4700 pub uuid: String,
4701 pub session_id: String,
4702}
4703
4704#[derive(Debug, Clone, Serialize, Deserialize)]
4709pub struct UiToastMessage {
4710 pub plugin: String,
4712 pub text: String,
4716 pub timeout_ms: u64,
4719 pub uuid: String,
4720 pub session_id: String,
4721}
4722
4723#[derive(Debug, Clone, Serialize, Deserialize)]
4727pub struct UiStatusMessage {
4728 pub plugin: String,
4730 pub text: Option<String>,
4735 pub uuid: String,
4736 pub session_id: String,
4737}
4738
4739#[derive(Debug, Clone, Serialize, Deserialize)]
4744pub struct UiInvalidateMessage {
4745 pub event: String,
4748 #[serde(default, skip_serializing_if = "Option::is_none")]
4753 pub instances: Option<Vec<UiInvalidatedInstance>>,
4754 #[serde(default, skip_serializing_if = "Option::is_none")]
4762 pub prompts_screened: Option<bool>,
4763 pub uuid: String,
4764 pub session_id: String,
4765}
4766
4767#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4769pub struct UiInvalidatedInstance {
4770 pub surface: String,
4772 pub component: String,
4774 pub instance_id: String,
4777}
4778
4779#[derive(Debug, Clone, Serialize, Deserialize)]
4786pub struct UiPanesMessage {
4787 pub panes: Vec<UiPane>,
4790 pub shown_id: Option<String>,
4793 pub focused_id: Option<String>,
4796 pub focus_requested_id: Option<String>,
4800 pub uuid: String,
4801 pub session_id: String,
4802}
4803
4804#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4806pub struct UiPane {
4807 pub id: String,
4810 pub title: String,
4813 pub plugin: String,
4815 #[serde(default, skip_serializing_if = "Option::is_none")]
4818 pub close_on_escape: Option<bool>,
4819 #[serde(default, skip_serializing_if = "Option::is_none")]
4822 pub hold_toasts: Option<bool>,
4823 #[serde(default, skip_serializing_if = "Option::is_none")]
4828 pub background_color: Option<String>,
4829 #[serde(default, skip_serializing_if = "Option::is_none")]
4832 pub rows: Option<u64>,
4833 #[serde(default, skip_serializing_if = "Option::is_none")]
4836 pub columns: Option<u64>,
4837}
4838
4839#[derive(Debug, Clone, Serialize, Deserialize)]
4847pub struct UiPlacedMessage {
4848 pub rows: Vec<UiPlacedRow>,
4850 pub uuid: String,
4851 pub session_id: String,
4852}
4853
4854#[derive(Debug, Clone, Serialize, Deserialize)]
4856pub struct UiPlacedRow {
4857 pub plugin: String,
4859 pub id: String,
4861 pub request_id: String,
4864 #[serde(default, skip_serializing_if = "Option::is_none")]
4867 pub after_uuid: Option<String>,
4868}
4869
4870#[derive(Debug, Clone, Serialize, Deserialize)]
4874pub struct UiScrollMessage {
4875 pub client_id: String,
4877 pub component: UiSiteComponent,
4879 pub instance_id: String,
4881 pub offset: u64,
4884 #[serde(default, skip_serializing_if = "Option::is_none")]
4887 pub follow_end: Option<bool>,
4888 pub uuid: String,
4889 pub session_id: String,
4890}
4891
4892#[derive(Debug, Clone, Serialize, Deserialize)]
4897pub struct UiFocusMessage {
4898 pub client_id: String,
4900 pub component: UiSiteComponent,
4902 pub instance_id: String,
4904 pub plugin: String,
4906 pub key: String,
4908 pub uuid: String,
4909 pub session_id: String,
4910}
4911
4912#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4914pub enum UiSiteComponent {
4915 Pane,
4917 AbovePrompt,
4919 Unknown(String),
4921}
4922
4923impl UiSiteComponent {
4924 pub fn as_str(&self) -> &str {
4925 match self {
4926 Self::Pane => "Pane",
4927 Self::AbovePrompt => "AbovePrompt",
4928 Self::Unknown(s) => s.as_str(),
4929 }
4930 }
4931}
4932
4933impl fmt::Display for UiSiteComponent {
4934 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4935 f.write_str(self.as_str())
4936 }
4937}
4938
4939impl From<&str> for UiSiteComponent {
4940 fn from(s: &str) -> Self {
4941 match s {
4942 "Pane" => Self::Pane,
4943 "AbovePrompt" => Self::AbovePrompt,
4944 other => Self::Unknown(other.to_string()),
4945 }
4946 }
4947}
4948
4949impl Serialize for UiSiteComponent {
4950 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
4951 serializer.serialize_str(self.as_str())
4952 }
4953}
4954
4955impl<'de> Deserialize<'de> for UiSiteComponent {
4956 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
4957 let s = String::deserialize(deserializer)?;
4958 Ok(Self::from(s.as_str()))
4959 }
4960}
4961
4962#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4965pub struct LocalCommandRun {
4966 pub command: String,
4968 pub args: String,
4971 #[serde(default, skip_serializing_if = "Option::is_none")]
4976 pub plugin: Option<String>,
4977}
4978
4979#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4983pub struct LocalCommandOutcome {
4984 pub kind: LocalCommandOutcomeKind,
4985 #[serde(default, skip_serializing_if = "Option::is_none")]
4988 pub suggestion: Option<String>,
4989}
4990
4991#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4996pub enum LocalCommandOutcomeKind {
4997 UnavailableHeadless,
4999 Unknown,
5002 Failed,
5004 RestartRequired,
5006 Other(String),
5008}
5009
5010impl LocalCommandOutcomeKind {
5011 pub fn as_str(&self) -> &str {
5012 match self {
5013 Self::UnavailableHeadless => "unavailable_headless",
5014 Self::Unknown => "unknown",
5015 Self::Failed => "failed",
5016 Self::RestartRequired => "restart_required",
5017 Self::Other(s) => s.as_str(),
5018 }
5019 }
5020}
5021
5022impl fmt::Display for LocalCommandOutcomeKind {
5023 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5024 f.write_str(self.as_str())
5025 }
5026}
5027
5028impl From<&str> for LocalCommandOutcomeKind {
5029 fn from(s: &str) -> Self {
5030 match s {
5031 "unavailable_headless" => Self::UnavailableHeadless,
5032 "unknown" => Self::Unknown,
5033 "failed" => Self::Failed,
5034 "restart_required" => Self::RestartRequired,
5035 other => Self::Other(other.to_string()),
5036 }
5037 }
5038}
5039
5040impl Serialize for LocalCommandOutcomeKind {
5041 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5042 serializer.serialize_str(self.as_str())
5043 }
5044}
5045
5046impl<'de> Deserialize<'de> for LocalCommandOutcomeKind {
5047 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5048 let s = String::deserialize(deserializer)?;
5049 Ok(Self::from(s.as_str()))
5050 }
5051}
5052
5053#[derive(Debug, Clone, Serialize, Deserialize)]
5059pub struct UsageReport {
5060 pub session: super::control::UsageSession,
5062 pub rate_limits: Option<UsageReportRateLimits>,
5066 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5067 pub extra: serde_json::Map<String, Value>,
5068}
5069
5070#[derive(Debug, Clone, Serialize, Deserialize)]
5072pub struct UsageReportRateLimits {
5073 pub limits: Option<Vec<UsageReportLimit>>,
5081 #[serde(default, skip_serializing_if = "Option::is_none")]
5084 pub extra_usage: Option<UsageReportExtraUsage>,
5085 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5086 pub extra: serde_json::Map<String, Value>,
5087}
5088
5089#[derive(Debug, Clone, Serialize, Deserialize)]
5091pub struct UsageReportLimit {
5092 pub kind: String,
5095 pub group: String,
5098 pub percent: f64,
5100 pub resets_at: Option<String>,
5102 #[serde(default, skip_serializing_if = "Option::is_none")]
5105 pub scope: Option<UsageReportScope>,
5106 #[serde(default, skip_serializing_if = "Option::is_none")]
5110 pub severity: Option<String>,
5111 #[serde(default, skip_serializing_if = "Option::is_none")]
5113 pub is_active: Option<bool>,
5114 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5115 pub extra: serde_json::Map<String, Value>,
5116}
5117
5118#[derive(Debug, Clone, Serialize, Deserialize)]
5120pub struct UsageReportScope {
5121 #[serde(default, skip_serializing_if = "Option::is_none")]
5122 pub model: Option<UsageReportScopeLabel>,
5123 #[serde(default, skip_serializing_if = "Option::is_none")]
5124 pub surface: Option<UsageReportScopeLabel>,
5125 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5126 pub extra: serde_json::Map<String, Value>,
5127}
5128
5129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5131pub struct UsageReportScopeLabel {
5132 pub display_name: String,
5133}
5134
5135#[derive(Debug, Clone, Serialize, Deserialize)]
5139pub struct UsageReportExtraUsage {
5140 pub is_enabled: bool,
5142 pub monthly_limit: Option<f64>,
5143 pub used_credits: Option<f64>,
5144 pub utilization: Option<f64>,
5145 #[serde(default, skip_serializing_if = "Option::is_none")]
5146 pub currency: Option<String>,
5147 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
5148 pub extra: serde_json::Map<String, Value>,
5149}
5150
5151#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5154pub struct BatchToolUse {
5155 pub id: String,
5156 pub name: String,
5157}
5158
5159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5164pub struct ContextUsage {
5165 pub model: String,
5167 pub total_tokens: u64,
5170 pub raw_max_tokens: u64,
5173 pub percentage: u64,
5175 #[serde(default, skip_serializing_if = "Option::is_none")]
5177 pub over_limit: Option<ContextOverLimit>,
5178 #[serde(default)]
5180 pub categories: Vec<ContextCategory>,
5181 #[serde(default)]
5183 pub mcp_tools: Vec<ContextMcpTool>,
5184 #[serde(default)]
5186 pub memory_files: Vec<ContextMemoryFile>,
5187 #[serde(default)]
5189 pub agents: Vec<ContextAgent>,
5190 #[serde(default, skip_serializing_if = "Option::is_none")]
5192 pub skills: Option<Vec<ContextSkill>>,
5193}
5194
5195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5197pub struct ContextOverLimit {
5198 pub tokens_over: u64,
5199 pub kind: String,
5202}
5203
5204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5206pub struct ContextCategory {
5207 pub name: String,
5210 pub tokens: u64,
5211 pub kind: String,
5215}
5216
5217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5219pub struct ContextMcpTool {
5220 pub name: String,
5222 pub server_name: String,
5223 pub tokens: u64,
5224}
5225
5226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5228pub struct ContextMemoryFile {
5229 pub path: String,
5230 #[serde(rename = "type")]
5232 pub file_type: String,
5233 pub tokens: u64,
5234}
5235
5236#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5238pub struct ContextAgent {
5239 pub agent_type: String,
5240 pub source: String,
5242 pub tokens: u64,
5243}
5244
5245#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5247pub struct ContextSkill {
5248 pub name: String,
5249 pub source: String,
5251 #[serde(default, skip_serializing_if = "Option::is_none")]
5252 pub plugin_name: Option<String>,
5253 pub tokens: u64,
5254}
5255
5256#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5258pub struct ToolUseMeta {
5259 pub id: String,
5260 pub display_name: String,
5261 #[serde(default, skip_serializing_if = "Option::is_none")]
5262 pub server_display_name: Option<String>,
5263 #[serde(default, skip_serializing_if = "Option::is_none")]
5264 pub icon_url: Option<String>,
5265 #[serde(default, skip_serializing_if = "Option::is_none")]
5270 pub server_id: Option<String>,
5271 #[serde(default, skip_serializing_if = "Option::is_none")]
5279 pub server_id_stamp: Option<String>,
5280 #[serde(default, skip_serializing_if = "Option::is_none")]
5283 pub tool_name: Option<String>,
5284}
5285
5286#[derive(Debug, Clone, Serialize, Deserialize)]
5288pub struct AssistantMessage {
5289 pub message: AssistantMessageContent,
5290 #[serde(alias = "sessionId")]
5291 pub session_id: String,
5292 #[serde(skip_serializing_if = "Option::is_none")]
5293 pub uuid: Option<String>,
5294 #[serde(skip_serializing_if = "Option::is_none")]
5295 pub parent_tool_use_id: Option<String>,
5296 #[serde(skip_serializing_if = "Option::is_none")]
5298 pub request_id: Option<String>,
5299 #[serde(default, skip_serializing_if = "Option::is_none")]
5304 pub refused_message_id: Option<String>,
5305 #[serde(default, skip_serializing_if = "Option::is_none")]
5314 pub user_message_uuid: Option<String>,
5315 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5321 pub user_message_uuids: Vec<String>,
5322 #[serde(default, skip_serializing_if = "Option::is_none")]
5326 pub agent_id: Option<String>,
5327 #[serde(skip_serializing_if = "Option::is_none")]
5330 pub subagent_type: Option<String>,
5331 #[serde(skip_serializing_if = "Option::is_none")]
5333 pub task_description: Option<String>,
5334 #[serde(skip_serializing_if = "Option::is_none")]
5335 pub error: Option<AssistantErrorKind>,
5336 #[serde(default, skip_serializing_if = "Option::is_none")]
5340 pub aborted: Option<bool>,
5341 #[serde(default, skip_serializing_if = "Option::is_none")]
5346 pub resumed_from_incomplete_thinking: Option<bool>,
5347 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5348 pub supersedes: Vec<String>,
5349 #[serde(skip_serializing_if = "Option::is_none")]
5350 pub timestamp: Option<String>,
5351 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5352 pub tool_use_meta: Vec<ToolUseMeta>,
5353 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5358 pub batch_tool_uses: Vec<BatchToolUse>,
5359 #[serde(default, skip_serializing_if = "Option::is_none")]
5365 pub wire_tool_inputs: Option<serde_json::Map<String, Value>>,
5366 #[serde(default, skip_serializing_if = "Option::is_none")]
5372 pub wire_ingest_context: Option<serde_json::Map<String, Value>>,
5373 #[serde(default, skip_serializing_if = "Option::is_none")]
5377 pub historical: Option<bool>,
5378 #[serde(default, skip_serializing_if = "Option::is_none")]
5386 pub resume_reason: Option<String>,
5387 #[serde(default, skip_serializing_if = "Option::is_none")]
5393 pub local_command_source: Option<String>,
5394 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5403 pub narration_block_indexes: Vec<usize>,
5404 #[serde(default, skip_serializing_if = "Option::is_none")]
5408 pub narration_hint: Option<String>,
5409 #[serde(default, skip_serializing_if = "Option::is_none")]
5418 pub thinking_duration_ms: Option<u64>,
5419 #[serde(default, skip_serializing_if = "Option::is_none")]
5430 pub thinking_display: Option<String>,
5431 #[serde(default, skip_serializing_if = "Option::is_none")]
5435 pub context_usage: Option<ContextUsage>,
5436 #[serde(default, skip_serializing_if = "Option::is_none")]
5444 pub usage_report: Option<Box<UsageReport>>,
5445 #[serde(default, skip_serializing_if = "Option::is_none")]
5452 pub local_command_run: Option<LocalCommandRun>,
5453 #[serde(default, skip_serializing_if = "Option::is_none")]
5459 pub local_command_outcome: Option<LocalCommandOutcome>,
5460 #[serde(default, skip_serializing_if = "Option::is_none")]
5461 pub is_meta: Option<bool>,
5462 #[serde(default, skip_serializing_if = "Option::is_none")]
5463 pub is_virtual: Option<bool>,
5464 #[serde(default, skip_serializing_if = "Option::is_none")]
5465 pub is_api_error_message: Option<bool>,
5466 #[serde(skip_serializing_if = "Option::is_none")]
5467 pub api_error_status: Option<u16>,
5468 #[serde(skip_serializing_if = "Option::is_none")]
5508 pub api_error: Option<String>,
5509 #[serde(default, skip_serializing_if = "Option::is_none")]
5516 pub api_error_code: Option<String>,
5517 #[serde(default, skip_serializing_if = "Option::is_none")]
5528 pub api_error_origin: Option<String>,
5529 #[serde(default, skip_serializing_if = "Option::is_none")]
5532 pub api_error_params: Option<ApiErrorParams>,
5533 #[serde(skip_serializing_if = "Option::is_none")]
5534 pub error_details: Option<String>,
5535 #[serde(skip_serializing_if = "Option::is_none")]
5536 pub advisor_model: Option<String>,
5537 #[serde(skip_serializing_if = "Option::is_none")]
5538 pub attribution_agent: Option<String>,
5539 #[serde(skip_serializing_if = "Option::is_none")]
5540 pub attribution_skill: Option<String>,
5541 #[serde(skip_serializing_if = "Option::is_none")]
5542 pub attribution_plugin: Option<String>,
5543 #[serde(skip_serializing_if = "Option::is_none")]
5544 pub attribution_mcp_server: Option<String>,
5545 #[serde(skip_serializing_if = "Option::is_none")]
5546 pub attribution_mcp_tool: Option<String>,
5547}
5548
5549#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
5553pub struct ApiErrorParams {
5554 #[serde(default, skip_serializing_if = "Option::is_none")]
5556 pub effort: Option<String>,
5557 #[serde(default, skip_serializing_if = "Option::is_none")]
5560 pub provider: Option<ApiErrorProvider>,
5561 #[serde(default, skip_serializing_if = "Option::is_none")]
5564 pub remedy: Option<ApiErrorRemedy>,
5565 #[serde(default, skip_serializing_if = "Option::is_none")]
5568 pub media: Option<ApiErrorMedia>,
5569 #[serde(default, skip_serializing_if = "Option::is_none")]
5571 pub media_reason: Option<ApiErrorMediaReason>,
5572 #[serde(default, skip_serializing_if = "Option::is_none")]
5591 pub rate_limit_info: Option<RateLimitInfo>,
5592}
5593
5594#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5596pub enum ApiErrorMedia {
5597 Image,
5598 Document,
5599 Unknown(String),
5601}
5602
5603impl ApiErrorMedia {
5604 pub fn as_str(&self) -> &str {
5605 match self {
5606 Self::Image => "image",
5607 Self::Document => "document",
5608 Self::Unknown(s) => s.as_str(),
5609 }
5610 }
5611}
5612
5613impl fmt::Display for ApiErrorMedia {
5614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5615 f.write_str(self.as_str())
5616 }
5617}
5618
5619impl From<&str> for ApiErrorMedia {
5620 fn from(s: &str) -> Self {
5621 match s {
5622 "image" => Self::Image,
5623 "document" => Self::Document,
5624 other => Self::Unknown(other.to_string()),
5625 }
5626 }
5627}
5628
5629impl Serialize for ApiErrorMedia {
5630 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5631 serializer.serialize_str(self.as_str())
5632 }
5633}
5634
5635impl<'de> Deserialize<'de> for ApiErrorMedia {
5636 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5637 let s = String::deserialize(deserializer)?;
5638 Ok(Self::from(s.as_str()))
5639 }
5640}
5641
5642#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5645pub enum ApiErrorMediaReason {
5646 Unprocessable,
5649 UnsupportedByModel,
5651 MediaBudget,
5654 Unknown(String),
5656}
5657
5658impl ApiErrorMediaReason {
5659 pub fn as_str(&self) -> &str {
5660 match self {
5661 Self::Unprocessable => "unprocessable",
5662 Self::UnsupportedByModel => "unsupported_by_model",
5663 Self::MediaBudget => "media_budget",
5664 Self::Unknown(s) => s.as_str(),
5665 }
5666 }
5667}
5668
5669impl fmt::Display for ApiErrorMediaReason {
5670 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5671 f.write_str(self.as_str())
5672 }
5673}
5674
5675impl From<&str> for ApiErrorMediaReason {
5676 fn from(s: &str) -> Self {
5677 match s {
5678 "unprocessable" => Self::Unprocessable,
5679 "unsupported_by_model" => Self::UnsupportedByModel,
5680 "media_budget" => Self::MediaBudget,
5681 other => Self::Unknown(other.to_string()),
5682 }
5683 }
5684}
5685
5686impl Serialize for ApiErrorMediaReason {
5687 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5688 serializer.serialize_str(self.as_str())
5689 }
5690}
5691
5692impl<'de> Deserialize<'de> for ApiErrorMediaReason {
5693 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5694 let s = String::deserialize(deserializer)?;
5695 Ok(Self::from(s.as_str()))
5696 }
5697}
5698
5699#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5701pub enum ApiErrorProvider {
5702 Bedrock,
5703 AnthropicAws,
5704 Mantle,
5705 AnthropicGoogleCloud,
5706 Vertex,
5707 Foundry,
5708 Gateway,
5709 Unknown(String),
5711}
5712
5713impl ApiErrorProvider {
5714 pub fn as_str(&self) -> &str {
5715 match self {
5716 Self::Bedrock => "bedrock",
5717 Self::AnthropicAws => "anthropicAws",
5718 Self::Mantle => "mantle",
5719 Self::AnthropicGoogleCloud => "anthropicGoogleCloud",
5720 Self::Vertex => "vertex",
5721 Self::Foundry => "foundry",
5722 Self::Gateway => "gateway",
5723 Self::Unknown(s) => s.as_str(),
5724 }
5725 }
5726}
5727
5728impl fmt::Display for ApiErrorProvider {
5729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5730 f.write_str(self.as_str())
5731 }
5732}
5733
5734impl From<&str> for ApiErrorProvider {
5735 fn from(s: &str) -> Self {
5736 match s {
5737 "bedrock" => Self::Bedrock,
5738 "anthropicAws" => Self::AnthropicAws,
5739 "mantle" => Self::Mantle,
5740 "anthropicGoogleCloud" => Self::AnthropicGoogleCloud,
5741 "vertex" => Self::Vertex,
5742 "foundry" => Self::Foundry,
5743 "gateway" => Self::Gateway,
5744 other => Self::Unknown(other.to_string()),
5745 }
5746 }
5747}
5748
5749impl Serialize for ApiErrorProvider {
5750 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5751 serializer.serialize_str(self.as_str())
5752 }
5753}
5754
5755impl<'de> Deserialize<'de> for ApiErrorProvider {
5756 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5757 let s = String::deserialize(deserializer)?;
5758 Ok(Self::from(s.as_str()))
5759 }
5760}
5761
5762#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5764pub enum ApiErrorRemedy {
5765 RefreshCommand,
5768 RefreshCredentials,
5770 Adc,
5773 GatewayToken,
5776 HostManaged,
5778 ModelAccess,
5781 Unknown(String),
5783}
5784
5785impl ApiErrorRemedy {
5786 pub fn as_str(&self) -> &str {
5787 match self {
5788 Self::RefreshCommand => "refresh_command",
5789 Self::RefreshCredentials => "refresh_credentials",
5790 Self::Adc => "adc",
5791 Self::GatewayToken => "gateway_token",
5792 Self::HostManaged => "host_managed",
5793 Self::ModelAccess => "model_access",
5794 Self::Unknown(s) => s.as_str(),
5795 }
5796 }
5797}
5798
5799impl fmt::Display for ApiErrorRemedy {
5800 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5801 f.write_str(self.as_str())
5802 }
5803}
5804
5805impl From<&str> for ApiErrorRemedy {
5806 fn from(s: &str) -> Self {
5807 match s {
5808 "refresh_command" => Self::RefreshCommand,
5809 "refresh_credentials" => Self::RefreshCredentials,
5810 "adc" => Self::Adc,
5811 "gateway_token" => Self::GatewayToken,
5812 "host_managed" => Self::HostManaged,
5813 "model_access" => Self::ModelAccess,
5814 other => Self::Unknown(other.to_string()),
5815 }
5816 }
5817}
5818
5819impl Serialize for ApiErrorRemedy {
5820 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5821 serializer.serialize_str(self.as_str())
5822 }
5823}
5824
5825impl<'de> Deserialize<'de> for ApiErrorRemedy {
5826 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
5827 let s = String::deserialize(deserializer)?;
5828 Ok(Self::from(s.as_str()))
5829 }
5830}
5831
5832#[derive(Debug, Clone, Serialize, Deserialize)]
5834pub struct AssistantMessageContent {
5835 pub id: String,
5836 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
5838 pub message_type: Option<String>,
5839 pub role: MessageRole,
5840 pub model: String,
5841 pub content: Vec<ContentBlock>,
5842 #[serde(skip_serializing_if = "Option::is_none")]
5843 pub stop_reason: Option<StopReason>,
5844 #[serde(skip_serializing_if = "Option::is_none")]
5845 pub stop_sequence: Option<String>,
5846 #[serde(skip_serializing_if = "Option::is_none")]
5847 pub usage: Option<AssistantUsage>,
5848 #[serde(skip_serializing_if = "Option::is_none")]
5850 pub stop_details: Option<Value>,
5851 #[serde(skip_serializing_if = "Option::is_none")]
5853 pub context_management: Option<Value>,
5854}
5855
5856#[derive(Debug, Clone, Serialize, Deserialize)]
5858pub struct AssistantUsage {
5859 #[serde(default)]
5861 pub input_tokens: u32,
5862
5863 #[serde(default)]
5865 pub output_tokens: u32,
5866
5867 #[serde(default)]
5869 pub cache_creation_input_tokens: u32,
5870
5871 #[serde(default)]
5873 pub cache_read_input_tokens: u32,
5874
5875 #[serde(skip_serializing_if = "Option::is_none")]
5877 pub service_tier: Option<String>,
5878
5879 #[serde(skip_serializing_if = "Option::is_none")]
5881 pub cache_creation: Option<CacheCreationDetails>,
5882
5883 #[serde(skip_serializing_if = "Option::is_none")]
5885 pub inference_geo: Option<String>,
5886
5887 #[serde(default, skip_serializing_if = "Option::is_none")]
5890 pub speed: Option<String>,
5891
5892 #[serde(default, skip_serializing_if = "Option::is_none")]
5895 pub fallback_credit: Option<Value>,
5896}
5897
5898#[derive(Debug, Clone, Serialize, Deserialize)]
5900pub struct CacheCreationDetails {
5901 #[serde(default)]
5903 pub ephemeral_1h_input_tokens: u32,
5904
5905 #[serde(default)]
5907 pub ephemeral_5m_input_tokens: u32,
5908}
5909
5910#[cfg(test)]
5911mod tests {
5912 use crate::io::ClaudeOutput;
5913
5914 #[test]
5915 fn test_subagent_usage_rollup_accumulates_task_results() {
5916 use super::SubagentUsageRollup;
5917
5918 let mut rollup = SubagentUsageRollup::default();
5919
5920 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}}"#;
5921 let output: ClaudeOutput = serde_json::from_str(task_result).unwrap();
5922 assert!(rollup.observe(&output));
5923 assert_eq!(rollup.subagent_tokens, 10201);
5924 assert_eq!(rollup.agent_count, 1);
5925 assert_eq!(rollup.tool_uses, 3);
5926 assert_eq!(rollup.duration_ms, 1853);
5927
5928 assert!(!rollup.observe(&output));
5930 assert_eq!(rollup.agent_count, 1);
5931 assert_eq!(rollup.subagent_tokens, 10201);
5932
5933 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}}"#;
5935 let output: ClaudeOutput = serde_json::from_str(second).unwrap();
5936 assert!(rollup.observe(&output));
5937 assert_eq!(rollup.agent_count, 2);
5938 assert_eq!(rollup.subagent_tokens, 10701);
5939 }
5940
5941 #[test]
5942 fn test_subagent_usage_rollup_ignores_non_task_results() {
5943 use super::SubagentUsageRollup;
5944
5945 let mut rollup = SubagentUsageRollup::default();
5946
5947 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}}"#;
5950 let output: ClaudeOutput = serde_json::from_str(tool_search).unwrap();
5951 assert!(!rollup.observe(&output));
5952
5953 let plain = r#"{"type":"user","message":{"role":"user","content":[]},"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1"}"#;
5955 let output: ClaudeOutput = serde_json::from_str(plain).unwrap();
5956 assert!(!rollup.observe(&output));
5957
5958 let system = r#"{"type":"system","subtype":"status","status":null,"session_id":"7fbc568e-2bd6-45aa-b217-a1cf80004ba1"}"#;
5960 let output: ClaudeOutput = serde_json::from_str(system).unwrap();
5961 assert!(!rollup.observe(&output));
5962
5963 assert_eq!(rollup, SubagentUsageRollup::default());
5964 }
5965
5966 #[test]
5967 fn test_subagent_usage_rollup_over_captured_session() {
5968 use super::SubagentUsageRollup;
5969
5970 let mut rollup = SubagentUsageRollup::default();
5971 let fixture =
5972 include_str!("../../test_cases/subagent_sessions/general_purpose_compute.jsonl");
5973 for line in fixture.lines().filter(|l| !l.trim().is_empty()) {
5974 if let Ok(output) = serde_json::from_str::<ClaudeOutput>(line) {
5975 rollup.observe(&output);
5976 }
5977 }
5978 assert_eq!(rollup.agent_count, 1);
5979 assert_eq!(rollup.subagent_tokens, 10201);
5980 }
5981
5982 #[test]
5983 fn test_system_message_init() {
5984 let json = r#"{
5985 "type": "system",
5986 "subtype": "init",
5987 "session_id": "test-session-123",
5988 "cwd": "/home/user/project",
5989 "model": "claude-sonnet-4",
5990 "tools": ["Bash", "Read", "Write"],
5991 "mcp_servers": [],
5992 "slash_commands": ["compact", "cost", "review"],
5993 "agents": ["Bash", "Explore", "Plan"],
5994 "plugins": [{"name": "rust-analyzer-lsp", "path": "/home/user/.claude/plugins/rust-analyzer-lsp/1.0.0"}],
5995 "skills": [],
5996 "claude_code_version": "2.1.15",
5997 "apiKeySource": "none",
5998 "output_style": "default",
5999 "permissionMode": "default"
6000 }"#;
6001
6002 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6003 if let ClaudeOutput::System(sys) = output {
6004 assert!(sys.is_init());
6005 assert!(!sys.is_status());
6006 assert!(!sys.is_compact_boundary());
6007
6008 let init = sys.as_init().expect("Should parse as init");
6009 assert_eq!(init.session_id, "test-session-123");
6010 assert_eq!(init.cwd, Some("/home/user/project".to_string()));
6011 assert_eq!(init.model, Some("claude-sonnet-4".to_string()));
6012 assert_eq!(init.tools, vec!["Bash", "Read", "Write"]);
6013 assert_eq!(init.slash_commands, vec!["compact", "cost", "review"]);
6014 assert_eq!(init.agents, vec!["Bash", "Explore", "Plan"]);
6015 assert_eq!(init.plugins.len(), 1);
6016 assert_eq!(init.plugins[0].name, "rust-analyzer-lsp");
6017 assert_eq!(init.claude_code_version, Some("2.1.15".to_string()));
6018 assert_eq!(init.api_key_source, Some(super::ApiKeySource::None));
6019 assert_eq!(init.output_style, Some(super::OutputStyle::Default));
6020 assert_eq!(
6021 init.permission_mode,
6022 Some(super::InitPermissionMode::Default)
6023 );
6024 } else {
6025 panic!("Expected System message");
6026 }
6027 }
6028
6029 #[test]
6030 fn test_system_message_init_from_real_capture() {
6031 let json = include_str!("../../test_cases/tool_use_captures/tool_msg_0.json");
6032 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6033 if let ClaudeOutput::System(sys) = output {
6034 let init = sys.as_init().expect("Should parse real init capture");
6035 assert_eq!(init.slash_commands.len(), 8);
6036 assert!(init.slash_commands.contains(&"compact".to_string()));
6037 assert!(init.slash_commands.contains(&"review".to_string()));
6038 assert_eq!(init.agents.len(), 5);
6039 assert!(init.agents.contains(&"Bash".to_string()));
6040 assert!(init.agents.contains(&"Explore".to_string()));
6041 assert_eq!(init.plugins.len(), 1);
6042 assert_eq!(init.plugins[0].name, "rust-analyzer-lsp");
6043 assert_eq!(init.claude_code_version, Some("2.1.15".to_string()));
6044 } else {
6045 panic!("Expected System message");
6046 }
6047 }
6048
6049 #[test]
6050 fn test_system_message_status() {
6051 let json = r#"{
6052 "type": "system",
6053 "subtype": "status",
6054 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6055 "status": "compacting",
6056 "uuid": "32eb9f9d-5ef7-47ff-8fce-bbe22fe7ed93"
6057 }"#;
6058
6059 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6060 if let ClaudeOutput::System(sys) = output {
6061 assert!(sys.is_status());
6062 assert!(!sys.is_init());
6063
6064 let status = sys.as_status().expect("Should parse as status");
6065 assert_eq!(status.session_id, "879c1a88-3756-4092-aa95-0020c4ed9692");
6066 assert_eq!(status.status, Some(super::StatusMessageStatus::Compacting));
6067 assert_eq!(
6068 status.uuid,
6069 Some("32eb9f9d-5ef7-47ff-8fce-bbe22fe7ed93".to_string())
6070 );
6071 } else {
6072 panic!("Expected System message");
6073 }
6074 }
6075
6076 #[test]
6077 fn test_system_message_status_null() {
6078 let json = r#"{
6079 "type": "system",
6080 "subtype": "status",
6081 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6082 "status": null,
6083 "uuid": "92d9637e-d00e-418e-acd2-a504e3861c6a"
6084 }"#;
6085
6086 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6087 if let ClaudeOutput::System(sys) = output {
6088 let status = sys.as_status().expect("Should parse as status");
6089 assert_eq!(status.status, None);
6090 } else {
6091 panic!("Expected System message");
6092 }
6093 }
6094
6095 #[test]
6096 fn test_system_message_task_started() {
6097 let json = r#"{
6098 "type": "system",
6099 "subtype": "task_started",
6100 "session_id": "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9",
6101 "task_id": "b6daf3f",
6102 "task_type": "local_bash",
6103 "tool_use_id": "toolu_011rfSTFumpJZdCCfzeD7jaS",
6104 "description": "Wait for CI on PR #12",
6105 "uuid": "c4243261-c128-4747-b8c3-5e1c7c10eeb8"
6106 }"#;
6107
6108 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6109 if let ClaudeOutput::System(sys) = output {
6110 assert!(sys.is_task_started());
6111 assert!(!sys.is_task_progress());
6112 assert!(!sys.is_task_notification());
6113
6114 let task = sys.as_task_started().expect("Should parse as task_started");
6115 assert_eq!(task.session_id, "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9");
6116 assert_eq!(task.task_id, "b6daf3f");
6117 assert_eq!(task.task_type, Some(super::TaskType::LocalBash));
6118 assert_eq!(
6119 task.tool_use_id.as_deref(),
6120 Some("toolu_011rfSTFumpJZdCCfzeD7jaS")
6121 );
6122 assert_eq!(task.description, "Wait for CI on PR #12");
6123 } else {
6124 panic!("Expected System message");
6125 }
6126 }
6127
6128 #[test]
6129 fn test_system_message_task_started_agent() {
6130 let json = r#"{
6131 "type": "system",
6132 "subtype": "task_started",
6133 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6134 "task_id": "a4a7e0906e5fc64cc",
6135 "task_type": "local_agent",
6136 "tool_use_id": "toolu_01SFz9FwZ1cYgCSy8vRM7wep",
6137 "description": "Explore Scene/ArrayScene duplication",
6138 "uuid": "85a39f5a-e4d4-47f7-9a6d-1125f1a8035f"
6139 }"#;
6140
6141 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6142 if let ClaudeOutput::System(sys) = output {
6143 let task = sys.as_task_started().expect("Should parse as task_started");
6144 assert_eq!(task.task_type, Some(super::TaskType::LocalAgent));
6145 assert_eq!(task.task_id, "a4a7e0906e5fc64cc");
6146 } else {
6147 panic!("Expected System message");
6148 }
6149 }
6150
6151 #[test]
6152 fn test_system_message_task_progress() {
6153 let json = r#"{
6154 "type": "system",
6155 "subtype": "task_progress",
6156 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6157 "task_id": "a4a7e0906e5fc64cc",
6158 "tool_use_id": "toolu_01SFz9FwZ1cYgCSy8vRM7wep",
6159 "description": "Reading src/jplephem/chebyshev.rs",
6160 "last_tool_name": "Read",
6161 "usage": {
6162 "duration_ms": 13996,
6163 "tool_uses": 9,
6164 "total_tokens": 38779
6165 },
6166 "uuid": "85a39f5a-e4d4-47f7-9a6d-1125f1a8035f"
6167 }"#;
6168
6169 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6170 if let ClaudeOutput::System(sys) = output {
6171 assert!(sys.is_task_progress());
6172 assert!(!sys.is_task_started());
6173
6174 let progress = sys
6175 .as_task_progress()
6176 .expect("Should parse as task_progress");
6177 assert_eq!(progress.task_id, "a4a7e0906e5fc64cc");
6178 assert_eq!(progress.description, "Reading src/jplephem/chebyshev.rs");
6179 assert_eq!(progress.last_tool_name.as_deref(), Some("Read"));
6180 assert_eq!(progress.usage.duration_ms, 13996);
6181 assert_eq!(progress.usage.tool_uses, 9);
6182 assert_eq!(progress.usage.total_tokens, 38779);
6183 } else {
6184 panic!("Expected System message");
6185 }
6186 }
6187
6188 #[test]
6189 fn test_system_message_task_notification_completed() {
6190 let json = r#"{
6191 "type": "system",
6192 "subtype": "task_notification",
6193 "session_id": "bff4f716-17c1-4255-ab7b-eea9d33824e3",
6194 "task_id": "a0ba761e9dc9c316f",
6195 "tool_use_id": "toolu_01Ho6XVXFLVNjTQ9YqowdBXW",
6196 "status": "completed",
6197 "summary": "Agent \"Write Hipparcos data source doc\" completed",
6198 "output_file": "",
6199 "usage": {
6200 "duration_ms": 172300,
6201 "tool_uses": 11,
6202 "total_tokens": 42005
6203 },
6204 "uuid": "269f49b9-218d-4c8d-9f7e-3a5383a0c5b2"
6205 }"#;
6206
6207 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6208 if let ClaudeOutput::System(sys) = output {
6209 assert!(sys.is_task_notification());
6210
6211 let notif = sys
6212 .as_task_notification()
6213 .expect("Should parse as task_notification");
6214 assert_eq!(notif.status, super::TaskStatus::Completed);
6215 assert_eq!(
6216 notif.summary,
6217 "Agent \"Write Hipparcos data source doc\" completed"
6218 );
6219 assert_eq!(notif.output_file, Some("".to_string()));
6220 assert_eq!(
6221 notif.tool_use_id,
6222 Some("toolu_01Ho6XVXFLVNjTQ9YqowdBXW".to_string())
6223 );
6224 let usage = notif.usage.expect("Should have usage");
6225 assert_eq!(usage.duration_ms, 172300);
6226 assert_eq!(usage.tool_uses, 11);
6227 assert_eq!(usage.total_tokens, 42005);
6228 } else {
6229 panic!("Expected System message");
6230 }
6231 }
6232
6233 #[test]
6234 fn test_system_message_task_notification_failed_no_usage() {
6235 let json = r#"{
6236 "type": "system",
6237 "subtype": "task_notification",
6238 "session_id": "ea629737-3c36-48a8-a1c4-ad761ad35784",
6239 "task_id": "b98f6a3",
6240 "status": "failed",
6241 "summary": "Background command \"Run FSM calibration\" failed with exit code 1",
6242 "output_file": "/tmp/claude-1000/tasks/b98f6a3.output"
6243 }"#;
6244
6245 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6246 if let ClaudeOutput::System(sys) = output {
6247 let notif = sys
6248 .as_task_notification()
6249 .expect("Should parse as task_notification");
6250 assert_eq!(notif.status, super::TaskStatus::Failed);
6251 assert!(notif.tool_use_id.is_none());
6252 assert!(notif.usage.is_none());
6253 assert_eq!(
6254 notif.output_file,
6255 Some("/tmp/claude-1000/tasks/b98f6a3.output".to_string())
6256 );
6257 } else {
6258 panic!("Expected System message");
6259 }
6260 }
6261
6262 #[test]
6268 fn test_task_messages_roundtrip_through_value() {
6269 let cases = [
6270 r#"{"type":"system","subtype":"task_started","session_id":"s1",
6271 "task_id":"t1","task_type":"local_bash","tool_use_id":"tu1",
6272 "description":"Sleep 3s","uuid":"u1"}"#,
6273 r#"{"type":"system","subtype":"task_progress","session_id":"s1",
6274 "task_id":"t1","tool_use_id":"tu1","description":"Running ls",
6275 "last_tool_name":"Bash",
6276 "usage":{"duration_ms":100,"tool_uses":1,"total_tokens":500},
6277 "uuid":"u2"}"#,
6278 r#"{"type":"system","subtype":"task_notification","session_id":"s1",
6279 "task_id":"t1","tool_use_id":"tu1","status":"completed",
6280 "summary":"done","output_file":"",
6281 "usage":{"duration_ms":100,"tool_uses":1,"total_tokens":500},
6282 "uuid":"u3"}"#,
6283 ];
6284
6285 for json in cases {
6286 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6287 let value = serde_json::to_value(&output).unwrap();
6288 let reparsed: ClaudeOutput = serde_json::from_value(value).unwrap();
6289
6290 let ClaudeOutput::System(sys) = reparsed else {
6291 panic!("Expected System variant after round-trip");
6292 };
6293
6294 match sys.subtype {
6295 super::SystemSubtype::TaskStarted => {
6296 assert!(
6297 sys.as_task_started().is_some(),
6298 "as_task_started failed after round-trip"
6299 );
6300 }
6301 super::SystemSubtype::TaskProgress => {
6302 assert!(
6303 sys.as_task_progress().is_some(),
6304 "as_task_progress failed after round-trip"
6305 );
6306 }
6307 super::SystemSubtype::TaskNotification => {
6308 assert!(
6309 sys.as_task_notification().is_some(),
6310 "as_task_notification failed after round-trip"
6311 );
6312 }
6313 other => panic!("unexpected subtype after round-trip: {other:?}"),
6314 }
6315 }
6316 }
6317
6318 #[test]
6319 fn test_system_message_compact_boundary() {
6320 let json = r#"{
6321 "type": "system",
6322 "subtype": "compact_boundary",
6323 "session_id": "879c1a88-3756-4092-aa95-0020c4ed9692",
6324 "compact_metadata": {
6325 "pre_tokens": 155285,
6326 "trigger": "auto"
6327 },
6328 "uuid": "a67780d5-74cb-48b1-9137-7a6e7cee45d7"
6329 }"#;
6330
6331 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6332 if let ClaudeOutput::System(sys) = output {
6333 assert!(sys.is_compact_boundary());
6334 assert!(!sys.is_init());
6335 assert!(!sys.is_status());
6336
6337 let compact = sys
6338 .as_compact_boundary()
6339 .expect("Should parse as compact_boundary");
6340 assert_eq!(compact.session_id, "879c1a88-3756-4092-aa95-0020c4ed9692");
6341 assert_eq!(compact.compact_metadata.pre_tokens, 155285);
6342 assert_eq!(
6343 compact.compact_metadata.trigger,
6344 super::CompactionTrigger::Auto
6345 );
6346 assert!(compact.summary.is_none());
6348 assert!(compact.leaf_message_count.is_none());
6349 assert!(compact.duration_ms.is_none());
6350 } else {
6351 panic!("Expected System message");
6352 }
6353 }
6354
6355 #[test]
6356 fn test_compact_boundary_with_summary_stats() {
6357 let json = r#"{
6359 "type": "system",
6360 "subtype": "compact_boundary",
6361 "session_id": "s1",
6362 "compact_metadata": { "pre_tokens": 1000, "trigger": "manual" },
6363 "summary": "Summarized the earlier exploration.",
6364 "leaf_message_count": 42,
6365 "duration_ms": 1234,
6366 "uuid": "u1"
6367 }"#;
6368 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6369 let ClaudeOutput::System(sys) = output else {
6370 panic!("Expected System message");
6371 };
6372 let compact = sys.as_compact_boundary().expect("compact_boundary");
6373 assert_eq!(
6374 compact.summary.as_deref(),
6375 Some("Summarized the earlier exploration.")
6376 );
6377 assert_eq!(compact.leaf_message_count, Some(42));
6378 assert_eq!(compact.duration_ms, Some(1234));
6379
6380 let json_alt = r#"{
6383 "type": "system",
6384 "subtype": "compact_boundary",
6385 "session_id": "s2",
6386 "compact_metadata": { "pre_tokens": 2000, "trigger": "auto" },
6387 "content": "alt-key summary",
6388 "message_count": 7
6389 }"#;
6390 let output: ClaudeOutput = serde_json::from_str(json_alt).unwrap();
6391 let ClaudeOutput::System(sys) = output else {
6392 panic!("Expected System message");
6393 };
6394 let compact = sys.as_compact_boundary().expect("compact_boundary");
6395 assert_eq!(compact.summary.as_deref(), Some("alt-key summary"));
6396 assert_eq!(compact.leaf_message_count, Some(7));
6397 }
6398
6399 #[test]
6400 fn test_init_message_with_new_fields() {
6401 let json = r#"{
6402 "type": "system",
6403 "subtype": "init",
6404 "session_id": "test-session",
6405 "cwd": "/home/user",
6406 "model": "claude-opus-4-7",
6407 "tools": ["Bash"],
6408 "mcp_servers": [],
6409 "permissionMode": "default",
6410 "apiKeySource": "none",
6411 "uuid": "44841a0d-182d-493a-86b5-79800d3d9665",
6412 "memory_paths": {"auto": "/home/user/.claude/projects/memory/"},
6413 "fast_mode_state": "off",
6414 "plugins": [{"name": "lsp", "path": "/plugins/lsp", "source": "lsp@official"}],
6415 "claude_code_version": "2.1.117"
6416 }"#;
6417
6418 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6419 if let ClaudeOutput::System(sys) = output {
6420 let init = sys.as_init().expect("Should parse as init");
6421 assert_eq!(
6422 init.uuid.as_deref(),
6423 Some("44841a0d-182d-493a-86b5-79800d3d9665")
6424 );
6425 assert!(init.memory_paths.is_some());
6426 assert_eq!(init.fast_mode_state.as_deref(), Some("off"));
6427 assert_eq!(init.plugins[0].source.as_deref(), Some("lsp@official"));
6428 assert_eq!(init.claude_code_version.as_deref(), Some("2.1.117"));
6429 } else {
6430 panic!("Expected System message");
6431 }
6432 }
6433
6434 #[test]
6435 fn test_assistant_message_with_new_fields() {
6436 let json = r#"{
6437 "type": "assistant",
6438 "message": {
6439 "id": "msg_1",
6440 "type": "message",
6441 "role": "assistant",
6442 "model": "claude-opus-4-7",
6443 "content": [{"type": "text", "text": "Hello"}],
6444 "stop_reason": "end_turn",
6445 "stop_details": null,
6446 "context_management": null,
6447 "usage": {
6448 "input_tokens": 100,
6449 "output_tokens": 10,
6450 "cache_creation_input_tokens": 50,
6451 "cache_read_input_tokens": 0,
6452 "service_tier": "standard",
6453 "inference_geo": "not_available"
6454 }
6455 },
6456 "session_id": "abc",
6457 "uuid": "msg-uuid-123"
6458 }"#;
6459
6460 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6461 if let ClaudeOutput::Assistant(asst) = output {
6462 assert_eq!(asst.message.stop_details, None);
6463 assert_eq!(asst.message.context_management, None);
6464 let usage = asst.message.usage.unwrap();
6465 assert_eq!(usage.inference_geo.as_deref(), Some("not_available"));
6466 } else {
6467 panic!("Expected Assistant message");
6468 }
6469 }
6470
6471 #[test]
6472 fn test_user_message_with_new_fields() {
6473 let json = r#"{
6474 "type": "user",
6475 "message": {
6476 "role": "user",
6477 "content": [{"type": "text", "text": "Hello"}]
6478 },
6479 "session_id": "9abbc466-dad0-4b8e-b6b0-cad5eb7a16b9",
6480 "parent_tool_use_id": "toolu_123",
6481 "uuid": "user-msg-456"
6482 }"#;
6483
6484 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6485 if let ClaudeOutput::User(user) = output {
6486 assert_eq!(user.parent_tool_use_id.as_deref(), Some("toolu_123"));
6487 assert_eq!(user.uuid.as_deref(), Some("user-msg-456"));
6488 } else {
6489 panic!("Expected User message");
6490 }
6491 }
6492
6493 #[test]
6499 fn test_user_message_preserves_tool_use_result_and_timestamp() {
6500 let json = r#"{
6501 "type":"user",
6502 "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"}]},
6503 "parent_tool_use_id":null,
6504 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6505 "uuid":"8ef6e997-a849-4d15-bed3-2837c3d3f4cd",
6506 "timestamp":"2026-05-12T23:12:04.121Z",
6507 "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"}}
6508 }"#;
6509
6510 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6511 let user = match output {
6512 ClaudeOutput::User(u) => u,
6513 other => panic!("Expected User message, got {:?}", other.message_type()),
6514 };
6515
6516 assert_eq!(user.timestamp.as_deref(), Some("2026-05-12T23:12:04.121Z"));
6517 let raw = user
6518 .tool_use_result
6519 .as_ref()
6520 .expect("tool_use_result must be captured");
6521 assert_eq!(raw["answers"]["Color"], "Blue");
6522 assert_eq!(raw["questions"][0]["header"], "Color");
6523
6524 let reser: serde_json::Value = serde_json::to_value(&user).unwrap();
6528 assert_eq!(reser["timestamp"], "2026-05-12T23:12:04.121Z");
6529 assert_eq!(reser["tool_use_result"]["answers"]["Color"], "Blue");
6530 assert_eq!(
6531 reser["tool_use_result"]["questions"][0]["question"],
6532 "Which color do you prefer?"
6533 );
6534
6535 let typed: crate::AskUserQuestionInput = user
6538 .tool_use_result_as::<crate::AskUserQuestionInput>()
6539 .expect("tool_use_result present")
6540 .expect("AskUserQuestionInput parses");
6541 assert_eq!(typed.questions.len(), 1);
6542 assert_eq!(typed.questions[0].header, "Color");
6543 let answers = typed.answers.expect("answers populated");
6544 assert_eq!(answers.get("Color").map(String::as_str), Some("Blue"));
6545 }
6546
6547 #[test]
6550 fn test_user_message_without_tool_use_result_omits_field() {
6551 let json = r#"{
6552 "type":"user",
6553 "message":{"role":"user","content":[{"type":"text","text":"hello"}]},
6554 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d"
6555 }"#;
6556
6557 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6558 let user = match output {
6559 ClaudeOutput::User(u) => u,
6560 _ => panic!("Expected User message"),
6561 };
6562 assert!(user.tool_use_result.is_none());
6563 assert!(user.timestamp.is_none());
6564
6565 let reser = serde_json::to_value(&user).unwrap();
6566 assert!(reser.get("tool_use_result").is_none());
6567 assert!(reser.get("timestamp").is_none());
6568 }
6569
6570 #[test]
6574 fn test_subagent_result_exposes_token_accounting() {
6575 let json = r#"{
6576 "type":"user",
6577 "message":{"role":"user","content":[{"tool_use_id":"toolu_01","type":"tool_result","content":[{"type":"text","text":"21"}]}]},
6578 "session_id":"d3fc5942-75e5-4aa1-a87d-b9484a176541",
6579 "tool_use_result":{
6580 "status":"completed",
6581 "prompt":"Count the .rs files.",
6582 "agentId":"ac4f0276e9d4b6232",
6583 "agentType":"Explore",
6584 "content":[{"type":"text","text":"21"}],
6585 "resolvedModel":"claude-haiku-4-5-20251001",
6586 "totalDurationMs":6869,
6587 "totalTokens":7834,
6588 "totalToolUseCount":1,
6589 "usage":{"input_tokens":6,"cache_creation_input_tokens":125,"cache_read_input_tokens":7699,"output_tokens":4,"service_tier":"standard"},
6590 "toolStats":{"readCount":0,"searchCount":0,"bashCount":1,"editFileCount":0,"linesAdded":0,"linesRemoved":0,"otherToolCount":0}
6591 }
6592 }"#;
6593
6594 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6595 let user = match output {
6596 ClaudeOutput::User(u) => u,
6597 _ => panic!("Expected User message"),
6598 };
6599
6600 let result = user.subagent_result().expect("subagent result parses");
6601 assert_eq!(result.agent_type.as_deref(), Some("Explore"));
6602 assert_eq!(
6603 result.resolved_model.as_deref(),
6604 Some("claude-haiku-4-5-20251001")
6605 );
6606 assert_eq!(result.total_tokens, Some(7834));
6607 assert_eq!(result.total_duration_ms, Some(6869));
6608 assert_eq!(result.total_tool_use_count, Some(1));
6609
6610 let usage = result.usage.expect("nested usage present");
6611 assert_eq!(usage.input_tokens, 6);
6612 assert_eq!(usage.cache_read_input_tokens, 7699);
6613
6614 let stats = result.tool_stats.expect("toolStats present");
6615 assert_eq!(stats.bash_count, 1);
6616 }
6617
6618 #[test]
6622 fn test_subagent_result_absent_for_non_task_result() {
6623 let json = r#"{
6624 "type":"user",
6625 "message":{"role":"user","content":[{"type":"text","text":"hi"}]},
6626 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6627 "tool_use_result":{"questions":[],"answers":{"Color":"Blue"}}
6628 }"#;
6629
6630 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6631 let user = match output {
6632 ClaudeOutput::User(u) => u,
6633 _ => panic!("Expected User message"),
6634 };
6635
6636 let result = user.subagent_result().expect("lenient parse");
6637 assert_eq!(result.total_tokens, None);
6638 assert_eq!(result.agent_type, None);
6639 }
6640
6641 #[test]
6642 fn test_init_fast_mode_reason_and_mcp_server_errors_fully_wrapped() {
6643 use serde_json::Value;
6644
6645 let raw: Value = serde_json::from_str(
6646 r#"{
6647 "type":"system","subtype":"init","session_id":"s1","uuid":"u1",
6648 "fast_mode_state":"off",
6649 "fast_mode_disabled_reason":"not_first_party",
6650 "mcp_server_errors":[{"name":"broken","type":"invalid_config","message":"url entry with no type"}]
6651 }"#,
6652 )
6653 .unwrap();
6654 crate::io::assert_fully_wrapped(&raw);
6655
6656 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6657 let ClaudeOutput::System(sys) = output else {
6658 panic!("expected System");
6659 };
6660 let init = sys.as_init().expect("parses as init");
6661 assert_eq!(
6662 init.fast_mode_disabled_reason,
6663 Some(crate::FastModeDisabledReason::NotFirstParty)
6664 );
6665 let errs = init.mcp_server_errors.unwrap();
6666 assert_eq!(errs.len(), 1);
6667 assert_eq!(errs[0].name, "broken");
6668 assert_eq!(errs[0].error_type, "invalid_config");
6669 }
6670
6671 #[test]
6672 fn test_code_change_published_fully_wrapped() {
6673 use super::{KnownSystemEvent, SystemSubtype};
6674 use serde_json::Value;
6675
6676 let raw: Value = serde_json::from_str(
6677 r#"{
6678 "type":"system","subtype":"code_change_published",
6679 "provider":"github","url":"https://github.com/owner/repo/pull/42",
6680 "repo":"owner/repo","identifier":"42",
6681 "uuid":"u1","session_id":"s1"
6682 }"#,
6683 )
6684 .unwrap();
6685 crate::io::assert_fully_wrapped(&raw);
6686
6687 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6688 let ClaudeOutput::System(sys) = output else {
6689 panic!("expected System");
6690 };
6691 assert_eq!(sys.subtype, SystemSubtype::CodeChangePublished);
6692 let Some(KnownSystemEvent::CodeChangePublished(msg)) = sys.as_known_system_event() else {
6693 panic!("expected CodeChangePublished event");
6694 };
6695 assert_eq!(msg.provider, "github");
6696 assert_eq!(msg.repo, "owner/repo");
6697 assert_eq!(msg.identifier, "42");
6698
6699 assert!(sys.is_code_change_published());
6700 assert!(!sys.is_vcs_state_changed());
6701 let direct = sys.as_code_change_published().expect("direct accessor");
6702 assert_eq!(direct.url, "https://github.com/owner/repo/pull/42");
6703 assert!(sys.as_vcs_state_changed().is_none());
6704 }
6705
6706 #[test]
6707 fn test_feedback_draft_queued_fully_wrapped() {
6708 use super::{KnownSystemEvent, SystemSubtype};
6709 use serde_json::Value;
6710
6711 let raw: Value = serde_json::from_str(
6712 r#"{
6713 "type":"system","subtype":"feedback_draft_queued",
6714 "draft_id":"draft-1","draft_type":"bug_report",
6715 "title":"Tool output was truncated",
6716 "details_preview":"The last command omitted its final lines",
6717 "uuid":"u1","session_id":"s1","future_field":"preserved"
6718 }"#,
6719 )
6720 .unwrap();
6721 crate::io::assert_fully_wrapped(&raw);
6722
6723 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6724 let ClaudeOutput::System(sys) = output else {
6725 panic!("expected System");
6726 };
6727 assert_eq!(sys.subtype, SystemSubtype::FeedbackDraftQueued);
6728 assert!(sys.is_feedback_draft_queued());
6729 assert!(!sys.is_vcs_state_changed());
6730
6731 let direct = sys
6732 .as_feedback_draft_queued()
6733 .expect("direct typed accessor");
6734 assert_eq!(direct.draft_id, "draft-1");
6735 assert_eq!(direct.draft_type, "bug_report");
6736 assert_eq!(direct.extra["future_field"], "preserved");
6737
6738 let Some(KnownSystemEvent::FeedbackDraftQueued(known)) = sys.as_known_system_event() else {
6739 panic!("expected FeedbackDraftQueued event");
6740 };
6741 assert_eq!(known.title, "Tool output was truncated");
6742 assert_eq!(
6743 sys.typed_value().expect("typed value")["future_field"],
6744 "preserved"
6745 );
6746 }
6747
6748 #[test]
6749 fn test_cloud_session_delta_fully_wrapped() {
6750 use super::{KnownSystemEvent, SystemSubtype};
6751 use serde_json::Value;
6752
6753 let raw: Value = serde_json::from_str(
6754 r#"{
6755 "type":"system","subtype":"cloud_session_delta",
6756 "seq":3,"changed":["serving","connection"],
6757 "cloud_session":{"id":"session_abc","view_url":"https://example.invalid/s/abc",
6758 "serving":{"state":"on"},"connection":{"state":"live"}},
6759 "uuid":"u1","session_id":"s1","future_field":"preserved"
6760 }"#,
6761 )
6762 .unwrap();
6763 crate::io::assert_fully_wrapped(&raw);
6764
6765 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6766 let ClaudeOutput::System(sys) = output else {
6767 panic!("expected System");
6768 };
6769 assert_eq!(sys.subtype, SystemSubtype::CloudSessionDelta);
6770 assert!(sys.is_cloud_session_delta());
6771 assert!(!sys.is_feedback_draft_queued());
6772
6773 let direct = sys.as_cloud_session_delta().expect("direct typed accessor");
6774 assert_eq!(direct.seq, 3);
6775 assert_eq!(direct.changed, vec!["serving", "connection"]);
6776 assert_eq!(direct.cloud_session["id"], "session_abc");
6777 assert_eq!(direct.extra["future_field"], "preserved");
6778
6779 let Some(KnownSystemEvent::CloudSessionDelta(known)) = sys.as_known_system_event() else {
6780 panic!("expected CloudSessionDelta event");
6781 };
6782 assert_eq!(known.session_id, "s1");
6783 assert_eq!(
6784 sys.typed_value().expect("typed value")["future_field"],
6785 "preserved"
6786 );
6787 }
6788
6789 #[test]
6790 fn test_vcs_state_changed_fully_wrapped() {
6791 use super::{KnownSystemEvent, VcsMutationKind};
6792 use serde_json::Value;
6793
6794 for kind in ["commit", "push", "merge", "rebase"] {
6795 let raw: Value = serde_json::from_str(&format!(
6796 r#"{{"type":"system","subtype":"vcs_state_changed","kind":"{}","cwd":"/repo","uuid":"u1","session_id":"s1"}}"#,
6797 kind
6798 ))
6799 .unwrap();
6800 crate::io::assert_fully_wrapped(&raw);
6801
6802 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6803 let ClaudeOutput::System(sys) = output else {
6804 panic!("expected System");
6805 };
6806 let Some(KnownSystemEvent::VcsStateChanged(msg)) = sys.as_known_system_event() else {
6807 panic!("expected VcsStateChanged event");
6808 };
6809 assert_eq!(msg.kind.as_str(), kind);
6810 assert!(!matches!(msg.kind, VcsMutationKind::Unknown(_)));
6811 }
6812
6813 let raw: Value = serde_json::from_str(
6815 r#"{"type":"system","subtype":"vcs_state_changed","kind":"tag","cwd":"/repo","uuid":"u2","session_id":"s2"}"#,
6816 )
6817 .unwrap();
6818 crate::io::assert_fully_wrapped(&raw);
6819 let output: ClaudeOutput = serde_json::from_value(raw).unwrap();
6820 let ClaudeOutput::System(sys) = output else {
6821 panic!("expected System");
6822 };
6823 let Some(KnownSystemEvent::VcsStateChanged(msg)) = sys.as_known_system_event() else {
6824 panic!("expected VcsStateChanged event");
6825 };
6826 assert_eq!(msg.kind, VcsMutationKind::Unknown("tag".to_string()));
6827
6828 assert!(sys.is_vcs_state_changed());
6829 let direct = sys.as_vcs_state_changed().expect("direct accessor");
6830 assert_eq!(direct.cwd, "/repo");
6831 assert!(sys.as_code_change_published().is_none());
6832 }
6833
6834 #[test]
6835 fn test_assistant_aborted_and_resume_flags_roundtrip() {
6836 let json = r#"{
6837 "type":"assistant",
6838 "message":{"id":"msg_1","role":"assistant","model":"claude-3","content":[{"type":"text","text":"partial"}]},
6839 "session_id":"s1",
6840 "aborted":true,
6841 "resumed_from_incomplete_thinking":true
6842 }"#;
6843 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6844 let ClaudeOutput::Assistant(msg) = &output else {
6845 panic!("expected Assistant");
6846 };
6847 assert_eq!(msg.aborted, Some(true));
6848 assert_eq!(msg.resumed_from_incomplete_thinking, Some(true));
6849 let reserialized = serde_json::to_string(&output).unwrap();
6850 assert!(reserialized.contains("\"aborted\":true"));
6851 assert!(reserialized.contains("\"resumed_from_incomplete_thinking\":true"));
6852
6853 let json = r#"{
6855 "type":"assistant",
6856 "message":{"id":"msg_2","role":"assistant","model":"claude-3","content":[]},
6857 "session_id":"s2"
6858 }"#;
6859 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6860 let reserialized = serde_json::to_string(&output).unwrap();
6861 assert!(!reserialized.contains("aborted"));
6862 assert!(!reserialized.contains("resumed_from_incomplete_thinking"));
6863 }
6864
6865 #[test]
6866 fn test_user_tool_result_meta_roundtrip() {
6867 let json = r#"{
6868 "type":"user",
6869 "message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_1","content":"denied"}]},
6870 "session_id":"622ae0c3-3d50-4fa7-9ee0-69d691238c6d",
6871 "tool_result_meta":[
6872 {"id":"toolu_1","non_execution_kind":"user-rejected","user_feedback":"use the staging db"},
6873 {"id":"toolu_2","non_execution_kind":"permission-rule"}
6874 ]
6875 }"#;
6876 let output: ClaudeOutput = serde_json::from_str(json).unwrap();
6877 let ClaudeOutput::User(user) = &output else {
6878 panic!("expected User");
6879 };
6880 let meta = user.tool_result_meta.as_ref().unwrap();
6881 assert_eq!(meta.len(), 2);
6882 assert_eq!(meta[0].non_execution_kind.as_deref(), Some("user-rejected"));
6883 assert_eq!(meta[0].user_feedback.as_deref(), Some("use the staging db"));
6884 assert_eq!(meta[1].user_feedback, None);
6885
6886 let reserialized = serde_json::to_string(&output).unwrap();
6887 assert!(reserialized.contains("\"non_execution_kind\":\"user-rejected\""));
6888 assert!(!reserialized.contains("\"user_feedback\":null"));
6889 }
6890
6891 #[test]
6895 fn model_refusal_fallback_scope_roundtrips_and_defaults() {
6896 use super::{ModelRefusalFallbackMessage, RefusalFallbackScope};
6897 let with_scope = serde_json::json!({
6898 "trigger": "refusal",
6899 "direction": "retry",
6900 "scope": "local",
6901 "original_model": "claude-fable-5",
6902 "fallback_model": "claude-opus-5",
6903 "request_id": null,
6904 "content": "Refused; retried on fallback model.",
6905 "uuid": "u1",
6906 "session_id": "s1"
6907 });
6908 let msg: ModelRefusalFallbackMessage = serde_json::from_value(with_scope.clone()).unwrap();
6909 assert_eq!(msg.scope, Some(RefusalFallbackScope::Local));
6910 assert_eq!(serde_json::to_value(&msg).unwrap(), with_scope);
6911
6912 let mut without = with_scope.clone();
6915 without.as_object_mut().unwrap().remove("scope");
6916 let msg: ModelRefusalFallbackMessage = serde_json::from_value(without.clone()).unwrap();
6917 assert_eq!(msg.scope, None);
6918 assert_eq!(serde_json::to_value(&msg).unwrap(), without);
6919
6920 assert_eq!(
6922 RefusalFallbackScope::from("workspace").as_str(),
6923 "workspace"
6924 );
6925 }
6926}