Files
will.anderson b77f537dc6 fix cross-repo path deps; remove el compiler from neuron-lang/
- neuron-runtime, neuron-store, neuron-migrate: fix path deps
  (products/engram/ → foundation/engram/engrams/, products/el/ → foundation/el/engrams/)
- neuron-rs workspace: fix axon-events/axon-protocol paths
  (../../../platform/ → ../../platform/, paths were off by one level)
- neuron-lang/compiler/ removed (el self-hosting compiler now lives in foundation/el/el-compiler/)
2026-04-29 03:27:39 -05:00

485 lines
19 KiB
EmacsLisp

// 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<String,String> params envelope.
// Missing optional params return empty string / empty array / zero.
@accessor
fn param_str(params: Map<String, String>, key: String) -> String {
params[key]
}
@accessor
fn param_int(params: Map<String, String>, 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<String, String>, 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<String, String> 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<String, String>,
) -> Result<Map<String, String>, 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))
}