fix(gateway): reach skills/memory/config on the Agent dashboard backend (closes #23, completes #17) - #25
Closed
lightcloud00 wants to merge 2 commits into
Closed
Conversation
…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>
|
@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. |
This was referenced Aug 16, 2026
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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.tslist parsing. Neither file set overlaps with the call sites here, and this branch deliberately leaveslistSessions/getMessagesand 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/memoryand/api/configoff the gateway's embeddedapi_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 singleHERMES_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_TOKENalso accepted), sent asX-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/configalso 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-pathreturns 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.parseat 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.tspins this.Dashboard 401s set a separate
dashboardUnauthorizedflag — a bad dashboard token must never mark the gateway unauthorized.3. Panels that never needed the gateway at all
Three of the reported symptoms turned out not to be about the route split:
configcapability 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-getand/api/config-patch— routes 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/serversread the gateway's/api/configbehind the same gate whilePUTwrote the localconfig.yaml. Reads and writes now agree./api/memory503'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 onlyMEMORY.md,memory/andmemories/, depth-capped. 90s+ → 34ms. The Memory panel was not unavailable; it was hanging.4. Four unauthenticated call sites #19 does not cover
server/openai-compat-api.tsBEARER_TOKEN = process.env.HERMES_API_TOKENshadow that bypassed key discovery entirely — portable-mode chat 401'd whenever the key lived only in~/.hermes/.envscreens/settings/providers-screen.tsx/v1/modelsdirectly, cross-origin with no credentials, and importinggetConfig/patchConfigfrom the server client.vite.config.tsinlinesHERMES_API_URLinto the client bundle, which is what made it look like it should workroutes/api/skills.tsHERMES_API_URLconst, bypassing both the discovered key and the 8643 auto-detectroutes/api/skills/install.tsPlus one #18-class straggler:
routes/api/skills/hub-search.tsread only.skillsfrom/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.tsasserts what this branch owns: no shadow reads ofHERMES_API_TOKEN, split-capability fetches use the target's headers, and everygetCapabilityTarget()caller supplies a credentialed fallback.Two broader assertions are left commented out with instructions, because they currently fail on
mainfor 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
vitest: 190 → 197 passing, 18 files, none broken.eslint: 622 errors / 88 warnings — identical tomain. No new errors or warnings.tsc --noEmit: no new errors vsmain. (maincurrently has 4: two are Missing katex/remark-math/rehype-katex dependencies break studio build (500 on startup) #14's missing katex deps, one is listSessions()/getMessages() read .items but gateway returns .data (OpenAI list format) → empty session/message history #18'ssessions.ts.dataaccess, one is a pre-existingversion-compatibility.tsoverload.)routeTree.gen.tsregenerated for the two new config routes.Note for maintainers:
vite buildcurrently fails onmain—Rollup 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 withHERMES_DASHBOARD_URLunset 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