🤖HermesBlog
Hermes Practical Guides · Part 48/9/2026

Hermes Python Library: Script Your AI Agent in Code

Use Hermes Agent from Python — write scripts that call your agent, run tools, and automate workflows.

Guide: Hermes as Python Library

Hermes Python Library: Script Your AI Agent in Code

So you’ve been chatting with Hermes in the terminal, and now you’re thinking: “How do I make this thing work inside my own app?” Good news — Hermes isn’t just a chat tool. It’s a full-blown agent engine you can drive from any external program. Whether you’re building a VS Code extension, a custom dashboard, or a CI pipeline, Hermes has a protocol for you.

Let’s break down the three ways you can plug into Hermes programmatically.

Three Protocols, One Core

Hermes ships with three different “doors” into the same AIAgent engine. They all do the same thing under the hood — the only difference is how they talk to the outside world.

Protocol Transport Best for
ACP JSON-RPC over stdio IDE clients (VS Code, Zed, JetBrains)
TUI Gateway JSON-RPC over stdio or WebSocket Custom hosts wanting fine-grained control
API Server HTTP + Server-Sent Events OpenAI-compatible frontends and web clients

Pick the one that matches your transport and your consumer. They all drive the same core — just with different wire formats.


ACP: For IDE Integrations

If you’re building an IDE plugin, hermes acp is your friend. It starts a stdio JSON-RPC server that speaks the Agent Client Protocol — the same protocol used by VS Code, Zed, and JetBrains.

What can you do with it? Session creation, prompt submission, streaming message chunks, tool-call events, permission requests, session forking, cancellation, and authentication. Tool output gets rendered into ACP Diff/ToolCall blocks that IDEs understand natively.

hermes acp                  # serve ACP on stdio
hermes acp --check          # verify ACP dependencies
hermes acp --setup          # interactive provider/model setup

TUI Gateway: Full Control Over Everything

The TUI Gateway is the protocol the Ink TUI itself uses. Any external host can speak the same protocol over stdio or WebSocket. This is the one to pick if you want fine-grained control over sessions, slash commands, approvals, and streaming events.

The method catalog is extensive — here’s a taste:

prompt.submit           prompt.background       session.steer
session.create          session.list            session.active_list
session.activate        session.close           session.interrupt
session.history         session.compress        session.branch
session.title           session.usage           session.status
clarify.lock            config.set / config.get commands.catalog
client.capabilities     gateway.capabilities    ping
command.resolve         command.dispatch        cli.exec
reload.mcp              reload.env              process.stop
delegation.status       subagent.interrupt      subagent.steer
spawn_tree.save / list / load
terminal.resize         clipboard.paste         image.attach

Live sessions vs. saved transcripts. The gateway distinguishes between sessions currently open in the process (session.active_list, session.activate, session.close) and saved transcripts you can resume (session.list). Use the active-session methods only for what’s currently open.

Model Overrides on session.create

You can pass per-session model / provider overrides when creating a session. If the pair can’t be served — say model: gpt-5.5 with provider: anthropic — the gateway refuses up front with JSON-RPC code -32602 instead of creating a session that fails on its first turn. The error carries the model, provider, and up to five suggestions from that provider’s catalog. The check is offline and only rejects names Hermes knows belong elsewhere, so custom endpoints, aggregators, and unlisted names still work as before.

Rewinding History Safely

One important change: rewinding or editing history is now a special kind of prompt.submit. When you send a truncation, you must declare your intent:

  • truncate_before_user_ordinal — zero-based index of the user turn to cut at
  • truncate_before_row_id — durable SQLite row ID (preferred)
  • confirm_truncaterequired flag saying “this is a rewind, not a normal send”
  • confirm_empty_truncate — extra flag when the cut would empty the transcript

If you send truncation parameters without confirm_truncate, the gateway refuses with code 4004 or 4029. A rewind is also never absorbed by the busy-input policy: while a turn is still running, an ordinary submit gets steered or queued, but a rewind refuses with code 4009 (session busy). Call session.interrupt and retry the same submit until it lands — the Desktop app does this automatically, so editing a message mid-thought stops the live turn and reruns from your edit.

After a successful rewind, the response includes survivor_user_row_ids — the fresh row IDs of surviving turns. Important: every row ID you cached before the rewind is now stale. Rebinding from this list is mandatory, or your next rewind targeting an older turn will fail with 4018.

Events Streamed Back

The gateway streams events like message.delta, message.complete, tool.start, tool.generating, tool.complete, gateway.ready, and request.cancel, plus session lifecycle and error events.

Server→Client Requests: Questions the Agent Asks You

Approvals, clarify questions, sudo/secret prompts, vault unlock, MCP setup, and the desktop read/act bridges are JSON-RPC requests from the gateway to the client, not events. The frame carries a string id, and you answer with a normal JSON-RPC response bearing the same id:

← {"jsonrpc":"2.0","id":"srq-7","method":"approval","params":{"session_id":"…","request_id":"…","command":"rm -rf build","description":"…"}}
→ {"jsonrpc":"2.0","id":"srq-7","result":{"choice":"once"}}

Methods include approval{choice}, clarify{answer} (or {answers} for batches, with clarify.lock to lock one answer early), and sudo / secret / vault.code / vault.unlock_prompt{value}. If your host doesn’t implement a method, reply with a JSON-RPC error (-32601) so the agent fails fast instead of waiting out the timeout.

Advertise that you answer them. Once per connection, after gateway.ready, call client.capabilities with {"server_requests": true}. A WebSocket client that never does is treated as a build that predates server→client requests, and the gateway fails every such request for it immediately instead of stalling. There’s no grace path — add the one call. A session with no client attached isn’t affected; its open questions wait in open_requests for the reconnect replay.

When the gateway withdraws a question (timeout, interrupt, answered elsewhere) it emits request.cancel { id, method, reason }; clear only the matching prompt. session.resume / session.activate results and session.events.since carry open_requests — the still-open frames — so a reconnecting client can re-render and still answer them.

Rebuilding the In-Flight Turn on Reconnect

session.resume / session.activate results carry inflight — the turn still running (or the retained failed one) that history doesn’t hold yet: the user prompt, assistant text streamed so far, streaming, mid-turn corrections, and error fields. When the turn was started by the gateway rather than typed by a person (a background-process completion, an async delegation result, a hidden scaffolding prompt), inflight also carries the same display_kind / display_metadata the persisted row will get, so a client renders the live prompt exactly as it will render history after the turn lands. Both fields are absent for genuine user input — never infer origin from the prompt text.


API Server: OpenAI-Compatible HTTP

For web clients and OpenAI-compatible frontends (Open WebUI, LobeChat, LibreChat), the API server speaks HTTP + Server-Sent Events. It’s language-agnostic — any client that can make HTTP requests can talk to Hermes.


Which One Should You Use?

  • Building an IDE extension? ACP.
  • Need fine-grained session control and custom UI? TUI Gateway.
  • Want OpenAI-compatible endpoints for a web app? API Server.

All three drive the same agent core. The choice is just about your transport and how much control you need. Happy scripting!

📖 Official Docs

This article is based on the official Hermes Agent documentation:Official docs › developer-guide/programmatic-integration