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};
18
19#[cfg(feature = "openapi")]
20use utoipa::ToSchema;
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
24#[cfg_attr(feature = "openapi", derive(ToSchema))]
25#[serde(rename_all = "snake_case")]
26pub enum SubagentStatus {
27 Spawning,
28 Running,
29 Completed,
30 Failed,
31 Cancelled,
32 MaxIterationsReached,
33}
34
35impl std::fmt::Display for SubagentStatus {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 match self {
38 SubagentStatus::Spawning => write!(f, "spawning"),
39 SubagentStatus::Running => write!(f, "running"),
40 SubagentStatus::Completed => write!(f, "completed"),
41 SubagentStatus::Failed => write!(f, "failed"),
42 SubagentStatus::Cancelled => write!(f, "cancelled"),
43 SubagentStatus::MaxIterationsReached => write!(f, "max_iterations_reached"),
44 }
45 }
46}
47
48impl From<&str> for SubagentStatus {
49 fn from(s: &str) -> Self {
50 match s {
51 "spawning" => SubagentStatus::Spawning,
52 "running" => SubagentStatus::Running,
53 "completed" => SubagentStatus::Completed,
54 "failed" => SubagentStatus::Failed,
55 "cancelled" => SubagentStatus::Cancelled,
56 "max_iterations_reached" => SubagentStatus::MaxIterationsReached,
57 _ => SubagentStatus::Spawning,
58 }
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
68#[cfg_attr(feature = "openapi", derive(ToSchema))]
69#[serde(rename_all = "lowercase")]
70pub enum SessionStatus {
71 Started,
73 Active,
75 Idle,
77 WaitingForToolResults,
79 Paused,
81}
82
83impl std::fmt::Display for SessionStatus {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 match self {
86 SessionStatus::Started => write!(f, "started"),
87 SessionStatus::Active => write!(f, "active"),
88 SessionStatus::Idle => write!(f, "idle"),
89 SessionStatus::WaitingForToolResults => write!(f, "waiting_for_tool_results"),
90 SessionStatus::Paused => write!(f, "paused"),
91 }
92 }
93}
94
95impl From<&str> for SessionStatus {
96 fn from(s: &str) -> Self {
97 match s {
98 "active" => SessionStatus::Active,
99 "idle" => SessionStatus::Idle,
100 "waiting_for_tool_results" => SessionStatus::WaitingForToolResults,
101 "paused" => SessionStatus::Paused,
102 "running" => SessionStatus::Active,
104 "pending" | "completed" | "failed" => SessionStatus::Idle,
105 _ => SessionStatus::Started,
106 }
107 }
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
113#[cfg_attr(feature = "openapi", derive(ToSchema))]
114pub struct Session {
115 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "session_01933b5a00007000800000000000001"))]
117 pub id: SessionId,
118 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "org_00000000000000000000000000000001"))]
120 pub organization_id: String,
121 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "harness_01933b5a00007000800000000000001"))]
123 pub harness_id: HarnessId,
124 #[serde(skip_serializing_if = "Option::is_none")]
126 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "agent_01933b5a00007000800000000000001"))]
127 pub agent_id: Option<AgentId>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "agentver_01933b5a00007000800000000000001"))]
131 pub agent_version_id: Option<AgentVersionId>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "identity_01933b5a00007000800000000000001"))]
135 pub agent_identity_id: Option<AgentIdentityId>,
136 #[cfg_attr(feature = "openapi", schema(value_type = String, example = "principal_01933b5a000070008000000000000001"))]
138 pub owner_principal_id: PrincipalId,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 #[cfg_attr(
142 feature = "openapi",
143 schema(example = "550e8400-e29b-41d4-a716-446655440000")
144 )]
145 pub resolved_owner_user_id: Option<uuid::Uuid>,
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub owner: Option<PrincipalSummary>,
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub effective_owner: Option<PrincipalSummary>,
152 #[serde(skip_serializing_if = "Option::is_none")]
154 #[cfg_attr(feature = "openapi", schema(example = "Q3 marketing brief"))]
155 pub title: Option<String>,
156 #[serde(skip_serializing_if = "Option::is_none")]
158 #[cfg_attr(feature = "openapi", schema(example = "en-US"))]
159 pub locale: Option<String>,
160 #[serde(skip_serializing_if = "Option::is_none")]
162 #[cfg_attr(
163 feature = "openapi",
164 schema(example = "Help me draft the Q3 marketing plan")
165 )]
166 pub preview: Option<String>,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 #[cfg_attr(
170 feature = "openapi",
171 schema(example = "Here is a Q3 plan covering the three pillars we discussed...")
172 )]
173 pub output_preview: Option<String>,
174 #[serde(default)]
176 #[cfg_attr(feature = "openapi", schema(example = json!(["marketing", "q3", "draft"])))]
177 pub tags: Vec<String>,
178 #[serde(skip_serializing_if = "Option::is_none")]
181 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "model_01933b5a00007000800000000000001"))]
182 pub model_id: Option<ModelId>,
183 #[serde(default, skip_serializing_if = "Vec::is_empty")]
186 pub capabilities: Vec<AgentCapabilityConfig>,
187 #[serde(default, skip_serializing_if = "Vec::is_empty")]
189 pub tools: Vec<ToolDefinition>,
190 #[serde(
192 default,
193 rename = "mcpServers",
194 alias = "mcp_servers",
195 skip_serializing_if = "scoped_mcp_servers_is_empty"
196 )]
197 pub mcp_servers: ScopedMcpServers,
198 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub system_prompt: Option<String>,
202 #[serde(default, skip_serializing_if = "Vec::is_empty")]
205 pub initial_files: Vec<crate::session_file::InitialFile>,
206 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub hints: Option<std::collections::HashMap<String, serde_json::Value>>,
213 #[serde(default, skip_serializing_if = "Option::is_none")]
216 pub network_access: Option<NetworkAccessList>,
217 #[serde(default, skip_serializing_if = "Option::is_none")]
219 #[cfg_attr(feature = "openapi", schema(example = 50))]
220 pub max_iterations: Option<usize>,
221 pub status: SessionStatus,
223 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:00:00Z"))]
225 pub created_at: DateTime<Utc>,
226 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:14:32Z"))]
228 pub updated_at: DateTime<Utc>,
229 #[serde(skip_serializing_if = "Option::is_none")]
231 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:00:01Z"))]
232 pub started_at: Option<DateTime<Utc>>,
233 #[serde(skip_serializing_if = "Option::is_none")]
235 #[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:14:32Z"))]
236 pub finished_at: Option<DateTime<Utc>>,
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub usage: Option<TokenUsage>,
240 #[serde(skip_serializing_if = "Option::is_none")]
243 #[cfg_attr(feature = "openapi", schema(example = false))]
244 pub is_pinned: Option<bool>,
245 #[serde(skip_serializing_if = "Option::is_none")]
248 #[cfg_attr(feature = "openapi", schema(example = 2))]
249 pub active_schedule_count: Option<u32>,
250 #[serde(default, skip_serializing_if = "Vec::is_empty")]
255 #[cfg_attr(feature = "openapi", schema(example = json!(["file_system", "secrets"])))]
256 pub features: Vec<String>,
257
258 #[serde(skip_serializing_if = "Option::is_none")]
261 #[cfg_attr(feature = "openapi", schema(value_type = Option<String>))]
262 pub parent_session_id: Option<SessionId>,
263 #[serde(skip_serializing_if = "Option::is_none")]
265 #[cfg_attr(feature = "openapi", schema(example = "Test Runner"))]
266 pub subagent_name: Option<String>,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 #[cfg_attr(
270 feature = "openapi",
271 schema(example = "Run the integration test suite and report failures.")
272 )]
273 pub subagent_task: Option<String>,
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub subagent_status: Option<SubagentStatus>,
277
278 #[serde(skip_serializing_if = "Option::is_none")]
282 #[cfg_attr(feature = "openapi", schema(example = "blueprint_research_pack"))]
283 pub blueprint_id: Option<String>,
284 #[serde(skip_serializing_if = "Option::is_none")]
287 #[cfg_attr(feature = "openapi", schema(value_type = Option<Object>))]
288 pub blueprint_config: Option<serde_json::Value>,
289}