Skip to main content

tandem_types/
session.rs

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 = "contextMode", alias = "context_mode")]
111    pub context_mode: Option<ContextMode>,
112    #[serde(default, alias = "writeRequired", alias = "write_required")]
113    pub write_required: Option<bool>,
114    #[serde(
115        default,
116        alias = "prewriteRequirements",
117        alias = "prewrite_requirements"
118    )]
119    pub prewrite_requirements: Option<PrewriteRequirements>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, Default)]
123pub struct PrewriteRequirements {
124    #[serde(
125        default,
126        alias = "workspaceInspectionRequired",
127        alias = "workspace_inspection_required"
128    )]
129    pub workspace_inspection_required: bool,
130    #[serde(
131        default,
132        alias = "webResearchRequired",
133        alias = "web_research_required"
134    )]
135    pub web_research_required: bool,
136    #[serde(
137        default,
138        alias = "concreteReadRequired",
139        alias = "concrete_read_required"
140    )]
141    pub concrete_read_required: bool,
142    #[serde(
143        default,
144        alias = "successfulWebResearchRequired",
145        alias = "successful_web_research_required"
146    )]
147    pub successful_web_research_required: bool,
148    #[serde(
149        default,
150        alias = "repairOnUnmetRequirements",
151        alias = "repair_on_unmet_requirements"
152    )]
153    pub repair_on_unmet_requirements: bool,
154    #[serde(default, alias = "repairBudget", alias = "repair_budget")]
155    pub repair_budget: Option<u32>,
156    #[serde(
157        default,
158        alias = "repairExhaustionBehavior",
159        alias = "repair_exhaustion_behavior"
160    )]
161    pub repair_exhaustion_behavior: Option<PrewriteRepairExhaustionBehavior>,
162    #[serde(default, alias = "coverageMode", alias = "coverage_mode")]
163    pub coverage_mode: PrewriteCoverageMode,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
167#[serde(rename_all = "snake_case")]
168pub enum PrewriteRepairExhaustionBehavior {
169    WaiveAndWrite,
170    FailClosed,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
174#[serde(rename_all = "snake_case")]
175pub enum PrewriteCoverageMode {
176    #[default]
177    None,
178    FilesReviewedBacked,
179    ResearchCorpus,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
183#[serde(rename_all = "snake_case")]
184pub enum ToolMode {
185    Auto,
186    None,
187    Required,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
191#[serde(rename_all = "snake_case")]
192pub enum ContextMode {
193    Auto,
194    Compact,
195    Full,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct TodoItem {
200    pub id: String,
201    pub content: String,
202    pub status: String,
203}