Compare commits

...
Author SHA1 Message Date
Bailey Dixon 0a5649d016 docs: refresh upstream API baseline references 2026-06-04 21:59:52 -04:00
13 changed files with 95 additions and 69 deletions
+17 -16
View File
@@ -33,25 +33,26 @@ Chat goes directly to the API server via HTTP/SSE. The API key (Bearer token) is
| `GET /health` | Health check | — |
| `GET/POST/PATCH/DELETE /api/jobs/*` | Cron job management (api_server surface) | — |
**Non-standard endpoints (provided by fork OR by plugin bootstrap):**
**Baseline upstream endpoints vs compatibility endpoints:**
These endpoints are not in stock upstream `gateway/platforms/api_server.py`. There are three ways a hermes-agent install can serve them:
Upstream hermes-agent now has a native baseline for API Server session control and skill/toolset discovery:
1. **Codename-11 fork** (`feat/session-api` branch, deployed on the `axiom` branch) — adds them natively. Submitted upstream as PR [#8556](https://github.com/NousResearch/hermes-agent/pull/8556) *"feat(api-server): add session management API for frontend clients"* — scope is broader than the title: sessions CRUD + session chat/stream + memory + skills + config + available-models.
2. **Bootstrap injection** (`hermes_relay_bootstrap/`) — monkey-patches aiohttp on startup via `.pth` file. Does NOT inject `/api/sessions/{id}/chat/stream` — use `/v1/runs` for chat.
3. **Upstream-merged** (post PR #8556) — bootstrap auto-detects and no-ops.
1. **Native upstream** — commit [`f7527b0`](https://github.com/NousResearch/hermes-agent/commit/f7527b0fdb54f01691547df03fc65a6d367f9fde), merged via PR [#33134](https://github.com/NousResearch/hermes-agent/pull/33134), salvaged the focused session-control work from closed PR [#29302](https://github.com/NousResearch/hermes-agent/pull/29302). It provides `/api/sessions/*`, session chat/stream, fork/messages, plus `/v1/skills` and `/v1/toolsets`.
2. **Codename-11 `axiom` fork** — still carries compatibility/client-metadata routes that upstream does not provide yet: `/api/sessions/search`, `/api/memory`, `/api/skills` detail routes, `/api/config`, and `/api/available-models`.
3. **Bootstrap injection** (`hermes_relay_bootstrap/`) — monkey-patches aiohttp on startup via `.pth` file and injects only missing compatibility routes for older or partial upstream builds. It should remain per-route/per-feature, not all-or-nothing.
| Endpoint | Purpose | Provided by |
|----------|---------|-------------|
| `GET /api/sessions` (CRUD) | Session list/create/rename/delete/fork | Fork OR bootstrap OR upstream-merged |
| `GET /api/sessions/{id}/messages` | Conversation history | Fork OR bootstrap OR upstream-merged |
| `GET /api/sessions/search` | Full-text message search | Fork OR bootstrap OR upstream-merged |
| `POST /api/sessions/{id}/chat/stream` | Session-based SSE chat | Fork OR upstream-merged ONLY (NOT bootstrap) |
| `GET /api/config`, `PATCH /api/config` | Personalities + model config | Fork OR bootstrap OR upstream-merged |
| `GET /api/skills`, `/{name}` | Skill discovery (list + detail) | Fork OR bootstrap OR upstream-merged |
| `GET /api/sessions` (CRUD) | Session list/create/rename/delete/fork | Native upstream OR fork OR bootstrap |
| `GET /api/sessions/{id}/messages` | Conversation history | Native upstream OR fork OR bootstrap |
| `GET /api/sessions/search` | Full-text message search | Fork OR bootstrap only |
| `POST /api/sessions/{id}/chat/stream` | Session-based SSE chat | Native upstream OR fork only (NOT bootstrap) |
| `GET /v1/skills` | Skill list metadata | Native upstream OR fork |
| `GET /api/config`, `PATCH /api/config` | Personalities + model config | Fork OR bootstrap only |
| `GET /api/skills`, `/{name}` | Legacy skill discovery/detail routes | Fork OR bootstrap only; Android prefers `/v1/skills` first |
| `PUT /api/skills/toggle` | Enable/disable installed skill | `hermes_cli/web_server.py` dashboard surface; mirrored into bootstrap |
| `GET/POST/PATCH/DELETE /api/memory` | Memory CRUD | Fork OR bootstrap OR upstream-merged |
| `GET /api/available-models` | Provider model list | Fork OR bootstrap OR upstream-merged |
| `GET/POST/PATCH/DELETE /api/memory` | Memory CRUD | Fork OR bootstrap only |
| `GET /api/available-models` | Provider-aware model list | Fork OR bootstrap only |
The Android client probes per-endpoint capability via `HermesApiClient.probeCapabilities()` (returns `ServerCapabilities`). When `streamingEndpoint = "auto"`, `ConnectionViewModel.resolveStreamingEndpoint()` picks `sessions` or `runs` based on the capability snapshot.
@@ -67,7 +68,7 @@ hermes-agent ships a second web server at `hermes_cli/web_server.py` that hosts
## Key Instructions
- **Always verify upstream before assuming an endpoint exists.** Check `gateway/platforms/api_server.py` in hermes-agent. If an endpoint isn't there, document whether bootstrap injects it or it requires the fork.
- If we use a non-standard endpoint, ensure `probeCapabilities()` covers it and the auto-resolver degrades gracefully.
- **Bootstrap maintenance:** Remove `hermes_relay_bootstrap/` in one PR once PR #8556 merges. It's no-op-compatible, so leaving it in place during rollout is harmless.
- **Bootstrap maintenance:** Do not remove `hermes_relay_bootstrap/` just because upstream has native sessions. It can start shrinking only after each Relay-consuming compatibility route has a native replacement or the Android/Desktop clients have migrated away from it.
## Repository Layout
@@ -107,7 +108,7 @@ hermes-android/
│ ├── tools/ # android_navigate.py, android_notifications.py
│ └── dashboard/ # hermes-agent dashboard plugin — manifest, React UI, FastAPI proxy
├── relay_server/ ← Thin compat shim → plugin.relay (legacy entrypoint)
├── hermes_relay_bootstrap/ ← Runtime patch for vanilla upstream; removable after PR #8556
├── hermes_relay_bootstrap/ ← Runtime patch for vanilla/partial upstream compatibility routes
├── skills/devops/hermes-relay-pair/ ← /hermes-relay-pair slash command
├── scripts/ ← dev.bat, bridge-smoke.sh, bump-version.sh
└── docs/ ← spec, decisions, security, relay-server, mcp-tooling
@@ -230,7 +231,7 @@ hermes-android/
| `plugin/pair.py` | QR payload builder + CLI; `build_payload(sign=True)`; `--register-code` fallback |
| `install.sh` | Canonical installer — 6 steps; idempotent; drops `hermes-relay-update` shim |
| `uninstall.sh` | Canonical uninstaller; reverses install.sh; never touches `.env` or `state.db` |
| `hermes_relay_bootstrap/` | Runtime patch for vanilla upstream; no-op on fork/upstream-merged; remove after PR #8556 |
| `hermes_relay_bootstrap/` | Runtime patch for vanilla/partial upstream compatibility routes; shrink per route group after native parity or client migration |
| **Plugin — Dashboard** | |
| `plugin/dashboard/manifest.json` | Declares tab, entry bundle, and FastAPI module for hermes-agent discovery |
| `plugin/dashboard/plugin_api.py` | FastAPI router proxying 5 routes to relay over loopback; `/pairing` body = API-server overrides (host/port/tls/api_key), relay URL auto-derived |
+10
View File
@@ -1,5 +1,15 @@
# Hermes-Relay — Dev Log
## 2026-06-05 — Refresh upstream baseline docs after native session merge
**Context.** Upstream Hermes Agent merged native API Server session controls as commit `f7527b0` via PR #33134, and Android now prefers native `/v1/skills` with legacy fallback. Several Relay docs still treated PR #8556/#29302 as the future removal trigger for `hermes_relay_bootstrap/`.
**What changed.** Updated contributor/user docs to separate native upstream baseline routes (`/api/sessions/*`, `/v1/skills`, `/v1/toolsets`) from Relay compatibility routes that still require `axiom` or bootstrap (`/api/sessions/search`, `/api/memory`, `/api/config`, legacy skill detail routes, `/api/available-models`, voice aliases). The bootstrap retirement rule is now per route group, not wholesale deletion after one upstream session merge.
**Verification.** Ran stale-reference searches for `#8556`, `#29302`, and legacy skills routes after edits; remaining references are historical devlog/plans or explicitly marked compatibility/fallback.
---
## 2026-06-05 — Prefer upstream `/v1/skills` with legacy fallback
**Context.** Upstream Hermes Agent now has baseline skill/session API surface area, while Axiom's fork still preserves richer Relay-specific `/api/*` compatibility routes. The Android client should begin consuming upstream-compatible skill listings when present without breaking older fork/bootstrap installs.
+2 -2
View File
@@ -101,7 +101,7 @@ Small follow-ons to v0.4 deliberately deferred to keep the v0.4.0 release surfac
**What the middleware can do (near-term, ships via install.sh).** New aiohttp middleware in `hermes_relay_bootstrap/_command_middleware.py`, installed at the same `_PatchedApplication.__setitem__` hook as the current route injection so it lands before `AppRunner.setup()` freezes the app. Filters by `request.path in ("/v1/runs", "/v1/chat/completions")` — zero-cost fast path for everything else. On chat paths: parses the body, lazy-imports `GATEWAY_KNOWN_COMMANDS` + `resolve_command()` + `gateway_help_lines()` from `hermes_cli.commands`, and splits on command type:
- **Stateless commands** (`/help`, `/commands`, and any others the upstream Option B PR ends up supporting without router state) — actually dispatch, emit a synthetic SSE stream matching the runs handler's existing event shape so the Android client at `HermesApiClient.kt:655-715` renders it as a normal assistant turn.
- **Stateful commands** (`/model`, `/new`, `/retry`, `/undo`, `/compress`, `/title`, `/resume`, `/branch`, `/rollback`, `/yolo`, `/reasoning`, `/personality`, etc. — most of the registry) — emit a synthetic SSE stream whose content is a short, helpful notice: *"The `/model` command requires a persistent session and isn't available on the stateless `/v1/runs` endpoint. Use `/api/sessions/{id}/chat/stream` (post-PR-#8556) or a channel with session state. For commands that work here, type `/help`."* This replaces the LLM hallucination with a deterministic, accurate message that points the user at the real fix.
- **Stateful commands** (`/model`, `/new`, `/retry`, `/undo`, `/compress`, `/title`, `/resume`, `/branch`, `/rollback`, `/yolo`, `/reasoning`, `/personality`, etc. — most of the registry) — emit a synthetic SSE stream whose content is a short, helpful notice: *"The `/model` command requires a persistent session and isn't available on the stateless `/v1/runs` endpoint. Use `/api/sessions/{id}/chat/stream` or a channel with session state. For commands that work here, type `/help`."* This replaces the LLM hallucination with a deterministic, accurate message that points the user at the real fix.
**On no match** (unknown command, cli-only command, or plain text): falls through to `handler(request)` unchanged. Fork-detects the same way the existing injection does — if the upstream preprocessor PR lands first, the middleware no-ops.
@@ -109,7 +109,7 @@ Small follow-ons to v0.4 deliberately deferred to keep the v0.4.0 release surfac
**Files.** New `hermes_relay_bootstrap/_command_middleware.py` (~150 LOC), one-line append in `_patch.py` inside `_maybe_register_routes`, stdlib `unittest` coverage in `plugin/tests/test_bootstrap_command_middleware.py` mirroring the existing `test_bootstrap_patch.py` harness. Mirrors the upstream Option B PR exactly so the two can be reviewed side-by-side.
**Phase 2 — stateful dispatch on the session chat stream endpoint (post PR #8556).** Once PR #8556 merges and `/api/sessions/{id}/chat/stream` ships natively in upstream, a separate middleware (or a follow-up upstream PR) can add a preprocessor **scoped to that endpoint only**, leveraging the `session_id` in the URL as the persistence handle. At that point stateful commands become a dict write against session-scoped state — `session.model_override = new_model` — without needing to refactor `GatewayRouter` or plumb api_server into the router. Much smaller than a full router refactor, and it matches upstream's partition: `/v1/*` stays stateless, statefulness lives on `/api/sessions/*`. Blocked on #8556 landing.
**Phase 2 — stateful dispatch on the session chat stream endpoint (unblocked by PR #33134 / commit `f7527b0`).** Since `/api/sessions/{id}/chat/stream` now ships natively in upstream, a separate middleware (or a follow-up upstream PR) can add a preprocessor **scoped to that endpoint only**, leveraging the `session_id` in the URL as the persistence handle. At that point stateful commands become a dict write against session-scoped state — `session.model_override = new_model` — without needing to refactor `GatewayRouter` or plumb api_server into the router. Much smaller than a full router refactor, and it matches upstream's partition: `/v1/*` stays stateless, statefulness lives on `/api/sessions/*`.
## Future — v0.5+
+3 -3
View File
@@ -78,10 +78,10 @@ Things to look into:
- **Tool registration discoverability** — `android_*` tools register at gateway import time. There's no canonical "list installed plugin tools" API. Would adding one to upstream make sense, or is `gateway tool list` already enough?
- **Versioning + compatibility ranges** — `pip install -e` doesn't enforce version pins between hermes-agent and our plugin. A breaking change in upstream's plugin loader could silently break us. Do we need a `hermes_compat: ">=0.8.0,<1.0.0"` field somewhere?
- **`hermes-relay-self-setup` SKILL.md as a precedent** — we just shipped a self-installing skill that an LLM can fetch from a raw GitHub URL and execute. Does this pattern generalize? Could it become a recommended way for any third-party Hermes project to ship setup automation?
- **Bootstrap injection** — `hermes_relay_bootstrap/` monkey-patches `aiohttp.web.Application` to inject endpoints into vanilla upstream. This is intentional but feels like a hack. Upstream PR #8556 (`feat/session-api`) will eventually let us delete it — verified 2026-04-15 that its scope covers the full bootstrap surface (sessions, memory, skills, config, available-models). Track that PR's status periodically.
- **Gateway slash-command preprocessor — upstream Stage 1 PR.** Sibling follow-up to #8556. Intercepts known gateway commands on `/v1/runs` + `/v1/chat/completions`, dispatches the stateless ones (`/help`, `/commands`) via `gateway_help_lines()`, returns a deterministic "use a channel with session state" notice for the stateful majority. Currently being prepared in `C:/Users/Bailey/Desktop/Open-Projects/hermes-agent-pr-prep/` on branch `feat/api-server-gateway-commands`; awaiting subagent's code + draft PR body before pushing. See `docs/upstream-contributions.md` §5.
- **Bootstrap injection shrink path** — `hermes_relay_bootstrap/` monkey-patches `aiohttp.web.Application` to inject endpoints into vanilla/partial upstream. Upstream commit `f7527b0` via PR #33134 now covers baseline sessions/chat/fork/message history, and `/v1/skills` covers list metadata. Do **not** delete the bootstrap wholesale yet: Relay still depends on compatibility routes that upstream lacks or does not match (`/api/sessions/search`, `/api/memory`, `/api/config`, legacy `/api/skills` detail routes, `/api/available-models`, and voice aliases). Shrink per route group only after native parity or client migration.
- **Gateway slash-command preprocessor — upstream Stage 1 PR.** Follow-up to the native session-control baseline from PR #33134 / commit `f7527b0`. Intercepts known gateway commands on `/v1/runs` + `/v1/chat/completions`, dispatches the stateless ones (`/help`, `/commands`) via `gateway_help_lines()`, returns a deterministic "use a channel with session state" notice for the stateful majority. Currently being prepared in `C:/Users/Bailey/Desktop/Open-Projects/hermes-agent-pr-prep/` on branch `feat/api-server-gateway-commands`; awaiting subagent's code + draft PR body before pushing. See `docs/upstream-contributions.md` §5.
- **Gateway slash-command preprocessor — bootstrap middleware (Stage 1 equivalent).** Sibling shim in `hermes_relay_bootstrap/_command_middleware.py` that mirrors the upstream Stage 1 PR as an aiohttp middleware injected at bootstrap time. Ships the hallucination fix to vanilla-upstream installs before the upstream PR lands. Planned for v0.4.1, after the current bridge feature branch wraps. See `ROADMAP.md` v0.4.1 entry.
- **Stage 2 — stateful slash-command dispatch on `/api/sessions/{id}/chat/stream`.** Blocked on PR #8556 merging. Once session primitives ship upstream, add a preprocessor scoped to the session chat stream endpoint only, using `session_id` as the persistence handle. Separate upstream PR + matching bootstrap middleware. See `docs/upstream-contributions.md` §5 ("Stage 2").
- **Stage 2 — stateful slash-command dispatch on `/api/sessions/{id}/chat/stream`.** Unblocked by upstream PR #33134 / commit `f7527b0`. Add a preprocessor scoped to the session chat stream endpoint only, using `session_id` as the persistence handle. Separate upstream PR + matching bootstrap middleware. See `docs/upstream-contributions.md` §5 ("Stage 2").
When the answer becomes clearer, this section becomes either an ADR in `docs/decisions.md` or a Plan under `Plans/`.
+5 -3
View File
@@ -259,8 +259,9 @@ GET /api/memory?target=memory // or target=user
### Skills
```
GET /api/skills # optional ?category= filter
GET /api/skills/{name}
GET /v1/skills # native upstream list metadata
GET /api/skills # legacy fallback, optional ?category= filter
GET /api/skills/{name} # legacy detail route when provided by fork/bootstrap
```
> `/api/skills/categories` was removed from upstream as dead code (commit 8d023e43) and is not re-injected by the bootstrap.
@@ -272,7 +273,8 @@ Probe endpoints to detect what's available:
GET /health -> basic connectivity
GET /api/sessions -> enhanced Hermes session API
GET /v1/models -> model listing (OpenAI-compatible)
GET /api/skills -> skills support
GET /v1/skills -> native skills support
GET /api/skills -> legacy skills fallback
GET /api/memory -> memory support
GET /api/config -> config API
+34 -25
View File
@@ -80,15 +80,19 @@ The app supports two streaming endpoints, selectable in Settings:
| **Sessions** (`/api/sessions/{id}/chat/stream`) | Inline text annotations (`` `💻 terminal` ``) — client parses from markdown | Hermes-native SSE (assistant.delta, tool.progress, etc.) or OpenAI-format (delta.content) |
| **Runs** (`/v1/runs` + `/v1/runs/{run_id}/events`) | **Structured events** (tool.started, tool.completed) — real-time tool cards | Hermes lifecycle events (message.delta, tool.started, tool.completed, run.completed) |
**Important upstream note:** The `/api/sessions` CRUD endpoints are moving
toward upstream Hermes core through focused PR
[#29302](https://github.com/NousResearch/hermes-agent/pull/29302), which covers
**Important upstream note:** The `/api/sessions` CRUD/chat endpoints landed in
upstream Hermes core via PR
[#33134](https://github.com/NousResearch/hermes-agent/pull/33134) / commit
[`f7527b0`](https://github.com/NousResearch/hermes-agent/commit/f7527b0fdb54f01691547df03fc65a6d367f9fde),
which salvaged the focused PR
[#29302](https://github.com/NousResearch/hermes-agent/pull/29302). It covers
session list/create/read/update/delete, messages, fork, chat, and chat stream.
Until that reaches a released core build, `hermes_relay_bootstrap/` still ships
with the plugin and runs at Python interpreter startup via `.pth`. The bootstrap
now composes with partial upstream support: native routes win per method/path,
and the relay only injects missing compatibility gaps such as config, skills, or
memory when core does not provide them.
`hermes_relay_bootstrap/` still ships with the plugin and runs at Python
interpreter startup via `.pth` for older cores and for compatibility gaps that
upstream still does not provide. The bootstrap composes with partial upstream
support: native routes win per method/path, and the relay only injects missing
compatibility gaps such as search, config, legacy skills detail routes,
available-models, or memory when core does not provide them.
The app's `probeCapabilities()` returns a per-endpoint snapshot, and `ConnectionViewModel.resolveStreamingEndpoint()` collapses `streamingEndpoint = "auto"` (the default for new installs) to a concrete `"sessions"` or `"runs"` choice based on what the server actually exposes.
@@ -499,11 +503,14 @@ Adopting from ARC's workflow patterns:
### 16. Runtime API Server Patch via .pth Bootstrap (2026-04-12)
**Context:** The Android app depends on API-server routes for session history,
profile/config metadata, skills, and memory-backed UI. Upstream core is now
moving in focused pieces rather than one large frontend API patch: PR
[#29302](https://github.com/NousResearch/hermes-agent/pull/29302) covers the
canonical `/api/sessions/*` surface, while config/skills/memory still remain
compatibility routes in this repo until core exposes stable equivalents. Without
profile/config metadata, skills, and memory-backed UI. Upstream core has started
absorbing the surface in focused pieces rather than one large frontend API patch:
PR [#33134](https://github.com/NousResearch/hermes-agent/pull/33134) / commit
[`f7527b0`](https://github.com/NousResearch/hermes-agent/commit/f7527b0fdb54f01691547df03fc65a6d367f9fde)
now covers the canonical `/api/sessions/*` surface, and `/v1/skills` covers
skill list metadata. Config, legacy skill detail routes, memory, search, and
provider-aware model metadata still remain compatibility routes in this repo
until core exposes stable equivalents or clients migrate away from them. Without
the bootstrap, users on older vanilla upstream builds lose session browsing,
metadata-backed settings, and history-on-restart behavior.
@@ -520,16 +527,17 @@ We considered four options:
1. **Zero modifications to hermes-agent's filesystem.** `git pull` / `hermes update` see no local changes, so they always work cleanly. The patch lives entirely in `hermes_relay_bootstrap/` inside our own repo.
2. **Single-file containment of all ported logic.** `_handlers.py` is 500 lines of straight-line aiohttp handler code with explicit `adapter` parameters (closures, not bound methods). Easy to audit, easy to delete.
3. **Feature detection by method/path, not broad route family.** Native upstream
routes win one method/path at a time. This matters because PR #29302 can land
`/api/sessions/*` before core has stable config/skills/memory APIs; the
bootstrap must not skip those remaining compatibility routes just because a
sessions route exists.
routes win one method/path at a time. This matters because PR #33134 landed
`/api/sessions/*` before core had stable config/legacy skill detail/memory/search
APIs; the bootstrap must not skip those remaining compatibility routes just
because a sessions or `/v1/skills` route exists.
4. **Trust model already established.** The user installed our plugin into their hermes-agent venv. They've already consented to having the plugin import hermes-agent internals (it does this for relay tools, voice endpoints, media registry). Monkey-patching `aiohttp.web.Application` is in the same trust bucket.
5. **Surface-by-surface removal.** When PR #29302 or equivalent reaches a
released hermes-agent version, the sessions compatibility routes should go
quiet automatically. Config, skills, memory, and command preprocessing remain
until their native replacements exist. Full bootstrap deletion happens only
after every compatibility route group has a stable core equivalent.
5. **Surface-by-surface removal.** When a supported hermes-agent baseline includes
PR #33134 / `f7527b0`, the sessions compatibility routes should go quiet
automatically. Config, legacy skills detail routes, memory, search,
available-models, and command preprocessing remain until their native
replacements exist or the clients migrate. Full bootstrap deletion happens
only after every compatibility route group has a stable core equivalent.
6. **`/v1/runs` is genuinely better for chat than `/api/sessions/{id}/chat/stream`.** It's standard upstream, supports `X-Hermes-Session-Id` for continuation, and emits live structured tool events. The fork's chat handler exists because upstream didn't HAVE this clean structured-event runs API at the time the fork was cut — but upstream does now.
**The Android client adapts via `streamingEndpoint = "auto"`.** New `ServerCapabilities` data class returned by `HermesApiClient.probeCapabilities()` captures per-endpoint presence (`sessionsApi`, `sessionsChatStream`, `runs`, `portable`, `healthy`). `ConnectionViewModel.resolveStreamingEndpoint()` collapses `"auto"` to `"sessions"` (when chat-stream handler is present, i.e. fork or upstream-merged) or `"runs"` (otherwise, i.e. bootstrap-injected vanilla upstream). The setting still supports manual `"sessions"` / `"runs"` overrides for debugging.
@@ -548,10 +556,11 @@ We considered four options:
- `install.sh` step 2 — copies the `.pth` into the venv site-packages
**Removal path** is now per surface:
1. Sessions: after PR #29302 or equivalent ships in released core, keep the
1. Sessions: with PR #33134 / `f7527b0` in the supported core baseline, keep the
bootstrap installed but verify it skips native `/api/sessions/*` routes.
2. Config/skills/memory: remove those compatibility handlers only after stable
core APIs exist and Android probes prefer them.
2. Config/legacy skills/memory/search/available-models: remove those
compatibility handlers only after stable core APIs exist and Android probes
prefer them, or after clients migrate away from the legacy route group.
3. Slash middleware: remove after native API-server slash preprocessing exists.
4. Full cleanup: delete `hermes_relay_bootstrap/`, delete
`hermes_relay_bootstrap.pth`, remove the `.pth` install block, and update
+4 -2
View File
@@ -293,8 +293,10 @@ which probes `hermes gateway run --help | grep tailscale`. When
that returns true (PR #9295 has landed in your hermes-agent install),
the helper still works but the canonical path
(`hermes gateway run --tailscale`) is preferred and the helper will
be removed in a future release. Same retirement pattern as
`hermes_relay_bootstrap/` after PR #8556.
be removed in a future release. Treat helper retirement like
`hermes_relay_bootstrap/`: remove it only after the native path has parity
for every Relay-consuming route/group, not merely after one upstream
baseline lands.
### Forward-auth gateways (Authelia, Cloudflare Access) in front of the API server
+3 -3
View File
@@ -361,7 +361,7 @@ Bottom navigation bar with 4 tabs:
3. Remaining top-bar actions (session drawer hamburger, ambient toggle, etc.).
- **Session drawer** (swipe from left or hamburger icon) — session list with title, timestamp, message count. Create, switch, rename, delete.
- **Chat view** — message bubbles with markdown rendering, streaming text, tool call cards (Off/Compact/Detailed display modes)
- **Input bar** — text field with 4096 char limit, `/` palette button, send button, stop button during streaming. Inline autocomplete on `/` keystroke + full searchable command palette (bottom sheet). Commands sourced from: 29 gateway built-ins, dynamic personalities from `config.agent.personalities`, and server skills from `GET /api/skills`.
- **Input bar** — text field with 4096 char limit, `/` palette button, send button, stop button during streaming. Inline autocomplete on `/` keystroke + full searchable command palette (bottom sheet). Commands sourced from: 29 gateway built-ins, dynamic personalities from `config.agent.personalities`, and server skills from `GET /v1/skills` with legacy `/api/skills` fallback.
- **Empty state** — Logo + "Start a conversation" + suggestion chips that populate input
- **Agent sheet — Profile section (v0.6.0, updated 2026-05-18)** — upstream Hermes profiles auto-discovered by the relay at `~/.hermes/profiles/*/`. Selecting one routes chat/session calls to that profile's advertised `api_server_url` when present, giving proper Hermes isolation for sessions, memory, tools, provider auth, and SOUL/default model. If no profile API route is advertised, the app falls back to overlaying `model` + `SOUL.md` (as `system_message`) on the active Connection's API server. Selection is persisted per Connection/profile context. Hidden when the server advertises no profiles. See `docs/decisions.md` §21.
- **Agent sheet — Personality section** — personalities fetched from `GET /api/config` (`config.agent.personalities`). Shows server default (from `config.display.personality`) + all configured. Active personality name shown on assistant chat bubbles.
@@ -499,7 +499,7 @@ Additional API endpoints used:
5. DELETE /api/sessions/{session_id} → delete session
6. GET /api/sessions/{session_id}/messages → fetch message history
7. GET /api/config → personalities (for personality picker, `config.agent.personalities`)
8. GET /api/skills → available skills (for command palette + autocomplete)
8. GET /v1/skills → available skills (for command palette + autocomplete; fallback GET /api/skills)
```
Key classes:
@@ -888,7 +888,7 @@ Current versions as of v0.3.0. Source of truth is `gradle/libs.versions.toml`
| **WebAPI chat** | HTTP to `localhost:8642/api/sessions/*/chat/stream` (SSE) |
| **WebAPI sessions** | `GET/POST/PATCH/DELETE /api/sessions` for CRUD |
| **Personalities** | `GET /api/config` → `config.agent.personalities` for picker + command palette |
| **Server skills** | `GET /api/skills` — dynamic skill discovery for command palette + autocomplete |
| **Server skills** | `GET /v1/skills` first, fallback `GET /api/skills` — dynamic skill discovery for command palette + autocomplete |
| **Plugin system** | `register_tool()` via `ctx` for `android_*` tools |
| **Gateway** | Chat channel goes through WebAPI, not directly to gateway |
| **Memory/Skills** | Accessible through agent chat (no direct API needed for MVP) |
+8 -6
View File
@@ -2,9 +2,11 @@
Improvements that would benefit hermes-relay (and other frontends) if added to [NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent).
## Current Upstream PR Alignment
## Current Upstream Alignment
- PR #29302 (`feat: add API server session controls`) is the canonical upstream path for `/api/sessions/*`, message history, fork, chat, and chat stream. Hermes-Relay should prefer these native routes when present and keep the bootstrap as a per-route compatibility overlay only for older or partial core builds.
- PR #33134 (`feat(api-server): session control API — sessions/chat/fork/SSE-stream (salvages #29302)`) landed upstream as commit [`f7527b0`](https://github.com/NousResearch/hermes-agent/commit/f7527b0fdb54f01691547df03fc65a6d367f9fde). It is now the canonical baseline for `/api/sessions/*`, message history, fork, chat, and chat stream.
- `GET /v1/skills` and `GET /v1/toolsets` are native upstream capability-discovery routes. Hermes-Relay's Android client prefers `/v1/skills` first and falls back to legacy `/api/skills` for older/fork/bootstrap installs.
- Keep `hermes_relay_bootstrap` as a per-route compatibility overlay only for older or partial core builds. Do not remove it wholesale while Relay still needs routes without native upstream parity: `/api/sessions/search`, `/api/memory`, `/api/config`, legacy skill detail routes under `/api/skills`, `/api/available-models`, and voice aliases.
- PR #8199 (`feat(api): add native audio transcription and speech endpoints`) is the canonical upstream path for core STT/TTS execution through `/v1/audio/transcriptions` and `/v1/audio/speech`. Hermes-Relay should keep `/voice/*` as the paired-device facade but eventually call those native core endpoints internally before falling back to private helper imports.
- PR #29364 (`feat: add API server audio endpoints`) should not become a competing `/api/audio/*` API if #8199 remains the accepted audio base. Rework it as a discovery/compatibility follow-up or close it after confirming the upstream maintainer preference.
@@ -38,7 +40,7 @@ Improvements that would benefit hermes-relay (and other frontends) if added to [
**Impact:** All frontends (hermes-relay, hermes-workspace, ClawPort) could dynamically show available commands without hardcoding. New commands added upstream would appear automatically.
**Workaround (current):** 29 gateway commands hardcoded in `ChatScreen.kt`, manually synced with `hermes_cli/commands.py`. Personality commands generated from `GET /api/config`. Skills from `GET /api/skills`.
**Workaround (current):** 29 gateway commands hardcoded in `ChatScreen.kt`, manually synced with `hermes_cli/commands.py`. Personality commands generated from `GET /api/config`. Skills prefer native `GET /v1/skills` and fall back to legacy `GET /api/skills`.
## 2. Personality Switching via Dedicated API Parameter
@@ -103,16 +105,16 @@ except Exception as _exc:
**Proposed — a two-stage arc, each stage a small, independently reviewable PR:**
**Stage 1 — stateless preprocessor (sibling follow-up to PR #29302).** A lightweight preprocessor in `api_server.py`'s `/v1/runs` + `/v1/chat/completions` handlers that detects a leading `/` in the user text, matches the first token against `GATEWAY_KNOWN_COMMANDS`, and splits on command type:
**Stage 1 — stateless preprocessor (follow-up to PR #33134 / commit `f7527b0`).** A lightweight preprocessor in `api_server.py`'s `/v1/runs` + `/v1/chat/completions` handlers that detects a leading `/` in the user text, matches the first token against `GATEWAY_KNOWN_COMMANDS`, and splits on command type:
- **Stateless commands** (`/help`, `/commands`, and any others that can execute without touching router-owned state) are dispatched via existing helpers (`gateway_help_lines()` at `hermes_cli/commands.py:340`) and returned as a synthetic SSE stream matching the handlers' existing event shape.
- **Stateful commands** (`/model`, `/new`, `/retry`, `/undo`, `/compress`, `/title`, `/resume`, `/branch`, `/rollback`, `/yolo`, `/reasoning`, `/personality`, and most of the registry) return a deterministic, helpful SSE notice along the lines of *"The `/model` command requires a persistent session and isn't available on the stateless `/v1/runs` endpoint. Use `/api/sessions/{id}/chat/stream` (from PR #29302) or a channel with session state (Discord, CLI, Telegram)."*
- **Stateful commands** (`/model`, `/new`, `/retry`, `/undo`, `/compress`, `/title`, `/resume`, `/branch`, `/rollback`, `/yolo`, `/reasoning`, `/personality`, and most of the registry) return a deterministic, helpful SSE notice along the lines of *"The `/model` command requires a persistent session and isn't available on the stateless `/v1/runs` endpoint. Use `/api/sessions/{id}/chat/stream` or a channel with session state (Discord, CLI, Telegram)."*
- **Unknown** and **cli-only** commands fall through to the LLM path unchanged.
- **Preprocessor exceptions** fall through to the LLM path unchanged — a preprocessor bug must never take down a normal chat request.
This respects upstream's intentional design (api_server stays stateless, no router coupling) while fixing the hallucination symptom and unlocking the commands that *can* run statelessly.
**Stage 2 — stateful dispatch on `/api/sessions/{id}/chat/stream` (after PR #29302 lands).** Once session management primitives ship, a separate PR adds a preprocessor **scoped to the session chat stream endpoint only**, using the URL's `session_id` as the persistence handle. Stateful commands become session-scoped dict writes (`session.model_override = new_model`) without refactoring `GatewayRouter` or plumbing api_server into the router. This matches upstream's partition cleanly: `/v1/*` remains stateless and OpenAI-compatible; statefulness lives on `/api/sessions/*`.
**Stage 2 — stateful dispatch on `/api/sessions/{id}/chat/stream` (now unblocked).** Now that session management primitives have landed upstream, a separate PR can add a preprocessor **scoped to the session chat stream endpoint only**, using the URL's `session_id` as the persistence handle. Stateful commands become session-scoped dict writes (`session.model_override = new_model`) without refactoring `GatewayRouter` or plumbing api_server into the router. This matches upstream's partition cleanly: `/v1/*` remains stateless and OpenAI-compatible; statefulness lives on `/api/sessions/*`.
**Why not one big PR:** a full GatewayRouter refactor plus api_server plumbing was considered and rejected. It would touch 10+ files across subsystems normally owned separately, fight the documented "api_server is excluded from router notification" design decision, and review as a much larger change than the value added. The two-stage arc ships faster, reviews cleaner, and matches the upstream partition better.
+5 -5
View File
@@ -1,6 +1,6 @@
# Upstream Hermes Integration Sync
Last reviewed: 2026-05-20
Last reviewed: 2026-06-05
This document tracks how Hermes-Relay integrates with Hermes upstream surfaces, which
parts use supported extension points, and which parts are compatibility layers that
@@ -37,8 +37,8 @@ relay, dashboard, Android app, desktop app, bootstrap package, or user docs.
| Agent tools | Tool Gateway tools registered through plugin context | `ctx.register_tool(...)` in `plugin/__init__.py`; schemas and handlers in `plugin/tools/*` | Aligned with custom transports | Tool registration should stay in `register(ctx)`; transport details stay behind handlers. |
| Dashboard tab and plugin API | Dashboard plugin manifest plus plugin API routes under the Hermes dashboard plugin mount | `plugin/dashboard/manifest.json`, `plugin/dashboard/plugin_api.py` | Aligned wrapper | Dashboard routes may proxy relay state, but discovery and mounting should stay upstream-native. |
| Chat and model API | OpenAI-compatible API server routes such as `/v1/chat/completions`, `/v1/models`, `/health`, and supported streaming routes | Android `HermesApiClient`, relay docs, Web API docs | Mixed | Prefer standard API routes first; use `/api/sessions` only when capability probes find it. |
| Sessions API | Proposed upstream API-server session controls in NousResearch/hermes-agent PR #29302 (`/api/sessions`, messages, fork, chat, chat stream) | Android `HermesApiClient`; compatibility overlay in `hermes_relay_bootstrap/*` | Upstream-pending with fallback | Prefer native `/api/sessions/*` when present. Bootstrap must skip native routes per method/path and only inject missing compatibility routes. |
| Config, skills, memory APIs | Not documented as stable upstream API-server routes in current public docs | `hermes_relay_bootstrap/*`, `docs/HERMES-WEBAPI-REFERENCE.md` | Compatibility layer | Keep separate from the sessions retirement path. Do not skip these just because native `/api/sessions` exists. |
| Sessions API | Native upstream API-server session controls from PR #33134 / commit `f7527b0` (`/api/sessions`, messages, fork, chat, chat stream) | Android `HermesApiClient`; compatibility overlay in `hermes_relay_bootstrap/*` for older cores | Upstream baseline with fallback | Prefer native `/api/sessions/*` when present. Bootstrap must skip native routes per method/path and only inject missing compatibility routes. |
| Config, skills, memory APIs | Native upstream provides `/v1/skills` and `/v1/toolsets`; config, memory, legacy skill detail routes, search, and available-models are not stable upstream API-server routes | `hermes_relay_bootstrap/*`, `docs/HERMES-WEBAPI-REFERENCE.md` | Mixed: native skill list + compatibility layer | Keep separate from the sessions retirement path. Do not skip these just because native `/api/sessions` or `/v1/skills` exists. |
| Mobile, desktop, and terminal relay transport | No general upstream plugin WSS transport for persistent remote clients in current public docs | `plugin/relay/server.py`, `plugin/relay/channels/*` | Custom | Keep the relay protocol documented and avoid leaking relay-only assumptions into upstream API clients. |
| Pairing QR and relay session minting | No upstream pairing or device-registration method for remote mobile clients in current public docs | `plugin/pair.py`, relay `/pairing/*`, Android QR parser | Custom | QR payloads should keep API credentials (`key`) separate from relay credentials (`relay.code`). |
| Basic STT/TTS over HTTP | Proposed upstream API-server audio endpoints in PR #8199 (`/v1/audio/transcriptions`, `/v1/audio/speech`) | Relay `/voice/config`, `/voice/transcribe`, `/voice/synthesize`; Android `RelayVoiceClient`; `plugin/relay/upstream_voice.py` | Custom wrapper pending upstream replacement | Keep `/voice/*` as the relay auth/session compatibility facade. Once core audio endpoints land, prefer proxying to native `/v1/audio/*` for STT/TTS work before falling back to private helper imports. |
@@ -50,7 +50,7 @@ relay, dashboard, Android app, desktop app, bootstrap package, or user docs.
| Deviation | Owner files | Why it exists | Guard or fallback | Retirement condition |
| --- | --- | --- | --- | --- |
| API bootstrap route and middleware injection | `hermes_relay_bootstrap/*` | Native installs need session/config/skills/memory endpoints and slash-command preprocessing before upstream exposes stable equivalents. | Method/path feature detection skips native upstream routes and injects only missing compatibility gaps; upstream-module checks skip middleware when native slash preprocessing exists. | Retire per surface: sessions after PR #29302 or equivalent ships in a released core; config/skills/memory after stable core APIs exist; slash middleware after native preprocessing exists. |
| API bootstrap route and middleware injection | `hermes_relay_bootstrap/*` | Native/older installs need compatibility routes and slash-command preprocessing before upstream exposes stable equivalents. | Method/path feature detection skips native upstream routes and injects only missing compatibility gaps; upstream-module checks skip middleware when native slash preprocessing exists. | Retire per surface: sessions after the supported baseline includes PR #33134/`f7527b0` and tests prove no Relay session route gaps remain; config/legacy skills/memory/search/available-models after stable core APIs or client migrations exist; slash middleware after native preprocessing exists. |
| Plugin CLI shim fallback | `plugin/__init__.py`, `plugin/cli.py`, install scripts | Some Hermes versions do not wire third-party plugin CLI commands into the top-level parser. | `ctx.register_cli_command` is attempted first; standalone shims fill the gap. | Remove shims once upstream plugin CLI discovery is stable for native installs. |
| Relay HTTP and WSS server | `plugin/relay/server.py`, `plugin/relay/channels/*` | Mobile, desktop, terminal, media, push, and bridge features need persistent client channels and relay-owned session state. | Keep upstream API calls separate from relay session calls and document the protocol in `docs/relay-protocol.md`. | Replace pieces only when upstream provides equivalent remote-client transport or platform adapters. |
| Pairing schema with `relay.code` | `plugin/pair.py`, Android pairing parser, relay `/pairing/*` | An API bearer key authenticates Hermes API calls but does not create relay sessions or describe WSS endpoints. | QR payloads carry direct API credentials and relay credentials as separate families. | Remove custom pairing when upstream offers native remote-device registration and relay discovery. |
@@ -118,7 +118,7 @@ upgrading the supported Hermes baseline.
- `GET /api/sessions?limit=1` only as an enhanced-management capability probe
- Relay health and info endpoints from `docs/relay-server.md`
- Dashboard plugin overview under the Hermes plugin API mount
- `GET /v1/capabilities` and the native `/api/sessions/*` route set when testing a core build with PR #29302 or equivalent
- `GET /v1/capabilities`, `GET /v1/skills`, and the native `/api/sessions/*` route set when testing a core build with PR #33134 / commit `f7527b0` or equivalent
- `POST /v1/audio/transcriptions` and `POST /v1/audio/speech` when testing a core build with PR #8199 or equivalent
- Voice config, transcription, synthesis, and realtime routes only with relay session auth or a valid Hermes API bearer
6. Update this file when upstream adds a supported replacement for a custom layer.
+1 -1
View File
@@ -12,7 +12,7 @@ The plugin is a thin observer — it never modifies state, never writes to your
**On your server:**
- hermes-agent with the Dashboard Plugin System (upstream commit `01214a7f` on `axiom`, or any later `main` once [PR #8556](https://github.com/NousResearch/hermes-agent/pull/8556) and its dashboard followups merge). `hermes dashboard start` must already work for you.
- hermes-agent with the Dashboard Plugin System (upstream commit `01214a7f` on `axiom`, or any later `main` that includes the dashboard plugin follow-ups). `hermes dashboard start` must already work for you.
- The canonical Hermes-Relay install — if you ran the one-liner on the [Quick Start](/guide/getting-started), you're done. The installer symlinks `~/.hermes/plugins/hermes-relay` → the plugin subtree and the dashboard scanner picks up `plugin/dashboard/manifest.json` automatically.
- A gateway restart after install: `systemctl --user restart hermes-gateway`.
+1 -1
View File
@@ -55,7 +55,7 @@ All commands are fetched dynamically from the server where possible:
- **Configuration**: `/model`, `/personality`, `/reasoning`, `/yolo`, `/verbose`, `/voice`
- **Info**: `/help`, `/status`, `/usage`, `/insights`, `/commands`
- **Personalities**: generated from server config (`config.agent.personalities`) — `/personality victor`, `/personality creative`, etc.
- **Skills**: dynamically fetched from `GET /api/skills` — 90+ server skills grouped by category (creative, devops, research, etc.)
- **Skills**: dynamically fetched from `GET /v1/skills` with fallback to legacy `GET /api/skills` — server skills grouped by category (creative, devops, research, etc.)
## Tool Execution
+2 -2
View File
@@ -18,9 +18,9 @@ Hermes-Relay voice endpoints can reuse this same Bearer token. Relay validates i
## How endpoints get served
Installing the plugin via `install.sh` is enough to make all of the endpoints below work — including the management ones (`/api/sessions/*`, `/api/memory`, `/api/skills`, `/api/config`, `/api/available-models`). The plugin wires the gateway up at install time so these are served on the same `:8642` host as the standard `/v1/*` endpoints, with the same `Authorization: Bearer …` auth.
Current upstream hermes-agent includes the baseline session API (`/api/sessions/*`, chat, stream, fork, messages) and native capability discovery (`/v1/skills`, `/v1/toolsets`). Installing the Relay plugin via `install.sh` keeps older or partial core builds compatible by injecting only the missing management endpoints Relay still consumes, such as `/api/sessions/search`, `/api/memory`, `/api/config`, legacy `/api/skills` detail routes, `/api/available-models`, and voice aliases. These are served on the same `:8642` host as the standard `/v1/*` endpoints, with the same optional `Authorization: Bearer ***` auth.
**Chat streaming uses standard `/v1/runs`** by default — it emits structured `tool.started`/`tool.completed` SSE events for live tool progress cards in the Android app. The app's `Settings → Chat → Streaming endpoint = "Auto"` (default) probes per-endpoint capability and picks the best chat path automatically; you can manually force `Sessions` or `Runs` mode for debugging.
**Chat streaming uses standard `/v1/runs`** by default — it emits structured `tool.started`/`tool.completed` SSE events for live tool progress cards in the Android app. The app's `Settings → Chat → Streaming endpoint = "Auto"` (default) probes per-endpoint capability and picks the best chat path automatically; you can manually force `Sessions` or `Runs` mode for debugging. Skill discovery prefers `GET /v1/skills` and falls back to legacy `GET /api/skills`.
## Endpoints