test+docs: add capability flag tests and documentation
Tests: - adapter-registry: verify capability flags are exposed, undefined when unset, and correctly declared on built-in claude_local adapter - adapter-routes: verify GET /api/adapters includes capabilities object, correct flags for codex_local/process/cursor, and derived supportsSkills Documentation: - docs/adapters/creating-an-adapter.md: new "Capability Flags" section documenting all flags, their defaults, and usage example for external adapter plugins Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -95,6 +95,51 @@ describe("server adapter registry", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("exposes capability flags from registered adapters", () => {
|
||||
const adapterWithCaps: ServerAdapterModule = {
|
||||
type: "external_test",
|
||||
execute: async () => ({ exitCode: 0, signal: null, timedOut: false }),
|
||||
testEnvironment: async () => ({
|
||||
adapterType: "external_test",
|
||||
status: "pass" as const,
|
||||
checks: [],
|
||||
testedAt: new Date(0).toISOString(),
|
||||
}),
|
||||
supportsLocalAgentJwt: true,
|
||||
supportsInstructionsBundle: true,
|
||||
instructionsPathKey: "customPathKey",
|
||||
requiresMaterializedRuntimeSkills: true,
|
||||
};
|
||||
|
||||
registerServerAdapter(adapterWithCaps);
|
||||
|
||||
const resolved = findActiveServerAdapter("external_test");
|
||||
expect(resolved).not.toBeNull();
|
||||
expect(resolved!.supportsInstructionsBundle).toBe(true);
|
||||
expect(resolved!.instructionsPathKey).toBe("customPathKey");
|
||||
expect(resolved!.requiresMaterializedRuntimeSkills).toBe(true);
|
||||
expect(resolved!.supportsLocalAgentJwt).toBe(true);
|
||||
});
|
||||
|
||||
it("returns undefined for capability flags on adapters that do not set them", () => {
|
||||
registerServerAdapter(externalAdapter);
|
||||
|
||||
const resolved = findActiveServerAdapter("external_test");
|
||||
expect(resolved).not.toBeNull();
|
||||
expect(resolved!.supportsInstructionsBundle).toBeUndefined();
|
||||
expect(resolved!.instructionsPathKey).toBeUndefined();
|
||||
expect(resolved!.requiresMaterializedRuntimeSkills).toBeUndefined();
|
||||
});
|
||||
|
||||
it("built-in claude_local adapter declares capability flags", () => {
|
||||
const adapter = findActiveServerAdapter("claude_local");
|
||||
expect(adapter).not.toBeNull();
|
||||
expect(adapter!.supportsInstructionsBundle).toBe(true);
|
||||
expect(adapter!.instructionsPathKey).toBe("instructionsFilePath");
|
||||
expect(adapter!.requiresMaterializedRuntimeSkills).toBe(false);
|
||||
expect(adapter!.supportsLocalAgentJwt).toBe(true);
|
||||
});
|
||||
|
||||
it("switches active adapter behavior back to the builtin when an override is paused", async () => {
|
||||
const builtIn = findServerAdapter("claude_local");
|
||||
expect(builtIn).not.toBeNull();
|
||||
|
||||
@@ -57,6 +57,75 @@ describe("adapter routes", () => {
|
||||
unregisterServerAdapter("claude_local");
|
||||
});
|
||||
|
||||
it("GET /api/adapters includes capabilities object for each adapter", async () => {
|
||||
const app = createApp();
|
||||
|
||||
const res = await request(app).get("/api/adapters");
|
||||
expect(res.status).toBe(200);
|
||||
expect(Array.isArray(res.body)).toBe(true);
|
||||
expect(res.body.length).toBeGreaterThan(0);
|
||||
|
||||
// Every adapter should have a capabilities object
|
||||
for (const adapter of res.body) {
|
||||
expect(adapter.capabilities).toBeDefined();
|
||||
expect(typeof adapter.capabilities.supportsInstructionsBundle).toBe("boolean");
|
||||
expect(typeof adapter.capabilities.supportsSkills).toBe("boolean");
|
||||
expect(typeof adapter.capabilities.supportsLocalAgentJwt).toBe("boolean");
|
||||
expect(typeof adapter.capabilities.requiresMaterializedRuntimeSkills).toBe("boolean");
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/adapters returns correct capabilities for built-in adapters", async () => {
|
||||
const app = createApp();
|
||||
|
||||
const res = await request(app).get("/api/adapters");
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
// codex_local has instructions bundle + skills + jwt, no materialized skills
|
||||
// (claude_local is overridden by beforeEach, so check codex_local instead)
|
||||
const codexLocal = res.body.find((a: any) => a.type === "codex_local");
|
||||
expect(codexLocal).toBeDefined();
|
||||
expect(codexLocal.capabilities).toMatchObject({
|
||||
supportsInstructionsBundle: true,
|
||||
supportsSkills: true,
|
||||
supportsLocalAgentJwt: true,
|
||||
requiresMaterializedRuntimeSkills: false,
|
||||
});
|
||||
|
||||
// process adapter should have no local capabilities
|
||||
const processAdapter = res.body.find((a: any) => a.type === "process");
|
||||
expect(processAdapter).toBeDefined();
|
||||
expect(processAdapter.capabilities).toMatchObject({
|
||||
supportsInstructionsBundle: false,
|
||||
supportsSkills: false,
|
||||
supportsLocalAgentJwt: false,
|
||||
requiresMaterializedRuntimeSkills: false,
|
||||
});
|
||||
|
||||
// cursor adapter should require materialized runtime skills
|
||||
const cursorAdapter = res.body.find((a: any) => a.type === "cursor");
|
||||
expect(cursorAdapter).toBeDefined();
|
||||
expect(cursorAdapter.capabilities.requiresMaterializedRuntimeSkills).toBe(true);
|
||||
expect(cursorAdapter.capabilities.supportsInstructionsBundle).toBe(true);
|
||||
});
|
||||
|
||||
it("GET /api/adapters derives supportsSkills from listSkills/syncSkills presence", async () => {
|
||||
const app = createApp();
|
||||
|
||||
const res = await request(app).get("/api/adapters");
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
// http adapter has no listSkills/syncSkills
|
||||
const httpAdapter = res.body.find((a: any) => a.type === "http");
|
||||
expect(httpAdapter).toBeDefined();
|
||||
expect(httpAdapter.capabilities.supportsSkills).toBe(false);
|
||||
|
||||
// codex_local has listSkills/syncSkills
|
||||
const codexLocal = res.body.find((a: any) => a.type === "codex_local");
|
||||
expect(codexLocal).toBeDefined();
|
||||
expect(codexLocal.capabilities.supportsSkills).toBe(true);
|
||||
});
|
||||
|
||||
it("uses the active adapter when resolving config schema for a paused builtin override", async () => {
|
||||
const app = createApp();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user