Skip to content

fix(gateway): reach skills/memory/config on the Agent dashboard backend (closes #23, completes #17) - #25

Closed
lightcloud00 wants to merge 2 commits into
JPeetz:mainfrom
lightcloud00:fix/issue-23-dashboard-split-c35
Closed

fix(gateway): reach skills/memory/config on the Agent dashboard backend (closes #23, completes #17)#25
lightcloud00 wants to merge 2 commits into
JPeetz:mainfrom
lightcloud00:fix/issue-23-dashboard-split-c35

Conversation

@lightcloud00

Copy link
Copy Markdown

What

Makes Skills, Memory and Config work against Hermes Agent v0.19+, where those routes no longer live on the gateway. Closes #23, and closes four unauthenticated gateway call sites from #17 that PR #19 does not cover.

This complements #19 and #20 rather than competing with them. I checked both diffs: #19 fixes the eight route files issue #17 names, #20 fixes hermes-api.ts/context-usage.ts list parsing. Neither file set overlaps with the call sites here, and this branch deliberately leaves listSessions/getMessages and those eight routes untouched so both PRs still apply cleanly.

1. The actual cause of #23

The reporter's diagnosis is correct. Agent v0.19 moved /api/skills, /api/memory and /api/config off the gateway's embedded api_server (8642) onto the agent's own dashboard backend (hermes_cli/web_server.py, separate process, default 9119, its own session auth). Studio probes all three against the single HERMES_API_URL, gets 404, and marks them missing — so on every current agent the panels report "unavailable" and the startup log tells the user to upgrade an agent that is already newer than the advice.

Optional second backend. HERMES_DASHBOARD_URL + HERMES_DASHBOARD_SESSION_TOKEN (HERMES_DASHBOARD_TOKEN also accepted), sent as X-Hermes-Session-Token. The three capabilities are probed on the dashboard first and fall back to the gateway for pre-v0.19 agents; getCapabilityTarget() then routes each data call to whichever server answered, with that server's credentials.

Both variables are server-side only and never inlined into the client bundle. Neither is required — with them unset nothing contacts the dashboard and every panel keeps working from local ~/.hermes. skills/memory/config also no longer gate the "enhanced" tier or the upgrade warning, because their absence from the gateway is now the normal state rather than a version-lag signal.

2. A dashboard probe cannot go by status code

Worth flagging for review, because it is not obvious: the dashboard serves an SPA whose catch-all answers any unknown path with 200 text/html. Measured against a live v0.19 dashboard — /definitely-not-a-real-path returns 200 HTML while the real /api/* routes return JSON.

A status-only check therefore reports every capability as present on a dashboard whose routes have moved or whose URL is mistyped, and then feeds HTML to JSON.parse at data-fetch time. Requiring a JSON content type fails safe in the right direction: a real endpoint answering in some other content type is merely missed, and all three degrade to their local fallbacks. src/test/dashboard-probe.test.ts pins this.

Dashboard 401s set a separate dashboardUnauthorized flag — a bad dashboard token must never mark the gateway unauthorized.

The dashboard token is ephemeral by default: the dashboard generates a random one per process unless started with HERMES_DASHBOARD_SESSION_TOKEN set. Documented in docs/HERMES_AGENT_COMPATIBILITY.md, since a hardcoded value otherwise goes stale on the next agent restart.

3. Panels that never needed the gateway at all

Three of the reported symptoms turned out not to be about the route split:

  • Settings/Providers was gated on the config capability while being 100% filesystem-backed. A probe of an endpoint the panel does not use was blanking it. Gate removed. It also fetched /api/config-get and /api/config-patchroutes that have never existed in this repo, so they 404'd unconditionally for anyone who got past the gate, on every agent version. Added, local-filesystem, behind the session check.
  • /api/mcp/servers read the gateway's /api/config behind the same gate while PUT wrote the local config.yaml. Reads and writes now agree.
  • /api/memory 503'd. It now degrades to the same local listing the Memory screen uses — but that listing had to be fixed first: it walked the entire Hermes home to find markdown in at most three places, following symlinked skill directories, synchronously. On the machine this was measured on that is ~400k files, and because it is synchronous it took every queued request down with it. Now walks only MEMORY.md, memory/ and memories/, depth-capped. 90s+ → 34ms. The Memory panel was not unavailable; it was hanging.

4. Four unauthenticated call sites #19 does not cover

Pri Site Defect
P0 server/openai-compat-api.ts kept a private BEARER_TOKEN = process.env.HERMES_API_TOKEN shadow that bypassed key discovery entirely — portable-mode chat 401'd whenever the key lived only in ~/.hermes/.env
P1 screens/settings/providers-screen.tsx a client component fetching the gateway's /v1/models directly, cross-origin with no credentials, and importing getConfig/patchConfig from the server client. vite.config.ts inlines HERMES_API_URL into the client bundle, which is what made it look like it should work
P1 routes/api/skills.ts local HERMES_API_URL const, bypassing both the discovered key and the 8643 auto-detect
P2 routes/api/skills/install.ts POSTed unauthenticated and fell silently through to the clawhub CLI, so a native install looked like "clawhub not installed"

Plus one #18-class straggler: routes/api/skills/hub-search.ts read only .skills from /api/skills, never .data/.items — so against a gateway answering in the OpenAI list shape the skills marketplace showed every skill as not-installed.

5. Guard

src/test/gateway-auth-coverage.test.ts asserts what this branch owns: no shadow reads of HERMES_API_TOKEN, split-capability fetches use the target's headers, and every getCapabilityTarget() caller supplies a credentialed fallback.

Two broader assertions are left commented out with instructions, because they currently fail on main for call sites that belong to #19 — asserting them here would duplicate that PR's work and make this branch depend on it. They should be enabled once #19 lands; the comment says exactly that.

Verification

Note for maintainers: vite build currently fails on mainRollup failed to resolve import "rehype-katex" — which is #14. #21 fixes that; it is unrelated to this branch.

Reviewing this

The riskiest change is gateway-capabilities.ts. The behaviour with HERMES_DASHBOARD_URL unset is intended to be byte-for-byte the old behaviour except that skills/memory/config no longer gate the enhanced tier — worth confirming that reading, since that is the compatibility promise for everyone not running v0.19+.

Happy to split this into smaller PRs if that is easier to review, and happy to rebase after #19/#20 land.

Closes #23.

🤖 Generated with Claude Code

lightcloud00 and others added 2 commits August 15, 2026 21:41
…tz#23)

Hermes Agent v0.19 moved /api/skills, /api/memory and /api/config off the
gateway's embedded api_server (8642) onto the agent's own web dashboard
backend (hermes_cli/web_server.py, separate process, default 9119, its own
session auth). Studio probes all three against the single HERMES_API_URL, gets
404, and marks them missing — so on every current agent the Skills, Memory and
Config panels report "unavailable" and the startup log tells users to upgrade
an agent that is already newer than the advice.

Optional second backend. HERMES_DASHBOARD_URL + HERMES_DASHBOARD_SESSION_TOKEN
(HERMES_DASHBOARD_TOKEN also accepted), sent as X-Hermes-Session-Token. The
three capabilities are probed on the dashboard first and fall back to the
gateway for pre-v0.19 agents; getCapabilityTarget() then routes each data call
to whichever server answered, with that server's credentials. Both vars are
server-side only. With neither set, nothing contacts the dashboard and every
panel keeps working from local ~/.hermes.

A dashboard probe cannot go by status code. The dashboard serves an SPA whose
catch-all answers ANY unknown path with 200 text/html — measured against a
live v0.19 dashboard, where /definitely-not-a-real-path returns 200 HTML while
the real /api/* routes return JSON. Status-only checks therefore report every
capability present on a dashboard whose routes moved or whose URL is mistyped,
then feed HTML to JSON.parse. Requiring a JSON content type fails safe.
Dashboard 401s set a separate dashboardUnauthorized flag, so a bad dashboard
token never marks the gateway itself unauthorized.

skills/memory/config no longer gate the "enhanced" tier or the upgrade
warning: their absence from the gateway is the normal state now, not a
version-lag signal.

Panels that never needed the gateway at all:

- Settings/Providers was gated on the `config` capability while being 100%
  filesystem-backed, so a probe of an endpoint it does not use blanked it.
  Gate removed. It also fetched /api/config-get and /api/config-patch —
  routes that have never existed in this repo and 404'd unconditionally.
  Added, local-filesystem, behind Studio's session check.
- /api/mcp/servers read the gateway's /api/config behind the same gate while
  PUT wrote the local config.yaml. Reads and writes now agree.
- /api/memory 503'd; it now degrades to the same local listing the Memory
  screen uses. That listing needed fixing first: it walked the entire Hermes
  home (~400k files on the machine this was measured on, following symlinked
  skill dirs) to find markdown in at most three places, synchronously, taking
  every queued request with it. Now walks only MEMORY.md, memory/ and
  memories/, depth-capped. 90s+ -> 34ms. The Memory panel was not
  unavailable, it was hanging.

Also closes four unauthenticated gateway call sites that issue JPeetz#17 does not
name and PR JPeetz#19 does not cover (verified against its diff):

- openai-compat-api.ts kept a private
  `BEARER_TOKEN = process.env.HERMES_API_TOKEN` shadow that bypassed key
  discovery entirely, so portable-mode chat 401'd whenever the key lived only
  in ~/.hermes/.env.
- providers-screen.tsx is a client component that fetched the gateway's
  /v1/models directly — cross-origin, no credentials — and imported
  getConfig/patchConfig from the server client. Both now go through Studio's
  own routes.
- skills.ts kept a local HERMES_API_URL const, bypassing the discovered key
  and the 8643 auto-detect.
- skills/install.ts POSTed unauthenticated and fell silently through to the
  clawhub CLI, so a native install looked like "clawhub not installed".

And one JPeetz#18-class straggler: skills/hub-search.ts read only `.skills` from
/api/skills, never `.data`/`.items`, so against a gateway answering in the
OpenAI list shape the marketplace showed every skill as not-installed.

gateway-auth-coverage.test.ts asserts what this branch owns: no shadow reads
of HERMES_API_TOKEN, split-capability fetches use the target's headers, and
every getCapabilityTarget() caller supplies a credentialed fallback. The two
broader assertions are left commented with instructions to enable them once
PR JPeetz#19 lands — asserting them now would duplicate that PR's work.

Closes JPeetz#23.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 16, 2026

Copy link
Copy Markdown

@lightcloud00 is attempting to deploy a commit to the Joerg Peetz's projects Team on Vercel.

A member of the Team first needs to authorize it.

@lightcloud00

Copy link
Copy Markdown
Author

Closing this — we're continuing this work on our own repo (lightcloud00/Hermes-Studio) rather than upstream. Nothing here is abandoned; the branch and all its commits are preserved there. Thanks for the project, and apologies for the churn in your PR queue.

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.

Skills / Memory / Config panels always show unavailable against current Hermes Agent (v0.19.0+) — routes moved to a separate server

1 participant