conversation_api/execution/event.rs
1//! Durable Agent events and their delivery boundary.
2
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::execution::{
8 ActionId, EventId, ExternalError, InvocationContext, MessageId, RuntimeSnapshot, UsageSummary,
9};
10
11/// Private execution projection. Only `item` is exposed to clients.
12#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
13#[serde(deny_unknown_fields)]
14pub struct InteractionRequest {
15 pub item: crate::PendingInteractionItem,
16 /// Retire a real prepared record on conversation termination, never a business session.
17 pub prepared_action_id: Option<ActionId>,
18}
19
20/// Event emitted by the Runtime.
21#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
22#[serde(tag = "type", rename_all = "snake_case")]
23#[serde(deny_unknown_fields)]
24pub enum AgentEvent {
25 /// A run has started or resumed.
26 #[serde(deserialize_with = "crate::execution::deserialize_empty_variant")]
27 Started,
28 /// The run requires user input before it can continue.
29 InteractionRequired {
30 /// Batch identifier answered by `SubmitInteraction`.
31 batch_id: MessageId,
32 /// Prepared actions awaiting one decision each.
33 items: Vec<InteractionRequest>,
34 /// Usage accumulated before Runtime suspended the run.
35 usage: UsageSummary,
36 },
37 /// The run completed successfully.
38 Completed {
39 /// Final structured result.
40 result: Value,
41 /// Aggregated usage for the completed run.
42 usage: UsageSummary,
43 },
44 /// The run failed definitively.
45 Failed {
46 /// Catalog member from [`crate::RunErrorCode`].
47 code: String,
48 /// Safe user-facing or diagnostic message.
49 message: String,
50 /// Usage settled before the failure, cancellation, or interruption.
51 usage: UsageSummary,
52 },
53}
54
55/// Best-effort, request-scoped observation that is not part of the durable outbox.
56#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
57#[serde(tag = "type", rename_all = "snake_case")]
58#[serde(deny_unknown_fields)]
59pub enum AgentObservation {
60 /// Coarse progress for live presentation.
61 Progress {
62 /// Stable progress label.
63 status: String,
64 },
65 /// Temporary assistant text emitted before a tool batch.
66 CompanionText {
67 /// Text shown while work continues.
68 content: String,
69 },
70 /// A tool attempt is starting.
71 ToolStarted {
72 /// Model tool-call identifier.
73 call_id: String,
74 /// Stable tool name.
75 tool_name: String,
76 },
77 /// A tool attempt produced a result.
78 ToolResult {
79 /// Model tool-call identifier.
80 call_id: String,
81 /// Stable tool name.
82 tool_name: String,
83 /// Structured result.
84 result: Value,
85 /// Whether execution failed.
86 is_error: bool,
87 },
88 /// A validated model tool-call batch has been checkpointed for execution.
89 ToolCallsScheduled {
90 /// Tool names in model order.
91 tool_names: Vec<String>,
92 },
93 /// A tool batch completed and its results are available to the next model step.
94 ToolCallsCompleted {
95 /// Number of completed tool calls.
96 count: u32,
97 },
98 /// Optional diagnostic payload requested by the caller.
99 DebugData {
100 /// Diagnostic namespace.
101 scope: String,
102 /// Structured diagnostic value.
103 payload: Value,
104 },
105}
106
107/// Sequenced event persisted as part of a thread transition.
108#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
109pub struct DurableEvent {
110 /// Globally unique event identifier used for deduplication.
111 pub id: EventId,
112 /// Monotonic sequence within a thread.
113 pub sequence: u64,
114 /// Frozen routing, ownership, and run identity for this event.
115 pub context: InvocationContext,
116 /// Event payload.
117 pub event: AgentEvent,
118}
119
120/// Delivery boundary for events that have already been durably committed.
121#[async_trait]
122pub trait EventPublisher: Send + Sync {
123 /// Publishes one committed stable-boundary event with at-least-once delivery semantics.
124 ///
125 /// `snapshot` is the exact Runtime generation committed with `event`. Implementations must
126 /// never reload a newer snapshot while encoding the event.
127 async fn publish(
128 &self,
129 snapshot: &RuntimeSnapshot,
130 event: &DurableEvent,
131 ) -> Result<(), ExternalError>;
132}
133
134/// Best-effort observer for non-durable UI progress.
135#[async_trait]
136pub trait Observer: Send + Sync {
137 /// Delivers one request-scoped observation.
138 async fn observe(&self, context: &InvocationContext, observation: &AgentObservation);
139}