Skip to main content

agent_sdk_core/records/
journal.rs

1//! Durable run-journal records. Use these records as replayable truth for messages,
2//! effects, checkpoints, output, and recovery. Record constructors are data-only;
3//! append side effects happen through journal ports.
4//!
5pub use crate::domain::JournalCursor;
6
7use serde::{Deserialize, Serialize};
8
9use crate::{
10    approval_records::ApprovalRecord,
11    domain::{
12        AgentError, AgentId, AgentPoolId, AttemptId, ContentRef, ContextProjectionId,
13        CorrelationEntry, DedupeKey, DestinationRef, EntityRef, IdempotencyKey, MessageId,
14        PolicyRef, PrivacyClass, RunId, SourceRef, TopicId, TurnId, WakeConditionId,
15    },
16    effect::{EffectIntent, EffectResult},
17    event::{EventCorrelation, EventFilterFingerprint},
18    extension_records::ExtensionActionRecord,
19    hook_records::HookRecord,
20    output_delivery::OutputDeliveryRecord,
21    provider::{ProviderStopReason, ProviderUsage},
22    realtime_records::RealtimeSessionRecord,
23    records_isolation::IsolationRecord,
24    stream_records::StreamRuleRecord,
25    structured_output::{
26        RepairExhaustionRecord, RepairRecord, StructuredOutputLifecycleRecord, ValidationRecord,
27    },
28    subagent_records::{ChildLifecycleRecord, SubagentRecord},
29    tool_records::ToolCallRecord,
30    validated_output::{TypedResultPublicationRecord, ValidatedOutput, ValidationReportRecord},
31};
32
33/// Constant value for the records::journal contract. Use it to keep SDK
34/// records and tests aligned on the same stable value.
35pub const JOURNAL_SCHEMA_VERSION: u16 = 1;
36
37#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
38#[serde(rename_all = "snake_case")]
39/// Enumerates the finite journal record kind cases.
40/// Serialized names are part of the SDK contract; update fixtures when variants change.
41pub enum JournalRecordKind {
42    /// Use this variant when the contract needs to represent run; selecting it has no side effect by itself.
43    Run,
44    /// Use this variant when the contract needs to represent turn; selecting it has no side effect by itself.
45    Turn,
46    /// Use this variant when the contract needs to represent context; selecting it has no side effect by itself.
47    Context,
48    /// Use this variant when the contract needs to represent message; selecting it has no side effect by itself.
49    Message,
50    /// Use this variant when the contract needs to represent model attempt; selecting it has no side effect by itself.
51    ModelAttempt,
52    /// Use this variant when the contract needs to represent structured output; selecting it has no side effect by itself.
53    StructuredOutput,
54    /// Use this variant when the contract needs to represent stream rule; selecting it has no side effect by itself.
55    StreamRule,
56    /// Use this variant when the contract needs to represent realtime session; selecting it has no side effect by itself.
57    RealtimeSession,
58    /// Use this variant when the contract needs to represent hook; selecting it has no side effect by itself.
59    Hook,
60    /// Use this variant when the contract needs to represent approval; selecting it has no side effect by itself.
61    Approval,
62    /// Use this variant when the contract needs to represent tool; selecting it has no side effect by itself.
63    Tool,
64    /// Use this variant when the contract needs to represent isolation; selecting it has no side effect by itself.
65    Isolation,
66    /// Use this variant when the contract needs to represent child lifecycle; selecting it has no side effect by itself.
67    ChildLifecycle,
68    /// Use this variant when the contract needs to represent agent pool; selecting it has no side effect by itself.
69    AgentPool,
70    /// Use this variant when the contract needs to represent run message; selecting it has no side effect by itself.
71    RunMessage,
72    /// Use this variant when the contract needs to represent wake; selecting it has no side effect by itself.
73    Wake,
74    /// Use this variant when the contract needs to represent output dispatch; selecting it has no side effect by itself.
75    OutputDispatch,
76    /// Use this variant when the contract needs to represent subagent; selecting it has no side effect by itself.
77    Subagent,
78    /// Use this variant when the contract needs to represent extension action; selecting it has no side effect by itself.
79    ExtensionAction,
80    /// Use this variant when the contract needs to represent telemetry; selecting it has no side effect by itself.
81    Telemetry,
82    /// Use this variant when the contract needs to represent recovery; selecting it has no side effect by itself.
83    Recovery,
84    /// Use this variant when the contract needs to represent checkpoint; selecting it has no side effect by itself.
85    Checkpoint,
86    /// Use this variant when the contract needs to represent effect intent; selecting it has no side effect by itself.
87    EffectIntent,
88    /// Use this variant when the contract needs to represent effect result; selecting it has no side effect by itself.
89    EffectResult,
90}
91
92#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
93/// Carries the event index projection record payload for journal, event, or fixture surfaces.
94/// 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.
95pub struct EventIndexProjection {
96    /// Run identifier used for lineage, filtering, replay, and dedupe.
97    pub run_id: RunId,
98    /// Agent identifier used for lineage, filtering, and ownership checks.
99    pub agent_id: AgentId,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    /// Turn identifier for one loop turn within a run.
102    pub turn_id: Option<TurnId>,
103    /// Event family used by this record or request.
104    pub event_family: String,
105    /// Kind discriminator for event kind.
106    /// Use it to route finite match arms without parsing display text.
107    pub event_kind: String,
108    /// Source label or ref for this item; it is metadata and does not fetch
109    /// content by itself.
110    pub source: SourceRef,
111    #[serde(skip_serializing_if = "Option::is_none")]
112    /// Destination label or ref for this item; it is metadata and does not
113    /// deliver content by itself.
114    pub destination: Option<DestinationRef>,
115    /// Typed subject ref reference. Resolving or executing it is a separate
116    /// policy-gated step.
117    pub subject_ref: EntityRef,
118    #[serde(default, skip_serializing_if = "Vec::is_empty")]
119    /// Typed related refs references. Resolving them is separate from
120    /// constructing this record.
121    pub related_refs: Vec<EntityRef>,
122    #[serde(default, skip_serializing_if = "Vec::is_empty")]
123    /// Correlation-key selector for event filtering.
124    /// `Any` leaves correlation keys unconstrained; `Include` restricts matches to listed keys.
125    pub correlation_keys: Vec<CorrelationEntry>,
126    #[serde(default, skip_serializing_if = "Vec::is_empty")]
127    /// Tag selector for event filtering.
128    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
129    pub tags: Vec<String>,
130    /// Privacy class used for projection, telemetry, and raw-content access
131    /// decisions.
132    pub privacy_class: PrivacyClass,
133    /// Delivery-semantic selector for event filtering.
134    /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
135    /// semantics.
136    pub delivery_semantics: String,
137}
138
139#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
140/// Carries the journal record record payload for journal, event, or fixture surfaces.
141/// 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.
142pub struct JournalRecord {
143    /// Wire schema version for this record shape.
144    /// Use it for compatibility checks before deserializing or replaying stored data.
145    pub journal_schema_version: u16,
146    /// Journal seq used by this record or request.
147    pub journal_seq: u64,
148    /// Stable record id used for typed lineage, lookup, or dedupe.
149    pub record_id: String,
150    /// Kind discriminator for record kind.
151    /// Use it to route finite match arms without parsing display text.
152    pub record_kind: JournalRecordKind,
153    /// Run identifier used for lineage, filtering, replay, and dedupe.
154    pub run_id: RunId,
155    /// Agent identifier used for lineage, filtering, and ownership checks.
156    pub agent_id: AgentId,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    /// Turn identifier for one loop turn within a run.
159    pub turn_id: Option<TurnId>,
160    #[serde(skip_serializing_if = "Option::is_none")]
161    /// Attempt identifier for retry, repair, provider, or tool execution
162    /// evidence.
163    pub attempt_id: Option<AttemptId>,
164    /// Typed subject ref reference. Resolving or executing it is a separate
165    /// policy-gated step.
166    pub subject_ref: EntityRef,
167    #[serde(default, skip_serializing_if = "Vec::is_empty")]
168    /// Typed related refs references. Resolving them is separate from
169    /// constructing this record.
170    pub related_refs: Vec<EntityRef>,
171    #[serde(default, skip_serializing_if = "Vec::is_empty")]
172    /// Typed causal refs references. Resolving them is separate from
173    /// constructing this record.
174    pub causal_refs: Vec<String>,
175    /// Source label or ref for this item; it is metadata and does not fetch
176    /// content by itself.
177    pub source: SourceRef,
178    #[serde(skip_serializing_if = "Option::is_none")]
179    /// Destination label or ref for this item; it is metadata and does not
180    /// deliver content by itself.
181    pub destination: Option<DestinationRef>,
182    #[serde(default, skip_serializing_if = "Vec::is_empty")]
183    /// Correlation-key selector for event filtering.
184    /// `Any` leaves correlation keys unconstrained; `Include` restricts matches to listed keys.
185    pub correlation_keys: Vec<CorrelationEntry>,
186    #[serde(default, skip_serializing_if = "Vec::is_empty")]
187    /// Tag selector for event filtering.
188    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
189    pub tags: Vec<String>,
190    /// Delivery-semantic selector for event filtering.
191    /// `Any` leaves delivery semantics unconstrained; `Include` restricts matches to listed
192    /// semantics.
193    pub delivery_semantics: String,
194    /// Event index used by this record or request.
195    pub event_index: EventIndexProjection,
196    /// Timestamp in milliseconds associated with this record.
197    /// Use it for ordering and diagnostics; durable causality still comes from ids and cursors.
198    pub timestamp_millis: u64,
199    /// Fingerprint of the runtime package snapshot in force when this value was produced.
200    /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
201    /// not execute package behavior.
202    pub runtime_package_fingerprint: String,
203    /// Privacy class used for projection, telemetry, and raw-content access
204    /// decisions.
205    pub privacy: PrivacyClass,
206    #[serde(default, skip_serializing_if = "Vec::is_empty")]
207    /// Content references associated with this record; resolving them is a
208    /// separate policy-gated step.
209    pub content_refs: Vec<ContentRef>,
210    /// Stable redaction policy id used for typed lineage, lookup, or dedupe.
211    pub redaction_policy_id: String,
212    #[serde(skip_serializing_if = "Option::is_none")]
213    /// Idempotency setting or key for deduping retries.
214    /// Use it to prevent duplicate side effects during replay or repair.
215    pub idempotency_key: Option<IdempotencyKey>,
216    #[serde(skip_serializing_if = "Option::is_none")]
217    /// Dedupe policy or key for a side-effecting operation.
218    /// Replay and repair use it to avoid sending or executing the same effect twice.
219    pub dedupe_key: Option<DedupeKey>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    /// Typed checkpoint ref reference. Resolving or executing it is a
222    /// separate policy-gated step.
223    pub checkpoint_ref: Option<String>,
224    /// Payload carried by this record.
225    /// Use the surrounding policy and redaction fields to decide whether it can be exposed.
226    pub payload: JournalRecordPayload,
227}
228
229impl JournalRecord {
230    /// Returns an updated records::journal value with effect intent applied.
231    /// This is data construction only and does not execute the configured
232    /// behavior.
233    pub fn effect_intent(base: JournalRecordBase, intent: EffectIntent) -> Self {
234        let mut event_index = base.event_index("effect", "intent");
235        let effect_ref =
236            EntityRef::new(crate::domain::EntityKind::Effect, intent.effect_id.clone());
237        event_index.subject_ref = intent.subject_ref.clone();
238        event_index.related_refs = vec![effect_ref.clone()];
239        Self {
240            journal_schema_version: JOURNAL_SCHEMA_VERSION,
241            journal_seq: base.journal_seq,
242            record_id: base.record_id,
243            record_kind: JournalRecordKind::EffectIntent,
244            run_id: base.run_id,
245            agent_id: base.agent_id,
246            turn_id: base.turn_id,
247            attempt_id: base.attempt_id,
248            subject_ref: intent.subject_ref.clone(),
249            related_refs: vec![effect_ref],
250            causal_refs: base.causal_refs,
251            source: intent.source.clone(),
252            destination: intent.destination.clone(),
253            correlation_keys: Vec::new(),
254            tags: base.tags,
255            delivery_semantics: "journal_backed".to_string(),
256            event_index,
257            timestamp_millis: base.timestamp_millis,
258            runtime_package_fingerprint: base.runtime_package_fingerprint,
259            privacy: base.privacy,
260            content_refs: intent.content_refs.clone(),
261            redaction_policy_id: base.redaction_policy_id,
262            idempotency_key: intent.idempotency_key.clone(),
263            dedupe_key: intent.dedupe_key.clone(),
264            checkpoint_ref: base.checkpoint_ref,
265            payload: JournalRecordPayload::EffectIntent(intent),
266        }
267    }
268
269    /// Builds the effect result record or result value.
270    /// This is data-only and does not perform I/O, call host ports, append journals, publish
271    /// events, or start processes.
272    pub fn effect_result(base: JournalRecordBase, result: EffectResult) -> Self {
273        let effect_ref =
274            EntityRef::new(crate::domain::EntityKind::Effect, result.effect_id.clone());
275        let mut event_index = base.event_index("effect", "result");
276        event_index.subject_ref = effect_ref.clone();
277        event_index.related_refs = vec![effect_ref.clone()];
278        Self {
279            journal_schema_version: JOURNAL_SCHEMA_VERSION,
280            journal_seq: base.journal_seq,
281            record_id: base.record_id,
282            record_kind: JournalRecordKind::EffectResult,
283            run_id: base.run_id,
284            agent_id: base.agent_id,
285            turn_id: base.turn_id,
286            attempt_id: base.attempt_id,
287            subject_ref: effect_ref.clone(),
288            related_refs: vec![effect_ref],
289            causal_refs: base.causal_refs,
290            source: base.source,
291            destination: base.destination,
292            correlation_keys: Vec::new(),
293            tags: base.tags,
294            delivery_semantics: "journal_backed".to_string(),
295            event_index,
296            timestamp_millis: base.timestamp_millis,
297            runtime_package_fingerprint: base.runtime_package_fingerprint,
298            privacy: base.privacy,
299            content_refs: result.content_refs.clone(),
300            redaction_policy_id: base.redaction_policy_id,
301            idempotency_key: None,
302            dedupe_key: None,
303            checkpoint_ref: base.checkpoint_ref,
304            payload: JournalRecordPayload::EffectResult(result),
305        }
306    }
307
308    /// Builds the checkpoint record or result value.
309    /// This is data-only and does not perform I/O, call host ports, append journals, publish
310    /// events, or start processes.
311    pub fn checkpoint(base: JournalRecordBase, checkpoint: RunCheckpoint) -> Self {
312        let event_index = base.event_index("checkpoint", "saved");
313        Self {
314            journal_schema_version: JOURNAL_SCHEMA_VERSION,
315            journal_seq: base.journal_seq,
316            record_id: base.record_id,
317            record_kind: JournalRecordKind::Checkpoint,
318            run_id: base.run_id.clone(),
319            agent_id: base.agent_id,
320            turn_id: base.turn_id,
321            attempt_id: base.attempt_id,
322            subject_ref: EntityRef::run(base.run_id),
323            related_refs: Vec::new(),
324            causal_refs: base.causal_refs,
325            source: base.source,
326            destination: base.destination,
327            correlation_keys: Vec::new(),
328            tags: base.tags,
329            delivery_semantics: "journal_backed".to_string(),
330            event_index,
331            timestamp_millis: base.timestamp_millis,
332            runtime_package_fingerprint: base.runtime_package_fingerprint,
333            privacy: base.privacy,
334            content_refs: checkpoint.content_ref_manifest.clone(),
335            redaction_policy_id: base.redaction_policy_id,
336            idempotency_key: None,
337            dedupe_key: None,
338            checkpoint_ref: Some(checkpoint.checkpoint_id.clone()),
339            payload: JournalRecordPayload::Checkpoint(checkpoint),
340        }
341    }
342
343    /// Recovery.
344    /// This is data-only and does not perform I/O, call host ports, append journals, publish
345    /// events, or start processes.
346    pub fn recovery(base: JournalRecordBase, recovery: RecoveryMarker) -> Self {
347        let event_index = base.event_index("recovery", "marker");
348        Self {
349            journal_schema_version: JOURNAL_SCHEMA_VERSION,
350            journal_seq: base.journal_seq,
351            record_id: base.record_id,
352            record_kind: JournalRecordKind::Recovery,
353            run_id: base.run_id.clone(),
354            agent_id: base.agent_id,
355            turn_id: base.turn_id,
356            attempt_id: base.attempt_id,
357            subject_ref: EntityRef::run(base.run_id),
358            related_refs: Vec::new(),
359            causal_refs: base.causal_refs,
360            source: base.source,
361            destination: base.destination,
362            correlation_keys: Vec::new(),
363            tags: base.tags,
364            delivery_semantics: "journal_backed".to_string(),
365            event_index,
366            timestamp_millis: base.timestamp_millis,
367            runtime_package_fingerprint: base.runtime_package_fingerprint,
368            privacy: base.privacy,
369            content_refs: Vec::new(),
370            redaction_policy_id: base.redaction_policy_id,
371            idempotency_key: None,
372            dedupe_key: None,
373            checkpoint_ref: base.checkpoint_ref,
374            payload: JournalRecordPayload::Recovery(recovery),
375        }
376    }
377
378    /// Builds the feature record record or result value.
379    /// This is data-only and does not perform I/O, call host ports, append journals, publish
380    /// events, or start processes.
381    #[expect(
382        clippy::too_many_arguments,
383        reason = "feature records intentionally expose journal/event lineage fields; replacing this with a builder needs a separate public API review"
384    )]
385    pub fn feature_record(
386        base: JournalRecordBase,
387        record_kind: JournalRecordKind,
388        event_family: impl Into<String>,
389        event_kind: impl Into<String>,
390        subject_ref: EntityRef,
391        related_refs: Vec<EntityRef>,
392        content_refs: Vec<ContentRef>,
393        payload: JournalRecordPayload,
394    ) -> Self {
395        let mut event_index = base.event_index(event_family, event_kind);
396        event_index.subject_ref = subject_ref.clone();
397        event_index.related_refs = related_refs.clone();
398        Self {
399            journal_schema_version: JOURNAL_SCHEMA_VERSION,
400            journal_seq: base.journal_seq,
401            record_id: base.record_id,
402            record_kind,
403            run_id: base.run_id,
404            agent_id: base.agent_id,
405            turn_id: base.turn_id,
406            attempt_id: base.attempt_id,
407            subject_ref,
408            related_refs,
409            causal_refs: base.causal_refs,
410            source: base.source,
411            destination: base.destination,
412            correlation_keys: Vec::new(),
413            tags: base.tags,
414            delivery_semantics: "journal_backed".to_string(),
415            event_index,
416            timestamp_millis: base.timestamp_millis,
417            runtime_package_fingerprint: base.runtime_package_fingerprint,
418            privacy: base.privacy,
419            content_refs,
420            redaction_policy_id: base.redaction_policy_id,
421            idempotency_key: None,
422            dedupe_key: None,
423            checkpoint_ref: base.checkpoint_ref,
424            payload,
425        }
426    }
427}
428
429#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
430/// Carries the journal record base record payload for journal, event, or fixture surfaces.
431/// 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.
432pub struct JournalRecordBase {
433    /// Journal seq used by this record or request.
434    pub journal_seq: u64,
435    /// Stable record id used for typed lineage, lookup, or dedupe.
436    pub record_id: String,
437    /// Run identifier used for lineage, filtering, replay, and dedupe.
438    pub run_id: RunId,
439    /// Agent identifier used for lineage, filtering, and ownership checks.
440    pub agent_id: AgentId,
441    #[serde(skip_serializing_if = "Option::is_none")]
442    /// Turn identifier for one loop turn within a run.
443    pub turn_id: Option<TurnId>,
444    #[serde(skip_serializing_if = "Option::is_none")]
445    /// Attempt identifier for retry, repair, provider, or tool execution
446    /// evidence.
447    pub attempt_id: Option<AttemptId>,
448    /// Source label or ref for this item; it is metadata and does not fetch
449    /// content by itself.
450    pub source: SourceRef,
451    #[serde(skip_serializing_if = "Option::is_none")]
452    /// Destination label or ref for this item; it is metadata and does not
453    /// deliver content by itself.
454    pub destination: Option<DestinationRef>,
455    #[serde(default, skip_serializing_if = "Vec::is_empty")]
456    /// Typed causal refs references. Resolving them is separate from
457    /// constructing this record.
458    pub causal_refs: Vec<String>,
459    #[serde(default, skip_serializing_if = "Vec::is_empty")]
460    /// Tag selector for event filtering.
461    /// `Any` leaves tags unconstrained; `Include` restricts matches to listed event tags.
462    pub tags: Vec<String>,
463    /// Timestamp in milliseconds associated with this record.
464    /// Use it for ordering and diagnostics; durable causality still comes from ids and cursors.
465    pub timestamp_millis: u64,
466    /// Fingerprint of the runtime package snapshot in force when this value was produced.
467    /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
468    /// not execute package behavior.
469    pub runtime_package_fingerprint: String,
470    /// Privacy class used for projection, telemetry, and raw-content access
471    /// decisions.
472    pub privacy: PrivacyClass,
473    /// Stable redaction policy id used for typed lineage, lookup, or dedupe.
474    pub redaction_policy_id: String,
475    #[serde(skip_serializing_if = "Option::is_none")]
476    /// Typed checkpoint ref reference. Resolving or executing it is a
477    /// separate policy-gated step.
478    pub checkpoint_ref: Option<String>,
479}
480
481impl JournalRecordBase {
482    /// Creates a new records::journal value with explicit
483    /// caller-provided inputs. This constructor is data-only and
484    /// performs no I/O or external side effects.
485    pub fn new(
486        journal_seq: u64,
487        record_id: impl Into<String>,
488        run_id: RunId,
489        agent_id: AgentId,
490        source: SourceRef,
491    ) -> Self {
492        Self {
493            journal_seq,
494            record_id: record_id.into(),
495            run_id,
496            agent_id,
497            turn_id: None,
498            attempt_id: None,
499            source,
500            destination: None,
501            causal_refs: Vec::new(),
502            tags: Vec::new(),
503            timestamp_millis: 0,
504            runtime_package_fingerprint: "runtime.package.fingerprint.test".to_string(),
505            privacy: PrivacyClass::ContentRefsOnly,
506            redaction_policy_id: "redaction.default".to_string(),
507            checkpoint_ref: None,
508        }
509    }
510
511    fn event_index(
512        &self,
513        event_family: impl Into<String>,
514        event_kind: impl Into<String>,
515    ) -> EventIndexProjection {
516        EventIndexProjection {
517            run_id: self.run_id.clone(),
518            agent_id: self.agent_id.clone(),
519            turn_id: self.turn_id.clone(),
520            event_family: event_family.into(),
521            event_kind: event_kind.into(),
522            source: self.source.clone(),
523            destination: self.destination.clone(),
524            subject_ref: EntityRef::run(self.run_id.clone()),
525            related_refs: Vec::new(),
526            correlation_keys: Vec::new(),
527            tags: self.tags.clone(),
528            privacy_class: self.privacy,
529            delivery_semantics: "journal_backed".to_string(),
530        }
531    }
532}
533
534#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
535#[serde(tag = "type", rename_all = "snake_case")]
536/// Enumerates the finite journal record payload cases.
537/// Serialized names are part of the SDK contract; update fixtures when variants change.
538#[expect(
539    clippy::large_enum_variant,
540    reason = "journal payloads are durable serde records; direct variant payloads are intentionally preserved until a fixture-reviewed envelope redesign"
541)]
542pub enum JournalRecordPayload {
543    /// Use this variant when the contract needs to represent run lifecycle; selecting it has no side effect by itself.
544    RunLifecycle(RunLifecycleRecord),
545    /// Use this variant when the contract needs to represent context projection; selecting it has no side effect by itself.
546    ContextProjection(ContextProjectionRecord),
547    /// Use this variant when the contract needs to represent model attempt; selecting it has no side effect by itself.
548    ModelAttempt(ModelAttemptRecord),
549    /// Use this variant when the contract needs to represent message; selecting it has no side effect by itself.
550    Message(MessageRecord),
551    /// Use this variant when the contract needs to represent structured output; selecting it has no side effect by itself.
552    StructuredOutput(StructuredOutputRecord),
553    /// Use this variant when the contract needs to represent approval; selecting it has no side effect by itself.
554    Approval(ApprovalRecord),
555    /// Use this variant when the contract needs to represent tool; selecting it has no side effect by itself.
556    Tool(ToolCallRecord),
557    /// Use this variant when the contract needs to represent output delivery; selecting it has no side effect by itself.
558    OutputDelivery(OutputDeliveryRecord),
559    /// Use this variant when the contract needs to represent hook; selecting it has no side effect by itself.
560    Hook(HookRecord),
561    /// Use this variant when the contract needs to represent stream rule; selecting it has no side effect by itself.
562    StreamRule(StreamRuleRecord),
563    /// Use this variant when the contract needs to represent realtime session; selecting it has no side effect by itself.
564    RealtimeSession(RealtimeSessionRecord),
565    /// Use this variant when the contract needs to represent isolation; selecting it has no side effect by itself.
566    Isolation(IsolationRecord),
567    /// Use this variant when the contract needs to represent child lifecycle; selecting it has no side effect by itself.
568    ChildLifecycle(ChildLifecycleRecord),
569    /// Use this variant when the contract needs to represent agent pool; selecting it has no side effect by itself.
570    AgentPool(AgentPoolRecord),
571    /// Use this variant when the contract needs to represent run message; selecting it has no side effect by itself.
572    RunMessage(RunMessageRecord),
573    /// Use this variant when the contract needs to represent wake; selecting it has no side effect by itself.
574    Wake(WakeRecord),
575    /// Use this variant when the contract needs to represent subagent; selecting it has no side effect by itself.
576    Subagent(SubagentRecord),
577    /// Use this variant when the contract needs to represent extension action; selecting it has no side effect by itself.
578    ExtensionAction(ExtensionActionRecord),
579    /// Use this variant when the contract needs to represent effect intent; selecting it has no side effect by itself.
580    EffectIntent(EffectIntent),
581    /// Use this variant when the contract needs to represent effect result; selecting it has no side effect by itself.
582    EffectResult(EffectResult),
583    /// Use this variant when the contract needs to represent checkpoint; selecting it has no side effect by itself.
584    Checkpoint(RunCheckpoint),
585    /// Use this variant when the contract needs to represent recovery; selecting it has no side effect by itself.
586    Recovery(RecoveryMarker),
587    /// Use this variant when the contract needs to represent terminal result; selecting it has no side effect by itself.
588    TerminalResult(TerminalResultMarker),
589}
590
591#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
592#[serde(tag = "record_type", content = "record", rename_all = "snake_case")]
593/// Enumerates the finite structured output record cases.
594/// Serialized names are part of the SDK contract; update fixtures when variants change.
595#[expect(
596    clippy::large_enum_variant,
597    reason = "structured-output payloads preserve serde and pattern-match ergonomics; boxing variants should be a dedicated contract migration"
598)]
599pub enum StructuredOutputRecord {
600    /// Use this variant when the contract needs to represent lifecycle; selecting it has no side effect by itself.
601    Lifecycle(StructuredOutputLifecycleRecord),
602    /// Use this variant when the contract needs to represent validation; selecting it has no side effect by itself.
603    Validation(ValidationRecord),
604    /// Use this variant when the contract needs to represent repair; selecting it has no side effect by itself.
605    Repair(RepairRecord),
606    /// Use this variant when the contract needs to represent repair exhaustion; selecting it has no side effect by itself.
607    RepairExhaustion(RepairExhaustionRecord),
608    /// Use this variant when the contract needs to represent validation report; selecting it has no side effect by itself.
609    ValidationReport(ValidationReportRecord),
610    /// Use this variant when the contract needs to represent validated output; selecting it has no side effect by itself.
611    ValidatedOutput(ValidatedOutput),
612    /// Use this variant when the contract needs to represent typed result publication; selecting it has no side effect by itself.
613    TypedResultPublication(TypedResultPublicationRecord),
614}
615
616#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
617/// Carries the run lifecycle record 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 RunLifecycleRecord {
620    /// Finite status for this record or lifecycle stage.
621    pub status: String,
622    /// Redacted explanation for a denial, failure, status, or package delta.
623    pub reason: String,
624}
625
626#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
627/// Carries the context projection record record payload for journal, event, or fixture surfaces.
628/// 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.
629pub struct ContextProjectionRecord {
630    /// Stable projection id used for typed lineage, lookup, or dedupe.
631    pub projection_id: ContextProjectionId,
632    /// Count of selected item items observed or included in this record.
633    pub selected_item_count: u32,
634    /// Provider destination used by this record or request.
635    pub provider_destination: DestinationRef,
636}
637
638#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
639/// Carries the model attempt record record payload for journal, event, or fixture surfaces.
640/// 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.
641pub struct ModelAttemptRecord {
642    /// Stable provider route id used for typed lineage, lookup, or dedupe.
643    pub provider_route_id: String,
644    /// Stable provider model id used for typed lineage, lookup, or dedupe.
645    pub provider_model_id: String,
646    /// Count of request message items observed or included in this record.
647    pub request_message_count: u32,
648    #[serde(skip_serializing_if = "Option::is_none")]
649    /// Optional stop reason value.
650    /// When absent, callers should use the documented default or skip that optional behavior.
651    pub stop_reason: Option<ProviderStopReason>,
652    #[serde(skip_serializing_if = "Option::is_none")]
653    /// Optional usage value.
654    /// When absent, callers should use the documented default or skip that optional behavior.
655    pub usage: Option<ProviderUsage>,
656}
657
658#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
659/// Carries the message record record payload for journal, event, or fixture surfaces.
660/// 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.
661pub struct MessageRecord {
662    /// Message identifier for transcript, projection, or provider-response
663    /// lineage.
664    pub message_id: MessageId,
665    /// Role used by this record or request.
666    pub role: String,
667    /// Redacted human-readable summary safe for events, telemetry, and logs.
668    pub redacted_summary: String,
669}
670
671#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
672/// Carries the agent pool record record payload for journal, event, or fixture surfaces.
673/// 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.
674pub struct AgentPoolRecord {
675    /// Stable pool id used for typed lineage, lookup, or dedupe.
676    pub pool_id: AgentPoolId,
677    #[serde(default, skip_serializing_if = "Vec::is_empty")]
678    /// Identifiers used to select or correlate member run values.
679    /// Use them for typed lookup, filtering, or lineage instead of stringly typed matching.
680    pub member_run_ids: Vec<RunId>,
681    #[serde(default, skip_serializing_if = "Vec::is_empty")]
682    /// Collection of topics values.
683    /// Ordering and membership should be treated as part of the serialized contract when
684    /// relevant.
685    pub topics: Vec<TopicId>,
686    #[serde(default, skip_serializing_if = "Vec::is_empty")]
687    /// Policy references that govern admission, projection, execution, or
688    /// delivery.
689    pub policy_refs: Vec<PolicyRef>,
690    /// Lifecycle status used by this record or request.
691    pub lifecycle_status: AgentPoolLifecycleStatus,
692}
693
694#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
695#[serde(rename_all = "snake_case")]
696/// Enumerates the finite agent pool lifecycle status cases.
697/// Serialized names are part of the SDK contract; update fixtures when variants change.
698pub enum AgentPoolLifecycleStatus {
699    /// Use this variant when the contract needs to represent created; selecting it has no side effect by itself.
700    Created,
701    /// Use this variant when the contract needs to represent run joined; selecting it has no side effect by itself.
702    RunJoined,
703    /// Use this variant when the contract needs to represent run left; selecting it has no side effect by itself.
704    RunLeft,
705}
706
707#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
708/// Carries the run message record record payload for journal, event, or fixture surfaces.
709/// 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.
710pub struct RunMessageRecord {
711    /// Message identifier for transcript, projection, or provider-response
712    /// lineage.
713    pub message_id: MessageId,
714    /// Stable source run id used for typed lineage, lookup, or dedupe.
715    pub source_run_id: RunId,
716    /// Address target used by this record or request.
717    pub address_target: RunMessageAddressTargetRecord,
718    /// Content reference where payload bytes or structured tool output are
719    /// stored.
720    pub content_ref: ContentRef,
721    /// Correlation used by this record or request.
722    pub correlation: EventCorrelation,
723    #[serde(skip_serializing_if = "Option::is_none")]
724    /// Optional reply to value.
725    /// When absent, callers should use the documented default or skip that optional behavior.
726    pub reply_to: Option<MessageId>,
727    /// Output delivery setting or policy.
728    /// Delivery coordinators use it to decide sink mode, dedupe, and required evidence.
729    pub delivery_status: RunMessageDeliveryStatus,
730    #[serde(default, skip_serializing_if = "Vec::is_empty")]
731    /// Collection of delivered to values.
732    /// Ordering and membership should be treated as part of the serialized contract when
733    /// relevant.
734    pub delivered_to: Vec<RunId>,
735    #[serde(default, skip_serializing_if = "Vec::is_empty")]
736    /// Policy references that govern admission, projection, execution, or
737    /// delivery.
738    pub policy_refs: Vec<PolicyRef>,
739    /// Idempotency setting or key for deduping retries.
740    /// Use it to prevent duplicate side effects during replay or repair.
741    pub idempotency_key: IdempotencyKey,
742    #[serde(skip_serializing_if = "Option::is_none")]
743    /// Optional effect intent value.
744    /// When absent, callers should use the documented default or skip that optional behavior.
745    pub effect_intent: Option<EffectIntent>,
746    #[serde(skip_serializing_if = "Option::is_none")]
747    /// Optional effect result value.
748    /// When absent, callers should use the documented default or skip that optional behavior.
749    pub effect_result: Option<EffectResult>,
750}
751
752#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
753#[serde(tag = "type", rename_all = "snake_case")]
754/// Enumerates the finite run message address target record cases.
755/// Serialized names are part of the SDK contract; update fixtures when variants change.
756pub enum RunMessageAddressTargetRecord {
757    /// Use this variant when the contract needs to represent run; selecting it has no side effect by itself.
758    Run {
759        /// Run identifier used for lineage, filtering, replay, and dedupe.
760        run_id: RunId,
761    },
762    /// Use this variant when the contract needs to represent agent; selecting it has no side effect by itself.
763    Agent {
764        /// Agent identifier used for lineage, filtering, and ownership
765        /// checks.
766        agent_id: AgentId,
767    },
768    /// Use this variant when the contract needs to represent topic; selecting it has no side effect by itself.
769    Topic {
770        /// Stable topic id used for typed lineage, lookup, or dedupe.
771        topic_id: TopicId,
772    },
773    /// Use this variant when the contract needs to represent pool; selecting it has no side effect by itself.
774    Pool {
775        /// Stable pool id used for typed lineage, lookup, or dedupe.
776        pool_id: AgentPoolId,
777    },
778}
779
780#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
781#[serde(rename_all = "snake_case")]
782/// Enumerates the finite run message delivery status cases.
783/// Serialized names are part of the SDK contract; update fixtures when variants change.
784pub enum RunMessageDeliveryStatus {
785    /// Use this variant when the contract needs to represent accepted; selecting it has no side effect by itself.
786    Accepted,
787    /// Use this variant when the contract needs to represent delivered; selecting it has no side effect by itself.
788    Delivered,
789    /// Use this variant when the contract needs to represent responded; selecting it has no side effect by itself.
790    Responded,
791    /// Use this variant when the contract needs to represent failed; selecting it has no side effect by itself.
792    Failed,
793    /// Use this variant when the contract needs to represent timed out; selecting it has no side effect by itself.
794    TimedOut,
795    /// Use this variant when the contract needs to represent expired; selecting it has no side effect by itself.
796    Expired,
797    /// Use this variant when the contract needs to represent cancelled; selecting it has no side effect by itself.
798    Cancelled,
799}
800
801#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
802/// Carries the wake record record payload for journal, event, or fixture surfaces.
803/// 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.
804pub struct WakeRecord {
805    /// Stable condition id used for typed lineage, lookup, or dedupe.
806    pub condition_id: WakeConditionId,
807    /// Run identifier used for lineage, filtering, replay, and dedupe.
808    pub run_id: RunId,
809    /// Deterministic event filter fingerprint used for stale checks, package
810    /// evidence, or replay comparisons.
811    pub event_filter_fingerprint: EventFilterFingerprint,
812    #[serde(skip_serializing_if = "Option::is_none")]
813    /// Time value in milliseconds for timeout millis.
814    /// Use it for timeout, ordering, or diagnostic calculations.
815    pub timeout_millis: Option<u64>,
816    /// Resume policy used by this record or request.
817    pub resume_policy: WakeResumeInputPolicyRecord,
818    /// Trigger status used by this record or request.
819    pub trigger_status: WakeTriggerStatus,
820    #[serde(default, skip_serializing_if = "Vec::is_empty")]
821    /// Policy references that govern admission, projection, execution, or
822    /// delivery.
823    pub policy_refs: Vec<PolicyRef>,
824    /// Idempotency setting or key for deduping retries.
825    /// Use it to prevent duplicate side effects during replay or repair.
826    pub idempotency_key: IdempotencyKey,
827    #[serde(skip_serializing_if = "Option::is_none")]
828    /// Stable matched event id used for typed lineage, lookup, or dedupe.
829    pub matched_event_id: Option<crate::domain::EventId>,
830}
831
832#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
833#[serde(rename_all = "snake_case")]
834/// Enumerates the finite wake resume input policy record cases.
835/// Serialized names are part of the SDK contract; update fixtures when variants change.
836pub enum WakeResumeInputPolicyRecord {
837    /// Use this variant when the contract needs to represent matching event refs; selecting it has no side effect by itself.
838    MatchingEventRefs,
839    /// Use this variant when the contract needs to represent redacted summary; selecting it has no side effect by itself.
840    RedactedSummary,
841    /// Use this variant when the contract needs to represent none; selecting it has no side effect by itself.
842    None,
843}
844
845#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
846#[serde(rename_all = "snake_case")]
847/// Enumerates the finite wake trigger status cases.
848/// Serialized names are part of the SDK contract; update fixtures when variants change.
849pub enum WakeTriggerStatus {
850    /// Use this variant when the contract needs to represent registered; selecting it has no side effect by itself.
851    Registered,
852    /// Use this variant when the contract needs to represent triggered; selecting it has no side effect by itself.
853    Triggered,
854    /// Use this variant when the contract needs to represent timed out; selecting it has no side effect by itself.
855    TimedOut,
856    /// Use this variant when the contract needs to represent cancelled; selecting it has no side effect by itself.
857    Cancelled,
858    /// Use this variant when the contract needs to represent failed; selecting it has no side effect by itself.
859    Failed,
860}
861
862#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
863/// Carries the run checkpoint record payload for journal, event, or fixture surfaces.
864/// 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.
865pub struct RunCheckpoint {
866    /// Stable checkpoint id used for typed lineage, lookup, or dedupe.
867    pub checkpoint_id: String,
868    /// Run identifier used for lineage, filtering, replay, and dedupe.
869    pub run_id: RunId,
870    /// Checkpoint seq used by this record or request.
871    pub checkpoint_seq: u64,
872    /// Covers journal seq used by this record or request.
873    pub covers_journal_seq: u64,
874    /// Loop state used by this record or request.
875    pub loop_state: String,
876    #[serde(skip_serializing_if = "Option::is_none")]
877    /// Turn identifier for one loop turn within a run.
878    pub turn_id: Option<TurnId>,
879    #[serde(skip_serializing_if = "Option::is_none")]
880    /// Attempt identifier for retry, repair, provider, or tool execution
881    /// evidence.
882    pub attempt_id: Option<AttemptId>,
883    /// Fingerprint of the runtime package snapshot in force when this value was produced.
884    /// Use it for replay, dedupe, and package-lineage checks; the field is evidence and does
885    /// not execute package behavior.
886    pub runtime_package_fingerprint: String,
887    #[serde(default, skip_serializing_if = "Vec::is_empty")]
888    /// Collection of pending side effects values.
889    /// Ordering and membership should be treated as part of the serialized contract when
890    /// relevant.
891    pub pending_side_effects: Vec<PendingSideEffect>,
892    #[serde(default, skip_serializing_if = "Vec::is_empty")]
893    /// Collection of pending approvals values.
894    /// Ordering and membership should be treated as part of the serialized contract when
895    /// relevant.
896    pub pending_approvals: Vec<String>,
897    #[serde(default, skip_serializing_if = "Vec::is_empty")]
898    /// Content reference associated with this value.
899    /// Resolve it through policy-gated content stores instead of embedding raw content.
900    pub content_ref_manifest: Vec<ContentRef>,
901    /// Deterministic state hash used for stale checks, package evidence, or
902    /// replay comparisons.
903    pub state_hash: String,
904    /// Time value in milliseconds for created at millis.
905    /// Use it for timeout, ordering, or diagnostic calculations.
906    pub created_at_millis: u64,
907    /// Stable writer id used for typed lineage, lookup, or dedupe.
908    pub writer_id: String,
909}
910
911impl RunCheckpoint {
912    /// Validates the records::journal invariants and returns a typed
913    /// error on failure. Validation is pure and does not perform I/O,
914    /// dispatch, journal appends, or adapter calls.
915    pub fn validate_against_latest_seq(&self, latest_journal_seq: u64) -> Result<(), AgentError> {
916        if self.covers_journal_seq > latest_journal_seq {
917            return Err(AgentError::contract_violation(
918                "checkpoint covers_journal_seq cannot point past latest committed journal record",
919            ));
920        }
921        Ok(())
922    }
923}
924
925#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
926/// Carries the pending side effect record payload for journal, event, or fixture surfaces.
927/// 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.
928pub struct PendingSideEffect {
929    /// Stable effect id used for typed lineage, lookup, or dedupe.
930    pub effect_id: crate::domain::EffectId,
931    /// Stable intent record id used for typed lineage, lookup, or dedupe.
932    pub intent_record_id: String,
933    #[serde(skip_serializing_if = "Option::is_none")]
934    /// Idempotency setting or key for deduping retries.
935    /// Use it to prevent duplicate side effects during replay or repair.
936    pub idempotency_key: Option<IdempotencyKey>,
937    #[serde(skip_serializing_if = "Option::is_none")]
938    /// Dedupe policy or key for a side-effecting operation.
939    /// Replay and repair use it to avoid sending or executing the same effect twice.
940    pub dedupe_key: Option<DedupeKey>,
941    /// Reason a pending side effect is unsafe to retry automatically.
942    /// Recovery uses it to require repair or reconciliation before continuing.
943    pub unsafe_pending_reason: String,
944}
945
946#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
947/// Carries the terminal result marker record payload for journal, event, or fixture surfaces.
948/// 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.
949pub struct TerminalResultMarker {
950    /// Stable effect id used for typed lineage, lookup, or dedupe.
951    pub effect_id: crate::domain::EffectId,
952    /// Stable result record id used for typed lineage, lookup, or dedupe.
953    pub result_record_id: String,
954    /// Terminal status used by this record or request.
955    pub terminal_status: String,
956}
957
958#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
959/// Carries the recovery marker record payload for journal, event, or fixture surfaces.
960/// 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.
961pub struct RecoveryMarker {
962    /// Collection of unsafe pending values.
963    /// Ordering and membership should be treated as part of the serialized contract when
964    /// relevant.
965    pub unsafe_pending: Vec<PendingSideEffect>,
966    /// Recovery reason used by this record or request.
967    pub recovery_reason: String,
968    #[serde(default, skip_serializing_if = "Vec::is_empty")]
969    /// Policy references that govern admission, projection, execution, or
970    /// delivery.
971    pub policy_refs: Vec<PolicyRef>,
972}