Skip to content

feat(mobile): add native mobile shell for Hermes Desktop - #52673

Open
realfishsam wants to merge 74 commits into
NousResearch:mainfrom
realfishsam:main
Open

feat(mobile): add native mobile shell for Hermes Desktop#52673
realfishsam wants to merge 74 commits into
NousResearch:mainfrom
realfishsam:main

Conversation

@realfishsam

@realfishsam realfishsam commented Jun 25, 2026

Copy link
Copy Markdown

Summary

This PR adds a standalone mobile app for Hermes. It is currently validated on iPhone, and the Expo project includes Android configuration as well.

It is not a native rewrite of the Hermes UI. The app is an Expo/React Native mobile shell that runs the existing Hermes Desktop renderer inside a WebView, then bridges renderer API calls to a configured Hermes gateway. On first launch, the app asks for the gateway URL, profile, and token, stores that locally, and then behaves like an installable mobile client for an existing Hermes instance.

Most of the size of this PR comes from landing the first mobile workspace and carrying over the Desktop renderer code needed to make the shell usable on phone-sized screens. The generated renderer bundle was initially tracked by mistake; that has been removed and ignored, so future mobile changes should be much smaller.

The architecture choice here is to keep one shared Hermes renderer/UI, wrap it in a native mobile shell, and apply only the mobile layout fixes needed for safe areas, keyboard behavior, sidebar navigation, and tap targets.

I tested this as a Release build on a physical iPhone, including install/launch via Xcode/Expo + devicectl. The PR includes screenshots and a short demo video from that iPhone build, plus docs for the exact physical-device install flow, including signing and nested-framework issues encountered during setup. Android configuration is present in the Expo project, but the physical-device verification in this PR is iPhone-focused.

Known current status: this PR needs to be rebased against current main; the current merge conflict is in package-lock.json.

Demo

Hermes Mobile demo

Build-to-iPhone docs

iPhone screenshots

Empty state Conversation Settings detail
Hermes iPhone empty state Hermes iPhone conversation Hermes iPhone settings detail

Verification

  • git diff --check
  • npm run typecheck from apps/mobile
  • Built Release app and installed/launched on a physical iPhone via Xcode/Expo + devicectl
  • Rebuilt/installed the no-debug-overlay Release build to the physical iPhone

Featured on r/hermesagent: https://www.reddit.com/r/hermesagent/comments/1ugv7i7/hermes_mobile/

@realfishsam
realfishsam requested a review from a team June 25, 2026 20:20
@alt-glitch alt-glitch added type/feature New feature or request comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have labels Jun 25, 2026
@marckernest

Copy link
Copy Markdown

The title makes some sense but it's missing some core info or I'm having trouble finding it. Are you proposing a standalone native iPhone installation?

@realfishsam

Copy link
Copy Markdown
Author

Yes — this is proposing a standalone installable iPhone app.

It is not a separate native rewrite of Hermes. It is an Expo/React Native iOS shell that runs the existing Desktop renderer in WKWebView, with a small native bridge for gateway-backed API calls and first-run gateway/profile/token setup. I rewrote the PR description to make that architecture and current status clearer, and added the iPhone demo video to the PR.

@realfishsam realfishsam changed the title feat(mobile): add standalone Hermes iPhone app feat(mobile): add native mobile shell for Hermes Desktop Jun 27, 2026
@realfishsam realfishsam changed the title feat(mobile): add native mobile shell for Hermes Desktop feat(mobile): add native iPhone shell for Hermes Desktop Jun 27, 2026
@realfishsam realfishsam changed the title feat(mobile): add native iPhone shell for Hermes Desktop feat(mobile): add native mobile shell for Hermes Desktop Jun 27, 2026
…, keyboard

- Mobile drawer: portal out of the collapsed <Pane> wrapper, fix the
  pointer-down/up double-toggle that ate the first tap, auto-close on
  session/nav/Settings tap.
- Safe-area awareness across chat header, overlay close button, dialog &
  sheet close buttons, sheet top/bottom edges, and the docked composer.
- Tap targets: titlebar buttons and composer input bumped to 44 pt; all
  controls inside OverlayView ≥44 pt with 16 px inputs (no iOS zoom).
- iOS keyboard: visualViewport now drives --keyboard-inset; the composer
  rides up with the keyboard and last-message clearance grows so sent
  messages stay visible.
- Intro centering: pb-composer (not pt-composer) so the wordmark sits
  visually centered between the top of the screen and the chatbox.
- Native bridge: route diagnostics to Metro console (no on-screen overlay),
  fix getBootstrapState shape so the install overlay doesn't crash, and
  wrap unknown desktop methods so renderer fallbacks don't throw
  unhandled rejections.
- Tooltips disabled on mobile-standalone (no hover on touch).
…moval

- Settings, Command Center, Cron, Profiles, Messaging now open to a list
  on mobile and drill into the detail with a back button (no more list
  + detail stacked on a 393pt screen).
- Sidebar: unified scroll (nav + sessions in one column, no dead space
  between search and PINNED), bigger tap-friendly session rows, darker
  backdrop (bg-black/45 + blur), hamburger gets a Claude-Code style
  filled-chip backdrop so it stays legible over busy chat content.
- Chat header: plain title on mobile — the SessionActionsMenu dropdown
  crowded the Dynamic Island and the menu painted under it. Sidebar
  session rows also short-circuit SessionActionsMenu / SessionContextMenu
  so long-press doesn't surface a desktop-shaped menu.
- Overlay close: "Done" pill bottom-right on mobile instead of a tiny X
  next to the safe-area; touch-friendly and unambiguous.
- Switch / toggle: excluded from the mobile button-min-height rule so
  they render as switches, not enlarged blobs.
- Adds three iPhone screenshots (empty state, conversation, settings
  drill-down) under docs/screenshots/.
assets/renderer/ is Vite output from `npm run renderer:bundle` — hashed
filenames per build, ~220k lines of fonts + compiled bundle. Was tracked
before the gitignore rule landed, so gitignore was silently bypassed.
Untrack and re-add the rule.
Vite emits CSS inside dist/assets/ that references siblings as ./foo.ttf
(e.g. codicon-*.ttf font face). The bundler only matched the basename
when the file lived at dist root; assets under dist/assets/ never got
inlined and rendered as tofu in the standalone WebView.
…ar regions

SafeAreaView wrapped the WebView with the dark root backgroundColor and
clipped it out of the safe-area regions, producing black bars at the
notch and home indicator when the renderer ran a light theme. The
renderer CSS already pads for env(safe-area-inset-*), so let the WebView
fill the screen and own the insets.
The default iOS input-accessory toolbar (prev/next/done) appeared above
the on-screen keyboard and shoved the composer up. The renderer has its
own keyboard handling, so suppress it at the native layer.
- Hide the sidebar trigger on skills (added to OVERLAY_VIEWS).
- Replace the horizontal category tag-cloud with a drill-down list on
  mobile: tap a category to filter to its skills, back link returns.
- Add a Done pill bottom-right (mirrors OverlayView mobile pattern) so
  there is a clear way out without the titlebar control.
- Bump PageSearchShell top padding on mobile so headers clear the
  Dynamic Island instead of sitting flush against it.
Messaging and Artifacts weren't in OVERLAY_VIEWS, so the titlebar sidebar
trigger still rendered (overlapping the search field) and there was no
mobile dismiss affordance. Extract the Done pill that Skills was using
into a shared MobileDonePill component and mount it from all three
non-OverlayView mobile menus.
Renderer code that calls e.g. window.hermesDesktop.fetchLinkTitle gets a
Promise.resolve(undefined) instead of undefined when the mobile bridge
doesn't stub the method, avoiding 'X.then is not a function' crashes.
Artifacts crashed with 'bridge(normalizedUrl).then is not a function'
because the mobile renderer bridge's fallback object had no
fetchLinkTitle stub. Inline-link title fetching is a desktop-only
nicety; on mobile return an empty string instead of undefined.
@alt-glitch alt-glitch added comp/dashboard Web dashboard / control panel UI (dashboard/, landing) sweeper:risk-automation Sweeper risk: may affect CI, automerge, label sync, or maintainer automation P2 Medium — degraded but workaround exists and removed comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/cron Cron scheduler and job management comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-automation Sweeper risk: may affect CI, automerge, label sync, or maintainer automation comp/dashboard Web dashboard / control panel UI (dashboard/, landing) labels Jul 29, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related mobile-shell decision: #53772 is the explicit conflict-resolved successor of this PR and #64962 is a competing lean iOS shell. Before either route is merged, choose one approach. Separately, this head commits a self-labelled development seed containing a gateway session credential in apps/mobile/index.html; revoke/rotate it and remove the seed before further review.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages needs-repro Bug needs reproduction steps P1 High — major feature broken, no workaround and removed type/feature New feature or request P2 Medium — degraded but workaround exists needs-repro Bug needs reproduction steps labels Jul 29, 2026
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have and removed type/security Security vulnerability or hardening P1 High — major feature broken, no workaround labels Jul 29, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Correction after reviewing current head 7fb8754: the diff uses environment-driven gateway configuration and documentation placeholders; I did not find a committed bearer credential. The earlier credential-exposure conclusion is therefore not substantiated on the current patch. This remains a competing mobile-shell architecture alongside #53772 and #64962.

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

Labels

comp/desktop Electron desktop app (apps/desktop/*) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.