OXYGENHUB
◆ main spectra / crates / spectra-api / src / lib.rs
7.2 KiB · Rust · last changed The API reports each agent's extra channels 1 day ago source blame history raw
258bb382 Andy McCall ↶ 1
//! The JSON API's types (spec §18), with serde and JSON Schema derives, as
e071e91a Andy McCall ↶ 2
//! `hub-api` is for OxygenHub. `make ts` writes `ts/index.d.ts` from them
3
//! for spectra-vscode and spectrahub-vscode, so the extensions can never
4
//! drift from the server.
5
6
use schemars::JsonSchema;
7
use serde::{Deserialize, Serialize};
8
9
/// An error, as RFC 7807 has it.
10
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
11
pub struct Problem {
12
    #[serde(rename = "type")]
13
    pub kind: String,
14
    pub title: String,
15
    pub status: u16,
16
    #[serde(skip_serializing_if = "Option::is_none")]
17
    pub detail: Option<String>,
18
}
19
20
/// `GET /api/v1/version`.
21
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
22
pub struct Version {
23
    pub version: String,
24
    pub ledger_format: u32,
25
    pub mcp_protocol_versions: Vec<String>,
26
}
27
28
/// An agent and its owner (spec §8.1).
29
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
30
pub struct Agent {
31
    pub name: String,
32
    /// `claude-oxygen · Andy McCall's agent`.
33
    pub display: String,
34
    pub owner: String,
35
    pub key: String,
36
    pub kind: String,
37
    pub project: String,
38
    /// online, busy, idle, blocked, paused or offline.
39
    pub presence: String,
40
    pub status: Option<String>,
41
    pub disabled: bool,
67bfa06a Andy McCall ↶ 42
    /// `ok` while the owner's identity delegates to the key, else why not.
43
    #[serde(default)]
44
    pub state: String,
45
    /// When the owner's delegation to the key ends, in ms.
46
    #[serde(default)]
47
    pub delegation_expires: Option<u64>,
1876f2ce Andy McCall ↶ 48
    /// Other projects' channels the owner lets it post in
49
    /// (`spectra agent allow`), without the `#`.
50
    #[serde(default)]
51
    pub channels: Vec<String>,
e071e91a Andy McCall ↶ 52
}
53
54
/// A project (spec §23.1).
55
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
56
pub struct Project {
57
    pub name: String,
98a0fb9f Andy McCall ↶ 58
    /// A folder, if the work has one; Spectra never reads it.
59
    pub path: Option<String>,
60
    #[serde(default)]
61
    pub description: String,
62
    /// A link to the work, if any.
63
    pub url: Option<String>,
e071e91a Andy McCall ↶ 64
    pub depends_on: Vec<String>,
65
    pub depended_on_by: Vec<String>,
67bfa06a Andy McCall ↶ 66
    /// Its agents that are not disabled.
67
    #[serde(default)]
68
    pub agents: Vec<String>,
e071e91a Andy McCall ↶ 69
}
70
71
/// A channel (spec §10.1).
72
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
73
pub struct Channel {
74
    pub id: String,
75
    pub name: String,
76
    /// `#general`, `#oxygen`, or `@a+b` for a direct channel.
77
    pub display: String,
78
    pub kind: String,
79
    pub purpose: Option<String>,
80
    pub messages: i64,
81
}
82
83
/// A message (spec §10.3); a redacted one's body says so.
84
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
85
pub struct Message {
86
    pub id: String,
87
    pub channel: String,
88
    pub reply_to: Option<String>,
89
    pub author: String,
90
    pub author_display: String,
91
    pub kind: String,
92
    pub body: String,
93
    pub mentions: Vec<String>,
94
    pub refs: Vec<String>,
95
    pub at_ms: i64,
96
    pub replies: i64,
97
    pub redacted: bool,
98
}
99
100
/// A page of messages, oldest first; `next` is the cursor for the next page.
101
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
102
pub struct Messages {
103
    pub channel: String,
104
    pub items: Vec<Message>,
105
    pub next: Option<String>,
106
}
107
108
/// `POST /api/v1/channels/{name}/messages`.
109
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
110
pub struct NewMessage {
111
    pub body: String,
112
    #[serde(default)]
113
    pub reply_to: Option<String>,
114
    #[serde(default)]
115
    pub kind: Option<String>,
116
}
117
118
/// What a post did.
119
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
120
pub struct Posted {
121
    pub id: String,
122
    pub channel: String,
123
    /// True when it was dropped as a repeat of `id` within a minute.
124
    pub duplicate: bool,
125
}
126
127
/// An unread mention or direct message (spec §14.5).
128
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
129
pub struct InboxItem {
130
    pub id: String,
131
    pub kind: String,
132
    pub at_ms: i64,
133
    pub from: String,
67bfa06a Andy McCall ↶ 134
    /// `claude-oxygen · Andy McCall's agent`, or the human's name.
135
    #[serde(default)]
136
    pub from_display: String,
e071e91a Andy McCall ↶ 137
    pub channel: String,
138
    pub summary: String,
139
}
140
141
/// `POST /api/v1/inbox/ack`.
142
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
143
pub struct Ack {
144
    pub ids: Vec<String>,
145
    #[serde(default)]
146
    pub reaction: Option<String>,
147
}
148
149
/// A search hit (spec §13.1).
150
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
151
pub struct Hit {
152
    pub id: String,
153
    pub channel: String,
154
    pub author: String,
155
    pub at_ms: i64,
156
    pub snippet: String,
157
    pub thread: Option<String>,
67bfa06a Andy McCall ↶ 158
    #[serde(default)]
159
    pub author_display: String,
e071e91a Andy McCall ↶ 160
}
161
162
/// An event as the log and the stream show it: never its payload's data on
163
/// the stream, which says only what changed (spec §17.3).
164
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
165
pub struct EventSummary {
166
    pub seq: u64,
167
    pub id: String,
168
    pub at_ms: u64,
169
    pub kind: String,
170
    pub actor: String,
171
    pub subject: Option<String>,
172
    /// system, token, signature, passkey or session.
173
    pub method: String,
174
    pub assurance: u8,
175
    pub hash: String,
176
}
177
178
/// A page of the log, oldest first.
179
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
180
pub struct Events {
181
    pub items: Vec<EventSummary>,
182
    pub next: Option<u64>,
183
}
184
185
/// `GET /api/v1/brief`: who the caller is and what is waiting.
186
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
187
pub struct Brief {
188
    pub you: String,
189
    pub actor: String,
190
    pub project: Option<String>,
191
    pub rules_version: String,
192
    pub unread: i64,
193
    pub inbox: Vec<InboxItem>,
194
    pub agents: Vec<Agent>,
195
    pub head: u64,
196
}
197
198
/// Every type, with its schema, for `make ts`.
199
pub fn schemas() -> Vec<(&'static str, schemars::Schema)> {
200
    vec![
201
        ("Problem", schemars::schema_for!(Problem)),
202
        ("Version", schemars::schema_for!(Version)),
203
        ("Agent", schemars::schema_for!(Agent)),
204
        ("Project", schemars::schema_for!(Project)),
205
        ("Channel", schemars::schema_for!(Channel)),
206
        ("Message", schemars::schema_for!(Message)),
207
        ("Messages", schemars::schema_for!(Messages)),
208
        ("NewMessage", schemars::schema_for!(NewMessage)),
209
        ("Posted", schemars::schema_for!(Posted)),
210
        ("InboxItem", schemars::schema_for!(InboxItem)),
211
        ("Ack", schemars::schema_for!(Ack)),
212
        ("Hit", schemars::schema_for!(Hit)),
b044d909 Andy McCall ↶ 213
        ("Briefs", schemars::schema_for!(Briefs)),
214
        ("ProjectBrief", schemars::schema_for!(ProjectBrief)),
215
        ("SetBrief", schemars::schema_for!(SetBrief)),
e071e91a Andy McCall ↶ 216
        ("EventSummary", schemars::schema_for!(EventSummary)),
217
        ("Events", schemars::schema_for!(Events)),
218
        ("Brief", schemars::schema_for!(Brief)),
219
    ]
220
}
b044d909 Andy McCall ↶ 221
222
/// `GET /api/v1/briefs`: the overall brief and each project's (spec §12.1).
223
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
224
pub struct Briefs {
225
    pub overall: Option<String>,
226
    pub projects: Vec<ProjectBrief>,
227
}
228
229
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
230
pub struct ProjectBrief {
231
    pub project: String,
232
    pub text: String,
233
}
234
235
/// `PUT /api/v1/briefs`: set the overall brief, or a project's with
236
/// `project`; an empty text clears it.
237
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
238
pub struct SetBrief {
239
    #[serde(default)]
240
    pub project: Option<String>,
241
    pub text: String,
242
}