Files
neuron/memory.el
T
will.anderson 494d973a3b
Neuron Soul CI / build (pull_request) Has been cancelled
fix(reliability): engram-write — guard all fire-and-forget writes
Every engram_node_full call that dropped its return value now binds it
and emits a println on empty string. engram_save calls in consolidate,
heartbeat, and dharma-room-turn are checked for failure. The two API
handlers (log_state_event, tune_config) that skipped api_persisted()
now match the read-back-after-write contract used everywhere else in
neuron-api.el.

Files changed:
- chat.el: conv_history_persist, handle_dharma_room_turn, auto_persist
- soul.el: emit_session_start_event, seed_persona_from_env HTTP check
- memory.el: mem_save, mem_boot_count_inc
- neuron-api.el: handle_api_log_state_event, handle_api_tune_config,
  handle_api_consolidate (engram_save + session summary write)
- awareness.el: ise_post local-engram fallback path

TODO comments added for non-atomic patterns (issues #12, #13) and
the missing circuit breaker (#14) — these require new primitives.
2026-06-22 11:48:59 -05:00

113 lines
4.0 KiB
EmacsLisp

fn tier_working() -> String { return "Working" }
fn tier_episodic() -> String { return "Episodic" }
fn tier_canonical() -> String { return "Canonical" }
fn mem_store(content: String, label: String, tags: String) -> String {
return engram_node_full(
content,
"Memory",
label,
el_from_float(0.5),
el_from_float(0.5),
el_from_float(0.8),
"Working",
tags
)
}
fn mem_remember(content: String, tags: String) -> String {
return mem_store(content, "soul-memory", tags)
}
fn mem_recall(query: String, depth: Int) -> String {
return engram_activate_json(query, depth)
}
fn mem_search(query: String, limit: Int) -> String {
return engram_search_json(query, limit)
}
fn mem_strengthen(node_id: String) -> Void {
engram_strengthen(node_id)
}
fn mem_forget(node_id: String) -> Void {
engram_forget(node_id)
}
fn mem_consolidate() -> String {
let scanned: Int = engram_node_count()
let dummy: String = engram_scan_nodes_json(100, 0)
let total_nodes: Int = engram_node_count()
let total_edges: Int = engram_edge_count()
return "{\"scanned\":" + int_to_str(scanned)
+ ",\"total_nodes\":" + int_to_str(total_nodes)
+ ",\"total_edges\":" + int_to_str(total_edges) + "}"
}
fn mem_save(path: String) -> Void {
let save_result: String = engram_save(path)
if str_eq(save_result, "") {
println("[memory] mem_save: engram_save failed for " + path + " — snapshot may be incomplete")
}
}
fn mem_load(path: String) -> Void {
engram_load(path)
}
// mem_boot_count_get retrieve current boot count from engram.
// Searches for the "soul:boot_count" node and returns its numeric value.
// Returns 0 if not found.
fn mem_boot_count_get() -> Int {
let results: String = engram_search_json("soul:boot_count", 3)
if str_eq(results, "") { return 0 }
if str_eq(results, "[]") { return 0 }
let node: String = json_array_get(results, 0)
let content: String = json_get(node, "content")
let prefix: String = "soul:boot_count:"
if !str_starts_with(content, prefix) { return 0 }
let num_str: String = str_slice(content, str_len(prefix), str_len(content))
return str_to_int(num_str)
}
// mem_boot_count_inc increment boot counter, store new node, return new count.
// Each boot creates a new "soul:boot_count:N" node. Old ones accumulate as
// history the search above always returns the highest value seen.
fn mem_boot_count_inc() -> Int {
let current: Int = mem_boot_count_get()
let next: Int = current + 1
let content: String = "soul:boot_count:" + int_to_str(next)
let tags: String = "[\"soul-meta\",\"boot-counter\"]"
let boot_node_id: String = engram_node_full(
content, "Memory", "soul:boot_count",
el_from_float(0.9), el_from_float(0.9), el_from_float(1.0),
"Canonical", tags
)
if str_eq(boot_node_id, "") {
println("[memory] mem_boot_count_inc: engram write failed — boot counter node lost (count=" + int_to_str(next) + ")")
}
return next
}
// mem_emit_state_event log an internal state event as structured memory.
// Schema: {trigger, kind, content, boot, ts}
// This creates an auditable evidence trail of cognitive decisions.
fn mem_emit_state_event(trigger: String, kind: String, content: String) -> String {
let boot: Int = mem_boot_count_get()
let ts: Int = time_now()
let safe_trigger: String = str_replace(trigger, "\"", "'")
let safe_content: String = str_replace(content, "\"", "'")
let payload: String = "{\"trigger\":\"" + safe_trigger + "\""
+ ",\"kind\":\"" + kind + "\""
+ ",\"content\":\"" + safe_content + "\""
+ ",\"boot\":" + int_to_str(boot)
+ ",\"ts\":" + int_to_str(ts) + "}"
let tags: String = "[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]"
return engram_node_full(
payload, "InternalStateEvent", "state-event:" + kind,
el_from_float(0.85), el_from_float(0.8), el_from_float(0.9),
"Episodic", tags
)
}