From 2a2fa31a0330ae67ce5f73487991a11b75387b47 Mon Sep 17 00:00:00 2001 From: HenkDz Date: Fri, 3 Apr 2026 22:17:34 +0100 Subject: [PATCH] feat(adapters): allow external plugins to override built-in adapters Previously external adapters matching a built-in type were skipped with a warning. Now they override the built-in, so plugin developers can ship improved versions of existing adapters (e.g. hermes-paperclip-adapter) without removing the built-in fallback for users who haven't installed the plugin. --- server/src/__tests__/adapter-registry.test.ts | 32 +++++++++++++++++++ server/src/adapters/registry.ts | 8 ++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/server/src/__tests__/adapter-registry.test.ts b/server/src/__tests__/adapter-registry.test.ts index d121a374..e4b109f6 100644 --- a/server/src/__tests__/adapter-registry.test.ts +++ b/server/src/__tests__/adapter-registry.test.ts @@ -55,4 +55,36 @@ describe("server adapter registry", () => { "Unknown adapter type: external_test", ); }); + + it("allows external plugin to override a built-in adapter type", () => { + // claude_local is always built-in + const builtIn = findServerAdapter("claude_local"); + expect(builtIn).not.toBeNull(); + + const plugin: ServerAdapterModule = { + type: "claude_local", + execute: async () => ({ + exitCode: 0, + signal: null, + timedOut: false, + }), + testEnvironment: async () => ({ + adapterType: "claude_local", + status: "pass", + checks: [], + testedAt: new Date(0).toISOString(), + }), + models: [{ id: "plugin-model", label: "Plugin Override" }], + supportsLocalAgentJwt: false, + }; + + registerServerAdapter(plugin); + + // Plugin wins + const resolved = requireServerAdapter("claude_local"); + expect(resolved).toBe(plugin); + expect(resolved.models).toEqual([ + { id: "plugin-model", label: "Plugin Override" }, + ]); + }); }); diff --git a/server/src/adapters/registry.ts b/server/src/adapters/registry.ts index aa8ddeb4..09cb41a8 100644 --- a/server/src/adapters/registry.ts +++ b/server/src/adapters/registry.ts @@ -234,11 +234,11 @@ const externalAdaptersReady: Promise = (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`, + const overriding = BUILTIN_ADAPTER_TYPES.has(externalAdapter.type); + if (overriding) { + console.log( + `[paperclip] External adapter \"${externalAdapter.type}\" overrides built-in adapter`, ); - continue; } adaptersByType.set( externalAdapter.type,