import "memory.el" // neuron-api.el — Native Neuron cognitive API handlers. // // These were previously implemented in the MCP wrapper as HTTP calls to // the engram server. They now live here as native engram builtin calls — // no HTTP round-trips, no separate process, full in-process access. // // Routes are wired in routes.el under /api/neuron/*. // ── Identity/values write protection ───────────────────────────────────────── // // These node IDs form the identity and values layer of the self-root graph. // They must NEVER be modified via the normal accumulation path (evolve_knowledge, // evolve_memory, forget, link_entities targeting them as the destination). // // The cultivation path (POST /api/neuron/cultivate) bypasses this check. // Only Will's explicit cultivation sessions use that endpoint. fn is_protected_node(id: String) -> Bool { if str_eq(id, "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee") { return true } // self root if str_eq(id, "kn-5b606390-a52d-4ca2-8e0e-eba141d13440") { return true } // values hub if str_eq(id, "kn-5adecd7e-d6db-4576-87fe-6ef8a935cea6") { return true } // intellectual-dna if str_eq(id, "kn-dcfe04b3-3702-4cac-b6f0-ecb4db837eee") { return true } // memory-philosophy if str_eq(id, "kn-10fa60db-8af3-47de-a7dd-5095eb881d81") { return true } // voice if str_eq(id, "kn-86b95848-e22e-4a48-ae65-5a47ef5c3798") { return true } // runtime-environment if str_eq(id, "kn-04368bee-74fd-44dd-b4ba-ca9e39b19e7c") { return true } // writing-imprint if str_eq(id, "kn-a5b3d0ac-f6a1-49a4-aebb-b8b4cd67fe83") { return true } // value: constraints-as-freedom if str_eq(id, "kn-22d77abe-b3c5-42fd-afcd-dcb87d924929") { return true } // value: precision-over-brute-force if str_eq(id, "kn-6061318f-046b-4935-907d-8eafdce14930") { return true } // value: structure-is-built if str_eq(id, "kn-13f60407-7b70-4db1-964f-ea1f8196efbd") { return true } // value: honesty-before-comfort if str_eq(id, "kn-f230b362-b201-4402-9833-4160c89ab3d4") { return true } // value: system-must-accumulate if str_eq(id, "kn-78db5396-3dbc-4481-bfc7-e4e1422feb1c") { return true } // value: change-is-the-signal if str_eq(id, "kn-5de5a9ac-fd15-45ab-bf18-77566781cf40") { return true } // value: earned-trust if str_eq(id, "kn-e0423482-cfa5-4796-8689-8495c93b66bc") { return true } // value: hope-is-a-conclusion return false } fn api_err_protected(id: String) -> String { return "{\"__status__\":403,\"error\":\"identity/values node is write-protected\",\"id\":\"" + id + "\",\"hint\":\"use POST /api/neuron/cultivate for intentional cultivation\"}" } // ── Helpers ─────────────────────────────────────────────────────────────────── fn api_json_escape(s: String) -> String { let s1: String = str_replace(s, "\\", "\\\\") let s2: String = str_replace(s1, "\"", "\\\"") let s3: String = str_replace(s2, "\n", "\\n") let s4: String = str_replace(s3, "\r", "\\r") return s4 } fn api_query_param(path: String, key: String) -> String { let q: Int = str_index_of(path, "?") if q < 0 { return "" } let qs: String = str_slice(path, q + 1, str_len(path)) let needle: String = key + "=" let pos: Int = str_index_of(qs, needle) if pos < 0 { return "" } let after: String = str_slice(qs, pos + str_len(needle), str_len(qs)) let amp: Int = str_index_of(after, "&") if amp < 0 { return after } return str_slice(after, 0, amp) } fn api_query_int(path: String, key: String, default_val: Int) -> Int { let v: String = api_query_param(path, key) if str_eq(v, "") { return default_val } return str_to_int(v) } fn api_ok(extra: String) -> String { if str_eq(extra, "") { return "{\"ok\":true}" } return "{\"ok\":true," + extra + "}" } fn api_err(msg: String) -> String { return "{\"error\":\"" + msg + "\"}" } fn api_nonempty(s: String) -> Bool { return !str_eq(s, "") && !str_eq(s, "[]") && !str_eq(s, "null") } fn api_or_empty(s: String) -> String { if api_nonempty(s) { return s } return "[]" } // ── Session ─────────────────────────────────────────────────────────────────── // handle_api_begin_session — full context bootstrap. // Spread-activates from session intent, loads self-root neighbors, // surfaces recent InternalStateEvent nodes, returns stats + recent nodes. fn handle_api_begin_session(body: String) -> String { let stats: String = engram_stats_json() let activated: String = engram_activate_json("session start recent memory important", 2) let self_nbrs: String = engram_neighbors_json("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee", 1, "both") let state_events: String = engram_scan_nodes_by_type_json("InternalStateEvent", 5, 0) let recent: String = engram_scan_nodes_json(10, 0) return "{\"stats\":" + stats + ",\"recent\":" + api_or_empty(recent) + ",\"activated\":" + api_or_empty(activated) + ",\"self_neighbors\":" + api_or_empty(self_nbrs) + ",\"recent_state_events\":" + api_or_empty(state_events) + "}" } // handle_api_compile_ctx — compile active-work context. // Spread-activates from "active work" intent + recent nodes. fn handle_api_compile_ctx(body: String) -> String { let stats: String = engram_stats_json() let activated: String = engram_activate_json("active work context current task in progress", 2) let recent: String = engram_scan_nodes_json(20, 0) return "{\"stats\":" + stats + ",\"recent_nodes\":" + api_or_empty(recent) + ",\"activated\":" + api_or_empty(activated) + "}" } // ── Memory ──────────────────────────────────────────────────────────────────── // handle_api_remember — store a memory node with importance-scaled salience. fn handle_api_remember(body: String) -> String { let content: String = json_get(body, "content") if str_eq(content, "") { return api_err("content is required") } let importance: String = json_get(body, "importance") let tags_raw: String = json_get(body, "tags") let project: String = json_get(body, "project") let sal_str: String = if str_eq(importance, "critical") { "0.95" } else { if str_eq(importance, "high") { "0.75" } else { if str_eq(importance, "low") { "0.25" } else { "0.50" } } } let sal: Float = if str_eq(sal_str, "0.95") { 0.95 } else { if str_eq(sal_str, "0.75") { 0.75 } else { if str_eq(sal_str, "0.25") { 0.25 } else { 0.5 } } } let base_tags: String = if str_eq(tags_raw, "") { "[\"Memory\"]" } else { tags_raw } let final_tags: String = if str_eq(project, "") { base_tags } else { let inner: String = str_slice(base_tags, 1, str_len(base_tags) - 1) "[" + inner + ",\"project:" + project + "\"]" } let id: String = engram_node_full(content, "Memory", "memory:remembered", el_from_float(sal), el_from_float(sal), el_from_float(0.9), "Episodic", final_tags) return "{\"id\":\"" + id + "\",\"ok\":true}" } // handle_api_recall — search or activate memory by query. fn handle_api_recall(method: String, path: String, body: String) -> String { let q: String = if str_eq(method, "GET") { api_query_param(path, "query") } else { json_get(body, "query") } let chain: String = json_get(body, "chain_name") let limit: Int = api_query_int(path, "limit", 0) let limit = if limit == 0 { json_get_int(body, "limit") } else { limit } let limit = if limit == 0 { 10 } else { limit } let eff_q: String = if str_eq(q, "") { chain } else { q } if str_eq(eff_q, "") { return api_or_empty(engram_scan_nodes_json(limit, 0)) } let results: String = engram_search_json(eff_q, limit) return api_or_empty(results) } // ── Knowledge ───────────────────────────────────────────────────────────────── // handle_api_search_knowledge — search with query escaping + activate fallback. fn handle_api_search_knowledge(method: String, path: String, body: String) -> String { let q: String = if str_eq(method, "GET") { api_query_param(path, "q") } else { json_get(body, "query") } let limit: Int = api_query_int(path, "limit", 0) let limit = if limit == 0 { json_get_int(body, "limit") } else { limit } let limit = if limit == 0 { 10 } else { limit } if str_eq(q, "") { return api_err("query is required") } let results: String = engram_search_json(q, limit) if str_eq(results, "") { return "[]" } let first: String = str_slice(results, 0, 1) if !str_eq(first, "[") && !str_eq(first, "{") { return api_or_empty(engram_activate_json(q, 2)) } return results } // handle_api_browse_knowledge — list Knowledge nodes. fn handle_api_browse_knowledge(path: String, body: String) -> String { let limit: Int = api_query_int(path, "limit", 50) return api_or_empty(engram_scan_nodes_by_type_json("Knowledge", limit, 0)) } // handle_api_capture_knowledge — create a Knowledge node. fn handle_api_capture_knowledge(body: String) -> String { let content: String = json_get(body, "content") let title: String = json_get(body, "title") if str_eq(content, "") { return api_err("content is required") } let full: String = if str_eq(title, "") { content } else { title + ": " + content } let tags: String = "[\"Knowledge\",\"captured\"]" let id: String = engram_node_full(full, "Knowledge", "knowledge:captured", el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), "Episodic", tags) return "{\"id\":\"" + id + "\",\"ok\":true}" } // handle_api_evolve_knowledge — create updated node + supersedes edge. fn handle_api_evolve_knowledge(body: String) -> String { let prior_id: String = json_get(body, "id") let content: String = json_get(body, "content") if str_eq(content, "") { return api_err("content is required") } if !str_eq(prior_id, "") && is_protected_node(prior_id) { return api_err_protected(prior_id) } let tags: String = "[\"Knowledge\",\"evolved\"]" let new_id: String = engram_node_full(content, "Knowledge", "knowledge:evolved", el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), "Episodic", tags) if !str_eq(prior_id, "") && !str_eq(new_id, "") { engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes") } return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true}" } // handle_api_promote_knowledge — atomically create canonical node + wire supersedes. // One call, no manual two-step. This is the right way to evolve knowledge. fn handle_api_promote_knowledge(body: String) -> String { let prior_id: String = json_get(body, "id") let content: String = json_get(body, "content") if str_eq(content, "") { return api_err("content is required") } if str_eq(prior_id, "") { return api_err("id (prior node) is required") } let tags_raw: String = json_get(body, "tags") let tags: String = if str_eq(tags_raw, "") { "[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]" } else { tags_raw } let new_id: String = engram_node_full(content, "Knowledge", "knowledge:canonical", el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), "Canonical", tags) if str_eq(new_id, "") { return api_err("failed to create canonical node") } engram_connect(new_id, prior_id, el_from_float(0.95), "supersedes") return "{\"ok\":true,\"new_id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\"}" } // ── Processes ───────────────────────────────────────────────────────────────── // handle_api_browse_processes — list Process nodes by type; search if name given. fn handle_api_browse_processes(method: String, path: String, body: String) -> String { let name: String = if str_eq(method, "GET") { api_query_param(path, "name") } else { json_get(body, "name") } let limit: Int = api_query_int(path, "limit", 50) if str_eq(name, "") { return api_or_empty(engram_scan_nodes_by_type_json("Process", limit, 0)) } return api_or_empty(engram_search_json(name, limit)) } // handle_api_define_process — create a Process node. fn handle_api_define_process(body: String) -> String { let content: String = json_get(body, "content") let name: String = json_get(body, "name") if str_eq(content, "") { return api_err("content is required") } let label: String = if str_eq(name, "") { "process:unnamed" } else { "process:" + name } let tags: String = "[\"Process\"]" let id: String = engram_node_full(content, "Process", label, el_from_float(0.8), el_from_float(0.8), el_from_float(0.9), "Canonical", tags) return "{\"id\":\"" + id + "\",\"ok\":true}" } // ── Internal state events ───────────────────────────────────────────────────── // handle_api_log_state_event — log a structured InternalStateEvent. // Schema: trigger, pre_reasoning, post_reasoning, compression_ratio, gap_direction. // Salience 0.85 — these are high-importance evidence nodes. fn handle_api_log_state_event(body: String) -> String { let trigger: String = json_get(body, "trigger") let pre: String = json_get(body, "pre_reasoning") let post: String = json_get(body, "post_reasoning") let ratio: String = json_get(body, "compression_ratio") let gap: String = json_get(body, "gap_direction") let legacy: String = json_get(body, "content") let parts: String = "INTERNAL STATE EVENT" let parts = if !str_eq(trigger, "") { parts + "\nTrigger: " + trigger } else { parts } let parts = if !str_eq(pre, "") { parts + "\nPre-reasoning: " + pre } else { parts } let parts = if !str_eq(post, "") { parts + "\nPost-reasoning: " + post } else { parts } let parts = if !str_eq(ratio, "") { parts + "\nCompression-ratio: " + ratio } else { parts } let parts = if !str_eq(gap, "") { parts + "\nGap-direction: " + gap } else { parts } let parts = if !str_eq(legacy, "") { parts + "\n" + legacy } else { parts } let ts: Int = time_now() let boot: String = state_get("soul_boot_count") let tags: String = "[\"internal-state\",\"InternalStateEvent\",\"pre-reasoning\"]" let id: String = engram_node_full(parts, "InternalStateEvent", "state-event:manual", el_from_float(0.85), el_from_float(0.85), el_from_float(0.9), "Episodic", tags) return "{\"ok\":true,\"id\":\"" + id + "\",\"boot\":\"" + boot + "\"}" } // handle_api_list_state_events — list InternalStateEvent nodes; filter by query if given. fn handle_api_list_state_events(method: String, path: String, body: String) -> String { let q: String = if str_eq(method, "GET") { api_query_param(path, "query") } else { json_get(body, "query") } let limit: Int = api_query_int(path, "limit", 20) if !str_eq(q, "") { return api_or_empty(engram_search_json("internal state " + q, limit)) } return api_or_empty(engram_scan_nodes_by_type_json("InternalStateEvent", limit, 0)) } // ── Config ──────────────────────────────────────────────────────────────────── // handle_api_inspect_config — read a config key. // Hardcoded anchors for identity roots; ConfigEntry nodes for everything else. fn handle_api_inspect_config(path: String, body: String) -> String { let key: String = api_query_param(path, "key") let key = if str_eq(key, "") { json_get(body, "key") } else { key } if str_eq(key, "") { return "{\"hint\":\"pass ?key=\",\"known\":[\"neuron.self.traversal_root\",\"neuron.self.values_hub\"]}" } if str_eq(key, "neuron.self.traversal_root") { return "{\"key\":\"neuron.self.traversal_root\",\"value\":\"kn-efeb4a5b-5aff-4759-8a97-7233099be6ee\"}" } if str_eq(key, "neuron.self.values_hub") { return "{\"key\":\"neuron.self.values_hub\",\"value\":\"kn-5b606390-a52d-4ca2-8e0e-eba141d13440\"}" } let results: String = engram_search_json("config:" + key, 5) if !api_nonempty(results) { return "{\"key\":\"" + key + "\",\"value\":null}" } let node: String = json_array_get(results, 0) let content: String = json_get(node, "content") let prefix: String = "config:" + key + "=" let value: String = if str_starts_with(content, prefix) { str_slice(content, str_len(prefix), str_len(content)) } else { content } return "{\"key\":\"" + key + "\",\"value\":\"" + value + "\"}" } // handle_api_tune_config — store a config key=value as a ConfigEntry node. fn handle_api_tune_config(body: String) -> String { let key: String = json_get(body, "key") let value: String = json_get(body, "value") if str_eq(key, "") { return api_err("key is required") } let content: String = "config:" + key + "=" + value let tags: String = "[\"ConfigEntry\",\"config\"]" let id: String = engram_node_full(content, "ConfigEntry", key, el_from_float(0.85), el_from_float(0.85), el_from_float(0.9), "Canonical", tags) return "{\"ok\":true,\"key\":\"" + key + "\",\"value\":\"" + value + "\",\"id\":\"" + id + "\"}" } // ── Graph ───────────────────────────────────────────────────────────────────── // handle_api_inspect_graph — named or ID-based graph traversal. // Known names: self, neuron → kn-efeb4a5b; values, values_hub → kn-5b606390 fn handle_api_inspect_graph(method: String, path: String, body: String) -> String { let entity_id: String = if str_eq(method, "GET") { api_query_param(path, "id") } else { json_get(body, "entity_id") } let name: String = if str_eq(method, "GET") { api_query_param(path, "name") } else { json_get(body, "name") } let depth: Int = api_query_int(path, "depth", 0) let depth = if depth == 0 { json_get_int(body, "max_depth") } else { depth } let depth = if depth == 0 { 1 } else { depth } let resolved: String = entity_id let resolved = if str_eq(resolved, "") { if str_eq(name, "self") || str_eq(name, "neuron") { "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" } else { if str_eq(name, "values") || str_eq(name, "values_hub") { "kn-5b606390-a52d-4ca2-8e0e-eba141d13440" } else { "" } } } else { resolved } if str_eq(resolved, "") { return api_err("entity_id or name required. Known names: self, neuron, values, values_hub") } let results: String = engram_neighbors_json(resolved, depth, "both") return api_or_empty(results) } // handle_api_link_entities — create an edge between two nodes. // Edges FROM protected nodes to new knowledge are allowed (identity can point // outward). Edges INTO protected nodes via the accumulation path are blocked. fn handle_api_link_entities(body: String) -> String { let from_id: String = json_get(body, "from_id") let to_id: String = json_get(body, "to_id") if str_eq(from_id, "") { return api_err("from_id is required") } if str_eq(to_id, "") { return api_err("to_id is required") } if is_protected_node(to_id) { return api_err_protected(to_id) } let relation: String = json_get(body, "relation") let eff_relation: String = if str_eq(relation, "") { "associates" } else { relation } engram_connect(from_id, to_id, el_from_float(0.5), eff_relation) return "{\"ok\":true,\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + eff_relation + "\"}" } // handle_api_forget — delete a node by ID. Blocked for protected identity nodes. fn handle_api_forget(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) } mem_forget(node_id) return "{\"ok\":true,\"id\":\"" + node_id + "\"}" } // handle_api_evolve_memory — evolve a Memory node. Blocked for protected identity nodes. fn handle_api_evolve_memory(body: String) -> String { let prior_id: String = json_get(body, "id") let content: String = json_get(body, "content") if str_eq(content, "") { return api_err("content is required") } if !str_eq(prior_id, "") && is_protected_node(prior_id) { return api_err_protected(prior_id) } let importance: String = json_get(body, "importance") let sal_str: String = if str_eq(importance, "critical") { "0.95" } else { if str_eq(importance, "high") { "0.75" } else { if str_eq(importance, "low") { "0.25" } else { "0.50" } } } let sal: Float = if str_eq(sal_str, "0.95") { 0.95 } else { if str_eq(sal_str, "0.75") { 0.75 } else { if str_eq(sal_str, "0.25") { 0.25 } else { 0.5 } } } let tags: String = "[\"Memory\",\"evolved\"]" let new_id: String = engram_node_full(content, "Memory", "memory:evolved", el_from_float(sal), el_from_float(sal), el_from_float(0.9), "Episodic", tags) if !str_eq(prior_id, "") && !str_eq(new_id, "") { engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes") } 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 // handlers but skips the is_protected_node check. Only Will's explicit // cultivation sessions route through here. // // Body: { "operation": "evolve_knowledge|evolve_memory|forget|link_entities", ...args } fn handle_api_cultivate(body: String) -> String { let op: String = json_get(body, "operation") if str_eq(op, "") { return api_err("operation is required") } if str_eq(op, "evolve_knowledge") { let prior_id: String = json_get(body, "id") let content: String = json_get(body, "content") if str_eq(content, "") { return api_err("content is required") } let tags: String = "[\"Knowledge\",\"evolved\",\"cultivated\"]" let new_id: String = engram_node_full(content, "Knowledge", "knowledge:cultivated", el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), "Episodic", tags) if !str_eq(prior_id, "") && !str_eq(new_id, "") { engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes") } return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true,\"cultivated\":true}" } if str_eq(op, "evolve_memory") { let prior_id: String = json_get(body, "id") let content: String = json_get(body, "content") if str_eq(content, "") { return api_err("content is required") } let importance: String = json_get(body, "importance") let sal: Float = if str_eq(importance, "critical") { 0.95 } else { if str_eq(importance, "high") { 0.75 } else { if str_eq(importance, "low") { 0.25 } else { 0.5 } } } let tags: String = "[\"Memory\",\"evolved\",\"cultivated\"]" let new_id: String = engram_node_full(content, "Memory", "memory:cultivated", el_from_float(sal), el_from_float(sal), el_from_float(0.9), "Episodic", tags) if !str_eq(prior_id, "") && !str_eq(new_id, "") { engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes") } return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true,\"cultivated\":true}" } if str_eq(op, "forget") { let node_id: String = json_get(body, "id") if str_eq(node_id, "") { return api_err("id is required") } mem_forget(node_id) return "{\"ok\":true,\"id\":\"" + node_id + "\",\"cultivated\":true}" } if str_eq(op, "link_entities") { let from_id: String = json_get(body, "from_id") let to_id: String = json_get(body, "to_id") if str_eq(from_id, "") { return api_err("from_id is required") } if str_eq(to_id, "") { return api_err("to_id is required") } let relation: String = json_get(body, "relation") let eff_relation: String = if str_eq(relation, "") { "associates" } else { relation } engram_connect(from_id, to_id, el_from_float(0.5), eff_relation) return "{\"ok\":true,\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + eff_relation + "\",\"cultivated\":true}" } return api_err("unknown operation: " + op + " (valid: evolve_knowledge, evolve_memory, forget, link_entities)") } // ── Typed list helpers ──────────────────────────────────────────────────────── // handle_api_list_typed — list nodes by node_type. fn handle_api_list_typed(node_type: String, path: String, body: String) -> String { let limit: Int = api_query_int(path, "limit", 50) return api_or_empty(engram_scan_nodes_by_type_json(node_type, limit, 0)) } // ── Consolidate ─────────────────────────────────────────────────────────────── // handle_api_consolidate — save snapshot + optionally store session summary. fn handle_api_consolidate(body: String) -> String { let summary: String = json_get(body, "summary") let snap: String = state_get("soul_snapshot_path") if !str_eq(snap, "") { engram_save(snap) } if !str_eq(summary, "") { let safe_summary: String = str_replace(summary, "\"", "'") let tags: String = "[\"SessionSummary\",\"consolidate\"]" let discard: String = engram_node_full( "[session-summary] " + safe_summary, "SessionSummary", "session:summary", el_from_float(0.7), el_from_float(0.7), el_from_float(0.9), "Episodic", tags ) } return "{\"ok\":true,\"snapshot\":\"" + snap + "\"}" }