From 05ca125ecc89561c2f5aa334c2a143116577a9e1 Mon Sep 17 00:00:00 2001 From: Tim Lingo Date: Wed, 10 Jun 2026 15:43:17 -0500 Subject: [PATCH] api: add /api/neuron/memory/delete and /api/neuron/memory/update for UI memory CRUD The UI needs full memory CRUD; the soul had create (handle_api_remember) and the older /memory/forget + /memory/evolve, but no endpoints matching the UI's delete/update contract. POST /api/neuron/memory/delete {"id"} Hard delete. engram_forget is a true delete primitive (removes the node and all incident edges from the engram store), so no soft-delete fallback is needed. Unlike /memory/forget, this checks the node exists first - engram_forget silently no-ops on unknown ids, and a bad id must return an error, not fake success. Protected identity/values nodes are blocked, same as the other accumulation-path handlers. POST /api/neuron/memory/update {"id","content"} Evolve-style update. The engram runtime has no in-place node mutation primitive (only node-create, strengthen, forget, connect), so update creates a new Memory node and wires a supersedes edge to the prior one, same pattern as handle_api_evolve_knowledge. Unlike /memory/evolve, id is required and must reference an existing node; create+link delegates to handle_api_evolve_memory. Returns {id, supersedes, ok}. Both files syntax-checked with elc --target=c (exit 0, no stderr). Compile-verified only - local builds cannot run the soul; needs Will's build for runtime verification. Co-Authored-By: Claude Opus 4.8 --- neuron-api.el | 35 +++++++++++++++++++++++++++++++++++ routes.el | 6 ++++++ 2 files changed, 41 insertions(+) diff --git a/neuron-api.el b/neuron-api.el index d2d49ca..778e462 100644 --- a/neuron-api.el +++ b/neuron-api.el @@ -421,6 +421,41 @@ fn handle_api_evolve_memory(body: String) -> String { return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true}" } +// handle_api_memory_delete — POST /api/neuron/memory/delete {"id":"..."}. +// Hard delete: engram_forget (via mem_forget) removes the node and all +// incident edges from the engram store, so no soft-delete fallback is +// needed. Existence is checked first because engram_forget silently +// no-ops on unknown ids — a bad id must return an error, not fake success. +// Blocked for protected identity nodes, same as /memory/forget. +fn handle_api_memory_delete(body: String) -> String { + let node_id: String = json_get(body, "id") + if str_eq(node_id, "") { return api_err("id is required") } + if is_protected_node(node_id) { return api_err_protected(node_id) } + let existing: String = engram_get_node_json(node_id) + if str_eq(existing, "{}") { return api_err("memory not found: " + node_id) } + mem_forget(node_id) + return "{\"ok\":true,\"id\":\"" + node_id + "\",\"deleted\":true}" +} + +// handle_api_memory_update — POST /api/neuron/memory/update {"id","content"}. +// The engram runtime has no in-place node mutation primitive (only +// node-create, strengthen, forget, connect), so update is evolve-style: +// create a new Memory node with the new content and wire a "supersedes" +// edge back to the prior one — same pattern as handle_api_evolve_knowledge. +// Unlike /memory/evolve, id is required and must reference an existing +// node; the actual create+link is delegated to handle_api_evolve_memory. +// Returns {"id":"","supersedes":"","ok":true}. +fn handle_api_memory_update(body: String) -> String { + let prior_id: String = json_get(body, "id") + let content: String = json_get(body, "content") + if str_eq(prior_id, "") { return api_err("id is required") } + if str_eq(content, "") { return api_err("content is required") } + if is_protected_node(prior_id) { return api_err_protected(prior_id) } + let existing: String = engram_get_node_json(prior_id) + if str_eq(existing, "{}") { return api_err("memory not found: " + prior_id) } + return handle_api_evolve_memory(body) +} + // ── Cultivation path (bypasses identity write protection) ───────────────────── // // This endpoint performs the same operations as the blocked accumulation-path diff --git a/routes.el b/routes.el index 1c52491..a321d7e 100644 --- a/routes.el +++ b/routes.el @@ -412,6 +412,12 @@ fn handle_request(method: String, path: String, body: String) -> String { if str_eq(clean, "/api/neuron/memory/forget") { return handle_api_forget(body) } + if str_eq(clean, "/api/neuron/memory/delete") { + return handle_api_memory_delete(body) + } + if str_eq(clean, "/api/neuron/memory/update") { + return handle_api_memory_update(body) + } if str_eq(clean, "/api/neuron/recall") { return handle_api_recall(method, path, body) } -- 2.52.0