use crate::shared::ContentBlock;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::sync::mpsc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentMessage {
pub session_id: String,
pub at: u64,
#[serde(flatten)]
pub kind: MessageKind,
}
impl AgentMessage {
pub fn new(session_id: &str, kind: MessageKind) -> Self {
let at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
Self {
session_id: session_id.to_string(),
at,
kind,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MessageKind {
UserMessage { text: String },
ReasoningStarted,
PartialAssistantMessage { delta: String },
AssistantMessage { blocks: Vec<ContentBlock> },
PartialToolCall {
call_id: String,
name: String,
delta: String,
},
ToolCall {
call_id: String,
name: String,
arguments: Value,
},
ToolOutput { chunk: String },
ToolResponse {
call_id: String,
output: Vec<ContentBlock>,
is_error: bool,
},
}
#[derive(Clone)]
pub struct MessageChannel {
tx: mpsc::UnboundedSender<AgentMessage>,
}
impl MessageChannel {
pub fn new() -> (Self, mpsc::UnboundedReceiver<AgentMessage>) {
let (tx, rx) = mpsc::unbounded_channel();
(Self { tx }, rx)
}
pub fn disconnected() -> Self {
let (tx, _rx) = mpsc::unbounded_channel();
Self { tx }
}
pub fn send(&self, session_id: &str, kind: MessageKind) {
let _ = self.tx.send(AgentMessage::new(session_id, kind));
}
}
impl Default for MessageChannel {
fn default() -> Self {
Self::disconnected()
}
}