1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5pub struct RepositoryFingerprint {
6 pub remote_url: Option<String>,
7 pub host: Option<String>,
8 pub owner: Option<String>,
9 pub repo_name: Option<String>,
10 pub default_branch: Option<String>,
11}
12
13impl RepositoryFingerprint {
14 pub fn has_safe_identity(&self) -> bool {
15 self.remote_url
16 .as_ref()
17 .is_some_and(|value| !value.trim().is_empty())
18 || (self
19 .host
20 .as_ref()
21 .is_some_and(|value| !value.trim().is_empty())
22 && self
23 .owner
24 .as_ref()
25 .is_some_and(|value| !value.trim().is_empty())
26 && self
27 .repo_name
28 .as_ref()
29 .is_some_and(|value| !value.trim().is_empty()))
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct CheckoutBinding {
35 pub project_id: String,
36 pub local_root: Option<String>,
37 pub branch: Option<String>,
38 pub last_commit: Option<String>,
39 pub client_label: Option<String>,
40 pub last_sync: Option<String>,
41}
42
43impl Default for CheckoutBinding {
44 fn default() -> Self {
45 Self {
46 project_id: "pending".to_string(),
47 local_root: None,
48 branch: None,
49 last_commit: None,
50 client_label: None,
51 last_sync: None,
52 }
53 }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ProjectResolutionRequest {
58 pub fingerprint: RepositoryFingerprint,
59 pub suggested_slug: Option<String>,
60 #[serde(rename = "checkout_binding", alias = "workspace_binding")]
61 pub checkout_binding: Option<CheckoutBinding>,
62 pub project_metadata: Option<ProjectMetadataEnvelope>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ProjectRecord {
67 pub project_id: String,
68 pub slug: String,
69 pub fingerprint: Option<RepositoryFingerprint>,
70 pub created_at: String,
71 pub updated_at: String,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct ProjectResolutionResponse {
76 pub project: ProjectRecord,
77 #[serde(rename = "checkout_bound", alias = "workspace_bound")]
78 pub checkout_bound: bool,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct ToolDefinition {
83 pub name: String,
84 pub description: String,
85 #[serde(rename = "inputSchema")]
86 pub input_schema: Value,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct ToolListResponse {
91 pub tools: Vec<ToolDefinition>,
92 pub total: usize,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ToolCallRequest {
97 pub name: String,
98 pub arguments: Map<String, Value>,
99 pub project_id: Option<String>,
100 pub project_slug: Option<String>,
101 pub repository_fingerprint: Option<RepositoryFingerprint>,
102 #[serde(rename = "checkout_binding", alias = "workspace_binding")]
103 pub checkout_binding: Option<CheckoutBinding>,
104 pub project_metadata: Option<ProjectMetadataEnvelope>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct ToolCallResponse {
109 pub result: Value,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct ServerConnection {
114 pub endpoint: String,
115 pub token: String,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct ProjectMetadataEnvelope {
120 pub schema_version: u32,
121 pub summary: ProjectMetadataSummary,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct ProjectMetadataSummary {
126 pub total_file_count: u64,
127 pub source_file_count: u64,
128 pub markers: Vec<String>,
129 pub languages: Vec<ProjectLanguageStat>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct ProjectLanguageStat {
134 pub language: String,
135 pub file_count: u64,
136}
137
138#[derive(Debug, Clone)]
139pub struct ProjectContext {
140 pub project_slug: String,
141 pub project_root: String,
142 pub fingerprint: RepositoryFingerprint,
143 pub checkout_binding: CheckoutBinding,
144 pub project_metadata: Option<ProjectMetadataEnvelope>,
145}
146
147pub type RepositoryContext = ProjectContext;
148pub type WorkspaceBinding = CheckoutBinding;
149#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct TelemetryIngestRequest {
153 pub tool_name: String,
154 pub tokens_original: i64,
155 pub tokens_saved: i64,
156 pub duration_ms: i64,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub mode: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub repository_fingerprint: Option<RepositoryFingerprint>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub checkout_binding: Option<CheckoutBinding>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub project_slug: Option<String>,
165}