feat: add curated curriculum agent skills

This commit is contained in:
2026-07-11 12:22:01 +08:00
parent 1a892ccb54
commit 96e120e02c
28 changed files with 1785 additions and 6 deletions
+14 -1
View File
@@ -29,7 +29,7 @@
* `workspace.ts` `confine()` path validator as a tool wrapper — the OS sandbox
* is the mechanism, the contract pins the invariant.
*/
import { query, type HookCallback, type McpServerConfig, type SDKMessage, type SDKAssistantMessage, type SDKUserMessage, type SDKResultMessage, type SDKPartialAssistantMessage } from "@anthropic-ai/claude-agent-sdk";
import { query, type HookCallback, type McpServerConfig, type SDKMessage, type SDKAssistantMessage, type SDKUserMessage, type SDKResultMessage, type SDKPartialAssistantMessage, type SDKSystemMessage } from "@anthropic-ai/claude-agent-sdk";
import type { PrismaClient } from "@prisma/client";
import { claudeSdkToolConfigForRole } from "./roleTools.js";
import { createAgentSecurityPolicy } from "./security.js";
@@ -91,6 +91,8 @@ export interface RunResult {
readonly costUsd?: number | undefined;
readonly numTurns: number;
readonly sdkSessionId?: string | undefined;
/** Skill ids reported by the SDK init event, not merely requested options. */
readonly initializedSkillIds?: readonly string[] | undefined;
readonly error?: string;
}
@@ -131,6 +133,7 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
let costUsd: number | undefined;
let numTurns = 0;
let sdkSessionId: string | undefined;
let initializedSkillIds: readonly string[] | undefined;
let error: string | undefined;
try {
await persistAgentMessage(req, "user", req.prompt);
@@ -166,6 +169,8 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
// The project workspace is untrusted input. Do not load user/project
// settings that could widen tools, hooks, MCP servers, or sandbox paths.
settingSources: [],
plugins: [{ type: "local", path: security.skillPluginRoot, skipMcpDiscovery: true }],
skills: [...security.skillIds],
strictMcpConfig: true,
// Claude Code 2.1.202 can honor the per-call opt-out despite
// sandbox.allowUnsandboxedCommands=false. Enforce the invariant again at
@@ -191,6 +196,12 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
for await (const message of conversation) {
switch (message.type) {
case "system": {
if (message.subtype === "init") {
initializedSkillIds = [...(message as SDKSystemMessage).skills];
}
break;
}
case "stream_event": {
const evt = (message as SDKPartialAssistantMessage).event;
if (evt.type === "content_block_delta" && evt.delta.type === "text_delta") {
@@ -287,6 +298,7 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
...(costUsd !== undefined ? { costUsd } : {}),
numTurns,
sdkSessionId,
...(initializedSkillIds !== undefined ? { initializedSkillIds } : {}),
...(error !== undefined ? { error } : {}),
};
} catch (e) {
@@ -298,6 +310,7 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
...(costUsd !== undefined ? { costUsd } : {}),
numTurns,
sdkSessionId,
...(initializedSkillIds !== undefined ? { initializedSkillIds } : {}),
...(aborted ? {} : { error: e instanceof Error ? e.message : String(e) }),
};
}