| 1 | //! Event names and payloads emitted to the webview. |
| 2 | |
| 3 | use ed_agent::EngineStatus; |
| 4 | use serde::{Deserialize, Serialize}; |
| 5 | |
| 6 | /// Emitted whenever the engine's lifecycle status changes. |
| 7 | /// |
| 8 | /// The name matches what the existing applications already emit, so adopting |
| 9 | /// Ed needs no frontend change. |
| 10 | pub const EVENT_CHAT_STATUS_CHANGED: &str = "chat_status_changed"; |
| 11 | |
| 12 | /// Emitted once an inference turn completes, successfully or not. |
| 13 | pub const EVENT_CHAT_REPLY: &str = "chat_reply"; |
| 14 | |
| 15 | /// Emitted with a [`ToolCall`](ed_agent::ToolCall) when the model asks for a |
| 16 | /// tool, before any approval or execution. |
| 17 | pub const EVENT_CHAT_TOOL_REQUESTED: &str = "chat_tool_requested"; |
| 18 | |
| 19 | /// Emitted with an [`ApprovalRequest`](ed_agent::ApprovalRequest) when a |
| 20 | /// mutating tool is waiting on the user. |
| 21 | /// |
| 22 | /// This is a notification, not the approval itself. Ed asks the |
| 23 | /// [`ApprovalHandler`](ed_agent::ApprovalHandler) the host passed to |
| 24 | /// [`Ed::with_agent`](ed_agent::Ed::with_agent) for the decision; this event |
| 25 | /// exists so the webview can put a sheet on screen while that call is pending. |
| 26 | pub const EVENT_CHAT_APPROVAL_REQUESTED: &str = "chat_approval_requested"; |
| 27 | |
| 28 | /// Emitted with a [`ToolCall`](ed_agent::ToolCall) once a tool is approved and |
| 29 | /// about to run. |
| 30 | pub const EVENT_CHAT_TOOL_STARTED: &str = "chat_tool_started"; |
| 31 | |
| 32 | /// Emitted with a [`ToolExecutionResult`](ed_agent::ToolExecutionResult) when a |
| 33 | /// tool finishes, whether it succeeded or failed. |
| 34 | pub const EVENT_CHAT_TOOL_FINISHED: &str = "chat_tool_finished"; |
| 35 | |
| 36 | /// Emitted with an [`AgentReply`](ed_agent::AgentReply) when a full agent turn |
| 37 | /// completes. |
| 38 | /// |
| 39 | /// Distinct from [`EVENT_CHAT_REPLY`], which covers the single-shot |
| 40 | /// [`Ed::send`](ed_agent::Ed::send) path. A turn reports one or the other, |
| 41 | /// never both. |
| 42 | pub const EVENT_CHAT_AGENT_REPLY: &str = "chat_agent_reply"; |
| 43 | |
| 44 | /// Emitted with a string when Ed has something the user should see but the turn |
| 45 | /// is still viable, such as an unverified model or a spent tool-round budget. |
| 46 | pub const EVENT_CHAT_WARNING: &str = "chat_warning"; |
| 47 | |
| 48 | /// Emitted with a [`TextDeltaPayload`] for each piece of a reply as it is |
| 49 | /// generated. Concatenate the deltas of one `id` to grow the assistant bubble; |
| 50 | /// [`EVENT_CHAT_REPLY`] still arrives at the end with the whole text. |
| 51 | pub const EVENT_CHAT_TEXT_DELTA: &str = "chat_text_delta"; |
| 52 | |
| 53 | /// Emitted with an [`ApprovalResolvedPayload`] once an approval request has |
| 54 | /// been answered, timed out, or cancelled, so the webview can close its sheet. |
| 55 | pub const EVENT_CHAT_APPROVAL_RESOLVED: &str = "chat_approval_resolved"; |
| 56 | |
| 57 | /// Payload for [`EVENT_CHAT_TEXT_DELTA`]. |
| 58 | /// |
| 59 | /// `session` and `id` are empty when the turn didn't come through a `submit` |
| 60 | /// call, which is the only thing that knows them. |
| 61 | #[derive(Debug, Clone, Serialize, Deserialize)] |
| 62 | pub struct TextDeltaPayload { |
| 63 | pub session: String, |
| 64 | pub id: String, |
| 65 | pub delta: String, |
| 66 | } |
| 67 | |
| 68 | /// Payload for [`EVENT_CHAT_REPLY`] when the turn came through `submit`. |
| 69 | /// |
| 70 | /// A superset of [`ChatReply`](ed_agent::ChatReply), so a listener written for |
| 71 | /// the single-session event keeps working. |
| 72 | #[derive(Debug, Clone, Serialize, Deserialize)] |
| 73 | pub struct SubmitReplyPayload { |
| 74 | pub id: String, |
| 75 | pub session: String, |
| 76 | pub reply: Option<String>, |
| 77 | pub duration: Option<String>, |
| 78 | pub error: Option<String>, |
| 79 | } |
| 80 | |
| 81 | /// Payload for [`EVENT_CHAT_APPROVAL_REQUESTED`] when the approval handler is |
| 82 | /// [`TauriApprovals`](crate::TauriApprovals). Answer it with |
| 83 | /// `chat_respond_approval { request_id, decision }`. |
| 84 | #[derive(Debug, Clone, Serialize, Deserialize)] |
| 85 | pub struct ApprovalRequestedPayload { |
| 86 | pub request_id: String, |
| 87 | pub session: String, |
| 88 | pub call: ed_agent::ToolCall, |
| 89 | pub risk: ed_agent::ToolRisk, |
| 90 | } |
| 91 | |
| 92 | /// Payload for [`EVENT_CHAT_APPROVAL_RESOLVED`]. |
| 93 | #[derive(Debug, Clone, Serialize, Deserialize)] |
| 94 | pub struct ApprovalResolvedPayload { |
| 95 | pub request_id: String, |
| 96 | } |
| 97 | |
| 98 | /// Payload for [`EVENT_CHAT_STATUS_CHANGED`]. |
| 99 | /// |
| 100 | /// `status` serialises to `"unloaded"`, `"loading"`, `"ready"`, `"generating"`, |
| 101 | /// or `"error"` — [`EngineStatus`] is `#[serde(rename_all = "snake_case")]`, |
| 102 | /// which is the same set of strings applications were previously declaring as |
| 103 | /// their own constants. |
| 104 | #[derive(Debug, Clone, Serialize, Deserialize)] |
| 105 | pub struct ChatStatusPayload { |
| 106 | pub status: EngineStatus, |
| 107 | pub model_name: Option<String>, |
| 108 | pub error: Option<String>, |
| 109 | } |