foliko 1.1.4 → 1.1.6

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.
Files changed (69) hide show
  1. package/.agent/agents/code-assistant.json +4 -1
  2. package/.agent/agents/file-assistant.json +4 -1
  3. package/.agent/agents/orchestrator-demo.md +53 -0
  4. package/.agent/agents/orchestrator.json +7 -0
  5. package/.agent/data/plugins-state.json +4 -0
  6. package/.agent/mcp_config.json +6 -0
  7. package/.agent/memory/feedback/mnrsiuoc-e1ru74.md +9 -0
  8. package/.agent/memory/feedback/mnrt2mmz-98az6n.md +9 -0
  9. package/.agent/memory/feedback/mnrtqrhm-kxsicz.md +9 -0
  10. package/.agent/memory/feedback/mnrts8vg-i0ngzp.md +15 -0
  11. package/.agent/memory/feedback/mnrtt7jt-c0trb2.md +9 -0
  12. package/.agent/memory/feedback/mnruc2f0-5s52la.md +16 -0
  13. package/.agent/memory/feedback/mnrumbmx-63sa0v.md +9 -0
  14. package/.agent/memory/project/mnrp7p5n-8enm2a.md +31 -0
  15. package/.agent/memory/project/mnrp9ifb-yynks0.md +40 -0
  16. package/.agent/memory/project/mnrpb3b8-f617s4.md +25 -0
  17. package/.agent/memory/project/mnrrmqgg-focprv.md +9 -0
  18. package/.agent/memory/project/mnrtykbh-6atsor.md +9 -0
  19. package/.agent/memory/project/mnru9jiu-kgau16.md +35 -0
  20. package/.agent/memory/reference/mnrnvpwo-rcqv9m.md +52 -0
  21. package/.agent/memory/reference/mnrovxvz-zy9xqm.md +25 -0
  22. package/.agent/memory/reference/mnroxabj-1b3930.md +68 -0
  23. package/.agent/memory/reference/mnrpjtlp-mnb9od.md +35 -0
  24. package/.agent/memory/reference/mnrps1x3-6b8xfm.md +28 -0
  25. package/.agent/memory/reference/mnrpt9ov-15er5w.md +22 -0
  26. package/.agent/memory/reference/mnrq82dn-y9tv9e.md +50 -0
  27. package/.agent/memory/reference/mnrqnr5v-v75drf.md +34 -0
  28. package/.agent/memory/reference/mnrrfzys-urudaf.md +31 -0
  29. package/.agent/memory/reference/mnrrocha-t0027n.md +21 -0
  30. package/.agent/memory/reference/mnrukklc-bxndsb.md +35 -0
  31. package/.agent/memory/user/mnrt39t8-8eosy0.md +9 -0
  32. package/.agent/plugins/marknative/fonts.zip +0 -0
  33. package/.agent/sessions/cli_default.json +4493 -1183
  34. package/.agent/test-agent.js +35 -0
  35. package/cli/src/commands/chat.js +69 -1
  36. package/cli/src/index.js +11 -2
  37. package/foliko-cloud-rising.png +0 -0
  38. package/foliko-dawn-of-ai.png +0 -0
  39. package/foliko-mindful-observation.png +0 -0
  40. package/foliko-stellar-dreams.png +0 -0
  41. package/foliko-zen-jing.png +0 -0
  42. package/foliko-zen-kong.png +0 -0
  43. package/foliko-zen-wu.png +0 -0
  44. package/package.json +2 -2
  45. package/plugins/coordinator-plugin.js +282 -0
  46. package/plugins/default-plugins.js +1 -1
  47. package/plugins/extension-executor-plugin.js +27 -1
  48. package/plugins/memory-plugin.js +228 -57
  49. package/src/core/agent-chat.js +206 -115
  50. package/src/core/agent.js +258 -71
  51. package/src/core/coordinator-manager.js +341 -0
  52. package/src/core/framework.js +50 -0
  53. package/src/core/plugin-base.js +30 -17
  54. package/src/core/sub-agent-config.js +8 -1
  55. package/src/core/worker-agent.js +176 -0
  56. package/src/executors/mcp-executor.js +4 -4
  57. package/zen_karesansui.png +0 -0
  58. package/.agent/plugins/marknative/update-readme.js +0 -134
  59. package/output/emoji-segoe-test-v2.png +0 -0
  60. package/output/emoji-segoe-test.png +0 -0
  61. package/output/emoji-test.png +0 -0
  62. package/output/emoji-windows-test.png +0 -0
  63. package/output/foliko-emoji-poster.png +0 -0
  64. package/output/foliko-muji-poster-final.png +0 -0
  65. package/output/foliko-muji-poster-v2.png +0 -0
  66. package/output/foliko-muji-poster.png +0 -0
  67. package/output/foliko-share.png +0 -0
  68. package/output/progress-circle-test.png +0 -0
  69. package/output/vb-agent-poster.png +0 -0
@@ -10,7 +10,7 @@ const { z } = require('zod')
10
10
  const fs = require('fs')
11
11
  const path = require('path')
12
12
  const { EventEmitter } = require('events')
13
-
13
+ const {cleanResponse} =require('../src/utils')
14
14
  /**
15
15
  * 记忆类型常量
16
16
  */
@@ -147,44 +147,60 @@ function _extractJSON(text) {
147
147
 
148
148
  // 去掉首尾空白
149
149
  text = text.trim()
150
- if (!text.startsWith('{')) return null
151
150
 
152
- // 查找配对的括号位置
153
- let braceCount = 0
154
- let inString = false
155
- let escaped = false
151
+ // 方法1:如果直接以 { 开头,尝试直接解析
152
+ if (text.startsWith('{')) {
153
+ // 查找配对的括号位置
154
+ let braceCount = 0
155
+ let inString = false
156
+ let escaped = false
156
157
 
157
- for (let i = 0; i < text.length; i++) {
158
- const char = text[i]
158
+ for (let i = 0; i < text.length; i++) {
159
+ const char = text[i]
159
160
 
160
- if (escaped) {
161
- escaped = false
162
- continue
163
- }
161
+ if (escaped) {
162
+ escaped = false
163
+ continue
164
+ }
164
165
 
165
- if (char === '\\' && inString) {
166
- escaped = true
167
- continue
168
- }
166
+ if (char === '\\' && inString) {
167
+ escaped = true
168
+ continue
169
+ }
169
170
 
170
- if (char === '"') {
171
- inString = !inString
172
- continue
173
- }
171
+ if (char === '"') {
172
+ inString = !inString
173
+ continue
174
+ }
174
175
 
175
- if (!inString) {
176
- if (char === '{') {
177
- braceCount++
178
- } else if (char === '}') {
179
- braceCount--
180
- if (braceCount === 0) {
181
- // 找到完整的 JSON 对象
182
- return text.substring(0, i + 1)
176
+ if (!inString) {
177
+ if (char === '{') {
178
+ braceCount++
179
+ } else if (char === '}') {
180
+ braceCount--
181
+ if (braceCount === 0) {
182
+ return text.substring(0, i + 1)
183
+ }
183
184
  }
184
185
  }
185
186
  }
186
187
  }
187
188
 
189
+ // 方法2:从 thinking 文本中查找 JSON(处理 AI 输出 thinking 的情况)
190
+ // 查找最后一个 { 到最后一个 } 之间的内容
191
+ const firstBrace = text.indexOf('{')
192
+ const lastBrace = text.lastIndexOf('}')
193
+
194
+ if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
195
+ const potential = text.substring(firstBrace, lastBrace + 1)
196
+ try {
197
+ JSON.parse(potential) // 验证是否是有效 JSON
198
+ return potential
199
+ } catch (e) {
200
+ // 不是有效 JSON,继续尝试其他方法
201
+ }
202
+ }
203
+
188
204
  return null
189
205
  }
190
206
 
@@ -541,6 +557,7 @@ class MemoryPlugin extends Plugin {
541
557
  this._framework = null
542
558
  this._store = null
543
559
  this._memoryAgent = null // 用于记忆提取的子 agent
560
+ this._isExtracting = false // 防止递归调用的标志
544
561
  }
545
562
 
546
563
  install(framework) {
@@ -552,6 +569,15 @@ class MemoryPlugin extends Plugin {
552
569
  // 初始化存储
553
570
  this._store = new MemoryStore(this.config.memoryDir)
554
571
 
572
+ // 创建复用的记忆提取子 Agent
573
+ this._memoryAgent = framework.createSubAgent({
574
+ name: 'memory-extractor',
575
+ role: '记忆提取专家',
576
+ description: '专门负责从对话和错误中提取记忆的子 Agent',
577
+ tools: {}, // 不需要额外工具,隔离主 Agent 工具
578
+ parentTools: [] // 不继承任何工具,纯粹做提取
579
+ })
580
+
555
581
  // 注册工具
556
582
  this._registerTools(framework)
557
583
 
@@ -787,12 +813,156 @@ class MemoryPlugin extends Plugin {
787
813
  _setupAutoExtract(framework) {
788
814
  // 监听 agent 消息完成事件,在对话轮次结束时提取记忆
789
815
  framework.on('agent:message', async ({ userMessage, assistantResponse }) => {
816
+ // 防止递归调用:如果正在提取中,跳过此次事件
817
+ if (this._isExtracting) {
818
+ log.debug('Skipping agent:message event during memory extraction')
819
+ return
820
+ }
821
+
790
822
  try {
791
823
  await this._extractMemoryFromConversation(userMessage, assistantResponse)
792
824
  } catch (err) {
793
825
  log.warn('Auto memory extraction failed:', err.message)
794
826
  }
795
827
  })
828
+
829
+ // 监听所有工具调用错误(包括 ext_call, MCP, 原生工具等)
830
+ framework.on('tool:error', async (data) => {
831
+ try {
832
+ await this._extractErrorLesson({
833
+ name: data.name || data.tool || 'unknown-tool',
834
+ args: data.args || {},
835
+ error: data.error || data.message || String(data),
836
+ source: data.source || 'tool'
837
+ })
838
+ } catch (err) {
839
+ log.warn('Auto tool error lesson extraction failed:', err.message)
840
+ }
841
+ })
842
+
843
+ // 监听 tool-call 别名(AI SDK 6.x 格式)
844
+ framework.on('tool-error', async (data) => {
845
+ try {
846
+ await this._extractErrorLesson({
847
+ name: data.name || data.tool || 'unknown-tool',
848
+ args: data.args || {},
849
+ error: data.error || data.message || String(data),
850
+ source: data.source || 'tool'
851
+ })
852
+ } catch (err) {
853
+ log.warn('Auto tool-error lesson extraction failed:', err.message)
854
+ }
855
+ })
856
+
857
+ // 监听工具重试事件(重试可能意味着配置或参数问题)
858
+ framework.on('tool:retry', async (data) => {
859
+ try {
860
+ const { name, args, error, attempt } = data
861
+ await this._extractErrorLesson({
862
+ name: name || 'unknown-tool',
863
+ args: args || {},
864
+ error: `Retry attempt ${attempt} failed: ${error || data.message || 'unknown'}`,
865
+ source: 'tool-retry'
866
+ })
867
+ } catch (err) {
868
+ log.warn('Auto tool retry lesson extraction failed:', err.message)
869
+ }
870
+ })
871
+
872
+ // 监听 agent 对话过程中的错误
873
+ framework.on('error', async (data) => {
874
+ try {
875
+ const errorObj = typeof data === 'object' ? data : { error: String(data) }
876
+ await this._extractErrorLesson({
877
+ name: errorObj.context || 'agent-error',
878
+ args: errorObj.data || {},
879
+ error: errorObj.error || errorObj.message || String(data),
880
+ source: 'agent'
881
+ })
882
+ } catch (err) {
883
+ log.warn('Auto agent error lesson extraction failed:', err.message)
884
+ }
885
+ })
886
+
887
+ // 监听 agent status 错误
888
+ framework.on('status', async (data) => {
889
+ if (data.status === 'error' && data.error) {
890
+ try {
891
+ await this._extractErrorLesson({
892
+ name: 'agent-status-error',
893
+ args: {},
894
+ error: data.error,
895
+ source: 'agent-status'
896
+ })
897
+ } catch (err) {
898
+ log.warn('Auto status error lesson extraction failed:', err.message)
899
+ }
900
+ }
901
+ })
902
+
903
+ // 监听会话上下文错误
904
+ framework.on('session:context-destroyed', async (data) => {
905
+ // 上下文销毁通常是正常的,但如果有错误信息则记录
906
+ if (data && data.error) {
907
+ try {
908
+ await this._extractErrorLesson({
909
+ name: 'session-context-error',
910
+ args: { sessionId: data.sessionId },
911
+ error: data.error,
912
+ source: 'session'
913
+ })
914
+ } catch (err) {
915
+ log.warn('Auto session error lesson extraction failed:', err.message)
916
+ }
917
+ }
918
+ })
919
+ }
920
+
921
+ /**
922
+ * 从错误中提取经验教训并保存
923
+ */
924
+ async _extractErrorLesson(errorData) {
925
+ const { name, args, error, source } = errorData
926
+
927
+ // 构建提取提示 - 强调"如何下次避免"
928
+ const extractPrompt = `你是一个经验总结专家。请分析以下错误,总结如何在未来避免类似错误。
929
+
930
+ 错误信息:
931
+ - 工具名称: ${name}
932
+ - 调用参数: ${JSON.stringify(args)}
933
+ - 错误原因: ${error}
934
+ - 来源: ${source || 'unknown'}
935
+
936
+ 请按以下格式返回(仅返回 JSON,不要有其他内容):
937
+ {
938
+ "extract": true/false,
939
+ "name": "【避免指南】具体说明避免什么(如:避免不传 plugin 参数调用 ext_call)",
940
+ "tags": ["error", "avoid", "${name}"],
941
+ "content": "以【下次这样做】为开头,说明正确的做法。例如:\n【下次这样做】调用 ext_call 时必须传入 plugin、tool、args 三个参数。错误原因:缺少 plugin 参数导致扩展插件无法定位。"
942
+ }
943
+
944
+ 如果错误是无关紧要的(如用户主动取消、超时重试等),返回:
945
+ {"extract": false}`
946
+
947
+ try {
948
+ const result = await this._runMemoryExtractionAgent(extractPrompt)
949
+
950
+ if (result && result.extract === true) {
951
+ try {
952
+ const saved = this._store.add({
953
+ name: result.name,
954
+ type: result.type || MEMORY_TYPES.FEEDBACK,
955
+ project: null,
956
+ tags: result.tags || ['error', 'avoid'],
957
+ body: result.content
958
+ })
959
+ } catch (saveErr) {
960
+ log.warn('[Memory] Failed to save error lesson:', saveErr.message)
961
+ }
962
+ }
963
+ } catch (err) {
964
+ log.warn('[Memory] Error lesson extraction failed:', err.message)
965
+ }
796
966
  }
797
967
 
798
968
  /**
@@ -851,35 +1021,24 @@ ${typeof assistantResponse === 'string' ? assistantResponse : JSON.stringify(ass
851
1021
  * 使用独立的轻量子 agent 处理记忆提取,不占用主 agent 资源
852
1022
  */
853
1023
  async _runMemoryExtractionAgent(prompt) {
854
- try {
855
- // 获取 ai 插件获取模型配置
856
- const aiPlugin = this._framework.pluginManager.get('ai')
857
- if (!aiPlugin) {
858
- log.warn('Memory extraction: AI plugin not found')
859
- return null
860
- }
1024
+ if (!this._memoryAgent) {
1025
+ log.warn('Memory extraction: memory sub-agent not found')
1026
+ return null
1027
+ }
861
1028
 
862
- const aiClient = aiPlugin.getAIClient()
863
- if (!aiClient) {
864
- log.warn('Memory extraction: AI client not found')
865
- return null
866
- }
1029
+ // 设置提取标志,防止递归调用
1030
+ this._isExtracting = true
867
1031
 
868
- // 直接使用 generateText,设置 maxSteps: 1 避免多轮对话消耗过多 context
869
- const { generateText } = require('ai')
1032
+ try {
1033
+ const fullPrompt = `你是一个记忆提取专家。直接返回 JSON 格式的答案,不要输出任何思考过程、解释或其他内容。格式:{"extract":true/false,"name":"...","tags":[...],"content":"..."}
870
1034
 
871
- const { text } = await generateText({
872
- model: aiClient,
873
- system: '你是一个记忆提取专家,直接返回 JSON,不要有其他内容。',
874
- prompt,
875
- maxTokens: 500,
876
- temperature: 0.3,
877
- maxSteps: 1
878
- })
1035
+ ${prompt}`
1036
+
1037
+ const result = await this._memoryAgent.chat(fullPrompt, { maxSteps: 1 })
879
1038
 
880
1039
  // 解析 JSON 响应
881
- if (text) {
882
- const cleanedText = text.trim()
1040
+ if (result?.message) {
1041
+ const cleanedText = cleanResponse(result.message.trim())
883
1042
 
884
1043
  // 移除 markdown 代码块包裹
885
1044
  let jsonText = cleanedText
@@ -891,14 +1050,14 @@ ${typeof assistantResponse === 'string' ? assistantResponse : JSON.stringify(ass
891
1050
  const jsonStr = _extractJSON(jsonText)
892
1051
  if (jsonStr) {
893
1052
  try {
894
- const result = JSON.parse(jsonStr)
895
- return result
1053
+ const parsed = JSON.parse(jsonStr)
1054
+ return parsed
896
1055
  } catch (parseErr) {
897
1056
  // JSON 解析失败,尝试清理不可见字符后重试
898
1057
  const cleaned = jsonStr.replace(/[\u0000-\u001F\u007F-\u009F\u200B-\u200F\u2028\u2029]/g, '')
899
1058
  try {
900
- const result = JSON.parse(cleaned)
901
- return result
1059
+ const parsed = JSON.parse(cleaned)
1060
+ return parsed
902
1061
  } catch (e) {
903
1062
  log.warn('Failed to parse extracted JSON:', jsonStr.substring(0, 200))
904
1063
  }
@@ -907,6 +1066,9 @@ ${typeof assistantResponse === 'string' ? assistantResponse : JSON.stringify(ass
907
1066
  }
908
1067
  } catch (err) {
909
1068
  log.warn('Memory extraction agent failed:', err.message)
1069
+ } finally {
1070
+ // 必须清除提取标志
1071
+ this._isExtracting = false
910
1072
  }
911
1073
 
912
1074
  return null
@@ -955,13 +1117,22 @@ ${typeof assistantResponse === 'string' ? assistantResponse : JSON.stringify(ass
955
1117
  try {
956
1118
  const userMemories = this._store.listByType(MEMORY_TYPES.USER, 10)
957
1119
  const projectMemories = this._store.listByType(MEMORY_TYPES.PROJECT, 5)
1120
+ const feedbackMemories = this._store.search('', { type: MEMORY_TYPES.FEEDBACK, tags: ['avoid'], limit: 10 })
958
1121
 
959
- if (userMemories.length === 0 && projectMemories.length === 0) {
1122
+ if (userMemories.length === 0 && projectMemories.length === 0 && feedbackMemories.length === 0) {
960
1123
  return null
961
1124
  }
962
1125
 
963
1126
  const parts = ['【记忆上下文】']
964
1127
 
1128
+ // 错误避免指南(优先显示,放在最前面)
1129
+ if (feedbackMemories.length > 0) {
1130
+ parts.push('【错误避免指南 - 必须遵守】')
1131
+ for (const m of feedbackMemories) {
1132
+ parts.push(`- ${m.body}`)
1133
+ }
1134
+ }
1135
+
965
1136
  if (userMemories.length > 0) {
966
1137
  parts.push('【用户偏好】')
967
1138
  for (const m of userMemories) {