diff --git a/data/version.txt b/data/version.txt index 314c3d7..ab67981 100644 --- a/data/version.txt +++ b/data/version.txt @@ -1 +1 @@ -1.1.5 \ No newline at end of file +1.1.6 \ No newline at end of file diff --git a/src/agents/productionAgent/index.ts b/src/agents/productionAgent/index.ts index f7efa7e..924cd4c 100644 --- a/src/agents/productionAgent/index.ts +++ b/src/agents/productionAgent/index.ts @@ -1,4 +1,5 @@ import { Socket } from "socket.io"; +import { z } from "zod"; import { tool, jsonSchema } from "ai"; import u from "@/utils"; import Memory from "@/utils/agent/memory"; @@ -137,14 +138,11 @@ async function createSubAgent(parentCtx: AgentContext) { return fullResponse; } - const promptInput = jsonSchema<{ prompt: string }>({ - type: "object", - properties: { - prompt: { type: "string", description: "交给子Agent的任务简约描述,100字以内" }, - }, - required: ["prompt"], - additionalProperties: false, - }); + const promptInput = z + .object({ + prompt: z.string().describe("交给子Agent的任务简约描述,100字以内"), + }) + .toJSONSchema(); const projectInfo = await u.db("o_project").where("id", resTool.data.projectId).first(); if (!projectInfo) throw new Error(`项目不存在,ID: ${resTool.data.projectId}`); @@ -199,7 +197,7 @@ async function createSubAgent(parentCtx: AgentContext) { //衍生资产分析与信息写入 const run_sub_agent_derive_assets = tool({ description: "运行执行subAgent来完成衍生资产分析与信息写入相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "production_execution_derive_assets.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); @@ -221,7 +219,7 @@ async function createSubAgent(parentCtx: AgentContext) { //衍生资产图片生成 const run_sub_agent_generate_assets = tool({ description: "运行执行subAgent来完成衍生资产图片生成相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "production_execution_generate_assets.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); @@ -243,7 +241,7 @@ async function createSubAgent(parentCtx: AgentContext) { //拍摄计划 const run_sub_agent_director_plan = tool({ description: "运行执行subAgent来完成导演规划相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "production_execution_director_plan.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); @@ -268,7 +266,7 @@ async function createSubAgent(parentCtx: AgentContext) { //分镜图生成 const run_sub_agent_storyboard_gen = tool({ description: "运行执行subAgent来完成分镜图生成相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "production_execution_storyboard_gen.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); @@ -302,7 +300,7 @@ async function createSubAgent(parentCtx: AgentContext) { //分镜面板写入 const run_sub_agent_storyboard_panel = tool({ description: "运行执行subAgent来完成分镜面板写入相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "production_execution_storyboard_panel.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); @@ -328,7 +326,7 @@ async function createSubAgent(parentCtx: AgentContext) { //分镜表写入 const run_sub_agent_storyboard_table = tool({ description: "运行执行subAgent来完成分镜表构建相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "production_execution_storyboard_table.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); @@ -352,7 +350,7 @@ async function createSubAgent(parentCtx: AgentContext) { const run_sub_agent_supervision = tool({ description: "运行监督层subAgent执行独立任务,完成后返回结果", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "production_agent_supervision.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); diff --git a/src/agents/productionAgent/tools.ts b/src/agents/productionAgent/tools.ts index 03b5101..360b49d 100644 --- a/src/agents/productionAgent/tools.ts +++ b/src/agents/productionAgent/tools.ts @@ -52,7 +52,7 @@ export const flowDataSchema = z.object({ export type FlowData = z.infer; -const flowDataKeys = Object.keys(flowDataSchema.shape) as (keyof FlowData)[]; +const keySchema = z.enum(Object.keys(flowDataSchema.shape) as [keyof FlowData, ...Array]); const flowDataKeyLabels = Object.fromEntries( Object.entries(flowDataSchema.shape).map(([key, schema]) => [key, (schema as z.ZodTypeAny).description ?? key]), ) as Record; @@ -69,14 +69,13 @@ export default (toolCpnfig: ToolConfig) => { const tools: Record = { get_flowData: tool({ description: "获取工作区数据", - inputSchema: jsonSchema<{ key: keyof FlowData }>({ - type: "object", - properties: { - key: { type: "string", enum: flowDataKeys as string[], description: "数据key" }, - }, - required: ["key"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ key: keyof FlowData }>( + z + .object({ + key: keySchema.describe("数据key"), + }) + .toJSONSchema(), + ), execute: async ({ key }) => { const thinking = msg.thinking(`正在获取${flowDataKeyLabels[key]}工作区数据...`); console.log("[tools] get_flowData", key); @@ -89,22 +88,20 @@ export default (toolCpnfig: ToolConfig) => { }), add_deriveAsset: tool({ description: "新增或更新衍生资产", - inputSchema: jsonSchema<{ assetsId: number; id: number | null; name: string; desc: string }>({ - type: "object", - properties: { - assetsId: { type: "number", description: "关联的资产ID" }, - id: { type: ["number", "null"], description: "衍生资产ID,如果新增则为空" }, - name: { type: "string", description: "衍生资产名称" }, - desc: { type: "string", description: "衍生资产描述" }, - }, - required: ["assetsId", "id", "name", "desc"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ assetsId: number; id: number | null; name: string; desc: string }>( + z + .object({ + assetsId: z.number().describe("关联的资产ID"), + id: z.number().nullable().describe("衍生资产ID,如果新增则为空"), + name: z.string().describe("衍生资产名称"), + desc: z.string().describe("衍生资产描述"), + }) + .toJSONSchema(), + ), execute: async (raw) => { // 容错:LLM 偶尔传 "null" 字符串或空串,统一规范为 null const idRaw = raw.id as unknown; - const normalizedId = - idRaw === "null" || idRaw === "" || idRaw === undefined ? null : (idRaw as number | null); + const normalizedId = idRaw === "null" || idRaw === "" || idRaw === undefined ? null : (idRaw as number | null); const deriveAsset = { ...raw, id: normalizedId }; const thinking = msg.thinking("正在操作资产..."); @@ -139,15 +136,14 @@ export default (toolCpnfig: ToolConfig) => { }), del_deriveAsset: tool({ description: "删除衍生资产", - inputSchema: jsonSchema<{ assetsId: number; id: number }>({ - type: "object", - properties: { - assetsId: { type: "number", description: "关联的资产ID" }, - id: { type: "number", description: "衍生资产ID" }, - }, - required: ["assetsId", "id"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ assetsId: number; id: number }>( + z + .object({ + assetsId: z.number().describe("关联的资产ID"), + id: z.number().describe("衍生资产ID"), + }) + .toJSONSchema(), + ), execute: async ({ assetsId, id }) => { const thinking = msg.thinking("正在操作资产..."); const { scriptId } = resTool.data; @@ -162,14 +158,13 @@ export default (toolCpnfig: ToolConfig) => { }), generate_deriveAsset: tool({ description: "生成衍生资产图片", - inputSchema: jsonSchema<{ ids: number[] }>({ - type: "object", - properties: { - ids: { type: "array", items: { type: "number" }, description: "需要生成的 衍生资产ID" }, - }, - required: ["ids"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ ids: number[] }>( + z + .object({ + ids: z.array(z.number()).describe("需要生成的 衍生资产ID"), + }) + .toJSONSchema(), + ), execute: async ({ ids }) => { const thinking = msg.thinking("正在生成衍生资产..."); new Promise((resolve) => socket.emit("generateDeriveAsset", { ids }, (res: any) => resolve(res))) @@ -189,14 +184,13 @@ export default (toolCpnfig: ToolConfig) => { }), generate_storyboard: tool({ description: "生成分镜图片", - inputSchema: jsonSchema<{ ids: number[] }>({ - type: "object", - properties: { - ids: { type: "array", items: { type: "number" }, description: "必须获取真实的分镜ID,支持批量生成" }, - }, - required: ["ids"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ ids: number[] }>( + z + .object({ + ids: z.array(z.number()).describe("必须获取真实的分镜ID,支持批量生成"), + }) + .toJSONSchema(), + ), execute: async ({ ids }) => { const thinking = msg.thinking("正在生成分镜..."); new Promise((resolve) => socket.emit("generateStoryboard", { ids }, (res: any) => resolve(res))) diff --git a/src/agents/scriptAgent/index.ts b/src/agents/scriptAgent/index.ts index a0d5764..a86f37a 100644 --- a/src/agents/scriptAgent/index.ts +++ b/src/agents/scriptAgent/index.ts @@ -1,5 +1,6 @@ import { Socket } from "socket.io"; import { tool, jsonSchema } from "ai"; +import { z } from "zod"; import u from "@/utils"; import Memory from "@/utils/agent/memory"; import useTools from "@/agents/scriptAgent/tools"; @@ -132,18 +133,15 @@ function createSubAgent(parentCtx: AgentContext) { return fullResponse; } - const promptInput = jsonSchema<{ prompt: string }>({ - type: "object", - properties: { - prompt: { type: "string", description: "交给子Agent的任务简约描述,100字以内" }, - }, - required: ["prompt"], - additionalProperties: false, - }); + const promptInput = z + .object({ + prompt: z.string().describe("交给子Agent的任务简约描述,100字以内"), + }) + .toJSONSchema(); const run_sub_agent_storySkeleton = tool({ description: "运行执行subAgent来完成故事骨架相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "script_execution_skeleton.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); @@ -163,7 +161,7 @@ function createSubAgent(parentCtx: AgentContext) { const run_sub_agent_adaptationStrategy = tool({ description: "运行执行subAgent来完成改编策略相关任务", - inputSchema: promptInput, + inputSchema: jsonSchema<{ prompt: string }>(promptInput), execute: async ({ prompt }) => { const skill = path.join(u.getPath("skills"), "script_execution_adaptation.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); diff --git a/src/agents/scriptAgent/tools.ts b/src/agents/scriptAgent/tools.ts index 8ca7bde..3260faa 100644 --- a/src/agents/scriptAgent/tools.ts +++ b/src/agents/scriptAgent/tools.ts @@ -16,7 +16,7 @@ export const planData = z.object({ export type planData = z.infer; -const planDataKeys = Object.keys(planData.shape) as (keyof planData)[]; +const keySchema = z.enum(Object.keys(planData.shape) as [keyof planData, ...Array]); const planDataKeyLabels = Object.fromEntries( Object.entries(planData.shape).map(([key, schema]) => [key, (schema as z.ZodTypeAny).description ?? key]), ) as Record; @@ -33,14 +33,13 @@ export default (toolCpnfig: ToolConfig) => { const tools: Record = { get_novel_events: tool({ description: "获取章节事件", - inputSchema: jsonSchema<{ chapterIndexs: number[] }>({ - type: "object", - properties: { - chapterIndexs: { type: "array", items: { type: "number" }, description: "章节的编号" }, - }, - required: ["chapterIndexs"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ chapterIndexs: number[] }>( + z + .object({ + chapterIndexs: z.array(z.number()).describe("章节的编号"), + }) + .toJSONSchema(), + ), execute: async ({ chapterIndexs }) => { console.log("[tools] get_novel_events", chapterIndexs); const thinking = msg.thinking("正在查询章节事件..."); @@ -59,14 +58,13 @@ export default (toolCpnfig: ToolConfig) => { }), get_planData: tool({ description: "获取工作区数据", - inputSchema: jsonSchema<{ key: keyof planData }>({ - type: "object", - properties: { - key: { type: "string", enum: planDataKeys as string[], description: "数据key" }, - }, - required: ["key"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ key: keyof planData }>( + z + .object({ + key: keySchema.describe("数据key"), + }) + .toJSONSchema(), + ), execute: async ({ key }) => { console.log("[tools] get_planData", key); const thinking = msg.thinking(`正在获取${planDataKeyLabels[key]}工作区数据...`); @@ -79,14 +77,13 @@ export default (toolCpnfig: ToolConfig) => { }), get_novel_text: tool({ description: "获取小说章节原始文本内容", - inputSchema: jsonSchema<{ chapterIndex: string }>({ - type: "object", - properties: { - chapterIndex: { type: "string", description: "章节编号" }, - }, - required: ["chapterIndex"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ chapterIndex: string }>( + z + .object({ + chapterIndex: z.string().describe("章节编号"), + }) + .toJSONSchema(), + ), execute: async ({ chapterIndex }) => { console.log("[tools] get_novel_text", "[tools] get_novel_text", chapterIndex); const thinking = msg.thinking(`正在获取小说章节原文...`); @@ -100,14 +97,13 @@ export default (toolCpnfig: ToolConfig) => { }), get_script_content: tool({ description: "获取剧本本内容", - inputSchema: jsonSchema<{ ids: string[] }>({ - type: "object", - properties: { - ids: { type: "array", items: { type: "string" }, description: "脚本id" }, - }, - required: ["ids"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ ids: string[] }>( + z + .object({ + ids: z.array(z.string()).describe("脚本id"), + }) + .toJSONSchema(), + ), execute: async ({ ids }) => { console.log("[tools] get_script_content", "[tools] get_script_content", ids); const thinking = msg.thinking(`正在获取脚本内容...`); diff --git a/src/routes/cornerScape/batchBindAudio.ts b/src/routes/cornerScape/batchBindAudio.ts index 98dd5f1..dbf29a3 100644 --- a/src/routes/cornerScape/batchBindAudio.ts +++ b/src/routes/cornerScape/batchBindAudio.ts @@ -22,40 +22,37 @@ export default router.post( .whereIn("id", assetsIds) .andWhere("projectId", projectId) .select("id", "name", "describe"); - console.log("%c Line:20 🍎 assetsData", "background:#b03734", assetsData); - const audioData = await u.db("o_assets").where("type", "audio").whereNull("assetsId").andWhere("projectId", projectId).select("id", "name", "describe"); - console.log("%c Line:26 🍋 audioData", "background:#ea7e5c", audioData); + const audioData = await u + .db("o_assets") + .where("type", "audio") + .whereNull("assetsId") + .andWhere("projectId", projectId) + .select("id", "name", "describe"); async function processGroup() { try { const resultTool = tool({ description: "返回结果时必须调用这个工具", - inputSchema: jsonSchema<{ result: { id: number; audioIds: number[] }[] }>({ - type: "object", - properties: { - result: { - type: "array", - description: "适配的音色列表,id为资产id,audioIds为适配的音频id 无适配内容可以为 空数组", - items: { - type: "object", - properties: { - id: { type: "number" }, - audioIds: { type: "array", items: { type: "number" }, description: "适配的音频id 无适配内容可以为 空数组" }, - }, - required: ["id", "audioIds"], - additionalProperties: false, - }, - }, - }, - required: ["result"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ result: { id: number; audioIds: number[] }[] }>( + z + .object({ + result: z + .array( + z.object({ + id: z.number(), + audioIds: z.array(z.number()).describe("适配的音频id 无适配内容可以为 空数组"), + }), + ) + .describe("适配的音色列表,id为资产id,audioIds为适配的音频id 无适配内容可以为 空数组"), + }) + .toJSONSchema(), + ), execute: async ({ result }) => { console.log("[tools] extractAssets result", result); for (const item of result) { - await u.db("o_assetsRole2Audio").where("assetsRoleId", item.id).delete() + await u.db("o_assetsRole2Audio").where("assetsRoleId", item.id).delete(); if (item.audioIds.length) - await u.db("o_assetsRole2Audio").insert(item.audioIds.map(i => ({ assetsRoleId: item.id, assetsAudioId: i }))) + await u.db("o_assetsRole2Audio").insert(item.audioIds.map((i) => ({ assetsRoleId: item.id, assetsAudioId: i }))); } return "无需回复用户任何内容"; }, @@ -69,21 +66,20 @@ export default router.post( }, { role: "user", - content: `音频内容:${audioData.map(i => `Id:${i.id},音色名称:${i.name},描述:${i.describe}`).join("\n")}\n\n - 资产内容:${assetsData.map(i => `ID:${i.id},名称:${i.name},描述:${i.describe}`).join("\n")}\n\n + content: `音频内容:${audioData.map((i) => `Id:${i.id},音色名称:${i.name},描述:${i.describe}`).join("\n")}\n\n + 资产内容:${assetsData.map((i) => `ID:${i.id},名称:${i.name},描述:${i.describe}`).join("\n")}\n\n 请根据提供的资产内容描述 与 对应已有的音色 进行匹配,返回适配的音色`, }, ], tools: { resultTool }, }); console.log("%c Line:44 🍞 text", "background:#f5ce50", text); - } catch (e) { console.error(`提取失败:`, e); return; } } - await processGroup() + await processGroup(); res.status(200).send(success()); }, ); diff --git a/src/routes/script/extractAssets.ts b/src/routes/script/extractAssets.ts index d2effae..fe2f7f1 100644 --- a/src/routes/script/extractAssets.ts +++ b/src/routes/script/extractAssets.ts @@ -131,10 +131,7 @@ export default router.post( } // 去重:相同 scriptId + assetId 只保留一条 - const uniqueRows = [ - ...new Map(scriptAssetRows.map((r) => [`${r.scriptId}_${r.assetId}`, r])).values(), - ]; - + const uniqueRows = [...new Map(scriptAssetRows.map((r) => [`${r.scriptId}_${r.assetId}`, r])).values()]; // 先删除本批 scriptId 的旧关联,再插入新的 await u.db("o_scriptAssets").whereIn("scriptId", batchScriptIds).delete(); @@ -188,43 +185,19 @@ export default router.post( try { const resultTool = tool({ description: "返回结果时必须调用这个工具", - inputSchema: jsonSchema<{ newAssets: NewAsset[]; existingAssetRefs: ExistingAssetRef[] }>({ - type: "object", - properties: { - newAssets: { - type: "array", - description: "新发现的资产列表(不在已有资产列表中的),需要完整的 prompt、name、desc、type 和使用该资产的 scriptIds", - items: { - type: "object", - properties: { - name: { type: "string", description: "资产名称,仅为名称不做其他任何表述" }, - desc: { type: "string", description: "资产描述" }, - type: { type: "string", enum: ["role", "tool", "scene"], description: "资产类型" }, - scriptIds: { type: "array", items: { type: "number" }, description: "使用该资产的剧本id数组" }, - }, - required: ["name", "desc", "type", "scriptIds"], - additionalProperties: false, - }, - }, - existingAssetRefs: { - type: "array", - description: "已有资产的引用列表(在已有资产列表中已存在的),只需给出资产名称和使用该资产的 scriptIds", - items: { - type: "object", - properties: { - name: { type: "string", description: "已有资产的名称,必须与已有资产列表中的名称完全一致" }, - scriptIds: { type: "array", items: { type: "number" }, description: "使用该资产的剧本id数组" }, - }, - required: ["name", "scriptIds"], - additionalProperties: false, - }, - }, - }, - required: ["newAssets", "existingAssetRefs"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ newAssets: NewAsset[]; existingAssetRefs: ExistingAssetRef[] }>( + z + .object({ + newAssets: z + .array(NewAssetSchema) + .describe("新发现的资产列表(不在已有资产列表中的),需要完整的 prompt、name、desc、type 和使用该资产的 scriptIds"), + existingAssetRefs: z + .array(ExistingAssetRefSchema) + .describe("已有资产的引用列表(在已有资产列表中已存在的),只需给出资产名称和使用该资产的 scriptIds"), + }) + .toJSONSchema(), + ), execute: async ({ newAssets, existingAssetRefs }) => { - if (newAssets?.length) collectedNew = newAssets; if (existingAssetRefs?.length) collectedExisting = existingAssetRefs; return "无需回复用户任何内容"; diff --git a/src/routes/setting/vendorConfig/modelTest.ts b/src/routes/setting/vendorConfig/modelTest.ts index 1ed3cf0..a42a5d1 100644 --- a/src/routes/setting/vendorConfig/modelTest.ts +++ b/src/routes/setting/vendorConfig/modelTest.ts @@ -57,14 +57,13 @@ export default router.post( const getWeatherTool = tool({ description: "Get the weather in a location", - inputSchema: jsonSchema<{ location: string }>({ - type: "object", - properties: { - location: { type: "string", description: "The location to get the weather for" }, - }, - required: ["location"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ location: string }>( + z + .object({ + location: z.string().describe("The location to get the weather for"), + }) + .toJSONSchema(), + ), execute: async ({ location }) => { return { location, diff --git a/src/utils/agent/memory.ts b/src/utils/agent/memory.ts index c875bc7..0b5a6b7 100644 --- a/src/utils/agent/memory.ts +++ b/src/utils/agent/memory.ts @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from "uuid"; import { getEmbedding, cosineSimilarity } from "./embedding"; import type { memories as MemoryRow } from "@/types/database"; import { tool, jsonSchema } from "ai"; +import { z } from "zod"; // ── 可调配置默认值 ── const DEFAULTS: { @@ -200,14 +201,13 @@ class Memory { return { deepRetrieve: tool({ description: "深度检索记忆:当你需要回忆与某个关键词相关的详细历史信息时使用此工具", - inputSchema: jsonSchema<{ keyword: string }>({ - type: "object", - properties: { - keyword: { type: "string", description: "要检索的关键词" }, - }, - required: ["keyword"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ keyword: string }>( + z + .object({ + keyword: z.string().describe("要检索的关键词"), + }) + .toJSONSchema(), + ), execute: async ({ keyword }) => { const results = await this.deepRetrieve(keyword); if (results.length === 0) return { found: false, message: "未找到相关记忆" }; diff --git a/src/utils/agent/skillsTools.ts b/src/utils/agent/skillsTools.ts index 3531eb9..9def939 100644 --- a/src/utils/agent/skillsTools.ts +++ b/src/utils/agent/skillsTools.ts @@ -1,3 +1,4 @@ +import { z } from "zod"; import { tool, jsonSchema } from "ai"; import path from "path"; import isPathInside from "is-path-inside"; @@ -184,14 +185,13 @@ export function createSkillTools(skills: { name: string; description: string }[] return { activate_skill: tool({ description: `激活一个技能,加载其完整指令和捆绑资源列表到上下文。可用技能:${skillNames.join(", ")}`, - inputSchema: jsonSchema<{ name: string }>({ - type: "object", - properties: { - name: { type: "string", enum: skillNames, description: "要激活的技能名称" }, - }, - required: ["name"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ name: string }>( + z + .object({ + name: z.enum(skillNames as [string, ...string[]]).describe("要激活的技能名称"), + }) + .toJSONSchema(), + ), execute: async ({ name }) => { if (activated.has(name)) { console.log(`⚡[主技能] ℹ️ 技能 "${name}" 已激活,跳过重复注入`); @@ -226,14 +226,13 @@ export function createSkillTools(skills: { name: string; description: string }[] }), read_skill_file: tool({ description: "读取已激活技能目录下的资源文件。传入 activate_skill 返回的 skill_resources 中的文件路径。", - inputSchema: jsonSchema<{ filePath: string }>({ - type: "object", - properties: { - filePath: { type: "string", description: "资源文件的相对路径,来自 activate_skill 返回的 skill_resources" }, - }, - required: ["filePath"], - additionalProperties: false, - }), + inputSchema: jsonSchema<{ filePath: string }>( + z + .object({ + filePath: z.string().describe("资源文件的相对路径,来自 activate_skill 返回的 skill_resources"), + }) + .toJSONSchema(), + ), execute: async ({ filePath }) => { const normalizedInputPath = toUnixPath(filePath).trim(); if (!normalizedInputPath) {