1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type")]
10pub enum Entry {
11 #[serde(rename = "user")]
13 User(UserEntry),
14
15 #[serde(rename = "assistant")]
16 Assistant(AssistantEntry),
17
18 #[serde(rename = "system")]
19 System(SystemEntry),
20
21 #[serde(rename = "attachment")]
22 Attachment(AttachmentEntry),
23
24 #[serde(rename = "progress")]
25 Progress(ProgressEntry),
26
27 #[serde(rename = "permission-mode")]
29 PermissionMode(PermissionModeEntry),
30
31 #[serde(rename = "last-prompt")]
32 LastPrompt(LastPromptEntry),
33
34 #[serde(rename = "ai-title")]
35 AiTitle(AiTitleEntry),
36
37 #[serde(rename = "custom-title")]
38 CustomTitle(CustomTitleEntry),
39
40 #[serde(rename = "agent-name")]
41 AgentName(AgentNameEntry),
42
43 #[serde(rename = "agent-color")]
44 AgentColor(AgentColorEntry),
45
46 #[serde(rename = "agent-setting")]
47 AgentSetting(AgentSettingEntry),
48
49 #[serde(rename = "tag")]
50 Tag(TagEntry),
51
52 #[serde(rename = "summary")]
53 Summary(SummaryEntry),
54
55 #[serde(rename = "task-summary")]
56 TaskSummary(TaskSummaryEntry),
57
58 #[serde(rename = "pr-link")]
59 PrLink(PrLinkEntry),
60
61 #[serde(rename = "mode")]
62 Mode(ModeEntry),
63
64 #[serde(rename = "worktree-state")]
65 WorktreeState(WorktreeStateEntry),
66
67 #[serde(rename = "content-replacement")]
68 ContentReplacement(ContentReplacementEntry),
69
70 #[serde(rename = "file-history-snapshot")]
71 FileHistorySnapshot(FileHistorySnapshotEntry),
72
73 #[serde(rename = "attribution-snapshot")]
74 AttributionSnapshot(AttributionSnapshotEntry),
75
76 #[serde(rename = "queue-operation")]
77 QueueOperation(QueueOperationEntry),
78
79 #[serde(rename = "marble-origami-commit")]
80 ContextCollapseCommit(ContextCollapseCommitEntry),
81
82 #[serde(rename = "marble-origami-snapshot")]
83 ContextCollapseSnapshot(ContextCollapseSnapshotEntry),
84
85 #[serde(rename = "speculation-accept")]
86 SpeculationAccept(SpeculationAcceptEntry),
87
88 #[serde(other)]
92 Unknown,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct Envelope {
106 pub uuid: String,
107
108 pub parent_uuid: Option<String>,
110
111 #[serde(skip_serializing_if = "Option::is_none")]
114 pub logical_parent_uuid: Option<String>,
115
116 pub is_sidechain: bool,
117 pub session_id: String,
118 pub timestamp: String,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub user_type: Option<String>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub entrypoint: Option<String>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub cwd: Option<String>,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub version: Option<String>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub git_branch: Option<String>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub slug: Option<String>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub agent_id: Option<String>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub team_name: Option<String>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub agent_name: Option<String>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub agent_color: Option<String>,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub prompt_id: Option<String>,
155
156 #[serde(rename = "isMeta", skip_serializing_if = "Option::is_none")]
158 pub is_meta: Option<bool>,
159
160 #[serde(rename = "forkedFrom", skip_serializing_if = "Option::is_none")]
162 pub forked_from: Option<ForkedFrom>,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct ForkedFrom {
168 pub message_uuid: String,
169 pub session_id: String,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct UserEntry {
178 #[serde(flatten)]
179 pub envelope: Envelope,
180 pub message: UserMessage,
181
182 #[serde(rename = "toolUseResult", skip_serializing_if = "Option::is_none")]
185 pub tool_use_result: Option<Value>,
186
187 #[serde(
189 rename = "sourceToolAssistantUUID",
190 skip_serializing_if = "Option::is_none"
191 )]
192 pub source_tool_assistant_uuid: Option<String>,
193
194 #[serde(rename = "sourceToolUseID", skip_serializing_if = "Option::is_none")]
196 pub source_tool_use_id: Option<String>,
197
198 #[serde(rename = "permissionMode", skip_serializing_if = "Option::is_none")]
199 pub permission_mode: Option<String>,
200
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub origin: Option<Value>,
203
204 #[serde(rename = "isCompactSummary", skip_serializing_if = "Option::is_none")]
205 pub is_compact_summary: Option<bool>,
206
207 #[serde(
208 rename = "isVisibleInTranscriptOnly",
209 skip_serializing_if = "Option::is_none"
210 )]
211 pub is_visible_in_transcript_only: Option<bool>,
212
213 #[serde(rename = "imagePasteIds", skip_serializing_if = "Option::is_none")]
214 pub image_paste_ids: Option<Vec<u64>>,
215
216 #[serde(rename = "planContent", skip_serializing_if = "Option::is_none")]
217 pub plan_content: Option<String>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct UserMessage {
222 pub role: UserRole,
223 pub content: UserContent,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
227#[serde(rename_all = "lowercase")]
228pub enum UserRole {
229 User,
230 #[serde(other)]
231 Unknown,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(untagged)]
237pub enum UserContent {
238 Text(String),
239 Blocks(Vec<UserContentBlock>),
240 Other(Value),
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(tag = "type", rename_all = "snake_case")]
246pub enum UserContentBlock {
247 Text {
248 text: String,
249 },
250
251 ToolResult {
252 tool_use_id: String,
253 content: Value,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 is_error: Option<bool>,
259 },
260
261 Image {
262 source: ImageSource,
263 },
264
265 Document {
266 source: DocumentSource,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 title: Option<String>,
269 },
270
271 #[serde(other)]
273 Unknown,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
277#[serde(tag = "type", rename_all = "snake_case")]
278pub enum ImageSource {
279 Base64 {
280 media_type: String,
281 data: String,
282 },
283 Url {
284 url: String,
285 },
286 #[serde(other)]
288 Unknown,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292#[serde(tag = "type", rename_all = "snake_case")]
293pub enum DocumentSource {
294 Base64 {
295 media_type: String,
296 data: String,
297 },
298 Text {
299 data: String,
300 },
301 Url {
302 url: String,
303 },
304 #[serde(other)]
306 Unknown,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
314#[serde(rename_all = "camelCase")]
315pub struct AssistantEntry {
316 #[serde(flatten)]
317 pub envelope: Envelope,
318 pub message: AssistantMessage,
319
320 #[serde(skip_serializing_if = "Option::is_none")]
321 pub request_id: Option<String>,
322
323 #[serde(rename = "isApiErrorMessage", skip_serializing_if = "Option::is_none")]
324 pub is_api_error_message: Option<bool>,
325
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub error: Option<String>,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct AssistantMessage {
332 pub id: String,
333 #[serde(rename = "type")]
335 pub msg_type: String,
336 pub role: AssistantRole,
337 #[serde(default)]
338 pub model: Option<String>,
339
340 #[serde(
342 default,
343 skip_serializing_if = "Option::is_none",
344 with = "opt_nullable"
345 )]
346 pub container: Option<Option<Value>>,
347
348 pub content: Vec<AssistantContentBlock>,
349
350 pub stop_reason: Option<String>,
353
354 pub stop_sequence: Option<String>,
356
357 #[serde(
360 default,
361 skip_serializing_if = "Option::is_none",
362 with = "opt_nullable"
363 )]
364 pub stop_details: Option<Option<Value>>,
365
366 pub usage: AssistantUsage,
367
368 #[serde(
370 default,
371 skip_serializing_if = "Option::is_none",
372 with = "opt_nullable"
373 )]
374 pub context_management: Option<Option<Value>>,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
378#[serde(rename_all = "lowercase")]
379pub enum AssistantRole {
380 Assistant,
381 #[serde(other)]
382 Unknown,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
386#[serde(tag = "type", rename_all = "snake_case")]
387pub enum AssistantContentBlock {
388 Text {
389 text: String,
390 },
391
392 Thinking {
396 thinking: String,
397 signature: String,
398 },
399
400 RedactedThinking {
401 data: String,
402 },
403
404 ToolUse {
405 id: String,
406 name: String,
407 input: Value,
408 #[serde(skip_serializing_if = "Option::is_none")]
410 caller: Option<ToolUseCaller>,
411 },
412
413 #[serde(other)]
415 Unknown,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct ToolUseCaller {
420 #[serde(rename = "type")]
421 pub caller_type: String,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
426pub struct AssistantUsage {
427 pub input_tokens: u64,
428 pub output_tokens: u64,
429
430 #[serde(skip_serializing_if = "Option::is_none")]
431 pub cache_creation_input_tokens: Option<u64>,
432
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub cache_read_input_tokens: Option<u64>,
435
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub server_tool_use: Option<ServerToolUse>,
438
439 #[serde(
441 default,
442 skip_serializing_if = "Option::is_none",
443 with = "opt_nullable"
444 )]
445 pub service_tier: Option<Option<Value>>,
446
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub cache_creation: Option<CacheCreation>,
449
450 #[serde(
452 default,
453 skip_serializing_if = "Option::is_none",
454 with = "opt_nullable"
455 )]
456 pub inference_geo: Option<Option<Value>>,
457
458 #[serde(
460 default,
461 skip_serializing_if = "Option::is_none",
462 with = "opt_nullable"
463 )]
464 pub iterations: Option<Option<Value>>,
465
466 #[serde(
468 default,
469 skip_serializing_if = "Option::is_none",
470 with = "opt_nullable"
471 )]
472 pub speed: Option<Option<Value>>,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct ServerToolUse {
477 #[serde(default)]
478 pub web_search_requests: u64,
479 #[serde(default)]
480 pub web_fetch_requests: u64,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct CacheCreation {
485 #[serde(skip_serializing_if = "Option::is_none")]
486 pub ephemeral_1h_input_tokens: Option<u64>,
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub ephemeral_5m_input_tokens: Option<u64>,
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct UsageIteration {
493 pub input_tokens: u64,
494 pub output_tokens: u64,
495
496 #[serde(skip_serializing_if = "Option::is_none")]
497 pub cache_read_input_tokens: Option<u64>,
498
499 #[serde(skip_serializing_if = "Option::is_none")]
500 pub cache_creation_input_tokens: Option<u64>,
501
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub cache_creation: Option<CacheCreation>,
504
505 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
507 pub iter_type: Option<String>,
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
519#[serde(rename_all = "camelCase")]
520pub struct SystemEntry {
521 #[serde(flatten)]
522 pub envelope: Envelope,
523
524 pub subtype: SystemSubtype,
525
526 #[serde(skip_serializing_if = "Option::is_none")]
528 pub content: Option<String>,
529
530 #[serde(skip_serializing_if = "Option::is_none")]
532 pub level: Option<String>,
533
534 #[serde(rename = "isMeta", skip_serializing_if = "Option::is_none")]
536 pub is_meta: Option<bool>,
537
538 #[serde(skip_serializing_if = "Option::is_none")]
540 pub cause: Option<Value>,
541
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub error: Option<Value>,
544
545 #[serde(rename = "retryInMs", skip_serializing_if = "Option::is_none")]
546 pub retry_in_ms: Option<f64>,
547
548 #[serde(rename = "retryAttempt", skip_serializing_if = "Option::is_none")]
549 pub retry_attempt: Option<u32>,
550
551 #[serde(rename = "maxRetries", skip_serializing_if = "Option::is_none")]
552 pub max_retries: Option<u32>,
553
554 #[serde(rename = "hookCount", skip_serializing_if = "Option::is_none")]
556 pub hook_count: Option<u32>,
557
558 #[serde(rename = "hookInfos", skip_serializing_if = "Option::is_none")]
559 pub hook_infos: Option<Vec<HookInfo>>,
560
561 #[serde(rename = "hookErrors", skip_serializing_if = "Option::is_none")]
562 pub hook_errors: Option<Vec<Value>>,
563
564 #[serde(
565 rename = "preventedContinuation",
566 skip_serializing_if = "Option::is_none"
567 )]
568 pub prevented_continuation: Option<bool>,
569
570 #[serde(rename = "stopReason", skip_serializing_if = "Option::is_none")]
571 pub stop_reason: Option<String>,
572
573 #[serde(rename = "hasOutput", skip_serializing_if = "Option::is_none")]
574 pub has_output: Option<bool>,
575
576 #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
577 pub tool_use_id: Option<String>,
578
579 #[serde(rename = "durationMs", skip_serializing_if = "Option::is_none")]
581 pub duration_ms: Option<f64>,
582
583 #[serde(rename = "messageCount", skip_serializing_if = "Option::is_none")]
584 pub message_count: Option<u32>,
585
586 #[serde(skip_serializing_if = "Option::is_none")]
588 pub url: Option<String>,
589
590 #[serde(rename = "upgradeNudge", skip_serializing_if = "Option::is_none")]
591 pub upgrade_nudge: Option<String>,
592
593 #[serde(rename = "compactMetadata", skip_serializing_if = "Option::is_none")]
595 pub compact_metadata: Option<CompactMetadata>,
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
599#[serde(rename_all = "snake_case")]
600pub enum SystemSubtype {
601 ApiError,
602 AwaySummary,
603 BridgeStatus,
604 CompactBoundary,
605 Informational,
606 LocalCommand,
607 ScheduledTaskFire,
608 StopHookSummary,
609 TurnDuration,
610 MicrocompactBoundary,
611 PermissionRetry,
612 AgentsKilled,
613 #[serde(other)]
614 Unknown,
615}
616
617#[derive(Debug, Clone, Serialize, Deserialize)]
618#[serde(rename_all = "camelCase")]
619pub struct HookInfo {
620 pub command: String,
621 pub duration_ms: u64,
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
625#[serde(rename_all = "camelCase")]
626pub struct PreservedSegment {
627 pub head_uuid: String,
628 pub anchor_uuid: String,
629 pub tail_uuid: String,
630}
631
632#[derive(Debug, Clone, Serialize, Deserialize)]
633#[serde(rename_all = "camelCase")]
634pub struct CompactMetadata {
635 pub trigger: String,
636 #[serde(skip_serializing_if = "Option::is_none")]
637 pub pre_tokens: Option<u64>,
638 #[serde(skip_serializing_if = "Option::is_none")]
639 pub post_tokens: Option<u64>,
640 #[serde(skip_serializing_if = "Option::is_none")]
641 pub duration_ms: Option<u64>,
642 #[serde(skip_serializing_if = "Option::is_none")]
643 pub preserved_segment: Option<PreservedSegment>,
644 #[serde(
645 rename = "preCompactDiscoveredTools",
646 skip_serializing_if = "Option::is_none"
647 )]
648 pub pre_compact_discovered_tools: Option<Vec<String>>,
649}
650
651#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct AttachmentEntry {
657 #[serde(flatten)]
658 pub envelope: Envelope,
659 pub attachment: AttachmentData,
660}
661
662#[derive(Debug, Clone, Serialize, Deserialize)]
663#[serde(tag = "type", rename_all = "snake_case")]
664pub enum AttachmentData {
665 HookSuccess(HookResultAttachment),
667 HookNonBlockingError(HookResultAttachment),
668 HookBlockingError(HookResultAttachment),
669 HookCancelled(HookResultAttachment),
670
671 HookAdditionalContext {
672 content: Vec<String>,
673 #[serde(rename = "hookName", skip_serializing_if = "Option::is_none")]
674 hook_name: Option<String>,
675 #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
676 tool_use_id: Option<String>,
677 #[serde(rename = "hookEvent", skip_serializing_if = "Option::is_none")]
678 hook_event: Option<String>,
679 },
680
681 HookPermissionDecision {
682 decision: String,
683 #[serde(rename = "hookName", skip_serializing_if = "Option::is_none")]
684 hook_name: Option<String>,
685 #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
686 tool_use_id: Option<String>,
687 #[serde(rename = "hookEvent", skip_serializing_if = "Option::is_none")]
688 hook_event: Option<String>,
689 },
690
691 File {
693 filename: String,
694 content: FileAttachmentContent,
695 #[serde(rename = "displayPath", skip_serializing_if = "Option::is_none")]
696 display_path: Option<String>,
697 },
698
699 EditedTextFile {
700 filename: String,
701 snippet: String,
703 },
704
705 Directory {
706 path: String,
707 content: String,
708 #[serde(rename = "displayPath")]
709 display_path: String,
710 },
711
712 CompactFileReference {
713 filename: String,
714 #[serde(rename = "displayPath")]
715 display_path: String,
716 },
717
718 CommandPermissions {
720 #[serde(rename = "allowedTools")]
721 allowed_tools: Vec<String>,
722 },
723
724 PlanMode {
726 #[serde(rename = "reminderType")]
727 reminder_type: String,
728 #[serde(rename = "isSubAgent")]
729 is_sub_agent: bool,
730 #[serde(rename = "planFilePath", skip_serializing_if = "Option::is_none")]
731 plan_file_path: Option<String>,
732 #[serde(rename = "planExists")]
733 plan_exists: bool,
734 },
735
736 PlanModeExit {
737 #[serde(rename = "planFilePath", skip_serializing_if = "Option::is_none")]
738 plan_file_path: Option<String>,
739 #[serde(rename = "planExists")]
740 plan_exists: bool,
741 },
742
743 SkillListing {
745 content: String,
746 #[serde(rename = "isInitial", skip_serializing_if = "Option::is_none")]
748 is_initial: Option<bool>,
749 #[serde(rename = "skillCount", skip_serializing_if = "Option::is_none")]
751 skill_count: Option<u32>,
752 },
753
754 DynamicSkill {
755 #[serde(rename = "skillDir")]
756 skill_dir: String,
757 #[serde(rename = "skillNames")]
758 skill_names: Vec<String>,
759 #[serde(rename = "displayPath")]
760 display_path: String,
761 },
762
763 InvokedSkills {
764 skills: Vec<InvokedSkill>,
765 },
766
767 TaskReminder {
769 content: Vec<Value>,
770 #[serde(rename = "itemCount")]
771 item_count: u32,
772 },
773
774 Diagnostics {
776 files: Vec<DiagnosticsFile>,
777 #[serde(rename = "isNew")]
778 is_new: bool,
779 },
780
781 DateChange {
783 #[serde(rename = "newDate")]
784 new_date: String,
785 },
786
787 DeferredToolsDelta {
789 #[serde(rename = "addedNames")]
790 added_names: Vec<String>,
791 #[serde(rename = "addedLines", skip_serializing_if = "Option::is_none")]
794 added_lines: Option<Vec<String>>,
795 #[serde(rename = "removedNames", skip_serializing_if = "Option::is_none")]
796 removed_names: Option<Vec<String>>,
797 },
798
799 McpInstructionsDelta {
800 #[serde(rename = "addedNames")]
801 added_names: Vec<String>,
802 #[serde(rename = "addedBlocks")]
803 added_blocks: Vec<String>,
804 #[serde(rename = "removedNames", skip_serializing_if = "Option::is_none")]
805 removed_names: Option<Vec<String>>,
806 },
807
808 UltrathinkEffort {
810 level: String,
811 },
812
813 QueuedCommand {
815 prompt: String,
816 #[serde(rename = "commandMode", skip_serializing_if = "Option::is_none")]
817 command_mode: Option<String>,
818 },
819
820 NestedMemory {
822 path: String,
823 content: NestedMemoryContent,
824 #[serde(rename = "displayPath")]
825 display_path: String,
826 },
827
828 #[serde(other)]
830 Unknown,
831}
832
833#[derive(Debug, Clone, Serialize, Deserialize)]
834pub struct NestedMemoryContent {
835 pub path: String,
836 #[serde(rename = "type")]
838 pub memory_type: String,
839 pub content: String,
840 #[serde(
841 rename = "contentDiffersFromDisk",
842 skip_serializing_if = "Option::is_none"
843 )]
844 pub content_differs_from_disk: Option<bool>,
845}
846
847#[derive(Debug, Clone, Serialize, Deserialize)]
848#[serde(rename_all = "camelCase")]
849pub struct HookResultAttachment {
850 #[serde(rename = "hookName", skip_serializing_if = "Option::is_none")]
851 pub hook_name: Option<String>,
852 #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
853 pub tool_use_id: Option<String>,
854 #[serde(rename = "hookEvent", skip_serializing_if = "Option::is_none")]
855 pub hook_event: Option<String>,
856 #[serde(skip_serializing_if = "Option::is_none")]
857 pub content: Option<String>,
858 #[serde(skip_serializing_if = "Option::is_none")]
859 pub stdout: Option<String>,
860 #[serde(skip_serializing_if = "Option::is_none")]
861 pub stderr: Option<String>,
862 #[serde(skip_serializing_if = "Option::is_none")]
863 pub exit_code: Option<i32>,
864 #[serde(skip_serializing_if = "Option::is_none")]
865 pub command: Option<String>,
866 #[serde(skip_serializing_if = "Option::is_none")]
867 pub duration_ms: Option<u64>,
868 #[serde(rename = "blockingError", skip_serializing_if = "Option::is_none")]
869 pub blocking_error: Option<Value>,
870}
871
872#[derive(Debug, Clone, Serialize, Deserialize)]
874#[serde(rename_all = "camelCase")]
875pub struct FileAttachmentContent {
876 #[serde(rename = "type")]
877 pub content_type: String,
878 pub file: FileData,
879}
880
881#[derive(Debug, Clone, Serialize, Deserialize)]
882#[serde(rename_all = "camelCase")]
883pub struct FileData {
884 pub file_path: String,
885 #[serde(skip_serializing_if = "Option::is_none")]
886 pub content: Option<String>,
887 #[serde(rename = "numLines", skip_serializing_if = "Option::is_none")]
888 pub num_lines: Option<u64>,
889 #[serde(rename = "startLine", skip_serializing_if = "Option::is_none")]
890 pub start_line: Option<u64>,
891 #[serde(rename = "totalLines", skip_serializing_if = "Option::is_none")]
892 pub total_lines: Option<u64>,
893}
894
895#[derive(Debug, Clone, Serialize, Deserialize)]
896pub struct InvokedSkill {
897 pub name: String,
898 pub path: String,
899 pub content: String,
900}
901
902#[derive(Debug, Clone, Serialize, Deserialize)]
903pub struct DiagnosticsFile {
904 pub uri: String,
905 pub diagnostics: Vec<Diagnostic>,
906}
907
908#[derive(Debug, Clone, Serialize, Deserialize)]
909pub struct Diagnostic {
910 pub message: String,
911 pub severity: String,
912 pub range: DiagnosticRange,
913 #[serde(skip_serializing_if = "Option::is_none")]
914 pub source: Option<String>,
915 #[serde(skip_serializing_if = "Option::is_none")]
916 pub code: Option<Value>,
917}
918
919#[derive(Debug, Clone, Serialize, Deserialize)]
920pub struct DiagnosticRange {
921 pub start: DiagnosticPosition,
922 pub end: DiagnosticPosition,
923}
924
925#[derive(Debug, Clone, Serialize, Deserialize)]
926pub struct DiagnosticPosition {
927 pub line: u32,
928 pub character: u32,
929}
930
931#[derive(Debug, Clone, Serialize, Deserialize)]
936#[serde(rename_all = "camelCase")]
937pub struct ProgressEntry {
938 #[serde(flatten)]
939 pub envelope: Envelope,
940
941 pub data: ProgressData,
942
943 #[serde(rename = "parentToolUseID", skip_serializing_if = "Option::is_none")]
944 pub parent_tool_use_id: Option<String>,
945
946 #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
947 pub tool_use_id: Option<String>,
948}
949
950#[derive(Debug, Clone, Serialize, Deserialize)]
951#[serde(rename_all = "camelCase")]
952pub struct ProgressData {
953 #[serde(rename = "type")]
954 pub data_type: String,
955 #[serde(rename = "hookEvent", skip_serializing_if = "Option::is_none")]
956 pub hook_event: Option<String>,
957 #[serde(rename = "hookName", skip_serializing_if = "Option::is_none")]
958 pub hook_name: Option<String>,
959 #[serde(skip_serializing_if = "Option::is_none")]
960 pub command: Option<String>,
961 #[serde(rename = "agentId", skip_serializing_if = "Option::is_none")]
963 pub agent_id: Option<String>,
964 #[serde(skip_serializing_if = "Option::is_none")]
965 pub prompt: Option<String>,
966 #[serde(skip_serializing_if = "Option::is_none")]
967 pub message: Option<Value>,
968 #[serde(skip_serializing_if = "Option::is_none")]
970 pub query: Option<String>,
971 #[serde(rename = "resultCount", skip_serializing_if = "Option::is_none")]
972 pub result_count: Option<u32>,
973 #[serde(rename = "elapsedTimeSeconds", skip_serializing_if = "Option::is_none")]
975 pub elapsed_time_seconds: Option<f64>,
976 #[serde(rename = "fullOutput", skip_serializing_if = "Option::is_none")]
977 pub full_output: Option<String>,
978 #[serde(rename = "output", skip_serializing_if = "Option::is_none")]
979 pub output: Option<String>,
980 #[serde(rename = "timeoutMs", skip_serializing_if = "Option::is_none")]
981 pub timeout_ms: Option<u64>,
982 #[serde(rename = "totalLines", skip_serializing_if = "Option::is_none")]
983 pub total_lines: Option<u64>,
984 #[serde(rename = "totalBytes", skip_serializing_if = "Option::is_none")]
985 pub total_bytes: Option<u64>,
986 #[serde(rename = "taskId", skip_serializing_if = "Option::is_none")]
987 pub task_id: Option<String>,
988 #[serde(rename = "serverName", skip_serializing_if = "Option::is_none")]
990 pub server_name: Option<String>,
991 #[serde(rename = "status", skip_serializing_if = "Option::is_none")]
992 pub status: Option<String>,
993 #[serde(rename = "toolName", skip_serializing_if = "Option::is_none")]
994 pub tool_name: Option<String>,
995 #[serde(rename = "elapsedTimeMs", skip_serializing_if = "Option::is_none")]
996 pub elapsed_time_ms: Option<f64>,
997 #[serde(rename = "taskDescription", skip_serializing_if = "Option::is_none")]
999 pub task_description: Option<String>,
1000 #[serde(rename = "taskType", skip_serializing_if = "Option::is_none")]
1001 pub task_type: Option<String>,
1002}
1003
1004#[derive(Debug, Clone, Serialize, Deserialize)]
1009#[serde(rename_all = "camelCase")]
1010pub struct PermissionModeEntry {
1011 pub permission_mode: String,
1012 pub session_id: String,
1013}
1014
1015#[derive(Debug, Clone, Serialize, Deserialize)]
1016#[serde(rename_all = "camelCase")]
1017pub struct LastPromptEntry {
1018 pub last_prompt: String,
1019 pub session_id: String,
1020}
1021
1022#[derive(Debug, Clone, Serialize, Deserialize)]
1023#[serde(rename_all = "camelCase")]
1024pub struct AiTitleEntry {
1025 pub ai_title: String,
1026 pub session_id: String,
1027}
1028
1029#[derive(Debug, Clone, Serialize, Deserialize)]
1030#[serde(rename_all = "camelCase")]
1031pub struct CustomTitleEntry {
1032 pub custom_title: String,
1033 pub session_id: String,
1034}
1035
1036#[derive(Debug, Clone, Serialize, Deserialize)]
1037#[serde(rename_all = "camelCase")]
1038pub struct AgentNameEntry {
1039 pub agent_name: String,
1040 pub session_id: String,
1041}
1042
1043#[derive(Debug, Clone, Serialize, Deserialize)]
1044#[serde(rename_all = "camelCase")]
1045pub struct AgentColorEntry {
1046 pub agent_color: String,
1047 pub session_id: String,
1048}
1049
1050#[derive(Debug, Clone, Serialize, Deserialize)]
1051#[serde(rename_all = "camelCase")]
1052pub struct AgentSettingEntry {
1053 pub agent_setting: String,
1054 pub session_id: String,
1055}
1056
1057#[derive(Debug, Clone, Serialize, Deserialize)]
1058#[serde(rename_all = "camelCase")]
1059pub struct TagEntry {
1060 pub tag: String,
1061 pub session_id: String,
1062}
1063
1064#[derive(Debug, Clone, Serialize, Deserialize)]
1065#[serde(rename_all = "camelCase")]
1066pub struct SummaryEntry {
1067 pub leaf_uuid: String,
1068 pub summary: String,
1069 pub session_id: String,
1070}
1071
1072#[derive(Debug, Clone, Serialize, Deserialize)]
1073#[serde(rename_all = "camelCase")]
1074pub struct TaskSummaryEntry {
1075 pub summary: String,
1076 pub session_id: String,
1077 pub timestamp: String,
1078}
1079
1080#[derive(Debug, Clone, Serialize, Deserialize)]
1081#[serde(rename_all = "camelCase")]
1082pub struct PrLinkEntry {
1083 pub session_id: String,
1084 pub pr_number: u32,
1085 pub pr_url: String,
1086 pub pr_repository: String,
1087 pub timestamp: String,
1088}
1089
1090#[derive(Debug, Clone, Serialize, Deserialize)]
1091#[serde(rename_all = "camelCase")]
1092pub struct ModeEntry {
1093 pub mode: SessionMode,
1094 pub session_id: String,
1095}
1096
1097#[derive(Debug, Clone, Serialize, Deserialize)]
1098#[serde(rename_all = "lowercase")]
1099pub enum SessionMode {
1100 Coordinator,
1101 Normal,
1102 #[serde(other)]
1103 Unknown,
1104}
1105
1106#[derive(Debug, Clone, Serialize, Deserialize)]
1108#[serde(rename_all = "camelCase")]
1109pub struct WorktreeStateEntry {
1110 pub session_id: String,
1111 pub worktree_session: Option<PersistedWorktreeSession>,
1113}
1114
1115#[derive(Debug, Clone, Serialize, Deserialize)]
1116#[serde(rename_all = "camelCase")]
1117pub struct PersistedWorktreeSession {
1118 pub original_cwd: String,
1119 pub worktree_path: String,
1120 pub worktree_name: String,
1121 pub session_id: String,
1122
1123 #[serde(skip_serializing_if = "Option::is_none")]
1124 pub worktree_branch: Option<String>,
1125
1126 #[serde(skip_serializing_if = "Option::is_none")]
1127 pub original_branch: Option<String>,
1128
1129 #[serde(skip_serializing_if = "Option::is_none")]
1130 pub original_head_commit: Option<String>,
1131
1132 #[serde(rename = "tmuxSessionName", skip_serializing_if = "Option::is_none")]
1133 pub tmux_session_name: Option<String>,
1134
1135 #[serde(skip_serializing_if = "Option::is_none")]
1136 pub hook_based: Option<bool>,
1137}
1138
1139#[derive(Debug, Clone, Serialize, Deserialize)]
1140#[serde(rename_all = "camelCase")]
1141pub struct ContentReplacementEntry {
1142 pub session_id: String,
1143 pub replacements: Vec<Value>,
1144 #[serde(skip_serializing_if = "Option::is_none")]
1145 pub agent_id: Option<String>,
1146}
1147
1148#[derive(Debug, Clone, Serialize, Deserialize)]
1149#[serde(rename_all = "camelCase")]
1150pub struct FileHistorySnapshotEntry {
1151 pub message_id: String,
1152 pub snapshot: FileHistorySnapshot,
1153 pub is_snapshot_update: bool,
1154}
1155
1156#[derive(Debug, Clone, Serialize, Deserialize)]
1157#[serde(rename_all = "camelCase")]
1158pub struct FileHistorySnapshot {
1159 pub message_id: String,
1160 pub tracked_file_backups: Value,
1161 pub timestamp: String,
1162}
1163
1164#[derive(Debug, Clone, Serialize, Deserialize)]
1165#[serde(rename_all = "camelCase")]
1166pub struct AttributionSnapshotEntry {
1167 pub message_id: String,
1168 pub surface: String,
1169 pub file_states: Value,
1170
1171 #[serde(skip_serializing_if = "Option::is_none")]
1172 pub prompt_count: Option<u32>,
1173
1174 #[serde(skip_serializing_if = "Option::is_none")]
1175 pub prompt_count_at_last_commit: Option<u32>,
1176
1177 #[serde(skip_serializing_if = "Option::is_none")]
1178 pub permission_prompt_count: Option<u32>,
1179
1180 #[serde(skip_serializing_if = "Option::is_none")]
1181 pub permission_prompt_count_at_last_commit: Option<u32>,
1182
1183 #[serde(skip_serializing_if = "Option::is_none")]
1184 pub escape_count: Option<u32>,
1185
1186 #[serde(skip_serializing_if = "Option::is_none")]
1187 pub escape_count_at_last_commit: Option<u32>,
1188}
1189
1190#[derive(Debug, Clone, Serialize, Deserialize)]
1191#[serde(rename_all = "camelCase")]
1192pub struct QueueOperationEntry {
1193 pub operation: String,
1194 pub timestamp: String,
1195 pub session_id: String,
1196 #[serde(skip_serializing_if = "Option::is_none")]
1197 pub content: Option<String>,
1198}
1199
1200#[derive(Debug, Clone, Serialize, Deserialize)]
1205#[serde(rename_all = "camelCase")]
1206pub struct ContextCollapseCommitEntry {
1207 pub session_id: String,
1208 pub collapse_id: String,
1209 pub summary_uuid: String,
1210 pub summary_content: String,
1211 pub summary: String,
1212 pub first_archived_uuid: String,
1213 pub last_archived_uuid: String,
1214}
1215
1216#[derive(Debug, Clone, Serialize, Deserialize)]
1217#[serde(rename_all = "camelCase")]
1218pub struct ContextCollapseSnapshotEntry {
1219 pub session_id: String,
1220 pub staged: Vec<StagedSpan>,
1221 pub armed: bool,
1222 pub last_spawn_tokens: u64,
1223}
1224
1225#[derive(Debug, Clone, Serialize, Deserialize)]
1226#[serde(rename_all = "camelCase")]
1227pub struct StagedSpan {
1228 pub start_uuid: String,
1229 pub end_uuid: String,
1230 pub summary: String,
1231 pub risk: f64,
1232 pub staged_at: u64,
1233}
1234
1235#[derive(Debug, Clone, Serialize, Deserialize)]
1236#[serde(rename_all = "camelCase")]
1237pub struct SpeculationAcceptEntry {
1238 pub timestamp: String,
1239 pub time_saved_ms: u64,
1240}
1241
1242mod opt_nullable {
1255 use serde::{Deserialize, Deserializer, Serialize, Serializer};
1256 use serde_json::Value;
1257
1258 pub fn serialize<S>(val: &Option<Option<Value>>, ser: S) -> Result<S::Ok, S::Error>
1259 where
1260 S: Serializer,
1261 {
1262 match val {
1263 None => unreachable!("skip_serializing_if = \"Option::is_none\" should prevent this"),
1264 Some(inner) => inner.serialize(ser),
1265 }
1266 }
1267
1268 pub fn deserialize<'de, D>(de: D) -> Result<Option<Option<Value>>, D::Error>
1269 where
1270 D: Deserializer<'de>,
1271 {
1272 Ok(Some(Option::<Value>::deserialize(de)?))
1273 }
1274}
1275
1276#[cfg(test)]
1277mod tests {
1278 use super::*;
1279
1280 #[test]
1281 fn attachment_data_unknown_variant() {
1282 let json = r#"{"type":"future_attachment_shape","some_field":42}"#;
1283 let v: AttachmentData = serde_json::from_str(json).unwrap();
1284 assert!(matches!(v, AttachmentData::Unknown));
1285 }
1286
1287 #[test]
1288 fn attachment_data_nested_memory_variant() {
1289 let json = r#"{"type":"nested_memory","path":"/p/CLAUDE.md","content":{"path":"/p/CLAUDE.md","type":"Project","content":"hi","contentDiffersFromDisk":false},"displayPath":"CLAUDE.md"}"#;
1290 let v: AttachmentData = serde_json::from_str(json).unwrap();
1291 match v {
1292 AttachmentData::NestedMemory {
1293 path,
1294 content,
1295 display_path,
1296 } => {
1297 assert_eq!(path, "/p/CLAUDE.md");
1298 assert_eq!(content.memory_type, "Project");
1299 assert_eq!(content.content, "hi");
1300 assert_eq!(content.content_differs_from_disk, Some(false));
1301 assert_eq!(display_path, "CLAUDE.md");
1302 }
1303 other => panic!("expected NestedMemory, got {other:?}"),
1304 }
1305 }
1306
1307 #[test]
1308 fn assistant_content_block_unknown_variant() {
1309 let json = r#"{"type":"future_modality","data":"foo"}"#;
1310 let v: AssistantContentBlock = serde_json::from_str(json).unwrap();
1311 assert!(matches!(v, AssistantContentBlock::Unknown));
1312 }
1313
1314 #[test]
1315 fn user_content_block_unknown_variant() {
1316 let json = r#"{"type":"video","url":"https://example.com"}"#;
1317 let v: UserContentBlock = serde_json::from_str(json).unwrap();
1318 assert!(matches!(v, UserContentBlock::Unknown));
1319 }
1320
1321 #[test]
1322 fn image_source_unknown_variant() {
1323 let json = r#"{"type":"s3_bucket","key":"foo"}"#;
1324 let v: ImageSource = serde_json::from_str(json).unwrap();
1325 assert!(matches!(v, ImageSource::Unknown));
1326 }
1327
1328 #[test]
1329 fn document_source_unknown_variant() {
1330 let json = r#"{"type":"pdf","data":"base64data"}"#;
1331 let v: DocumentSource = serde_json::from_str(json).unwrap();
1332 assert!(matches!(v, DocumentSource::Unknown));
1333 }
1334
1335 #[test]
1337 fn attachment_data_known_variant_unaffected() {
1338 let json = r#"{"type":"date_change","newDate":"2024-01-01"}"#;
1339 let v: AttachmentData = serde_json::from_str(json).unwrap();
1340 assert!(matches!(v, AttachmentData::DateChange { .. }));
1341 }
1342
1343 #[test]
1344 fn assistant_content_block_known_variant_unaffected() {
1345 let json = r#"{"type":"text","text":"hello"}"#;
1346 let v: AssistantContentBlock = serde_json::from_str(json).unwrap();
1347 assert!(matches!(v, AssistantContentBlock::Text { .. }));
1348 }
1349
1350 #[test]
1355 fn user_content_unknown_shape_does_not_fail() {
1356 let json = r#"{"type":"future_format","data":42}"#;
1357 let v: UserContent = serde_json::from_str(json).unwrap();
1358 assert!(matches!(v, UserContent::Other(_)));
1359 }
1360
1361 #[test]
1364 fn server_tool_use_missing_field_uses_default() {
1365 let json = r#"{"web_search_requests":3}"#;
1366 let v: ServerToolUse = serde_json::from_str(json).unwrap();
1367 assert_eq!(v.web_search_requests, 3);
1368 assert_eq!(v.web_fetch_requests, 0);
1369 }
1370
1371 #[test]
1373 fn user_role_unknown_value_does_not_fail() {
1374 let json = r#""operator""#;
1375 let v: UserRole = serde_json::from_str(json).unwrap();
1376 assert!(matches!(v, UserRole::Unknown));
1377 }
1378
1379 #[test]
1381 fn assistant_role_unknown_value_does_not_fail() {
1382 let json = r#""system_agent""#;
1383 let v: AssistantRole = serde_json::from_str(json).unwrap();
1384 assert!(matches!(v, AssistantRole::Unknown));
1385 }
1386
1387 #[test]
1389 fn session_mode_unknown_value_does_not_fail() {
1390 let json = r#""background""#;
1391 let v: SessionMode = serde_json::from_str(json).unwrap();
1392 assert!(matches!(v, SessionMode::Unknown));
1393 }
1394
1395 #[test]
1398 fn assistant_message_missing_model_uses_default() {
1399 let json = r#"{
1400 "id": "msg_err1",
1401 "type": "message",
1402 "role": "assistant",
1403 "content": [],
1404 "stop_reason": "error",
1405 "stop_sequence": null,
1406 "usage": {"input_tokens": 0, "output_tokens": 0}
1407 }"#;
1408 let v: AssistantMessage = serde_json::from_str(json).unwrap();
1409 assert!(v.model.is_none());
1410 }
1411}