Compare commits

...

1 Commits

Author SHA1 Message Date
will.anderson c8d5f57de7 soul: engram-store wiring (soul client path) + elc-fold source fixes
- soul.el/memory.el: ENGRAM_STORE-gated store boot/checkpoint (client path) + fix a
  latent memory.el segfault (engram_save returns Int, was str_eq'd as a pointer).
- sessions.el/safety.el/chat.el: fix malformed El constructs that hung the elc fold
  (unterminated string literals, block-expression, undefined distill_transcript).
2026-08-12 14:13:35 -05:00
5 changed files with 51 additions and 8 deletions
+14 -2
View File
@@ -885,7 +885,7 @@ fn handle_chat(body: String) -> String {
// Cross-session affective context: on session start (no history yet), check engram
// for recent distress signals within 72h and prepend a care directive if found.
let affective_prefix: String = {
let affective_prefix: String = if true {
// Runs every turn. Uses correct BellEvent/PositiveEvent tags.
let aff_now_ts: Int = time_now()
let aff_cutoff: Int = aff_now_ts - 259200
@@ -940,7 +940,7 @@ fn handle_chat(body: String) -> String {
"[RECENT CONTEXT: User recently shared exciting or joyful news. Acknowledge and celebrate with them when relevant.]\n\n"
} else { "" }
}
}
} else { "" }
let ctx: String = engram_compile(activation_seed)
let system: String = affective_prefix + build_system_prompt(ctx, true)
@@ -2086,6 +2086,18 @@ fn handle_chat_as_soul(body: String) -> String {
// The soul's engram activates on the transcript content its own recall,
// not external injection. The system prompt is just identity.
//
// distill_transcript reduce a full room transcript to the salient tail that
// should drive engram activation. Minimal implementation: keep the last 4000
// characters (the recent exchange + trailing question). A full transcript still
// activates correctly; this just bounds the activation seed for long rooms.
fn distill_transcript(transcript: String) -> String {
let tlen: Int = str_len(transcript)
if tlen <= 4000 {
return transcript
}
return str_slice(transcript, tlen - 4000, tlen)
}
// After responding, the soul records what it said in its own engram.
// That is how it learns. Not from being told about the room.
fn handle_dharma_room_turn(body: String) -> String {
+16 -2
View File
@@ -109,8 +109,22 @@ fn mem_consolidate() -> String {
}
fn mem_save(path: String) -> Void {
let save_result: String = engram_save(path)
if str_eq(save_result, "") {
// ENGRAM_STORE on: the durable path is a checkpoint of the resident graph into
// the paged store (persists learned hebb + activation-formed edges via the WAL).
// The JSON engram_save stays available as an explicit export elsewhere; here the
// store is authoritative, so we checkpoint instead of writing the snapshot.
let store_flag: String = env("ENGRAM_STORE")
if str_eq(store_flag, "1") || str_eq(store_flag, "on") || str_eq(store_flag, "true") {
let ck: Int = engram_store_checkpoint()
if ck == 0 {
println("[memory] mem_save: engram_store_checkpoint returned 0 — checkpoint may have failed")
}
return
}
// engram_save returns Int 1/0 (ok/fail), not a String checking it as a
// String (str_eq(save_result, "")) dereferences a tagged int and segfaults.
let save_result: Int = engram_save(path)
if save_result == 0 {
println("[memory] mem_save: engram_save failed for " + path + " — snapshot may be incomplete")
}
}
+1 -1
View File
@@ -244,7 +244,7 @@ fn safety_general_hard_phrases() -> String {
}
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\""]"
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\"]"
}
// ISSUE 5 TODO: phrase lists are rebuilt from JSON literals on every call.
+2 -2
View File
@@ -503,10 +503,10 @@ fn session_hist_save(session_id: String, hist: String) -> Void {
let last_role: String = json_get(last_entry, "role")
let last_content: String = json_get(last_entry, "content")
let topic_snip: String = if str_len(last_content) > 200 { str_slice(last_content, 0, 200) } else { last_content }
let safe_topic: String = str_replace(topic_snip, """, "'")
let safe_topic: String = str_replace(topic_snip, "\"", "'")
let ts_now: String = int_to_str(time_now())
let topic_content: String = "last-session-topic | ts:" + ts_now + " | session:" + session_id + " | topic:" + safe_topic
let topic_tags: String = "["last-session-topic","conv:history","Conversation","session:topic"]"
let topic_tags: String = "[\"last-session-topic\",\"conv:history\",\"Conversation\",\"session:topic\"]"
let topic_label: String = "last-session-topic:" + session_id
// Delete old last-session-topic node for this session before writing fresh
let old_topic: String = engram_search_json("last-session-topic:" + session_id, 2)
+18 -1
View File
@@ -500,11 +500,28 @@ println("[soul] boot - cgi=" + soul_cgi_id + " port=" + int_to_str(port))
let using_http_engram: Bool = !str_eq(engram_url_raw, "")
// ENGRAM_STORE (tiered paged store): when the flag is on, the durable neuron.egm
// store not the JSON snapshot is the source of truth. engram_store_boot does
// import-once + WAL-replay + resident load; every structural mutation is mirrored
// into the store's WAL, and mem_save() checkpoints the resident graph (incl.
// learned hebb) so associations survive a restart. Flag OFF is unchanged.
let engram_store_flag: String = env("ENGRAM_STORE")
let engram_store_on: Bool = str_eq(engram_store_flag, "1") || str_eq(engram_store_flag, "on") || str_eq(engram_store_flag, "true")
let engram_data_dir_raw: String = env("ENGRAM_DATA_DIR")
let engram_data_dir: String = if str_eq(engram_data_dir_raw, "") { env("HOME") + "/.neuron/engram" } else { engram_data_dir_raw }
// Always try local snapshot first. If it has content (>50 nodes) it was
// previously seeded from HTTP Engram and is kept up-to-date by the awareness
// loop use it. This preserves sessions and memories across restarts.
// HTTP Engram is only used for the very first boot (empty/absent snapshot).
engram_load(snapshot)
if engram_store_on {
let booted: Int = engram_store_boot(engram_data_dir)
println("[soul] ENGRAM_STORE on — booted paged store from " + engram_data_dir
+ " (rc=" + int_to_str(booted) + ") nodes=" + int_to_str(engram_node_count())
+ " edges=" + int_to_str(engram_edge_count()))
} else {
engram_load(snapshot)
}
let local_node_count: Int = engram_node_count()
let snapshot_usable: Bool = local_node_count > 50