Split context_state_summary: ContextBudget for compaction, UI-only for display

context_state_summary() was used for both compaction decisions (just
needs token counts) and debug screen display (needs full tree with
labels). Split into:

- Agent::context_budget() -> ContextBudget: cheap token counting by
  category, used by compact(), restore_from_log(), mind event loop
- ContextBudget::format(): replaces sections_budget_string() which
  fragily pattern-matched on section name strings
- context_state_summary(): now UI-only, formatting code stays here

Also extracted entry_sections() as shared helper with include_memories
param — false for context_state_summary (memories have own section),
true for conversation_sections_from() (subconscious screen shows all).

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-07 19:02:58 -04:00
parent 9e49398689
commit cf1c64f936
4 changed files with 130 additions and 86 deletions

View file

@ -249,7 +249,12 @@ impl Mind {
/// Initialize — restore log, start daemons and background agents.
pub async fn subconscious_snapshots(&self) -> Vec<SubconsciousSnapshot> {
self.subconscious.lock().await.snapshots()
let store = crate::store::Store::cached().await.ok();
let store_guard = match &store {
Some(s) => Some(s.lock().await),
None => None,
};
self.subconscious.lock().await.snapshots(store_guard.as_deref())
}
pub async fn subconscious_walked(&self) -> Vec<String> {
@ -262,6 +267,10 @@ impl Mind {
ag.restore_from_log();
ag.changed.notify_one();
drop(ag);
// Load persistent subconscious state
let state_path = self.config.session_dir.join("subconscious-state.json");
self.subconscious.lock().await.set_state_path(state_path);
}
pub fn turn_watch(&self) -> tokio::sync::watch::Receiver<bool> {
@ -276,8 +285,7 @@ impl Mind {
MindCommand::Compact => {
let threshold = compaction_threshold(&self.config.app) as usize;
let mut ag = self.agent.lock().await;
let sections = ag.context_state_summary();
if crate::agent::context::sections_used(&sections) > threshold {
if ag.context_budget().total() > threshold {
ag.compact();
ag.notify("compacted");
}
@ -374,9 +382,7 @@ impl Mind {
// Compact if over budget before sending
let threshold = compaction_threshold(&self.config.app) as usize;
let used = crate::agent::context::sections_used(
&ag.context_state_summary());
if used > threshold {
if ag.context_budget().total() > threshold {
ag.compact();
ag.notify("compacted");
}