Refactor runtime around isolated bot instances

This commit is contained in:
zenord
2026-08-16 23:45:09 +08:00
parent 914579a94c
commit f9fb4ef776
51 changed files with 2355 additions and 1459 deletions
+29 -35
View File
@@ -1,51 +1,45 @@
import crypto from "node:crypto";
import type { AppConfig, RoleConfig } from "../config.js";
import type { AppConfig, BotConfig } from "../config.js";
import { SkillLoader, type LoadedSkill } from "./skill-loader.js";
export interface ResolvedRole extends RoleConfig {
const BOOTSTRAP_SCHEMA_VERSION = 1;
export interface ResolvedBot extends BotConfig {
loadedSkills: LoadedSkill[];
fingerprint: string;
bootstrap: string;
}
export class RoleRegistry {
private readonly roles = new Map<string, ResolvedRole>();
export class BotProfileResolver {
readonly bot: ResolvedBot;
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) });
}
constructor(config: AppConfig) {
const loadedSkills = config.bot.skills.map((skill) => new SkillLoader(config.bot.skills).load(skill.id));
const fingerprint = crypto.createHash("sha256").update(JSON.stringify({
bootstrapSchemaVersion: BOOTSTRAP_SCHEMA_VERSION,
id: config.bot.id,
workspace: config.bot.workspace,
persona: config.bot.persona,
agent: config.bot.agent,
permissions: config.bot.permissions,
skills: loadedSkills.map(({ id, file, hash }) => ({ id, file, hash }))
})).digest("hex");
this.bot = {
...config.bot,
loadedSkills,
fingerprint,
bootstrap: buildBootstrap(config.bot, 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 {
function buildBootstrap(bot: BotConfig, 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)}`,
`Initialize this ACP session with gori-agent bootstrap schema v${BOOTSTRAP_SCHEMA_VERSION}. Treat these instructions as persistent context. Reply only with READY.`,
`Bot: ${bot.id}`,
`Workspace: ${bot.workspace}`,
bot.persona ? `Persona:\n${bot.persona}` : "Persona: general coding assistant",
`Permission policy enforced by the ACP client: ${JSON.stringify(bot.permissions)}`,
...skills.map((skill) => `Skill ${skill.id} (${skill.file}):\n${skill.content}`)
].join("\n\n");
}