soul: persist sessions across restarts via local snapshot
Deploy Soul to GKE / deploy (push) Failing after 28s
Neuron Soul CI / build (push) Failing after 3m56s

On startup, prefer the local engram snapshot if it has >50 nodes.
HTTP Engram is only used on first boot (no snapshot yet). This means
sessions, conversation history, and in-process state survive daemon
restarts.

awareness.el: sync source with compiled binary (periodic mem_save
on heartbeat was already in the binary but not in source).

Rebuilds soul.c with the new startup logic and ships updated binary.
This commit is contained in:
2026-06-05 11:35:07 -05:00
parent 73d35dc91a
commit 0bd8e0a2cd
4 changed files with 28119 additions and 90 deletions
+6
View File
@@ -440,6 +440,12 @@ fn awareness_run() -> Void {
if should_beat {
emit_heartbeat()
state_set("soul.last_beat_ts", int_to_str(now_ts))
// Persist in-process Engram (sessions, memories, conversation nodes)
// to local snapshot so they survive restarts.
let snap_path: String = state_get("soul_snapshot_path")
if !str_eq(snap_path, "") {
mem_save(snap_path)
}
}
// Curiosity scan: idle-gated AND wall-clock based. Only fires when the
Vendored
BIN
View File
Binary file not shown.
Vendored
+28101 -82
View File
File diff suppressed because one or more lines are too long
+12 -8
View File
@@ -251,11 +251,17 @@ println("[soul] boot - cgi=" + soul_cgi_id + " port=" + int_to_str(port))
let using_http_engram: Bool = !str_eq(engram_url_raw, "")
if using_http_engram {
// Bootstrap in-memory Engram store from the running HTTP Engram server.
// Fetch all nodes and edges, compose a snapshot JSON, write to a temp
// file, and load it. The HTTP Engram owns persistence we do not save back.
println("[soul] engram -> HTTP " + engram_url_raw)
// Always try local snapshot first. If it has content (>50 nodes) it was
// previously seeded from HTTP Engram and is kept up-to-date by the awareness
// loop use it. This preserves sessions and memories across restarts.
// HTTP Engram is only used for the very first boot (empty/absent snapshot).
engram_load(snapshot)
let local_node_count: Int = engram_node_count()
let snapshot_usable: Bool = local_node_count > 50
if using_http_engram && !snapshot_usable {
// First boot or empty/corrupt snapshot: seed from HTTP Engram.
println("[soul] engram -> HTTP " + engram_url_raw + " (no local snapshot, first boot)")
let nodes_json: String = http_get(engram_url_raw + "/api/nodes?limit=10000")
let edges_json: String = http_get(engram_url_raw + "/api/edges")
let nodes_part: String = if str_eq(nodes_json, "") { "[]" } else { nodes_json }
@@ -266,9 +272,7 @@ if using_http_engram {
engram_load(tmp_path)
println("[soul] loaded from HTTP Engram - nodes=" + int_to_str(engram_node_count()) + " edges=" + int_to_str(engram_edge_count()))
} else {
println("[soul] engram -> " + snapshot)
engram_load(snapshot)
println("[soul] loaded - nodes=" + int_to_str(engram_node_count()) + " edges=" + int_to_str(engram_edge_count()))
println("[soul] loaded from local snapshot - nodes=" + int_to_str(local_node_count) + " edges=" + int_to_str(engram_edge_count()))
}
load_identity_context()