diff --git a/chat.el b/chat.el index e3094b4..69e9df9 100644 --- a/chat.el +++ b/chat.el @@ -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 { diff --git a/memory.el b/memory.el index 0265eed..29dc685 100644 --- a/memory.el +++ b/memory.el @@ -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") } } diff --git a/safety.el b/safety.el index 05bff15..aaea9cd 100644 --- a/safety.el +++ b/safety.el @@ -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. diff --git a/sessions.el b/sessions.el index ea5c931..a5941a7 100644 --- a/sessions.el +++ b/sessions.el @@ -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) diff --git a/soul.el b/soul.el index 0c981a3..64c9ba6 100644 --- a/soul.el +++ b/soul.el @@ -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