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 = "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}