@saber2pr/ai-agent 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,7 +46,6 @@ const tools_1 = require("@langchain/core/tools");
46
46
  const openai_1 = require("@langchain/openai");
47
47
  const config_1 = require("../config/config");
48
48
  const builtin_1 = require("../tools/builtin");
49
- const jsonSchemaToZod_1 = require("../utils/jsonSchemaToZod");
50
49
  class McpChainAgent {
51
50
  constructor(options) {
52
51
  this.allTools = [];
@@ -125,7 +124,7 @@ class McpChainAgent {
125
124
  const langchainTools = this.allTools.map(t => new tools_1.DynamicStructuredTool({
126
125
  name: t.function.name,
127
126
  description: t.function.description || "",
128
- schema: (0, jsonSchemaToZod_1.jsonSchemaToZod)(t.function.parameters),
127
+ schema: (t.function.parameters),
129
128
  func: async (args) => await t._handler(args),
130
129
  }));
131
130
  const prompt = prompts_1.PromptTemplate.fromTemplate(`{system_prompt}
@@ -1,11 +1,16 @@
1
1
  import { BaseMessage } from "@langchain/core/messages";
2
2
  import { AgentOptions } from "../types/type";
3
3
  export declare const CONFIG_FILE: string;
4
+ interface TokenUsage {
5
+ total: number;
6
+ }
4
7
  declare const AgentState: import("@langchain/langgraph").AnnotationRoot<{
5
8
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
6
9
  auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
7
10
  targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
8
11
  mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
12
+ tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
13
+ totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
9
14
  }>;
10
15
  export default class McpGraphAgent {
11
16
  private model;
@@ -15,6 +20,8 @@ export default class McpGraphAgent {
15
20
  private checkpointer;
16
21
  private langchainTools;
17
22
  private stopLoadingFunc;
23
+ private verbose;
24
+ private alwaysSystem;
18
25
  constructor(options?: AgentOptions);
19
26
  private showLoading;
20
27
  private startLoading;
@@ -26,30 +33,44 @@ export default class McpGraphAgent {
26
33
  private renderOutput;
27
34
  callModel(state: typeof AgentState.State): Promise<{
28
35
  messages: unknown[];
36
+ tokenUsage: {
37
+ total: number;
38
+ };
39
+ totalDuration: number;
29
40
  }>;
41
+ private getRecentToolCalls;
30
42
  trackProgress(state: typeof AgentState.State): Promise<{
31
43
  auditedFiles: string[];
32
44
  }>;
45
+ private printFinalSummary;
33
46
  createGraph(): Promise<import("@langchain/langgraph").CompiledStateGraph<import("@langchain/langgraph").StateType<{
34
47
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
35
48
  auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
36
49
  targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
37
50
  mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
51
+ tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
52
+ totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
38
53
  }>, import("@langchain/langgraph").UpdateType<{
39
54
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
40
55
  auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
41
56
  targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
42
57
  mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
58
+ tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
59
+ totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
43
60
  }>, "tools" | "agent" | "__start__" | "progress", {
44
61
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
45
62
  auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
46
63
  targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
47
64
  mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
65
+ tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
66
+ totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
48
67
  }, {
49
68
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
50
69
  auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
51
70
  targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
52
71
  mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
72
+ tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
73
+ totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
53
74
  }, import("@langchain/langgraph").StateDefinition>>;
54
75
  }
55
76
  export {};
@@ -13,10 +13,14 @@ const readline_1 = __importDefault(require("readline"));
13
13
  const fs_1 = __importDefault(require("fs"));
14
14
  const path_1 = __importDefault(require("path"));
15
15
  const os_1 = __importDefault(require("os"));
16
+ const events_1 = require("events");
16
17
  const builtin_1 = require("../tools/builtin");
17
18
  const convertToLangChainTool_1 = require("../utils/convertToLangChainTool");
18
19
  exports.CONFIG_FILE = path_1.default.join(os_1.default.homedir(), ".saber2pr-agent.json");
19
- // --- 1. 定义状态 (State) ---
20
+ // 全局设置:修复 AbortSignal 监听器数量警告
21
+ // LangChain 的 HTTP 客户端会创建多个 AbortSignal,需要增加默认限制
22
+ events_1.EventEmitter.defaultMaxListeners = 100;
23
+ // --- 2. 定义状态 (State) ---
20
24
  const AgentState = langgraph_1.Annotation.Root({
21
25
  messages: (0, langgraph_1.Annotation)({
22
26
  reducer: (x, y) => x.concat(y),
@@ -34,33 +38,48 @@ const AgentState = langgraph_1.Annotation.Root({
34
38
  reducer: (x, y) => y !== null && y !== void 0 ? y : x,
35
39
  default: () => "chat",
36
40
  }),
41
+ // ✅ Token 累加器
42
+ tokenUsage: (0, langgraph_1.Annotation)({
43
+ reducer: (x, y) => ({
44
+ total: ((x === null || x === void 0 ? void 0 : x.total) || 0) + ((y === null || y === void 0 ? void 0 : y.total) || 0),
45
+ }),
46
+ default: () => ({ total: 0 }),
47
+ }),
48
+ // ✅ 耗时累加器
49
+ totalDuration: (0, langgraph_1.Annotation)({
50
+ reducer: (x, y) => (x || 0) + (y || 0),
51
+ default: () => 0,
52
+ }),
37
53
  });
38
54
  class McpGraphAgent {
39
55
  constructor(options = {}) {
40
56
  this.checkpointer = new langgraph_1.MemorySaver();
41
57
  this.langchainTools = [];
42
- // ✅ 用于存储清理 loading 的函数
43
58
  this.stopLoadingFunc = null;
44
59
  this.options = options;
60
+ this.verbose = options.verbose || false;
61
+ this.alwaysSystem = options.alwaysSystem || true;
45
62
  this.targetDir = options.targetDir || process.cwd();
46
63
  process.setMaxListeners(100);
47
- // ✅ 退出清理
64
+ // ✅ 修复 AbortSignal 监听器数量警告
65
+ // LangChain 的 HTTP 客户端会创建多个 AbortSignal,需要增加默认限制
66
+ // 设置 EventEmitter 的默认 maxListeners,这会影响所有事件发射器(包括 AbortSignal)
67
+ events_1.EventEmitter.defaultMaxListeners = 100;
48
68
  const cleanup = () => {
49
69
  this.stopLoading();
50
- process.stdout.write('\u001B[?25h'); // 显示光标
70
+ process.stdout.write('\u001B[?25h');
51
71
  process.exit(0);
52
72
  };
53
73
  process.on("SIGINT", cleanup);
54
74
  process.on("SIGTERM", cleanup);
75
+ // ✅ 初始化内置工具
55
76
  const builtinToolInfos = (0, builtin_1.createDefaultBuiltinTools)({ options });
56
77
  this.langchainTools = [...builtinToolInfos, ...(options.tools || [])].map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t));
57
78
  this.toolNode = new prebuilt_1.ToolNode(this.langchainTools);
58
79
  }
59
- // ✅ 1. 复现你提供的 showLoading 效果
60
80
  showLoading(text) {
61
81
  const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
62
82
  let i = 0;
63
- // 隐藏光标,防止闪烁
64
83
  process.stdout.write('\u001B[?25l');
65
84
  const timer = setInterval(() => {
66
85
  process.stdout.write(`\r\x1b[36m${chars[i]}\x1b[0m ${text}`);
@@ -68,11 +87,10 @@ class McpGraphAgent {
68
87
  }, 80);
69
88
  return () => {
70
89
  clearInterval(timer);
71
- process.stdout.write('\r\x1b[K'); // 清除这一行
72
- process.stdout.write('\u001B[?25h'); // 恢复光标
90
+ process.stdout.write('\r\x1b[K');
91
+ process.stdout.write('\u001B[?25h');
73
92
  };
74
93
  }
75
- // ✅ 2. 封装内部调用方法
76
94
  startLoading(text) {
77
95
  this.stopLoading();
78
96
  this.stopLoadingFunc = this.showLoading(text);
@@ -96,6 +114,7 @@ class McpGraphAgent {
96
114
  temperature: 0,
97
115
  });
98
116
  }
117
+ // 绑定工具,使模型具备调用能力
99
118
  this.model = modelInstance.bindTools(this.langchainTools);
100
119
  return this.model;
101
120
  }
@@ -124,8 +143,8 @@ class McpGraphAgent {
124
143
  const stream = await app.stream({
125
144
  messages: [new messages_1.HumanMessage(query)],
126
145
  mode: "auto",
127
- targetCount: 4
128
- }, { configurable: { thread_id: "auto_worker" }, recursionLimit: 100 });
146
+ targetCount: 4,
147
+ }, { configurable: { thread_id: "auto_worker" }, recursionLimit: 100, debug: this.verbose });
129
148
  for await (const output of stream)
130
149
  this.renderOutput(output);
131
150
  }
@@ -154,18 +173,59 @@ class McpGraphAgent {
154
173
  ask();
155
174
  }
156
175
  renderOutput(output) {
157
- var _a, _b;
158
- this.stopLoading(); // 收到任何输出前,先关掉转圈圈
176
+ var _a, _b, _c;
177
+ this.stopLoading(); // 停止加载动画
159
178
  const agentNode = output.agent;
179
+ // ✅ 打印工具执行结果(tools 节点的输出)
180
+ const toolsNode = output.tools;
181
+ if (toolsNode && toolsNode.messages) {
182
+ const toolMessages = Array.isArray(toolsNode.messages) ? toolsNode.messages : [];
183
+ // 获取最近的 AI 消息以匹配 tool_call_id
184
+ const lastAiMsg = (_a = agentNode === null || agentNode === void 0 ? void 0 : agentNode.messages) === null || _a === void 0 ? void 0 : _a[agentNode.messages.length - 1];
185
+ const toolCallMap = new Map();
186
+ if (lastAiMsg === null || lastAiMsg === void 0 ? void 0 : lastAiMsg.tool_calls) {
187
+ lastAiMsg.tool_calls.forEach((tc) => {
188
+ if (tc.id)
189
+ toolCallMap.set(tc.id, tc.name);
190
+ });
191
+ }
192
+ toolMessages.forEach((msg) => {
193
+ // ToolMessage 有 tool_call_id 字段
194
+ const toolCallId = msg.tool_call_id || msg.id;
195
+ if (toolCallId) {
196
+ const toolName = toolCallMap.get(toolCallId) || msg.name || 'unknown';
197
+ const content = typeof msg.content === 'string'
198
+ ? msg.content
199
+ : JSON.stringify(msg.content);
200
+ // 如果内容太长,截断显示
201
+ const displayContent = content.length > 500
202
+ ? content.substring(0, 500) + '...'
203
+ : content;
204
+ console.log(`✅ [工具结果] ${toolName}: ${displayContent}`);
205
+ }
206
+ });
207
+ }
160
208
  if (agentNode) {
161
- const msg = agentNode.messages[0];
162
- const reasoning = (_a = msg.additional_kwargs) === null || _a === void 0 ? void 0 : _a.reasoning;
209
+ const msg = agentNode.messages[agentNode.messages.length - 1];
210
+ // 1. 打印思考过程(如果有)
211
+ const reasoning = (_b = msg.additional_kwargs) === null || _b === void 0 ? void 0 : _b.reasoning;
163
212
  if (reasoning) {
164
- console.log("\n🧠 [思考过程]:\n" + "─".repeat(50) + "\n" + reasoning + "\n" + "─".repeat(50));
213
+ console.log(`\n🧠 [思考]: ${reasoning}`);
214
+ }
215
+ // 2. 打印 AI 回复内容
216
+ if (msg.content) {
217
+ console.log(`🤖 [AI]: ${msg.content}`);
218
+ }
219
+ // ✅ 3. 实时打印当次统计信息
220
+ // 这里的 meta 数据是从 AgentGraphModel 的 _generate 中塞进去的
221
+ const meta = msg.response_metadata || {};
222
+ const token = meta.token || 0;
223
+ const duration = meta.duration || 0;
224
+ if (token > 0 || duration > 0) {
225
+ process.stdout.write(`📊 \x1b[2m[实时统计] 消耗: ${token} tokens | 耗时: ${duration}ms\x1b[0m\n`);
165
226
  }
166
- if (msg.content)
167
- console.log("🤖 [AI]:", msg.content);
168
- if ((_b = msg.tool_calls) === null || _b === void 0 ? void 0 : _b.length) {
227
+ // 4. 打印工具调用情况
228
+ if ((_c = msg.tool_calls) === null || _c === void 0 ? void 0 : _c.length) {
169
229
  msg.tool_calls.forEach((call) => {
170
230
  console.log(`🛠️ [调用工具]: ${call.name} 📦 参数: ${JSON.stringify(call.args)}`);
171
231
  });
@@ -173,46 +233,113 @@ class McpGraphAgent {
173
233
  }
174
234
  }
175
235
  async callModel(state) {
176
- const prompt = prompts_1.ChatPromptTemplate.fromMessages([
177
- ["system", `你是一个代码专家。工作目录:${this.targetDir}。
178
- 当前模式:{mode}。进度:{doneCount}/{targetCount}。
236
+ const auditedListStr = state.auditedFiles.length > 0
237
+ ? state.auditedFiles.map(f => `\n - ${f}`).join("")
238
+ : "暂无";
239
+ const recentToolCalls = this.getRecentToolCalls(state.messages);
240
+ const recentToolCallsStr = recentToolCalls.length > 0
241
+ ? `\n\n⚠️ 最近调用的工具(避免重复调用相同工具和参数):\n${recentToolCalls.map(tc => ` - ${tc.name}(${JSON.stringify(tc.args)})`).join("\n")}`
242
+ : "";
243
+ // 1. 构建当前的系统提示词模板
244
+ const systemPromptTemplate = `你是一个代码专家。工作目录:${this.targetDir}。
245
+ 当前模式:{mode}
246
+ 进度:{doneCount}/{targetCount}
179
247
  已审计文件:{auditedList}
180
248
 
181
- {extraPrompt}`],
249
+ # 格式要求
250
+ 1. Arguments 必须是纯粹的 JSON 对象,严禁将其作为字符串放入引号中。
251
+ 2. 严禁对 JSON 内容进行二次转义。
252
+
253
+ # 指令
254
+ 1. 一旦完成对一个文件的修改(apply_fix),请立即调用 generate_review 总结该文件的变动。
255
+ 2. 避免陷入在同一个文件上的无限循环尝试。
256
+ 3. 不要重复调用相同的工具和参数,如果工具已经返回结果,请基于结果继续工作而不是再次调用。{recentToolCalls}
257
+ {extraPrompt}`;
258
+ // 2. 核心逻辑:处理消息上下文
259
+ let inputMessages;
260
+ // ✅ 检查 options 中的 alwaysSystem 参数 (默认为 true 或根据你的需求设置)
261
+ // 如果不希望每次都携带(即只在首轮携带),则过滤掉历史消息里的 SystemMessage
262
+ if (this.options.alwaysSystem === false) {
263
+ inputMessages = state.messages.filter(msg => msg._getType() !== "system");
264
+ }
265
+ else {
266
+ // 默认模式:保持干净,由 PromptTemplate 重新生成最新的 System 状态
267
+ inputMessages = state.messages.filter(msg => msg._getType() !== "system");
268
+ }
269
+ const prompt = prompts_1.ChatPromptTemplate.fromMessages([
270
+ ["system", systemPromptTemplate],
182
271
  new prompts_1.MessagesPlaceholder("messages"),
183
272
  ]);
184
- // ✅ 调用 AI 前开启转圈圈
185
- this.startLoading("AI 正在分析并思考中...");
273
+ this.startLoading("AI 正在分析并思考中");
186
274
  try {
187
275
  const chain = prompt.pipe(this.model);
188
276
  const response = await chain.invoke({
189
- messages: state.messages,
277
+ messages: inputMessages, // ✅ 使用处理后的消息列表
190
278
  mode: state.mode,
191
279
  targetCount: state.targetCount,
192
280
  doneCount: state.auditedFiles.length,
193
- auditedList: state.auditedFiles.join(", "),
281
+ auditedList: auditedListStr,
282
+ recentToolCalls: recentToolCallsStr,
194
283
  extraPrompt: this.options.extraSystemPrompt || "",
195
284
  });
196
285
  this.stopLoading();
197
- return { messages: [response] };
286
+ const meta = response.response_metadata || {};
287
+ const currentToken = Number(meta.token) || 0;
288
+ const currentDuration = Number(meta.duration) || 0;
289
+ return {
290
+ messages: [response],
291
+ tokenUsage: { total: currentToken },
292
+ totalDuration: currentDuration
293
+ };
198
294
  }
199
295
  catch (error) {
200
296
  this.stopLoading();
201
297
  throw error;
202
298
  }
203
299
  }
300
+ getRecentToolCalls(messages, limit = 5) {
301
+ var _a;
302
+ const toolCalls = [];
303
+ // 从后往前遍历消息,收集最近的工具调用
304
+ for (let i = messages.length - 1; i >= 0 && toolCalls.length < limit; i--) {
305
+ const msg = messages[i];
306
+ if ((_a = msg.tool_calls) === null || _a === void 0 ? void 0 : _a.length) {
307
+ for (const tc of msg.tool_calls) {
308
+ toolCalls.push({ name: tc.name, args: tc.args });
309
+ if (toolCalls.length >= limit)
310
+ break;
311
+ }
312
+ }
313
+ }
314
+ return toolCalls;
315
+ }
204
316
  async trackProgress(state) {
205
317
  var _a;
206
318
  const lastAiMsg = state.messages[state.messages.length - 1];
207
- const newFiles = [];
319
+ const currentAudited = new Set(state.auditedFiles);
208
320
  if ((_a = lastAiMsg === null || lastAiMsg === void 0 ? void 0 : lastAiMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length) {
209
321
  for (const tc of lastAiMsg.tool_calls) {
210
- const file = tc.args.path || tc.args.filePath || tc.args.file;
211
- if (file && typeof file === 'string')
212
- newFiles.push(file);
322
+ // 兼容所有可能的路径参数字段
323
+ const file = tc.args.path || tc.args.filePath || tc.args.file || tc.args.file_path;
324
+ if (file && typeof file === 'string') {
325
+ currentAudited.add(file);
326
+ }
213
327
  }
214
328
  }
215
- return { auditedFiles: newFiles };
329
+ return { auditedFiles: Array.from(currentAudited) };
330
+ }
331
+ printFinalSummary(state) {
332
+ var _a;
333
+ const totalTokens = ((_a = state.tokenUsage) === null || _a === void 0 ? void 0 : _a.total) || 0;
334
+ const totalMs = state.totalDuration || 0;
335
+ if (totalTokens > 0 || totalMs > 0) {
336
+ console.log("\n" + "═".repeat(50));
337
+ console.log(`🏁 \x1b[32;1m[审计任务全量结算]\x1b[0m`);
338
+ console.log(` - 累计消耗总额: \x1b[33m${totalTokens}\x1b[0m Tokens`);
339
+ console.log(` - 累计执行耗时: \x1b[36m${(totalMs / 1000).toFixed(2)}\x1b[0m s`);
340
+ console.log(` - 审计文件总数: ${state.auditedFiles.length} 个`);
341
+ console.log("═".repeat(50) + "\n");
342
+ }
216
343
  }
217
344
  async createGraph() {
218
345
  const workflow = new langgraph_1.StateGraph(AgentState)
@@ -221,12 +348,25 @@ class McpGraphAgent {
221
348
  .addNode("progress", (state) => this.trackProgress(state))
222
349
  .addEdge(langgraph_1.START, "agent")
223
350
  .addConditionalEdges("agent", (state) => {
224
- var _a;
225
- const lastMsg = state.messages[state.messages.length - 1];
226
- if ((_a = lastMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length)
351
+ const messages = state.messages;
352
+ const lastMsg = messages[messages.length - 1];
353
+ const content = lastMsg.content || "";
354
+ // 1. 如果 AI 想要调用工具,去 tools 节点
355
+ if (lastMsg.tool_calls && lastMsg.tool_calls.length > 0) {
227
356
  return "tools";
228
- if (state.mode === "auto" && state.auditedFiles.length < state.targetCount)
229
- return "agent";
357
+ }
358
+ // 2. 判定结束的条件:
359
+ // - 模式是 auto 且审计完成
360
+ // - 或者 AI 明确输出了结束语
361
+ // - 或者 AI 输出了普通内容且没有工具调用(针对问答模式)
362
+ const isAutoFinished = state.mode === "auto" && state.auditedFiles.length >= state.targetCount;
363
+ const isFinalAnswer = content.includes("Final Answer");
364
+ // ✅ 修复核心:如果 AI 只是在聊天(没有工具调用),直接结束,不要跳回 agent
365
+ if (isAutoFinished || isFinalAnswer || state.mode === "chat") {
366
+ this.printFinalSummary(state);
367
+ return langgraph_1.END;
368
+ }
369
+ // 兜底:如果是在 auto 模式且还没干完活,才跳回 agent(通常不会走到这里)
230
370
  return langgraph_1.END;
231
371
  })
232
372
  .addEdge("tools", "progress")
package/lib/core/agent.js CHANGED
@@ -46,6 +46,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
46
46
  const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
47
47
  const config_1 = require("../config/config");
48
48
  const builtin_1 = require("../tools/builtin");
49
+ const jsonSchemaToZod_1 = require("../utils/jsonSchemaToZod");
49
50
  class McpAgent {
50
51
  constructor(options) {
51
52
  this.modelName = "";
@@ -202,7 +203,7 @@ class McpAgent {
202
203
  function: {
203
204
  name: `${name}__${t.name}`,
204
205
  description: t.description,
205
- parameters: t.inputSchema,
206
+ parameters: (0, jsonSchemaToZod_1.jsonSchemaToZod)(t.inputSchema),
206
207
  },
207
208
  _originalName: t.name,
208
209
  _client: client,
@@ -4,18 +4,16 @@ import { ChatResult } from '@langchain/core/outputs';
4
4
  export interface AgentGraphLLMResponse {
5
5
  text: string;
6
6
  reasoning?: string;
7
- chatId?: string;
7
+ token?: number;
8
+ duration?: number;
8
9
  }
9
10
  export declare abstract class AgentGraphModel extends BaseChatModel {
10
11
  protected boundTools?: any[];
11
- protected chatId?: string;
12
- constructor(fields?: BaseChatModelParams & {
13
- chatId?: string;
14
- });
12
+ constructor(fields?: BaseChatModelParams);
15
13
  bindTools(tools: any[]): any;
16
- abstract callApi(prompt: string, chatId?: string): Promise<AgentGraphLLMResponse>;
14
+ abstract callApi(prompt: string): Promise<AgentGraphLLMResponse>;
17
15
  _generate(messages: BaseMessage[]): Promise<ChatResult>;
18
- private serializeMessages;
19
16
  private parseToolCalls;
17
+ private serializeMessages;
20
18
  _llmType(): string;
21
19
  }
@@ -7,7 +7,6 @@ const function_calling_1 = require("@langchain/core/utils/function_calling");
7
7
  class AgentGraphModel extends chat_models_1.BaseChatModel {
8
8
  constructor(fields) {
9
9
  super(fields || {});
10
- this.chatId = fields === null || fields === void 0 ? void 0 : fields.chatId;
11
10
  }
12
11
  bindTools(tools) {
13
12
  this.boundTools = tools.map(t => (0, function_calling_1.convertToOpenAITool)(t));
@@ -15,11 +14,9 @@ class AgentGraphModel extends chat_models_1.BaseChatModel {
15
14
  }
16
15
  async _generate(messages) {
17
16
  const fullPrompt = this.serializeMessages(messages);
18
- const response = await this.callApi(fullPrompt, this.chatId);
19
- if (response.chatId)
20
- this.chatId = response.chatId;
21
- let { text, reasoning } = response;
22
- // ✅ 通用逻辑:解析 <think> 标签
17
+ const response = await this.callApi(fullPrompt);
18
+ let { text, reasoning, token, duration } = response;
19
+ // 1. 处理思考内容
23
20
  if (!reasoning && text.includes("<think>")) {
24
21
  const match = text.match(/<think>([\s\S]*?)<\/think>/);
25
22
  if (match) {
@@ -27,18 +24,69 @@ class AgentGraphModel extends chat_models_1.BaseChatModel {
27
24
  text = text.replace(/<think>[\s\S]*?<\/think>/, "").trim();
28
25
  }
29
26
  }
27
+ // 2. 解析工具调用
30
28
  const toolCalls = this.parseToolCalls(text);
29
+ // AgentGraphModel.ts 的 _generate 方法内
31
30
  return {
32
31
  generations: [{
33
32
  text,
34
33
  message: new messages_1.AIMessage({
35
34
  content: text,
36
35
  tool_calls: toolCalls,
37
- additional_kwargs: { reasoning: reasoning || "" }
36
+ additional_kwargs: {
37
+ reasoning: reasoning || "",
38
+ token: response.token, // 👈 必须
39
+ duration: response.duration, // 👈 必须
40
+ },
41
+ response_metadata: {
42
+ reasoning: reasoning || "",
43
+ token: response.token, // 👈 McpGraphAgent 读取路径
44
+ duration: response.duration,
45
+ }
38
46
  })
39
47
  }]
40
48
  };
41
49
  }
50
+ parseToolCalls(text) {
51
+ const actionMatch = text.match(/Action:\s*(\w+)/);
52
+ const argsMatch = text.match(/Arguments:\s*({[\s\S]*?})(?=\n|$)/);
53
+ if (!actionMatch)
54
+ return [];
55
+ let args = {};
56
+ if (argsMatch) {
57
+ try {
58
+ let raw = argsMatch[1].trim();
59
+ // ✅ 关键修复:递归解析,直到它变成真正的对象
60
+ // 这能处理 "\"{\\\"path\\\":...}\"" 这种套娃字符串
61
+ let safetyDepth = 0;
62
+ while (typeof raw === 'string' && safetyDepth < 5) {
63
+ try {
64
+ const parsed = JSON.parse(raw);
65
+ if (typeof parsed === 'object' && parsed !== null) {
66
+ raw = parsed;
67
+ break;
68
+ }
69
+ raw = parsed; // 如果解析后还是 string,继续剥
70
+ }
71
+ catch {
72
+ break; // 解析不动了,跳出
73
+ }
74
+ safetyDepth++;
75
+ }
76
+ args = raw;
77
+ }
78
+ catch (e) {
79
+ args = {};
80
+ }
81
+ }
82
+ // ✅ 此时返回的 args 必须是 object 类型
83
+ return [{
84
+ name: actionMatch[1],
85
+ args: typeof args === 'object' ? args : {},
86
+ id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
87
+ type: "tool_call",
88
+ }];
89
+ }
42
90
  serializeMessages(messages) {
43
91
  const systemMsg = messages.find(m => m._getType() === 'system');
44
92
  const lastMsg = messages[messages.length - 1];
@@ -58,39 +106,6 @@ ${format(lastMsg)}
58
106
  3. Arguments: {JSON}
59
107
  `.trim();
60
108
  }
61
- parseToolCalls(text) {
62
- const actionMatch = text.match(/Action:\s*(\w+)/);
63
- const argsMatch = text.match(/Arguments:\s*({[\s\S]*})/);
64
- if (!actionMatch)
65
- return [];
66
- let args = {};
67
- if (argsMatch) {
68
- try {
69
- // 强力解析逻辑,处理物理换行
70
- const rawArgs = argsMatch[1].trim().replace(/\n/g, "\\n");
71
- args = JSON.parse(rawArgs);
72
- // 参数映射
73
- const anyArgs = args;
74
- if (anyArgs.file_path && !anyArgs.path)
75
- anyArgs.path = anyArgs.file_path;
76
- if (anyArgs.filePath && !anyArgs.path)
77
- anyArgs.path = anyArgs.filePath;
78
- if (anyArgs.path && !anyArgs.filePath)
79
- anyArgs.filePath = anyArgs.path;
80
- if (anyArgs.file && !anyArgs.filePath)
81
- anyArgs.filePath = anyArgs.file;
82
- }
83
- catch (e) {
84
- console.warn("JSON Parse Error", e);
85
- }
86
- }
87
- return [{
88
- name: actionMatch[1],
89
- args,
90
- id: `call_${Date.now()}`,
91
- type: "tool_call",
92
- }];
93
- }
94
109
  _llmType() { return "agent_graph_model"; }
95
110
  }
96
111
  exports.AgentGraphModel = AgentGraphModel;
@@ -9,13 +9,12 @@ const minimatch_1 = require("minimatch");
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const zod_1 = require("zod");
11
11
  const createTool_1 = require("../../utils/createTool");
12
- const jsonSchemaToZod_1 = require("../../utils/jsonSchemaToZod");
13
12
  const lib_1 = require("./lib");
14
13
  // Schema definitions
15
14
  const ReadTextFileArgsSchema = zod_1.z.object({
16
15
  path: zod_1.z.string(),
17
- tail: zod_1.z.number().optional().describe('If provided, returns only the last N lines of the file'),
18
- head: zod_1.z.number().optional().describe('If provided, returns only the first N lines of the file')
16
+ tail: zod_1.z.number().nullable().describe('If provided, returns only the last N lines of the file'),
17
+ head: zod_1.z.number().nullable().describe('If provided, returns only the first N lines of the file')
19
18
  });
20
19
  const ReadMultipleFilesArgsSchema = zod_1.z.object({
21
20
  paths: zod_1.z
@@ -34,7 +33,7 @@ const EditOperation = zod_1.z.object({
34
33
  const EditFileArgsSchema = zod_1.z.object({
35
34
  path: zod_1.z.string(),
36
35
  edits: zod_1.z.array(EditOperation),
37
- dryRun: zod_1.z.boolean().default(false).describe('Preview changes using git-style diff format')
36
+ dryRun: zod_1.z.boolean().nullable().default(false).describe('Preview changes using git-style diff format')
38
37
  });
39
38
  const CreateDirectoryArgsSchema = zod_1.z.object({
40
39
  path: zod_1.z.string(),
@@ -44,11 +43,11 @@ const ListDirectoryArgsSchema = zod_1.z.object({
44
43
  });
45
44
  const ListDirectoryWithSizesArgsSchema = zod_1.z.object({
46
45
  path: zod_1.z.string(),
47
- sortBy: zod_1.z.enum(['name', 'size']).optional().default('name').describe('Sort entries by name or size'),
46
+ sortBy: zod_1.z.enum(['name', 'size']).nullable().default('name').describe('Sort entries by name or size'),
48
47
  });
49
48
  const DirectoryTreeArgsSchema = zod_1.z.object({
50
49
  path: zod_1.z.string(),
51
- excludePatterns: zod_1.z.array(zod_1.z.string()).optional().default([])
50
+ excludePatterns: zod_1.z.array(zod_1.z.string()).nullable().default([])
52
51
  });
53
52
  const MoveFileArgsSchema = zod_1.z.object({
54
53
  source: zod_1.z.string(),
@@ -57,7 +56,7 @@ const MoveFileArgsSchema = zod_1.z.object({
57
56
  const SearchFilesArgsSchema = zod_1.z.object({
58
57
  path: zod_1.z.string(),
59
58
  pattern: zod_1.z.string(),
60
- excludePatterns: zod_1.z.array(zod_1.z.string()).optional().default([])
59
+ excludePatterns: zod_1.z.array(zod_1.z.string()).nullable().default([])
61
60
  });
62
61
  const GetFileInfoArgsSchema = zod_1.z.object({
63
62
  path: zod_1.z.string(),
@@ -90,8 +89,7 @@ const getFilesystemTools = (targetDir) => {
90
89
  "the contents of a single file. Use the 'head' parameter to read only " +
91
90
  "the first N lines of a file, or the 'tail' parameter to read only " +
92
91
  "the last N lines of a file. Operates on the file as text regardless of extension.",
93
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(ReadTextFileArgsSchema, 'ReadTextFileArgsSchema'),
94
- validateParams: ["path"],
92
+ parameters: ReadTextFileArgsSchema,
95
93
  handler: readTextFileHandler
96
94
  });
97
95
  const readMultipleFilesTool = (0, createTool_1.createTool)({
@@ -101,8 +99,7 @@ const getFilesystemTools = (targetDir) => {
101
99
  "or compare multiple files. Each file's content is returned with its " +
102
100
  "path as a reference. Failed reads for individual files won't stop " +
103
101
  "the entire operation. Only works within allowed directories.",
104
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(ReadMultipleFilesArgsSchema, 'ReadMultipleFilesArgsSchema'),
105
- validateParams: ["paths"],
102
+ parameters: ReadMultipleFilesArgsSchema,
106
103
  handler: async (args) => {
107
104
  const results = await Promise.all(args.paths.map(async (filePath) => {
108
105
  try {
@@ -124,8 +121,7 @@ const getFilesystemTools = (targetDir) => {
124
121
  description: "Create a new file or completely overwrite an existing file with new content. " +
125
122
  "Use with caution as it will overwrite existing files without warning. " +
126
123
  "Handles text content with proper encoding. Only works within allowed directories.",
127
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(WriteFileArgsSchema, 'WriteFileArgsSchema'),
128
- validateParams: ["path", "content"],
124
+ parameters: WriteFileArgsSchema,
129
125
  handler: async (args) => {
130
126
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
131
127
  await (0, lib_1.writeFileContent)(validPath, args.content);
@@ -138,8 +134,7 @@ const getFilesystemTools = (targetDir) => {
138
134
  description: "Make line-based edits to a text file. Each edit replaces exact line sequences " +
139
135
  "with new content. Returns a git-style diff showing the changes made. " +
140
136
  "Only works within allowed directories.",
141
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(EditFileArgsSchema, 'EditFileArgsSchema'),
142
- validateParams: ["path", "edits"],
137
+ parameters: EditFileArgsSchema,
143
138
  handler: async (args) => {
144
139
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
145
140
  const result = await (0, lib_1.applyFileEdits)(validPath, args.edits, args.dryRun);
@@ -152,8 +147,7 @@ const getFilesystemTools = (targetDir) => {
152
147
  "nested directories in one operation. If the directory already exists, " +
153
148
  "this operation will succeed silently. Perfect for setting up directory " +
154
149
  "structures for projects or ensuring required paths exist. Only works within allowed directories.",
155
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(CreateDirectoryArgsSchema, 'CreateDirectoryArgsSchema'),
156
- validateParams: ["path"],
150
+ parameters: CreateDirectoryArgsSchema,
157
151
  handler: async (args) => {
158
152
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
159
153
  await promises_1.default.mkdir(validPath, { recursive: true });
@@ -167,8 +161,7 @@ const getFilesystemTools = (targetDir) => {
167
161
  "Results clearly distinguish between files and directories with [FILE] and [DIR] " +
168
162
  "prefixes. This tool is essential for understanding directory structure and " +
169
163
  "finding specific files within a directory. Only works within allowed directories.",
170
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(ListDirectoryArgsSchema, 'ListDirectoryArgsSchema'),
171
- validateParams: ["path"],
164
+ parameters: ListDirectoryArgsSchema,
172
165
  handler: async (args) => {
173
166
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
174
167
  const entries = await promises_1.default.readdir(validPath, { withFileTypes: true });
@@ -184,8 +177,7 @@ const getFilesystemTools = (targetDir) => {
184
177
  "Results clearly distinguish between files and directories with [FILE] and [DIR] " +
185
178
  "prefixes. This tool is useful for understanding directory structure and " +
186
179
  "finding specific files within a directory. Only works within allowed directories.",
187
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(ListDirectoryWithSizesArgsSchema, 'ListDirectoryWithSizesArgsSchema'),
188
- validateParams: ["path"],
180
+ parameters: ListDirectoryWithSizesArgsSchema,
189
181
  handler: async (args) => {
190
182
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
191
183
  const entries = await promises_1.default.readdir(validPath, { withFileTypes: true });
@@ -239,8 +231,7 @@ const getFilesystemTools = (targetDir) => {
239
231
  "Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " +
240
232
  "Files have no children array, while directories always have a children array (which may be empty). " +
241
233
  "The output is formatted with 2-space indentation for readability. Only works within allowed directories.",
242
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(DirectoryTreeArgsSchema, 'DirectoryTreeArgsSchema'),
243
- validateParams: ["path"],
234
+ parameters: DirectoryTreeArgsSchema,
244
235
  handler: async (args) => {
245
236
  const rootPath = args.path;
246
237
  async function buildTree(currentPath, excludePatterns = []) {
@@ -284,8 +275,7 @@ const getFilesystemTools = (targetDir) => {
284
275
  "and rename them in a single operation. If the destination exists, the " +
285
276
  "operation will fail. Works across different directories and can be used " +
286
277
  "for simple renaming within the same directory. Both source and destination must be within allowed directories.",
287
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(MoveFileArgsSchema, 'MoveFileArgsSchema'),
288
- validateParams: ["source", "destination"],
278
+ parameters: MoveFileArgsSchema,
289
279
  handler: async (args) => {
290
280
  const validSourcePath = await (0, lib_1.validatePath)(targetDir, args.source);
291
281
  const validDestPath = await (0, lib_1.validatePath)(targetDir, args.destination);
@@ -298,8 +288,7 @@ const getFilesystemTools = (targetDir) => {
298
288
  name: "search_files",
299
289
  description: "Search for files matching a specific pattern in a specified path. " +
300
290
  "Returns a list of files that match the pattern. Only works within allowed directories.",
301
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(SearchFilesArgsSchema, 'SearchFilesArgsSchema'),
302
- validateParams: ["path", "pattern"],
291
+ parameters: SearchFilesArgsSchema,
303
292
  handler: async (args) => {
304
293
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
305
294
  const results = await (0, lib_1.searchFilesWithValidation)(targetDir, validPath, args.pattern, [], { excludePatterns: args.excludePatterns });
@@ -311,8 +300,7 @@ const getFilesystemTools = (targetDir) => {
311
300
  name: "get_file_info",
312
301
  description: "Get detailed information about a file, including its size, last modified time, and type. " +
313
302
  "Only works within allowed directories.",
314
- parameters: (0, jsonSchemaToZod_1.zodObjectToJsonSchema)(GetFileInfoArgsSchema, 'GetFileInfoArgsSchema'),
315
- validateParams: ["path"],
303
+ parameters: GetFileInfoArgsSchema,
316
304
  handler: async (args) => {
317
305
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
318
306
  const info = await (0, lib_1.getFileStats)(validPath);
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getTsLspTools = void 0;
4
4
  const ts_context_mcp_1 = require("@saber2pr/ts-context-mcp");
5
5
  const createTool_1 = require("../../utils/createTool");
6
+ const zod_1 = require("zod");
6
7
  const getTsLspTools = (targetDir) => {
7
8
  const engine = new ts_context_mcp_1.PromptEngine(targetDir);
8
9
  return [
9
10
  (0, createTool_1.createTool)({
10
11
  name: "get_repo_map",
11
12
  description: "获取项目全局文件结构及导出清单,用于快速定位代码",
12
- parameters: { type: "object", properties: {} },
13
+ parameters: zod_1.z.object({}),
13
14
  handler: async () => {
14
15
  engine.refresh();
15
16
  return engine.getRepoMap();
@@ -18,15 +19,13 @@ const getTsLspTools = (targetDir) => {
18
19
  (0, createTool_1.createTool)({
19
20
  name: "analyze_deps",
20
21
  description: "分析指定文件的依赖关系,支持 tsconfig 路径别名解析",
21
- parameters: { type: "object", properties: { filePath: { type: "string", description: "文件相对路径" } } },
22
- validateParams: ["filePath"],
22
+ parameters: zod_1.z.object({ filePath: zod_1.z.string().describe("文件相对路径") }),
23
23
  handler: async ({ filePath }) => engine.getDeps(filePath),
24
24
  }),
25
25
  (0, createTool_1.createTool)({
26
26
  name: "read_skeleton",
27
27
  description: "提取文件的结构定义(接口、类、方法签名),忽略具体实现以节省 Token",
28
- parameters: { type: "object", properties: { filePath: { type: "string", description: "文件相对路径" } } },
29
- validateParams: ["filePath"],
28
+ parameters: zod_1.z.object({ filePath: zod_1.z.string().describe("文件相对路径") }),
30
29
  handler: async (args) => {
31
30
  const pathArg = args === null || args === void 0 ? void 0 : args.filePath;
32
31
  if (typeof pathArg !== "string" || pathArg.trim() === "") {
@@ -45,8 +44,7 @@ const getTsLspTools = (targetDir) => {
45
44
  (0, createTool_1.createTool)({
46
45
  name: "get_method_body",
47
46
  description: "获取指定文件内某个方法或函数的完整实现代码",
48
- parameters: { type: "object", properties: { filePath: { type: "string", description: "文件相对路径" }, methodName: { type: "string", description: "方法名或函数名" } } },
49
- validateParams: ["filePath", 'methodName'],
47
+ parameters: zod_1.z.object({ filePath: zod_1.z.string().describe("文件相对路径"), methodName: zod_1.z.string().describe("方法名或函数名") }),
50
48
  handler: async ({ filePath, methodName }) => {
51
49
  return engine.getMethodImplementation(filePath, methodName);
52
50
  },
@@ -1,4 +1,5 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index';
2
+ import { z } from 'zod';
2
3
  export interface ApiConfig {
3
4
  baseURL: string;
4
5
  apiKey: string;
@@ -9,7 +10,7 @@ export interface ToolInfo {
9
10
  function: {
10
11
  name: string;
11
12
  description?: string;
12
- parameters: any;
13
+ parameters: z.ZodObject<any>;
13
14
  };
14
15
  _handler?: (args: any) => Promise<any>;
15
16
  _client?: Client;
@@ -58,4 +59,8 @@ export interface AgentOptions {
58
59
  * only for graph agent
59
60
  */
60
61
  modelName?: string;
62
+ /**
63
+ * only for graph agent
64
+ */
65
+ alwaysSystem?: boolean;
61
66
  }
@@ -2,12 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.convertToLangChainTool = convertToLangChainTool;
4
4
  const tools_1 = require("@langchain/core/tools");
5
- const jsonSchemaToZod_1 = require("./jsonSchemaToZod");
6
5
  function convertToLangChainTool(info) {
7
6
  return new tools_1.DynamicStructuredTool({
8
7
  name: info.function.name,
9
8
  description: info.function.description || "",
10
- schema: (0, jsonSchemaToZod_1.jsonSchemaToZod)(info.function.parameters),
9
+ schema: info.function.parameters,
11
10
  func: async (args) => {
12
11
  if (info._handler)
13
12
  return await info._handler(args);
@@ -1,9 +1,12 @@
1
1
  import { ToolInfo } from '../types/type';
2
+ import { z } from 'zod';
2
3
  export interface CreateToolOptions {
3
4
  name: string;
4
5
  description: string;
5
- parameters: any;
6
+ /**
7
+ * zod@3.23.8
8
+ */
9
+ parameters: z.ZodObject<any>;
6
10
  handler: (args: any) => Promise<string>;
7
- validateParams?: string[];
8
11
  }
9
12
  export declare function createTool(options: CreateToolOptions): ToolInfo;
@@ -10,7 +10,6 @@ function createTool(options) {
10
10
  parameters: options.parameters,
11
11
  },
12
12
  _handler: async (input) => {
13
- var _a;
14
13
  // 兼容处理:如果 input 是字符串,尝试解析为 JSON 对象
15
14
  let args = input;
16
15
  if (typeof input === 'string') {
@@ -21,15 +20,6 @@ function createTool(options) {
21
20
  args = input;
22
21
  }
23
22
  }
24
- if (((_a = options.validateParams) === null || _a === void 0 ? void 0 : _a.length) > 0) {
25
- for (const arg in args) {
26
- if (options.validateParams.includes(arg)) {
27
- if (typeof args[arg] === 'undefined') {
28
- return `Error: 参数 '${arg}' 缺失`;
29
- }
30
- }
31
- }
32
- }
33
23
  return options.handler(args);
34
24
  },
35
25
  };
@@ -1,12 +1,2 @@
1
1
  import { z } from 'zod';
2
- export declare const zodObjectToJsonSchema: (zodObject: z.ZodObject<any>, name: string) => z.ZodObject<any, z.UnknownKeysParam, z.ZodTypeAny, {
3
- [x: string]: any;
4
- }, {
5
- [x: string]: any;
6
- }> | {
7
- title?: string;
8
- default?: any;
9
- description?: string;
10
- markdownDescription?: string;
11
- };
12
2
  export declare function jsonSchemaToZod(parameters: any): z.ZodObject<any>;
@@ -1,14 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.zodObjectToJsonSchema = void 0;
4
3
  exports.jsonSchemaToZod = jsonSchemaToZod;
5
4
  const zod_1 = require("zod");
6
- const zod_to_json_schema_1 = require("zod-to-json-schema");
7
- const zodObjectToJsonSchema = (zodObject, name) => {
8
- var _a, _b;
9
- return ((_b = (_a = (0, zod_to_json_schema_1.zodToJsonSchema)(zodObject, name)) === null || _a === void 0 ? void 0 : _a.definitions) === null || _b === void 0 ? void 0 : _b[name]) || zodObject;
10
- };
11
- exports.zodObjectToJsonSchema = zodObjectToJsonSchema;
12
5
  function jsonSchemaToZod(parameters) {
13
6
  var _a;
14
7
  if (!parameters || typeof parameters !== 'object') {
@@ -57,8 +50,8 @@ function jsonSchemaToZod(parameters) {
57
50
  let schema = convertProp(prop);
58
51
  // 处理必填项
59
52
  const isRequired = (_a = parameters.required) === null || _a === void 0 ? void 0 : _a.includes(key);
60
- obj[key] = isRequired ? schema : schema.optional();
53
+ obj[key] = isRequired ? schema : schema.nullable();
61
54
  }
62
55
  // 使用 passthrough 或 loose 以增加容错性
63
- return zod_1.z.object(obj).passthrough();
56
+ return zod_1.z.object(obj);
64
57
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saber2pr/ai-agent",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "description": "AI Assistant CLI.",
5
5
  "author": "saber2pr",
6
6
  "license": "ISC",
@@ -34,8 +34,7 @@
34
34
  "langchain": "0.3.15",
35
35
  "minimatch": "^10.0.1",
36
36
  "openai": "^6.16.0",
37
- "zod": "3.23.8",
38
- "zod-to-json-schema": "3.23.2"
37
+ "zod": "3.23.8"
39
38
  },
40
39
  "resolutions": {
41
40
  "@langchain/core": "0.3.39"