Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/pr-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Verify README release window
run: |
python3 -m unittest scripts.test_check_readme_release_window
python3 scripts/check_readme_release_window.py

- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Verify README release window
run: python3 scripts/check_readme_release_window.py

- name: Set up Java
uses: actions/setup-java@v4
with:
Expand Down
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Changelog

All notable changes to this project are documented here. This project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). Release notes for
versions prior to 1.0.7 are in the **What's new** sections of the [README](README.md).
All notable changes since v1.0.7 are documented here. This project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). The [README](README.md)
shows only the three latest releases.

## [2.1.22] - 2026-10-19

Expand Down
1 change: 1 addition & 0 deletions CODE_QUALITY_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This checklist must be completed before every release PR. Mark each item as pass
## Release
- [ ] Version bumped in `pubspec.yaml`
- [ ] `CHANGELOG.md` updated, or release notes drafted if the changelog is intentionally deferred
- [ ] README **What's new** shows the three latest stable changelog releases, with older notes retained in `CHANGELOG.md`
- [ ] CI/CD builds a clean APK, or the local build command and output path are recorded
- [ ] Release PR links to this checklist and includes any exceptions

Expand Down
237 changes: 8 additions & 229 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,231 +110,6 @@ sanitized implementation and validation record.
prompt never loads remote note images automatically (PR #143,
[@igitur](https://github.com/igitur)).

## What's new in v2.1.19

- **Pin chats on this device** — use a chat's action menu to keep it above the
dated Workspace Chats list or classic session list. Pins persist per saved
connection, survive app restarts and chat compression, and are included in
app backups. Pinning does not alter Hermes's server-side state or another
device's order; search and chat filters still apply (PR #140).

## What's new in v2.1.18

- **Composer controls stay above the keyboard** — multiline captions and
multiple attachment drafts now remain usable in portrait, landscape, narrow
screens, large text, and layouts containing gateway notices. Drafts and
status details scroll before the editor and Send button move off-screen.

## What's new in v2.1.17

- **Model selection on API-only connections** — choose and apply a provider and
model over port `8642` without enabling the Desktop Dashboard on port `9119`.
The choice stays attached to the same chat and new chats retain automatic
titles.

## What's new in v2.1.16

- **Correct Project labels in chat** — the sticky chat header now resolves its
authoritative server-side Project after opening, reconnecting, or binding a
new draft to its durable session, without delaying turn recovery.

## What's new in v2.1.15

- **Desktop-parity notifications** — receive configurable Android notifications
for completed and failed turns, approvals, clarifications, plugin events,
credit changes, and other background activity, then return safely to the
owning chat.
- **Inline delivered files** — valid `MEDIA:` references become authenticated,
tappable file cards without turning streamed fragments or code examples into
downloads.
- **Drafts survive navigation and restarts** — unsent composer text is kept per
saved connection and chat, while live session chips refresh only when their
pane is visible.

## What's new in v2.1.14

- **Navigation labels stay readable** — bottom-bar labels now fit on one line
on narrow phones at every supported text size and across every shipped
locale, with concise Russian bar labels that preserve the full pane titles.

## What's new in v2.1.13

- **Select exactly the text you need** — long-press any chat message to open a
selectable-text dialog, then copy part or all of the message. The dialog is
readable in light and dark themes and localised in all nine shipped languages.

## What's new in v2.1.12

- **Per-app language selection** — Android 13 and newer now list all nine
shipped Hermes locales in the system's app-language settings.
- **Accurate idle sessions** — unended chats no longer remain permanently
active when the Gateway omits a liveness flag; Projects placeholders are
ignored and recent activity gets a bounded five-minute window.

## What's new in v2.1.11

- **Eight new languages** — the app is now localised in Japanese, Simplified
Chinese, Korean, Spanish, French, German, Brazilian Portuguese, and Russian.
- **Reliable locale behaviour** — unsupported locales fall back to English,
Russian counts use the correct plural forms, and Chinese and Portuguese
region variants do not receive an incompatible catalogue.

## What's new in v2.1.10

- **Existing chats stay on target** — sending from a server-listed chat now
continues that exact session instead of creating a new sibling session.
- **Single creation path for new chats** — local drafts use recovery v2 without
racing a second legacy session creation, while explicit fallback remains safe.

## What's new in v2.1.9

- **Interactive Gateway requests on Android** — clarification, approval, sudo,
and secret prompts now reach the phone during a running turn and recover
safely across reconnects.
- **Fast, safe large-chat restore** — large sessions load a bounded recent
transcript without blocking the composer, then reconcile authoritative
history without losing newer turns.
- **Accurate session status** — Android now uses the Gateway's explicit active
state instead of treating every unended session as still running.

## What's new in v2.1.8

v2.1.8 fixes two connection-lifecycle problems diagnosed and resolved by
[@igitur](https://github.com/igitur) in PRs #111 and #113.

- **Custom HTTPS ports work as entered** — an explicit Port field value is
honoured even when it is `8642`; leaving the field blank still infers `443`
for HTTPS and `8642` for HTTP.
- **Turns survive leaving the chat** — navigating away during a running turn no
longer closes its SSE connection and interrupts the work server-side. The
detached client is released after completion, failure, cancellation, or its
safety timeout.

## What's new in v2.1.7

v2.1.7 brings the Android daily-driver workspace into line with stock Hermes
gateways through the extensive compatibility work contributed by
[@Thaeland](https://github.com/Thaeland) in PR #106.

- **Reliable detached replies** — interrupted socket connections now resume the
stored session, keep retrying across long-running turns and app backgrounding,
and use durable message IDs so replies are recovered even when the server's
capped history window rolls over.
- **Stock Projects support** — new Projects receive safely provisioned folders,
chat moves use the stock `session.workspace.move` contract, and project labels,
archived chats, migration, search, and compatibility states follow the real
gateway wire shapes.
- **Correct session pagination** — pinned-session backfills can no longer skip,
duplicate, or prematurely end later pages. Mutable OFFSET scans no longer
delete local assignments they cannot prove are stale.
- **Safer transport and offline state** — stale runtime bindings reattach,
socket creation and authentication are single-flight, network requests are
bounded, and concurrent Project mutations cannot persist optimistic or stale
cache snapshots.
- **Additional resilience** — archived reads are profile-scoped and complete,
machine sessions stay out of human chat lists, deliberate reconnects are
explained, and Android can discover installed speech-recognition services.

## What's new in v2.1.6

- **Private gateway certificates** — Android now trusts certificate authorities
that the device user explicitly installed, so private Caddy and Tailscale
gateway endpoints can connect without weakening normal certificate checks
(#108).

## What's new in v2.1.0

v2.1.0 merges the community daily-driver workspace edition from
[@CarlosReyesPena](https://github.com/CarlosReyesPena) (PR #88): a new
Workspace home with Projects, Chats and Activity panes, server-backed session
search, config backup and restore, and quick-chat intents — plus a large
accompanying test suite (900+ tests).

- **Workspace shell** — a new Home with attention-ranked "needs you" digest,
global New (project or quick) chat button, Activity operational timeline of
gateway turns, and a More pane that routes Cron, Skills, Memory, Settings
and the Hermes dashboard.
- **Projects pane** — server-owned Hermes projects via the gateway
`projects.*` RPC family: project tree overview, per-project chats,
migration preview and write path for local Spaces, chat moves between
projects, per-project search, and safe deletion. Legacy gateways degrade
gracefully to a labelled local-only compatibility mode.
- **Chats browser** — All / Recent / Unassigned / Archived filters, date
grouping, running/done status, pinned and archived session flags, and a
session→project label fallback for older APIs.
- **Session search** — three selectable modes: on-device (default), full-text
via the dashboard FTS5 endpoint with matching-excerpt results, and
AI-assisted query rewriting through Hermes (requires the corresponding
server support). Mode is remembered per connection; provider API keys never
leave the server.
- **Configuration backup & restore** — export all connections and app
preferences to a single encrypted file (PBKDF2 + AES-256-GCM), import in
merge or replace mode, with restore reachable from the empty-connection
state before any server is configured.
- **Quick chat lifecycle** — share-target and app-shortcut entry points,
share/review sheets for text and files, and a 72-hour quick-chat archive
policy that never archives blocked or running work.
- **Resilience** — gateway capability discovery (never assume the newest
server), fresh TCP per request with a 20 s timeout to fix stale keep-alive
hangs, and runtime Android 13+ notification permission request.
- **Chat UI** — sticky context header (project, model, reasoning effort,
connection state), You/Hermes role labels, long-press action sheet, real
fenced code blocks with copy and wrap/scroll toggle, and full tool output
on expanded activity cards.

## What's new in v2.0.1

- **Background turn notifications** — Android notifications fire when a Desktop
Gateway turn completes while the app is backgrounded, mirroring Hermes Desktop
tray notification behaviour. Notifications are automatically cleared when you
return to the app.
- **Scroll bug resolved** — the inherited mid-history scroll-jump on session
reopen (from upstream PR #71) has been fully resolved. The
`ChatScrollCoordinator` only aligns to the end on initial load, never inside
the streaming completion handler.

## What's new in v2.0.0

v2.0.0 merges the community Remote Gateway edition from
[@CristianGCiocoi](https://github.com/CristianGCiocoi).

- **Unified Desktop Gateway JSON-RPC transport** — text, images, and files share
one WebSocket session instead of split REST+WebSocket paths.
- **Per-chat model and thinking-effort selection** without changing the profile
default.
- **Multi-attachment uploads** — up to 10 files per message, 16 MiB each.
- **Copy/Select, Read Aloud, Edit+Resend, Regenerate, Stop, Export, Search,
Rename, Branch, Delete** for conversations.
- **Native Desktop Gateway event handling** — approvals, sudo, secrets,
clarifications, reasoning, tool activity, notifications, background results,
reviews, and subagents.
- **Voice dictation** — stage voice input before explicit send.
- **Durable turn recovery** — completed gateway responses survive Android
process-kill lifecycle events.
- **Persistent accessible text size** setting.
- **ATLAS document intake metadata** for mobile uploads.
- **Secure Android credential storage** with platform key material.
- **113+ Flutter tests** plus a synthetic gateway contract suite under
`tools/fake_gateway/`.

## What's new in v1.0.8

- **Reverse-proxy path prefixes** — configure separate path prefixes for the Gateway API and dashboard, e.g. `/profile/peter` before `/api` and `/v1`, and `/dashboard` before dashboard `/api` routes.
- **Proxied dashboard mode** — enable **Dashboard behind proxy** when nginx/Caddy/your host injects dashboard authentication. In this mode the app sends clean dashboard requests without trying to scrape a dashboard session token or perform password login.
- **Prefix-aware validation and chat** — API-key validation, session browsing, existing chat history, streaming chat completions, and dashboard drawer screens all use the configured prefixes.

## What's new in v1.0.7

- **Password-protected dashboards** — the Memory/Cron/Skills/Settings tabs now work against a dashboard secured with basic-auth, not just an open (`--insecure`) one. The app logs in via the dashboard's `/auth/password-login` flow and reuses the session cookie (the same mechanism the desktop client uses).
- **Configurable dashboard port** — set a custom dashboard port per connection when it isn't the default `9119`.
- **Dashboard details in the connection flow** — set the dashboard port/username/password while adding a connection (expand **Custom dashboard details**) or later via **⋮ → Dashboard Login**, with validation before saving.

## What's new in v1.0.6

- **Voice chat support** — tap the microphone in chat to dictate a message to Hermes, and Hermes can speak the response back.
- Spoken replies can be toggled from the chat input bar.
- Android/iOS microphone and speech-recognition permissions are included.

## Features

- **Hermes chat on Android** — browse sessions, create new chats, and send prompts to your Hermes Agent.
Expand All @@ -354,19 +129,23 @@ v2.0.0 merges the community Remote Gateway edition from

## Screenshots

The Home, Chats, More, Cron Jobs and Settings screenshots use anonymised example details.

<table>
<tr>
<td align="center"><img src="docs/screenshots/01-session-list.jpg" width="220" alt="Session list"><br><sub>Session list</sub></td>
<td align="center"><img src="docs/screenshots/02-navigation-drawer.jpg" width="220" alt="Navigation drawer"><br><sub>Navigation drawer</sub></td>
<td align="center"><img src="docs/screenshots/03-cron-jobs.jpg" width="220" alt="Cron jobs"><br><sub>Cron jobs</sub></td>
<td align="center"><img src="docs/screenshots/00-home.jpg" width="220" alt="Home dashboard with example tasks"><br><sub>Home</sub></td>
<td align="center"><img src="docs/screenshots/01-session-list.jpg" width="220" alt="Chats browser with example conversations"><br><sub>Chats</sub></td>
<td align="center"><img src="docs/screenshots/02-more.jpg" width="220" alt="More tab with workspace and organisation features"><br><sub>More</sub></td>
</tr>
<tr>
<td align="center"><img src="docs/screenshots/03-cron-jobs.jpg" width="220" alt="Cron jobs with example schedules"><br><sub>Cron jobs</sub></td>
<td align="center"><img src="docs/screenshots/04-add-cron-job.jpg" width="220" alt="Add cron job"><br><sub>Add cron job</sub></td>
<td align="center"><img src="docs/screenshots/05-memory.jpg" width="220" alt="Memory"><br><sub>Memory</sub></td>
<td align="center"><img src="docs/screenshots/06-settings.jpg" width="220" alt="Settings"><br><sub>Settings</sub></td>
</tr>
<tr>
<td align="center"><img src="docs/screenshots/06-settings.jpg" width="220" alt="Settings with an example model and provider"><br><sub>Settings</sub></td>
<td align="center"><img src="docs/screenshots/07-skills.jpg" width="220" alt="Skills"><br><sub>Skills</sub></td>
<td align="center"><img src="docs/screenshots/08-update-available.png" width="220" alt="Update available dialog for Hermes Android v2.1.22"><br><sub>Update available</sub></td>
</tr>
</table>

Expand Down
Binary file added docs/screenshots/00-home.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/01-session-list.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/02-more.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/screenshots/02-navigation-drawer.jpg
Binary file not shown.
Binary file modified docs/screenshots/03-cron-jobs.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/06-settings.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/08-update-available.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions scripts/check_readme_release_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Keep the README's short release window aligned with the changelog."""

import re
from pathlib import Path


HEADING = re.compile(r"^## (.+)$", re.MULTILINE)
RELEASE_HEADING = re.compile(r".*what['’]s new in v(\d+\.\d+\.\d+)$", re.IGNORECASE)
CHANGELOG_RELEASE = re.compile(r"^## \[(\d+\.\d+\.\d+)\]", re.MULTILINE)


def check_release_window(readme: str, changelog: str) -> list[str]:
latest = CHANGELOG_RELEASE.findall(changelog)[:3]
if len(latest) != 3:
raise ValueError("CHANGELOG.md needs at least three stable release entries")

headings = [match.group(1) for match in HEADING.finditer(readme)]
releases = [
(index, match.group(1))
for index, heading in enumerate(headings)
if (match := RELEASE_HEADING.fullmatch(heading))
]
versions = [version for _, version in releases]
if versions != latest:
raise ValueError(
f"README.md must show exactly the three latest changelog releases "
f"{latest}; found {versions}"
)

first = releases[0][0]
if headings[first + 3 : first + 4] != ["Features"] or [
index for index, _ in releases
] != list(range(first, first + 3)):
raise ValueError("README release entries must be consecutive and end before Features")
return latest


def main() -> None:
root = Path(__file__).resolve().parent.parent
try:
versions = check_release_window(
(root / "README.md").read_text(encoding="utf-8"),
(root / "CHANGELOG.md").read_text(encoding="utf-8"),
)
except ValueError as error:
raise SystemExit(str(error)) from error
print(f"README release window: {', '.join(versions)}")


if __name__ == "__main__":
main()
Loading
Loading