@saber2pr/ai-agent 0.0.49 → 0.0.51

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.
@@ -6,9 +6,6 @@ interface TokenUsage {
6
6
  }
7
7
  declare const AgentState: import("@langchain/langgraph").AnnotationRoot<{
8
8
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
9
- auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
10
- targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
11
- mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
12
9
  tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
13
10
  totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
14
11
  }>;
@@ -24,7 +21,6 @@ export default class McpGraphAgent<T extends AgentGraphModel = any> {
24
21
  private alwaysSystem;
25
22
  private recursionLimit;
26
23
  private apiConfig;
27
- private maxTargetCount;
28
24
  private maxTokens;
29
25
  private mcpClients;
30
26
  private streamEnabled;
@@ -66,36 +62,21 @@ export default class McpGraphAgent<T extends AgentGraphModel = any> {
66
62
  totalDuration: number;
67
63
  }>;
68
64
  private getRecentToolCalls;
69
- trackProgress(state: typeof AgentState.State): Promise<{
70
- auditedFiles: string[];
71
- }>;
72
65
  private printFinalSummary;
73
66
  createGraph(): Promise<import("@langchain/langgraph").CompiledStateGraph<import("@langchain/langgraph").StateType<{
74
67
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
75
- auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
76
- targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
77
- mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
78
68
  tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
79
69
  totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
80
70
  }>, import("@langchain/langgraph").UpdateType<{
81
71
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
82
- auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
83
- targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
84
- mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
85
72
  tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
86
73
  totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
87
- }>, "tools" | "__start__" | "agent" | "progress", {
74
+ }>, "tools" | "__start__" | "agent", {
88
75
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
89
- auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
90
- targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
91
- mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
92
76
  tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
93
77
  totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
94
78
  }, {
95
79
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
96
- auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
97
- targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
98
- mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
99
80
  tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
100
81
  totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
101
82
  }, import("@langchain/langgraph").StateDefinition>>;
@@ -29,18 +29,6 @@ const AgentState = langgraph_1.Annotation.Root({
29
29
  reducer: (x, y) => x.concat(y),
30
30
  default: () => [],
31
31
  }),
32
- auditedFiles: (0, langgraph_1.Annotation)({
33
- reducer: (x, y) => Array.from(new Set([...x, ...y])),
34
- default: () => [],
35
- }),
36
- targetCount: (0, langgraph_1.Annotation)({
37
- reducer: (x, y) => y !== null && y !== void 0 ? y : x,
38
- default: () => 4,
39
- }),
40
- mode: (0, langgraph_1.Annotation)({
41
- reducer: (x, y) => y !== null && y !== void 0 ? y : x,
42
- default: () => 'chat',
43
- }),
44
32
  // ✅ Token 累加器
45
33
  tokenUsage: (0, langgraph_1.Annotation)({
46
34
  reducer: (x, y) => ({
@@ -67,8 +55,7 @@ class McpGraphAgent {
67
55
  this.targetDir = options.targetDir || process.cwd();
68
56
  this.recursionLimit = options.recursionLimit || 80;
69
57
  this.apiConfig = options.apiConfig;
70
- this.maxTargetCount = options.maxTargetCount || 4;
71
- this.maxTokens = options.maxTokens || 8000;
58
+ this.maxTokens = options.maxTokens || 100000;
72
59
  this.streamEnabled = options.stream || false;
73
60
  process.setMaxListeners(100);
74
61
  // ✅ 修复 AbortSignal 监听器数量警告
@@ -283,8 +270,6 @@ class McpGraphAgent {
283
270
  const app = await this.createGraph();
284
271
  const graphStream = await app.stream({
285
272
  messages: [new messages_1.HumanMessage(query)],
286
- mode: 'auto',
287
- targetCount: this.maxTargetCount,
288
273
  }, {
289
274
  configurable: { thread_id: 'auto_worker' },
290
275
  recursionLimit: this.recursionLimit,
@@ -312,8 +297,6 @@ class McpGraphAgent {
312
297
  const app = await this.createGraph();
313
298
  const graphStream = await app.stream({
314
299
  messages: [new messages_1.HumanMessage(query)],
315
- mode: 'auto',
316
- targetCount: this.maxTargetCount,
317
300
  }, {
318
301
  configurable: { thread_id: 'stream_worker' },
319
302
  recursionLimit: this.recursionLimit,
@@ -346,7 +329,7 @@ class McpGraphAgent {
346
329
  rl.close();
347
330
  return;
348
331
  }
349
- const graphStream = await app.stream({ messages: [new messages_1.HumanMessage(input)], mode: 'chat' }, {
332
+ const graphStream = await app.stream({ messages: [new messages_1.HumanMessage(input)] }, {
350
333
  configurable: { thread_id: 'session' },
351
334
  recursionLimit: this.recursionLimit,
352
335
  debug: this.verbose,
@@ -416,7 +399,6 @@ class McpGraphAgent {
416
399
  }
417
400
  }
418
401
  async callModel(state) {
419
- const auditedListStr = state.auditedFiles.length > 0 ? state.auditedFiles.map(f => `\n - ${f}`).join('') : '暂无';
420
402
  const recentToolCalls = this.getRecentToolCalls(state.messages);
421
403
  const recentToolCallsStr = recentToolCalls.length > 0
422
404
  ? `\n\n⚠️ 最近调用的工具(避免重复调用相同工具和参数):\n${recentToolCalls
@@ -425,9 +407,6 @@ class McpGraphAgent {
425
407
  : '';
426
408
  // 1. 构建当前的系统提示词模板
427
409
  const systemPromptTemplate = `你是一个代码专家。工作目录:${this.targetDir}。
428
- 当前模式:{mode}
429
- 进度:{doneCount}/{targetCount}
430
- 已审计文件:{auditedList}
431
410
 
432
411
  # 🛠️ 工具调用规范
433
412
  1. Arguments 必须是纯粹的 JSON 对象,严禁将其作为字符串放入引号中。
@@ -458,10 +437,6 @@ class McpGraphAgent {
458
437
  try {
459
438
  const promptParams = {
460
439
  messages: inputMessages,
461
- mode: state.mode,
462
- targetCount: state.targetCount,
463
- doneCount: state.auditedFiles.length,
464
- auditedList: auditedListStr,
465
440
  recentToolCalls: recentToolCallsStr,
466
441
  extraPrompt: this.options.extraSystemPrompt || '',
467
442
  };
@@ -611,21 +586,6 @@ class McpGraphAgent {
611
586
  }
612
587
  return toolCalls;
613
588
  }
614
- async trackProgress(state) {
615
- var _a;
616
- const lastAiMsg = state.messages[state.messages.length - 1];
617
- const currentAudited = new Set(state.auditedFiles);
618
- if ((_a = lastAiMsg === null || lastAiMsg === void 0 ? void 0 : lastAiMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length) {
619
- for (const tc of lastAiMsg.tool_calls) {
620
- // 兼容所有可能的路径参数字段
621
- const file = tc.args.path || tc.args.filePath || tc.args.file || tc.args.file_path;
622
- if (file && typeof file === 'string') {
623
- currentAudited.add(file);
624
- }
625
- }
626
- }
627
- return { auditedFiles: Array.from(currentAudited) };
628
- }
629
589
  printFinalSummary(state) {
630
590
  var _a;
631
591
  const totalTokens = ((_a = state.tokenUsage) === null || _a === void 0 ? void 0 : _a.total) || 0;
@@ -635,7 +595,6 @@ class McpGraphAgent {
635
595
  console.log(`🏁 \x1b[32;1m[审计任务全量结算]\x1b[0m`);
636
596
  console.log(` - 累计消耗总额: \x1b[33m${totalTokens}\x1b[0m Tokens`);
637
597
  console.log(` - 累计执行耗时: \x1b[36m${(totalMs / 1000).toFixed(2)}\x1b[0m s`);
638
- console.log(` - 审计文件总数: ${state.auditedFiles.length} 个`);
639
598
  console.log('═'.repeat(50) + '\n');
640
599
  }
641
600
  }
@@ -643,7 +602,6 @@ class McpGraphAgent {
643
602
  const workflow = new langgraph_1.StateGraph(AgentState)
644
603
  .addNode('agent', state => this.callModel(state))
645
604
  .addNode('tools', this.toolNode)
646
- .addNode('progress', state => this.trackProgress(state))
647
605
  .addEdge(langgraph_1.START, 'agent')
648
606
  .addConditionalEdges('agent', state => {
649
607
  const { messages } = state;
@@ -653,6 +611,9 @@ class McpGraphAgent {
653
611
  // 如果已消耗 Token 超过了 options 中设置的 maxTokens (假设是总限额)
654
612
  if (this.options.maxTokens && state.tokenUsage.total >= this.options.maxTokens) {
655
613
  console.warn('⚠️ [警告] 已达到最大 Token 限制,强制结束任务。');
614
+ state.messages.push(new messages_1.AIMessage({
615
+ content: `⚠️ 抱歉,当前任务消耗的 Token 已达上限 ${this.options.maxTokens}。为了防止无限循环,我已停止执行。你可以尝试分步骤指令,继续发送指令,直到任务完成。`
616
+ }));
656
617
  this.printFinalSummary(state);
657
618
  return langgraph_1.END;
658
619
  }
@@ -660,22 +621,23 @@ class McpGraphAgent {
660
621
  if (lastMsg.tool_calls && lastMsg.tool_calls.length > 0) {
661
622
  return 'tools';
662
623
  }
663
- // 2. 判定结束的条件:
664
- // - 模式是 auto 且审计完成
665
- // - 或者 AI 明确输出了结束语
666
- // - 或者 AI 输出了普通内容且没有工具调用(针对问答模式)
667
- const isAutoFinished = state.mode === 'auto' && state.auditedFiles.length > state.targetCount;
668
624
  const isFinalAnswer = content.includes('Final Answer');
669
- // 修复核心:如果 AI 只是在聊天(没有工具调用),直接结束,不要跳回 agent
670
- if (isAutoFinished || isFinalAnswer || state.mode === 'chat') {
625
+ if (isFinalAnswer) {
671
626
  this.printFinalSummary(state);
672
627
  return langgraph_1.END;
673
628
  }
674
- // 兜底:如果是在 auto 模式且还没干完活,才跳回 agent(通常不会走到这里)
629
+ if (messages.length > 50) {
630
+ state.messages.push(new messages_1.AIMessage({
631
+ content: `💡 **嘿,我们已经聊了很多了!**
632
+
633
+ 为了保证服务质量,我通常会在 50 步操作后停下来和您确认一下。这样可以避免我产生“幻觉”或者做一些无用功。
634
+
635
+ **任务还没完成吗?** 没关系,只要您回复“继续”,我马上就回来接着干!或者您有什么新的想法要告诉我?`
636
+ }));
637
+ return langgraph_1.END;
638
+ }
675
639
  return langgraph_1.END;
676
- })
677
- .addEdge('tools', 'progress')
678
- .addEdge('progress', 'agent');
640
+ }).addEdge('tools', 'agent');
679
641
  return workflow.compile({ checkpointer: this.checkpointer });
680
642
  }
681
643
  }
@@ -153,10 +153,7 @@ const getFilesystemTools = (targetDir) => {
153
153
  });
154
154
  const createDirectoryTool = (0, createTool_1.createTool)({
155
155
  name: 'create_directory',
156
- description: 'Create a new directory or ensure a directory exists. Can create multiple ' +
157
- 'nested directories in one operation. If the directory already exists, ' +
158
- 'this operation will succeed silently. Perfect for setting up directory ' +
159
- 'structures for projects or ensuring required paths exist. Only works within allowed directories.',
156
+ description: '递归创建目录。支持多级嵌套。若目录已存在则静默成功。仅限允许目录。',
160
157
  parameters: CreateDirectoryArgsSchema,
161
158
  handler: async (args) => {
162
159
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
@@ -167,10 +164,7 @@ const getFilesystemTools = (targetDir) => {
167
164
  });
168
165
  const listDirectoryWithSizesTool = (0, createTool_1.createTool)({
169
166
  name: 'list_directory',
170
- description: 'Get a detailed listing of all files and directories in a specified path, including sizes. ' +
171
- 'Results clearly distinguish between files and directories with [FILE] and [DIR] ' +
172
- 'prefixes. This tool is useful for understanding directory structure and ' +
173
- 'finding specific files within a directory. Only works within allowed directories.',
167
+ description: '列出目录内容。返回带 [FILE]/[DIR] 前缀的条目、大小及汇总。支持按名称或大小排序。',
174
168
  parameters: ListDirectoryWithSizesArgsSchema,
175
169
  handler: async (args) => {
176
170
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
@@ -257,10 +251,7 @@ const getFilesystemTools = (targetDir) => {
257
251
  });
258
252
  const moveFileTool = (0, createTool_1.createTool)({
259
253
  name: 'move_file',
260
- description: 'Move or rename files and directories. Can move files between directories ' +
261
- 'and rename them in a single operation. If the destination exists, the ' +
262
- 'operation will fail. Works across different directories and can be used ' +
263
- 'for simple renaming within the same directory. Both source and destination must be within allowed directories.',
254
+ description: '移动或重命名文件/目录。目标路径若已存在则失败。源与目标均须在允许目录内。',
264
255
  parameters: MoveFileArgsSchema,
265
256
  handler: async (args) => {
266
257
  const validSourcePath = await (0, lib_1.validatePath)(targetDir, args.source);
@@ -272,9 +263,7 @@ const getFilesystemTools = (targetDir) => {
272
263
  });
273
264
  const searchFilesTool = (0, createTool_1.createTool)({
274
265
  name: 'search_files',
275
- description: 'Search for files matching a specific pattern in a specified path. ' +
276
- 'Returns a list of files that match the pattern. Only works within allowed directories.' +
277
- 'Used only for filename search',
266
+ description: '在指定路径下搜索匹配模式的文件名。返回匹配的相对路径列表。',
278
267
  parameters: SearchFilesArgsSchema,
279
268
  handler: async (args) => {
280
269
  const combinedExcludes = [...DEFAULT_IGNORE, ...(args.excludePatterns || [])];
@@ -7,7 +7,7 @@ export interface ApiConfig {
7
7
  model: string;
8
8
  }
9
9
  export interface ToolInfo {
10
- type: "function";
10
+ type: 'function';
11
11
  function: {
12
12
  name: string;
13
13
  description?: string;
@@ -38,7 +38,6 @@ export interface GraphAgentOptions<T extends AgentGraphModel = any> extends Agen
38
38
  apiModel?: T;
39
39
  alwaysSystem?: boolean;
40
40
  recursionLimit?: number;
41
- maxTargetCount?: number;
42
41
  /** 是否启用流式输出,默认 false */
43
42
  stream?: boolean;
44
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saber2pr/ai-agent",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "AI Assistant CLI.",
5
5
  "author": "saber2pr",
6
6
  "license": "ISC",