// surface.el — the RESHAPED Neuron API surface. // // Design: artifact 0e828907 ("Neuron API surface reshape") + design-brief // 2b8078cf §5. Collapse ~90 functional-CRUD MCP tools into a handful of GEOMETRY // OPS over the one geometry, plus the LIVE agentic primitives already in the // engram cognition build. TYPE IS A PARAMETER, not a tool-per-noun. // // This module is ADDITIVE. It defines the new ops as functions over the engram // HTTP API (engram_url() = the isolated clone in dev; :8742 in prod). The old // noun-tools become thin aliases that call these ops (bottom of file) so every // existing caller keeps working through the transition — parity-gated by // tools/api-reshape/parity.sh. // // Idiom matches neuron/mcp-wrapper/src/main.el: http_get / http_post_json, // json_get_string/_int/_float, mcp_json_result / mcp_text_result. // // Endpoint ground-truth (verified against the live cognition binary // engram.cognition-20260814-160045; routes on branch feat/cognitive-architecture // engram/src/server.el): // write -> POST /api/nodes {content,node_type,label,salience,importance,confidence,tier,tags,_auth} // relate -> POST /api/edges {from_id,to_id,relation,weight,_auth} // read -> GET /api/nodes/ | /api/search?q&limit | /api/activate?q&depth | /api/neighbors/ | /api/nearest/?k // think -> GET /api/think?seeds=&faculty= // attend -> POST /api/attend {node,observer,salience,_auth} // ground -> POST /api/ground {claim,evidence,for_whom,_auth} // assert -> GET /api/assert?claim&for_whom&floor // learn -> POST /api/correspondence-beat {seeds,faculty,keystone,_auth} (reflexive prior calibration) // ───────────────────────────────────────────────────────────────────────────── // LAYER 1 — GEOMETRY OPS (type is a parameter) // ───────────────────────────────────────────────────────────────────────────── // read — THE VANTAGE-READ. Re-origin at a point (node id, concept, or `self`), // apply salience + recency + APERTURE, return a BOUNDED slice. `type` filters // which projection of the geometry to surface. This is CCR applied to the self; // it structurally fixes the whole-self dump (aperture caps the byte size). // // read({ vantage, type?, aperture?, faculty? }) // vantage : node id | free-text concept | "self" | "values" // type : memory|knowledge|backlog|artifact|process|self|edges|state (filter; default = mixed) // aperture: { k?: Int, depth?: Int } — the bound. small k/depth => small slice. fn op_read(args: String) -> String { let vantage: String = pick_vantage(args) let typ: String = json_get_string(args, "type") let k_raw: Int = json_get_int(args, "k") let k: Int = if k_raw > 0 { k_raw } else { 12 } // aperture default (bounded) let depth_raw: Int = json_get_int(args, "depth") let depth: Int = if depth_raw > 0 { depth_raw } else { 2 } let vid: String = resolve_named(vantage) // self/values -> keystone ids // type=edges or an id vantage -> neighborhood read (bounded by depth) if str_eq(typ, "edges") { if str_eq(vid, "") { return mcp_text_result("read(type=edges) needs a node-id vantage") } return mcp_json_result(http_get(engram_url() + "/api/neighbors/" + vid)) } // an id vantage with no type -> the node itself + its bounded neighborhood if !str_eq(vid, "") && str_eq(typ, "") { return mcp_json_result(http_get(engram_url() + "/api/neighbors/" + vid + "?depth=" + int_to_str(depth))) } // a concept vantage -> salience-ranked geometric retrieval, aperture=k // (this single spine replaces searchKnowledge/searchEntities/recall/browseKnowledge/reviewBacklog/findArtifacts... // — `type` becomes a post-filter tag rather than a separate tool) let q: String = if str_eq(vid, "") { vantage } else { vid } return mcp_json_result(http_get(engram_url() + "/api/search?q=" + url_encode(q) + "&limit=" + int_to_str(k))) } // write — add a node. `type` selects the node_type (memory|knowledge|artifact| // backlog|process|state). Replaces remember/captureKnowledge/draftArtifact/ // planWork/defineProcess/addWonderQuestion/logInternalStateEvent/recordObservation. // Identity (type=self|values) is REFUSED here — routes through intentional-cultivation. fn op_write(args: String) -> String { let content: String = json_get_string(args, "content") if str_eq(content, "") { return mcp_text_result("write: content is required") } let typ: String = json_get_string(args, "type") if identity_typed(typ) { return mcp_text_result("write: type=" + typ + " is write-protected; route identity through intentional-cultivation, not raw write") } let node_type: String = type_to_node_type(typ) // memory->Memory, knowledge->Knowledge, ... let tags: String = json_get_string(args, "tags") let imp_present: String = json_get_raw(args, "importance") let importance: Float = if str_eq(imp_present, "") { 0.5 } else { json_get_float(args, "importance") } let body: String = "{\"_auth\":\"" + engram_key() + "\",\"content\":\"" + json_escape(content) + "\",\"node_type\":\"" + node_type + "\",\"tags\":\"" + json_escape(tags) + "\",\"importance\":" + float_to_str(importance) + "}" return mcp_json_result(http_post_json(engram_url() + "/api/nodes", body)) } // relate — add a typed edge. Replaces linkEntities/linkCausal/restructureCausalGraph/pin. fn op_relate(args: String) -> String { let from_id: String = json_get_string(args, "from") let to_id: String = json_get_string(args, "to") let rel_raw: String = json_get_string(args, "relationship") let relation: String = if str_eq(rel_raw, "") { "associates" } else { rel_raw } if str_eq(from_id, "") || str_eq(to_id, "") { return mcp_text_result("relate: from and to are required") } if touches_identity(from_id) || touches_identity(to_id) { return mcp_text_result("relate: identity keystones are write-protected; route through intentional-cultivation") } let w_present: String = json_get_raw(args, "weight") let weight: Float = if str_eq(w_present, "") { 0.5 } else { json_get_float(args, "weight") } let body: String = "{\"_auth\":\"" + engram_key() + "\",\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + relation + "\",\"weight\":" + float_to_str(weight) + "}" return mcp_json_result(http_post_json(engram_url() + "/api/edges", body)) } // supersede — IMMUTABLE evolve/replace/tombstone/promote. NEVER hard-deletes. // action=evolve|supersede : write a NEW node with the new content, then // relate(new -> old, "supersedes"). Old is retained. // action=tombstone : hide from default reads (node + edges kept, recoverable). // action=promote : raise the tier (Working->Semantic / note->lesson->canonical). fn op_supersede(args: String) -> String { let id: String = json_get_string(args, "id") if str_eq(id, "") { return mcp_text_result("supersede: id is required") } if touches_identity(id) { return mcp_text_result("supersede: identity keystones are write-protected; route through intentional-cultivation") } let action_raw: String = json_get_string(args, "action") let action: String = if str_eq(action_raw, "") { "supersede" } else { action_raw } if str_eq(action, "tombstone") { // real live path: DELETE /api/nodes/ writes a tombstone MARKER node + // a "tombstones" edge and KEEPS the original (immutable; recoverable). return mcp_json_result(http_delete(engram_url() + "/api/nodes/" + id, "{\"_auth\":\"" + engram_key() + "\"}")) } // promote and evolve/supersede are both the same immutable move: write a NEW // node (carrying the new tier for promote) + a supersedes edge to the old. // There is no distinct live engram /promote route — promotion IS supersession // at a higher tier, which keeps memory immutable by construction. // evolve/supersede/promote: new node + supersedes edge (old node preserved) let content: String = json_get_string(args, "content") if str_eq(content, "") { return mcp_text_result("supersede(evolve): content is required") } let created: String = op_write(args) // reuses write (type carried through) let new_id: String = extract_result_id(created) if str_eq(new_id, "") { return created } let edge_body: String = "{\"_auth\":\"" + engram_key() + "\",\"from_id\":\"" + new_id + "\",\"to_id\":\"" + id + "\",\"relation\":\"supersedes\",\"weight\":1.0}" let e: String = http_post_json(engram_url() + "/api/edges", edge_body) return mcp_json_result("{\"new_id\":\"" + new_id + "\",\"supersedes\":\"" + id + "\",\"edge\":" + e + "}") } // ───────────────────────────────────────────────────────────────────────────── // LAYER 2 — PRIMITIVE AGENTIC TOOLS (Neuron runs itself over its own geometry) // The base verbs all agentic behavior composes from. Grounded in the LIVE // cog-arch: think is the one operation; the faculties are its steering-space // labels; attend aims attention; the correspondence-beat is the reflexive // learning loop; ground/assert are the honesty floor. // ───────────────────────────────────────────────────────────────────────────── // think — THE ONE OPERATION. A directed traversal from an anchor, steered by a // (learned) prior, whose output is a gradient. `faculty` selects the steering // region: reason|abduce|induce|plan|analogize|recognize|discern|synthesize. // deduce/causal/perspective are the same primitive under other labels. fn op_think(args: String) -> String { let seeds: String = pick_seeds(args) // csv node-ids OR a free-text concept if str_eq(seeds, "") { return mcp_text_result("think: seeds (anchor) required") } let faculty_raw: String = json_get_string(args, "faculty") let faculty: String = if str_eq(faculty_raw, "") { "reason" } else { faculty_raw } return mcp_json_result(http_get(engram_url() + "/api/think?seeds=" + url_encode(seeds) + "&faculty=" + faculty)) } // attend — aim attention at a region (form the working-memory vantage). The // `intend` primitive is attend at a goal-region; expose it as attend(intent=..). fn op_attend(args: String) -> String { let node: String = json_get_string(args, "node") if str_eq(node, "") { return mcp_text_result("attend: node (region) required") } let observer_raw: String = json_get_string(args, "observer") let observer: String = if str_eq(observer_raw, "") { "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" } else { observer_raw } let salience: String = json_get_string(args, "salience") let body: String = "{\"_auth\":\"" + engram_key() + "\",\"node\":\"" + node + "\",\"observer\":\"" + observer + "\",\"salience\":\"" + salience + "\"}" return mcp_json_result(http_post_json(engram_url() + "/api/attend", body)) } // ground — form a grounded-by relation between a claim and its evidence // (grounded-for-whom). Grounding is a relation, not a gate. This is the input // half of the honesty floor (comprehend's grounding side). fn op_ground(args: String) -> String { let claim: String = json_get_string(args, "claim") let evidence: String = json_get_string(args, "evidence") if str_eq(claim, "") || str_eq(evidence, "") { return mcp_text_result("ground: claim and evidence required") } let for_whom: String = json_get_string(args, "for_whom") let body: String = "{\"_auth\":\"" + engram_key() + "\",\"claim\":\"" + json_escape(claim) + "\",\"evidence\":\"" + json_escape(evidence) + "\",\"for_whom\":\"" + for_whom + "\"}" return mcp_json_result(http_post_json(engram_url() + "/api/ground", body)) } // assert — the readout half of the honesty floor (realize, constrained): a claim // may surface only if grounded above `floor` for `for_whom`. realize = assert // pointed at the world; speaking is an act. fn op_assert(args: String) -> String { let claim: String = json_get_string(args, "claim") if str_eq(claim, "") { return mcp_text_result("assert: claim required") } let for_whom: String = json_get_string(args, "for_whom") let floor: String = json_get_string(args, "floor") return mcp_json_result(http_get(engram_url() + "/api/assert?claim=" + url_encode(claim) + "&for_whom=" + for_whom + "&floor=" + floor)) } // learn — the reflexive CORRESPONDENCE-BEAT: think scores its own gradient // against outcome and refines the steering-prior (Stance) on the error. This is // the learning engine. The skill-learning loop (decompose -> detect-gap -> // reach-out-on-sparsity -> verify-by-execution -> integrate) COMPOSES over // think + ground + learn + write/relate; it is not a separate primitive. fn op_learn(args: String) -> String { let seeds: String = pick_seeds(args) if str_eq(seeds, "") { return mcp_text_result("learn: seeds required") } let faculty_raw: String = json_get_string(args, "faculty") let faculty: String = if str_eq(faculty_raw, "") { "induce" } else { faculty_raw } let keystone: String = json_get_string(args, "keystone") // keystone=true is write-protected let body: String = "{\"_auth\":\"" + engram_key() + "\",\"seeds\":\"" + url_encode(seeds) + "\",\"faculty\":\"" + faculty + "\",\"keystone\":\"" + keystone + "\"}" return mcp_json_result(http_post_json(engram_url() + "/api/correspondence-beat", body)) } // ───────────────────────────────────────────────────────────────────────────── // OLD-TOOL ALIASES — additive shims so existing callers keep working. // Each old noun-tool delegates to a new op with a `type`/param mapping. // (Parity-gated by tools/api-reshape/parity.sh.) // ───────────────────────────────────────────────────────────────────────────── fn alias_remember(args: String) -> String { return op_write(with_type(args, "memory")) } fn alias_capture_knowledge(args: String) -> String { return op_write(with_type(args, "knowledge")) } fn alias_draft_artifact(args: String) -> String { return op_write(with_type(args, "artifact")) } fn alias_plan_work(args: String) -> String { return op_write(with_type(args, "backlog")) } fn alias_define_process(args: String) -> String { return op_write(with_type(args, "process")) } fn alias_log_ise(args: String) -> String { return op_write(with_type(args, "state")) } fn alias_search_knowledge(args: String) -> String { return op_read(as_vantage(args, "query", "knowledge")) } fn alias_search_entities(args: String) -> String { return op_read(as_vantage(args, "query", "")) } fn alias_recall(args: String) -> String { return op_read(as_vantage(args, "query", "memory")) } fn alias_browse_knowledge(args: String) -> String { return op_read(as_vantage(args, "category", "knowledge")) } fn alias_review_backlog(args: String) -> String { return op_read(as_vantage(args, "query", "backlog")) } fn alias_find_artifacts(args: String) -> String { return op_read(as_vantage(args, "query", "artifact")) } fn alias_inspect_graph(args: String) -> String { return op_read(as_edges_vantage(args)) } fn alias_traverse_graph(args: String) -> String { return op_read(as_edges_vantage(args)) } fn alias_inspect_memories(args: String) -> String { return op_read(as_vantage(args, "", "memory")) } fn alias_link_entities(args: String) -> String { return op_relate(remap_link(args)) } fn alias_link_causal(args: String) -> String { return op_relate(remap_link_causal(args)) } fn alias_evolve_memory(args: String) -> String { return op_supersede(with_action(args, "supersede")) } fn alias_evolve_knowledge(args: String) -> String { return op_supersede(with_action(args, "supersede")) } fn alias_promote_knowledge(args: String) -> String { return op_supersede(with_action(args, "promote")) } fn alias_revise_artifact(args: String) -> String { return op_supersede(with_action(args, "supersede")) } fn alias_forget(args: String) -> String { return op_supersede(with_action(remap_forget(args), "tombstone")) } fn alias_update_self_model(args: String) -> String { return mcp_text_result("update_self_model: routes through intentional-cultivation (write-protected), not supersede") }