Skip to main content
Glama

Native Agent Runtime Router (NAR)

English | 简体中文

Route coding tasks from an orchestrator agent (Codex / opencode / Claude Code) to native CLI worker agents (ZCode, any ACP-compatible agent) through a small, deterministic, model-free kernel.

The kernel never calls a model. It handles task state, bounded waiting, workspace locking, fixed verification, one pre-authorized repair, cancellation with confirmation, budgets, recovery, usage accounting and per-agent scoring — so the orchestrator spends its tokens on judgment, not on polling and log-shuffling.

Orchestrator (Codex / opencode / Claude Code)
        |  MCP (5 stable tools)  or  CLI (`nar`)
        v
  Model-free kernel: tasks, locks, verification, repair, budget, recovery
        v
  Pluggable native adapters
   ├── zcode-native   (ZCode app-server protocol 0.16, verified live on 0.16.5)
   ├── acp-generic    (Agent Client Protocol, verified live on opencode 1.18.15)
   └── your adapter   (small class, see docs/BACKENDS.md)

More native agents are being supported…

  • MCP: nar-mcp exposes exactly five tools: agents, run, wait, inspect, cancel. New backends are added by config, not by new tools.

  • CLI: nar shares the same kernel (debugging, scripting, humans).

  • Token discipline: compact results by default; full logs/diffs on disk, paged via inspect; deterministic scoring per task (zero tokens) feeds routing.

Install

Requires Python >= 3.10. For the ZCode adapter: Node.js >= 22 and the ZCode desktop app (or its CLI on PATH). For ACP agents: any ACP server binary.

Install from the official GitHub source:

git clone https://github.com/BerineYang/native-agent-router.git
cd native-agent-router
python -m pip install .

If git clone is unreliable, download the official v1.0.0 branch source ZIP, extract it, open a terminal in the extracted directory, and run:

python -m pip install .

For development, replace the install command with python -m pip install -e .. In the repository directory, pipx install . also works.

On Windows, verify the installation with the module form first. It works even when the Python scripts directory is missing from PATH:

python -m native_agent_router doctor

If the scripts directory is on PATH, the shorter command is equivalent:

nar doctor

The doctor command uses [OK], [X], and [i] markers for the ZCode bundle, Node.js, Git, and configuration checks. It never prints credentials. A missing ZCode entry matters only when you intend to use the zcode-native adapter.

Related MCP server: kagero

Quickstart

  1. Create the config (optional — auto-discovery works without it):

mkdir -p ~/.native-agent-router
cp config.example.json ~/.native-agent-router/agents.json
  1. List agents, submit a complete work unit, wait, inspect:

nar agents
nar run zcode "Implement parse_flag() in flags.py per the docstring; only modify flags.py" \
    /abs/path/to/project --mode build --policy allow --scope flags.py --verify "python -m pytest -q" --wait 600
nar wait <task_id>
nar inspect <task_id> summary
nar inspect <task_id> diagnose      # sanitized, shareable error report
  1. Continue the SAME native session for follow-up work on the same module:

nar run zcode "Now handle the --verbose edge case we discussed" /abs/path/to/project \
    --session-ref <previous_task_id> --mode build --policy allow --verify "python -m pytest -q"

Configuration (the teaching section)

Config file lookup order: --config flag → NAR_CONFIG env → <home>/agents.json (home = NAR_HOME env or ~/.native-agent-router) → built-in defaults + auto-discovery.

Top-level settings

Key

Default

Meaning

wait_default_sec

120

default bounded block for wait

wait_max_sec

3600

cap on a single wait call (does NOT cap task timeouts)

timeout_default_sec

1800

default per-turn timeout

verify_timeout_sec

600

timeout per verification command

max_result_chars

12000

reserved for configuration compatibility; current result fields have their own fixed bounds

log_tail_chars

4000

default page size for inspect when no limit is supplied

repair_default

true

one pre-authorized targeted repair per task

verify_allow_shell

false

verify commands run without a shell (argv / shlex.split); set true only if you need shell semantics

require_git_baseline

false

fail-closed: refuse to run a worker in a non-git workspace so changes are always auditable

Agents

Every task must name an agent_id explicitly. There is no global "selected backend" that other calls could silently change.

Key

Applies to

Meaning

adapter

all

zcode-native, acp-generic, or stub (tests)

enabled

all

set false to hide an agent

command

acp-generic

argv that starts the ACP server, e.g. ["opencode","acp"]

cwd_arg

acp-generic

some ACP servers want --cwd <ws>; set the flag name here

model

both

model selection (see below)

model_args

acp-generic

CLI-arg model injection template, e.g. ["--model","{model}"]

provider

zcode-native

ZCode provider id from your own ~/.zcode/v2/config.json

thought_level

zcode-native

e.g. low / high / max (per model capabilities)

default_mode

both

defaults to plan; ZCode also supports build / edit / yolo

default_permission_policy

both

deny (default, honest) or allow (auto-approve within one task)

tool_allowlist

zcode-native

native tool-set restriction, e.g. ["Read","Grep","Glob"]

zcode_home

zcode-native

"isolated" = keep NAR's ZCode sessions in a private home (see docs/SECURITY.md)

credentials

zcode-native

auto (default): reuse the user's own ZCode config; see below

env

both

extra environment variables for the agent subprocess; values are stored in agents.json

Model selection

  • ZCode (zcode-native): set provider + model (+ optional thought_level). NAR builds ZCode 0.16's runtimeModel payload from your own ~/.zcode/v2/config.json and passes it inside the protocol only. No credential is ever copied to disk by default, no provider/base URL/billing channel is changed, and cold session/resume works (verified live on 0.16.5).

  • ACP agents (acp-generic): NAR first tries the ACP-native path — session/set_config_option / session/set_model (works with opencode, whose session/new advertises a model config option) — then falls back to model_args CLI substitution (e.g. gemini --experimental-acp --model X).

ZCode discovery (how zcode.cjs is found)

Order: ZCODE_BIN env → zcode on PATH → Windows registry uninstall entries (InstallLocation) → %LOCALAPPDATA%\Programs\ZCode → Program Files\ZCode on every drive. If none match, nar doctor tells you exactly what to set. The desktop app does not add its CLI to PATH — that is normal.

Credentials, honestly

credentials: "auto" reads (read-only) the provider you already configured in the ZCode desktop app and passes the same provider/model/base-URL/key to the headless app-server in the protocol payload only. NAR never logs keys, never uploads them anywhere, never switches billing channels, and never bypasses authentication or plan limits. If you prefer a private session store, set zcode_home: "isolated" (documents trade-offs in docs/SECURITY.md).

MCP integration

Run nar-mcp over stdio. Examples:

Codex (~/.codex/config.toml):

[mcp_servers.native-agent-router]
command = "nar-mcp"
args = []

opencode (opencode.json in your project or ~/.config/opencode/):

{ "mcp": { "native-agent-router": { "type": "local", "command": ["nar-mcp"], "enabled": true } } }

Claude Code (.mcp.json):

{ "mcpServers": { "native-agent-router": { "command": "nar-mcp" } } }

Then ask your orchestrator, in one sentence: "Run agents, then run this task to zcode with scope and verify, and wait for it." See skill/native-agent-router/SKILL.md for the delegation rules you can drop into any agent's skill folder.

The five MCP tools

Tool

Purpose

agents

configured agents + capabilities + score stats (routing data)

run

submit one complete work unit (goal/scope/forbid/verify/budget/mode/session_ref)

wait

bounded block until terminal/blocked; never busy-polls

inspect

paged on-demand reads: status/summary/diff/verify/log/raw/usage/diagnose

cancel

request stop and report whether it was confirmed

run returning means submitted, not done. Terminal states are succeeded|failed|cancelled|interrupted. blocked is observable but nonterminal: the kernel keeps the workspace lock and watches the native session until a real terminal event arrives (or you cancel/kill). A token budget violation uses status failed with code budget_exceeded.

CLI reference

nar agents | doctor | list
nar run <agent_id> <goal> <workspace> [--scope ...] [--verify "cmd"] [--mode build]
        [--policy allow|deny] [--session-ref TASK] [--timeout N] [--wait N]
        [--budget N] [--idempotency-key K] [--no-repair]
nar wait <task_id> [--timeout N]
nar inspect <task_id> [status|summary|diff|verify|log|raw|usage|diagnose] [--offset N] [--limit N]
nar cancel <task_id>
nar kill <task_id> --yes        # force: terminate agent + release lock (user boundary)
nar stats [agent_id]            # deterministic per-agent score statistics

Tests

pip install -e ".[dev]"
pytest -q                 # 67 tests, no model calls, no network
pytest -q -m real         # opt-in: hits your real installed agents (spends tokens!)

Docs

ARCHITECTURE.md · BACKENDS.md · VERIFY.md · BENCHMARK.md · SECURITY.md · ROLLBACK.md · Skill

Status & honesty

  • Verified live on this machine: ZCode 0.16.5 (read-only, native resume, edit+verify, permission handling, real usage), opencode 1.18.15 over ACP (handshake, model switch, resume). Evidence paths in docs/VERIFY.md.

  • Not yet verified here: Gemini CLI (not installed), Claude Code as ACP (installed but currently unusable per user), Codex as orchestrator client.

  • No claim of "optimal" or fixed savings percentage: see BENCHMARK.md for what was measured and what wasn't.

  • MIT licensed. Independent community project; not affiliated with Z.AI/ZCode, OpenAI Codex, or SST/opencode.

Available Tools

5 tools
agentsA

List configured worker agents and their capabilities (adapter type, transport, native session resume support, usage accounting quality) plus per-agent SCORE STATS (avg_score, success_rate, repair_rate, out_of_scope_rate, blocked_rate, avg_tokens, median_duration_sec, last_tasks). Use the stats to route work: high-score agents for hard units, cheaper/lower-score agents for well-specified mechanical units. Use this before run() to pick a valid agent_id.

Inputs: no named parameters. Returns: result

cancelA

Request cancellation and report whether the stop was CONFIRMED by the native runtime (confirmed=true only on an observed terminal/exit). A cancel request alone is not a stop.

Inputs: task_id (required).

inspectA

Read task details on demand (logs/diffs stay on disk; read slices here). what: status | summary | diff | verify | log | raw | usage | diagnose.

  • diagnose returns a compact SANITIZED report (secrets/emails/blobs redacted, size-bounded) with a stable code and actionable hints — safe to share.

Inputs: task_id (required), what, limit, offset.

runA

Submit a complete work unit to a worker agent and return its task_id.

Requirements:

  • agent_id: exact id from agents(); every task is explicitly bound to one agent.

  • goal: full objective, constraints and context in ONE prompt (complete work unit; do not ping-pong per file/line).

  • workspace: directory path (canonicalized to an absolute path by the kernel).

  • scope_files: RELATIVE paths the worker may modify (absolute or escaping paths are rejected; enforced by diff audit).

  • forbid: extra prohibitions.

  • verify: commands executed by the kernel after the turn; the worker cannot pass by editing them. Prefer list form; string form is arg-split (no shell) unless config verify_allow_shell is on. Exit code 0 = pass.

  • mode: adapter-specific permission mode (zcode: plan|build|edit|yolo).

  • session_ref: task_id of a previous task on the SAME agent+workspace to continue its native session. Cross-agent/workspace reuse is rejected.

  • timeout_sec: per-turn timeout; on timeout the task becomes blocked (never silently resubmitted; the lock is held and a watcher keeps observing).

  • wait_sec: bounded block until finish (default: return immediately).

  • budget_tokens: hard token budget; exceeded -> task failed (budget_exceeded).

  • permission_policy: "deny" (default) or "allow" (auto-allow within this task).

  • repair_allowed: one pre-authorized targeted repair attempt (default true).

  • idempotency_key: resubmitting the same key returns the original task.

Returns a snapshot dict with ok/status/code. A run() success only means SUBMITTED, not done — follow with wait()/inspect().

Inputs: goal (required), agent_id (required), workspace (required), mode, +10 more.

waitA

Bounded blocking wait for a task to reach a terminal or blocked state. Does not busy-poll. Returns the snapshot dict; if it still runs when the timeout expires, wait_timed_out=true — call again later. 'submitted'/'running' is NOT completion; only status succeeded/failed/blocked/cancelled/interrupted is.

Inputs: task_id (required), timeout_sec.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.2/5.0

    Scored across 5 tools

    Disambiguation5/5

    Each tool covers a distinct lifecycle stage: agents discovers workers, run submits a unit of work, wait blocks for completion, inspect reads task details, and cancel aborts. There is no meaningful overlap or ambiguity between them.

    Naming Consistency4/5

    The verb-style names run, wait, inspect, and cancel are clear and consistently formatted. 'agents' is a minor deviation since it is a noun command rather than a verb_noun action like list_agents, but the pattern is still easy to predict.

    Tool Count5/5

    Five tools is well-scoped for the server's purpose: agent discovery, task submission, waiting, inspection, and cancellation. Each tool has a clear role and none feel redundant.

    Completeness4/5

    The core agent-routing and task lifecycle is well covered: discover agents, submit tasks, wait, inspect, and cancel. A minor gap is the lack of a task-listing or search tool if a task_id is lost, but this does not prevent normal workflows.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables unified access to multiple coding-agent CLIs via MCP, with tools for running prompts, managing sessions, and retrieving task outputs.
      MIT
    • A
      license
      B
      quality
      B
      maintenance
      Enables any MCP host to delegate work to multiple coding-agent CLIs such as Codex, Claude Code, and Antigravity as subagents, preserving native sessions and supporting team-based supervision and agent-to-agent messaging.
      17
      9 npm
      Apache 2.0
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables any MCP-compatible client to run reusable, Markdown-defined coding agents (such as reviewers, test writers, or investigators) through a configurable coding CLI backend, returning results into the same conversation. Agent definitions and backend/model settings can be shared across clients, and sessions can persist across multiple calls for longer work.
      182 npm
      MIT