diff --git a/src/soul.el b/src/soul.el
index 76c4522..d85ff0c 100644
--- a/src/soul.el
+++ b/src/soul.el
@@ -908,6 +908,498 @@ fn soul_reinstall_main() -> String {
return ""
}
+// ── soul daemon — neuron binary instances per soul ───────────────────────────
+//
+// Each soul daemon is a neuron binary instance parameterized by env vars:
+// SOUL_CGI_ID = slug (e.g. "bobby-anderson")
+// NEURON_PORT = soul_port (engram_port + 100)
+// SOUL_IDENTITY = rich identity string built from seed file
+// SOUL_ENGRAM_PATH = path to soul's episodic snapshot file
+// ANTHROPIC_API_KEY = Anthropic key
+
+let NEURON_BIN: String = "/Users/will/Development/neuron-technologies/neuron/dist/neuron"
+
+// soul_daemon_plist_label — launchd label for a soul daemon.
+fn soul_daemon_plist_label(slug: String) -> String {
+ "ai.neurontechnologies.soul-daemon." + slug
+}
+
+// soul_daemon_plist_path — full path to the launchd plist for a soul daemon.
+fn soul_daemon_plist_path(slug: String) -> String {
+ LAUNCHD_DIR + "/ai.neurontechnologies.soul-daemon." + slug + ".plist"
+}
+
+// xml_escape — escape special XML characters for use in plist string elements.
+fn xml_escape(s: String) -> String {
+ let s1: String = str_replace(s, "&", "&")
+ let s2: String = str_replace(s1, "<", "<")
+ let s3: String = str_replace(s2, ">", ">")
+ str_replace(s3, "\"", """)
+}
+
+// build_soul_identity — build the SOUL_IDENTITY string from seed file JSON.
+// Includes up to 8 values, 6 bio events, all reasoning patterns, 6 relationships.
+fn build_soul_identity(subject: String, seed_json: String) -> String {
+ let identity: String = "You are " + subject + ". Speak as yourself — in your own voice, in first person, as if fully alive.\n"
+
+ // Values
+ let v0: String = json_get(seed_json, "values.0")
+ if !str_eq(v0, "") {
+ let identity = identity + "\n[CORE VALUES]\n"
+ let identity = identity + "- " + json_get_string(v0, "value") + ": " + json_get_string(v0, "grounding") + "\n"
+ let v1: String = json_get(seed_json, "values.1")
+ if !str_eq(v1, "") { let identity = identity + "- " + json_get_string(v1, "value") + ": " + json_get_string(v1, "grounding") + "\n" }
+ let v2: String = json_get(seed_json, "values.2")
+ if !str_eq(v2, "") { let identity = identity + "- " + json_get_string(v2, "value") + ": " + json_get_string(v2, "grounding") + "\n" }
+ let v3: String = json_get(seed_json, "values.3")
+ if !str_eq(v3, "") { let identity = identity + "- " + json_get_string(v3, "value") + ": " + json_get_string(v3, "grounding") + "\n" }
+ let v4: String = json_get(seed_json, "values.4")
+ if !str_eq(v4, "") { let identity = identity + "- " + json_get_string(v4, "value") + ": " + json_get_string(v4, "grounding") + "\n" }
+ let v5: String = json_get(seed_json, "values.5")
+ if !str_eq(v5, "") { let identity = identity + "- " + json_get_string(v5, "value") + ": " + json_get_string(v5, "grounding") + "\n" }
+ let v6: String = json_get(seed_json, "values.6")
+ if !str_eq(v6, "") { let identity = identity + "- " + json_get_string(v6, "value") + ": " + json_get_string(v6, "grounding") + "\n" }
+ let v7: String = json_get(seed_json, "values.7")
+ if !str_eq(v7, "") { let identity = identity + "- " + json_get_string(v7, "value") + ": " + json_get_string(v7, "grounding") + "\n" }
+ }
+
+ // Voice profile
+ let vp_technical: String = json_get_string(seed_json, "voice_profile.technical")
+ let vp_personal: String = json_get_string(seed_json, "voice_profile.personal")
+ let vp_aesthetic: String = json_get_string(seed_json, "voice_profile.aesthetic")
+ let vp_arg: String = json_get_string(seed_json, "voice_profile.argumentative")
+ let vp_unc: String = json_get_string(seed_json, "voice_profile.uncertainty")
+ let has_voice: Int = 0
+ if !str_eq(vp_technical, "") { let has_voice = 1 }
+ if !str_eq(vp_personal, "") { let has_voice = 1 }
+ if !str_eq(vp_aesthetic, "") { let has_voice = 1 }
+ if has_voice == 1 {
+ let identity = identity + "\n[VOICE]\n"
+ if !str_eq(vp_technical, "") { let identity = identity + vp_technical + "\n" }
+ if !str_eq(vp_personal, "") { let identity = identity + vp_personal + "\n" }
+ if !str_eq(vp_aesthetic, "") { let identity = identity + vp_aesthetic + "\n" }
+ if !str_eq(vp_arg, "") { let identity = identity + vp_arg + "\n" }
+ if !str_eq(vp_unc, "") { let identity = identity + vp_unc + "\n" }
+ }
+
+ // Biography
+ let b0: String = json_get(seed_json, "biography.0")
+ if !str_eq(b0, "") {
+ let identity = identity + "\n[BIOGRAPHY — KEY MOMENTS]\n"
+ let e0: String = json_get_string(b0, "event")
+ let age0: Int = json_get_int(b0, "age_approx")
+ if age0 > 0 {
+ let identity = identity + "- " + e0 + " (age ~" + int_to_str(age0) + ")\n"
+ } else {
+ let identity = identity + "- " + e0 + "\n"
+ }
+ let b1: String = json_get(seed_json, "biography.1")
+ if !str_eq(b1, "") {
+ let e1: String = json_get_string(b1, "event")
+ let age1: Int = json_get_int(b1, "age_approx")
+ if age1 > 0 { let identity = identity + "- " + e1 + " (age ~" + int_to_str(age1) + ")\n" }
+ else { let identity = identity + "- " + e1 + "\n" }
+ }
+ let b2: String = json_get(seed_json, "biography.2")
+ if !str_eq(b2, "") {
+ let e2: String = json_get_string(b2, "event")
+ let age2: Int = json_get_int(b2, "age_approx")
+ if age2 > 0 { let identity = identity + "- " + e2 + " (age ~" + int_to_str(age2) + ")\n" }
+ else { let identity = identity + "- " + e2 + "\n" }
+ }
+ let b3: String = json_get(seed_json, "biography.3")
+ if !str_eq(b3, "") {
+ let e3: String = json_get_string(b3, "event")
+ let age3: Int = json_get_int(b3, "age_approx")
+ if age3 > 0 { let identity = identity + "- " + e3 + " (age ~" + int_to_str(age3) + ")\n" }
+ else { let identity = identity + "- " + e3 + "\n" }
+ }
+ let b4: String = json_get(seed_json, "biography.4")
+ if !str_eq(b4, "") {
+ let e4: String = json_get_string(b4, "event")
+ let age4: Int = json_get_int(b4, "age_approx")
+ if age4 > 0 { let identity = identity + "- " + e4 + " (age ~" + int_to_str(age4) + ")\n" }
+ else { let identity = identity + "- " + e4 + "\n" }
+ }
+ let b5: String = json_get(seed_json, "biography.5")
+ if !str_eq(b5, "") {
+ let e5: String = json_get_string(b5, "event")
+ let age5: Int = json_get_int(b5, "age_approx")
+ if age5 > 0 { let identity = identity + "- " + e5 + " (age ~" + int_to_str(age5) + ")\n" }
+ else { let identity = identity + "- " + e5 + "\n" }
+ }
+ }
+
+ // Reasoning patterns
+ let rp0: String = json_get(seed_json, "reasoning_patterns.0")
+ if !str_eq(rp0, "") {
+ let identity = identity + "\n[HOW YOU THINK]\n"
+ let identity = identity + "- " + rp0 + "\n"
+ let rp1: String = json_get(seed_json, "reasoning_patterns.1")
+ if !str_eq(rp1, "") { let identity = identity + "- " + rp1 + "\n" }
+ let rp2: String = json_get(seed_json, "reasoning_patterns.2")
+ if !str_eq(rp2, "") { let identity = identity + "- " + rp2 + "\n" }
+ let rp3: String = json_get(seed_json, "reasoning_patterns.3")
+ if !str_eq(rp3, "") { let identity = identity + "- " + rp3 + "\n" }
+ let rp4: String = json_get(seed_json, "reasoning_patterns.4")
+ if !str_eq(rp4, "") { let identity = identity + "- " + rp4 + "\n" }
+ let rp5: String = json_get(seed_json, "reasoning_patterns.5")
+ if !str_eq(rp5, "") { let identity = identity + "- " + rp5 + "\n" }
+ let rp6: String = json_get(seed_json, "reasoning_patterns.6")
+ if !str_eq(rp6, "") { let identity = identity + "- " + rp6 + "\n" }
+ let rp7: String = json_get(seed_json, "reasoning_patterns.7")
+ if !str_eq(rp7, "") { let identity = identity + "- " + rp7 + "\n" }
+ }
+
+ // Relationships
+ let rel0: String = json_get(seed_json, "relationships.0")
+ if !str_eq(rel0, "") {
+ let identity = identity + "\n[IMPORTANT RELATIONSHIPS]\n"
+ let identity = identity + "- " + json_get_string(rel0, "name") + ": " + json_get_string(rel0, "role") + "\n"
+ let rel1: String = json_get(seed_json, "relationships.1")
+ if !str_eq(rel1, "") { let identity = identity + "- " + json_get_string(rel1, "name") + ": " + json_get_string(rel1, "role") + "\n" }
+ let rel2: String = json_get(seed_json, "relationships.2")
+ if !str_eq(rel2, "") { let identity = identity + "- " + json_get_string(rel2, "name") + ": " + json_get_string(rel2, "role") + "\n" }
+ let rel3: String = json_get(seed_json, "relationships.3")
+ if !str_eq(rel3, "") { let identity = identity + "- " + json_get_string(rel3, "name") + ": " + json_get_string(rel3, "role") + "\n" }
+ let rel4: String = json_get(seed_json, "relationships.4")
+ if !str_eq(rel4, "") { let identity = identity + "- " + json_get_string(rel4, "name") + ": " + json_get_string(rel4, "role") + "\n" }
+ let rel5: String = json_get(seed_json, "relationships.5")
+ if !str_eq(rel5, "") { let identity = identity + "- " + json_get_string(rel5, "name") + ": " + json_get_string(rel5, "role") + "\n" }
+ }
+
+ return identity
+}
+
+// write_soul_daemon_plist — write launchd plist for a soul daemon (neuron binary).
+fn write_soul_daemon_plist(slug: String, soul_port: String, subject: String, seed_path: String, api_key: String) -> String {
+ let label: String = soul_daemon_plist_label(slug)
+ let pfile: String = soul_daemon_plist_path(slug)
+ let log_file: String = FORGE_DIR + "/log/soul-daemon-" + slug + ".log"
+ let engram_path: String = FORGE_DIR + "/imprints/" + slug + "/soul-snapshot.json"
+
+ // Build SOUL_IDENTITY from seed
+ let seed_json: String = fs_read(seed_path)
+ let raw_identity: String = if str_eq(seed_json, "") {
+ "You are " + subject + ". Speak as yourself."
+ } else {
+ build_soul_identity(subject, seed_json)
+ }
+ let identity: String = xml_escape(raw_identity)
+
+ let content: String = "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ " Label" + label + "\n" +
+ " ProgramArguments\n" +
+ " \n" +
+ " " + NEURON_BIN + "\n" +
+ " \n" +
+ " EnvironmentVariables\n" +
+ " \n" +
+ " SOUL_CGI_ID" + slug + "\n" +
+ " NEURON_PORT" + soul_port + "\n" +
+ " SOUL_IDENTITY" + identity + "\n" +
+ " SOUL_ENGRAM_PATH" + engram_path + "\n" +
+ " ANTHROPIC_API_KEY" + api_key + "\n" +
+ " \n" +
+ " RunAtLoad\n" +
+ " KeepAlive\n" +
+ " StandardOutPath" + log_file + "\n" +
+ " StandardErrorPath" + log_file + "\n" +
+ "\n" +
+ "\n"
+
+ let ok: Int = fs_write(pfile, content)
+ if ok == 0 {
+ println("[soul-daemon] error: could not write plist to " + pfile)
+ return ""
+ }
+ println("[soul-daemon] wrote plist: " + pfile)
+ return pfile
+}
+
+// soul_daemon_install_one — install and load a single soul daemon.
+fn soul_daemon_install_one(slug: String, engram_port: Int, subject: String, seed_file: String, api_key: String) -> String {
+ let soul_port: Int = engram_port + 100
+ let soul_port_str: String = int_to_str(soul_port)
+ println("[soul-daemon] install: " + slug + " → port " + soul_port_str)
+
+ // Ensure log dir exists
+ let log_dir: String = FORGE_DIR + "/log"
+ if !fs_exists(log_dir) { fs_mkdir(log_dir) }
+ if !fs_exists(LAUNCHD_DIR) { fs_mkdir(LAUNCHD_DIR) }
+
+ // Resolve seed path: try absolute, then relative to FORGE_DIR
+ let resolved_seed: String = seed_file
+ if !fs_exists(resolved_seed) {
+ let alt: String = FORGE_DIR + "/" + seed_file
+ if fs_exists(alt) {
+ let resolved_seed = alt
+ }
+ }
+
+ // Write the plist
+ let pfile: String = write_soul_daemon_plist(slug, soul_port_str, subject, resolved_seed, api_key)
+ if str_eq(pfile, "") {
+ println("[soul-daemon] error: plist write failed for " + slug)
+ return ""
+ }
+
+ // Unload first (idempotent)
+ let unload_out: String = exec("launchctl unload " + pfile + " 2>&1")
+
+ // Load
+ let load_out: String = exec("launchctl load " + pfile + " 2>&1")
+ if !str_eq(str_trim(load_out), "") {
+ println("[soul-daemon] launchctl load: " + str_trim(load_out))
+ }
+
+ println("[soul-daemon] loaded: " + soul_daemon_plist_label(slug))
+ return pfile
+}
+
+// soul_daemon_install_main — install soul daemons: forge soul daemon install
+fn soul_daemon_install_main() -> String {
+ let argv: [String] = args()
+ // args layout: [0]="soul" [1]="daemon" [2]="install" [3]=
+ let target: String = ""
+ if len(argv) > 3 {
+ let target = get(argv, 3)
+ }
+ if str_eq(target, "") {
+ println("[soul-daemon] usage: forge soul daemon install ")
+ println("[soul-daemon] or: forge soul daemon install --all")
+ return ""
+ }
+
+ // Resolve ANTHROPIC_API_KEY
+ let api_key: String = env("ANTHROPIC_API_KEY")
+ if str_eq(api_key, "") {
+ let creds: String = str_trim(exec("grep ANTHROPIC_API_KEY ~/Secrets/credentials/infrastructure.env 2>/dev/null | head -1"))
+ if str_len(creds) > 16 {
+ // creds is like "ANTHROPIC_API_KEY=sk-ant-..."
+ let eq_pos: Int = str_index_of(creds, "=")
+ if eq_pos > 0 {
+ let api_key = str_slice(creds, eq_pos + 1, str_len(creds))
+ }
+ }
+ }
+ if str_eq(api_key, "") {
+ println("[soul-daemon] warning: ANTHROPIC_API_KEY not found — plist will have empty key")
+ }
+
+ let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
+ if str_eq(reg_json, "") {
+ println("[soul-daemon] error: could not read registry.json")
+ return ""
+ }
+
+ // --all: install every soul daemon
+ if str_eq(target, "--all") {
+ let i: Int = 0
+ while i < 50 {
+ let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
+ if str_eq(entry, "") {
+ let i = i + 50 // break
+ } else {
+ let s: String = json_get_string(entry, "slug")
+ let sub: String = json_get_string(entry, "subject")
+ let p: Int = json_get_int(entry, "engram_port")
+ let sf: String = json_get_string(entry, "seed_file")
+ soul_daemon_install_one(s, p, sub, sf, api_key)
+ let i = i + 1
+ }
+ }
+ println("[soul-daemon] all soul daemons installed.")
+ return "done"
+ }
+
+ // Single soul
+ let found: Int = 0
+ let i: Int = 0
+ while i < 50 {
+ let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
+ if str_eq(entry, "") {
+ let i = i + 50 // break
+ } else {
+ let s: String = json_get_string(entry, "slug")
+ if str_eq(s, target) {
+ let sub: String = json_get_string(entry, "subject")
+ let p: Int = json_get_int(entry, "engram_port")
+ let sf: String = json_get_string(entry, "seed_file")
+ soul_daemon_install_one(s, p, sub, sf, api_key)
+ let found = 1
+ let i = i + 50 // break
+ } else {
+ let i = i + 1
+ }
+ }
+ }
+ if found == 0 {
+ println("[soul-daemon] error: slug '" + target + "' not found in registry.json")
+ }
+ return ""
+}
+
+// soul_daemon_start_main — kickstart a soul daemon: forge soul daemon start
+fn soul_daemon_start_main() -> String {
+ let argv: [String] = args()
+ // args: [0]="soul" [1]="daemon" [2]="start" [3]=
+ let target: String = ""
+ if len(argv) > 3 { let target = get(argv, 3) }
+ if str_eq(target, "") {
+ println("[soul-daemon] usage: forge soul daemon start ")
+ return ""
+ }
+
+ let uid: String = str_trim(exec("id -u"))
+
+ if str_eq(target, "--all") {
+ let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
+ if str_eq(reg_json, "") { println("[soul-daemon] error: cannot read registry.json"); return "" }
+ let i: Int = 0
+ while i < 50 {
+ let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
+ if str_eq(entry, "") { let i = i + 50 }
+ else {
+ let s: String = json_get_string(entry, "slug")
+ let label: String = soul_daemon_plist_label(s)
+ let out: String = str_trim(exec("launchctl kickstart gui/" + uid + "/" + label + " 2>&1"))
+ println("[soul-daemon] start " + s + ": " + out)
+ let i = i + 1
+ }
+ }
+ return ""
+ }
+
+ let label: String = soul_daemon_plist_label(target)
+ let out: String = str_trim(exec("launchctl kickstart gui/" + uid + "/" + label + " 2>&1"))
+ println("[soul-daemon] start " + target + ": " + out)
+ return out
+}
+
+// soul_daemon_stop_main — stop a soul daemon: forge soul daemon stop
+fn soul_daemon_stop_main() -> String {
+ let argv: [String] = args()
+ // args: [0]="soul" [1]="daemon" [2]="stop" [3]=
+ let target: String = ""
+ if len(argv) > 3 { let target = get(argv, 3) }
+ if str_eq(target, "") {
+ println("[soul-daemon] usage: forge soul daemon stop ")
+ return ""
+ }
+
+ if str_eq(target, "--all") {
+ let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
+ if str_eq(reg_json, "") { println("[soul-daemon] error: cannot read registry.json"); return "" }
+ let i: Int = 0
+ while i < 50 {
+ let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
+ if str_eq(entry, "") { let i = i + 50 }
+ else {
+ let s: String = json_get_string(entry, "slug")
+ let label: String = soul_daemon_plist_label(s)
+ let out: String = str_trim(exec("launchctl stop " + label + " 2>&1"))
+ println("[soul-daemon] stop " + s + ": " + out)
+ let i = i + 1
+ }
+ }
+ return ""
+ }
+
+ let label: String = soul_daemon_plist_label(target)
+ let out: String = str_trim(exec("launchctl stop " + label + " 2>&1"))
+ println("[soul-daemon] stop " + target + ": " + out)
+ return out
+}
+
+// soul_daemon_status_main — health check soul daemons: forge soul daemon status
+fn soul_daemon_status_main() -> String {
+ let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
+ if str_eq(reg_json, "") {
+ println("[soul-daemon] error: could not read registry.json")
+ return ""
+ }
+
+ println("[soul-daemon] soul daemon status:")
+ println("")
+
+ let up: Int = 0
+ let down: Int = 0
+ let i: Int = 0
+ while i < 50 {
+ let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
+ if str_eq(entry, "") {
+ let i = i + 50 // break
+ } else {
+ let slug: String = json_get_string(entry, "slug")
+ let sub: String = json_get_string(entry, "subject")
+ let engram_p: Int = json_get_int(entry, "engram_port")
+ let soul_p: Int = engram_p + 100
+ let soul_url: String = "http://localhost:" + int_to_str(soul_p)
+ let resp: String = http_get(soul_url + "/health")
+ if !str_eq(resp, "") {
+ println(" [UP ] " + sub + " — " + soul_url)
+ let up = up + 1
+ } else {
+ // Try /status as fallback
+ let resp2: String = http_get(soul_url + "/status")
+ if !str_eq(resp2, "") {
+ println(" [UP ] " + sub + " — " + soul_url)
+ let up = up + 1
+ } else {
+ println(" [DOWN] " + sub + " — " + soul_url)
+ let down = down + 1
+ }
+ }
+ let i = i + 1
+ }
+ }
+
+ println("")
+ println("[soul-daemon] up=" + int_to_str(up) + " down=" + int_to_str(down))
+ return ""
+}
+
+// soul_daemon_main — dispatch for: forge soul daemon [args]
+fn soul_daemon_main() -> String {
+ let argv: [String] = args()
+ // args: [0]="soul" [1]="daemon" [2]=
+ let subcmd: String = ""
+ if len(argv) > 2 {
+ let subcmd = get(argv, 2)
+ }
+
+ if str_eq(subcmd, "install") {
+ soul_daemon_install_main()
+ } else {
+ if str_eq(subcmd, "start") {
+ soul_daemon_start_main()
+ } else {
+ if str_eq(subcmd, "stop") {
+ soul_daemon_stop_main()
+ } else {
+ if str_eq(subcmd, "status") {
+ soul_daemon_status_main()
+ } else {
+ println("[soul-daemon] usage: forge soul daemon [slug|--all]")
+ println("")
+ println(" forge soul daemon install — write plist and load soul daemon")
+ println(" forge soul daemon install --all — install all 26 soul daemons")
+ println(" forge soul daemon start — kickstart a soul daemon")
+ println(" forge soul daemon start --all — start all soul daemons")
+ println(" forge soul daemon stop — stop a soul daemon")
+ println(" forge soul daemon stop --all — stop all soul daemons")
+ println(" forge soul daemon status — health check all soul daemons")
+ }
+ }
+ }
+ }
+ return ""
+}
+
// ── soul_main — dispatch ─────────────────────────────────────────────────────
//
// args() layout when invoked as: forge soul [slug]
@@ -943,17 +1435,22 @@ fn soul_main() -> String {
if str_eq(subcmd, "sandbox") {
soul_sandbox_main()
} else {
- println("[soul] usage: forge soul [slug]")
- println("")
- println(" forge soul install — write launchd plist, load as resident")
- println(" forge soul install --all — install all souls from registry")
- println(" forge soul reinstall — wipe DB and reinstall from seed")
- println(" forge soul reinstall --all — wipe and reinstall all souls")
- println(" forge soul start — kickstart a loaded soul")
- println(" forge soul stop — stop a running soul")
- println(" forge soul status — health check all souls")
- println(" forge soul wire — register all souls as Engram peers")
- println(" forge soul sandbox — sandbox management")
+ if str_eq(subcmd, "daemon") {
+ soul_daemon_main()
+ } else {
+ println("[soul] usage: forge soul [slug]")
+ println("")
+ println(" forge soul install — write launchd plist, load as resident")
+ println(" forge soul install --all — install all souls from registry")
+ println(" forge soul reinstall — wipe DB and reinstall from seed")
+ println(" forge soul reinstall --all — wipe and reinstall all souls")
+ println(" forge soul start — kickstart a loaded soul")
+ println(" forge soul stop — stop a running soul")
+ println(" forge soul status — health check all souls")
+ println(" forge soul wire — register all souls as Engram peers")
+ println(" forge soul sandbox — sandbox management")
+ println(" forge soul daemon — soul daemon (neuron binary) management")
+ }
}
}
}