neo
neo is a Rust coding-agent harness for
long-running autonomous software work.
Core Capabilities
- Multi-provider LLM runtime: OpenAI-compatible, OpenAI Codex account auth, Anthropic, Gemini, Ollama/Ollama Cloud, OpenRouter
- Tool loop with file IO, shell, web search, workers, browser control, and gateway routing
- Session persistence, resumable transcripts, and worktree-isolated workers
- Config-driven gateway routing with deterministic session-key generation
- CDP browser automation against Chrome/Chromium-compatible targets
- Ralph supervisor loop with fresh-session cycling, checkpointing, and host verification commands
- Always-on OpenClaw-style gateway daemon for routed channel ingress
Build
$HOME/.cargo/bin/cargo test
$HOME/.cargo/bin/cargo buildInstall
One-line installer for the current private repo:
gh api -H "Accept: application/vnd.github.raw" repos/AgentsLabs/neo/contents/install.sh | bashThis requires gh auth login first.
If the repository is later made public, the raw curl form also works:
curl -fsSL https://raw.githubusercontent.com/AgentsLabs/neo/main/install.sh | bashManual install with Cargo:
$HOME/.cargo/bin/cargo install --git https://github.com/AgentsLabs/neo.git --branch main --locked --forceUseful installer overrides:
REPO_URL=https://github.com/AgentsLabs/neo.git BRANCH=main INSTALL_ROOT=$HOME/.local curl -fsSL https://raw.githubusercontent.com/AgentsLabs/neo/main/install.sh | bashAfter install, ensure your path includes the install root:
export PATH="$HOME/.local/bin:$PATH"Quick Start
$HOME/.local/bin/neoRun from source:
$HOME/.cargo/bin/cargo runUseful commands:
/login status/login codex/login ollama/setup provider-gateway/gateway show/gateway route slack group C123/browser status/browser tabs/browser open https://example.com/browser screenshot artifacts/page.png/ralph status/ralph run refactor the parser and verify with cargo test
TUI behavior:
/modelsopens a keyboard-driven model picker dropdown/login codexlaunches the official Codex subscription login flow/login ollamalaunches the Ollama Cloud login flow/setup provider-gatewayruns an OpenAI-compatible LLM provider gateway setup wizard- the main input prompt is a minimal
> - action progress is shown in-line while the CLI works
Feature flags:
--cdpenables browser/CDP tools--cdp-discovery-url http://127.0.0.1:9222pointsneoat a local Chrome/Chromium debugger--cdp-url ws://127.0.0.1:9222/devtools/page/<id>can be used when you already have a websocket target--searchenables DuckDuckGo web search tools
Help
Show built-in help:
neo --help
neo --versionInside the interactive shell:
/helpshows the command reference/configshows the resolved runtime config/providershows the active model/provider/setup provider-gatewayconfigurescustom-openaiagainst an OpenAI-compatible provider gateway/modelsand/use-model <name>manage models for the active provider/search <query>runs web search when--searchis set/gatewayinspects routing/browserinspects or drives the CDP browser when--cdpis set/ralphcontrols the outer autonomous loop/agentsmanages isolated worker runs/sessioninspects and resumes saved sessions/shell,!<command>, and/terminalgive shell access
Common startup options:
neo --config-file neo_config.json
neo --workspace .
neo --provider ollama --model lfm
neo --provider ollama-cloud
neo --provider openai-codex --model gpt-5.3-codex
neo --cdp --cdp-discovery-url http://127.0.0.1:9222 --search
neo --tui
neo --ralph
neo --resume-session <session-id>
neo --gateway-daemon --gateway-bind 127.0.0.1:8787Config
neo loads neo_config.json from
the workspace when present. Fallback order is:
neo_config.jsonconfig.json
Example:
{
"provider": "ollama",
"model": "lfm",
"workspace": ".",
"autonomous": true,
"max_tool_rounds": 12,
"search_enabled": true,
"ralph": true,
"ralph_max_iterations": 8,
"ralph_fresh_session": true,
"ralph_verify_commands": ["cargo test"],
"gateway_bind": "127.0.0.1:8787",
"gateway_autorun": false,
"gateway": {
"enabled": true,
"default_agent": "main",
"bindings": [
{
"agent_id": "support",
"channel": "slack",
"peer_kind": "group",
"peer_id": "C123"
}
]
},
"browser": {
"enabled": true,
"discovery_url": "http://127.0.0.1:9222",
"default_timeout_ms": 15000
}
}Gateway Model
The gateway layer is host-controlled. neo
includes:
GatewayConfigwith named bindings- deterministic session keys such as
agent:<agentId>:<channel>:group:<id>:thread:<threadId> - a
gateway_routetool for embedded or higher-level hosts /gateway route ...for direct inspection during development- an always-on HTTP gateway daemon for ingesting external channel events
Gateway daemon:
neo --gateway-daemon --gateway-bind 127.0.0.1:8787HTTP endpoints:
GET /healthGET /sessionsPOST /ingest
Example ingest:
curl -X POST http://127.0.0.1:8787/ingest \
-H 'Content-Type: application/json' \
-d '{
"envelope": {
"channel": "slack",
"account_id": "workspace-1",
"peer_kind": "group",
"peer_id": "C123",
"thread_id": "T456"
},
"message": "hello from gateway daemon",
"autorun": false
}'Persistence:
- gateway session records are stored under the repo
control root, usually
.git/neo/gateway/ - mapped
neosessions are stored under.git/neo/sessions/ - with
--gateway-autorun, the daemon spawns one-shot localneoruns against the mapped session
Channel Config
Channel routing is configured under
gateway.bindings in
neo_config.json.
Binding fields:
agent_id: logical agent lane to route intochannel: transport label such asslack,discord,telegram, orwebhookaccount_id: optional account/workspace/tenant matcherpeer_kind: optional peer class matcherpeer_id: optional channel, room, DM, or thread root identifier
Supported peer_kind values:
maindirectgroupchannel
Matching rules:
- bindings are checked in order
- the first matching binding wins
- if no binding matches,
gateway.default_agentis used - omitted
account_id,peer_kind, andpeer_idbehave like wildcards
Session-key behavior:
mainanddirectcollapse toagent:<agent_id>:maingroupandchannelexpand toagent:<agent_id>:<channel>:<group|channel>:<peer_id>- if
thread_idis present,:thread:<thread_id>is appended
Minimal example:
{
"gateway": {
"enabled": true,
"default_agent": "main",
"bindings": [
{
"agent_id": "support",
"channel": "slack",
"peer_kind": "group",
"peer_id": "C123"
}
]
}
}Multi-channel example:
{
"gateway": {
"enabled": true,
"default_agent": "main",
"bindings": [
{
"agent_id": "ops",
"channel": "slack",
"account_id": "workspace-1",
"peer_kind": "group",
"peer_id": "COPS"
},
{
"agent_id": "support",
"channel": "discord",
"peer_kind": "channel",
"peer_id": "998877"
},
{
"agent_id": "dm",
"channel": "telegram",
"peer_kind": "direct"
}
]
}
}How to think about each peer_kind:
main: a single shared lane, usually for control traffic or top-level default routingdirect: one-to-one user conversations; by default these collapse into the main lane unless you add a more specific transport/account split outside the session keygroup: group-chat style rooms where a stable room ID should isolate contextchannel: broadcast/forum channel style surfaces where a stable channel ID should isolate context
Ingress example by peer kind:
{
"envelope": {
"channel": "slack",
"account_id": "workspace-1",
"peer_kind": "group",
"peer_id": "C123",
"thread_id": "T456"
},
"message": "hello from channel config docs",
"autorun": false
}Recommended pattern:
- Set
default_agenttomain. - Add the most specific bindings first.
- Use
account_idwhen one transport serves multiple tenants. - Use
grouporchannelfor any surface where context must stay isolated per room. - Leave
autorunoff until you trust the routing and session isolation behavior.
Browser Control
The browser runtime is CDP-backed and enabled with
--cdp or browser.enabled=true. It
currently supports:
- browser status via
/json/version - tab listing via
/json/list - open, activate, and close tab flows
- page navigation
- JavaScript evaluation through
Runtime.evaluate - PNG screenshots through
Page.captureScreenshot - coordinate clicks through
Input.dispatchMouseEvent - text entry through
Input.insertText - simple load waiting via
document.readyState
Ralph Loop
The Ralph supervisor loop is an outer loop around the inner tool loop.
Run the local loop wrapper:
scripts/ralph-loop.sh prd.jsonIf prd.json has a features
array, the script runs one feature per Ralph invocation;
otherwise it uses objective.
What it does:
- runs the inner agent loop against a goal
- records a checkpoint to
.neo/ralph-loop.json - runs verification commands outside the model’s control
- optionally starts a fresh session for the next iteration
- carries forward only compressed context from the previous iteration
Relevant config keys:
ralphralph_max_iterationsralph_verify_commandsralph_sleep_msralph_fresh_sessionralph_checkpoint_file
Worker Model
Workers run in isolated worktrees:
- separate workspace per worker
- copied config, skills, and memory
- persistent worker registry
- master agent remains responsible for coordination
Harness Review
Strengths:
- The inner loop is simple and stable: model call, tool execution, repeat.
- Sessions, worktrees, and worker isolation are strong foundations for autonomous execution.
- Gateway routing is deterministic instead of model-chosen.
- Browser control is first-class instead of an external manual dependency.
- The outer Ralph loop moves verification back to the host.
Gaps:
- The gateway daemon is intentionally minimal and uses a compact local HTTP ingress instead of a full multi-transport control plane.
- CDP control is intentionally minimal; it lacks robust DOM refs, snapshots, and stale-element recovery.
- Ralph checkpoints are single-file snapshots, not an append-only iteration ledger.
- Verification is command-based, but there is no policy engine for flaky runs or escalation.
- There is no concurrency controller for many simultaneous long-lived runs.
Recommended next steps:
- Add an append-only Ralph run ledger with per-iteration summaries and artifacts.
- Add transport adapters and a richer control plane on top of the daemon.
- Upgrade browser targeting from coordinates to snapshot/reference-based interactions.
- Add run leases, heartbeats, and cancellation semantics.
- Add compaction and memory-budget policies between Ralph iterations.
- Add queueing and lane controls for concurrent routed sessions.
Validation
$HOME/.cargo/bin/cargo test