forked from EduCraft/curriculum-project-hub
feat: make agent roles and skills dynamic
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { chmod, lstat, mkdir, realpath } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { isAbsolute, join, relative, resolve } from "node:path";
|
||||
import { validateCuratedSkillPlugin } from "./curatedSkills.js";
|
||||
import type { RoleSkillEntry } from "./models.js";
|
||||
import { prepareRunSkillPlugin, readSkillStoreRoot } from "./skillStore.js";
|
||||
|
||||
const PROVIDER_ENV_KEYS = new Set([
|
||||
"ANTHROPIC_BASE_URL",
|
||||
@@ -33,8 +34,10 @@ const SANDBOX_HIDDEN_ENV_KEYS = [
|
||||
const MAX_AGENT_TMP_PREFIX_BYTES = 56;
|
||||
|
||||
export interface AgentSecurityInput {
|
||||
readonly runId: string;
|
||||
readonly workspaceRoot: string;
|
||||
readonly workspaceDir: string;
|
||||
readonly skills?: readonly RoleSkillEntry[] | undefined;
|
||||
/** Run-scoped loopback proxy capability; customer provider secrets are forbidden here. */
|
||||
readonly providerProxyEnv?: Readonly<Record<string, string | undefined>> | undefined;
|
||||
readonly hostEnv?: Readonly<Record<string, string | undefined>> | undefined;
|
||||
@@ -62,7 +65,8 @@ export interface AgentSecurityPolicy {
|
||||
readonly workspaceRoot: string;
|
||||
readonly env: Record<string, string | undefined>;
|
||||
readonly skillIds: readonly string[];
|
||||
readonly skillPluginRoot: string;
|
||||
readonly skillPluginRoot?: string | undefined;
|
||||
cleanup(): Promise<void>;
|
||||
readonly sandbox: AgentSandboxPolicy;
|
||||
}
|
||||
|
||||
@@ -82,7 +86,6 @@ export async function createAgentSecurityPolicy(input: AgentSecurityInput): Prom
|
||||
const agentState = await ensureDirectoryTree(runtimeRoot, ["state"]);
|
||||
const agentTmp = await ensureDirectoryTree(cphRoot, ["t"]);
|
||||
assertShortAgentTemp(agentTmp);
|
||||
const skillPlugin = await validateCuratedSkillPlugin();
|
||||
|
||||
const path = hostEnv["PATH"]?.trim();
|
||||
if (path === undefined || path === "") {
|
||||
@@ -119,12 +122,21 @@ export async function createAgentSecurityPolicy(input: AgentSecurityInput): Prom
|
||||
|
||||
const sensitiveReadPaths = hostSensitiveReadPaths(hostEnv);
|
||||
const runtimeReadPaths = hostRuntimeReadPaths(hostEnv);
|
||||
const selectedSkills = input.skills ?? [];
|
||||
const skillPlugin = selectedSkills.length === 0
|
||||
? null
|
||||
: await prepareRunSkillPlugin({
|
||||
storeRoot: readSkillStoreRoot(hostEnv),
|
||||
runId: input.runId,
|
||||
skills: selectedSkills,
|
||||
});
|
||||
return {
|
||||
cwd: workspaceDir,
|
||||
workspaceRoot,
|
||||
env,
|
||||
skillIds: skillPlugin.skillIds,
|
||||
skillPluginRoot: skillPlugin.root,
|
||||
skillIds: skillPlugin?.skillIds ?? [],
|
||||
...(skillPlugin !== null ? { skillPluginRoot: skillPlugin.root } : {}),
|
||||
cleanup: skillPlugin?.cleanup ?? (async () => {}),
|
||||
sandbox: {
|
||||
enabled: true,
|
||||
failIfUnavailable: true,
|
||||
@@ -140,7 +152,7 @@ export async function createAgentSecurityPolicy(input: AgentSecurityInput): Prom
|
||||
// workspace plus the named system runtime needed to execute tools.
|
||||
// SDK allowRead takes precedence over matching denyRead paths.
|
||||
denyRead: ["/"],
|
||||
allowRead: [workspaceDir, skillPlugin.root, ...runtimeReadPaths],
|
||||
allowRead: [workspaceDir, ...(skillPlugin !== null ? [skillPlugin.root] : []), ...runtimeReadPaths],
|
||||
},
|
||||
credentials: {
|
||||
files: sensitiveReadPaths.map((path) => ({ path, mode: "deny" as const })),
|
||||
|
||||
Reference in New Issue
Block a user