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}