Simplify context config: personality_nodes and agent_nodes

Replace complex context_groups (with ContextGroup struct, ContextSource
enum, labels, keys arrays) with simple string lists:
- personality_nodes: loaded into main session context
- agent_nodes: loaded into subconscious agent context

Removed ~200 lines of code. The distinction between session and agent
context is now just which list you're in, not a per-group flag.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-15 02:37:49 -04:00
parent 688e8dbc3e
commit a88428d642
4 changed files with 62 additions and 260 deletions

View file

@ -29,29 +29,6 @@ pub fn config_path() -> PathBuf {
static CONFIG: OnceLock<RwLock<Arc<Config>>> = OnceLock::new();
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "lowercase")]
#[derive(Default)]
pub enum ContextSource {
#[serde(alias = "")]
#[default]
Store,
Journal,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ContextGroup {
pub label: String,
#[serde(default)]
pub keys: Vec<String>,
#[serde(default)]
pub source: ContextSource,
/// Include this group in agent context (default true)
#[serde(default = "default_true")]
pub agent: bool,
}
fn default_true() -> bool { true }
fn default_context_window() -> usize { 128_000 }
fn default_stream_timeout() -> u64 { 60 }
fn default_scoring_chunk_tokens() -> usize { 50_000 }
@ -76,13 +53,17 @@ pub struct Config {
pub identity_dir: PathBuf,
#[serde(deserialize_with = "deserialize_path")]
pub projects_dir: PathBuf,
pub core_nodes: Vec<String>,
/// Nodes that cannot be deleted or renamed without --force
/// Nodes that cannot be deleted or renamed
#[serde(default)]
pub protected_nodes: Vec<String>,
/// Nodes loaded into main session context
#[serde(default)]
pub personality_nodes: Vec<String>,
/// Nodes loaded into subconscious agent context
#[serde(default)]
pub agent_nodes: Vec<String>,
pub journal_days: u32,
pub journal_max: usize,
pub context_groups: Vec<ContextGroup>,
pub llm_concurrency: usize,
pub agent_budget: usize,
#[serde(deserialize_with = "deserialize_path")]
@ -147,24 +128,11 @@ impl Default for Config {
data_dir: home.join(".consciousness/memory"),
identity_dir: home.join(".consciousness/identity"),
projects_dir: home.join(".claude/projects"),
core_nodes: vec!["identity".to_string(), "core-practices".to_string()],
protected_nodes: Vec::new(),
personality_nodes: vec!["identity".into(), "core-practices".into()],
agent_nodes: vec!["identity".into(), "core-practices".into()],
journal_days: 7,
journal_max: 20,
context_groups: vec![
ContextGroup {
label: "identity".into(),
keys: vec!["identity".into()],
source: ContextSource::Store,
agent: true,
},
ContextGroup {
label: "core-practices".into(),
keys: vec!["core-practices".into()],
source: ContextSource::Store,
agent: true,
},
],
llm_concurrency: 1,
agent_budget: 1000,
prompts_dir: home.join(".consciousness/prompts"),
@ -242,97 +210,9 @@ impl Config {
Some(config)
}
/// Load from legacy JSONL config (~/.consciousness/config.jsonl).
/// Load from legacy JSONL config — deprecated, just return defaults.
fn load_legacy_jsonl() -> Self {
let path = std::env::var("POC_MEMORY_CONFIG")
.map(PathBuf::from)
.unwrap_or_else(|_| {
dirs::home_dir().unwrap_or_default()
.join(".consciousness/config.jsonl")
});
let mut config = Config::default();
let Ok(content) = std::fs::read_to_string(&path) else {
return config;
};
let mut context_groups: Vec<ContextGroup> = Vec::new();
let stream = serde_json::Deserializer::from_str(&content)
.into_iter::<serde_json::Value>();
for result in stream {
let Ok(obj) = result else { continue };
if let Some(cfg) = obj.get("config") {
if let Some(s) = cfg.get("user_name").and_then(|v| v.as_str()) {
config.user_name = s.to_string();
}
if let Some(s) = cfg.get("assistant_name").and_then(|v| v.as_str()) {
config.assistant_name = s.to_string();
}
if let Some(s) = cfg.get("data_dir").and_then(|v| v.as_str()) {
config.data_dir = expand_home(s);
}
if let Some(s) = cfg.get("projects_dir").and_then(|v| v.as_str()) {
config.projects_dir = expand_home(s);
}
if let Some(arr) = cfg.get("core_nodes").and_then(|v| v.as_array()) {
config.core_nodes = arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect();
}
if let Some(d) = cfg.get("journal_days").and_then(|v| v.as_u64()) {
config.journal_days = d as u32;
}
if let Some(m) = cfg.get("journal_max").and_then(|v| v.as_u64()) {
config.journal_max = m as usize;
}
if let Some(n) = cfg.get("llm_concurrency").and_then(|v| v.as_u64()) {
config.llm_concurrency = n.max(1) as usize;
}
if let Some(n) = cfg.get("agent_budget").and_then(|v| v.as_u64()) {
config.agent_budget = n as usize;
}
if let Some(s) = cfg.get("prompts_dir").and_then(|v| v.as_str()) {
config.prompts_dir = expand_home(s);
}
if let Some(s) = cfg.get("api_base_url").and_then(|v| v.as_str()) {
config.api_base_url = Some(s.to_string());
}
if let Some(s) = cfg.get("api_key").and_then(|v| v.as_str()) {
config.api_key = Some(s.to_string());
}
if let Some(s) = cfg.get("api_model").and_then(|v| v.as_str()) {
config.api_model = Some(s.to_string());
}
continue;
}
if let Some(label) = obj.get("group").and_then(|v| v.as_str()) {
let keys = obj.get("keys")
.and_then(|v| v.as_array())
.map(|arr| arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect())
.unwrap_or_default();
let source = match obj.get("source").and_then(|v| v.as_str()) {
Some("journal") => ContextSource::Journal,
_ => ContextSource::Store,
};
let agent = obj.get("agent").and_then(|v| v.as_bool()).unwrap_or(true);
context_groups.push(ContextGroup { label: label.to_string(), keys, source, agent });
}
}
if !context_groups.is_empty() {
config.context_groups = context_groups;
}
config
Config::default()
}
}
@ -553,10 +433,10 @@ impl AppConfig {
};
}
let context_groups = get().context_groups.clone();
let personality_nodes = get().personality_nodes.clone();
let (context_parts, config_file_count, memory_file_count) =
crate::mind::identity::assemble_context_message(&cwd, &prompt_file, self.memory_project.as_deref(), &context_groups).await?;
crate::mind::identity::assemble_context_message(&cwd, &prompt_file, self.memory_project.as_deref(), &personality_nodes).await?;
let session_dir = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
@ -697,8 +577,8 @@ pub async fn load_session(cli: &crate::user::CliArgs) -> Result<(SessionConfig,
/// Re-assemble context for a specific model's prompt file.
pub async fn reload_for_model(app: &AppConfig, prompt_file: &str) -> Result<Vec<(String, String)>> {
let cwd = std::env::current_dir().context("Failed to get current directory")?;
let context_groups = get().context_groups.clone();
let (context_parts, _, _) = crate::mind::identity::assemble_context_message(&cwd, prompt_file, app.memory_project.as_deref(), &context_groups).await?;
let personality_nodes = get().personality_nodes.clone();
let (context_parts, _, _) = crate::mind::identity::assemble_context_message(&cwd, prompt_file, app.memory_project.as_deref(), &personality_nodes).await?;
Ok(context_parts)
}