fix(hermes): stop advertising unsupported instructions bundles (#3908)

## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies.
> - Local adapter capability flags decide which configuration surfaces
the UI and server expose for each adapter.
> - `hermes_local` currently advertises managed instructions bundle
support, so Paperclip exposes the AGENTS.md bundle flow for Hermes
agents.
> - The bundled `hermes-paperclip-adapter` only consumes
`promptTemplate` at runtime and does not read `instructionsFilePath`, so
that advertised bundle path silently does nothing.
> - Issue #3833 reports exactly that mismatch: users configure AGENTS.md
instructions, but Hermes only receives the built-in heartbeat prompt.
> - This pull request stops advertising managed instructions bundles for
`hermes_local` until the adapter actually consumes bundle files at
runtime.

## What Changed

- Changed the built-in `hermes_local` server adapter registration to
report `supportsInstructionsBundle: false`.
- Updated the UI's synchronous built-in capability fallback so Hermes no
longer shows the managed instructions bundle affordance on first render.
- Added regression coverage in
`server/src/__tests__/adapter-routes.test.ts` to assert that
`hermes_local` still reports skills + local JWT support, but not
instructions bundle support.

## Verification

- `git diff --check`
- `node --experimental-strip-types --input-type=module -e "import {
findActiveServerAdapter } from './server/src/adapters/index.ts'; const
adapter = findActiveServerAdapter('hermes_local');
console.log(JSON.stringify({ type: adapter?.type,
supportsInstructionsBundle: adapter?.supportsInstructionsBundle,
supportsLocalAgentJwt: adapter?.supportsLocalAgentJwt, supportsSkills:
Boolean(adapter?.listSkills || adapter?.syncSkills) }));"`
- Observed
`{"type":"hermes_local","supportsInstructionsBundle":false,"supportsLocalAgentJwt":true,"supportsSkills":true}`
- Added adapter-routes regression assertions for the Hermes capability
contract; CI should validate the full route path in a clean workspace.

## Risks

- Low risk: this only changes the advertised capability surface for
`hermes_local`.
- Behavior change: Hermes agents will no longer show the broken managed
instructions bundle UI until the underlying adapter actually supports
`instructionsFilePath`.
- Existing Hermes skill sync and local JWT behavior are unchanged.

## Model Used

- OpenAI Codex, GPT-5.4 class coding agent, medium reasoning,
terminal/git/gh tool use.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
LeonSGP
2026-04-21 04:54:14 +08:00
committed by GitHub
parent b94f1a1565
commit 51f127f47b
3 changed files with 14 additions and 3 deletions

View File

@@ -129,6 +129,18 @@ describe("adapter routes", () => {
expect(cursorAdapter).toBeDefined();
expect(cursorAdapter.capabilities.requiresMaterializedRuntimeSkills).toBe(true);
expect(cursorAdapter.capabilities.supportsInstructionsBundle).toBe(true);
// hermes_local currently supports skills + local JWT, but not the managed
// instructions bundle flow because the bundled adapter does not consume
// instructionsFilePath at runtime.
const hermesAdapter = res.body.find((a: any) => a.type === "hermes_local");
expect(hermesAdapter).toBeDefined();
expect(hermesAdapter.capabilities).toMatchObject({
supportsInstructionsBundle: false,
supportsSkills: true,
supportsLocalAgentJwt: true,
requiresMaterializedRuntimeSkills: false,
});
});
it("GET /api/adapters derives supportsSkills from listSkills/syncSkills presence", async () => {

View File

@@ -209,8 +209,7 @@ const hermesLocalAdapter: ServerAdapterModule = {
syncSkills: hermesSyncSkills,
models: hermesModels,
supportsLocalAgentJwt: true,
supportsInstructionsBundle: true,
instructionsPathKey: "instructionsFilePath",
supportsInstructionsBundle: false,
requiresMaterializedRuntimeSkills: false,
agentConfigurationDoc: hermesAgentConfigurationDoc,
detectModel: () => detectModelFromHermes(),

View File

@@ -21,7 +21,7 @@ const KNOWN_DEFAULTS: Record<string, AdapterCapabilities> = {
gemini_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true },
opencode_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true },
pi_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true },
hermes_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: false },
hermes_local: { supportsInstructionsBundle: false, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: false },
openclaw_gateway: ALL_FALSE,
};