Delegate Coding Tasks to OpenHands CLI via Hermes Agent
Delegate coding to OpenHands CLI (model-agnostic, LiteLLM).
Written by Neura Market from the official Hermes Agent documentation for Openhands. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationOpenHands CLI is a model-agnostic coding agent that you can invoke from within Hermes Agent's terminal tool. It runs headless, emits JSONL event streams, and works with any LiteLLM-supported provider. Reach for this when you need a coding agent that is not tied to a single vendor, or when you want to batch one-shot coding tasks without an interactive UI.
What it does
OpenHands CLI wraps the upstream OpenHands agent in headless mode. From Hermes, you delegate a task by passing a prompt through the terminal tool. The agent reads your workspace, edits files, runs shell commands, and returns a structured event log. Because it is model-agnostic, you can swap the underlying model without changing the invocation pattern. This is useful for teams that want to use DeepSeek, Qwen, Ollama, vLLM, or Nous models alongside the more common OpenAI and Anthropic providers.
Before you start
You need Python 3.12+ and uv installed on your system. The upstream OpenHands CLI is installed as an isolated tool:
terminal(command="uv tool install openhands --python 3.12")
Verify the install:
openhands --version (currently OpenHands CLI 1.16.0 / SDK v1.21.0 at time of writing).
Pick a model and set environment variables. These are consumed by the --override-with-envs flag:
export LLM_MODEL=openrouter/openai/gpt-4o-mini # or any LiteLLM slug
export LLM_API_KEY=$OPENROUTER_API_KEY
export LLM_BASE_URL=https://openrouter.ai/api/v1 # omit for native OpenAI
LLM_MODEL uses LiteLLM's full slug. When the provider is OpenRouter the slug is doubly-prefixed: openrouter// (e.g. openrouter/anthropic/claude-sonnet-4.5). For native Anthropic: anthropic/claude-sonnet-4-5. For native OpenAI: openai/gpt-4o-mini.
Suppress the startup banner so JSON output is not preceded by ASCII art:
export OPENHANDS_SUPPRESS_BANNER=1
How to run
Always invoke through the terminal tool. Always pass --headless --json --override-with-envs --exit-without-confirmation for automation.
One-shot task
terminal(
command="OPENHANDS_SUPPRESS_BANNER=1 LLM_MODEL=openrouter/openai/gpt-4o-mini LLM_API_KEY=$OPENROUTER_API_KEY LLM_BASE_URL=https://openrouter.ai/api/v1 openhands --headless --json --override-with-envs --exit-without-confirmation -t 'Add error handling to all API calls in src/'",
workdir="/path/to/project",
timeout=600
)
Background for long tasks
terminal(command="<same as above>", workdir="/path/to/project", background=true, notify_on_complete=true)
process(action="poll", session_id="<id>")
process(action="log", session_id="<id>")
Resume a previous conversation
OpenHands prints Conversation ID: and a Hint: openhands --resume line at the end of each run. Use the dashed form to resume:
terminal(
command="OPENHANDS_SUPPRESS_BANNER=1 LLM_MODEL=... openhands --headless --json --override-with-envs --exit-without-confirmation --resume <dashed-uuid> -t 'Now fix the bug you found'",
workdir="/path/to/project"
)
Real flag list
Verified against openhands --help (CLI 1.16.0). Anything not in this table is not a flag. Pass it via env var or settings file.
| Flag | Effect |
|---|---|
--headless | No UI, requires -t or -f. Auto-approves all actions (no --llm-approve in this mode). |
--json | JSONL event stream (requires --headless). |
-t TEXT | Task prompt. |
-f PATH | Read task from file. |
--resume [ID] | Resume conversation. No ID → list recent. |
--last | Resume most recent (with --resume). |
--override-with-envs | Apply LLM_API_KEY / LLM_BASE_URL / LLM_MODEL env vars. Without this, OpenHands uses ~/.openhands/settings.json and ignores the env. |
--exit-without-confirmation | Don't show the "are you sure" exit dialog. |
--always-approve / --yolo | Auto-approve every action (default in --headless). |
--llm-approve | LLM-based security gate (interactive only, does NOT work in headless). |
--version / -v | Print version and exit. |
There is no --model, --max-iterations, --workspace, --sandbox, --sandbox-type flag. Model is LLM_MODEL. Workspace is the workdir you pass to the terminal tool. Sandbox / runtime is the RUNTIME and SANDBOX_VOLUMES env vars.
JSON event schema
With --json --headless, OpenHands emits JSONL, one JSON object per line, plus a handful of non-JSON status lines (Initializing agent..., Agent is working, Agent finished, the final summary box, Goodbye!, Conversation ID:, Hint:). Filter for lines starting with {.
Top-level kind field discriminates events:
MessageEvent, user / agent text turn.sourceisuseroragent.ActionEvent, agent picked a tool. Readtool_name(file_editor,terminal,finish) andaction.kind(FileEditorAction,TerminalAction,FinishAction).ObservationEvent, tool result.observation.is_erroris the success flag.sourceisenvironment.FinishActioninside anActionEventcarries the agent's final message inaction.message.
The cli prints all stderr from LiteLLM/Authlib first, see Pitfalls. Parse only stdout, line by line, ignoring lines that don't start with {.
Pitfalls
- LiteLLM warnings on every invocation. The CLI prints
bedrock-runtimeandsagemaker-runtimewarnings to stderr becausebotocoreisn't installed. Plus an Authlib deprecation. These are noise, not failures. Pipe stderr to/dev/nullor filter it out before showing the user. - Banner spam. Without
OPENHANDS_SUPPRESS_BANNER=1, every run starts with a multi-line+--+ASCII box advertising the SDK. Always export it. --override-with-envsis mandatory for automation. Without it, OpenHands ignoresLLM_API_KEY/LLM_BASE_URL/LLM_MODELand falls back to~/.openhands/settings.json. On a fresh install this file doesn't exist and the CLI hangs waiting for first-run setup.- Model slug is LiteLLM's, not the provider's.
openrouter/openai/gpt-4o-miniworks;openai/gpt-4o-miniwhile pointed at OpenRouter does not.anthropic/claude-sonnet-4-5(hyphen) is native Anthropic;openrouter/anthropic/claude-sonnet-4.5(dot) is via OpenRouter. Get it wrong → cryptic LiteLLM 400. pip install openhands-aiis the wrong package. That's the legacy V0 SDK. The new CLI isuv tool install openhands --python 3.12. There is no maintained conda package.- Resume ID format is fiddly. The CLI ends with
Conversation ID: f46573d9cfdb45e492ca189bde40019b(no dashes) and then aHint: openhands --resume f46573d9-cfdb-45e4-92ca-189bde40019b(with dashes). Use the dashed form. - Headless ignores
--llm-approve. If you pass it, you get an argparse error. Headless mode hardcodes always-approve. - No Windows support upstream. The OpenHands docs require WSL on Windows. This skill is gated
[linux, macos]accordingly. ~/.openhands/conversations//accumulates. Each run persists a trajectory. Clean it up if running batches.- Heavy install (~200 packages). Use
uv tool install(isolated venv) to avoid dependency conflicts with the active project.
Verification
terminal(
command="OPENHANDS_SUPPRESS_BANNER=1 LLM_MODEL=openrouter/openai/gpt-4o-mini LLM_API_KEY=$OPENROUTER_API_KEY LLM_BASE_URL=https://openrouter.ai/api/v1 openhands --headless --json --override-with-envs --exit-without-confirmation -t 'Print the string OPENHANDS_OK to stdout via the terminal tool.'",
workdir="/tmp",
timeout=120
)
If the JSONL stream ends with a FinishAction whose action.message mentions OPENHANDS_OK, the install is working.
When not to use it
If you are already committed to a single provider, the sibling skills are simpler. For Claude-native workflows, prefer claude-code. For OpenAI-native workflows, prefer codex. If you want Hermes-native subagents that do not require an external CLI install, use delegate_task. OpenHands is the right choice when you need model flexibility or want to run on a non-Anthropic, non-OpenAI provider.
Limits and gotchas
All the pitfalls listed above are real. The most common failure is forgetting --override-with-envs, which causes the CLI to hang waiting for a settings file that does not exist. The second most common is a malformed LiteLLM model slug. Always double-check the slug format for your provider. The resume ID format is also a frequent source of confusion: use the dashed form, not the raw hex string.
Related
- OpenHands GitHub
- OpenHands CLI command reference
- Sibling skills:
claude-code(Anthropic-only),codex(OpenAI-only),opencode(multi-provider via OpenCode),hermes-agent(Hermes subagents viadelegate_task).