forked from EduCraft/curriculum-project-hub
feat: make agent roles and skills dynamic
This commit is contained in:
+13
-2
@@ -33,6 +33,7 @@ import { query, type HookCallback, type McpServerConfig, type SDKMessage, type S
|
||||
import type { PrismaClient } from "@prisma/client";
|
||||
import { claudeSdkToolConfigForRole } from "./roleTools.js";
|
||||
import { createAgentSecurityPolicy } from "./security.js";
|
||||
import type { RoleSkillEntry } from "./models.js";
|
||||
|
||||
export interface ProjectContext {
|
||||
readonly projectId: string;
|
||||
@@ -66,6 +67,7 @@ export interface RunRequest {
|
||||
* means no tools.
|
||||
*/
|
||||
readonly tools?: readonly string[] | undefined;
|
||||
readonly skills?: readonly RoleSkillEntry[] | undefined;
|
||||
readonly mcpServers?: Record<string, McpServerConfig> | undefined;
|
||||
readonly maxTurns?: number;
|
||||
readonly runId: string;
|
||||
@@ -135,6 +137,7 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
|
||||
let sdkSessionId: string | undefined;
|
||||
let initializedSkillIds: readonly string[] | undefined;
|
||||
let error: string | undefined;
|
||||
let cleanupSecurity = async (): Promise<void> => {};
|
||||
try {
|
||||
await persistAgentMessage(req, "user", req.prompt);
|
||||
const toolConfig = claudeSdkToolConfigForRole(req.tools);
|
||||
@@ -143,17 +146,21 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
|
||||
throw new Error("Agent run requires the configured workspace root");
|
||||
}
|
||||
const security = await createAgentSecurityPolicy({
|
||||
runId: req.runId,
|
||||
workspaceRoot,
|
||||
workspaceDir: req.project.workspaceDir,
|
||||
skills: req.skills,
|
||||
providerProxyEnv: req.providerProxyEnv,
|
||||
});
|
||||
cleanupSecurity = security.cleanup;
|
||||
const hasSkills = security.skillIds.length > 0;
|
||||
|
||||
type QueryOptions = NonNullable<Parameters<typeof query>[0]["options"]>;
|
||||
const options: QueryOptions = {
|
||||
cwd: security.cwd,
|
||||
// `skills` controls discovery/allowlisting, but an explicit `tools`
|
||||
// list still has to expose the Skill dispatcher itself.
|
||||
tools: [...toolConfig.tools, "Skill"],
|
||||
tools: [...toolConfig.tools, ...(hasSkills ? ["Skill"] : [])],
|
||||
allowedTools: [...toolConfig.allowedTools],
|
||||
maxTurns: cap,
|
||||
includePartialMessages: true,
|
||||
@@ -172,7 +179,9 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
|
||||
// settings that could widen tools, hooks, MCP servers, or sandbox paths.
|
||||
settingSources: [],
|
||||
settings: { disableBundledSkills: true },
|
||||
plugins: [{ type: "local", path: security.skillPluginRoot, skipMcpDiscovery: true }],
|
||||
...(hasSkills && security.skillPluginRoot !== undefined
|
||||
? { plugins: [{ type: "local" as const, path: security.skillPluginRoot, skipMcpDiscovery: true }] }
|
||||
: {}),
|
||||
skills: [...security.skillIds],
|
||||
strictMcpConfig: true,
|
||||
// Claude Code 2.1.202 can honor the per-call opt-out despite
|
||||
@@ -316,6 +325,8 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
|
||||
...(initializedSkillIds !== undefined ? { initializedSkillIds } : {}),
|
||||
...(aborted ? {} : { error: e instanceof Error ? e.message : String(e) }),
|
||||
};
|
||||
} finally {
|
||||
await cleanupSecurity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user