unconscious/subconscious: use Option<AutoAgent> instead of placeholder

Previously, spawning an agent used std::mem::replace with an empty-name
AutoAgent as placeholder. This caused ghost stats entries under "" when
those placeholders accidentally got their stats logged.

Now uses Option<AutoAgent> with .take() - the type honestly represents
that the agent is unavailable while running. Panic recovery in
subconscious now properly recreates the agent from its definition.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-12 20:11:40 -04:00
parent 33156d9ab3
commit b94e056372
3 changed files with 37 additions and 23 deletions

View file

@ -33,7 +33,7 @@ fn save_enabled_config(map: &HashMap<String, bool>) {
struct UnconsciousAgent {
name: String,
enabled: bool,
auto: AutoAgent,
auto: Option<AutoAgent>,
handle: Option<tokio::task::JoinHandle<(AutoAgent, Result<(), String>)>>,
/// Shared agent handle — UI locks to read context live.
pub agent: Option<std::sync::Arc<crate::agent::Agent>>,
@ -103,7 +103,7 @@ impl Unconscious {
agents.push(UnconsciousAgent {
name: def.agent.clone(),
enabled,
auto,
auto: Some(auto),
handle: None,
agent: None,
last_run: None,
@ -187,7 +187,7 @@ impl Unconscious {
// Get the AutoAgent back from the finished task (stats already updated)
match handle.now_or_never() {
Some(Ok((auto_back, result))) => {
agent.auto = auto_back;
agent.auto = Some(auto_back);
match result {
Ok(_) => dbglog!("[unconscious] {} completed (run {})",
agent.name, crate::agent::oneshot::get_stats(&agent.name).runs),
@ -244,9 +244,11 @@ impl Unconscious {
store.record_agent_visits(&batch.node_keys, &name).ok();
}
// 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.6, 0));
// Take auto out for the spawned task
let Some(mut auto) = self.agents[idx].auto.take() else {
dbglog!("[unconscious] {} already running", name);
return;
};
let orig_steps = std::mem::replace(&mut auto.steps,
batch.steps.iter().map(|s| AutoStep {
prompt: s.prompt.clone(),
@ -261,7 +263,7 @@ impl Unconscious {
if base_url.is_empty() || model.is_empty() {
dbglog!("[unconscious] API not configured");
auto.steps = orig_steps;
self.agents[idx].auto = auto;
self.agents[idx].auto = Some(auto);
return;
}
@ -271,7 +273,7 @@ impl Unconscious {
Err(e) => {
dbglog!("[unconscious] config: {}", e);
auto.steps = orig_steps;
self.agents[idx].auto = auto;
self.agents[idx].auto = Some(auto);
return;
}
};