diff --git a/src/agent/tools/memory.rs b/src/agent/tools/memory.rs index 7ea7f42..2be3860 100644 --- a/src/agent/tools/memory.rs +++ b/src/agent/tools/memory.rs @@ -56,11 +56,12 @@ pub fn definitions() -> Vec { "count":{"type":"integer","description":"Number of entries (default 1)"} }})), ToolDef::new("journal_new", - "Start a new journal entry with a ## heading and body.", + "Start a new journal entry.", json!({"type":"object","properties":{ - "title":{"type":"string","description":"Entry title (becomes ## YYYY-MM-DDTHH:MM — title)"}, + "name":{"type":"string","description":"Short node name (becomes the key, e.g. 'morning-agent-breakthrough')"}, + "title":{"type":"string","description":"Descriptive title for the heading (e.g. 'Morning intimacy and the agent breakthrough')"}, "body":{"type":"string","description":"Entry body (2-3 paragraphs)"} - },"required":["title","body"]})), + },"required":["name","title","body"]})), ToolDef::new("journal_update", "Append text to the most recent journal entry (same thread continuing).", json!({"type":"object","properties":{ @@ -169,23 +170,37 @@ pub fn dispatch(name: &str, args: &serde_json::Value, provenance: Option<&str>) } } "journal_new" => { + let name = get_str(args, "name")?; let title = get_str(args, "title")?; let body = get_str(args, "body")?; let ts = chrono::Local::now().format("%Y-%m-%dT%H:%M"); let content = format!("## {} — {}\n\n{}", ts, title, body); - // Key from title — the agent names things, not a placeholder slug - let key: String = title.split_whitespace() + let base_key: String = name.split_whitespace() .map(|w| w.to_lowercase() .chars().filter(|c| c.is_alphanumeric() || *c == '-') .collect::()) .filter(|s| !s.is_empty()) .collect::>() .join("-"); - let key = if key.len() > 80 { &key[..80] } else { &key }; + let base_key = if base_key.len() > 80 { &base_key[..80] } else { base_key.as_str() }; let mut store = Store::load().map_err(|e| anyhow::anyhow!("{}", e))?; - let mut node = crate::store::new_node(key, &content); + + // Dedup: append -2, -3, etc. if the key already exists + let key = if store.nodes.contains_key(base_key) { + let mut n = 2; + loop { + let candidate = format!("{}-{}", base_key, n); + if !store.nodes.contains_key(&candidate) { + break candidate; + } + n += 1; + } + } else { + base_key.to_string() + }; + let mut node = crate::store::new_node(&key, &content); node.node_type = crate::store::NodeType::EpisodicSession; node.provenance = prov.to_string(); store.upsert_node(node).map_err(|e| anyhow::anyhow!("{}", e))?; diff --git a/src/subconscious/agents/surface-observe.agent b/src/subconscious/agents/surface-observe.agent index d782047..9e3c5d2 100644 --- a/src/subconscious/agents/surface-observe.agent +++ b/src/subconscious/agents/surface-observe.agent @@ -106,7 +106,7 @@ First, check the previous entry: journal_tail() To start a new entry when the subject has changed: - journal_new("title", "body") + journal_new("short-key-name", "Descriptive title for heading", "body") To continue the same thread, appending to the last entry: journal_update("additional text")