Files
neuron/neuron-api.el
T
will.anderson ffadafb0bf soul: native cognitive API — port all MCP logic to soul daemon
neuron-api.el is a new first-class El module that implements all Neuron
cognitive API handlers natively — no HTTP round-trips, no MCP wrapper,
direct engram builtin calls. All capabilities that previously lived in
the MCP wrapper adapter now live here in the soul.

Handlers: begin_session, compile_ctx, remember, recall, search_knowledge,
browse_knowledge, capture_knowledge, evolve_knowledge, promote_knowledge,
browse_processes, define_process, log_state_event, list_state_events,
inspect_config, tune_config, inspect_graph, link_entities, list_typed,
consolidate.

Routes wired in routes.el under /api/neuron/* (GET + POST).

Also compiles all loop-1/loop-2 .el source changes into dist/*.c and
rebuilds the binary. memory.elh and neuron-api.elh updated with new exports.
2026-05-06 22:27:34 -05:00

381 lines
19 KiB
EmacsLisp

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/*.
// 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") }
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=<name>\",\"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.
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") }
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 + "\"}"
}
// 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 + "\"}"
}