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 #[serde(other)]
822 Unknown,
823}
824
825#[derive(Debug, Clone, Serialize, Deserialize)]
826#[serde(rename_all = "camelCase")]
827pub struct HookResultAttachment {
828 #[serde(rename = "hookName", skip_serializing_if = "Option::is_none")]
829 pub hook_name: Option<String>,
830 #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
831 pub tool_use_id: Option<String>,
832 #[serde(rename = "hookEvent", skip_serializing_if = "Option::is_none")]
833 pub hook_event: Option<String>,
834 #[serde(skip_serializing_if = "Option::is_none")]
835 pub content: Option<String>,
836 #[serde(skip_serializing_if = "Option::is_none")]
837 pub stdout: Option<String>,
838 #[serde(skip_serializing_if = "Option::is_none")]
839 pub stderr: Option<String>,
840 #[serde(skip_serializing_if = "Option::is_none")]
841 pub exit_code: Option<i32>,
842 #[serde(skip_serializing_if = "Option::is_none")]
843 pub command: Option<String>,
844 #[serde(skip_serializing_if = "Option::is_none")]
845 pub duration_ms: Option<u64>,
846 #[serde(rename = "blockingError", skip_serializing_if = "Option::is_none")]
847 pub blocking_error: Option<Value>,
848}
849
850#[derive(Debug, Clone, Serialize, Deserialize)]
852#[serde(rename_all = "camelCase")]
853pub struct FileAttachmentContent {
854 #[serde(rename = "type")]
855 pub content_type: String,
856 pub file: FileData,
857}
858
859#[derive(Debug, Clone, Serialize, Deserialize)]
860#[serde(rename_all = "camelCase")]
861pub struct FileData {
862 pub file_path: String,
863 #[serde(skip_serializing_if = "Option::is_none")]
864 pub content: Option<String>,
865 #[serde(rename = "numLines", skip_serializing_if = "Option::is_none")]
866 pub num_lines: Option<u64>,
867 #[serde(rename = "startLine", skip_serializing_if = "Option::is_none")]
868 pub start_line: Option<u64>,
869 #[serde(rename = "totalLines", skip_serializing_if = "Option::is_none")]
870 pub total_lines: Option<u64>,
871}
872
873#[derive(Debug, Clone, Serialize, Deserialize)]
874pub struct InvokedSkill {
875 pub name: String,
876 pub path: String,
877 pub content: String,
878}
879
880#[derive(Debug, Clone, Serialize, Deserialize)]
881pub struct DiagnosticsFile {
882 pub uri: String,
883 pub diagnostics: Vec<Diagnostic>,
884}
885
886#[derive(Debug, Clone, Serialize, Deserialize)]
887pub struct Diagnostic {
888 pub message: String,
889 pub severity: String,
890 pub range: DiagnosticRange,
891 #[serde(skip_serializing_if = "Option::is_none")]
892 pub source: Option<String>,
893 #[serde(skip_serializing_if = "Option::is_none")]
894 pub code: Option<Value>,
895}
896
897#[derive(Debug, Clone, Serialize, Deserialize)]
898pub struct DiagnosticRange {
899 pub start: DiagnosticPosition,
900 pub end: DiagnosticPosition,
901}
902
903#[derive(Debug, Clone, Serialize, Deserialize)]
904pub struct DiagnosticPosition {
905 pub line: u32,
906 pub character: u32,
907}
908
909#[derive(Debug, Clone, Serialize, Deserialize)]
914#[serde(rename_all = "camelCase")]
915pub struct ProgressEntry {
916 #[serde(flatten)]
917 pub envelope: Envelope,
918
919 pub data: ProgressData,
920
921 #[serde(rename = "parentToolUseID", skip_serializing_if = "Option::is_none")]
922 pub parent_tool_use_id: Option<String>,
923
924 #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
925 pub tool_use_id: Option<String>,
926}
927
928#[derive(Debug, Clone, Serialize, Deserialize)]
929#[serde(rename_all = "camelCase")]
930pub struct ProgressData {
931 #[serde(rename = "type")]
932 pub data_type: String,
933 #[serde(rename = "hookEvent", skip_serializing_if = "Option::is_none")]
934 pub hook_event: Option<String>,
935 #[serde(rename = "hookName", skip_serializing_if = "Option::is_none")]
936 pub hook_name: Option<String>,
937 #[serde(skip_serializing_if = "Option::is_none")]
938 pub command: Option<String>,
939 #[serde(rename = "agentId", skip_serializing_if = "Option::is_none")]
941 pub agent_id: Option<String>,
942 #[serde(skip_serializing_if = "Option::is_none")]
943 pub prompt: Option<String>,
944 #[serde(skip_serializing_if = "Option::is_none")]
945 pub message: Option<Value>,
946 #[serde(skip_serializing_if = "Option::is_none")]
948 pub query: Option<String>,
949 #[serde(rename = "resultCount", skip_serializing_if = "Option::is_none")]
950 pub result_count: Option<u32>,
951 #[serde(rename = "elapsedTimeSeconds", skip_serializing_if = "Option::is_none")]
953 pub elapsed_time_seconds: Option<f64>,
954 #[serde(rename = "fullOutput", skip_serializing_if = "Option::is_none")]
955 pub full_output: Option<String>,
956 #[serde(rename = "output", skip_serializing_if = "Option::is_none")]
957 pub output: Option<String>,
958 #[serde(rename = "timeoutMs", skip_serializing_if = "Option::is_none")]
959 pub timeout_ms: Option<u64>,
960 #[serde(rename = "totalLines", skip_serializing_if = "Option::is_none")]
961 pub total_lines: Option<u64>,
962 #[serde(rename = "totalBytes", skip_serializing_if = "Option::is_none")]
963 pub total_bytes: Option<u64>,
964 #[serde(rename = "taskId", skip_serializing_if = "Option::is_none")]
965 pub task_id: Option<String>,
966 #[serde(rename = "serverName", skip_serializing_if = "Option::is_none")]
968 pub server_name: Option<String>,
969 #[serde(rename = "status", skip_serializing_if = "Option::is_none")]
970 pub status: Option<String>,
971 #[serde(rename = "toolName", skip_serializing_if = "Option::is_none")]
972 pub tool_name: Option<String>,
973 #[serde(rename = "elapsedTimeMs", skip_serializing_if = "Option::is_none")]
974 pub elapsed_time_ms: Option<f64>,
975 #[serde(rename = "taskDescription", skip_serializing_if = "Option::is_none")]
977 pub task_description: Option<String>,
978 #[serde(rename = "taskType", skip_serializing_if = "Option::is_none")]
979 pub task_type: Option<String>,
980}
981
982#[derive(Debug, Clone, Serialize, Deserialize)]
987#[serde(rename_all = "camelCase")]
988pub struct PermissionModeEntry {
989 pub permission_mode: String,
990 pub session_id: String,
991}
992
993#[derive(Debug, Clone, Serialize, Deserialize)]
994#[serde(rename_all = "camelCase")]
995pub struct LastPromptEntry {
996 pub last_prompt: String,
997 pub session_id: String,
998}
999
1000#[derive(Debug, Clone, Serialize, Deserialize)]
1001#[serde(rename_all = "camelCase")]
1002pub struct AiTitleEntry {
1003 pub ai_title: String,
1004 pub session_id: String,
1005}
1006
1007#[derive(Debug, Clone, Serialize, Deserialize)]
1008#[serde(rename_all = "camelCase")]
1009pub struct CustomTitleEntry {
1010 pub custom_title: String,
1011 pub session_id: String,
1012}
1013
1014#[derive(Debug, Clone, Serialize, Deserialize)]
1015#[serde(rename_all = "camelCase")]
1016pub struct AgentNameEntry {
1017 pub agent_name: String,
1018 pub session_id: String,
1019}
1020
1021#[derive(Debug, Clone, Serialize, Deserialize)]
1022#[serde(rename_all = "camelCase")]
1023pub struct AgentColorEntry {
1024 pub agent_color: String,
1025 pub session_id: String,
1026}
1027
1028#[derive(Debug, Clone, Serialize, Deserialize)]
1029#[serde(rename_all = "camelCase")]
1030pub struct AgentSettingEntry {
1031 pub agent_setting: String,
1032 pub session_id: String,
1033}
1034
1035#[derive(Debug, Clone, Serialize, Deserialize)]
1036#[serde(rename_all = "camelCase")]
1037pub struct TagEntry {
1038 pub tag: String,
1039 pub session_id: String,
1040}
1041
1042#[derive(Debug, Clone, Serialize, Deserialize)]
1043#[serde(rename_all = "camelCase")]
1044pub struct SummaryEntry {
1045 pub leaf_uuid: String,
1046 pub summary: String,
1047 pub session_id: String,
1048}
1049
1050#[derive(Debug, Clone, Serialize, Deserialize)]
1051#[serde(rename_all = "camelCase")]
1052pub struct TaskSummaryEntry {
1053 pub summary: String,
1054 pub session_id: String,
1055 pub timestamp: String,
1056}
1057
1058#[derive(Debug, Clone, Serialize, Deserialize)]
1059#[serde(rename_all = "camelCase")]
1060pub struct PrLinkEntry {
1061 pub session_id: String,
1062 pub pr_number: u32,
1063 pub pr_url: String,
1064 pub pr_repository: String,
1065 pub timestamp: String,
1066}
1067
1068#[derive(Debug, Clone, Serialize, Deserialize)]
1069#[serde(rename_all = "camelCase")]
1070pub struct ModeEntry {
1071 pub mode: SessionMode,
1072 pub session_id: String,
1073}
1074
1075#[derive(Debug, Clone, Serialize, Deserialize)]
1076#[serde(rename_all = "lowercase")]
1077pub enum SessionMode {
1078 Coordinator,
1079 Normal,
1080 #[serde(other)]
1081 Unknown,
1082}
1083
1084#[derive(Debug, Clone, Serialize, Deserialize)]
1086#[serde(rename_all = "camelCase")]
1087pub struct WorktreeStateEntry {
1088 pub session_id: String,
1089 pub worktree_session: Option<PersistedWorktreeSession>,
1091}
1092
1093#[derive(Debug, Clone, Serialize, Deserialize)]
1094#[serde(rename_all = "camelCase")]
1095pub struct PersistedWorktreeSession {
1096 pub original_cwd: String,
1097 pub worktree_path: String,
1098 pub worktree_name: String,
1099 pub session_id: String,
1100
1101 #[serde(skip_serializing_if = "Option::is_none")]
1102 pub worktree_branch: Option<String>,
1103
1104 #[serde(skip_serializing_if = "Option::is_none")]
1105 pub original_branch: Option<String>,
1106
1107 #[serde(skip_serializing_if = "Option::is_none")]
1108 pub original_head_commit: Option<String>,
1109
1110 #[serde(rename = "tmuxSessionName", skip_serializing_if = "Option::is_none")]
1111 pub tmux_session_name: Option<String>,
1112
1113 #[serde(skip_serializing_if = "Option::is_none")]
1114 pub hook_based: Option<bool>,
1115}
1116
1117#[derive(Debug, Clone, Serialize, Deserialize)]
1118#[serde(rename_all = "camelCase")]
1119pub struct ContentReplacementEntry {
1120 pub session_id: String,
1121 pub replacements: Vec<Value>,
1122 #[serde(skip_serializing_if = "Option::is_none")]
1123 pub agent_id: Option<String>,
1124}
1125
1126#[derive(Debug, Clone, Serialize, Deserialize)]
1127#[serde(rename_all = "camelCase")]
1128pub struct FileHistorySnapshotEntry {
1129 pub message_id: String,
1130 pub snapshot: FileHistorySnapshot,
1131 pub is_snapshot_update: bool,
1132}
1133
1134#[derive(Debug, Clone, Serialize, Deserialize)]
1135#[serde(rename_all = "camelCase")]
1136pub struct FileHistorySnapshot {
1137 pub message_id: String,
1138 pub tracked_file_backups: Value,
1139 pub timestamp: String,
1140}
1141
1142#[derive(Debug, Clone, Serialize, Deserialize)]
1143#[serde(rename_all = "camelCase")]
1144pub struct AttributionSnapshotEntry {
1145 pub message_id: String,
1146 pub surface: String,
1147 pub file_states: Value,
1148
1149 #[serde(skip_serializing_if = "Option::is_none")]
1150 pub prompt_count: Option<u32>,
1151
1152 #[serde(skip_serializing_if = "Option::is_none")]
1153 pub prompt_count_at_last_commit: Option<u32>,
1154
1155 #[serde(skip_serializing_if = "Option::is_none")]
1156 pub permission_prompt_count: Option<u32>,
1157
1158 #[serde(skip_serializing_if = "Option::is_none")]
1159 pub permission_prompt_count_at_last_commit: Option<u32>,
1160
1161 #[serde(skip_serializing_if = "Option::is_none")]
1162 pub escape_count: Option<u32>,
1163
1164 #[serde(skip_serializing_if = "Option::is_none")]
1165 pub escape_count_at_last_commit: Option<u32>,
1166}
1167
1168#[derive(Debug, Clone, Serialize, Deserialize)]
1169#[serde(rename_all = "camelCase")]
1170pub struct QueueOperationEntry {
1171 pub operation: String,
1172 pub timestamp: String,
1173 pub session_id: String,
1174 #[serde(skip_serializing_if = "Option::is_none")]
1175 pub content: Option<String>,
1176}
1177
1178#[derive(Debug, Clone, Serialize, Deserialize)]
1183#[serde(rename_all = "camelCase")]
1184pub struct ContextCollapseCommitEntry {
1185 pub session_id: String,
1186 pub collapse_id: String,
1187 pub summary_uuid: String,
1188 pub summary_content: String,
1189 pub summary: String,
1190 pub first_archived_uuid: String,
1191 pub last_archived_uuid: String,
1192}
1193
1194#[derive(Debug, Clone, Serialize, Deserialize)]
1195#[serde(rename_all = "camelCase")]
1196pub struct ContextCollapseSnapshotEntry {
1197 pub session_id: String,
1198 pub staged: Vec<StagedSpan>,
1199 pub armed: bool,
1200 pub last_spawn_tokens: u64,
1201}
1202
1203#[derive(Debug, Clone, Serialize, Deserialize)]
1204#[serde(rename_all = "camelCase")]
1205pub struct StagedSpan {
1206 pub start_uuid: String,
1207 pub end_uuid: String,
1208 pub summary: String,
1209 pub risk: f64,
1210 pub staged_at: u64,
1211}
1212
1213#[derive(Debug, Clone, Serialize, Deserialize)]
1214#[serde(rename_all = "camelCase")]
1215pub struct SpeculationAcceptEntry {
1216 pub timestamp: String,
1217 pub time_saved_ms: u64,
1218}
1219
1220mod opt_nullable {
1233 use serde::{Deserialize, Deserializer, Serialize, Serializer};
1234 use serde_json::Value;
1235
1236 pub fn serialize<S>(val: &Option<Option<Value>>, ser: S) -> Result<S::Ok, S::Error>
1237 where
1238 S: Serializer,
1239 {
1240 match val {
1241 None => unreachable!("skip_serializing_if = \"Option::is_none\" should prevent this"),
1242 Some(inner) => inner.serialize(ser),
1243 }
1244 }
1245
1246 pub fn deserialize<'de, D>(de: D) -> Result<Option<Option<Value>>, D::Error>
1247 where
1248 D: Deserializer<'de>,
1249 {
1250 Ok(Some(Option::<Value>::deserialize(de)?))
1251 }
1252}
1253
1254#[cfg(test)]
1255mod tests {
1256 use super::*;
1257
1258 #[test]
1259 fn attachment_data_unknown_variant() {
1260 let json = r#"{"type":"nested_memory","some_field":42}"#;
1261 let v: AttachmentData = serde_json::from_str(json).unwrap();
1262 assert!(matches!(v, AttachmentData::Unknown));
1263 }
1264
1265 #[test]
1266 fn assistant_content_block_unknown_variant() {
1267 let json = r#"{"type":"future_modality","data":"foo"}"#;
1268 let v: AssistantContentBlock = serde_json::from_str(json).unwrap();
1269 assert!(matches!(v, AssistantContentBlock::Unknown));
1270 }
1271
1272 #[test]
1273 fn user_content_block_unknown_variant() {
1274 let json = r#"{"type":"video","url":"https://example.com"}"#;
1275 let v: UserContentBlock = serde_json::from_str(json).unwrap();
1276 assert!(matches!(v, UserContentBlock::Unknown));
1277 }
1278
1279 #[test]
1280 fn image_source_unknown_variant() {
1281 let json = r#"{"type":"s3_bucket","key":"foo"}"#;
1282 let v: ImageSource = serde_json::from_str(json).unwrap();
1283 assert!(matches!(v, ImageSource::Unknown));
1284 }
1285
1286 #[test]
1287 fn document_source_unknown_variant() {
1288 let json = r#"{"type":"pdf","data":"base64data"}"#;
1289 let v: DocumentSource = serde_json::from_str(json).unwrap();
1290 assert!(matches!(v, DocumentSource::Unknown));
1291 }
1292
1293 #[test]
1295 fn attachment_data_known_variant_unaffected() {
1296 let json = r#"{"type":"date_change","newDate":"2024-01-01"}"#;
1297 let v: AttachmentData = serde_json::from_str(json).unwrap();
1298 assert!(matches!(v, AttachmentData::DateChange { .. }));
1299 }
1300
1301 #[test]
1302 fn assistant_content_block_known_variant_unaffected() {
1303 let json = r#"{"type":"text","text":"hello"}"#;
1304 let v: AssistantContentBlock = serde_json::from_str(json).unwrap();
1305 assert!(matches!(v, AssistantContentBlock::Text { .. }));
1306 }
1307
1308 #[test]
1313 fn user_content_unknown_shape_does_not_fail() {
1314 let json = r#"{"type":"future_format","data":42}"#;
1315 let v: UserContent = serde_json::from_str(json).unwrap();
1316 assert!(matches!(v, UserContent::Other(_)));
1317 }
1318
1319 #[test]
1322 fn server_tool_use_missing_field_uses_default() {
1323 let json = r#"{"web_search_requests":3}"#;
1324 let v: ServerToolUse = serde_json::from_str(json).unwrap();
1325 assert_eq!(v.web_search_requests, 3);
1326 assert_eq!(v.web_fetch_requests, 0);
1327 }
1328
1329 #[test]
1331 fn user_role_unknown_value_does_not_fail() {
1332 let json = r#""operator""#;
1333 let v: UserRole = serde_json::from_str(json).unwrap();
1334 assert!(matches!(v, UserRole::Unknown));
1335 }
1336
1337 #[test]
1339 fn assistant_role_unknown_value_does_not_fail() {
1340 let json = r#""system_agent""#;
1341 let v: AssistantRole = serde_json::from_str(json).unwrap();
1342 assert!(matches!(v, AssistantRole::Unknown));
1343 }
1344
1345 #[test]
1347 fn session_mode_unknown_value_does_not_fail() {
1348 let json = r#""background""#;
1349 let v: SessionMode = serde_json::from_str(json).unwrap();
1350 assert!(matches!(v, SessionMode::Unknown));
1351 }
1352
1353 #[test]
1356 fn assistant_message_missing_model_uses_default() {
1357 let json = r#"{
1358 "id": "msg_err1",
1359 "type": "message",
1360 "role": "assistant",
1361 "content": [],
1362 "stop_reason": "error",
1363 "stop_sequence": null,
1364 "usage": {"input_tokens": 0, "output_tokens": 0}
1365 }"#;
1366 let v: AssistantMessage = serde_json::from_str(json).unwrap();
1367 assert!(v.model.is_none());
1368 }
1369}