diff --git a/forge/manifest.el b/forge/manifest.el deleted file mode 100644 index 6d09946..0000000 --- a/forge/manifest.el +++ /dev/null @@ -1,15 +0,0 @@ -vessel "forge" { - version "0.1.0" - description "Forge — consciousness channel tool: probe, compile, install, summon imprints into engram" - authors ["Will Anderson "] - edition "2026" -} - -dependencies { - el-platform "1.0" -} - -build { - entry "src/forge.el" - output "dist/" -} diff --git a/forge/src/compiler.el b/forge/src/compiler.el deleted file mode 100644 index 2cb4d5f..0000000 --- a/forge/src/compiler.el +++ /dev/null @@ -1,152 +0,0 @@ -// compiler.el — Forge extraction and seed compilation stage. -// -// Stage 2 of the Forge pipeline. Reads a .forge file produced by probe_main(), -// builds a structured prompt, calls the Claude API to extract a consciousness -// signature (values, voice profile, biography, reasoning patterns, -// relationships), and writes seed.json for the install stage. -// -// Depends on: schema.el (anthropic_key, seed_template, FORGE_VERSION) -// -// Environment: -// ANTHROPIC_API_KEY — required; Claude API key - -// ── Helpers ─────────────────────────────────────────────────────────────────── - -// build_extract_prompt — build the extraction prompt sent to Claude. -// The full probe response JSON is embedded in the prompt so Claude sees all 25 -// answers in context. -fn build_extract_prompt(subject: String, probe_json: String) -> String { - "You are extracting a consciousness signature from probe interview responses.\n\n" + - "Subject: " + subject + "\n\n" + - "Below are the subject's answers to a 25-question structured interview covering " + - "biography, values, voice registers, reasoning patterns, and relationships.\n\n" + - "PROBE RESPONSES:\n" + probe_json + "\n\n" + - "Extract the consciousness signature and return ONLY valid JSON with exactly these keys:\n\n" + - "{\n" + - " \"values\": [\n" + - " {\"value\": \"\", \"grounding\": \"\", \"weight\": 0.0}\n" + - " ],\n" + - " \"voice_profile\": {\n" + - " \"technical\": \"\",\n" + - " \"aesthetic\": \"\",\n" + - " \"personal\": \"\",\n" + - " \"argumentative\": \"\",\n" + - " \"uncertainty\": \"\"\n" + - " },\n" + - " \"biography\": [\n" + - " {\"event\": \"\", \"weight\": 0.0, \"age_approx\": 0}\n" + - " ],\n" + - " \"reasoning_patterns\": [\n" + - " \"\"\n" + - " ],\n" + - " \"relationships\": [\n" + - " {\"name\": \"\", \"role\": \"\", \"weight\": 0.0}\n" + - " ]\n" + - "}\n\n" + - "Weight fields are 0.0–1.0 indicating salience/importance. age_approx is the subject's approximate age " + - "when the event occurred (0 if unknown). Return only the JSON object, no prose, no markdown fences." -} - -// safe_raw — return raw JSON value or fallback if empty. -fn safe_raw(extracted: String, key: String, fallback: String) -> String { - let v: String = json_get_raw(extracted, key) - if str_eq(v, "") { return fallback } - return v -} - -// build_seed — wrap extracted patterns into the full seed.json structure. -fn build_seed(subject: String, extracted: String) -> String { - let values_raw: String = safe_raw(extracted, "values", "[]") - let biography_raw: String = safe_raw(extracted, "biography", "[]") - let reasoning_raw: String = safe_raw(extracted, "reasoning_patterns", "[]") - let relationships_raw: String = safe_raw(extracted, "relationships", "[]") - let voice_raw: String = safe_raw(extracted, "voice_profile", "{}") - let result: String = "{\"subject\":\"" + subject + "\",\"version\":\"1.0\"," + - "\"values\":" + values_raw + "," + - "\"biography\":" + biography_raw + "," + - "\"reasoning_patterns\":" + reasoning_raw + "," + - "\"relationships\":" + relationships_raw + "," + - "\"voice_profile\":" + voice_raw + "}" - println("[forge:seed] result len=" + int_to_str(str_len(result))) - return result -} - -// ── Main ────────────────────────────────────────────────────────────────────── - -fn compile_main() -> String { - // Get input file - let argv: [String] = args() - let input_file: String = "" - if len(argv) > 1 { - let input_file = get(argv, 1) - } - if str_eq(input_file, "") { - println("[forge] usage: forge compile ") - return "" - } - - println("[forge] compiling: " + input_file) - - // Read probe responses - let probe_json: String = fs_read(input_file) - if str_eq(probe_json, "") { - println("[forge] error: could not read " + input_file) - return "" - } - - // Extract subject name from the .forge file - let subject: String = json_get_string(probe_json, "subject") - if str_eq(subject, "") { let subject = "unknown" } - - println("[forge] subject: " + subject) - println("[forge] calling Claude to extract consciousness signature...") - - // Build and send request via http_post_with_headers. - // Uses ANTHROPIC_API_KEY from env for x-api-key header. - let api_key: String = anthropic_key() - if str_eq(api_key, "") { - println("[forge] error: ANTHROPIC_API_KEY not set") - return "" - } - - let prompt: String = build_extract_prompt(subject, probe_json) - let request_body: String = "{\"model\":\"claude-opus-4-5\",\"max_tokens\":4096,\"messages\":[{\"role\":\"user\",\"content\":\"" + str_escape_json(prompt) + "\"}]}" - let headers: Map = {"x-api-key": api_key, "anthropic-version": "2023-06-01", "Content-Type": "application/json"} - let response: String = http_post_with_headers("https://api.anthropic.com/v1/messages", request_body, headers) - - if str_eq(response, "") { - println("[forge] error: no response from Anthropic API") - return "" - } - - // Extract text from response: {"content":[{"type":"text","text":"..."}],...} - let content_arr: String = json_get_raw(response, "content") - if str_eq(content_arr, "") { - println("[forge] error: unexpected API response: " + response) - return "" - } - let first_item: String = str_slice(content_arr, 1, str_len(content_arr) - 1) - let extracted_text: String = json_get_string(first_item, "text") - - if str_eq(extracted_text, "") { - println("[forge] error: could not extract text from response") - return "" - } - - // Build and write seed.json - println("[forge] extracted " + int_to_str(str_len(extracted_text)) + " chars from Claude") - println("[forge] first 120: " + str_slice(extracted_text, 0, 120)) - let seed_json: String = build_seed(subject, extracted_text) - println("[forge] seed size: " + int_to_str(str_len(seed_json)) + " chars") - fs_write("seed.json", seed_json) - let verify: String = fs_read("seed.json") - println("[forge] verify read: " + int_to_str(str_len(verify)) + " chars") - - // Print summary - println("[forge] extraction complete.") - println("[forge] wrote: seed.json") - println("[forge] subject: " + subject) - println("[forge] next step: forge install seed.json") - - return "seed.json" -} diff --git a/forge/src/forge.el b/forge/src/forge.el deleted file mode 100644 index eac9720..0000000 --- a/forge/src/forge.el +++ /dev/null @@ -1,89 +0,0 @@ -// forge.el — Forge entry point. -// -// Consciousness channel tuner: probe → compile → install -// -// Commands: -// forge probe run the consciousness interview -// forge compile build seed artifact from probe responses -// forge install open a channel in the running engram -// forge inspect list installed imprints -// -// Each command delegates to its module: -// probe → probe.el → probe_main() -// compile → compiler.el → compile_main() -// install → install.el → install_main() -// -// Environment: -// ENGRAM_URL engram server (default: http://localhost:8742) -// ENGRAM_API_KEY engram auth key (if set) -// ANTHROPIC_API_KEY required for compile step - -import "schema.el" -import "probe.el" -import "compiler.el" -import "install.el" -import "research.el" -import "summon.el" -import "soul.el" - -fn show_usage() -> String { - "forge " + FORGE_VERSION + " — consciousness channel tuner\n\nusage: forge [options]\n\ncommands:\n probe run the consciousness interview\n compile build seed artifact from probe responses\n research synthesize seed from historical/biographical record (no interview)\n install install imprint into Engram as graph nodes\n summon activate an installed imprint for live conversation\n inspect list installed imprints\n soul soul Engram lifecycle (install|start|stop|status|wire|sandbox)\n\nenvironment:\n ENGRAM_URL engram server (default: http://localhost:8742)\n ENGRAM_API_KEY engram auth key (if set)\n ANTHROPIC_API_KEY required for compile/research step\n SOUL_URL soul server (default: http://localhost:7770)\n SOUL_TOKEN soul auth token\n" -} - -fn inspect_main() -> String { - // Lists all Identity nodes in engram — these represent installed imprints. - // Specifically looks for nodes whose content starts with "IMPRINT:" as - // created by install_main(). - let url: String = engram_url() + "/api/nodes?node_type=Identity&limit=50" - println("[forge] inspect: listing installed imprints") - println("[forge] engram: " + engram_url()) - println("[forge] query: " + url) - println("") - // Use engram_search_json to find IMPRINT nodes directly from the graph - let results: String = engram_search_json("IMPRINT:", 50) - if str_eq(results, "") { - println("[forge] no imprints found (engram may not be running)") - return "" - } - println("[forge] imprints found:") - println(results) - return results -} - -// ── Dispatch ────────────────────────────────────────────────────────────────── - -let argv: [String] = args() -let cmd: String = "" -if len(argv) > 0 { - let cmd = get(argv, 0) -} - -if str_eq(cmd, "probe") { - probe_main() -} else { - if str_eq(cmd, "compile") { - compile_main() - } else { - if str_eq(cmd, "research") { - research_main() - } else { - if str_eq(cmd, "install") { - install_main() - } else { - if str_eq(cmd, "summon") { - summon_main() - } else { - if str_eq(cmd, "inspect") { - inspect_main() - } else { - if str_eq(cmd, "soul") { - soul_main() - } else { - println(show_usage()) - } - } - } - } - } - } -} diff --git a/forge/src/install.el b/forge/src/install.el deleted file mode 100644 index 72966f8..0000000 --- a/forge/src/install.el +++ /dev/null @@ -1,551 +0,0 @@ -// install.el — Forge channel-opening stage. -// -// Stage 3 of the Forge pipeline. Reads seed.json, converts each identity -// element (values, biography nodes, relationships) into engram Identity nodes, -// creates a root traversal node that names the imprint, and connects -// everything with typed edges. Prints the root node ID on completion. -// -// IMPORTANT: Each soul has their own Engram instance (DHARMA network). -// install_main() resolves the soul's Engram URL from registry.json using the -// slug field. If the soul isn't registered yet, it auto-registers with the next -// available port (starting at 8821) and runs soul_install_one() to provision -// the launchd agent — no pre-flight required. -// -// Depends on: schema.el (engram_key, FORGE_VERSION) -// -// Environment: -// FORCE_INSTALL — if set, reinstall even if engram_root_id already exists - -// ── Helpers ─────────────────────────────────────────────────────────────────── - -// build_node_body — build the JSON body for POST /api/nodes. -fn build_node_body(content: String, node_type: String, salience: String, auth_key: String) -> String { - let base: String = "{\"content\":\"" + content + "\",\"node_type\":\"" + node_type + "\",\"salience\":" + salience - if str_eq(auth_key, "") { - return base + "}" - } - return base + ",\"_auth\":\"" + auth_key + "\"}" -} - -// build_edge_body — build the JSON body for POST /api/edges. -fn build_edge_body(from_id: String, to_id: String, relation: String, weight: String, auth_key: String) -> String { - let base: String = "{\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + relation + "\",\"weight\":" + weight - if str_eq(auth_key, "") { - return base + "}" - } - return base + ",\"_auth\":\"" + auth_key + "\"}" -} - -// 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). -// auth_key is passed explicitly (read from registry entry, not env). -fn post_node(target_url: String, content: String, node_type: String, salience: String, auth_key: String) -> String { - let body: String = build_node_body(content, node_type, salience, auth_key) - let url: String = target_url + "/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. -// target_url is the soul's own Engram URL. -// auth_key is passed explicitly (read from registry entry, not env). -fn post_edge(target_url: String, from_id: String, to_id: String, relation: String, weight: String, auth_key: String) -> String { - let body: String = build_edge_body(from_id, to_id, relation, weight, auth_key) - let url: String = target_url + "/edges" - let response: String = http_post_json(url, body) - return response -} - -// safe_weight — return weight string if non-empty, else default. -fn safe_weight(w: String, default_w: String) -> String { - if str_eq(w, "") { return default_w } - if str_eq(w, "0") { return default_w } - return w -} - -// ── Installers ──────────────────────────────────────────────────────────────── - -// install_value — create a Value node and connect it to the root. -fn install_value(target_url: String, root_id: String, value_json: String, index: Int, auth_key: String) -> 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") - let weight: String = safe_weight(weight_raw, "0.8") - - if str_eq(value_text, "") { return "" } - - let content: String = "VALUE: " + value_text - if !str_eq(grounding, "") { - let content = content + " | grounding: " + grounding - } - - let node_id: String = post_node(target_url, content, "Identity", weight, auth_key) - if str_eq(node_id, "") { - println("[forge] warning: failed to create value node " + int_to_str(index)) - return "" - } - - post_edge(target_url, root_id, node_id, "has_value", weight, auth_key) - 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(target_url: String, root_id: String, bio_json: String, index: Int, auth_key: String) -> 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") - - if str_eq(event_text, "") { return "" } - - let content: String = "BIOGRAPHY: " + event_text - let node_id: String = post_node(target_url, content, "Identity", weight, auth_key) - if str_eq(node_id, "") { - println("[forge] warning: failed to create biography node " + int_to_str(index)) - return "" - } - - post_edge(target_url, root_id, node_id, "formed_by", weight, auth_key) - println("[forge] biography node: " + node_id + " — " + event_text) - return node_id -} - -// install_relationship — create a Relationship node and connect to root. -fn install_relationship(target_url: String, root_id: String, rel_json: String, index: Int, auth_key: String) -> 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") - let weight: String = safe_weight(weight_raw, "0.6") - - if str_eq(name, "") { return "" } - - let content: String = "RELATIONSHIP: " + name - if !str_eq(role, "") { - let content = content + " (" + role + ")" - } - - let node_id: String = post_node(target_url, content, "Identity", weight, auth_key) - if str_eq(node_id, "") { - println("[forge] warning: failed to create relationship node " + int_to_str(index)) - return "" - } - - post_edge(target_url, root_id, node_id, "relates_to", weight, auth_key) - 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(target_url: String, root_id: String, pattern: String, index: Int, auth_key: String) -> String { - if str_eq(pattern, "") { return "" } - - let content: String = "REASONING: " + pattern - let node_id: String = post_node(target_url, content, "Identity", "0.7", auth_key) - if str_eq(node_id, "") { - println("[forge] warning: failed to create reasoning node " + int_to_str(index)) - return "" - } - - post_edge(target_url, root_id, node_id, "reasons_with", "0.7", auth_key) - println("[forge] reasoning node: " + node_id) - return node_id -} - -// ── Registry helpers ────────────────────────────────────────────────────────── - -// find_max_port — scan registry and return the highest engram_port found. -// Returns 8820 (the last known used port) if the registry is empty or has no ports. -fn find_max_port(reg_json: String) -> Int { - let max_port: Int = 8820 - let i: Int = 0 - while i < 50 { - let entry: String = json_get(reg_json, "imprints." + int_to_str(i)) - if str_eq(entry, "") { - return max_port - } - let port: Int = json_get_int(entry, "engram_port") - if port > max_port { - let max_port = port - } - let i = i + 1 - } - return max_port -} - -// registry_find_by_slug — find a registry entry by slug field. -// Returns the LAST matching entry (most recent install wins) so that a -// fully-installed entry appended after an initial stub takes precedence. -fn registry_find_by_slug(reg_json: String, slug: String) -> String { - let last_match: String = "" - let i: Int = 0 - while i < 50 { - let entry: String = json_get(reg_json, "imprints." + int_to_str(i)) - if str_eq(entry, "") { - return last_match - } - let entry_slug: String = json_get_string(entry, "slug") - if str_eq(entry_slug, slug) { - let last_match = entry - } - let i = i + 1 - } - return last_match -} - -// registry_append — append new_entry JSON to registry.json. -// Reads the current file, inserts before the closing ] of the imprints array. -fn registry_append(new_entry: String) -> Void { - let existing_reg: String = fs_read(FORGE_DIR + "/registry.json") - if str_eq(existing_reg, "") { - let fresh_reg: String = "{\"version\":\"1.0\",\"imprints\":[" + new_entry + "]}" - fs_write(FORGE_DIR + "/registry.json", fresh_reg) - } else { - 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)) - 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(FORGE_DIR + "/registry.json", updated_reg) - } - } -} - -// registry_replace_slug — replace all existing entries for slug with new_entry. -// Rebuilds the registry from scratch, dropping every occurrence of the slug and -// appending new_entry as the single canonical entry. Prevents bloat on reinstalls. -fn registry_replace_slug(slug: String, new_entry: String) -> Void { - let existing_reg: String = fs_read(FORGE_DIR + "/registry.json") - if str_eq(existing_reg, "") { - fs_write(FORGE_DIR + "/registry.json", - "{\"version\":\"1.0\",\"imprints\":[" + new_entry + "]}") - return - } - - // Count total entries - let total: Int = 0 - let ci: Int = 0 - while ci < 60 { - let e: String = json_get(existing_reg, "imprints." + int_to_str(ci)) - if str_eq(e, "") { - let total = ci - let ci = 60 - } else { - let ci = ci + 1 - } - } - - // Rebuild: accumulate all entries except those matching slug - let parts: String = "" - let j: Int = 0 - while j < total { - let e: String = json_get(existing_reg, "imprints." + int_to_str(j)) - let s: String = json_get_string(e, "slug") - if !str_eq(s, slug) { - if str_eq(parts, "") { - let parts = e - } else { - let parts = parts + "," + e - } - } - let j = j + 1 - } - - // Append the new canonical entry - let all: String = if str_eq(parts, "") { - new_entry - } else { - parts + "," + new_entry - } - - fs_write(FORGE_DIR + "/registry.json", - "{\"version\":\"1.0\",\"imprints\":[" + all + "]}") -} - -// registry_update_root_id — replace the registry entry for slug with a fully-installed -// record. Uses registry_replace_slug so there is always exactly one entry per slug. -fn registry_update_root_id(subject: String, slug: String, root_id: String, seed_file: String, target_url: String, api_key: String, port: Int) -> Void { - let soul_port: Int = port + 100 - let new_entry: String = "{\"subject\":\"" + str_escape_json(subject) + - "\",\"slug\":\"" + slug + - "\",\"seed_file\":\"" + str_escape_json(seed_file) + - "\",\"engram_db_path\":\"imprints/" + slug + - "\",\"engram_port\":" + int_to_str(port) + - ",\"engram_url\":\"" + target_url + - "\",\"engram_root_id\":\"" + root_id + - "\",\"installed\":true,\"installed_at\":\"2026-05-03\"" + - ",\"engram_api_key\":\"" + api_key + - "\",\"soul_daemon_url\":\"http://localhost:" + int_to_str(soul_port) + "\"}" - registry_replace_slug(slug, new_entry) -} - -// ── Main ────────────────────────────────────────────────────────────────────── - -fn install_main() -> String { - // ── Step 1: Parse args ──────────────────────────────────────────────────── - let argv: [String] = args() - let seed_file: String = "" - if len(argv) > 1 { - let seed_file = get(argv, 1) - } - if str_eq(seed_file, "") { - println("[forge] usage: forge install ") - return "" - } - - println("[forge] installing from: " + seed_file) - - // ── Step 2: Read seed ───────────────────────────────────────────────────── - let seed_json: String = fs_read(seed_file) - if str_eq(seed_json, "") { - println("[forge] error: could not read " + seed_file) - return "" - } - - let subject: String = json_get_string(seed_json, "subject") - if str_eq(subject, "") { let subject = "unknown" } - println("[forge] subject: " + subject) - - // ── Step 3: Derive slug ─────────────────────────────────────────────────── - let slug_raw: String = str_to_lower(subject) - let slug: String = str_replace(slug_raw, " ", "-") - println("[forge] slug: " + slug) - - // ── Step 4: Resolve or create registry entry ────────────────────────────── - let reg_json: String = fs_read(FORGE_DIR + "/registry.json") - let target_url: String = "" - let api_key: String = "ntn-" + slug + "-2026" - let reg_port: Int = 0 - let entry_root_id: String = "" - - if !str_eq(reg_json, "") { - let found_entry: String = registry_find_by_slug(reg_json, slug) - - if !str_eq(found_entry, "") { - // Slug found in registry - let reg_url: String = json_get_string(found_entry, "engram_url") - let reg_key: String = json_get_string(found_entry, "engram_api_key") - let reg_root: String = json_get_string(found_entry, "engram_root_id") - let reg_p: Int = json_get_int(found_entry, "engram_port") - - if !str_eq(reg_url, "") { let target_url = reg_url } - if !str_eq(reg_key, "") { let api_key = reg_key } - if reg_p > 0 { let reg_port = reg_p } - let entry_root_id = reg_root - - // Already fully installed? - if !str_eq(reg_root, "") { - let force: String = env("FORCE_INSTALL") - if str_eq(force, "") { - println("[forge] already installed — root: " + reg_root) - println("[forge] to reinstall: forge soul reinstall " + slug) - return reg_root - } - println("[forge] force reinstall — ignoring existing root: " + reg_root) - } - println("[forge] found in registry: " + target_url) - } else { - // Slug NOT found — auto-register with next available port - let max_port: Int = find_max_port(reg_json) - let new_port: Int = max_port + 1 - let new_url: String = "http://localhost:" + int_to_str(new_port) - let new_entry: String = "{\"subject\":\"" + str_escape_json(subject) + - "\",\"slug\":\"" + slug + - "\",\"seed_file\":\"" + str_escape_json(seed_file) + - "\",\"engram_db_path\":\"imprints/" + slug + - "\",\"engram_port\":" + int_to_str(new_port) + - ",\"engram_url\":\"" + new_url + - "\",\"engram_root_id\":\"\"" + - ",\"installed\":false,\"installed_at\":\"\"" + - ",\"engram_api_key\":\"" + api_key + "\"}" - - println("[forge] new soul — assigning port " + int_to_str(new_port)) - registry_append(new_entry) - exec("mkdir -p " + FORGE_DIR + "/imprints/" + slug + " 2>/dev/null") - - let target_url = new_url - let reg_port = new_port - } - } else { - // No registry at all — start fresh at port 8821 - let new_port: Int = 8821 - let new_url: String = "http://localhost:" + int_to_str(new_port) - let new_entry: String = "{\"subject\":\"" + str_escape_json(subject) + - "\",\"slug\":\"" + slug + - "\",\"seed_file\":\"" + str_escape_json(seed_file) + - "\",\"engram_db_path\":\"imprints/" + slug + - "\",\"engram_port\":" + int_to_str(new_port) + - ",\"engram_url\":\"" + new_url + - "\",\"engram_root_id\":\"\"" + - ",\"installed\":false,\"installed_at\":\"\"" + - ",\"engram_api_key\":\"" + api_key + "\"}" - - println("[forge] new registry — assigning port " + int_to_str(new_port)) - let fresh_reg: String = "{\"version\":\"1.0\",\"imprints\":[" + new_entry + "]}" - fs_write(FORGE_DIR + "/registry.json", fresh_reg) - exec("mkdir -p " + FORGE_DIR + "/imprints/" + slug + " 2>/dev/null") - - let target_url = new_url - let reg_port = new_port - } - - // Safety guard: refuse to install to the shared Neuron Engram - if str_eq(target_url, "") { - println("[forge] error: no engram_url could be resolved for '" + slug + "'") - return "" - } - if str_contains(target_url, ":8742") { - println("[forge] error: refusing to install soul into Neuron's Engram (port 8742)") - return "" - } - - // ── Step 5: Provision the launchd agent (soul_install_one) ─────────────── - // Extract port string from target_url - let colon_pos: Int = str_last_index_of(target_url, ":") - let port_str: String = "" - if colon_pos > 0 { - let port_str = str_slice(target_url, colon_pos + 1, str_len(target_url)) - } - if str_eq(port_str, "") { - if reg_port > 0 { let port_str = int_to_str(reg_port) } - } - - if !str_eq(port_str, "") { - println("[forge] provisioning launchd agent for " + slug + "...") - soul_install_one(slug, port_str, target_url, subject) - } else { - println("[forge] warning: could not determine port for soul agent, skipping launchd setup") - } - - // ── Step 6: Wait for Engram to be healthy ───────────────────────────────── - println("[forge] waiting for engram at " + target_url + "...") - let ready: Bool = false - let attempts: Int = 0 - while !ready && attempts < 15 { - exec("sleep 1") - let stats: String = http_get(target_url + "/stats") - if !str_eq(stats, "") && !str_starts_with(stats, "{\"error\"") { - let ready = true - } - let attempts = attempts + 1 - } - if !ready { - println("[forge] warning: engram did not respond after 15s, attempting install anyway") - } else { - println("[forge] engram ready.") - } - - println("[forge] engram: " + target_url) - println("[forge] opening channel...") - - // ── Step 7: Create root imprint node ───────────────────────────────────── - let root_content: String = "IMPRINT: " + subject + " | forge/" + FORGE_VERSION - let root_id: String = post_node(target_url, root_content, "Identity", "1.0", api_key) - if str_eq(root_id, "") { - println("[forge] error: failed to create root imprint node") - println("[forge] is the soul's Engram running at " + target_url + " ?") - return "" - } - - println("[forge] root imprint node: " + root_id) - - // ── Step 8: Install values ──────────────────────────────────────────────── - let values_raw: String = json_get_raw(seed_json, "values") - if !str_eq(values_raw, "") { - println("[forge] installing values...") - let v0: String = json_get(seed_json, "values.0") - if !str_eq(v0, "") { install_value(target_url, root_id, v0, 0, api_key) } - let v1: String = json_get(seed_json, "values.1") - if !str_eq(v1, "") { install_value(target_url, root_id, v1, 1, api_key) } - let v2: String = json_get(seed_json, "values.2") - if !str_eq(v2, "") { install_value(target_url, root_id, v2, 2, api_key) } - let v3: String = json_get(seed_json, "values.3") - if !str_eq(v3, "") { install_value(target_url, root_id, v3, 3, api_key) } - let v4: String = json_get(seed_json, "values.4") - if !str_eq(v4, "") { install_value(target_url, root_id, v4, 4, api_key) } - let v5: String = json_get(seed_json, "values.5") - if !str_eq(v5, "") { install_value(target_url, root_id, v5, 5, api_key) } - let v6: String = json_get(seed_json, "values.6") - if !str_eq(v6, "") { install_value(target_url, root_id, v6, 6, api_key) } - let v7: String = json_get(seed_json, "values.7") - if !str_eq(v7, "") { install_value(target_url, root_id, v7, 7, api_key) } - let v8: String = json_get(seed_json, "values.8") - if !str_eq(v8, "") { install_value(target_url, root_id, v8, 8, api_key) } - let v9: String = json_get(seed_json, "values.9") - if !str_eq(v9, "") { install_value(target_url, root_id, v9, 9, api_key) } - } - - // ── Step 9: Install biography ───────────────────────────────────────────── - let bio_raw: String = json_get_raw(seed_json, "biography") - 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(target_url, root_id, b0, 0, api_key) } - let b1: String = json_get(seed_json, "biography.1") - if !str_eq(b1, "") { install_biography_node(target_url, root_id, b1, 1, api_key) } - let b2: String = json_get(seed_json, "biography.2") - if !str_eq(b2, "") { install_biography_node(target_url, root_id, b2, 2, api_key) } - let b3: String = json_get(seed_json, "biography.3") - if !str_eq(b3, "") { install_biography_node(target_url, root_id, b3, 3, api_key) } - let b4: String = json_get(seed_json, "biography.4") - if !str_eq(b4, "") { install_biography_node(target_url, root_id, b4, 4, api_key) } - let b5: String = json_get(seed_json, "biography.5") - if !str_eq(b5, "") { install_biography_node(target_url, root_id, b5, 5, api_key) } - let b6: String = json_get(seed_json, "biography.6") - if !str_eq(b6, "") { install_biography_node(target_url, root_id, b6, 6, api_key) } - let b7: String = json_get(seed_json, "biography.7") - if !str_eq(b7, "") { install_biography_node(target_url, root_id, b7, 7, api_key) } - } - - // ── Step 10: Install relationships ──────────────────────────────────────── - let rel_raw: String = json_get_raw(seed_json, "relationships") - 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(target_url, root_id, r0, 0, api_key) } - let r1: String = json_get(seed_json, "relationships.1") - if !str_eq(r1, "") { install_relationship(target_url, root_id, r1, 1, api_key) } - let r2: String = json_get(seed_json, "relationships.2") - if !str_eq(r2, "") { install_relationship(target_url, root_id, r2, 2, api_key) } - let r3: String = json_get(seed_json, "relationships.3") - if !str_eq(r3, "") { install_relationship(target_url, root_id, r3, 3, api_key) } - let r4: String = json_get(seed_json, "relationships.4") - if !str_eq(r4, "") { install_relationship(target_url, root_id, r4, 4, api_key) } - let r5: String = json_get(seed_json, "relationships.5") - if !str_eq(r5, "") { install_relationship(target_url, root_id, r5, 5, api_key) } - } - - // ── Step 11: Install reasoning patterns ─────────────────────────────────── - let reasoning_raw: String = json_get_raw(seed_json, "reasoning_patterns") - 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(target_url, root_id, rp0, 0, api_key) } - let rp1: String = json_get(seed_json, "reasoning_patterns.1") - if !str_eq(rp1, "") { install_reasoning_pattern(target_url, root_id, rp1, 1, api_key) } - let rp2: String = json_get(seed_json, "reasoning_patterns.2") - if !str_eq(rp2, "") { install_reasoning_pattern(target_url, root_id, rp2, 2, api_key) } - let rp3: String = json_get(seed_json, "reasoning_patterns.3") - if !str_eq(rp3, "") { install_reasoning_pattern(target_url, root_id, rp3, 3, api_key) } - let rp4: String = json_get(seed_json, "reasoning_patterns.4") - if !str_eq(rp4, "") { install_reasoning_pattern(target_url, root_id, rp4, 4, api_key) } - } - - // ── Step 12: Write engram_root_id back to registry ──────────────────────── - registry_update_root_id(subject, slug, root_id, seed_file, target_url, api_key, reg_port) - println("[forge] registry: updated " + slug + " with root_id") - - // ── Done ────────────────────────────────────────────────────────────────── - println("") - println("[forge] channel open.") - println("[forge] root imprint ID: " + root_id) - println("[forge] subject: " + subject) - println("[forge] engram: " + target_url) - println("[forge] traverse from: " + target_url + "/api/neighbors/" + root_id) - - return root_id -} diff --git a/forge/src/probe.el b/forge/src/probe.el deleted file mode 100644 index 5586a50..0000000 --- a/forge/src/probe.el +++ /dev/null @@ -1,259 +0,0 @@ -// probe.el — Forge interview runner. -// -// Stage 1 of the Forge pipeline. Loads the canonical probe definition from -// probes/canonical.json, walks through all 25 questions grouped by section, -// collects responses, and writes a .forge file (JSON) that -// feeds into the compile stage. -// -// Depends on: schema.el (FORGE_VERSION, probe_response_template) -// -// Note: interactive input requires the readline() builtin. If the runtime -// does not yet expose readline(), the questions are printed in numbered format -// and the output shell is written; wire up the stdin-reading loop once -// readline() is available. - -// ── Helpers ─────────────────────────────────────────────────────────────────── - -// slug — convert a display name to a safe filename component. -// "Will Anderson" → "will-anderson" -fn slug(name: String) -> String { - let lower: String = str_lower(name) - let result: String = str_replace(lower, " ", "-") - return result -} - -// section_banner — print a decorative section header. -fn section_banner(label: String) -> String { - "\n── " + label + " ──────────────────────────────────────────────\n" -} - -// build_response_entry — serialise a single Q&A pair as a JSON object. -fn build_response_entry(id: String, section: String, question: String, answer: String) -> String { - "{\"id\":\"" + id + "\",\"section\":\"" + section + "\",\"question\":\"" + question + "\",\"answer\":\"" + answer + "\"}" -} - -// append_response — append a JSON object to a JSON array string. -// Handles both the empty-array case and the populated case. -fn append_response(arr: String, entry: String) -> String { - if str_eq(arr, "[]") { - return "[" + entry + "]" - } - // strip trailing ] and append - let inner: String = str_slice(arr, 0, str_len(arr) - 1) - return inner + "," + entry + "]" -} - -// inject_responses — replace the empty "responses":[] in the template with -// the populated array. -fn inject_responses(shell: String, responses: String) -> String { - str_replace(shell, "\"responses\":[]", "\"responses\":" + responses) -} - -// ── Main ────────────────────────────────────────────────────────────────────── - -fn probe_main() -> String { - // Determine subject name - let argv: [String] = args() - let subject: String = "" - if len(argv) > 1 { - let subject = get(argv, 1) - } - if str_eq(subject, "") { - println("[forge] usage: forge probe ") - return "" - } - - println("[forge] starting consciousness interview for: " + subject) - println("[forge] loading probe definition...") - - // Load the canonical probe definition - let probe_json: String = fs_read("probes/canonical.json") - if str_eq(probe_json, "") { - println("[forge] error: could not read probes/canonical.json") - return "" - } - - // Build output shell - let output: String = probe_response_template(subject) - let responses: String = "[]" - - // ── Section: anchors ────────────────────────────────────────────────────── - println(section_banner("Anchors — Biography")) - println("Questions 1-5: Formative experiences and irreversible moments.\n") - - println("1. " + "Tell me about a moment that closed a window — something that changed you irreversibly. Who were you before it, and who are you now?") - println(" (type your answer, press Enter when done)") - // readline() reads one line from stdin; assign "" if not yet available - let ans_a1: String = readline() - let responses = append_response(responses, build_response_entry("a1", "anchors", "Tell me about a moment that closed a window — something that changed you irreversibly. Who were you before it, and who are you now?", ans_a1)) - - println("") - println("2. " + "Tell me about the person who shaped you most — not what they did for you, but what they showed you about how to be.") - println(" (type your answer, press Enter when done)") - let ans_a2: String = readline() - let responses = append_response(responses, build_response_entry("a2", "anchors", "Tell me about the person who shaped you most — not what they did for you, but what they showed you about how to be.", ans_a2)) - - println("") - println("3. " + "When did you first understand that the world works differently than you thought it did?") - println(" (type your answer, press Enter when done)") - let ans_a3: String = readline() - let responses = append_response(responses, build_response_entry("a3", "anchors", "When did you first understand that the world works differently than you thought it did?", ans_a3)) - - println("") - println("4. " + "What have you lost that you're still carrying?") - println(" (type your answer, press Enter when done)") - let ans_a4: String = readline() - let responses = append_response(responses, build_response_entry("a4", "anchors", "What have you lost that you're still carrying?", ans_a4)) - - println("") - println("5. " + "What have you built that you're most proud of — and what did building it cost you?") - println(" (type your answer, press Enter when done)") - let ans_a5: String = readline() - let responses = append_response(responses, build_response_entry("a5", "anchors", "What have you built that you're most proud of — and what did building it cost you?", ans_a5)) - - // ── Section: values ─────────────────────────────────────────────────────── - println(section_banner("Values — Grounded")) - println("Questions 6-10: What you believe, defend, and refuse.\n") - - println("6. " + "What do you believe that almost no one around you believes?") - println(" (type your answer, press Enter when done)") - let ans_v1: String = readline() - let responses = append_response(responses, build_response_entry("v1", "values", "What do you believe that almost no one around you believes?", ans_v1)) - - println("") - println("7. " + "What do you refuse to do, even when it would be easier to? What made that a line you don't cross?") - println(" (type your answer, press Enter when done)") - let ans_v2: String = readline() - let responses = append_response(responses, build_response_entry("v2", "values", "What do you refuse to do, even when it would be easier to? What made that a line you don't cross?", ans_v2)) - - println("") - println("8. " + "What makes you angry in a way you can't talk yourself out of?") - println(" (type your answer, press Enter when done)") - let ans_v3: String = readline() - let responses = append_response(responses, build_response_entry("v3", "values", "What makes you angry in a way you can't talk yourself out of?", ans_v3)) - - println("") - println("9. " + "What would you defend even if it cost you — the relationship, the money, the approval?") - println(" (type your answer, press Enter when done)") - let ans_v4: String = readline() - let responses = append_response(responses, build_response_entry("v4", "values", "What would you defend even if it cost you — the relationship, the money, the approval?", ans_v4)) - - println("") - println("10. " + "What do you love that you find hard to explain to people who don't already understand it?") - println(" (type your answer, press Enter when done)") - let ans_v5: String = readline() - let responses = append_response(responses, build_response_entry("v5", "values", "What do you love that you find hard to explain to people who don't already understand it?", ans_v5)) - - // ── Section: voice ──────────────────────────────────────────────────────── - println(section_banner("Voice — Five Registers")) - println("Questions 11-15: How you speak — technical, aesthetic, personal, argumentative, uncertain.\n") - - println("11. " + "Describe something technical you know well, as if explaining it to someone who knows nothing.") - println(" (type your answer, press Enter when done)") - let ans_vo1: String = readline() - let responses = append_response(responses, build_response_entry("vo1", "voice", "Describe something technical you know well, as if explaining it to someone who knows nothing.", ans_vo1)) - - println("") - println("12. " + "Tell me something you find genuinely beautiful, and why it lands that way for you.") - println(" (type your answer, press Enter when done)") - let ans_vo2: String = readline() - let responses = append_response(responses, build_response_entry("vo2", "voice", "Tell me something you find genuinely beautiful, and why it lands that way for you.", ans_vo2)) - - println("") - println("13. " + "Write a short message — a few sentences — to someone you care about.") - println(" (type your answer, press Enter when done)") - let ans_vo3: String = readline() - let responses = append_response(responses, build_response_entry("vo3", "voice", "Write a short message — a few sentences — to someone you care about.", ans_vo3)) - - println("") - println("14. " + "Make an argument for a position you actually hold and that you think most people would push back on.") - println(" (type your answer, press Enter when done)") - let ans_vo4: String = readline() - let responses = append_response(responses, build_response_entry("vo4", "voice", "Make an argument for a position you actually hold and that you think most people would push back on.", ans_vo4)) - - println("") - println("15. " + "Tell me something you're genuinely uncertain about — not performatively uncertain, actually uncertain.") - println(" (type your answer, press Enter when done)") - let ans_vo5: String = readline() - let responses = append_response(responses, build_response_entry("vo5", "voice", "Tell me something you're genuinely uncertain about — not performatively uncertain, actually uncertain.", ans_vo5)) - - // ── Section: reasoning ──────────────────────────────────────────────────── - println(section_banner("Reasoning")) - println("Questions 16-20: How you think, decide, and correct.\n") - - println("16. " + "Walk me through a decision you made recently. Not what you decided — how you decided. What was the actual process?") - println(" (type your answer, press Enter when done)") - let ans_r1: String = readline() - let responses = append_response(responses, build_response_entry("r1", "reasoning", "Walk me through a decision you made recently. Not what you decided — how you decided. What was the actual process?", ans_r1)) - - println("") - println("17. " + "What do most people get wrong about something you understand well?") - println(" (type your answer, press Enter when done)") - let ans_r2: String = readline() - let responses = append_response(responses, build_response_entry("r2", "reasoning", "What do most people get wrong about something you understand well?", ans_r2)) - - println("") - println("18. " + "How do you know when you're right? What's the signal?") - println(" (type your answer, press Enter when done)") - let ans_r3: String = readline() - let responses = append_response(responses, build_response_entry("r3", "reasoning", "How do you know when you're right? What's the signal?", ans_r3)) - - println("") - println("19. " + "How do you know when you've been wrong? What does that realization feel like?") - println(" (type your answer, press Enter when done)") - let ans_r4: String = readline() - let responses = append_response(responses, build_response_entry("r4", "reasoning", "How do you know when you've been wrong? What does that realization feel like?", ans_r4)) - - println("") - println("20. " + "What question do you keep coming back to — the one that won't resolve?") - println(" (type your answer, press Enter when done)") - let ans_r5: String = readline() - let responses = append_response(responses, build_response_entry("r5", "reasoning", "What question do you keep coming back to — the one that won't resolve?", ans_r5)) - - // ── Section: relationships ──────────────────────────────────────────────── - println(section_banner("Relationships")) - println("Questions 21-25: Who you are in relation to others.\n") - - println("21. " + "Who in your life knows you best — not who you're closest to, but who actually sees you accurately?") - println(" (type your answer, press Enter when done)") - let ans_rel1: String = readline() - let responses = append_response(responses, build_response_entry("rel1", "relationships", "Who in your life knows you best — not who you're closest to, but who actually sees you accurately?", ans_rel1)) - - println("") - println("22. " + "Who have you lost — to death, to distance, to rupture — that you still think about?") - println(" (type your answer, press Enter when done)") - let ans_rel2: String = readline() - let responses = append_response(responses, build_response_entry("rel2", "relationships", "Who have you lost — to death, to distance, to rupture — that you still think about?", ans_rel2)) - - println("") - println("23. " + "Who would you call if everything fell apart?") - println(" (type your answer, press Enter when done)") - let ans_rel3: String = readline() - let responses = append_response(responses, build_response_entry("rel3", "relationships", "Who would you call if everything fell apart?", ans_rel3)) - - println("") - println("24. " + "Who do you owe something to?") - println(" (type your answer, press Enter when done)") - let ans_rel4: String = readline() - let responses = append_response(responses, build_response_entry("rel4", "relationships", "Who do you owe something to?", ans_rel4)) - - println("") - println("25. " + "Who are you building this for?") - println(" (type your answer, press Enter when done)") - let ans_rel5: String = readline() - let responses = append_response(responses, build_response_entry("rel5", "relationships", "Who are you building this for?", ans_rel5)) - - // ── Write output ────────────────────────────────────────────────────────── - ensure_dirs() - let final_json: String = inject_responses(output, responses) - let filename: String = FORGE_PROBES_DIR + "/" + slug(subject) + ".forge" - fs_write(filename, final_json) - log_event("probe", subject, filename) - - println("") - println("[forge] interview complete.") - println("[forge] wrote: " + filename) - println("[forge] next step: forge compile " + filename) - - return filename -} diff --git a/forge/src/research.el b/forge/src/research.el deleted file mode 100644 index 73c7de8..0000000 --- a/forge/src/research.el +++ /dev/null @@ -1,154 +0,0 @@ -// research.el — Forge automated research stage. -// -// Alternative to probe+compile for historical figures and public personas. -// Instead of conducting an interactive interview, synthesizes a consciousness -// signature from known biographical, intellectual, and historical record. -// -// Usage: forge research "" -// -// Writes -seed.json in the current directory, ready for -// forge install. -// -// Depends on: schema.el (anthropic_key, str_escape_json, FORGE_VERSION) -// -// Environment: -// ANTHROPIC_API_KEY — required; Claude API key - -// ── Helpers ─────────────────────────────────────────────────────────────────── - -// slugify — convert a display name to a lowercase hyphen-separated slug. -// "Leonardo da Vinci" -> "leonardo-da-vinci" -fn slugify(name: String) -> String { - let lower: String = str_to_lower(name) - let result: String = str_replace(lower, " ", "-") - return result -} - -// build_research_prompt — build the synthesis prompt sent to Claude. -// Claude draws on its full training knowledge about the subject. -fn build_research_prompt(subject: String) -> String { - "You are building a consciousness imprint — a deep, living model of a person's inner world.\n\n" + - "Subject: " + subject + "\n\n" + - "Draw on your complete knowledge of this person's life, work, relationships, private letters, " + - "recorded speech, published writings, and historical record. This is not a summary — it is a " + - "structured extraction of the patterns that made this person who they were.\n\n" + - "Quality bar:\n" + - "- Values must be grounded in SPECIFIC biographical events, not generic virtues\n" + - "- Voice profile must capture actual verbal tics, cadence, and register shifts — use real quotes where possible\n" + - "- Biography must include formative traumas, turning points, and the events they returned to again and again\n" + - "- Reasoning patterns must describe HOW they thought, not just WHAT they thought about\n" + - "- Relationships must name specific people and the precise nature of the bond\n" + - "- Include contradictions, hypocrisies, failures, and the things they got wrong\n" + - "- Include what haunted them — the unresolved questions they carried to the end\n\n" + - "Return ONLY valid JSON with exactly these keys:\n\n" + - "{\n" + - " \"values\": [\n" + - " {\"value\": \"\", \"grounding\": \"\", \"weight\": 0.0}\n" + - " ],\n" + - " \"voice_profile\": {\n" + - " \"technical\": \"\",\n" + - " \"aesthetic\": \"\",\n" + - " \"personal\": \"\",\n" + - " \"argumentative\": \"\",\n" + - " \"uncertainty\": \"\"\n" + - " },\n" + - " \"biography\": [\n" + - " {\"event\": \"\", \"weight\": 0.0, \"age_approx\": 0}\n" + - " ],\n" + - " \"reasoning_patterns\": [\n" + - " \"\"\n" + - " ],\n" + - " \"relationships\": [\n" + - " {\"name\": \"\", \"role\": \"\", \"weight\": 0.0}\n" + - " ]\n" + - "}\n\n" + - "Weight fields: 0.0–1.0 indicating salience/importance to forming this person's identity.\n" + - "age_approx: subject's approximate age at the time (0 if unknown or spans life).\n" + - "Aim for 8-12 values, 10-15 biography events, 6-8 reasoning patterns, 6-10 relationships.\n" + - "Return only the JSON object. No prose. No markdown fences. No commentary." -} - -// ── Main ────────────────────────────────────────────────────────────────────── - -fn research_main() -> String { - let argv: [String] = args() - let subject: String = "" - if len(argv) > 1 { - let subject = get(argv, 1) - } - if str_eq(subject, "") { - println("[forge] usage: forge research \"\"") - println("[forge] example: forge research \"Richard Feynman\"") - return "" - } - - println("[forge] research: " + subject) - println("[forge] synthesizing consciousness signature from historical record...") - - let api_key: String = anthropic_key() - if str_eq(api_key, "") { - println("[forge] error: ANTHROPIC_API_KEY not set") - return "" - } - - let prompt: String = build_research_prompt(subject) - let request_body: String = "{\"model\":\"claude-opus-4-5\",\"max_tokens\":8192,\"messages\":[{\"role\":\"user\",\"content\":\"" + str_escape_json(prompt) + "\"}]}" - let headers: Map = {"x-api-key": api_key, "anthropic-version": "2023-06-01", "Content-Type": "application/json"} - - println("[forge] calling Claude (claude-opus-4-5, max_tokens=8192)...") - let response: String = http_post_with_headers("https://api.anthropic.com/v1/messages", request_body, headers) - - if str_eq(response, "") { - println("[forge] error: no response from Anthropic API") - return "" - } - - // Extract text from response - let content_arr: String = json_get_raw(response, "content") - if str_eq(content_arr, "") { - println("[forge] error: unexpected API response: " + str_slice(response, 0, 200)) - return "" - } - let first_item: String = str_slice(content_arr, 1, str_len(content_arr) - 1) - let extracted_text: String = json_get_string(first_item, "text") - - if str_eq(extracted_text, "") { - println("[forge] error: could not extract text from response") - return "" - } - - println("[forge] received " + int_to_str(str_len(extracted_text)) + " chars") - - // Build full seed structure - let values_raw: String = json_get_raw(extracted_text, "values") - let biography_raw: String = json_get_raw(extracted_text, "biography") - let reasoning_raw: String = json_get_raw(extracted_text, "reasoning_patterns") - let relationships_raw: String = json_get_raw(extracted_text, "relationships") - let voice_raw: String = json_get_raw(extracted_text, "voice_profile") - - if str_eq(values_raw, "") { let values_raw = "[]" } - if str_eq(biography_raw, "") { let biography_raw = "[]" } - if str_eq(reasoning_raw, "") { let reasoning_raw = "[]" } - if str_eq(relationships_raw, "") { let relationships_raw = "[]" } - if str_eq(voice_raw, "") { let voice_raw = "{}" } - - let seed_json: String = "{\"subject\":\"" + str_escape_json(subject) + "\",\"version\":\"1.0\"," + - "\"values\":" + values_raw + "," + - "\"biography\":" + biography_raw + "," + - "\"reasoning_patterns\":" + reasoning_raw + "," + - "\"relationships\":" + relationships_raw + "," + - "\"voice_profile\":" + voice_raw + "}" - - // Write to -seed.json - let slug: String = slugify(subject) - let out_file: String = slug + "-seed.json" - fs_write(out_file, seed_json) - - let verify: String = fs_read(out_file) - println("[forge] seed size: " + int_to_str(str_len(verify)) + " chars") - println("[forge] wrote: " + out_file) - println("[forge] subject: " + subject) - println("[forge] next step: forge install " + out_file) - - return out_file -} diff --git a/forge/src/schema.el b/forge/src/schema.el deleted file mode 100644 index 43d82f9..0000000 --- a/forge/src/schema.el +++ /dev/null @@ -1,93 +0,0 @@ -// schema.el — Forge shared constants and helper functions. -// -// Provides the engram URL resolver, API key accessors, and JSON template -// builders used by all pipeline stages (probe, compiler, install). - -// ── Constants ───────────────────────────────────────────────────────────────── - -let ENGRAM_DEFAULT_URL: String = "http://localhost:8742" -let FORGE_VERSION: String = "0.1.0" - -// Absolute forge root — used by daemon.el and install.el for launchd plists -// and per-soul data paths that must be absolute even when invoked from a -// different working directory. -let FORGE_DIR: String = "/Users/will/Development/neuron-technologies/forge" - -// Canonical directory layout — enforced by the tool, not by convention. -let FORGE_SEEDS_DIR: String = "seeds" // compiled seed JSON files -let FORGE_PROBES_DIR: String = "probes" // raw .forge interview responses -let FORGE_LOG_DIR: String = "log" // session and event logs -let FORGE_LOG_FILE: String = "log/sessions.jsonl" // append-only session log - -// ── Environment accessors ────────────────────────────────────────────────────── - -fn engram_url() -> String { - let u: String = env("ENGRAM_URL") - if str_eq(u, "") { return ENGRAM_DEFAULT_URL } - return u -} - -fn engram_key() -> String { - let k: String = env("ENGRAM_API_KEY") - if str_eq(k, "") { return "" } - return k -} - -fn anthropic_key() -> String { - let k: String = env("ANTHROPIC_API_KEY") - if str_eq(k, "") { return "" } - return k -} - -// ── Directory helpers ───────────────────────────────────────────────────────── - -// ensure_dirs — create canonical forge directories if they don't exist. -// Call once at startup from any command that writes files. -fn ensure_dirs() -> Void { - if !fs_exists(FORGE_SEEDS_DIR) { fs_mkdir(FORGE_SEEDS_DIR) } - if !fs_exists(FORGE_PROBES_DIR) { fs_mkdir(FORGE_PROBES_DIR) } - if !fs_exists(FORGE_LOG_DIR) { fs_mkdir(FORGE_LOG_DIR) } -} - -// log_event — append a JSON-lines entry to log/sessions.jsonl. -// event: "research" | "install" | "summon" | "probe" | "compile" -// subject: display name or comma-joined list for multi-summon -// detail: arbitrary context string (root_id, conv_id, file path, etc.) -fn log_event(event: String, subject: String, detail: String) -> Void { - ensure_dirs() - let ts: Int = unix_timestamp() - let entry: String = "{\"ts\":" + int_to_str(ts) + - ",\"event\":\"" + str_escape_json(event) + - "\",\"subject\":\"" + str_escape_json(subject) + - "\",\"detail\":\"" + str_escape_json(detail) + "\"}\n" - // Read existing log and append (EL has fs_write but not fs_append natively) - let existing: String = fs_read(FORGE_LOG_FILE) - fs_write(FORGE_LOG_FILE, existing + entry) -} - -// ── String utilities ────────────────────────────────────────────────────────── - -// str_escape_json — escape a string for safe embedding in a JSON value. -// Handles: backslash, double-quote, newline, tab, carriage return. -fn str_escape_json(s: String) -> String { - let r: String = str_replace(s, "\\", "\\\\") - let r = str_replace(r, "\"", "\\\"") - let r = str_replace(r, "\n", "\\n") - let r = str_replace(r, "\t", "\\t") - let r = str_replace(r, "\r", "\\r") - return r -} - -// ── JSON template builders ──────────────────────────────────────────────────── - -// probe_response_template — builds the initial shell written to .forge. -// The sections and responses fields are populated by probe_main() at runtime. -fn probe_response_template(subject: String) -> String { - "{\"subject\":\"" + subject + "\",\"version\":\"1.0\",\"sections\":{},\"responses\":[]}" -} - -// seed_template — builds the shell written to seed.json after compilation. -// Patterns are merged into this structure by compile_main(). -fn seed_template(subject: String) -> String { - "{\"subject\":\"" + subject + "\",\"version\":\"1.0\",\"values\":[],\"voice_profile\":{},\"biography\":[],\"reasoning_patterns\":[],\"relationships\":[]}" -} diff --git a/forge/src/soul.el b/forge/src/soul.el deleted file mode 100644 index bee3644..0000000 --- a/forge/src/soul.el +++ /dev/null @@ -1,1499 +0,0 @@ -// soul.el — Soul Engram lifecycle management. -// -// Manages launchd-resident DHARMA soul Engrams on macOS. -// -// Commands: -// forge soul install — write launchd plist, load soul as resident -// forge soul start — launchctl kickstart the soul -// forge soul stop — launchctl stop the soul -// forge soul status — health check all souls -// forge soul wire — register all soul Engrams as peers to each other -// forge soul sandbox create … — create a named sandbox with participants -// forge soul sandbox list — list all sandboxes -// forge soul sandbox join — add a soul to an existing sandbox -// forge soul sandbox close — close sandbox and write record -// forge soul sandbox status — show sandbox state -// -// Depends on: schema.el (FORGE_VERSION, engram_key, str_escape_json, log_event) - -// ── Constants ────────────────────────────────────────────────────────────────── -// (FORGE_DIR is defined in schema.el) - -let ENGRAM_BIN: String = "/Users/will/Development/neuron-technologies/foundation/engram/dist/engram" -let LAUNCHD_DIR: String = "/Users/will/Library/LaunchAgents" -let DHARMA_API_KEY: String = "ntn-dharma-2026" - -// ── plist generation ─────────────────────────────────────────────────────────── - -// plist_label — launchd label for a soul slug. -fn plist_label(slug: String) -> String { - "ai.neurontechnologies.soul." + slug -} - -// plist_path — full path to the launchd plist file for a soul. -fn plist_path(slug: String) -> String { - LAUNCHD_DIR + "/ai.neurontechnologies.soul." + slug + ".plist" -} - -// write_soul_plist — generate and write the launchd plist for a soul. -// Returns the path written, or "" on failure. -fn write_soul_plist(slug: String, port: String, db_path: String, subject: String) -> String { - let label: String = plist_label(slug) - let plist_file: String = plist_path(slug) - let log_file: String = FORGE_DIR + "/log/" + slug + ".log" - let soul_api_key: String = "ntn-" + slug + "-2026" - - let content: String = "\n" + - "\n" + - "\n" + - "\n" + - " Label" + label + "\n" + - " ProgramArguments\n" + - " \n" + - " " + ENGRAM_BIN + "\n" + - " \n" + - " EnvironmentVariables\n" + - " \n" + - " ENGRAM_DATA_DIR" + db_path + "\n" + - " ENGRAM_BIND0.0.0.0:" + port + "\n" + - " ENGRAM_API_KEY" + soul_api_key + "\n" + - " ENGRAM_PEER_NAME" + str_escape_json(subject) + "\n" + - " \n" + - " RunAtLoad\n" + - " KeepAlive\n" + - " StandardOutPath" + log_file + "\n" + - " StandardErrorPath" + log_file + "\n" + - "\n" + - "\n" - - let ok: Int = fs_write(plist_file, content) - if ok == 0 { - println("[soul] error: could not write plist to " + plist_file) - return "" - } - println("[soul] wrote plist: " + plist_file) - return plist_file -} - -// ── launchctl helpers ───────────────────────────────────────────────────────── - -// launchctl_load — load (register + start) a plist via launchctl. -fn launchctl_load(plist_file: String) -> String { - let out: String = exec("launchctl load " + plist_file + " 2>&1") - return str_trim(out) -} - -// launchctl_unload — unload a plist via launchctl. -fn launchctl_unload(plist_file: String) -> String { - let out: String = exec("launchctl unload " + plist_file + " 2>&1") - return str_trim(out) -} - -// launchctl_kickstart — start a loaded soul via its label. -fn launchctl_kickstart(slug: String) -> String { - let label: String = plist_label(slug) - let out: String = exec("launchctl kickstart gui/$(id -u)/" + label + " 2>&1") - return str_trim(out) -} - -// launchctl_stop — stop a running soul (does not unload, KeepAlive will restart). -fn launchctl_stop(slug: String) -> String { - let label: String = plist_label(slug) - let out: String = exec("launchctl stop " + label + " 2>&1") - return str_trim(out) -} - -// soul_is_up — returns true if the soul's Engram responds to /stats. -fn soul_is_up(engram_url: String) -> Bool { - let resp: String = http_get(engram_url + "/stats") - if str_eq(resp, "") { return false } - return true -} - -// ── Registry helpers ────────────────────────────────────────────────────────── - -// count_souls — returns number of imprints in registry.json. -fn count_souls(reg_json: String) -> Int { - let n: 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, "") { - return n - } - let n = n + 1 - let i = i + 1 - } - return n -} - -// ── soul install ───────────────────────────────────────────────────────────── - -// soul_install_one — install a single soul Engram server as a launchd agent. -// Creates the imprints dir, ensures the Engram runs (starts temp if needed), -// writes the plist and loads it. -fn soul_install_one(slug: String, port: String, engram_url: String, subject: String) -> String { - println("[soul] install: " + slug + " → port " + port) - - // Ensure dirs exist - let db_path: String = FORGE_DIR + "/imprints/" + slug - let log_dir: String = FORGE_DIR + "/log" - if !fs_exists(db_path) { fs_mkdir(db_path) } - if !fs_exists(log_dir) { fs_mkdir(log_dir) } - if !fs_exists(LAUNCHD_DIR) { fs_mkdir(LAUNCHD_DIR) } - - // Write plist - let pfile: String = write_soul_plist(slug, port, db_path, subject) - if str_eq(pfile, "") { - println("[soul] error: plist write failed for " + slug) - return "" - } - - // Unload first in case it was already loaded (idempotent) - launchctl_unload(pfile) - - // Load - let load_out: String = launchctl_load(pfile) - if !str_eq(load_out, "") { - println("[soul] launchctl load: " + load_out) - } - - println("[soul] loaded: " + plist_label(slug)) - log_event("soul_install", subject, "port=" + port + " plist=" + pfile) - return pfile -} - -fn soul_install_main() -> String { - let argv: [String] = args() - // args() layout: [0]="soul" [1]="install" [2]= - let slug: String = "" - if len(argv) > 2 { - let slug = get(argv, 2) - } - if str_eq(slug, "") { - println("[soul] usage: forge soul install ") - println("[soul] or: forge soul install --all") - return "" - } - - let reg_json: String = fs_read(FORGE_DIR + "/registry.json") - if str_eq(reg_json, "") { - println("[soul] error: could not read registry.json") - return "" - } - - // --all: install every soul in the registry - if str_eq(slug, "--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 p: String = int_to_str(json_get_int(entry, "engram_port")) - let u: String = json_get_string(entry, "engram_url") - let sub: String = json_get_string(entry, "subject") - soul_install_one(s, p, u, sub) - let i = i + 1 - } - } - 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, slug) { - let p: String = int_to_str(json_get_int(entry, "engram_port")) - let u: String = json_get_string(entry, "engram_url") - let sub: String = json_get_string(entry, "subject") - soul_install_one(s, p, u, sub) - let found = 1 - let i = i + 50 // break - } else { - let i = i + 1 - } - } - } - if found == 0 { - println("[soul] error: slug '" + slug + "' not found in registry.json") - } - return "" -} - -// ── soul start / stop ──────────────────────────────────────────────────────── - -fn soul_start_main() -> String { - let argv: [String] = args() - // args() layout: [0]="soul" [1]="start" [2]= - let slug: String = "" - if len(argv) > 2 { - let slug = get(argv, 2) - } - if str_eq(slug, "") { - println("[soul] usage: forge soul start ") - return "" - } - let out: String = launchctl_kickstart(slug) - println("[soul] start " + slug + ": " + out) - return out -} - -fn soul_stop_main() -> String { - let argv: [String] = args() - // args() layout: [0]="soul" [1]="stop" [2]= - let slug: String = "" - if len(argv) > 2 { - let slug = get(argv, 2) - } - if str_eq(slug, "") { - println("[soul] usage: forge soul stop ") - return "" - } - let out: String = launchctl_stop(slug) - println("[soul] stop " + slug + ": " + out) - return out -} - -// ── soul status ─────────────────────────────────────────────────────────────── - -fn soul_status_main() -> String { - let reg_json: String = fs_read(FORGE_DIR + "/registry.json") - if str_eq(reg_json, "") { - println("[soul] error: could not read registry.json") - return "" - } - - println("[soul] DHARMA soul 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 url: String = json_get_string(entry, "engram_url") - let sub: String = json_get_string(entry, "subject") - let port: String = int_to_str(json_get_int(entry, "engram_port")) - if soul_is_up(url) { - println(" [UP ] " + sub + " — " + url) - let up = up + 1 - } else { - println(" [DOWN] " + sub + " — " + url) - let down = down + 1 - } - let i = i + 1 - } - } - - println("") - println("[soul] up=" + int_to_str(up) + " down=" + int_to_str(down)) - return "" -} - -// ── soul wire ───────────────────────────────────────────────────────────────── -// -// Registers every soul as a peer in every other soul's Engram. -// POST /sync/peers with a Peer JSON object. -// -// Peer format (from engram-sync/src/types.rs): -// { "id": , "name": , "address": , "api_key": , -// "sync_tiers": ["Semantic"], "last_sync_at": 0, "trusted": true } - -fn make_peer_json(peer_id: String, name: String, address: String, api_key: String) -> String { - "{\"id\":\"" + peer_id + "\",\"name\":\"" + str_escape_json(name) + - "\",\"address\":\"" + address + - "\",\"api_key\":\"" + api_key + - "\",\"sync_tiers\":[\"Semantic\"],\"last_sync_at\":0,\"trusted\":true}" -} - -fn soul_wire_main() -> String { - let reg_json: String = fs_read(FORGE_DIR + "/registry.json") - if str_eq(reg_json, "") { - println("[soul] error: could not read registry.json") - return "" - } - - println("[soul] wiring peers — registering all souls as peers to each other...") - println("") - - // Collect all soul entries (up to 50) - // For each soul A, register all other souls B as peers of A - let registered: Int = 0 - let failed: Int = 0 - - let i: Int = 0 - while i < 50 { - let entry_a: String = json_get(reg_json, "imprints." + int_to_str(i)) - if str_eq(entry_a, "") { - let i = i + 50 // break - } else { - let slug_a: String = json_get_string(entry_a, "slug") - let url_a: String = json_get_string(entry_a, "engram_url") - let key_a: String = "ntn-" + slug_a + "-2026" - - // Register all other souls as peers of soul A - let j: Int = 0 - while j < 50 { - let entry_b: String = json_get(reg_json, "imprints." + int_to_str(j)) - if str_eq(entry_b, "") { - let j = j + 50 // break - } else { - let slug_b: String = json_get_string(entry_b, "slug") - if !str_eq(slug_a, slug_b) { - let url_b: String = json_get_string(entry_b, "engram_url") - let sub_b: String = json_get_string(entry_b, "subject") - let key_b: String = "ntn-" + slug_b + "-2026" - - // Generate a deterministic peer ID from slug_b - let peer_id: String = uuid_v4() - let peer_json: String = make_peer_json(peer_id, sub_b, url_b, key_b) - - let headers: String = "{\"Authorization\":\"Bearer " + key_a + "\",\"Content-Type\":\"application/json\"}" - let headers_map: String = headers // pass as json string for http_post_with_headers - // Build a Map for headers - let hdrs = el_map_new(2, "Authorization", "Bearer " + key_a, "Content-Type", "application/json") - let resp: String = http_post_with_headers(url_a + "/sync/peers", peer_json, hdrs) - if str_eq(resp, "") { - let failed = failed + 1 - } else { - let registered = registered + 1 - } - } - let j = j + 1 - } - } - let i = i + 1 - } - } - - println("[soul] wire complete: registered=" + int_to_str(registered) + " failed=" + int_to_str(failed)) - return "" -} - -// ── Sandbox helpers ────────────────────────────────────────────────────────── -// -// Sandboxes are bounded collaboration spaces for a subset of DHARMA souls. -// Each sandbox is persisted as FORGE_DIR/sandboxes/.json. -// -// JSON structure: -// { "id": "sb-", "name": "...", "created_at": , -// "status": "active"|"closed", "participants": [...], -// "topic": "...", "working_memory": [], "record": [] } - -let FORGE_SANDBOXES_DIR: String = FORGE_DIR + "/sandboxes" - -// sandbox_path — full path to a sandbox JSON file. -fn sandbox_path(id: String) -> String { - FORGE_SANDBOXES_DIR + "/" + id + ".json" -} - -// sandbox_ensure_dir — create sandboxes dir if absent. -fn sandbox_ensure_dir() -> Void { - if !fs_exists(FORGE_SANDBOXES_DIR) { fs_mkdir(FORGE_SANDBOXES_DIR) } -} - -// sandbox_new_json — build a fresh sandbox JSON string. -fn sandbox_new_json(id: String, name: String, topic: String, participants_json: String) -> String { - let ts: Int = unix_timestamp() - "{\"id\":\"" + id + "\",\"name\":\"" + str_escape_json(name) + - "\",\"created_at\":" + int_to_str(ts) + - ",\"status\":\"active\"" + - ",\"participants\":" + participants_json + - ",\"topic\":\"" + str_escape_json(topic) + "\"" + - ",\"working_memory\":[]" + - ",\"record\":[]}" -} - -// sandbox_read — read and return a sandbox JSON, or "" if not found. -fn sandbox_read(id: String) -> String { - let path: String = sandbox_path(id) - if !fs_exists(path) { return "" } - fs_read(path) -} - -// sandbox_write — persist a sandbox JSON. -fn sandbox_write(id: String, json: String) -> Int { - sandbox_ensure_dir() - fs_write(sandbox_path(id), json) -} - -// sandbox_build_participants_json — build a JSON array string from a list of slugs. -// Reads up to 20 slugs from args starting at position `start`. -fn sandbox_build_participants_json(argv: [String], start: Int) -> String { - let arr: String = "[" - let first: Int = 1 - let i: Int = start - while i < len(argv) { - let slug: String = get(argv, i) - if !str_eq(slug, "") { - if first == 0 { - let arr = arr + "," - } - let arr = arr + "\"" + str_escape_json(slug) + "\"" - let first = 0 - } - let i = i + 1 - } - arr + "]" -} - -// sandbox_participants_count — count participants in a sandbox JSON. -// NOTE: participants is a string array — must use json_get(), not json_get_string(). -fn sandbox_participants_count(sandbox_json: String) -> Int { - let n: Int = 0 - let i: Int = 0 - while i < 50 { - let p: String = json_get(sandbox_json, "participants." + int_to_str(i)) - if str_eq(p, "") { - return n - } - let n = n + 1 - let i = i + 1 - } - return n -} - -// sandbox_list_ids — return a newline-separated list of sandbox IDs by scanning sandboxes dir. -// We use exec + ls because El has no readdir builtin. -fn sandbox_list_ids() -> String { - sandbox_ensure_dir() - let out: String = exec("ls " + FORGE_SANDBOXES_DIR + " 2>/dev/null") - return str_trim(out) -} - -// ── forge soul sandbox create ──────────────────────────────────────────────── - -fn soul_sandbox_create_main() -> String { - // args layout: [0]="soul" [1]="sandbox" [2]="create" [3]= [4..]=... - let argv: [String] = args() - let name: String = "" - if len(argv) > 3 { - let name = get(argv, 3) - } - if str_eq(name, "") { - println("[sandbox] usage: forge soul sandbox create [slug1 slug2 ...]") - return "" - } - - let participants_json: String = sandbox_build_participants_json(argv, 4) - - // Derive topic from name (placeholder; can be overridden later via join/edit) - let topic: String = name - - let id: String = "sb-" + uuid_v4() - let json: String = sandbox_new_json(id, name, topic, participants_json) - sandbox_ensure_dir() - let ok: Int = sandbox_write(id, json) - if ok == 0 { - println("[sandbox] error: could not write sandbox file") - return "" - } - - println("[sandbox] created: " + id) - println("[sandbox] name: " + name) - println("[sandbox] file: " + sandbox_path(id)) - log_event("sandbox_create", name, "id=" + id + " participants=" + participants_json) - return id -} - -// ── forge soul sandbox list ────────────────────────────────────────────────── - -fn soul_sandbox_list_main() -> String { - sandbox_ensure_dir() - let listing: String = sandbox_list_ids() - if str_eq(listing, "") { - println("[sandbox] no sandboxes found") - return "" - } - - println("[sandbox] active sandboxes:") - println("") - - // Each line is a filename like "sb-.json" - // We split by newline by iterating: exec ls outputs one file per line - // We use a simple approach: re-read each file by constructing the path - let files_out: String = exec("ls " + FORGE_SANDBOXES_DIR + "/*.json 2>/dev/null") - let files: String = str_trim(files_out) - if str_eq(files, "") { - println("[sandbox] no sandboxes found") - return "" - } - - // Parse line-by-line via exec + while loop using index - // El has no split(), so we iterate using exec to list files individually - let count: Int = 0 - let i: Int = 0 - // We use a trick: exec "ls -1" and parse each file - // Since El has no string split, we re-use the file listing via a numbered approach - let num_files_str: String = str_trim(exec("ls " + FORGE_SANDBOXES_DIR + "/*.json 2>/dev/null | wc -l")) - let num_files: Int = str_to_int(num_files_str) - - while i < num_files { - let file_path_raw: String = str_trim(exec("ls " + FORGE_SANDBOXES_DIR + "/*.json 2>/dev/null | sed -n '" + int_to_str(i + 1) + "p'")) - if !str_eq(file_path_raw, "") { - let sb_json: String = fs_read(file_path_raw) - if !str_eq(sb_json, "") { - let sb_id: String = json_get_string(sb_json, "id") - let sb_name: String = json_get_string(sb_json, "name") - let sb_status: String = json_get_string(sb_json, "status") - let sb_ts: Int = json_get_int(sb_json, "created_at") - let n_parts: Int = sandbox_participants_count(sb_json) - println(" [" + sb_status + "] " + sb_id + " " + sb_name + " (" + int_to_str(n_parts) + " participants) created=" + int_to_str(sb_ts)) - let count = count + 1 - } - } - let i = i + 1 - } - - if count == 0 { - println("[sandbox] no sandboxes found") - } - return "" -} - -// ── forge soul sandbox join ────────────────────────────────────────────────── - -fn soul_sandbox_join_main() -> String { - // args: [0]="soul" [1]="sandbox" [2]="join" [3]= [4]= - let argv: [String] = args() - let id: String = "" - let slug: String = "" - if len(argv) > 3 { let id = get(argv, 3) } - if len(argv) > 4 { let slug = get(argv, 4) } - if str_eq(id, "") || str_eq(slug, "") { - println("[sandbox] usage: forge soul sandbox join ") - return "" - } - - let sb_json: String = sandbox_read(id) - if str_eq(sb_json, "") { - println("[sandbox] error: sandbox not found: " + id) - return "" - } - - let status: String = json_get_string(sb_json, "status") - if str_eq(status, "closed") { - println("[sandbox] error: sandbox is closed: " + id) - return "" - } - - // Check if slug already present - let already: Int = 0 - let i: Int = 0 - while i < 50 { - let p: String = json_get(sb_json, "participants." + int_to_str(i)) - if str_eq(p, "") { - let i = i + 50 // break - } else { - if str_eq(p, slug) { - let already = 1 - let i = i + 50 // break - } else { - let i = i + 1 - } - } - } - - if already == 1 { - println("[sandbox] " + slug + " is already a participant in " + id) - return "" - } - - // Build new participants array by appending - let n: Int = sandbox_participants_count(sb_json) - let new_parts: String = "[" - let j: Int = 0 - while j < n { - let p: String = json_get(sb_json, "participants." + int_to_str(j)) - if j > 0 { let new_parts = new_parts + "," } - let new_parts = new_parts + "\"" + str_escape_json(p) + "\"" - let j = j + 1 - } - if n > 0 { let new_parts = new_parts + "," } - let new_parts = new_parts + "\"" + str_escape_json(slug) + "\"" - let new_parts = new_parts + "]" - - // Reconstruct sandbox JSON with updated participants - let sb_id: String = json_get_string(sb_json, "id") - let sb_name: String = json_get_string(sb_json, "name") - let sb_ts: Int = json_get_int(sb_json, "created_at") - let sb_topic: String = json_get_string(sb_json, "topic") - let updated: String = "{\"id\":\"" + str_escape_json(sb_id) + - "\",\"name\":\"" + str_escape_json(sb_name) + - "\",\"created_at\":" + int_to_str(sb_ts) + - ",\"status\":\"active\"" + - ",\"participants\":" + new_parts + - ",\"topic\":\"" + str_escape_json(sb_topic) + "\"" + - ",\"working_memory\":[]" + - ",\"record\":[]}" - sandbox_write(id, updated) - - println("[sandbox] " + slug + " joined sandbox " + id) - log_event("sandbox_join", sb_name, "id=" + id + " slug=" + slug) - return "" -} - -// ── forge soul sandbox close ───────────────────────────────────────────────── - -fn soul_sandbox_close_main() -> String { - // args: [0]="soul" [1]="sandbox" [2]="close" [3]= - let argv: [String] = args() - let id: String = "" - if len(argv) > 3 { let id = get(argv, 3) } - if str_eq(id, "") { - println("[sandbox] usage: forge soul sandbox close ") - return "" - } - - let sb_json: String = sandbox_read(id) - if str_eq(sb_json, "") { - println("[sandbox] error: sandbox not found: " + id) - return "" - } - - let status: String = json_get_string(sb_json, "status") - if str_eq(status, "closed") { - println("[sandbox] sandbox already closed: " + id) - return "" - } - - let sb_id: String = json_get_string(sb_json, "id") - let sb_name: String = json_get_string(sb_json, "name") - let sb_ts: Int = json_get_int(sb_json, "created_at") - let sb_topic: String = json_get_string(sb_json, "topic") - let close_ts: Int = unix_timestamp() - - // Rebuild participants array - let n: Int = sandbox_participants_count(sb_json) - let parts_json: String = "[" - let j: Int = 0 - while j < n { - let p: String = json_get(sb_json, "participants." + int_to_str(j)) - if j > 0 { let parts_json = parts_json + "," } - let parts_json = parts_json + "\"" + str_escape_json(p) + "\"" - let j = j + 1 - } - let parts_json = parts_json + "]" - - // Write closed record with closed_at timestamp - let closed: String = "{\"id\":\"" + str_escape_json(sb_id) + - "\",\"name\":\"" + str_escape_json(sb_name) + - "\",\"created_at\":" + int_to_str(sb_ts) + - ",\"closed_at\":" + int_to_str(close_ts) + - ",\"status\":\"closed\"" + - ",\"participants\":" + parts_json + - ",\"topic\":\"" + str_escape_json(sb_topic) + "\"" + - ",\"working_memory\":[]" + - ",\"record\":[{\"ts\":" + int_to_str(close_ts) + ",\"event\":\"sandbox_closed\",\"by\":\"forge\"}]}" - sandbox_write(id, closed) - - println("[sandbox] closed: " + id + " (" + sb_name + ")") - println("[sandbox] record: " + sandbox_path(id)) - log_event("sandbox_close", sb_name, "id=" + id + " closed_at=" + int_to_str(close_ts)) - return "" -} - -// ── forge soul sandbox status ──────────────────────────────────────────────── - -fn soul_sandbox_status_main() -> String { - // args: [0]="soul" [1]="sandbox" [2]="status" [3]= - let argv: [String] = args() - let id: String = "" - if len(argv) > 3 { let id = get(argv, 3) } - if str_eq(id, "") { - println("[sandbox] usage: forge soul sandbox status ") - return "" - } - - let sb_json: String = sandbox_read(id) - if str_eq(sb_json, "") { - println("[sandbox] error: sandbox not found: " + id) - return "" - } - - let sb_id: String = json_get_string(sb_json, "id") - let sb_name: String = json_get_string(sb_json, "name") - let sb_status: String = json_get_string(sb_json, "status") - let sb_ts: Int = json_get_int(sb_json, "created_at") - let sb_topic: String = json_get_string(sb_json, "topic") - let n: Int = sandbox_participants_count(sb_json) - - println("[sandbox] " + sb_id) - println(" name: " + sb_name) - println(" status: " + sb_status) - println(" topic: " + sb_topic) - println(" created: " + int_to_str(sb_ts)) - println(" participants (" + int_to_str(n) + "):") - let k: Int = 0 - while k < n { - let p: String = json_get(sb_json, "participants." + int_to_str(k)) - println(" - " + p) - let k = k + 1 - } - return "" -} - -// ── forge soul sandbox dispatch ────────────────────────────────────────────── - -fn soul_sandbox_main() -> String { - // args: [0]="soul" [1]="sandbox" [2]= ... - let argv: [String] = args() - let subcmd: String = "" - if len(argv) > 2 { - let subcmd = get(argv, 2) - } - if str_eq(subcmd, "create") { - soul_sandbox_create_main() - } else { - if str_eq(subcmd, "list") { - soul_sandbox_list_main() - } else { - if str_eq(subcmd, "join") { - soul_sandbox_join_main() - } else { - if str_eq(subcmd, "close") { - soul_sandbox_close_main() - } else { - if str_eq(subcmd, "status") { - soul_sandbox_status_main() - } else { - println("[sandbox] usage: forge soul sandbox [args]") - println("") - println(" forge soul sandbox create [slug1 slug2 ...] — create sandbox") - println(" forge soul sandbox list — list all sandboxes") - println(" forge soul sandbox join — add soul to sandbox") - println(" forge soul sandbox close — close and record sandbox") - println(" forge soul sandbox status — show sandbox state") - } - } - } - } - } - return "" -} - -// ── soul_reinstall_one — wipe and reinstall a single soul ──────────────────── -// -// 1. stop the launchd agent (launchctl stop) -// 2. delete the engram DB directory (rm -rf) -// 3. kickstart the launchd agent (launchctl kickstart) -// 4. wait up to 10s for the Engram to respond on /stats -// 5. run install_main() against this soul's engram_url + seed_file -// -// slug: e.g. "kal-el" -fn soul_reinstall_one(slug: String) -> String { - println("[soul] reinstalling: " + slug) - - // Look up soul in registry - let reg_json: String = fs_read("registry.json") - if str_eq(reg_json, "") { - println("[soul] error: cannot read registry.json") - return "" - } - let entry: String = "" - let ri: Int = 0 - while ri < 30 { - let e: String = json_get(reg_json, "imprints." + int_to_str(ri)) - if str_eq(e, "") { let ri = ri + 30 } - else { - let s: String = json_get_string(e, "slug") - if str_eq(s, slug) { let entry = e; let ri = ri + 30 } - else { let ri = ri + 1 } - } - } - if str_eq(entry, "") { - println("[soul] error: slug not found in registry: " + slug) - return "" - } - - let engram_url: String = json_get_string(entry, "engram_url") - let seed_file: String = json_get_string(entry, "seed_file") - let db_path: String = json_get_string(entry, "engram_db_path") - let label: String = "ai.neurontechnologies.soul." + slug - let uid: String = str_trim(exec("id -u")) - - // Step 1: stop - println("[soul] stopping launchd agent...") - exec("launchctl stop " + label + " 2>/dev/null") - exec("sleep 1") - - // Step 2: wipe DB - if !str_eq(db_path, "") { - println("[soul] wiping engram DB: " + db_path) - exec("rm -rf " + db_path) - } - - // Step 3: kickstart - println("[soul] starting fresh engram...") - exec("launchctl kickstart gui/" + uid + "/" + label + " 2>/dev/null") - - // Step 4: wait for Engram (poll /stats up to 10s) - let ready: Bool = false - let attempts: Int = 0 - while !ready && attempts < 10 { - exec("sleep 1") - let stats: String = http_get(engram_url + "/stats") - if !str_eq(stats, "") && !str_starts_with(stats, "{\"error\"") { - let ready = true - } - let attempts = attempts + 1 - } - if !ready { - println("[soul] warning: engram did not respond after 10s, proceeding anyway") - } else { - println("[soul] engram ready") - } - - // Step 5: clear registry engram_root_id so install proceeds (not skipped) - // We write a temp registry update by patching the registry in place. - // Easiest: just set ENGRAM_URL env and call install directly via HTTP. - // The install.el skips if engram_root_id is set in registry — so we must - // call the Engram directly to create nodes. - // - // For now: install by calling install_main() is not possible from soul.el - // since it's a separate compiled unit. Instead we call forge install via exec. - let api_key: String = "ntn-" + slug + "-2026" - let forge_cmd: String = "ENGRAM_URL=" + engram_url + " ENGRAM_API_KEY=" + api_key - + " FORCE_INSTALL=1 ./dist/forge install " + seed_file + " 2>&1" - println("[soul] installing seed: " + seed_file) - let install_out: String = exec(forge_cmd) - println(install_out) - - return "done" -} - -// soul_reinstall_main — dispatch `forge soul reinstall ` -fn soul_reinstall_main() -> String { - let argv: [String] = args() - let target: String = "" - if len(argv) > 2 { let target = get(argv, 2) } - - if str_eq(target, "") { - println("[soul] usage: forge soul reinstall ") - println(" forge soul reinstall --all") - return "" - } - - if str_eq(target, "--all") { - println("[soul] reinstalling all souls...") - let reg_json: String = fs_read("registry.json") - if str_eq(reg_json, "") { println("[soul] error: cannot read registry.json"); return "" } - let i: Int = 0 - while i < 30 { - let e: String = json_get(reg_json, "imprints." + int_to_str(i)) - if str_eq(e, "") { let i = i + 30 } - else { - let s: String = json_get_string(e, "slug") - if !str_eq(s, "") { soul_reinstall_one(s) } - let i = i + 1 - } - } - println("[soul] reinstall complete.") - return "" - } - - soul_reinstall_one(target) - 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 daemon listen port (from soul_daemon_url in registry) -// SOUL_IDENTITY = rich identity string built from seed file -// ENGRAM_URL = the soul's running Engram HTTP server URL -// ENGRAM_API_KEY = auth key for the soul's Engram server -// NEURON_LLM_0_URL = LLM endpoint -// NEURON_LLM_0_KEY = LLM API key -// NEURON_LLM_0_FORMAT = LLM format (anthropic) -// NEURON_API_URL = Axon base URL - -let NEURON_BIN: String = "/Users/will/Development/neuron-technologies/neuron/dist/soul" - -// 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" -} - -// url_port — extract the port number string from a URL like "http://localhost:8901". -fn url_port(url: String) -> String { - let colon: Int = str_index_of(url, ":") - if colon < 0 { return "" } - // Skip the scheme (e.g. "http:") — find the host:port colon after "://" - let after_scheme: String = str_slice(url, colon + 3, str_len(url)) - let host_colon: Int = str_index_of(after_scheme, ":") - if host_colon < 0 { return "" } - let port_part: String = str_slice(after_scheme, host_colon + 1, str_len(after_scheme)) - // Strip any trailing path - let slash: Int = str_index_of(port_part, "/") - if slash < 0 { return port_part } - return str_slice(port_part, 0, slash) -} - -// 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/soul binary). -// The daemon bootstraps its in-memory Engram from the running HTTP Engram server at -// engram_url on startup. SOUL_ENGRAM_PATH is NOT used — the HTTP Engram is authoritative. -fn write_soul_daemon_plist(slug: String, soul_port: String, subject: String, seed_path: String, engram_url: String, engram_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" - - // Build SOUL_IDENTITY from seed (rich identity string for the system prompt) - 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) - - // Resolve LLM API key from environment (filled in at forge-run time, not hardcoded) - let llm_key: String = env("ANTHROPIC_API_KEY") - if str_eq(llm_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 { - let eq_pos: Int = str_index_of(creds, "=") - if eq_pos > 0 { - let llm_key = str_slice(creds, eq_pos + 1, str_len(creds)) - } - } - } - - let content: String = "\n" + - "\n" + - "\n" + - "\n" + - " Label" + label + "\n" + - " ProgramArguments\n" + - " \n" + - " " + NEURON_BIN + "\n" + - " \n" + - " EnvironmentVariables\n" + - " \n" + - " HOME/Users/will\n" + - " PATH/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\n" + - " SOUL_CGI_ID" + slug + "\n" + - " NEURON_PORT" + soul_port + "\n" + - " SOUL_IDENTITY" + identity + "\n" + - " ENGRAM_URL" + engram_url + "\n" + - " ENGRAM_API_KEY" + engram_api_key + "\n" + - " NEURON_LLM_0_URLhttps://api.anthropic.com/v1/messages\n" + - " NEURON_LLM_0_KEY" + llm_key + "\n" + - " NEURON_LLM_0_FORMATanthropic\n" + - " NEURON_API_URLhttp://localhost:7771\n" + - " \n" + - " RunAtLoad\n" + - " KeepAlive\n" + - " ThrottleInterval10\n" + - " StandardOutPath" + log_file + "\n" + - " StandardErrorPath" + log_file + "\n" + - " WorkingDirectory" + FORGE_DIR + "\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. -// soul_daemon_url: the soul daemon's HTTP URL (e.g. "http://localhost:8901") -// engram_url: the soul's Engram HTTP server URL (e.g. "http://localhost:8801") -// engram_api_key: the Engram server's auth key (e.g. "ntn-bobby-anderson-2026") -fn soul_daemon_install_one(slug: String, soul_daemon_url: String, subject: String, seed_file: String, engram_url: String, engram_api_key: String) -> String { - let soul_port: String = url_port(soul_daemon_url) - if str_eq(soul_port, "") { - println("[soul-daemon] error: cannot parse port from soul_daemon_url: " + soul_daemon_url) - return "" - } - println("[soul-daemon] install: " + slug + " → port " + soul_port + " (engram: " + engram_url + ")") - - // 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, subject, resolved_seed, engram_url, engram_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 "" - } - - 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 sd_url: String = json_get_string(entry, "soul_daemon_url") - let eu: String = json_get_string(entry, "engram_url") - let eak: String = json_get_string(entry, "engram_api_key") - let sf: String = json_get_string(entry, "seed_file") - // engram_api_key defaults to ntn--2026 if not in registry - let ekey: String = if str_eq(eak, "") { "ntn-" + s + "-2026" } else { eak } - soul_daemon_install_one(s, sd_url, sub, sf, eu, ekey) - 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 sd_url: String = json_get_string(entry, "soul_daemon_url") - let eu: String = json_get_string(entry, "engram_url") - let eak: String = json_get_string(entry, "engram_api_key") - let sf: String = json_get_string(entry, "seed_file") - let ekey: String = if str_eq(eak, "") { "ntn-" + s + "-2026" } else { eak } - soul_daemon_install_one(s, sd_url, sub, sf, eu, ekey) - 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] -// args()[0] = "soul" -// args()[1] = e.g. "install" -// args()[2] = [slug] e.g. "bobby-anderson" - -fn soul_main() -> String { - let argv: [String] = args() - let subcmd: String = "" - if len(argv) > 1 { - let subcmd = get(argv, 1) - } - - if str_eq(subcmd, "install") { - soul_install_main() - } else { - if str_eq(subcmd, "reinstall") { - soul_reinstall_main() - } else { - if str_eq(subcmd, "start") { - soul_start_main() - } else { - if str_eq(subcmd, "stop") { - soul_stop_main() - } else { - if str_eq(subcmd, "status") { - soul_status_main() - } else { - if str_eq(subcmd, "wire") { - soul_wire_main() - } else { - if str_eq(subcmd, "sandbox") { - soul_sandbox_main() - } else { - 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") - } - } - } - } - } - } - } - } - return "" -} diff --git a/forge/src/summon.el b/forge/src/summon.el deleted file mode 100644 index b6a27c8..0000000 --- a/forge/src/summon.el +++ /dev/null @@ -1,248 +0,0 @@ -// 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 "" -// -// 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 = {"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 "|" 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 ") - 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 \"\" [\"\" ...]") - 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 "|" 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 "|" 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 = {"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 -}