Skip to main content

agents-relay

Version 0.1.0 Python 3.10+ PyPI License

Stdlib HTTP and Telegram relay in front of agents-harness runner.loop.
One subprocess per turn. Trailer parsing for session metadata. No identity store, no traces, no writes under ~/.agents. Approval decisions and downloaded files live in the relay state dir (AGENTS_RELAY_STATE, default ~/.agents-relay).


Quickstart

pip install agents-relay

Needs runner.loop on PATH (LOOP_CMD, default python -m runner.loop). That comes from agents-harness 0.1.0 or newer (approvals, --detached-session, and the {user} approval substitution need it).


Architecture

Component Responsibility
HTTP Adapter Serves /v1/turn and /v1/inject endpoints with secret header validation
Telegram Adapter Long-poll bot updates with real-time thinking status edits, parallel jobs, and Approve/Deny callbacks
Loop Client Spawns buffered runner.loop subprocess per turn with trailer parsing and repeatable --attach
Outbound Send Direct Telegram push via agents-relay send for reminders and notifications
Approval Gate agents-relay approve asks an allowlisted chat and blocks on a decision file
  • Subprocess Isolation: One runner.loop process per request. No in-process LLM logic.
  • No harness state: No identity storage, no traces, no writes under ~/.agents. Relay files (approvals, inbox) stay in AGENTS_RELAY_STATE.
  • Streaming Status: Throttled progress streaming and real-time status edits during long-running tool rounds.
  • Zero Bloat: Pure Python standard library (http.server, urllib.request, subprocess).

Capabilities & Roadmap

Core Capabilities (Implemented)

  • HTTP Turn Endpoints (/v1/turn, /v1/inject): Buffered turn execution with secret header validation.
  • Telegram Long-Poll Gateway: Bot API updates with real-time thinking status message editing.
  • Direct Outbound CLI (send, inject, approve): Host-initiated push messages, simulated turns, and Telegram Approve/Deny for mutating tools.
  • Parallel Telegram jobs: Updates are dispatched off the poll thread. Per-chat cap (default 3) with a queue. /jobs, /stop, /stop <id> kill the loop process group.
  • Long replies: Split into several Telegram messages at paragraph boundaries with balanced HTML. No silent 4096 cut.
  • Attachments: Telegram photos and documents, and /v1/turn attachments, are passed to the loop as --attach.
  • Mobile Formatting: Automatic Markdown table conversion to Telegram-readable bullet lists.
  • Trailer Parsing: Extracts session, user ID, alias, and return code from harness stdout trailers.
  • Subprocess Isolation: One runner.loop process per request. No in-process LLM state.
  • Zero Bloat Runtime: Pure Python standard library only.

Planned (Roadmap)

  • SSE Token Streaming (/v1/stream): Direct server-sent events for client web UIs.
  • A2A Board Mailbox Proxy (/v1/a2a/mail, /v1/a2a/peers): Peer-to-peer agent relay for board instances.
  • Inbound Webhook Hub (/v1/webhook/{source}): Direct ingress for CI/CD and deployment alerts.
  • Multi-Channel Adapters: Signal / Discord gateway adapters alongside Telegram.

Commands

Machine-readable catalog: python -m agents_relay --help-json (do not scrape --help).

Command Purpose
agents-relay serve HTTP /v1/turn, /v1/inject (and /v1/alert, /webhook/alert). Telegram long-poll if TELEGRAM_BOT_TOKEN is set
agents-relay serve --no-telegram HTTP only
agents-relay send --user <chat_id> --text "..." One outbound Telegram message (--chat-id alias). Allowlist + token from env
agents-relay inject --user <chat_id> --text "..." Same inbound handler as poll (thinking edits + final). JSON on stdout. Allowlist + token from env
agents-relay approve --user <chat_id> --timeout 300 Read a tool-approval JSON object on stdin. Send Approve/Deny. Exit 0 approved, 1 denied, 2 timeout or unavailable. One JSON line on stdout: {"note":"..."}

Telegram chats can send /jobs, /stop, and /stop <id> while serve is polling. /stop kills that chat's runner.loop process group (the loop and child tools that stayed in the group).


Environment

Variable Description
LOOP_CMD Loop argv (default python -m runner.loop)
LOOP_PROVIDER Provider name passed through (default echo)
RELAY_SECRET Shared secret (GATEWAY_SECRET fallback). Empty = no auth
TELEGRAM_BOT_TOKEN Enables poll + send
TELEGRAM_ALLOWED_CHAT_IDS Comma-separated numeric IDs. Empty refuses the Telegram adapter unless AGENTS_RELAY_ALLOW_ANYONE=1
AGENTS_RELAY_ALLOW_ANYONE 1 opts in to an empty allowlist. Logs a loud warning. Anyone who can message the bot can run turns and approve tools
AGENTS_RELAY_STATE Relay files (default ~/.agents-relay). Approvals: $AGENTS_RELAY_STATE/approvals/<id>.json. Inbox: .../inbox/
AGENTS_RELAY_APPROVER Chat id used by approve when --user is missing or not numeric (HTTP turns often pass anonymous)
AGENTS_RELAY_ATTACH_DIR Only local /v1/turn attachment paths under this directory are accepted (default: the relay state dir)
AGENTS_RELAY_MAX_JOBS Global cap on parallel Telegram turns (default 8)
AGENTS_RELAY_MAX_JOBS_PER_CHAT Parallel turns per chat before extra messages queue (default 3, queue cap 20)
AGENTS_RELAY_WAIT_TEXT Telegram placeholder while a turn runs (default One moment …). Status and fallback lines are English
RELAY_HOST / RELAY_PORT Bind (default 127.0.0.1:8787; GATEWAY_HOST / GATEWAY_PORT fallbacks)
TELEGRAM_POLL_TIMEOUT Long-poll seconds, clamped 1–50 (default 50)

POST /v1/turn accepts attachments: a list of {"path": "/abs/file", "mime": "image/png"} or {"url": "https://...", "mime": "..."}. A local path must already exist and must resolve inside AGENTS_RELAY_ATTACH_DIR (default: the relay state dir). Paths outside that directory are rejected with HTTP 400. url must be http or https and is downloaded into the relay inbox (20 MB cap). When mime is set, the saved file extension matches it (image/jpeg → .jpg) so the harness can infer the type. Each file is passed to the loop as --attach. Photos and documents from Telegram take the same --attach path. The loop --user for a Telegram turn is the numeric chat id so AGENTS_APPROVAL_CMD can substitute {user}.

approve --user may be a non-numeric turn id (HTTP defaults to anonymous). Then the prompt goes to AGENTS_RELAY_APPROVER, or to the only entry in TELEGRAM_ALLOWED_CHAT_IDS when that list has exactly one chat. Otherwise approve exits 2 and prints {"note":"..."}. A numeric --user is used as given.

approve and serve must share AGENTS_RELAY_STATE on the same host. The poll loop answers callback_query and writes the decision. Only allowlisted user ids can press Approve or Deny. In a private chat that id is the chat id. In a group, list both the group chat id and the approving user id.

HTTP secret headers supported: X-Relay-Secret, Relay-Secret, X-Gateway-Secret, Gateway-Secret.


Constraints

  • Python 3.10+, standard library only.
  • Does not import harness internals. Strictly spawns LOOP_CMD per turn.
  • Klanker / host agents own identity and traces. This process is strictly I/O.

Tests

pytest

or via standard library:

python -m unittest discover -s tests -p "test_*.py"

License

MIT. See LICENSE.

Metadata

Release files for agents-relay 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agents-relay 0.1.0
File Size Uploaded
agents_relay-0.1.0.tar.gz 49.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agents-relay 0.1.0
File Interpreter ABI Platform
agents_relay-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 86.1 kB

Release files / agents_relay-0.1.0.tar.gz

Download URL agents_relay-0.1.0.tar.gz
Size 49.7 kB
Tags Source
SHA-256 checksum
How to use checksums
d87868ca29e713d540dd36c022ef129e13d2fe0cdfb2eda96e6947c84fc2efc8
BLAKE2b-256 checksum
How to use checksums
37bab492cb66d45425ff8836a3fcc2e93f5c1ebde60cbb3eea2985e6d8a71cad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / agents_relay-0.1.0-py3-none-any.whl

Download URL agents_relay-0.1.0-py3-none-any.whl
Size 36.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
665345acb69519f6bc403f64946e1759a916fb812e6abe73bde66eabd0df99e4
BLAKE2b-256 checksum
How to use checksums
0a02a094eecb97cfa447ff12914da737fffa13cf77f3b8ecb594a17fff1e0353
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page