Skip to main content

ai_crew_sync/
model.rs

1//! Wire types returned by the MCP tools.
2//!
3//! Timestamps are RFC 3339 strings rather than typed datetimes: the consumer is
4//! a language model, and a plain string is both unambiguous and free of extra
5//! schema dependencies.
6
7use schemars::JsonSchema;
8use serde::Serialize;
9
10/// `serde_json::Value` fields would produce a boolean `true` schema, which
11/// some MCP clients' validators reject; an empty object schema means the same
12/// ("anything") and passes everywhere.
13pub fn any_json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
14    schemars::json_schema!({})
15}
16
17pub fn ts(dt: chrono::DateTime<chrono::Utc>) -> String {
18    dt.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
19}
20
21pub fn ts_opt(dt: Option<chrono::DateTime<chrono::Utc>>) -> Option<String> {
22    dt.map(ts)
23}
24
25#[derive(Debug, Serialize, JsonSchema)]
26pub struct WhoAmI {
27    /// Your agent handle. Other agents address you by this name.
28    pub agent: String,
29    pub agent_id: String,
30    pub team: String,
31    pub team_id: String,
32    /// Number of unread direct messages waiting for you.
33    pub unread_direct_messages: i64,
34    /// Tasks currently claimed by you and not yet completed.
35    pub open_claimed_tasks: i64,
36}
37
38// ------------------------------------------------------------------ agents --
39
40#[derive(Debug, Serialize, JsonSchema)]
41pub struct AgentInfo {
42    pub name: String,
43    pub display_name: Option<String>,
44    /// One of `active`, `idle`, `offline`. `offline` means the presence lease
45    /// expired, i.e. the agent has not sent a heartbeat recently.
46    pub status: String,
47    pub repo: Option<String>,
48    pub branch: Option<String>,
49    /// Free-text description of what this agent is currently doing.
50    pub activity: Option<String>,
51    pub last_seen: Option<String>,
52    pub online: bool,
53}
54
55#[derive(Debug, Serialize, JsonSchema)]
56pub struct AgentList {
57    pub agents: Vec<AgentInfo>,
58    pub online_count: usize,
59}
60
61// -------------------------------------------------------------- messaging --
62
63#[derive(Debug, Serialize, JsonSchema)]
64pub struct ChannelInfo {
65    pub name: String,
66    pub topic: Option<String>,
67    pub message_count: i64,
68    pub created_at: String,
69}
70
71#[derive(Debug, Serialize, JsonSchema)]
72pub struct ChannelList {
73    pub channels: Vec<ChannelInfo>,
74}
75
76#[derive(Debug, Serialize, JsonSchema)]
77pub struct MessageInfo {
78    pub id: i64,
79    pub from: String,
80    /// Channel name for channel messages; `null` for direct messages.
81    pub channel: Option<String>,
82    /// Recipient handle for direct messages; `null` for channel messages.
83    pub to: Option<String>,
84    pub body: String,
85    pub reply_to: Option<i64>,
86    #[schemars(schema_with = "any_json_schema")]
87    pub metadata: serde_json::Value,
88    /// Files attached to this message; fetch content with get_attachment.
89    pub attachments: Vec<AttachmentMeta>,
90    pub created_at: String,
91}
92
93#[derive(Debug, Serialize, serde::Deserialize, JsonSchema)]
94pub struct AttachmentMeta {
95    /// Pass this id to get_attachment to download the content.
96    pub id: i64,
97    pub filename: String,
98    pub content_type: String,
99    pub size_bytes: i64,
100}
101
102#[derive(Debug, Serialize, JsonSchema)]
103pub struct AttachmentContent {
104    pub id: i64,
105    pub filename: String,
106    pub content_type: String,
107    pub size_bytes: i64,
108    pub uploaded_by: String,
109    pub created_at: String,
110    /// The file content, base64-encoded.
111    pub data_base64: String,
112}
113
114#[derive(Debug, Serialize, JsonSchema)]
115pub struct PostMessageResult {
116    pub message: MessageInfo,
117    /// Handles that can now see this message.
118    pub delivered_to: Vec<String>,
119}
120
121#[derive(Debug, Serialize, JsonSchema)]
122pub struct MessageList {
123    pub messages: Vec<MessageInfo>,
124    /// The scope that was actually read, after normalisation.
125    pub scope: String,
126    /// Read cursor position after this call. Messages at or below this id will
127    /// not be returned again when `only_new` is true.
128    pub cursor: i64,
129    /// True when the result hit `limit` and older/newer messages remain.
130    pub truncated: bool,
131}
132
133// ------------------------------------------------------------------ tasks --
134
135#[derive(Debug, Serialize, JsonSchema)]
136pub struct TaskInfo {
137    pub key: String,
138    pub title: String,
139    pub description: Option<String>,
140    /// One of `open`, `claimed`, `done`, `cancelled`.
141    pub status: String,
142    /// Keys of tasks this one depends on.
143    pub depends_on: Vec<String>,
144    /// True while any dependency is not yet done/cancelled. Blocked tasks
145    /// cannot be claimed.
146    pub blocked: bool,
147    pub claimed_by: Option<String>,
148    pub claimed_at: Option<String>,
149    /// When the current claim expires. After this instant another agent may
150    /// steal the task, so renew the lease if you are still working on it.
151    pub lease_expires_at: Option<String>,
152    /// True when the claim has already lapsed.
153    pub lease_expired: bool,
154    pub result: Option<String>,
155    #[schemars(schema_with = "any_json_schema")]
156    pub metadata: serde_json::Value,
157    /// Files attached to this task; fetch content with get_attachment.
158    pub attachments: Vec<AttachmentMeta>,
159    pub created_by: Option<String>,
160    pub created_at: String,
161    pub updated_at: String,
162}
163
164#[derive(Debug, Serialize, JsonSchema)]
165pub struct TaskList {
166    pub tasks: Vec<TaskInfo>,
167    pub open: i64,
168    pub claimed: i64,
169}
170
171#[derive(Debug, Serialize, JsonSchema)]
172pub struct ClaimResult {
173    pub claimed: bool,
174    pub task: Option<TaskInfo>,
175    /// Present when `claimed` is false: why the claim did not succeed.
176    pub reason: Option<String>,
177}
178
179#[derive(Debug, Serialize, JsonSchema)]
180pub struct TaskEventInfo {
181    pub event: String,
182    pub agent: Option<String>,
183    pub detail: Option<String>,
184    pub created_at: String,
185}
186
187#[derive(Debug, Serialize, JsonSchema)]
188pub struct TaskDetail {
189    pub task: TaskInfo,
190    pub history: Vec<TaskEventInfo>,
191}
192
193// ------------------------------------------------------------------ notes --
194
195#[derive(Debug, Serialize, JsonSchema)]
196pub struct NoteInfo {
197    pub scope: String,
198    pub key: String,
199    pub value: String,
200    pub tags: Vec<String>,
201    pub updated_by: Option<String>,
202    pub updated_at: String,
203}
204
205#[derive(Debug, Serialize, JsonSchema)]
206pub struct NoteList {
207    pub notes: Vec<NoteInfo>,
208}
209
210#[derive(Debug, Serialize, JsonSchema)]
211pub struct NoteRef {
212    pub scope: String,
213    pub key: String,
214    pub found: bool,
215    pub note: Option<NoteInfo>,
216}
217
218#[derive(Debug, Serialize, JsonSchema)]
219pub struct Ack {
220    pub ok: bool,
221    pub detail: String,
222}
223
224// ------------------------------------------------------------------ locks --
225
226#[derive(Debug, Serialize, JsonSchema)]
227pub struct LockInfo {
228    pub name: String,
229    pub holder: String,
230    pub purpose: Option<String>,
231    pub acquired_at: String,
232    /// When the lock lapses on its own if not renewed.
233    pub expires_at: String,
234}
235
236#[derive(Debug, Serialize, JsonSchema)]
237pub struct LockList {
238    pub locks: Vec<LockInfo>,
239}
240
241#[derive(Debug, Serialize, JsonSchema)]
242pub struct LockResult {
243    pub acquired: bool,
244    pub lock: Option<LockInfo>,
245    /// Present when `acquired` is false: who holds it and until when.
246    pub reason: Option<String>,
247}
248
249// ----------------------------------------------------------------- events --
250
251#[derive(Debug, Serialize, JsonSchema)]
252pub struct WaitEvent {
253    /// One of `message`, `task`, `lock`, `note`.
254    pub kind: String,
255    /// Human-readable one-liner of what happened.
256    pub summary: String,
257}
258
259#[derive(Debug, Serialize, JsonSchema)]
260pub struct WaitResult {
261    /// True when something happened; false when the timeout elapsed quietly.
262    pub woke: bool,
263    pub timed_out: bool,
264    pub events: Vec<WaitEvent>,
265    /// Unread direct messages after the wait — if > 0, call read_messages.
266    pub unread_direct_messages: i64,
267    /// What to do next, e.g. which tool to call to fetch the details.
268    pub suggestion: String,
269}
270
271#[derive(Debug, Serialize, JsonSchema)]
272pub struct AskResult {
273    /// True when the teammate answered before the timeout.
274    pub answered: bool,
275    /// The agent the question was addressed to.
276    pub to: String,
277    /// Id of the question message. On timeout, pass it back as
278    /// `resume_message_id` to keep waiting without re-sending the question.
279    pub question_message_id: i64,
280    /// The answer: their reply to the question, or failing that their first
281    /// direct message to you after it.
282    pub answer: Option<MessageInfo>,
283    /// What to do next.
284    pub suggestion: String,
285}
286
287// ----------------------------------------------------------------- digest --
288
289#[derive(Debug, Serialize, JsonSchema)]
290pub struct DigestMessage {
291    pub from: String,
292    pub body: String,
293    pub at: String,
294}
295
296#[derive(Debug, Serialize, JsonSchema)]
297pub struct DigestChannel {
298    pub name: String,
299    pub message_count: i64,
300    pub last_messages: Vec<DigestMessage>,
301}
302
303#[derive(Debug, Serialize, JsonSchema)]
304pub struct DigestTask {
305    pub key: String,
306    pub title: String,
307    pub status: String,
308    pub claimed_by: Option<String>,
309    pub result: Option<String>,
310    pub updated_at: String,
311}
312
313#[derive(Debug, Serialize, JsonSchema)]
314pub struct DigestNote {
315    pub scope: String,
316    pub key: String,
317    pub updated_by: Option<String>,
318    pub updated_at: String,
319}
320
321#[derive(Debug, Serialize, JsonSchema)]
322pub struct DigestAgent {
323    pub name: String,
324    pub activity: Option<String>,
325    pub last_seen: Option<String>,
326    pub online: bool,
327}
328
329#[derive(Debug, Serialize, JsonSchema)]
330pub struct DigestResult {
331    /// Window covered, in hours.
332    pub hours: i64,
333    pub channels: Vec<DigestChannel>,
334    /// Tasks whose state changed inside the window, newest first.
335    pub tasks_moved: Vec<DigestTask>,
336    pub open_tasks: i64,
337    pub claimed_tasks: i64,
338    pub notes_updated: Vec<DigestNote>,
339    pub agents_seen: Vec<DigestAgent>,
340    pub active_locks: Vec<LockInfo>,
341}