Skip to main content

agent_sdk_core/records/
event.rs

1//! Canonical event records for live observation and telemetry projection. Use these
2//! records to describe what happened without requiring raw content capture.
3//! Constructors are data-only; delivery belongs to event buses and sinks.
4//!
5use std::num::NonZeroUsize;
6
7use serde::{Deserialize, Serialize};
8use sha2::{Digest, Sha256};
9
10use crate::{
11    domain::{
12        AgentError, AgentId, ArchiveCursorId, ContextItemId, CorrelationEntry, DestinationKind,
13        DestinationRef, EntityKind, EntityRef, EventId, JournalCursor, MessageId, PolicyRef,
14        PrivacyClass, RunId, SessionId, SourceKind, SourceRef, SpanId, TraceId, TurnId,
15    },
16    ids::AttemptId,
17};
18
19macro_rules! typed_string {
20    ($name:ident, $debug:literal) => {
21        #[doc = concat!(
22                            "Typed event-string wrapper for `",
23                            stringify!($name),
24                            "`. Use it for stable event filter, cursor, or fingerprint fields; ",
25                            "constructing it is data-only and performs no side effects."
26                        )]
27        #[derive(Clone, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
28        #[serde(transparent)]
29        pub struct $name(String);
30
31        impl $name {
32            /// Creates a new records::event value with explicit
33            /// caller-provided inputs. This constructor is data-only
34            /// and performs no I/O or external side effects.
35            pub fn new(value: impl Into<String>) -> Self {
36                Self(value.into())
37            }
38
39            /// Returns this value as str. The accessor is side-effect
40            /// free and keeps ownership with the caller.
41            pub fn as_str(&self) -> &str {
42                &self.0
43            }
44        }
45
46        impl core::fmt::Debug for $name {
47            fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48                formatter.write_str(concat!($debug, "(redacted)"))
49            }
50        }
51
52        impl core::fmt::Display for $name {
53            fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54                formatter.write_str(concat!($debug, "(redacted)"))
55            }
56        }
57    };
58}
59
60/// Constant value for the records::event contract. Use it to keep SDK
61/// records and tests aligned on the same stable value.
62pub const EVENT_SCHEMA_VERSION: u16 = 1;
63
64#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
65#[serde(rename_all = "snake_case")]
66/// Enumerates the finite event family cases.
67/// Serialized names are part of the SDK contract; update fixtures when variants change.
68pub enum EventFamily {
69    /// Use this variant when the contract needs to represent run; selecting it has no side effect by itself.
70    Run,
71    /// Use this variant when the contract needs to represent turn; selecting it has no side effect by itself.
72    Turn,
73    /// Use this variant when the contract needs to represent message; selecting it has no side effect by itself.
74    Message,
75    /// Use this variant when the contract needs to represent model; selecting it has no side effect by itself.
76    Model,
77    /// Use this variant when the contract needs to represent tool; selecting it has no side effect by itself.
78    Tool,
79    /// Use this variant when the contract needs to represent approval; selecting it has no side effect by itself.
80    Approval,
81    /// Use this variant when the contract needs to represent hook; selecting it has no side effect by itself.
82    Hook,
83    /// Use this variant when the contract needs to represent context; selecting it has no side effect by itself.
84    Context,
85    /// Use this variant when the contract needs to represent stream rule; selecting it has no side effect by itself.
86    StreamRule,
87    /// Use this variant when the contract needs to represent realtime; selecting it has no side effect by itself.
88    Realtime,
89    /// Use this variant when the contract needs to represent isolation; selecting it has no side effect by itself.
90    Isolation,
91    /// Use this variant when the contract needs to represent child lifecycle; selecting it has no side effect by itself.
92    ChildLifecycle,
93    /// Use this variant when the contract needs to represent agent pool; selecting it has no side effect by itself.
94    AgentPool,
95    /// Use this variant when the contract needs to represent subagent; selecting it has no side effect by itself.
96    Subagent,
97    /// Use this variant when the contract needs to represent extension; selecting it has no side effect by itself.
98    Extension,
99    /// Use this variant when the contract needs to represent structured output; selecting it has no side effect by itself.
100    StructuredOutput,
101    /// Use this variant when the contract needs to represent output; selecting it has no side effect by itself.
102    Output,
103    /// Use this variant when the contract needs to represent output delivery; selecting it has no side effect by itself.
104    OutputDelivery,
105    /// Use this variant when the contract needs to represent telemetry; selecting it has no side effect by itself.
106    Telemetry,
107    /// Use this variant when the contract needs to represent recovery; selecting it has no side effect by itself.
108    Recovery,
109}
110
111#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
112#[serde(rename_all = "snake_case")]
113/// Enumerates the finite event kind cases.
114/// Serialized names are part of the SDK contract; update fixtures when variants change.
115pub enum EventKind {
116    /// Use this variant when the contract needs to represent run started; selecting it has no side effect by itself.
117    RunStarted,
118    /// Use this variant when the contract needs to represent run completed; selecting it has no side effect by itself.
119    RunCompleted,
120    /// Use this variant when the contract needs to represent run failed; selecting it has no side effect by itself.
121    RunFailed,
122    /// Use this variant when the contract needs to represent run cancelled; selecting it has no side effect by itself.
123    RunCancelled,
124    /// Use this variant when the contract needs to represent run checkpointed; selecting it has no side effect by itself.
125    RunCheckpointed,
126    /// Use this variant when the contract needs to represent run cancel requested; selecting it has no side effect by itself.
127    RunCancelRequested,
128    /// Use this variant when the contract needs to represent run resume requested; selecting it has no side effect by itself.
129    RunResumeRequested,
130    /// Use this variant when the contract needs to represent run resume failed; selecting it has no side effect by itself.
131    RunResumeFailed,
132    /// Use this variant when the contract needs to represent turn started; selecting it has no side effect by itself.
133    TurnStarted,
134    /// Use this variant when the contract needs to represent turn completed; selecting it has no side effect by itself.
135    TurnCompleted,
136    /// Use this variant when the contract needs to represent turn failed; selecting it has no side effect by itself.
137    TurnFailed,
138    /// Use this variant when the contract needs to represent message accepted; selecting it has no side effect by itself.
139    MessageAccepted,
140    /// Use this variant when the contract needs to represent message committed; selecting it has no side effect by itself.
141    MessageCommitted,
142    /// Use this variant when the contract needs to represent provider request projected; selecting it has no side effect by itself.
143    ProviderRequestProjected,
144    /// Use this variant when the contract needs to represent model attempt started; selecting it has no side effect by itself.
145    ModelAttemptStarted,
146    /// Use this variant when the contract needs to represent model stream delta; selecting it has no side effect by itself.
147    ModelStreamDelta,
148    /// Use this variant when the contract needs to represent model message completed; selecting it has no side effect by itself.
149    ModelMessageCompleted,
150    /// Use this variant when the contract needs to represent model attempt failed; selecting it has no side effect by itself.
151    ModelAttemptFailed,
152    /// Use this variant when the contract needs to represent tool requested; selecting it has no side effect by itself.
153    ToolRequested,
154    /// Use this variant when the contract needs to represent tool started; selecting it has no side effect by itself.
155    ToolStarted,
156    /// Use this variant when the contract needs to represent tool completed; selecting it has no side effect by itself.
157    ToolCompleted,
158    /// Use this variant when the contract needs to represent tool failed; selecting it has no side effect by itself.
159    ToolFailed,
160    /// Use this variant when the contract needs to represent tool denied; selecting it has no side effect by itself.
161    ToolDenied,
162    /// Use this variant when the contract needs to represent tool recovery required; selecting it has no side effect by itself.
163    ToolRecoveryRequired,
164    /// Use this variant when the contract needs to represent approval requested; selecting it has no side effect by itself.
165    ApprovalRequested,
166    /// Use this variant when the contract needs to represent approval dispatched; selecting it has no side effect by itself.
167    ApprovalDispatched,
168    /// Use this variant when the contract needs to represent approval dispatch unavailable; selecting it has no side effect by itself.
169    ApprovalDispatchUnavailable,
170    /// Use this variant when the contract needs to represent approval responded; selecting it has no side effect by itself.
171    ApprovalResponded,
172    /// Use this variant when the contract needs to represent approval denied; selecting it has no side effect by itself.
173    ApprovalDenied,
174    /// Use this variant when the contract needs to represent approval timed out; selecting it has no side effect by itself.
175    ApprovalTimedOut,
176    /// Use this variant when the contract needs to represent approval cancelled; selecting it has no side effect by itself.
177    ApprovalCancelled,
178    /// Use this variant when the contract needs to represent hook registered; selecting it has no side effect by itself.
179    HookRegistered,
180    /// Use this variant when the contract needs to represent hook invoked; selecting it has no side effect by itself.
181    HookInvoked,
182    /// Use this variant when the contract needs to represent hook completed; selecting it has no side effect by itself.
183    HookCompleted,
184    /// Use this variant when the contract needs to represent hook failed; selecting it has no side effect by itself.
185    HookFailed,
186    /// Use this variant when the contract needs to represent hook timed out; selecting it has no side effect by itself.
187    HookTimedOut,
188    /// Use this variant when the contract needs to represent hook cancelled; selecting it has no side effect by itself.
189    HookCancelled,
190    /// Use this variant when the contract needs to represent hook response applied; selecting it has no side effect by itself.
191    HookResponseApplied,
192    /// Use this variant when the contract needs to represent hook response rejected; selecting it has no side effect by itself.
193    HookResponseRejected,
194    /// Use this variant when the contract needs to represent context assembled; selecting it has no side effect by itself.
195    ContextAssembled,
196    /// Use this variant when the contract needs to represent stream rule registered; selecting it has no side effect by itself.
197    StreamRuleRegistered,
198    /// Use this variant when the contract needs to represent stream rule compile failed; selecting it has no side effect by itself.
199    StreamRuleCompileFailed,
200    /// Use this variant when the contract needs to represent stream rule matched; selecting it has no side effect by itself.
201    StreamRuleMatched,
202    /// Use this variant when the contract needs to represent stream intervention requested; selecting it has no side effect by itself.
203    StreamInterventionRequested,
204    /// Use this variant when the contract needs to represent stream intervention applied; selecting it has no side effect by itself.
205    StreamInterventionApplied,
206    /// Use this variant when the contract needs to represent stream rule repeat state recorded; selecting it has no side effect by itself.
207    StreamRuleRepeatStateRecorded,
208    /// Use this variant when the contract needs to represent realtime connect requested; selecting it has no side effect by itself.
209    RealtimeConnectRequested,
210    /// Use this variant when the contract needs to represent realtime connected; selecting it has no side effect by itself.
211    RealtimeConnected,
212    /// Use this variant when the contract needs to represent realtime input send requested; selecting it has no side effect by itself.
213    RealtimeInputSendRequested,
214    /// Use this variant when the contract needs to represent realtime input sent; selecting it has no side effect by itself.
215    RealtimeInputSent,
216    /// Use this variant when the contract needs to represent realtime output receive requested; selecting it has no side effect by itself.
217    RealtimeOutputReceiveRequested,
218    /// Use this variant when the contract needs to represent realtime output received; selecting it has no side effect by itself.
219    RealtimeOutputReceived,
220    /// Use this variant when the contract needs to represent realtime interrupted; selecting it has no side effect by itself.
221    RealtimeInterrupted,
222    /// Use this variant when the contract needs to represent realtime restart requested; selecting it has no side effect by itself.
223    RealtimeRestartRequested,
224    /// Use this variant when the contract needs to represent realtime restart started; selecting it has no side effect by itself.
225    RealtimeRestartStarted,
226    /// Use this variant when the contract needs to represent realtime restart completed; selecting it has no side effect by itself.
227    RealtimeRestartCompleted,
228    /// Use this variant when the contract needs to represent realtime restart failed; selecting it has no side effect by itself.
229    RealtimeRestartFailed,
230    /// Use this variant when the contract needs to represent realtime close requested; selecting it has no side effect by itself.
231    RealtimeCloseRequested,
232    /// Use this variant when the contract needs to represent realtime closed; selecting it has no side effect by itself.
233    RealtimeClosed,
234    /// Use this variant when the contract needs to represent realtime backpressure applied; selecting it has no side effect by itself.
235    RealtimeBackpressureApplied,
236    /// Use this variant when the contract needs to represent isolation requested; selecting it has no side effect by itself.
237    IsolationRequested,
238    /// Use this variant when the contract needs to represent isolation adapter health checked; selecting it has no side effect by itself.
239    IsolationAdapterHealthChecked,
240    /// Use this variant when the contract needs to represent isolation capability matched; selecting it has no side effect by itself.
241    IsolationCapabilityMatched,
242    /// Use this variant when the contract needs to represent isolation downgrade denied; selecting it has no side effect by itself.
243    IsolationDowngradeDenied,
244    /// Use this variant when the contract needs to represent isolation downgrade approved; selecting it has no side effect by itself.
245    IsolationDowngradeApproved,
246    /// Use this variant when the contract needs to represent isolation environment prepared; selecting it has no side effect by itself.
247    IsolationEnvironmentPrepared,
248    /// Use this variant when the contract needs to represent isolation process started; selecting it has no side effect by itself.
249    IsolationProcessStarted,
250    /// Use this variant when the contract needs to represent isolation process io captured; selecting it has no side effect by itself.
251    IsolationProcessIoCaptured,
252    /// Use this variant when the contract needs to represent isolation process stats recorded; selecting it has no side effect by itself.
253    IsolationProcessStatsRecorded,
254    /// Use this variant when the contract needs to represent isolation cleanup started; selecting it has no side effect by itself.
255    IsolationCleanupStarted,
256    /// Use this variant when the contract needs to represent isolation cleanup completed; selecting it has no side effect by itself.
257    IsolationCleanupCompleted,
258    /// Use this variant when the contract needs to represent isolation cleanup failed; selecting it has no side effect by itself.
259    IsolationCleanupFailed,
260    /// Use this variant when the contract needs to represent isolation failed; selecting it has no side effect by itself.
261    IsolationFailed,
262    /// Use this variant when the contract needs to represent child lifecycle requested; selecting it has no side effect by itself.
263    ChildLifecycleRequested,
264    /// Use this variant when the contract needs to represent child lifecycle completed; selecting it has no side effect by itself.
265    ChildLifecycleCompleted,
266    /// Use this variant when the contract needs to represent child lifecycle detached; selecting it has no side effect by itself.
267    ChildLifecycleDetached,
268    /// Use this variant when the contract needs to represent subagent started; selecting it has no side effect by itself.
269    SubagentStarted,
270    /// Use this variant when the contract needs to represent subagent handoff; selecting it has no side effect by itself.
271    SubagentHandoff,
272    /// Use this variant when the contract needs to represent subagent event wrapped; selecting it has no side effect by itself.
273    SubagentEventWrapped,
274    /// Use this variant when the contract needs to represent subagent usage rolled up; selecting it has no side effect by itself.
275    SubagentUsageRolledUp,
276    /// Use this variant when the contract needs to represent subagent completed; selecting it has no side effect by itself.
277    SubagentCompleted,
278    /// Use this variant when the contract needs to represent extension action submitted; selecting it has no side effect by itself.
279    ExtensionActionSubmitted,
280    /// Use this variant when the contract needs to represent extension action started; selecting it has no side effect by itself.
281    ExtensionActionStarted,
282    /// Use this variant when the contract needs to represent extension action completed; selecting it has no side effect by itself.
283    ExtensionActionCompleted,
284    /// Use this variant when the contract needs to represent extension action failed; selecting it has no side effect by itself.
285    ExtensionActionFailed,
286    /// Use this variant when the contract needs to represent extension action denied; selecting it has no side effect by itself.
287    ExtensionActionDenied,
288    /// Use this variant when the contract needs to represent output dispatch requested; selecting it has no side effect by itself.
289    OutputDispatchRequested,
290    /// Use this variant when the contract needs to represent output dispatch completed; selecting it has no side effect by itself.
291    OutputDispatchCompleted,
292    /// Use this variant when the contract needs to represent output dispatch failed; selecting it has no side effect by itself.
293    OutputDispatchFailed,
294    /// Use this variant when the contract needs to represent output dispatch deduped; selecting it has no side effect by itself.
295    OutputDispatchDeduped,
296    /// Use this variant when the contract needs to represent structured output requested; selecting it has no side effect by itself.
297    StructuredOutputRequested,
298    /// Use this variant when the contract needs to represent structured output validation started; selecting it has no side effect by itself.
299    StructuredOutputValidationStarted,
300    /// Use this variant when the contract needs to represent structured output validation failed; selecting it has no side effect by itself.
301    StructuredOutputValidationFailed,
302    /// Use this variant when the contract needs to represent structured output repair requested; selecting it has no side effect by itself.
303    StructuredOutputRepairRequested,
304    /// Use this variant when the contract needs to represent structured output validated; selecting it has no side effect by itself.
305    StructuredOutputValidated,
306    /// Use this variant when the contract needs to represent structured output failed; selecting it has no side effect by itself.
307    StructuredOutputFailed,
308    /// Use this variant when the contract needs to represent telemetry sink failed; selecting it has no side effect by itself.
309    TelemetrySinkFailed,
310    /// Use this variant when the contract needs to represent telemetry sink recovered; selecting it has no side effect by itself.
311    TelemetrySinkRecovered,
312    /// Use this variant when the contract needs to represent usage recorded; selecting it has no side effect by itself.
313    UsageRecorded,
314    /// Use this variant when the contract needs to represent cost estimated; selecting it has no side effect by itself.
315    CostEstimated,
316    /// Use this variant when the contract needs to represent cost corrected; selecting it has no side effect by itself.
317    CostCorrected,
318    /// Use this variant when the contract needs to represent replay started; selecting it has no side effect by itself.
319    ReplayStarted,
320    /// Use this variant when the contract needs to represent replay completed; selecting it has no side effect by itself.
321    ReplayCompleted,
322    /// Use this variant when the contract needs to represent replay failed; selecting it has no side effect by itself.
323    ReplayFailed,
324    /// Use this variant when the contract needs to represent agent pool created; selecting it has no side effect by itself.
325    AgentPoolCreated,
326    /// Use this variant when the contract needs to represent agent pool run joined; selecting it has no side effect by itself.
327    AgentPoolRunJoined,
328    /// Use this variant when the contract needs to represent agent pool run left; selecting it has no side effect by itself.
329    AgentPoolRunLeft,
330    /// Use this variant when the contract needs to represent run message accepted; selecting it has no side effect by itself.
331    RunMessageAccepted,
332    /// Use this variant when the contract needs to represent run message delivered; selecting it has no side effect by itself.
333    RunMessageDelivered,
334    /// Use this variant when the contract needs to represent run message responded; selecting it has no side effect by itself.
335    RunMessageResponded,
336    /// Use this variant when the contract needs to represent run message failed; selecting it has no side effect by itself.
337    RunMessageFailed,
338    /// Use this variant when the contract needs to represent run message timed out; selecting it has no side effect by itself.
339    RunMessageTimedOut,
340    /// Use this variant when the contract needs to represent run message expired; selecting it has no side effect by itself.
341    RunMessageExpired,
342    /// Use this variant when the contract needs to represent run message cancelled; selecting it has no side effect by itself.
343    RunMessageCancelled,
344    /// Use this variant when the contract needs to represent wake condition registered; selecting it has no side effect by itself.
345    WakeConditionRegistered,
346    /// Use this variant when the contract needs to represent wake condition triggered; selecting it has no side effect by itself.
347    WakeConditionTriggered,
348    /// Use this variant when the contract needs to represent wake condition timed out; selecting it has no side effect by itself.
349    WakeConditionTimedOut,
350    /// Use this variant when the contract needs to represent wake condition cancelled; selecting it has no side effect by itself.
351    WakeConditionCancelled,
352    /// Use this variant when the contract needs to represent wake condition failed; selecting it has no side effect by itself.
353    WakeConditionFailed,
354}
355
356impl EventKind {
357    /// Reports whether this value is terminal. The check is pure and
358    /// does not mutate SDK or host state.
359    pub fn is_terminal(&self) -> bool {
360        matches!(
361            self,
362            Self::RunCompleted
363                | Self::RunFailed
364                | Self::RunCancelled
365                | Self::TurnCompleted
366                | Self::TurnFailed
367                | Self::ModelMessageCompleted
368                | Self::ModelAttemptFailed
369                | Self::ToolCompleted
370                | Self::ToolFailed
371                | Self::ToolDenied
372                | Self::ToolRecoveryRequired
373                | Self::ApprovalResponded
374                | Self::OutputDispatchCompleted
375                | Self::OutputDispatchFailed
376                | Self::OutputDispatchDeduped
377                | Self::ApprovalDenied
378                | Self::ApprovalTimedOut
379                | Self::ApprovalCancelled
380                | Self::ApprovalDispatchUnavailable
381                | Self::HookCompleted
382                | Self::HookFailed
383                | Self::HookTimedOut
384                | Self::HookCancelled
385                | Self::HookResponseApplied
386                | Self::HookResponseRejected
387                | Self::RealtimeRestartFailed
388                | Self::RealtimeClosed
389                | Self::IsolationCleanupCompleted
390                | Self::IsolationCleanupFailed
391                | Self::IsolationFailed
392                | Self::ChildLifecycleCompleted
393                | Self::ChildLifecycleDetached
394                | Self::SubagentCompleted
395                | Self::ExtensionActionCompleted
396                | Self::ExtensionActionFailed
397                | Self::ExtensionActionDenied
398                | Self::StructuredOutputValidated
399                | Self::StructuredOutputFailed
400                | Self::ReplayCompleted
401                | Self::ReplayFailed
402                | Self::RunMessageResponded
403                | Self::RunMessageFailed
404                | Self::RunMessageTimedOut
405                | Self::RunMessageExpired
406                | Self::RunMessageCancelled
407                | Self::WakeConditionTriggered
408                | Self::WakeConditionTimedOut
409                | Self::WakeConditionCancelled
410                | Self::WakeConditionFailed
411        )
412    }
413}
414
415#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
416/// Carries the agent event record payload for journal, event, or fixture surfaces.
417/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
418pub struct AgentEvent {
419    /// Envelope used by this record or request.
420    pub envelope: EventEnvelope,
421    /// Payload carried by this record.
422    /// Use the surrounding policy and redaction fields to decide whether it can be exposed.
423    pub payload: EventPayload,
424}
425
426impl AgentEvent {
427    /// Builds the envelope only value.
428    /// This is data construction and performs no I/O, journal append, event publication, or
429    /// process work.
430    pub fn envelope_only(envelope: EventEnvelope) -> Self {
431        Self {
432            envelope,
433            payload: EventPayload::EnvelopeOnly,
434        }
435    }
436
437    /// Returns this value with its redacted summary setting replaced.
438    /// The method follows builder-style data construction and does not
439    /// execute external work.
440    pub fn with_redacted_summary(
441        envelope: EventEnvelope,
442        redacted_summary: impl Into<String>,
443    ) -> Self {
444        Self {
445            envelope,
446            payload: EventPayload::RedactedSummary {
447                redacted_summary: redacted_summary.into(),
448                payload_refs: Vec::new(),
449            },
450        }
451    }
452
453    /// Reads the stored redacted summary without registry or runtime work.
454    /// This is data-only and does not perform I/O, call host ports, append journals, publish
455    /// events, or start processes.
456    pub fn redacted_summary(&self) -> Option<&str> {
457        match &self.payload {
458            EventPayload::EnvelopeOnly => None,
459            EventPayload::RedactedSummary {
460                redacted_summary, ..
461            } => Some(redacted_summary.as_str()),
462        }
463    }
464}
465
466#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
467/// Carries the event envelope record payload for journal, event, or fixture surfaces.
468/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
469pub struct EventEnvelope {
470    /// Wire schema version used for compatibility checks.
471    pub schema_version: u16,
472    /// Event identifier used to correlate live events with journal or replay
473    /// evidence.
474    pub event_id: EventId,
475    /// Event seq used by this record or request.
476    pub event_seq: u64,
477    /// Event family used by this record or request.
478    pub event_family: EventFamily,
479    /// Kind discriminator for event kind.
480    /// Use it to route finite match arms without parsing display text.
481    pub event_kind: EventKind,
482    /// Wire schema version for this record shape.
483    /// Use it for compatibility checks before deserializing or replaying stored data.
484    pub payload_schema_version: u16,
485    /// Timestamp in milliseconds associated with this record.
486    /// Use it for ordering and diagnostics; durable causality still comes from ids and cursors.
487    pub timestamp: String,
488    /// Recorded at used by this record or request.
489    pub recorded_at: String,
490    /// Run identifier used for lineage, filtering, replay, and dedupe.
491    pub run_id: RunId,
492    #[serde(skip_serializing_if = "Option::is_none")]
493    /// Optional host-provided session identifier for grouping related turns.
494    pub session_id: Option<SessionId>,
495    /// Agent identifier used for lineage, filtering, and ownership checks.
496    pub agent_id: AgentId,
497    #[serde(skip_serializing_if = "Option::is_none")]
498    /// Turn identifier for one loop turn within a run.
499    pub turn_id: Option<TurnId>,
500    #[serde(skip_serializing_if = "Option::is_none")]
501    /// Attempt identifier for retry, repair, provider, or tool execution
502    /// evidence.
503    pub attempt_id: Option<AttemptId>,
504    #[serde(skip_serializing_if = "Option::is_none")]
505    /// Message identifier for transcript, projection, or provider-response
506    /// lineage.
507    pub message_id: Option<MessageId>,
508    #[serde(skip_serializing_if = "Option::is_none")]
509    /// Stable context item id used for typed lineage, lookup, or dedupe.
510    pub context_item_id: Option<ContextItemId>,
511    /// Stable trace id used for typed lineage, lookup, or dedupe.
512    pub trace_id: TraceId,
513    /// Stable span id used for typed lineage, lookup, or dedupe.
514    pub span_id: SpanId,
515    #[serde(skip_serializing_if = "Option::is_none")]
516    /// Stable parent event id used for typed lineage, lookup, or dedupe.
517    pub parent_event_id: Option<EventId>,
518    #[serde(skip_serializing_if = "Option::is_none")]
519    /// Optional caused by value.
520    /// When absent, callers should use the documented default or skip that optional behavior.
521    pub caused_by: Option<CausalRef>,
522    /// Typed subject ref reference. Resolving or executing it is a separate
523    /// policy-gated step.
524    pub subject_ref: EntityRef,
525    #[serde(default, skip_serializing_if = "Vec::is_empty")]
526    /// Typed related refs references. Resolving them is separate from
527    /// constructing this record.
528    pub related_refs: Vec<EntityRef>,
529    #[serde(default, skip_serializing_if = "Vec::is_empty")]
530    /// Typed causal refs references. Resolving them is separate from
531    /// constructing this record.
532    pub causal_refs: Vec<CausalRef>,
533    /// Correlation used by this record or request.
534    pub correlation: EventCorrelation,
535    #[serde(default, skip_serializing_if = "Vec::is_empty")]
536    /// Tag selector for event filtering.
537    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
538    pub tags: Vec<EventTag>,
539    /// Source label or ref for this item; it is metadata and does not fetch
540    /// content by itself.
541    pub source: SourceRef,
542    #[serde(skip_serializing_if = "Option::is_none")]
543    /// Destination label or ref for this item; it is metadata and does not
544    /// deliver content by itself.
545    pub destination: Option<DestinationRef>,
546    #[serde(default, skip_serializing_if = "Vec::is_empty")]
547    /// Policy references that govern admission, projection, execution, or
548    /// delivery.
549    pub policy_refs: Vec<PolicyRef>,
550    #[serde(skip_serializing_if = "Option::is_none")]
551    /// Cursor identifying a replay, export, or subscription position.
552    /// Use it to resume without widening the original scope.
553    pub journal_cursor: Option<JournalCursor>,
554    #[serde(skip_serializing_if = "Option::is_none")]
555    /// Optional state before value.
556    /// When absent, callers should use the documented default or skip that optional behavior.
557    pub state_before: Option<String>,
558    #[serde(skip_serializing_if = "Option::is_none")]
559    /// Optional state after value.
560    /// When absent, callers should use the documented default or skip that optional behavior.
561    pub state_after: Option<String>,
562    /// Delivery-semantic selector for event filtering.
563    /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
564    /// semantics.
565    pub delivery_semantics: EventDeliverySemantics,
566    /// Privacy class used for projection, telemetry, and raw-content access
567    /// decisions.
568    pub privacy: PrivacyClass,
569    /// Content capture used by this record or request.
570    pub content_capture: ContentCaptureMode,
571    /// Stable redaction policy id used for typed lineage, lookup, or dedupe.
572    pub redaction_policy_id: String,
573    /// Fingerprint of the runtime package snapshot in force when this value was produced.
574    /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
575    /// not execute package behavior.
576    pub runtime_package_fingerprint: String,
577}
578
579impl EventEnvelope {
580    /// Builds the cursor value.
581    /// This is data construction and performs no I/O, journal append, event publication, or
582    /// process work.
583    pub fn cursor(&self, scope: EventStreamScope) -> EventCursor {
584        EventCursor {
585            scope,
586            event_seq: self.event_seq,
587            event_id: self.event_id.clone(),
588            journal_cursor: self.journal_cursor.clone(),
589        }
590    }
591
592    /// Builds the redacted summary value.
593    /// This is data construction and performs no I/O, journal append, event publication, or
594    /// process work.
595    pub fn redacted_summary(&self) -> String {
596        format!("{:?}/{:?}", self.event_family, self.event_kind)
597    }
598}
599
600#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
601#[serde(tag = "mode", rename_all = "snake_case")]
602/// Enumerates the finite event payload cases.
603/// Serialized names are part of the SDK contract; update fixtures when variants change.
604pub enum EventPayload {
605    /// Use this variant when the contract needs to represent envelope only; selecting it has no side effect by itself.
606    EnvelopeOnly,
607    /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
608    RedactedSummary {
609        /// Redacted human-readable summary safe for events, telemetry, and
610        /// logs.
611        redacted_summary: String,
612        #[serde(default, skip_serializing_if = "Vec::is_empty")]
613        /// Typed payload refs references. Resolving them is separate from
614        /// constructing this record.
615        payload_refs: Vec<EntityRef>,
616    },
617}
618
619#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
620/// Carries the causal ref record payload for journal, event, or fixture surfaces.
621/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
622pub struct CausalRef {
623    #[serde(skip_serializing_if = "Option::is_none")]
624    /// Event identifier used to correlate live events with journal or replay
625    /// evidence.
626    pub event_id: Option<EventId>,
627    /// Typed subject ref reference. Resolving or executing it is a separate
628    /// policy-gated step.
629    pub subject_ref: EntityRef,
630    #[serde(skip_serializing_if = "Option::is_none")]
631    /// Redacted explanation for a denial, failure, status, or package delta.
632    pub reason: Option<String>,
633}
634
635#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
636/// Carries the event correlation record payload for journal, event, or fixture surfaces.
637/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
638pub struct EventCorrelation {
639    #[serde(default, skip_serializing_if = "Vec::is_empty")]
640    /// Bounded entries included in this record. Limits and truncation are
641    /// represented by companion metadata when applicable.
642    pub entries: Vec<CorrelationEntry>,
643}
644
645#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
646#[serde(transparent)]
647/// Carries the event tag record payload for journal, event, or fixture surfaces.
648/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
649pub struct EventTag(String);
650
651impl EventTag {
652    /// Creates a new records::event value with explicit caller-provided
653    /// inputs. This constructor is data-only and performs no I/O or
654    /// external side effects.
655    pub fn new(value: impl Into<String>) -> Self {
656        Self(value.into())
657    }
658
659    /// Returns this value as str. The accessor is side-effect free and
660    /// keeps ownership with the caller.
661    pub fn as_str(&self) -> &str {
662        &self.0
663    }
664}
665
666#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
667/// Carries the event frame record payload for journal, event, or fixture surfaces.
668/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
669pub struct EventFrame {
670    /// Event used by this record or request.
671    pub event: AgentEvent,
672    /// Cursor identifying a replay, export, or subscription position.
673    /// Use it to resume without widening the original scope.
674    pub cursor: EventCursor,
675    #[serde(skip_serializing_if = "Option::is_none")]
676    /// Cursor identifying a replay, export, or subscription position.
677    /// Use it to resume without widening the original scope.
678    pub archive_cursor: Option<ArchiveCursor>,
679    #[serde(skip_serializing_if = "Option::is_none")]
680    /// Overflow policy applied when a subscriber queue reaches capacity.
681    /// It decides whether to drop, summarize, backpressure, or fail the subscriber.
682    pub overflow: Option<EventOverflowNotice>,
683}
684
685#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
686/// Carries the event cursor record payload for journal, event, or fixture surfaces.
687/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
688pub struct EventCursor {
689    /// Scope used by this record or request.
690    pub scope: EventStreamScope,
691    /// Event seq used by this record or request.
692    pub event_seq: u64,
693    /// Event identifier used to correlate live events with journal or replay
694    /// evidence.
695    pub event_id: EventId,
696    #[serde(skip_serializing_if = "Option::is_none")]
697    /// Cursor identifying a replay, export, or subscription position.
698    /// Use it to resume without widening the original scope.
699    pub journal_cursor: Option<JournalCursor>,
700}
701
702#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
703#[serde(rename_all = "snake_case")]
704/// Enumerates the finite event stream scope cases.
705/// Serialized names are part of the SDK contract; update fixtures when variants change.
706pub enum EventStreamScope {
707    /// Use this variant when the contract needs to represent all; selecting it has no side effect by itself.
708    All,
709    /// Use this variant when the contract needs to represent run; selecting it has no side effect by itself.
710    Run(RunId),
711    /// Use this variant when the contract needs to represent agent; selecting it has no side effect by itself.
712    Agent(AgentId),
713    /// Use this variant when the contract needs to represent filter; selecting it has no side effect by itself.
714    Filter {
715        /// Stable filter id used for typed lineage, lookup, or dedupe.
716        filter_id: EventFilterId,
717        /// Deterministic filter fingerprint used for stale checks, package
718        /// evidence, or replay comparisons.
719        filter_fingerprint: EventFilterFingerprint,
720    },
721}
722
723#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
724/// Carries the archive cursor record payload for journal, event, or fixture surfaces.
725/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
726pub struct ArchiveCursor {
727    /// Stable archive id used for typed lineage, lookup, or dedupe.
728    pub archive_id: ArchiveCursorId,
729    /// Position used by this record or request.
730    pub position: String,
731    #[serde(skip_serializing_if = "Option::is_none")]
732    /// Event identifier used to correlate live events with journal or replay
733    /// evidence.
734    pub event_id: Option<EventId>,
735    #[serde(skip_serializing_if = "Option::is_none")]
736    /// Optional watermark value.
737    /// When absent, callers should use the documented default or skip that optional behavior.
738    pub watermark: Option<String>,
739}
740
741#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
742/// Carries the event overflow notice record payload for journal, event, or fixture surfaces.
743/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
744pub struct EventOverflowNotice {
745    /// Policy used by this record or request.
746    pub policy: SubscriberOverflowPolicy,
747    /// Count of dropped items observed or included in this record.
748    pub dropped_count: u64,
749    #[serde(skip_serializing_if = "Option::is_none")]
750    /// Optional gap start value.
751    /// When absent, callers should use the documented default or skip that optional behavior.
752    pub gap_start: Option<EventCursor>,
753    /// Gap end used by this record or request.
754    pub gap_end: EventCursor,
755    #[serde(skip_serializing_if = "Option::is_none")]
756    /// Repair policy used after structured output validation fails.
757    /// It controls whether repair is attempted and which policy gates must approve it.
758    pub repair_from: Option<JournalCursor>,
759    /// Whether terminal preserved is enabled.
760    /// Policy, validation, or routing code uses this flag to choose the explicit behavior.
761    pub terminal_preserved: bool,
762    /// Redacted explanation for a denial, failure, status, or package delta.
763    pub reason: EventOverflowReason,
764}
765
766#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
767#[serde(rename_all = "snake_case")]
768/// Enumerates the finite event overflow reason cases.
769/// Serialized names are part of the SDK contract; update fixtures when variants change.
770pub enum EventOverflowReason {
771    /// Use this variant when the contract needs to represent subscriber queue full; selecting it has no side effect by itself.
772    SubscriberQueueFull,
773    /// Use this variant when the contract needs to represent subscriber lagged; selecting it has no side effect by itself.
774    SubscriberLagged,
775    /// Use this variant when the contract needs to represent live buffer expired; selecting it has no side effect by itself.
776    LiveBufferExpired,
777    /// Use this variant when the contract needs to represent policy dropped progress; selecting it has no side effect by itself.
778    PolicyDroppedProgress,
779    /// Use this variant when the contract needs to represent policy dropped non terminal; selecting it has no side effect by itself.
780    PolicyDroppedNonTerminal,
781}
782
783#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
784#[serde(rename_all = "snake_case")]
785/// Enumerates the finite event delivery semantics cases.
786/// Serialized names are part of the SDK contract; update fixtures when variants change.
787pub enum EventDeliverySemantics {
788    /// Use this variant when the contract needs to represent best effort live; selecting it has no side effect by itself.
789    BestEffortLive,
790    /// Use this variant when the contract needs to represent journal backed; selecting it has no side effect by itself.
791    JournalBacked,
792    /// Use this variant when the contract needs to represent derived replay; selecting it has no side effect by itself.
793    DerivedReplay,
794    /// Use this variant when the contract needs to represent diagnostic only; selecting it has no side effect by itself.
795    DiagnosticOnly,
796}
797
798#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
799#[serde(rename_all = "snake_case")]
800/// Enumerates the finite content capture mode cases.
801/// Serialized names are part of the SDK contract; update fixtures when variants change.
802pub enum ContentCaptureMode {
803    /// Use this variant when the contract needs to represent off; selecting it has no side effect by itself.
804    Off,
805    /// Use this variant when the contract needs to represent metadata only; selecting it has no side effect by itself.
806    MetadataOnly,
807    /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
808    RedactedSummary,
809    /// Use this variant when the contract needs to represent payload refs; selecting it has no side effect by itself.
810    PayloadRefs,
811    /// Use this variant when the contract needs to represent raw content; selecting it has no side effect by itself.
812    RawContent,
813}
814
815#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
816#[serde(rename_all = "snake_case")]
817/// Enumerates the finite payload access mode cases.
818/// Serialized names are part of the SDK contract; update fixtures when variants change.
819pub enum PayloadAccessMode {
820    /// Use this variant when the contract needs to represent envelope only; selecting it has no side effect by itself.
821    EnvelopeOnly,
822    /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
823    RedactedSummary,
824    /// Use this variant when the contract needs to represent payload refs; selecting it has no side effect by itself.
825    PayloadRefs,
826    /// Use this variant when the contract needs to represent full payload if policy allows; selecting it has no side effect by itself.
827    FullPayloadIfPolicyAllows,
828}
829
830#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
831#[serde(rename_all = "snake_case")]
832/// Enumerates the finite subscriber overflow policy cases.
833/// Serialized names are part of the SDK contract; update fixtures when variants change.
834pub enum SubscriberOverflowPolicy {
835    /// Use this variant when the contract needs to represent drop non terminal; selecting it has no side effect by itself.
836    DropNonTerminal,
837    /// Use this variant when the contract needs to represent drop progress; selecting it has no side effect by itself.
838    DropProgress,
839    /// Use this variant when the contract needs to represent summarize and continue; selecting it has no side effect by itself.
840    SummarizeAndContinue,
841    /// Use this variant when the contract needs to represent backpressure caller; selecting it has no side effect by itself.
842    BackpressureCaller,
843    /// Use this variant when the contract needs to represent fail subscriber; selecting it has no side effect by itself.
844    FailSubscriber,
845}
846
847#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
848/// Carries the subscriber queue config record payload for journal, event, or fixture surfaces.
849/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
850pub struct SubscriberQueueConfig {
851    /// Total subscriber queue capacity.
852    /// This bounds buffered event frames for a live subscriber.
853    pub capacity: NonZeroUsize,
854    /// Queue slots reserved for terminal frames.
855    /// This keeps important terminal events available even when non-terminal frames overflow.
856    pub terminal_reserve: NonZeroUsize,
857    /// Overflow policy applied when a subscriber queue reaches capacity.
858    /// It decides whether to drop, summarize, backpressure, or fail the subscriber.
859    pub overflow: SubscriberOverflowPolicy,
860}
861
862impl Default for SubscriberQueueConfig {
863    fn default() -> Self {
864        Self {
865            capacity: NonZeroUsize::new(64).expect("nonzero default capacity"),
866            terminal_reserve: NonZeroUsize::new(1).expect("nonzero terminal reserve"),
867            overflow: SubscriberOverflowPolicy::DropNonTerminal,
868        }
869    }
870}
871
872#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
873#[serde(rename_all = "snake_case")]
874/// Enumerates the finite event filter set cases.
875/// Serialized names are part of the SDK contract; update fixtures when variants change.
876pub enum EventFilterSet<T> {
877    #[default]
878    /// Use this variant when the contract needs to represent any; selecting it has no side effect by itself.
879    Any,
880    /// Use this variant when the contract needs to represent include; selecting it has no side effect by itself.
881    Include(Vec<T>),
882}
883
884impl<T: PartialEq> EventFilterSet<T> {
885    /// Returns matches for the current value.
886    /// This is a read-only or data-construction helper unless the method body explicitly calls
887    /// a port or store.
888    pub fn matches(&self, candidate: &T) -> bool {
889        match self {
890            Self::Any => true,
891            Self::Include(values) => values.contains(candidate),
892        }
893    }
894
895    /// Reports whether this value is any. The check is pure and does
896    /// not mutate SDK or host state.
897    pub fn is_any(&self) -> bool {
898        matches!(self, Self::Any)
899    }
900}
901
902#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
903/// Carries the event filter record payload for journal, event, or fixture surfaces.
904/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
905pub struct EventFilter {
906    /// Run-id selector for event filtering.
907    /// `Any` leaves run ids unconstrained; `Include` restricts matches to the listed runs.
908    pub run_ids: EventFilterSet<RunId>,
909    /// Session-id selector for event filtering.
910    /// `Any` leaves session ids unconstrained; `Include` restricts matches to listed sessions.
911    pub session_ids: EventFilterSet<SessionId>,
912    /// Agent-id selector for event filtering.
913    /// `Any` leaves agent ids unconstrained; `Include` restricts matches to the listed agents.
914    pub agent_ids: EventFilterSet<AgentId>,
915    /// Turn-id selector for event filtering.
916    /// `Any` leaves turn ids unconstrained; `Include` restricts matches to the listed turns.
917    pub turn_ids: EventFilterSet<TurnId>,
918    /// Event-family selector for event filtering.
919    /// `Any` leaves event families unconstrained; `Include` restricts matches to listed
920    /// families.
921    pub families: EventFilterSet<EventFamily>,
922    /// Event-kind selector for event filtering.
923    /// `Any` leaves event kinds unconstrained; `Include` restricts matches to listed event
924    /// kinds.
925    pub kinds: EventFilterSet<EventKind>,
926    /// Source-kind selector for event filtering.
927    /// `Any` leaves source kinds unconstrained; `Include` restricts matches to listed source
928    /// kinds.
929    pub source_kinds: EventFilterSet<SourceKind>,
930    /// Destination-kind selector for event filtering.
931    /// `Any` leaves destination kinds unconstrained; `Include` restricts matches to listed
932    /// destination kinds.
933    pub destination_kinds: EventFilterSet<DestinationKind>,
934    /// Subject entity-kind selector for event filtering.
935    /// `Any` leaves subject kinds unconstrained; `Include` restricts matches to listed entity
936    /// kinds.
937    pub subject_kinds: EventFilterSet<EntityKind>,
938    /// Related-entity kind selector for event filtering.
939    /// `Any` leaves related kinds unconstrained; `Include` restricts matches to listed entity
940    /// kinds.
941    pub related_entity_kinds: EventFilterSet<EntityKind>,
942    /// Correlation-key selector for event filtering.
943    /// `Any` leaves correlation keys unconstrained; `Include` restricts matches to listed keys.
944    pub correlation_keys: EventFilterSet<crate::domain::CorrelationKey>,
945    /// Tag selector for event filtering.
946    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
947    pub tags: EventFilterSet<EventTag>,
948    /// Privacy-class selector for event filtering.
949    /// `Any` leaves privacy classes unconstrained; `Include` restricts matches to listed
950    /// classes.
951    pub privacy_classes: EventFilterSet<PrivacyClass>,
952    /// Delivery-semantic selector for event filtering.
953    /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
954    /// semantics.
955    pub delivery_semantics: EventFilterSet<EventDeliverySemantics>,
956    /// Whether the filter should match only terminal event frames.
957    /// When true, non-terminal frames are excluded even if all other selectors match.
958    pub terminal_only: bool,
959    /// Payload access mode allowed for matching event frames.
960    /// Use it to keep subscriptions envelope-only unless payload access is explicitly
961    /// requested.
962    pub payload_access: PayloadAccessMode,
963    /// Subscriber queue settings used for streams created with this filter.
964    /// It controls capacity, terminal reserve, and overflow behavior for the subscriber.
965    pub queue: SubscriberQueueConfig,
966}
967
968impl Default for EventFilter {
969    fn default() -> Self {
970        Self {
971            run_ids: EventFilterSet::Any,
972            session_ids: EventFilterSet::Any,
973            agent_ids: EventFilterSet::Any,
974            turn_ids: EventFilterSet::Any,
975            families: EventFilterSet::Any,
976            kinds: EventFilterSet::Any,
977            source_kinds: EventFilterSet::Any,
978            destination_kinds: EventFilterSet::Any,
979            subject_kinds: EventFilterSet::Any,
980            related_entity_kinds: EventFilterSet::Any,
981            correlation_keys: EventFilterSet::Any,
982            tags: EventFilterSet::Any,
983            privacy_classes: EventFilterSet::Any,
984            delivery_semantics: EventFilterSet::Any,
985            terminal_only: false,
986            payload_access: PayloadAccessMode::EnvelopeOnly,
987            queue: SubscriberQueueConfig::default(),
988        }
989    }
990}
991
992impl EventFilter {
993    /// Builds the terminal run events value.
994    /// This is data construction and performs no I/O, journal append, event publication, or
995    /// process work.
996    pub fn terminal_run_events() -> Self {
997        Self {
998            families: EventFilterSet::Include(vec![EventFamily::Run]),
999            terminal_only: true,
1000            ..Self::default()
1001        }
1002    }
1003
1004    /// Builds the run value with the documented defaults.
1005    /// This is data-only and does not perform I/O, call host ports, append journals, publish
1006    /// events, or start processes.
1007    pub fn run(run_id: RunId) -> Self {
1008        Self {
1009            run_ids: EventFilterSet::Include(vec![run_id]),
1010            ..Self::default()
1011        }
1012    }
1013
1014    /// Returns agent for the current value.
1015    /// This is a read-only or data-construction helper unless the method body explicitly calls
1016    /// a port or store.
1017    pub fn agent(agent_id: AgentId) -> Self {
1018        Self {
1019            agent_ids: EventFilterSet::Include(vec![agent_id]),
1020            ..Self::default()
1021        }
1022    }
1023
1024    /// Returns compile for the current value.
1025    /// This is a read-only or data-construction helper unless the method body explicitly calls
1026    /// a port or store.
1027    pub fn compile(self) -> Result<CompiledEventFilter, AgentError> {
1028        CompiledEventFilter::new(self)
1029    }
1030
1031    fn indexed_fields(&self) -> Vec<EventIndexField> {
1032        let mut fields = Vec::new();
1033        if !self.run_ids.is_any() {
1034            fields.push(EventIndexField::RunId);
1035        }
1036        if !self.session_ids.is_any() {
1037            fields.push(EventIndexField::SessionId);
1038        }
1039        if !self.agent_ids.is_any() {
1040            fields.push(EventIndexField::AgentId);
1041        }
1042        if !self.turn_ids.is_any() {
1043            fields.push(EventIndexField::TurnId);
1044        }
1045        if !self.families.is_any() {
1046            fields.push(EventIndexField::EventFamily);
1047        }
1048        if !self.kinds.is_any() {
1049            fields.push(EventIndexField::EventKind);
1050        }
1051        if !self.source_kinds.is_any() {
1052            fields.push(EventIndexField::Source);
1053        }
1054        if !self.destination_kinds.is_any() {
1055            fields.push(EventIndexField::Destination);
1056        }
1057        if !self.subject_kinds.is_any() {
1058            fields.push(EventIndexField::SubjectKind);
1059        }
1060        if !self.related_entity_kinds.is_any() {
1061            fields.push(EventIndexField::RelatedEntityKind);
1062        }
1063        if !self.correlation_keys.is_any() {
1064            fields.push(EventIndexField::CorrelationKey);
1065        }
1066        if !self.tags.is_any() {
1067            fields.push(EventIndexField::Tag);
1068        }
1069        if !self.privacy_classes.is_any() {
1070            fields.push(EventIndexField::Privacy);
1071        }
1072        if !self.delivery_semantics.is_any() {
1073            fields.push(EventIndexField::DeliverySemantics);
1074        }
1075        fields
1076    }
1077
1078    fn matches_envelope(&self, envelope: &EventEnvelope) -> bool {
1079        self.run_ids.matches(&envelope.run_id)
1080            && option_matches(&self.session_ids, envelope.session_id.as_ref())
1081            && self.agent_ids.matches(&envelope.agent_id)
1082            && option_matches(&self.turn_ids, envelope.turn_id.as_ref())
1083            && self.families.matches(&envelope.event_family)
1084            && self.kinds.matches(&envelope.event_kind)
1085            && self.source_kinds.matches(&envelope.source.kind)
1086            && option_matches(
1087                &self.destination_kinds,
1088                envelope
1089                    .destination
1090                    .as_ref()
1091                    .map(|destination| &destination.kind),
1092            )
1093            && self.subject_kinds.matches(&envelope.subject_ref.kind)
1094            && any_matches(
1095                &self.related_entity_kinds,
1096                envelope.related_refs.iter().map(|entity| &entity.kind),
1097            )
1098            && any_matches(
1099                &self.correlation_keys,
1100                envelope.correlation.entries.iter().map(|entry| &entry.key),
1101            )
1102            && any_matches(&self.tags, envelope.tags.iter())
1103            && self.privacy_classes.matches(&envelope.privacy)
1104            && self
1105                .delivery_semantics
1106                .matches(&envelope.delivery_semantics)
1107            && (!self.terminal_only || envelope.event_kind.is_terminal())
1108    }
1109}
1110
1111#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1112/// Carries the compiled event filter record payload for journal, event, or fixture surfaces.
1113/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
1114pub struct CompiledEventFilter {
1115    /// Stable filter id used for typed lineage, lookup, or dedupe.
1116    pub filter_id: EventFilterId,
1117    /// Deterministic filter fingerprint used for stale checks, package
1118    /// evidence, or replay comparisons.
1119    pub filter_fingerprint: EventFilterFingerprint,
1120    /// Collection of indexed fields values.
1121    /// Ordering and membership should be treated as part of the serialized contract when
1122    /// relevant.
1123    pub indexed_fields: Vec<EventIndexField>,
1124    /// Payload access mode allowed for matching event frames.
1125    /// Use it to keep subscriptions envelope-only unless payload access is explicitly
1126    /// requested.
1127    pub payload_access: PayloadAccessMode,
1128    /// Subscriber queue settings used for streams created with this filter.
1129    /// It controls capacity, terminal reserve, and overflow behavior for the subscriber.
1130    pub queue: SubscriberQueueConfig,
1131    criteria: EventFilter,
1132}
1133
1134impl CompiledEventFilter {
1135    /// Creates a new records::event value with explicit caller-provided
1136    /// inputs. This constructor is data-only and performs no I/O or
1137    /// external side effects.
1138    pub fn new(criteria: EventFilter) -> Result<Self, AgentError> {
1139        let encoded = serde_json::to_vec(&criteria)
1140            .map_err(|error| AgentError::contract_violation(error.to_string()))?;
1141        let fingerprint = format!("sha256:{:x}", Sha256::digest(encoded));
1142        let filter_id = EventFilterId::new(format!("filter.{:x}", Sha256::digest(&fingerprint)));
1143        let indexed_fields = criteria.indexed_fields();
1144
1145        Ok(Self {
1146            filter_id,
1147            filter_fingerprint: EventFilterFingerprint::new(fingerprint),
1148            indexed_fields,
1149            payload_access: criteria.payload_access.clone(),
1150            queue: criteria.queue.clone(),
1151            criteria,
1152        })
1153    }
1154
1155    /// Returns matches envelope for the current value.
1156    /// This is a read-only or data-construction helper unless the method body explicitly calls
1157    /// a port or store.
1158    pub fn matches_envelope(&self, envelope: &EventEnvelope) -> bool {
1159        self.criteria.matches_envelope(envelope)
1160    }
1161
1162    /// Returns cursor scope derived from the supplied state.
1163    /// This is data-only and does not perform I/O, call host ports, append journals, publish
1164    /// events, or start processes.
1165    pub fn cursor_scope(&self) -> EventStreamScope {
1166        EventStreamScope::Filter {
1167            filter_id: self.filter_id.clone(),
1168            filter_fingerprint: self.filter_fingerprint.clone(),
1169        }
1170    }
1171
1172    /// Returns the criteria currently held by this value.
1173    /// This is data-only and does not perform I/O, call host ports, append journals, publish
1174    /// events, or start processes.
1175    pub fn criteria(&self) -> &EventFilter {
1176        &self.criteria
1177    }
1178}
1179
1180fn option_matches<T: PartialEq>(filter: &EventFilterSet<T>, candidate: Option<&T>) -> bool {
1181    match filter {
1182        EventFilterSet::Any => true,
1183        EventFilterSet::Include(_) => candidate.is_some_and(|candidate| filter.matches(candidate)),
1184    }
1185}
1186
1187fn any_matches<'a, T: PartialEq + 'a>(
1188    filter: &EventFilterSet<T>,
1189    candidates: impl Iterator<Item = &'a T>,
1190) -> bool {
1191    match filter {
1192        EventFilterSet::Any => true,
1193        EventFilterSet::Include(_) => candidates
1194            .into_iter()
1195            .any(|candidate| filter.matches(candidate)),
1196    }
1197}
1198
1199#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1200#[serde(rename_all = "snake_case")]
1201/// Enumerates the finite event index field cases.
1202/// Serialized names are part of the SDK contract; update fixtures when variants change.
1203pub enum EventIndexField {
1204    /// Use this variant when the contract needs to represent run id; selecting it has no side effect by itself.
1205    RunId,
1206    /// Use this variant when the contract needs to represent session id; selecting it has no side effect by itself.
1207    SessionId,
1208    /// Use this variant when the contract needs to represent agent id; selecting it has no side effect by itself.
1209    AgentId,
1210    /// Use this variant when the contract needs to represent turn id; selecting it has no side effect by itself.
1211    TurnId,
1212    /// Use this variant when the contract needs to represent event family; selecting it has no side effect by itself.
1213    EventFamily,
1214    /// Use this variant when the contract needs to represent event kind; selecting it has no side effect by itself.
1215    EventKind,
1216    /// Use this variant when the contract needs to represent source; selecting it has no side effect by itself.
1217    Source,
1218    /// Use this variant when the contract needs to represent destination; selecting it has no side effect by itself.
1219    Destination,
1220    /// Use this variant when the contract needs to represent subject kind; selecting it has no side effect by itself.
1221    SubjectKind,
1222    /// Use this variant when the contract needs to represent related entity kind; selecting it has no side effect by itself.
1223    RelatedEntityKind,
1224    /// Use this variant when the contract needs to represent correlation key; selecting it has no side effect by itself.
1225    CorrelationKey,
1226    /// Use this variant when the contract needs to represent tag; selecting it has no side effect by itself.
1227    Tag,
1228    /// Use this variant when the contract needs to represent privacy; selecting it has no side effect by itself.
1229    Privacy,
1230    /// Use this variant when the contract needs to represent delivery semantics; selecting it has no side effect by itself.
1231    DeliverySemantics,
1232}
1233
1234typed_string!(EventFilterId, "EventFilterId");
1235typed_string!(EventFilterFingerprint, "EventFilterFingerprint");
1236
1237#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1238/// Carries the subscription options record payload for journal, event, or fixture surfaces.
1239/// Creating or cloning it only preserves serialized SDK state; append, publish, replay, or export effects are documented on the runtime and port methods that store it.
1240pub struct SubscriptionOptions {
1241    /// Subscriber queue settings used for streams created with this filter.
1242    /// It controls capacity, terminal reserve, and overflow behavior for the subscriber.
1243    pub queue: SubscriberQueueConfig,
1244    /// Payload access mode allowed for matching event frames.
1245    /// Use it to keep subscriptions envelope-only unless payload access is explicitly
1246    /// requested.
1247    pub payload_access: PayloadAccessMode,
1248}
1249
1250impl Default for SubscriptionOptions {
1251    fn default() -> Self {
1252        Self {
1253            queue: SubscriberQueueConfig::default(),
1254            payload_access: PayloadAccessMode::EnvelopeOnly,
1255        }
1256    }
1257}
1258
1259/// Returns cursor compatible derived from the supplied state.
1260/// This is data-only and does not perform I/O, call host ports, append journals, publish
1261/// events, or start processes.
1262pub fn cursor_compatible(
1263    requested_scope: &EventStreamScope,
1264    cursor: Option<&EventCursor>,
1265) -> Result<(), AgentError> {
1266    let Some(cursor) = cursor else {
1267        return Ok(());
1268    };
1269
1270    let compatible = match (requested_scope, &cursor.scope) {
1271        (EventStreamScope::All, EventStreamScope::All) => true,
1272        (EventStreamScope::Run(requested), EventStreamScope::Run(cursor_run)) => {
1273            requested == cursor_run
1274        }
1275        (EventStreamScope::Agent(requested), EventStreamScope::Agent(cursor_agent)) => {
1276            requested == cursor_agent
1277        }
1278        (
1279            EventStreamScope::Filter {
1280                filter_fingerprint: requested,
1281                ..
1282            },
1283            EventStreamScope::Filter {
1284                filter_fingerprint: cursor,
1285                ..
1286            },
1287        ) => requested == cursor,
1288        _ => false,
1289    };
1290
1291    if compatible {
1292        Ok(())
1293    } else {
1294        Err(AgentError::contract_violation(
1295            "cursor scope mismatch for requested event stream",
1296        ))
1297    }
1298}