Hermes (Nous Research)
Hermes Agent is a multi-provider Python coding agent. On Jetty it's the most flexible runtime — it speaks Anthropic, OpenAI, OpenRouter, Google, and AWS Bedrock natively, including third-party Bedrock catalog models (DeepSeek, Zhipu GLM, etc.) that other runtimes can't reach.
Use the hermes runtime when you need:
- A non-Anthropic Bedrock model (DeepSeek, Zhipu, Mistral, Llama)
- Multi-provider parity in one runbook (drop-in swap between Bedrock, OpenAI, OpenRouter, and Gemini)
- Native AWS SigV4 /
AWS_BEARER_TOKEN_BEDROCKBedrock access - A single binary that drives every gateway your collection has keys for
Provider support
| Provider | Supported | How it routes |
|---|---|---|
| AWS Bedrock | ✅ | Built-in bedrock plugin via hermes-agent[bedrock] (Anthropic Bedrock SDK / SigV4) |
| Anthropic | ✅ | Custom jetty_anthropic provider (anthropic_messages transport, direct API) |
| OpenAI | ✅ | Custom jetty_openai provider (chat_completions transport, direct API) |
| OpenRouter | ✅ | Custom jetty_openrouter provider (chat_completions transport, pinned to https://openrouter.ai/api/v1) |
| ✅ | Built-in gemini provider |
Confirmed-working models
These configurations have been verified end-to-end in production runs (metaphrenie/write-a-poem, May 2026). For Bedrock, use the cross-region inference profile form (us.anthropic.*) for Anthropic models and the bare catalog ID for third-party models.
| Provider | Model ID | Notes |
|---|---|---|
| AWS Bedrock | us.anthropic.claude-sonnet-4-6 | Cross-region inference profile |
| AWS Bedrock | us.anthropic.claude-haiku-4-5-20251001-v1:0 | Full versioned profile also accepted |
| AWS Bedrock | deepseek.v3.2 | Third-party Bedrock catalog model |
| AWS Bedrock | zai.glm-5 | Third-party Bedrock catalog model |
Known-broken model IDs
| Model ID | What happens | Use instead |
|---|---|---|
anthropic.claude-sonnet-4-6 (bare) | Bedrock rejects without the us. prefix on us-east-1 | us.anthropic.claude-sonnet-4-6 |
qwen.qwen3-32b-v1:0 | Fails — qwen models on Bedrock require a different ID shape or aren't enabled on the bearer-token's account | Pick a different model |
meta.llama4-maverick-17b-instruct-v1:0 | Fails for the same reason | Pick a different model |
If a Bedrock model fails silently, the upstream error is suppressed by Hermes's -Q flag but is captured in ~/.hermes/logs/errors.log inside the sandbox. Jetty also surfaces "agent 'hermes' produced no /app/results files" in the trajectory output as a hard failure.
Setup
Configure provider keys on your collection. Hermes auto-routes based on which keys are present.
TOKEN="$(cat ~/.config/jetty/token)"
cat <<'BODY' | curl -s -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://flows-api.jetty.io/api/v1/collections/{COLLECTION}/environment" \
--data-binary @-
{
"environment_variables": {
"AWS_BEARER_TOKEN_BEDROCK": "ABSK...",
"AWS_REGION": "us-east-1"
}
}
BODY
For Anthropic, set ANTHROPIC_API_KEY; for OpenAI, OPENAI_API_KEY; for Google, GEMINI_API_KEY.
Use it in a runbook
---
agent: hermes
model: us.anthropic.claude-sonnet-4-6
snapshot: python312-uv
---
# Write a Poem
Generate a haiku about {{prompt}} and write it to `{{results_dir}}/poem.md`.
Or via the Chat Completions API:
{
"model": "us.anthropic.claude-sonnet-4-6",
"jetty": {
"runbook": true,
"agent": "hermes",
"collection": "my-collection",
"task": "my-task"
}
}
How routing works
When Jetty creates the sandbox, the runner reads the collection's environment variables and picks a provider in this priority order:
AWS_BEARER_TOKEN_BEDROCKorAWS_ACCESS_KEY_ID→--provider bedrock(built-in plugin)ANTHROPIC_API_KEY→--provider jetty_anthropic(custom direct-API entry)OPENROUTER_API_KEY→--provider jetty_openrouter(custom OpenRouter entry)GEMINI_API_KEY/GOOGLE_API_KEY→--provider geminiOPENAI_API_KEY→--provider jetty_openai(custom direct-API entry)
The order is important on two counts. First, Hermes v0.12.0+ requires one of OPENAI_API_KEY / OPENROUTER_API_KEY / ANTHROPIC_API_KEY in the env to pass its first-run guard. When routing through Bedrock without one of those, Jetty sets a sentinel OPENAI_API_KEY that's never actually read — but Bedrock is still chosen because AWS_BEARER_TOKEN_BEDROCK is checked first. Second, OPENROUTER_API_KEY is checked before OPENAI_API_KEY so that fan-out collections with both keys configured still route OpenRouter calls through the explicit OpenRouter entry rather than falling through to OpenAI.
Limitations
- Token usage is captured post-run by reading
~/.hermes/sessions/session_<id>.jsonafter the agent exits. The streaming log file (/logs/agent/hermes.txt) contains only the final assistant text plus asession_id:anchor — usage numbers do not stream. - Errors under
-Qare quiet by design. If a run fails with no obvious cause, check/logs/agent/hermes.txtfirst; if it's empty, the model call itself returned 4xx/5xx silently. See Troubleshooting below or runscripts/debug_hermes_bedrock.pyfrom the mise repo to reproduce in isolation. - OpenRouter base URL is hardcoded to
https://openrouter.ai/api/v1. Hermes's config schema doesn't support env-var indirection on theapi:field, so the YAML baked at install time is the URL. Corp-gateway / self-hosted OpenRouter proxies that work withclaude-code/codex/opencode(which honorOPENROUTER_BASE_URL) currently can't be wired through hermes without rebuilding the snapshot.
Troubleshooting
A run that fails fast (under 10 seconds) with success: false and an empty results_files is almost always the model call returning 4xx. Common causes:
- 404 "model does not exist" — the model ID isn't in your account's Bedrock catalog or is missing the regional prefix.
- 401 "is not available for this account" — the model exists but you don't have entitlement; check the AWS Bedrock console.
- Expired
AWS_BEARER_TOKEN_BEDROCK— bearer tokens expire; re-issue from the Bedrock console.
To reproduce locally inside a sandbox identical to production, see mise/scripts/debug_hermes_bedrock.py.