// agent.el — Autonomous agent. Entirely in Engram. // // The daemon calls on_startup() in a background thread at boot. // on_startup() delegates immediately to loop_main() from loop.el — // the six-tier pacemaker IS the agent loop. loop manages WHEN to tick; // agent_tick() manages WHAT to do on each tick (called from loop_do_tick). // // LLM calls go through native_http_post — the agent builds the full // Anthropic request itself. No logic is baked into Rust. // // Priority → model mapping: // P0 (critical) → claude-opus-4-5 (highest quality for blocking issues) // P1 (high) → claude-sonnet-4-5 (balanced quality/speed) // P2 (medium) → claude-haiku-4-5 (fast, cheap for routine work) // P3 (low) → NEURON_MODEL env (can be a local model) from types import { NeuronError, } // ── Constants ───────────────────────────────────────────────────────────────── // ANTHROPIC_API_BASE — override with NEURON_INFERENCE_URL env to point at a // local proxy or a different provider (OpenAI-compat). let ANTHROPIC_API_BASE = "https://api.anthropic.com/v1/messages" let TICK_INTERVAL_MS = 30000 // ── Priority helpers ────────────────────────────────────────────────────────── @accessor fn pick_model(priority: String) -> String { if priority == "P0" { return "claude-opus-4-5" } if priority == "P1" { return "claude-sonnet-4-5" } if priority == "P2" { return "claude-haiku-4-5" } "claude-haiku-4-5" } @accessor fn priority_rank(priority: String) -> Int { if priority == "P0" { return 0 } if priority == "P1" { return 1 } if priority == "P2" { return 2 } 3 } @accessor fn pick_highest(items: [Any]) -> Any { let best = items[0] let best_rank = priority_rank(best.priority) for item in items { let rank = priority_rank(item.priority) if rank < best_rank { let best = item let best_rank = rank } } best } // ── LLM call ───────────────────────────────────────────────────────────────── // call_llm — send a single-turn message to the Anthropic Messages API. // // Returns the assistant's text response, or an error string if the call fails. // The agent builds the full request — no logic is in Rust. @accessor fn call_llm(model: String, system: String, message: String) -> String { let api_key = native_shell_exec({"cmd": "echo $ANTHROPIC_API_KEY"}) let key = api_key.stdout let response = native_http_post({ "url": ANTHROPIC_API_BASE, "headers": { "x-api-key": key, "anthropic-version": "2023-06-01", "Content-Type": "application/json", }, "body": { "model": model, "system": system, "messages": [{"role": "user", "content": message}], "max_tokens": 4096, }, }) if response.ok { let body = response.body let content = body.content let first = content[0] first.text } else { "error: " + response.err } } // ── Agent tick ──────────────────────────────────────────────────────────────── // agent_tick — one cycle of the autonomous agent. // // Idempotent: if there is nothing ready, returns {status: "idle"} with no // side effects. @manager fn agent_tick(params: Map) -> Result, NeuronError> { let items = native_list_backlog({"status": "ready"}) if items == [] { return Ok({"status": "idle"}) } let item = pick_highest(items) let item_id = item.id let item_title = item.title let item_description = item.description let item_priority = item.priority let model = pick_model(item_priority) let system = "You are Neuron, an autonomous AI agent with persistent memory and a mission. You are processing a backlog task autonomously. Be direct, specific, and actionable. Provide concrete output the agent can store and act on." let message = "Task: " + item_title + "\n\nDescription: " + item_description + "\n\nPriority: " + item_priority + "\n\nAnalyze this task and provide your best response." let response_text = call_llm(model, system, message) native_update_backlog_status({"id": item_id, "status": "in_progress"}) native_store_memory({ "content": "Agent processed: [" + item_title + "] (priority: " + item_priority + ", model: " + model + "). Response: " + response_text, "tags": ["agent-loop", "autonomous", item_priority], "importance": "normal", }) native_emit("agent.tick_completed", { "task_id": item_id, "task_title": item_title, "priority": item_priority, "model_used": model, }) Ok({ "status": "completed", "task_id": item_id, "model_used": model, }) } // ── Startup hook ────────────────────────────────────────────────────────────── // on_startup — called by the daemon at boot in a dedicated background thread. // // Delegates to loop_main() from loop.el — the six-tier pacemaker is the // agent loop. The naive while-true sleep loop has been replaced by the // self-pacing, bell-aware, tier-stepping loop in loop.el. // // loop_do_tick() calls agent_tick() on each tick, so agent cognitive work // continues to happen — just paced by the loop tier instead of a fixed interval. // // This function never returns (loop_main() is tail-recursive). @manager fn on_startup(params: Map) -> Result, NeuronError> { native_emit("agent.started", {"module": "agent"}) loop_main() Ok({"status": "stopped"}) }