Skip to content

feat(app): surface Hermes agent in client (follow-up to #1322) - #1329

Closed
chphch wants to merge 3 commits into
slopus:mainfrom
chphch:feat/1129-hermes-app-surfacing
Closed

feat(app): surface Hermes agent in client (follow-up to #1322)#1329
chphch wants to merge 3 commits into
slopus:mainfrom
chphch:feat/1129-hermes-app-surfacing

Conversation

@chphch

@chphch chphch commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

What changes

  • Agent picker + icon (app/(app)/new/index.tsx, app/(app)/dev/session-composer.tsx) — new icon-hermes{,@2x,@3x}.png purple "H" monogram placeholder until Nous Research ships an official asset.
  • Agent Defaults screen (app/(app)/settings/agents.tsx) — Hermes section with permission/model rows.
  • Flavor → label (app/(app)/session/[id]/info.tsx) → 'Hermes'.
  • Type unions: NewSessionAgentType, ops.startSession agent option, AgentInput.agentType, modelModeOptions.AgentFlavor, raw provider enum in sync/typesRaw.ts.
  • sync/agentDefaults:
    • agentKeys includes 'hermes'.
    • AgentDefaultOverridesSchema.hermes slot added (optional, like other agents).
    • codeAgentDefaults.hermes set to neutral defaults — model picker is populated at runtime from ACP available_models, so the static fallback intentionally stays empty.
  • components/modelModeOptions:
    • getHermesPermissionModes exposes only default (Hermes ACP currently doesn't advertise operating modes — hooks_auto_accept is CLI-side).
    • getHermesModelModes ships a sentinel default; the real list comes from metadata.models populated by the ACP available_models event at session start.
  • sync/storageTypes.cliAvailability.hermes: z.boolean().optional() for backward-compat with daemons that predate Hermes.
  • i18n: 11 locale tables (_default.ts + 10 translations: en/zh-Hans/es/ru/pl/zh-Hant/pt/it/ja/ca) gain the agents.hermes = 'Hermes' row.

Why split it off #1322

#1322 has been kept tight to match the Kimi/#1276 precedent (CLI-only, no app surface). The Devin PR #1317 took the opposite approach (CLI + app in one PR) and is harder to review. Keeping the app surfacing as its own PR makes #1322 reviewable alongside #1276 and isolates the icon/i18n churn here.

Proof

Hermes surfaced in the app — agent picker + Agent Defaults rows, and the new-session picker:

Hermes in Agent Defaults

Hermes in new-session picker

Test plan

  • pnpm -r typecheck — 6/6 workspace packages clean.
  • pnpm --filter happy-app test — 487 passed / 57 skipped (no regressions vs main).
  • Manual smoke from worktree: happy hermes boots ACP, server reports 26 OpenRouter models + 9 slash commands. With this PR applied, the app surfaces Hermes in the agent picker and resolves the flavor label correctly.

Notes for reviewers

  • The placeholder icons are a 128/256/384 px purple "H" monogram. Happy to swap them for an official Nous Research asset before merge — just say the word and I'll respin.
  • Backward-compat: cliAvailability.hermes is .optional() so older daemons keep validating. No wire-format break.
  • No changes to CLI / server / shared happy-wire package — purely app-side.

🤖 Generated with Claude Code
via Happy

@chphch

chphch commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

Proof — Hermes surfaces in Settings → Agent Defaults

Captured locally from this branch's worktree (feat/1129-hermes-app-surfacing rebased on c08153ce from #1322), via standalone happy-server (PGlite, port 3056) + Expo web (port 8089), authenticated through the dev-token URL bypass and driven headlessly with Playwright. End-to-end run ≈ 90s.

Hermes in Agent Defaults

The animation loops between two states of app/(app)/settings/agents.tsx:

  1. Collapsed list of all 5 agentsCLAUDE CODE, CODEX, GEMINI, HERMES (new), OPENCLAW. The screen iterates agentKeys from sync/agentDefaults.ts, so the new 'hermes' entry shows up unconditionally — independent of cliAvailability (the agent-picker filter for the new-session flow uses that, and only renders Hermes once the daemon also reports hermes: true).
  2. Hermes Permission row expanded — shows the single Use code default row + default permissions option, matching getHermesPermissionModes() in components/modelModeOptions.ts. That's intentional — Hermes ACP currently exposes only the default surface (hooks_auto_accept is CLI-side); operating modes will populate from metadata.operatingModes once the agent advertises them.

Note that this PR does not surface Hermes in the new-session agent picker on its own — that path also needs the daemon to be running with cliAvailability.hermes: true, which is what #1322 delivers. Stacked together (c08153ce + e7e692f9), the full flow works: install hermes-agent[acp], run happy hermes, and the agent shows up in both the picker and the defaults screen.

CI (typecheck + happy-app tests) is green from the previous PR commit — this commit only adds app-side surfacing.

@chphch

chphch commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

Proof — Hermes also surfaces in the new-session agent picker (with #1322 amendment)

Follow-up to the previous proof comment — that one only covered the Settings → Agent Defaults path. While capturing the live picker flow, I discovered a gap: even with the daemon paired and cliAvailability populated, Hermes was being filtered out of the new-session picker because packages/happy-cli/src/utils/detectCLI.ts did not detect hermes. The picker filter at app/(app)/new/index.tsx:677 is ALL_AGENTS.filter(a => availability[a.key] === true), so an undefined availability.hermes drops it from the list whenever a machine is selected.

This trace shows the new-session picker cycling through all five agents — claude codecodexopenclawgeminihermes (held one extra beat) — proving Hermes is now reachable end-to-end:

Hermes in new-session picker

Captured locally from feat/1129-hermes-app-surfacing rebased on c08153ce (#1322) plus the detectCLI.ts patch below, via standalone happy-server (PGlite, port 3057) + Expo web (port 8091) + a live happy-cli daemon paired to the same dev_token account. End-to-end run ≈ 90s.

Required amendment to #1322

The previous comment claimed "#1322 delivers cliAvailability.hermes: true" — that turned out to be inaccurate. The PR added the ACP factory wiring but missed the discovery side. To make the full flow work, three CLI files need to be updated on top of c08153ce:

  • packages/happy-cli/src/utils/detectCLI.ts — add hermes: boolean to CLIAvailability and detect it in both detectPosix() and detectWindows() via commandExists('hermes') / checkCommand('hermes').
  • packages/happy-cli/src/api/types.ts — add hermes: z.boolean().optional() to MachineMetadataSchema.cliAvailability (optional for backward compat with older CLIs).
  • packages/happy-cli/src/api/apiMachine.ts — extend the cliAvailabilityChanged check to include prev.hermes !== newAvailability.hermes so the re-detect loop pushes the change.

I'll open a small follow-up PR against the same branch (or amend #1322 if the maintainer prefers) with these three changes. The app-side surfacing in this PR (#1329) is correct as-is — it doesn't depend on detection; once detection lands, Hermes appears in the picker without further app changes.

Verification artifacts

  • Cycle frames: claude code (frame 0) → codex (1) → openclaw (2) → gemini (3) → hermes (4, purple "H" monogram visible) → claude code (5, cycle wraps)
  • hermes resolves because the patched daemon pushed cliAvailability.hermes: true to the server, and the app's picker filter at new/index.tsx:677 now sees it as truthy.

chphch added a commit to chphch/happy that referenced this pull request May 25, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@chphch
chphch force-pushed the feat/1129-hermes-app-surfacing branch from e7e692f to 9866afd Compare May 25, 2026 11:28
chphch added a commit to chphch/happy that referenced this pull request May 25, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
chphch added a commit to chphch/happy that referenced this pull request Jun 8, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
chphch added a commit to chphch/happy that referenced this pull request Jun 8, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
chphch added a commit to chphch/happy that referenced this pull request Jun 8, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
chphch added a commit to chphch/happy that referenced this pull request Jun 9, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
chphch added a commit to chphch/happy that referenced this pull request Jun 11, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@openclaw-bc

Copy link
Copy Markdown

Follow-up findings from local testing of the Hermes app surfacing work:

  1. App-created Hermes sessions do not spawn through the daemon path

happy hermes works from the CLI, but creating a Hermes session from the app goes through the daemon /spawn-session path. That path currently does not fully accept/dispatch agent: "hermes", so the app selection can no-op or fail before launching the Hermes runner.

Suggested areas to update:

  • /spawn-session schema should accept hermes
  • SpawnSessionOptions.agent should include hermes
  • daemon regular spawn should map hermes to the happy hermes command
  • daemon tmux spawn should also map hermes
  • add coverage for both regular and tmux daemon Hermes spawns
  1. Hermes permission mode fallback should match Hermes ACP session mode ids

The Hermes ACP adapter exposes session modes as default, accept_edits, and dont_ask. The app fallback should use those exact ids so an initial message can request the intended mode before runtime metadata is available.

  1. Hermes should prefer runtime ACP metadata when available

Hermes is ACP-based and can report operating modes through metadata. The app should prefer metadata.operatingModes for existing sessions, and only fall back to the hardcoded Hermes list when metadata is absent. Otherwise future/current Hermes runtime modes can be hidden by stale hardcoded values.

  1. Hermes should not expose Happy worktree controls

Hermes behaves more like OpenClaw here: the Happy worktree picker/creation flow is not supported for Hermes sessions. The new session UI should hide/reset worktree selection for Hermes.

  1. Permission display should understand Hermes snake_case keys

The permission style/badge mapping should recognize accept_edits and dont_ask, otherwise Hermes modes render as generic/default permission states.

chphch added a commit to chphch/happy that referenced this pull request Jul 19, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
chphch added a commit to chphch/happy that referenced this pull request Jul 19, 2026
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@chphch
chphch force-pushed the feat/1129-hermes-app-surfacing branch from 9866afd to f103473 Compare July 19, 2026 12:36
@hansai-art

Copy link
Copy Markdown

Real-world confirmation of the symptom this PR describes, from an end user rather than a reviewer. I have no competing PR and no stake in the diff.

Your PR body says it exactly:

the agent picker, icons, flavor labels, defaults and option pickers are all hardcoded to the prior agent set, so a CLI advertising hermes: true is invisible to the user

That is precisely what I hit today, and I can add the other half of the evidence: the CLI side already works, so this PR is the only thing standing between users and a working Hermes flow.

CLI side: works unmodified, today

happy acp -- hermes acp

happy 1.1.9-beta.0, hermes v0.17.0 (2026.6.19), macOS 15 arm64. From ~/.happy/logs/:

[AcpBackend] Initializing connection (timeout: 60000ms)...
acp_adapter.server: ACP client connected
acp_adapter.server: Initialize from happy-cli (protocol v1)
[AcpBackend] Initialize completed
acp_adapter.session: Created ACP session 6584d2cc-...
acp_adapter.server: refreshed tool surface after ACP MCP registration (26 tools)

26 tools, the happy MCP server registers back into Hermes (mcp_happy_change_title), and Hermes advertises a real command surface via available_commands_update: help, model, tools, context, reset, compact, steer, queue, version. Protocol v1 negotiates despite Hermes pinning the Python agent-client-protocol==0.9.0 against happy's npm SDK. I posted the fuller trace on #1322.

App side: not reachable, three attempts

The agent picker on Android offers exactly four entries: claude code, codex, openclaw, gemini (deprecated). No Hermes, as you said.

What I had not expected is that there is also no way to reach the already-running CLI-started session from the app. Three attempts, each ended with the daemon spawning a new Claude Code session instead. From ~/.happy/sessions.json:

cms1xodbwz4j7wc0unvvcolaa   flavor='acp'      startedBy=terminal   ← the hermes session
cms1xps6jhd4awc0u96wnw3zi   flavor='claude'   startedBy=daemon     ← attempt 1
cms1y5hnchswoyc0ty3hbsu68   flavor='claude'   startedBy=daemon     ← attempt 2

The flavor='acp' session's log shows zero prompts across its whole lifetime — it was never spoken to. Asking the session that did respond "are you Hermes or Claude Code?" got: "I am Claude Code (model: claude-sonnet-4-6), running in Happy's Agent SDK."

So from a user's point of view the CLI bridge is complete and the app is the entire remaining gap. That is a stronger argument for this PR than "it would be nice to have Hermes in the list."

One adjacent gap that this PR does not close

While tracing the above I found that agentKeys in sync/agentDefaults.ts omits 'acp' and 'opencode', so normalizeAgentKey() collapses both to 'claude' and generic ACP sessions get seeded from Claude's defaults table (bypassPermissions / opus). Filed separately as #1583 rather than pushed at this PR, because it survives this PR: adding a named hermes key does not help happy acp -- <anything else>. Your "neutral defaults, model picker is populated at runtime from ACP available_models" reasoning is the precedent I pointed at there.

Nothing here needs action from you. Posting it so there is user-side evidence attached to the PR, since it has been open since 2026-05-25 with no maintainer comment and the CLI half (#1322) is mergeable: clean.

chphch and others added 3 commits July 27, 2026 07:38
Adds first-class support for NousResearch/hermes-agent via the Agent
Client Protocol, mirroring the pattern in slopus#1276 (Kimi CLI). Hermes ships
ACP out of the box through `hermes acp` (installed via
`pip install 'hermes-agent[acp]'`).

Closes slopus#1129.

- New factory `agent/factories/hermes.ts` (+ unit tests) builds an
  AcpBackend with `command='hermes'`, `args=['acp']`.
- New `hermes/runHermes.ts` thin wrapper around the generic ACP runner,
  with a `hermesCliAvailable()` pre-flight that exits with a `pip install`
  hint when `hermes` is not on PATH.
- The pre-check fires in the `happy hermes` subcommand BEFORE
  auth/daemon setup, so first-time users without Hermes installed are
  not dragged through the QR flow only to hit an install error.
- Wires `hermes` into KNOWN_ACP_AGENTS, resolveSessionFlavor, AgentId,
  BackendFlavor, and the factories barrel.
- README + `--help` list `happy hermes` alongside the other agents.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
The previous commit added the Hermes ACP factory and wiring but missed
the discovery side, so the daemon never advertised `cliAvailability.hermes`
to the server. The app-side picker in slopus#1329 filters agents by
`cliAvailability[a.key] === true`, which silently dropped Hermes whenever
a machine was selected.

- detectCLI.ts: add `hermes: boolean` to `CLIAvailability`, detect via
  `commandExists('hermes')` (POSIX) and `checkCommand('hermes')` (Windows).
- api/types.ts: add `hermes: z.boolean().optional()` to
  `MachineMetadataSchema.cliAvailability` — optional so older CLIs without
  this patch still validate.
- api/apiMachine.ts: include `prev.hermes !== newAvailability.hermes` in
  the `cliAvailabilityChanged` check so the re-detect loop pushes the
  change when `hermes` is installed mid-session.

Verified end-to-end with slopus#1329 stacked on top: live daemon + Expo web,
new-session agent picker cycles claude code → codex → openclaw → gemini →
hermes. Proof GIF posted on slopus#1329.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
PR slopus#1322 wires Hermes Agent into the CLI (mirrors the Kimi/slopus#1276
self-hosted-CLI pattern). On its own that exposes no entry point in the
mobile/web app: the agent picker, icons, flavor labels, defaults and
permission/model option pickers are all hardcoded to the prior agent
set, so a CLI with hermes available is invisible to the user.

This change threads `'hermes'` through every app-side type/list:

- Agent picker + icon (`new/index.tsx`, dev composer) — new purple "H"
  monogram placeholder until Nous Research ships an official asset.
- Settings → Agent Defaults (`settings/agents.tsx`) — Hermes group with
  permission/model rows.
- Flavor → label resolver (`session/[id]/info.tsx`) → 'Hermes'.
- Type unions: `NewSessionAgentType`, `ops.startSession` agent option,
  `AgentInput` agentType, `modelModeOptions` AgentFlavor, raw session
  provider enum.
- `agentDefaults`: `agentKeys` includes hermes; per-agent override slot
  added; static `codeAgentDefaults.hermes` set to neutral defaults
  (model picker is populated at runtime from ACP `available_models`,
  so the static fallback intentionally stays empty).
- `modelModeOptions`: `getHermesPermissionModes` exposes only
  `default` (Hermes ACP currently doesn't advertise operating modes —
  `hooks_auto_accept` is CLI-side). `getHermesModelModes` ships a
  sentinel `default`; the real list comes from
  `metadata.models` populated by the ACP `available_models` event.
- `storageTypes.cliAvailability.hermes`: optional boolean for
  backward-compat with CLIs that predate Hermes — older daemons keep
  validating without breakage.
- 11 locale tables (`_default.ts` + 10 translations) gain the
  `agents.hermes = 'Hermes'` row.

Verification:
- `pnpm -r typecheck`: 6/6 packages clean.
- `pnpm --filter happy-app test`: 487 passed, 57 skipped (pre-existing).
- Manual smoke: `happy hermes` from worktree boots ACP, server reports
  26 OpenRouter models + 9 slash commands; with this PR the app surfaces
  Hermes in the agent picker and resolves its flavor label correctly.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@chphch
chphch force-pushed the feat/1129-hermes-app-surfacing branch from f103473 to 9abd36c Compare July 26, 2026 22:39
@chphch

chphch commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (3c0155c2) — was conflicting. Force-pushed, same three commits (the first two are #1322's, which this follows up on).

Single conflict, in modelModeOptions.ts: main added the Rig metadata branch to getAvailablePermissionModes right where this PR adds hermes to the flavor list. Both kept — the Rig branch runs first, then hermes joins the hardcoded-modes list.

pnpm typecheck clean in both packages; app suite 779/779, CLI suite 784/784.

@chphch

chphch commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Closing this along with #1322 — I've stopped focusing on Hermes, and this PR in particular shouldn't stay open when there's a substantive review on it I never answered.

@openclaw-bc — you left five concrete findings back on 2026-07-01 and I never actioned them. That's on me. Rather than let that work evaporate, here it is as the handover list; I re-checked the branch today and none of the five are addressed, so this is an accurate map of the remaining work:

  1. App-created Hermes sessions don't spawn. The daemon /spawn-session path doesn't accept/dispatch agent: "hermes" — no hermes string in packages/happy-cli/src/daemon at all. Needs: the schema, SpawnSessionOptions.agent, and both the regular and tmux spawn mappings to happy hermes, plus coverage for both. This is the one that matters most — without it the picker entry this PR adds leads nowhere from the app.
  2. Permission-mode fallback ids. Should be Hermes ACP's actual default / accept_edits / dont_ask; neither string exists anywhere in the branch today.
  3. Prefer runtime metadata.operatingModes over the hardcoded list. Still inverted: modelModeOptions.ts:314 has hermes inside the hardcoded-modes branch, so reported runtime modes are ignored. (I put it there resolving a rebase conflict today, which was mechanically right and semantically exactly your point.)
  4. Hide worktree controls for Hermes — Hermes behaves like OpenClaw here; not done.
  5. Permission badge mapping should understand the snake_case keys — not done.

@hansai-art — thank you for the end-user side (comment); "the CLI already works, so this PR is the only thing between users and a working Hermes flow" is exactly the right framing, and it's still true. Item 1 above is the concrete reason the app half doesn't get there yet.

Branch stays on my fork (chphch:feat/1129-hermes-app-surfacing), rebased onto current main and CI-green as of today:

gh pr checkout 1329

Please do pick this up if you want it — reopen under your own name, no permission needed, no attribution expected. The five items above plus a working daemon spawn path is a complete, well-scoped piece of work for whoever wants Hermes in the app. If a maintainer prefers it stay mine, ping me and I'll reopen.

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.

3 participants