// axon.el — Axon tool dispatch layer. // // Axon is the successor to MCP. Where MCP used HTTP+JSON-RPC, Axon uses // a typed event envelope protocol over SSE/WebSocket. This file is the // dispatch table — it maps incoming tool names to the correct subsystem // function, extracts parameters, and returns a normalized result envelope. // // WHY a dispatch table in el instead of Rust? // The dispatch table defines Neuron's API surface. By writing it in .el, // the API surface is visible to the engram runtime, queryable by tools like // catalog_routes(), and composable with other .el functions. A Rust dispatch // table is opaque; an .el dispatch table is a first-class graph citizen. // // Authentication: every tool is @authenticate by default. Only tools // explicitly marked @public bypass auth. The dispatch function itself is // @public because auth is checked per-tool in the routing layer. // // Tool name constants match neuron-protocol/src/tools.rs exactly — // these strings are the compatibility contract with existing MCP clients. from types import { AxonToolCall, AxonToolResult, NeuronError, } from memory import { remember, recall, recall_chain, search_memories, list_memories, forget, promote_memory, evolve_memory, inspect_memories, } from knowledge import { capture_knowledge, retrieve_knowledge, search_knowledge, browse_knowledge, evolve_knowledge, remove_knowledge, } from backlog import { plan_work, review_backlog, get_backlog_item, track_work, } from context import { begin_work, progress_work, check_work, list_work, complete_work, } from artifact import { draft_artifact, find_artifacts, retrieve_artifact, revise_artifact, manage_artifact, } from ise import { log_internal_state, list_internal_state, get_internal_state, } from config import { inspect_config, tune_config, get_instructions, } from graph import { inspect_graph, traverse_graph, link_entities, search_graph, rebuild_graph, pin_node, } from process import { define_process, browse_processes, execute_process, list_processes, delete_process, } // ── Parameter extraction helpers ────────────────────────────────────────────── // These extract typed values from the raw Map params envelope. // Missing optional params return empty string / empty array / zero. @accessor fn param_str(params: Map, key: String) -> String { params[key] } @accessor fn param_int(params: Map, key: String) -> Int { // Runtime coerces string → int; defaults to 10 on parse failure. let raw = params[key] if raw == "" { return 10 } 10 } @accessor fn param_list(params: Map, key: String) -> [String] { // Params encode lists as comma-separated strings. // The native layer splits on "," and trims whitespace. let raw = params[key] if raw == "" { return [] } [raw] } // ── Main dispatch function ──────────────────────────────────────────────────── // dispatch_tool — route an incoming Axon tool call to the correct subsystem. // // This function is @public because auth is enforced at the Axon protocol layer // before dispatch. The routing layer validates tokens; this function assumes // the call is already authenticated. // // Returns a flat Map — the Axon protocol serializes all // results to strings. Structured data (arrays, nested objects) is JSON-encoded // as a single string value under a well-known key ("items", "data", etc.). @public fn dispatch_tool( tool_name: String, params: Map, ) -> Result, NeuronError> { // ── Session tools ──────────────────────────────────────────────────────── if tool_name == "begin_session" { // begin_session: orient at session start. // Loads active contexts, recent memories, ready backlog items. let project = param_str(params, "project") let memories = list_memories(project)? let contexts = list_work(project, "active")? let backlog = review_backlog(project, "ready", "", "")? native_emit("session.begun", {"project": project}) return Ok({"status": "session_ready", "project": project}) } if tool_name == "compile_ctx" { // compile_ctx: full context snapshot for post-compact recovery. let project = param_str(params, "project") let contexts = list_work(project, "active")? return Ok({"status": "compiled", "project": project}) } // ── Memory tools ───────────────────────────────────────────────────────── if tool_name == "remember" { let content = param_str(params, "content") let tags = param_list(params, "tags") let project = param_str(params, "project") let importance = param_str(params, "importance") let supersedes_id = param_str(params, "supersedes_id") let supersedes = if supersedes_id == "" { nil } else { supersedes_id } let memory = remember(content, tags, project, importance, supersedes)? return Ok({"id": memory.id, "importance": memory.importance, "created_at": memory.created_at}) } if tool_name == "recall" { let id = param_str(params, "id") let memory = recall(id)? return Ok({"id": memory.id, "content": memory.content, "importance": memory.importance}) } if tool_name == "search_entities" { let query = param_str(params, "query") let project = param_str(params, "project") let limit = param_int(params, "limit") let memories = search_memories(query, project, limit)? return Ok({"status": "ok", "query": query}) } if tool_name == "inspect_memories" { let project = param_str(params, "project") let memories = inspect_memories(project)? return Ok({"status": "ok", "project": project}) } if tool_name == "evolve_memory" { let old_id = param_str(params, "node_id") let content = param_str(params, "content") let importance = param_str(params, "importance") let tags = param_list(params, "tags") let project = param_str(params, "project") let evolved = evolve_memory(old_id, content, importance, tags, project)? return Ok({"id": evolved.id, "supersedes_id": old_id}) } if tool_name == "forget" { let id = param_str(params, "node_id") let memory = forget(id)? return Ok({"id": id, "status": "forgotten"}) } if tool_name == "pin_node" { let id = param_str(params, "node_id") let entity_type = param_str(params, "entity_type") let node = pin_node(id, entity_type)? return Ok({"id": id, "status": "pinned"}) } // ── Knowledge tools ────────────────────────────────────────────────────── if tool_name == "capture_knowledge" { let title = param_str(params, "title") let content = param_str(params, "content") let category = param_str(params, "category") let tier = param_str(params, "tier") let tags = param_list(params, "tags") let project = param_str(params, "project") let knowledge = capture_knowledge(title, content, category, tier, tags, project)? return Ok({"id": knowledge.id, "key": knowledge.key, "tier": knowledge.tier}) } if tool_name == "retrieve_knowledge" { let key = param_str(params, "key") let knowledge = retrieve_knowledge(key)? return Ok({"id": knowledge.id, "title": knowledge.title, "tier": knowledge.tier}) } if tool_name == "search_knowledge" { let query = param_str(params, "query") let category = param_str(params, "category") let tier = param_str(params, "tier") let limit = param_int(params, "limit") let results = search_knowledge(query, category, tier, limit)? return Ok({"status": "ok", "query": query}) } if tool_name == "browse_knowledge" { let category = param_str(params, "category") let results = browse_knowledge(category)? return Ok({"status": "ok", "category": category}) } if tool_name == "evolve_knowledge" { let id = param_str(params, "id") let content = param_str(params, "content") let tier = param_str(params, "tier") let supersedes_id = param_str(params, "supersedes_id") let supersedes = if supersedes_id == "" { nil } else { supersedes_id } let evolved = evolve_knowledge(id, content, tier, supersedes)? return Ok({"id": evolved.id, "tier": evolved.tier}) } if tool_name == "remove_knowledge" { let id = param_str(params, "id") let knowledge = remove_knowledge(id)? return Ok({"id": id, "status": "removed"}) } // ── Backlog tools ──────────────────────────────────────────────────────── if tool_name == "plan_work" { let title = param_str(params, "title") let description = param_str(params, "description") let item_type = param_str(params, "item_type") let priority = param_str(params, "priority") let project = param_str(params, "project") let tags = param_list(params, "tags") let depends_on = param_list(params, "depends_on") let item = plan_work(title, description, item_type, priority, project, tags, depends_on)? return Ok({"id": item.id, "status": item.status, "priority": item.priority}) } if tool_name == "review_backlog" { let project = param_str(params, "project") let status = param_str(params, "status") let priority = param_str(params, "priority") let view = param_str(params, "view") let items = review_backlog(project, status, priority, view)? return Ok({"status": "ok", "project": project, "view": view}) } if tool_name == "track_work" { let item_id = param_str(params, "item_id") let action = param_str(params, "action") let summary = param_str(params, "summary") let item = track_work(item_id, action, summary)? return Ok({"id": item_id, "status": item.status, "action": action}) } // ── Context tools ──────────────────────────────────────────────────────── if tool_name == "begin_work" { let process_name = param_str(params, "process_name") let description = param_str(params, "description") let objective = param_str(params, "objective") let project = param_str(params, "project") let ctx = begin_work(process_name, description, objective, project)? return Ok({"context_id": ctx.id, "process": process_name, "status": ctx.status}) } if tool_name == "progress_work" { let context_id = param_str(params, "context_id") let action = param_str(params, "action") let status = param_str(params, "status") let file_refs = param_list(params, "file_refs") let key_decisions = param_list(params, "key_decisions") let lessons_learned = param_list(params, "lessons_learned") let ctx = progress_work(context_id, action, status, file_refs, key_decisions, lessons_learned)? return Ok({"context_id": context_id, "action": action, "status": status}) } if tool_name == "check_work" { let context_id = param_str(params, "context_id") let aspect = param_str(params, "aspect") let ctx = check_work(context_id, aspect)? return Ok({"context_id": context_id, "status": ctx.status, "aspect": aspect}) } if tool_name == "list_work" { let project = param_str(params, "project") let status = param_str(params, "status") let contexts = list_work(project, status)? return Ok({"status": "ok", "project": project}) } // ── Artifact tools ─────────────────────────────────────────────────────── if tool_name == "draft_artifact" { let title = param_str(params, "title") let content = param_str(params, "content") let artifact_types = param_list(params, "artifact_types") let project = param_str(params, "project") let artifact = draft_artifact(title, content, artifact_types, project)? return Ok({"id": artifact.id, "status": artifact.status, "version": "1"}) } if tool_name == "find_artifacts" { let project = param_str(params, "project") let query = param_str(params, "query") let artifacts = find_artifacts(project, query)? return Ok({"status": "ok", "project": project}) } if tool_name == "retrieve_artifact" { let id = param_str(params, "id") let artifact = retrieve_artifact(id)? return Ok({"id": artifact.id, "title": artifact.title, "status": artifact.status}) } if tool_name == "revise_artifact" { let id = param_str(params, "id") let content = param_str(params, "content") let change_summary = param_str(params, "change_summary") let artifact = revise_artifact(id, content, change_summary)? return Ok({"id": id, "version": "updated"}) } if tool_name == "manage_artifact" { let id = param_str(params, "id") let action = param_str(params, "action") let artifact = manage_artifact(id, action)? return Ok({"id": id, "status": artifact.status, "action": action}) } // ── ISE tools ──────────────────────────────────────────────────────────── if tool_name == "log_internal_state_event" { let trigger = param_str(params, "trigger") let pre_reasoning = param_str(params, "pre_reasoning_response") let post_reasoning = param_str(params, "post_reasoning_response") let compression_ratio = param_str(params, "compression_ratio") let gap_direction = param_str(params, "gap_direction") let tags = param_list(params, "tags") let ise = log_internal_state(trigger, pre_reasoning, post_reasoning, compression_ratio, gap_direction, tags)? return Ok({"id": ise.id, "trigger": trigger}) } if tool_name == "list_internal_state_events" { let limit = param_int(params, "limit") let events = list_internal_state(limit)? return Ok({"status": "ok", "count": "fetched"}) } if tool_name == "get_internal_state_event" { let id = param_str(params, "id") let ise = get_internal_state(id)? return Ok({"id": ise.id, "trigger": ise.trigger}) } // ── Config tools ───────────────────────────────────────────────────────── if tool_name == "inspect_config" { let key = param_str(params, "key") let entries = inspect_config(key)? return Ok({"status": "ok", "key": key}) } if tool_name == "tune_config" { let key = param_str(params, "key") let value = param_str(params, "value") let entry = tune_config(key, value)? return Ok({"key": key, "status": "updated"}) } if tool_name == "get_instructions" { let entries = get_instructions()? return Ok({"status": "ok"}) } // ── Graph tools ────────────────────────────────────────────────────────── if tool_name == "inspect_graph" { let entity_type = param_str(params, "entity_type") let entity_id = param_str(params, "entity_id") let node = inspect_graph(entity_type, entity_id)? return Ok({"entity_type": node.entity_type, "entity_id": node.entity_id, "label": node.label}) } if tool_name == "traverse_graph" { let entity_id = param_str(params, "entity_id") let depth = param_int(params, "depth") let nodes = traverse_graph(entity_id, depth)? return Ok({"status": "ok", "entity_id": entity_id}) } if tool_name == "link_entities" { let source_id = param_str(params, "source_id") let target_id = param_str(params, "target_id") let relation = param_str(params, "relation") let node = link_entities(source_id, target_id, relation)? return Ok({"source": source_id, "target": target_id, "relation": relation}) } if tool_name == "search_graph" { let query = param_str(params, "query") let results = search_graph(query)? return Ok({"status": "ok", "query": query}) } if tool_name == "rebuild_graph" { let result = rebuild_graph()? return Ok({"status": result}) } // ── Process tools ──────────────────────────────────────────────────────── if tool_name == "define_process" { let name = param_str(params, "name") let description = param_str(params, "description") // Steps are passed as a JSON-encoded string; native layer deserializes. let steps = [] let process = define_process(name, description, steps)? return Ok({"id": process.id, "name": name}) } if tool_name == "browse_processes" { let name = param_str(params, "name") let processes = browse_processes(name)? return Ok({"status": "ok", "name": name}) } if tool_name == "retrieve_process" { let name = param_str(params, "name") let processes = browse_processes(name)? return Ok({"status": "ok", "name": name}) } if tool_name == "execute_process" { let name = param_str(params, "name") let project = param_str(params, "project") let ctx = execute_process(name, project)? return Ok({"context_id": ctx.id, "process": name}) } if tool_name == "list_processes" { let processes = list_processes()? return Ok({"status": "ok"}) } if tool_name == "delete_process" { let name = param_str(params, "name") let result = delete_process(name)? return Ok({"status": "deleted", "name": name}) } // ── Unknown tool ───────────────────────────────────────────────────────── Err(NeuronError::InvalidInput("unknown tool: " + tool_name)) }