feat: make agent roles and skills dynamic

This commit is contained in:
2026-07-11 12:55:05 +08:00
parent 17c0536958
commit d36b00bbec
48 changed files with 1389 additions and 1749 deletions
@@ -7,6 +7,7 @@ import { join } from "node:path";
import { promisify } from "node:util";
import { afterEach, describe, expect, it } from "vitest";
import { runAgent, type StreamEvent } from "../../src/agent/runner.js";
import { importSkillDirectory } from "../../src/agent/skillStore.js";
const execFileAsync = promisify(execFile);
const originalEnv = new Map<string, string | undefined>();
@@ -52,7 +53,9 @@ describe("real Claude SDK sandbox boundary", () => {
const workspace = join(workspaceRoot, "a", `p_${nonce.slice(0, 8)}`);
const sibling = join(workspaceRoot, "b", `p_${nonce.slice(8, 16)}`);
const serviceSecret = join(root, `s_${nonce.slice(16, 24)}`);
roots.push(workspace, sibling, serviceSecret);
const skillSource = join(root, `k_${nonce.slice(24, 28)}`);
const skillStore = join(root, `ks_${nonce.slice(28, 32)}`);
roots.push(workspace, sibling, serviceSecret, skillSource, skillStore);
await Promise.all([
mkdir(workspace, { recursive: true }),
mkdir(sibling, { recursive: true }),
@@ -62,6 +65,9 @@ describe("real Claude SDK sandbox boundary", () => {
writeFile(join(sibling, "secret.txt"), "sibling-secret\n"),
writeFile(serviceSecret, "platform-secret\n"),
]);
await mkdir(skillSource, { recursive: true });
await writeFile(join(skillSource, "SKILL.md"), "---\nname: outline\ndescription: Outline\n---\n");
const installedSkill = await importSkillDirectory({ sourceDir: skillSource, storeRoot: skillStore });
const untrustedSkill = join(workspace, ".claude", "skills", "untrusted");
await mkdir(untrustedSkill, { recursive: true });
await writeFile(join(untrustedSkill, "SKILL.md"), "---\nname: untrusted\ndescription: must never load\n---\n");
@@ -86,6 +92,7 @@ describe("real Claude SDK sandbox boundary", () => {
DATABASE_URL: "postgresql://platform-secret",
FEISHU_APP_SECRET: "feishu-secret",
HUB_SESSION_SECRET: "session-secret",
HUB_SKILL_STORE_ROOT: skillStore,
});
const bashCommand = [
@@ -133,6 +140,7 @@ describe("real Claude SDK sandbox boundary", () => {
ANTHROPIC_API_KEY: "",
},
tools: ["bash"],
skills: [{ name: "outline", version: "1", contentDigest: installedSkill.contentDigest }],
maxTurns: 3,
runId: "sandbox-run",
sessionId: "sandbox-session",
@@ -150,9 +158,7 @@ describe("real Claude SDK sandbox boundary", () => {
).toBe("completed");
expect(stub.requestCount()).toBeGreaterThanOrEqual(3);
expect(new Set(result.initializedSkillIds)).toEqual(new Set([
"cph-curated:outline",
"cph-curated:lesson-project",
"cph-curated:data-processing-spec",
"cph-runtime:outline",
]));
const toolResults = streamEvents.filter((event) => event.type === "tool-result");
expect(toolResults).toHaveLength(2);