agent_sdk_core/records/effect.rs
1//! Shared effect-intent and effect-result records. Use these records before and after
2//! externally visible work such as tools, output delivery, extension actions, or
3//! child starts. The records themselves do not execute the effect.
4//!
5use serde::{Deserialize, Serialize};
6
7use crate::domain::{
8 ContentRef, DedupeKey, DestinationRef, EffectId, EntityRef, IdempotencyKey, PolicyRef,
9 SourceRef,
10};
11
12#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
13#[serde(rename_all = "snake_case")]
14/// Enumerates the finite effect kind cases.
15/// Serialized names are part of the SDK contract; update fixtures when variants change.
16pub enum EffectKind {
17 /// Use this variant when the contract needs to represent provider request; selecting it has no side effect by itself.
18 ProviderRequest,
19 /// Use this variant when the contract needs to represent tool execution; selecting it has no side effect by itself.
20 ToolExecution,
21 /// Use this variant when the contract needs to represent approval dispatch; selecting it has no side effect by itself.
22 ApprovalDispatch,
23 /// Use this variant when the contract needs to represent memory write; selecting it has no side effect by itself.
24 MemoryWrite,
25 /// Use this variant when the contract needs to represent extension action; selecting it has no side effect by itself.
26 ExtensionAction,
27 /// Use this variant when the contract needs to represent output delivery; selecting it has no side effect by itself.
28 OutputDelivery,
29 /// Use this variant when the contract needs to represent file write; selecting it has no side effect by itself.
30 FileWrite,
31 /// Use this variant when the contract needs to represent process start; selecting it has no side effect by itself.
32 ProcessStart,
33 /// Use this variant when the contract needs to represent process signal; selecting it has no side effect by itself.
34 ProcessSignal,
35 /// Use this variant when the contract needs to represent isolated process start; selecting it has no side effect by itself.
36 IsolatedProcessStart,
37 /// Use this variant when the contract needs to represent child agent start; selecting it has no side effect by itself.
38 ChildAgentStart,
39 /// Use this variant when the contract needs to represent run message delivery; selecting it has no side effect by itself.
40 RunMessageDelivery,
41 /// Use this variant when the contract needs to represent child artifact shutdown; selecting it has no side effect by itself.
42 ChildArtifactShutdown,
43 /// Use this variant when the contract needs to represent detach transfer; selecting it has no side effect by itself.
44 DetachTransfer,
45 /// Use this variant when the contract needs to represent hook mutation; selecting it has no side effect by itself.
46 HookMutation,
47}
48
49#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
50/// Carries the effect intent record payload for journal, event, or fixture surfaces.
51/// 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.
52pub struct EffectIntent {
53 /// Stable effect id used for typed lineage, lookup, or dedupe.
54 pub effect_id: EffectId,
55 /// Kind/category for this record, capability, event, or detected
56 /// resource.
57 pub kind: EffectKind,
58 /// Typed subject ref reference. Resolving or executing it is a separate
59 /// policy-gated step.
60 pub subject_ref: EntityRef,
61 /// Source label or ref for this item; it is metadata and does not fetch
62 /// content by itself.
63 pub source: SourceRef,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 /// Destination label or ref for this item; it is metadata and does not
66 /// deliver content by itself.
67 pub destination: Option<DestinationRef>,
68 #[serde(default, skip_serializing_if = "Vec::is_empty")]
69 /// Policy references that govern admission, projection, execution, or
70 /// delivery.
71 pub policy_refs: Vec<PolicyRef>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 /// Idempotency setting or key for deduping retries.
74 /// Use it to prevent duplicate side effects during replay or repair.
75 pub idempotency_key: Option<IdempotencyKey>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 /// Dedupe policy or key for a side-effecting operation.
78 /// Replay and repair use it to avoid sending or executing the same effect twice.
79 pub dedupe_key: Option<DedupeKey>,
80 #[serde(default, skip_serializing_if = "Vec::is_empty")]
81 /// Content references associated with this record; resolving them is a
82 /// separate policy-gated step.
83 pub content_refs: Vec<ContentRef>,
84 /// Redacted human-readable summary safe for events, telemetry, and logs.
85 pub redacted_summary: String,
86}
87
88impl EffectIntent {
89 /// Creates a new records::effect value with explicit
90 /// caller-provided inputs. This constructor is data-only and
91 /// performs no I/O or external side effects.
92 pub fn new(
93 effect_id: EffectId,
94 kind: EffectKind,
95 subject_ref: EntityRef,
96 source: SourceRef,
97 redacted_summary: impl Into<String>,
98 ) -> Self {
99 Self {
100 effect_id,
101 kind,
102 subject_ref,
103 source,
104 destination: None,
105 policy_refs: Vec::new(),
106 idempotency_key: None,
107 dedupe_key: None,
108 content_refs: Vec::new(),
109 redacted_summary: redacted_summary.into(),
110 }
111 }
112}
113
114#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
115#[serde(rename_all = "snake_case")]
116/// Enumerates the finite effect terminal status cases.
117/// Serialized names are part of the SDK contract; update fixtures when variants change.
118pub enum EffectTerminalStatus {
119 /// Use this variant when the contract needs to represent completed; selecting it has no side effect by itself.
120 Completed,
121 /// Use this variant when the contract needs to represent failed; selecting it has no side effect by itself.
122 Failed,
123 /// Use this variant when the contract needs to represent timed out; selecting it has no side effect by itself.
124 TimedOut,
125 /// Use this variant when the contract needs to represent cancelled; selecting it has no side effect by itself.
126 Cancelled,
127 /// Use this variant when the contract needs to represent denied before execution; selecting it has no side effect by itself.
128 DeniedBeforeExecution,
129 /// Use this variant when the contract needs to represent unknown; selecting it has no side effect by itself.
130 Unknown,
131}
132
133#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
134/// Carries the effect result record payload for journal, event, or fixture surfaces.
135/// 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.
136pub struct EffectResult {
137 /// Stable effect id used for typed lineage, lookup, or dedupe.
138 pub effect_id: EffectId,
139 /// Terminal status used by this record or request.
140 pub terminal_status: EffectTerminalStatus,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 /// Stable external operation id used for typed lineage, lookup, or
143 /// dedupe.
144 pub external_operation_id: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 /// Typed reconciliation ref reference. Resolving or executing it is a
147 /// separate policy-gated step.
148 pub reconciliation_ref: Option<String>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 /// Typed error ref reference. Resolving or executing it is a separate
151 /// policy-gated step.
152 pub error_ref: Option<String>,
153 #[serde(default, skip_serializing_if = "Vec::is_empty")]
154 /// Content references associated with this record; resolving them is a
155 /// separate policy-gated step.
156 pub content_refs: Vec<ContentRef>,
157 /// Redacted human-readable summary safe for events, telemetry, and logs.
158 pub redacted_summary: String,
159}
160
161impl EffectResult {
162 /// Returns an updated value with completed configured.
163 /// This is data-only and does not perform I/O, call host ports, append journals, publish
164 /// events, or start processes.
165 pub fn completed(effect_id: EffectId, redacted_summary: impl Into<String>) -> Self {
166 Self {
167 effect_id,
168 terminal_status: EffectTerminalStatus::Completed,
169 external_operation_id: None,
170 reconciliation_ref: None,
171 error_ref: None,
172 content_refs: Vec::new(),
173 redacted_summary: redacted_summary.into(),
174 }
175 }
176
177 /// Builds the unknown record or result value.
178 /// This is data-only and does not perform I/O, call host ports, append journals, publish
179 /// events, or start processes.
180 pub fn unknown(effect_id: EffectId, redacted_summary: impl Into<String>) -> Self {
181 Self {
182 effect_id,
183 terminal_status: EffectTerminalStatus::Unknown,
184 external_operation_id: None,
185 reconciliation_ref: None,
186 error_ref: None,
187 content_refs: Vec::new(),
188 redacted_summary: redacted_summary.into(),
189 }
190 }
191}