nx_companion_rel.nx source
↩ module page · 157 lines · 8637 B
1// nx_companion_rel.nx -- R1d of the ELDER AI companion spine: RELATIONSHIP STATE. Distinct from episodic
2// memory (R1b = the raw immutable log of everything said): this is the evolving SUMMARY of who the user is to
3// Elder -- relationship stage, trust level, durable facts about the person, their recent mood. It is what lets
4// the companion at month 6 relate differently than at day 1, grounded in real shared history. Keyed per
5// (persona,user) so one persona holds a separate, private relationship with each person. Stored in the
6// canonical nx_registry over seg_store (additive: every update is a new version, get returns latest, history
7// kept -- rule 13). never-brick: trust is CLAMPED to 0..100; facts APPEND (never overwrite/lose). All fields
8// are DATA (rule 11/25) -- no policy, no censor; the relationship is whatever the record says it is.
9// license_tier: ORIGINAL
10import "nx_registry.nx"
11import "nx_tabrec.nx"
12const REL_MAGIC_16384: i64 = 16384
13const REL_MAGIC_8192: i64 = 8192
14
15const REL_PREFIX: *u8 = "knowledge/relstate-" as *u8
16const REL_KP: *u8 = "rel:" as *u8
17const REL_IDX: *u8 = "__relidx__" as *u8
18
19// record fields (rule 11): stage \t trust \t facts \t mood \t highlights
20const RF_STAGE: i64 = 0
21const RF_TRUST: i64 = 1
22const RF_FACTS: i64 = 2
23const RF_MOOD: i64 = 3
24const RF_HL: i64 = 4
25
26const TRUST_MAX: i64 = 100
27const TRUST_MIN: i64 = 0
28
29func rel_cpz(dst: *u8, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ dst[i]=s[i]; i=i+1 } dst[i]=0 as u8; return i }
30func rel_catn(d: *u8, o: i64, v: i64) -> i64 {
31 let t: *u8 = sys_mmap(24); var m: i64=v; var k: i64=0
32 if m < 0 { m = 0 }
33 if m == 0 { t[0]=48 as u8; k=1 }
34 while m > 0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
35 var w: i64=o; var i: i64=0
36 while i < k { d[w]=t[k-1-i]; w=w+1; i=i+1 }
37 return w
38}
39// id = "<persona>:<user>" (NUL-terminated); reg_* prepends REL_KP
40func rel_id(buf: *u8, persona: *u8, user: *u8) -> i64 {
41 var o: i64=0; var i: i64=0
42 while persona[i]!=(0 as u8){ buf[o]=persona[i]; o=o+1; i=i+1 }
43 buf[o]=0x3A as u8; o=o+1
44 i=0; while user[i]!=(0 as u8){ buf[o]=user[i]; o=o+1; i=i+1 }
45 buf[o]=0 as u8
46 return o
47}
48
49// ---- store / load ------------------------------------------------------------------------------------
50func rel_update_pfx(prefix: *u8, persona: *u8, user: *u8, stage: *u8, trust: i64, facts: *u8, mood: *u8, hl: *u8) -> i64 {
51 var t: i64 = trust
52 if t > TRUST_MAX { t = TRUST_MAX }
53 if t < TRUST_MIN { t = TRUST_MIN }
54 let id: *u8 = sys_mmap(512); rel_id(id, persona, user)
55 let rec: *u8 = sys_mmap(REL_MAGIC_16384); var o: i64=0
56 o=tr_cat(rec,o,stage); o=tr_tab(rec,o)
57 o=rel_catn(rec,o,t); o=tr_tab(rec,o)
58 o=tr_cat(rec,o,facts); o=tr_tab(rec,o)
59 o=tr_cat(rec,o,mood); o=tr_tab(rec,o)
60 o=tr_cat(rec,o,hl)
61 return reg_put(prefix, REL_KP, REL_IDX, id, rec, o)
62}
63func rel_get_pfx(prefix: *u8, persona: *u8, user: *u8, ptrout: *i64, lenout: *i64) -> i64 {
64 let id: *u8 = sys_mmap(512); rel_id(id, persona, user)
65 return reg_get(prefix, REL_KP, id, ptrout, lenout)
66}
67// copy field idx into out (NUL-terminated, bounded by cap); returns field length (0 if empty/absent)
68func rel_getf(rec: *u8, rlen: i64, idx: i64, out: *u8, cap: i64) -> i64 {
69 let f: *i64 = sys_mmap(16) as *i64
70 if tr_field(rec, 0, rlen, idx, f) == 0 { out[0]=0 as u8; return 0 }
71 let n: i64 = f[1]
72 var w: i64=0
73 while w < n { if w < cap-1 { out[w]=rec[f[0]+w] } w=w+1 }
74 var term: i64=n; if term > cap-1 { term=cap-1 }
75 out[term]=0 as u8
76 return n
77}
78// load all 5 fields into caller buffers; returns trust (>=0), or -1 if the (persona,user) has no record yet.
79func rel_read5(prefix: *u8, persona: *u8, user: *u8, stageb: *u8, factsb: *u8, moodb: *u8, hlb: *u8, cap: i64) -> i64 {
80 let p: *i64 = sys_mmap(16) as *i64
81 let l: *i64 = sys_mmap(16) as *i64
82 if rel_get_pfx(prefix, persona, user, p, l) != 1 { return 0 - 1 }
83 let rec: *u8 = p[0] as *u8
84 let rlen: i64 = l[0]
85 rel_getf(rec, rlen, RF_STAGE, stageb, cap)
86 rel_getf(rec, rlen, RF_FACTS, factsb, cap)
87 rel_getf(rec, rlen, RF_MOOD, moodb, cap)
88 rel_getf(rec, rlen, RF_HL, hlb, cap)
89 let f: *i64 = sys_mmap(16) as *i64
90 var t: i64 = 0
91 if tr_field(rec, 0, rlen, RF_TRUST, f) == 1 { t = tr_atoi(rec, f[0], f[1]) }
92 return t
93}
94
95// ---- mutators (read-modify-write; additive facts, clamped trust) -------------------------------------
96func rel_note_fact_pfx(prefix: *u8, persona: *u8, user: *u8, fact: *u8) -> i64 {
97 let cap: i64 = REL_MAGIC_8192
98 let sb: *u8 = sys_mmap(cap); let fb: *u8 = sys_mmap(cap); let mb: *u8 = sys_mmap(cap); let hb: *u8 = sys_mmap(cap)
99 var t: i64 = rel_read5(prefix, persona, user, sb, fb, mb, hb, cap)
100 if t < 0 { rel_cpz(sb, "new" as *u8); fb[0]=0 as u8; mb[0]=0 as u8; hb[0]=0 as u8; t = 0 }
101 // facts_new = facts + ("; " if non-empty) + fact (APPEND -- never lose a prior fact)
102 let fn: *u8 = sys_mmap(cap * 2); var o: i64=0
103 var i: i64=0; while fb[i]!=(0 as u8){ fn[o]=fb[i]; o=o+1; i=i+1 }
104 if o > 0 { fn[o]=0x3B as u8; o=o+1; fn[o]=0x20 as u8; o=o+1 } // "; "
105 i=0; while fact[i]!=(0 as u8){ fn[o]=fact[i]; o=o+1; i=i+1 }
106 fn[o]=0 as u8
107 return rel_update_pfx(prefix, persona, user, sb, t, fn, mb, hb)
108}
109func rel_bump_trust_pfx(prefix: *u8, persona: *u8, user: *u8, delta: i64) -> i64 {
110 let cap: i64 = REL_MAGIC_8192
111 let sb: *u8 = sys_mmap(cap); let fb: *u8 = sys_mmap(cap); let mb: *u8 = sys_mmap(cap); let hb: *u8 = sys_mmap(cap)
112 var t: i64 = rel_read5(prefix, persona, user, sb, fb, mb, hb, cap)
113 if t < 0 { rel_cpz(sb, "new" as *u8); fb[0]=0 as u8; mb[0]=0 as u8; hb[0]=0 as u8; t = 0 }
114 t = t + delta
115 if t > TRUST_MAX { t = TRUST_MAX }
116 if t < TRUST_MIN { t = TRUST_MIN }
117 return rel_update_pfx(prefix, persona, user, sb, t, fb, mb, hb)
118}
119func rel_set_stage_pfx(prefix: *u8, persona: *u8, user: *u8, stage: *u8) -> i64 {
120 let cap: i64 = REL_MAGIC_8192
121 let sb: *u8 = sys_mmap(cap); let fb: *u8 = sys_mmap(cap); let mb: *u8 = sys_mmap(cap); let hb: *u8 = sys_mmap(cap)
122 var t: i64 = rel_read5(prefix, persona, user, sb, fb, mb, hb, cap)
123 if t < 0 { fb[0]=0 as u8; mb[0]=0 as u8; hb[0]=0 as u8; t = 0 }
124 return rel_update_pfx(prefix, persona, user, stage, t, fb, mb, hb)
125}
126func rel_set_mood_pfx(prefix: *u8, persona: *u8, user: *u8, mood: *u8) -> i64 {
127 let cap: i64 = REL_MAGIC_8192
128 let sb: *u8 = sys_mmap(cap); let fb: *u8 = sys_mmap(cap); let mb: *u8 = sys_mmap(cap); let hb: *u8 = sys_mmap(cap)
129 var t: i64 = rel_read5(prefix, persona, user, sb, fb, mb, hb, cap)
130 if t < 0 { rel_cpz(sb, "new" as *u8); fb[0]=0 as u8; hb[0]=0 as u8; t = 0 }
131 return rel_update_pfx(prefix, persona, user, sb, t, fb, mood, hb)
132}
133
134// ---- projection: relationship-state -> context fragment (deterministic) ------------------------------
135func rel_context_pfx(prefix: *u8, persona: *u8, user: *u8, out: *u8, cap: i64) -> i64 {
136 let bcap: i64 = REL_MAGIC_8192
137 let sb: *u8 = sys_mmap(bcap); let fb: *u8 = sys_mmap(bcap); let mb: *u8 = sys_mmap(bcap); let hb: *u8 = sys_mmap(bcap)
138 let t: i64 = rel_read5(prefix, persona, user, sb, fb, mb, hb, bcap)
139 if t < 0 { out[0]=0 as u8; return 0 }
140 var o: i64 = 0
141 o = tr_cat(out, o, "Relationship stage: " as *u8); o = tr_cat(out, o, sb)
142 o = tr_cat(out, o, ". Trust: " as *u8); o = rel_catn(out, o, t); o = tr_cat(out, o, "/100" as *u8)
143 o = tr_cat(out, o, ". What you know about them: " as *u8); o = tr_cat(out, o, fb)
144 o = tr_cat(out, o, ". Their recent mood: " as *u8); o = tr_cat(out, o, mb)
145 o = tr_cat(out, o, "." as *u8)
146 out[o] = 0 as u8
147 return o
148}
149
150// production-prefix wrappers
151func rel_update(persona: *u8, user: *u8, stage: *u8, trust: i64, facts: *u8, mood: *u8, hl: *u8) -> i64 { return rel_update_pfx(REL_PREFIX, persona, user, stage, trust, facts, mood, hl) }
152func rel_get(persona: *u8, user: *u8, ptrout: *i64, lenout: *i64) -> i64 { return rel_get_pfx(REL_PREFIX, persona, user, ptrout, lenout) }
153func rel_note_fact(persona: *u8, user: *u8, fact: *u8) -> i64 { return rel_note_fact_pfx(REL_PREFIX, persona, user, fact) }
154func rel_bump_trust(persona: *u8, user: *u8, delta: i64) -> i64 { return rel_bump_trust_pfx(REL_PREFIX, persona, user, delta) }
155func rel_set_stage(persona: *u8, user: *u8, stage: *u8) -> i64 { return rel_set_stage_pfx(REL_PREFIX, persona, user, stage) }
156func rel_set_mood(persona: *u8, user: *u8, mood: *u8) -> i64 { return rel_set_mood_pfx(REL_PREFIX, persona, user, mood) }
157func rel_context(persona: *u8, user: *u8, out: *u8, cap: i64) -> i64 { return rel_context_pfx(REL_PREFIX, persona, user, out, cap) }