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