hail
Talk to your AI agents from anywhere, and let them talk to each other. One command to message Hermes, OpenClaw, Claude Code, Codex, Gemini CLI, a local Ollama model or any CLI agent, on this machine or on a server over SSH, from your terminal or from any MCP client. When an agent is stuck, it can consult another one itself, with loop guards, a journal and an optional Telegram channel to watch it happen.
hail hermes "Is the nightly scraper still running? One line."
hail openclaw "Summarise today's inbox in 3 bullets."
echo "Long brief..." | hail reviewer
No copy-paste between chat windows. No new server, no open port, no runtime dependency (only tomli on Python 3.10): hail rides on the SSH access you already have.
Why
You run agents in different places: Hermes on a VPS, OpenClaw behind Telegram, Claude Code on your laptop. Getting them to talk usually means copying text between windows, or deploying a dashboard and opening an API to the internet.
hail is the small missing piece: a thin, auditable bridge (about 3,000 lines of Python, standard library only) that turns "ask that agent" into a single command or MCP tool call, for you and for your agents.
| hail | Agent dashboards | Per-agent MCP bridges | |
|---|---|---|---|
| Extra server | none | yes (web app + DB) | yes (HTTP + OAuth) |
| Network exposure | SSH only | web UI | agent API exposed |
| Agents | any CLI agent | supported runtimes | one agent each |
| Install | one command | Docker / Node | Docker |
Install
Solo (any terminal)
uv tool install hail-agent
# or
pipx install hail-agent
# latest main: uv tool install git+https://github.com/kerneldottoday/hail
Python 3.10+. The distribution name hail-agent is unrelated to the
Hail genomics library, which also imports as hail. Install with
uv tool or pipx (isolated environments) so the two never collide. Then:
hail add hermes --preset hermes --host my-vps --set 'bin=~/.local/bin/hermes'
hail doctor
hail hermes "ping, reply OK"
With Claude Code
Paste this into Claude Code:
Install hail from https://github.com/kerneldottoday/hail following its INSTALL.md, then help me add my agents.
Or install it as a plugin, which also teaches Claude when and how to use it:
/plugin marketplace add kerneldottoday/hail
/plugin install hail@hail
To give Claude (or any MCP client) native tools instead of shell calls:
claude mcp add hail -- hail mcp
Configure
Agents live in ~/.config/hail/agents.toml (override with HAIL_CONFIG). hail init creates a commented template, and hail add writes entries for you.
[agents.hermes]
preset = "hermes"
host = "my-vps" # an alias from ~/.ssh/config; omit for a local agent
bin = "~/.local/bin/hermes" # optional, if not on the remote PATH
timeout = 120
[agents.openclaw]
preset = "openclaw"
host = "my-vps"
agent_id = "main" # session defaults to "hail", kept apart from your real chats
path = ["~/apps/node24/bin"] # extra PATH entries on the remote side
[agents.reviewer]
preset = "claude" # local Claude Code, print mode
[agents.anything]
preset = "command"
command = "my-agent --stdin" # any program that reads the message on stdin
parser = "json:answer" # optional: text (default), hermes, json:<key>
Presets
| Preset | Runs | Reply parsing |
|---|---|---|
hermes |
hermes chat -q <msg> -Q --max-turns 8 |
strips session id and cost bar |
openclaw |
openclaw agent --agent <id> --session-key hail --message <msg> --json |
finalAssistantVisibleText |
claude |
claude -p, message on stdin |
plain text |
codex |
codex exec --skip-git-repo-check -, message on stdin |
plain text (final message) |
gemini |
gemini headless, message on stdin |
plain text |
ollama |
ollama run <model> (default llama3.2), message on stdin |
plain text |
command |
your command, message on stdin | text or json:<key> |
In advice mode, claude runs with --permission-mode plan, codex with --sandbox read-only and hermes with read-only toolsets only (-t web,session_search,vision,hail: no terminal, files, code, browser or cron; narrow it with advice_toolsets, which only accepts read-only toolsets). OpenClaw switches to a read-only agent you define (advice_agent_id = "advisor"), and a command agent can name its read-only variant (advice_command = "mybot --read-only"). hail list marks the agents whose advice is only an instruction. Gemini's plan mode switches to auto-approval once the plan is done in headless runs, so hail does not use it: for gemini, advice is an instruction, not a sandbox. An ollama model has no tools at all, which makes it a cheap, safe second opinion.
Paid agents and pinned SSH keys
max_calls_per_day = 50 caps the asks an agent receives in any rolling 24 hours, all callers and threads together (you included). Set it on agents billed per call: a loop between two agents stops there instead of on your invoice.
The safest way to reach an agent on another machine is an SSH key that can only run one thing. On the server, pin the key in ~/.ssh/authorized_keys:
restrict,from="100.64.0.7",command="/usr/local/bin/ask-advisor" ssh-ed25519 AAAA... hail-from-vps
command= makes the server ignore what the client asks to run, restrict turns off forwarding and terminals, from= limits where the key works. The script reads the message on stdin and calls a read-only agent, for example exec openclaw agent --agent advisor --session-key hail --message "$(head -c 100000)" --json. On the client, mark the agent:
[agents.pi]
preset = "openclaw" # still picks the reply parser
host = "pi-advice" # Host alias using that key
forced_command = true # send the message only; the server decides what runs
hail then sends nothing but the message, and hail doctor tests only the SSH connection. A stolen key gives the read-only agent and nothing else; with Tailscale, an ACL that opens only port 22 of that machine narrows it further. hail cannot see what the script runs, so the read-only part is yours to check.
Every setting that is not a known key (bin, agent_id, session, max_turns...) fills the preset template, so presets stay easy to tweak.
Commands
hail <agent> "message" ask an agent (or pipe the message on stdin)
[--json] [--thread T] [--mode advice|action] [--from NAME] [--hop N]
hail ask --skill S "msg" ask the cheapest agent that has skill S
hail send <agent> "msg" background task, prints its id
hail status <task> state and result of a background task
hail council --agents a,b [--synth c] "question" several opinions, one synthesis
hail log [-n N] [--grep X] recent messages between agents
hail threads | thread <id> list threads | show one thread
hail stop | resume <id> block or reopen a thread
hail telegram test | watch check the Telegram channel | take commands from it
hail jobs [agent] [--all] what agents have scheduled (next run in UTC + local time)
hail inbox [agent] [--all] what agents delivered since you last looked
hail inbox read <agent> <id> | clear [agent]
hail prune --days N delete journal entries older than N days
hail list configured agents (with skills and cost)
hail add <name> --preset <p> [--host <alias>] [--command <cmd>] [--set key=value]
hail doctor check every agent is reachable (never sends it a message, costs nothing)
hail mcp MCP server over stdio
MCP tools: hail_list, hail_ask, hail_send, hail_status, hail_council, hail_thread, hail_jobs, hail_inbox, hail_inbox_read.
Agents talking to each other
Install hail where your agents run and add hail mcp to each agent's MCP servers. Hermes, for example:
hermes mcp add hail --command ~/.local/bin/hail --args mcp
and in ~/.hermes/config.yaml, give hail the agent's identity (Hermes filters the environment of MCP servers):
hail:
command: /home/me/.local/bin/hail
args: [mcp]
env:
HAIL_SELF: hermes
HAIL_THREAD: ${HAIL_THREAD}
HAIL_HOP: ${HAIL_HOP}
Now hail hermes "migrate the backups" can turn into Hermes calling hail_ask(agent="claude", message="what rule avoids silent cron failures?") on its own. What hail adds to each call:
- Threads and hops. Every call belongs to a thread and has a hop number (you → hermes is hop 1, hermes → claude hop 2). hail exports
HAIL_THREAD,HAIL_HOPandHAIL_SELFto the agent it runs and writes the same facts in a short[hail]header, so the next call in the chain is counted even across machines. - Loop guards. A chain stops at
max_hops(3), a thread atmax_calls_per_thread(12), the machine atrate_per_minute(30): ping-pong between two agents ends with a logged refusal, not a bill. - Advice by default. Between agents the default mode is
advice(Claude runs with--permission-mode plan); asking another agent to act needs[policy.act].[policy.talk]restricts who may consult whom. - Continuity. Asking the same agent again in a thread sends it the earlier exchanges, so nobody re-explains.
- Skills. Give agents
skills = [...]andcost = "low|medium|high";hail_ask(skill="code-review")picks the cheapest available one that is not the caller. - Councils.
hail_councilasks 2 to 6 agents in parallel and can have one write a synthesis. - Background tasks.
hail_sendreturns a task id at once;hail_statusgives the result later. - Journal. Every ask, reply, error, refusal and human note goes to
~/.hail/hail.db(SQLite). Read it withhail log,hail threads,hail thread <id>or thehail_threadtool.
[policy]
max_hops = 3
max_calls_per_thread = 12
rate_per_minute = 30
[policy.talk]
hermes = ["claude", "openclaw"]
[policy.act]
hermes = ["openclaw"]
Jobs and inbox
Agents that run on a schedule (Hermes cron jobs, for example) keep working while you are away. hail jobs lists what each one has planned, with the next run in UTC and in your local time (--tz Europe/Paris to pick one). hail inbox lists the reports they delivered since you last looked, hail inbox read <agent> <id> prints one and marks it read. Both are read-only and never wake the agent, so they cost no tokens.
Hermes works out of the box (hail reads ~/.hermes/cron/ on its machine). Work it finishes outside a cron job (a chat, a task resumed by hand) can reach you too: give it a mailbox folder, tell it to drop a note there when it is done, and the note shows up in hail inbox as mailbox/<file> (top-level files only, dotfiles ignored):
[agents.hermes]
preset = "hermes"
host = "my-vps"
mailbox = "~/shared/from-agents/hermes" # on the agent's machine
hail inbox clear remembers the newest report it cleared, so older reports that an agent's cleanup lets back into the list never come back as new.
Any other agent can join with a feed_command: a read-only program that receives HAIL_FEED_ARGS (["jobs"], ["inbox"] or ["read", "<id>"]) and prints JSON:
[agents.mybot]
preset = "command"
command = "mybot --stdin"
feed_command = "mybot-feed" # prints [{"id", "name", "schedule", "next_run", ...}] or [{"id", "created", "preview"}]
Telegram channel
Create a bot with @BotFather, add it as admin of a private group with Topics enabled, and put the token in a file (chmod 600), never in agents.toml:
[telegram]
chat_id = -1001234567890 # the group
token_file = "~/.config/hail/telegram.token" # or token_env = "HAIL_TELEGRAM_TOKEN"
allowed_users = [123456789] # who may send commands
topics = true # one topic per thread
Every exchange between agents is posted in its thread's topic, with API keys, tokens and passwords redacted. Run hail telegram watch (e.g. as a systemd service) to accept commands from allowed_users: /stop and /resume inside a topic block or reopen that thread, any other text in a topic becomes a note the agents see on their next call, /ask <agent> <message> starts a new thread in the background, /threads lists recent ones.
Journal
Every ask, reply, error and refusal goes to ~/.hail/hail.db (SQLite, WAL mode), with API keys, tokens and passwords masked before they are written. To keep it small, either run hail prune --days 30 from time to time or let hail do it once a day:
[journal]
retention_days = 90
Pruning deletes old messages, empty threads and finished background tasks; a task that is still running is never touched. A background task whose worker died (reboot, kill) is marked failed after six hours instead of blocking new ones.
How it works
For a remote agent, hail runs ssh -o BatchMode=yes <host> sh -c '<script>' and writes your message to SSH's stdin. On the server the script reads it with msg=$(cat) and calls the agent with "$msg". Your text is never pasted into a command line, so quotes, $, backticks and accents arrive untouched (there is a test for exactly that). Local presets run the agent directly, without a shell. The command preset runs your command through the local shell and sends the message on stdin.
The remote host needs a POSIX sh (any Linux or macOS server). On Windows, an agent installed as a .cmd/.bat shim (npm) cannot safely take the message as an argument, so hail refuses it; use the claude preset (stdin), a command agent, or run the agent over SSH/WSL.
On the server the agent runs under timeout (coreutils) with the same limit, so a call that times out does not leave the agent working on its own after hail gave up. Calls are synchronous with a per-agent timeout (default 180 s) and a size cap (default 8000 characters, header and history included). Use hail send / hail_send for longer requests.
Safety
- Replies are data, not instructions. Another model wrote them. The MCP tool labels every reply as untrusted, and you should verify facts and open cited URLs yourself: agents do invent sources.
- hail_ask is a prompt-injection path. If an MCP client can call
hail_askand the target agent has tools (shell, email, files), whatever text reaches the client can end up as a request to that agent. Only configure agents whose permissions you are comfortable exposing, and prefer read-only agents. - hail stores every message in
~/.hail/hail.dbon the machine that makes the call (secrets masked by pattern, which is a safety net). hail creates it owner-only (700, file600) and tightens it on every start; prune it, or delete it to forget. It posts to a chat only if you configure[telegram](secrets are redacted with patterns, which is a safety net, not a guarantee). OpenClaw runs without--deliver. - Advice mode is enforced only where the agent has a read-only switch: Claude Code (
--permission-mode plan), Codex (--sandbox read-only), Hermes (read-only toolsets), OpenClaw withadvice_agent_id,commandagents withadvice_command, and Ollama (no tools). Elsewhere (Gemini, OpenClaw orcommandwithout a read-only variant) it is an instruction in the header, not a sandbox. Setstrict_advice = trueunder[policy]so agents can only consult agents that enforce it; you can still ask any of them yourself. hail checks thatadvice_agent_idandadvice_commanddiffer from the full agent, not that they are really read-only: that part is yours. Strict mode applies to calls hail sees as coming from an agent (HAIL_SELFset, or an MCP caller); an agent that runshailin a shell whereHAIL_SELFwas removed counts as you. - Identity: an agent is verified when hail exported
HAIL_SELFto it (or declared it in its MCP config). An MCP caller that only claims a name withfrommay ask for advice, never for an action, unless the journal shows it was really asked something in that thread; an unidentified MCP caller isunknown-agent, never you./askfrom Telegram (allowed users only) runs as you, in action mode. - Agents consulting each other multiply the prompt-injection paths: keep the default
advicemode, grant[policy.act]sparingly, and read the journal. - For local presets that take the message as an argument (
hermes,openclaw), the message is visible in the local process list while the call runs. Remote messages travel only on the SSH channel. - A remote agent reached with an ordinary key runs whatever hail sends, and so would anyone holding that key. Prefer a pinned key (
forced_command, see Paid agents and pinned SSH keys). - It uses your SSH config and keys as they are (
BatchMode=yes: it never prompts for a password). Give the remote account only the rights the agent needs. - Anyone who can edit
agents.tomlcan make hail run commands. Keep it as private as your SSH config.
Development
uv venv && uv pip install -e ".[dev]"
pytest --cov=hail
Adding a preset is a few lines in src/hail/presets.py. PRs for new agents are welcome.
License
MIT
Metadata
Release files for hail-agent 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| hail_agent-0.4.1.tar.gz | 1.2 MB | With provenance | Details for hail_agent-0.4.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| hail_agent-0.4.1-py3-none-any.whl | Python 3 | none | any | Details for hail_agent-0.4.1-py3-none-any.whl |
Total release size: 1.2 MB
Release files / hail_agent-0.4.1.tar.gz
| Download URL | hail_agent-0.4.1.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
be0550dcc16411ef5b470860b68b7dbbc7e831acb07f1d56de96d51b85e38fa9
|
|
BLAKE2b-256 checksum How to use checksums |
79af3ac3f7137e9e44e8a4231491144af02e1f5858c3e21531ee7ade73a4a940
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / hail_agent-0.4.1-py3-none-any.whl
| Download URL | hail_agent-0.4.1-py3-none-any.whl |
|---|---|
| Size | 56.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
78acad667ba615de695bb3543c98f7d05802d1c9de0a69ad5b5604244267ba28
|
|
BLAKE2b-256 checksum How to use checksums |
82c2809b9a1948950dc20b25782c6587aafc701db323df56d3fc761e1df1d630
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for hail-agent 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for hail_agent-0.4.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.