169 lines
8.9 KiB
Markdown
169 lines
8.9 KiB
Markdown
---
|
|
sidebar_position: 8
|
|
title: "Programmatic Integration"
|
|
description: "Three protocols for driving hermes-agent from external programs: ACP, the TUI gateway JSON-RPC, and the OpenAI-compatible HTTP API"
|
|
---
|
|
|
|
# Programmatic Integration
|
|
|
|
Hermes ships three protocols for driving the agent from external programs — IDE plugins, custom UIs, CI pipelines, embedded sub-agents. Pick the one that matches your transport and consumer.
|
|
|
|
| Protocol | Transport | Best for | Defined by |
|
|
|----------|-----------|----------|------------|
|
|
| **ACP** | JSON-RPC over stdio | IDE clients (VS Code, Zed, JetBrains) that already speak the [Agent Client Protocol](https://github.com/zed-industries/agent-client-protocol) | `acp_adapter/` |
|
|
| **TUI gateway** | JSON-RPC over stdio (or WebSocket) | Custom hosts that want fine-grained control of sessions, slash commands, approvals, and streaming events | `tui_gateway/server.py` |
|
|
| **API server** | HTTP + Server-Sent Events | OpenAI-compatible frontends (Open WebUI, LobeChat, LibreChat…) and language-agnostic web clients | `gateway/platforms/api_server.py` |
|
|
|
|
All three drive the same `AIAgent` core. They differ only in wire format and which set of features they expose.
|
|
|
|
---
|
|
|
|
## ACP (Agent Client Protocol)
|
|
|
|
`hermes acp` starts a stdio JSON-RPC server speaking ACP. Used in production by VS Code (Zed Industries' ACP extension), Zed, and any JetBrains IDE with an ACP plugin.
|
|
|
|
Capabilities exposed: session creation, prompt submission, streaming agent message chunks, tool-call events, permission requests, session fork, cancel, and authentication. Tool output is rendered into ACP `Diff`/`ToolCall` content blocks the IDE understands.
|
|
|
|
Full lifecycle, event bridge, and approval flow: [ACP Internals](./acp-internals).
|
|
|
|
```bash
|
|
hermes acp # serve ACP on stdio
|
|
hermes acp --check # verify ACP dependencies and adapter imports
|
|
hermes acp --setup # interactive provider/model setup for ACP terminal auth
|
|
```
|
|
|
|
---
|
|
|
|
## TUI Gateway JSON-RPC
|
|
|
|
`tui_gateway/server.py` is the protocol the Ink TUI (`hermes --tui`) and the embedded dashboard PTY bridge talk to. Any external host can speak the same protocol over stdio (or WebSocket via `tui_gateway/ws.py`).
|
|
|
|
### Method catalog (selected)
|
|
|
|
```
|
|
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.respond sudo.respond secret.respond
|
|
approval.respond config.set / config.get commands.catalog
|
|
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
|
|
```
|
|
|
|
`session.active_list`, `session.activate`, and `session.close` are the process-local live-session controls used by the TUI session switcher. Use `session.list` / `/resume` for saved transcript discovery; use the active-session methods only for sessions that are currently open in the TUI gateway process.
|
|
|
|
### Rewinding history on `prompt.submit`
|
|
|
|
A rewind / edit / regenerate is a `prompt.submit` that drops part of the stored transcript before running the new turn. Because that write is a destructive rewrite of the session's durable rows, the gateway honors it only when the client states its intent:
|
|
|
|
| Parameter | Meaning |
|
|
|-----------|---------|
|
|
| `truncate_before_user_ordinal` | Zero-based index of the user turn to cut at. Everything from that turn onward is dropped. Display-only timeline rows (`display_kind`) are not counted. |
|
|
| `confirm_truncate` | Required whenever an ordinal is sent. Declares that this submit really is a rewind, not an ordinary send that happens to carry a leftover ordinal. |
|
|
| `confirm_empty_truncate` | Additionally required when the cut would leave the transcript empty (ordinal `0`). |
|
|
|
|
An ordinal without `confirm_truncate` is refused with code `4029` and nothing is written. Hosts that implement rewind must set the flag at the moment the user asks for it, and must never keep the ordinal in state across ordinary submits.
|
|
|
|
### Events streamed back
|
|
|
|
`message.delta`, `message.complete`, `tool.start`, `tool.progress`, `tool.complete`, `approval.request`, `clarify.request`, `sudo.request`, `sudo.expire`, `secret.request`, `secret.expire`, `gateway.ready`, plus session lifecycle and error events. Expiry events carry the original `{ request_id }`; external hosts should clear only the matching pending prompt.
|
|
|
|
### Pi-style RPC mapping
|
|
|
|
Every command in the Pi-mono RPC spec ([issue #360](https://github.com/NousResearch/hermes-agent/issues/360)) has a TUI-gateway equivalent:
|
|
|
|
| Pi command | Hermes equivalent |
|
|
|------------|-------------------|
|
|
| `prompt` | `prompt.submit` (or ACP `session/prompt`) |
|
|
| `steer` | `session.steer` |
|
|
| `follow_up` | `prompt.submit` queued after current turn |
|
|
| `abort` | `session.interrupt` |
|
|
| `set_model` | `command.dispatch` for `/model <provider:model>` (mid-session, persistent) |
|
|
| `compact` | `session.compress` |
|
|
| `get_state` | `session.status` |
|
|
| `get_messages` | `session.history` |
|
|
| `switch_session` | `session.resume` |
|
|
| `fork` | `session.branch` |
|
|
| `ui_request` / `ui_response` | `clarify.respond` / `sudo.respond` / `secret.respond` / `approval.respond` |
|
|
|
|
---
|
|
|
|
## OpenAI-Compatible API Server
|
|
|
|
`gateway/platforms/api_server.py` exposes hermes over HTTP for any client that already speaks the OpenAI format. Useful when you want a web frontend, a curl-driven CI runner, or a non-Python consumer.
|
|
|
|
Endpoints:
|
|
|
|
```
|
|
POST /v1/chat/completions OpenAI Chat Completions (streaming via SSE)
|
|
POST /v1/responses OpenAI Responses API (stateful)
|
|
POST /v1/runs Start a run, returns run_id (202)
|
|
GET /v1/runs/{id} Run status
|
|
GET /v1/runs/{id}/events SSE stream of lifecycle events
|
|
POST /v1/runs/{id}/approval Resolve a pending approval
|
|
POST /v1/runs/{id}/stop Interrupt the run
|
|
GET /v1/capabilities Machine-readable feature flags
|
|
GET /v1/models Lists hermes-agent
|
|
GET /api/model/options Provider-aware picker inventory
|
|
GET /health, /health/detailed
|
|
```
|
|
|
|
Setup, headers (`X-Hermes-Session-Id`, `X-Hermes-Session-Key`), and frontend wiring: [API Server](../user-guide/features/api-server).
|
|
|
|
### Model catalog surfaces
|
|
|
|
The OpenAI-compatible API intentionally keeps `GET /v1/models` minimal: it is
|
|
the compatibility endpoint frontends expect, not the full Hermes provider/model
|
|
picker catalog.
|
|
|
|
If an external control plane needs Hermes' curated provider rows, per-model
|
|
pricing, or capability hints, use one of the authenticated picker surfaces:
|
|
|
|
- API server REST: `GET /api/model/options` with the API-server bearer key
|
|
- Dashboard backend REST: `GET /api/model/options` with `X-Hermes-Session-Token`
|
|
- TUI gateway RPC: `model.options`
|
|
|
|
Those surfaces share the same payload builder and the same custom-provider
|
|
probe policy:
|
|
|
|
- Normal open: probe only the current custom provider so offline saved
|
|
endpoints do not stall the picker.
|
|
- Explicit refresh (`refresh=1` or `refresh: true`): bust the provider-model
|
|
cache and probe all saved custom providers so live catalogs repopulate fully.
|
|
|
|
Use `/v1/models` for OpenAI-client compatibility. Use `/api/model/options` or
|
|
`model.options` when you are building a Hermes-aware model picker.
|
|
|
|
---
|
|
|
|
## Which one should I use?
|
|
|
|
- **You're writing an IDE plugin and the IDE already speaks ACP** → ACP. Zero protocol work on the IDE side.
|
|
- **You're writing a custom desktop / web / TUI host and want every Hermes feature** (slash commands, approvals, clarify, multi-agent, session branching) → TUI gateway JSON-RPC.
|
|
- **You want any OpenAI-compatible frontend, a language-agnostic HTTP client, or curl-driven automation** → API server.
|
|
- **You want a Python in-process embed without a subprocess** → import `run_agent.AIAgent` directly. See [Agent Loop](./agent-loop).
|
|
|
|
---
|
|
|
|
## Model hot-swapping
|
|
|
|
Mid-session model switching works on every surface — it's the `/model` slash command under the hood.
|
|
|
|
- **CLI / TUI:** `/model claude-sonnet-4` or `/model openrouter:anthropic/claude-sonnet-4.6`
|
|
- **TUI gateway RPC:** `command.dispatch` with `{"command": "/model claude-sonnet-4"}`
|
|
- **ACP:** the IDE sends the slash command as a prompt; the agent dispatches it
|
|
- **API server:** include a `model` field in the request body
|
|
|
|
Provider-aware resolution (the same model name picks the right format for whatever provider you're on) is built in. See `hermes_cli/model_switch.py`.
|
|
|
|
---
|
|
|
|
## A note on `--mode rpc`
|
|
|
|
Hermes does not have a `--mode rpc` flag. The three protocols above already cover the use cases — ACP for IDE-protocol clients, the TUI gateway for stdio JSON-RPC hosts, and the API server for HTTP. If you find a real gap that none of them fill, open an issue with the concrete consumer you're building.
|