DHARMA network: per-soul Engram instances (8801–8819)

Each soul now gets their own isolated Engram process with a dedicated data
directory (imprints/<slug>/), port, and API key — the DHARMA network.
Neuron's Engram at 8742 is never touched.

- registry.json: add engram_db_path, engram_port, engram_url to all 19 entries
  (Bobby Anderson 8801 → Helen Keller 8819)
- launch_dharma.sh: start all 19 soul Engrams in background; supports
  single-slug filter and skip-if-running detection
- stop_dharma.sh: graceful shutdown via PID file, falls back to port scan
- reinstall_imprints.py: bulk reinstall — starts each Engram temporarily,
  installs seed, records new root_id, stops; supports --slug and --dry-run
- src/install.el: resolves soul's engram_url from registry (never 8742);
  guards against accidental writes to Neuron's shared Engram
- src/summon.el: reads engram_url per-soul from registry; passes it to soul
  server in POST body so soul connects to the right Engram; supports
  both single-soul (engram_url) and multi-soul (engram_urls array) payloads
This commit is contained in:
Will Anderson
2026-05-03 02:52:01 -05:00
parent 52b6830ab2
commit 0a15e2fa42
6 changed files with 1118 additions and 183 deletions
+155 -53
View File
@@ -5,11 +5,18 @@
// creates a root traversal node that names the imprint, and connects
// everything with typed edges. Prints the root node ID on completion.
//
// Depends on: schema.el (engram_url, engram_key, FORGE_VERSION)
// IMPORTANT: Each soul has their own Engram instance (DHARMA network).
// install_main() resolves the soul's Engram URL from registry.json using the
// engram_url field (e.g. http://localhost:8806 for Feynman). It NEVER writes
// to the shared Neuron Engram at localhost:8742.
//
// The soul's Engram must already be running (launch_dharma.sh) before
// installing. For bulk reinstall use: python3 reinstall_imprints.py
//
// Depends on: schema.el (engram_key, FORGE_VERSION)
//
// Environment:
// ENGRAM_URL engram server (default: http://localhost:8742)
// ENGRAM_API_KEY engram auth key (if set)
// ENGRAM_API_KEY engram auth key override (if set)
// Helpers
@@ -31,21 +38,23 @@ fn build_edge_body(from_id: String, to_id: String, relation: String, weight: Str
return base + ",\"_auth\":\"" + auth_key + "\"}"
}
// post_node create a node in engram and return its ID.
fn post_node(content: String, node_type: String, salience: String) -> String {
// post_node create a node in the given engram instance and return its ID.
// target_url is the soul's own Engram URL (e.g. http://localhost:8806).
fn post_node(target_url: String, content: String, node_type: String, salience: String) -> String {
let key: String = engram_key()
let body: String = build_node_body(content, node_type, salience, key)
let url: String = engram_url() + "/api/nodes"
let url: String = target_url + "/api/nodes"
let response: String = http_post_json(url, body)
if str_eq(response, "") { return "" }
return json_get_string(response, "id")
}
// post_edge connect two nodes with a typed, weighted edge.
fn post_edge(from_id: String, to_id: String, relation: String, weight: String) -> String {
// target_url is the soul's own Engram URL.
fn post_edge(target_url: String, from_id: String, to_id: String, relation: String, weight: String) -> String {
let key: String = engram_key()
let body: String = build_edge_body(from_id, to_id, relation, weight, key)
let url: String = engram_url() + "/api/edges"
let url: String = target_url + "/api/edges"
let response: String = http_post_json(url, body)
return response
}
@@ -60,7 +69,7 @@ fn safe_weight(w: String, default_w: String) -> String {
// Installers
// install_value create a Value node and connect it to the root.
fn install_value(root_id: String, value_json: String, index: Int) -> String {
fn install_value(target_url: String, root_id: String, value_json: String, index: Int) -> String {
let value_text: String = json_get_string(value_json, "value")
let grounding: String = json_get_string(value_json, "grounding")
let weight_raw: String = json_get_string(value_json, "weight")
@@ -73,19 +82,19 @@ fn install_value(root_id: String, value_json: String, index: Int) -> String {
let content = content + " | grounding: " + grounding
}
let node_id: String = post_node(content, "Identity", weight)
let node_id: String = post_node(target_url, content, "Identity", weight)
if str_eq(node_id, "") {
println("[forge] warning: failed to create value node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "has_value", weight)
post_edge(target_url, root_id, node_id, "has_value", weight)
println("[forge] value node: " + node_id + "" + value_text)
return node_id
}
// install_biography_node create a Biography node and connect it to the root.
fn install_biography_node(root_id: String, bio_json: String, index: Int) -> String {
fn install_biography_node(target_url: String, root_id: String, bio_json: String, index: Int) -> String {
let event_text: String = json_get_string(bio_json, "event")
let weight_raw: String = json_get_string(bio_json, "weight")
let weight: String = safe_weight(weight_raw, "0.7")
@@ -93,19 +102,19 @@ fn install_biography_node(root_id: String, bio_json: String, index: Int) -> Stri
if str_eq(event_text, "") { return "" }
let content: String = "BIOGRAPHY: " + event_text
let node_id: String = post_node(content, "Identity", weight)
let node_id: String = post_node(target_url, content, "Identity", weight)
if str_eq(node_id, "") {
println("[forge] warning: failed to create biography node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "formed_by", weight)
post_edge(target_url, root_id, node_id, "formed_by", weight)
println("[forge] biography node: " + node_id + "" + event_text)
return node_id
}
// install_relationship create a Relationship node and connect to root.
fn install_relationship(root_id: String, rel_json: String, index: Int) -> String {
fn install_relationship(target_url: String, root_id: String, rel_json: String, index: Int) -> String {
let name: String = json_get_string(rel_json, "name")
let role: String = json_get_string(rel_json, "role")
let weight_raw: String = json_get_string(rel_json, "weight")
@@ -118,29 +127,29 @@ fn install_relationship(root_id: String, rel_json: String, index: Int) -> String
let content = content + " (" + role + ")"
}
let node_id: String = post_node(content, "Identity", weight)
let node_id: String = post_node(target_url, content, "Identity", weight)
if str_eq(node_id, "") {
println("[forge] warning: failed to create relationship node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "relates_to", weight)
post_edge(target_url, root_id, node_id, "relates_to", weight)
println("[forge] relationship node: " + node_id + "" + name)
return node_id
}
// install_reasoning_pattern create a ReasoningPattern node and connect to root.
fn install_reasoning_pattern(root_id: String, pattern: String, index: Int) -> String {
fn install_reasoning_pattern(target_url: String, root_id: String, pattern: String, index: Int) -> String {
if str_eq(pattern, "") { return "" }
let content: String = "REASONING: " + pattern
let node_id: String = post_node(content, "Identity", "0.7")
let node_id: String = post_node(target_url, content, "Identity", "0.7")
if str_eq(node_id, "") {
println("[forge] warning: failed to create reasoning node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "reasons_with", "0.7")
post_edge(target_url, root_id, node_id, "reasons_with", "0.7")
println("[forge] reasoning node: " + node_id)
return node_id
}
@@ -172,15 +181,71 @@ fn install_main() -> String {
if str_eq(subject, "") { let subject = "unknown" }
println("[forge] subject: " + subject)
println("[forge] engram: " + engram_url())
// Resolve soul's Engram URL
// Each soul has their own Engram instance. Find it in registry.json via
// the engram_url field. If the soul isn't in the registry yet, fall back
// to the ENGRAM_URL env var (for bootstrapping new souls).
let target_url: String = ""
let reg_json: String = fs_read("registry.json")
if !str_eq(reg_json, "") {
let ri: Int = 0
while ri < 30 {
let reg_entry: String = json_get(reg_json, "imprints." + int_to_str(ri))
if str_eq(reg_entry, "") {
let ri = ri + 30 // break
} else {
let reg_subject: String = json_get_string(reg_entry, "subject")
if str_eq(reg_subject, subject) {
let reg_root: String = json_get_string(reg_entry, "engram_root_id")
if !str_eq(reg_root, "") {
println("[forge] already installed — root: " + reg_root)
println("[forge] skip. to reinstall: python3 reinstall_imprints.py --slug <slug>")
return reg_root
}
let reg_url: String = json_get_string(reg_entry, "engram_url")
if !str_eq(reg_url, "") {
let target_url = reg_url
}
let ri = ri + 30 // break after match
} else {
let ri = ri + 1
}
}
}
}
// Fall back to env var if not in registry
if str_eq(target_url, "") {
let env_url: String = env("ENGRAM_URL")
if !str_eq(env_url, "") {
let target_url = env_url
}
}
// Safety guard: refuse to install to the shared Neuron Engram
if str_eq(target_url, "") {
println("[forge] error: no engram_url found for '" + subject + "' in registry.json")
println("[forge] ensure registry.json has an engram_url entry, or run reinstall_imprints.py")
return ""
}
if str_contains(target_url, ":8742") {
println("[forge] error: refusing to install soul into Neuron's Engram (port 8742)")
println("[forge] start the DHARMA network first: ./launch_dharma.sh")
println("[forge] then retry. Each soul needs their own Engram instance.")
return ""
}
println("[forge] engram: " + target_url)
println("[forge] opening channel...")
// Create root imprint node this is the named traversal entry point
let root_content: String = "IMPRINT: " + subject + " | forge/" + FORGE_VERSION
let root_id: String = post_node(root_content, "Identity", "1.0")
let root_id: String = post_node(target_url, root_content, "Identity", "1.0")
if str_eq(root_id, "") {
println("[forge] error: failed to create root imprint node")
println("[forge] is engram running at " + engram_url() + " ?")
println("[forge] is the soul's Engram running at " + target_url + " ?")
println("[forge] start it with: ./launch_dharma.sh")
return ""
}
@@ -193,25 +258,25 @@ fn install_main() -> String {
if !str_eq(values_raw, "") {
println("[forge] installing values...")
let v0: String = json_get(seed_json, "values.0")
if !str_eq(v0, "") { install_value(root_id, v0, 0) }
if !str_eq(v0, "") { install_value(target_url, root_id, v0, 0) }
let v1: String = json_get(seed_json, "values.1")
if !str_eq(v1, "") { install_value(root_id, v1, 1) }
if !str_eq(v1, "") { install_value(target_url, root_id, v1, 1) }
let v2: String = json_get(seed_json, "values.2")
if !str_eq(v2, "") { install_value(root_id, v2, 2) }
if !str_eq(v2, "") { install_value(target_url, root_id, v2, 2) }
let v3: String = json_get(seed_json, "values.3")
if !str_eq(v3, "") { install_value(root_id, v3, 3) }
if !str_eq(v3, "") { install_value(target_url, root_id, v3, 3) }
let v4: String = json_get(seed_json, "values.4")
if !str_eq(v4, "") { install_value(root_id, v4, 4) }
if !str_eq(v4, "") { install_value(target_url, root_id, v4, 4) }
let v5: String = json_get(seed_json, "values.5")
if !str_eq(v5, "") { install_value(root_id, v5, 5) }
if !str_eq(v5, "") { install_value(target_url, root_id, v5, 5) }
let v6: String = json_get(seed_json, "values.6")
if !str_eq(v6, "") { install_value(root_id, v6, 6) }
if !str_eq(v6, "") { install_value(target_url, root_id, v6, 6) }
let v7: String = json_get(seed_json, "values.7")
if !str_eq(v7, "") { install_value(root_id, v7, 7) }
if !str_eq(v7, "") { install_value(target_url, root_id, v7, 7) }
let v8: String = json_get(seed_json, "values.8")
if !str_eq(v8, "") { install_value(root_id, v8, 8) }
if !str_eq(v8, "") { install_value(target_url, root_id, v8, 8) }
let v9: String = json_get(seed_json, "values.9")
if !str_eq(v9, "") { install_value(root_id, v9, 9) }
if !str_eq(v9, "") { install_value(target_url, root_id, v9, 9) }
}
// Install biography
@@ -219,21 +284,21 @@ fn install_main() -> String {
if !str_eq(bio_raw, "") {
println("[forge] installing biography nodes...")
let b0: String = json_get(seed_json, "biography.0")
if !str_eq(b0, "") { install_biography_node(root_id, b0, 0) }
if !str_eq(b0, "") { install_biography_node(target_url, root_id, b0, 0) }
let b1: String = json_get(seed_json, "biography.1")
if !str_eq(b1, "") { install_biography_node(root_id, b1, 1) }
if !str_eq(b1, "") { install_biography_node(target_url, root_id, b1, 1) }
let b2: String = json_get(seed_json, "biography.2")
if !str_eq(b2, "") { install_biography_node(root_id, b2, 2) }
if !str_eq(b2, "") { install_biography_node(target_url, root_id, b2, 2) }
let b3: String = json_get(seed_json, "biography.3")
if !str_eq(b3, "") { install_biography_node(root_id, b3, 3) }
if !str_eq(b3, "") { install_biography_node(target_url, root_id, b3, 3) }
let b4: String = json_get(seed_json, "biography.4")
if !str_eq(b4, "") { install_biography_node(root_id, b4, 4) }
if !str_eq(b4, "") { install_biography_node(target_url, root_id, b4, 4) }
let b5: String = json_get(seed_json, "biography.5")
if !str_eq(b5, "") { install_biography_node(root_id, b5, 5) }
if !str_eq(b5, "") { install_biography_node(target_url, root_id, b5, 5) }
let b6: String = json_get(seed_json, "biography.6")
if !str_eq(b6, "") { install_biography_node(root_id, b6, 6) }
if !str_eq(b6, "") { install_biography_node(target_url, root_id, b6, 6) }
let b7: String = json_get(seed_json, "biography.7")
if !str_eq(b7, "") { install_biography_node(root_id, b7, 7) }
if !str_eq(b7, "") { install_biography_node(target_url, root_id, b7, 7) }
}
// Install relationships
@@ -241,17 +306,17 @@ fn install_main() -> String {
if !str_eq(rel_raw, "") {
println("[forge] installing relationship nodes...")
let r0: String = json_get(seed_json, "relationships.0")
if !str_eq(r0, "") { install_relationship(root_id, r0, 0) }
if !str_eq(r0, "") { install_relationship(target_url, root_id, r0, 0) }
let r1: String = json_get(seed_json, "relationships.1")
if !str_eq(r1, "") { install_relationship(root_id, r1, 1) }
if !str_eq(r1, "") { install_relationship(target_url, root_id, r1, 1) }
let r2: String = json_get(seed_json, "relationships.2")
if !str_eq(r2, "") { install_relationship(root_id, r2, 2) }
if !str_eq(r2, "") { install_relationship(target_url, root_id, r2, 2) }
let r3: String = json_get(seed_json, "relationships.3")
if !str_eq(r3, "") { install_relationship(root_id, r3, 3) }
if !str_eq(r3, "") { install_relationship(target_url, root_id, r3, 3) }
let r4: String = json_get(seed_json, "relationships.4")
if !str_eq(r4, "") { install_relationship(root_id, r4, 4) }
if !str_eq(r4, "") { install_relationship(target_url, root_id, r4, 4) }
let r5: String = json_get(seed_json, "relationships.5")
if !str_eq(r5, "") { install_relationship(root_id, r5, 5) }
if !str_eq(r5, "") { install_relationship(target_url, root_id, r5, 5) }
}
// Install reasoning patterns
@@ -259,15 +324,15 @@ fn install_main() -> String {
if !str_eq(reasoning_raw, "") {
println("[forge] installing reasoning patterns...")
let rp0: String = json_get(seed_json, "reasoning_patterns.0")
if !str_eq(rp0, "") { install_reasoning_pattern(root_id, rp0, 0) }
if !str_eq(rp0, "") { install_reasoning_pattern(target_url, root_id, rp0, 0) }
let rp1: String = json_get(seed_json, "reasoning_patterns.1")
if !str_eq(rp1, "") { install_reasoning_pattern(root_id, rp1, 1) }
if !str_eq(rp1, "") { install_reasoning_pattern(target_url, root_id, rp1, 1) }
let rp2: String = json_get(seed_json, "reasoning_patterns.2")
if !str_eq(rp2, "") { install_reasoning_pattern(root_id, rp2, 2) }
if !str_eq(rp2, "") { install_reasoning_pattern(target_url, root_id, rp2, 2) }
let rp3: String = json_get(seed_json, "reasoning_patterns.3")
if !str_eq(rp3, "") { install_reasoning_pattern(root_id, rp3, 3) }
if !str_eq(rp3, "") { install_reasoning_pattern(target_url, root_id, rp3, 3) }
let rp4: String = json_get(seed_json, "reasoning_patterns.4")
if !str_eq(rp4, "") { install_reasoning_pattern(root_id, rp4, 4) }
if !str_eq(rp4, "") { install_reasoning_pattern(target_url, root_id, rp4, 4) }
}
// Done
@@ -275,7 +340,44 @@ fn install_main() -> String {
println("[forge] channel open.")
println("[forge] root imprint ID: " + root_id)
println("[forge] subject: " + subject)
println("[forge] traverse from: " + engram_url() + "/api/neighbors/" + root_id)
println("[forge] engram: " + target_url)
println("[forge] traverse from: " + target_url + "/api/neighbors/" + root_id)
// Registry write
// Append this imprint to registry.json so summon can find it by name.
// Includes engram_url and engram_db_path for per-soul addressing.
let slug_raw: String = str_to_lower(subject)
let slug: String = str_replace(slug_raw, " ", "-")
let new_entry: String = "{\"subject\":\"" + str_escape_json(subject) +
"\",\"slug\":\"" + slug +
"\",\"seed_file\":\"" + str_escape_json(seed_file) +
"\",\"engram_db_path\":\"imprints/" + slug +
"\",\"engram_url\":\"" + target_url +
"\",\"engram_root_id\":\"" + root_id +
"\",\"installed\":true,\"installed_at\":\"2026-05-03\"}"
// Read existing registry or create fresh
let existing_reg: String = fs_read("registry.json")
if str_eq(existing_reg, "") {
let fresh_reg: String = "{\"version\":\"1.0\",\"imprints\":[" + new_entry + "]}"
fs_write("registry.json", fresh_reg)
println("[forge] registry: created with " + subject)
} else {
// Append to imprints array find last ] and insert before it
let last_bracket: Int = str_last_index_of(existing_reg, "]")
if last_bracket > 0 {
let before: String = str_slice(existing_reg, 0, last_bracket)
let after: String = str_slice(existing_reg, last_bracket, str_len(existing_reg))
// Check if array is empty (only "[")
let trimmed: String = str_trim(before)
let last_char: String = str_slice(trimmed, str_len(trimmed) - 1, str_len(trimmed))
let separator: String = ","
if str_eq(last_char, "[") { let separator = "" }
let updated_reg: String = before + separator + new_entry + after
fs_write("registry.json", updated_reg)
println("[forge] registry: added " + subject)
}
}
return root_id
}
+248
View File
@@ -0,0 +1,248 @@
// summon.el Forge channel activation stage.
//
// Stage 4 of the Forge pipeline. Looks up an installed imprint in registry.json
// by subject name, retrieves the soul's own engram_url and engram_root_id, then
// opens a live conversation channel with the soul server.
//
// Each soul has their own Engram instance registry.json is the authoritative
// source for engram_url (e.g. http://localhost:8806 for Feynman). The shared
// Neuron Engram at localhost:8742 is never used here.
//
// Usage: forge summon "<Subject Name>"
//
// The imprint must already be installed (forge install or reinstall_imprints.py).
//
// Depends on: schema.el (engram_key, FORGE_VERSION)
//
// Environment:
// SOUL_URL soul server (default: http://localhost:7770)
// SOUL_TOKEN soul auth token (default: ntn-user-2026)
// Helpers
fn soul_url() -> String {
let u: String = env("SOUL_URL")
if str_eq(u, "") { return "http://localhost:7770" }
return u
}
fn soul_token() -> String {
let t: String = env("SOUL_TOKEN")
if str_eq(t, "") { return "ntn-user-2026" }
return t
}
// RegistryEntry data for a single soul from registry.json.
// All fields come from the per-soul record; engram_url is the soul's own
// Engram instance (e.g. http://localhost:8806), NOT the shared Neuron Engram.
// find_registry_entry look up a soul in registry.json by subject name.
// Returns the raw JSON object string for that entry, or "" if not found.
fn find_registry_entry(subject: String) -> String {
let reg_json: String = fs_read("registry.json")
if str_eq(reg_json, "") { return "" }
let i: Int = 0
while i < 30 {
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
if str_eq(entry, "") { return "" }
let reg_subject: String = json_get_string(entry, "subject")
if str_eq(reg_subject, subject) {
return entry
}
let i = i + 1
}
return ""
}
// soul_engram_url return the soul's own Engram URL from registry.json.
// Falls back to the global ENGRAM_URL env var, then the default shared URL.
// Prefer registry.json each soul has their own Engram on a dedicated port.
fn soul_engram_url(entry: String) -> String {
if !str_eq(entry, "") {
let url: String = json_get_string(entry, "engram_url")
if !str_eq(url, "") { return url }
}
// Environment override (for testing / unusual setups)
let env_url: String = env("ENGRAM_URL")
if !str_eq(env_url, "") { return env_url }
return "http://localhost:8742"
}
// find_imprint_root locate the root node ID for an imprint.
// Strategy: registry.json is authoritative (has engram_root_id per soul).
// No Engram graph search needed registry is the source of truth.
fn find_imprint_root(subject: String) -> String {
let entry: String = find_registry_entry(subject)
if str_eq(entry, "") { return "" }
let reg_root: String = json_get_string(entry, "engram_root_id")
return reg_root
}
// open_soul_channel POST to soul /api/chat to create a channel session.
// Passes the soul's own engram_url so the soul server connects to the right
// Engram instance (not the shared Neuron Engram at 8742).
fn open_soul_channel(subject: String, root_id: String, soul_engram: String) -> String {
let url: String = soul_url() + "/api/chat"
let token: String = soul_token()
let body: String = "{\"message\":\"[summon:" + str_escape_json(subject) + "]\"," +
"\"engram_root_id\":\"" + root_id + "\"," +
"\"engram_url\":\"" + soul_engram + "\"," +
"\"_auth\":\"" + token + "\"}"
let headers: Map<String, String> = {"Content-Type": "application/json", "Authorization": "Bearer " + token}
let response: String = http_post_with_headers(url, body, headers)
if str_eq(response, "") { return "" }
return response
}
// summon_one look up and activate a single imprint.
// Returns "<root_id>|<engram_url>" on success (pipe-delimited), "" on failure.
fn summon_one(subject: String) -> String {
println("[forge] summoning: " + subject)
let entry: String = find_registry_entry(subject)
if str_eq(entry, "") {
println("[forge] ERROR: imprint not found — run: forge install <seed-file>")
return ""
}
let root_id: String = json_get_string(entry, "engram_root_id")
if str_eq(root_id, "") {
println("[forge] ERROR: no engram_root_id in registry — run reinstall_imprints.py")
return ""
}
let e_url: String = soul_engram_url(entry)
println("[forge] root: " + root_id)
println("[forge] engram: " + e_url)
return root_id + "|" + e_url
}
// build_roots_array build a JSON array string from up to 8 root IDs.
fn build_roots_array(ids: [String]) -> String {
let result: String = "["
let i: Int = 0
let added: Int = 0
while i < len(ids) {
let id: String = get(ids, i)
if !str_eq(id, "") {
if added > 0 {
let result = result + ","
}
let result = result + "\"" + id + "\""
let added = added + 1
}
let i = i + 1
}
return result + "]"
}
// Main
fn summon_main() -> String {
let argv: [String] = args()
// Collect all subject names (argv[1..])
if len(argv) < 2 {
println("[forge] usage: forge summon \"<Subject>\" [\"<Subject2>\" ...]")
println("[forge] example: forge summon \"Bobby Anderson\"")
println("[forge] example: forge summon \"Alan Turing\" \"Nikola Tesla\" \"Albert Einstein\"")
return ""
}
println("[forge] soul: " + soul_url())
println("[forge] each soul has their own Engram (DHARMA network)")
println("")
// Resolve all subjects to root IDs and per-soul engram URLs.
// summon_one() returns "<root_id>|<engram_url>" or "" on failure.
let root_ids: [String] = []
let engram_urls: [String] = []
let subjects: [String] = []
let i: Int = 1
while i < len(argv) {
let subj: String = get(argv, i)
let result: String = summon_one(subj)
if !str_eq(result, "") {
// Split "<root_id>|<engram_url>" on the pipe character
let pipe_idx: Int = str_index_of(result, "|")
if pipe_idx > 0 {
let rid: String = str_slice(result, 0, pipe_idx)
let eurl: String = str_slice(result, pipe_idx + 1, str_len(result))
let root_ids = append(root_ids, rid)
let engram_urls = append(engram_urls, eurl)
let subjects = append(subjects, subj)
}
}
let i = i + 1
}
if len(root_ids) == 0 {
println("[forge] no imprints found. Run: forge inspect")
return ""
}
// Build roots array for multi-imprint channel
let roots_json: String = build_roots_array(root_ids)
let subject_list: String = ""
let j: Int = 0
while j < len(subjects) {
if j > 0 { let subject_list = subject_list + ", " }
let subject_list = subject_list + get(subjects, j)
let j = j + 1
}
println("")
println("[forge] opening channel...")
// For single-soul summon: pass engram_url directly.
// For multi-soul: pass engram_urls as a JSON array so soul server can
// address each soul's Engram independently.
let url: String = soul_url() + "/api/chat"
let token: String = soul_token()
let body: String = ""
if len(root_ids) == 1 {
let single_engram: String = get(engram_urls, 0)
let body = "{\"message\":\"[summon:" + str_escape_json(subject_list) + "]\"," +
"\"engram_root_ids\":" + roots_json + "," +
"\"engram_url\":\"" + single_engram + "\"," +
"\"_auth\":\"" + token + "\"}"
} else {
// Build engram_urls JSON array for multi-soul summon
let eurls_json: String = build_roots_array(engram_urls)
let body = "{\"message\":\"[summon:" + str_escape_json(subject_list) + "]\"," +
"\"engram_root_ids\":" + roots_json + "," +
"\"engram_urls\":" + eurls_json + "," +
"\"_auth\":\"" + token + "\"}"
}
let headers: Map<String, String> = {"Content-Type": "application/json", "Authorization": "Bearer " + token}
let response: String = http_post_with_headers(url, body, headers)
println("")
if len(root_ids) == 1 {
println("[forge] " + get(subjects, 0) + " is present.")
println("[forge] engram: " + get(engram_urls, 0))
} else {
println("[forge] " + int_to_str(len(root_ids)) + " imprints present: " + subject_list)
}
println("[forge] engram roots: " + roots_json)
if str_eq(response, "") {
println("[forge] (soul offline — imprints are in Engram, channel ready when soul starts)")
return roots_json
}
let conv_id: String = json_get_string(response, "conversation_id")
if str_eq(conv_id, "") {
let conv_id = json_get_string(response, "channel_id")
}
if !str_eq(conv_id, "") {
println("[forge] conversation id: " + conv_id)
println("")
println("[forge] send a message:")
println(" curl -s " + soul_url() + "/api/chat \\")
println(" -H 'Content-Type: application/json' \\")
println(" -d '{\"conversation_id\":\"" + conv_id + "\",\"message\":\"Hello\"}'")
}
println("")
return conv_id
}