1use std::str::FromStr;
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use serde_json::{Map, Value, json};
5use thiserror::Error;
6
7use crate::envelope::{deserialize_unique_value, validate_read_version};
8use crate::metadata::validate_json_bounds;
9use crate::{
10 ApprovalId, BranchId, CURRENT_PROTOCOL_VERSION, ContentBlock, EventId, ExactCost,
11 HostedToolOutcome, MAX_HOSTED_TOOL_SOURCES, MessageId, ProtocolMetadata, ProtocolTimestamp,
12 ProtocolVersion, RunId, SessionId, SessionSequence, ToolCallId, ToolPresentation, TurnId,
13 Usage,
14};
15
16pub const MAX_EVENT_DELTA_BYTES: usize = 64 * 1024;
18pub const MAX_PROGRESS_MESSAGE_BYTES: usize = 4096;
20pub const MAX_APPROVAL_ITEMS: usize = 64;
22pub const MAX_APPROVAL_ITEM_BYTES: usize = 1024;
24pub const MAX_UNKNOWN_EVENT_BYTES: usize = 64 * 1024;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "snake_case")]
30pub enum EventCompatibility {
31 SkippableObservation,
33 RequiredStateBearing,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
39#[serde(rename_all = "snake_case")]
40pub enum AgentEventType {
41 RunStarted,
43 MessageDelta,
45 ToolCallRequested,
47 ApprovalRequested,
49 ToolExecutionProgress,
51 ToolExecutionPreview,
53 HostedToolStarted,
55 HostedToolCompleted,
57 ModelRetryScheduled,
59 ModelRetryStarted,
61 TurnCheckpointed,
63 SessionCompacted,
65 SessionForked,
67 RunFinished,
69}
70
71impl AgentEventType {
72 pub const ALL: [Self; 14] = [
74 Self::RunStarted,
75 Self::MessageDelta,
76 Self::ToolCallRequested,
77 Self::ApprovalRequested,
78 Self::ToolExecutionProgress,
79 Self::ToolExecutionPreview,
80 Self::HostedToolStarted,
81 Self::HostedToolCompleted,
82 Self::ModelRetryScheduled,
83 Self::ModelRetryStarted,
84 Self::TurnCheckpointed,
85 Self::SessionCompacted,
86 Self::SessionForked,
87 Self::RunFinished,
88 ];
89
90 #[must_use]
92 pub const fn compatibility(self) -> EventCompatibility {
93 match self {
94 Self::MessageDelta
95 | Self::ToolExecutionProgress
96 | Self::ToolExecutionPreview
97 | Self::HostedToolStarted
98 | Self::HostedToolCompleted
99 | Self::ModelRetryScheduled
100 | Self::ModelRetryStarted => EventCompatibility::SkippableObservation,
101 Self::RunStarted
102 | Self::ToolCallRequested
103 | Self::ApprovalRequested
104 | Self::TurnCheckpointed
105 | Self::SessionCompacted
106 | Self::SessionForked
107 | Self::RunFinished => EventCompatibility::RequiredStateBearing,
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub enum EventDelta {
115 TextDelta {
117 text: String,
119 },
120 ThinkingDelta {
122 text: String,
124 },
125}
126
127impl EventDelta {
128 fn validate(&self) -> Result<(), EventValidationError> {
129 let text = match self {
130 Self::TextDelta { text } | Self::ThinkingDelta { text } => text,
131 };
132 if text.is_empty() || text.len() > MAX_EVENT_DELTA_BYTES || text.contains('\0') {
133 Err(EventValidationError::InvalidDelta)
134 } else {
135 Ok(())
136 }
137 }
138}
139
140#[derive(Serialize, Deserialize)]
141#[serde(remote = "EventDelta", tag = "type", rename_all = "snake_case")]
142enum EventDeltaDef {
143 TextDelta { text: String },
144 ThinkingDelta { text: String },
145}
146
147impl Serialize for EventDelta {
148 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: Serializer,
151 {
152 self.validate().map_err(serde::ser::Error::custom)?;
153 EventDeltaDef::serialize(self, serializer)
154 }
155}
156
157impl<'de> Deserialize<'de> for EventDelta {
158 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
159 where
160 D: Deserializer<'de>,
161 {
162 let delta = EventDeltaDef::deserialize(deserializer)?;
163 delta.validate().map_err(serde::de::Error::custom)?;
164 Ok(delta)
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
170#[serde(rename_all = "snake_case")]
171pub enum RunStatus {
172 Completed,
174 Cancelled,
176 Failed,
178 Interrupted,
180}
181
182#[derive(Debug, Clone, PartialEq)]
184pub enum AgentEvent {
185 RunStarted {},
187 MessageDelta {
189 message_id: MessageId,
191 content_index: u32,
193 delta: EventDelta,
195 },
196 ToolCallRequested {
198 tool_call_id: ToolCallId,
200 tool_name: String,
202 arguments: Value,
204 },
205 ApprovalRequested {
207 approval_id: ApprovalId,
209 tool_call_id: ToolCallId,
211 capabilities: Vec<String>,
213 resources: Vec<String>,
215 expires_at: ProtocolTimestamp,
217 },
218 ToolExecutionProgress {
220 tool_call_id: ToolCallId,
222 message: String,
224 completed_units: u64,
226 total_units: Option<u64>,
228 },
229 ToolExecutionPreview {
231 tool_call_id: ToolCallId,
233 presentation: ToolPresentation,
235 },
236 HostedToolStarted {
238 tool_call_id: ToolCallId,
240 tool_name: String,
242 },
243 HostedToolCompleted {
245 tool_call_id: ToolCallId,
247 tool_name: String,
249 arguments: Value,
251 outcome: HostedToolOutcome,
253 source_count: u32,
255 },
256 ModelRetryScheduled {
258 message_id: MessageId,
260 attempt: u32,
262 max_retries: u32,
264 delay_ms: u64,
266 },
267 ModelRetryStarted {
269 message_id: MessageId,
271 attempt: u32,
273 max_retries: u32,
275 },
276 TurnCheckpointed {},
278 SessionCompacted {
280 summary_message_id: MessageId,
282 compacted_through_message_id: MessageId,
284 },
285 SessionForked {
287 source_branch_id: BranchId,
289 branch_id: BranchId,
291 from_message_id: MessageId,
293 },
294 RunFinished {
296 status: RunStatus,
298 usage: Option<Usage>,
300 cost: Option<ExactCost>,
302 },
303}
304
305impl AgentEvent {
306 #[must_use]
308 pub const fn event_type(&self) -> AgentEventType {
309 match self {
310 Self::RunStarted {} => AgentEventType::RunStarted,
311 Self::MessageDelta { .. } => AgentEventType::MessageDelta,
312 Self::ToolCallRequested { .. } => AgentEventType::ToolCallRequested,
313 Self::ApprovalRequested { .. } => AgentEventType::ApprovalRequested,
314 Self::ToolExecutionProgress { .. } => AgentEventType::ToolExecutionProgress,
315 Self::ToolExecutionPreview { .. } => AgentEventType::ToolExecutionPreview,
316 Self::HostedToolStarted { .. } => AgentEventType::HostedToolStarted,
317 Self::HostedToolCompleted { .. } => AgentEventType::HostedToolCompleted,
318 Self::ModelRetryScheduled { .. } => AgentEventType::ModelRetryScheduled,
319 Self::ModelRetryStarted { .. } => AgentEventType::ModelRetryStarted,
320 Self::TurnCheckpointed {} => AgentEventType::TurnCheckpointed,
321 Self::SessionCompacted { .. } => AgentEventType::SessionCompacted,
322 Self::SessionForked { .. } => AgentEventType::SessionForked,
323 Self::RunFinished { .. } => AgentEventType::RunFinished,
324 }
325 }
326
327 fn validate(&self) -> Result<(), EventValidationError> {
328 match self {
329 Self::MessageDelta { delta, .. } => delta.validate(),
330 Self::ToolCallRequested {
331 tool_call_id,
332 tool_name,
333 arguments,
334 } => {
335 ContentBlock::tool_call(*tool_call_id, tool_name.clone(), arguments.clone())?;
336 Ok(())
337 }
338 Self::ApprovalRequested {
339 capabilities,
340 resources,
341 ..
342 } => {
343 validate_items(capabilities)?;
344 validate_items(resources)
345 }
346 Self::ToolExecutionProgress {
347 message,
348 completed_units,
349 total_units,
350 ..
351 } => {
352 if message.is_empty()
353 || message.len() > MAX_PROGRESS_MESSAGE_BYTES
354 || message.contains('\0')
355 || *completed_units > crate::MAX_SAFE_INTEGER
356 || total_units.is_some_and(|total| {
357 total > crate::MAX_SAFE_INTEGER || *completed_units > total
358 })
359 {
360 return Err(EventValidationError::InvalidProgress);
361 }
362 Ok(())
363 }
364 Self::HostedToolStarted {
365 tool_call_id,
366 tool_name,
367 } => {
368 ContentBlock::tool_call(*tool_call_id, tool_name.clone(), json!({}))?;
369 Ok(())
370 }
371 Self::HostedToolCompleted {
372 tool_call_id,
373 tool_name,
374 arguments,
375 source_count,
376 ..
377 } => {
378 ContentBlock::tool_call(*tool_call_id, tool_name.clone(), arguments.clone())?;
379 let Ok(source_count) = usize::try_from(*source_count) else {
380 return Err(EventValidationError::InvalidHostedToolObservation);
381 };
382 if source_count > MAX_HOSTED_TOOL_SOURCES {
383 return Err(EventValidationError::InvalidHostedToolObservation);
384 }
385 Ok(())
386 }
387 Self::ModelRetryScheduled {
388 attempt,
389 max_retries,
390 delay_ms,
391 ..
392 } => validate_model_retry(*attempt, *max_retries, Some(*delay_ms)),
393 Self::ModelRetryStarted {
394 attempt,
395 max_retries,
396 ..
397 } => validate_model_retry(*attempt, *max_retries, None),
398 Self::ToolExecutionPreview { .. }
399 | Self::RunStarted {}
400 | Self::TurnCheckpointed {}
401 | Self::SessionCompacted { .. }
402 | Self::SessionForked { .. }
403 | Self::RunFinished { .. } => Ok(()),
404 }
405 }
406}
407
408#[derive(Serialize, Deserialize)]
409#[serde(
410 remote = "AgentEvent",
411 tag = "type",
412 content = "payload",
413 rename_all = "snake_case"
414)]
415enum AgentEventDef {
416 RunStarted {},
417 MessageDelta {
418 #[serde(rename = "messageId")]
419 message_id: MessageId,
420 #[serde(rename = "contentIndex")]
421 content_index: u32,
422 delta: EventDelta,
423 },
424 ToolCallRequested {
425 #[serde(rename = "toolCallId")]
426 tool_call_id: ToolCallId,
427 #[serde(rename = "toolName")]
428 tool_name: String,
429 arguments: Value,
430 },
431 ApprovalRequested {
432 #[serde(rename = "approvalId")]
433 approval_id: ApprovalId,
434 #[serde(rename = "toolCallId")]
435 tool_call_id: ToolCallId,
436 capabilities: Vec<String>,
437 resources: Vec<String>,
438 #[serde(rename = "expiresAt")]
439 expires_at: ProtocolTimestamp,
440 },
441 ToolExecutionProgress {
442 #[serde(rename = "toolCallId")]
443 tool_call_id: ToolCallId,
444 message: String,
445 #[serde(rename = "completedUnits")]
446 completed_units: u64,
447 #[serde(rename = "totalUnits", skip_serializing_if = "Option::is_none")]
448 total_units: Option<u64>,
449 },
450 ToolExecutionPreview {
451 #[serde(rename = "toolCallId")]
452 tool_call_id: ToolCallId,
453 presentation: ToolPresentation,
454 },
455 HostedToolStarted {
456 #[serde(rename = "toolCallId")]
457 tool_call_id: ToolCallId,
458 #[serde(rename = "toolName")]
459 tool_name: String,
460 },
461 HostedToolCompleted {
462 #[serde(rename = "toolCallId")]
463 tool_call_id: ToolCallId,
464 #[serde(rename = "toolName")]
465 tool_name: String,
466 arguments: Value,
467 outcome: HostedToolOutcome,
468 #[serde(rename = "sourceCount")]
469 source_count: u32,
470 },
471 ModelRetryScheduled {
472 #[serde(rename = "messageId")]
473 message_id: MessageId,
474 attempt: u32,
475 #[serde(rename = "maxRetries")]
476 max_retries: u32,
477 #[serde(rename = "delayMs")]
478 delay_ms: u64,
479 },
480 ModelRetryStarted {
481 #[serde(rename = "messageId")]
482 message_id: MessageId,
483 attempt: u32,
484 #[serde(rename = "maxRetries")]
485 max_retries: u32,
486 },
487 TurnCheckpointed {},
488 SessionCompacted {
489 #[serde(rename = "summaryMessageId")]
490 summary_message_id: MessageId,
491 #[serde(rename = "compactedThroughMessageId")]
492 compacted_through_message_id: MessageId,
493 },
494 SessionForked {
495 #[serde(rename = "sourceBranchId")]
496 source_branch_id: BranchId,
497 #[serde(rename = "branchId")]
498 branch_id: BranchId,
499 #[serde(rename = "fromMessageId")]
500 from_message_id: MessageId,
501 },
502 RunFinished {
503 status: RunStatus,
504 #[serde(skip_serializing_if = "Option::is_none")]
505 usage: Option<Usage>,
506 #[serde(skip_serializing_if = "Option::is_none")]
507 cost: Option<ExactCost>,
508 },
509}
510
511impl Serialize for AgentEvent {
512 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
513 where
514 S: Serializer,
515 {
516 self.validate().map_err(serde::ser::Error::custom)?;
517 AgentEventDef::serialize(self, serializer)
518 }
519}
520
521impl<'de> Deserialize<'de> for AgentEvent {
522 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
523 where
524 D: Deserializer<'de>,
525 {
526 let event = AgentEventDef::deserialize(deserializer)?;
527 event.validate().map_err(serde::de::Error::custom)?;
528 Ok(event)
529 }
530}
531
532#[derive(Debug, Clone, PartialEq)]
534pub struct EventEnvelope {
535 protocol_version: ProtocolVersion,
536 event_id: EventId,
537 session_id: SessionId,
538 run_id: Option<RunId>,
539 turn_id: Option<TurnId>,
540 sequence: SessionSequence,
541 timestamp: ProtocolTimestamp,
542 metadata: ProtocolMetadata,
543 event: AgentEvent,
544}
545
546impl EventEnvelope {
547 #[allow(clippy::too_many_arguments)]
554 pub fn new(
555 event_id: EventId,
556 session_id: SessionId,
557 run_id: Option<RunId>,
558 turn_id: Option<TurnId>,
559 sequence: SessionSequence,
560 timestamp: ProtocolTimestamp,
561 metadata: ProtocolMetadata,
562 event: AgentEvent,
563 ) -> Result<Self, EventValidationError> {
564 let envelope = Self {
565 protocol_version: CURRENT_PROTOCOL_VERSION,
566 event_id,
567 session_id,
568 run_id,
569 turn_id,
570 sequence,
571 timestamp,
572 metadata,
573 event,
574 };
575 envelope.validate()?;
576 Ok(envelope)
577 }
578
579 pub fn inspect_value(value: Value) -> Result<EventInspection, EventDecodeError> {
586 let discriminator = value
587 .as_object()
588 .and_then(|object| object.get("type"))
589 .and_then(Value::as_str)
590 .ok_or_else(|| EventDecodeError::Invalid("missing event type".to_owned()))?
591 .to_owned();
592 if AgentEventTypeText::from_str(&discriminator).is_ok() {
593 return serde_json::from_value(value)
594 .map(EventInspection::Known)
595 .map_err(|error| EventDecodeError::Invalid(error.to_string()));
596 }
597 inspect_unknown(&value, discriminator)
598 }
599
600 #[must_use]
602 pub const fn protocol_version(&self) -> ProtocolVersion {
603 self.protocol_version
604 }
605
606 #[must_use]
608 pub const fn event_id(&self) -> EventId {
609 self.event_id
610 }
611
612 #[must_use]
614 pub const fn session_id(&self) -> SessionId {
615 self.session_id
616 }
617
618 #[must_use]
620 pub const fn run_id(&self) -> Option<RunId> {
621 self.run_id
622 }
623
624 #[must_use]
626 pub const fn turn_id(&self) -> Option<TurnId> {
627 self.turn_id
628 }
629
630 #[must_use]
632 pub const fn sequence(&self) -> SessionSequence {
633 self.sequence
634 }
635
636 #[must_use]
638 pub const fn timestamp(&self) -> ProtocolTimestamp {
639 self.timestamp
640 }
641
642 #[must_use]
644 pub const fn metadata(&self) -> &ProtocolMetadata {
645 &self.metadata
646 }
647
648 #[must_use]
650 pub const fn event(&self) -> &AgentEvent {
651 &self.event
652 }
653
654 #[must_use]
656 pub const fn event_type(&self) -> AgentEventType {
657 self.event.event_type()
658 }
659
660 fn validate(&self) -> Result<(), EventValidationError> {
661 self.event.validate()?;
662 let (requires_run, requires_turn) = match self.event.event_type() {
663 AgentEventType::RunStarted | AgentEventType::RunFinished => (true, false),
664 AgentEventType::MessageDelta
665 | AgentEventType::ToolCallRequested
666 | AgentEventType::ApprovalRequested
667 | AgentEventType::ToolExecutionProgress
668 | AgentEventType::ToolExecutionPreview
669 | AgentEventType::HostedToolStarted
670 | AgentEventType::HostedToolCompleted
671 | AgentEventType::ModelRetryScheduled
672 | AgentEventType::ModelRetryStarted
673 | AgentEventType::TurnCheckpointed => (true, true),
674 AgentEventType::SessionCompacted | AgentEventType::SessionForked => (false, false),
675 };
676 if self.run_id.is_some() != requires_run || self.turn_id.is_some() != requires_turn {
677 return Err(EventValidationError::InvalidReferences);
678 }
679 Ok(())
680 }
681}
682
683impl Serialize for EventEnvelope {
684 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
685 where
686 S: Serializer,
687 {
688 self.validate().map_err(serde::ser::Error::custom)?;
689 let mut value = serde_json::to_value(&self.event).map_err(serde::ser::Error::custom)?;
690 let object = value
691 .as_object_mut()
692 .ok_or_else(|| serde::ser::Error::custom("event must encode as object"))?;
693 object.insert("protocolVersion".to_owned(), json!(self.protocol_version));
694 object.insert("eventId".to_owned(), json!(self.event_id));
695 object.insert("sessionId".to_owned(), json!(self.session_id));
696 if let Some(run_id) = self.run_id {
697 object.insert("runId".to_owned(), json!(run_id));
698 }
699 if let Some(turn_id) = self.turn_id {
700 object.insert("turnId".to_owned(), json!(turn_id));
701 }
702 object.insert("sequence".to_owned(), json!(self.sequence));
703 object.insert("timestamp".to_owned(), json!(self.timestamp));
704 if self.event_type().compatibility() == EventCompatibility::SkippableObservation {
705 object.insert(
706 "compatibility".to_owned(),
707 json!(EventCompatibility::SkippableObservation),
708 );
709 }
710 if !self.metadata.is_empty() {
711 object.insert("metadata".to_owned(), json!(self.metadata));
712 }
713 value.serialize(serializer)
714 }
715}
716
717impl<'de> Deserialize<'de> for EventEnvelope {
718 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
719 where
720 D: Deserializer<'de>,
721 {
722 let mut value = deserialize_unique_value(deserializer)?;
723 let object = value
724 .as_object_mut()
725 .ok_or_else(|| serde::de::Error::custom("event envelope must be an object"))?;
726 let protocol_version = take(object, "protocolVersion").map_err(serde::de::Error::custom)?;
727 validate_read_version(protocol_version).map_err(serde::de::Error::custom)?;
728 let event_id = take(object, "eventId").map_err(serde::de::Error::custom)?;
729 let session_id = take(object, "sessionId").map_err(serde::de::Error::custom)?;
730 let run_id = take_optional(object, "runId").map_err(serde::de::Error::custom)?;
731 let turn_id = take_optional(object, "turnId").map_err(serde::de::Error::custom)?;
732 let sequence = take(object, "sequence").map_err(serde::de::Error::custom)?;
733 let timestamp = take(object, "timestamp").map_err(serde::de::Error::custom)?;
734 let metadata = take_optional(object, "metadata")
735 .map_err(serde::de::Error::custom)?
736 .unwrap_or_default();
737 object.remove("compatibility");
738 let event = AgentEvent::deserialize(Value::Object(std::mem::take(object)))
739 .map_err(serde::de::Error::custom)?;
740 let envelope = Self {
741 protocol_version,
742 event_id,
743 session_id,
744 run_id,
745 turn_id,
746 sequence,
747 timestamp,
748 metadata,
749 event,
750 };
751 envelope.validate().map_err(serde::de::Error::custom)?;
752 Ok(envelope)
753 }
754}
755
756#[derive(Debug, Clone, PartialEq)]
758pub enum EventInspection {
759 Known(EventEnvelope),
761 UnknownSkippable(UnknownSkippableEvent),
763}
764
765#[derive(Debug, Clone, PartialEq, Eq)]
767pub struct UnknownSkippableEvent {
768 event_type: String,
769 protocol_version: ProtocolVersion,
770 event_id: EventId,
771 session_id: SessionId,
772 run_id: Option<RunId>,
773 turn_id: Option<TurnId>,
774 sequence: SessionSequence,
775 timestamp: ProtocolTimestamp,
776}
777
778impl UnknownSkippableEvent {
779 #[must_use]
781 pub fn event_type(&self) -> &str {
782 &self.event_type
783 }
784
785 #[must_use]
787 pub const fn protocol_version(&self) -> ProtocolVersion {
788 self.protocol_version
789 }
790
791 #[must_use]
793 pub const fn sequence(&self) -> SessionSequence {
794 self.sequence
795 }
796
797 #[must_use]
799 pub const fn event_id(&self) -> EventId {
800 self.event_id
801 }
802
803 #[must_use]
805 pub const fn session_id(&self) -> SessionId {
806 self.session_id
807 }
808
809 #[must_use]
811 pub const fn run_id(&self) -> Option<RunId> {
812 self.run_id
813 }
814
815 #[must_use]
817 pub const fn turn_id(&self) -> Option<TurnId> {
818 self.turn_id
819 }
820
821 #[must_use]
823 pub const fn timestamp(&self) -> ProtocolTimestamp {
824 self.timestamp
825 }
826}
827
828#[derive(Debug, Error)]
830pub enum EventDecodeError {
831 #[error("unsupported state-bearing event type: {event_type}")]
833 UnsupportedStateBearing {
834 event_type: String,
836 },
837 #[error("invalid event: {0}")]
839 Invalid(String),
840}
841
842#[derive(Debug, Error)]
844pub enum EventValidationError {
845 #[error("streaming delta is invalid")]
847 InvalidDelta,
848 #[error("tool-call event payload is invalid: {0}")]
850 InvalidToolCall(#[from] crate::ContentValidationError),
851 #[error("approval event items are invalid")]
853 InvalidApprovalItems,
854 #[error("tool progress event is invalid")]
856 InvalidProgress,
857 #[error("hosted tool observation is invalid")]
859 InvalidHostedToolObservation,
860 #[error("model retry observation is invalid")]
862 InvalidModelRetry,
863 #[error("event runId/turnId references do not match the event kind")]
865 InvalidReferences,
866}
867
868#[derive(Debug, Clone, Copy, PartialEq, Eq)]
869struct AgentEventTypeText;
870
871impl FromStr for AgentEventTypeText {
872 type Err = ();
873
874 fn from_str(value: &str) -> Result<Self, Self::Err> {
875 if [
876 "run_started",
877 "message_delta",
878 "tool_call_requested",
879 "approval_requested",
880 "tool_execution_progress",
881 "tool_execution_preview",
882 "hosted_tool_started",
883 "hosted_tool_completed",
884 "model_retry_scheduled",
885 "model_retry_started",
886 "turn_checkpointed",
887 "session_compacted",
888 "session_forked",
889 "run_finished",
890 ]
891 .contains(&value)
892 {
893 Ok(Self)
894 } else {
895 Err(())
896 }
897 }
898}
899
900fn inspect_unknown(value: &Value, event_type: String) -> Result<EventInspection, EventDecodeError> {
901 if !valid_discriminator(&event_type) {
902 return Err(EventDecodeError::Invalid("invalid event type".to_owned()));
903 }
904 validate_json_bounds(value, MAX_UNKNOWN_EVENT_BYTES, 32)
905 .map_err(|error| EventDecodeError::Invalid(error.to_string()))?;
906 let object = value
907 .as_object()
908 .ok_or_else(|| EventDecodeError::Invalid("event must be an object".to_owned()))?;
909 let compatibility =
910 object
911 .get("compatibility")
912 .ok_or_else(|| EventDecodeError::UnsupportedStateBearing {
913 event_type: event_type.clone(),
914 })?;
915 let compatibility: EventCompatibility = serde_json::from_value(compatibility.clone())
916 .map_err(|error| EventDecodeError::Invalid(error.to_string()))?;
917 if compatibility != EventCompatibility::SkippableObservation {
918 return Err(EventDecodeError::UnsupportedStateBearing { event_type });
919 }
920 let protocol_version = field(object, "protocolVersion")?;
921 validate_read_version(protocol_version)
922 .map_err(|message| EventDecodeError::Invalid(message.to_owned()))?;
923 let payload = object
924 .get("payload")
925 .ok_or_else(|| EventDecodeError::Invalid("missing event payload".to_owned()))?;
926 validate_json_bounds(payload, MAX_UNKNOWN_EVENT_BYTES, 32)
927 .map_err(|error| EventDecodeError::Invalid(error.to_string()))?;
928 Ok(EventInspection::UnknownSkippable(UnknownSkippableEvent {
929 event_type,
930 protocol_version,
931 event_id: field(object, "eventId")?,
932 session_id: field(object, "sessionId")?,
933 run_id: optional_field(object, "runId")?,
934 turn_id: optional_field(object, "turnId")?,
935 sequence: field(object, "sequence")?,
936 timestamp: field(object, "timestamp")?,
937 }))
938}
939
940fn validate_items(items: &[String]) -> Result<(), EventValidationError> {
941 if items.is_empty()
942 || items.len() > MAX_APPROVAL_ITEMS
943 || items.iter().any(|item| {
944 item.is_empty()
945 || item.len() > MAX_APPROVAL_ITEM_BYTES
946 || item.chars().any(char::is_control)
947 })
948 {
949 Err(EventValidationError::InvalidApprovalItems)
950 } else {
951 Ok(())
952 }
953}
954
955fn validate_model_retry(
956 attempt: u32,
957 max_retries: u32,
958 delay_ms: Option<u64>,
959) -> Result<(), EventValidationError> {
960 if attempt == 0
961 || max_retries == 0
962 || attempt > max_retries
963 || delay_ms.is_some_and(|delay| delay > crate::MAX_SAFE_INTEGER)
964 {
965 Err(EventValidationError::InvalidModelRetry)
966 } else {
967 Ok(())
968 }
969}
970
971fn valid_discriminator(value: &str) -> bool {
972 !value.is_empty()
973 && value.len() <= 128
974 && value
975 .bytes()
976 .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'_')
977}
978
979fn take<T>(object: &mut Map<String, Value>, key: &str) -> Result<T, serde_json::Error>
980where
981 T: for<'de> Deserialize<'de>,
982{
983 serde_json::from_value(object.remove(key).unwrap_or(Value::Null))
984}
985
986fn take_optional<T>(
987 object: &mut Map<String, Value>,
988 key: &str,
989) -> Result<Option<T>, serde_json::Error>
990where
991 T: for<'de> Deserialize<'de>,
992{
993 object.remove(key).map_or(Ok(None), serde_json::from_value)
994}
995
996fn field<T>(object: &Map<String, Value>, key: &str) -> Result<T, EventDecodeError>
997where
998 T: for<'de> Deserialize<'de>,
999{
1000 serde_json::from_value(object.get(key).cloned().unwrap_or(Value::Null))
1001 .map_err(|error| EventDecodeError::Invalid(error.to_string()))
1002}
1003
1004fn optional_field<T>(object: &Map<String, Value>, key: &str) -> Result<Option<T>, EventDecodeError>
1005where
1006 T: for<'de> Deserialize<'de>,
1007{
1008 object
1009 .get(key)
1010 .cloned()
1011 .map_or(Ok(None), serde_json::from_value)
1012 .map_err(|error| EventDecodeError::Invalid(error.to_string()))
1013}