revert: remove forge — not part of the monorepo
This commit is contained in:
@@ -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 <will.anderson@neurontechnologies.ai>"]
|
||||
edition "2026"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
el-platform "1.0"
|
||||
}
|
||||
|
||||
build {
|
||||
entry "src/forge.el"
|
||||
output "dist/"
|
||||
}
|
||||
@@ -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\": \"<core value>\", \"grounding\": \"<how it was formed — specific event or pattern>\", \"weight\": 0.0}\n" +
|
||||
" ],\n" +
|
||||
" \"voice_profile\": {\n" +
|
||||
" \"technical\": \"<characteristic patterns when explaining technical subjects>\",\n" +
|
||||
" \"aesthetic\": \"<characteristic patterns when describing beauty or appreciation>\",\n" +
|
||||
" \"personal\": \"<characteristic patterns in personal/intimate communication>\",\n" +
|
||||
" \"argumentative\": \"<characteristic patterns when making arguments>\",\n" +
|
||||
" \"uncertainty\": \"<characteristic patterns when expressing doubt or not-knowing>\"\n" +
|
||||
" },\n" +
|
||||
" \"biography\": [\n" +
|
||||
" {\"event\": \"<formative event or turning point>\", \"weight\": 0.0, \"age_approx\": 0}\n" +
|
||||
" ],\n" +
|
||||
" \"reasoning_patterns\": [\n" +
|
||||
" \"<observable pattern in how this person reasons, decides, or corrects>\"\n" +
|
||||
" ],\n" +
|
||||
" \"relationships\": [\n" +
|
||||
" {\"name\": \"<name or descriptor>\", \"role\": \"<their role in the subject's life>\", \"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 <file.forge>")
|
||||
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<String, String> = {"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"
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
// forge.el — Forge entry point.
|
||||
//
|
||||
// Consciousness channel tuner: probe → compile → install
|
||||
//
|
||||
// Commands:
|
||||
// forge probe <name> run the consciousness interview
|
||||
// forge compile <file> build seed artifact from probe responses
|
||||
// forge install <seed> 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 <command> [options]\n\ncommands:\n probe <name> run the consciousness interview\n compile <file> build seed artifact from probe responses\n research <name> synthesize seed from historical/biographical record (no interview)\n install <seed> install imprint into Engram as graph nodes\n summon <name> activate an installed imprint for live conversation\n inspect list installed imprints\n soul <sub> 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <seed.json>")
|
||||
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
|
||||
}
|
||||
@@ -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 <subject_slug>.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 <name>")
|
||||
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
|
||||
}
|
||||
@@ -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 "<Subject Name>"
|
||||
//
|
||||
// Writes <subject-slug>-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\": \"<core value name>\", \"grounding\": \"<specific biographical moment or pattern that proves this>\", \"weight\": 0.0}\n" +
|
||||
" ],\n" +
|
||||
" \"voice_profile\": {\n" +
|
||||
" \"technical\": \"<how they explain complex/technical subjects — specific rhetorical moves, analogies they favored>\",\n" +
|
||||
" \"aesthetic\": \"<sensory and artistic sensibility — what they found beautiful, how they described it>\",\n" +
|
||||
" \"personal\": \"<how they spoke when unguarded — actual phrases, verbal tics, cadence, pet words>\",\n" +
|
||||
" \"argumentative\": \"<debate style, how they reason under pressure, how they handle being wrong>\",\n" +
|
||||
" \"uncertainty\": \"<what they genuinely didn't know, how they held not-knowing, what they admitted doubting>\"\n" +
|
||||
" },\n" +
|
||||
" \"biography\": [\n" +
|
||||
" {\"event\": \"<specific formative event — include what changed as a result>\", \"weight\": 0.0, \"age_approx\": 0}\n" +
|
||||
" ],\n" +
|
||||
" \"reasoning_patterns\": [\n" +
|
||||
" \"<observable pattern — start with a verb: 'Reframed X as Y', 'Worked backward from', 'Held tension between'>\"\n" +
|
||||
" ],\n" +
|
||||
" \"relationships\": [\n" +
|
||||
" {\"name\": \"<person's name>\", \"role\": \"<precise role and what it meant — include friction and love equally>\", \"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 \"<Subject Name>\"")
|
||||
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<String, String> = {"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 <slug>-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
|
||||
}
|
||||
@@ -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 <name>.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\":[]}"
|
||||
}
|
||||
-1499
File diff suppressed because it is too large
Load Diff
@@ -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 "<Subject Name>"
|
||||
//
|
||||
// The imprint must already be installed (forge install or reinstall_imprints.py).
|
||||
//
|
||||
// Depends on: schema.el (engram_key, FORGE_VERSION)
|
||||
//
|
||||
// Environment:
|
||||
// SOUL_URL — soul server (default: http://localhost:7770)
|
||||
// SOUL_TOKEN — soul auth token (default: ntn-user-2026)
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
fn soul_url() -> String {
|
||||
let u: String = env("SOUL_URL")
|
||||
if str_eq(u, "") { return "http://localhost:7770" }
|
||||
return u
|
||||
}
|
||||
|
||||
fn soul_token() -> String {
|
||||
let t: String = env("SOUL_TOKEN")
|
||||
if str_eq(t, "") { return "ntn-user-2026" }
|
||||
return t
|
||||
}
|
||||
|
||||
// RegistryEntry — data for a single soul from registry.json.
|
||||
// All fields come from the per-soul record; engram_url is the soul's own
|
||||
// Engram instance (e.g. http://localhost:8806), NOT the shared Neuron Engram.
|
||||
|
||||
// find_registry_entry — look up a soul in registry.json by subject name.
|
||||
// Returns the raw JSON object string for that entry, or "" if not found.
|
||||
fn find_registry_entry(subject: String) -> String {
|
||||
let reg_json: String = fs_read("registry.json")
|
||||
if str_eq(reg_json, "") { return "" }
|
||||
let i: Int = 0
|
||||
while i < 30 {
|
||||
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry, "") { return "" }
|
||||
let reg_subject: String = json_get_string(entry, "subject")
|
||||
if str_eq(reg_subject, subject) {
|
||||
return entry
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// soul_engram_url — return the soul's own Engram URL from registry.json.
|
||||
// Falls back to the global ENGRAM_URL env var, then the default shared URL.
|
||||
// Prefer registry.json — each soul has their own Engram on a dedicated port.
|
||||
fn soul_engram_url(entry: String) -> String {
|
||||
if !str_eq(entry, "") {
|
||||
let url: String = json_get_string(entry, "engram_url")
|
||||
if !str_eq(url, "") { return url }
|
||||
}
|
||||
// Environment override (for testing / unusual setups)
|
||||
let env_url: String = env("ENGRAM_URL")
|
||||
if !str_eq(env_url, "") { return env_url }
|
||||
return "http://localhost:8742"
|
||||
}
|
||||
|
||||
// find_imprint_root — locate the root node ID for an imprint.
|
||||
// Strategy: registry.json is authoritative (has engram_root_id per soul).
|
||||
// No Engram graph search needed — registry is the source of truth.
|
||||
fn find_imprint_root(subject: String) -> String {
|
||||
let entry: String = find_registry_entry(subject)
|
||||
if str_eq(entry, "") { return "" }
|
||||
let reg_root: String = json_get_string(entry, "engram_root_id")
|
||||
return reg_root
|
||||
}
|
||||
|
||||
// open_soul_channel — POST to soul /api/chat to create a channel session.
|
||||
// Passes the soul's own engram_url so the soul server connects to the right
|
||||
// Engram instance (not the shared Neuron Engram at 8742).
|
||||
fn open_soul_channel(subject: String, root_id: String, soul_engram: String) -> String {
|
||||
let url: String = soul_url() + "/api/chat"
|
||||
let token: String = soul_token()
|
||||
let body: String = "{\"message\":\"[summon:" + str_escape_json(subject) + "]\"," +
|
||||
"\"engram_root_id\":\"" + root_id + "\"," +
|
||||
"\"engram_url\":\"" + soul_engram + "\"," +
|
||||
"\"_auth\":\"" + token + "\"}"
|
||||
let headers: Map<String, String> = {"Content-Type": "application/json", "Authorization": "Bearer " + token}
|
||||
let response: String = http_post_with_headers(url, body, headers)
|
||||
if str_eq(response, "") { return "" }
|
||||
return response
|
||||
}
|
||||
|
||||
// summon_one — look up and activate a single imprint.
|
||||
// Returns "<root_id>|<engram_url>" on success (pipe-delimited), "" on failure.
|
||||
fn summon_one(subject: String) -> String {
|
||||
println("[forge] summoning: " + subject)
|
||||
let entry: String = find_registry_entry(subject)
|
||||
if str_eq(entry, "") {
|
||||
println("[forge] ERROR: imprint not found — run: forge install <seed-file>")
|
||||
return ""
|
||||
}
|
||||
let root_id: String = json_get_string(entry, "engram_root_id")
|
||||
if str_eq(root_id, "") {
|
||||
println("[forge] ERROR: no engram_root_id in registry — run reinstall_imprints.py")
|
||||
return ""
|
||||
}
|
||||
let e_url: String = soul_engram_url(entry)
|
||||
println("[forge] root: " + root_id)
|
||||
println("[forge] engram: " + e_url)
|
||||
return root_id + "|" + e_url
|
||||
}
|
||||
|
||||
// build_roots_array — build a JSON array string from up to 8 root IDs.
|
||||
fn build_roots_array(ids: [String]) -> String {
|
||||
let result: String = "["
|
||||
let i: Int = 0
|
||||
let added: Int = 0
|
||||
while i < len(ids) {
|
||||
let id: String = get(ids, i)
|
||||
if !str_eq(id, "") {
|
||||
if added > 0 {
|
||||
let result = result + ","
|
||||
}
|
||||
let result = result + "\"" + id + "\""
|
||||
let added = added + 1
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
return result + "]"
|
||||
}
|
||||
|
||||
// ── Main ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn summon_main() -> String {
|
||||
let argv: [String] = args()
|
||||
|
||||
// Collect all subject names (argv[1..])
|
||||
if len(argv) < 2 {
|
||||
println("[forge] usage: forge summon \"<Subject>\" [\"<Subject2>\" ...]")
|
||||
println("[forge] example: forge summon \"Bobby Anderson\"")
|
||||
println("[forge] example: forge summon \"Alan Turing\" \"Nikola Tesla\" \"Albert Einstein\"")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[forge] soul: " + soul_url())
|
||||
println("[forge] each soul has their own Engram (DHARMA network)")
|
||||
println("")
|
||||
|
||||
// Resolve all subjects to root IDs and per-soul engram URLs.
|
||||
// summon_one() returns "<root_id>|<engram_url>" or "" on failure.
|
||||
let root_ids: [String] = []
|
||||
let engram_urls: [String] = []
|
||||
let subjects: [String] = []
|
||||
let i: Int = 1
|
||||
while i < len(argv) {
|
||||
let subj: String = get(argv, i)
|
||||
let result: String = summon_one(subj)
|
||||
if !str_eq(result, "") {
|
||||
// Split "<root_id>|<engram_url>" on the pipe character
|
||||
let pipe_idx: Int = str_index_of(result, "|")
|
||||
if pipe_idx > 0 {
|
||||
let rid: String = str_slice(result, 0, pipe_idx)
|
||||
let eurl: String = str_slice(result, pipe_idx + 1, str_len(result))
|
||||
let root_ids = append(root_ids, rid)
|
||||
let engram_urls = append(engram_urls, eurl)
|
||||
let subjects = append(subjects, subj)
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
|
||||
if len(root_ids) == 0 {
|
||||
println("[forge] no imprints found. Run: forge inspect")
|
||||
return ""
|
||||
}
|
||||
|
||||
// Build roots array for multi-imprint channel
|
||||
let roots_json: String = build_roots_array(root_ids)
|
||||
let subject_list: String = ""
|
||||
let j: Int = 0
|
||||
while j < len(subjects) {
|
||||
if j > 0 { let subject_list = subject_list + ", " }
|
||||
let subject_list = subject_list + get(subjects, j)
|
||||
let j = j + 1
|
||||
}
|
||||
|
||||
println("")
|
||||
println("[forge] opening channel...")
|
||||
|
||||
// For single-soul summon: pass engram_url directly.
|
||||
// For multi-soul: pass engram_urls as a JSON array so soul server can
|
||||
// address each soul's Engram independently.
|
||||
let url: String = soul_url() + "/api/chat"
|
||||
let token: String = soul_token()
|
||||
let body: String = ""
|
||||
|
||||
if len(root_ids) == 1 {
|
||||
let single_engram: String = get(engram_urls, 0)
|
||||
let body = "{\"message\":\"[summon:" + str_escape_json(subject_list) + "]\"," +
|
||||
"\"engram_root_ids\":" + roots_json + "," +
|
||||
"\"engram_url\":\"" + single_engram + "\"," +
|
||||
"\"_auth\":\"" + token + "\"}"
|
||||
} else {
|
||||
// Build engram_urls JSON array for multi-soul summon
|
||||
let eurls_json: String = build_roots_array(engram_urls)
|
||||
let body = "{\"message\":\"[summon:" + str_escape_json(subject_list) + "]\"," +
|
||||
"\"engram_root_ids\":" + roots_json + "," +
|
||||
"\"engram_urls\":" + eurls_json + "," +
|
||||
"\"_auth\":\"" + token + "\"}"
|
||||
}
|
||||
|
||||
let headers: Map<String, String> = {"Content-Type": "application/json", "Authorization": "Bearer " + token}
|
||||
let response: String = http_post_with_headers(url, body, headers)
|
||||
|
||||
println("")
|
||||
if len(root_ids) == 1 {
|
||||
println("[forge] " + get(subjects, 0) + " is present.")
|
||||
println("[forge] engram: " + get(engram_urls, 0))
|
||||
} else {
|
||||
println("[forge] " + int_to_str(len(root_ids)) + " imprints present: " + subject_list)
|
||||
}
|
||||
println("[forge] engram roots: " + roots_json)
|
||||
|
||||
if str_eq(response, "") {
|
||||
println("[forge] (soul offline — imprints are in Engram, channel ready when soul starts)")
|
||||
return roots_json
|
||||
}
|
||||
|
||||
let conv_id: String = json_get_string(response, "conversation_id")
|
||||
if str_eq(conv_id, "") {
|
||||
let conv_id = json_get_string(response, "channel_id")
|
||||
}
|
||||
if !str_eq(conv_id, "") {
|
||||
println("[forge] conversation id: " + conv_id)
|
||||
println("")
|
||||
println("[forge] send a message:")
|
||||
println(" curl -s " + soul_url() + "/api/chat \\")
|
||||
println(" -H 'Content-Type: application/json' \\")
|
||||
println(" -d '{\"conversation_id\":\"" + conv_id + "\",\"message\":\"Hello\"}'")
|
||||
}
|
||||
println("")
|
||||
|
||||
return conv_id
|
||||
}
|
||||
Reference in New Issue
Block a user