Resolve seen lists from ContextState, not filesystem
{{seen_current}} and {{seen_previous}} now read Memory entry keys
directly from the conscious agent's ContextState — the single source
of truth for what's been surfaced. No more reading session files
written by the old process-spawning path.
{{input:walked}} still reads from the output dir (inter-run state
written by the surface agent's output() tool).
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
e2e0371726
commit
ba62e0a767
1 changed files with 26 additions and 40 deletions
|
|
@ -75,7 +75,12 @@ impl SubconsciousAgent {
|
|||
|
||||
/// Resolve {{placeholder}} templates in subconscious agent prompts.
|
||||
/// Handles: seen_current, seen_previous, input:KEY.
|
||||
fn resolve_prompt(template: &str, session_id: &str, output_dir: &std::path::Path) -> String {
|
||||
/// Resolve {{placeholder}} templates in subconscious agent prompts.
|
||||
fn resolve_prompt(
|
||||
template: &str,
|
||||
memory_keys: &[String],
|
||||
output_dir: &std::path::Path,
|
||||
) -> String {
|
||||
let mut result = String::with_capacity(template.len());
|
||||
let mut rest = template;
|
||||
while let Some(start) = rest.find("{{") {
|
||||
|
|
@ -84,8 +89,16 @@ fn resolve_prompt(template: &str, session_id: &str, output_dir: &std::path::Path
|
|||
if let Some(end) = after.find("}}") {
|
||||
let name = after[..end].trim();
|
||||
let replacement = match name {
|
||||
"seen_current" => resolve_seen_list(session_id, ""),
|
||||
"seen_previous" => resolve_seen_list(session_id, "-prev"),
|
||||
"seen_current" | "seen_previous" => {
|
||||
if memory_keys.is_empty() {
|
||||
"(none)".to_string()
|
||||
} else {
|
||||
memory_keys.iter()
|
||||
.map(|k| format!("- {}", k))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
}
|
||||
_ if name.starts_with("input:") => {
|
||||
let key = &name[6..];
|
||||
std::fs::read_to_string(output_dir.join(key))
|
||||
|
|
@ -102,7 +115,6 @@ fn resolve_prompt(template: &str, session_id: &str, output_dir: &std::path::Path
|
|||
result.push_str(&replacement);
|
||||
rest = &after[end + 2..];
|
||||
} else {
|
||||
// Unclosed {{ — pass through
|
||||
result.push_str("{{");
|
||||
rest = after;
|
||||
}
|
||||
|
|
@ -110,38 +122,6 @@ fn resolve_prompt(template: &str, session_id: &str, output_dir: &std::path::Path
|
|||
result.push_str(rest);
|
||||
result
|
||||
}
|
||||
|
||||
fn resolve_seen_list(session_id: &str, suffix: &str) -> String {
|
||||
if session_id.is_empty() { return "(no session)".to_string(); }
|
||||
|
||||
let path = crate::store::memory_dir()
|
||||
.join("sessions")
|
||||
.join(format!("seen{}-{}", suffix, session_id));
|
||||
|
||||
let entries: Vec<(String, String)> = std::fs::read_to_string(&path).ok()
|
||||
.map(|content| {
|
||||
content.lines()
|
||||
.filter(|s| !s.is_empty())
|
||||
.filter_map(|line| {
|
||||
let (ts, key) = line.split_once('\t')?;
|
||||
Some((ts.to_string(), key.to_string()))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
if entries.is_empty() { return "(none)".to_string(); }
|
||||
|
||||
let mut sorted = entries;
|
||||
sorted.sort_by(|a, b| b.0.cmp(&a.0));
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
sorted.into_iter()
|
||||
.filter(|(_, key)| seen.insert(key.clone()))
|
||||
.take(20)
|
||||
.map(|(ts, key)| format!("- {} ({})", key, ts))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
/// Which pane streaming text should go to.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StreamTarget {
|
||||
|
|
@ -540,10 +520,16 @@ impl Mind {
|
|||
.sum::<u64>()
|
||||
};
|
||||
|
||||
// Get session_id for placeholder resolution
|
||||
let session_id = {
|
||||
// Get memory keys from conscious agent for placeholder resolution
|
||||
let memory_keys: Vec<String> = {
|
||||
let ag = self.agent.lock().await;
|
||||
ag.session_id.clone()
|
||||
ag.context.entries.iter().filter_map(|e| {
|
||||
if let crate::agent::context::ConversationEntry::Memory { key, .. } = e {
|
||||
Some(key.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect()
|
||||
};
|
||||
|
||||
// Collect which agents to trigger (can't hold lock across await)
|
||||
|
|
@ -563,7 +549,7 @@ impl Mind {
|
|||
.join("agent-output").join(&sub.name);
|
||||
|
||||
let steps: Vec<AutoStep> = sub.def.steps.iter().map(|s| {
|
||||
let prompt = resolve_prompt(&s.prompt, &session_id, &output_dir);
|
||||
let prompt = resolve_prompt(&s.prompt, &memory_keys, &output_dir);
|
||||
AutoStep { prompt, phase: s.phase.clone() }
|
||||
}).collect();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue