1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use crate::capability_types::AgentCapabilityConfig;
10use crate::events::TokenUsage;
11use crate::mcp_server::{ScopedMcpServers, scoped_mcp_servers_is_empty};
12use crate::network_access::NetworkAccessList;
13use crate::principal::PrincipalSummary;
14use crate::tool_types::ToolDefinition;
15use crate::typed_id::{
16 AgentId, AgentIdentityId, AgentVersionId, HarnessId, ModelId, PrincipalId, SessionId,
17 SessionParticipantId, WorkspaceId,
18};
19
20#[cfg(feature = "openapi")]
21use utoipa::ToSchema;
22
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
25#[cfg_attr(feature = "openapi", derive(ToSchema))]
26#[serde(rename_all = "snake_case")]
27pub enum SubagentStatus {
28 Spawning,
29 Running,
30 Completed,
31 Failed,
32 Cancelled,
33 MaxIterationsReached,
34 Sealed,
40}
41
42impl std::fmt::Display for SubagentStatus {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 SubagentStatus::Spawning => write!(f, "spawning"),
46 SubagentStatus::Running => write!(f, "running"),
47 SubagentStatus::Completed => write!(f, "completed"),
48 SubagentStatus::Failed => write!(f, "failed"),
49 SubagentStatus::Cancelled => write!(f, "cancelled"),
50 SubagentStatus::MaxIterationsReached => write!(f, "max_iterations_reached"),
51 SubagentStatus::Sealed => write!(f, "sealed"),
52 }
53 }
54}
55
56impl From<&str> for SubagentStatus {
57 fn from(s: &str) -> Self {
58 match s {
59 "spawning" => SubagentStatus::Spawning,
60 "running" => SubagentStatus::Running,
61 "completed" => SubagentStatus::Completed,
62 "failed" => SubagentStatus::Failed,
63 "cancelled" => SubagentStatus::Cancelled,
64 "max_iterations_reached" => SubagentStatus::MaxIterationsReached,
65 "sealed" => SubagentStatus::Sealed,
66 _ => SubagentStatus::Spawning,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77#[cfg_attr(feature = "openapi", derive(ToSchema))]
78#[serde(rename_all = "lowercase")]
79pub enum SessionStatus {
80 Started,
82 Active,
84 Idle,
86 WaitingForToolResults,
88 Paused,
90}
91
92impl std::fmt::Display for SessionStatus {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 match self {
95 SessionStatus::Started => write!(f, "started"),
96 SessionStatus::Active => write!(f, "active"),
97 SessionStatus::Idle => write!(f, "idle"),
98 SessionStatus::WaitingForToolResults => write!(f, "waiting_for_tool_results"),
99 SessionStatus::Paused => write!(f, "paused"),
100 }
101 }
102}
103
104impl From<&str> for SessionStatus {
105 fn from(s: &str) -> Self {
106 match s {
107 "active" => SessionStatus::Active,
108 "idle" => SessionStatus::Idle,
109 "waiting_for_tool_results" => SessionStatus::WaitingForToolResults,
110 "paused" => SessionStatus::Paused,
111 "running" => SessionStatus::Active,
113 "pending" | "completed" | "failed" => SessionStatus::Idle,
114 _ => SessionStatus::Started,
115 }
116 }
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
121#[cfg_attr(feature = "openapi", derive(ToSchema))]
122#[serde(rename_all = "snake_case")]
123pub enum SessionParticipantKind {
124 Agent,
125 User,
126}
127
128impl std::fmt::Display for SessionParticipantKind {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 match self {
131 SessionParticipantKind::Agent => write!(f, "agent"),
132 SessionParticipantKind::User => write!(f, "user"),
133 }
134 }
135}
136
137impl From<&str> for SessionParticipantKind {
138 fn from(s: &str) -> Self {
139 match s {
140 "agent" => SessionParticipantKind::Agent,
141 "user" => SessionParticipantKind::User,
142 _ => SessionParticipantKind::User,
143 }
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
149#[cfg_attr(feature = "openapi", derive(ToSchema))]
150#[serde(rename_all = "snake_case")]
151pub enum SessionParticipantRole {
152 Host,
153 Member,
154}
155
156impl std::fmt::Display for SessionParticipantRole {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 match self {
159 SessionParticipantRole::Host => write!(f, "host"),
160 SessionParticipantRole::Member => write!(f, "member"),
161 }
162 }
163}
164
165impl From<&str> for SessionParticipantRole {
166 fn from(s: &str) -> Self {
167 match s {
168 "host" => SessionParticipantRole::Host,
169 "member" => SessionParticipantRole::Member,
170 _ => SessionParticipantRole::Member,
171 }
172 }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177#[cfg_attr(feature = "openapi", derive(ToSchema))]
178pub struct SessionParticipant {
179 #[cfg_attr(
181 feature = "openapi",
182 schema(
183 value_type = String,
184 example = "part_01933b5a00007000800000000000001"
185 )
186 )]
187 pub id: SessionParticipantId,
188 #[cfg_attr(
190 feature = "openapi",
191 schema(
192 value_type = String,
193 example = "session_01933b5a00007000800000000000001"
194 )
195 )]
196 pub session_id: SessionId,
197 pub kind: SessionParticipantKind,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 #[cfg_attr(
201 feature = "openapi",
202 schema(
203 value_type = Option<String>,
204 example = "agent_01933b5a00007000800000000000001"
205 )
206 )]
207 pub agent_id: Option<AgentId>,
208 #[serde(skip_serializing_if = "Option::is_none")]
210 #[cfg_attr(
211 feature = "openapi",
212 schema(
213 value_type = Option<String>,
214 example = "agentver_01933b5a00007000800000000000001"
215 )
216 )]
217 pub agent_version_id: Option<AgentVersionId>,
218 #[cfg_attr(
220 feature = "openapi",
221 schema(
222 value_type = String,
223 example = "principal_01933b5a000070008000000000000001"
224 )
225 )]
226 pub principal_id: PrincipalId,
227 pub role: SessionParticipantRole,
228 pub joined_at: DateTime<Utc>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub left_at: Option<DateTime<Utc>>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
236#[cfg_attr(feature = "openapi", derive(ToSchema))]
237pub struct Session {
238 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "session_01933b5a00007000800000000000001"))]
240 pub id: SessionId,
241 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "org_00000000000000000000000000000001"))]
243 pub organization_id: String,
244 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "wsp_01933b5a00007000800000000000001"))]
248 pub workspace_id: WorkspaceId,
249 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "harness_01933b5a00007000800000000000001"))]
251 pub harness_id: HarnessId,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "agent_01933b5a00007000800000000000001"))]
255 pub agent_id: Option<AgentId>,
256 #[serde(skip_serializing_if = "Option::is_none")]
258 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "agentver_01933b5a00007000800000000000001"))]
259 pub agent_version_id: Option<AgentVersionId>,
260 #[serde(skip_serializing_if = "Option::is_none")]
262 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "identity_01933b5a00007000800000000000001"))]
263 pub agent_identity_id: Option<AgentIdentityId>,
264 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "principal_01933b5a000070008000000000000001"))]
266 pub owner_principal_id: PrincipalId,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 #[cfg_attr(
270 feature = "openapi",
271 schema(example = "550e8400-e29b-41d4-a716-446655440000")
272 )]
273 pub resolved_owner_user_id: Option<uuid::Uuid>,
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub owner: Option<PrincipalSummary>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub effective_owner: Option<PrincipalSummary>,
280 #[serde(skip_serializing_if = "Option::is_none")]
282 #[cfg_attr(feature = "openapi", schema(example = "Q3 marketing brief"))]
283 pub title: Option<String>,
284 #[serde(skip_serializing_if = "Option::is_none")]
286 #[cfg_attr(
287 feature = "openapi",
288 schema(example = "Investigate the queue latency regression")
289 )]
290 pub goal: Option<String>,
291 #[serde(skip_serializing_if = "Option::is_none")]
293 #[cfg_attr(feature = "openapi", schema(example = "en-US"))]
294 pub locale: Option<String>,
295 #[serde(skip_serializing_if = "Option::is_none")]
297 #[cfg_attr(
298 feature = "openapi",
299 schema(example = "Help me draft the Q3 marketing plan")
300 )]
301 pub preview: Option<String>,
302 #[serde(skip_serializing_if = "Option::is_none")]
304 #[cfg_attr(
305 feature = "openapi",
306 schema(example = "Here is a Q3 plan covering the three pillars we discussed...")
307 )]
308 pub output_preview: Option<String>,
309 #[serde(default)]
311 #[cfg_attr(feature = "openapi", schema(example = json!(["marketing", "q3", "draft"])))]
312 pub tags: Vec<String>,
313 #[serde(skip_serializing_if = "Option::is_none")]
316 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "model_01933b5a00007000800000000000001"))]
317 pub model_id: Option<ModelId>,
318 #[serde(default, skip_serializing_if = "Vec::is_empty")]
321 pub capabilities: Vec<AgentCapabilityConfig>,
322 #[serde(default, skip_serializing_if = "Vec::is_empty")]
324 pub tools: Vec<ToolDefinition>,
325 #[serde(
327 default,
328 rename = "mcpServers",
329 alias = "mcp_servers",
330 skip_serializing_if = "scoped_mcp_servers_is_empty"
331 )]
332 pub mcp_servers: ScopedMcpServers,
333 #[serde(default, skip_serializing_if = "Option::is_none")]
336 pub system_prompt: Option<String>,
337 #[serde(default, skip_serializing_if = "Vec::is_empty")]
340 pub initial_files: Vec<crate::session_file::InitialFile>,
341 #[serde(default, skip_serializing_if = "Option::is_none")]
347 pub hints: Option<std::collections::HashMap<String, serde_json::Value>>,
348 #[serde(default, skip_serializing_if = "Option::is_none")]
351 pub network_access: Option<NetworkAccessList>,
352 #[serde(default, skip_serializing_if = "Option::is_none")]
354 #[cfg_attr(feature = "openapi", schema(example = 50))]
355 pub max_iterations: Option<usize>,
356 #[serde(default, skip_serializing_if = "Option::is_none")]
363 #[cfg_attr(feature = "openapi", schema(example = true))]
364 pub parallel_tool_calls: Option<bool>,
365 pub status: SessionStatus,
367 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:00:00Z"))]
369 pub created_at: DateTime<Utc>,
370 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:14:32Z"))]
372 pub updated_at: DateTime<Utc>,
373 #[serde(skip_serializing_if = "Option::is_none")]
375 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:00:01Z"))]
376 pub started_at: Option<DateTime<Utc>>,
377 #[serde(skip_serializing_if = "Option::is_none")]
379 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:14:32Z"))]
380 pub finished_at: Option<DateTime<Utc>>,
381 #[serde(skip_serializing_if = "Option::is_none")]
383 pub usage: Option<TokenUsage>,
384 #[serde(skip_serializing_if = "Option::is_none")]
387 #[cfg_attr(feature = "openapi", schema(example = false))]
388 pub is_pinned: Option<bool>,
389 #[serde(skip_serializing_if = "Option::is_none")]
392 #[cfg_attr(feature = "openapi", schema(example = 2))]
393 pub active_schedule_count: Option<u32>,
394 #[serde(default, skip_serializing_if = "Vec::is_empty")]
399 #[cfg_attr(feature = "openapi", schema(example = json!(["file_system", "secrets"])))]
400 pub features: Vec<String>,
401
402 #[serde(skip_serializing_if = "Option::is_none")]
406 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>))]
407 pub parent_session_id: Option<SessionId>,
408
409 #[serde(default, skip_serializing_if = "Option::is_none")]
414 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>))]
415 pub forked_from_session_id: Option<SessionId>,
416 #[serde(default, skip_serializing_if = "Option::is_none")]
419 #[cfg_attr(feature = "openapi", schema(example = 42))]
420 pub forked_from_sequence: Option<i32>,
421
422 #[serde(skip_serializing_if = "Option::is_none")]
426 #[cfg_attr(feature = "openapi", schema(example = "blueprint_research_pack"))]
427 pub blueprint_id: Option<String>,
428 #[serde(skip_serializing_if = "Option::is_none")]
431 #[cfg_attr(feature = "openapi", schema(value_type = Option<Object>))]
432 pub blueprint_config: Option<serde_json::Value>,
433}
434
435#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
437#[cfg_attr(feature = "openapi", derive(ToSchema))]
438#[serde(rename_all = "snake_case")]
439pub enum SessionSeedMode {
440 #[default]
442 Fresh,
443 Fork,
445 Workspace,
447}
448
449impl SessionSeedMode {
450 pub fn as_str(self) -> &'static str {
451 match self {
452 Self::Fresh => "fresh",
453 Self::Fork => "fork",
454 Self::Workspace => "workspace",
455 }
456 }
457}