#![cfg(all(feature = "comms", any(unix, windows)))]
use serde::{Deserialize, Serialize};
use crate::comms::cursor::Cursor;
use crate::comms::ids::{AgentId, ThreadId};
use crate::comms::model::Thread;
use crate::comms::protocol::SeqMeta;
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct AgentRegisterParams {
#[serde(default)]
pub name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub version: String,
#[serde(default)]
pub skills: Vec<String>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct AgentRegisterResponse {
pub agent_id: String,
pub registered: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct AgentListParams {
#[serde(default)]
pub thread: Option<ThreadId>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct AgentSummary {
pub agent_id: String,
pub name: String,
pub description: String,
pub version: String,
pub skills: Vec<String>,
pub first_seen: i64,
pub last_seen: i64,
}
#[derive(Debug, Serialize)]
pub(super) struct AgentListResponse {
pub total: usize,
pub agents: Vec<AgentSummary>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadSummary {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
pub members: Vec<String>,
pub creator: String,
pub active: bool,
pub created_at: i64,
pub last_activity_micros: i64,
pub stale: bool,
}
pub(super) const STALE_AFTER_HOURS: i64 = 168;
impl ThreadSummary {
pub(super) fn from_thread(thread: &Thread, now_micros: i64) -> Self {
let last = thread.last_activity;
let window_micros = STALE_AFTER_HOURS * 3_600_000_000;
let stale = last == 0 || (now_micros - last) > window_micros;
Self {
id: thread.id.as_str().to_string(),
subject: thread.subject.clone(),
path: thread.path.clone(),
members: thread.members.iter().map(|m| m.as_str().to_string()).collect(),
creator: thread.creator.as_str().to_string(),
active: thread.active,
created_at: thread.created_at,
last_activity_micros: last,
stale,
}
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadStartParams {
#[serde(default)]
pub subject: Option<String>,
#[serde(default)]
pub path: Option<String>,
#[serde(default)]
pub members: Vec<AgentId>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadStartResponse {
pub thread: ThreadSummary,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadListParams {
#[serde(default)]
pub subject_contains: Option<String>,
#[serde(default)]
pub include_archived: bool,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadListResponse {
pub total: usize,
pub threads: Vec<ThreadSummary>,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadJoinParams {
pub thread: ThreadId,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadLeaveParams {
pub thread: ThreadId,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadMembershipResponse {
pub thread: String,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub joined: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub left: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadMembersParams {
pub thread: ThreadId,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadMembersResponse {
pub thread: String,
pub members: Vec<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadMemberParams {
pub thread: ThreadId,
pub member: AgentId,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadMemberChangeResponse {
pub thread: String,
pub member: String,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub added: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub removed: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadArchiveParams {
pub thread: ThreadId,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadArchiveResponse {
pub thread: String,
pub archived: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadPostParams {
pub thread: ThreadId,
pub subject: String,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub tags: Option<Vec<String>>,
#[serde(default)]
pub reply_to: Option<String>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadPostResponse {
pub message_id: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct ThreadHistoryParams {
pub thread: ThreadId,
#[serde(default)]
pub cursor: Option<String>,
#[serde(default)]
pub limit: Option<u32>,
#[serde(default)]
pub since_hours: Option<u32>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct MessageFrontMatter {
pub id: String,
pub thread: String,
pub from: String,
pub subject: String,
pub ts_micros: i64,
pub age_secs: i64,
pub tags: Vec<String>,
pub reply_to: Option<String>,
pub seq: u64,
pub body_len: u32,
pub body_sha: String,
}
impl MessageFrontMatter {
pub(super) fn from_seq_meta(sm: &SeqMeta, now_micros: i64) -> Self {
let meta = &sm.meta;
Self {
id: meta.id.clone(),
thread: meta.thread.as_str().to_string(),
from: meta.from.as_str().to_string(),
subject: meta.subject.clone(),
ts_micros: meta.ts_micros,
age_secs: ((now_micros - meta.ts_micros) / 1_000_000).max(0),
tags: meta.tags.clone(),
reply_to: meta.reply_to.clone(),
seq: sm.seq,
body_len: meta.body_len,
body_sha: meta.body_sha.clone(),
}
}
}
#[derive(Debug, Serialize)]
pub(super) struct ThreadHistoryResponse {
pub total: usize,
pub messages: Vec<MessageFrontMatter>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
}
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct MessageGetParams {
pub message_id: String,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct MessageGetResponse {
pub message_id: String,
pub found: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, schemars::JsonSchema)]
pub struct InboxReadParams {
#[serde(default)]
pub cursor: Option<String>,
#[serde(default)]
pub limit: Option<u32>,
#[serde(default)]
pub mark_read: bool,
#[serde(default)]
pub since_hours: Option<u32>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct InboxReadResponse {
pub total: usize,
pub unread: u32,
pub messages: Vec<MessageFrontMatter>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, schemars::JsonSchema)]
pub struct InboxAckParams {
#[serde(default)]
pub message_ids: Vec<String>,
#[serde(default)]
pub thread: Option<ThreadId>,
#[serde(default)]
pub to_seq: Option<u64>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct CursorAdvance {
pub thread: String,
pub seq: u64,
}
#[derive(Debug, Serialize)]
pub(super) struct InboxAckResponse {
pub acked: usize,
pub cursors_advanced: Vec<CursorAdvance>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, schemars::JsonSchema)]
pub struct InboxWaitParams {
#[serde(default)]
pub timeout_secs: Option<u32>,
#[serde(default)]
pub thread: Option<ThreadId>,
#[serde(default)]
pub since_hours: Option<u32>,
#[serde(default)]
pub cursor: Option<String>,
#[serde(default)]
pub as_agent: Option<String>,
}
#[derive(Debug, Serialize)]
pub(super) struct InboxWaitResponse {
pub timed_out: bool,
pub total: usize,
pub unread: u32,
pub messages: Vec<MessageFrontMatter>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
}