Skip to content

feat(clients): add a native Android client for Hermes Studio - #2307

Open
twuijri wants to merge 1 commit into
EKKOLearnAI:mainfrom
twuijri:android-client
Open

feat(clients): add a native Android client for Hermes Studio#2307
twuijri wants to merge 1 commit into
EKKOLearnAI:mainfrom
twuijri:android-client

Conversation

@twuijri

@twuijri twuijri commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Addresses the Android half of #2184. Opening it as code rather than a proposal so there is something concrete to accept, reshape, or turn down.

Everything here is additive. One new directory, clients/android/. No existing file is modified, the server is unchanged, and the Node workspace gains no dependency — the app builds on its own with gradle assembleDebug (JDK 17, compileSdk 34).

First run states the requirement A reply streaming in
First run is honest about what this is — the app is only a front end; the user installs Hermes Studio on a machine they own and comes back with the address. A reply arriving word by word over /chat-run, with the stop button, the reasoning folded underneath, and the tool the agent is running named while it works.
Settings Channels
Settings as a table of contents. Eleven desktop tabs do not fit a phone, so each group opens its own screen. Channels are set up from the phone — each one asks for exactly the fields platformEnvMap maps, and saving lets the server restart the gateway itself.

How it talks to this server

Nothing invented, nothing added server-side:

Purpose Endpoint
Sign in, verify a token POST /api/auth/login · GET /api/auth/me
Conversations and history GET /api/hermes/sessions · …/conversations/{id}/messages
Send a message, streaming Socket.IO /chat-runrun, abort, with the bearer token in the handshake
Send a message, fallback POST /api/chat-run/runs when the socket cannot connect
Group rooms GET/POST/DELETE /api/hermes/group-chat/rooms · Socket.IO /group-chat
Attachments, voice POST /upload · POST /api/hermes/stt/transcribe
Scheduled jobs GET/POST/PATCH/DELETE /api/hermes/jobs (+ pause, resume, run)
Profiles, models, config /api/hermes/profiles · /api/hermes/available-models · /api/hermes/config (+ /credentials)

Profile-scoped calls carry X-Hermes-Profile, the same as the web UI.

What it does today

  • Your conversations and their real history, per profile or across all of them
  • Streaming replies with a stop button; the reasoning is kept and folded under each reply
  • Attachments, camera, and voice — a recording goes through your own STT provider, or is sent as audio
  • Group rooms: create one, choose its agents, post into it, watch replies arrive
  • Scheduled jobs: create, edit, pause, resume, run now, read the output
  • Channel credentials for the ten platforms, gateway restart and auto-start with its profile policy
  • Settings screens mirroring the Studio tabs: account and IP locks, user management, provider keys, agent, memory, compression, session, privacy, proxy, display
  • Ten locales, right-to-left included, with a test that fails the build on a missing key

Bearer token in EncryptedSharedPreferences backed by the Keystore. Traffic goes only to the address the user enters. No analytics, no hosted service in between.

Reading order, if you review it

  1. clients/android/app/src/main/java/us/i3u/hermesstudio/HermesApi.kt — every HTTP call the app makes, in one file
  2. ChatSocket.kt and GroupSocket.kt — the two namespaces and the fallback
  3. clients/android/README.md — what works, and how to run it against tools/mock-studio.py without a server

If you would rather not take it in-tree

Entirely reasonable — a Kotlin app inside a Node monorepo is a real maintenance surface, and CI here does not build it. Two alternatives that cost you nothing:

  • keep it as a separate repo under this org, and I maintain it there, or
  • leave it community-run and link it from the README as the community Android client.

I will keep maintaining it either way; the point of this PR is to let you choose with the code in front of you rather than in the abstract. Licensing: I have left my own LICENSE file out, so anything merged carries this repository's terms.

An iOS client would map almost one-to-one from HermesApi.kt, and I am happy to help whoever wants to build it with the protocol side.

The web UI is built for a desktop screen, and EKKOLearnAI#2184 asks for a phone client
that is not a WebView wrapper. This adds one under clients/android: Kotlin
and Compose, talking to this server over the endpoints and Socket.IO
namespaces the web UI already uses.

Nothing outside clients/android is touched and the server is unchanged: the
app signs in with POST /api/auth/login, streams over /chat-run with the token
in the Socket.IO handshake, posts into rooms over /group-chat, and reads and
writes the same profile-scoped config the settings pages use — including the
X-Hermes-Profile header. When the socket cannot connect it falls back to
POST /api/chat-run/runs, so a reverse proxy that drops WebSocket upgrades
costs the streaming, not the answer.

Scope today: conversations with history, streaming replies with a stop
button and the reasoning folded under each one, attachments, camera and
voice through the profile's own STT, group rooms that can be created and
posted into, scheduled jobs, channel credentials, and the settings screens
that mirror Studio's tabs. Ten locales, right-to-left included.

The build is self-contained (gradle assembleDebug, JDK 17, compileSdk 34)
and adds no dependency to the Node workspace. tools/mock-studio.py answers
the endpoints the app calls, so every screen can be opened without a running
Hermes Studio.
@twuijri

twuijri commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@EKKOLearnAI — following up on this one. I'd rather have a decision than leave it sitting: do you want the Android client in this repo, or kept outside as a community client you link from the README?

Either answer works for me and I'll keep maintaining it. In-tree means it ships and versions with Studio, and the protocol stays in sync automatically — the cost is a Kotlin app inside a Node monorepo that your CI doesn't build. Out-of-tree costs you nothing and keeps this repo single-language; the risk is drift whenever the API changes.

If in-tree is the concern rather than the idea, I'm happy to reshape it however you prefer — separate directory, separate release workflow, or a thinner client. Since opening this I've also contributed #2310 and #2328, so the protocol side is something I'm already keeping current.

No rush on my side — just tell me which direction and I'll follow it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant