@@ -12,47 +12,113 @@ fn chat_default_model() -> String {
return " claude-sonnet-4-5 "
}
// parse_salience_100 — convert a %g-serialized float to integer * 100.
// The C runtime serializes floats with %g which trims trailing zeros:
// 0.70 → " 0.7 " , 0.60 → " 0.6 " , 0.50 → " 0.5 " , 1.0 → " 1 "
// The naive str_replace ( " . " , " " ) approach breaks for single-decimal strings:
// " 0.7 " → " 07 " → str_to_int → 7 ( WRONG, should be 70 )
// " 0.5 " → " 05 " → str_to_int → 5 ( WRONG, should be 50 )
// " 0.85 " → " 085 " → str_to_int → 85 ( accidentally correct — two decimal digits )
// Fix: use str_index_of to find the decimal point and scale accordingly:
// No decimal ( " 1 " ) : multiply raw by 100
// One decimal digit ( " 0.7 " ) : multiply stripped value by 10
// Two+ decimal digits ( " 0.85 " ) : stripped value is already in hundredths
fn parse_salience_100 ( s: String ) -> Int {
if str_eq ( s, " " ) { return 70 }
let dot_pos: Int = str_index_of ( s, " . " )
let raw: Int = if dot_pos < 0 {
// No decimal point — integer like " 1 " means 100%
str_to_int ( s ) * 100
} else {
let after_dot: String = str_slice ( s, dot_pos + 1 , str_len ( s ) )
let decimal_digits: Int = str_len ( after_dot )
let stripped: Int = str_to_int ( str_replace ( s, " . " , " " ) )
if decimal_digits == 1 { stripped * 10 } else { stripped }
}
if raw > 100 { 100 } else { if raw < 0 { 0 } else { raw } }
}
// engram_score_node — compute a recency x relevance score for a single engram
// node JSON object. Higher is better. Score = salience * importance * recency_factor.
// recency_factor decays linearly over 30 days: nodes updated today score 1.0 ,
// nodes 30+ days old score 0.1 ( floor ) . Nodes with no created_at score 0.5.
// This keeps fresh, high-salience nodes at the top and pushes stale low-signal
// nodes to the bottom so they get trimmed when we cap context size.
// node JSON object. Higher is better.
//
// Bugs fixed vs original implementation:
// 1. FLOAT PARSING: parse_salience_100 correctly handles %g single-decimal output.
// " 0.7 " → 70 , " 0.6 " → 60 , " 0.5 " → 50 ( was: 7 , 6 , 5 — scored near zero and
// were filtered by threshold=25, making the function broken for the majority
// of the graph where conv/utterance nodes have salience/importance ≈ 0.6/0.7 ) .
// 2. RECENCY USES LAST TOUCH: uses max ( created_at, updated_at, last_activated ) so
// nodes strengthened by engram_strengthen ( ) after chat turns are not penalised
// for a stale created_at. A node referenced yesterday but created 25 days ago
// now correctly scores as fresh rather than borderline-filtered.
// 3. COMPRESSED RECENCY RANGE: old formula ( sal * imp * recency / 10000 ) gave
// recency a 10x dynamic range ( 10-100 ) vs 1.9x for salience/importance. A
// canonical high-importance node at 30 days scored the same as a fresh noise
// node. New formula compresses recency to 1.54x via ( 50 + recency/2 ) weight.
// 4. SOFTER FLOOR: recency floor raised from 10 to 30 with tier-aware decay windows
// so canonical identity/persona nodes never bottom out to near-zero.
fn engram_score_node ( node_json: String ) -> Int {
let salience_str: String = json_get ( node_json, " salience " )
let importance_str: String = json_get ( node_json, " importance " )
let created_str: String = json_get ( node_json, " created_at " )
let updated_str: String = json_get ( node_json, " updated_at " )
let activated_str: String = json_get ( node_json, " last_activated " )
let tier_str: String = json_get ( node_json, " tier " )
// Parse as floats via * 100 integer arithmetic ( el has no float math )
let salience_100: Int = if str_eq ( salience_str, " " ) { 70 } else {
let s : Int = str_to_int ( str_replace ( salience_str, " . " , " " ) )
// Clamp to 0-100 range ( value was e.g. " 0.85 " -> parsed " 085 " = 85 )
if s > 100 { 100 } else { if s < 0 { 0 } else { s } }
}
let importance_100: Int = if str_eq ( importance_str, " " ) { 70 } else {
let v: Int = str_to_int ( str_replace ( importance_str, " . " , " " ) )
if v > 100 { 100 } else { if v < 0 { 0 } else { v } }
}
// parse_salience_100 handles " 0.7 " → 70 , " 0.85 " → 85 , " 1.0 " → 100 , " 1 " → 100
let salience_100: Int = parse_salience_100 ( salience_str )
let importance_100 : Int = parse_salience_100 ( importance_str )
// Recency: decay from 100 ( today ) to 10 ( 30+ days ) . created_at is Unix seconds.
// Recency: use max ( created_at, updated_at, last_activated ) .
// last_activated is updated by engram_strengthen ( ) every chat turn — nodes
// actively referenced score fresh regardless of original write time.
let now_ts: Int = time_now ( )
let recency_100 : Int = if str_eq ( created_str, " " ) { 50 } else {
let cre ated_ts: Int = str_to_int ( created_str )
let age_secs: Int = now_ts - created_ts
let age_days : Int = age_secs / 86400
let decay : Int = if age_days >= 30 { 10 } else { 100 - ( age_days * 3 ) }
if decay < 10 { 10 } else { decay }
let c reated_ts : Int = if str_eq ( created_str, " " ) { 0 } else { str_to_int ( created_str ) }
let upd ated_ts: Int = if str_eq ( updated_str, " " ) { 0 } else { str_to_int ( updated_str ) }
let activated_ts: Int = if str_eq ( activated_str, " " ) { 0 } else { str_to_int ( activated_str ) }
let best_ts_ab : Int = if updated_ts > created_ts { updated_ts } else { created_ts }
let best_ts : Int = if activated_ts > best_ts_ab { activated_ts } else { best_ts_ab }
let recency_100: Int = if best_ts == 0 { 50 } else {
let age_secs: Int = now_ts - best_ts
// Guard against clock skew ( future timestamps ) : treat as brand new.
let age_days: Int = if age_secs < 0 { 0 } else { age_secs / 86400 }
// Tier-aware decay, softer floor ( 30 not 10 ) :
// Canonical: 365-day window — foundational identity/persona nodes.
// Episodic: 90-day window — conversation context fades moderately.
// Working/untiered: 35-day window — transient task state.
let is_canonical: Bool = str_eq ( tier_str, " Canonical " )
let is_episodic: Bool = str_eq ( tier_str, " Episodic " )
let decay: Int = if is_canonical {
let drop: Int = if age_days >= 365 { 70 } else { age_days * 70 / 365 }
100 - drop
} else {
if is_episodic {
if age_days >= 90 { 30 } else { 100 - ( age_days * 70 / 90 ) }
} else {
if age_days >= 35 { 30 } else { 100 - ( age_days * 2 ) }
}
}
if decay < 30 { 30 } else { decay }
}
// Combined score 0-1000000 ( no floats ) : salience * importance * recency / 10000
return salience_100 * importance_100 * recency_100 / 10000
// Compressed recency weight ( 50 + recency/2 ) : range 65-100 ( 1.54x dynamic range ) .
// Old formula had 10x recency range which drowned out relevance for old-but-important
// nodes. New: relevance ( 0-100 ) × recency_weight ( 65-100 ) / 100 → score 0-100.
// salience_100 and importance_100 are already in the 0-100 range ( parse_salience_100
// returns e.g. 70 for " 0.7 " ) . Dividing by 100 keeps relevance in 0-100.
// Dividing by 10000 caused integer truncation to 0 for all real-world nodes
// ( e.g., sal=0.7, imp=0.7 → 70*70/10000 = 0 instead of 49 ) .
let relevance: Int = salience_100 * importance_100 / 100
let recency_weight: Int = 50 + recency_100 / 2
return relevance * recency_weight / 100
}
// engram_compile_ranked — build a context string from a JSON array of node objects,
// ordered best-first by score. Only nodes above a minimum score ( 25 = salience 0.5 *
// importance 0.5 * recency 1.0 ) are included ; the rest are noise. Returns at most
// max_nodes entries concatenated as JSON array text. Because el has no sort primitive,
// we do a single selection pass picking the top N by linear scan ( N=10 cap ) .
// ordered best-first by score. Only nodes above threshold=10 are included.
// With corrected formula ( sal*imp/100 ) : sal=0.5*imp=0.5 at max recency scores 25 ;
// sal=0.5*imp=0.5 at Working floor ( recency=30, weight=65 ) scores 16.
// Threshold=10 gives safe headroom for low-salience nodes near the recency floor,
// while still filtering near-zero noise ( e.g., sal=0.1*imp=0.1 → score ≤ 1 ) .
// Returns at most max_nodes entries. max_nodes must not exceed 20 ( sentinel limit ) .
fn engram_compile_ranked ( nodes_json: String, max_nodes: Int ) -> String {
if str_eq ( nodes_json, " " ) { return " " }
if str_eq ( nodes_json, " [] " ) { return " " }
@@ -73,8 +139,10 @@ fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String {
while ci < total {
let node: String = json_array_get ( nodes_json, ci )
let score: Int = engram_score_node ( node )
// Only include reasonably relevant nodes ( threshold=25 )
let above_thresh: Bool = score >= 25
// Threshold=10: allows moderately-relevant older nodes while filtering noise.
// Example: sal=0.5 imp=0.5 at Working recency floor ( 35+ days ) → score 16 ,
// which passes. A near-zero node ( sal=0.1 imp=0.1 ) → score ≤ 1 , filtered.
let above_thresh: Bool = score >= 10
// Check this index wasn 't already selected ( sentinel: look for idx marker )
let idx_marker: String = " \" _sel_ " + int_to_str ( ci ) + " \" "
let already_picked: Bool = str_contains ( selected, idx_marker )
@@ -101,7 +169,7 @@ fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String {
// Strip the _sel_N sentinel fields that were used for duplicate-detection bookkeeping.
// The sentinels have the form " \" _sel_N \" :1, " ( trailing comma, space before next key ) .
// We injected them as the first field in each object, so the pattern is predictable.
// Because el has no regex, remove up to 10 possible sentinel variants by literal replace.
// Because el has no regex, remove up to 20 possible sentinel variants by literal replace.
let clean: String = " [ " + selected + " ] "
let c0: String = str_replace ( clean, " \" _sel_0 \" :1, " , " " )
let c1: String = str_replace ( c0, " \" _sel_1 \" :1, " , " " )
@@ -113,7 +181,17 @@ fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String {
let c7: String = str_replace ( c6, " \" _sel_7 \" :1, " , " " )
let c8: String = str_replace ( c7, " \" _sel_8 \" :1, " , " " )
let c9: String = str_replace ( c8, " \" _sel_9 \" :1, " , " " )
return c9
let c10: String = str_replace ( c9, " \" _sel_10 \" :1, " , " " )
let c11: String = str_replace ( c10, " \" _sel_11 \" :1, " , " " )
let c12: String = str_replace ( c11, " \" _sel_12 \" :1, " , " " )
let c13: String = str_replace ( c12, " \" _sel_13 \" :1, " , " " )
let c14: String = str_replace ( c13, " \" _sel_14 \" :1, " , " " )
let c15: String = str_replace ( c14, " \" _sel_15 \" :1, " , " " )
let c16: String = str_replace ( c15, " \" _sel_16 \" :1, " , " " )
let c17: String = str_replace ( c16, " \" _sel_17 \" :1, " , " " )
let c18: String = str_replace ( c17, " \" _sel_18 \" :1, " , " " )
let c19: String = str_replace ( c18, " \" _sel_19 \" :1, " , " " )
return c19
}
fn engram_compile ( intent: String ) -> String {
@@ -124,8 +202,11 @@ fn engram_compile(intent: String) -> String {
let act_ok: Bool = !str_eq ( activate_json, " " ) && !str_eq ( activate_json, " [] " )
let srch_ok: Bool = !str_eq ( search_json, " " ) && !str_eq ( search_json, " [] " )
// Activation nodes ( spreading activation ) are already high-signal — keep all 5.
let act_part: String = if act_ok { activate_json } else { " " }
// Activation nodes ( spreading activation ) are high-signal but apply scoring via
// engram_compile_ranked with threshold=5 to exclude genuinely zero-quality stale
// nodes that happen to be graph-connected. The threshold of 5 is well below the
// search path threshold of 15 to preserve the activation path 's higher recall.
let act_part: String = if act_ok { engram_compile_ranked ( activate_json, 5 ) } else { " " }
// Rank search results and keep only the top 8 ( was: flat 15 unranked ) .
// This cuts context noise roughly in half while preserving the best-scoring nodes.
@@ -181,31 +262,7 @@ fn engram_compile(intent: String) -> String {
let bn_ts: Int = if str_eq ( bn_ts_raw, " " ) { 0 } else { str_to_int ( bn_ts_raw ) }
if bn_ts > cutoff_ts { bn0 } else { " " }
} else { " " }
// Positive emotion context: check for recent joy/success moments within 72h.
let pos_ec_nodes: String = engram_search_json ( " PositiveEvent joy:high joy:low affective " , 3 )
let pos_ec_ok: Bool = !str_eq ( pos_ec_nodes, " " ) && !str_eq ( pos_ec_nodes, " [] " )
let recent_positive_ec: String = if pos_ec_ok {
let pec0: String = json_array_get ( pos_ec_nodes, 0 )
let pec_content: String = json_get ( pec0, " content " )
let pec_ts_marker: String = " | ts: "
let pec_ts_pos: Int = str_index_of ( pec_content, pec_ts_marker )
let pec_ts_raw: String = if pec_ts_pos >= 0 {
let pec_ts_start: Int = pec_ts_pos + str_len ( pec_ts_marker )
let pec_rest: String = str_slice ( pec_content, pec_ts_start, str_len ( pec_content ) )
let pec_next: Int = str_index_of ( pec_rest, " | " )
if pec_next < 0 { pec_rest } else { str_slice ( pec_rest, 0 , pec_next ) }
} else {
let pec_ca: String = json_get ( pec0, " created_at " )
if str_eq ( pec_ca, " " ) { json_get ( pec0, " updated_at " ) } else { pec_ca }
}
let pec_ts: Int = if str_eq ( pec_ts_raw, " " ) { 0 } else { str_to_int ( pec_ts_raw ) }
if pec_ts > cutoff_ts { pec0 } else { " " }
} else { " " }
let affective_part: String = if !str_eq ( recent_bell, " " ) {
recent_bell
} else {
if !str_eq ( recent_positive_ec, " " ) { recent_positive_ec } else { " " }
}
let affective_part: String = if !str_eq ( recent_bell, " " ) { recent_bell } else { " " }
let sep1: String = if !str_eq ( act_part, " " ) && !str_eq ( srch_part, " " ) { " \n " } else { " " }
let sep2: String = if ( !str_eq ( act_part, " " ) || !str_eq ( srch_part, " " ) ) && !str_eq ( scan_part, " " ) { " \n " } else { " " }
@@ -265,7 +322,7 @@ fn build_system_prompt(ctx: String) -> String {
safety_addendum
}
return identity + date_line + voice_rules + security_rules + capability_rules + identity_block + affective_boot_block + engram_block + safety_block
return identity + date_line + voice_rules + security_rules + capability_rules + identity_block + engram_block + safety_block
}
fn hist_append ( hist: String, role: String, content: String ) -> String {
@@ -424,62 +481,22 @@ 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 = {
// Runs every turn. Uses correct BellEvent/PositiveEvent tags.
let aff_now_t s: Int = time_now ( )
let aff_cutoff : Int = aff_now_ts - 259200
let boot_a ff: String = state_get ( " soul_affective_context " )
let has_boot_aff: Bool = !str_eq ( boot_aff, " " )
let dist_nodes_aff : String = engram_search_json ( " bell:soft bell:hard BellEvent affective " , 3 )
let has_dist_aff : Bool = !str_eq ( dist_nodes_aff, " " ) && !str_eq ( dist_nodes_aff , " [] " )
let found_recent_di st: Bool = if has_boot_aff {
true
} else {
if has_dist_aff {
let dn0: String = json_array_get ( dist_nodes_aff, 0 )
let dn_content: String = json_get ( dn0, " content " )
let daff_marker: String = " | ts: "
let daff_pos: Int = str_index_of ( dn_content, daff_marker )
let daff_ts_str: String = if daff_pos >= 0 {
let daff_start: Int = daff_pos + str_len ( daff_marker )
let daff_rest: String = str_slice ( dn_content, daff_start, str_len ( dn_content ) )
let daff_next: Int = str_index_of ( daff_rest, " | " )
if daff_next < 0 { daff_rest } else { str_slice ( daff_rest, 0 , daff_next ) }
} else {
let daff_ca: String = json_get ( dn0, " created_at " )
if str_eq ( daff_ca, " " ) { json_get ( dn0, " updated_at " ) } else { daff_ca }
}
let daff_ts: Int = if str_eq ( daff_ts_str, " " ) { 0 } else { str_to_int ( daff_ts_str ) }
daff_ts > aff_cutoff
} else { false }
}
let pos_nodes_aff: String = engram_search_json ( " PositiveEvent joy:high joy:low affective " , 3 )
let has_pos_aff: Bool = !str_eq ( pos_nodes_aff, " " ) && !str_eq ( pos_nodes_aff, " [] " )
let found_recent_pos: Bool = if has_pos_aff && !found_recent_dist {
let pn0: String = json_array_get ( pos_nodes_aff, 0 )
let pn_content: String = json_get ( pn0, " content " )
let paff_marker: String = " | ts: "
let paff_pos: Int = str_index_of ( pn_content, paff_marker )
let paff_ts_str: String = if paff_pos >= 0 {
let paff_start: Int = paff_pos + str_len ( paff_marker )
let paff_rest: String = str_slice ( pn_content, paff_start, str_len ( pn_content ) )
let paff_next: Int = str_index_of ( paff_rest, " | " )
if paff_next < 0 { paff_rest } else { str_slice ( paff_rest, 0 , paff_next ) }
} else {
let paff_ca: String = json_get ( pn0, " created_at " )
if str_eq ( paff_ca, " " ) { json_get ( pn0, " updated_at " ) } else { paff_ca }
}
let paff_ts: Int = if str_eq ( paff_ts_str, " " ) { 0 } else { str_to_int ( paff_ts_str ) }
paff_ts > aff_cutoff
let affective_prefix: String = if hist_len == 0 {
let distress_nodes: String = engram_search_json ( " bell distress crisis loss grief despair " , 3 )
let has_node s: Bool = !str_eq ( distress_nodes, " " ) && !str_eq ( distress_nodes, " [] " )
let now_ts : Int = time_now ( )
let cuto ff: Int = now_ts - 259200
let found_recent: Bool = if has_nodes {
let dn0 : String = json_array_get ( distress_nodes, 0 )
let ts0_raw : String = json_get ( dn0 , " created_at " )
let ts0_ str : String = if str_eq ( ts0_raw, " " ) { json_get ( dn0, " updated_at " ) } else { ts0_raw }
let ts0: Int = if str_eq ( ts0_str, " " ) { 0 } else { str_to_int ( ts0_str ) }
ts0 > cutoff
} else { false }
if found_recent_dist {
if found_recent {
" [RECENT CONTEXT: User recently expressed significant distress. Monitor for indirect crisis signals and respond with care.] \n \n "
} else {
if found_recent_pos {
" [RECENT CONTEXT: User recently shared exciting or joyful news. Acknowledge and celebrate with them when relevant.] \n \n "
} else { " " }
}
}
} else { " " }
} else { " " }
let ctx: String = engram_compile ( activation_seed )
let system: String = affective_prefix + build_system_prompt ( ctx )
@@ -1038,7 +1055,7 @@ fn handle_chat_agentic(body: String) -> String {
if str_eq ( screen_action, " hard_bell " ) {
safety_log_bell ( " hard " , json_get ( screen_result, " reason " ) , str_slice ( message, 0 , 80 ) )
return " { \" reply \" : \" " + json_safe ( safety_validate ( " " , " hard_bell " ) ) + " \" , \" model \" : \" \" , \" agentic \" :true, \" tools_used \" :[]} "
}
let req_model: String = json_get ( body, " model " )
let model: String = if str_eq ( req_model, " " ) { chat_default_model ( ) } else { req_model }
@@ -1574,18 +1591,13 @@ fn auto_persist(req: String, resp: String) -> Void {
// consistent with what safety_screen already evaluated for this turn.
let bell_level: String = safety_detect_bell_level ( message )
let is_bell: Bool = !str_eq ( bell_level, " none " )
let positive_level: String = safety_detect_positive_level ( message )
let is_positive: Bool = !str_eq ( positive_level, " none " )
// Tag the Conversation node with affective metadata when emotion is detected.
// Tag the Conversation node with bell metadata when distress is present so
// subsequent affective queries ( e.g. engram_compile ) can find this exchange.
let tags: String = if is_bell {
" [ \" Conversation \" , \" chat \" , \" timestamped \" , \" bell: " + bell_level + " \" , \" affective \" ] "
} else {
if is_positive {
" [ \" Conversation \" , \" chat \" , \" timestamped \" , \" joy: " + positive_level + " \" , \" affective \" ] "
} else {
" [ \" Conversation \" , \" chat \" , \" timestamped \" ] "
}
" [ \" Conversation \" , \" chat \" , \" timestamped \" ] "
}
let content: String = " { \" q \" : \" " + safe_msg + " \" "
@@ -1671,28 +1683,6 @@ fn auto_persist(req: String, resp: String) -> Void {
}
state_set ( signal_key, safe_summary )
}
// Dedicated PositiveEvent node for joy/pride/success moments.
if is_positive {
let pos_summary: String = if str_len ( message ) > 120 { str_slice ( message, 0 , 120 ) } else { message }
let safe_pos_sum: String = str_replace ( pos_summary, " \" " , " ' " )
let pos_content: String = " POSITIVE: " + positive_level
+ " | ts: " + ts_str
+ " | summary: " + safe_pos_sum
let pos_sal_a: String = if str_eq ( positive_level, " high " ) { el_from_float ( 0.88 ) } else { el_from_float ( 0.75 ) }
let pos_sal_b: String = if str_eq ( positive_level, " high " ) { el_from_float ( 0.88 ) } else { el_from_float ( 0.75 ) }
let pos_sal_c: String = if str_eq ( positive_level, " high " ) { el_from_float ( 0.95 ) } else { el_from_float ( 0.85 ) }
let pos_tags: String = " [ \" joy \" , \" positive \" , \" joy: " + positive_level + " \" , \" affective \" , \" PositiveEvent \" ] "
let pos_ts_label: String = int_to_str ( time_now ( ) )
let pos_label: String = " joy: " + positive_level + " : " + pos_ts_label
let pos_node_id: String = engram_node_full (
pos_content, " PositiveEvent " , pos_label,
pos_sal_a, pos_sal_b, pos_sal_c, " Episodic " , pos_tags
)
if str_eq ( pos_node_id, " " ) {
println ( " [chat] auto_persist: PositiveEvent write failed (ts= " + ts_str + " ) " )
}
}
}
// strengthen_chat_nodes — strengthen the engram nodes that were activated during a chat.