Add ServerAdapterModule capabilities from fork's adapter-utils 0.3.1

Bring the K8s adapter up to parity with the fork's ServerAdapterModule
contract by adding sessionManagement, listSkills/syncSkills, listModels
with Bedrock detection, and promptBundleKey support in the session codec.

- Declare sessionManagement with nativeContextManagement: "confirmed"
  so Paperclip skips threshold-based session compaction (Claude manages
  its own context)
- Add ephemeral skill management (listSkills/syncSkills) mirroring
  claude_local — reports skill state without runtime persistence since
  skills are injected via prompt bundle into ephemeral Job pods
- Add listModels() with Bedrock environment detection, returning
  region-qualified model IDs when CLAUDE_CODE_USE_BEDROCK or
  ANTHROPIC_BEDROCK_BASE_URL are set
- Extend session codec to round-trip promptBundleKey field
- Remove the `as ServerAdapterModule` cast — the return type now
  satisfies the full interface

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-14 11:13:18 +00:00
parent 10a5004c02
commit 389bbb6f99
7 changed files with 395 additions and 19 deletions
+101
View File
@@ -0,0 +1,101 @@
import type {
AdapterSkillContext,
AdapterSkillSnapshot,
AdapterSkillEntry,
} from "@paperclipai/adapter-utils";
import {
readPaperclipRuntimeSkillEntries,
resolvePaperclipDesiredSkillNames,
readInstalledSkillTargets,
} from "@paperclipai/adapter-utils/server-utils";
import path from "node:path";
const SKILLS_HOME = "/paperclip/.claude/skills";
async function buildK8sSkillSnapshot(
config: Record<string, unknown>,
): Promise<AdapterSkillSnapshot> {
const availableEntries = await readPaperclipRuntimeSkillEntries(config, import.meta.dirname ?? __dirname);
const availableByKey = new Map(availableEntries.map((e) => [e.key, e]));
const desiredSkills = resolvePaperclipDesiredSkillNames(config, availableEntries);
const desiredSet = new Set(desiredSkills);
const installed = await readInstalledSkillTargets(SKILLS_HOME);
const entries: AdapterSkillEntry[] = availableEntries.map((entry) => ({
key: entry.key,
runtimeName: entry.runtimeName,
desired: desiredSet.has(entry.key),
managed: true,
state: desiredSet.has(entry.key) ? "configured" : "available",
origin: entry.required ? "paperclip_required" : "company_managed",
originLabel: entry.required ? "Required by Paperclip" : "Managed by Paperclip",
readOnly: false,
sourcePath: entry.source,
targetPath: null,
detail: desiredSet.has(entry.key)
? "Injected via prompt bundle into ephemeral K8s Job pods."
: null,
required: Boolean(entry.required),
requiredReason: entry.requiredReason ?? null,
}));
const warnings: string[] = [];
for (const desiredSkill of desiredSkills) {
if (availableByKey.has(desiredSkill)) continue;
warnings.push(`Desired skill "${desiredSkill}" is not available from the Paperclip skills directory.`);
entries.push({
key: desiredSkill,
runtimeName: null,
desired: true,
managed: true,
state: "missing",
origin: "external_unknown",
originLabel: "External or unavailable",
readOnly: false,
sourcePath: undefined,
targetPath: undefined,
detail: "Paperclip cannot find this skill in the runtime skills directory.",
});
}
for (const [name, installedEntry] of installed.entries()) {
if (availableEntries.some((e) => e.runtimeName === name)) continue;
entries.push({
key: name,
runtimeName: name,
desired: false,
managed: false,
state: "external",
origin: "user_installed",
originLabel: "User-installed",
locationLabel: "~/.claude/skills",
readOnly: true,
sourcePath: null,
targetPath: installedEntry.targetPath ?? path.join(SKILLS_HOME, name),
detail: "Installed outside Paperclip management in the Claude skills home.",
});
}
entries.sort((a, b) => a.key.localeCompare(b.key));
return {
adapterType: "claude_k8s",
supported: true,
mode: "ephemeral",
desiredSkills,
entries,
warnings,
};
}
export async function listK8sSkills(ctx: AdapterSkillContext): Promise<AdapterSkillSnapshot> {
return buildK8sSkillSnapshot(ctx.config);
}
export async function syncK8sSkills(
ctx: AdapterSkillContext,
_desiredSkills: string[],
): Promise<AdapterSkillSnapshot> {
return buildK8sSkillSnapshot(ctx.config);
}