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 = "coverageMode", alias = "coverage_mode")]
155    pub coverage_mode: PrewriteCoverageMode,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
159#[serde(rename_all = "snake_case")]
160pub enum PrewriteCoverageMode {
161    #[default]
162    None,
163    FilesReviewedBacked,
164    ResearchCorpus,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
168#[serde(rename_all = "snake_case")]
169pub enum ToolMode {
170    Auto,
171    None,
172    Required,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
176#[serde(rename_all = "snake_case")]
177pub enum ContextMode {
178    Auto,
179    Compact,
180    Full,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct TodoItem {
185    pub id: String,
186    pub content: String,
187    pub status: String,
188}