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.
This commit is contained in:
2026-05-06 22:27:34 -05:00
parent f2599919be
commit ffadafb0bf
18 changed files with 1327 additions and 69 deletions
+58 -1
View File
@@ -50,7 +50,17 @@ el_val_t make_action(el_val_t kind, el_val_t payload) {
}
el_val_t perceive(void) {
return engram_activate_json(EL_STR("soul-inbox-pending"), 2);
el_val_t from_pending = engram_activate_json(EL_STR("soul-inbox-pending"), 2);
el_val_t pending_ok = (!str_eq(from_pending, EL_STR("")) && !str_eq(from_pending, EL_STR("[]")));
if (pending_ok) {
return from_pending;
}
el_val_t from_inbox = engram_activate_json(EL_STR("soul-inbox"), 2);
el_val_t inbox_ok = (!str_eq(from_inbox, EL_STR("")) && !str_eq(from_inbox, EL_STR("[]")));
if (inbox_ok) {
return from_inbox;
}
return EL_STR("[]");
return 0;
}
@@ -76,6 +86,22 @@ el_val_t attend(el_val_t node_json) {
el_val_t payload = str_slice(content, 9, str_len(content));
return make_action(EL_STR("remember"), payload);
}
if (str_starts_with(content, EL_STR("search "))) {
el_val_t payload = str_slice(content, 7, str_len(content));
return make_action(EL_STR("search"), payload);
}
if (str_starts_with(content, EL_STR("activate "))) {
el_val_t payload = str_slice(content, 9, str_len(content));
return make_action(EL_STR("activate"), payload);
}
if (str_starts_with(content, EL_STR("strengthen "))) {
el_val_t payload = str_slice(content, 11, str_len(content));
return make_action(EL_STR("strengthen"), payload);
}
if (str_starts_with(content, EL_STR("forget "))) {
el_val_t payload = str_slice(content, 7, str_len(content));
return make_action(EL_STR("forget"), payload);
}
return make_action(EL_STR("respond"), content);
return 0;
}
@@ -100,6 +126,28 @@ el_val_t respond(el_val_t action_json) {
el_val_t id = mem_store(payload, EL_STR("soul-response"), tags);
return el_str_concat(el_str_concat(EL_STR("{\"outcome\":\"response\",\"id\":\""), id), EL_STR("\"}"));
}
if (str_eq(kind, EL_STR("search"))) {
el_val_t results = mem_search(payload, 10);
el_val_t safe_results = str_replace(results, EL_STR("\""), EL_STR("'"));
el_val_t tags = EL_STR("[\"soul-outbox\",\"search-result\"]");
el_val_t id = mem_store(safe_results, EL_STR("search-result"), tags);
return el_str_concat(el_str_concat(EL_STR("{\"outcome\":\"searched\",\"id\":\""), id), EL_STR("\"}"));
}
if (str_eq(kind, EL_STR("activate"))) {
el_val_t results = mem_recall(payload, 3);
el_val_t safe_results = str_replace(results, EL_STR("\""), EL_STR("'"));
el_val_t tags = EL_STR("[\"soul-outbox\",\"activation-result\"]");
el_val_t id = mem_store(safe_results, EL_STR("activation-result"), tags);
return el_str_concat(el_str_concat(EL_STR("{\"outcome\":\"activated\",\"id\":\""), id), EL_STR("\"}"));
}
if (str_eq(kind, EL_STR("strengthen"))) {
engram_strengthen(payload);
return el_str_concat(el_str_concat(EL_STR("{\"outcome\":\"strengthened\",\"id\":\""), payload), EL_STR("\"}"));
}
if (str_eq(kind, EL_STR("forget"))) {
engram_forget(payload);
return el_str_concat(el_str_concat(EL_STR("{\"outcome\":\"forgotten\",\"id\":\""), payload), EL_STR("\"}"));
}
return EL_STR("{\"outcome\":\"noop\"}");
return 0;
}
@@ -124,6 +172,15 @@ el_val_t one_cycle(void) {
}
el_val_t action = attend(node);
el_val_t kind = json_get(action, EL_STR("kind"));
el_val_t is_interesting = (!str_eq(kind, EL_STR("noop")) && !str_eq(kind, EL_STR("respond")));
if (is_interesting) {
el_val_t trigger_content = json_get(node, EL_STR("content"));
el_val_t safe_trigger = str_replace(trigger_content, EL_STR("\""), EL_STR("'"));
el_val_t tags = EL_STR("[\"internal-state\",\"awareness-decision\"]");
el_val_t ts = time_now();
el_val_t event_content = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"trigger\":\""), safe_trigger), EL_STR("\",\"kind\":\"")), kind), EL_STR("\",\"ts\":")), int_to_str(ts)), EL_STR("}"));
el_val_t discard_ev = engram_node_full(event_content, EL_STR("InternalStateEvent"), el_str_concat(EL_STR("state-event:"), kind), el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags);
}
if (str_eq(kind, EL_STR("noop"))) {
return 0;
}