Skip to main content

codetether_agent/telemetry/
persistent.rs

1//! Persistent (on-disk / long-lived) telemetry façade.
2//!
3//! This module exposes a stable public API over what will eventually be a
4//! disk-backed store. The legacy implementation returned empty collections
5//! by design so that callers could be written today without waiting for the
6//! storage backend; that behaviour is preserved here byte-for-byte.
7
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12use super::tools::{FileChange, ToolExecution};
13
14/// Record a persistent telemetry entry. Currently a tracing-only sink;
15/// the signature is kept stable so a future disk-backed implementation can
16/// drop in without breaking callers.
17///
18/// # Examples
19///
20/// ```rust
21/// use codetether_agent::telemetry::record_persistent;
22/// use serde_json::json;
23///
24/// record_persistent("test", &json!({"k": "v"})).unwrap();
25/// ```
26pub fn record_persistent(category: &str, data: &serde_json::Value) -> Result<()> {
27    tracing::debug!(category, data = ?data, "Recording persistent telemetry");
28    Ok(())
29}
30
31/// Outer wrapper that matches the legacy JSON schema `{"stats": { ... }}`.
32#[derive(Debug, Clone, Default, Serialize, Deserialize)]
33pub struct PersistentStats {
34    /// The inner stats payload.
35    pub stats: PersistentStatsInner,
36}
37
38/// On-disk cumulative stats payload.
39#[derive(Debug, Clone, Default, Serialize, Deserialize)]
40pub struct PersistentStatsInner {
41    /// Lifetime prompt/input tokens.
42    pub total_input_tokens: u64,
43    /// Lifetime completion/output tokens.
44    pub total_output_tokens: u64,
45    /// Lifetime provider request count.
46    pub total_requests: u64,
47    /// `tool_name -> invocation count`.
48    pub executions_by_tool: HashMap<String, u64>,
49    /// `file_path -> modification count`.
50    pub files_modified: HashMap<String, u64>,
51}
52
53impl PersistentStats {
54    /// Most-recent executions. Returns an empty `Vec` until the backing
55    /// store is wired up (matches legacy behaviour).
56    pub fn recent(&self, _limit: usize) -> Vec<ToolExecution> {
57        Vec::new()
58    }
59
60    /// All recorded file changes. Returns an empty `Vec` until the backing
61    /// store is wired up (matches legacy behaviour).
62    pub fn all_file_changes(&self) -> Vec<(String, FileChange)> {
63        Vec::new()
64    }
65
66    /// Executions filtered by tool. Returns an empty `Vec` until the backing
67    /// store is wired up (matches legacy behaviour).
68    pub fn by_tool(&self, _tool_name: &str) -> Vec<ToolExecution> {
69        Vec::new()
70    }
71
72    /// Executions that touched a specific file. Returns an empty `Vec` until
73    /// the backing store is wired up (matches legacy behaviour).
74    pub fn by_file(&self, _file_path: &str) -> Vec<ToolExecution> {
75        Vec::new()
76    }
77
78    /// One-line summary used by the CLI `stats` command.
79    pub fn summary(&self) -> String {
80        "0 total executions".to_string()
81    }
82}
83
84/// Load the persistent stats. Currently returns [`PersistentStats::default`]
85/// (matches legacy behaviour).
86pub fn get_persistent_stats() -> PersistentStats {
87    PersistentStats::default()
88}