docs: initial Hermes Mobile planning scaffold

This commit is contained in:
Hermes Agent
2026-07-09 03:45:48 +00:00
commit 2cd4730324
14 changed files with 2139 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
+37
View File
@@ -0,0 +1,37 @@
# dependencies
node_modules/
.pnpm-store/
# builds
dist/
build/
.out/
.next/
coverage/
# env/secrets
.env
.env.*
!.env.example
*.pem
*.key
# runtime data
data/
uploads/
*.db
*.db-shm
*.db-wal
logs/
# OS/editor
.DS_Store
.vscode/
.idea/
*.swp
# python bridge
__pycache__/
*.pyc
.venv/
venv/
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Bonzi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+166
View File
@@ -0,0 +1,166 @@
# Proposed Project Structure
```text
hermes-mobile/
README.md
PROJECT_STRUCTURE.md
LICENSE
.gitignore
.editorconfig
package.json # pnpm workspace root, later
pnpm-workspace.yaml
tsconfig.base.json
install.sh # one-liner installer entrypoint, later
docs/
PRODUCT_PLAN.md
ARCHITECTURE.md
DESIGN_SYSTEM.md
COMPANION_SERVER.md
MOBILE_APP.md
INSTALL.md
TESTING.md
ROADMAP.md
apps/
mobile/ # Android-first PWA
package.json
vite.config.ts
index.html
public/
manifest.webmanifest
icons/
src/
app/
App.tsx
router.tsx
providers.tsx
screens/
AskScreen/
ActivityScreen/
CronScreen/
FilesScreen/
SettingsScreen/
components/
agent-status-orb/
composer/
tool-card/
event-timeline/
upload-tray/
voice-recorder/
cron-job-card/
approval-card/
bottom-nav/
app-shell/
features/
tasks/
sessions/
cron/
files/
approvals/
notifications/
pairing/
settings/
lib/
api-client.ts
realtime.ts
push.ts
audio.ts
file-utils.ts
format.ts
styles/
globals.css
tokens.css
main.tsx
companion/ # local server installed beside Hermes Agent
package.json
src/
app.ts
config/
index.ts
schema.ts
auth/
pairing.ts
sessions.ts
middleware.ts
db/
client.ts
migrations/
repositories/
events/
bus.ts
types.ts
websocket.ts
sse.ts
hermes/
HermesAdapter.ts
CliHermesAdapter.ts
ApiHermesAdapter.ts
PythonHermesAdapter.ts
promptFormatting.ts
tasks/
TaskManager.ts
TaskQueue.ts
taskTypes.ts
uploads/
uploadRoutes.ts
UploadStore.ts
fileValidation.ts
notifications/
NotificationProvider.ts
WebPushProvider.ts
NtfyProvider.ts
GotifyProvider.ts
NoopProvider.ts
cron/
CronService.ts
HermesCronAdapter.ts
approvals/
ApprovalService.ts
routes/
health.ts
tasks.ts
uploads.ts
sessions.ts
cron.ts
approvals.ts
settings.ts
system/
hermesHealth.ts
logs.ts
index.ts
scripts/
hermes_worker.py # direct Python bridge later
packages/
shared/ # shared TS types/schemas
package.json
src/
events.ts
api.ts
tasks.ts
cron.ts
files.ts
notifications.ts
ui/ # optional shared UI primitives later
package.json
src/
scripts/
install.sh
uninstall.sh
dev-hermes-smoke.sh
build-release.sh
deploy/
systemd/
hermes-mobile-companion.service
nginx/
hermes-mobile.conf
caddy/
Caddyfile.example
```
## Modularity Notes
- `apps/mobile` should never import Hermes-specific server internals directly. It uses `packages/shared` schemas and API client only.
- `apps/companion/src/hermes` is the only layer that knows how to talk to Hermes Agent.
- `apps/companion/src/notifications` is provider-swappable.
- `apps/companion/src/cron` wraps Hermes cron rather than replacing it.
- `packages/shared` defines stable event types used by both app and server.
- `scripts/` and `deploy/` stay boring and auditable.
+153
View File
@@ -0,0 +1,153 @@
# Hermes Mobile
Native-feeling Android-first PWA and companion server for [Hermes Agent](https://github.com/NousResearch/hermes-agent).
Hermes Mobile is not a generic LLM chat UI. It is a self-hosted, Hermes-native control surface designed for phone use: prompt by text or voice, upload files, watch tool calls live, approve actions, manage cron jobs, and get notified when long-running agent work is done.
Repo: https://git.molberg.cloud/bonzi/hermes-mobile
## Status
Planning scaffold only. No production code yet.
## Product Goals
- Feel like a real Android app, not a web page pretending to be chat.
- Run privately on the same Linux machine as Hermes Agent.
- Keep Hermes Agent untouched where possible; operate as a companion layer above/alongside it.
- Expose Hermes-specific concepts: tool calls, sessions, jobs, generated files, approvals, background tasks.
- Make installation one-liner simple for a homelab machine.
- Stay modular enough that the app, companion server, Hermes integration, notification providers, and install scripts can evolve independently.
## Planned Components
```text
Android / mobile browser
|
| HTTPS + WebSocket/SSE + Web Push
v
Hermes Mobile App (PWA / later Capacitor)
|
v
Hermes Companion Server
|
+-- Hermes CLI / Python AIAgent bridge
+-- Hermes API server bridge
+-- Hermes session DB reader
+-- Hermes cron manager
+-- Upload/file store
+-- Push notification provider
```
## MVP Feature Set
1. Mobile-first app shell
- Installable PWA
- Android-style bottom navigation
- Offline-friendly shell
- Dark, playful visual system inspired by happy.engineering / Happy app
2. Chat / prompt screen
- Text prompts
- Voice note recording
- File uploads: zip, png, jpg, pdf, txt, logs, folders later
- Streaming responses
- “Agent is working” state
- Completion notification
3. Hermes-native activity timeline
- Tool call started / finished
- Terminal commands
- File reads/writes
- Browser actions
- Home Assistant actions
- Cron changes
- Subagent delegation
- Expand/collapse raw output
4. Sessions
- Recent sessions
- Resume session
- Rename / pin / archive
- Per-session attachments and generated files
5. Cron
- List cron jobs
- Run now
- Pause/resume/remove
- View last output
- Delivery target: app notification / in-app inbox
6. Approvals
- Push approval to phone for dangerous commands
- Approve / deny with short audit log
7. Companion settings
- Connect to a companion server URL
- Pair using one-time code or passkey
- Configure notification backend
- View Hermes health/status
## Docs
- [Product Plan](docs/PRODUCT_PLAN.md)
- [Architecture](docs/ARCHITECTURE.md)
- [Design System](docs/DESIGN_SYSTEM.md)
- [Companion Server](docs/COMPANION_SERVER.md)
- [Mobile App Structure](docs/MOBILE_APP.md)
- [Install Strategy](docs/INSTALL.md)
- [Testing Strategy](docs/TESTING.md)
- [Roadmap](docs/ROADMAP.md)
## Proposed Stack
Frontend:
- TypeScript
- React + Vite or Next.js static/PWA mode
- Tailwind CSS
- Zustand or TanStack Query for client state
- Service Worker + Web Push
- Capacitor later, only if PWA limitations matter
Companion server:
- TypeScript Node.js initially, because file uploads, WebSocket/SSE, install scripts, and PM2/systemd are straightforward
- Fastify or Hono
- SQLite for app metadata
- Local filesystem upload store
- Python bridge subprocess or direct Hermes module calls where safe
Why not Open WebUI/LibreChat:
- They are model chat UIs.
- Hermes needs agent-native events, cron, approvals, file artifacts, process logs, sessions, and tool timelines.
- Forking a generic app would probably become more work than a clean, small purpose-built app.
## One-liner install target
Final goal:
```bash
curl -fsSL https://git.molberg.cloud/bonzi/hermes-mobile/raw/branch/main/install.sh | bash
```
or, for local/self-hosted raw auth limitations, a release artifact URL:
```bash
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash
```
The installer should:
- Detect Hermes Agent install and config path
- Create a system user or use current user depending mode
- Install Node runtime if missing
- Download release bundle
- Create config at `/etc/hermes-mobile/config.yaml` or `~/.config/hermes-mobile/config.yaml`
- Configure systemd service `hermes-mobile-companion`
- Print pairing URL / one-time setup code
## Development Philosophy
- Companion server owns mobile UX state, not Hermes core.
- Hermes remains the agent runtime/source of truth for actual work.
- Every Hermes integration goes through an adapter interface so we can switch between CLI, API server, direct Python, or future Hermes-native event APIs.
- Tool-call UI should prefer structured events, but gracefully degrade to parsed CLI/API logs during early versions.
- Keep secrets out of repo. Use environment/config files and future vault integration.
+311
View File
@@ -0,0 +1,311 @@
# Architecture
## Overview
Hermes Mobile is split into two deployable parts:
1. Mobile app
- Installable PWA designed for Android.
- Talks only to the companion server.
- Does not need direct access to Hermes files or credentials.
2. Companion server
- Runs on the same Linux machine as Hermes Agent.
- Bridges mobile UI actions to Hermes Agent.
- Owns upload storage, push notifications, pairing/auth, app metadata, and realtime event fanout.
```text
+---------------------------+
| Android PWA |
| - Chat |
| - Tool timeline |
| - Cron UI |
| - Files |
| - Approvals |
+-------------+-------------+
|
| HTTPS REST + WebSocket/SSE + Web Push
v
+---------------------------+
| Hermes Companion Server |
| - Auth/pairing |
| - Task orchestrator |
| - Hermes adapters |
| - Event bus |
| - Upload store |
| - Cron wrapper |
| - Notification providers |
+------+------+-------------+
| |
| +--------------------+
| |
v v
+-------------+ +--------------+
| Hermes CLI | | Hermes files |
| Hermes API | | sessions/db |
| AIAgent | | cron jobs |
+-------------+ +--------------+
```
## Key Architectural Rule
The companion server should depend on stable Hermes surfaces first, but isolate every integration behind interfaces.
```ts
interface HermesAdapter {
health(): Promise<HermesHealth>
startTask(input: StartTaskInput): AsyncIterable<HermesEvent>
continueSession(sessionId: string, input: StartTaskInput): AsyncIterable<HermesEvent>
stopTask(taskId: string): Promise<void>
}
```
Possible implementations:
- `CliHermesAdapter`: spawns `hermes chat -q` or interactive process. Easiest baseline, weakest event detail.
- `ApiHermesAdapter`: calls Hermes API server. Good for compatibility, but generic OpenAI API hides some tool events.
- `PythonHermesAdapter`: imports Hermes Agent modules and uses callbacks for stream/tool progress. Best long-term event fidelity.
- `GatewayHermesAdapter`: uses existing gateway/session machinery if exposed.
## Companion Server Modules
```text
companion/
src/
app.ts # server bootstrap
config/ # env/yaml config loading
auth/ # pairing, sessions, tokens, passkeys later
db/ # SQLite schema/migrations/repositories
events/ # event bus + SSE/WebSocket fanout
hermes/ # adapter interface + implementations
tasks/ # task lifecycle/orchestration
uploads/ # multipart upload handling + retention
notifications/ # webpush/ntfy/gotify providers
cron/ # Hermes cron wrapper
approvals/ # approval queue + Hermes callbacks later
routes/ # REST API routes
ws/ # realtime endpoints
system/ # service health, logs, install info
```
## Event Bus
Everything that happens in the companion becomes an event.
Core event shape:
```ts
type CompanionEvent = {
id: string
type: string
taskId?: string
sessionId?: string
ts: string
level?: 'debug' | 'info' | 'warn' | 'error'
payload: unknown
}
```
Important event types:
```text
task.created
task.started
task.status
task.finished
task.failed
task.cancelled
assistant.delta
assistant.message
assistant.final
tool_call.started
tool_call.output
tool_call.finished
tool_call.failed
file.uploaded
file.generated
approval.required
approval.resolved
cron.job.listed
cron.job.updated
cron.job.finished
notification.sent
notification.failed
```
The mobile app subscribes by:
- WebSocket for active app foreground.
- SSE fallback.
- Push notifications when app is background/closed.
## Data Storage
### SQLite
Companion-owned metadata:
- paired devices
- auth sessions
- tasks
- task events
- uploaded files metadata
- notification subscriptions
- user settings/cache
Do not duplicate full Hermes session content unless needed for mobile indexing. Hermes session DB remains source of truth.
### Filesystem
Default paths:
```text
/var/lib/hermes-mobile/
hermes-mobile.db
uploads/
<task-id>/
generated/
cache/
logs/
```
User-mode install paths:
```text
~/.local/share/hermes-mobile/
~/.config/hermes-mobile/config.yaml
```
## API Surface
### Auth / Pairing
```http
POST /api/pair/start
POST /api/pair/confirm
POST /api/auth/login
POST /api/auth/logout
GET /api/me
```
Pairing flow:
1. Installer prints `https://host/pair?code=123-456`.
2. User opens on phone.
3. Server binds device and issues token.
4. Future sessions use secure cookie or bearer token.
### Tasks
```http
POST /api/tasks
GET /api/tasks
GET /api/tasks/:id
POST /api/tasks/:id/stop
GET /api/tasks/:id/events
WS /api/tasks/:id/stream
```
### Uploads
```http
POST /api/uploads
GET /api/files
GET /api/files/:id/download
DELETE /api/files/:id
```
### Sessions
```http
GET /api/sessions
GET /api/sessions/:id
POST /api/sessions/:id/continue
PATCH /api/sessions/:id
```
### Cron
```http
GET /api/cron/jobs
POST /api/cron/jobs/:id/run
POST /api/cron/jobs/:id/pause
POST /api/cron/jobs/:id/resume
DELETE /api/cron/jobs/:id
```
### System
```http
GET /api/health
GET /api/hermes/health
POST /api/hermes/test
GET /api/logs
```
## Hermes Integration Notes
Existing useful surfaces observed:
- Hermes API server exposes `/v1/chat/completions`, `/v1/responses`, `/v1/models`, `/health`.
- `AIAgent` supports `stream_delta_callback` and `tool_progress_callback` constructor params.
- Cron jobs are manageable through Hermes CLI/tooling and stored under Hermes home.
- Gateway platforms already handle STT/media patterns, but mobile should not depend on Telegram.
Long-term ideal:
- Add/consume a Hermes-native event stream from AIAgent that emits structured tool events.
- Companion server can either import Hermes modules or call a local IPC endpoint.
## Security Model
Default deployments:
1. LAN/Tailscale only, simple pairing token.
2. Public HTTPS, pairing token + device token + optional passkey.
Security requirements:
- No unauthenticated access to prompt endpoints.
- Upload size limits.
- File paths normalized; no arbitrary file read from upload endpoints.
- CSRF protection if cookie auth used.
- Audit log for approvals.
- Secrets remain in Hermes config/vault, not in mobile app.
## Notification Architecture
Provider interface:
```ts
interface NotificationProvider {
send(device: Device, message: NotificationMessage): Promise<void>
test(device: Device): Promise<void>
}
```
Providers:
- `WebPushProvider`
- `NtfyProvider`
- `GotifyProvider`
- `NoopProvider`
## Deployment Modes
### User mode
- Runs as current user.
- Uses `~/.config/hermes-mobile` and `~/.local/share/hermes-mobile`.
- Good for dev/single-user boxes.
### System mode
- Creates `hermes-mobile` system user or runs under Hermes user.
- Uses `/etc/hermes-mobile` and `/var/lib/hermes-mobile`.
- systemd service.
- Better production posture.
## Future Native Wrapper
If Android PWA is insufficient:
- Add Capacitor app under `apps/android-shell`.
- Reuse same web app.
- Gain native share target, richer push, microphone/file APIs.
+254
View File
@@ -0,0 +1,254 @@
# Companion Server Plan
## Purpose
The companion server is the local service installed beside Hermes Agent. It turns Hermes into a phone-friendly app backend without forcing Hermes core to become a mobile backend.
Responsibilities:
- Authenticate/pair phones
- Accept prompts, voice recordings, and files
- Start/continue Hermes tasks
- Stream Hermes activity to the app
- Store app metadata
- Send push notifications
- Expose cron/job/session/file APIs
- Provide health checks and install/update status
## Runtime
Preferred MVP runtime: Node.js + TypeScript.
Reasons:
- Great fit for HTTP/WebSocket/SSE/file uploads.
- Easy bundling for one-liner installs.
- PM2/systemd deployment is straightforward in Zeb's environment.
- Frontend and backend can share TypeScript event schemas.
Potential framework:
- Fastify: mature, fast, good plugins.
- Hono: simple, edge-like API, also good.
Initial pick: Fastify unless we want ultra-minimal.
## Config
Config locations:
System mode:
```text
/etc/hermes-mobile/config.yaml
/var/lib/hermes-mobile/
```
User mode:
```text
~/.config/hermes-mobile/config.yaml
~/.local/share/hermes-mobile/
```
Example:
```yaml
server:
host: 0.0.0.0
port: 8787
public_url: https://hermes-mobile.molberg.cloud
auth:
pairing_enabled: true
token_ttl_days: 90
passkeys_enabled: false
hermes:
home: /root/.hermes
mode: python # python | api | cli
cli_bin: /root/.local/bin/hermes
api_base_url: http://127.0.0.1:18793/v1
api_key: ""
storage:
data_dir: /var/lib/hermes-mobile
max_upload_mb: 512
retention_days: 30
notifications:
provider: webpush # webpush | ntfy | gotify | none
ntfy_url: https://ntfy.example.com/hermes
```
## Hermes Adapter Strategy
### Phase 1: CLI/API hybrid
- Use Hermes CLI for health tests and simple prompts.
- Use Hermes API server if available for OpenAI-compatible chat.
- Parse coarse progress where possible.
Pros:
- Requires few/no Hermes changes.
- Easy to prove app UX.
Cons:
- Tool-call visibility limited.
### Phase 2: Direct Python adapter
Run a Python worker process that imports Hermes `AIAgent` and communicates with Node over stdio or local socket.
Benefits:
- Can pass `stream_delta_callback` and `tool_progress_callback`.
- More structured event stream.
- Better session control.
Potential shape:
```text
Node companion <-- JSON lines --> Python hermes_worker.py <-- imports --> AIAgent
```
Worker commands:
- `health`
- `start_task`
- `continue_session`
- `cancel_task`
- `list_sessions`
- `cron_action`
Worker emits events:
- `assistant.delta`
- `tool_call.started`
- `tool_call.output`
- `tool_call.finished`
- `assistant.final`
### Phase 3: Hermes-native plugin/API
If Hermes Agent grows a stable event API, switch adapter implementation without rewriting app/server.
## Task Lifecycle
```text
created -> queued -> running -> finished
-> failed
-> cancelled
-> waiting_approval
```
Task record:
```ts
type Task = {
id: string
sessionId?: string
title?: string
status: TaskStatus
inputText: string
attachmentIds: string[]
createdAt: string
startedAt?: string
finishedAt?: string
error?: string
}
```
## Upload Handling
Use multipart upload endpoint.
Pipeline:
1. Validate file count/size/type.
2. Store under `uploads/<task-id-or-upload-id>/original-name`.
3. Record metadata in SQLite.
4. Include absolute file paths in Hermes prompt.
Prompt augmentation example:
```text
The user uploaded these files:
- screenshot.png: /var/lib/hermes-mobile/uploads/abc/screenshot.png (image/png, 320 KB)
- source.zip: /var/lib/hermes-mobile/uploads/abc/source.zip (application/zip, 12 MB)
User prompt:
<actual user prompt>
```
## Voice Handling
MVP options:
1. Client records `.webm`/`.ogg`, server stores it, then sends file path to Hermes with instruction to transcribe/analyze.
2. Server transcribes with local faster-whisper and sends text prompt.
Recommended MVP:
- Server-side transcription using local faster-whisper if available.
- Fallback: pass audio file to Hermes as attachment.
## Notifications
Events that trigger notifications:
- task completed after app backgrounded
- task failed
- approval required
- cron job finished/failed
Use provider abstraction:
```ts
interface NotificationProvider {
sendCompletion(task: Task): Promise<void>
sendFailure(task: Task): Promise<void>
sendApproval(approval: Approval): Promise<void>
sendCron(job: CronEvent): Promise<void>
}
```
## Pairing/Auth
MVP pairing:
1. Installer/server logs one-time code.
2. Phone opens `/pair` and enters code or uses link.
3. Server stores device and creates token.
4. Token stored in secure-ish PWA storage. Later use passkeys.
For public exposure, add:
- Rate limiting
- Token rotation
- Session revoke UI
- Optional Tailscale-only mode
## Cron Integration
Initial implementation can shell out to Hermes CLI:
```bash
hermes cron list --json
hermes cron run <id>
hermes cron pause <id>
hermes cron resume <id>
```
If CLI JSON is not available, import cron modules from Hermes Python or parse job files carefully.
## System Health
Health endpoint should check:
- companion server up
- DB writable
- upload dir writable
- Hermes CLI exists
- Hermes config exists
- Hermes API server reachable if configured
- Python worker reachable if configured
- notification provider configured
## Logging
- Structured JSON logs for service.
- Human-readable recent logs in UI.
- Do not log secrets, bearer tokens, or full uploaded file content.
## Open Questions
- Should companion run as same user as Hermes Agent for direct module/file access?
- Should public access be Cloudflare Access/Tailscale-first instead of app auth-first?
- How much Hermes core should be extended for better event streaming?
+222
View File
@@ -0,0 +1,222 @@
# Design System
## Visual Direction
Target: visually inspired by `happy.engineering` and the Happy app: polished, playful, soft, modern, high-motion, emotionally warm, and app-like.
Not a terminal dashboard. Not a generic ChatGPT clone. It should feel like a friendly command center for a personal agent.
## Keywords
- Native Android feel
- Playful but competent
- Soft cards
- Friendly gradients
- Rounded app surfaces
- Large touch targets
- Motion-rich microinteractions
- Calm dark mode
- “Agent is alive” status cues
## Initial Theme Direction
Because Zeb likes both stark terminal aesthetics and polished UIs, Hermes Mobile should combine:
- Happy-style colorful/soft app surfaces
- Hermes identity through icons, agent activity, and subtle terminal/tool-call details
- Dark-first interface for phone use
- Optional light mode later
## Color Palette Draft
```text
Background: #080A0F
Surface: #10141D
Elevated surface: #171D29
Card border: rgba(255,255,255,0.08)
Text primary: #F7F8FC
Text secondary: #AAB2C5
Muted: #687085
Hermes gold: #F2C14E
Happy blue: #6EA8FF
Happy purple: #A78BFA
Happy green: #63E6BE
Danger coral: #FF6B6B
Warning amber: #FFD166
```
Gradients:
```css
--gradient-orb: radial-gradient(circle at 30% 20%, #6EA8FF 0%, #A78BFA 45%, #080A0F 100%);
--gradient-action: linear-gradient(135deg, #6EA8FF, #A78BFA, #63E6BE);
--gradient-hermes: linear-gradient(135deg, #F2C14E, #FF8A65);
```
## Typography
Primary UI:
- Inter, Geist, or system Android sans.
Mono/details:
- JetBrains Mono for tool outputs, command snippets, paths, logs.
Type scale:
- Screen title: 28-34px, 700
- Section title: 18-20px, 650
- Body: 15-16px
- Metadata: 12-13px
- Tool output: 12-13px mono
## Layout
### Mobile shell
- Full-screen app layout.
- Safe-area aware.
- Bottom navigation with 4-5 tabs:
- Ask
- Activity
- Cron
- Files
- Settings
### Ask screen
```text
┌─────────────────────────┐
│ Hermes status orb │
│ │
│ Recent / current task │
│ ┌───────────────────┐ │
│ │ assistant answer │ │
│ └───────────────────┘ │
│ ┌───────────────────┐ │
│ │ tool timeline │ │
│ └───────────────────┘ │
│ │
│ [ + ] [ hold voice ] │
│ [ prompt input ↑ ] │
└─────────────────────────┘
```
Input should be thumb-first:
- Attachment left
- Voice pill / mic
- Send button right
- Input grows to 4-6 lines max
## Components
### Agent Status Orb
A small animated orb/avatar indicating:
- idle
- thinking
- using tools
- waiting for approval
- done
- error
This gives the app life without pretending to be a person.
### Tool Card
States:
- queued
- running
- success
- failed
- cancelled
Collapsed view:
```text
[terminal icon] terminal • 2.4s • success
npm test
```
Expanded view:
- Arguments summary
- Output preview
- Copy button
- Open file if applicable
- Error highlighting
### Timeline
Vertical event feed with icons and subtle connecting line.
Events:
- Prompt received
- Tool started
- Tool output
- File generated
- Notification sent
- Final answer
### Approval Card
Should feel serious but not scary.
```text
Approval required
Hermes wants to run:
rm -rf /tmp/build-cache
Risk: destructive file operation
[ Deny ] [ Approve once ]
```
### Cron Job Card
```text
Homelab health check
Every day 09:00
Last run: success · 2h ago
[Run now] [Pause]
```
### Upload Tray
Shows selected files before sending:
- thumbnail for images
- file icon for zip/pdf/log
- remove button
- total size
## Motion
Use motion sparingly but meaningfully:
- status orb breathing while thinking
- tool card expands smoothly
- file upload progress liquid/soft bar
- send button morphs to spinner
- completion haptic-like visual pulse
Prefer CSS transitions and Framer Motion if bundle size acceptable.
## Android-native Feeling
- Installable PWA manifest with proper icons.
- Standalone display mode.
- No browser chrome when installed.
- Bottom nav like native Material apps.
- Pull-to-refresh disabled; in-app refresh buttons where needed.
- Back gesture should close panels before navigating away.
- Use Android share target later so user can share files/text to Hermes.
## Accessibility
- Minimum 44px tap targets.
- Color is not the only status indicator.
- Reduced motion support.
- Good contrast in dark mode.
- Voice actions have text alternatives.
## Design TODO
- Capture screenshots/references from happy.engineering and Happy app.
- Build a visual moodboard.
- Design first mockups for Ask, Tool Timeline, Cron, Settings.
- Decide exact logo/avatar direction.
+224
View File
@@ -0,0 +1,224 @@
# Install Strategy
## Goal
A one-liner installer that sets up the companion server beside an existing Hermes Agent install.
Target UX:
```bash
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash
```
or from Gitea release/raw if accessible without auth:
```bash
curl -fsSL https://git.molberg.cloud/bonzi/hermes-mobile/raw/branch/main/install.sh | bash
```
Because Zeb's Gitea raw URLs may require auth, release assets or a mirrored static install URL are safer for the public one-liner.
## Installer Responsibilities
1. Detect OS/package manager.
2. Detect Hermes Agent:
- `command -v hermes`
- `~/.hermes/config.yaml`
- optional `HERMES_HOME`
3. Check Node.js version or install bundled binary/runtime.
4. Download latest Hermes Mobile companion release.
5. Create config.
6. Create data directories.
7. Install systemd service.
8. Start service.
9. Print pairing URL and one-time code.
## Installation Modes
### System mode (default for servers)
Paths:
```text
/opt/hermes-mobile/ app bundle
/etc/hermes-mobile/config.yaml config
/var/lib/hermes-mobile/ DB/uploads/cache
/var/log/hermes-mobile/ logs if not journald-only
```
Service:
```text
hermes-mobile-companion.service
```
Run user:
- Option A: current user/root if Hermes is root-installed.
- Option B: `hermes-mobile` user with group/read permissions to Hermes paths.
For Zeb's box, same user/root is simplest initially because Hermes files live under `/root/.hermes`.
### User mode
```bash
curl ... | bash -s -- --user
```
Paths:
```text
~/.local/opt/hermes-mobile/
~/.config/hermes-mobile/config.yaml
~/.local/share/hermes-mobile/
~/.config/systemd/user/hermes-mobile-companion.service
```
## Service Unit Draft
```ini
[Unit]
Description=Hermes Mobile Companion Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/hermes-mobile
Environment=NODE_ENV=production
Environment=HERMES_MOBILE_CONFIG=/etc/hermes-mobile/config.yaml
ExecStart=/usr/bin/node /opt/hermes-mobile/server/index.js
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
```
## Config Generation
Installer should write:
```yaml
server:
host: 0.0.0.0
port: 8787
public_url: ""
hermes:
home: /root/.hermes
cli_bin: /root/.local/bin/hermes
mode: python
api_base_url: http://127.0.0.1:18793/v1
storage:
data_dir: /var/lib/hermes-mobile
max_upload_mb: 512
auth:
pairing_enabled: true
notifications:
provider: webpush
```
## Pairing Output
After start:
```text
Hermes Mobile installed.
Open this on your phone:
http://SERVER-IP:8787/pair?code=123-456
Pairing code:
123-456
Service:
systemctl status hermes-mobile-companion
Logs:
journalctl -u hermes-mobile-companion -f
```
## Update
```bash
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash -s -- --update
```
Should:
- Download latest release.
- Preserve config and data.
- Restart service.
- Run DB migrations.
## Uninstall
```bash
curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash -s -- --uninstall
```
Should ask/flag whether to keep data:
- `--keep-data`
- `--purge`
## Release Layout
Build artifact:
```text
hermes-mobile-linux-x64.tar.gz
server/
index.js
package.json
node_modules/ or bundled single executable
public/
PWA assets
scripts/
hermes_worker.py
migrations/
```
Better later:
- single static binary via Bun/Node SEA if stable enough.
- Docker image optional but not the primary install path.
## One-liner Safety
Installer should:
- Show what it will do.
- Avoid deleting data unless explicitly asked.
- Not overwrite existing config without backup.
- Use `set -euo pipefail`.
- Log to temp file.
- Verify download checksum if release metadata supports it.
## Development Install
From cloned repo:
```bash
pnpm install
pnpm dev
```
Companion dev server:
```bash
pnpm --filter companion dev
```
Mobile dev server:
```bash
pnpm --filter mobile dev --host 0.0.0.0
```
## Future Cloudflare/Tailscale
Expose options:
- Tailscale only: bind to `100.x` IP or localhost behind `tailscale serve`.
- Cloudflare Tunnel: companion prints local port and expected reverse proxy config.
- Nginx/Caddy snippets generated by installer.
+221
View File
@@ -0,0 +1,221 @@
# Mobile App Plan
## App Type
Start as a PWA optimized for Android Chrome.
Why PWA first:
- Fast iteration.
- Easy self-hosting.
- No app store.
- Installable to home screen.
- Web Push support on Android is good.
- Same frontend can later be wrapped with Capacitor.
Potential later native shell:
- Capacitor Android for native share target, richer notifications, and better file/mic APIs.
## Frontend Stack
Recommended:
- Vite + React + TypeScript
- Tailwind CSS
- TanStack Query for server data
- Zustand for local UI/session state
- Framer Motion for motion where useful
- Workbox or Vite PWA plugin
- Zod for API schema validation
Alternative:
- Next.js if SSR/server components become useful, but for a companion-served app Vite is simpler.
## App Package Structure
```text
apps/mobile/
src/
app/
App.tsx
router.tsx
providers.tsx
screens/
AskScreen/
ActivityScreen/
SessionsScreen/
CronScreen/
FilesScreen/
SettingsScreen/
components/
agent-status-orb/
composer/
tool-card/
event-timeline/
upload-tray/
voice-recorder/
cron-job-card/
approval-card/
bottom-nav/
app-shell/
features/
tasks/
sessions/
cron/
files/
approvals/
notifications/
pairing/
settings/
lib/
api-client.ts
realtime.ts
push.ts
audio.ts
file-utils.ts
format.ts
styles/
globals.css
tokens.css
assets/
icons/
splash/
main.tsx
public/
manifest.webmanifest
service-worker.ts
```
## Navigation
Bottom tabs:
- Ask
- Activity
- Cron
- Files
- Settings
Sessions can be either:
- a sub-screen from Ask, or
- included in Activity initially.
## Ask Screen Behavior
States:
- idle
- composing
- uploading
- sending
- running
- waiting_approval
- finished
- failed
Composer:
- Text input
- Attach file button
- Hold/tap voice recording button
- Send button
- Optional mode chips: New / Continue / Background
When task starts:
- Input collapses to bottom.
- Task timeline appears.
- Assistant response streams.
- Tool cards appear in chronological order.
## Realtime
Use WebSocket first:
- `/api/realtime?token=...`
- Subscribe to task/session events.
Fallback:
- SSE `/api/tasks/:id/events/stream`
- Polling last resort.
Frontend event store should append events idempotently by event ID.
## Push Notifications
PWA flow:
1. User enables notifications in Settings.
2. App registers service worker.
3. App subscribes to PushManager.
4. Subscription sent to companion server.
5. Server sends Web Push on completion/approval/cron events.
Fallback if Web Push fails:
- Configure ntfy link in Settings.
## Voice Recording
Use MediaRecorder.
Preferred mime order:
- `audio/webm;codecs=opus`
- `audio/ogg;codecs=opus`
- browser default fallback
UI:
- Press/tap mic to start.
- Big recording sheet with timer/waveform.
- Cancel / send.
- Upload progress.
## File Upload UX
- Android file picker through `<input type=file multiple>`.
- Share Target API later for “Share to Hermes”.
- Preview thumbnails for images.
- File chips for archives/docs.
- Upload before task send or as part of task multipart.
## Offline/Background Behavior
- App shell should load offline.
- Draft prompt persists locally.
- Running task state reloads from server after reconnect.
- No attempt to run Hermes offline.
## Settings UX
First launch:
1. Enter companion URL or scan QR/pairing link.
2. Pair device.
3. Test connection.
4. Enable notifications.
5. Land on Ask.
Settings fields:
- Companion URL
- Device name
- Notification status/test
- Theme
- Default task mode
- Upload retention display
- Hermes health
## Android Polish Checklist
- `display: standalone` manifest.
- Proper app icon masks/adaptive icon assets.
- Theme color matching dark background.
- Avoid viewport resize bugs with keyboard.
- Safe-area padding.
- Disable browser pull-to-refresh where possible and add in-app refresh.
- Haptics via Vibration API where appropriate.
- Back button behavior for modals/sheets.
## Accessibility
- Visible focus states.
- Reduced motion mode.
- ARIA labels for icon buttons.
- Captions/transcript for voice notes.
## Future Capacitor Features
- Native push via FCM.
- Native share target.
- Background upload reliability.
- Local biometric unlock.
- Better notification actions: Approve/Deny from notification.
+250
View File
@@ -0,0 +1,250 @@
# Product Plan
## Working Name
Hermes Mobile
## One-line Pitch
A private Android-first app for talking to Hermes Agent with voice, files, live tool calls, approvals, cron controls, and completion notifications.
## Target User
Primary: Zeb / homelab power user who already runs Hermes Agent on a Linux machine and wants a better phone interface than Telegram.
Secondary later:
- Developers running Hermes locally/remotely
- Homelab admins
- People who want an agent command center rather than a chatbot
## Non-goals
- Not a generic OpenAI chat frontend.
- Not a multi-provider model playground.
- Not a public SaaS.
- Not a replacement for Hermes Agent internals.
- Not a Matrix/Discord/Telegram clone.
## Design Principles
1. Agent-first, not chat-first
- The UI should show what Hermes is doing, not just the final response.
- Tool calls are first-class objects.
2. Phone-native ergonomics
- Thumb-reachable controls.
- Large tap targets.
- Voice and upload actions always nearby.
- Background task status survives app close/reopen.
3. Private by default
- Self-hosted companion server.
- Local uploads.
- Optional LAN/Tailscale-only mode.
- No third-party notification requirement unless chosen.
4. Modular integrations
- Hermes CLI bridge now.
- Hermes API bridge where useful.
- Direct Python/event bridge later.
- Notification providers swappable.
5. Install should feel boring
- One command.
- Clear service status.
- Easy update/uninstall.
## Core Screens
### 1. Home / Ask
Purpose: send a prompt fast.
Features:
- Text box with multiline support
- Hold-to-record voice note
- Attach button
- Model/session selector compact chip
- “New task” vs “continue session” toggle
- Streaming answer
- Activity timeline under/alongside answer
- Completion toast/push
### 2. Activity
Purpose: see active and recent agent runs.
Features:
- Running tasks
- Tool-call cards
- Logs/output previews
- Generated files
- Error states
- Stop/cancel if supported
### 3. Sessions
Purpose: browse/resume history.
Features:
- Recent conversations
- Search
- Rename/pin/archive
- Session metadata: platform, started, last active, tool count, attachments
### 4. Cron
Purpose: manage scheduled Hermes tasks from phone.
Features:
- Job list grouped by active/paused/completed
- Run now
- Pause/resume/remove
- Edit prompt/schedule later
- Last output viewer
- Delivery target setting: in-app inbox, push, Matrix/Telegram/etc.
### 5. Files
Purpose: manage uploaded/generated artifacts.
Features:
- Upload inbox
- Generated media/files from responses
- Download/share
- Link file into a new prompt
- Retention controls later
### 6. Approvals
Purpose: approve dangerous actions safely.
Features:
- Pending approvals queue
- Command/action preview
- Risk label
- Approve once / deny
- Expiry countdown
- Audit log
### 7. Settings
Purpose: connect to companion server and configure app.
Features:
- Server URL
- Pairing code / passkey login
- Notification test
- Theme
- Hermes health
- Storage/retention
- STT/TTS preferences
## Feature Details
### Text Prompting
Modes:
- Quick ask: starts new task/session.
- Continue: appends to selected session.
- Background task: app can be closed, push when finished.
### Voice Notes
MVP flow:
1. Browser MediaRecorder captures audio.
2. Upload audio file to companion.
3. Companion either:
- passes audio path to Hermes gateway/STT path, or
- transcribes with configured local Whisper/faster-whisper, then sends text prompt.
4. UI shows transcript for confirmation if desired.
Later:
- Voice-to-voice replies using Hermes TTS.
- Streaming partial transcript.
### File Uploads
Supported MVP:
- png/jpg/webp/gif
- zip/tar/gz
- pdf
- txt/log/md/json/csv
- arbitrary binary as attachment
Prompt format to Hermes:
- Store file locally in companion upload store.
- Send prompt with absolute paths and metadata.
- Example: `User uploaded files: /var/lib/hermes-mobile/uploads/.../archive.zip`
### Tool Calls
Event model:
- `tool_call.started`
- `tool_call.delta`
- `tool_call.finished`
- `tool_call.failed`
Tool cards show:
- Icon/name
- Arguments summary
- Status
- Duration
- Expandable result
- Copy/open file actions where relevant
### Notifications
MVP notification events:
- task finished
- task failed
- approval required
- cron job completed/failed
Providers:
- Web Push for installed PWA
- ntfy fallback
- Gotify fallback
- Home Assistant persistent notification optional
### Cron Triggers
The app should not reimplement Hermes cron. It should wrap Hermes cronjob functions/CLI:
- list jobs
- run job
- pause/resume/remove
- create/edit later
### “Prompting itself” / Hermes test harness
Because Hermes Mobile runs next to Hermes Agent, the companion can test connectivity by sending a harmless prompt through:
- Hermes CLI: `hermes chat -q "ping"`
- API server: `/v1/chat/completions`
- Direct Python bridge later
The Settings health page should include a `Test Hermes` button.
## MVP Acceptance Criteria
- Install companion server on same Linux machine as Hermes.
- Open mobile PWA on Android, pair to companion.
- Send text prompt to Hermes and receive answer.
- Upload a PNG or ZIP and include it in prompt context.
- Record a voice note and turn it into a prompt.
- See at least coarse tool activity while Hermes runs.
- Receive push/ntfy notification when task completes.
- View cron jobs and trigger one manually.
## Risks
1. Hermes API server may not expose enough event detail.
- Mitigation: start with CLI/direct Python bridge and add event callbacks.
2. PWA push notification quirks.
- Mitigation: support ntfy/Gotify fallback.
3. Mobile browser audio quirks.
- Mitigation: test Android Chrome first; Capacitor later if needed.
4. Tool-call streaming requires Hermes integration changes.
- Mitigation: companion adapter interface; use available callbacks in AIAgent where possible.
+132
View File
@@ -0,0 +1,132 @@
# Roadmap
## Phase 0 — Planning Scaffold
Status: current.
Deliverables:
- Repo created
- Product plan
- Architecture plan
- Design direction
- Install strategy
- Project structure
## Phase 1 — Skeleton
Goal: empty but runnable monorepo.
Deliverables:
- pnpm workspace
- `apps/mobile` Vite React PWA
- `apps/companion` Fastify server
- shared TypeScript package for event/API schemas
- basic app shell with bottom nav
- `/api/health`
- local dev scripts
## Phase 2 — Basic Hermes Prompting
Goal: phone can send text prompt and receive final answer.
Deliverables:
- pairing/auth MVP
- Settings connect flow
- task creation endpoint
- Hermes CLI/API adapter MVP
- Ask screen sends prompt
- response display
- task history in SQLite
## Phase 3 — Realtime Activity + Tool Timeline
Goal: see Hermes work live.
Deliverables:
- event bus
- WebSocket/SSE stream
- tool timeline UI
- Python worker adapter using AIAgent callbacks if needed
- coarse/fine tool event mapping
## Phase 4 — Uploads + Voice
Goal: prompt with files and voice notes.
Deliverables:
- multipart upload endpoint
- upload tray UI
- image/archive/pdf metadata
- voice recorder UI
- local STT or Hermes STT bridge
- attach files to task prompt
## Phase 5 — Notifications
Goal: close phone and get told when done.
Deliverables:
- PWA service worker
- Web Push subscription management
- notification provider abstraction
- ntfy fallback
- completion/failure/approval notifications
## Phase 6 — Cron + Approvals
Goal: mobile control plane.
Deliverables:
- cron list/run/pause/resume/remove
- cron output viewer
- approval queue UI
- companion approval API
- Hermes approval integration strategy
## Phase 7 — Installer + Release
Goal: one-liner install.
Deliverables:
- production build
- install.sh
- systemd service
- update/uninstall modes
- release artifact
- pairing URL output
## Phase 8 — Android Polish
Goal: feels native.
Deliverables:
- refined Happy-inspired UI
- animations/microinteractions
- adaptive icons/splash
- share target
- haptics
- offline shell polish
- optional Capacitor wrapper evaluation
## Phase 9 — Deep Hermes Integration
Goal: full Hermes-native experience.
Deliverables:
- structured event stream from Hermes core or stable Python adapter
- richer session browsing
- generated file detection
- background process control
- subagent visualization
- memory/skills dashboards possibly
## Open Feature Ideas
- Quick action tiles: “Check homelab”, “Run DV status”, “Start coding task”.
- Voice-to-voice mode.
- Per-task model/toolset selection.
- “Watch mode” for long-running coding tasks.
- Home Assistant widgets/actions.
- Secure notification actions: approve/deny from notification.
- Android native share target.
- QR pairing from terminal output.
+119
View File
@@ -0,0 +1,119 @@
# Testing Strategy
## Philosophy
Hermes Mobile must be tested as an app plus a local service plus a Hermes integration. Generic unit tests are not enough; the important thing is whether a phone can send work to Hermes, see activity, close the app, and get notified when done.
## Test Layers
### 1. Unit Tests
Frontend:
- event reducer/idempotency
- upload tray state
- voice recorder state machine
- API client error handling
- cron job card states
Companion:
- config loading
- auth pairing tokens
- notification provider interface
- upload path normalization
- event bus fanout
- task state transitions
### 2. Integration Tests
Companion server with fake Hermes adapter:
- create task
- stream events
- upload file + attach to task
- completion notification queued
- cron list/run mocks
Hermes adapter contract tests:
- CLI adapter health
- API adapter health
- Python worker JSONL protocol
### 3. End-to-End Tests
Use Playwright against local dev server.
Scenarios:
- first-time pairing
- send text prompt
- upload PNG and send prompt
- record/upload fake audio blob
- watch tool timeline update
- cron run button
- notification permission flow mocked
### 4. Real Hermes Smoke Tests
On a machine with Hermes installed:
```bash
hermes chat -q "Reply with exactly: hermes-mobile-ok"
```
Companion health test should do the equivalent and verify response.
More advanced smoke:
- Ask Hermes to run a harmless terminal command: `pwd`.
- Verify a tool-call event appears.
- Upload a small text file and ask Hermes to summarize it.
## Manual Android QA
Test on Android Chrome installed PWA:
- Install app to home screen.
- Launch standalone mode.
- Keyboard does not break composer layout.
- Back button closes sheets/modals first.
- Voice recording works.
- File picker works for image/zip/pdf.
- Push notification arrives after app is backgrounded.
- App reconnects to running task after being killed/reopened.
## Compatibility Targets
- Android Chrome latest
- Android Firefox optional
- Desktop Chrome for dev
- iOS Safari later, not MVP priority
## Observability for Testing
Companion should expose:
```http
GET /api/health
GET /api/debug/events/recent
GET /api/debug/config/redacted
POST /api/hermes/test
POST /api/notifications/test
```
Only expose debug endpoints in dev mode or authenticated admin mode.
## CI Plan
Later:
- pnpm lint
- pnpm typecheck
- pnpm test
- pnpm e2e with fake adapter
- build release artifact
## Dogfooding Plan
The app should be used to prompt Hermes about its own repo.
Example dogfood tasks:
- “Run the test suite for Hermes Mobile and summarize failures.”
- “Inspect the companion logs and fix the upload bug.”
- “Create a cron job that reminds me if companion is down.”
This is important because Hermes is both the target agent and the tool used to build the app.
Executable
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
# Placeholder installer. Real implementation will arrive after the skeleton server exists.
# Target UX:
# curl -fsSL https://hermes-mobile.molberg.cloud/install.sh | bash
echo "Hermes Mobile installer placeholder"
echo "This repo is currently a planning scaffold; no companion server is installed yet."
echo
if command -v hermes >/dev/null 2>&1; then
echo "Detected Hermes CLI: $(command -v hermes)"
else
echo "Hermes CLI not found in PATH."
fi
echo "Planned install docs: docs/INSTALL.md"