foliko 1.0.87 → 1.1.1

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 (259) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +325 -21
  39. package/.agent/data/plugins-state.json +194 -162
  40. package/.agent/data/puppeteer-sessions/undefined.json +6 -0
  41. package/.agent/mcp_config.json +0 -1
  42. package/.agent/mcp_config_updated.json +12 -0
  43. package/.agent/plugins/poster-plugin/README.md +304 -0
  44. package/.agent/plugins/poster-plugin/fonts/NotoColorEmoji-Regular.ttf +0 -0
  45. package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
  46. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
  47. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221/347/262/227/344/275/223.ttf +0 -0
  48. package/.agent/plugins/poster-plugin/index.js +13 -0
  49. package/.agent/plugins/poster-plugin/package.json +28 -0
  50. package/.agent/plugins/poster-plugin/src/canvas.js +161 -0
  51. package/.agent/plugins/poster-plugin/src/components/arrow.js +84 -0
  52. package/.agent/plugins/poster-plugin/src/components/avatar.js +71 -0
  53. package/.agent/plugins/poster-plugin/src/components/badge.js +85 -0
  54. package/.agent/plugins/poster-plugin/src/components/card.js +88 -0
  55. package/.agent/plugins/poster-plugin/src/components/chart.js +127 -0
  56. package/.agent/plugins/poster-plugin/src/components/chip.js +88 -0
  57. package/.agent/plugins/poster-plugin/src/components/columns.js +107 -0
  58. package/.agent/plugins/poster-plugin/src/components/cta.js +85 -0
  59. package/.agent/plugins/poster-plugin/src/components/divider.js +55 -0
  60. package/.agent/plugins/poster-plugin/src/components/feature.js +85 -0
  61. package/.agent/plugins/poster-plugin/src/components/featureGrid.js +112 -0
  62. package/.agent/plugins/poster-plugin/src/components/grid.js +118 -0
  63. package/.agent/plugins/poster-plugin/src/components/imageFrame.js +155 -0
  64. package/.agent/plugins/poster-plugin/src/components/index.js +62 -0
  65. package/.agent/plugins/poster-plugin/src/components/listItem.js +146 -0
  66. package/.agent/plugins/poster-plugin/src/components/notification.js +123 -0
  67. package/.agent/plugins/poster-plugin/src/components/progress.js +79 -0
  68. package/.agent/plugins/poster-plugin/src/components/progressCircle.js +117 -0
  69. package/.agent/plugins/poster-plugin/src/components/quote.js +97 -0
  70. package/.agent/plugins/poster-plugin/src/components/rating.js +85 -0
  71. package/.agent/plugins/poster-plugin/src/components/star.js +70 -0
  72. package/.agent/plugins/poster-plugin/src/components/statCard.js +105 -0
  73. package/.agent/plugins/poster-plugin/src/components/stepper.js +118 -0
  74. package/.agent/plugins/poster-plugin/src/components/table.js +159 -0
  75. package/.agent/plugins/poster-plugin/src/components/tagCloud.js +78 -0
  76. package/.agent/plugins/poster-plugin/src/components/timeline.js +105 -0
  77. package/.agent/plugins/poster-plugin/src/components/watermark.js +52 -0
  78. package/.agent/plugins/poster-plugin/src/composer.js +1904 -0
  79. package/.agent/plugins/poster-plugin/src/elements/artText.js +60 -0
  80. package/.agent/plugins/poster-plugin/src/elements/background.js +52 -0
  81. package/.agent/plugins/poster-plugin/src/elements/circle.js +31 -0
  82. package/.agent/plugins/poster-plugin/src/elements/image.js +71 -0
  83. package/.agent/plugins/poster-plugin/src/elements/index.js +26 -0
  84. package/.agent/plugins/poster-plugin/src/elements/line.js +23 -0
  85. package/.agent/plugins/poster-plugin/src/elements/polygon.js +63 -0
  86. package/.agent/plugins/poster-plugin/src/elements/rectangle.js +32 -0
  87. package/.agent/plugins/poster-plugin/src/elements/svg.js +92 -0
  88. package/.agent/plugins/poster-plugin/src/elements/text.js +107 -0
  89. package/.agent/plugins/poster-plugin/src/fonts.js +233 -0
  90. package/.agent/plugins/poster-plugin/src/index.js +1658 -0
  91. package/.agent/plugins/poster-plugin/src/presets.js +36 -0
  92. package/.agent/plugins/poster-plugin/src/templates/business.js +60 -0
  93. package/.agent/plugins/poster-plugin/src/templates/gradient.js +64 -0
  94. package/.agent/plugins/poster-plugin/src/templates/index.js +43 -0
  95. package/.agent/plugins/poster-plugin/src/templates/modern.js +69 -0
  96. package/.agent/plugins/poster-plugin/src/templates/simple.js +58 -0
  97. package/.agent/plugins/poster-plugin/src/templates/social.js +62 -0
  98. package/.agent/plugins/poster-plugin/src/templates/tech.js +84 -0
  99. package/.agent/plugins/{temp-repo/puppeteer-plugin → puppeteer-plugin}/index.js +1 -1
  100. package/.agent/plugins.json +5 -11
  101. package/.agent/rules/GEMINI.md +273 -0
  102. package/.agent/rules/allow-rule.md +77 -0
  103. package/.agent/rules/log-rule.md +83 -0
  104. package/.agent/rules/security-rule.md +93 -0
  105. package/.agent/scripts/auto_preview.py +148 -0
  106. package/.agent/scripts/checklist.py +217 -0
  107. package/.agent/scripts/session_manager.py +120 -0
  108. package/.agent/scripts/verify_all.py +327 -0
  109. package/.agent/sessions/cli_default.json +419 -0
  110. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +2195 -0
  111. package/.agent/skills/api-patterns/SKILL.md +81 -0
  112. package/.agent/skills/api-patterns/api-style.md +42 -0
  113. package/.agent/skills/api-patterns/auth.md +24 -0
  114. package/.agent/skills/api-patterns/documentation.md +26 -0
  115. package/.agent/skills/api-patterns/graphql.md +41 -0
  116. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  117. package/.agent/skills/api-patterns/response.md +37 -0
  118. package/.agent/skills/api-patterns/rest.md +40 -0
  119. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  120. package/.agent/skills/api-patterns/security-testing.md +122 -0
  121. package/.agent/skills/api-patterns/trpc.md +41 -0
  122. package/.agent/skills/api-patterns/versioning.md +22 -0
  123. package/.agent/skills/app-builder/SKILL.md +75 -0
  124. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  125. package/.agent/skills/app-builder/feature-building.md +53 -0
  126. package/.agent/skills/app-builder/project-detection.md +34 -0
  127. package/.agent/skills/app-builder/scaffolding.md +118 -0
  128. package/.agent/skills/app-builder/tech-stack.md +40 -0
  129. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  130. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  131. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  132. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  133. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  134. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  135. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  136. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  137. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  138. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  139. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  140. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  141. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  142. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  143. package/.agent/skills/architecture/SKILL.md +55 -0
  144. package/.agent/skills/architecture/context-discovery.md +43 -0
  145. package/.agent/skills/architecture/examples.md +94 -0
  146. package/.agent/skills/architecture/pattern-selection.md +68 -0
  147. package/.agent/skills/architecture/patterns-reference.md +50 -0
  148. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  149. package/.agent/skills/clean-code/SKILL.md +201 -0
  150. package/.agent/skills/doc.md +177 -0
  151. package/.agent/skills/frontend-design/SKILL.md +418 -0
  152. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  153. package/.agent/skills/frontend-design/color-system.md +311 -0
  154. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  155. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  156. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  157. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  158. package/.agent/skills/frontend-design/typography-system.md +345 -0
  159. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  160. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  161. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  162. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  163. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  164. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  165. package/.agent/workflows/brainstorm.md +113 -0
  166. package/.agent/workflows/create.md +59 -0
  167. package/.agent/workflows/debug.md +103 -0
  168. package/.agent/workflows/deploy.md +176 -0
  169. package/.agent/workflows/enhance.md +63 -0
  170. package/.agent/workflows/orchestrate.md +237 -0
  171. package/.agent/workflows/plan.md +89 -0
  172. package/.agent/workflows/preview.md +81 -0
  173. package/.agent/workflows/simple-test.md +42 -0
  174. package/.agent/workflows/status.md +86 -0
  175. package/.agent/workflows/structured-orchestrate.md +180 -0
  176. package/.agent/workflows/test.md +144 -0
  177. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  178. package/.claude/settings.local.json +20 -8
  179. package/.env.example +56 -56
  180. package/CLAUDE.md +144 -108
  181. package/README.md +441 -441
  182. package/calc_tokens_weixin.js +81 -0
  183. package/cli/src/commands/chat.js +2 -1
  184. package/docs/CONTEXT_DESIGN.md +1596 -0
  185. package/examples/test-concurrent-chat.js +60 -60
  186. package/foliko-creative-3.png +0 -0
  187. package/foliko-creative-4.png +0 -0
  188. package/foliko-creative-5.png +0 -0
  189. package/package.json +2 -2
  190. package/plugins/default-plugins.js +2 -1
  191. package/plugins/extension-executor-plugin.js +91 -2
  192. package/plugins/file-system-plugin.js +2 -2
  193. package/plugins/memory-plugin.js +984 -0
  194. package/plugins/session-plugin.js +57 -1
  195. package/plugins/weixin-plugin.js +33 -23
  196. package/skills/find-skills/AGENTS.md +162 -162
  197. package/skills/find-skills/SKILL.md +133 -133
  198. package/skills/poster-guide/SKILL.md +1059 -0
  199. package/skills/python-plugin-dev/SKILL.md +238 -238
  200. package/skills/skill-guide/SKILL.md +130 -108
  201. package/src/capabilities/skill-manager.js +99 -0
  202. package/src/core/agent-chat.js +620 -141
  203. package/src/core/agent-context.js +188 -0
  204. package/src/core/agent.js +6 -2
  205. package/src/core/context-manager.js +283 -0
  206. package/src/core/framework.js +264 -3
  207. package/src/core/plugin-manager.js +79 -2
  208. package/src/core/request-context.js +98 -0
  209. package/src/core/session-context.js +341 -0
  210. package/src/core/session-storage.js +274 -0
  211. package/src/executors/mcp-executor.js +2 -2
  212. package/src/utils/index.js +239 -67
  213. package/src/utils/plugin-helpers.js +17 -0
  214. package/story-cover-book-v2.png +0 -0
  215. package/story-cover-japanese-1.png +0 -0
  216. package/story-cover-japanese-2.png +0 -0
  217. package/story-cover-japanese-3.png +0 -0
  218. package/story-cover-moran.png +0 -0
  219. package/undefined.png +0 -0
  220. package//346/265/267/346/212/245/346/217/222/344/273/266.md +621 -0
  221. package/.agent/agents/code-assistant.json +0 -14
  222. package/.agent/agents/email-assistant.json +0 -14
  223. package/.agent/agents/file-assistant.json +0 -15
  224. package/.agent/agents/system-assistant.json +0 -15
  225. package/.agent/agents/web-assistant.json +0 -12
  226. package/.agent/data/ambient/goals.json +0 -50
  227. package/.agent/data/ambient/memories.json +0 -7
  228. package/.agent/data/scheduler/tasks.json +0 -1
  229. package/.agent/package.json +0 -8
  230. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  231. package/.agent/plugins/daytona/README.md +0 -89
  232. package/.agent/plugins/daytona/index.js +0 -377
  233. package/.agent/plugins/daytona/package.json +0 -12
  234. package/.agent/plugins/marknative/README.md +0 -134
  235. package/.agent/plugins/marknative/index.js +0 -228
  236. package/.agent/plugins/marknative/package.json +0 -12
  237. package/.agent/plugins/marknative/update-readme.js +0 -134
  238. package/.agent/plugins/system-info/index.js +0 -387
  239. package/.agent/plugins/system-info/package.json +0 -4
  240. package/.agent/plugins/system-info/test.js +0 -40
  241. package/.agent/plugins/temp-repo/LICENSE +0 -201
  242. package/.agent/plugins/test_plugin.py +0 -304
  243. package/.agent/python-scripts/test_sample.py +0 -24
  244. package/.agent/skills/agent-browser/SKILL.md +0 -311
  245. package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
  246. package/.agent/skills/sysinfo/SKILL.md +0 -38
  247. package/.agent/skills/sysinfo/system-info.sh +0 -130
  248. package/.agent/skills/workflow/SKILL.md +0 -324
  249. package/.agent/workflows/email-digest.json +0 -50
  250. package/.agent/workflows/file-backup.json +0 -21
  251. package/.agent/workflows/get-ip-notify.json +0 -32
  252. package/.agent/workflows/news-aggregator.json +0 -93
  253. package/.agent/workflows/news-dashboard-v2.json +0 -94
  254. package/.agent/workflows/notification-batch.json +0 -32
  255. package/examples/test-chat-debug.js +0 -102
  256. package/examples/test-chat-result.js +0 -76
  257. package/examples/test-chat-stream-diff.js +0 -63
  258. /package/.agent/plugins/{temp-repo/puppeteer-plugin → puppeteer-plugin}/README.md +0 -0
  259. /package/.agent/plugins/{temp-repo/puppeteer-plugin → puppeteer-plugin}/package.json +0 -0
@@ -0,0 +1,984 @@
1
+ /**
2
+ * Memory 记忆插件
3
+ * 四层记忆系统:user, feedback, project, reference
4
+ */
5
+
6
+ const { Plugin } = require('../src/core/plugin-base')
7
+ const { logger } = require('../src/utils/logger')
8
+ const log = logger.child('Memory')
9
+ const { z } = require('zod')
10
+ const fs = require('fs')
11
+ const path = require('path')
12
+
13
+ /**
14
+ * 记忆类型常量
15
+ */
16
+ const MEMORY_TYPES = {
17
+ USER: 'user',
18
+ FEEDBACK: 'feedback',
19
+ PROJECT: 'project',
20
+ REFERENCE: 'reference'
21
+ }
22
+
23
+ /**
24
+ * 记忆存储路径
25
+ */
26
+ const MEMORY_BASE_DIR = '.agent/memory'
27
+
28
+ /**
29
+ * 解析 YAML frontmatter(从 skill-manager 复用)
30
+ */
31
+ function parseFrontmatter(content) {
32
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
33
+ if (!match) return null
34
+
35
+ const frontmatter = {}
36
+ const lines = match[1].split('\n')
37
+ let currentKey = null
38
+
39
+ for (const line of lines) {
40
+ if (currentKey === 'metadata') {
41
+ if (line.match(/^ {2}[a-zA-Z]/)) {
42
+ const colonIndex = line.indexOf(':')
43
+ if (colonIndex > 0) {
44
+ const key = line.substring(2, colonIndex).trim()
45
+ const value = line.substring(colonIndex + 1).trim().replace(/^["']|["']$/g, '')
46
+ frontmatter[currentKey][key] = value
47
+ continue
48
+ }
49
+ } else if (line.match(/^[a-zA-Z]/)) {
50
+ currentKey = null
51
+ continue
52
+ }
53
+ }
54
+
55
+ const colonIndex = line.indexOf(':')
56
+ if (colonIndex === -1) continue
57
+
58
+ const key = line.substring(0, colonIndex).trim()
59
+ let value = line.substring(colonIndex + 1).trim()
60
+
61
+ if (
62
+ (value.startsWith('"') && value.endsWith('"')) ||
63
+ (value.startsWith("'") && value.endsWith("'"))
64
+ ) {
65
+ value = value.slice(1, -1)
66
+ }
67
+
68
+ if (key === 'metadata') {
69
+ currentKey = 'metadata'
70
+ frontmatter.metadata = {}
71
+ } else if (key === 'tags') {
72
+ frontmatter[key] = value
73
+ .split(',')
74
+ .map(t => t.trim().replace(/^["']|["']$/g, ''))
75
+ .filter(t => t)
76
+ } else {
77
+ frontmatter[key] = value
78
+ }
79
+ }
80
+
81
+ return frontmatter
82
+ }
83
+
84
+ /**
85
+ * 序列化 frontmatter
86
+ */
87
+ function serializeFrontmatter(data) {
88
+ const lines = ['---']
89
+ for (const [key, value] of Object.entries(data)) {
90
+ if (key === 'metadata') {
91
+ lines.push('metadata:')
92
+ for (const [mk, mv] of Object.entries(value)) {
93
+ lines.push(` ${mk}: "${mv}"`)
94
+ }
95
+ } else if (Array.isArray(value)) {
96
+ lines.push(`${key}: [${value.join(', ')}]`)
97
+ } else {
98
+ lines.push(`${key}: "${value}"`)
99
+ }
100
+ }
101
+ lines.push('---')
102
+ return lines.join('\n')
103
+ }
104
+
105
+ /**
106
+ * 路径安全检查
107
+ */
108
+ function sanitizeMemoryPath(relativePath, baseDir) {
109
+ // 拒绝绝对路径
110
+ if (path.isAbsolute(relativePath)) {
111
+ throw new Error('Absolute paths are not allowed')
112
+ }
113
+
114
+ // 拒绝包含 .. 的路径
115
+ if (relativePath.includes('..')) {
116
+ throw new Error('Parent directory references are not allowed')
117
+ }
118
+
119
+ // 解析并验证最终路径在 baseDir 内
120
+ const resolved = path.resolve(baseDir, relativePath)
121
+ const baseResolved = path.resolve(baseDir)
122
+
123
+ if (!resolved.startsWith(baseResolved)) {
124
+ throw new Error('Path outside memory directory is not allowed')
125
+ }
126
+
127
+ return resolved
128
+ }
129
+
130
+ /**
131
+ * 生成记忆 ID
132
+ */
133
+ function generateMemoryId() {
134
+ const timestamp = Date.now().toString(36)
135
+ const random = Math.random().toString(36).substring(2, 8)
136
+ return `${timestamp}-${random}`
137
+ }
138
+
139
+ /**
140
+ * 从文本中提取 JSON 对象(处理嵌套括号)
141
+ * @param {string} text - 输入文本
142
+ * @returns {string|null} 提取的 JSON 字符串,失败返回 null
143
+ */
144
+ function _extractJSON(text) {
145
+ if (!text || typeof text !== 'string') return null
146
+
147
+ // 去掉首尾空白
148
+ text = text.trim()
149
+ if (!text.startsWith('{')) return null
150
+
151
+ // 查找配对的括号位置
152
+ let braceCount = 0
153
+ let inString = false
154
+ let escaped = false
155
+
156
+ for (let i = 0; i < text.length; i++) {
157
+ const char = text[i]
158
+
159
+ if (escaped) {
160
+ escaped = false
161
+ continue
162
+ }
163
+
164
+ if (char === '\\' && inString) {
165
+ escaped = true
166
+ continue
167
+ }
168
+
169
+ if (char === '"') {
170
+ inString = !inString
171
+ continue
172
+ }
173
+
174
+ if (!inString) {
175
+ if (char === '{') {
176
+ braceCount++
177
+ } else if (char === '}') {
178
+ braceCount--
179
+ if (braceCount === 0) {
180
+ // 找到完整的 JSON 对象
181
+ return text.substring(0, i + 1)
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ return null
188
+ }
189
+
190
+ /**
191
+ * MemoryStore - 双层存储(内存 + 文件)
192
+ */
193
+ class MemoryStore {
194
+ constructor(baseDir) {
195
+ this._baseDir = path.resolve(process.cwd(), baseDir)
196
+ this._memory = new Map() // memoryId -> memory object
197
+ this._indexByType = new Map() // type -> Set of memoryIds
198
+ this._indexByProject = new Map() // project -> Set of memoryIds
199
+ this._fileIndex = new Map() // memoryId -> filePath
200
+
201
+ // 初始化目录结构
202
+ this._initDirs()
203
+ // 加载已有记忆
204
+ this._loadAll()
205
+ }
206
+
207
+ _initDirs() {
208
+ const dirs = [
209
+ this._baseDir,
210
+ path.join(this._baseDir, 'user'),
211
+ path.join(this._baseDir, 'feedback'),
212
+ path.join(this._baseDir, 'project'),
213
+ path.join(this._baseDir, 'reference')
214
+ ]
215
+
216
+ for (const dir of dirs) {
217
+ if (!fs.existsSync(dir)) {
218
+ fs.mkdirSync(dir, { recursive: true })
219
+ }
220
+ }
221
+ }
222
+
223
+ _getTypeDir(type) {
224
+ return path.join(this._baseDir, type)
225
+ }
226
+
227
+ _loadAll() {
228
+ for (const type of Object.values(MEMORY_TYPES)) {
229
+ const typeDir = this._getTypeDir(type)
230
+ if (!fs.existsSync(typeDir)) continue
231
+
232
+ try {
233
+ const files = fs.readdirSync(typeDir)
234
+ for (const file of files) {
235
+ if (!file.endsWith('.md')) continue
236
+ const filePath = path.join(typeDir, file)
237
+ try {
238
+ const memory = this._loadFromFile(filePath)
239
+ if (memory && memory.id) {
240
+ this._addToIndexes(memory)
241
+ this._fileIndex.set(memory.id, filePath)
242
+ }
243
+ } catch (err) {
244
+ log.warn(`Failed to load memory from ${filePath}:`, err.message)
245
+ }
246
+ }
247
+ } catch (err) {
248
+ log.warn(`Failed to read memory directory ${typeDir}:`, err.message)
249
+ }
250
+ }
251
+
252
+ log.info(`Loaded ${this._memory.size} memories from disk`)
253
+ }
254
+
255
+ _loadFromFile(filePath) {
256
+ const content = fs.readFileSync(filePath, 'utf-8')
257
+ const frontmatter = parseFrontmatter(content)
258
+
259
+ if (!frontmatter) {
260
+ return null
261
+ }
262
+
263
+ // 移除 frontmatter 获取正文
264
+ const contentMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/)
265
+ const body = contentMatch ? contentMatch[1].trim() : content
266
+
267
+ return {
268
+ id: frontmatter.id || path.basename(filePath, '.md'),
269
+ name: frontmatter.name || '',
270
+ type: frontmatter.type || 'user',
271
+ project: frontmatter.project || null,
272
+ tags: frontmatter.tags || [],
273
+ created: frontmatter.created || new Date().toISOString().split('T')[0],
274
+ body
275
+ }
276
+ }
277
+
278
+ _saveToFile(memory) {
279
+ const typeDir = this._getTypeDir(memory.type)
280
+ const fileName = `${memory.id}.md`
281
+ const filePath = path.join(typeDir, fileName)
282
+
283
+ const frontmatter = {
284
+ id: memory.id,
285
+ name: memory.name,
286
+ type: memory.type,
287
+ project: memory.project,
288
+ tags: memory.tags,
289
+ created: memory.created
290
+ }
291
+
292
+ const content = serializeFrontmatter(frontmatter) + '\n' + memory.body
293
+ fs.writeFileSync(filePath, content, 'utf-8')
294
+ return filePath
295
+ }
296
+
297
+ _addToIndexes(memory) {
298
+ this._memory.set(memory.id, memory)
299
+
300
+ // 按类型索引
301
+ if (!this._indexByType.has(memory.type)) {
302
+ this._indexByType.set(memory.type, new Set())
303
+ }
304
+ this._indexByType.get(memory.type).add(memory.id)
305
+
306
+ // 按项目索引
307
+ if (memory.project) {
308
+ if (!this._indexByProject.has(memory.project)) {
309
+ this._indexByProject.set(memory.project, new Set())
310
+ }
311
+ this._indexByProject.get(memory.project).add(memory.id)
312
+ }
313
+ }
314
+
315
+ _removeFromIndexes(memory) {
316
+ this._memory.delete(memory.id)
317
+
318
+ const typeSet = this._indexByType.get(memory.type)
319
+ if (typeSet) typeSet.delete(memory.id)
320
+
321
+ if (memory.project) {
322
+ const projectSet = this._indexByProject.get(memory.project)
323
+ if (projectSet) projectSet.delete(memory.id)
324
+ }
325
+ }
326
+
327
+ // 公开 API
328
+
329
+ /**
330
+ * 添加记忆
331
+ */
332
+ add(memory) {
333
+ const id = memory.id || generateMemoryId()
334
+ const now = new Date().toISOString().split('T')[0]
335
+
336
+ const newMemory = {
337
+ id,
338
+ name: memory.name || '',
339
+ type: memory.type || MEMORY_TYPES.USER,
340
+ project: memory.project || null,
341
+ tags: memory.tags || [],
342
+ created: memory.created || now,
343
+ body: memory.body || ''
344
+ }
345
+
346
+ // 验证类型
347
+ if (!Object.values(MEMORY_TYPES).includes(newMemory.type)) {
348
+ throw new Error(`Invalid memory type: ${newMemory.type}`)
349
+ }
350
+
351
+ const filePath = this._saveToFile(newMemory)
352
+ this._addToIndexes(newMemory)
353
+ this._fileIndex.set(id, filePath)
354
+
355
+ log.debug(`Added memory: ${id} (${newMemory.type})`)
356
+ return newMemory
357
+ }
358
+
359
+ /**
360
+ * 获取记忆
361
+ */
362
+ get(id) {
363
+ return this._memory.get(id) || null
364
+ }
365
+
366
+ /**
367
+ * 更新记忆
368
+ */
369
+ update(id, updates) {
370
+ const memory = this._memory.get(id)
371
+ if (!memory) {
372
+ throw new Error(`Memory not found: ${id}`)
373
+ }
374
+
375
+ const oldType = memory.type
376
+ const oldProject = memory.project
377
+
378
+ // 应用更新
379
+ if (updates.name !== undefined) memory.name = updates.name
380
+ if (updates.type !== undefined) memory.type = updates.type
381
+ if (updates.project !== undefined) memory.project = updates.project
382
+ if (updates.tags !== undefined) memory.tags = updates.tags
383
+ if (updates.body !== undefined) memory.body = updates.body
384
+
385
+ // 如果类型或项目改变,需要重建索引
386
+ if (oldType !== memory.type || oldProject !== memory.project) {
387
+ this._removeFromIndexes({ ...memory, type: oldType, project: oldProject })
388
+ this._addToIndexes(memory)
389
+ }
390
+
391
+ // 重新保存文件
392
+ const filePath = this._saveToFile(memory)
393
+ this._fileIndex.set(id, filePath)
394
+
395
+ log.debug(`Updated memory: ${id}`)
396
+ return memory
397
+ }
398
+
399
+ /**
400
+ * 删除记忆
401
+ */
402
+ delete(id) {
403
+ const memory = this._memory.get(id)
404
+ if (!memory) {
405
+ return false
406
+ }
407
+
408
+ // 删除文件
409
+ const filePath = this._fileIndex.get(id)
410
+ if (filePath && fs.existsSync(filePath)) {
411
+ fs.unlinkSync(filePath)
412
+ }
413
+
414
+ this._removeFromIndexes(memory)
415
+ this._fileIndex.delete(id)
416
+
417
+ log.debug(`Deleted memory: ${id}`)
418
+ return true
419
+ }
420
+
421
+ /**
422
+ * 搜索记忆
423
+ */
424
+ search(query, options = {}) {
425
+ const { type, project, tags, limit = 50 } = options
426
+ let results = []
427
+
428
+ // 根据类型筛选
429
+ if (type) {
430
+ const typeSet = this._indexByType.get(type)
431
+ if (typeSet) {
432
+ results = Array.from(typeSet).map(id => this._memory.get(id)).filter(Boolean)
433
+ }
434
+ } else {
435
+ results = Array.from(this._memory.values())
436
+ }
437
+
438
+ // 根据项目筛选
439
+ if (project) {
440
+ results = results.filter(m => m.project === project)
441
+ }
442
+
443
+ // 根据标签筛选
444
+ if (tags && tags.length > 0) {
445
+ results = results.filter(m =>
446
+ tags.some(tag => m.tags && m.tags.includes(tag))
447
+ )
448
+ }
449
+
450
+ // 根据查询字符串搜索(名称 + 正文)
451
+ if (query && query.trim()) {
452
+ const lowerQuery = query.toLowerCase()
453
+ results = results.filter(m =>
454
+ m.name.toLowerCase().includes(lowerQuery) ||
455
+ m.body.toLowerCase().includes(lowerQuery)
456
+ )
457
+ }
458
+
459
+ // 限制返回数量
460
+ return results.slice(0, limit)
461
+ }
462
+
463
+ /**
464
+ * 列出指定类型的所有记忆
465
+ */
466
+ listByType(type, limit = 100) {
467
+ const typeSet = this._indexByType.get(type)
468
+ if (!typeSet) return []
469
+ return Array.from(typeSet)
470
+ .map(id => this._memory.get(id))
471
+ .filter(Boolean)
472
+ .slice(0, limit)
473
+ }
474
+
475
+ /**
476
+ * 按项目列出记忆
477
+ */
478
+ listByProject(project, limit = 100) {
479
+ const projectSet = this._indexByProject.get(project)
480
+ if (!projectSet) return []
481
+ return Array.from(projectSet)
482
+ .map(id => this._memory.get(id))
483
+ .filter(Boolean)
484
+ .slice(0, limit)
485
+ }
486
+
487
+ /**
488
+ * 获取所有项目列表
489
+ */
490
+ getProjects() {
491
+ return Array.from(this._indexByProject.keys())
492
+ }
493
+
494
+ /**
495
+ * 获取统计信息
496
+ */
497
+ getStats() {
498
+ const stats = {}
499
+ for (const type of Object.values(MEMORY_TYPES)) {
500
+ const typeSet = this._indexByType.get(type)
501
+ stats[type] = typeSet ? typeSet.size : 0
502
+ }
503
+ stats.total = this._memory.size
504
+ return stats
505
+ }
506
+ }
507
+
508
+ /**
509
+ * MemoryPlugin
510
+ */
511
+ class MemoryPlugin extends Plugin {
512
+ constructor(config = {}) {
513
+ super()
514
+ this.name = 'memory'
515
+ this.version = '1.0.0'
516
+ this.description = '四层记忆系统,支持 user/feedback/project/reference 四种记忆类型'
517
+ this.priority = 6 // 在 session 之后加载
518
+
519
+ this.system = true
520
+
521
+ this.config = {
522
+ memoryDir: config.memoryDir || MEMORY_BASE_DIR,
523
+ autoExtract: config.autoExtract !== false, // 默认开启自动提取
524
+ extractOnTopics: ['provider', 'preference', 'setting', 'user'] // 触发自动提取的关键词
525
+ }
526
+
527
+ this._framework = null
528
+ this._store = null
529
+ this._memoryAgent = null // 用于记忆提取的子 agent
530
+ }
531
+
532
+ install(framework) {
533
+ this._framework = framework
534
+ return this
535
+ }
536
+
537
+ start(framework) {
538
+ // 初始化存储
539
+ this._store = new MemoryStore(this.config.memoryDir)
540
+
541
+ // 注册工具
542
+ this._registerTools(framework)
543
+
544
+ // 设置自动记忆提取(监听对话结束事件)
545
+ if (this.config.autoExtract) {
546
+ this._setupAutoExtract(framework)
547
+ }
548
+
549
+ // 注册 memory-context 到系统提示(优先级 350)
550
+ this._registerMemoryContext(framework)
551
+
552
+ log.info('Memory plugin started')
553
+ return this
554
+ }
555
+
556
+ _registerTools(framework) {
557
+ // memory_search - 搜索记忆
558
+ framework.registerTool({
559
+ name: 'memory_search',
560
+ description: '搜索记忆库,支持按类型、项目、标签搜索',
561
+ inputSchema: z.object({
562
+ query: z.string().optional().describe('搜索关键词'),
563
+ type: z.enum(['user', 'feedback', 'project', 'reference']).optional().describe('记忆类型'),
564
+ project: z.string().optional().describe('项目名称'),
565
+ tags: z.array(z.string()).optional().describe('标签列表'),
566
+ limit: z.number().optional().describe('返回数量限制,默认 50')
567
+ }),
568
+ execute: async (args) => {
569
+ try {
570
+ const results = this._store.search(args.query, {
571
+ type: args.type,
572
+ project: args.project,
573
+ tags: args.tags,
574
+ limit: args.limit || 50
575
+ })
576
+
577
+ return {
578
+ success: true,
579
+ count: results.length,
580
+ memories: results.map(m => ({
581
+ id: m.id,
582
+ name: m.name,
583
+ type: m.type,
584
+ project: m.project,
585
+ tags: m.tags,
586
+ created: m.created,
587
+ preview: m.body.substring(0, 200)
588
+ }))
589
+ }
590
+ } catch (err) {
591
+ return { success: false, error: err.message }
592
+ }
593
+ }
594
+ })
595
+
596
+ // memory_write - 写入记忆
597
+ framework.registerTool({
598
+ name: 'memory_write',
599
+ description: '写入新的记忆,支持 YAML frontmatter',
600
+ inputSchema: z.object({
601
+ name: z.string().describe('记忆名称'),
602
+ type: z.enum(['user', 'feedback', 'project', 'reference']).describe('记忆类型'),
603
+ project: z.string().optional().describe('关联项目'),
604
+ tags: z.array(z.string()).optional().describe('标签'),
605
+ content: z.string().describe('记忆内容')
606
+ }),
607
+ execute: async (args) => {
608
+ try {
609
+ const memory = this._store.add({
610
+ name: args.name,
611
+ type: args.type,
612
+ project: args.project || null,
613
+ tags: args.tags || [],
614
+ body: args.content
615
+ })
616
+
617
+ return {
618
+ success: true,
619
+ id: memory.id,
620
+ message: `Memory created: ${memory.id}`
621
+ }
622
+ } catch (err) {
623
+ return { success: false, error: err.message }
624
+ }
625
+ }
626
+ })
627
+
628
+ // memory_list - 列出记忆
629
+ framework.registerTool({
630
+ name: 'memory_list',
631
+ description: '列出指定类型的记忆',
632
+ inputSchema: z.object({
633
+ type: z.enum(['user', 'feedback', 'project', 'reference']).describe('记忆类型'),
634
+ project: z.string().optional().describe('项目名称(当类型为 project 时)'),
635
+ limit: z.number().optional().describe('返回数量限制,默认 100')
636
+ }),
637
+ execute: async (args) => {
638
+ try {
639
+ let memories
640
+ if (args.type === 'project' && args.project) {
641
+ memories = this._store.listByProject(args.project, args.limit || 100)
642
+ } else {
643
+ memories = this._store.listByType(args.type, args.limit || 100)
644
+ }
645
+
646
+ return {
647
+ success: true,
648
+ count: memories.length,
649
+ memories: memories.map(m => ({
650
+ id: m.id,
651
+ name: m.name,
652
+ type: m.type,
653
+ project: m.project,
654
+ tags: m.tags,
655
+ created: m.created,
656
+ preview: m.body.substring(0, 200)
657
+ }))
658
+ }
659
+ } catch (err) {
660
+ return { success: false, error: err.message }
661
+ }
662
+ }
663
+ })
664
+
665
+ // memory_delete - 删除记忆
666
+ framework.registerTool({
667
+ name: 'memory_delete',
668
+ description: '删除指定记忆',
669
+ inputSchema: z.object({
670
+ id: z.string().describe('记忆 ID')
671
+ }),
672
+ execute: async (args) => {
673
+ try {
674
+ const deleted = this._store.delete(args.id)
675
+ return { success: deleted, deleted: args.id }
676
+ } catch (err) {
677
+ return { success: false, error: err.message }
678
+ }
679
+ }
680
+ })
681
+
682
+ // memory_update - 更新记忆
683
+ framework.registerTool({
684
+ name: 'memory_update',
685
+ description: '更新已有记忆',
686
+ inputSchema: z.object({
687
+ id: z.string().describe('记忆 ID'),
688
+ name: z.string().optional().describe('新名称'),
689
+ type: z.enum(['user', 'feedback', 'project', 'reference']).optional().describe('新类型'),
690
+ project: z.string().optional().describe('新关联项目'),
691
+ tags: z.array(z.string()).optional().describe('新标签'),
692
+ content: z.string().optional().describe('新内容')
693
+ }),
694
+ execute: async (args) => {
695
+ try {
696
+ const updates = {}
697
+ if (args.name !== undefined) updates.name = args.name
698
+ if (args.type !== undefined) updates.type = args.type
699
+ if (args.project !== undefined) updates.project = args.project
700
+ if (args.tags !== undefined) updates.tags = args.tags
701
+ if (args.content !== undefined) updates.body = args.content
702
+
703
+ const memory = this._store.update(args.id, updates)
704
+ return {
705
+ success: true,
706
+ id: memory.id,
707
+ message: `Memory updated: ${memory.id}`
708
+ }
709
+ } catch (err) {
710
+ return { success: false, error: err.message }
711
+ }
712
+ }
713
+ })
714
+
715
+ // memory_get - 获取单条记忆详情
716
+ framework.registerTool({
717
+ name: 'memory_get',
718
+ description: '获取单条记忆的完整内容',
719
+ inputSchema: z.object({
720
+ id: z.string().describe('记忆 ID')
721
+ }),
722
+ execute: async (args) => {
723
+ try {
724
+ const memory = this._store.get(args.id)
725
+ if (!memory) {
726
+ return { success: false, error: 'Memory not found' }
727
+ }
728
+ return {
729
+ success: true,
730
+ memory: {
731
+ id: memory.id,
732
+ name: memory.name,
733
+ type: memory.type,
734
+ project: memory.project,
735
+ tags: memory.tags,
736
+ created: memory.created,
737
+ content: memory.body
738
+ }
739
+ }
740
+ } catch (err) {
741
+ return { success: false, error: err.message }
742
+ }
743
+ }
744
+ })
745
+
746
+ // memory_stats - 获取记忆统计
747
+ framework.registerTool({
748
+ name: 'memory_stats',
749
+ description: '获取记忆统计信息',
750
+ inputSchema: z.object({}),
751
+ execute: async () => {
752
+ try {
753
+ const stats = this._store.getStats()
754
+ const projects = this._store.getProjects()
755
+ return {
756
+ success: true,
757
+ stats,
758
+ projects
759
+ }
760
+ } catch (err) {
761
+ return { success: false, error: err.message }
762
+ }
763
+ }
764
+ })
765
+ }
766
+
767
+ /**
768
+ * 设置自动记忆提取
769
+ */
770
+ _setupAutoExtract(framework) {
771
+ // 监听 agent 消息完成事件,在对话轮次结束时提取记忆
772
+ framework.on('agent:message', async ({ userMessage, assistantResponse }) => {
773
+ try {
774
+ await this._extractMemoryFromConversation(userMessage, assistantResponse)
775
+ } catch (err) {
776
+ log.warn('Auto memory extraction failed:', err.message)
777
+ }
778
+ })
779
+ }
780
+
781
+ /**
782
+ * 从对话中提取记忆
783
+ */
784
+ async _extractMemoryFromConversation(userMessage, assistantResponse) {
785
+ // 构建提取提示
786
+ const extractPrompt = `你是一个记忆提取专家。请分析以下对话,提取有价值的信息到记忆系统。
787
+
788
+ 用户消息:
789
+ ${typeof userMessage === 'string' ? userMessage : JSON.stringify(userMessage)}
790
+
791
+ 助手回复:
792
+ ${typeof assistantResponse === 'string' ? assistantResponse : JSON.stringify(assistantResponse)}
793
+
794
+ 请分析并决定是否需要提取记忆。记忆类型:
795
+ - user: 用户偏好、习惯、设置(如喜欢用 deepseek provider)
796
+ - feedback: 用户反馈、建议、批评
797
+ - project: 项目相关信息、上下文
798
+ - reference: 外部知识、参考资料
799
+
800
+ 如果需要提取,请按以下格式返回(仅返回 JSON,不要有其他内容):
801
+ {
802
+ "extract": true/false,
803
+ "type": "user/feedback/project/reference",
804
+ "name": "记忆名称",
805
+ "project": "项目名(仅 project 类型需要)",
806
+ "tags": ["tag1", "tag2"],
807
+ "content": "要记忆的内容,包含 Why(为什么重要)和 How to apply(如何使用)"
808
+ }
809
+
810
+ 如果不需要提取,返回:
811
+ {"extract": false}`
812
+
813
+ try {
814
+ // 使用 memory-subagent 执行提取(受限工具权限)
815
+ const result = await this._runMemoryExtractionAgent(extractPrompt)
816
+
817
+ if (result && result.extract === true) {
818
+ this._store.add({
819
+ name: result.name,
820
+ type: result.type,
821
+ project: result.project || null,
822
+ tags: result.tags || [],
823
+ body: result.content
824
+ })
825
+ log.info(`Auto-extracted memory: ${result.name} (${result.type})`)
826
+ }
827
+ } catch (err) {
828
+ log.warn('Memory extraction agent failed:', err.message)
829
+ }
830
+ }
831
+
832
+ /**
833
+ * 运行记忆提取子 agent
834
+ * 使用独立的轻量子 agent 处理记忆提取,不占用主 agent 资源
835
+ */
836
+ async _runMemoryExtractionAgent(prompt) {
837
+ try {
838
+ // 获取 ai 插件获取模型配置
839
+ const aiPlugin = this._framework.pluginManager.get('ai')
840
+ if (!aiPlugin) {
841
+ log.warn('Memory extraction: AI plugin not found')
842
+ return null
843
+ }
844
+
845
+ const aiClient = aiPlugin.getAIClient()
846
+ if (!aiClient) {
847
+ log.warn('Memory extraction: AI client not found')
848
+ return null
849
+ }
850
+
851
+ // 直接使用 generateText,设置 maxSteps: 1 避免多轮对话消耗过多 context
852
+ const { generateText } = require('ai')
853
+
854
+ const { text } = await generateText({
855
+ model: aiClient,
856
+ system: '你是一个记忆提取专家,直接返回 JSON,不要有其他内容。',
857
+ prompt,
858
+ maxTokens: 500,
859
+ temperature: 0.3,
860
+ maxSteps: 1
861
+ })
862
+
863
+ // 解析 JSON 响应
864
+ if (text) {
865
+ const cleanedText = text.trim()
866
+
867
+ // 移除 markdown 代码块包裹
868
+ let jsonText = cleanedText
869
+ if (jsonText.startsWith('```')) {
870
+ jsonText = jsonText.replace(/^```[\w]*\n?/, '').replace(/\n?```$/, '').trim()
871
+ }
872
+
873
+ // 尝试提取并解析 JSON
874
+ const jsonStr = _extractJSON(jsonText)
875
+ if (jsonStr) {
876
+ try {
877
+ const result = JSON.parse(jsonStr)
878
+ return result
879
+ } catch (parseErr) {
880
+ // JSON 解析失败,尝试清理不可见字符后重试
881
+ const cleaned = jsonStr.replace(/[\u0000-\u001F\u007F-\u009F\u200B-\u200F\u2028\u2029]/g, '')
882
+ try {
883
+ const result = JSON.parse(cleaned)
884
+ return result
885
+ } catch (e) {
886
+ log.warn('Failed to parse extracted JSON:', jsonStr.substring(0, 200))
887
+ }
888
+ }
889
+ }
890
+ }
891
+ } catch (err) {
892
+ log.warn('Memory extraction agent failed:', err.message)
893
+ }
894
+
895
+ return null
896
+ }
897
+
898
+ /**
899
+ * 注册 memory-context 到系统提示
900
+ */
901
+ _registerMemoryContext(framework) {
902
+ // 获取主 agent 并注册 memory-context 部分
903
+ const mainAgent = framework._mainAgent
904
+ if (mainAgent) {
905
+ mainAgent.registerPromptPart('memory-context', 350, () => {
906
+ return this._buildMemoryContext()
907
+ })
908
+ }
909
+
910
+ // 也监听 framework:ready 事件,以防主 agent 还没创建
911
+ framework.on('framework:ready', () => {
912
+ const agent = framework._mainAgent
913
+ if (agent) {
914
+ agent.registerPromptPart('memory-context', 350, () => {
915
+ return this._buildMemoryContext()
916
+ })
917
+ }
918
+ })
919
+ }
920
+
921
+ /**
922
+ * 构建记忆上下文(注入到系统提示)
923
+ */
924
+ _buildMemoryContext() {
925
+ try {
926
+ // 获取所有 user 类型记忆作为用户偏好
927
+ const userMemories = this._store.listByType(MEMORY_TYPES.USER, 10)
928
+ const projectMemories = this._store.listByType(MEMORY_TYPES.PROJECT, 5)
929
+
930
+ if (userMemories.length === 0 && projectMemories.length === 0) {
931
+ return null
932
+ }
933
+
934
+ const parts = ['【记忆上下文】']
935
+
936
+ if (userMemories.length > 0) {
937
+ parts.push('【用户偏好】')
938
+ for (const m of userMemories) {
939
+ parts.push(`- ${m.name}: ${m.body.substring(0, 150)}${m.body.length > 150 ? '...' : ''}`)
940
+ }
941
+ }
942
+
943
+ if (projectMemories.length > 0) {
944
+ parts.push('【项目上下文】')
945
+ for (const m of projectMemories) {
946
+ const projectTag = m.project ? `[${m.project}] ` : ''
947
+ parts.push(`- ${projectTag}${m.name}: ${m.body.substring(0, 100)}${m.body.length > 100 ? '...' : ''}`)
948
+ }
949
+ }
950
+
951
+ return parts.join('\n')
952
+ } catch (err) {
953
+ log.warn('Failed to build memory context:', err.message)
954
+ return null
955
+ }
956
+ }
957
+
958
+ /**
959
+ * 获取存储实例(供其他插件使用)
960
+ */
961
+ getStore() {
962
+ return this._store
963
+ }
964
+
965
+ /**
966
+ * 搜索记忆(公开 API)
967
+ */
968
+ searchMemories(query, options = {}) {
969
+ return this._store.search(query, options)
970
+ }
971
+
972
+ reload(framework) {
973
+ this._framework = framework
974
+ // 重新初始化存储
975
+ this._store = new MemoryStore(this.config.memoryDir)
976
+ }
977
+
978
+ uninstall(framework) {
979
+ this._store = null
980
+ this._framework = null
981
+ }
982
+ }
983
+
984
+ module.exports = MemoryPlugin