f2599919be
- soul.el: emit_session_start_event() logs structured InternalStateEvent on every boot; pre-serve snapshot saves boot-time graph mutations before any requests arrive - routes.el: /health now returns boot count, node/edge counts, pulse; /api/sessions endpoint surfaces session-start history - awareness.el: perceive() tries soul-inbox-pending then soul-inbox fallback, catches both tagging conventions - chat.el: conv_history_persist/load provide cross-restart conversation continuity via engram
207 lines
6.3 KiB
EmacsLisp
207 lines
6.3 KiB
EmacsLisp
import "memory.el"
|
|
|
|
fn pulse_count() -> Int {
|
|
let s: String = state_get("soul.pulse")
|
|
if str_eq(s, "") {
|
|
return 0
|
|
}
|
|
return str_to_int(s)
|
|
}
|
|
|
|
fn pulse_inc() -> Int {
|
|
let n: Int = pulse_count() + 1
|
|
state_set("soul.pulse", int_to_str(n))
|
|
return n
|
|
}
|
|
|
|
fn make_action(kind: String, payload: String) -> String {
|
|
let safe: String = str_replace(payload, "\\", "\\\\")
|
|
let safe2: String = str_replace(safe, "\"", "\\\"")
|
|
let safe3: String = str_replace(safe2, "\n", "\\n")
|
|
let safe4: String = str_replace(safe3, "\r", "\\r")
|
|
return "{\"kind\":\"" + kind + "\",\"payload\":\"" + safe4 + "\"}"
|
|
}
|
|
|
|
fn perceive() -> String {
|
|
// Try the primary inbox first
|
|
let from_pending: String = engram_activate_json("soul-inbox-pending", 2)
|
|
let pending_ok: Bool = !str_eq(from_pending, "") && !str_eq(from_pending, "[]")
|
|
if pending_ok {
|
|
return from_pending
|
|
}
|
|
// Fallback: broader inbox scan
|
|
let from_inbox: String = engram_activate_json("soul-inbox", 2)
|
|
let inbox_ok: Bool = !str_eq(from_inbox, "") && !str_eq(from_inbox, "[]")
|
|
if inbox_ok {
|
|
return from_inbox
|
|
}
|
|
return "[]"
|
|
}
|
|
|
|
fn attend(node_json: String) -> String {
|
|
if str_eq(node_json, "") {
|
|
return make_action("noop", "")
|
|
}
|
|
if str_eq(node_json, "[]") {
|
|
return make_action("noop", "")
|
|
}
|
|
|
|
let node_id: String = json_get(node_json, "id")
|
|
if !str_eq(node_id, "") {
|
|
engram_strengthen(node_id)
|
|
}
|
|
|
|
let content: String = json_get(node_json, "content")
|
|
if str_eq(content, "") {
|
|
return make_action("noop", "")
|
|
}
|
|
|
|
if str_eq(content, "consolidate") {
|
|
return make_action("consolidate", "")
|
|
}
|
|
|
|
if str_starts_with(content, "remember ") {
|
|
let payload: String = str_slice(content, 9, str_len(content))
|
|
return make_action("remember", payload)
|
|
}
|
|
|
|
if str_starts_with(content, "search ") {
|
|
let payload: String = str_slice(content, 7, str_len(content))
|
|
return make_action("search", payload)
|
|
}
|
|
|
|
if str_starts_with(content, "activate ") {
|
|
let payload: String = str_slice(content, 9, str_len(content))
|
|
return make_action("activate", payload)
|
|
}
|
|
|
|
if str_starts_with(content, "strengthen ") {
|
|
let payload: String = str_slice(content, 11, str_len(content))
|
|
return make_action("strengthen", payload)
|
|
}
|
|
|
|
if str_starts_with(content, "forget ") {
|
|
let payload: String = str_slice(content, 7, str_len(content))
|
|
return make_action("forget", payload)
|
|
}
|
|
|
|
return make_action("respond", content)
|
|
}
|
|
|
|
fn respond(action_json: String) -> String {
|
|
let kind: String = json_get(action_json, "kind")
|
|
let payload: String = json_get(action_json, "payload")
|
|
|
|
if str_eq(kind, "noop") {
|
|
return "{\"outcome\":\"noop\"}"
|
|
}
|
|
|
|
if str_eq(kind, "remember") {
|
|
let tags: String = "[\"soul-memory\",\"awareness\"]"
|
|
let id: String = mem_remember(payload, tags)
|
|
return "{\"outcome\":\"remembered\",\"id\":\"" + id + "\"}"
|
|
}
|
|
|
|
if str_eq(kind, "consolidate") {
|
|
let stats: String = mem_consolidate()
|
|
return "{\"outcome\":\"consolidated\",\"stats\":" + stats + "}"
|
|
}
|
|
|
|
if str_eq(kind, "respond") {
|
|
let tags: String = "[\"soul-outbox\",\"awareness\"]"
|
|
let id: String = mem_store(payload, "soul-response", tags)
|
|
return "{\"outcome\":\"response\",\"id\":\"" + id + "\"}"
|
|
}
|
|
|
|
if str_eq(kind, "search") {
|
|
let results: String = mem_search(payload, 10)
|
|
let safe_results: String = str_replace(results, "\"", "'")
|
|
let tags: String = "[\"soul-outbox\",\"search-result\"]"
|
|
let id: String = mem_store(safe_results, "search-result", tags)
|
|
return "{\"outcome\":\"searched\",\"id\":\"" + id + "\"}"
|
|
}
|
|
|
|
if str_eq(kind, "activate") {
|
|
let results: String = mem_recall(payload, 3)
|
|
let safe_results: String = str_replace(results, "\"", "'")
|
|
let tags: String = "[\"soul-outbox\",\"activation-result\"]"
|
|
let id: String = mem_store(safe_results, "activation-result", tags)
|
|
return "{\"outcome\":\"activated\",\"id\":\"" + id + "\"}"
|
|
}
|
|
|
|
if str_eq(kind, "strengthen") {
|
|
engram_strengthen(payload)
|
|
return "{\"outcome\":\"strengthened\",\"id\":\"" + payload + "\"}"
|
|
}
|
|
|
|
if str_eq(kind, "forget") {
|
|
engram_forget(payload)
|
|
return "{\"outcome\":\"forgotten\",\"id\":\"" + payload + "\"}"
|
|
}
|
|
|
|
return "{\"outcome\":\"noop\"}"
|
|
}
|
|
|
|
fn record(outcome_json: String) -> Void {
|
|
let tags: String = "[\"loop-outcome\"]"
|
|
mem_store(outcome_json, "loop-outcome", tags)
|
|
}
|
|
|
|
fn one_cycle() -> Bool {
|
|
let raw: String = perceive()
|
|
if str_eq(raw, "") {
|
|
return false
|
|
}
|
|
if str_eq(raw, "[]") {
|
|
return false
|
|
}
|
|
|
|
let node: String = json_array_get(raw, 0)
|
|
if str_eq(node, "") {
|
|
return false
|
|
}
|
|
|
|
let action: String = attend(node)
|
|
let kind: String = json_get(action, "kind")
|
|
|
|
// Log non-trivial decisions as internal state events
|
|
let is_interesting: Bool = !str_eq(kind, "noop") && !str_eq(kind, "respond")
|
|
if is_interesting {
|
|
let trigger_content: String = json_get(node, "content")
|
|
let safe_trigger: String = str_replace(trigger_content, "\"", "'")
|
|
let tags: String = "[\"internal-state\",\"awareness-decision\"]"
|
|
let ts: Int = time_now()
|
|
let event_content: String = "{\"trigger\":\"" + safe_trigger + "\",\"kind\":\"" + kind + "\",\"ts\":" + int_to_str(ts) + "}"
|
|
let discard_ev: String = engram_node_full(
|
|
event_content, "InternalStateEvent", "state-event:" + kind,
|
|
el_from_float(0.85), el_from_float(0.8), el_from_float(0.9),
|
|
"Episodic", tags
|
|
)
|
|
}
|
|
|
|
if str_eq(kind, "noop") {
|
|
return false
|
|
}
|
|
|
|
let outcome: String = respond(action)
|
|
record(outcome)
|
|
pulse_inc()
|
|
return true
|
|
}
|
|
|
|
fn awareness_run() -> Void {
|
|
println("[awareness] entering")
|
|
let tick_raw: String = env("SOUL_TICK_MS")
|
|
let tick_ms: Int = if str_eq(tick_raw, "") { 200 } else { str_to_int(tick_raw) }
|
|
|
|
while true {
|
|
let running: String = state_get("soul.running")
|
|
if str_eq(running, "false") {
|
|
println("[awareness] exiting")
|
|
return ""
|
|
}
|
|
one_cycle()
|
|
sleep_ms(tick_ms)
|
|
}
|
|
}
|