Skip to main content

tracevault_core/
streaming.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sha2::{Digest, Sha256};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6#[serde(rename_all = "snake_case")]
7pub enum StreamEventType {
8    ToolUse,
9    Transcript,
10    SessionStart,
11    SessionEnd,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct StreamEventRequest {
16    pub protocol_version: u32,
17    #[serde(default)]
18    pub tool: Option<String>,
19    pub event_type: StreamEventType,
20    pub session_id: String,
21    pub timestamp: DateTime<Utc>,
22    pub hook_event_name: Option<String>,
23    pub tool_name: Option<String>,
24    pub tool_input: Option<serde_json::Value>,
25    pub tool_response: Option<serde_json::Value>,
26    pub event_index: Option<i32>,
27    pub transcript_lines: Option<Vec<serde_json::Value>>,
28    pub transcript_offset: Option<i64>,
29    pub model: Option<String>,
30    pub cwd: Option<String>,
31    pub final_stats: Option<SessionFinalStats>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct SessionFinalStats {
36    pub duration_ms: Option<i64>,
37    pub total_tokens: Option<i64>,
38    pub input_tokens: Option<i64>,
39    pub output_tokens: Option<i64>,
40    pub cache_read_tokens: Option<i64>,
41    pub cache_write_tokens: Option<i64>,
42    pub user_messages: Option<i32>,
43    pub assistant_messages: Option<i32>,
44    pub total_tool_calls: Option<i32>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct StreamEventResponse {
49    pub session_db_id: uuid::Uuid,
50    pub event_db_id: Option<uuid::Uuid>,
51    pub status: String,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct CommitPushRequest {
56    pub commit_sha: String,
57    pub branch: Option<String>,
58    pub author: String,
59    pub message: Option<String>,
60    pub diff_data: Option<serde_json::Value>,
61    pub committed_at: Option<DateTime<Utc>>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct CommitPushResponse {
66    pub commit_db_id: uuid::Uuid,
67    pub attributions_count: i64,
68}
69
70#[derive(Debug, Clone)]
71pub struct ExtractedFileChange {
72    pub file_path: String,
73    pub change_type: String,
74    pub diff_text: Option<String>,
75    pub content_hash: Option<String>,
76}
77
78pub fn is_file_modifying_tool(tool_name: &str) -> bool {
79    matches!(tool_name, "Write" | "Edit" | "Bash")
80}
81
82pub fn extract_file_change(
83    tool_name: &str,
84    tool_input: &serde_json::Value,
85) -> Option<ExtractedFileChange> {
86    match tool_name {
87        "Write" => {
88            let file_path = tool_input.get("file_path")?.as_str()?.to_string();
89            let content = tool_input.get("content")?.as_str()?;
90            let mut hasher = Sha256::new();
91            hasher.update(content.as_bytes());
92            let hash = format!("{:x}", hasher.finalize());
93            let diff = content
94                .lines()
95                .map(|l| format!("+{l}"))
96                .collect::<Vec<_>>()
97                .join("\n");
98            Some(ExtractedFileChange {
99                file_path,
100                change_type: "create".to_string(),
101                diff_text: Some(diff),
102                content_hash: Some(hash),
103            })
104        }
105        "Edit" => {
106            let file_path = tool_input.get("file_path")?.as_str()?.to_string();
107            let old_string = tool_input.get("old_string")?.as_str()?;
108            let new_string = tool_input.get("new_string")?.as_str()?;
109            let diff = format!("--- {old_string}\n+++ {new_string}");
110            Some(ExtractedFileChange {
111                file_path,
112                change_type: "edit".to_string(),
113                diff_text: Some(diff),
114                content_hash: None,
115            })
116        }
117        _ => None,
118    }
119}