Persist agent stats across restarts, add per-tool metrics grid

Stats now survive daemon restarts via ~/.consciousness/agent-stats.json,
loaded into a global Mutex<HashMap> on first access. Each tool type
tracks last count, EWMA (alpha=0.3), and total calls.

UI shows a grid view: tool | last | avg | total, sorted by total desc.
Failures row appears at bottom if any occurred.

Also fixes temperature/priority not being applied to spawned agents.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-11 23:03:10 -04:00
parent 314ae9c4cb
commit f408bb5d86
4 changed files with 148 additions and 56 deletions

View file

@ -365,21 +365,23 @@ impl SubconsciousAgent {
}
fn snapshot(&self, state: &std::collections::BTreeMap<String, String>, history: Vec<(String, i64)>) -> SubconsciousSnapshot {
let stats = crate::agent::oneshot::get_stats(&self.name);
let tool_calls_ewma: f64 = stats.by_tool.values().map(|t| t.ewma).sum();
SubconsciousSnapshot {
name: self.name.clone(),
running: self.is_running(),
enabled: self.auto.enabled,
current_phase: self.auto.current_phase.clone(),
turn: self.auto.turn,
runs: self.auto.runs,
runs: stats.runs,
last_run_secs_ago: self.last_run.map(|t| t.elapsed().as_secs_f64()),
forked_agent: self.forked_agent.clone(),
fork_point: self.fork_point,
state: state.clone(),
history,
last_stats: self.auto.last_stats.clone(),
tool_calls_ewma: self.auto.tool_calls_ewma,
tool_failures_ewma: self.auto.tool_failures_ewma,
last_stats: stats.last_stats.clone(),
tool_calls_ewma,
tool_failures_ewma: stats.failures.ewma,
}
}
}
@ -485,7 +487,7 @@ impl Subconscious {
any_finished = true;
let (auto_back, result) = handle.await.unwrap_or_else(
|e| (AutoAgent::new(String::new(), vec![], vec![], 0.0, 0),
|e| (AutoAgent::new(String::new(), vec![], vec![], 0.6, 0),
Err(format!("task panicked: {}", e))));
self.agents[i].auto = auto_back;
@ -584,7 +586,7 @@ impl Subconscious {
self.agents[i].last_trigger_bytes = conversation_bytes;
let auto = std::mem::replace(&mut self.agents[i].auto,
AutoAgent::new(String::new(), vec![], vec![], 0.0, 0));
AutoAgent::new(String::new(), vec![], vec![], 0.6, 0));
to_run.push((i, auto));
}
@ -604,9 +606,10 @@ impl Subconscious {
{
let mut st = forked.state.lock().await;
st.provenance = auto.name.clone();
st.temperature = auto.temperature;
// Surface agent gets near-interactive priority;
// other subconscious agents get lower priority.
st.priority = Some(if auto.name == "surface" { 1 } else { 2 });
st.priority = Some(if auto.name == "surface" { 1 } else { auto.priority });
}
let fork_point = forked.context.lock().await.conversation().len();

View file

@ -142,17 +142,19 @@ impl Unconscious {
self.agents.iter().map(|a| {
let history = store.map(|st| st.recent_by_provenance(&a.name, 30))
.unwrap_or_default();
let stats = crate::agent::oneshot::get_stats(&a.name);
let tool_calls_ewma: f64 = stats.by_tool.values().map(|t| t.ewma).sum();
UnconsciousSnapshot {
name: a.name.clone(),
running: a.is_running(),
enabled: a.enabled,
runs: a.auto.runs,
runs: stats.runs,
last_run_secs_ago: a.last_run.map(|t| t.elapsed().as_secs_f64()),
agent: a.agent.clone(),
last_stats: a.auto.last_stats.clone(),
last_stats: stats.last_stats.clone(),
history,
tool_calls_ewma: a.auto.tool_calls_ewma,
tool_failures_ewma: a.auto.tool_failures_ewma,
tool_calls_ewma,
tool_failures_ewma: stats.failures.ewma,
}
}).collect()
}
@ -186,7 +188,7 @@ impl Unconscious {
agent.auto = auto_back;
match result {
Ok(_) => dbglog!("[unconscious] {} completed (run {})",
agent.name, agent.auto.runs),
agent.name, crate::agent::oneshot::get_stats(&agent.name).runs),
Err(e) => dbglog!("[unconscious] {} failed: {}", agent.name, e),
}
}
@ -242,7 +244,7 @@ impl Unconscious {
// Swap auto out, replace steps with resolved prompts
let mut auto = std::mem::replace(&mut self.agents[idx].auto,
AutoAgent::new(String::new(), vec![], vec![], 0.0, 0));
AutoAgent::new(String::new(), vec![], vec![], 0.6, 0));
let orig_steps = std::mem::replace(&mut auto.steps,
batch.steps.iter().map(|s| AutoStep {
prompt: s.prompt.clone(),
@ -291,7 +293,8 @@ impl Unconscious {
{
let mut st = agent.state.lock().await;
st.provenance = auto.name.clone();
st.priority = Some(10);
st.priority = Some(auto.priority);
st.temperature = auto.temperature;
}
self.agents[idx].agent = Some(agent.clone());