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, 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 /// Agent identifier used for lineage, filtering, and ownership checks.
493 pub agent_id: AgentId,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 /// Turn identifier for one loop turn within a run.
496 pub turn_id: Option<TurnId>,
497 #[serde(skip_serializing_if = "Option::is_none")]
498 /// Attempt identifier for retry, repair, provider, or tool execution
499 /// evidence.
500 pub attempt_id: Option<AttemptId>,
501 #[serde(skip_serializing_if = "Option::is_none")]
502 /// Message identifier for transcript, projection, or provider-response
503 /// lineage.
504 pub message_id: Option<MessageId>,
505 #[serde(skip_serializing_if = "Option::is_none")]
506 /// Stable context item id used for typed lineage, lookup, or dedupe.
507 pub context_item_id: Option<ContextItemId>,
508 /// Stable trace id used for typed lineage, lookup, or dedupe.
509 pub trace_id: TraceId,
510 /// Stable span id used for typed lineage, lookup, or dedupe.
511 pub span_id: SpanId,
512 #[serde(skip_serializing_if = "Option::is_none")]
513 /// Stable parent event id used for typed lineage, lookup, or dedupe.
514 pub parent_event_id: Option<EventId>,
515 #[serde(skip_serializing_if = "Option::is_none")]
516 /// Optional caused by value.
517 /// When absent, callers should use the documented default or skip that optional behavior.
518 pub caused_by: Option<CausalRef>,
519 /// Typed subject ref reference. Resolving or executing it is a separate
520 /// policy-gated step.
521 pub subject_ref: EntityRef,
522 #[serde(default, skip_serializing_if = "Vec::is_empty")]
523 /// Typed related refs references. Resolving them is separate from
524 /// constructing this record.
525 pub related_refs: Vec<EntityRef>,
526 #[serde(default, skip_serializing_if = "Vec::is_empty")]
527 /// Typed causal refs references. Resolving them is separate from
528 /// constructing this record.
529 pub causal_refs: Vec<CausalRef>,
530 /// Correlation used by this record or request.
531 pub correlation: EventCorrelation,
532 #[serde(default, skip_serializing_if = "Vec::is_empty")]
533 /// Tag selector for event filtering.
534 /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
535 pub tags: Vec<EventTag>,
536 /// Source label or ref for this item; it is metadata and does not fetch
537 /// content by itself.
538 pub source: SourceRef,
539 #[serde(skip_serializing_if = "Option::is_none")]
540 /// Destination label or ref for this item; it is metadata and does not
541 /// deliver content by itself.
542 pub destination: Option<DestinationRef>,
543 #[serde(default, skip_serializing_if = "Vec::is_empty")]
544 /// Policy references that govern admission, projection, execution, or
545 /// delivery.
546 pub policy_refs: Vec<PolicyRef>,
547 #[serde(skip_serializing_if = "Option::is_none")]
548 /// Cursor identifying a replay, export, or subscription position.
549 /// Use it to resume without widening the original scope.
550 pub journal_cursor: Option<JournalCursor>,
551 #[serde(skip_serializing_if = "Option::is_none")]
552 /// Optional state before value.
553 /// When absent, callers should use the documented default or skip that optional behavior.
554 pub state_before: Option<String>,
555 #[serde(skip_serializing_if = "Option::is_none")]
556 /// Optional state after value.
557 /// When absent, callers should use the documented default or skip that optional behavior.
558 pub state_after: Option<String>,
559 /// Delivery-semantic selector for event filtering.
560 /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
561 /// semantics.
562 pub delivery_semantics: EventDeliverySemantics,
563 /// Privacy class used for projection, telemetry, and raw-content access
564 /// decisions.
565 pub privacy: PrivacyClass,
566 /// Content capture used by this record or request.
567 pub content_capture: ContentCaptureMode,
568 /// Stable redaction policy id used for typed lineage, lookup, or dedupe.
569 pub redaction_policy_id: String,
570 /// Fingerprint of the runtime package snapshot in force when this value was produced.
571 /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
572 /// not execute package behavior.
573 pub runtime_package_fingerprint: String,
574}
575
576impl EventEnvelope {
577 /// Builds the cursor value.
578 /// This is data construction and performs no I/O, journal append, event publication, or
579 /// process work.
580 pub fn cursor(&self, scope: EventStreamScope) -> EventCursor {
581 EventCursor {
582 scope,
583 event_seq: self.event_seq,
584 event_id: self.event_id.clone(),
585 journal_cursor: self.journal_cursor.clone(),
586 }
587 }
588
589 /// Builds the redacted summary value.
590 /// This is data construction and performs no I/O, journal append, event publication, or
591 /// process work.
592 pub fn redacted_summary(&self) -> String {
593 format!("{:?}/{:?}", self.event_family, self.event_kind)
594 }
595}
596
597#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
598#[serde(tag = "mode", rename_all = "snake_case")]
599/// Enumerates the finite event payload cases.
600/// Serialized names are part of the SDK contract; update fixtures when variants change.
601pub enum EventPayload {
602 /// Use this variant when the contract needs to represent envelope only; selecting it has no side effect by itself.
603 EnvelopeOnly,
604 /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
605 RedactedSummary {
606 /// Redacted human-readable summary safe for events, telemetry, and
607 /// logs.
608 redacted_summary: String,
609 #[serde(default, skip_serializing_if = "Vec::is_empty")]
610 /// Typed payload refs references. Resolving them is separate from
611 /// constructing this record.
612 payload_refs: Vec<EntityRef>,
613 },
614}
615
616#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
617/// Carries the causal ref record payload for journal, event, or fixture surfaces.
618/// 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.
619pub struct CausalRef {
620 #[serde(skip_serializing_if = "Option::is_none")]
621 /// Event identifier used to correlate live events with journal or replay
622 /// evidence.
623 pub event_id: Option<EventId>,
624 /// Typed subject ref reference. Resolving or executing it is a separate
625 /// policy-gated step.
626 pub subject_ref: EntityRef,
627 #[serde(skip_serializing_if = "Option::is_none")]
628 /// Redacted explanation for a denial, failure, status, or package delta.
629 pub reason: Option<String>,
630}
631
632#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
633/// Carries the event correlation record payload for journal, event, or fixture surfaces.
634/// 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.
635pub struct EventCorrelation {
636 #[serde(default, skip_serializing_if = "Vec::is_empty")]
637 /// Bounded entries included in this record. Limits and truncation are
638 /// represented by companion metadata when applicable.
639 pub entries: Vec<CorrelationEntry>,
640}
641
642#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
643#[serde(transparent)]
644/// Carries the event tag record payload for journal, event, or fixture surfaces.
645/// 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.
646pub struct EventTag(String);
647
648impl EventTag {
649 /// Creates a new records::event value with explicit caller-provided
650 /// inputs. This constructor is data-only and performs no I/O or
651 /// external side effects.
652 pub fn new(value: impl Into<String>) -> Self {
653 Self(value.into())
654 }
655
656 /// Returns this value as str. The accessor is side-effect free and
657 /// keeps ownership with the caller.
658 pub fn as_str(&self) -> &str {
659 &self.0
660 }
661}
662
663#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
664/// Carries the event frame record payload for journal, event, or fixture surfaces.
665/// 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.
666pub struct EventFrame {
667 /// Event used by this record or request.
668 pub event: AgentEvent,
669 /// Cursor identifying a replay, export, or subscription position.
670 /// Use it to resume without widening the original scope.
671 pub cursor: EventCursor,
672 #[serde(skip_serializing_if = "Option::is_none")]
673 /// Cursor identifying a replay, export, or subscription position.
674 /// Use it to resume without widening the original scope.
675 pub archive_cursor: Option<ArchiveCursor>,
676 #[serde(skip_serializing_if = "Option::is_none")]
677 /// Overflow policy applied when a subscriber queue reaches capacity.
678 /// It decides whether to drop, summarize, backpressure, or fail the subscriber.
679 pub overflow: Option<EventOverflowNotice>,
680}
681
682#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
683/// Carries the event cursor record payload for journal, event, or fixture surfaces.
684/// 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.
685pub struct EventCursor {
686 /// Scope used by this record or request.
687 pub scope: EventStreamScope,
688 /// Event seq used by this record or request.
689 pub event_seq: u64,
690 /// Event identifier used to correlate live events with journal or replay
691 /// evidence.
692 pub event_id: EventId,
693 #[serde(skip_serializing_if = "Option::is_none")]
694 /// Cursor identifying a replay, export, or subscription position.
695 /// Use it to resume without widening the original scope.
696 pub journal_cursor: Option<JournalCursor>,
697}
698
699#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
700#[serde(rename_all = "snake_case")]
701/// Enumerates the finite event stream scope cases.
702/// Serialized names are part of the SDK contract; update fixtures when variants change.
703pub enum EventStreamScope {
704 /// Use this variant when the contract needs to represent all; selecting it has no side effect by itself.
705 All,
706 /// Use this variant when the contract needs to represent run; selecting it has no side effect by itself.
707 Run(RunId),
708 /// Use this variant when the contract needs to represent agent; selecting it has no side effect by itself.
709 Agent(AgentId),
710 /// Use this variant when the contract needs to represent filter; selecting it has no side effect by itself.
711 Filter {
712 /// Stable filter id used for typed lineage, lookup, or dedupe.
713 filter_id: EventFilterId,
714 /// Deterministic filter fingerprint used for stale checks, package
715 /// evidence, or replay comparisons.
716 filter_fingerprint: EventFilterFingerprint,
717 },
718}
719
720#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
721/// Carries the archive cursor record payload for journal, event, or fixture surfaces.
722/// 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.
723pub struct ArchiveCursor {
724 /// Stable archive id used for typed lineage, lookup, or dedupe.
725 pub archive_id: ArchiveCursorId,
726 /// Position used by this record or request.
727 pub position: String,
728 #[serde(skip_serializing_if = "Option::is_none")]
729 /// Event identifier used to correlate live events with journal or replay
730 /// evidence.
731 pub event_id: Option<EventId>,
732 #[serde(skip_serializing_if = "Option::is_none")]
733 /// Optional watermark value.
734 /// When absent, callers should use the documented default or skip that optional behavior.
735 pub watermark: Option<String>,
736}
737
738#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
739/// Carries the event overflow notice record payload for journal, event, or fixture surfaces.
740/// 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.
741pub struct EventOverflowNotice {
742 /// Policy used by this record or request.
743 pub policy: SubscriberOverflowPolicy,
744 /// Count of dropped items observed or included in this record.
745 pub dropped_count: u64,
746 #[serde(skip_serializing_if = "Option::is_none")]
747 /// Optional gap start value.
748 /// When absent, callers should use the documented default or skip that optional behavior.
749 pub gap_start: Option<EventCursor>,
750 /// Gap end used by this record or request.
751 pub gap_end: EventCursor,
752 #[serde(skip_serializing_if = "Option::is_none")]
753 /// Repair policy used after structured output validation fails.
754 /// It controls whether repair is attempted and which policy gates must approve it.
755 pub repair_from: Option<JournalCursor>,
756 /// Whether terminal preserved is enabled.
757 /// Policy, validation, or routing code uses this flag to choose the explicit behavior.
758 pub terminal_preserved: bool,
759 /// Redacted explanation for a denial, failure, status, or package delta.
760 pub reason: EventOverflowReason,
761}
762
763#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
764#[serde(rename_all = "snake_case")]
765/// Enumerates the finite event overflow reason cases.
766/// Serialized names are part of the SDK contract; update fixtures when variants change.
767pub enum EventOverflowReason {
768 /// Use this variant when the contract needs to represent subscriber queue full; selecting it has no side effect by itself.
769 SubscriberQueueFull,
770 /// Use this variant when the contract needs to represent subscriber lagged; selecting it has no side effect by itself.
771 SubscriberLagged,
772 /// Use this variant when the contract needs to represent live buffer expired; selecting it has no side effect by itself.
773 LiveBufferExpired,
774 /// Use this variant when the contract needs to represent policy dropped progress; selecting it has no side effect by itself.
775 PolicyDroppedProgress,
776 /// Use this variant when the contract needs to represent policy dropped non terminal; selecting it has no side effect by itself.
777 PolicyDroppedNonTerminal,
778}
779
780#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
781#[serde(rename_all = "snake_case")]
782/// Enumerates the finite event delivery semantics cases.
783/// Serialized names are part of the SDK contract; update fixtures when variants change.
784pub enum EventDeliverySemantics {
785 /// Use this variant when the contract needs to represent best effort live; selecting it has no side effect by itself.
786 BestEffortLive,
787 /// Use this variant when the contract needs to represent journal backed; selecting it has no side effect by itself.
788 JournalBacked,
789 /// Use this variant when the contract needs to represent derived replay; selecting it has no side effect by itself.
790 DerivedReplay,
791 /// Use this variant when the contract needs to represent diagnostic only; selecting it has no side effect by itself.
792 DiagnosticOnly,
793}
794
795#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
796#[serde(rename_all = "snake_case")]
797/// Enumerates the finite content capture mode cases.
798/// Serialized names are part of the SDK contract; update fixtures when variants change.
799pub enum ContentCaptureMode {
800 /// Use this variant when the contract needs to represent off; selecting it has no side effect by itself.
801 Off,
802 /// Use this variant when the contract needs to represent metadata only; selecting it has no side effect by itself.
803 MetadataOnly,
804 /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
805 RedactedSummary,
806 /// Use this variant when the contract needs to represent payload refs; selecting it has no side effect by itself.
807 PayloadRefs,
808 /// Use this variant when the contract needs to represent raw content; selecting it has no side effect by itself.
809 RawContent,
810}
811
812#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
813#[serde(rename_all = "snake_case")]
814/// Enumerates the finite payload access mode cases.
815/// Serialized names are part of the SDK contract; update fixtures when variants change.
816pub enum PayloadAccessMode {
817 /// Use this variant when the contract needs to represent envelope only; selecting it has no side effect by itself.
818 EnvelopeOnly,
819 /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
820 RedactedSummary,
821 /// Use this variant when the contract needs to represent payload refs; selecting it has no side effect by itself.
822 PayloadRefs,
823 /// Use this variant when the contract needs to represent full payload if policy allows; selecting it has no side effect by itself.
824 FullPayloadIfPolicyAllows,
825}
826
827#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
828#[serde(rename_all = "snake_case")]
829/// Enumerates the finite subscriber overflow policy cases.
830/// Serialized names are part of the SDK contract; update fixtures when variants change.
831pub enum SubscriberOverflowPolicy {
832 /// Use this variant when the contract needs to represent drop non terminal; selecting it has no side effect by itself.
833 DropNonTerminal,
834 /// Use this variant when the contract needs to represent drop progress; selecting it has no side effect by itself.
835 DropProgress,
836 /// Use this variant when the contract needs to represent summarize and continue; selecting it has no side effect by itself.
837 SummarizeAndContinue,
838 /// Use this variant when the contract needs to represent backpressure caller; selecting it has no side effect by itself.
839 BackpressureCaller,
840 /// Use this variant when the contract needs to represent fail subscriber; selecting it has no side effect by itself.
841 FailSubscriber,
842}
843
844#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
845/// Carries the subscriber queue config record payload for journal, event, or fixture surfaces.
846/// 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.
847pub struct SubscriberQueueConfig {
848 /// Total subscriber queue capacity.
849 /// This bounds buffered event frames for a live subscriber.
850 pub capacity: NonZeroUsize,
851 /// Queue slots reserved for terminal frames.
852 /// This keeps important terminal events available even when non-terminal frames overflow.
853 pub terminal_reserve: NonZeroUsize,
854 /// Overflow policy applied when a subscriber queue reaches capacity.
855 /// It decides whether to drop, summarize, backpressure, or fail the subscriber.
856 pub overflow: SubscriberOverflowPolicy,
857}
858
859impl Default for SubscriberQueueConfig {
860 fn default() -> Self {
861 Self {
862 capacity: NonZeroUsize::new(64).expect("nonzero default capacity"),
863 terminal_reserve: NonZeroUsize::new(1).expect("nonzero terminal reserve"),
864 overflow: SubscriberOverflowPolicy::DropNonTerminal,
865 }
866 }
867}
868
869#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
870#[serde(rename_all = "snake_case")]
871/// Enumerates the finite event filter set cases.
872/// Serialized names are part of the SDK contract; update fixtures when variants change.
873pub enum EventFilterSet<T> {
874 #[default]
875 /// Use this variant when the contract needs to represent any; selecting it has no side effect by itself.
876 Any,
877 /// Use this variant when the contract needs to represent include; selecting it has no side effect by itself.
878 Include(Vec<T>),
879}
880
881impl<T: PartialEq> EventFilterSet<T> {
882 /// Returns matches for the current value.
883 /// This is a read-only or data-construction helper unless the method body explicitly calls
884 /// a port or store.
885 pub fn matches(&self, candidate: &T) -> bool {
886 match self {
887 Self::Any => true,
888 Self::Include(values) => values.contains(candidate),
889 }
890 }
891
892 /// Reports whether this value is any. The check is pure and does
893 /// not mutate SDK or host state.
894 pub fn is_any(&self) -> bool {
895 matches!(self, Self::Any)
896 }
897}
898
899#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
900/// Carries the event filter record payload for journal, event, or fixture surfaces.
901/// 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.
902pub struct EventFilter {
903 /// Run-id selector for event filtering.
904 /// `Any` leaves run ids unconstrained; `Include` restricts matches to the listed runs.
905 pub run_ids: EventFilterSet<RunId>,
906 /// Agent-id selector for event filtering.
907 /// `Any` leaves agent ids unconstrained; `Include` restricts matches to the listed agents.
908 pub agent_ids: EventFilterSet<AgentId>,
909 /// Turn-id selector for event filtering.
910 /// `Any` leaves turn ids unconstrained; `Include` restricts matches to the listed turns.
911 pub turn_ids: EventFilterSet<TurnId>,
912 /// Event-family selector for event filtering.
913 /// `Any` leaves event families unconstrained; `Include` restricts matches to listed
914 /// families.
915 pub families: EventFilterSet<EventFamily>,
916 /// Event-kind selector for event filtering.
917 /// `Any` leaves event kinds unconstrained; `Include` restricts matches to listed event
918 /// kinds.
919 pub kinds: EventFilterSet<EventKind>,
920 /// Source-kind selector for event filtering.
921 /// `Any` leaves source kinds unconstrained; `Include` restricts matches to listed source
922 /// kinds.
923 pub source_kinds: EventFilterSet<SourceKind>,
924 /// Destination-kind selector for event filtering.
925 /// `Any` leaves destination kinds unconstrained; `Include` restricts matches to listed
926 /// destination kinds.
927 pub destination_kinds: EventFilterSet<DestinationKind>,
928 /// Subject entity-kind selector for event filtering.
929 /// `Any` leaves subject kinds unconstrained; `Include` restricts matches to listed entity
930 /// kinds.
931 pub subject_kinds: EventFilterSet<EntityKind>,
932 /// Related-entity kind selector for event filtering.
933 /// `Any` leaves related kinds unconstrained; `Include` restricts matches to listed entity
934 /// kinds.
935 pub related_entity_kinds: EventFilterSet<EntityKind>,
936 /// Correlation-key selector for event filtering.
937 /// `Any` leaves correlation keys unconstrained; `Include` restricts matches to listed keys.
938 pub correlation_keys: EventFilterSet<crate::domain::CorrelationKey>,
939 /// Tag selector for event filtering.
940 /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
941 pub tags: EventFilterSet<EventTag>,
942 /// Privacy-class selector for event filtering.
943 /// `Any` leaves privacy classes unconstrained; `Include` restricts matches to listed
944 /// classes.
945 pub privacy_classes: EventFilterSet<PrivacyClass>,
946 /// Delivery-semantic selector for event filtering.
947 /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
948 /// semantics.
949 pub delivery_semantics: EventFilterSet<EventDeliverySemantics>,
950 /// Whether the filter should match only terminal event frames.
951 /// When true, non-terminal frames are excluded even if all other selectors match.
952 pub terminal_only: bool,
953 /// Payload access mode allowed for matching event frames.
954 /// Use it to keep subscriptions envelope-only unless payload access is explicitly
955 /// requested.
956 pub payload_access: PayloadAccessMode,
957 /// Subscriber queue settings used for streams created with this filter.
958 /// It controls capacity, terminal reserve, and overflow behavior for the subscriber.
959 pub queue: SubscriberQueueConfig,
960}
961
962impl Default for EventFilter {
963 fn default() -> Self {
964 Self {
965 run_ids: EventFilterSet::Any,
966 agent_ids: EventFilterSet::Any,
967 turn_ids: EventFilterSet::Any,
968 families: EventFilterSet::Any,
969 kinds: EventFilterSet::Any,
970 source_kinds: EventFilterSet::Any,
971 destination_kinds: EventFilterSet::Any,
972 subject_kinds: EventFilterSet::Any,
973 related_entity_kinds: EventFilterSet::Any,
974 correlation_keys: EventFilterSet::Any,
975 tags: EventFilterSet::Any,
976 privacy_classes: EventFilterSet::Any,
977 delivery_semantics: EventFilterSet::Any,
978 terminal_only: false,
979 payload_access: PayloadAccessMode::EnvelopeOnly,
980 queue: SubscriberQueueConfig::default(),
981 }
982 }
983}
984
985impl EventFilter {
986 /// Builds the terminal run events value.
987 /// This is data construction and performs no I/O, journal append, event publication, or
988 /// process work.
989 pub fn terminal_run_events() -> Self {
990 Self {
991 families: EventFilterSet::Include(vec![EventFamily::Run]),
992 terminal_only: true,
993 ..Self::default()
994 }
995 }
996
997 /// Builds the run value with the documented defaults.
998 /// This is data-only and does not perform I/O, call host ports, append journals, publish
999 /// events, or start processes.
1000 pub fn run(run_id: RunId) -> Self {
1001 Self {
1002 run_ids: EventFilterSet::Include(vec![run_id]),
1003 ..Self::default()
1004 }
1005 }
1006
1007 /// Returns agent for the current value.
1008 /// This is a read-only or data-construction helper unless the method body explicitly calls
1009 /// a port or store.
1010 pub fn agent(agent_id: AgentId) -> Self {
1011 Self {
1012 agent_ids: EventFilterSet::Include(vec![agent_id]),
1013 ..Self::default()
1014 }
1015 }
1016
1017 /// Returns compile for the current value.
1018 /// This is a read-only or data-construction helper unless the method body explicitly calls
1019 /// a port or store.
1020 pub fn compile(self) -> Result<CompiledEventFilter, AgentError> {
1021 CompiledEventFilter::new(self)
1022 }
1023
1024 fn indexed_fields(&self) -> Vec<EventIndexField> {
1025 let mut fields = Vec::new();
1026 if !self.run_ids.is_any() {
1027 fields.push(EventIndexField::RunId);
1028 }
1029 if !self.agent_ids.is_any() {
1030 fields.push(EventIndexField::AgentId);
1031 }
1032 if !self.turn_ids.is_any() {
1033 fields.push(EventIndexField::TurnId);
1034 }
1035 if !self.families.is_any() {
1036 fields.push(EventIndexField::EventFamily);
1037 }
1038 if !self.kinds.is_any() {
1039 fields.push(EventIndexField::EventKind);
1040 }
1041 if !self.source_kinds.is_any() {
1042 fields.push(EventIndexField::Source);
1043 }
1044 if !self.destination_kinds.is_any() {
1045 fields.push(EventIndexField::Destination);
1046 }
1047 if !self.subject_kinds.is_any() {
1048 fields.push(EventIndexField::SubjectKind);
1049 }
1050 if !self.related_entity_kinds.is_any() {
1051 fields.push(EventIndexField::RelatedEntityKind);
1052 }
1053 if !self.correlation_keys.is_any() {
1054 fields.push(EventIndexField::CorrelationKey);
1055 }
1056 if !self.tags.is_any() {
1057 fields.push(EventIndexField::Tag);
1058 }
1059 if !self.privacy_classes.is_any() {
1060 fields.push(EventIndexField::Privacy);
1061 }
1062 if !self.delivery_semantics.is_any() {
1063 fields.push(EventIndexField::DeliverySemantics);
1064 }
1065 fields
1066 }
1067
1068 fn matches_envelope(&self, envelope: &EventEnvelope) -> bool {
1069 self.run_ids.matches(&envelope.run_id)
1070 && self.agent_ids.matches(&envelope.agent_id)
1071 && option_matches(&self.turn_ids, envelope.turn_id.as_ref())
1072 && self.families.matches(&envelope.event_family)
1073 && self.kinds.matches(&envelope.event_kind)
1074 && self.source_kinds.matches(&envelope.source.kind)
1075 && option_matches(
1076 &self.destination_kinds,
1077 envelope
1078 .destination
1079 .as_ref()
1080 .map(|destination| &destination.kind),
1081 )
1082 && self.subject_kinds.matches(&envelope.subject_ref.kind)
1083 && any_matches(
1084 &self.related_entity_kinds,
1085 envelope.related_refs.iter().map(|entity| &entity.kind),
1086 )
1087 && any_matches(
1088 &self.correlation_keys,
1089 envelope.correlation.entries.iter().map(|entry| &entry.key),
1090 )
1091 && any_matches(&self.tags, envelope.tags.iter())
1092 && self.privacy_classes.matches(&envelope.privacy)
1093 && self
1094 .delivery_semantics
1095 .matches(&envelope.delivery_semantics)
1096 && (!self.terminal_only || envelope.event_kind.is_terminal())
1097 }
1098}
1099
1100#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1101/// Carries the compiled event filter record payload for journal, event, or fixture surfaces.
1102/// 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.
1103pub struct CompiledEventFilter {
1104 /// Stable filter id used for typed lineage, lookup, or dedupe.
1105 pub filter_id: EventFilterId,
1106 /// Deterministic filter fingerprint used for stale checks, package
1107 /// evidence, or replay comparisons.
1108 pub filter_fingerprint: EventFilterFingerprint,
1109 /// Collection of indexed fields values.
1110 /// Ordering and membership should be treated as part of the serialized contract when
1111 /// relevant.
1112 pub indexed_fields: Vec<EventIndexField>,
1113 /// Payload access mode allowed for matching event frames.
1114 /// Use it to keep subscriptions envelope-only unless payload access is explicitly
1115 /// requested.
1116 pub payload_access: PayloadAccessMode,
1117 /// Subscriber queue settings used for streams created with this filter.
1118 /// It controls capacity, terminal reserve, and overflow behavior for the subscriber.
1119 pub queue: SubscriberQueueConfig,
1120 criteria: EventFilter,
1121}
1122
1123impl CompiledEventFilter {
1124 /// Creates a new records::event value with explicit caller-provided
1125 /// inputs. This constructor is data-only and performs no I/O or
1126 /// external side effects.
1127 pub fn new(criteria: EventFilter) -> Result<Self, AgentError> {
1128 let encoded = serde_json::to_vec(&criteria)
1129 .map_err(|error| AgentError::contract_violation(error.to_string()))?;
1130 let fingerprint = format!("sha256:{:x}", Sha256::digest(encoded));
1131 let filter_id = EventFilterId::new(format!("filter.{:x}", Sha256::digest(&fingerprint)));
1132 let indexed_fields = criteria.indexed_fields();
1133
1134 Ok(Self {
1135 filter_id,
1136 filter_fingerprint: EventFilterFingerprint::new(fingerprint),
1137 indexed_fields,
1138 payload_access: criteria.payload_access.clone(),
1139 queue: criteria.queue.clone(),
1140 criteria,
1141 })
1142 }
1143
1144 /// Returns matches envelope for the current value.
1145 /// This is a read-only or data-construction helper unless the method body explicitly calls
1146 /// a port or store.
1147 pub fn matches_envelope(&self, envelope: &EventEnvelope) -> bool {
1148 self.criteria.matches_envelope(envelope)
1149 }
1150
1151 /// Returns cursor scope derived from the supplied state.
1152 /// This is data-only and does not perform I/O, call host ports, append journals, publish
1153 /// events, or start processes.
1154 pub fn cursor_scope(&self) -> EventStreamScope {
1155 EventStreamScope::Filter {
1156 filter_id: self.filter_id.clone(),
1157 filter_fingerprint: self.filter_fingerprint.clone(),
1158 }
1159 }
1160
1161 /// Returns the criteria currently held by this value.
1162 /// This is data-only and does not perform I/O, call host ports, append journals, publish
1163 /// events, or start processes.
1164 pub fn criteria(&self) -> &EventFilter {
1165 &self.criteria
1166 }
1167}
1168
1169fn option_matches<T: PartialEq>(filter: &EventFilterSet<T>, candidate: Option<&T>) -> bool {
1170 match filter {
1171 EventFilterSet::Any => true,
1172 EventFilterSet::Include(_) => candidate.is_some_and(|candidate| filter.matches(candidate)),
1173 }
1174}
1175
1176fn any_matches<'a, T: PartialEq + 'a>(
1177 filter: &EventFilterSet<T>,
1178 candidates: impl Iterator<Item = &'a T>,
1179) -> bool {
1180 match filter {
1181 EventFilterSet::Any => true,
1182 EventFilterSet::Include(_) => candidates
1183 .into_iter()
1184 .any(|candidate| filter.matches(candidate)),
1185 }
1186}
1187
1188#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1189#[serde(rename_all = "snake_case")]
1190/// Enumerates the finite event index field cases.
1191/// Serialized names are part of the SDK contract; update fixtures when variants change.
1192pub enum EventIndexField {
1193 /// Use this variant when the contract needs to represent run id; selecting it has no side effect by itself.
1194 RunId,
1195 /// Use this variant when the contract needs to represent agent id; selecting it has no side effect by itself.
1196 AgentId,
1197 /// Use this variant when the contract needs to represent turn id; selecting it has no side effect by itself.
1198 TurnId,
1199 /// Use this variant when the contract needs to represent event family; selecting it has no side effect by itself.
1200 EventFamily,
1201 /// Use this variant when the contract needs to represent event kind; selecting it has no side effect by itself.
1202 EventKind,
1203 /// Use this variant when the contract needs to represent source; selecting it has no side effect by itself.
1204 Source,
1205 /// Use this variant when the contract needs to represent destination; selecting it has no side effect by itself.
1206 Destination,
1207 /// Use this variant when the contract needs to represent subject kind; selecting it has no side effect by itself.
1208 SubjectKind,
1209 /// Use this variant when the contract needs to represent related entity kind; selecting it has no side effect by itself.
1210 RelatedEntityKind,
1211 /// Use this variant when the contract needs to represent correlation key; selecting it has no side effect by itself.
1212 CorrelationKey,
1213 /// Use this variant when the contract needs to represent tag; selecting it has no side effect by itself.
1214 Tag,
1215 /// Use this variant when the contract needs to represent privacy; selecting it has no side effect by itself.
1216 Privacy,
1217 /// Use this variant when the contract needs to represent delivery semantics; selecting it has no side effect by itself.
1218 DeliverySemantics,
1219}
1220
1221typed_string!(EventFilterId, "EventFilterId");
1222typed_string!(EventFilterFingerprint, "EventFilterFingerprint");
1223
1224#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1225/// Carries the subscription options record payload for journal, event, or fixture surfaces.
1226/// 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.
1227pub struct SubscriptionOptions {
1228 /// Subscriber queue settings used for streams created with this filter.
1229 /// It controls capacity, terminal reserve, and overflow behavior for the subscriber.
1230 pub queue: SubscriberQueueConfig,
1231 /// Payload access mode allowed for matching event frames.
1232 /// Use it to keep subscriptions envelope-only unless payload access is explicitly
1233 /// requested.
1234 pub payload_access: PayloadAccessMode,
1235}
1236
1237impl Default for SubscriptionOptions {
1238 fn default() -> Self {
1239 Self {
1240 queue: SubscriberQueueConfig::default(),
1241 payload_access: PayloadAccessMode::EnvelopeOnly,
1242 }
1243 }
1244}
1245
1246/// Returns cursor compatible derived from the supplied state.
1247/// This is data-only and does not perform I/O, call host ports, append journals, publish
1248/// events, or start processes.
1249pub fn cursor_compatible(
1250 requested_scope: &EventStreamScope,
1251 cursor: Option<&EventCursor>,
1252) -> Result<(), AgentError> {
1253 let Some(cursor) = cursor else {
1254 return Ok(());
1255 };
1256
1257 let compatible = match (requested_scope, &cursor.scope) {
1258 (EventStreamScope::All, EventStreamScope::All) => true,
1259 (EventStreamScope::Run(requested), EventStreamScope::Run(cursor_run)) => {
1260 requested == cursor_run
1261 }
1262 (EventStreamScope::Agent(requested), EventStreamScope::Agent(cursor_agent)) => {
1263 requested == cursor_agent
1264 }
1265 (
1266 EventStreamScope::Filter {
1267 filter_fingerprint: requested,
1268 ..
1269 },
1270 EventStreamScope::Filter {
1271 filter_fingerprint: cursor,
1272 ..
1273 },
1274 ) => requested == cursor,
1275 _ => false,
1276 };
1277
1278 if compatible {
1279 Ok(())
1280 } else {
1281 Err(AgentError::contract_violation(
1282 "cursor scope mismatch for requested event stream",
1283 ))
1284 }
1285}