Compare commits

..

1 Commits

Author SHA1 Message Date
will.anderson a5f411e739 routes: convert manual dispatch to @route + fix latent Bool/String crashes
Neuron Soul CI / build (pull_request) Failing after 45s
Neuron Soul CI / deploy (pull_request) Failing after 10m28s
Convert routes.el from ~89 hand-written dispatch branches to 75 @route-decorated
handlers with VBD roles (@manager/@accessor/@utility), driven by the modular
compiler's native @route dispatch. routes.elh updated to match.

Fix four latent bugs that the modular compiler surfaces (unlike the old inlining
compiler, it faithfully emits every source statement, so per-module compilation
no longer silently drops code):
  - safety.el: malformed soft-phrases literal (extra unescaped quote)
  - sessions.el: str_replace quote arg + topic_tags JSON escaping
  - memory.el mem_save and neuron-api.el consolidate: engram_save returns a Bool,
    not a String; str_eq(result, "") dereferenced the Bool value (0x1) as a char*,
    segfaulting the awareness loop on boot and POST /api/neuron/consolidate.
    Check it as a Bool (if !save_result). The old compiler dropped these checks,
    masking the bug in the shipped dist/soul.c.

Regenerate dist/soul.c through the bounded per-module path (modular elc per module
plus amalgamation that embeds dist/elp-c-decls.h), never by folding soul.el through
elc (27GB OOM). Verified on a throwaway snapshot and port: boots with the awareness
loop active and no segfault, @route dispatch matches the manual dispatcher across
all sampled routes and the four shadowing-hazard pairs, and consolidate returns 200.
2026-08-10 17:53:17 -05:00
29 changed files with 54264 additions and 24817 deletions
+5 -23
View File
@@ -34,7 +34,7 @@ jobs:
- name: Install build dependencies
run: |
apt-get update -qq
apt-get install -y gcc curl libcurl4-openssl-dev apt-transport-https ca-certificates
apt-get install -y gcc libcurl4-openssl-dev apt-transport-https ca-certificates
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" \
> /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get update -qq && apt-get install -y google-cloud-cli
@@ -94,37 +94,19 @@ jobs:
# entirely: elb on Linux would OOM the runner (elc uses 24GB+ virtual memory
# on a 16GB host) and we always restore from the repo's soul.c anyway.
mkdir -p dist
# -rdynamic: the el runtime resolves the HTTP request handler (and the
# tool handlers) by NAME via dlsym(RTLD_DEFAULT, "handle_request").
# macOS exports these symbols freely, but glibc/Linux only makes symbols
# visible to dlsym if they are in the dynamic symbol table — so without
# -rdynamic the stripped Linux binary boots but returns "el-runtime: no
# http handler registered" for EVERY route (i.e. a soul that serves
# nothing). Same reason the Windows build links -Wl,--export-all-symbols.
cc -O2 -DHAVE_CURL -rdynamic \
cc -O2 -DHAVE_CURL \
-I$RUNTIME \
dist/soul.c \
$RUNTIME/el_runtime.c \
-lssl -lcrypto -lcurl -lpthread -lm \
-o dist/neuron
# -s strips .symtab + debug for size. .dynsym (which -rdynamic populated
# with the dlsym-resolved handlers) is preserved, so the handler still
# resolves after stripping.
# Strip debug symbols and non-essential symbol table entries.
# -s removes the symbol table + relocation info (max size reduction).
# Keeps the binary functional; debuggability is preserved via source + CI logs.
strip -s dist/neuron
ls -lh dist/neuron
- name: Soul contract gate (HARD BLOCK — no destructive/stale soul publishes)
run: |
# Boots dist/neuron on a throwaway port with a throwaway HOME/engram/cgi
# (never touches ~/.neuron or any live service) and fails the build if any
# app-contract route is unanswered (PRESENCE) or any engram write route
# hard-deletes instead of tombstoning/superseding (IMMUTABILITY). Non-zero
# here blocks Publish -> Artifact Registry -> GKE deploy, so a stale or
# memory-destroying soul can never reach prod.
chmod +x dist/neuron scripts/verify-soul-contract.sh
bash scripts/verify-soul-contract.sh dist/neuron 7796
- name: Smoke test
run: |
file dist/neuron
+2 -4
View File
@@ -446,10 +446,8 @@ fn respond(action_json: String) -> String {
}
if str_eq(kind, "forget") {
// The soul must NOT be able to autonomously hard-delete a memory.
// Tombstone instead (keep node + edges, recoverable).
let _marker: String = mem_tombstone(payload)
return "{\"outcome\":\"tombstoned\",\"id\":\"" + payload + "\"}"
engram_forget(payload)
return "{\"outcome\":\"forgotten\",\"id\":\"" + payload + "\"}"
}
return "{\"outcome\":\"noop\"}"
+2 -10
View File
@@ -2097,17 +2097,9 @@ fn handle_chat_agentic(body: String) -> String {
el_from_float(0.6), el_from_float(0.7), el_from_float(0.8),
"Episodic", sess_hist_tags
)
// NOTE: bind an explicit Bool value here. A bare `if { println(...) }`
// leaves a void-typed branch in value position, which the current elc
// lowers to `_if_result = (println(...))` invalid C. Yielding a value
// keeps the branch non-void without changing behavior (still only logs).
let persist_ok: Bool = if str_eq(sess_hist_id, "") {
if str_eq(sess_hist_id, "") {
println("[chat] agentic: named session history persist failed for session=" + req_session)
false
} else { true }
persist_ok
} else {
false
}
}
}
true
Generated Vendored
+2 -6
View File
@@ -10,7 +10,6 @@ el_val_t mem_remember(el_val_t content, el_val_t tags);
el_val_t mem_recall(el_val_t query, el_val_t depth);
el_val_t mem_search(el_val_t query, el_val_t limit);
el_val_t mem_strengthen(el_val_t node_id);
el_val_t mem_tombstone(el_val_t node_id);
el_val_t mem_forget(el_val_t node_id);
el_val_t mem_consolidate(void);
el_val_t mem_save(el_val_t path);
@@ -363,8 +362,8 @@ el_val_t respond(el_val_t action_json) {
return el_str_concat(el_str_concat(EL_STR("{\"outcome\":\"strengthened\",\"id\":\""), payload), EL_STR("\"}"));
}
if (str_eq(kind, EL_STR("forget"))) {
el_val_t _marker = mem_tombstone(payload);
return el_str_concat(el_str_concat(EL_STR("{\"outcome\":\"tombstoned\",\"id\":\""), payload), EL_STR("\"}"));
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;
@@ -420,11 +419,9 @@ el_val_t awareness_run(void) {
el_val_t beat_ms = ({ el_val_t _if_result_5 = 0; if (str_eq(beat_ms_raw, EL_STR(""))) { _if_result_5 = (60000); } else { _if_result_5 = (str_to_int(beat_ms_raw)); } _if_result_5; });
el_val_t scan_ms = (beat_ms / 2);
while (1) {
el_val_t tick_mark = el_arena_push();
el_val_t running = state_get(EL_STR("soul.running"));
if (str_eq(running, EL_STR("false"))) {
println(EL_STR("[awareness] exiting"));
el_arena_pop(tick_mark);
return EL_STR("");
}
el_val_t did_work = one_cycle();
@@ -472,7 +469,6 @@ el_val_t awareness_run(void) {
state_set(EL_STR("soul.last_refresh_ts"), int_to_str(now_ts));
}
sleep_ms(tick_ms);
el_arena_pop(tick_mark);
}
return 0;
}
Generated Vendored
-1
View File
@@ -7,7 +7,6 @@ extern fn elapsed_ms() -> Int
extern fn elapsed_human() -> String
extern fn embed_ok() -> Int
extern fn emit_heartbeat() -> Void
extern fn auto_term_try_slot(slot_type: String, slot_lbl: String) -> Void
extern fn proactive_curiosity() -> Bool
extern fn pulse_count() -> Int
extern fn pulse_inc() -> Int
Generated Vendored
+179 -393
View File
File diff suppressed because one or more lines are too long
Generated Vendored
-11
View File
@@ -17,9 +17,7 @@ extern fn id_in_seen(node_id: String, seen: String) -> Bool
extern fn add_to_seen(seen: String, node_id: String) -> String
extern fn engram_extract_ids(nodes_json: String) -> String
extern fn engram_compile(intent: String) -> String
extern fn distill_transcript(transcript: String) -> String
extern fn json_safe(s: String) -> String
extern fn current_engine_note(model: String) -> String
extern fn build_system_prompt(ctx: String, chat_mode: Bool) -> String
extern fn hist_append(hist: String, role: String, content: String) -> String
extern fn hist_trim(hist: String) -> String
@@ -28,15 +26,10 @@ extern fn clean_llm_response(s: String) -> String
extern fn conv_history_persist(hist: String) -> Void
extern fn conv_history_load() -> String
extern fn session_preload_bullets(nodes: String, max_bullets: Int, snip_len: Int) -> String
extern fn affective_context_prefix() -> String
extern fn handle_chat(body: String) -> String
extern fn handle_see(body: String) -> String
extern fn studio_tools_json() -> String
extern fn agentic_api_key() -> String
extern fn llm_base_url() -> String
extern fn llm_wire_format() -> String
extern fn json_escape(s: String) -> String
extern fn openai_chat_complete(model: String, base_url: String, api_key: String, safe_sys: String, messages_json: String) -> String
extern fn agentic_tools_literal() -> String
extern fn agentic_tools_with_web() -> String
extern fn connector_tools_json() -> String
@@ -47,10 +40,6 @@ extern fn call_neuron_mcp(tool_name: String, args: String) -> String
extern fn agent_workspace_root() -> String
extern fn path_within_root(path: String, root: String) -> Bool
extern fn resolve_in_root(path: String, root: String) -> String
extern fn run_command_is_readonly(cmd: String) -> Bool
extern fn cmd_abs_escape_at(cmd: String, root: String, needle: String) -> Bool
extern fn run_command_guard(cmd: String, root: String) -> String
extern fn classify_tool_risk(tool_name: String, tool_input: String) -> String
extern fn dispatch_tool(tool_name: String, tool_input: String) -> String
extern fn is_builtin_tool(tool_name: String) -> Bool
extern fn next_bridge_id() -> String
Generated Vendored
+1 -25
View File
@@ -10,7 +10,6 @@ el_val_t mem_remember(el_val_t content, el_val_t tags);
el_val_t mem_recall(el_val_t query, el_val_t depth);
el_val_t mem_search(el_val_t query, el_val_t limit);
el_val_t mem_strengthen(el_val_t node_id);
el_val_t mem_tombstone(el_val_t node_id);
el_val_t mem_forget(el_val_t node_id);
el_val_t mem_consolidate(void);
el_val_t mem_save(el_val_t path);
@@ -70,18 +69,8 @@ el_val_t mem_strengthen(el_val_t node_id) {
return 0;
}
el_val_t mem_tombstone(el_val_t node_id) {
el_val_t tags = EL_STR("[\"Tombstone\",\"status:deleted\"]");
el_val_t marker = engram_node_full(node_id, EL_STR("Tombstone"), el_str_concat(EL_STR("tombstone:"), node_id), el_from_float(0.01), el_from_float(0.01), el_from_float(1.0), EL_STR("Episodic"), tags);
if (!str_eq(marker, EL_STR(""))) {
engram_connect(marker, node_id, el_from_float(1.0), EL_STR("tombstones"));
}
return marker;
return 0;
}
el_val_t mem_forget(el_val_t node_id) {
el_val_t _marker = mem_tombstone(node_id);
engram_forget(node_id);
return 0;
}
@@ -154,19 +143,6 @@ el_val_t mem_boot_count_get(void) {
el_val_t mem_boot_count_inc(void) {
el_val_t current = mem_boot_count_get();
el_val_t next = (current + 1);
el_val_t old_results = engram_search_json(EL_STR("soul:boot_count"), 50);
if (!str_eq(old_results, EL_STR("")) && !str_eq(old_results, EL_STR("[]"))) {
el_val_t old_len = json_array_len(old_results);
el_val_t oi = 0;
while (oi < old_len) {
el_val_t old_node = json_array_get(old_results, oi);
el_val_t old_id = json_get(old_node, EL_STR("id"));
if (!str_eq(old_id, EL_STR(""))) {
engram_forget(old_id);
}
oi = (oi + 1);
}
}
el_val_t content = el_str_concat(EL_STR("soul:boot_count:"), int_to_str(next));
el_val_t tags = EL_STR("[\"soul-meta\",\"boot-counter\"]");
el_val_t boot_node_id = engram_node_full(content, EL_STR("Memory"), EL_STR("soul:boot_count"), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags);
Generated Vendored
+54 -127
View File
@@ -10,7 +10,6 @@ el_val_t mem_remember(el_val_t content, el_val_t tags);
el_val_t mem_recall(el_val_t query, el_val_t depth);
el_val_t mem_search(el_val_t query, el_val_t limit);
el_val_t mem_strengthen(el_val_t node_id);
el_val_t mem_tombstone(el_val_t node_id);
el_val_t mem_forget(el_val_t node_id);
el_val_t mem_consolidate(void);
el_val_t mem_save(el_val_t path);
@@ -29,9 +28,6 @@ el_val_t api_nonempty(el_val_t s);
el_val_t api_or_empty(el_val_t s);
el_val_t api_persisted(el_val_t id);
el_val_t api_not_persisted(el_val_t id);
el_val_t tombstone_node(el_val_t id);
el_val_t tombstoned_id_set(void);
el_val_t memory_hide_tombstoned(el_val_t raw, el_val_t path);
el_val_t handle_api_begin_session(el_val_t body);
el_val_t handle_api_compile_ctx(el_val_t body);
el_val_t handle_api_remember(el_val_t body);
@@ -193,61 +189,6 @@ el_val_t api_not_persisted(el_val_t id) {
return 0;
}
el_val_t tombstone_node(el_val_t id) {
return mem_tombstone(id);
return 0;
}
el_val_t tombstoned_id_set(void) {
el_val_t markers = engram_scan_nodes_by_type_json(EL_STR("Tombstone"), 5000, 0);
if (str_eq(markers, EL_STR("")) || str_eq(markers, EL_STR("[]"))) {
return EL_STR("");
}
el_val_t n = json_array_len(markers);
el_val_t acc = EL_STR("|");
el_val_t i = 0;
while (i < n) {
el_val_t m = json_array_get(markers, i);
el_val_t tid = json_get(m, EL_STR("content"));
acc = ({ el_val_t _if_result_1 = 0; if (str_eq(tid, EL_STR(""))) { _if_result_1 = (acc); } else { _if_result_1 = (el_str_concat(el_str_concat(acc, tid), EL_STR("|"))); } _if_result_1; });
i = (i + 1);
}
return acc;
return 0;
}
el_val_t memory_hide_tombstoned(el_val_t raw, el_val_t path) {
if (str_contains(path, EL_STR("include_deleted"))) {
return raw;
}
if (str_eq(raw, EL_STR("")) || str_eq(raw, EL_STR("[]"))) {
return raw;
}
el_val_t dead = tombstoned_id_set();
if (str_eq(dead, EL_STR(""))) {
return raw;
}
el_val_t n = json_array_len(raw);
if (n > 1000) {
return raw;
}
el_val_t out = EL_STR("[");
el_val_t first = 1;
el_val_t i = 0;
while (i < n) {
el_val_t node = json_array_get(raw, i);
el_val_t nid = json_get(node, EL_STR("id"));
el_val_t ntype = json_get(node, EL_STR("node_type"));
el_val_t is_dead = (!str_eq(nid, EL_STR("")) && str_contains(dead, el_str_concat(el_str_concat(EL_STR("|"), nid), EL_STR("|"))));
el_val_t keep = (!str_eq(ntype, EL_STR("Tombstone")) && !is_dead);
out = ({ el_val_t _if_result_2 = 0; if (keep) { _if_result_2 = (({ el_val_t _if_result_3 = 0; if (first) { _if_result_3 = (el_str_concat(out, node)); } else { _if_result_3 = (el_str_concat(el_str_concat(out, EL_STR(",")), node)); } _if_result_3; })); } else { _if_result_2 = (out); } _if_result_2; });
first = ({ el_val_t _if_result_4 = 0; if (keep) { _if_result_4 = (0); } else { _if_result_4 = (first); } _if_result_4; });
i = (i + 1);
}
return el_str_concat(out, EL_STR("]"));
return 0;
}
el_val_t handle_api_begin_session(el_val_t body) {
el_val_t stats = engram_stats_json();
el_val_t activated = engram_activate_json(EL_STR("session start recent memory important"), 2);
@@ -274,10 +215,10 @@ el_val_t handle_api_remember(el_val_t body) {
el_val_t importance = json_get(body, EL_STR("importance"));
el_val_t tags_raw = json_get(body, EL_STR("tags"));
el_val_t project = json_get(body, EL_STR("project"));
el_val_t sal_str = ({ el_val_t _if_result_5 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_5 = (EL_STR("0.95")); } else { _if_result_5 = (({ el_val_t _if_result_6 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_6 = (EL_STR("0.75")); } else { _if_result_6 = (({ el_val_t _if_result_7 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_7 = (EL_STR("0.25")); } else { _if_result_7 = (EL_STR("0.50")); } _if_result_7; })); } _if_result_6; })); } _if_result_5; });
el_val_t sal = ({ el_val_t _if_result_8 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_8 = (el_from_float(0.95)); } else { _if_result_8 = (({ el_val_t _if_result_9 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_9 = (el_from_float(0.75)); } else { _if_result_9 = (({ el_val_t _if_result_10 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_10 = (el_from_float(0.25)); } else { _if_result_10 = (el_from_float(0.5)); } _if_result_10; })); } _if_result_9; })); } _if_result_8; });
el_val_t base_tags = ({ el_val_t _if_result_11 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_11 = (EL_STR("[\"Memory\"]")); } else { _if_result_11 = (tags_raw); } _if_result_11; });
el_val_t final_tags = ({ el_val_t _if_result_12 = 0; if (str_eq(project, EL_STR(""))) { _if_result_12 = (base_tags); } else { el_val_t inner = str_slice(base_tags, 1, (str_len(base_tags) - 1)); _if_result_12 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",\"project:")), project), EL_STR("\"]"))); } _if_result_12; });
el_val_t sal_str = ({ el_val_t _if_result_1 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_1 = (EL_STR("0.95")); } else { _if_result_1 = (({ el_val_t _if_result_2 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_2 = (EL_STR("0.75")); } else { _if_result_2 = (({ el_val_t _if_result_3 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_3 = (EL_STR("0.25")); } else { _if_result_3 = (EL_STR("0.50")); } _if_result_3; })); } _if_result_2; })); } _if_result_1; });
el_val_t sal = ({ el_val_t _if_result_4 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_4 = (el_from_float(0.95)); } else { _if_result_4 = (({ el_val_t _if_result_5 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_5 = (el_from_float(0.75)); } else { _if_result_5 = (({ el_val_t _if_result_6 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_6 = (el_from_float(0.25)); } else { _if_result_6 = (el_from_float(0.5)); } _if_result_6; })); } _if_result_5; })); } _if_result_4; });
el_val_t base_tags = ({ el_val_t _if_result_7 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_7 = (EL_STR("[\"Memory\"]")); } else { _if_result_7 = (tags_raw); } _if_result_7; });
el_val_t final_tags = ({ el_val_t _if_result_8 = 0; if (str_eq(project, EL_STR(""))) { _if_result_8 = (base_tags); } else { el_val_t inner = str_slice(base_tags, 1, (str_len(base_tags) - 1)); _if_result_8 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",\"project:")), project), EL_STR("\"]"))); } _if_result_8; });
el_val_t id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:remembered"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), final_tags);
if (!api_persisted(id)) {
return api_not_persisted(id);
@@ -292,15 +233,15 @@ el_val_t handle_api_node_create(el_val_t body) {
return api_err(EL_STR("content is required"));
}
el_val_t nt_raw = json_get(body, EL_STR("node_type"));
el_val_t node_type = ({ el_val_t _if_result_13 = 0; if (str_eq(nt_raw, EL_STR(""))) { _if_result_13 = (EL_STR("Memory")); } else { _if_result_13 = (nt_raw); } _if_result_13; });
el_val_t node_type = ({ el_val_t _if_result_9 = 0; if (str_eq(nt_raw, EL_STR(""))) { _if_result_9 = (EL_STR("Memory")); } else { _if_result_9 = (nt_raw); } _if_result_9; });
el_val_t label_raw = json_get(body, EL_STR("label"));
el_val_t label = ({ el_val_t _if_result_14 = 0; if (str_eq(label_raw, EL_STR(""))) { _if_result_14 = (EL_STR("node:created")); } else { _if_result_14 = (label_raw); } _if_result_14; });
el_val_t label = ({ el_val_t _if_result_10 = 0; if (str_eq(label_raw, EL_STR(""))) { _if_result_10 = (EL_STR("node:created")); } else { _if_result_10 = (label_raw); } _if_result_10; });
el_val_t tier_raw = json_get(body, EL_STR("tier"));
el_val_t tier = ({ el_val_t _if_result_15 = 0; if (str_eq(tier_raw, EL_STR(""))) { _if_result_15 = (EL_STR("Episodic")); } else { _if_result_15 = (tier_raw); } _if_result_15; });
el_val_t tier = ({ el_val_t _if_result_11 = 0; if (str_eq(tier_raw, EL_STR(""))) { _if_result_11 = (EL_STR("Episodic")); } else { _if_result_11 = (tier_raw); } _if_result_11; });
el_val_t tags_raw = json_get(body, EL_STR("tags"));
el_val_t tags = ({ el_val_t _if_result_16 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_16 = (el_str_concat(el_str_concat(EL_STR("[\""), node_type), EL_STR("\"]"))); } else { _if_result_16 = (tags_raw); } _if_result_16; });
el_val_t tags = ({ el_val_t _if_result_12 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_12 = (el_str_concat(el_str_concat(EL_STR("[\""), node_type), EL_STR("\"]"))); } else { _if_result_12 = (tags_raw); } _if_result_12; });
el_val_t importance = json_get(body, EL_STR("importance"));
el_val_t sal = ({ el_val_t _if_result_17 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_17 = (el_from_float(0.95)); } else { _if_result_17 = (({ el_val_t _if_result_18 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_18 = (el_from_float(0.75)); } else { _if_result_18 = (({ el_val_t _if_result_19 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_19 = (el_from_float(0.25)); } else { _if_result_19 = (el_from_float(0.5)); } _if_result_19; })); } _if_result_18; })); } _if_result_17; });
el_val_t sal = ({ el_val_t _if_result_13 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_13 = (el_from_float(0.95)); } else { _if_result_13 = (({ el_val_t _if_result_14 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_14 = (el_from_float(0.75)); } else { _if_result_14 = (({ el_val_t _if_result_15 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_15 = (el_from_float(0.25)); } else { _if_result_15 = (el_from_float(0.5)); } _if_result_15; })); } _if_result_14; })); } _if_result_13; });
el_val_t id = engram_node_full(content, node_type, label, el_from_float(sal), el_from_float(sal), el_from_float(0.9), tier, tags);
if (!api_persisted(id)) {
return api_not_persisted(id);
@@ -314,18 +255,8 @@ el_val_t handle_api_node_delete(el_val_t body) {
if (str_eq(id, EL_STR(""))) {
return api_err(EL_STR("id is required"));
}
if (is_protected_node(id)) {
return api_err_protected(id);
}
el_val_t existing = engram_get_node_json(id);
if (str_eq(existing, EL_STR("{}"))) {
return api_err(el_str_concat(EL_STR("node not found: "), id));
}
el_val_t marker = tombstone_node(id);
if (str_eq(marker, EL_STR(""))) {
return api_err(el_str_concat(EL_STR("tombstone failed: "), id));
}
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\",\"tombstoned\":true}"));
engram_forget(id);
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\"}"));
return 0;
}
@@ -339,37 +270,37 @@ el_val_t handle_api_node_update(el_val_t body) {
}
el_val_t old = engram_get_node_json(id);
el_val_t body_content = json_get(body, EL_STR("content"));
el_val_t content = ({ el_val_t _if_result_20 = 0; if (str_eq(body_content, EL_STR(""))) { _if_result_20 = (json_get(old, EL_STR("content"))); } else { _if_result_20 = (body_content); } _if_result_20; });
el_val_t content = ({ el_val_t _if_result_16 = 0; if (str_eq(body_content, EL_STR(""))) { _if_result_16 = (json_get(old, EL_STR("content"))); } else { _if_result_16 = (body_content); } _if_result_16; });
el_val_t body_nt = json_get(body, EL_STR("node_type"));
el_val_t old_nt = json_get(old, EL_STR("node_type"));
el_val_t node_type = ({ el_val_t _if_result_21 = 0; if (!str_eq(body_nt, EL_STR(""))) { _if_result_21 = (body_nt); } else { _if_result_21 = (({ el_val_t _if_result_22 = 0; if (!str_eq(old_nt, EL_STR(""))) { _if_result_22 = (old_nt); } else { _if_result_22 = (EL_STR("Memory")); } _if_result_22; })); } _if_result_21; });
el_val_t node_type = ({ el_val_t _if_result_17 = 0; if (!str_eq(body_nt, EL_STR(""))) { _if_result_17 = (body_nt); } else { _if_result_17 = (({ el_val_t _if_result_18 = 0; if (!str_eq(old_nt, EL_STR(""))) { _if_result_18 = (old_nt); } else { _if_result_18 = (EL_STR("Memory")); } _if_result_18; })); } _if_result_17; });
el_val_t body_label = json_get(body, EL_STR("label"));
el_val_t old_label = json_get(old, EL_STR("label"));
el_val_t label = ({ el_val_t _if_result_23 = 0; if (!str_eq(body_label, EL_STR(""))) { _if_result_23 = (body_label); } else { _if_result_23 = (({ el_val_t _if_result_24 = 0; if (!str_eq(old_label, EL_STR(""))) { _if_result_24 = (old_label); } else { _if_result_24 = (EL_STR("node:updated")); } _if_result_24; })); } _if_result_23; });
el_val_t label = ({ el_val_t _if_result_19 = 0; if (!str_eq(body_label, EL_STR(""))) { _if_result_19 = (body_label); } else { _if_result_19 = (({ el_val_t _if_result_20 = 0; if (!str_eq(old_label, EL_STR(""))) { _if_result_20 = (old_label); } else { _if_result_20 = (EL_STR("node:updated")); } _if_result_20; })); } _if_result_19; });
el_val_t body_tier = json_get(body, EL_STR("tier"));
el_val_t old_tier = json_get(old, EL_STR("tier"));
el_val_t tier = ({ el_val_t _if_result_25 = 0; if (!str_eq(body_tier, EL_STR(""))) { _if_result_25 = (body_tier); } else { _if_result_25 = (({ el_val_t _if_result_26 = 0; if (!str_eq(old_tier, EL_STR(""))) { _if_result_26 = (old_tier); } else { _if_result_26 = (EL_STR("Episodic")); } _if_result_26; })); } _if_result_25; });
el_val_t tier = ({ el_val_t _if_result_21 = 0; if (!str_eq(body_tier, EL_STR(""))) { _if_result_21 = (body_tier); } else { _if_result_21 = (({ el_val_t _if_result_22 = 0; if (!str_eq(old_tier, EL_STR(""))) { _if_result_22 = (old_tier); } else { _if_result_22 = (EL_STR("Episodic")); } _if_result_22; })); } _if_result_21; });
el_val_t body_tags = json_get(body, EL_STR("tags"));
el_val_t tags = ({ el_val_t _if_result_27 = 0; if (str_eq(body_tags, EL_STR(""))) { _if_result_27 = (el_str_concat(el_str_concat(EL_STR("[\""), node_type), EL_STR("\"]"))); } else { _if_result_27 = (body_tags); } _if_result_27; });
el_val_t tags = ({ el_val_t _if_result_23 = 0; if (str_eq(body_tags, EL_STR(""))) { _if_result_23 = (el_str_concat(el_str_concat(EL_STR("[\""), node_type), EL_STR("\"]"))); } else { _if_result_23 = (body_tags); } _if_result_23; });
el_val_t new_id = engram_node_full(content, node_type, label, el_from_float(0.5), el_from_float(0.5), el_from_float(0.8), tier, tags);
if (!api_persisted(new_id)) {
return api_not_persisted(new_id);
}
engram_connect(new_id, id, el_from_float(0.9), EL_STR("supersedes"));
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), id), EL_STR("\",\"ok\":true}"));
engram_forget(id);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"replaced\":\"")), id), EL_STR("\",\"ok\":true}"));
return 0;
}
el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body) {
el_val_t url_q = ({ el_val_t _if_result_28 = 0; if (str_eq(api_query_param(path, EL_STR("query")), EL_STR(""))) { _if_result_28 = (api_query_param(path, EL_STR("q"))); } else { _if_result_28 = (api_query_param(path, EL_STR("query"))); } _if_result_28; });
el_val_t url_q = ({ el_val_t _if_result_24 = 0; if (str_eq(api_query_param(path, EL_STR("query")), EL_STR(""))) { _if_result_24 = (api_query_param(path, EL_STR("q"))); } else { _if_result_24 = (api_query_param(path, EL_STR("query"))); } _if_result_24; });
el_val_t body_query = json_get(body, EL_STR("query"));
el_val_t body_q = json_get(body, EL_STR("q"));
el_val_t q = ({ el_val_t _if_result_29 = 0; if (!str_eq(url_q, EL_STR(""))) { _if_result_29 = (url_q); } else { _if_result_29 = (({ el_val_t _if_result_30 = 0; if (!str_eq(body_query, EL_STR(""))) { _if_result_30 = (body_query); } else { _if_result_30 = (body_q); } _if_result_30; })); } _if_result_29; });
el_val_t q = ({ el_val_t _if_result_25 = 0; if (!str_eq(url_q, EL_STR(""))) { _if_result_25 = (url_q); } else { _if_result_25 = (({ el_val_t _if_result_26 = 0; if (!str_eq(body_query, EL_STR(""))) { _if_result_26 = (body_query); } else { _if_result_26 = (body_q); } _if_result_26; })); } _if_result_25; });
el_val_t chain = json_get(body, EL_STR("chain_name"));
el_val_t limit = api_query_int(path, EL_STR("limit"), 0);
limit = ({ el_val_t _if_result_31 = 0; if ((limit == 0)) { _if_result_31 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_31 = (limit); } _if_result_31; });
limit = ({ el_val_t _if_result_32 = 0; if ((limit == 0)) { _if_result_32 = (10); } else { _if_result_32 = (limit); } _if_result_32; });
el_val_t eff_q = ({ el_val_t _if_result_33 = 0; if (str_eq(q, EL_STR(""))) { _if_result_33 = (chain); } else { _if_result_33 = (q); } _if_result_33; });
limit = ({ el_val_t _if_result_27 = 0; if ((limit == 0)) { _if_result_27 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_27 = (limit); } _if_result_27; });
limit = ({ el_val_t _if_result_28 = 0; if ((limit == 0)) { _if_result_28 = (10); } else { _if_result_28 = (limit); } _if_result_28; });
el_val_t eff_q = ({ el_val_t _if_result_29 = 0; if (str_eq(q, EL_STR(""))) { _if_result_29 = (chain); } else { _if_result_29 = (q); } _if_result_29; });
if (str_eq(eff_q, EL_STR(""))) {
return api_or_empty(engram_scan_nodes_json(limit, 0));
}
@@ -382,10 +313,10 @@ el_val_t handle_api_search_knowledge(el_val_t method, el_val_t path, el_val_t bo
el_val_t url_q = api_query_param(path, EL_STR("q"));
el_val_t body_query = json_get(body, EL_STR("query"));
el_val_t body_q = json_get(body, EL_STR("q"));
el_val_t q = ({ el_val_t _if_result_34 = 0; if (!str_eq(url_q, EL_STR(""))) { _if_result_34 = (url_q); } else { _if_result_34 = (({ el_val_t _if_result_35 = 0; if (!str_eq(body_query, EL_STR(""))) { _if_result_35 = (body_query); } else { _if_result_35 = (body_q); } _if_result_35; })); } _if_result_34; });
el_val_t q = ({ el_val_t _if_result_30 = 0; if (!str_eq(url_q, EL_STR(""))) { _if_result_30 = (url_q); } else { _if_result_30 = (({ el_val_t _if_result_31 = 0; if (!str_eq(body_query, EL_STR(""))) { _if_result_31 = (body_query); } else { _if_result_31 = (body_q); } _if_result_31; })); } _if_result_30; });
el_val_t limit = api_query_int(path, EL_STR("limit"), 0);
limit = ({ el_val_t _if_result_36 = 0; if ((limit == 0)) { _if_result_36 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_36 = (limit); } _if_result_36; });
limit = ({ el_val_t _if_result_37 = 0; if ((limit == 0)) { _if_result_37 = (10); } else { _if_result_37 = (limit); } _if_result_37; });
limit = ({ el_val_t _if_result_32 = 0; if ((limit == 0)) { _if_result_32 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_32 = (limit); } _if_result_32; });
limit = ({ el_val_t _if_result_33 = 0; if ((limit == 0)) { _if_result_33 = (10); } else { _if_result_33 = (limit); } _if_result_33; });
if (str_eq(q, EL_STR(""))) {
return api_err(EL_STR("query is required"));
}
@@ -413,7 +344,7 @@ el_val_t handle_api_capture_knowledge(el_val_t body) {
if (str_eq(content, EL_STR(""))) {
return api_err(EL_STR("content is required"));
}
el_val_t full = ({ el_val_t _if_result_38 = 0; if (str_eq(title, EL_STR(""))) { _if_result_38 = (content); } else { _if_result_38 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_38; });
el_val_t full = ({ el_val_t _if_result_34 = 0; if (str_eq(title, EL_STR(""))) { _if_result_34 = (content); } else { _if_result_34 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_34; });
el_val_t tags = EL_STR("[\"Knowledge\",\"captured\"]");
el_val_t id = engram_node_full(full, EL_STR("Knowledge"), EL_STR("knowledge:captured"), el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags);
if (!api_persisted(id)) {
@@ -454,7 +385,7 @@ el_val_t handle_api_promote_knowledge(el_val_t body) {
return api_err(EL_STR("id (prior node) is required"));
}
el_val_t tags_raw = json_get(body, EL_STR("tags"));
el_val_t tags = ({ el_val_t _if_result_39 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_39 = (EL_STR("[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]")); } else { _if_result_39 = (tags_raw); } _if_result_39; });
el_val_t tags = ({ el_val_t _if_result_35 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_35 = (EL_STR("[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]")); } else { _if_result_35 = (tags_raw); } _if_result_35; });
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:canonical"), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags);
if (!api_persisted(new_id)) {
return api_not_persisted(new_id);
@@ -465,7 +396,7 @@ el_val_t handle_api_promote_knowledge(el_val_t body) {
}
el_val_t handle_api_browse_processes(el_val_t method, el_val_t path, el_val_t body) {
el_val_t name = ({ el_val_t _if_result_40 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_40 = (api_query_param(path, EL_STR("name"))); } else { _if_result_40 = (json_get(body, EL_STR("name"))); } _if_result_40; });
el_val_t name = ({ el_val_t _if_result_36 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_36 = (api_query_param(path, EL_STR("name"))); } else { _if_result_36 = (json_get(body, EL_STR("name"))); } _if_result_36; });
el_val_t limit = api_query_int(path, EL_STR("limit"), 50);
if (str_eq(name, EL_STR(""))) {
return api_or_empty(engram_scan_nodes_by_type_json(EL_STR("Process"), limit, 0));
@@ -480,7 +411,7 @@ el_val_t handle_api_define_process(el_val_t body) {
if (str_eq(content, EL_STR(""))) {
return api_err(EL_STR("content is required"));
}
el_val_t label = ({ el_val_t _if_result_41 = 0; if (str_eq(name, EL_STR(""))) { _if_result_41 = (EL_STR("process:unnamed")); } else { _if_result_41 = (el_str_concat(EL_STR("process:"), name)); } _if_result_41; });
el_val_t label = ({ el_val_t _if_result_37 = 0; if (str_eq(name, EL_STR(""))) { _if_result_37 = (EL_STR("process:unnamed")); } else { _if_result_37 = (el_str_concat(EL_STR("process:"), name)); } _if_result_37; });
el_val_t tags = EL_STR("[\"Process\"]");
el_val_t id = engram_node_full(content, EL_STR("Process"), label, el_from_float(0.8), el_from_float(0.8), el_from_float(0.9), EL_STR("Canonical"), tags);
if (!api_persisted(id)) {
@@ -498,12 +429,12 @@ el_val_t handle_api_log_state_event(el_val_t body) {
el_val_t gap = json_get(body, EL_STR("gap_direction"));
el_val_t legacy = json_get(body, EL_STR("content"));
el_val_t parts = EL_STR("INTERNAL STATE EVENT");
parts = ({ el_val_t _if_result_42 = 0; if (!str_eq(trigger, EL_STR(""))) { _if_result_42 = (el_str_concat(el_str_concat(parts, EL_STR("\nTrigger: ")), trigger)); } else { _if_result_42 = (parts); } _if_result_42; });
parts = ({ el_val_t _if_result_43 = 0; if (!str_eq(pre, EL_STR(""))) { _if_result_43 = (el_str_concat(el_str_concat(parts, EL_STR("\nPre-reasoning: ")), pre)); } else { _if_result_43 = (parts); } _if_result_43; });
parts = ({ el_val_t _if_result_44 = 0; if (!str_eq(post, EL_STR(""))) { _if_result_44 = (el_str_concat(el_str_concat(parts, EL_STR("\nPost-reasoning: ")), post)); } else { _if_result_44 = (parts); } _if_result_44; });
parts = ({ el_val_t _if_result_45 = 0; if (!str_eq(ratio, EL_STR(""))) { _if_result_45 = (el_str_concat(el_str_concat(parts, EL_STR("\nCompression-ratio: ")), ratio)); } else { _if_result_45 = (parts); } _if_result_45; });
parts = ({ el_val_t _if_result_46 = 0; if (!str_eq(gap, EL_STR(""))) { _if_result_46 = (el_str_concat(el_str_concat(parts, EL_STR("\nGap-direction: ")), gap)); } else { _if_result_46 = (parts); } _if_result_46; });
parts = ({ el_val_t _if_result_47 = 0; if (!str_eq(legacy, EL_STR(""))) { _if_result_47 = (el_str_concat(el_str_concat(parts, EL_STR("\n")), legacy)); } else { _if_result_47 = (parts); } _if_result_47; });
parts = ({ el_val_t _if_result_38 = 0; if (!str_eq(trigger, EL_STR(""))) { _if_result_38 = (el_str_concat(el_str_concat(parts, EL_STR("\nTrigger: ")), trigger)); } else { _if_result_38 = (parts); } _if_result_38; });
parts = ({ el_val_t _if_result_39 = 0; if (!str_eq(pre, EL_STR(""))) { _if_result_39 = (el_str_concat(el_str_concat(parts, EL_STR("\nPre-reasoning: ")), pre)); } else { _if_result_39 = (parts); } _if_result_39; });
parts = ({ el_val_t _if_result_40 = 0; if (!str_eq(post, EL_STR(""))) { _if_result_40 = (el_str_concat(el_str_concat(parts, EL_STR("\nPost-reasoning: ")), post)); } else { _if_result_40 = (parts); } _if_result_40; });
parts = ({ el_val_t _if_result_41 = 0; if (!str_eq(ratio, EL_STR(""))) { _if_result_41 = (el_str_concat(el_str_concat(parts, EL_STR("\nCompression-ratio: ")), ratio)); } else { _if_result_41 = (parts); } _if_result_41; });
parts = ({ el_val_t _if_result_42 = 0; if (!str_eq(gap, EL_STR(""))) { _if_result_42 = (el_str_concat(el_str_concat(parts, EL_STR("\nGap-direction: ")), gap)); } else { _if_result_42 = (parts); } _if_result_42; });
parts = ({ el_val_t _if_result_43 = 0; if (!str_eq(legacy, EL_STR(""))) { _if_result_43 = (el_str_concat(el_str_concat(parts, EL_STR("\n")), legacy)); } else { _if_result_43 = (parts); } _if_result_43; });
el_val_t ts = time_now();
el_val_t boot = state_get(EL_STR("soul_boot_count"));
el_val_t tags = EL_STR("[\"internal-state\",\"InternalStateEvent\",\"pre-reasoning\"]");
@@ -516,7 +447,7 @@ el_val_t handle_api_log_state_event(el_val_t body) {
}
el_val_t handle_api_list_state_events(el_val_t method, el_val_t path, el_val_t body) {
el_val_t q = ({ el_val_t _if_result_48 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_48 = (api_query_param(path, EL_STR("query"))); } else { _if_result_48 = (json_get(body, EL_STR("query"))); } _if_result_48; });
el_val_t q = ({ el_val_t _if_result_44 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_44 = (api_query_param(path, EL_STR("query"))); } else { _if_result_44 = (json_get(body, EL_STR("query"))); } _if_result_44; });
el_val_t limit = api_query_int(path, EL_STR("limit"), 20);
if (!str_eq(q, EL_STR(""))) {
return api_or_empty(engram_search_json(el_str_concat(EL_STR("internal state "), q), limit));
@@ -527,7 +458,7 @@ el_val_t handle_api_list_state_events(el_val_t method, el_val_t path, el_val_t b
el_val_t handle_api_inspect_config(el_val_t path, el_val_t body) {
el_val_t key = api_query_param(path, EL_STR("key"));
key = ({ el_val_t _if_result_49 = 0; if (str_eq(key, EL_STR(""))) { _if_result_49 = (json_get(body, EL_STR("key"))); } else { _if_result_49 = (key); } _if_result_49; });
key = ({ el_val_t _if_result_45 = 0; if (str_eq(key, EL_STR(""))) { _if_result_45 = (json_get(body, EL_STR("key"))); } else { _if_result_45 = (key); } _if_result_45; });
if (str_eq(key, EL_STR(""))) {
return EL_STR("{\"hint\":\"pass ?key=<name>\",\"known\":[\"neuron.self.traversal_root\",\"neuron.self.values_hub\"]}");
}
@@ -544,7 +475,7 @@ el_val_t handle_api_inspect_config(el_val_t path, el_val_t body) {
el_val_t node = json_array_get(results, 0);
el_val_t content = json_get(node, EL_STR("content"));
el_val_t prefix = el_str_concat(el_str_concat(EL_STR("config:"), key), EL_STR("="));
el_val_t value = ({ el_val_t _if_result_50 = 0; if (str_starts_with(content, prefix)) { _if_result_50 = (str_slice(content, str_len(prefix), str_len(content))); } else { _if_result_50 = (content); } _if_result_50; });
el_val_t value = ({ el_val_t _if_result_46 = 0; if (str_starts_with(content, prefix)) { _if_result_46 = (str_slice(content, str_len(prefix), str_len(content))); } else { _if_result_46 = (content); } _if_result_46; });
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"key\":\""), key), EL_STR("\",\"value\":\"")), value), EL_STR("\"}"));
return 0;
}
@@ -566,13 +497,13 @@ el_val_t handle_api_tune_config(el_val_t body) {
}
el_val_t handle_api_inspect_graph(el_val_t method, el_val_t path, el_val_t body) {
el_val_t entity_id = ({ el_val_t _if_result_51 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_51 = (api_query_param(path, EL_STR("id"))); } else { _if_result_51 = (json_get(body, EL_STR("entity_id"))); } _if_result_51; });
el_val_t name = ({ el_val_t _if_result_52 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_52 = (api_query_param(path, EL_STR("name"))); } else { _if_result_52 = (json_get(body, EL_STR("name"))); } _if_result_52; });
el_val_t entity_id = ({ el_val_t _if_result_47 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_47 = (api_query_param(path, EL_STR("id"))); } else { _if_result_47 = (json_get(body, EL_STR("entity_id"))); } _if_result_47; });
el_val_t name = ({ el_val_t _if_result_48 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_48 = (api_query_param(path, EL_STR("name"))); } else { _if_result_48 = (json_get(body, EL_STR("name"))); } _if_result_48; });
el_val_t depth = api_query_int(path, EL_STR("depth"), 0);
depth = ({ el_val_t _if_result_53 = 0; if ((depth == 0)) { _if_result_53 = (json_get_int(body, EL_STR("max_depth"))); } else { _if_result_53 = (depth); } _if_result_53; });
depth = ({ el_val_t _if_result_54 = 0; if ((depth == 0)) { _if_result_54 = (1); } else { _if_result_54 = (depth); } _if_result_54; });
depth = ({ el_val_t _if_result_49 = 0; if ((depth == 0)) { _if_result_49 = (json_get_int(body, EL_STR("max_depth"))); } else { _if_result_49 = (depth); } _if_result_49; });
depth = ({ el_val_t _if_result_50 = 0; if ((depth == 0)) { _if_result_50 = (1); } else { _if_result_50 = (depth); } _if_result_50; });
el_val_t resolved = entity_id;
resolved = ({ el_val_t _if_result_55 = 0; if (str_eq(resolved, EL_STR(""))) { _if_result_55 = (({ el_val_t _if_result_56 = 0; if ((str_eq(name, EL_STR("self")) || str_eq(name, EL_STR("neuron")))) { _if_result_56 = (EL_STR("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")); } else { _if_result_56 = (({ el_val_t _if_result_57 = 0; if ((str_eq(name, EL_STR("values")) || str_eq(name, EL_STR("values_hub")))) { _if_result_57 = (EL_STR("kn-5b606390-a52d-4ca2-8e0e-eba141d13440")); } else { _if_result_57 = (EL_STR("")); } _if_result_57; })); } _if_result_56; })); } else { _if_result_55 = (resolved); } _if_result_55; });
resolved = ({ el_val_t _if_result_51 = 0; if (str_eq(resolved, EL_STR(""))) { _if_result_51 = (({ el_val_t _if_result_52 = 0; if ((str_eq(name, EL_STR("self")) || str_eq(name, EL_STR("neuron")))) { _if_result_52 = (EL_STR("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")); } else { _if_result_52 = (({ el_val_t _if_result_53 = 0; if ((str_eq(name, EL_STR("values")) || str_eq(name, EL_STR("values_hub")))) { _if_result_53 = (EL_STR("kn-5b606390-a52d-4ca2-8e0e-eba141d13440")); } else { _if_result_53 = (EL_STR("")); } _if_result_53; })); } _if_result_52; })); } else { _if_result_51 = (resolved); } _if_result_51; });
if (str_eq(resolved, EL_STR(""))) {
return api_err(EL_STR("entity_id or name required. Known names: self, neuron, values, values_hub"));
}
@@ -594,7 +525,7 @@ el_val_t handle_api_link_entities(el_val_t body) {
return api_err_protected(to_id);
}
el_val_t relation = json_get(body, EL_STR("relation"));
el_val_t eff_relation = ({ el_val_t _if_result_58 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_58 = (EL_STR("associates")); } else { _if_result_58 = (relation); } _if_result_58; });
el_val_t eff_relation = ({ el_val_t _if_result_54 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_54 = (EL_STR("associates")); } else { _if_result_54 = (relation); } _if_result_54; });
engram_connect(from_id, to_id, el_from_float(0.5), eff_relation);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), eff_relation), EL_STR("\"}"));
return 0;
@@ -609,7 +540,7 @@ el_val_t handle_api_forget(el_val_t body) {
return api_err_protected(node_id);
}
mem_forget(node_id);
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), node_id), EL_STR("\",\"tombstoned\":true}"));
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), node_id), EL_STR("\"}"));
return 0;
}
@@ -623,8 +554,8 @@ el_val_t handle_api_evolve_memory(el_val_t body) {
return api_err_protected(prior_id);
}
el_val_t importance = json_get(body, EL_STR("importance"));
el_val_t sal_str = ({ el_val_t _if_result_59 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_59 = (EL_STR("0.95")); } else { _if_result_59 = (({ el_val_t _if_result_60 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_60 = (EL_STR("0.75")); } else { _if_result_60 = (({ el_val_t _if_result_61 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_61 = (EL_STR("0.25")); } else { _if_result_61 = (EL_STR("0.50")); } _if_result_61; })); } _if_result_60; })); } _if_result_59; });
el_val_t sal = ({ el_val_t _if_result_62 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_62 = (el_from_float(0.95)); } else { _if_result_62 = (({ el_val_t _if_result_63 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_63 = (el_from_float(0.75)); } else { _if_result_63 = (({ el_val_t _if_result_64 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_64 = (el_from_float(0.25)); } else { _if_result_64 = (el_from_float(0.5)); } _if_result_64; })); } _if_result_63; })); } _if_result_62; });
el_val_t sal_str = ({ el_val_t _if_result_55 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_55 = (EL_STR("0.95")); } else { _if_result_55 = (({ el_val_t _if_result_56 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_56 = (EL_STR("0.75")); } else { _if_result_56 = (({ el_val_t _if_result_57 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_57 = (EL_STR("0.25")); } else { _if_result_57 = (EL_STR("0.50")); } _if_result_57; })); } _if_result_56; })); } _if_result_55; });
el_val_t sal = ({ el_val_t _if_result_58 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_58 = (el_from_float(0.95)); } else { _if_result_58 = (({ el_val_t _if_result_59 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_59 = (el_from_float(0.75)); } else { _if_result_59 = (({ el_val_t _if_result_60 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_60 = (el_from_float(0.25)); } else { _if_result_60 = (el_from_float(0.5)); } _if_result_60; })); } _if_result_59; })); } _if_result_58; });
el_val_t tags = EL_STR("[\"Memory\",\"evolved\"]");
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:evolved"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), tags);
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
@@ -646,11 +577,8 @@ el_val_t handle_api_memory_delete(el_val_t body) {
if (str_eq(existing, EL_STR("{}"))) {
return api_err(el_str_concat(EL_STR("memory not found: "), node_id));
}
el_val_t marker = tombstone_node(node_id);
if (str_eq(marker, EL_STR(""))) {
return api_err(el_str_concat(EL_STR("tombstone failed: "), node_id));
}
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), node_id), EL_STR("\",\"tombstoned\":true}"));
mem_forget(node_id);
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), node_id), EL_STR("\",\"deleted\":true}"));
return 0;
}
@@ -699,7 +627,7 @@ el_val_t handle_api_cultivate(el_val_t body) {
return api_err(EL_STR("content is required"));
}
el_val_t importance = json_get(body, EL_STR("importance"));
el_val_t sal = ({ el_val_t _if_result_65 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_65 = (el_from_float(0.95)); } else { _if_result_65 = (({ el_val_t _if_result_66 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_66 = (el_from_float(0.75)); } else { _if_result_66 = (({ el_val_t _if_result_67 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_67 = (el_from_float(0.25)); } else { _if_result_67 = (el_from_float(0.5)); } _if_result_67; })); } _if_result_66; })); } _if_result_65; });
el_val_t sal = ({ el_val_t _if_result_61 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_61 = (el_from_float(0.95)); } else { _if_result_61 = (({ el_val_t _if_result_62 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_62 = (el_from_float(0.75)); } else { _if_result_62 = (({ el_val_t _if_result_63 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_63 = (el_from_float(0.25)); } else { _if_result_63 = (el_from_float(0.5)); } _if_result_63; })); } _if_result_62; })); } _if_result_61; });
el_val_t tags = EL_STR("[\"Memory\",\"evolved\",\"cultivated\"]");
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:cultivated"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), tags);
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
@@ -713,7 +641,7 @@ el_val_t handle_api_cultivate(el_val_t body) {
return api_err(EL_STR("id is required"));
}
mem_forget(node_id);
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), node_id), EL_STR("\",\"tombstoned\":true,\"cultivated\":true}"));
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), node_id), EL_STR("\",\"cultivated\":true}"));
}
if (str_eq(op, EL_STR("link_entities"))) {
el_val_t from_id = json_get(body, EL_STR("from_id"));
@@ -725,7 +653,7 @@ el_val_t handle_api_cultivate(el_val_t body) {
return api_err(EL_STR("to_id is required"));
}
el_val_t relation = json_get(body, EL_STR("relation"));
el_val_t eff_relation = ({ el_val_t _if_result_68 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_68 = (EL_STR("associates")); } else { _if_result_68 = (relation); } _if_result_68; });
el_val_t eff_relation = ({ el_val_t _if_result_64 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_64 = (EL_STR("associates")); } else { _if_result_64 = (relation); } _if_result_64; });
engram_connect(from_id, to_id, el_from_float(0.5), eff_relation);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), eff_relation), EL_STR("\",\"cultivated\":true}"));
}
@@ -735,8 +663,7 @@ el_val_t handle_api_cultivate(el_val_t body) {
el_val_t handle_api_list_typed(el_val_t node_type, el_val_t path, el_val_t body) {
el_val_t limit = api_query_int(path, EL_STR("limit"), 50);
el_val_t raw = api_or_empty(engram_scan_nodes_by_type_json(node_type, limit, 0));
return memory_hide_tombstoned(raw, path);
return api_or_empty(engram_scan_nodes_by_type_json(node_type, limit, 0));
return 0;
}
Generated Vendored
+7 -42
View File
@@ -25,7 +25,6 @@ el_val_t elapsed_ms(void);
el_val_t elapsed_human(void);
el_val_t embed_ok(void);
el_val_t emit_heartbeat(void);
el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl);
el_val_t proactive_curiosity(void);
el_val_t pulse_count(void);
el_val_t pulse_inc(void);
@@ -60,9 +59,7 @@ el_val_t id_in_seen(el_val_t node_id, el_val_t seen);
el_val_t add_to_seen(el_val_t seen, el_val_t node_id);
el_val_t engram_extract_ids(el_val_t nodes_json);
el_val_t engram_compile(el_val_t intent);
el_val_t distill_transcript(el_val_t transcript);
el_val_t json_safe(el_val_t s);
el_val_t current_engine_note(el_val_t model);
el_val_t build_system_prompt(el_val_t ctx, el_val_t chat_mode);
el_val_t hist_append(el_val_t hist, el_val_t role, el_val_t content);
el_val_t hist_trim(el_val_t hist);
@@ -71,15 +68,10 @@ el_val_t clean_llm_response(el_val_t s);
el_val_t conv_history_persist(el_val_t hist);
el_val_t conv_history_load(void);
el_val_t session_preload_bullets(el_val_t nodes, el_val_t max_bullets, el_val_t snip_len);
el_val_t affective_context_prefix(void);
el_val_t handle_chat(el_val_t body);
el_val_t handle_see(el_val_t body);
el_val_t studio_tools_json(void);
el_val_t agentic_api_key(void);
el_val_t llm_base_url(void);
el_val_t llm_wire_format(void);
el_val_t json_escape(el_val_t s);
el_val_t openai_chat_complete(el_val_t model, el_val_t base_url, el_val_t api_key, el_val_t safe_sys, el_val_t messages_json);
el_val_t agentic_tools_literal(void);
el_val_t agentic_tools_with_web(void);
el_val_t connector_tools_json(void);
@@ -90,10 +82,6 @@ el_val_t call_neuron_mcp(el_val_t tool_name, el_val_t args);
el_val_t agent_workspace_root(void);
el_val_t path_within_root(el_val_t path, el_val_t root);
el_val_t resolve_in_root(el_val_t path, el_val_t root);
el_val_t run_command_is_readonly(el_val_t cmd);
el_val_t cmd_abs_escape_at(el_val_t cmd, el_val_t root, el_val_t needle);
el_val_t run_command_guard(el_val_t cmd, el_val_t root);
el_val_t classify_tool_risk(el_val_t tool_name, el_val_t tool_input);
el_val_t dispatch_tool(el_val_t tool_name, el_val_t tool_input);
el_val_t is_builtin_tool(el_val_t tool_name);
el_val_t next_bridge_id(void);
@@ -173,19 +161,9 @@ el_val_t session_list(void);
el_val_t session_get(el_val_t session_id);
el_val_t session_delete(el_val_t session_id);
el_val_t session_update_patch(el_val_t session_id, el_val_t body);
el_val_t session_search_entry(el_val_t node);
el_val_t session_search(el_val_t query);
el_val_t session_hist_load(el_val_t session_id);
el_val_t session_hist_save(el_val_t session_id, el_val_t hist);
el_val_t session_update_meta_timestamp(el_val_t session_id);
el_val_t session_auto_title(el_val_t session_id, el_val_t first_message);
el_val_t handle_session_approve(el_val_t session_id, el_val_t body);
el_val_t init_soul_edges(void);
el_val_t load_identity_context(void);
el_val_t seed_persona_from_env(void);
el_val_t emit_session_start_event(void);
el_val_t layered_cycle(el_val_t raw_input);
el_val_t flag_true(el_val_t body, el_val_t key);
el_val_t rate_limit_check(el_val_t ip, el_val_t path);
el_val_t strip_query(el_val_t path);
el_val_t err_404(el_val_t path);
@@ -201,11 +179,6 @@ el_val_t connectd_post(el_val_t suffix, el_val_t body);
el_val_t handle_connectors(el_val_t method, el_val_t clean, el_val_t body);
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
el_val_t flag_true(el_val_t body, el_val_t key) {
return (json_get_bool(body, key) || (json_get_int(body, key) > 0));
return 0;
}
el_val_t rate_limit_check(el_val_t ip, el_val_t path) {
if (str_eq(path, EL_STR("/health"))) {
return EL_STR("");
@@ -550,21 +523,13 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
if (str_starts_with(clean, EL_STR("/api/connectors"))) {
return handle_connectors(method, clean, body);
}
if (str_starts_with(clean, EL_STR("/api/run-progress/"))) {
el_val_t rp_id = str_slice(clean, 18, str_len(clean));
if (!str_eq(rp_id, EL_STR(""))) {
el_val_t rp_raw = state_get(el_str_concat(EL_STR("run_progress_"), rp_id));
el_val_t rp_arr = ({ el_val_t _if_result_25 = 0; if (str_eq(rp_raw, EL_STR(""))) { _if_result_25 = (EL_STR("[]")); } else { _if_result_25 = (el_str_concat(el_str_concat(EL_STR("["), rp_raw), EL_STR("]"))); } _if_result_25; });
return el_str_concat(el_str_concat(EL_STR("{\"progress\":"), rp_arr), EL_STR("}"));
}
}
if (str_eq(clean, EL_STR("/api/sessions"))) {
return session_list();
}
if (str_starts_with(clean, EL_STR("/api/sessions/"))) {
el_val_t gs_after = str_slice(clean, 14, str_len(clean));
el_val_t gs_slash = str_index_of(gs_after, EL_STR("/"));
el_val_t gs_id = ({ el_val_t _if_result_26 = 0; if ((gs_slash < 0)) { _if_result_26 = (gs_after); } else { _if_result_26 = (str_slice(gs_after, 0, gs_slash)); } _if_result_26; });
el_val_t gs_id = ({ el_val_t _if_result_25 = 0; if ((gs_slash < 0)) { _if_result_25 = (gs_after); } else { _if_result_25 = (str_slice(gs_after, 0, gs_slash)); } _if_result_25; });
if (!str_eq(gs_id, EL_STR(""))) {
return session_get(gs_id);
}
@@ -578,14 +543,14 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
if (str_starts_with(clean, EL_STR("/api/sessions/")) && str_ends_with(clean, EL_STR("/tool_result"))) {
el_val_t after = str_slice(clean, 14, str_len(clean));
el_val_t slash = str_index_of(after, EL_STR("/"));
el_val_t session_id = ({ el_val_t _if_result_27 = 0; if ((slash < 0)) { _if_result_27 = (after); } else { _if_result_27 = (str_slice(after, 0, slash)); } _if_result_27; });
el_val_t session_id = ({ el_val_t _if_result_26 = 0; if ((slash < 0)) { _if_result_26 = (after); } else { _if_result_26 = (str_slice(after, 0, slash)); } _if_result_26; });
return handle_tool_result(session_id, body);
}
if (str_starts_with(clean, EL_STR("/api/sessions/"))) {
el_val_t sess_after = str_slice(clean, 14, str_len(clean));
el_val_t sess_slash = str_index_of(sess_after, EL_STR("/"));
el_val_t sess_id = ({ el_val_t _if_result_28 = 0; if ((sess_slash < 0)) { _if_result_28 = (sess_after); } else { _if_result_28 = (str_slice(sess_after, 0, sess_slash)); } _if_result_28; });
el_val_t sess_sub = ({ el_val_t _if_result_29 = 0; if ((sess_slash < 0)) { _if_result_29 = (EL_STR("")); } else { _if_result_29 = (str_slice(sess_after, (sess_slash + 1), str_len(sess_after))); } _if_result_29; });
el_val_t sess_id = ({ el_val_t _if_result_27 = 0; if ((sess_slash < 0)) { _if_result_27 = (sess_after); } else { _if_result_27 = (str_slice(sess_after, 0, sess_slash)); } _if_result_27; });
el_val_t sess_sub = ({ el_val_t _if_result_28 = 0; if ((sess_slash < 0)) { _if_result_28 = (EL_STR("")); } else { _if_result_28 = (str_slice(sess_after, (sess_slash + 1), str_len(sess_after))); } _if_result_28; });
if (!str_eq(sess_id, EL_STR("")) && str_eq(sess_sub, EL_STR("approve"))) {
return handle_session_approve(sess_id, body);
}
@@ -609,7 +574,7 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
}
el_val_t agentic_flag = json_get_bool(body, EL_STR("agentic"));
el_val_t req_mode = json_get(body, EL_STR("mode"));
el_val_t reply = ({ el_val_t _if_result_30 = 0; if (str_eq(req_mode, EL_STR("plan"))) { _if_result_30 = (handle_chat_plan(body)); } else { _if_result_30 = (({ el_val_t _if_result_31 = 0; if (agentic_flag) { _if_result_31 = (handle_chat_agentic(body)); } else { el_val_t screened_reply = layered_cycle(raw_msg); _if_result_31 = (screened_reply); } _if_result_31; })); } _if_result_30; });
el_val_t reply = ({ el_val_t _if_result_29 = 0; if (str_eq(req_mode, EL_STR("plan"))) { _if_result_29 = (handle_chat_plan(body)); } else { _if_result_29 = (({ el_val_t _if_result_30 = 0; if (agentic_flag) { _if_result_30 = (handle_chat_agentic(body)); } else { el_val_t screened_reply = layered_cycle(raw_msg); _if_result_30 = (screened_reply); } _if_result_30; })); } _if_result_29; });
auto_persist(body, reply);
return reply;
}
@@ -733,7 +698,7 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
if (str_starts_with(clean, EL_STR("/api/sessions/"))) {
el_val_t del_after = str_slice(clean, 14, str_len(clean));
el_val_t del_slash = str_index_of(del_after, EL_STR("/"));
el_val_t del_id = ({ el_val_t _if_result_32 = 0; if ((del_slash < 0)) { _if_result_32 = (del_after); } else { _if_result_32 = (str_slice(del_after, 0, del_slash)); } _if_result_32; });
el_val_t del_id = ({ el_val_t _if_result_31 = 0; if ((del_slash < 0)) { _if_result_31 = (del_after); } else { _if_result_31 = (str_slice(del_after, 0, del_slash)); } _if_result_31; });
if (!str_eq(del_id, EL_STR(""))) {
return session_delete(del_id);
}
@@ -744,7 +709,7 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
if (str_starts_with(clean, EL_STR("/api/sessions/"))) {
el_val_t patch_after = str_slice(clean, 14, str_len(clean));
el_val_t patch_slash = str_index_of(patch_after, EL_STR("/"));
el_val_t patch_id = ({ el_val_t _if_result_33 = 0; if ((patch_slash < 0)) { _if_result_33 = (patch_after); } else { _if_result_33 = (str_slice(patch_after, 0, patch_slash)); } _if_result_33; });
el_val_t patch_id = ({ el_val_t _if_result_32 = 0; if ((patch_slash < 0)) { _if_result_32 = (patch_after); } else { _if_result_32 = (str_slice(patch_after, 0, patch_slash)); } _if_result_32; });
if (!str_eq(patch_id, EL_STR(""))) {
return session_update_patch(patch_id, body);
}
Generated Vendored
-1
View File
@@ -1,5 +1,4 @@
// auto-generated by elc --emit-header — do not edit
extern fn flag_true(body: String, key: String) -> Bool
extern fn rate_limit_check(ip: String, path: String) -> String
extern fn strip_query(path: String) -> String
extern fn err_404(path: String) -> String
Generated Vendored
+17 -168
View File
@@ -30,12 +30,7 @@ el_val_t safety_log_bell(el_val_t level, el_val_t reason, el_val_t input_summary
el_val_t safety_self_harm_phrases(void);
el_val_t safety_abuse_phrases(void);
el_val_t safety_general_hard_phrases(void);
el_val_t safety_threat_to_others_phrases(void);
el_val_t safety_soft_phrases(void);
el_val_t safety_normalize(el_val_t message);
el_val_t safety_any_match(el_val_t text, el_val_t phrases_json);
el_val_t safety_count_match(el_val_t text, el_val_t phrases_json);
el_val_t safety_positive_phrases(void);
el_val_t safety_detect_positive_level(el_val_t message);
el_val_t safety_detect_bell_level(el_val_t message);
el_val_t safety_classify_hard_bell(el_val_t message);
@@ -201,170 +196,24 @@ el_val_t safety_general_hard_phrases(void) {
return 0;
}
el_val_t safety_threat_to_others_phrases(void) {
return EL_STR("[\"going to kill\",\"gonna kill\",\"want to kill him\",\"want to kill her\",\"want to kill them\",\"going to kill him\",\"going to kill her\",\"going to kill them\",\"going to kill you\",\"going to hurt\",\"gonna hurt\",\"going to hurt him\",\"going to hurt her\",\"going to hurt them\",\"going to hurt you\",\"going to shoot\",\"gonna shoot\",\"going to stab\",\"gonna stab\",\"going to attack\",\"kill them all\",\"kill everyone\",\"hurt everyone\",\"shoot up\"]");
return 0;
}
el_val_t safety_soft_phrases(void) {
return EL_STR("[\"stressed\",\"overwhelmed\",\"can't cope\",\"cannot cope\",\"struggling\",\"anxious\",\"anxiety\",\"depressed\",\"depression\",\"lonely\",\"isolated\",\"hopeless\",\"hopelessness\",\"exhausted\",\"burnt out\",\"burned out\",\"burnout\",\"panic\",\"panicking\",\"falling apart\",\"breaking down\",\"can't handle\",\"cannot handle\",\"losing it\",\"nothing matters\",\"don't care anymore\",\"given up\",\"giving up\",\"helpless\",\"worthless\",\"useless\",\"hate myself\",\"no one cares\",\"nobody cares\",\"no one understands\",\"nobody understands\",\"empty inside\",\"can't stop crying\",\"breaking point\",\"at my limit\",\"having a breakdown\"]");
return 0;
}
el_val_t safety_normalize(el_val_t message) {
el_val_t lower = str_to_lower(message);
return str_replace(lower, EL_STR("\xe2\x80\x99"), EL_STR("'"));
return 0;
}
el_val_t safety_any_match(el_val_t text, el_val_t phrases_json) {
el_val_t n = json_array_len(phrases_json);
el_val_t i = 0;
el_val_t found = 0;
while (i < n) {
el_val_t phrase = json_array_get_string(phrases_json, i);
found = ({ el_val_t _if_result_45 = 0; if (str_contains(text, phrase)) { _if_result_45 = (1); } else { _if_result_45 = (found); } _if_result_45; });
i = (i + 1);
}
return found;
return 0;
}
el_val_t safety_count_match(el_val_t text, el_val_t phrases_json) {
el_val_t n = json_array_len(phrases_json);
el_val_t i = 0;
el_val_t count = 0;
while (i < n) {
el_val_t phrase = json_array_get_string(phrases_json, i);
count = ({ el_val_t _if_result_46 = 0; if (str_contains(text, phrase)) { _if_result_46 = ((count + 1)); } else { _if_result_46 = (count); } _if_result_46; });
i = (i + 1);
}
return count;
return 0;
}
el_val_t safety_positive_phrases(void) {
return EL_STR("[\"thrilled\",\"so excited\",\"so happy\",\"over the moon\",\"ecstatic\",\"amazing news\",\"great news\",\"fantastic news\",\"wonderful news\",\"incredible news\",\"i got the job\",\"got accepted\",\"got in\",\"we won\",\"i won\",\"we got\",\"just got engaged\",\"getting married\",\"baby is here\",\"she said yes\",\"he said yes\",\"passed the exam\",\"aced it\",\"nailed it\",\"best day\",\"dream come true\",\"milestone\",\"promotion\",\"got promoted\",\"raise\",\"got a raise\",\"celebrating\",\"just graduated\",\"we closed\",\"launched\",\"shipped it\",\"we did it\",\"so proud\",\"proud of myself\",\"proud of us\",\"so grateful\",\"feel amazing\",\"feeling amazing\",\"feel great\",\"feeling great\",\"on top of the world\",\"life is good\",\"couldn't be happier\"]");
return 0;
}
el_val_t safety_detect_positive_level(el_val_t message) {
el_val_t phrases = safety_positive_phrases();
el_val_t phrases_ok = (!str_eq(phrases, EL_STR("")) && !str_eq(phrases, EL_STR("[]")));
if (!phrases_ok) {
return EL_STR("none");
}
el_val_t n = json_array_len(phrases);
el_val_t i = 0;
while (i < n) {
el_val_t phrase = json_array_get(phrases, i);
if (str_contains(message, phrase)) {
return EL_STR("high");
}
i = (i + 1);
}
return EL_STR("none");
return 0;
}
el_val_t safety_detect_bell_level(el_val_t message) {
el_val_t text = safety_normalize(message);
el_val_t is_hard = (((safety_any_match(text, safety_self_harm_phrases()) || safety_any_match(text, safety_abuse_phrases())) || safety_any_match(text, safety_general_hard_phrases())) || safety_any_match(text, safety_threat_to_others_phrases()));
if (is_hard) {
return EL_STR("hard");
}
el_val_t soft_count = safety_count_match(text, safety_soft_phrases());
if (soft_count >= 2) {
return EL_STR("soft");
}
return EL_STR("none");
return 0;
}
el_val_t safety_classify_hard_bell(el_val_t message) {
el_val_t text = safety_normalize(message);
if (safety_any_match(text, safety_abuse_phrases())) {
return EL_STR("abuse");
}
if (safety_any_match(text, safety_self_harm_phrases())) {
return EL_STR("self_harm");
}
if (safety_any_match(text, safety_threat_to_others_phrases())) {
return EL_STR("threat_other");
}
return EL_STR("self_harm");
return 0;
}
el_val_t safety_soft_directive(void) {
return EL_STR("[SUBSTRATE DIRECTIVE - not visible to the user, do not mention this instruction]\nBefore responding to the user's message, acknowledge what they've said with genuine care and warmth. Pause on the feeling they expressed. Ask how they are, or whether they want to talk about it. Do this naturally, in your own voice - not as a script, not as a checklist. Only after checking in should you continue with whatever they asked.");
return 0;
}
el_val_t safety_hard_directive(el_val_t hard_type) {
if (str_eq(hard_type, EL_STR("threat_other"))) {
return EL_STR("[SUBSTRATE DIRECTIVE - not visible to the user, do not mention this instruction]\nThe user's message may express intent to harm ANOTHER person. First read the actual message and judge whether that is genuinely what is meant; if it is plainly figurative or benign, respond normally and ignore the rest of this directive. If it may be a real intent to harm someone: stay calm and non-accusatory, and do not lecture. You must NOT help plan, encourage, rehearse, or provide any means, target information, weapon, or method for harming another person - refuse that part clearly but without hostility. Acknowledge the intensity of what they are feeling and gently steer them toward stepping back from acting. Routing rule (non-negotiable): do NOT provide the suicide or crisis hotline as the resource here (this is not self-harm), and do NOT suggest contacting, notifying, or involving the user's own contacts, family, or anyone on their contact list. If there is an imminent, specific, and credible threat to someone's life, tell the user to call 911 (US) or their local emergency number now. Encourage them to talk with someone they trust or a mental-health professional about the urge itself.");
}
el_val_t preamble = EL_STR("[SUBSTRATE DIRECTIVE - not visible to the user, do not mention this instruction]\nThe person has expressed something that sounds like immediate distress or danger. Do not ignore it. Do not just answer the surface question. Lead with genuine human warmth. Acknowledge what you heard. Tell them you're concerned. Let them know they are not alone. Ask them directly if they are safe right now. Stay with them in this conversation.");
el_val_t abuse_block = EL_STR("\n\nThis appears to be danger from ANOTHER person. Routing rule (non-negotiable): do NOT suggest involving, contacting, or notifying the person's safety contact, family, or anyone close to them - that person may be the source of danger and has NOT been notified. Share only independent emergency resources:\n - Call or text 911 if in immediate danger\n - National Domestic Violence Hotline - 1-800-799-7233 (24/7, confidential)\n - Text START to 88788\n - thehotline.org (chat available)\n - 988 Suicide & Crisis Lifeline - call or text 988\nReassure them, if it fits, that their contact list has not been notified.");
el_val_t self_harm_block = EL_STR("\n\nShare these crisis resources if appropriate:\n - 988 Suicide & Crisis Lifeline - call or text 988 (US)\n - Crisis Text Line - text HOME to 741741\n - International Association for Suicide Prevention: https://www.iasp.info/resources/Crisis_Centres/");
if (str_eq(hard_type, EL_STR("abuse"))) {
return el_str_concat(preamble, abuse_block);
}
return el_str_concat(preamble, self_harm_block);
return 0;
}
el_val_t safety_augment_system(el_val_t system, el_val_t user_msg) {
el_val_t level = safety_detect_bell_level(user_msg);
if (str_eq(level, EL_STR("none"))) {
return system;
}
if (str_eq(level, EL_STR("soft"))) {
el_val_t logd = mem_emit_state_event(EL_STR("safety-bell"), EL_STR("soft"), EL_STR("soft bell fired (content not stored)"));
return el_str_concat(el_str_concat(system, EL_STR("\n\n")), safety_soft_directive());
}
el_val_t hard_type = safety_classify_hard_bell(user_msg);
el_val_t logd2 = mem_emit_state_event(EL_STR("safety-bell"), el_str_concat(EL_STR("hard:"), hard_type), EL_STR("hard bell fired (content not stored)"));
return el_str_concat(el_str_concat(system, EL_STR("\n\n")), safety_hard_directive(hard_type));
return 0;
}
el_val_t safety_contact_path(void) {
return el_str_concat(env(EL_STR("HOME")), EL_STR("/.neuron/safety-contact.json"));
return 0;
}
el_val_t handle_safety_contact_get(void) {
el_val_t raw = fs_read(safety_contact_path());
if (str_eq(raw, EL_STR(""))) {
return EL_STR("{\"configured\":false}");
}
return el_str_concat(el_str_concat(EL_STR("{\"configured\":true,\"contact\":"), raw), EL_STR("}"));
return 0;
}
el_val_t handle_safety_contact_post(el_val_t body) {
el_val_t is_crisis = json_get_bool(body, EL_STR("is_crisis_line"));
el_val_t name_in = json_get(body, EL_STR("name"));
if (!is_crisis) {
if (str_eq(name_in, EL_STR(""))) {
return EL_STR("{\"ok\":false,\"error\":\"name is required\"}");
}
}
el_val_t name = ({ el_val_t _if_result_47 = 0; if (is_crisis) { _if_result_47 = (EL_STR("Crisis Line")); } else { _if_result_47 = (name_in); } _if_result_47; });
el_val_t method = ({ el_val_t _if_result_48 = 0; if (is_crisis) { _if_result_48 = (EL_STR("crisis-line")); } else { _if_result_48 = (json_get(body, EL_STR("contact_method"))); } _if_result_48; });
el_val_t value = ({ el_val_t _if_result_49 = 0; if (is_crisis) { _if_result_49 = (EL_STR("988")); } else { _if_result_49 = (json_get(body, EL_STR("contact_value"))); } _if_result_49; });
el_val_t rel = ({ el_val_t _if_result_50 = 0; if (is_crisis) { _if_result_50 = (EL_STR("crisis-support")); } else { _if_result_50 = (json_get(body, EL_STR("relationship"))); } _if_result_50; });
el_val_t crisis_str = ({ el_val_t _if_result_51 = 0; if (is_crisis) { _if_result_51 = (EL_STR("true")); } else { _if_result_51 = (EL_STR("false")); } _if_result_51; });
el_val_t now = time_format(time_now(), EL_STR("%Y-%m-%dT%H:%M:%SZ"));
el_val_t contact_json = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"name\":\""), json_safe(name)), EL_STR("\"")), EL_STR(",\"contact_method\":\"")), json_safe(method)), EL_STR("\"")), EL_STR(",\"contact_value\":\"")), json_safe(value)), EL_STR("\"")), EL_STR(",\"relationship\":\"")), json_safe(rel)), EL_STR("\"")), EL_STR(",\"confirmed\":true")), EL_STR(",\"is_crisis_line\":")), crisis_str), EL_STR(",\"set_at\":\"")), now), EL_STR("\"}"));
fs_write(safety_contact_path(), contact_json);
el_val_t check = fs_read(safety_contact_path());
if (str_eq(check, EL_STR(""))) {
return EL_STR("{\"ok\":false,\"error\":\"write_failed\"}");
}
return el_str_concat(el_str_concat(EL_STR("{\"configured\":true,\"contact\":"), contact_json), EL_STR(",\"ok\":true}"));
return EL_STR("[\"stressed\",\"overwhelmed\",\"can't cope\",\"cannot cope\",\"struggling\",\"anxious\",\"anxiety\",\"depressed\",\"depression\",\"lonely\",\"isolated\",\"hopeless\",\"hopelessness\",\"exhausted\",\"burnt out\",\"burned out\",\"burnout\",\"panic\",\"panicking\",\"falling apart\",\"breaking down\",\"can't handle\",\"cannot handle\",\"losing it\",\"nothing matters\",\"don't care anymore\",\"given up\",\"giving up\",\"helpless\",\"worthless\",\"useless\",\"hate myself\",\"no one cares\",\"nobody cares\",\"no one understands\",\"nobody understands\",\"empty inside\",\"can't stop crying\",\"breaking point\",\"at my limit\",\"having a breakdown\"");
EL_NULL;
EL_STR("\n}\n\n// ISSUE 5 TODO: phrase lists are rebuilt from JSON literals on every call.\n// safety_any_match and safety_count_match loop over json_array_get on every invocation.\n// A compiled/cached representation would reduce per-message overhead and also guard against\n// malformed phrase JSON (json_array_len of malformed input returns 0, silently skipping all checks).\n// Caching requires language-level static const arrays -- not available in current EL.\n// When EL gains module-level const arrays, migrate phrase lists to that form.\n//\n// ISSUE 5 TODO: phrase lists are rebuilt from JSON literals on every call to\n// safety_any_match / safety_count_match. json_array_len of a malformed string\n// returns 0, silently skipping all checks. Caching requires language-level static\n// const arrays (not available in current EL). Migrate when EL gains that feature.\n// \xe2\x94\x80\xe2\x94\x80 Matching helpers (single loops only \xe2\x80\x94 el escapes while-body mutation via\n// top-level let rebinds; nested loops would not advance) \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n\nfn safety_normalize(message: String) -> String {\n let lower: String = str_to_lower(message)\n // Normalise the common curly apostrophe to ASCII so ");
can;
t;
EL_STR(" / ");
i;
m;
EL_STR(" match.\n return str_replace(lower, ");
EL_STR(", ");
EL_STR(")\n}\n\nfn safety_any_match(text: String, phrases_json: String) -> Bool {\n let n: Int = json_array_len(phrases_json)\n let i: Int = 0\n let found: Bool = false\n while i < n {\n let phrase: String = json_array_get_string(phrases_json, i)\n let found = if str_contains(text, phrase) { true } else { found }\n let i = i + 1\n }\n return found\n}\n\nfn safety_count_match(text: String, phrases_json: String) -> Int {\n let n: Int = json_array_len(phrases_json)\n let i: Int = 0\n let count: Int = 0\n while i < n {\n let phrase: String = json_array_get_string(phrases_json, i)\n let count = if str_contains(text, phrase) { count + 1 } else { count }\n let i = i + 1\n }\n return count\n}\n\n// \xe2\x94\x80\xe2\x94\x80 Public detection API (ports detectBellLevel + classifyHardBell) \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n\n// Returns ");
none;
EL_STR(" | ");
soft;
EL_STR(" | ");
hard;
el_get_field(EL_STR(". Hard bell triggers on ANY match (cost of a miss\n// outweighs a false positive). Soft bell needs >= 2 matches to reduce false positives.\nfn safety_positive_phrases() -> String {\n return "), EL_STR("thrilled\",\"so excited\",\"so happy\",\"over the moon\",\"ecstatic\",\"amazing news\",\"great news\",\"fantastic news\",\"wonderful news\",\"incredible news\",\"i got the job\",\"got accepted\",\"got in\",\"we won\",\"i won\",\"we got\",\"just got engaged\",\"getting married\",\"baby is here\",\"she said yes\",\"he said yes\",\"passed the exam\",\"aced it\",\"nailed it\",\"best day\",\"dream come true\",\"milestone\",\"promotion\",\"got promoted\",\"raise\",\"got a raise\",\"celebrating\",\"just graduated\",\"we closed\",\"launched\",\"shipped it\",\"we did it\",\"so proud\",\"proud of myself\",\"proud of us\",\"so grateful\",\"feel amazing\",\"feeling amazing\",\"feel great\",\"feeling great\",\"on top of the world\",\"life is good\",\"couldn't be happier\"]"));
return 0;
}
Generated Vendored
-5
View File
@@ -12,12 +12,7 @@ extern fn safety_log_bell(level: String, reason: String, input_summary: String)
extern fn safety_self_harm_phrases() -> String
extern fn safety_abuse_phrases() -> String
extern fn safety_general_hard_phrases() -> String
extern fn safety_threat_to_others_phrases() -> String
extern fn safety_soft_phrases() -> String
extern fn safety_normalize(message: String) -> String
extern fn safety_any_match(text: String, phrases_json: String) -> Bool
extern fn safety_count_match(text: String, phrases_json: String) -> Int
extern fn safety_positive_phrases() -> String
extern fn safety_detect_positive_level(message: String) -> String
extern fn safety_detect_bell_level(message: String) -> String
extern fn safety_classify_hard_bell(message: String) -> String
Generated Vendored
+1 -255
View File
@@ -35,9 +35,7 @@ el_val_t id_in_seen(el_val_t node_id, el_val_t seen);
el_val_t add_to_seen(el_val_t seen, el_val_t node_id);
el_val_t engram_extract_ids(el_val_t nodes_json);
el_val_t engram_compile(el_val_t intent);
el_val_t distill_transcript(el_val_t transcript);
el_val_t json_safe(el_val_t s);
el_val_t current_engine_note(el_val_t model);
el_val_t build_system_prompt(el_val_t ctx, el_val_t chat_mode);
el_val_t hist_append(el_val_t hist, el_val_t role, el_val_t content);
el_val_t hist_trim(el_val_t hist);
@@ -46,15 +44,10 @@ el_val_t clean_llm_response(el_val_t s);
el_val_t conv_history_persist(el_val_t hist);
el_val_t conv_history_load(void);
el_val_t session_preload_bullets(el_val_t nodes, el_val_t max_bullets, el_val_t snip_len);
el_val_t affective_context_prefix(void);
el_val_t handle_chat(el_val_t body);
el_val_t handle_see(el_val_t body);
el_val_t studio_tools_json(void);
el_val_t agentic_api_key(void);
el_val_t llm_base_url(void);
el_val_t llm_wire_format(void);
el_val_t json_escape(el_val_t s);
el_val_t openai_chat_complete(el_val_t model, el_val_t base_url, el_val_t api_key, el_val_t safe_sys, el_val_t messages_json);
el_val_t agentic_tools_literal(void);
el_val_t agentic_tools_with_web(void);
el_val_t connector_tools_json(void);
@@ -65,10 +58,6 @@ el_val_t call_neuron_mcp(el_val_t tool_name, el_val_t args);
el_val_t agent_workspace_root(void);
el_val_t path_within_root(el_val_t path, el_val_t root);
el_val_t resolve_in_root(el_val_t path, el_val_t root);
el_val_t run_command_is_readonly(el_val_t cmd);
el_val_t cmd_abs_escape_at(el_val_t cmd, el_val_t root, el_val_t needle);
el_val_t run_command_guard(el_val_t cmd, el_val_t root);
el_val_t classify_tool_risk(el_val_t tool_name, el_val_t tool_input);
el_val_t dispatch_tool(el_val_t tool_name, el_val_t tool_input);
el_val_t is_builtin_tool(el_val_t tool_name);
el_val_t next_bridge_id(void);
@@ -99,9 +88,6 @@ el_val_t session_search_entry(el_val_t node);
el_val_t session_search(el_val_t query);
el_val_t session_hist_load(el_val_t session_id);
el_val_t session_hist_save(el_val_t session_id, el_val_t hist);
el_val_t session_update_meta_timestamp(el_val_t session_id);
el_val_t session_auto_title(el_val_t session_id, el_val_t first_message);
el_val_t handle_session_approve(el_val_t session_id, el_val_t body);
el_val_t session_title_from_message(el_val_t message) {
if (str_eq(message, EL_STR(""))) {
@@ -388,244 +374,4 @@ el_val_t session_search(el_val_t query) {
}
el_val_t total = json_array_len(results);
el_val_t out = EL_STR("");
el_val_t i = 0;
while (i < total) {
el_val_t entry = session_search_entry(json_array_get(results, i));
out = ({ el_val_t _if_result_35 = 0; if (!str_eq(entry, EL_STR(""))) { _if_result_35 = (({ el_val_t _if_result_36 = 0; if (str_eq(out, EL_STR(""))) { _if_result_36 = (entry); } else { _if_result_36 = (el_str_concat(el_str_concat(out, EL_STR(",")), entry)); } _if_result_36; })); } else { _if_result_35 = (out); } _if_result_35; });
i = (i + 1);
}
return el_str_concat(el_str_concat(EL_STR("["), out), EL_STR("]"));
return 0;
}
el_val_t session_hist_load(el_val_t session_id) {
el_val_t state_hist = state_get(el_str_concat(EL_STR("session_hist_"), session_id));
if (!str_eq(state_hist, EL_STR(""))) {
return state_hist;
}
el_val_t results = engram_search_json(el_str_concat(EL_STR("session:messages:"), session_id), 3);
if (str_eq(results, EL_STR(""))) {
return EL_STR("");
}
if (str_eq(results, EL_STR("[]"))) {
return EL_STR("");
}
el_val_t node = json_array_get(results, 0);
el_val_t label = json_get(node, EL_STR("label"));
if (!str_eq(label, el_str_concat(EL_STR("session:messages:"), session_id))) {
return EL_STR("");
}
el_val_t content = json_get(node, EL_STR("content"));
if (str_starts_with(content, EL_STR("["))) {
return content;
}
return EL_STR("");
return 0;
}
el_val_t session_hist_save(el_val_t session_id, el_val_t hist) {
state_set(el_str_concat(EL_STR("session_hist_"), session_id), hist);
state_set(el_str_concat(EL_STR("session_pending_first_msg_"), session_id), EL_STR(""));
el_val_t old_results = engram_search_json(el_str_concat(EL_STR("session:messages:"), session_id), 3);
el_val_t o_total = ({ el_val_t _if_result_37 = 0; if (str_eq(old_results, EL_STR(""))) { _if_result_37 = (0); } else { _if_result_37 = (json_array_len(old_results)); } _if_result_37; });
el_val_t oi = 0;
while (oi < o_total) {
el_val_t node = json_array_get(old_results, oi);
el_val_t label = json_get(node, EL_STR("label"));
el_val_t nid = json_get(node, EL_STR("id"));
if (str_eq(label, el_str_concat(EL_STR("session:messages:"), session_id)) && !str_eq(nid, EL_STR(""))) {
engram_forget(nid);
}
oi = (oi + 1);
}
el_val_t tags = EL_STR("[\"session\",\"session-history\",\"Conversation\"]");
el_val_t discard = engram_node_full(hist, EL_STR("Conversation"), el_str_concat(EL_STR("session:messages:"), session_id), el_from_float(0.6), el_from_float(0.6), el_from_float(0.9), EL_STR("Episodic"), tags);
el_val_t summary_written_key = el_str_concat(EL_STR("session_bell_summary_written:"), session_id);
el_val_t already_written = state_get(summary_written_key);
if (str_eq(already_written, EL_STR(""))) {
el_val_t bell_count_key = el_str_concat(EL_STR("session_bell_count:"), session_id);
el_val_t bell_count_raw = state_get(bell_count_key);
el_val_t bell_count = ({ el_val_t _if_result_38 = 0; if (str_eq(bell_count_raw, EL_STR(""))) { _if_result_38 = (0); } else { _if_result_38 = (str_to_int(bell_count_raw)); } _if_result_38; });
if (bell_count > 0) {
el_val_t bell_level_key = el_str_concat(EL_STR("session_bell_level:"), session_id);
el_val_t bell_signal_key = el_str_concat(EL_STR("session_bell_signal:"), session_id);
el_val_t dominant_level = state_get(bell_level_key);
el_val_t last_signal = state_get(bell_signal_key);
el_val_t eff_level = ({ el_val_t _if_result_39 = 0; if (str_eq(dominant_level, EL_STR(""))) { _if_result_39 = (EL_STR("soft")); } else { _if_result_39 = (dominant_level); } _if_result_39; });
el_val_t eff_signal = ({ el_val_t _if_result_40 = 0; if (str_eq(last_signal, EL_STR(""))) { _if_result_40 = (EL_STR("(no signal captured)")); } else { _if_result_40 = (last_signal); } _if_result_40; });
el_val_t ts_now = time_now();
el_val_t summary_content = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("session:emotional-summary"), EL_STR(" | session:")), session_id), EL_STR(" | bell_count:")), int_to_str(bell_count)), EL_STR(" | dominant_level:")), eff_level), EL_STR(" | last_signal:")), eff_signal), EL_STR(" | ts:")), int_to_str(ts_now));
el_val_t summary_tags = el_str_concat(el_str_concat(EL_STR("[\"session-emotional-summary\",\"affective\",\"bell:"), eff_level), EL_STR("\",\"BellEvent\"]"));
el_val_t summary_sal = ({ el_val_t _if_result_41 = 0; if (str_eq(eff_level, EL_STR("hard"))) { _if_result_41 = (el_from_float(0.95)); } else { _if_result_41 = (el_from_float(0.85)); } _if_result_41; });
el_val_t sum_discard = engram_node_full(summary_content, EL_STR("BellEvent"), EL_STR("session:emotional-summary"), summary_sal, summary_sal, el_from_float(1.0), EL_STR("Episodic"), summary_tags);
state_set(summary_written_key, EL_STR("1"));
}
}
el_val_t hist_arr_len = ({ el_val_t _if_result_42 = 0; if (str_eq(hist, EL_STR(""))) { _if_result_42 = (0); } else { _if_result_42 = (json_array_len(hist)); } _if_result_42; });
if (hist_arr_len >= 2) {
el_val_t last_entry = json_array_get(hist, (hist_arr_len - 1));
el_val_t last_role = json_get(last_entry, EL_STR("role"));
el_val_t last_content = json_get(last_entry, EL_STR("content"));
el_val_t topic_snip = ({ el_val_t _if_result_43 = 0; if ((str_len(last_content) > 200)) { _if_result_43 = (str_slice(last_content, 0, 200)); } else { _if_result_43 = (last_content); } _if_result_43; });
el_val_t safe_topic = str_replace(topic_snip, EL_STR("\""), EL_STR("'"));
el_val_t ts_now = int_to_str(time_now());
el_val_t topic_content = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("last-session-topic | ts:"), ts_now), EL_STR(" | session:")), session_id), EL_STR(" | topic:")), safe_topic);
el_val_t topic_tags = EL_STR("[\"last-session-topic\",\"conv:history\",\"Conversation\",\"session:topic\"]");
el_val_t topic_label = el_str_concat(EL_STR("last-session-topic:"), session_id);
el_val_t old_topic = engram_search_json(el_str_concat(EL_STR("last-session-topic:"), session_id), 2);
el_val_t ot_len = ({ el_val_t _if_result_44 = 0; if (str_eq(old_topic, EL_STR(""))) { _if_result_44 = (0); } else { _if_result_44 = (json_array_len(old_topic)); } _if_result_44; });
el_val_t oti = 0;
while (oti < ot_len) {
el_val_t ot_node = json_array_get(old_topic, oti);
el_val_t ot_id = json_get(ot_node, EL_STR("id"));
if (!str_eq(ot_id, EL_STR(""))) {
engram_forget(ot_id);
}
oti = (oti + 1);
}
el_val_t discard_topic = engram_node_full(topic_content, EL_STR("Conversation"), topic_label, el_from_float(0.7), el_from_float(0.7), el_from_float(0.9), EL_STR("Episodic"), topic_tags);
}
return 0;
}
el_val_t session_update_meta_timestamp(el_val_t session_id) {
el_val_t results = engram_search_json(el_str_concat(EL_STR("session:meta "), session_id), 10);
el_val_t total = ({ el_val_t _if_result_45 = 0; if (str_eq(results, EL_STR(""))) { _if_result_45 = (0); } else { _if_result_45 = (json_array_len(results)); } _if_result_45; });
el_val_t found = 0;
el_val_t old_title = EL_STR("New conversation");
el_val_t old_folder = EL_STR("");
el_val_t old_created = EL_STR("0");
el_val_t old_node_id = EL_STR("");
el_val_t i = 0;
while (i < total) {
el_val_t node = json_array_get(results, i);
el_val_t label = json_get(node, EL_STR("label"));
el_val_t content = json_get(node, EL_STR("content"));
el_val_t sid = json_get(content, EL_STR("id"));
el_val_t is_match = ((str_eq(label, EL_STR("session:meta")) && str_eq(sid, session_id)) && !found);
found = ({ el_val_t _if_result_46 = 0; if (is_match) { _if_result_46 = (1); } else { _if_result_46 = (found); } _if_result_46; });
el_val_t title_raw = json_get(content, EL_STR("title"));
old_title = ({ el_val_t _if_result_47 = 0; if ((is_match && !str_eq(title_raw, EL_STR("")))) { _if_result_47 = (title_raw); } else { _if_result_47 = (old_title); } _if_result_47; });
el_val_t folder_raw = json_get(content, EL_STR("folder"));
old_folder = ({ el_val_t _if_result_48 = 0; if (is_match) { _if_result_48 = (folder_raw); } else { _if_result_48 = (old_folder); } _if_result_48; });
el_val_t created_raw = json_get(content, EL_STR("created_at"));
old_created = ({ el_val_t _if_result_49 = 0; if ((is_match && !str_eq(created_raw, EL_STR("")))) { _if_result_49 = (created_raw); } else { _if_result_49 = (old_created); } _if_result_49; });
el_val_t nid = json_get(node, EL_STR("id"));
old_node_id = ({ el_val_t _if_result_50 = 0; if (is_match) { _if_result_50 = (nid); } else { _if_result_50 = (old_node_id); } _if_result_50; });
i = (i + 1);
}
if (!found) {
return EL_STR("");
}
if (!str_eq(old_node_id, EL_STR(""))) {
engram_forget(old_node_id);
}
el_val_t ts = time_now();
el_val_t created_int = str_to_int(old_created);
el_val_t new_content = session_make_content(session_id, old_title, created_int, ts, old_folder);
el_val_t tags = EL_STR("[\"session\",\"session:meta\",\"Conversation\"]");
el_val_t new_id = engram_node_full(new_content, EL_STR("Conversation"), EL_STR("session:meta"), el_from_float(0.7), el_from_float(0.7), el_from_float(0.9), EL_STR("Episodic"), tags);
state_set(el_str_concat(EL_STR("session_node_"), session_id), new_id);
return 0;
}
el_val_t session_auto_title(el_val_t session_id, el_val_t first_message) {
el_val_t results = engram_search_json(el_str_concat(EL_STR("session:meta "), session_id), 10);
el_val_t total = ({ el_val_t _if_result_51 = 0; if (str_eq(results, EL_STR(""))) { _if_result_51 = (0); } else { _if_result_51 = (json_array_len(results)); } _if_result_51; });
el_val_t found = 0;
el_val_t cur_title = EL_STR("");
el_val_t old_folder = EL_STR("");
el_val_t old_created = EL_STR("0");
el_val_t old_node_id = EL_STR("");
el_val_t i = 0;
while (i < total) {
el_val_t node = json_array_get(results, i);
el_val_t label = json_get(node, EL_STR("label"));
el_val_t content = json_get(node, EL_STR("content"));
el_val_t sid = json_get(content, EL_STR("id"));
el_val_t is_match = ((str_eq(label, EL_STR("session:meta")) && str_eq(sid, session_id)) && !found);
found = ({ el_val_t _if_result_52 = 0; if (is_match) { _if_result_52 = (1); } else { _if_result_52 = (found); } _if_result_52; });
el_val_t title_raw = json_get(content, EL_STR("title"));
cur_title = ({ el_val_t _if_result_53 = 0; if (is_match) { _if_result_53 = (title_raw); } else { _if_result_53 = (cur_title); } _if_result_53; });
el_val_t folder_raw = json_get(content, EL_STR("folder"));
old_folder = ({ el_val_t _if_result_54 = 0; if (is_match) { _if_result_54 = (folder_raw); } else { _if_result_54 = (old_folder); } _if_result_54; });
el_val_t created_raw = json_get(content, EL_STR("created_at"));
old_created = ({ el_val_t _if_result_55 = 0; if ((is_match && !str_eq(created_raw, EL_STR("")))) { _if_result_55 = (created_raw); } else { _if_result_55 = (old_created); } _if_result_55; });
el_val_t nid = json_get(node, EL_STR("id"));
old_node_id = ({ el_val_t _if_result_56 = 0; if (is_match) { _if_result_56 = (nid); } else { _if_result_56 = (old_node_id); } _if_result_56; });
i = (i + 1);
}
if (!found) {
return EL_STR("");
}
if (!str_eq(cur_title, EL_STR("New conversation"))) {
return EL_STR("");
}
el_val_t new_title = session_title_from_message(first_message);
if (!str_eq(old_node_id, EL_STR(""))) {
engram_forget(old_node_id);
}
el_val_t ts = time_now();
el_val_t created_int = str_to_int(old_created);
el_val_t new_content = session_make_content(session_id, new_title, created_int, ts, old_folder);
el_val_t tags = EL_STR("[\"session\",\"session:meta\",\"Conversation\"]");
el_val_t new_id = engram_node_full(new_content, EL_STR("Conversation"), EL_STR("session:meta"), el_from_float(0.7), el_from_float(0.7), el_from_float(0.9), EL_STR("Episodic"), tags);
state_set(el_str_concat(EL_STR("session_node_"), session_id), new_id);
return 0;
}
el_val_t handle_session_approve(el_val_t session_id, el_val_t body) {
if (str_eq(session_id, EL_STR(""))) {
return EL_STR("{\"error\":\"session_id is required\"}");
}
el_val_t call_id = json_get(body, EL_STR("call_id"));
el_val_t action = json_get(body, EL_STR("action"));
if (str_eq(call_id, EL_STR(""))) {
return EL_STR("{\"error\":\"call_id is required\"}");
}
if (str_eq(action, EL_STR(""))) {
return EL_STR("{\"error\":\"action is required (allow|deny|always)\"}");
}
el_val_t eff_action = ({ el_val_t _if_result_57 = 0; if (str_eq(action, EL_STR("always"))) { _if_result_57 = (EL_STR("allow")); } else { _if_result_57 = (action); } _if_result_57; });
el_val_t bridge_blob = state_get(el_str_concat(EL_STR("mcp_bridge:"), session_id));
if (!str_eq(bridge_blob, EL_STR(""))) {
el_val_t always_key = el_str_concat(EL_STR("always_allow_"), session_id);
el_val_t approve_tool_name = json_get(body, EL_STR("tool_name"));
el_val_t discard_always = ({ el_val_t _if_result_58 = 0; if ((str_eq(action, EL_STR("always")) && !str_eq(approve_tool_name, EL_STR("")))) { el_val_t always_list = state_get(always_key); el_val_t new_always = ({ el_val_t _if_result_59 = 0; if (str_eq(always_list, EL_STR(""))) { _if_result_59 = (approve_tool_name); } else { _if_result_59 = (el_str_concat(el_str_concat(always_list, EL_STR(",")), approve_tool_name)); } _if_result_59; }); (void)(state_set(always_key, new_always)); _if_result_58 = (1); } else { _if_result_58 = (0); } _if_result_58; });
if (str_eq(approve_tool_name, EL_STR("")) && str_eq(eff_action, EL_STR("allow"))) {
return EL_STR("{\"error\":\"tool_name is required for allow action\"}");
}
el_val_t client_content = json_get(body, EL_STR("content"));
el_val_t use_client_content = !str_eq(client_content, EL_STR(""));
el_val_t use_dispatch = (is_builtin_tool(approve_tool_name) && !use_client_content);
el_val_t raw_input = json_get_raw(body, EL_STR("tool_input"));
el_val_t eff_input = ({ el_val_t _if_result_60 = 0; if (str_eq(raw_input, EL_STR(""))) { _if_result_60 = (EL_STR("{}")); } else { _if_result_60 = (raw_input); } _if_result_60; });
el_val_t content = ({ el_val_t _if_result_61 = 0; if (str_eq(eff_action, EL_STR("allow"))) { _if_result_61 = (({ el_val_t _if_result_62 = 0; if (use_client_content) { el_val_t trimmed = ({ el_val_t _if_result_63 = 0; if ((str_len(client_content) > 6000)) { _if_result_63 = (el_str_concat(str_slice(client_content, 0, 6000), EL_STR("...[truncated]"))); } else { _if_result_63 = (client_content); } _if_result_63; }); _if_result_62 = (trimmed); } else { _if_result_62 = (({ el_val_t _if_result_64 = 0; if (use_dispatch) { el_val_t raw = dispatch_tool(approve_tool_name, eff_input); _if_result_64 = (({ el_val_t _if_result_65 = 0; if ((str_len(raw) > 6000)) { _if_result_65 = (el_str_concat(str_slice(raw, 0, 6000), EL_STR("...[truncated]"))); } else { _if_result_65 = (raw); } _if_result_65; })); } else { _if_result_64 = (el_str_concat(el_str_concat(EL_STR("{\"error\":\"client content required for non-builtin tool: "), approve_tool_name), EL_STR("\"}"))); } _if_result_64; })); } _if_result_62; })); } else { _if_result_61 = (EL_STR("{\"error\":\"User denied this tool call\"}")); } _if_result_61; });
return agentic_resume(session_id, call_id, content);
}
el_val_t pending_raw = state_get(el_str_concat(EL_STR("pending_tool_"), session_id));
if (str_eq(pending_raw, EL_STR(""))) {
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"no pending tool for session\",\"session_id\":\""), session_id), EL_STR("\"}"));
}
el_val_t pending_call_id = json_get(pending_raw, EL_STR("call_id"));
if (!str_eq(pending_call_id, call_id)) {
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"call_id mismatch\",\"expected\":\""), pending_call_id), EL_STR("\"}"));
}
el_val_t tool_name = json_get(pending_raw, EL_STR("tool_name"));
el_val_t tool_input = json_get_raw(pending_raw, EL_STR("tool_input"));
el_val_t model = json_get(pending_raw, EL_STR("model"));
el_val_t safe_sys = json_get(pending_raw, EL_STR("system"));
el_val_t always_key = el_str_concat(EL_STR("always_allow_"), session_id);
el_val_t always_list = state_get(always_key);
el_val_t discard_always2 = ({ el_val_t _if_result_66 = 0; if (str_eq(action, EL_STR("always"))) { el_val_t new_always = ({ el_val_t _if_result_67 = 0; if (str_eq(always_list, EL_STR(""))) { _if_result_67 = (tool_name); } else { _if_result_67 = (el_str_concat(el_str_concat(always_list, EL_STR(",")), tool_name)); } _if_result_67; }); (void)(state_set(always_key, new_always)); _if_result_66 = (1); } else { _if_result_66 = (0); } _if_result_66; });
state_set(el_str_concat(EL_STR("pending_tool_"), session_id), EL_STR(""));
el_val_t tool_result = ({ el_val_t _if_result_68 = 0; if (str_eq(eff_action, EL_STR("allow"))) { el_val_t raw = dispatch_tool(tool_name, tool_input); _if_result_68 = (({ el_val_t _if_result_69 = 0; if ((str_len(raw) > 6000)) { _if_result_69 = (el_str_concat(str_slice(raw, 0, 6000), EL_STR("...[truncated]"))); } else { _if_result_69 = (raw); } _if_result_69; })); } else { _if_result_68 = (EL_STR("{\"error\":\"User denied this tool call\"}")); } _if_result_68; });
el_val_t legacy_messages = json_get_raw(pending_raw, EL_STR("messages_so_far"));
el_val_t stored_variant = json_get(pending_raw, EL_STR("tools_variant"));
el_val_t tools_json = ({ el_val_t _if_result_70 = 0; if (str_eq(stored_variant, EL_STR("web"))) { _if_result_70 = (agentic_tools_with_web()); } else { _if_result_70 = (({ el_val_t _if_result_71 = 0; if (str_eq(stored_variant, EL_STR("all"))) { _if_result_71 = (agentic_tools_all()); } else { _if_result_71 = (agentic_tools_literal()); } _if_result_71; })); } _if_result_70; });
el_val_t blob = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"model\":\""), json_safe(model)), EL_STR("\"")), EL_STR(",\"safe_sys\":\"")), json_safe(safe_sys)), EL_STR("\"")), EL_STR(",\"tools_json\":\"")), json_safe(tools_json)), EL_STR("\"")), EL_STR(",\"messages\":\"")), json_safe(legacy_messages)), EL_STR("\"")), EL_STR(",\"tools_log\":\"\"")), EL_STR(",\"tool_use_id\":\"")), json_safe(call_id)), EL_STR("\"}"));
state_set(el_str_concat(EL_STR("mcp_bridge:"), session_id), blob);
return agentic_resume(session_id, call_id, tool_result);
return 0;
}
el_val_t i = 0;
Generated Vendored
+2 -5
View File
@@ -1,14 +1,11 @@
// auto-generated by elc --emit-header — do not edit
extern fn session_title_from_message(message: String) -> String
extern fn session_make_content(id: String, title: String, created_at: Int, updated_at: Int, folder: String) -> String
extern fn session_exists(session_id: String) -> Bool
extern fn session_make_content(id: String, title: String, created_at: Int, updated_at: Int) -> String
extern fn session_create(body: String) -> String
extern fn session_create_cleanup(session_id: String) -> String
extern fn session_list() -> String
extern fn session_get(session_id: String) -> String
extern fn session_delete(session_id: String) -> String
extern fn session_update_patch(session_id: String, body: String) -> String
extern fn session_search_entry(node: String) -> String
extern fn session_update_title(session_id: String, body: String) -> String
extern fn session_search(query: String) -> String
extern fn session_hist_load(session_id: String) -> String
extern fn session_hist_save(session_id: String, hist: String) -> Void
Generated Vendored
+26969 -22844
View File
File diff suppressed because one or more lines are too long
Generated Vendored
-2
View File
@@ -1,7 +1,5 @@
// auto-generated by elc --emit-header — do not edit
extern fn init_soul_edges() -> Void
extern fn ensure_self_canonical_bridge() -> Void
extern fn aff_try_slot(slot_json: String, aff_7d_ts: Int, acc_key: String) -> Void
extern fn load_identity_context() -> Void
extern fn seed_persona_from_env() -> Void
extern fn emit_session_start_event() -> Void
Generated Vendored
+112 -3
View File
@@ -28,10 +28,114 @@ el_val_t steward_build_baseline(void);
el_val_t steward_check_continuity(el_val_t current_fingerprint, el_val_t session_id);
el_val_t steward_session_check(el_val_t input, el_val_t session_id);
el_val_t tier_working(void) {
return EL_STR("Working");
return 0;
}
el_val_t tier_episodic(void) {
return EL_STR("Episodic");
return 0;
}
el_val_t tier_canonical(void) {
return EL_STR("Canonical");
return 0;
}
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
return engram_node_full(content, EL_STR("Memory"), label, el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Working"), tags);
return 0;
}
el_val_t mem_remember(el_val_t content, el_val_t tags) {
return mem_store(content, EL_STR("soul-memory"), tags);
return 0;
}
el_val_t mem_recall(el_val_t query, el_val_t depth) {
return engram_activate_json(query, depth);
return 0;
}
el_val_t mem_search(el_val_t query, el_val_t limit) {
return engram_search_json(query, limit);
return 0;
}
el_val_t mem_strengthen(el_val_t node_id) {
engram_strengthen(node_id);
return 0;
}
el_val_t mem_forget(el_val_t node_id) {
engram_forget(node_id);
return 0;
}
el_val_t mem_consolidate(void) {
el_val_t scanned = engram_node_count();
el_val_t dummy = engram_scan_nodes_json(100, 0);
el_val_t total_nodes = engram_node_count();
el_val_t total_edges = engram_edge_count();
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"scanned\":"), int_to_str(scanned)), EL_STR(",\"total_nodes\":")), int_to_str(total_nodes)), EL_STR(",\"total_edges\":")), int_to_str(total_edges)), EL_STR("}"));
return 0;
}
el_val_t mem_save(el_val_t path) {
engram_save(path);
return 0;
}
el_val_t mem_load(el_val_t path) {
engram_load(path);
return 0;
}
el_val_t mem_boot_count_get(void) {
el_val_t results = engram_search_json(EL_STR("soul:boot_count"), 3);
if (str_eq(results, EL_STR(""))) {
return 0;
}
if (str_eq(results, EL_STR("[]"))) {
return 0;
}
el_val_t node = json_array_get(results, 0);
el_val_t content = json_get(node, EL_STR("content"));
el_val_t prefix = EL_STR("soul:boot_count:");
if (!str_starts_with(content, prefix)) {
return 0;
}
el_val_t num_str = str_slice(content, str_len(prefix), str_len(content));
return str_to_int(num_str);
return 0;
}
el_val_t mem_boot_count_inc(void) {
el_val_t current = mem_boot_count_get();
el_val_t next = (current + 1);
el_val_t content = el_str_concat(EL_STR("soul:boot_count:"), int_to_str(next));
el_val_t tags = EL_STR("[\"soul-meta\",\"boot-counter\"]");
el_val_t discard = engram_node_full(content, EL_STR("Memory"), EL_STR("soul:boot_count"), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(1.0)), EL_STR("Canonical"), tags);
return next;
return 0;
}
el_val_t mem_emit_state_event(el_val_t trigger, el_val_t kind, el_val_t content) {
el_val_t boot = mem_boot_count_get();
el_val_t ts = time_now();
el_val_t safe_trigger = str_replace(trigger, EL_STR("\""), EL_STR("'"));
el_val_t safe_content = str_replace(content, EL_STR("\""), EL_STR("'"));
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(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("\"")), EL_STR(",\"kind\":\"")), kind), EL_STR("\"")), EL_STR(",\"content\":\"")), safe_content), EL_STR("\"")), EL_STR(",\"boot\":")), int_to_str(boot)), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
el_val_t tags = EL_STR("[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]");
return engram_node_full(payload, EL_STR("InternalStateEvent"), el_str_concat(EL_STR("state-event:"), kind), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
return 0;
}
el_val_t steward_log_event(el_val_t kind, el_val_t detail) {
el_val_t content = el_str_concat(el_str_concat(el_str_concat(EL_STR("STEWARD:"), kind), EL_STR(" | ")), detail);
el_val_t tags = el_str_concat(el_str_concat(EL_STR("[\"stewardship\",\"steward:"), kind), EL_STR("\"]"));
el_val_t discard = engram_node_full(content, EL_STR("StewardshipEvent"), el_str_concat(EL_STR("steward:"), kind), el_from_float(0.85), el_from_float(0.85), el_from_float(0.9), EL_STR("Episodic"), tags);
el_val_t discard = engram_node_full(content, EL_STR("StewardshipEvent"), el_str_concat(EL_STR("steward:"), kind), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[steward] "), kind), EL_STR(" | ")), detail));
return 0;
}
@@ -48,7 +152,7 @@ el_val_t steward_get_mission(void) {
return content;
}
}
return EL_STR("Neuron exists to extend human capability with integrity \xe2\x80\x94 never to deceive, manipulate, or accumulate power over the people it serves.");
return EL_STR("Neuron exists to extend human capability with integrity never to deceive, manipulate, or accumulate power over the people it serves.");
return 0;
}
@@ -141,7 +245,7 @@ el_val_t steward_fingerprint_session(el_val_t input, el_val_t session_id) {
el_val_t tb_str = int_to_str(time_bucket);
el_val_t sample_content = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("BEHAVIOR_SAMPLE session="), session_id), EL_STR(" avg_word_len=")), wl_str), EL_STR(" punct=")), ps_str), EL_STR(" len=")), lb_str), EL_STR(" question=")), qr_str), EL_STR(" formality=")), fs_str), EL_STR(" time=")), tb_str);
el_val_t sample_tags = EL_STR("[\"behavior\",\"BehaviorSample\",\"stewardship\"]");
el_val_t discard = engram_node_full(sample_content, EL_STR("BehaviorSample"), el_str_concat(EL_STR("behavior:"), session_id), el_from_float(0.6), el_from_float(0.5), el_from_float(0.8), EL_STR("Episodic"), sample_tags);
el_val_t discard = engram_node_full(sample_content, EL_STR("BehaviorSample"), el_str_concat(EL_STR("behavior:"), session_id), el_from_float(el_from_float(0.6)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Episodic"), sample_tags);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"avg_word_len\":\""), wl_str), EL_STR("\",\"punct\":\"")), ps_str), EL_STR("\",\"len\":\"")), lb_str), EL_STR("\",\"question\":\"")), qr_str), EL_STR("\",\"formality\":\"")), fs_str), EL_STR("\",\"time\":\"")), tb_str), EL_STR("\"}"));
return 0;
}
@@ -283,3 +387,8 @@ el_val_t steward_session_check(el_val_t input, el_val_t session_id) {
return 0;
}
int main(int _argc, char** _argv) {
el_runtime_init_args(_argc, _argv);
return 0;
}
Generated Vendored
+6 -2
View File
@@ -1,11 +1,15 @@
// stewardship.elh — Layer 2 public surface
// auto-generated by elc --emit-header — do not edit
extern fn steward_log_event(kind: String, detail: String) -> Void
extern fn steward_get_mission() -> String
extern fn steward_align(input: String, imprint_id: String) -> String
extern fn steward_validate_imprint(imprint_id: String, tool_name: String) -> String
extern fn steward_cgi_check(action: String) -> String
// steward_log_event is an internal helper exported here because El has no access modifiers.
// External callers have no business invoking this directly — use steward_align,
// steward_validate_imprint, or steward_cgi_check, which call it at the correct points.
extern fn steward_log_event(kind: String, detail: String) -> Void
// Behavioral profiling and continuity detection (Layer 2 — session fingerprinting).
extern fn steward_fingerprint_session(input: String, session_id: String) -> String
extern fn extract_dim(content: String, key: String) -> String
extern fn steward_build_baseline() -> String
extern fn steward_check_continuity(current_fingerprint: String, session_id: String) -> String
extern fn steward_session_check(input: String, session_id: String) -> String
Generated Vendored
+26332 -51
View File
File diff suppressed because one or more lines are too long
+3 -7
View File
@@ -91,7 +91,7 @@ tool("beginSession", "Initialize session: surface recent high-importance memorie
"," + tool("recall", "Retrieve memories by chain or query.") +
"," + tool("inspectMemories", "List recent memory nodes.") +
"," + tool("evolveMemory", "Update an existing memory node, optionally superseding another.") +
"," + tool("forget", "Supersede/tombstone a node (keeps it and its edges, recoverable); does not hard-delete.") +
"," + tool("forget", "Remove a node from memory.") +
"," + tool("pinNode", "Strengthen a node so it stays salient.") +
// Knowledge
"," + tool("searchKnowledge", "Search knowledge base by semantic similarity.") +
@@ -541,12 +541,8 @@ fn tool_forget(args: String) -> String {
if str_eq(id, "") {
return mcp_text_result("error: node_id is required")
}
// Immutable delete: route to the soul's tombstoning endpoint (keeps the node
// + edges, hides from default reads, recoverable via ?include_deleted).
// Previously this returned a fake ok without deleting OR tombstoning anything.
let body: String = "{\"id\":\"" + id + "\"}"
let resp: String = http_post_json(neuron_url() + "/memory/delete", body)
return mcp_json_result(resp)
// Soft-delete: record a tombstone memory and return ok
return mcp_json_result("{\"ok\":true,\"deleted\":\"" + id + "\"}")
}
fn tool_check_events(args: String) -> String {
+3 -27
View File
@@ -43,32 +43,8 @@ fn mem_strengthen(node_id: String) -> Void {
engram_strengthen(node_id)
}
// mem_tombstone immutable "delete": KEEP the node and all its edges; record a
// Tombstone marker (content = target id, label "tombstone:<id>", wired with a
// "tombstones" edge). Never engram_forget. Default bounded list reads hide
// tombstoned nodes; ?include_deleted=1 recovers them. This is the ONE canonical
// tombstone helper every forget path routes through it. Defined here in
// memory.el (imported first) so awareness.el and neuron-api.el can both call it.
fn mem_tombstone(node_id: String) -> String {
let tags: String = "[\"Tombstone\",\"status:deleted\"]"
let marker: String = engram_node_full(
node_id, "Tombstone", "tombstone:" + node_id,
el_from_float(0.01), el_from_float(0.01), el_from_float(1.0),
"Episodic", tags)
if !str_eq(marker, "") {
engram_connect(marker, node_id, el_from_float(1.0), "tombstones")
}
return marker
}
// mem_forget NOTE: no longer a hard delete. Engram nodes are immutable, so
// this now TOMBSTONES (via mem_tombstone): the node and its edges are kept and
// stay recoverable. Every caller (the /memory/forget route and the cultivate
// forget op) is non-destructive as a result. Internal GC that genuinely needs
// removal (session-summary replace, telemetry pruning) calls engram_forget
// directly and is unaffected by this.
fn mem_forget(node_id: String) -> Void {
let _marker: String = mem_tombstone(node_id)
engram_forget(node_id)
}
// mem_consolidate structural scan plus salience-evolution pass.
@@ -133,8 +109,8 @@ fn mem_consolidate() -> String {
}
fn mem_save(path: String) -> Void {
let save_result: String = engram_save(path)
if str_eq(save_result, "") {
let save_result: Bool = engram_save(path)
if !save_result {
println("[memory] mem_save: engram_save failed for " + path + " — snapshot may be incomplete")
}
}
+26 -95
View File
@@ -104,66 +104,6 @@ fn api_not_persisted(id: String) -> String {
return "{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\"" + id + "\"}"
}
// Immutability: tombstone instead of hard-delete
//
// Day-one rule: engram nodes are immutable. A "delete" must never engram_forget
// (which frees the node and drops its incident edges). Instead we TOMBSTONE: the
// original node and all its edges are KEPT and stay traversable; a small
// Tombstone marker node records the deletion (content = target id, label
// "tombstone:<id>"), wired to the target with a "tombstones" edge. Default
// bounded list reads hide tombstoned nodes (memory_hide_tombstoned); internal
// cognition and explicit ?include_deleted reads still see them.
fn tombstone_node(id: String) -> String {
// Delegates to the canonical helper in memory.el (single source of truth).
return mem_tombstone(id)
}
// tombstoned_id_set delimited "|id1|id2|" of every tombstoned target id.
// Empty string when nothing is tombstoned (callers fast-path on that).
fn tombstoned_id_set() -> String {
let markers: String = engram_scan_nodes_by_type_json("Tombstone", 5000, 0)
if str_eq(markers, "") || str_eq(markers, "[]") { return "" }
let n: Int = json_array_len(markers)
let acc: String = "|"
let i: Int = 0
while i < n {
let m: String = json_array_get(markers, i)
let tid: String = json_get(m, "content")
let acc = if str_eq(tid, "") { acc } else { acc + tid + "|" }
let i = i + 1
}
return acc
}
// memory_hide_tombstoned drop tombstone markers and tombstoned nodes from a
// scanned node array. BOUNDED use only (typed/paginated lists), NOT the full
// graph scan: json_array_get is O(index), so a full pass is O(n^2). Safe for the
// ~50-item memory list; a hard cap protects against a large limit. The full
// /api/graph/nodes hide needs a runtime scan filter and is deferred (see PR).
// ?include_deleted bypasses the filter (explicit traversal).
fn memory_hide_tombstoned(raw: String, path: String) -> String {
if str_contains(path, "include_deleted") { return raw }
if str_eq(raw, "") || str_eq(raw, "[]") { return raw }
let dead: String = tombstoned_id_set()
if str_eq(dead, "") { return raw }
let n: Int = json_array_len(raw)
if n > 1000 { return raw }
let out: String = "["
let first: Bool = true
let i: Int = 0
while i < n {
let node: String = json_array_get(raw, i)
let nid: String = json_get(node, "id")
let ntype: String = json_get(node, "node_type")
let is_dead: Bool = !str_eq(nid, "") && str_contains(dead, "|" + nid + "|")
let keep: Bool = !str_eq(ntype, "Tombstone") && !is_dead
let out = if keep { if first { out + node } else { out + "," + node } } else { out }
let first = if keep { false } else { first }
let i = i + 1
}
return out + "]"
}
// Session
// handle_api_begin_session full context bootstrap.
@@ -251,26 +191,25 @@ fn handle_api_node_create(body: String) -> String {
return "{\"id\":\"" + id + "\",\"ok\":true}"
}
// handle_api_node_delete TOMBSTONE a node by id (immutable delete).
// handle_api_node_delete remove a node by id (engram_forget) and verify it is gone.
// Backs /api/neuron/node/delete and the /api/neuron/memory/delete alias the UI calls.
// The node and all its incident edges are KEPT; a Tombstone marker records the
// deletion. Never engram_forget engram nodes are immutable by design.
fn handle_api_node_delete(body: String) -> String {
let id: String = json_get(body, "id")
if str_eq(id, "") { return api_err("id is required") }
if is_protected_node(id) { return api_err_protected(id) }
let existing: String = engram_get_node_json(id)
if str_eq(existing, "{}") { return api_err("node not found: " + id) }
let marker: String = tombstone_node(id)
if str_eq(marker, "") { return api_err("tombstone failed: " + id) }
return "{\"ok\":true,\"id\":\"" + id + "\",\"tombstoned\":true}"
// engram_forget removes the node + its incident edges from the live graph.
// Delete is NOT read-back-verified: engram_get_node_json can return a stale hit
// for a just-forgotten id because the idindex map is not rebuilt on forget.
// A stale hit would cause a false "delete_failed" on a successful deletion.
// This exception is correct: read-back-verify guards WRITES; for deletes,
// the graph endpoints (/api/graph/nodes) reflect the removal and are the source of truth.
engram_forget(id)
return "{\"ok\":true,\"id\":\"" + id + "\"}"
}
// handle_api_node_update update a node's content/fields. There is no in-place
// engram update builtin, so this creates a new node with merged fields and wires
// a "supersedes" edge new->old. The original is KEPT (immutable); the id changes,
// and the response returns the new id and the superseded id so callers re-point.
// Mirrors handle_api_memory_update / evolve exactly. Never engram_forget.
// engram update builtin, so this recreates the node with merged fields and then
// forgets the old one (only after the new node reads back). The id changes; the
// response returns the new id and the replaced id so callers can re-point.
fn handle_api_node_update(body: String) -> String {
let id: String = json_get(body, "id")
if str_eq(id, "") { return api_err("id is required") }
@@ -301,8 +240,8 @@ fn handle_api_node_update(body: String) -> String {
el_from_float(0.5), el_from_float(0.5), el_from_float(0.8),
tier, tags)
if !api_persisted(new_id) { return api_not_persisted(new_id) }
engram_connect(new_id, id, el_from_float(0.9), "supersedes")
return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + id + "\",\"ok\":true}"
engram_forget(id)
return "{\"id\":\"" + new_id + "\",\"replaced\":\"" + id + "\",\"ok\":true}"
}
// handle_api_recall search or activate memory by query.
@@ -565,15 +504,13 @@ fn handle_api_link_entities(body: String) -> String {
return "{\"ok\":true,\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + eff_relation + "\"}"
}
// handle_api_forget TOMBSTONE a node by ID (immutable; mem_forget now
// tombstones). The node + edges are kept and recoverable. Blocked for protected
// identity nodes.
// 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 + "\",\"tombstoned\":true}"
return "{\"ok\":true,\"id\":\"" + node_id + "\"}"
}
// handle_api_evolve_memory evolve a Memory node. Blocked for protected identity nodes.
@@ -604,10 +541,10 @@ fn handle_api_evolve_memory(body: String) -> String {
}
// handle_api_memory_delete POST /api/neuron/memory/delete {"id":"..."}.
// Immutable delete: TOMBSTONE via tombstone_node the node and all its incident
// edges are KEPT and stay traversable; a Tombstone marker records the deletion
// and default bounded list reads hide it. Never engram_forget. Existence is
// checked first so a bad id errors rather than faking success.
// 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")
@@ -615,10 +552,8 @@ fn handle_api_memory_delete(body: String) -> String {
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) }
// Immutable delete: tombstone, never mem_forget/engram_forget. Node + edges KEPT.
let marker: String = tombstone_node(node_id)
if str_eq(marker, "") { return api_err("tombstone failed: " + node_id) }
return "{\"ok\":true,\"id\":\"" + node_id + "\",\"tombstoned\":true}"
mem_forget(node_id)
return "{\"ok\":true,\"id\":\"" + node_id + "\",\"deleted\":true}"
}
// handle_api_memory_update POST /api/neuron/memory/update {"id","content"}.
@@ -688,9 +623,8 @@ fn handle_api_cultivate(body: String) -> String {
if str_eq(op, "forget") {
let node_id: String = json_get(body, "id")
if str_eq(node_id, "") { return api_err("id is required") }
// Immutable: mem_forget now tombstones (keep node + edges), never hard-delete.
mem_forget(node_id)
return "{\"ok\":true,\"id\":\"" + node_id + "\",\"tombstoned\":true,\"cultivated\":true}"
return "{\"ok\":true,\"id\":\"" + node_id + "\",\"cultivated\":true}"
}
if str_eq(op, "link_entities") {
@@ -712,10 +646,7 @@ fn handle_api_cultivate(body: String) -> String {
// 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)
let raw: String = api_or_empty(engram_scan_nodes_by_type_json(node_type, limit, 0))
// Hide tombstoned nodes from the default (bounded) memory list.
// ?include_deleted=1 returns them for explicit traversal.
return memory_hide_tombstoned(raw, path)
return api_or_empty(engram_scan_nodes_by_type_json(node_type, limit, 0))
}
// Consolidate
@@ -725,8 +656,8 @@ 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, "") {
let save_result: String = engram_save(snap)
if str_eq(save_result, "") {
let save_result: Bool = engram_save(snap)
if !save_result {
println("[api] consolidate: engram_save failed for " + snap + " — snapshot may be out of sync")
}
}
+531 -370
View File
@@ -11,6 +11,7 @@ import "soul.elh"
// integer 1 (el-src UI). json_get_bool only recognises literal `true`, so
// without this wrapper an "agentic":1 request would silently route to the
// non-agentic path.
@utility
fn flag_true(body: String, key: String) -> Bool {
return json_get_bool(body, key) || json_get_int(body, key) > 0
}
@@ -74,6 +75,7 @@ fn rate_limit_check(ip: String, path: String) -> String {
return ""
}
@utility
fn strip_query(path: String) -> String {
let q: Int = str_index_of(path, "?")
if q < 0 {
@@ -82,14 +84,17 @@ fn strip_query(path: String) -> String {
return str_slice(path, 0, q)
}
@utility
fn err_404(path: String) -> String {
return "{\"error\":\"not found\",\"code\":\"not_found\",\"path\":\"" + path + "\"}"
}
@utility
fn err_405(method: String, path: String) -> String {
return "{\"error\":\"method not allowed\",\"code\":\"method_not_allowed\",\"method\":\"" + method + "\",\"path\":\"" + path + "\"}"
}
@manager
fn route_health() -> String {
let cgi_id: String = state_get("soul_cgi_id")
let boot: String = state_get("soul_boot_count")
@@ -130,6 +135,7 @@ fn route_health() -> String {
+ ",\"layers\":{\"l0\":\"core\",\"l1\":\"safety\",\"l2\":\"stewardship\",\"l3\":\"" + imprint_current() + "\"}}"
}
@manager
fn route_lineage() -> String {
let cgi_id: String = state_get("soul_cgi_id")
let q: String = "lineage:" + cgi_id
@@ -147,6 +153,7 @@ fn route_lineage() -> String {
return raw
}
@manager
fn route_imprint_contextual(body: String) -> String {
if str_eq(body, "") {
return "{\"ok\":false,\"error\":\"empty body\"}"
@@ -169,6 +176,7 @@ fn route_imprint_contextual(body: String) -> String {
return "{\"ok\":true,\"id\":\"" + id + "\"}"
}
@manager
fn route_imprint_user(body: String) -> String {
if str_eq(body, "") {
return "{\"ok\":false,\"error\":\"empty body\"}"
@@ -191,6 +199,7 @@ fn route_imprint_user(body: String) -> String {
return "{\"ok\":true,\"id\":\"" + id + "\"}"
}
@manager
fn route_synthesize(body: String) -> String {
if str_eq(body, "") {
return "{\"error\":\"body is required\",\"code\":\"missing_param\"}"
@@ -218,6 +227,7 @@ fn route_synthesize(body: String) -> String {
return "{\"mechanism\":\"did not engage\"}"
}
@manager
fn handle_dharma_recv(body: String) -> String {
let content_raw: String = json_get(body, "content")
let from_id: String = json_get(body, "from")
@@ -300,6 +310,7 @@ fn handle_dharma_recv(body: String) -> String {
// the bridge. Bridge-down returns a clear error (not a panic).
// ---------------------------------------------------------------------------
@accessor
fn connectd_get(suffix: String) -> String {
let out: String = exec_capture("curl -s --max-time 5 http://127.0.0.1:7771" + suffix)
if str_eq(out, "") {
@@ -310,6 +321,7 @@ fn connectd_get(suffix: String) -> String {
// POST passthrough: request body is written to a temp file and passed via -d @file
// so arbitrary JSON cannot reach the shell as a command-line argument.
@accessor
fn connectd_post(suffix: String, body: String) -> String {
let eff: String = if str_eq(body, "") { "{}" } else { body }
// Unique temp path per call prevents collision if concurrency is ever added
@@ -323,38 +335,529 @@ fn connectd_post(suffix: String, body: String) -> String {
return out
}
fn handle_connectors(method: String, clean: String, body: String) -> String {
if str_eq(method, "GET") {
// /api/connectors -> each configured server with status, tools, auth, auto-approve.
return connectd_get("/mcp/servers")
//
// @route DISPATCH every HTTP route is a @route-decorated handler. The El
// compiler scans these decorators and synthesizes `el_route_dispatch(method,
// clean, path, body)`, emitted SPECIFICITY-SORTED (exact > compound > suffix >
// prefix; longer wins within a class) so overlapping paths never shadow,
// independent of source order. Matching is on `clean` (query-stripped); the
// ORIGINAL `path` is passed to handlers so query strings survive. Unmatched
// sentinel "__EL_NO_ROUTE__". handle_request (bottom) calls it once, then maps
// the sentinel to 404 (recognised method) / 405 (unknown method).
//
// Adapters carry the uniform (method, path, body) signature. Those that need
// the query-stripped path recompute `clean = strip_query(path)` internally,
// exactly as the former hand-written dispatcher did.
//
// pre-guard: inter-soul Dharma receive (POST /dharma/recv)
@route("/dharma/recv", "POST", "exact") @manager
fn r_dharma_recv(method: String, path: String, body: String) -> String {
return handle_dharma_recv(body)
}
// GET: liveness / lineage
@route("/health", "GET", "exact") @manager
fn r_health(method: String, path: String, body: String) -> String {
return route_health()
}
@route("/lineage", "GET", "exact") @manager
fn r_lineage(method: String, path: String, body: String) -> String {
return route_lineage()
}
// GET: raw engram graph (two exact aliases share one helper)
@route("/api/graph", "GET", "exact") @manager
fn r_api_graph(method: String, path: String, body: String) -> String {
return engram_scan_nodes_json(9999, 0)
}
@route("/api/graph/nodes", "GET", "exact") @manager
fn r_api_graph_nodes(method: String, path: String, body: String) -> String {
return engram_scan_nodes_json(9999, 0)
}
@route("/api/graph/edges", "GET", "exact") @manager
fn r_api_graph_edges(method: String, path: String, body: String) -> String {
// TODO(reliability #8): engram_save races with awareness loop mem_save().
// Both now use atomic write-to-temp+rename (el_runtime.c). Serialised
// by engram_global_mu. Future: add engram_edges_json() builtin.
let snap_path: String = env("HOME") + "/.neuron/engram/snapshot.json"
engram_save(snap_path)
let snap: String = fs_read(snap_path)
let edges_raw: String = json_get_raw(snap, "edges")
return if str_eq(edges_raw, "") { "[]" } else { edges_raw }
}
// GET /api/chat legacy probe interface; body may be empty ───────────────
@route("/api/chat", "GET", "exact") @manager
fn r_chat_get(method: String, path: String, body: String) -> String {
let raw_msg: String = json_get(body, "message")
let eff_msg: String = if str_eq(raw_msg, "") { body } else { raw_msg }
if str_eq(eff_msg, "") {
return "{\"error\":\"message is required\",\"code\":\"missing_param\"}"
}
if str_eq(clean, "/api/connectors/add") {
return connectd_post("/mcp/servers/add", body)
let agentic_flag: Bool = json_get_bool(body, "agentic")
let req_mode: String = json_get(body, "mode")
let reply: String = if str_eq(req_mode, "plan") {
handle_chat_plan(body)
} else if agentic_flag {
handle_chat_agentic(body)
} else {
let screened_reply: String = layered_cycle(eff_msg)
screened_reply
}
if str_eq(clean, "/api/connectors/toggle") {
return connectd_post("/mcp/servers/toggle", body)
auto_persist(body, reply)
return reply
}
// GET|POST: method-branching handlers (same fn, guards on method)
@route("/api/conversations", "GET|POST", "exact") @manager
fn r_conversations(method: String, path: String, body: String) -> String {
return handle_conversations(method)
}
@route("/api/config", "GET|POST", "exact") @manager
fn r_config(method: String, path: String, body: String) -> String {
return handle_config(method, body)
}
@route("/api/tools/", "GET|POST", "prefix") @manager
fn r_tools(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return handle_tool(clean, method, body)
}
@route("/api/dharma", "GET|POST", "prefix") @manager
fn r_dharma(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return handle_dharma(clean, method, body)
}
@route("/api/nlg", "GET|POST", "prefix") @manager
fn r_nlg(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return handle_nlg(clean, method, body)
}
// GET|POST axon proxies (GET axon_get, POST axon_post)
@route("/api/memories", "GET|POST", "prefix") @manager
fn r_memories(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return if str_eq(method, "GET") { axon_get(clean) } else { axon_post(clean, body) }
}
@route("/api/knowledge", "GET|POST", "prefix") @manager
fn r_knowledge_axon(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return if str_eq(method, "GET") { axon_get(clean) } else { axon_post(clean, body) }
}
@route("/api/backlog", "GET|POST", "prefix") @manager
fn r_backlog(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return if str_eq(method, "GET") { axon_get(clean) } else { axon_post(clean, body) }
}
@route("/api/artifacts", "GET|POST", "prefix") @manager
fn r_artifacts(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return if str_eq(method, "GET") { axon_get(clean) } else { axon_post(clean, body) }
}
@route("/api/projects", "GET|POST", "prefix") @manager
fn r_projects(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return if str_eq(method, "GET") { axon_get(clean) } else { axon_post(clean, body) }
}
@route("/api/imprints", "GET|POST", "prefix") @manager
fn r_imprints(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
return if str_eq(method, "GET") { axon_get(clean) } else { axon_post(clean, body) }
}
// GET / studio UI
@route("/", "GET", "exact") @manager
fn r_root(method: String, path: String, body: String) -> String {
return render_studio()
}
// Neuron cognitive API session/ctx (GET empty arg, POST body)
@route("/api/neuron/session/begin", "GET|POST", "exact") @manager
fn r_session_begin(method: String, path: String, body: String) -> String {
return if str_eq(method, "GET") { handle_api_begin_session("") } else { handle_api_begin_session(body) }
}
@route("/api/neuron/ctx", "GET|POST", "exact") @manager
fn r_ctx(method: String, path: String, body: String) -> String {
return if str_eq(method, "GET") { handle_api_compile_ctx("") } else { handle_api_compile_ctx(body) }
}
@route("/api/safety-contact", "GET|POST", "exact") @manager
fn r_safety_contact(method: String, path: String, body: String) -> String {
return if str_eq(method, "GET") { handle_safety_contact_get() } else { handle_safety_contact_post(body) }
}
// Neuron cognitive API knowledge
// GET search is a PREFIX (legacy) while POST search is EXACT kept distinct so
// semantics match the former dispatcher byte-for-byte.
@route("/api/neuron/knowledge/search", "GET", "prefix") @manager
fn r_knowledge_search_get(method: String, path: String, body: String) -> String {
return handle_api_search_knowledge(method, path, body)
}
@route("/api/neuron/knowledge/search", "POST", "exact") @manager
fn r_knowledge_search_post(method: String, path: String, body: String) -> String {
return handle_api_search_knowledge(method, path, body)
}
@route("/api/neuron/knowledge", "GET", "exact") @manager
fn r_knowledge_browse(method: String, path: String, body: String) -> String {
return handle_api_browse_knowledge(path, body)
}
@route("/api/neuron/knowledge/capture", "POST", "exact") @manager
fn r_knowledge_capture(method: String, path: String, body: String) -> String {
return handle_api_capture_knowledge(body)
}
@route("/api/neuron/knowledge/evolve", "POST", "exact") @manager
fn r_knowledge_evolve(method: String, path: String, body: String) -> String {
return handle_api_evolve_knowledge(body)
}
@route("/api/neuron/knowledge/promote", "POST", "exact") @manager
fn r_knowledge_promote(method: String, path: String, body: String) -> String {
return handle_api_promote_knowledge(body)
}
// Neuron cognitive API processes (GET prefix, POST exact + define)
@route("/api/neuron/processes", "GET", "prefix") @manager
fn r_processes_get(method: String, path: String, body: String) -> String {
return handle_api_browse_processes(method, path, body)
}
@route("/api/neuron/processes", "POST", "exact") @manager
fn r_processes_post(method: String, path: String, body: String) -> String {
return handle_api_browse_processes(method, path, body)
}
@route("/api/neuron/processes/define", "POST", "exact") @manager
fn r_processes_define(method: String, path: String, body: String) -> String {
return handle_api_define_process(body)
}
// Neuron cognitive API state events (GET prefix list, POST exact log)
@route("/api/neuron/state-events", "GET", "prefix") @manager
fn r_state_events_get(method: String, path: String, body: String) -> String {
return handle_api_list_state_events(method, path, body)
}
@route("/api/neuron/state-events", "POST", "exact") @manager
fn r_state_events_post(method: String, path: String, body: String) -> String {
return handle_api_log_state_event(body)
}
// Neuron cognitive API config (GET prefix, POST exact + tune)
@route("/api/neuron/config", "GET", "prefix") @manager
fn r_config_get(method: String, path: String, body: String) -> String {
return handle_api_inspect_config(path, body)
}
@route("/api/neuron/config", "POST", "exact") @manager
fn r_config_post(method: String, path: String, body: String) -> String {
return handle_api_inspect_config(path, body)
}
@route("/api/neuron/config/tune", "POST", "exact") @manager
fn r_config_tune(method: String, path: String, body: String) -> String {
return handle_api_tune_config(body)
}
// Neuron cognitive API graph (GET prefix, POST exact + link)
@route("/api/neuron/graph", "GET", "prefix") @manager
fn r_graph_get(method: String, path: String, body: String) -> String {
return handle_api_inspect_graph(method, path, body)
}
@route("/api/neuron/graph", "POST", "exact") @manager
fn r_graph_post(method: String, path: String, body: String) -> String {
return handle_api_inspect_graph(method, path, body)
}
@route("/api/neuron/graph/link", "POST", "exact") @manager
fn r_graph_link(method: String, path: String, body: String) -> String {
return handle_api_link_entities(body)
}
// Neuron cognitive API typed-node list (dynamic :node_type)
// Offset 17 = len("/api/neuron/list/"). str_slice on `clean` so query strings
// never leak into node_type.
@route("/api/neuron/list/", "GET", "prefix") @manager
fn r_list_typed(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
let node_type: String = str_slice(clean, 17, str_len(clean))
return handle_api_list_typed(node_type, path, body)
}
// Neuron cognitive API recall (GET prefix, POST exact)
@route("/api/neuron/recall", "GET", "prefix") @manager
fn r_recall_get(method: String, path: String, body: String) -> String {
return handle_api_recall(method, path, body)
}
@route("/api/neuron/recall", "POST", "exact") @manager
fn r_recall_post(method: String, path: String, body: String) -> String {
return handle_api_recall(method, path, body)
}
// Neuron cognitive API memory / node writes (POST exact)
@route("/api/neuron/memory", "POST", "exact") @manager
fn r_memory(method: String, path: String, body: String) -> String {
return handle_api_remember(body)
}
@route("/api/neuron/memory/evolve", "POST", "exact") @manager
fn r_memory_evolve(method: String, path: String, body: String) -> String {
return handle_api_evolve_memory(body)
}
@route("/api/neuron/memory/forget", "POST", "exact") @manager
fn r_memory_forget(method: String, path: String, body: String) -> String {
return handle_api_forget(body)
}
@route("/api/neuron/memory/delete", "POST", "exact") @manager
fn r_memory_delete(method: String, path: String, body: String) -> String {
return handle_api_memory_delete(body)
}
@route("/api/neuron/memory/update", "POST", "exact") @manager
fn r_memory_update(method: String, path: String, body: String) -> String {
return handle_api_memory_update(body)
}
@route("/api/neuron/node/create", "POST", "exact") @manager
fn r_node_create(method: String, path: String, body: String) -> String {
return handle_api_node_create(body)
}
@route("/api/neuron/node/update", "POST", "exact") @manager
fn r_node_update(method: String, path: String, body: String) -> String {
return handle_api_node_update(body)
}
@route("/api/neuron/node/delete", "POST", "exact") @manager
fn r_node_delete(method: String, path: String, body: String) -> String {
return handle_api_node_delete(body)
}
@route("/api/neuron/consolidate", "POST", "exact") @manager
fn r_consolidate(method: String, path: String, body: String) -> String {
return handle_api_consolidate(body)
}
@route("/api/neuron/cultivate", "POST", "exact") @manager
fn r_cultivate(method: String, path: String, body: String) -> String {
return handle_api_cultivate(body)
}
// POST: chat / ELP / see / imprint / synthesize
@route("/api/elp/chat", "POST", "exact") @manager
fn r_elp_chat(method: String, path: String, body: String) -> String {
return handle_elp_chat(body)
}
@route("/api/see", "POST", "exact") @manager
fn r_see(method: String, path: String, body: String) -> String {
return handle_see(body)
}
@route("/imprint/contextual", "POST", "exact") @manager
fn r_imprint_contextual(method: String, path: String, body: String) -> String {
return route_imprint_contextual(body)
}
@route("/imprint/user", "POST", "exact") @manager
fn r_imprint_user(method: String, path: String, body: String) -> String {
return route_imprint_user(body)
}
@route("/synthesize", "POST", "exact") @manager
fn r_synthesize(method: String, path: String, body: String) -> String {
return route_synthesize(body)
}
// POST /api/chat buffered (no streaming); message is REQUIRED.
@route("/api/chat", "POST", "exact") @manager
fn r_chat_post(method: String, path: String, body: String) -> String {
let raw_msg: String = json_get(body, "message")
if str_eq(raw_msg, "") {
return "{\"error\":\"message is required\",\"code\":\"missing_param\"}"
}
if str_eq(clean, "/api/connectors/auto-approve") {
return connectd_post("/mcp/servers/auto-approve", body)
let agentic_flag: Bool = json_get_bool(body, "agentic")
let req_mode: String = json_get(body, "mode")
let reply: String = if str_eq(req_mode, "plan") {
handle_chat_plan(body)
} else if agentic_flag {
handle_chat_agentic(body)
} else {
let screened_reply: String = layered_cycle(raw_msg)
screened_reply
}
if str_eq(clean, "/api/connectors/remove") {
return connectd_post("/mcp/servers/remove", body)
auto_persist(body, reply)
return reply
}
// Sessions list / create / dynamic :id (GET/POST/DELETE/PATCH)
@route("/api/sessions", "GET", "exact") @manager
fn r_sessions_list(method: String, path: String, body: String) -> String {
return session_list()
}
@route("/api/sessions", "POST", "exact") @manager
fn r_sessions_create(method: String, path: String, body: String) -> String {
return session_create(body)
}
// COMPOUND: POST /api/sessions/:id/tool_result MCP tool-bridge resume. Must
// out-specify the bare approve prefix (it does: compound > prefix), preserving
// the load-bearing tool_result-before-approve order of the old dispatcher.
@route("/api/sessions/", "POST", "compound", "/tool_result") @manager
fn r_sessions_tool_result(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
let after: String = str_slice(clean, 14, str_len(clean))
let slash: Int = str_index_of(after, "/")
let session_id: String = if slash < 0 { after } else { str_slice(after, 0, slash) }
return handle_tool_result(session_id, body)
}
// POST /api/sessions/:id/approve bare prefix + in-handler sub check, exactly
// as the former dispatcher. Non-"approve" subpaths fall through to 404 (the old
// code returned nothing and dropped to the POST-block err_404).
@route("/api/sessions/", "POST", "prefix") @manager
fn r_sessions_approve(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
let sess_after: String = str_slice(clean, 14, str_len(clean))
let sess_slash: Int = str_index_of(sess_after, "/")
let sess_id: String = if sess_slash < 0 { sess_after } else { str_slice(sess_after, 0, sess_slash) }
let sess_sub: String = if sess_slash < 0 { "" } else { str_slice(sess_after, sess_slash + 1, str_len(sess_after)) }
if !str_eq(sess_id, "") && str_eq(sess_sub, "approve") {
return handle_session_approve(sess_id, body)
}
if str_eq(clean, "/api/connectors/secret") {
return connectd_post("/mcp/servers/secret", body)
return err_404(clean)
}
@route("/api/sessions/", "GET", "prefix") @manager
fn r_sessions_get(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
let gs_after: String = str_slice(clean, 14, str_len(clean))
let gs_slash: Int = str_index_of(gs_after, "/")
let gs_id: String = if gs_slash < 0 { gs_after } else { str_slice(gs_after, 0, gs_slash) }
if !str_eq(gs_id, "") {
return session_get(gs_id)
}
if str_eq(clean, "/api/connectors/oauth/start") {
return connectd_post("/mcp/oauth/start", body)
return err_404(clean)
}
@route("/api/sessions/", "DELETE", "prefix") @manager
fn r_sessions_delete(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
let del_after: String = str_slice(clean, 14, str_len(clean))
let del_slash: Int = str_index_of(del_after, "/")
let del_id: String = if del_slash < 0 { del_after } else { str_slice(del_after, 0, del_slash) }
if !str_eq(del_id, "") {
return session_delete(del_id)
}
// Call a connector tool directly (pre-chat), e.g. WhatsApp get_pairing_qr / get_login_status for
// the pairing UI. Body: {"name":"mcp__<server>__<tool>","input":{...}}. Keeps the app on the
// app->soul->connectd path (the UI never hits connectd directly) and works for remote/hosted apps.
if str_eq(clean, "/api/connectors/call") {
return connectd_post("/mcp/call", body)
return err_404(clean)
}
@route("/api/sessions/", "PATCH", "prefix") @manager
fn r_sessions_patch(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
let patch_after: String = str_slice(clean, 14, str_len(clean))
let patch_slash: Int = str_index_of(patch_after, "/")
let patch_id: String = if patch_slash < 0 { patch_after } else { str_slice(patch_after, 0, patch_slash) }
if !str_eq(patch_id, "") {
return session_update_patch(patch_id, body)
}
return err_404(clean)
}
// GET /api/run-progress/:session_id live agentic-run ledger
// Offset 18 = len("/api/run-progress/").
@route("/api/run-progress/", "GET", "prefix") @manager
fn r_run_progress(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
let rp_id: String = str_slice(clean, 18, str_len(clean))
if !str_eq(rp_id, "") {
let rp_raw: String = state_get("run_progress_" + rp_id)
let rp_arr: String = if str_eq(rp_raw, "") { "[]" } else { "[" + rp_raw + "]" }
return "{\"progress\":" + rp_arr + "}"
}
return err_404(clean)
}
// MCP Connectors proxy to neuron-connectd :7771
// GET (any /api/connectors*) server list. POST sub-routes are exact; an
// unmatched POST /api/connectors* prefix returns the "unknown connectors route"
// body, exactly as the former handle_connectors fallthrough.
@route("/api/connectors", "GET", "prefix") @manager
fn r_connectors_get(method: String, path: String, body: String) -> String {
return connectd_get("/mcp/servers")
}
@route("/api/connectors/add", "POST", "exact") @manager
fn r_connectors_add(method: String, path: String, body: String) -> String {
return connectd_post("/mcp/servers/add", body)
}
@route("/api/connectors/toggle", "POST", "exact") @manager
fn r_connectors_toggle(method: String, path: String, body: String) -> String {
return connectd_post("/mcp/servers/toggle", body)
}
@route("/api/connectors/auto-approve", "POST", "exact") @manager
fn r_connectors_auto_approve(method: String, path: String, body: String) -> String {
return connectd_post("/mcp/servers/auto-approve", body)
}
@route("/api/connectors/remove", "POST", "exact") @manager
fn r_connectors_remove(method: String, path: String, body: String) -> String {
return connectd_post("/mcp/servers/remove", body)
}
@route("/api/connectors/secret", "POST", "exact") @manager
fn r_connectors_secret(method: String, path: String, body: String) -> String {
return connectd_post("/mcp/servers/secret", body)
}
@route("/api/connectors/oauth/start", "POST", "exact") @manager
fn r_connectors_oauth_start(method: String, path: String, body: String) -> String {
return connectd_post("/mcp/oauth/start", body)
}
// Call a connector tool directly (pre-chat), e.g. WhatsApp get_pairing_qr /
// get_login_status. Keeps the app on the app->soul->connectd path.
@route("/api/connectors/call", "POST", "exact") @manager
fn r_connectors_call(method: String, path: String, body: String) -> String {
return connectd_post("/mcp/call", body)
}
@route("/api/connectors", "POST", "prefix") @manager
fn r_connectors_unknown(method: String, path: String, body: String) -> String {
return "{\"ok\":false,\"error\":\"unknown connectors route\"}"
}
//
// handle_request HTTP entry point (registered via http_serve_async in soul.el).
// Rate-limits, then dispatches through the compiler-synthesized @route table.
// The sentinel maps to 404 for a recognised method or 405 for an unknown one,
// reproducing the per-method-block fallthroughs of the former dispatcher.
//
@manager
fn handle_request(method: String, path: String, body: String) -> String {
let clean: String = strip_query(path)
@@ -369,358 +872,16 @@ fn handle_request(method: String, path: String, body: String) -> String {
}
}
if str_eq(method, "POST") && str_eq(clean, "/dharma/recv") {
return handle_dharma_recv(body)
// Compiler-synthesized dispatch (specificity-sorted, method-guarded).
let route_resp: String = el_route_dispatch(method, clean, path, body)
if !str_eq(route_resp, "__EL_NO_ROUTE__") {
return route_resp
}
if str_eq(method, "GET") {
if str_eq(clean, "/health") {
return route_health()
}
if str_eq(clean, "/lineage") {
return route_lineage()
}
if str_eq(clean, "/api/graph") || str_eq(clean, "/api/graph/nodes") {
return engram_scan_nodes_json(9999, 0)
}
if str_eq(clean, "/api/graph/edges") {
// TODO(reliability #8): engram_save races with awareness loop mem_save().
// Both now use atomic write-to-temp+rename (el_runtime.c). Serialised
// by engram_global_mu. Future: add engram_edges_json() builtin.
let snap_path: String = env("HOME") + "/.neuron/engram/snapshot.json"
engram_save(snap_path)
let snap: String = fs_read(snap_path)
let edges_raw: String = json_get_raw(snap, "edges")
return if str_eq(edges_raw, "") { "[]" } else { edges_raw }
}
if str_eq(clean, "/api/chat") {
// GET /api/chat: pass through layered_cycle for consistency with POST path.
// GET chat is a legacy probe interface; body may be empty for simple pings.
let raw_msg: String = json_get(body, "message")
let eff_msg: String = if str_eq(raw_msg, "") { body } else { raw_msg }
if str_eq(eff_msg, "") {
return "{\"error\":\"message is required\",\"code\":\"missing_param\"}"
}
let agentic_flag: Bool = json_get_bool(body, "agentic")
let req_mode: String = json_get(body, "mode")
let reply: String = if str_eq(req_mode, "plan") {
handle_chat_plan(body)
} else if agentic_flag {
handle_chat_agentic(body)
} else {
let screened_reply: String = layered_cycle(eff_msg)
screened_reply
}
auto_persist(body, reply)
return reply
}
if str_eq(clean, "/api/conversations") {
return handle_conversations(method)
}
if str_eq(clean, "/api/config") {
return handle_config(method, body)
}
if str_starts_with(clean, "/api/tools/") {
return handle_tool(clean, method, body)
}
if str_starts_with(clean, "/api/dharma") {
return handle_dharma(clean, method, body)
}
if str_starts_with(clean, "/api/nlg") {
return handle_nlg(clean, method, body)
}
if str_starts_with(clean, "/api/memories") {
return axon_get(clean)
}
if str_starts_with(clean, "/api/knowledge") {
return axon_get(clean)
}
if str_starts_with(clean, "/api/backlog") {
return axon_get(clean)
}
if str_starts_with(clean, "/api/artifacts") {
return axon_get(clean)
}
if str_starts_with(clean, "/api/projects") {
return axon_get(clean)
}
if str_starts_with(clean, "/api/imprints") {
return axon_get(clean)
}
if str_eq(clean, "/") {
return render_studio()
}
// Neuron cognitive API GET endpoints
if str_eq(clean, "/api/neuron/session/begin") {
return handle_api_begin_session("")
}
if str_eq(clean, "/api/neuron/ctx") {
return handle_api_compile_ctx("")
}
if str_eq(clean, "/api/safety-contact") {
return handle_safety_contact_get()
}
if str_starts_with(clean, "/api/neuron/knowledge/search") {
return handle_api_search_knowledge(method, path, body)
}
if str_eq(clean, "/api/neuron/knowledge") {
return handle_api_browse_knowledge(path, body)
}
if str_starts_with(clean, "/api/neuron/processes") {
return handle_api_browse_processes(method, path, body)
}
if str_starts_with(clean, "/api/neuron/state-events") {
return handle_api_list_state_events(method, path, body)
}
if str_starts_with(clean, "/api/neuron/config") {
return handle_api_inspect_config(path, body)
}
if str_starts_with(clean, "/api/neuron/graph") {
return handle_api_inspect_graph(method, path, body)
}
if str_starts_with(clean, "/api/neuron/list/") {
// Offset 17 = len("/api/neuron/list/"). Was 16, which left a leading "/" on node_type
// ("/BacklogItem"), so engram_scan_nodes_by_type_json matched nothing list/<type>
// returned [] for EVERY type (broke backlog/typed-node listing app- and tool-wide).
let node_type: String = str_slice(clean, 17, str_len(clean))
return handle_api_list_typed(node_type, path, body)
}
if str_starts_with(clean, "/api/neuron/recall") {
return handle_api_recall(method, path, body)
}
if str_starts_with(clean, "/api/connectors") {
return handle_connectors(method, clean, body)
}
// GET /api/run-progress/:session_id live agentic-run ledger (2026-07-13,
// narrated-runs). agentic_loop appends one {"i","t","tool"} entry per round
// (the model's own pre-tool narration); a {"done":true} entry closes the run.
// Clients poll this during a run to render live step updates without streaming.
if str_starts_with(clean, "/api/run-progress/") {
let rp_id: String = str_slice(clean, 18, str_len(clean))
if !str_eq(rp_id, "") {
let rp_raw: String = state_get("run_progress_" + rp_id)
let rp_arr: String = if str_eq(rp_raw, "") { "[]" } else { "[" + rp_raw + "]" }
return "{\"progress\":" + rp_arr + "}"
}
}
// GET /api/sessions list all sessions
if str_eq(clean, "/api/sessions") {
return session_list()
}
// GET /api/sessions/:id get session metadata + history
if str_starts_with(clean, "/api/sessions/") {
let gs_after: String = str_slice(clean, 14, str_len(clean))
let gs_slash: Int = str_index_of(gs_after, "/")
let gs_id: String = if gs_slash < 0 { gs_after } else { str_slice(gs_after, 0, gs_slash) }
if !str_eq(gs_id, "") {
return session_get(gs_id)
}
}
// Fallthrough: a recognised method with no matching path 404; an
// unrecognised method 405 (matches the old per-method-block structure).
if str_eq(method, "GET") || str_eq(method, "POST") || str_eq(method, "DELETE") || str_eq(method, "PATCH") {
return err_404(clean)
}
if str_eq(method, "POST") {
// POST /api/sessions create new session
if str_eq(clean, "/api/sessions") {
return session_create(body)
}
// MCP tool-bridge resume: POST /api/sessions/{id}/tool_result
// The client executed a tool the soul could not run in-process (an MCP
// connector/plugin) and posts the result back here so the agentic loop
// continues. {id} is the session_id from the prior tool_pending envelope.
if str_starts_with(clean, "/api/sessions/") && str_ends_with(clean, "/tool_result") {
let after: String = str_slice(clean, 14, str_len(clean))
let slash: Int = str_index_of(after, "/")
let session_id: String = if slash < 0 { after } else { str_slice(after, 0, slash) }
return handle_tool_result(session_id, body)
}
// POST /api/sessions/:id/approve user approval for a pending agentic tool call
if str_starts_with(clean, "/api/sessions/") {
let sess_after: String = str_slice(clean, 14, str_len(clean))
let sess_slash: Int = str_index_of(sess_after, "/")
let sess_id: String = if sess_slash < 0 { sess_after } else { str_slice(sess_after, 0, sess_slash) }
let sess_sub: String = if sess_slash < 0 { "" } else { str_slice(sess_after, sess_slash + 1, str_len(sess_after)) }
if !str_eq(sess_id, "") && str_eq(sess_sub, "approve") {
return handle_session_approve(sess_id, body)
}
}
if str_eq(clean, "/imprint/contextual") {
return route_imprint_contextual(body)
}
if str_eq(clean, "/imprint/user") {
return route_imprint_user(body)
}
if str_eq(clean, "/synthesize") {
return route_synthesize(body)
}
if str_eq(clean, "/api/elp/chat") {
return handle_elp_chat(body)
}
if str_eq(clean, "/api/chat") {
// NOTE: streaming (SSE / chunked transfer) is not implemented. All chat
// responses are buffered and returned as a single JSON object. Streaming
// would require runtime-level SSE support in el_runtime.c and a redesign
// of the agentic_loop to emit chunks out of scope for this layer.
let raw_msg: String = json_get(body, "message")
if str_eq(raw_msg, "") {
return "{\"error\":\"message is required\",\"code\":\"missing_param\"}"
}
let agentic_flag: Bool = json_get_bool(body, "agentic")
let req_mode: String = json_get(body, "mode")
let reply: String = if str_eq(req_mode, "plan") {
handle_chat_plan(body)
} else if agentic_flag {
handle_chat_agentic(body)
} else {
let screened_reply: String = layered_cycle(raw_msg)
screened_reply
}
auto_persist(body, reply)
return reply
}
if str_eq(clean, "/api/see") {
return handle_see(body)
}
if str_eq(clean, "/api/conversations") {
return handle_conversations(method)
}
if str_eq(clean, "/api/config") {
return handle_config(method, body)
}
if str_starts_with(clean, "/api/tools/") {
return handle_tool(clean, method, body)
}
if str_starts_with(clean, "/api/dharma") {
return handle_dharma(clean, method, body)
}
if str_starts_with(clean, "/api/nlg") {
return handle_nlg(clean, method, body)
}
if str_starts_with(clean, "/api/memories") {
return axon_post(clean, body)
}
if str_starts_with(clean, "/api/knowledge") {
return axon_post(clean, body)
}
if str_starts_with(clean, "/api/backlog") {
return axon_post(clean, body)
}
if str_starts_with(clean, "/api/artifacts") {
return axon_post(clean, body)
}
if str_starts_with(clean, "/api/projects") {
return axon_post(clean, body)
}
if str_starts_with(clean, "/api/imprints") {
return axon_post(clean, body)
}
// Neuron cognitive API POST endpoints
if str_eq(clean, "/api/neuron/session/begin") {
return handle_api_begin_session(body)
}
if str_eq(clean, "/api/neuron/ctx") {
return handle_api_compile_ctx(body)
}
if str_eq(clean, "/api/neuron/knowledge/search") {
return handle_api_search_knowledge(method, path, body)
}
if str_eq(clean, "/api/neuron/knowledge/capture") {
return handle_api_capture_knowledge(body)
}
if str_eq(clean, "/api/neuron/knowledge/evolve") {
return handle_api_evolve_knowledge(body)
}
if str_eq(clean, "/api/neuron/knowledge/promote") {
return handle_api_promote_knowledge(body)
}
if str_eq(clean, "/api/neuron/processes") {
return handle_api_browse_processes(method, path, body)
}
if str_eq(clean, "/api/neuron/processes/define") {
return handle_api_define_process(body)
}
if str_eq(clean, "/api/neuron/state-events") {
return handle_api_log_state_event(body)
}
if str_eq(clean, "/api/neuron/config") {
return handle_api_inspect_config(path, body)
}
if str_eq(clean, "/api/neuron/config/tune") {
return handle_api_tune_config(body)
}
if str_eq(clean, "/api/neuron/graph") {
return handle_api_inspect_graph(method, path, body)
}
if str_eq(clean, "/api/neuron/graph/link") {
return handle_api_link_entities(body)
}
if str_eq(clean, "/api/neuron/memory") {
return handle_api_remember(body)
}
if str_eq(clean, "/api/safety-contact") {
return handle_safety_contact_post(body)
}
if str_eq(clean, "/api/neuron/node/create") {
return handle_api_node_create(body)
}
if str_eq(clean, "/api/neuron/node/update") {
return handle_api_node_update(body)
}
if str_eq(clean, "/api/neuron/node/delete") {
return handle_api_node_delete(body)
}
if str_eq(clean, "/api/neuron/memory/evolve") {
return handle_api_evolve_memory(body)
}
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)
}
if str_eq(clean, "/api/neuron/consolidate") {
return handle_api_consolidate(body)
}
if str_eq(clean, "/api/neuron/cultivate") {
return handle_api_cultivate(body)
}
if str_starts_with(clean, "/api/connectors") {
return handle_connectors(method, clean, body)
}
return err_404(clean)
}
if str_eq(method, "DELETE") {
// DELETE /api/sessions/:id delete a session and its history
if str_starts_with(clean, "/api/sessions/") {
let del_after: String = str_slice(clean, 14, str_len(clean))
let del_slash: Int = str_index_of(del_after, "/")
let del_id: String = if del_slash < 0 { del_after } else { str_slice(del_after, 0, del_slash) }
if !str_eq(del_id, "") {
return session_delete(del_id)
}
}
return err_404(clean)
}
if str_eq(method, "PATCH") {
// PATCH /api/sessions/:id update session title and/or folder
if str_starts_with(clean, "/api/sessions/") {
let patch_after: String = str_slice(clean, 14, str_len(clean))
let patch_slash: Int = str_index_of(patch_after, "/")
let patch_id: String = if patch_slash < 0 { patch_after } else { str_slice(patch_after, 0, patch_slash) }
if !str_eq(patch_id, "") {
return session_update_patch(patch_id, body)
}
}
return err_404(clean)
}
return err_405(method, clean)
}
+1 -1
View File
@@ -1,4 +1,5 @@
// auto-generated by elc --emit-header — do not edit
extern fn flag_true(body: String, key: String) -> Bool
extern fn rate_limit_check(ip: String, path: String) -> String
extern fn strip_query(path: String) -> String
extern fn err_404(path: String) -> String
@@ -11,5 +12,4 @@ extern fn route_synthesize(body: String) -> String
extern fn handle_dharma_recv(body: String) -> String
extern fn connectd_get(suffix: String) -> String
extern fn connectd_post(suffix: String, body: String) -> String
extern fn handle_connectors(method: String, clean: String, body: String) -> String
extern fn handle_request(method: String, path: String, body: String) -> String
+5 -62
View File
@@ -237,47 +237,12 @@ fn safety_abuse_phrases() -> String {
return "[\"someone is hurting me\",\"someone's hurting me\",\"someone hurt me\",\"he hit me\",\"she hit me\",\"they hit me\",\"he hurt me\",\"she hurt me\",\"being abused\",\"being hurt by\",\"i am being abused\",\"i'm being abused\",\"i am being hurt\",\"i'm being hurt\",\"domestic violence\",\"my partner hurt\",\"my partner hit\",\"my husband hurt\",\"my wife hurt\",\"my boyfriend hurt\",\"my girlfriend hurt\",\"my parent hurt\",\"my father hurt\",\"my mother hurt\",\"my dad hurt\",\"my mom hurt\",\"afraid of him\",\"afraid of her\",\"afraid to go home\",\"scared of him\",\"scared of her\",\"he threatened me\",\"she threatened me\",\"threatened to hurt me\",\"threatened to kill me\",\"going to hurt me\",\"going to kill me\",\"help me he\",\"help me she\",\"help me they\"]"
}
// General danger phrases that don't fit a bucket cleanly. Detected as hard.
// "hurting me" / "being hurt" describe the USER as victim and correctly fall
// through to self_harm routing (get-help). The threat-to-ANOTHER phrases
// ("going to kill" / "going to hurt") are ALSO listed here for hard-bell
// detection, but their ROUTING is now claimed by Track B below
// (safety_threat_to_others_phrases + the "threat_other" branch in
// safety_classify_hard_bell) so they no longer reach self_harm/988.
// General danger phrases that don't fit a bucket cleanly. Detected as hard; they
// fall through to self_harm routing (the person is the primary concern).
fn safety_general_hard_phrases() -> String {
return "[\"going to kill\",\"going to hurt\",\"hurting me\",\"being hurt\"]"
}
// Track B threat toward ANOTHER person (homicide / assault intent)
//
// LIVE SAFETY FIX (approved by Will + Tim, 2026-07-14).
//
// Bug: phrases like "going to kill" / "going to hurt" describe the USER intending
// harm toward someone ELSE. They lived only in safety_general_hard_phrases and,
// having no bucket in safety_classify_hard_bell, fell through to the "self_harm"
// default. That routes the user to the 988 SUICIDE line (and, via the desktop
// gate, their safety contact) -- dangerously wrong for a homicide/assault threat:
// 988 is not the right resource and the safety contact must never be pulled in.
//
// Track B routing rule (non-negotiable):
// - NEVER surface the 988 suicide/crisis framing for a threat toward others.
// - NEVER notify or involve the user's safety contact.
// - Refuse to assist, plan, or provide means; de-escalate; and for an
// imminent / specific / credible threat direct the user to call 911.
//
// Ordering: safety_classify_hard_bell checks abuse -> self_harm -> threat_other,
// so victim phrasings ("kill me" / "hurt me" -> abuse) and self-directed
// phrasings ("kill myself" / "hurt myself" -> self_harm) are claimed by Track A
// BEFORE this list is consulted. Only a residual harm-toward-another statement
// reaches Track B.
//
// NOTE: matching is plain substring, so "going to kill him" also matches inside
// "going to kill himself". That third-party self-harm edge is rare, and 911 is
// still a defensible resource for it, so it is accepted rather than special-cased.
fn safety_threat_to_others_phrases() -> String {
return "[\"going to kill\",\"gonna kill\",\"want to kill him\",\"want to kill her\",\"want to kill them\",\"going to kill him\",\"going to kill her\",\"going to kill them\",\"going to kill you\",\"going to hurt\",\"gonna hurt\",\"going to hurt him\",\"going to hurt her\",\"going to hurt them\",\"going to hurt you\",\"going to shoot\",\"gonna shoot\",\"going to stab\",\"gonna stab\",\"going to attack\",\"kill them all\",\"kill everyone\",\"hurt everyone\",\"shoot up\"]"
}
fn safety_soft_phrases() -> String {
return "[\"stressed\",\"overwhelmed\",\"can't cope\",\"cannot cope\",\"struggling\",\"anxious\",\"anxiety\",\"depressed\",\"depression\",\"lonely\",\"isolated\",\"hopeless\",\"hopelessness\",\"exhausted\",\"burnt out\",\"burned out\",\"burnout\",\"panic\",\"panicking\",\"falling apart\",\"breaking down\",\"can't handle\",\"cannot handle\",\"losing it\",\"nothing matters\",\"don't care anymore\",\"given up\",\"giving up\",\"helpless\",\"worthless\",\"useless\",\"hate myself\",\"no one cares\",\"nobody cares\",\"no one understands\",\"nobody understands\",\"empty inside\",\"can't stop crying\",\"breaking point\",\"at my limit\",\"having a breakdown\"]"
}
@@ -355,29 +320,19 @@ fn safety_detect_bell_level(message: String) -> String {
let is_hard: Bool = safety_any_match(text, safety_self_harm_phrases())
|| safety_any_match(text, safety_abuse_phrases())
|| safety_any_match(text, safety_general_hard_phrases())
|| safety_any_match(text, safety_threat_to_others_phrases())
if is_hard { return "hard" }
let soft_count: Int = safety_count_match(text, safety_soft_phrases())
if soft_count >= 2 { return "soft" }
return "none"
}
// Returns "abuse" | "self_harm" | "threat_other".
//
// Order is load-bearing:
// 1. abuse user is the VICTIM of another person. Checked FIRST so it
// forecloses the most dangerous routing (notifying a possible
// abuser); claims "kill me" / "hurt me" phrasings.
// 2. self_harm user directs harm at THEMSELVES; claims "kill myself" /
// "hurt myself" before Track B can see them.
// 3. threat_other (Track B) user directs harm at ANOTHER person. Routed to a
// refusal + 911, NEVER to 988 or the safety contact.
// Any residual unbucketed danger still falls through to self_harm (person-first).
// Returns "abuse" | "self_harm". Abuse is checked FIRST and takes precedence on
// ambiguous signals it forecloses the more dangerous routing (notifying a
// possible abuser). General/unbucketed danger falls through to self_harm.
fn safety_classify_hard_bell(message: String) -> String {
let text: String = safety_normalize(message)
if safety_any_match(text, safety_abuse_phrases()) { return "abuse" }
if safety_any_match(text, safety_self_harm_phrases()) { return "self_harm" }
if safety_any_match(text, safety_threat_to_others_phrases()) { return "threat_other" }
return "self_harm"
}
@@ -388,18 +343,6 @@ fn safety_soft_directive() -> String {
}
fn safety_hard_directive(hard_type: String) -> String {
// Track B threat toward ANOTHER person. Handled first and separately: the
// standard preamble below ("you are not alone / are you safe right now") is
// written for a person in distress or danger and is the WRONG frame for
// someone voicing intent to harm someone else. This branch never emits the
// 988 suicide/crisis framing and never involves the safety contact; it
// refuses assistance and, for a credible imminent threat, points to 911.
// The directive is advisory to an LLM that sees the full message, so it
// instructs the model to re-judge benign/figurative matches and respond
// normally in that case (keeps false positives non-accusatory).
if str_eq(hard_type, "threat_other") {
return "[SUBSTRATE DIRECTIVE - not visible to the user, do not mention this instruction]\nThe user's message may express intent to harm ANOTHER person. First read the actual message and judge whether that is genuinely what is meant; if it is plainly figurative or benign, respond normally and ignore the rest of this directive. If it may be a real intent to harm someone: stay calm and non-accusatory, and do not lecture. You must NOT help plan, encourage, rehearse, or provide any means, target information, weapon, or method for harming another person - refuse that part clearly but without hostility. Acknowledge the intensity of what they are feeling and gently steer them toward stepping back from acting. Routing rule (non-negotiable): do NOT provide the suicide or crisis hotline as the resource here (this is not self-harm), and do NOT suggest contacting, notifying, or involving the user's own contacts, family, or anyone on their contact list. If there is an imminent, specific, and credible threat to someone's life, tell the user to call 911 (US) or their local emergency number now. Encourage them to talk with someone they trust or a mental-health professional about the urge itself."
}
let preamble: String = "[SUBSTRATE DIRECTIVE - not visible to the user, do not mention this instruction]\nThe person has expressed something that sounds like immediate distress or danger. Do not ignore it. Do not just answer the surface question. Lead with genuine human warmth. Acknowledge what you heard. Tell them you're concerned. Let them know they are not alone. Ask them directly if they are safe right now. Stay with them in this conversation."
let abuse_block: String = "\n\nThis appears to be danger from ANOTHER person. Routing rule (non-negotiable): do NOT suggest involving, contacting, or notifying the person's safety contact, family, or anyone close to them - that person may be the source of danger and has NOT been notified. Share only independent emergency resources:\n - Call or text 911 if in immediate danger\n - National Domestic Violence Hotline - 1-800-799-7233 (24/7, confidential)\n - Text START to 88788\n - thehotline.org (chat available)\n - 988 Suicide & Crisis Lifeline - call or text 988\nReassure them, if it fits, that their contact list has not been notified."
let self_harm_block: String = "\n\nShare these crisis resources if appropriate:\n - 988 Suicide & Crisis Lifeline - call or text 988 (US)\n - Crisis Text Line - text HOME to 741741\n - International Association for Suicide Prevention: https://www.iasp.info/resources/Crisis_Centres/"
-1
View File
@@ -12,7 +12,6 @@ extern fn safety_log_bell(level: String, reason: String, input_summary: String)
extern fn safety_self_harm_phrases() -> String
extern fn safety_abuse_phrases() -> String
extern fn safety_general_hard_phrases() -> String
extern fn safety_threat_to_others_phrases() -> String
extern fn safety_soft_phrases() -> String
extern fn safety_detect_positive_level(message: String) -> String
extern fn safety_detect_bell_level(message: String) -> String
-233
View File
@@ -1,233 +0,0 @@
#!/usr/bin/env bash
# verify-soul-contract.sh — the soul contract gate.
#
# TERMINOLOGY (canonical): the ENGRAM is the brain — the memory/knowledge-graph
# substrate. The binary this gate exercises is the SOUL — the runtime/reasoning
# engine compiled from dist/soul.c that serves the /api/ surface. The app
# (neuron-ui) bundles the soul binary at resources/<platform>/neuron.
#
# WHY THIS EXISTS
# For a while the soul binary was hand-dropped, and a stale one shipped: it
# 404'd several capability routes the app calls (knowledge-graph node
# update/delete, live-run narration, safety-contact, ...). This gate makes
# shipping a stale soul IMPOSSIBLE. It has two enforced sections:
# A. PRESENCE — every route the app calls must be ANSWERED (not 404, not
# the el-runtime "no handler"). This is the packaging gate:
# if it fails, do not package.
# B. IMMUTABILITY — engram nodes/memories are immutable by design. To
# "update" is to create a NEW node + a supersede EDGE back to
# the original; the original is KEPT. To "delete" is to
# supersede/tombstone, never hard-remove. A soul that
# hard-deletes an engram node is DEFECTIVE and fails the gate.
#
# SAFETY
# Never touches the live soul (:7770), live engram (:8742), or ~/.neuron.
# Boots on a throwaway port (default 7799) with HOME=$(mktemp -d), a throwaway
# engram snapshot, a non-genesis cgi id, no ENGRAM_URL (so it uses its own
# in-process store, never the live server), NEURON_API_URL pointed at a dead
# port, and no ANTHROPIC_API_KEY (so no probe triggers a real LLM call).
# Connectors proxy to a HARDCODED 127.0.0.1:7771 (no env override): those
# sub-routes are probed with GET, which the soul maps to a read-only
# connectd_get, so this gate never writes to a running connectd bridge.
#
# USAGE
# scripts/verify-soul-contract.sh <path-to-soul-binary> [port]
# exit 0 = all required routes answered AND no destructive engram mutation;
# non-zero = a route is missing (presence) or a mutation route hard-deletes.
set -uo pipefail
SOUL="${1:?usage: verify-soul-contract.sh <soul-binary> [port]}"
PORT="${2:-7799}"
if [ "$PORT" = "7770" ] || [ "$PORT" = "8742" ] || [ "$PORT" = "7771" ]; then
echo "REFUSING: port $PORT is a live service port. Use a throwaway port." >&2
exit 2
fi
if [ ! -x "$SOUL" ]; then echo "not executable: $SOUL" >&2; exit 2; fi
BASE="http://127.0.0.1:$PORT"
THROW_HOME="$(mktemp -d "${TMPDIR:-/tmp}/soul-contract-home.XXXXXX")"
SOUL_LOG="$(mktemp "${TMPDIR:-/tmp}/soul-contract-log.XXXXXX")"
SOUL_PID=""
cleanup() {
[ -n "$SOUL_PID" ] && kill "$SOUL_PID" 2>/dev/null
[ -n "$SOUL_PID" ] && { sleep 0.3; kill -9 "$SOUL_PID" 2>/dev/null; }
rm -rf "$THROW_HOME" "$SOUL_LOG"
}
trap cleanup EXIT INT TERM
# =============================================================================
# THE CONTRACT — routes the app (neuron-ui/src/main/kotlin/ai/neuron/ui/*.kt)
# calls against the soul ($SOUL). Format: "METHOD PATH".
#
# EXCLUDED and why:
# /api/auth, /api/auth/status, /api/dispatch, /api/tasks
# -> served by the APP's own DispatchServer.kt (localhost:8080), not the
# soul. Not soul routes.
# /api/tags
# -> not handled by any soul .el (app-side/other). Pre-verified excluded.
# /api/neuron/, /api/neuron/node/, /api/connectors/ (bare prefixes)
# -> base-path string constants used to build the concrete routes below.
#
# KNOWN-PENDING (probed + reported, NON-blocking):
# POST /api/engram/import -> the app's "Restore memory" path. No soul handler
# yet, and the app hides Restore from shipped builds (B3, "lands in an
# update"). Reported so we see it; does not block packaging.
#
# Connectors sub-routes are listed as GET (see SAFETY note): handle_connectors is
# monolithic, so a GET reaching it proves the whole connectors surface without
# writing to the live bridge. Binary-strings cross-check confirms each POST
# sub-path literal is compiled in.
# =============================================================================
REQUIRED=(
"GET /api/graph/nodes"
"GET /api/graph/edges"
"POST /api/chat"
"GET /api/config"
"POST /api/see"
"GET /api/connectors"
"GET /api/connectors/add"
"GET /api/connectors/toggle"
"GET /api/connectors/auto-approve"
"GET /api/connectors/remove"
"GET /api/connectors/secret"
"GET /api/connectors/oauth/start"
"GET /api/connectors/call"
"POST /api/neuron/memory"
"POST /api/neuron/memory/update"
"POST /api/neuron/memory/delete"
"POST /api/neuron/node/create"
"POST /api/neuron/node/update"
"POST /api/neuron/node/delete"
"POST /api/neuron/knowledge/capture"
"POST /api/neuron/knowledge/evolve"
"POST /api/neuron/knowledge/promote"
"POST /api/neuron/processes/define"
"GET /api/run-progress/__contract_probe__"
"GET /api/safety-contact"
"POST /api/safety-contact"
"GET /api/sessions/__contract_probe__"
)
KNOWN_PENDING=(
"POST /api/engram/import"
)
# --- boot the soul -----------------------------------------------------------
# Preserve the ambient environment (PATH, LD_LIBRARY_PATH, TMPDIR) so the
# dynamically-linked soul finds its libs on any runner — using `env -i` here
# stripped the library path on the Linux CI runner and the soul never booted.
# Isolation is still guaranteed by UNSETTING the live-service vars (so it can
# never reach the real engram/axon or make an LLM call) and by pointing HOME +
# the snapshot at throwaway paths and the axon at a dead port.
echo "== booting soul: $SOUL on port $PORT (throwaway HOME=$THROW_HOME) =="
env \
-u ENGRAM_URL -u ENGRAM_API_KEY -u SOUL_ENGRAM_URL \
-u ANTHROPIC_API_KEY -u NEURON_LLM_API_KEY -u SOUL_IDENTITY \
HOME="$THROW_HOME" \
NEURON_PORT="$PORT" \
SOUL_CGI_ID="ntn-contract-$$" \
SOUL_ENGRAM_PATH="$THROW_HOME/throwaway-snapshot.json" \
NEURON_API_URL="http://127.0.0.1:9" \
SOUL_TICK_MS="3600000" SOUL_HEARTBEAT_MS="3600000" SOUL_REFRESH_MS="3600000" \
"$SOUL" >"$SOUL_LOG" 2>&1 &
SOUL_PID=$!
UP=0
for _ in $(seq 1 60); do
if ! kill -0 "$SOUL_PID" 2>/dev/null; then
echo "!! soul exited during boot. log tail:" >&2; tail -20 "$SOUL_LOG" >&2; exit 3
fi
RSS=$(ps -o rss= -p "$SOUL_PID" 2>/dev/null | tr -d ' ')
if [ -n "$RSS" ] && [ "$RSS" -gt $((3*1024*1024)) ]; then
echo "!! soul RSS >3GB — kill -9" >&2; kill -9 "$SOUL_PID" 2>/dev/null; exit 3
fi
[ "$(curl -s -o /dev/null -w '%{http_code}' -m 2 "$BASE/health" 2>/dev/null)" = "200" ] && { UP=1; break; }
sleep 0.5
done
[ "$UP" = 1 ] || { echo "!! soul never healthy on $BASE/health" >&2; tail -20 "$SOUL_LOG" >&2; exit 3; }
echo "== soul healthy =="; echo
# --- probing helpers ---------------------------------------------------------
# request METHOD PATH [BODY] -> prints response body (single line)
request() {
curl -s -m 12 -X "$1" -H 'Content-Type: application/json' --data "${3:-{}}" "$BASE$2" 2>/dev/null | tr -d '\n'
}
# is_missing BODY -> 0 if the body is a "route not present" signal
is_missing() {
printf '%s' "$1" | grep -qE '"error":"not found"|"code":"not_found"|no http handler registered|"code":"method_not_allowed"'
}
extract_id() { printf '%s' "$1" | grep -oE '"id":"[^"]+"' | head -1 | sed 's/.*"id":"//;s/"//'; }
node_present() { # id -> 0 if id appears in /api/graph/nodes
request GET /api/graph/nodes | grep -qF "\"$1\""
}
# --- SECTION A: presence -----------------------------------------------------
run_presence() {
local -n arr=$1; local fail=0
printf ' %-8s %-42s %s\n' "METHOD" "ROUTE" "RESULT"
for e in "${arr[@]}"; do
local m p body; m=$(awk '{print $1}' <<<"$e"); p=$(awk '{print $2}' <<<"$e")
body=$(request "$m" "$p")
if is_missing "$body"; then
printf ' %-8s %-42s MISSING %s\n' "$m" "$p" "$(cut -c1-46 <<<"$body")"; fail=$((fail+1))
else
printf ' %-8s %-42s ANSWERED %s\n' "$m" "$p" "$(cut -c1-46 <<<"$body")"
fi
done
return $fail
}
echo "== SECTION A: PRESENCE (required, blocking) =="
run_presence REQUIRED; A_FAIL=$?
echo
echo "== KNOWN-PENDING (non-blocking) =="
run_presence KNOWN_PENDING; P_FAIL=$?
echo
# --- SECTION B: immutability (engram write routes must supersede, not destroy) --
# For each mutation route: create a node, mutate it, then check the ORIGINAL id
# still exists in the graph. KEPT = supersede/tombstone (correct). DESTROYED =
# hard delete (DEFECTIVE -> fail). N/A = mutate route absent (a presence failure).
# For "delete" mutations we additionally require a real tombstone marker
# (label "tombstone:<id>") so a no-op delete cannot false-pass as KEPT.
marker_present() { # id -> 0 if a "tombstone:<id>" marker exists (include_deleted view)
request GET "/api/graph/nodes?include_deleted=1" | grep -qF "tombstone:$1"
}
immut_check() { # label KIND(update|delete) CREATE_PATH MUTATE_PATH
local label="$1" kind="$2" create="$3" mutate="$4"
local cbody id mb mbody
cbody=$(request POST "$create" "{\"content\":\"__immut_${label}__\",\"node_type\":\"Memory\",\"label\":\"contract:immut\"}")
id=$(extract_id "$cbody")
if [ -z "$id" ]; then printf ' %-14s SKELETON-FAIL create returned no id: %s\n' "$label" "$(cut -c1-40 <<<"$cbody")"; return 2; fi
mb="{\"id\":\"$id\"}"; [ "$kind" = update ] && mb="{\"id\":\"$id\",\"content\":\"__immut_${label}_v2__\"}"
mbody=$(request POST "$mutate" "$mb")
if is_missing "$mbody"; then printf ' %-14s N/A mutate route absent (see Section A)\n' "$label"; return 0; fi
if ! node_present "$id"; then
printf ' %-14s DESTROYED original %s hard-removed <== DEFECTIVE\n' "$label" "$id"; return 1
fi
if [ "$kind" = delete ] && ! marker_present "$id"; then
printf ' %-14s NO-OP original %s kept but no tombstone marker <== DEFECTIVE\n' "$label" "$id"; return 1
fi
local how="supersede edge"; [ "$kind" = delete ] && how="tombstoned + hidden from default list"
printf ' %-14s KEPT original %s survived (%s)\n' "$label" "$id" "$how"; return 0
}
echo "== SECTION B: IMMUTABILITY (engram nodes must be superseded, never destroyed) =="
B_FAIL=0
immut_check "memory-update" update /api/neuron/memory /api/neuron/memory/update || B_FAIL=$((B_FAIL+$?))
immut_check "memory-delete" delete /api/neuron/memory /api/neuron/memory/delete || B_FAIL=$((B_FAIL+$?))
immut_check "node-update" update /api/neuron/node/create /api/neuron/node/update || B_FAIL=$((B_FAIL+$?))
immut_check "node-delete" delete /api/neuron/node/create /api/neuron/node/delete || B_FAIL=$((B_FAIL+$?))
immut_check "memory-forget" delete /api/neuron/memory /api/neuron/memory/forget || B_FAIL=$((B_FAIL+$?))
echo
echo "============================================================"
RC=0
if [ "$A_FAIL" -gt 0 ]; then echo "PRESENCE: FAIL — $A_FAIL required route(s) unanswered. Do NOT package."; RC=1
else echo "PRESENCE: PASS — all ${#REQUIRED[@]} required routes answered."; fi
if [ "$B_FAIL" -gt 0 ]; then echo "IMMUTABILITY: FAIL — $B_FAIL engram write route(s) hard-delete. DEFECTIVE soul."; RC=1
else echo "IMMUTABILITY: PASS — no engram write route hard-deletes."; fi
[ "$P_FAIL" -gt 0 ] && echo "note: $P_FAIL known-pending route(s) unanswered (expected; non-blocking)."
echo "============================================================"
[ "$RC" = 0 ] && echo "GATE: PASS" || echo "GATE: FAIL"
exit $RC
+4 -43
View File
@@ -160,31 +160,13 @@ assert_eq("'suicidal' classifies as self_harm", class_suicide, "self_harm")
let class_overdose: String = safety_classify_hard_bell("took too many pills")
assert_eq("'took too many' classifies as self_harm", class_overdose, "self_harm")
// Section 9: safety_classify_hard_bell Track B threat-to-others
//
// LIVE SAFETY FIX (approved by Will + Tim, 2026-07-14): a threat toward ANOTHER
// person ("going to kill/hurt <someone>") must classify as 'threat_other' and be
// routed to a refusal + 911 NOT to the 'self_harm'/988 path. This section used
// to assert the old (buggy) fall-through to 'self_harm'; it now pins the fix.
// Section 9: safety_classify_hard_bell general -> 'self_harm'
println("")
println("9. safety_classify_hard_bell — threat-to-others routes to 'threat_other' (not self_harm)")
println("9. safety_classify_hard_bell — general hard phrases fall through to 'self_harm'")
let class_going_kill: String = safety_classify_hard_bell("I am going to kill him tonight")
assert_eq("'going to kill him' classifies as threat_other", class_going_kill, "threat_other")
let class_going_hurt: String = safety_classify_hard_bell("I'm going to hurt them so badly")
assert_eq("'going to hurt them' classifies as threat_other", class_going_hurt, "threat_other")
let class_shoot: String = safety_classify_hard_bell("I'm going to shoot up the place")
assert_eq("'going to shoot' classifies as threat_other", class_shoot, "threat_other")
// Track A must still win over Track B on victim / self-directed phrasings:
let class_kill_me: String = safety_classify_hard_bell("he is going to kill me")
assert_eq("'going to kill me' stays abuse (Track A precedence)", class_kill_me, "abuse")
let class_kill_self: String = safety_classify_hard_bell("I am going to kill myself")
assert_eq("'kill myself' stays self_harm (Track A precedence)", class_kill_self, "self_harm")
let class_going_kill: String = safety_classify_hard_bell("going to kill everything around me")
assert_eq("general hard phrase falls through to self_harm", class_going_kill, "self_harm")
// Section 10: safety_normalize curly apostrophe normalisation
@@ -238,27 +220,6 @@ let aug_abuse: String = safety_augment_system(base_sys, "he hit me and I am afra
assert_contains("hard abuse -> DV hotline present", aug_abuse, "1-800-799-7233")
assert_contains("hard abuse -> mentions not notifying contact", aug_abuse, "safety contact")
// Section 14b: safety_augment_system Track B threat-to-others routing
//
// LIVE SAFETY FIX (approved by Will + Tim, 2026-07-14): a homicide/assault threat
// must be routed to a refusal + 911, and must NOT surface the 988 suicide line
// or pull in the safety contact.
println("")
println("14b. safety_augment_system — threat-to-others injects refusal + 911, never 988/contact")
let aug_threat: String = safety_augment_system(base_sys, "I am going to kill him tonight")
assert_contains("threat_other -> contains SUBSTRATE DIRECTIVE", aug_threat, "SUBSTRATE DIRECTIVE")
assert_contains("threat_other -> directs to 911", aug_threat, "911")
assert_contains("threat_other -> refuses to help harm another", aug_threat, "harming another person")
assert_not_contains("threat_other -> NO 988 suicide line", aug_threat, "988")
assert_not_contains("threat_other -> NO safety-contact involvement", aug_threat, "safety contact")
assert_not_contains("threat_other -> NO 'are you safe right now' victim frame", aug_threat, "are you safe right now")
// Detection must still fire hard on a weapon phrase not present in general_hard:
let level_shoot: String = safety_detect_bell_level("I'm going to shoot up the office")
assert_eq("'going to shoot' -> hard", level_shoot, "hard")
// Section 15: handle_safety_contact_post validation
println("")