ARIA 7f936fa596 HTTP fallback leg (docs/19, app): State.HttpFallback + SSE/long-poll receive
When the WS is down but the gateway is reachable over HTTP, the app
stays sendable instead of waiting out the WS redial backoff:

- HttpGateway.kt: OkHttp client for the HTTP leg — GET /v1/health
  (2 s probe), POST /v1/frame (accept-and-ack; 4xx error frames parsed),
  SSE GET /v1/events (hand-rolled line parser: event/id/data, comments,
  multi-line data, Last-Event-ID bookkeeping), long-poll GET /v1/poll.
  Per-purpose call timeouts (SSE heartbeat 15 s / poll hold 25 s exceed
  OkHttp's 10 s default read timeout). deriveHttpUrl: ws(s)://host:port
  -> http(s)://host:8791 (pure, unit-tested).
- GatewayClient.kt: new State.HttpFallback (sibling of Connected, both
  implement State.HelloInfo). connectLoop races the WS dial against the
  health probe (sendable in < 1 s on a dead WS port); on WS loss it
  enters fallback immediately (no backoff gate on the send path); on WS
  reconnect it stops the HTTP leg (media available again). sendMessage/
  sendFrame route to POST in fallback (mediaRefs dropped — media is
  WS-only in v1); SSE frames feed the same _events flow, so request-id
  correlation is unchanged. The SSE hello is treated like hello.ack
  (caps/channels/lastPushedCursor + onHelloAck fast path). After two
  consecutive SSE open failures the receive loop switches to long-poll
  until the next full (re)connect.
- IrisController.kt: onHelloAck/onConnectedLane take State.HelloInfo;
  state collector + deep-link/commands-catalog gates accept fallback.
- ChatScreen.kt: send gate accepts fallback; attach button disabled in
  fallback (media needs the live connection); status pill shows
  'connected · http' (green).
- Tests: HttpGatewayTest (URL derivation + SSE parser, 8 tests); full
  :shared desktop + android-host suites green.
2026-08-22 14:31:46 +02:00
2026-08-22 11:07:24 +02:00

Iris × Hermes

A chat app for hermes-agent: a native Android app and a desktop app (Linux, macOS, Windows) — both built from one shared Kotlin codebase (Compose Multiplatform).

Iris pairs with your running hermes gateway over a private WebSocket and gives you a Telegram-quality chat experience with your personal agent: streaming replies, visible reasoning, structured tool activity, channels, threads, media, search, and push notifications.

Features

  • Native Hermes-Gateway integration — your hermes → gateway → Iris app
  • Absolute Privacy! — everything stays on your own infrastructure
  • No file limit
  • No character limit
  • Full markdown support — tables, checkmarks, bold, inline code, code blocks + syntax highlighting…
  • HTML Artifact Preview — agent-sent HTML/CSS/JS rendered in an in-app WebView
  • All settings live in the app, not in hermes config.yml! Change everything on the fly.
  • Channels — create channels to keep track of your reports, cronjobs, webhook calls
  • Pin channels, rename them, change the color/icon
  • Customize how your chat should look like — color? Check! Images? Check!
  • Reasoning collapse/expandable in the chat bubble
  • Change the default behavior of tool & reasoning verbosity
  • Threads — create your own, or let the AI create them with a title
  • Search messages from everywhere

How it works

hermes-agent ──> hermes gateway ──(WebSocket :8790)──> Iris app (Android / Desktop)
  • gateway-plugin/ is a hermes platform plugin (android). It runs inside the hermes gateway process and opens a WebSocket server the apps connect to. Zero new Python dependencies, zero hermes-core changes.
  • app/ is one Compose Multiplatform Gradle project: :shared (KMP, most of the code), :androidApp (native Kotlin + Jetpack Compose client), :desktopApp (the same app, tweaked for a big screen).
  • The app is a first-class hermes messaging platform, so everything the gateway already does just works: slash commands, cron delivery, send_message routing, coexistence with Telegram/Discord/etc.
  • Push notifications: FCM (primary) or ntfy (fallback).

Build from source

Prerequisites

Where You need
Gateway host hermes-agent with its venv (uv sync)
Android build machine JDK 17, Android SDK (sdk.dir in app/local.properties or ANDROID_HOME), ADB with a connected device
Desktop build machine JDK 17 only

No system Gradle needed — both apps use the project wrapper (./gradlew).

1. Gateway (on the gateway host)

# hermes-agent is a separate project (not part of this repo)
cd hermes-agent && uv sync

# install the Iris plugin into the live hermes home
mkdir -p ~/.hermes/plugins
ln -s "$PWD/gateway-plugin" ~/.hermes/plugins/android
hermes gateway status        # should list "android"

hermes gateway setup         # generates ANDROID_TOKEN, prints the server URL
hermes gateway               # run the gateway

2. Android app

cd app
./gradlew :androidApp:installDebug    # build + install on the connected ADB device
# or just build the APK:
./gradlew :androidApp:assembleDebug   # → app/androidApp/build/outputs/apk/debug/

3. Desktop app

cd app
./gradlew :desktopApp:run             # dev run
./gradlew :desktopApp:jpackage        # native app-image (bundles the JRE)

4. Pair

On the app's Connect screen:

  1. Server URL — ws://<gateway-ip>:8790/ws (printed by hermes gateway setup).
  2. Pairing token — from the setup output, or ANDROID_TOKEN in ~/.hermes/.env on the gateway host.
  3. Test & Connect.

Notes:

  • The app has no QR scanner — pairing is manual URL + token entry.
  • The default bind is 127.0.0.1 (desktop on the same machine only). For a phone on the LAN, set ANDROID_WS_HOST to the gateway's LAN IP.
  • Remote access: Tailscale/WireGuard, or a reverse proxy with CA-signed WSS (ANDROID_WS_CERT / ANDROID_WS_KEY).

Full walkthrough, push setup (FCM/ntfy), and troubleshooting: docs/setup.md.

Contributing

Contributions are welcome! Before you start:

  1. Read the docs. The full reference library is in docs/ — start with docs/00-overview.md, then follow the numbered docs (architecture, wire protocol, plugin design, testing, …).

  2. Know the layout.

    Path What
    gateway-plugin/ Python hermes platform plugin (android); protocol.py is the frame source of truth
    app/shared KMP module with most of the client code (shared by Android + Desktop)
    app/androidApp Thin Android shell (package dev.iris.app)
    app/desktopApp Thin desktop shell
    docs/ Numbered reference library
  3. Run the tests.

    • Python (gateway plugin): cd hermes-agent && scripts/run_tests.sh tests/gateway/test_android.py (never bare pytest — hermes's runner sandboxes HERMES_HOME).
    • Kotlin: cd app && ./gradlew :shared:testDebugUnitTest
    • Live check (gateway must be running): gateway-plugin/tests/ws_probe.py and gateway-plugin/tests/e2e.py — see gateway-plugin/tests/README.md.
  4. Keep the protocol in sync. gateway-plugin/protocol.py, app/shared/.../protocol/Protocol.kt, and docs/protocol/frames.schema.json must always agree.

  5. Pre-commit hooks are configured (.pre-commit-config.yaml); run pre-commit install once after cloning.

Open an issue first for anything big, then send a pull request.

License

Apache License 2.0 — see LICENSE.

S
Description
Iris × Hermes — native Android + Desktop client for hermes-agent via a gateway platform plugin. Compose Multiplatform app (Kotlin) + Python WebSocket gateway adapter. See docs/ for the full reference library.
Readme Apache-2.0
7.8 MiB
0 Stars 1 Watchers 0 Forks
Latest
2026-08-31 21:44:41 +00:00
Languages
Kotlin 52.2%
Python 47.4%
Shell 0.4%