1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5use crate::{HostRuntimeContext, LocalImplicitTenant, Message, ModelSpec, TenantContext};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct SessionTime {
9 pub created: DateTime<Utc>,
10 pub updated: DateTime<Utc>,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Session {
15 pub id: String,
16 pub slug: Option<String>,
17 pub version: Option<String>,
18 pub project_id: Option<String>,
19 pub title: String,
20 pub directory: String,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub workspace_root: Option<String>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub origin_workspace_root: Option<String>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub attached_from_workspace: Option<String>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub attached_to_workspace: Option<String>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub attach_timestamp_ms: Option<u64>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub attach_reason: Option<String>,
33 #[serde(default)]
34 pub tenant_context: TenantContext,
35 pub time: SessionTime,
36 pub model: Option<ModelSpec>,
37 pub provider: Option<String>,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub environment: Option<HostRuntimeContext>,
40 #[serde(default)]
41 pub messages: Vec<Message>,
42}
43
44impl Session {
45 pub fn new(title: Option<String>, directory: Option<String>) -> Self {
46 let now = Utc::now();
47 Self {
48 id: Uuid::new_v4().to_string(),
49 slug: None,
50 version: Some("v1".to_string()),
51 project_id: None,
52 title: title.unwrap_or_else(|| "New session".to_string()),
53 directory: directory.unwrap_or_else(|| ".".to_string()),
54 workspace_root: None,
55 origin_workspace_root: None,
56 attached_from_workspace: None,
57 attached_to_workspace: None,
58 attach_timestamp_ms: None,
59 attach_reason: None,
60 tenant_context: LocalImplicitTenant.into(),
61 time: SessionTime {
62 created: now,
63 updated: now,
64 },
65 model: None,
66 provider: None,
67 environment: None,
68 messages: Vec::new(),
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use tandem_enterprise_contract::TenantSource;
77
78 #[test]
79 fn session_new_uses_local_implicit_tenant() {
80 let session = Session::new(Some("test".to_string()), Some(".".to_string()));
81 assert_eq!(session.tenant_context.org_id, "local");
82 assert_eq!(session.tenant_context.workspace_id, "local");
83 assert_eq!(session.tenant_context.source, TenantSource::LocalImplicit);
84 assert_eq!(session.tenant_context.actor_id, None);
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct CreateSessionRequest {
90 pub parent_id: Option<String>,
91 pub title: Option<String>,
92 pub directory: Option<String>,
93 pub workspace_root: Option<String>,
94 pub project_id: Option<String>,
95 pub model: Option<ModelSpec>,
96 pub provider: Option<String>,
97 pub permission: Option<Vec<serde_json::Value>>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct SendMessageRequest {
102 #[serde(default)]
103 pub parts: Vec<crate::MessagePartInput>,
104 pub model: Option<ModelSpec>,
105 pub agent: Option<String>,
106 #[serde(default, alias = "toolMode", alias = "tool_mode")]
107 pub tool_mode: Option<ToolMode>,
108 #[serde(default, alias = "toolAllowlist", alias = "tool_allowlist")]
109 pub tool_allowlist: Option<Vec<String>>,
110 #[serde(default, alias = "strictKbGrounding", alias = "strict_kb_grounding")]
111 pub strict_kb_grounding: Option<bool>,
112 #[serde(default, alias = "contextMode", alias = "context_mode")]
113 pub context_mode: Option<ContextMode>,
114 #[serde(default, alias = "writeRequired", alias = "write_required")]
115 pub write_required: Option<bool>,
116 #[serde(
117 default,
118 alias = "prewriteRequirements",
119 alias = "prewrite_requirements"
120 )]
121 pub prewrite_requirements: Option<PrewriteRequirements>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize, Default)]
125pub struct PrewriteRequirements {
126 #[serde(
127 default,
128 alias = "workspaceInspectionRequired",
129 alias = "workspace_inspection_required"
130 )]
131 pub workspace_inspection_required: bool,
132 #[serde(
133 default,
134 alias = "webResearchRequired",
135 alias = "web_research_required"
136 )]
137 pub web_research_required: bool,
138 #[serde(
139 default,
140 alias = "concreteReadRequired",
141 alias = "concrete_read_required"
142 )]
143 pub concrete_read_required: bool,
144 #[serde(
145 default,
146 alias = "successfulWebResearchRequired",
147 alias = "successful_web_research_required"
148 )]
149 pub successful_web_research_required: bool,
150 #[serde(
151 default,
152 alias = "repairOnUnmetRequirements",
153 alias = "repair_on_unmet_requirements"
154 )]
155 pub repair_on_unmet_requirements: bool,
156 #[serde(default, alias = "repairBudget", alias = "repair_budget")]
157 pub repair_budget: Option<u32>,
158 #[serde(
159 default,
160 alias = "repairExhaustionBehavior",
161 alias = "repair_exhaustion_behavior"
162 )]
163 pub repair_exhaustion_behavior: Option<PrewriteRepairExhaustionBehavior>,
164 #[serde(default, alias = "coverageMode", alias = "coverage_mode")]
165 pub coverage_mode: PrewriteCoverageMode,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
169#[serde(rename_all = "snake_case")]
170pub enum PrewriteRepairExhaustionBehavior {
171 WaiveAndWrite,
172 FailClosed,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
176#[serde(rename_all = "snake_case")]
177pub enum PrewriteCoverageMode {
178 #[default]
179 None,
180 FilesReviewedBacked,
181 ResearchCorpus,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
185#[serde(rename_all = "snake_case")]
186pub enum ToolMode {
187 Auto,
188 None,
189 Required,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
193#[serde(rename_all = "snake_case")]
194pub enum ContextMode {
195 Auto,
196 Compact,
197 Full,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct TodoItem {
202 pub id: String,
203 pub content: String,
204 pub status: String,
205}