journal_new: separate name from title, dedup keys

- journal_new(name, title, body): name becomes the node key,
  title goes in the ## heading. Agent picks short searchable names.
- Auto-dedup: if the key exists, append -2, -3, etc.
- CLI journal write also requires a name argument now.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
ProofOfConcept 2026-03-26 19:24:19 -04:00
parent 8eaf4c5956
commit 5647842412
2 changed files with 23 additions and 8 deletions

View file

@ -56,11 +56,12 @@ pub fn definitions() -> Vec<ToolDef> {
"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::<String>())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.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))?;

View file

@ -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")