52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
import crypto from "node:crypto";
|
|
import type { AppConfig, RoleConfig } from "../config.js";
|
|
import { SkillLoader, type LoadedSkill } from "./skill-loader.js";
|
|
|
|
export interface ResolvedRole extends RoleConfig {
|
|
loadedSkills: LoadedSkill[];
|
|
fingerprint: string;
|
|
bootstrap: string;
|
|
}
|
|
|
|
export class RoleRegistry {
|
|
private readonly roles = new Map<string, ResolvedRole>();
|
|
|
|
constructor(private readonly config: AppConfig) {
|
|
const loader = new SkillLoader(config.skills);
|
|
for (const role of config.roles) {
|
|
const loadedSkills = role.skills.map((id) => loader.load(id));
|
|
const fingerprint = crypto.createHash("sha256").update(JSON.stringify({
|
|
id: role.id,
|
|
backend: role.backend,
|
|
workspace: role.workspace,
|
|
persona: role.persona,
|
|
policy: role.policy,
|
|
skills: loadedSkills.map(({ id, file, hash }) => ({ id, file, hash }))
|
|
})).digest("hex");
|
|
this.roles.set(role.id, { ...role, loadedSkills, fingerprint, bootstrap: buildBootstrap(role, loadedSkills) });
|
|
}
|
|
}
|
|
|
|
get(id?: string): ResolvedRole {
|
|
const roleId = id || this.config.defaultRole;
|
|
const role = this.roles.get(roleId);
|
|
if (!role) throw new Error(`Unknown role: ${roleId}`);
|
|
return role;
|
|
}
|
|
|
|
has(id: string): boolean { return this.roles.has(id); }
|
|
list(): string[] { return [...this.roles.keys()].sort(); }
|
|
defaultId(): string { return this.config.defaultRole; }
|
|
}
|
|
|
|
function buildBootstrap(role: RoleConfig, skills: LoadedSkill[]): string {
|
|
return [
|
|
"Initialize this ACP session with the following role. Treat these instructions as persistent context. Reply only with READY.",
|
|
`Role: ${role.id}`,
|
|
`Workspace: ${role.workspace}`,
|
|
role.persona ? `Persona:\n${role.persona}` : "Persona: general coding assistant",
|
|
`Permission policy enforced by the ACP client: ${JSON.stringify(role.policy)}`,
|
|
...skills.map((skill) => `Skill ${skill.id} (${skill.file}):\n${skill.content}`)
|
|
].join("\n\n");
|
|
}
|