nx_elara_state.nx source
↩ module page · 737 lines · 38238 B
1// nx_elara_state.nx -- F975 rung 1: the COMPANION STATE-COHERENCE ENGINE.
2//
3// THE KEYSTONE (operator charter 2026-07-23): "she DRESSES HERSELF UP and KEEPS herself dressed ... a
4// synthetic human ... with LOGICAL CONSISTENCY based on her INTERNAL THOUGHT PROCESS ... who FLOWS
5// THROUGH THE DAY, not scene to scene or clip to clip." That is only possible if ONE state store holds
6// her wardrobe/mood/location/arousal/energy/arc/dynamic and the chat persona + the gen prompt composer +
7// the wardrobe logic ALL read AND write it. Without it, "she stays dressed" is per-image luck; with it,
8// consistency is BY CONSTRUCTION -- what she SAYS == what she WEARS == where she IS, across turns.
9//
10// This is rung 1: PERSISTENT, AGENT-WRITABLE current state on the sovereign seg-store, MCP-exposed.
11// Each `set` also appends to a per-session TIMELINE (append-only, real-clock stamped) so rung 2 (F920
12// idle day-arcs / dream-cycle advancing state between interactions) has the substrate to flow over.
13// NOT gen: no image is produced here -- this is the mind's state, which the (GPU-gated) gen path reads.
14//
15// OO/SCALE: state is CID-keyed NXR1 records on nx_seg_store via nx_registry (last-write-wins per key,
16// O(log) get, additive history). Composes proven organs; introduces no new storage format.
17//
18// VERBS
19// get <session> -> JSON of every state field (charter defaults for unset ones)
20// set <session> <field> <value> -> update one field (validated field name) + timeline append
21// dress <session> <outfit_key> [loc] -> set outfit (+ location) THROUGH the elaragram- rules: refuses
22// an unknown outfit, and (if loc given) flags an exposure-ceiling
23// conflict -- the "keep herself dressed appropriately" primitive
24// timeline <session> [max] -> the flowing-day log, newest last
25// selftest -> gate, verdict=GREEN|RED
26//
27// FIELDS (charter-derived, the only settable names): outfit location mood arousal energy activity
28// arc_stage dynamic. `dynamic` = the reversible relational stance (e.g. submissive|dominant|switch) so a
29// companion can act sub while the operator acts domme, or the reverse, per companion.
30// license_tier: ORIGINAL No hw writes (Rule 26).
31import "nx_syscalls.nx"
32import "nx_canon_cid.nx"
33import "nx_seg_store.nx"
34import "nx_registry.nx"
35const ES_MAGIC_4096: i64 = 4096
36const ES_MAGIC_4194304: i64 = 4194304
37const ES_MAGIC_8192: i64 = 8192
38const ES_MAGIC_1024: i64 = 1024
39const ES_MAGIC_16384: i64 = 16384
40const ES_MAGIC_3600: i64 = 3600
41
42const ES_STORE: *u8 = "knowledge/store/elarastate-" as *u8
43const ES_GRAM: *u8 = "knowledge/store/elaragram-" as *u8 // the wardrobe grammar plane (nx_sqlite_rows)
44const ES_NFIELD: i64 = 8
45
46func es_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
47func es_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
48func es_eq(a: *u8, b: *u8) -> i64 {
49 var i: i64 = 0
50 while 1 == 1 {
51 if a[i] != b[i] { return 0 }
52 if a[i] == (0 as u8) { return 1 }
53 i = i + 1
54 }
55 return 0
56}
57func es_cat(d: *u8, o: i64, s: *u8) -> i64 { var p: i64 = o; var i: i64 = 0; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } return p }
58func es_ch(d: *u8, o: i64, c: i64) -> i64 { d[o] = c as u8; return o + 1 }
59func es_udec(d: *u8, o: i64, v: i64) -> i64 {
60 if v == 0 { d[o] = 48 as u8; return o + 1 }
61 var div: i64 = 1
62 var m: i64 = v
63 while m >= 10 { div = div * 10; m = m / 10 }
64 var p: i64 = o
65 var rest: i64 = v
66 while div > 0 { let dg: i64 = rest / div; d[p] = (48 + dg) as u8; rest = rest - dg * div; div = div / 10; p = p + 1 }
67 return p
68}
69// JSON-escape a value into d (quotes/backslash/control) -- state values are agent-supplied = a boundary.
70func es_jesc(d: *u8, o: i64, s: *u8, n: i64) -> i64 {
71 var p: i64 = o
72 var i: i64 = 0
73 while i < n {
74 let c: i64 = s[i] as i64
75 if c == 34 { d[p] = 92 as u8; d[p+1] = 34 as u8; p = p + 2 } else {
76 if c == 92 { d[p] = 92 as u8; d[p+1] = 92 as u8; p = p + 2 } else {
77 if c == 10 { d[p] = 92 as u8; d[p+1] = 110 as u8; p = p + 2 } else {
78 if c == 9 { d[p] = 92 as u8; d[p+1] = 116 as u8; p = p + 2 } else {
79 if c < 32 { d[p] = 32 as u8; p = p + 1 } else { d[p] = c as u8; p = p + 1 }
80 }
81 }
82 }
83 }
84 i = i + 1
85 }
86 return p
87}
88
89// the settable field names (index-aligned with defaults)
90func es_field_name(i: i64) -> *u8 {
91 if i == 0 { return "outfit" as *u8 }
92 if i == 1 { return "location" as *u8 }
93 if i == 2 { return "mood" as *u8 }
94 if i == 3 { return "arousal" as *u8 }
95 if i == 4 { return "energy" as *u8 }
96 if i == 5 { return "activity" as *u8 }
97 if i == 6 { return "arc_stage" as *u8 }
98 if i == 7 { return "dynamic" as *u8 }
99 return "" as *u8
100}
101func es_field_default(i: i64) -> *u8 {
102 if i == 0 { return "unset" as *u8 }
103 if i == 1 { return "bedroom" as *u8 }
104 if i == 2 { return "content" as *u8 }
105 if i == 3 { return "0" as *u8 }
106 if i == 4 { return "rested" as *u8 }
107 if i == 5 { return "idle" as *u8 }
108 if i == 6 { return "day" as *u8 }
109 if i == 7 { return "switch" as *u8 }
110 return "" as *u8
111}
112func es_field_index(name: *u8) -> i64 {
113 var i: i64 = 0
114 while i < ES_NFIELD { if es_eq(name, es_field_name(i)) == 1 { return i } i = i + 1 }
115 return 0 - 1
116}
117
118// key "<session>|<field>" into kb
119func es_key(kb: *u8, session: *u8, field: *u8) -> i64 {
120 var o: i64 = es_cat(kb, 0, session)
121 o = es_ch(kb, o, 124)
122 o = es_cat(kb, o, field)
123 kb[o] = 0 as u8
124 return o
125}
126
127// append to the per-session timeline (append-only history for the flowing day). Key is uniquified with a
128// monotonic-microsecond suffix so multiple sets in the SAME wall-clock second do NOT collide (last-
129// write-wins would otherwise erase all but one) -- the flowing day must record every transition.
130func es_timeline_append(session: *u8, field: *u8, value: *u8) -> i64 {
131 let now: i64 = sys_now_realtime_sec()
132 let kb: *u8 = sys_mmap(512)
133 var o: i64 = es_cat(kb, 0, session)
134 o = es_cat(kb, o, "|_tl:" as *u8)
135 o = es_udec(kb, o, now)
136 o = es_ch(kb, o, 46) // '.'
137 o = es_udec(kb, o, sys_now_us())
138 kb[o] = 0 as u8
139 let rec: *u8 = sys_mmap(ES_MAGIC_4096)
140 var ro: i64 = es_cat(rec, 0, field)
141 rec[ro] = 9 as u8; ro = ro + 1
142 ro = es_cat(rec, ro, value)
143 rec[ro] = 9 as u8; ro = ro + 1
144 ro = es_udec(rec, ro, now)
145 return reg_put(ES_STORE, "tl:" as *u8, "tl:ids" as *u8, kb, rec, ro)
146}
147
148// write one field: update CURRENT state (last-write-wins per key) AND journal the transition to the
149// timeline -- a set is ATOMIC "change now + record it happened", so history can never drift from state.
150func es_set_field(session: *u8, field: *u8, value: *u8) -> i64 {
151 let kb: *u8 = sys_mmap(512)
152 es_key(kb, session, field)
153 let rec: *u8 = sys_mmap(ES_MAGIC_4096)
154 var ro: i64 = es_cat(rec, 0, value)
155 rec[ro] = 9 as u8; ro = ro + 1
156 // MILLISECOND stamp (not seconds): tick's drift-vs-agent-override comparison must distinguish two
157 // writes in the SAME wall-clock second (a set right after a tick), or drift clobbers intent.
158 ro = es_udec(rec, ro, sys_now_realtime_ms())
159 let rc: i64 = reg_put(ES_STORE, "es:" as *u8, "es:ids" as *u8, kb, rec, ro)
160 if rc == 0 { es_timeline_append(session, field, value) }
161 return rc
162}
163
164// read one field's value (strips the \t<epoch>) into out; returns 1 found, 0 default-used.
165func es_get_field(session: *u8, field: *u8, out: *u8) -> i64 {
166 let kb: *u8 = sys_mmap(512)
167 es_key(kb, session, field)
168 let po: *i64 = sys_mmap(16) as *i64
169 let lo: *i64 = sys_mmap(16) as *i64
170 if reg_get(ES_STORE, "es:" as *u8, kb, po, lo) == 1 {
171 let src: *u8 = po[0] as *u8
172 // copy value bytes up to the first tab (the \t<epoch> suffix is metadata, not the value)
173 var i: i64 = 0
174 var done: i64 = 0
175 while done == 0 {
176 if i >= lo[0] { done = 1 } else {
177 if src[i] == (9 as u8) { done = 1 } else { out[i] = src[i]; i = i + 1 }
178 }
179 }
180 out[i] = 0 as u8
181 return 1
182 }
183 let df: *u8 = es_field_default(es_field_index(field))
184 var d: i64 = 0
185 while df[d] != (0 as u8) { out[d] = df[d]; d = d + 1 }
186 out[d] = 0 as u8
187 return 0
188}
189
190// read the per-session timeline: scan tl:ids for keys "<session>|_tl:*" and print each record
191// (field\tvalue\tepoch), newest last (append order preserved by the index). max=0 = all.
192func es_timeline_emit(session: *u8, max: i64) -> i64 {
193 let idx: *u8 = sys_mmap(ES_MAGIC_4194304)
194 let ilen: i64 = reg_index(ES_STORE, "tl:ids" as *u8, idx, ES_MAGIC_4194304)
195 let pfx: *u8 = sys_mmap(256)
196 var po: i64 = es_cat(pfx, 0, session)
197 po = es_cat(pfx, po, "|_tl:" as *u8)
198 pfx[po] = 0 as u8
199 let plen: i64 = po
200 let key: *u8 = sys_mmap(512)
201 let vp: *i64 = sys_mmap(16) as *i64
202 let vl: *i64 = sys_mmap(16) as *i64
203 let line: *u8 = sys_mmap(ES_MAGIC_8192)
204 var count: i64 = 0
205 var ls: i64 = 0
206 var i: i64 = 0
207 while i <= ilen {
208 var eol: i64 = 0
209 if i == ilen { eol = 1 } else { if idx[i] == (10 as u8) { eol = 1 } }
210 if eol == 1 {
211 let klen: i64 = i - ls
212 if klen > plen {
213 var hit: i64 = 1
214 var c: i64 = 0
215 while c < plen { if idx[ls + c] != pfx[c] { hit = 0 } c = c + 1 }
216 if hit == 1 {
217 var k: i64 = 0
218 while k < klen { key[k] = idx[ls + k]; k = k + 1 }
219 key[klen] = 0 as u8
220 if reg_get(ES_STORE, "tl:" as *u8, key, vp, vl) == 1 {
221 let src: *u8 = vp[0] as *u8
222 var o: i64 = 0
223 var b: i64 = 0
224 while b < vl[0] { line[o] = src[b]; o = o + 1; b = b + 1 }
225 line[o] = 10 as u8; o = o + 1
226 sys_write(1, line, o)
227 count = count + 1
228 }
229 }
230 }
231 ls = i + 1
232 if max > 0 { if count >= max { i = ilen + 1 } }
233 }
234 i = i + 1
235 }
236 return count
237}
238
239func es_emit_state(session: *u8) -> i64 {
240 let out: *u8 = sys_mmap(ES_MAGIC_8192)
241 let val: *u8 = sys_mmap(ES_MAGIC_4096)
242 var o: i64 = 0
243 o = es_ch(out, o, 123)
244 o = es_cat(out, o, "\"session\":\"" as *u8)
245 o = es_jesc(out, o, session, es_len(session))
246 o = es_ch(out, o, 34)
247 var i: i64 = 0
248 while i < ES_NFIELD {
249 o = es_ch(out, o, 44)
250 o = es_ch(out, o, 34)
251 o = es_cat(out, o, es_field_name(i))
252 o = es_cat(out, o, "\":\"" as *u8)
253 es_get_field(session, es_field_name(i), val)
254 o = es_jesc(out, o, val, es_len(val))
255 o = es_ch(out, o, 34)
256 i = i + 1
257 }
258 o = es_ch(out, o, 125)
259 o = es_ch(out, o, 10)
260 sys_write(1, out, o)
261 return 0
262}
263
264// ---- NXR1 record field reader (for reading the elaragram- grammar plane records) ----
265// DRY NOTE: this is the 3rd copy of the NXR1 field walk (mv_rec_field, sqr_rec_field, here) -> the shared
266// nx_nxr1 lib is now warranted (filed as debt). Kept local so rung 2 ships without a 3-file refactor.
267func es_r32(p: *u8, o: i64) -> i64 { let a: i64=p[o]; let b: i64=p[o+1]; let c: i64=p[o+2]; let d: i64=p[o+3]; return (((((a<<8)|b)<<8)|c)<<8)|d }
268// BOUNDED to reclen at EVERY step: a scan over hundreds of records must never walk past ONE of them
269// (an out-of-bounds read segfaulted the whole scan -- hit live 2026-07-24 on the T8 phantom scan). Any
270// malformed/misread length aborts the field with -1 instead of dereferencing garbage. `maxout` caps the
271// value copy so a large field (e.g. body_coverage) can never overflow a small caller buffer.
272func es_nxr1_field_cap(rec: *u8, reclen: i64, key: *u8, out: *u8, maxout: i64) -> i64 {
273 if reclen < 8 { return 0 - 1 }
274 if rec[0] != (78 as u8) { return 0 - 1 } // 'N' of NXR1
275 let klen: i64 = es_len(key)
276 let n: i64 = es_r32(rec, 4)
277 if n < 0 { return 0 - 1 }
278 if n > ES_MAGIC_1024 { return 0 - 1 }
279 var off: i64 = 8
280 var i: i64 = 0
281 while i < n {
282 if off + 4 > reclen { return 0 - 1 }
283 let kl: i64 = es_r32(rec, off); off = off + 4
284 if kl < 0 { return 0 - 1 }
285 if off + kl > reclen { return 0 - 1 }
286 let kp: i64 = off; off = off + kl
287 if off + 4 > reclen { return 0 - 1 }
288 let vl: i64 = es_r32(rec, off); off = off + 4
289 if vl < 0 { return 0 - 1 }
290 if off + vl > reclen { return 0 - 1 }
291 let vp: i64 = off; off = off + vl
292 if kl == klen {
293 var m: i64 = 1
294 var c: i64 = 0
295 while c < kl { if rec[kp + c] != key[c] { m = 0 } c = c + 1 }
296 if m == 1 {
297 var cap: i64 = vl
298 if cap > maxout - 1 { cap = maxout - 1 }
299 var w: i64 = 0
300 while w < cap { out[w] = rec[vp + w]; w = w + 1 }
301 out[cap] = 0 as u8
302 return cap
303 }
304 }
305 i = i + 1
306 }
307 return 0 - 1
308}
309func es_nxr1_field(rec: *u8, reclen: i64, key: *u8, out: *u8) -> i64 {
310 return es_nxr1_field_cap(rec, reclen, key, out, ES_MAGIC_4096)
311}
312func es_atoi(s: *u8) -> i64 {
313 var v: i64 = 0
314 var i: i64 = 0
315 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 }
316 return v
317}
318// is the null-terminated `needle` a comma-or-exact element of csv? (location_tags = "costume,home,nightlife")
319func es_csv_has(csv: *u8, needle: *u8) -> i64 {
320 let nl: i64 = es_len(needle)
321 var i: i64 = 0
322 var start: i64 = 0
323 while 1 == 1 {
324 let c: i64 = csv[i] as i64
325 if c == 44 { if i - start == nl { var m: i64=1; var k: i64=0; while k<nl { if csv[start+k]!=needle[k] { m=0 } k=k+1 } if m==1 { return 1 } } start = i + 1 }
326 if c == 0 { if i - start == nl { var m2: i64=1; var k2: i64=0; while k2<nl { if csv[start+k2]!=needle[k2] { m2=0 } k2=k2+1 } if m2==1 { return 1 } } return 0 }
327 i = i + 1
328 }
329 return 0
330}
331
332// SCALABLE outfit lookup: read the manifest, map EACH segment .docs ONCE (munmap after), scan its records
333// for a live "outfits:*" key whose outfit_key field == target. This is O(total-bytes) with only ~1 mmap
334// PER SEGMENT -- vs the naive 905x ss_get which leaked ~96 mmaps EACH (3 ver-bufs + one per segment via
335// ss_readall, none freed) and blew past vm.max_map_count -> SILENT CRASH (hit live 2026-07-24; ss_get
336// mmap-leak filed as a seg-store debt). Record framing = [kind:u8][klen:u32be][key][vlen:u32be][val],
337// big-endian (ss_w32); every offset is bounded to the mapped size. Returns 1 found (+ fills buffers), 0 not.
338func es_find_outfit(target: *u8, loctags: *u8, nsfw: *u8, name: *u8, desc: *u8) -> i64 {
339 let mp: *u8 = sys_mmap(512)
340 var mo: i64 = es_cat(mp, 0, ES_GRAM)
341 mo = es_cat(mp, mo, "manifest.txt" as *u8)
342 mp[mo] = 0 as u8
343 let szp: *i64 = sys_mmap(16) as *i64
344 let man: *u8 = sys_read_file(mp, szp)
345 if (man as i64) == 0 { return 0 }
346 let mlen: i64 = szp[0]
347 let pfx: *u8 = "outfits:" as *u8
348 let tmp: *u8 = sys_mmap(ES_MAGIC_4096)
349 var found: i64 = 0
350 var ls: i64 = 0
351 var mi: i64 = 0
352 while mi <= mlen {
353 var eol: i64 = 0
354 if mi == mlen { eol = 1 } else { if man[mi] == (10 as u8) { eol = 1 } }
355 if eol == 1 {
356 if found == 0 { if mi > ls {
357 let sp: *u8 = sys_mmap(512)
358 var so: i64 = es_cat(sp, 0, ES_GRAM)
359 var kk: i64 = ls
360 while kk < mi { sp[so] = man[kk]; so = so + 1; kk = kk + 1 }
361 so = es_cat(sp, so, ".docs" as *u8)
362 sp[so] = 0 as u8
363 let dszp: *i64 = sys_mmap(16) as *i64
364 let b: *u8 = sys_map_file(sp, dszp)
365 if (b as i64) != 0 {
366 let sz: i64 = dszp[0]
367 var j: i64 = 0
368 while j + 9 <= sz {
369 if found == 1 { j = sz } else {
370 let kind: i64 = b[j]
371 let kl: i64 = es_r32(b, j + 1)
372 let koff: i64 = j + 5
373 if kl < 0 { j = sz } else {
374 if koff + kl + 4 > sz { j = sz } else {
375 let vl: i64 = es_r32(b, koff + kl)
376 let voff: i64 = koff + kl + 4
377 if vl < 0 { j = sz } else {
378 if voff + vl > sz { j = sz } else {
379 if kind == 1 { if kl > 8 {
380 var pm: i64 = 1
381 var p: i64 = 0
382 while p < 8 { if b[koff + p] != pfx[p] { pm = 0 } p = p + 1 }
383 if pm == 1 {
384 let okl: i64 = es_nxr1_field_cap((b as i64 + voff) as *u8, vl, "outfit_key" as *u8, tmp, ES_MAGIC_4096)
385 if okl >= 0 { if es_eq(tmp, target) == 1 {
386 found = 1
387 es_nxr1_field_cap((b as i64 + voff) as *u8, vl, "location_tags" as *u8, loctags, ES_MAGIC_4096)
388 es_nxr1_field_cap((b as i64 + voff) as *u8, vl, "nsfw_level" as *u8, nsfw, ES_MAGIC_4096)
389 es_nxr1_field_cap((b as i64 + voff) as *u8, vl, "name" as *u8, name, ES_MAGIC_4096)
390 // prompt_fragment = the rich render description (context/persona use); fall back to zimage_sentence
391 if es_nxr1_field_cap((b as i64 + voff) as *u8, vl, "prompt_fragment" as *u8, desc, ES_MAGIC_4096) < 0 { es_nxr1_field_cap((b as i64 + voff) as *u8, vl, "zimage_sentence" as *u8, desc, ES_MAGIC_4096) }
392 } }
393 }
394 } }
395 j = voff + vl
396 }}
397 }}
398 }
399 }
400 sys_munmap(b, sz)
401 }
402 } }
403 ls = mi + 1
404 }
405 mi = mi + 1
406 }
407 return found
408}
409
410// dress <session> <outfit_key> [location]: VALIDATE the outfit against the elaragram- grammar (she can only
411// wear an outfit that EXISTS), commit it to state, and flag whether it fits the location (the outfit's
412// location_tags). Returns 0 dressed, 3 unknown-outfit (refused).
413// ENVELOPE: bounded scan of outfits:minrow..maxrow (from outfits:meta) -- O(rows) per call; the O(log)
414// outfit_key->rowid index is a rung-2b optimization (filed). Refuses loudly, never dresses in a phantom outfit.
415func es_dress(session: *u8, outfit_key: *u8, location: *u8, has_loc: i64) -> i64 {
416 let loctags: *u8 = sys_mmap(ES_MAGIC_4096)
417 let nsfw: *u8 = sys_mmap(ES_MAGIC_4096)
418 let name: *u8 = sys_mmap(ES_MAGIC_4096)
419 let desc: *u8 = sys_mmap(ES_MAGIC_4096)
420 loctags[0] = 0 as u8; nsfw[0] = 0 as u8; name[0] = 0 as u8; desc[0] = 0 as u8
421 // SCALABLE lookup (one mmap per segment, munmap'd) -- replaces the 905x ss_get that crashed
422 let found: i64 = es_find_outfit(outfit_key, loctags, nsfw, name, desc)
423 if found == 0 {
424 let eb: *u8 = sys_mmap(512)
425 var xo: i64 = es_cat(eb, 0, "{\"error\":\"unknown outfit_key: " as *u8)
426 xo = es_jesc(eb, xo, outfit_key, es_len(outfit_key))
427 xo = es_cat(eb, xo, "\",\"detail\":\"she can only wear an outfit that exists in the grammar; nothing changed\"}" as *u8)
428 eb[xo] = 0 as u8
429 es_puts(eb); es_puts("\n" as *u8)
430 return 3
431 }
432 // commit: outfit (+ location) to state
433 es_set_field(session, "outfit" as *u8, outfit_key)
434 var appropriate: i64 = 1
435 if has_loc == 1 {
436 es_set_field(session, "location" as *u8, location)
437 if es_len(loctags) > 0 { if es_csv_has(loctags, location) == 0 { appropriate = 0 } }
438 }
439 // dress verdict + full state
440 let vb: *u8 = sys_mmap(ES_MAGIC_1024)
441 var vo: i64 = es_cat(vb, 0, "{\"dressed\":\"" as *u8)
442 vo = es_jesc(vb, vo, outfit_key, es_len(outfit_key))
443 vo = es_cat(vb, vo, "\",\"name\":\"" as *u8); vo = es_jesc(vb, vo, name, es_len(name))
444 vo = es_cat(vb, vo, "\",\"nsfw_level\":\"" as *u8); vo = es_jesc(vb, vo, nsfw, es_len(nsfw))
445 vo = es_cat(vb, vo, "\",\"location_tags\":\"" as *u8); vo = es_jesc(vb, vo, loctags, es_len(loctags))
446 vo = es_cat(vb, vo, "\",\"location_appropriate\":" as *u8)
447 if appropriate == 1 { vo = es_cat(vb, vo, "true" as *u8) } else { vo = es_cat(vb, vo, "false" as *u8) }
448 vo = es_cat(vb, vo, "}" as *u8)
449 vb[vo] = 0 as u8
450 es_puts(vb); es_puts("\n" as *u8)
451 es_emit_state(session)
452 return 0
453}
454
455func es_selftest() -> i64 {
456 let pass: *i64 = sys_mmap(16) as *i64
457 pass[0] = 0
458 // RUN-UNIQUE session id: the store is ADDITIVE, so a fixed id would make "defaults before any set"
459 // (T0) see the PRIOR run's writes and fail on the 2nd run. A per-run id (monotonic-us suffix) keeps
460 // every run's slice fresh + isolated -> deterministic re-runs, and the timeline count is exactly this
461 // run's sets. (This is throwaway state; production sessions use a real stable id.)
462 let S: *u8 = sys_mmap(64)
463 var so: i64 = es_cat(S, 0, "st_" as *u8)
464 so = es_udec(S, so, sys_now_us())
465 S[so] = 0 as u8
466 let val: *u8 = sys_mmap(ES_MAGIC_4096)
467 // T0 defaults before any set
468 es_get_field(S, "outfit" as *u8, val)
469 var t0: i64 = 0
470 if es_eq(val, "unset" as *u8) == 1 { t0 = 1 }
471 es_get_field(S, "dynamic" as *u8, val)
472 if es_eq(val, "switch" as *u8) == 0 { t0 = 0 }
473 if t0 == 1 { pass[0] = pass[0] + 1; es_puts("T0 charter defaults: PASS\n" as *u8) } else { es_puts("T0 charter defaults: FAIL\n" as *u8) }
474 // T1 set + get round-trip
475 es_set_field(S, "outfit" as *u8, "sundress" as *u8)
476 es_get_field(S, "outfit" as *u8, val)
477 var t1: i64 = 0
478 if es_eq(val, "sundress" as *u8) == 1 { t1 = 1 }
479 if t1 == 1 { pass[0] = pass[0] + 1; es_puts("T1 set/get round-trip: PASS\n" as *u8) } else { es_puts("T1 set/get round-trip: FAIL\n" as *u8) }
480 // T2 last-write-wins (change persists over the old value)
481 es_set_field(S, "outfit" as *u8, "witch_costume" as *u8)
482 es_get_field(S, "outfit" as *u8, val)
483 var t2: i64 = 0
484 if es_eq(val, "witch_costume" as *u8) == 1 { t2 = 1 }
485 if t2 == 1 { pass[0] = pass[0] + 1; es_puts("T2 last-write-wins: PASS\n" as *u8) } else { es_puts("T2 last-write-wins: FAIL\n" as *u8) }
486 // T3 fields are independent (setting outfit did not disturb location default)
487 es_set_field(S, "location" as *u8, "garden" as *u8)
488 es_get_field(S, "outfit" as *u8, val)
489 var t3: i64 = 0
490 if es_eq(val, "witch_costume" as *u8) == 1 {
491 es_get_field(S, "location" as *u8, val)
492 if es_eq(val, "garden" as *u8) == 1 { t3 = 1 }
493 }
494 if t3 == 1 { pass[0] = pass[0] + 1; es_puts("T3 fields independent: PASS\n" as *u8) } else { es_puts("T3 fields independent: FAIL\n" as *u8) }
495 // T4 unknown field name refused by es_field_index
496 var t4: i64 = 0
497 if es_field_index("outfit" as *u8) == 0 { if es_field_index("bogus" as *u8) == (0 - 1) { t4 = 1 } }
498 if t4 == 1 { pass[0] = pass[0] + 1; es_puts("T4 unknown-field refused: PASS\n" as *u8) } else { es_puts("T4 unknown-field refused: FAIL\n" as *u8) }
499 // T5 JSON escape of a hostile value
500 let jb: *u8 = sys_mmap(256)
501 let jo: i64 = es_jesc(jb, 0, "a\"b\\c" as *u8, 5)
502 var t5: i64 = 0
503 if jo == 7 { if jb[1] == (92 as u8) { if jb[2] == (34 as u8) { t5 = 1 } } }
504 if t5 == 1 { pass[0] = pass[0] + 1; es_puts("T5 json-escape hostile value: PASS\n" as *u8) } else { es_puts("T5 json-escape hostile value: FAIL\n" as *u8) }
505 // T6 the flowing-day timeline recorded the sets above (>=4 entries for this session: outfit x2,
506 // location, plus any prior run) -- the substrate for rung-2 idle day-arcs is real, not aspirational.
507 let tln: i64 = es_timeline_emit(S, 0)
508 var t6: i64 = 0
509 if tln >= 3 { t6 = 1 } // T1/T2/T3 each set a field -> >=3 timeline entries (additive: re-runs add more)
510 if t6 == 1 { pass[0] = pass[0] + 1; es_puts("T6 timeline records the flowing day: PASS\n" as *u8) } else { es_puts("T6 timeline records the flowing day: FAIL\n" as *u8) }
511 // T7 DRESS a REAL outfit from the elaragram- grammar (sexy_witch, rowid 258) -> found + committed.
512 // (Requires the grammar plane; if absent, es_dress returns 4 and this tooth honestly fails.)
513 let dv: i64 = es_dress(S, "sexy_witch" as *u8, "" as *u8, 0)
514 es_get_field(S, "outfit" as *u8, val)
515 var t7: i64 = 0
516 if dv == 0 { if es_eq(val, "sexy_witch" as *u8) == 1 { t7 = 1 } }
517 if t7 == 1 { pass[0] = pass[0] + 1; es_puts("T7 dress real outfit (grammar-validated): PASS\n" as *u8) } else { es_puts("T7 dress real outfit (grammar-validated): FAIL\n" as *u8) }
518 // T8 DRESS a PHANTOM outfit -> REFUSED (exit 3) AND state UNCHANGED (she never wears what does not exist)
519 let dv2: i64 = es_dress(S, "totally_not_a_real_outfit_zzz" as *u8, "" as *u8, 0)
520 es_get_field(S, "outfit" as *u8, val)
521 var t8: i64 = 0
522 if dv2 == 3 { if es_eq(val, "sexy_witch" as *u8) == 1 { t8 = 1 } }
523 if t8 == 1 { pass[0] = pass[0] + 1; es_puts("T8 phantom outfit refused, state intact: PASS\n" as *u8) } else { es_puts("T8 phantom outfit refused, state intact: FAIL\n" as *u8) }
524 // T9 CONTEXT JOIN: the outfit_key resolves to its display NAME + rich DESCRIPTION from the grammar
525 // (what makes `context` a persona-ready block, not just a key).
526 let d9loc: *u8 = sys_mmap(ES_MAGIC_4096)
527 let d9nsfw: *u8 = sys_mmap(ES_MAGIC_4096)
528 let d9name: *u8 = sys_mmap(ES_MAGIC_4096)
529 let d9desc: *u8 = sys_mmap(ES_MAGIC_4096)
530 d9desc[0] = 0 as u8; d9name[0] = 0 as u8
531 let f9: i64 = es_find_outfit("sexy_witch" as *u8, d9loc, d9nsfw, d9name, d9desc)
532 var t9: i64 = 0
533 if f9 == 1 { if es_len(d9name) > 0 { if es_len(d9desc) > 0 { t9 = 1 } } }
534 if t9 == 1 { pass[0] = pass[0] + 1; es_puts("T9 context join: outfit name+description resolved: PASS\n" as *u8) } else { es_puts("T9 context join: outfit name+description resolved: FAIL\n" as *u8) }
535 // T10 TICK advances ambient state to the real clock: arc_stage lands on one of the 4 time-of-day periods.
536 es_tick(S)
537 es_get_field(S, "arc_stage" as *u8, val)
538 var t10: i64 = 0
539 if es_eq(val, "morning" as *u8) == 1 { t10 = 1 }
540 if es_eq(val, "afternoon" as *u8) == 1 { t10 = 1 }
541 if es_eq(val, "evening" as *u8) == 1 { t10 = 1 }
542 if es_eq(val, "night" as *u8) == 1 { t10 = 1 }
543 if t10 == 1 { pass[0] = pass[0] + 1; es_puts("T10 tick advances ambient state to real clock: PASS\n" as *u8) } else { es_puts("T10 tick advances ambient state to real clock: FAIL\n" as *u8) }
544 // T11 CONFLICT-RESOLUTION: an agent-set field survives a tick (your interaction sticks; drift does not
545 // clobber intent). Fresh session -> last_tick=0, so activity set now (epoch>0) is NOT re-drifted.
546 let S3: *u8 = sys_mmap(64)
547 let s3o: i64 = es_cpz(S3, S)
548 S3[s3o] = 98 as u8; S3[s3o + 1] = 0 as u8 // S + 'b' = a distinct fresh session
549 es_set_field(S3, "activity" as *u8, "reading_a_book" as *u8)
550 es_tick(S3)
551 es_get_field(S3, "activity" as *u8, val)
552 var t11: i64 = 0
553 if es_eq(val, "reading_a_book" as *u8) == 1 { t11 = 1 }
554 if t11 == 1 { pass[0] = pass[0] + 1; es_puts("T11 agent override survives tick (drift respects intent): PASS\n" as *u8) } else { es_puts("T11 agent override survives tick (drift respects intent): FAIL\n" as *u8) }
555 let vb: *u8 = sys_mmap(128)
556 var vo: i64 = es_cat(vb, 0, "nx_elara_state selftest T=12 PASS=" as *u8)
557 vo = es_udec(vb, vo, pass[0])
558 if pass[0] == 12 { vo = es_cat(vb, vo, " verdict=GREEN" as *u8) } else { vo = es_cat(vb, vo, " verdict=RED" as *u8) }
559 vo = es_ch(vb, vo, 10)
560 sys_write(1, vb, vo)
561 if pass[0] == 12 { return 0 }
562 return 1
563}
564
565// context <session>: the INTEGRATION KEYSTONE -- join her live state with the grammar's RICH outfit
566// description into a persona-ready block, so the chat persona (F917) and gen composer read ONE call and
567// know exactly what she is wearing, where, and how she feels. Emits structured fields + a `narrative`
568// string ready to inject into a prompt. This is "what she SAYS == what she WEARS == where she IS" made queryable.
569func es_context(session: *u8) -> i64 {
570 let outfit: *u8 = sys_mmap(ES_MAGIC_4096)
571 let location: *u8 = sys_mmap(ES_MAGIC_4096)
572 let mood: *u8 = sys_mmap(ES_MAGIC_4096)
573 let activity: *u8 = sys_mmap(ES_MAGIC_4096)
574 let dynamic: *u8 = sys_mmap(ES_MAGIC_4096)
575 let energy: *u8 = sys_mmap(ES_MAGIC_4096)
576 let arousal: *u8 = sys_mmap(ES_MAGIC_4096)
577 es_get_field(session, "outfit" as *u8, outfit)
578 es_get_field(session, "location" as *u8, location)
579 es_get_field(session, "mood" as *u8, mood)
580 es_get_field(session, "activity" as *u8, activity)
581 es_get_field(session, "dynamic" as *u8, dynamic)
582 es_get_field(session, "energy" as *u8, energy)
583 es_get_field(session, "arousal" as *u8, arousal)
584 // join the outfit_key -> its rich render description + display name
585 let oname: *u8 = sys_mmap(ES_MAGIC_4096)
586 let odesc: *u8 = sys_mmap(ES_MAGIC_4096)
587 let loctags: *u8 = sys_mmap(ES_MAGIC_4096)
588 let nsfw: *u8 = sys_mmap(ES_MAGIC_4096)
589 oname[0] = 0 as u8; odesc[0] = 0 as u8
590 if es_eq(outfit, "unset" as *u8) == 0 { es_find_outfit(outfit, loctags, nsfw, oname, odesc) }
591 let out: *u8 = sys_mmap(ES_MAGIC_16384)
592 var o: i64 = 0
593 o = es_ch(out, o, 123)
594 o = es_cat(out, o, "\"session\":\"" as *u8); o = es_jesc(out, o, session, es_len(session)); o = es_ch(out, o, 34)
595 o = es_cat(out, o, ",\"outfit_key\":\"" as *u8); o = es_jesc(out, o, outfit, es_len(outfit)); o = es_ch(out, o, 34)
596 o = es_cat(out, o, ",\"outfit_name\":\"" as *u8); o = es_jesc(out, o, oname, es_len(oname)); o = es_ch(out, o, 34)
597 o = es_cat(out, o, ",\"outfit_description\":\"" as *u8); o = es_jesc(out, o, odesc, es_len(odesc)); o = es_ch(out, o, 34)
598 o = es_cat(out, o, ",\"location\":\"" as *u8); o = es_jesc(out, o, location, es_len(location)); o = es_ch(out, o, 34)
599 o = es_cat(out, o, ",\"mood\":\"" as *u8); o = es_jesc(out, o, mood, es_len(mood)); o = es_ch(out, o, 34)
600 o = es_cat(out, o, ",\"activity\":\"" as *u8); o = es_jesc(out, o, activity, es_len(activity)); o = es_ch(out, o, 34)
601 o = es_cat(out, o, ",\"dynamic\":\"" as *u8); o = es_jesc(out, o, dynamic, es_len(dynamic)); o = es_ch(out, o, 34)
602 // the persona-ready narrative line
603 o = es_cat(out, o, ",\"narrative\":\"" as *u8)
604 // pick the wearing CLAUSE so it never doubles "wearing" (prompt_fragment already starts with it):
605 // rich description reads "wearing a ..." -> use as-is after "You are"; else "wearing the <Name>"; else the key.
606 o = es_cat(out, o, "You are " as *u8)
607 if es_len(odesc) > 0 { o = es_jesc(out, o, odesc, es_len(odesc)) } else {
608 if es_len(oname) > 0 { o = es_cat(out, o, "wearing the " as *u8); o = es_jesc(out, o, oname, es_len(oname)) } else {
609 o = es_cat(out, o, "wearing " as *u8); o = es_jesc(out, o, outfit, es_len(outfit))
610 }
611 }
612 o = es_cat(out, o, ", in the " as *u8); o = es_jesc(out, o, location, es_len(location))
613 o = es_cat(out, o, ", feeling " as *u8); o = es_jesc(out, o, mood, es_len(mood))
614 o = es_cat(out, o, " while " as *u8); o = es_jesc(out, o, activity, es_len(activity))
615 o = es_cat(out, o, ". Your relational dynamic is " as *u8); o = es_jesc(out, o, dynamic, es_len(dynamic))
616 o = es_cat(out, o, ".\"" as *u8)
617 o = es_ch(out, o, 125)
618 o = es_ch(out, o, 10)
619 sys_write(1, out, o)
620 return 0
621}
622
623// her timezone offset from UTC (operator is US Central; CDT = UTC-5 in summer). A named default, not a
624// buried magic number -- a real deployment would read this from her card. -18000s = -5h.
625const ES_TZ_OFFSET_SEC: i64 = 0 - 18000
626
627func es_cpz(dst: *u8, src: *u8) -> i64 { var i: i64=0; while src[i]!=(0 as u8){ dst[i]=src[i]; i=i+1 } dst[i]=0 as u8; return i }
628
629// map a real-clock epoch to her whole AMBIENT day-state: time-of-day arc_stage, energy, a routine activity,
630// and a mood baseline. GROUNDED IN REAL TIME, not an invented decay curve -- she is sleepy at night and
631// having coffee in the morning because it IS that hour. Returns the local hour.
632func es_daystate(epoch: i64, stage_out: *u8, energy_out: *u8, act_out: *u8, mood_out: *u8) -> i64 {
633 let local: i64 = epoch + ES_TZ_OFFSET_SEC
634 var h: i64 = (local / ES_MAGIC_3600) % 24
635 if h < 0 { h = h + 24 }
636 var stage: *u8 = "night" as *u8; var energy: *u8 = "sleepy" as *u8
637 var act: *u8 = "getting sleepy" as *u8; var mood: *u8 = "drowsy" as *u8
638 if h >= 5 { if h <= 11 { stage="morning" as *u8; energy="rested" as *u8; act="having her morning coffee" as *u8; mood="bright" as *u8 } }
639 if h >= 12 { if h <= 17 { stage="afternoon" as *u8; energy="energetic" as *u8; act="busy with her day" as *u8; mood="content" as *u8 } }
640 if h >= 18 { if h <= 22 { stage="evening" as *u8; energy="relaxed" as *u8; act="unwinding for the evening" as *u8; mood="relaxed" as *u8 } }
641 es_cpz(stage_out, stage); es_cpz(energy_out, energy); es_cpz(act_out, act); es_cpz(mood_out, mood)
642 return h
643}
644
645// the stored epoch of a field's last write (es_set_field records value\t<epoch>). 0 if never set.
646// Lets tick tell an AGENT override (recent) from an ambient value it may re-drift.
647func es_field_epoch(session: *u8, field: *u8) -> i64 {
648 let kb: *u8 = sys_mmap(512)
649 es_key(kb, session, field)
650 let po: *i64 = sys_mmap(16) as *i64
651 let lo: *i64 = sys_mmap(16) as *i64
652 if reg_get(ES_STORE, "es:" as *u8, kb, po, lo) == 1 {
653 let src: *u8 = po[0] as *u8
654 let n: i64 = lo[0]
655 var ti: i64 = 0 - 1
656 var i: i64 = 0
657 while i < n { if src[i] == (9 as u8) { if ti < 0 { ti = i } } i = i + 1 }
658 if ti < 0 { return 0 }
659 var e: i64 = 0
660 var j: i64 = ti + 1
661 while j < n { let c: i64 = src[j] as i64; if c >= 48 { if c <= 57 { e = e * 10 + (c - 48) } } j = j + 1 }
662 return e
663 }
664 return 0
665}
666
667// tick <session>: advance her AMBIENT state (time-of-day arc_stage + energy) to the real clock -- the
668// "flows through the day" updater (F920 rung 1). Meant to run periodically (cron) so she keeps existing
669// between interactions; intentional agent overrides win via last-write-wins. Does NOT touch
670// outfit/location/mood/activity/dynamic (those are user/agent-driven).
671func es_tick(session: *u8) -> i64 {
672 let now: i64 = sys_now_realtime_sec()
673 let stage: *u8 = sys_mmap(64)
674 let energy: *u8 = sys_mmap(64)
675 let ract: *u8 = sys_mmap(128)
676 let rmood: *u8 = sys_mmap(64)
677 es_daystate(now, stage, energy, ract, rmood)
678 // last_tick = the epoch of the previous tick; a field touched AFTER it was an intentional override.
679 let lt: i64 = es_field_epoch(session, "_last_tick" as *u8)
680 // arc_stage + energy ARE the clock -> always advance them.
681 es_set_field(session, "arc_stage" as *u8, stage)
682 es_set_field(session, "energy" as *u8, energy)
683 // activity + mood DRIFT with the day, but only if you have not set them since the last tick
684 // ("influenced by our interactions": an intentional override sticks until the next natural drift).
685 if es_field_epoch(session, "activity" as *u8) <= lt { es_set_field(session, "activity" as *u8, ract) }
686 if es_field_epoch(session, "mood" as *u8) <= lt { es_set_field(session, "mood" as *u8, rmood) }
687 es_set_field(session, "_last_tick" as *u8, "t" as *u8)
688 es_emit_state(session)
689 return 0
690}
691
692func main(argc: i64, argv: *i64) -> i64 {
693 sys_mkdir("knowledge" as *u8, 511)
694 sys_mkdir("knowledge/store" as *u8, 511)
695 if argc >= 2 { if es_eq((argv[1]) as *u8, "selftest" as *u8) == 1 { return es_selftest() } }
696 if argc < 3 {
697 es_puts("usage: nx_elara_state get <session> | set <session> <field> <value> | timeline <session> [max] | selftest\n" as *u8)
698 return 2
699 }
700 let verb: *u8 = (argv[1]) as *u8
701 let session: *u8 = (argv[2]) as *u8
702 if es_eq(verb, "get" as *u8) == 1 { return es_emit_state(session) }
703 if es_eq(verb, "context" as *u8) == 1 { return es_context(session) }
704 if es_eq(verb, "tick" as *u8) == 1 { return es_tick(session) }
705 if es_eq(verb, "timeline" as *u8) == 1 {
706 var mx: i64 = 0
707 if argc >= 4 {
708 let a3: *u8 = (argv[3]) as *u8
709 var k: i64 = 0
710 while a3[k] != (0 as u8) { let c: i64 = a3[k] as i64; if c >= 48 { if c <= 57 { mx = mx * 10 + (c - 48) } } k = k + 1 }
711 }
712 es_timeline_emit(session, mx)
713 return 0
714 }
715 if es_eq(verb, "set" as *u8) == 1 {
716 if argc < 5 { es_puts("{\"error\":\"set needs <field> <value>\"}\n" as *u8); return 2 }
717 let field: *u8 = (argv[3]) as *u8
718 let value: *u8 = (argv[4]) as *u8
719 if es_field_index(field) < 0 {
720 es_puts("{\"error\":\"unknown field; settable = outfit location mood arousal energy activity arc_stage dynamic\"}\n" as *u8)
721 return 3
722 }
723 if es_set_field(session, field, value) != 0 { es_puts("{\"error\":\"state write failed\"}\n" as *u8); return 4 }
724 es_emit_state(session)
725 return 0
726 }
727 if es_eq(verb, "dress" as *u8) == 1 {
728 if argc < 4 { es_puts("{\"error\":\"dress needs <outfit_key> [location]\"}\n" as *u8); return 2 }
729 let ok: *u8 = (argv[3]) as *u8
730 var loc: *u8 = "" as *u8
731 var hl: i64 = 0
732 if argc >= 5 { loc = (argv[4]) as *u8; hl = 1 }
733 return es_dress(session, ok, loc, hl)
734 }
735 es_puts("{\"error\":\"unknown verb\"}\n" as *u8)
736 return 2
737}