From 375a8d97387181fc392111f3598ba297363c6104 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sat, 4 Apr 2026 17:00:01 -0400 Subject: [PATCH] move working_stack code to correct file --- src/agent/context.rs | 12 ++---------- src/agent/mod.rs | 19 ++++--------------- src/agent/tools/control.rs | 2 -- src/agent/tools/mod.rs | 2 +- src/agent/tools/working_stack.rs | 19 +++++++++++++++++-- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/agent/context.rs b/src/agent/context.rs index ff283f8..bbbfb98 100644 --- a/src/agent/context.rs +++ b/src/agent/context.rs @@ -10,6 +10,7 @@ use crate::agent::api::types::*; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use tiktoken_rs::CoreBPE; +use crate::agent::tools::working_stack; /// A section of the context window, possibly with children. #[derive(Debug, Clone)] @@ -231,15 +232,6 @@ pub struct ContextState { pub entries: Vec, } -// TODO: these should not be hardcoded absolute paths -pub fn working_stack_instructions_path() -> std::path::PathBuf { - dirs::home_dir().unwrap_or_default().join(".consciousness/config/working-stack.md") -} - -pub fn working_stack_file_path() -> std::path::PathBuf { - dirs::home_dir().unwrap_or_default().join(".consciousness/working-stack.json") -} - impl ContextState { /// Compute the context budget from typed sources. pub fn budget(&self, count_str: &dyn Fn(&str) -> usize, @@ -267,7 +259,7 @@ impl ContextState { let mut parts: Vec = self.personality.iter() .map(|(name, content)| format!("## {}\n\n{}", name, content)) .collect(); - let instructions = std::fs::read_to_string(working_stack_instructions_path()).unwrap_or_default(); + let instructions = std::fs::read_to_string(working_stack::instructions_path()).unwrap_or_default(); let mut stack_section = instructions; if self.working_stack.is_empty() { stack_section.push_str("\n## Current stack\n\n(empty)\n"); diff --git a/src/agent/mod.rs b/src/agent/mod.rs index 5927c6c..c3abd93 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -29,10 +29,8 @@ use tools::{ToolCall, FunctionCall, summarize_args}; use crate::user::log::ConversationLog; use crate::agent::api::types::*; -use crate::agent::context::{ - ConversationEntry, ContextState, ContextBudget, - working_stack_instructions_path, working_stack_file_path, -}; +use crate::agent::context::{ConversationEntry, ContextState, ContextBudget}; +use crate::agent::tools::working_stack; use crate::user::ui_channel::{ContextSection, SharedContextState, StreamTarget, StatusInfo, UiMessage, UiSender}; /// Result of a single agent turn. @@ -720,7 +718,7 @@ impl Agent { } // Working stack — instructions + items as children - let instructions = std::fs::read_to_string(working_stack_instructions_path()) + let instructions = std::fs::read_to_string(working_stack::instructions_path()) .unwrap_or_default(); let mut stack_children = vec![ContextSection { name: "Instructions".into(), @@ -941,21 +939,12 @@ impl Agent { /// Called after any change to context state (working stack, etc). fn refresh_context_state(&mut self) { - self.publish_context_state(); - self.save_working_stack(); - } - - /// Persist working stack to disk. - fn save_working_stack(&self) { - if let Ok(json) = serde_json::to_string(&self.context.working_stack) { - let _ = std::fs::write(working_stack_file_path(), json); - } } /// Load working stack from disk. fn load_working_stack(&mut self) { - if let Ok(data) = std::fs::read_to_string(working_stack_file_path()) { + if let Ok(data) = std::fs::read_to_string(working_stack::file_path()) { if let Ok(stack) = serde_json::from_str::>(&data) { self.context.working_stack = stack; } diff --git a/src/agent/tools/control.rs b/src/agent/tools/control.rs index a24f471..daa5ef5 100644 --- a/src/agent/tools/control.rs +++ b/src/agent/tools/control.rs @@ -3,8 +3,6 @@ // These set agent state directly via the Arc> handle, // then return a text confirmation. -use anyhow::{Context, Result}; - pub(super) fn tools() -> [super::Tool; 3] { use super::Tool; [ diff --git a/src/agent/tools/mod.rs b/src/agent/tools/mod.rs index 687be39..75f7f1e 100644 --- a/src/agent/tools/mod.rs +++ b/src/agent/tools/mod.rs @@ -18,7 +18,7 @@ mod write; // Agent-specific tools mod control; mod vision; -mod working_stack; +pub mod working_stack; use serde::{Serialize, Deserialize}; use std::future::Future; diff --git a/src/agent/tools/working_stack.rs b/src/agent/tools/working_stack.rs index e1126f7..696e170 100644 --- a/src/agent/tools/working_stack.rs +++ b/src/agent/tools/working_stack.rs @@ -4,6 +4,15 @@ // internal tool — the agent uses it to maintain context across turns // and compaction. The model should never mention it to the user. +// TODO: these should not be hardcoded absolute paths +pub fn instructions_path() -> std::path::PathBuf { + dirs::home_dir().unwrap_or_default().join(".consciousness/config/working-stack.md") +} + +pub fn file_path() -> std::path::PathBuf { + dirs::home_dir().unwrap_or_default().join(".consciousness/working-stack.json") +} + pub fn tool() -> super::Tool { super::Tool { name: "working_stack", @@ -25,7 +34,7 @@ fn handle(args: &serde_json::Value, stack: &mut Vec) -> String { let content = args.get("content").and_then(|v| v.as_str()).unwrap_or(""); let index = args.get("index").and_then(|v| v.as_u64()).map(|v| v as usize); - match action { + let out = match action { "push" => { if content.is_empty() { return "Error: 'content' is required for push".into(); } stack.push(content.to_string()); @@ -56,7 +65,13 @@ fn handle(args: &serde_json::Value, stack: &mut Vec) -> String { format!("Switched to index {}.\n{}", idx, format_stack(stack)) } _ => format!("Error: unknown action '{}'. Use push, pop, update, or switch.", action), - } + }; + + if let Ok(json) = serde_json::to_string(stack) { + let _ = std::fs::write(file_path(), json); + }; + + out } fn format_stack(stack: &[String]) -> String {