| 1 | use super::{BlockId, ParticipantId}; |
| 2 | use byte_unit::Byte; |
| 3 | use serde::{Deserialize, Serialize}; |
| 4 | |
| 5 | /// AI metadata for correlating terminal blocks with agent commands. |
| 6 | /// This allows viewers in shared sessions to associate terminal command blocks |
| 7 | /// with the agent tool calls that triggered them. |
| 8 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 9 | pub struct AICommandMetadata { |
| 10 | /// The tool call ID from the Multi-Agent API protocol. |
| 11 | /// Corresponds to action_id on the sharer side. |
| 12 | pub tool_call_id: String, |
| 13 | |
| 14 | /// Whether this command is being monitored by an agent as a long-running command. |
| 15 | #[serde(default)] |
| 16 | pub is_agent_monitored: bool, |
| 17 | } |
| 18 | |
| 19 | /// Types of terminal events that need to be ordered against each other. |
| 20 | #[derive(Clone, Deserialize, Serialize)] |
| 21 | pub enum OrderedTerminalEventType { |
| 22 | /// Bytes read off the sharer's pty (session contents). |
| 23 | PtyBytesRead { |
| 24 | bytes: Vec<u8>, |
| 25 | }, |
| 26 | /// A command is beginning to execute. |
| 27 | CommandExecutionStarted { |
| 28 | /// The ID of the participant who ran the command. |
| 29 | participant_id: ParticipantId, |
| 30 | /// AI metadata if this command was executed by an agent. |
| 31 | #[serde(default)] |
| 32 | ai_metadata: Option<AICommandMetadata>, |
| 33 | }, |
| 34 | CommandExecutionFinished { |
| 35 | next_block_id: BlockId, |
| 36 | }, |
| 37 | /// The sharer's terminal was resized. |
| 38 | Resize { |
| 39 | window_size: WindowSize, |
| 40 | }, |
| 41 | /// The sharer received an AI agent response event. Response events include all information needed to reconstruct a conversation, including: |
| 42 | // * The start and end of individual requests |
| 43 | // * Incremental agent output |
| 44 | // * Echoed user messages and tool call results |
| 45 | /// See https://github.com/warpdotdev/warp-proto-apis/blob/6310871f081b5f44b2d4e3e5d8fdfa3008b750b0/apis/multi_agent/v1/response.proto#L16-L17 |
| 46 | AgentResponseEvent { |
| 47 | /// The ID of the participant who sent the query to initiate this agent response. |
| 48 | response_initiator: Option<ParticipantId>, |
| 49 | /// The base64-encoded MAA ResponseEvent protocol buffer message. |
| 50 | response_event: String, |
| 51 | /// For forked conversations, this is the original conversation token that the |
| 52 | /// conversation was forked from. Viewers use this to link the new server-assigned |
| 53 | /// conversation token to an existing conversation created during historical replay. |
| 54 | #[serde(default)] |
| 55 | forked_from_conversation_token: Option<String>, |
| 56 | }, |
| 57 | /// Marks the start of historical agent conversation replay. |
| 58 | /// Viewers should use this to suppress live-conversation specific actions until replay ends |
| 59 | /// (e.g. the insertion of the ambient agent conversation tombstone). |
| 60 | AgentConversationReplayStarted, |
| 61 | /// Marks the end of historical agent conversation replay. |
| 62 | AgentConversationReplayEnded, |
| 63 | /// Emitted by the sandboxed Oz AgentDriver when the cloud-mode setup phase is complete but no |
| 64 | /// initial LLM turn will follow (e.g. empty-prompt local-to-cloud handoff with `--skip-initial-turn`). |
| 65 | CloudModeSetupPhaseEnded, |
| 66 | } |
| 67 | |
| 68 | /// Represents the size of a PTY. Mimics the winsize struct that |
| 69 | /// can be queried via [ioctl](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html). |
| 70 | #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)] |
| 71 | pub struct WindowSize { |
| 72 | pub num_rows: usize, |
| 73 | pub num_cols: usize, |
| 74 | } |
| 75 | |
| 76 | /// Override the Debug impl to avoid accidentally leaking sensitive |
| 77 | /// data in logs. |
| 78 | impl std::fmt::Debug for OrderedTerminalEventType { |
| 79 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 80 | match self { |
| 81 | Self::PtyBytesRead { .. } => f.write_str("PtyBytesRead"), |
| 82 | Self::CommandExecutionStarted { .. } => f.write_str("CommandExecutionStarted"), |
| 83 | Self::CommandExecutionFinished { .. } => f.write_str("CommandExecutionFinished"), |
| 84 | Self::Resize { .. } => f.write_str("Resize"), |
| 85 | Self::AgentResponseEvent { .. } => f.write_str("AgentResponseEvent"), |
| 86 | Self::AgentConversationReplayStarted => f.write_str("AgentConversationReplayStarted"), |
| 87 | Self::AgentConversationReplayEnded => f.write_str("AgentConversationReplayEnded"), |
| 88 | Self::CloudModeSetupPhaseEnded => f.write_str("CloudModeSetupPhaseEnded"), |
| 89 | } |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | impl OrderedTerminalEventType { |
| 94 | pub fn num_bytes(&self) -> Byte { |
| 95 | match &self { |
| 96 | OrderedTerminalEventType::PtyBytesRead { bytes } => bytes.len().into(), |
| 97 | OrderedTerminalEventType::AgentResponseEvent { response_event, .. } => { |
| 98 | response_event.len().into() |
| 99 | } |
| 100 | OrderedTerminalEventType::CommandExecutionStarted { .. } |
| 101 | | OrderedTerminalEventType::CommandExecutionFinished { .. } |
| 102 | | OrderedTerminalEventType::AgentConversationReplayStarted |
| 103 | | OrderedTerminalEventType::AgentConversationReplayEnded |
| 104 | | OrderedTerminalEventType::CloudModeSetupPhaseEnded |
| 105 | | OrderedTerminalEventType::Resize { .. } => Byte::from_u64(0), |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | /// Any terminal event where strict ordering against other terminal events is important. |
| 111 | #[derive(Clone, Debug, Deserialize, Serialize)] |
| 112 | pub struct OrderedTerminalEvent { |
| 113 | pub event_no: usize, |
| 114 | pub event_type: OrderedTerminalEventType, |
| 115 | } |
| 116 | |
| 117 | impl OrderedTerminalEvent { |
| 118 | pub fn num_bytes(&self) -> Byte { |
| 119 | self.event_type.num_bytes() |
| 120 | } |
| 121 | } |