forked from EduCraft/curriculum-project-hub
feat: make agent roles and skills dynamic
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { afterAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { DatabaseRuntimeSettings } from "../../src/settings/runtime.js";
|
||||
import { DEFAULT_ORG_ID, prisma, resetDb, seedTestOrganization, testSecretEnvelope } from "./helpers.js";
|
||||
|
||||
describe("Organization-scoped Agent runtime configuration", () => {
|
||||
beforeEach(async () => {
|
||||
await resetDb();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
|
||||
it("resolves role prompt, model, tools and skills from the project Organization", async () => {
|
||||
await seedTestOrganization("org_other", "other");
|
||||
await Promise.all([
|
||||
prisma.project.create({
|
||||
data: { id: "project-a", organizationId: DEFAULT_ORG_ID, name: "A", workspaceDir: "/tmp/a" },
|
||||
}),
|
||||
prisma.project.create({
|
||||
data: { id: "project-b", organizationId: "org_other", name: "B", workspaceDir: "/tmp/b" },
|
||||
}),
|
||||
]);
|
||||
const [skillA, skillB] = await Promise.all([
|
||||
prisma.organizationAgentSkill.create({
|
||||
data: {
|
||||
id: "skill-a",
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
name: "typst",
|
||||
version: "0.15.0",
|
||||
contentDigest: "a".repeat(64),
|
||||
},
|
||||
}),
|
||||
prisma.organizationAgentSkill.create({
|
||||
data: {
|
||||
id: "skill-b",
|
||||
organizationId: "org_other",
|
||||
name: "typst",
|
||||
version: "other",
|
||||
contentDigest: "b".repeat(64),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
const [roleA, roleB] = await Promise.all([
|
||||
prisma.organizationAgentRole.create({
|
||||
data: {
|
||||
id: "role-a",
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
roleId: "draft",
|
||||
label: "A Draft",
|
||||
defaultModel: "anthropic/claude-sonnet-5",
|
||||
systemPrompt: "prompt-a",
|
||||
tools: ["read_file", "cph_build"],
|
||||
},
|
||||
}),
|
||||
prisma.organizationAgentRole.create({
|
||||
data: {
|
||||
id: "role-b",
|
||||
organizationId: "org_other",
|
||||
roleId: "draft",
|
||||
label: "B Draft",
|
||||
systemPrompt: "prompt-b",
|
||||
tools: [],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
await Promise.all([
|
||||
prisma.organizationAgentRoleSkill.create({
|
||||
data: { organizationId: DEFAULT_ORG_ID, agentRoleId: roleA.id, agentSkillId: skillA.id },
|
||||
}),
|
||||
prisma.organizationAgentRoleSkill.create({
|
||||
data: { organizationId: "org_other", agentRoleId: roleB.id, agentSkillId: skillB.id },
|
||||
}),
|
||||
]);
|
||||
const settings = new DatabaseRuntimeSettings(prisma, testSecretEnvelope, {});
|
||||
|
||||
const registryA = await settings.modelRegistry({ projectId: "project-a" });
|
||||
const registryB = await settings.modelRegistry({ projectId: "project-b" });
|
||||
|
||||
expect(registryA.role("draft")).toMatchObject({
|
||||
label: "A Draft",
|
||||
systemPrompt: "prompt-a",
|
||||
tools: ["read_file", "cph_build"],
|
||||
skills: [{ name: "typst", version: "0.15.0", contentDigest: "a".repeat(64) }],
|
||||
});
|
||||
expect(registryB.role("draft")).toMatchObject({
|
||||
label: "B Draft",
|
||||
systemPrompt: "prompt-b",
|
||||
tools: [],
|
||||
skills: [{ name: "typst", version: "other", contentDigest: "b".repeat(64) }],
|
||||
});
|
||||
});
|
||||
|
||||
it("fails closed for missing scope and disabled role skills", async () => {
|
||||
await prisma.project.create({
|
||||
data: { id: "project-a", organizationId: DEFAULT_ORG_ID, name: "A", workspaceDir: "/tmp/a" },
|
||||
});
|
||||
const skill = await prisma.organizationAgentSkill.create({
|
||||
data: {
|
||||
id: "skill-disabled",
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
name: "typst",
|
||||
version: "0.15.0",
|
||||
contentDigest: "c".repeat(64),
|
||||
disabledAt: new Date(),
|
||||
},
|
||||
});
|
||||
const role = await prisma.organizationAgentRole.create({
|
||||
data: { id: "role-a", organizationId: DEFAULT_ORG_ID, roleId: "draft", label: "Draft" },
|
||||
});
|
||||
await prisma.organizationAgentRoleSkill.create({
|
||||
data: { organizationId: DEFAULT_ORG_ID, agentRoleId: role.id, agentSkillId: skill.id },
|
||||
});
|
||||
const settings = new DatabaseRuntimeSettings(prisma, testSecretEnvelope, {});
|
||||
|
||||
await expect(settings.modelRegistry()).rejects.toThrow("projectId is required");
|
||||
await expect(settings.modelRegistry({ projectId: "project-a" })).rejects.toThrow(
|
||||
"role draft selects disabled skill typst",
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user