fix(adapters): restore built-in Hermes and sync lockfile with server

Re-align phase1 with upstream: hermes_local ships via hermes-paperclip-adapter on the server and UI (hermes-local module). Fixes ERR_PNPM_OUTDATED_LOCKFILE from server/package.json missing a dep still present in the lockfile.

Add shared BUILTIN_ADAPTER_TYPES and skip external plugin registration when it would override a built-in type. Docs list Hermes as built-in; Droid remains the primary external example.

Made-with: Cursor
This commit is contained in:
HenkDz
2026-03-31 21:38:37 +01:00
parent 14d59da316
commit f884cbab78
11 changed files with 128 additions and 25 deletions
@@ -0,0 +1,15 @@
/**
* Adapter types shipped with Paperclip. External plugins must not replace these.
*/
export const BUILTIN_ADAPTER_TYPES = new Set([
"claude_local",
"codex_local",
"cursor",
"gemini_local",
"openclaw_gateway",
"opencode_local",
"pi_local",
"hermes_local",
"process",
"http",
]);
+36 -4
View File
@@ -67,6 +67,21 @@ import {
import {
agentConfigurationDoc as piAgentConfigurationDoc,
} from "@paperclipai/adapter-pi-local";
import {
execute as hermesExecute,
testEnvironment as hermesTestEnvironment,
sessionCodec as hermesSessionCodec,
listSkills as hermesListSkills,
syncSkills as hermesSyncSkills,
detectModel as detectModelFromHermes,
} from "hermes-paperclip-adapter/server";
import {
agentConfigurationDoc as hermesAgentConfigurationDoc,
models as hermesModels,
} from "hermes-paperclip-adapter";
import { BUILTIN_ADAPTER_TYPES } from "./builtin-adapter-types.js";
import { buildExternalAdapters } from "./plugin-loader.js";
import { getDisabledAdapterTypes } from "../services/adapter-plugin-store.js";
import { processAdapter } from "./process/index.js";
import { httpAdapter } from "./http/index.js";
@@ -163,6 +178,19 @@ const piLocalAdapter: ServerAdapterModule = {
agentConfigurationDoc: piAgentConfigurationDoc,
};
const hermesLocalAdapter: ServerAdapterModule = {
type: "hermes_local",
execute: hermesExecute,
testEnvironment: hermesTestEnvironment,
sessionCodec: hermesSessionCodec,
listSkills: hermesListSkills,
syncSkills: hermesSyncSkills,
models: hermesModels,
supportsLocalAgentJwt: true,
agentConfigurationDoc: hermesAgentConfigurationDoc,
detectModel: () => detectModelFromHermes(),
};
const adaptersByType = new Map<string, ServerAdapterModule>();
function registerBuiltInAdapters() {
@@ -174,6 +202,7 @@ function registerBuiltInAdapters() {
cursorLocalAdapter,
geminiLocalAdapter,
openclawGatewayAdapter,
hermesLocalAdapter,
processAdapter,
httpAdapter,
]) {
@@ -184,15 +213,12 @@ function registerBuiltInAdapters() {
registerBuiltInAdapters();
// ---------------------------------------------------------------------------
// Load external adapter plugins (droid, hermes, etc.)
// Load external adapter plugins (e.g. droid_local)
//
// External adapter packages export createServerAdapter() which returns a
// ServerAdapterModule. The host fills in sessionManagement.
// ---------------------------------------------------------------------------
import { buildExternalAdapters } from "./plugin-loader.js";
import { getDisabledAdapterTypes } from "../services/adapter-plugin-store.js";
/** Cached sync wrapper — the store is a simple JSON file read, safe to call frequently. */
function getDisabledAdapterTypesFromStore(): string[] {
return getDisabledAdapterTypes();
@@ -208,6 +234,12 @@ const externalAdaptersReady: Promise<void> = (async () => {
try {
const externalAdapters = await buildExternalAdapters();
for (const externalAdapter of externalAdapters) {
if (BUILTIN_ADAPTER_TYPES.has(externalAdapter.type)) {
console.warn(
`[paperclip] Skipping external adapter "${externalAdapter.type}" — conflicts with built-in adapter`,
);
continue;
}
adaptersByType.set(
externalAdapter.type,
{
+1 -16
View File
@@ -39,25 +39,10 @@ import type { ServerAdapterModule } from "../adapters/types.js";
import { loadExternalAdapterPackage, getUiParserSource, getOrExtractUiParserSource, reloadExternalAdapter } from "../adapters/plugin-loader.js";
import { logger } from "../middleware/logger.js";
import { assertBoard } from "./authz.js";
import { BUILTIN_ADAPTER_TYPES } from "../adapters/builtin-adapter-types.js";
const execFileAsync = promisify(execFile);
// ---------------------------------------------------------------------------
// Known built-in adapter types (cannot be removed via the API)
// ---------------------------------------------------------------------------
const BUILTIN_ADAPTER_TYPES = new Set([
"claude_local",
"codex_local",
"cursor",
"gemini_local",
"openclaw_gateway",
"opencode_local",
"pi_local",
"process",
"http",
]);
// ---------------------------------------------------------------------------
// Request / Response types
// ---------------------------------------------------------------------------
+1
View File
@@ -76,6 +76,7 @@ export function agentRoutes(db: Db) {
codex_local: "instructionsFilePath",
droid_local: "instructionsFilePath",
gemini_local: "instructionsFilePath",
hermes_local: "instructionsFilePath",
opencode_local: "instructionsFilePath",
cursor: "instructionsFilePath",
pi_local: "instructionsFilePath",