diff --git a/src/agent/mod.rs b/src/agent/mod.rs index feff433..cb897b7 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -28,7 +28,7 @@ use context::{ConversationEntry, ContextState}; use tools::{summarize_args, working_stack}; use crate::mind::log::ConversationLog; -use crate::agent::context::{ContextSection, SharedContextState}; +use crate::agent::context::ContextSection; use crate::subconscious::learn; // --- Activity tracking (RAII guards) --- @@ -166,8 +166,6 @@ pub struct Agent { tokenizer: CoreBPE, /// Mutable context state — personality, working stack, etc. pub context: ContextState, - /// Shared live context summary — TUI reads this directly for debug screen. - pub shared_context: SharedContextState, /// App config — used to reload identity on compaction and model switching. pub app_config: crate::config::AppConfig, pub prompt_file: String, @@ -193,7 +191,6 @@ impl Agent { app_config: crate::config::AppConfig, prompt_file: String, conversation_log: Option, - shared_context: SharedContextState, active_tools: tools::SharedActiveTools, ) -> Self { let tokenizer = tiktoken_rs::cl100k_base() @@ -223,7 +220,6 @@ impl Agent { conversation_log, tokenizer, context, - shared_context, app_config, prompt_file, session_id, @@ -236,7 +232,6 @@ impl Agent { agent.load_startup_journal(); agent.load_working_stack(); - agent.publish_context_state(); agent } @@ -265,7 +260,6 @@ impl Agent { conversation_log: None, tokenizer, context: self.context.clone(), - shared_context: context::shared_context_state(), app_config: self.app_config.clone(), prompt_file: self.prompt_file.clone(), session_id: self.session_id.clone(), @@ -490,7 +484,6 @@ impl Agent { if let Some(usage) = &usage { me.last_prompt_tokens = usage.prompt_tokens; - me.publish_context_state(); } // Empty response — nudge and retry @@ -542,7 +535,6 @@ impl Agent { for (call, output) in results { me.apply_tool_result(&call, output, &mut ds); } - me.publish_context_state(); continue; } @@ -657,7 +649,6 @@ impl Agent { let mut msg = Message::tool_result(&call.id, &output); msg.stamp(); self.push_entry(ConversationEntry::Memory { key: key.to_string(), message: msg }); - self.publish_context_state(); return; } } @@ -910,25 +901,6 @@ impl Agent { } } - /// Push the current context summary to the shared state for the TUI to read. - pub fn publish_context_state(&self) { - self.publish_context_state_with_scores(None); - } - - pub fn publish_context_state_with_scores(&self, memory_scores: Option<&learn::MemoryScore>) { - let summary = self.context_state_summary(memory_scores); - if let Ok(mut dbg) = std::fs::OpenOptions::new().create(true).append(true) - .open("/tmp/poc-journal-debug.log") { - use std::io::Write; - for s in &summary { - let _ = writeln!(dbg, "[publish] {} ({} tokens, {} children)", s.name, s.tokens, s.children.len()); - } - } - if let Ok(mut state) = self.shared_context.write() { - *state = summary; - } - } - /// Replace base64 image data in older messages with text placeholders. /// Keeps the 2 most recent images live (enough for motion/comparison). /// The tool result message before each image records what was loaded. @@ -1013,9 +985,8 @@ impl Agent { before, after, before_mem, after_mem, before_conv, after_conv); self.generation += 1; self.last_prompt_tokens = 0; - self.publish_context_state(); - let sections = self.shared_context.read().map(|s| s.clone()).unwrap_or_default(); + let sections = self.context_state_summary(None); dbglog!("[compact] budget: {}", context::sections_budget_string(§ions)); } @@ -1043,8 +1014,8 @@ impl Agent { self.context.entries = all; self.compact(); // Estimate prompt tokens from sections so status bar isn't 0 on startup - let sections = self.shared_context.read().map(|s| s.clone()).unwrap_or_default(); - self.last_prompt_tokens = context::sections_used(§ions) as u32; + self.last_prompt_tokens = context::sections_used( + &self.context_state_summary(None)) as u32; true } diff --git a/src/agent/oneshot.rs b/src/agent/oneshot.rs index 28e436d..beabaee 100644 --- a/src/agent/oneshot.rs +++ b/src/agent/oneshot.rs @@ -203,7 +203,10 @@ impl AutoAgent { let mut backend = Backend::Forked(forked); let result = self.run_with_backend(&mut backend, None).await; if let Backend::Forked(ref agent) = backend { + let total = agent.context.entries.len(); self.last_run_entries = agent.context.entries[fork_point..].to_vec(); + dbglog!("[auto] {} fork_point={} total={} captured={}", + self.name, fork_point, total, self.last_run_entries.len()); } self.steps = orig_steps; result @@ -214,6 +217,7 @@ impl AutoAgent { backend: &mut Backend, bail_fn: Option<&(dyn Fn(usize) -> Result<(), String> + Sync)>, ) -> Result { + dbglog!("[auto] {} starting, {} steps", self.name, self.steps.len()); self.turn = 0; self.outputs.clear(); self.current_phase = self.steps.first() @@ -235,6 +239,9 @@ impl AutoAgent { backend.log(format!("turn {} ({} messages)", self.turn, messages.len())); + dbglog!("[auto] {} turn {} ({} messages)", + self.name, self.turn, messages.len()); + let (msg, usage_opt) = Self::api_call_with_retry( &self.name, backend, &self.tools, &messages, &reasoning, self.sampling, self.priority).await?; diff --git a/src/mind/mod.rs b/src/mind/mod.rs index 5fefbb3..602fc21 100644 --- a/src/mind/mod.rs +++ b/src/mind/mod.rs @@ -215,7 +215,6 @@ impl Mind { config: SessionConfig, turn_tx: mpsc::Sender<(Result, StreamTarget)>, ) -> Self { - let shared_context = crate::agent::context::shared_context_state(); let shared_active_tools = crate::agent::tools::shared_active_tools(); let client = ApiClient::new(&config.api_base, &config.api_key, &config.model); @@ -230,7 +229,6 @@ impl Mind { config.app.clone(), config.prompt_file.clone(), conversation_log, - shared_context, shared_active_tools, ); let agent = Arc::new(tokio::sync::Mutex::new(ag)); @@ -278,7 +276,7 @@ impl Mind { MindCommand::Compact => { let threshold = compaction_threshold(&self.config.app) as usize; let mut ag = self.agent.lock().await; - let sections = ag.shared_context.read().map(|s| s.clone()).unwrap_or_default(); + let sections = ag.context_state_summary(None); if crate::agent::context::sections_used(§ions) > threshold { ag.compact(); ag.notify("compacted"); @@ -312,13 +310,12 @@ impl Mind { self.config.session_dir.join("conversation.jsonl"), ).ok(); let mut ag = self.agent.lock().await; - let shared_ctx = ag.shared_context.clone(); let shared_tools = ag.active_tools.clone(); *ag = Agent::new( ApiClient::new(&self.config.api_base, &self.config.api_key, &self.config.model), self.config.system_prompt.clone(), self.config.context_parts.clone(), self.config.app.clone(), self.config.prompt_file.clone(), - new_log, shared_ctx, shared_tools, + new_log, shared_tools, ); } } @@ -366,11 +363,8 @@ impl Mind { // Compact if over budget before sending let threshold = compaction_threshold(&self.config.app) as usize; - ag.publish_context_state(); - let used = { - let sections = ag.shared_context.read().map(|s| s.clone()).unwrap_or_default(); - crate::agent::context::sections_used(§ions) - }; + let used = crate::agent::context::sections_used( + &ag.context_state_summary(None)); if used > threshold { ag.compact(); ag.notify("compacted"); @@ -435,7 +429,6 @@ impl Mind { { let mut ag = self.agent.lock().await; ag.age_out_images(); - ag.publish_context_state(); } cmds.push(MindCommand::Compact); diff --git a/src/user/chat.rs b/src/user/chat.rs index 8078ec8..bd643e2 100644 --- a/src/user/chat.rs +++ b/src/user/chat.rs @@ -863,7 +863,7 @@ impl ScreenView for InteractScreen { agent.expire_activities(); app.status.prompt_tokens = agent.last_prompt_tokens(); app.status.model = agent.model().to_string(); - let sections = agent.shared_context.read().map(|s| s.clone()).unwrap_or_default(); + let sections = agent.context_state_summary(None); app.status.context_budget = crate::agent::context::sections_budget_string(§ions); app.activity = agent.activities.last() .map(|a| a.label.clone()) diff --git a/src/user/context.rs b/src/user/context.rs index 36dde52..af13932 100644 --- a/src/user/context.rs +++ b/src/user/context.rs @@ -16,18 +16,22 @@ use super::{App, ScreenView, screen_legend}; use crate::agent::context::ContextSection; pub(crate) struct ConsciousScreen { + agent: std::sync::Arc>, scroll: u16, selected: Option, expanded: std::collections::HashSet, } impl ConsciousScreen { - pub fn new() -> Self { - Self { scroll: 0, selected: None, expanded: std::collections::HashSet::new() } + pub fn new(agent: std::sync::Arc>) -> Self { + Self { agent, scroll: 0, selected: None, expanded: std::collections::HashSet::new() } } - fn read_context_state(&self, app: &App) -> Vec { - app.shared_context.read().map_or_else(|_| Vec::new(), |s| s.clone()) + fn read_context_state(&self) -> Vec { + match self.agent.try_lock() { + Ok(ag) => ag.context_state_summary(None), + Err(_) => Vec::new(), + } } fn item_count(&self, context_state: &[ContextSection]) -> usize { @@ -121,7 +125,7 @@ impl ScreenView for ConsciousScreen { for event in events { if let ratatui::crossterm::event::Event::Key(key) = event { if key.kind != ratatui::crossterm::event::KeyEventKind::Press { continue; } - let context_state = self.read_context_state(app); + let context_state = self.read_context_state(); let item_count = self.item_count(&context_state); match key.code { @@ -171,7 +175,7 @@ impl ScreenView for ConsciousScreen { if !app.status.context_budget.is_empty() { lines.push(Line::raw(format!(" Budget: {}", app.status.context_budget))); } - let context_state = self.read_context_state(app); + let context_state = self.read_context_state(); if !context_state.is_empty() { let total: usize = context_state.iter().map(|s| s.tokens).sum(); lines.push(Line::raw("")); diff --git a/src/user/mod.rs b/src/user/mod.rs index c06ff76..16a7d39 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -27,7 +27,6 @@ use ratatui::{ }; use std::io; -use crate::agent::context::SharedContextState; /// Status info for the bottom status bar. #[derive(Debug, Clone)] @@ -126,7 +125,6 @@ pub struct App { pub should_quit: bool, pub submitted: Vec, pub(crate) context_info: Option, - pub(crate) shared_context: SharedContextState, pub(crate) agent_state: Vec, pub(crate) walked_count: usize, pub(crate) channel_status: Vec, @@ -134,7 +132,7 @@ pub struct App { } impl App { - pub fn new(model: String, shared_context: SharedContextState, active_tools: crate::agent::tools::SharedActiveTools) -> Self { + pub fn new(model: String, active_tools: crate::agent::tools::SharedActiveTools) -> Self { Self { status: StatusInfo { dmn_state: "resting".into(), dmn_turns: 0, dmn_max_turns: 20, @@ -149,7 +147,7 @@ impl App { top_k: 20, active_tools, should_quit: false, submitted: Vec::new(), - context_info: None, shared_context, + context_info: None, agent_state: Vec::new(), walked_count: 0, channel_status: Vec::new(), idle_info: None, @@ -202,7 +200,6 @@ pub async fn start(cli: crate::user::CliArgs) -> Result<()> { let mind = crate::mind::Mind::new(config, turn_tx); - let shared_context = mind.agent.lock().await.shared_context.clone(); let shared_active_tools = mind.agent.lock().await.active_tools.clone(); let mut result = Ok(()); @@ -216,7 +213,7 @@ pub async fn start(cli: crate::user::CliArgs) -> Result<()> { // UI event loop s.spawn(async { result = run( - tui::App::new(String::new(), shared_context, shared_active_tools), + tui::App::new(String::new(), shared_active_tools), &mind, mind_tx, ).await; }); @@ -337,7 +334,7 @@ pub async fn run( Box::new(crate::user::chat::InteractScreen::new( mind.agent.clone(), mind.shared.clone(), mind_tx.clone(), )), - Box::new(crate::user::context::ConsciousScreen::new()), + Box::new(crate::user::context::ConsciousScreen::new(mind.agent.clone())), Box::new(crate::user::subconscious::SubconsciousScreen::new()), Box::new(crate::user::unconscious::UnconsciousScreen::new()), Box::new(crate::user::thalamus::ThalamusScreen::new()), @@ -455,12 +452,6 @@ pub async fn run( let idx = n as usize; if idx >= 1 && idx <= screens.len() { active_screen = idx; - // Refresh context state when switching to the conscious screen - if idx == 2 { - if let Ok(mut ag) = agent.try_lock() { - ag.publish_context_state(); - } - } } } else if key.modifiers.contains(KeyModifiers::CONTROL) { match key.code {