foliko 1.1.71 → 1.1.72

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.
@@ -59,7 +59,8 @@ const markdownTheme = {
59
59
  strikethrough: (text) => chalk.strikethrough(text),
60
60
  underline: (text) => chalk.underline(text),
61
61
  highlightCode: (code, lang) => {
62
- const highlighted = hl.highlight(code, { language: lang || 'text', theme: 'nord' });
62
+ const language = lang && hl.supportsLanguage(lang) ? lang : 'text';
63
+ const highlighted = hl.highlight(code, { language, theme: 'nord' });
63
64
  return highlighted.split('\n');
64
65
  },
65
66
  codeBlockIndent: "",
@@ -191,13 +192,13 @@ class ChatUI {
191
192
  ERROR: 'red',
192
193
  }
193
194
  // 监听错误日志,显示到 tooler
194
- this._logHandler = (data) => {
195
- if(!Object.keys(level_dict).includes(data.level))return;
196
- const level=folikoTerracotta(`[${data.level}]`)
197
- const level_key=level_dict[data.level]||'dim'
198
- this.statusBar.notifier.show(`${level} ${chalk[level_key](data.message)}`)
199
- };
200
- logger.on('log', this._logHandler);
195
+ // this._logHandler = (data) => {
196
+ // if(!Object.keys(level_dict).includes(data.level))return;
197
+ // const level=folikoTerracotta(`[${data.level}]`)
198
+ // const level_key=level_dict[data.level]||'dim'
199
+ // this.statusBar.notifier.show(`${level} ${chalk[level_key](data.message)}`)
200
+ // };
201
+ // logger.on('log', this._logHandler);
201
202
 
202
203
  // 监听通知事件,显示到通知区域
203
204
  this._notificationHandler = (data) => {
@@ -246,10 +247,10 @@ class ChatUI {
246
247
  clearTimeout(this._streamBufferTimer);
247
248
  this._streamBufferTimer = null;
248
249
  }
249
- if (this._logHandler && this.agent.framework) {
250
- logger.off('log', this._logHandler);
251
- this._logHandler = null;
252
- }
250
+ // if (this._logHandler && this.agent.framework) {
251
+ // logger.off('log', this._logHandler);
252
+ // this._logHandler = null;
253
+ // }
253
254
  if (this._notificationHandler && this.agent.framework) {
254
255
  this.agent.framework.off('notification', this._notificationHandler);
255
256
  this._notificationHandler = null;
@@ -283,6 +284,7 @@ class ChatUI {
283
284
  queue.add(function(){
284
285
  var next = this.next;
285
286
  self.agent.sendMessage(message, { sessionId:self.sessionId }).then(next).catch(function(e){
287
+ self.create_message(chalk.red(`[错误] ${e?.message || e || '发送消息失败'}\n`), colored('● ', RED))
286
288
  self.clear_message_done();
287
289
  });
288
290
  })
@@ -659,7 +661,7 @@ class ChatUI {
659
661
  this._streamBufferTimer = setTimeout(() => this._flushStreamBuffer(), 0);
660
662
  }
661
663
  } else if (chunk.type === 'tool-call') {
662
- const args = chunk.input ? JSON.stringify(chunk.input).slice(0, 50) : '';
664
+ const args = chunk.input ? JSON.stringify(chunk.input).slice(0, 30) : '';
663
665
  this.statusBar.tooler.show(`${chalk.yellow('[Tool]')} ${folikoGold(chunk.toolName)} ${chalk.gray(args+'...')}`)
664
666
  } else if(chunk.type==='tool-result'){
665
667
  const result = chunk.result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.1.71",
3
+ "version": "1.1.72",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -159,7 +159,7 @@ class ExtensionExecutorPlugin extends Plugin {
159
159
  if (plugin === 'mcp' && this._mcpExecutor) {
160
160
  const mcpToolDef = this._mcpExecutor.tools?.[tool];
161
161
  if (mcpToolDef && mcpToolDef.execute) {
162
- log.info(` ext_call [MCP]: tool=${tool}`);
162
+ //log.info(` ext_call [MCP]: tool=${tool}`);
163
163
  return await mcpToolDef.execute(toolArgs || {});
164
164
  }
165
165
  return { success: false, error: `MCP tool '${tool}' not found` };
@@ -10,6 +10,39 @@ const { logger } = require('../utils/logger');
10
10
  const log = logger.child('SkillManager');
11
11
  const { z } = require('zod');
12
12
  const { Command } = require('commander');
13
+
14
+ /**
15
+ * 简单的 shell 参数解析器(处理带引号的参数)
16
+ * @param {string} str - 参数字符串
17
+ * @returns {string[]} 参数数组
18
+ */
19
+ function parseShellArgs(str) {
20
+ if (!str || !str.trim()) return [];
21
+ const args = [];
22
+ let current = '';
23
+ let inQuote = false;
24
+ let quoteChar = '';
25
+
26
+ for (let i = 0; i < str.length; i++) {
27
+ const char = str[i];
28
+ if (!inQuote && (char === '"' || char === "'")) {
29
+ inQuote = true;
30
+ quoteChar = char;
31
+ } else if (inQuote && char === quoteChar) {
32
+ inQuote = false;
33
+ } else if (!inQuote && char === ' ') {
34
+ if (current.trim()) {
35
+ args.push(current.trim());
36
+ current = '';
37
+ }
38
+ } else {
39
+ current += char;
40
+ }
41
+ }
42
+ if (current.trim()) args.push(current.trim());
43
+ return args;
44
+ }
45
+
13
46
  /**
14
47
  * 验证 skill 名称
15
48
  * 1-64字符,字母数字、下划线和连字符,不能以连字符或下划线开头或结尾
@@ -185,14 +218,20 @@ class Skill {
185
218
  let argumentParser = cmd.argumentParser;
186
219
  if (!argumentParser && Array.isArray(cmd.options) && cmd.options.length > 0) {
187
220
  argumentParser = (rawArgs) => {
188
-
189
- const program = new Command();
190
- for (const opt of cmd.options) {
191
- program.option(opt.flags, opt.description, opt.defaultValue);
221
+ try {
222
+ const program = new Command();
223
+ program.exitOverride(); // 不自动退出
224
+ program.configureOutput({ writeErr: () => {} }); // 隐藏错误输出
225
+ for (const opt of cmd.options) {
226
+ program.option(opt.flags, opt.description, opt.defaultValue);
227
+ }
228
+ program.arguments('[args...]');
229
+ program.parse(['node', 'cmd', ...parseShellArgs(rawArgs)]);
230
+ return { ...program.opts(), args: program.args };
231
+ } catch (err) {
232
+ log.warn(`[SkillManager] Command parse error: ${err.message}`);
233
+ return { args: rawArgs };
192
234
  }
193
- program.arguments('[args...]');
194
- program.parse(['node', 'cmd', ...(rawArgs || '').split(' ').filter(Boolean)]);
195
- return { ...program.opts(), args: program.args };
196
235
  };
197
236
  }
198
237
 
@@ -500,6 +500,8 @@ class AgentChatHandler extends EventEmitter {
500
500
  const stream = result.fullStream;
501
501
  let fullText = '';
502
502
  const iterator = stream[Symbol.asyncIterator] ? stream : stream.fullStream;
503
+ // 追踪发送的 tool-call ID,过滤 API 返回的不匹配 tool-result
504
+ const sentToolCallIds = new Set();
503
505
 
504
506
  for await (const part of iterator || stream) {
505
507
  if (part.type === 'text-delta') {
@@ -510,8 +512,16 @@ class AgentChatHandler extends EventEmitter {
510
512
  reasoningContent += part.text || '';
511
513
  yield { type: 'thinking', text: part.text };
512
514
  } else if (part.type === 'tool-call') {
515
+ const toolCallId = part.toolCallId;
516
+ if (toolCallId) sentToolCallIds.add(toolCallId);
513
517
  yield { type: 'tool-call', toolName: part.toolName, input: part.input };
514
518
  } else if (part.type === 'tool-result') {
519
+ // 过滤 toolCallId 不匹配的 tool-result(API bug)
520
+ const resultToolCallId = part.toolCallId;
521
+ if (resultToolCallId && !sentToolCallIds.has(resultToolCallId)) {
522
+ logger.debug(`[stream]过滤不匹配的 tool-result: ${resultToolCallId}`);
523
+ continue;
524
+ }
515
525
  yield { type: 'tool-result', toolName: part.toolName, result: part.output };
516
526
  } else if (part.type === 'error') {
517
527
  // 统一错误消息