Skip to main content

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}