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,1596 @@
1
+ # 分层上下文架构设计方案
2
+
3
+ ## 一、当前问题分析
4
+
5
+ ### 1.1 核心问题
6
+
7
+ | 问题 | 位置 | 描述 |
8
+ | --------------------------------- | --------------------------- | --------------------------------------------------- |
9
+ | **共享 `_messages`** | `agent-chat.js:100` | `this._messages = []` 是实例级别,所有 session 共用 |
10
+ | **并行 session 冲突** | `_doChat()` | 多个 session 并行时,`_messages` 互相覆盖 |
11
+ | **Session 历史加载到共享内存** | `_loadHistoryFromSession()` | 加载历史到共享的 `_messages`,导致竞态条件 |
12
+ | **上下文层次不清晰** | `runWithContext()` | `context` 只有 `{sessionId, isStream}`,缺少分层 |
13
+ | **Handler 与 SessionPlugin 耦合** | `_loadHistoryFromSession()` | 直接依赖 `sessionPlugin.getHistory()` |
14
+
15
+ ### 1.2 问题代码示例
16
+
17
+ ```javascript
18
+ // agent-chat.js:100 - 共享的消息存储
19
+ this._messages = []; // 问题:所有 session 共用!
20
+
21
+ // agent-chat.js:899-904 - 并行时的竞态
22
+ if (sessionId) {
23
+ const savedMessages = this._loadHistoryFromSession(sessionId);
24
+ if (savedMessages.length > 0) {
25
+ this._messages = savedMessages; // 问题:覆盖其他 session 的消息
26
+ }
27
+ }
28
+ ```
29
+
30
+ ### 1.3 架构问题图示
31
+
32
+ ```
33
+ 当前架构:
34
+ ┌─────────────────────────────────────────────────────────────┐
35
+ │ AgentChatHandler │
36
+ │ ┌─────────────────────────────────────────────────────┐ │
37
+ │ │ this._messages[] ←── 所有 session 共用! │ │
38
+ │ │ this._sessionQueues ← Per-Session 队列(正确) │ │
39
+ │ └─────────────────────────────────────────────────────┘ │
40
+ └─────────────────────────────────────────────────────────────┘
41
+
42
+
43
+ ┌─────────────────────────────────────────────────────────────┐
44
+ │ SessionPlugin │
45
+ │ _sessions Map → { messages[], variables, metadata } │
46
+ └─────────────────────────────────────────────────────────────┘
47
+
48
+ 并行请求时:
49
+ Request A (session A) ──→ 加载 session A 历史 ──→ _messages = [A的历史]
50
+
51
+ Request B (session B) ──→ 加载 session B 历史 ──→ _messages = [B的历史] ← 覆盖!
52
+
53
+ Request A 继续 ──→ _messages 被 B 覆盖了! ──→ 保存到 A ──→ A 的历史变成 B 的
54
+ ```
55
+
56
+ ---
57
+
58
+ ## 二、设计目标
59
+
60
+ 1. **Per-Session 状态隔离** - 每个 session 有独立的消息存储
61
+ 2. **分层上下文** - Request / Session / Agent 三层分离
62
+ 3. **可追踪性** - 完整的 requestId、traceId 链路追踪
63
+ 4. **向后兼容** - 最小化对现有代码的改动
64
+ 5. **可扩展性** - 易于添加新的上下文层、支持多种存储后端
65
+
66
+ ---
67
+
68
+ ## 三、分层上下文架构
69
+
70
+ ### 3.1 层次结构
71
+
72
+ ```
73
+ ┌─────────────────────────────────────────────────────────────┐
74
+ │ Request Context │
75
+ │ ┌─────────────────────────────────────────────────────┐ │
76
+ │ │ requestId - 唯一请求 ID │ │
77
+ │ │ traceId - 分布式追踪 ID │ │
78
+ │ │ parentSpanId - 父 span ID(用于链路追踪) │ │
79
+ │ │ timeout - 请求超时时间 │ │
80
+ │ │ startTime - 请求开始时间 │ │
81
+ │ │ isStream - 是否为流式请求 │ │
82
+ │ │ userId - 用户标识(可选) │ │
83
+ │ └─────────────────────────────────────────────────────┘ │
84
+ └─────────────────────────────────────────────────────────────┘
85
+
86
+
87
+ ┌─────────────────────────────────────────────────────────────┐
88
+ │ Session Context │
89
+ │ ┌─────────────────────────────────────────────────────┐ │
90
+ │ │ sessionId - 会话 ID │ │
91
+ │ │ userId - 用户标识 │ │
92
+ │ │ variables - Map<key, value> 会话变量 │ │
93
+ │ │ metadata - 会话元数据 │ │
94
+ │ │ messageStore - Per-Session 消息存储 │ │
95
+ │ │ messages[] - 该 session 的对话历史 │ │
96
+ │ │ historyLoaded - 是否已加载历史 │ │
97
+ │ │ createdAt - 创建时间 │ │
98
+ │ │ lastActive - 最后活跃时间 │ │
99
+ │ └─────────────────────────────────────────────────────┘ │
100
+ └─────────────────────────────────────────────────────────────┘
101
+
102
+
103
+ ┌─────────────────────────────────────────────────────────────┐
104
+ │ Agent Context │
105
+ │ ┌─────────────────────────────────────────────────────┐ │
106
+ │ │ agentId - Agent 实例 ID │ │
107
+ │ │ name - Agent 名称 │ │
108
+ │ │ tools - 该 Agent 的工具注册表 │ │
109
+ │ │ skills - 该 Agent 的技能列表 │ │
110
+ │ │ systemPrompt - 系统提示词(动态构建) │ │
111
+ │ │ customData - Agent 级自定义数据 │ │
112
+ │ └─────────────────────────────────────────────────────┘ │
113
+ └─────────────────────────────────────────────────────────────┘
114
+ ```
115
+
116
+ ### 3.2 AsyncLocalStorage 隔离策略
117
+
118
+ ```javascript
119
+ // Framework 中的三层 AsyncLocalStorage
120
+ class Framework {
121
+ constructor() {
122
+ // 三层独立的异步上下文存储
123
+ this._requestStorage = new AsyncLocalStorage(); // Request 级别
124
+ this._sessionStorage = new AsyncLocalStorage(); // Session 级别
125
+ this._agentStorage = new AsyncLocalStorage(); // Agent 级别
126
+
127
+ // SessionContext 管理
128
+ this._sessionContexts = new Map(); // sessionId → SessionContext
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### 3.3 上下文获取方法
134
+
135
+ ```javascript
136
+ class Framework {
137
+ // Request Context
138
+ runWithContext(context, fn) {
139
+ return this._requestStorage.run(context, fn);
140
+ }
141
+
142
+ getRequestContext() {
143
+ return this._requestStorage.getStore() || null;
144
+ }
145
+
146
+ // Session Context
147
+ runInSession(sessionId, fn) {
148
+ const sessionCtx = this.getOrCreateSessionContext(sessionId);
149
+ return this._sessionStorage.run({ sessionContext: sessionCtx }, fn);
150
+ }
151
+
152
+ getSessionContext() {
153
+ return this._sessionStorage.getStore()?.sessionContext || null;
154
+ }
155
+
156
+ // Agent Context
157
+ runWithAgent(agentId, fn) {
158
+ const agentCtx = this._agentContexts.get(agentId);
159
+ return this._agentStorage.run({ agentContext: agentCtx }, fn);
160
+ }
161
+
162
+ getAgentContext() {
163
+ return this._agentStorage.getStore()?.agentContext || null;
164
+ }
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ## 四、核心类设计
171
+
172
+ ### 4.1 SessionContext 类
173
+
174
+ ```javascript
175
+ /**
176
+ * SessionContext - Session 级别的上下文封装
177
+ *
178
+ * 职责:
179
+ * 1. 管理 Per-Session 的消息存储
180
+ * 2. 管理会话变量(variables)
181
+ * 3. 跟踪会话元数据
182
+ * 4. 处理上下文压缩状态
183
+ */
184
+ class SessionContext {
185
+ /**
186
+ * @param {string} sessionId - 会话 ID
187
+ * @param {Framework} framework - 框架实例
188
+ * @param {Object} options - 配置选项
189
+ */
190
+ constructor(sessionId, framework, options = {}) {
191
+ this.sessionId = sessionId;
192
+ this.framework = framework;
193
+
194
+ // 会话变量(可被工具读写)
195
+ this.variables = new Map();
196
+
197
+ // 元数据
198
+ this.metadata = {
199
+ createdAt: Date.now(),
200
+ lastActive: Date.now(),
201
+ messageCount: 0,
202
+ compressionCount: 0,
203
+ ...options.metadata,
204
+ };
205
+
206
+ // Per-Session 消息存储
207
+ this.messageStore = {
208
+ messages: [], // 对话历史
209
+ historyLoaded: false, // 是否已从持久化加载
210
+ systemPrompt: null, // 当前系统提示词
211
+ };
212
+
213
+ // 压缩状态
214
+ this.compressionState = {
215
+ lastCompressedAt: null,
216
+ lastTokenCount: 0,
217
+ pendingCompression: false,
218
+ };
219
+
220
+ // 自定义数据插槽
221
+ this.customData = new Map();
222
+ }
223
+
224
+ // ==================== 消息管理 ====================
225
+
226
+ /**
227
+ * 获取消息历史
228
+ * @returns {Array} 消息数组
229
+ */
230
+ getMessages() {
231
+ return this.messageStore.messages;
232
+ }
233
+
234
+ /**
235
+ * 添加消息
236
+ * @param {Object} message - 消息对象
237
+ */
238
+ addMessage(message) {
239
+ this.messageStore.messages.push(message);
240
+ this.metadata.messageCount++;
241
+ this.metadata.lastActive = Date.now();
242
+ }
243
+
244
+ /**
245
+ * 添加多条消息
246
+ * @param {Array} messages - 消息数组
247
+ */
248
+ addMessages(messages) {
249
+ for (const msg of messages) {
250
+ this.addMessage(msg);
251
+ }
252
+ }
253
+
254
+ /**
255
+ * 替换所有消息(用于压缩后同步)
256
+ * @param {Array} messages - 新消息数组
257
+ */
258
+ replaceMessages(messages) {
259
+ this.messageStore.messages = messages;
260
+ }
261
+
262
+ /**
263
+ * 清空消息历史
264
+ */
265
+ clearMessages() {
266
+ this.messageStore.messages = [];
267
+ }
268
+
269
+ // ==================== 变量管理 ====================
270
+
271
+ /**
272
+ * 获取变量
273
+ * @param {string} key - 变量名
274
+ * @param {*} defaultValue - 默认值
275
+ * @returns {*}
276
+ */
277
+ getVariable(key, defaultValue = undefined) {
278
+ return this.variables.get(key) ?? defaultValue;
279
+ }
280
+
281
+ /**
282
+ * 设置变量
283
+ * @param {string} key - 变量名
284
+ * @param {*} value - 变量值
285
+ */
286
+ setVariable(key, value) {
287
+ this.variables.set(key, value);
288
+ }
289
+
290
+ /**
291
+ * 删除变量
292
+ * @param {string} key - 变量名
293
+ */
294
+ deleteVariable(key) {
295
+ this.variables.delete(key);
296
+ }
297
+
298
+ /**
299
+ * 获取所有变量
300
+ * @returns {Object}
301
+ */
302
+ getAllVariables() {
303
+ return Object.fromEntries(this.variables);
304
+ }
305
+
306
+ // ==================== 元数据 ====================
307
+
308
+ /**
309
+ * 更新最后活跃时间
310
+ */
311
+ touch() {
312
+ this.metadata.lastActive = Date.now();
313
+ }
314
+
315
+ /**
316
+ * 获取会话摘要
317
+ * @returns {Object}
318
+ */
319
+ getSummary() {
320
+ return {
321
+ sessionId: this.sessionId,
322
+ messageCount: this.metadata.messageCount,
323
+ createdAt: this.metadata.createdAt,
324
+ lastActive: this.metadata.lastActive,
325
+ variables: this.getAllVariables(),
326
+ };
327
+ }
328
+ }
329
+ ```
330
+
331
+ ### 4.2 RequestContext 类(轻量)
332
+
333
+ ```javascript
334
+ /**
335
+ * RequestContext - Request 级别的上下文封装
336
+ *
337
+ * 职责:
338
+ * 1. 追踪请求链路
339
+ * 2. 管理请求级超时
340
+ * 3. 传递请求元数据
341
+ */
342
+ class RequestContext {
343
+ constructor(options = {}) {
344
+ this.requestId = options.requestId || generateId();
345
+ this.traceId = options.traceId || generateId();
346
+ this.parentSpanId = options.parentSpanId || null;
347
+
348
+ this.timeout = options.timeout || 120000; // 默认 2 分钟
349
+ this.startTime = options.startTime || Date.now();
350
+
351
+ this.isStream = options.isStream || false;
352
+ this.userId = options.userId || null;
353
+
354
+ // 请求级状态
355
+ this.cancelled = false;
356
+ this.abortController = new AbortController();
357
+ }
358
+
359
+ /**
360
+ * 检查是否超时
361
+ * @returns {boolean}
362
+ */
363
+ isTimeout() {
364
+ return Date.now() - this.startTime > this.timeout;
365
+ }
366
+
367
+ /**
368
+ * 取消请求
369
+ */
370
+ cancel() {
371
+ this.cancelled = true;
372
+ this.abortController.abort();
373
+ }
374
+
375
+ /**
376
+ * 获取请求耗时(毫秒)
377
+ * @returns {number}
378
+ */
379
+ getElapsed() {
380
+ return Date.now() - this.startTime;
381
+ }
382
+ }
383
+ ```
384
+
385
+ ### 4.3 AgentContext 类(轻量)
386
+
387
+ ```javascript
388
+ /**
389
+ * AgentContext - Agent 级别的上下文封装
390
+ *
391
+ * 职责:
392
+ * 1. 隔离不同 Agent 实例的状态
393
+ * 2. 管理 Agent 级工具注册
394
+ * 3. 管理 Agent 级技能
395
+ */
396
+ class AgentContext {
397
+ constructor(agentId, agent) {
398
+ this.agentId = agentId;
399
+ this.agent = agent; // Agent 实例引用
400
+
401
+ // Agent 级工具注册(覆盖全局)
402
+ this.tools = new Map();
403
+
404
+ // Agent 级技能
405
+ this.skills = [];
406
+
407
+ // Agent 级系统提示词
408
+ this.systemPrompt = null;
409
+
410
+ // 自定义数据
411
+ this.customData = new Map();
412
+ }
413
+
414
+ /**
415
+ * 注册工具
416
+ * @param {Object} toolDef - 工具定义
417
+ */
418
+ registerTool(toolDef) {
419
+ this.tools.set(toolDef.name, toolDef);
420
+ }
421
+
422
+ /**
423
+ * 获取工具
424
+ * @param {string} name - 工具名
425
+ * @returns {Object|null}
426
+ */
427
+ getTool(name) {
428
+ return this.tools.get(name) || null;
429
+ }
430
+
431
+ /**
432
+ * 获取所有工具
433
+ * @returns {Array}
434
+ */
435
+ getAllTools() {
436
+ return Array.from(this.tools.values());
437
+ }
438
+ }
439
+ ```
440
+
441
+ ### 4.4 ContextManager 类
442
+
443
+ ```javascript
444
+ /**
445
+ * ContextManager - 上下文管理器
446
+ *
447
+ * 职责:
448
+ * 1. 统一管理所有 Context 的生命周期
449
+ * 2. 提供上下文查询接口
450
+ * 3. 处理上下文清理和资源释放
451
+ */
452
+ class ContextManager {
453
+ constructor(framework) {
454
+ this.framework = framework;
455
+
456
+ // Context 存储
457
+ this._requestContexts = new Map(); // requestId → RequestContext
458
+ this._sessionContexts = new Map(); // sessionId → SessionContext
459
+ this._agentContexts = new Map(); // agentId → AgentContext
460
+
461
+ // 引用计数(用于自动清理)
462
+ this._refCounts = new Map();
463
+ }
464
+
465
+ // ==================== Request Context ====================
466
+
467
+ /**
468
+ * 创建 Request Context
469
+ * @param {Object} options
470
+ * @returns {RequestContext}
471
+ */
472
+ createRequestContext(options = {}) {
473
+ const ctx = new RequestContext(options);
474
+ this._requestContexts.set(ctx.requestId, ctx);
475
+ this._refCounts.set(`request:${ctx.requestId}`, 1);
476
+ return ctx;
477
+ }
478
+
479
+ /**
480
+ * 获取 Request Context
481
+ * @param {string} requestId
482
+ * @returns {RequestContext|null}
483
+ */
484
+ getRequestContext(requestId) {
485
+ return this._requestContexts.get(requestId) || null;
486
+ }
487
+
488
+ /**
489
+ * 释放 Request Context
490
+ * @param {string} requestId
491
+ */
492
+ releaseRequestContext(requestId) {
493
+ const ctx = this._requestContexts.get(requestId);
494
+ if (ctx) {
495
+ ctx.cancel();
496
+ this._requestContexts.delete(requestId);
497
+ this._refCounts.delete(`request:${requestId}`);
498
+ }
499
+ }
500
+
501
+ // ==================== Session Context ====================
502
+
503
+ /**
504
+ * 获取或创建 Session Context
505
+ * @param {string} sessionId
506
+ * @param {Object} options
507
+ * @returns {SessionContext}
508
+ */
509
+ getOrCreateSessionContext(sessionId, options = {}) {
510
+ let ctx = this._sessionContexts.get(sessionId);
511
+ if (!ctx) {
512
+ ctx = new SessionContext(sessionId, this.framework, options);
513
+ this._sessionContexts.set(sessionId, ctx);
514
+ this._refCounts.set(`session:${sessionId}`, 1);
515
+ } else {
516
+ // 增加引用计数
517
+ this._refCounts.set(
518
+ `session:${sessionId}`,
519
+ (this._refCounts.get(`session:${sessionId}`) || 0) + 1
520
+ );
521
+ }
522
+ return ctx;
523
+ }
524
+
525
+ /**
526
+ * 获取 Session Context
527
+ * @param {string} sessionId
528
+ * @returns {SessionContext|null}
529
+ */
530
+ getSessionContext(sessionId) {
531
+ return this._sessionContexts.get(sessionId) || null;
532
+ }
533
+
534
+ /**
535
+ * 释放 Session Context
536
+ * @param {string} sessionId
537
+ */
538
+ releaseSessionContext(sessionId) {
539
+ const count = this._refCounts.get(`session:${sessionId}`) || 0;
540
+ if (count <= 1) {
541
+ this._sessionContexts.delete(sessionId);
542
+ this._refCounts.delete(`session:${sessionId}`);
543
+ } else {
544
+ this._refCounts.set(`session:${sessionId}`, count - 1);
545
+ }
546
+ }
547
+
548
+ // ==================== 统计 ====================
549
+
550
+ /**
551
+ * 获取统计信息
552
+ * @returns {Object}
553
+ */
554
+ getStats() {
555
+ return {
556
+ activeRequests: this._requestContexts.size,
557
+ activeSessions: this._sessionContexts.size,
558
+ activeAgents: this._agentContexts.size,
559
+ };
560
+ }
561
+ }
562
+ ```
563
+
564
+ ---
565
+
566
+ ## 五、Framework 改造
567
+
568
+ ### 5.1 新增方法
569
+
570
+ ```javascript
571
+ // src/core/framework.js 新增
572
+
573
+ const { AsyncLocalStorage } = require('async_hooks');
574
+
575
+ class Framework extends EventEmitter {
576
+ constructor(config = {}) {
577
+ super();
578
+ // ... 现有代码 ...
579
+
580
+ // 三层 AsyncLocalStorage
581
+ this._requestStorage = new AsyncLocalStorage();
582
+ this._sessionStorage = new AsyncLocalStorage();
583
+ this._agentStorage = new AsyncLocalStorage();
584
+
585
+ // Context 管理器
586
+ this._contextManager = new ContextManager(this);
587
+
588
+ // SessionContext 缓存(轻量管理,不依赖 ContextManager)
589
+ this._sessionContexts = new Map();
590
+ }
591
+
592
+ // ==================== Request Context ====================
593
+
594
+ /**
595
+ * 在 Request 上下文中执行函数
596
+ * @param {Object} context - Request 上下文
597
+ * @param {Function} fn - 异步函数
598
+ * @returns {Promise}
599
+ */
600
+ runWithContext(context, fn) {
601
+ return this._requestStorage.run(context, fn);
602
+ }
603
+
604
+ /**
605
+ * 获取当前 Request 上下文
606
+ * @returns {Object|null}
607
+ */
608
+ getRequestContext() {
609
+ return this._requestStorage.getStore() || null;
610
+ }
611
+
612
+ /**
613
+ * 创建 Request Context 并执行
614
+ * @param {Object} options - Request 选项
615
+ * @param {Function} fn - 异步函数
616
+ * @returns {Promise}
617
+ */
618
+ async withRequest(options, fn) {
619
+ const requestCtx = new RequestContext(options);
620
+ return this._requestStorage.run(requestCtx, fn);
621
+ }
622
+
623
+ // ==================== Session Context ====================
624
+
625
+ /**
626
+ * 获取或创建 SessionContext
627
+ * @param {string} sessionId - 会话 ID
628
+ * @param {Object} options - 配置选项
629
+ * @returns {SessionContext}
630
+ */
631
+ getOrCreateSessionContext(sessionId, options = {}) {
632
+ let ctx = this._sessionContexts.get(sessionId);
633
+ if (!ctx) {
634
+ ctx = new SessionContext(sessionId, this, options);
635
+ this._sessionContexts.set(sessionId, ctx);
636
+ this.emit('session:context-created', ctx);
637
+ }
638
+ return ctx;
639
+ }
640
+
641
+ /**
642
+ * 在 Session 上下文中执行函数
643
+ * @param {string} sessionId - 会话 ID
644
+ * @param {Object} options - Session 选项
645
+ * @param {Function} fn - 异步函数
646
+ * @returns {Promise}
647
+ */
648
+ async runInSession(sessionId, options, fn) {
649
+ const sessionCtx = this.getOrCreateSessionContext(sessionId, options);
650
+ const context = { sessionContext: sessionCtx, sessionId };
651
+ return this._sessionStorage.run(context, fn);
652
+ }
653
+
654
+ /**
655
+ * 获取当前 Session 上下文(便捷方法)
656
+ * @returns {SessionContext|null}
657
+ */
658
+ getSessionContext() {
659
+ const store = this._sessionStorage.getStore();
660
+ return store?.sessionContext || null;
661
+ }
662
+
663
+ /**
664
+ * 获取当前 sessionId(便捷方法)
665
+ * @returns {string|null}
666
+ */
667
+ getCurrentSessionId() {
668
+ const store = this._sessionStorage.getStore();
669
+ return store?.sessionId || null;
670
+ }
671
+
672
+ /**
673
+ * 销毁 Session Context
674
+ * @param {string} sessionId
675
+ */
676
+ destroySessionContext(sessionId) {
677
+ if (this._sessionContexts.has(sessionId)) {
678
+ const ctx = this._sessionContexts.get(sessionId);
679
+ this._sessionContexts.delete(sessionId);
680
+ this.emit('session:context-destroyed', ctx);
681
+ }
682
+ }
683
+
684
+ // ==================== Agent Context ====================
685
+
686
+ /**
687
+ * 获取或创建 AgentContext
688
+ * @param {string} agentId
689
+ * @param {Agent} agent
690
+ * @returns {AgentContext}
691
+ */
692
+ getOrCreateAgentContext(agentId, agent) {
693
+ if (!this._agentContexts) {
694
+ this._agentContexts = new Map();
695
+ }
696
+ let ctx = this._agentContexts.get(agentId);
697
+ if (!ctx) {
698
+ ctx = new AgentContext(agentId, agent);
699
+ this._agentContexts.set(agentId, ctx);
700
+ }
701
+ return ctx;
702
+ }
703
+
704
+ /**
705
+ * 在 Agent 上下文中执行函数
706
+ * @param {string} agentId
707
+ * @param {Function} fn
708
+ * @returns {Promise}
709
+ */
710
+ runWithAgent(agentId, fn) {
711
+ const ctx = this._agentContexts.get(agentId);
712
+ if (!ctx) {
713
+ throw new Error(`AgentContext not found: ${agentId}`);
714
+ }
715
+ return this._agentStorage.run({ agentContext: ctx, agentId }, fn);
716
+ }
717
+
718
+ /**
719
+ * 获取当前 Agent 上下文
720
+ * @returns {AgentContext|null}
721
+ */
722
+ getAgentContext() {
723
+ const store = this._agentStorage.getStore();
724
+ return store?.agentContext || null;
725
+ }
726
+
727
+ // ==================== 组合上下文 ====================
728
+
729
+ /**
730
+ * 在完整上下文中执行(Request + Session + Agent)
731
+ * @param {Object} requestOptions - Request 选项
732
+ * @param {string} sessionId - Session ID
733
+ * @param {string} agentId - Agent ID
734
+ * @param {Function} fn - 异步函数
735
+ * @returns {Promise}
736
+ */
737
+ async runInFullContext(requestOptions, sessionId, agentId, fn) {
738
+ const requestCtx = new RequestContext(requestOptions);
739
+ const sessionCtx = this.getOrCreateSessionContext(sessionId);
740
+ const agentCtx = this.getOrCreateAgentContext(
741
+ agentId,
742
+ this._agents.find((a) => a.id === agentId)
743
+ );
744
+
745
+ const requestStore = { ...requestCtx };
746
+ const sessionStore = { sessionContext: sessionCtx, sessionId };
747
+ const agentStore = { agentContext: agentCtx, agentId };
748
+
749
+ return this._requestStorage.run(requestStore, () => {
750
+ return this._sessionStorage.run(sessionStore, () => {
751
+ return this._agentStorage.run(agentStore, fn);
752
+ });
753
+ });
754
+ }
755
+ }
756
+ ```
757
+
758
+ ---
759
+
760
+ ## 六、AgentChatHandler 改造
761
+
762
+ ### 6.1 Per-Session 消息存储
763
+
764
+ ```javascript
765
+ // agent-chat.js 改造
766
+
767
+ class AgentChatHandler extends EventEmitter {
768
+ constructor(agent, config = {}) {
769
+ super();
770
+
771
+ // 移除共享的 _messages
772
+ // this._messages = []; // 删除!
773
+
774
+ // 保留队列机制(已经是 Per-Session,正确)
775
+ this._sessionQueues = new Map();
776
+ this._processingSessions = new Set();
777
+
778
+ // Per-Session 消息存储(替代原来的 _messages)
779
+ // 结构:sessionId → { messages: [], historyLoaded: false, compressionState: {} }
780
+ this._sessionMessageStores = new Map();
781
+
782
+ // ...
783
+ }
784
+
785
+ /**
786
+ * 获取当前 session 的消息存储
787
+ * @private
788
+ * @returns {Object} messageStore
789
+ */
790
+ _getSessionMessageStore(sessionId) {
791
+ if (!sessionId) {
792
+ // 无 session 的单次请求,使用临时存储
793
+ return { messages: [], historyLoaded: false };
794
+ }
795
+
796
+ if (!this._sessionMessageStores.has(sessionId)) {
797
+ this._sessionMessageStores.set(sessionId, {
798
+ messages: [],
799
+ historyLoaded: false,
800
+ compressionState: {
801
+ lastCompressedAt: null,
802
+ lastTokenCount: 0,
803
+ },
804
+ });
805
+ }
806
+ return this._sessionMessageStores.get(sessionId);
807
+ }
808
+
809
+ /**
810
+ * 从 session 存储加载聊天历史
811
+ * @param {string} sessionId
812
+ * @returns {Array}
813
+ * @private
814
+ */
815
+ _loadHistoryFromSession(sessionId) {
816
+ if (!sessionId) return [];
817
+
818
+ try {
819
+ // 优先从 SessionContext 获取
820
+ const sessionCtx = this.framework.getSessionContext();
821
+ if (sessionCtx && sessionCtx.sessionId === sessionId) {
822
+ const messages = sessionCtx.getMessages();
823
+ if (messages && messages.length > 0) {
824
+ return messages;
825
+ }
826
+ }
827
+
828
+ // 回退到 SessionPlugin
829
+ const sessionPlugin = this.agent.framework.pluginManager.get('session');
830
+ if (!sessionPlugin) return [];
831
+
832
+ const messages = sessionPlugin.getHistory(sessionId);
833
+ return messages || [];
834
+ } catch (err) {
835
+ // 忽略加载错误
836
+ }
837
+ return [];
838
+ }
839
+
840
+ /**
841
+ * 保存聊天历史到 session 存储
842
+ * @param {string} sessionId
843
+ * @param {Array} messages
844
+ * @private
845
+ */
846
+ _saveHistoryToSession(sessionId, messages) {
847
+ if (!sessionId) return;
848
+
849
+ try {
850
+ // 优先保存到 SessionContext
851
+ const sessionCtx = this.framework.getSessionContext();
852
+ if (sessionCtx && sessionCtx.sessionId === sessionId) {
853
+ sessionCtx.replaceMessages(messages);
854
+ return;
855
+ }
856
+
857
+ // 回退到 SessionPlugin
858
+ const sessionPlugin = this.agent.framework.pluginManager.get('session');
859
+ if (!sessionPlugin) return;
860
+
861
+ const cleanedMessages = prepareMessagesForAPI(messages);
862
+ sessionPlugin.replaceMessages(sessionId, cleanedMessages);
863
+ } catch (err) {
864
+ // 忽略保存错误
865
+ }
866
+ }
867
+
868
+ /**
869
+ * 执行实际聊天逻辑
870
+ * @private
871
+ */
872
+ async _doChat(message, options = {}) {
873
+ const sessionId = options.sessionId || null;
874
+ const framework = this.agent.framework;
875
+
876
+ // 获取当前 session 的消息存储
877
+ const messageStore = this._getSessionMessageStore(sessionId);
878
+ const messages = messageStore.messages; // 使用 Per-Session 的消息数组
879
+
880
+ try {
881
+ // 从 session 加载聊天历史(只加载一次)
882
+ if (sessionId && !messageStore.historyLoaded) {
883
+ const savedMessages = this._loadHistoryFromSession(sessionId);
884
+ if (savedMessages.length > 0) {
885
+ messages.push(...savedMessages);
886
+ messageStore.historyLoaded = true;
887
+ logger.info(`Loaded ${savedMessages.length} messages from session ${sessionId}`);
888
+ }
889
+ }
890
+
891
+ // 刷新系统提示词
892
+ this._systemPrompt = this.agent._buildSystemPrompt();
893
+
894
+ const userMessage =
895
+ typeof message === 'string' ? { role: 'user', content: message } : message;
896
+ messages.push(userMessage);
897
+
898
+ // 检查是否需要压缩
899
+ const totalTokens = this._countMessagesTokens(messages);
900
+ if (totalTokens > this._maxContextTokens * 0.7) {
901
+ await this._compressContext(sessionId, messages, messageStore);
902
+ }
903
+
904
+ // 构建上下文对象
905
+ const requestCtx = framework.getRequestContext() || {};
906
+ const context = {
907
+ ...requestCtx,
908
+ sessionId,
909
+ sessionContext: framework.getSessionContext(),
910
+ isStream: false,
911
+ };
912
+
913
+ // 使用 runWithContext 执行
914
+ const result = await framework.runWithContext(context, async () => {
915
+ // 使用 Per-Session 的消息数组
916
+ return agent.generate({ messages, ...this.providerOptions });
917
+ });
918
+
919
+ messages.push(...result.response.messages);
920
+
921
+ // 保存聊天历史
922
+ if (sessionId) {
923
+ this._saveHistoryToSession(sessionId, messages);
924
+ }
925
+
926
+ return {
927
+ success: true,
928
+ message: result.text || '',
929
+ stepCount: result.stepCount || 1,
930
+ };
931
+ } catch (err) {
932
+ this.emit('error', { error: err.message });
933
+ throw err;
934
+ }
935
+ }
936
+
937
+ /**
938
+ * 压缩上下文
939
+ * @private
940
+ */
941
+ async _compressContext(sessionId, messages, messageStore) {
942
+ // 使用传入的 messages 引用,直接修改
943
+ // ... 压缩逻辑保持不变,只是不再操作 this._messages
944
+ // ...
945
+ }
946
+ }
947
+ ```
948
+
949
+ ---
950
+
951
+ ## 七、使用示例
952
+
953
+ ### 7.1 完整请求流程
954
+
955
+ ```javascript
956
+ // 创建请求
957
+ const requestId = generateId();
958
+
959
+ // 在完整上下文中执行
960
+ await framework.runInFullContext(
961
+ { requestId, timeout: 60000, isStream: false },
962
+ sessionId,
963
+ agentId,
964
+ async () => {
965
+ // 在这里可以获取任何层级的上下文
966
+ const reqCtx = framework.getRequestContext();
967
+ const sessCtx = framework.getSessionContext();
968
+ const agentCtx = framework.getAgentContext();
969
+
970
+ console.log(`Request ${reqCtx.requestId}, Session ${sessCtx.sessionId}`);
971
+
972
+ // Agent 执行
973
+ const result = await agent.chat('Hello');
974
+
975
+ return result;
976
+ }
977
+ );
978
+ ```
979
+
980
+ ### 7.2 工具中访问上下文
981
+
982
+ ```javascript
983
+ // 在工具执行时获取 session 信息
984
+ framework.registerTool({
985
+ name: 'get_session_info',
986
+ description: 'Get current session information',
987
+ inputSchema: z.object({}),
988
+ execute: async (args, framework) => {
989
+ const sessionCtx = framework.getSessionContext();
990
+ const requestCtx = framework.getRequestContext();
991
+
992
+ return {
993
+ sessionId: sessionCtx?.sessionId,
994
+ messageCount: sessionCtx?.metadata.messageCount,
995
+ requestId: requestCtx?.requestId,
996
+ elapsed: requestCtx?.getElapsed(),
997
+ };
998
+ },
999
+ });
1000
+ ```
1001
+
1002
+ ### 7.3 Workflow 中使用
1003
+
1004
+ ```javascript
1005
+ // workflow-engine.js
1006
+ async executeStep(step, context) {
1007
+ const sessionId = context.variables?._sessionId;
1008
+
1009
+ // 在 session 上下文中执行
1010
+ await this.framework.runInSession(sessionId, {}, async () => {
1011
+ const sessionCtx = this.framework.getSessionContext();
1012
+ sessionCtx.setVariable('workflow_state', state);
1013
+
1014
+ // 执行工具
1015
+ const result = await this.framework.executeTool(step.tool, args);
1016
+
1017
+ // 更新状态
1018
+ state = sessionCtx.getVariable('workflow_state');
1019
+ });
1020
+ }
1021
+ ```
1022
+
1023
+ ---
1024
+
1025
+ ## 八、向后兼容
1026
+
1027
+ ### 8.1 保留原有接口
1028
+
1029
+ ```javascript
1030
+ // Framework 保留原有方法,但标记为 deprecated
1031
+ class Framework {
1032
+ /**
1033
+ * @deprecated 使用 runInSession 代替
1034
+ */
1035
+ runWithContext(context, fn) {
1036
+ console.warn('runWithContext is deprecated, use runInSession for session context');
1037
+ // 保持原有逻辑兼容
1038
+ return this._requestStorage.run(context, fn);
1039
+ }
1040
+
1041
+ /**
1042
+ * @deprecated 使用 getSessionContext 代替
1043
+ */
1044
+ getExecutionContext() {
1045
+ console.warn('getExecutionContext is deprecated, use getSessionContext');
1046
+ const store = this._requestStorage.getStore();
1047
+ if (store?.sessionContext) {
1048
+ return { sessionId: store.sessionContext.sessionId };
1049
+ }
1050
+ return store || { sessionId: null };
1051
+ }
1052
+ }
1053
+ ```
1054
+
1055
+ ### 8.2 SessionPlugin 兼容
1056
+
1057
+ ```javascript
1058
+ // SessionPlugin 继续提供原有接口,但内部可以使用 SessionContext
1059
+ class SessionPlugin {
1060
+ getHistory(sessionId) {
1061
+ // 优先从 SessionContext 获取
1062
+ const ctx = this._framework.getSessionContext();
1063
+ if (ctx && ctx.sessionId === sessionId) {
1064
+ return ctx.getMessages();
1065
+ }
1066
+
1067
+ // 回退到原有存储
1068
+ const session = this._sessions.get(sessionId);
1069
+ return session?.messages || [];
1070
+ }
1071
+ }
1072
+ ```
1073
+
1074
+ ---
1075
+
1076
+ ## 九、迁移步骤
1077
+
1078
+ ### Phase 1: 引入 SessionContext(✅ 已完成)
1079
+
1080
+ 1. ✅ 创建 `src/core/session-context.js`
1081
+ 2. ✅ 创建 `src/core/request-context.js`
1082
+ 3. ✅ 创建 `src/core/agent-context.js`
1083
+ 4. ✅ 创建 `src/core/context-manager.js`
1084
+ 5. ✅ 在 Framework 中添加三层 AsyncLocalStorage
1085
+ 6. ✅ 实现 `getOrCreateSessionContext()` 和 `getSessionContext()`
1086
+ 7. ✅ **不修改** `AgentChatHandler`,保持原有 `_messages` 逻辑
1087
+
1088
+ **完成时间**: 2026-04-06
1089
+ **验证**: Bootstrap 测试通过,新组件正常工作
1090
+
1091
+ ### Phase 2: AgentChatHandler Per-Session 改造(✅ 已完成)
1092
+
1093
+ 1. ✅ 在 `AgentChatHandler` 中添加 `_sessionMessageStores` Map
1094
+ 2. ✅ 修改 `_doChat` 使用 Per-Session 的消息数组
1095
+ 3. ✅ 修改 `_compressContext` 使用传入的 messages 引用
1096
+ 4. ✅ 修改 `_simpleCompress` 使用传入的 messages 引用
1097
+ 5. ✅ 修改 `chatStream` 使用 Per-Session 消息数组
1098
+ 6. ✅ 修改 `clearHistory` 使用 Per-Session 消息存储
1099
+ 7. ✅ 验证多 session 并行不冲突
1100
+
1101
+ **完成时间**: 2026-04-06
1102
+ **验证**: Per-Session 消息隔离测试通过,clearHistory 测试通过
1103
+
1104
+ ### Phase 3: 移除 SessionPlugin 依赖(✅ 已完成)
1105
+
1106
+ 1. ✅ `_loadHistoryFromSession` 直接使用 `framework.getOrCreateSessionContext()`
1107
+ 2. ✅ `_saveHistoryToSession` 直接使用 `framework.getOrCreateSessionContext()`
1108
+ 3. ✅ `clearHistory` 中使用 SessionContext 替代 SessionPlugin
1109
+ 4. ✅ 验证测试通过
1110
+
1111
+ **完成时间**: 2026-04-06
1112
+ **验证**: Bootstrap 测试通过,SessionContext 方法测试通过
1113
+
1114
+ ### Phase 4: 文档和示例(✅ 已完成)
1115
+
1116
+ 1. ✅ 更新 CLAUDE.md 中的架构说明
1117
+ 2. ✅ 更新 CLAUDE.md 中的 Key Patterns
1118
+ 3. ✅ 集成测试通过
1119
+
1120
+ **完成时间**: 2026-04-06
1121
+ **验证**: 集成测试全部通过
1122
+
1123
+ ---
1124
+
1125
+ ## 十、风险和注意事项
1126
+
1127
+ ### 10.1 AsyncLocalStorage 嵌套限制
1128
+
1129
+ - Node.js 的 AsyncLocalStorage 不支持嵌套相同的 storage 实例
1130
+ - 需要使用不同的 storage 实例隔离不同层级
1131
+
1132
+ ### 10.2 SessionContext 内存泄漏
1133
+
1134
+ - `_sessionContexts` Map 需要在 session 过期时清理
1135
+ - 需要与 SessionPlugin 的 TTL 机制协同
1136
+
1137
+ ### 10.3 向后兼容
1138
+
1139
+ - 现有代码使用 `getExecutionContext()` 获取 sessionId
1140
+ - 需要保留此方法但指向新的 SessionContext
1141
+
1142
+ ---
1143
+
1144
+ ## 十一、存储抽象层设计
1145
+
1146
+ ### 11.1 设计目标
1147
+
1148
+ 支持多种存储后端(内存、文件、JSON、数据库),通过接口抽象实现可插拔。
1149
+
1150
+ ### 11.2 架构图
1151
+
1152
+ ```
1153
+ ┌─────────────────────────────────────────────────────────────┐
1154
+ │ SessionContext │
1155
+ │ ┌─────────────────────────────────────────────────────┐ │
1156
+ │ │ messages[], variables, metadata... │ │
1157
+ │ └─────────────────────────────────────────────────────┘ │
1158
+ │ │ │
1159
+ │ ▼ │
1160
+ │ ┌─────────────────────────────────────────────────────┐ │
1161
+ │ │ SessionStorageAdapter │ │
1162
+ │ │ ┌─────────────────────────────────────────────┐ │ │
1163
+ │ │ │ interface SessionStorage { │ │ │
1164
+ │ │ │ async load(sessionId): SessionData │ │ │
1165
+ │ │ │ async save(sessionId, data): void │ │ │
1166
+ │ │ │ async delete(sessionId): void │ │ │
1167
+ │ │ │ async list(): string[] │ │ │
1168
+ │ │ │ } │ │ │
1169
+ │ │ └─────────────────────────────────────────────┘ │ │
1170
+ │ └─────────────────────────────────────────────────────┘ │
1171
+ │ │ │
1172
+ │ ┌────────────────────┼────────────────────┐ │
1173
+ │ ▼ ▼ ▼ │
1174
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
1175
+ │ │MemoryStorage│ │ FileStorage │ │ JsonStorage │ │
1176
+ │ │ (默认) │ │ (持久化) │ │ (可迁移) │ │
1177
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
1178
+ │ │
1179
+ │ ┌────────────────────┐ │
1180
+ │ ▼ ▼ │
1181
+ │ ┌─────────────┐ ┌─────────────┐ │
1182
+ │ │SQLiteStorage│ │MongoStorage │ (未来扩展) │
1183
+ │ └─────────────┘ └─────────────┘ │
1184
+ └─────────────────────────────────────────────────────────────┘
1185
+ ```
1186
+
1187
+ ### 11.3 接口定义
1188
+
1189
+ ```javascript
1190
+ /**
1191
+ * Session 存储接口
1192
+ * 所有存储后端必须实现此接口
1193
+ */
1194
+ class SessionStorageAdapter {
1195
+ /**
1196
+ * 加载会话数据
1197
+ * @param {string} sessionId - 会话 ID
1198
+ * @returns {Promise<Object|null>} 会话数据,不存在返回 null
1199
+ */
1200
+ async load(sessionId) {
1201
+ throw new Error('Not implemented');
1202
+ }
1203
+
1204
+ /**
1205
+ * 保存会话数据
1206
+ * @param {string} sessionId - 会话 ID
1207
+ * @param {Object} data - 会话数据
1208
+ * @returns {Promise<void>}
1209
+ */
1210
+ async save(sessionId, data) {
1211
+ throw new Error('Not implemented');
1212
+ }
1213
+
1214
+ /**
1215
+ * 删除会话数据
1216
+ * @param {string} sessionId - 会话 ID
1217
+ * @returns {Promise<void>}
1218
+ */
1219
+ async delete(sessionId) {
1220
+ throw new Error('Not implemented');
1221
+ }
1222
+
1223
+ /**
1224
+ * 列出所有会话 ID
1225
+ * @returns {Promise<string[]>}
1226
+ */
1227
+ async list() {
1228
+ throw new Error('Not implemented');
1229
+ }
1230
+
1231
+ /**
1232
+ * 检查存储是否健康
1233
+ * @returns {Promise<boolean>}
1234
+ */
1235
+ async healthCheck() {
1236
+ throw new Error('Not implemented');
1237
+ }
1238
+ }
1239
+ ```
1240
+
1241
+ ### 11.4 内置存储实现
1242
+
1243
+ #### 11.4.1 MemoryStorage(默认)
1244
+
1245
+ ```javascript
1246
+ /**
1247
+ * 内存存储 - 默认实现
1248
+ * 适用于单进程、无需持久化的场景
1249
+ */
1250
+ class MemoryStorage extends SessionStorageAdapter {
1251
+ constructor() {
1252
+ super();
1253
+ this._store = new Map(); // sessionId → data
1254
+ }
1255
+
1256
+ async load(sessionId) {
1257
+ return this._store.get(sessionId) || null;
1258
+ }
1259
+
1260
+ async save(sessionId, data) {
1261
+ this._store.set(sessionId, {
1262
+ ...data,
1263
+ _cachedAt: Date.now(),
1264
+ });
1265
+ }
1266
+
1267
+ async delete(sessionId) {
1268
+ this._store.delete(sessionId);
1269
+ }
1270
+
1271
+ async list() {
1272
+ return Array.from(this._store.keys());
1273
+ }
1274
+
1275
+ async healthCheck() {
1276
+ return true;
1277
+ }
1278
+ }
1279
+ ```
1280
+
1281
+ #### 11.4.2 JsonStorage(文件)
1282
+
1283
+ ```javascript
1284
+ /**
1285
+ * JSON 文件存储
1286
+ * 适用于需要持久化、数据量较小的场景
1287
+ */
1288
+ class JsonStorage extends SessionStorageAdapter {
1289
+ /**
1290
+ * @param {Object} options
1291
+ * @param {string} options.dir - 存储目录
1292
+ * @param {string} [options.filePattern='{sessionId}.json'] - 文件名模式
1293
+ */
1294
+ constructor(options = {}) {
1295
+ super();
1296
+ this.dir = options.dir || '.sessions';
1297
+ this.filePattern = options.filePattern || '{sessionId}.json';
1298
+ this._ensureDir();
1299
+ }
1300
+
1301
+ _getFilePath(sessionId) {
1302
+ const filename = this.filePattern.replace('{sessionId}', sessionId);
1303
+ return path.join(this.dir, filename);
1304
+ }
1305
+
1306
+ _ensureDir() {
1307
+ if (!fs.existsSync(this.dir)) {
1308
+ fs.mkdirSync(this.dir, { recursive: true });
1309
+ }
1310
+ }
1311
+
1312
+ async load(sessionId) {
1313
+ const filePath = this._getFilePath(sessionId);
1314
+ if (!fs.existsSync(filePath)) {
1315
+ return null;
1316
+ }
1317
+ const content = await fs.readFile(filePath, 'utf-8');
1318
+ return JSON.parse(content);
1319
+ }
1320
+
1321
+ async save(sessionId, data) {
1322
+ const filePath = this._getFilePath(sessionId);
1323
+ await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');
1324
+ }
1325
+
1326
+ async delete(sessionId) {
1327
+ const filePath = this._getFilePath(sessionId);
1328
+ if (fs.existsSync(filePath)) {
1329
+ await fs.unlink(filePath);
1330
+ }
1331
+ }
1332
+
1333
+ async list() {
1334
+ if (!fs.existsSync(this.dir)) {
1335
+ return [];
1336
+ }
1337
+ const files = await fs.readdir(this.dir);
1338
+ return files.filter((f) => f.endsWith('.json')).map((f) => f.replace('.json', ''));
1339
+ }
1340
+
1341
+ async healthCheck() {
1342
+ return fs.existsSync(this.dir) && fs.statSync(this.dir).isDirectory();
1343
+ }
1344
+ }
1345
+ ```
1346
+
1347
+ #### 11.4.3 SQLiteStorage(示例)
1348
+
1349
+ ```javascript
1350
+ /**
1351
+ * SQLite 数据库存储
1352
+ * 适用于生产环境、需要事务支持的场景
1353
+ */
1354
+ class SQLiteStorage extends SessionStorageAdapter {
1355
+ /**
1356
+ * @param {Object} options
1357
+ * @param {string} [options.dbPath=':memory:'] - 数据库路径
1358
+ */
1359
+ constructor(options = {}) {
1360
+ super();
1361
+ this.dbPath = options.dbPath || '.sessions/sessions.db';
1362
+ this._db = null;
1363
+ }
1364
+
1365
+ async _getDb() {
1366
+ if (!this._db) {
1367
+ const Database = require('better-sqlite3');
1368
+ this._db = new Database(this.dbPath);
1369
+ this._initTable();
1370
+ }
1371
+ return this._db;
1372
+ }
1373
+
1374
+ _initTable() {
1375
+ const db = this._db;
1376
+ db.exec(`
1377
+ CREATE TABLE IF NOT EXISTS sessions (
1378
+ session_id TEXT PRIMARY KEY,
1379
+ data TEXT NOT NULL,
1380
+ created_at INTEGER NOT NULL,
1381
+ updated_at INTEGER NOT NULL
1382
+ )
1383
+ `);
1384
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_updated_at ON sessions(updated_at)`);
1385
+ }
1386
+
1387
+ async load(sessionId) {
1388
+ const db = await this._getDb();
1389
+ const row = db.prepare('SELECT data FROM sessions WHERE session_id = ?').get(sessionId);
1390
+ return row ? JSON.parse(row.data) : null;
1391
+ }
1392
+
1393
+ async save(sessionId, data) {
1394
+ const db = await this._getDb();
1395
+ const now = Date.now();
1396
+ const stmt = db.prepare(`
1397
+ INSERT OR REPLACE INTO sessions (session_id, data, created_at, updated_at)
1398
+ VALUES (?, ?, COALESCE((SELECT created_at FROM sessions WHERE session_id = ?), ?), ?)
1399
+ `);
1400
+ stmt.run(sessionId, JSON.stringify(data), sessionId, now, now);
1401
+ }
1402
+
1403
+ async delete(sessionId) {
1404
+ const db = await this._getDb();
1405
+ db.prepare('DELETE FROM sessions WHERE session_id = ?').run(sessionId);
1406
+ }
1407
+
1408
+ async list() {
1409
+ const db = await this._getDb();
1410
+ const rows = db.prepare('SELECT session_id FROM sessions').all();
1411
+ return rows.map((r) => r.session_id);
1412
+ }
1413
+
1414
+ async healthCheck() {
1415
+ try {
1416
+ const db = await this._getDb();
1417
+ db.prepare('SELECT 1').get();
1418
+ return true;
1419
+ } catch {
1420
+ return false;
1421
+ }
1422
+ }
1423
+ }
1424
+ ```
1425
+
1426
+ ### 11.5 SessionContext 集成
1427
+
1428
+ ```javascript
1429
+ class SessionContext {
1430
+ /**
1431
+ * @param {string} sessionId
1432
+ * @param {Framework} framework
1433
+ * @param {Object} options
1434
+ * @param {SessionStorageAdapter} [options.storage] - 存储适配器,默认 MemoryStorage
1435
+ */
1436
+ constructor(sessionId, framework, options = {}) {
1437
+ this.sessionId = sessionId;
1438
+ this.framework = framework;
1439
+ this.storage = options.storage || new MemoryStorage();
1440
+
1441
+ // ... 其他初始化
1442
+ }
1443
+
1444
+ /**
1445
+ * 加载会话数据(从存储)
1446
+ * @returns {Promise<boolean>} 是否加载成功
1447
+ */
1448
+ async loadFromStorage() {
1449
+ const data = await this.storage.load(this.sessionId);
1450
+ if (data) {
1451
+ this.messageStore.messages = data.messages || [];
1452
+ this.messageStore.historyLoaded = true;
1453
+
1454
+ // 恢复 variables
1455
+ if (data.variables) {
1456
+ this.variables = new Map(Object.entries(data.variables));
1457
+ }
1458
+
1459
+ // 恢复 metadata
1460
+ if (data.metadata) {
1461
+ this.metadata = { ...this.metadata, ...data.metadata };
1462
+ }
1463
+
1464
+ return true;
1465
+ }
1466
+ return false;
1467
+ }
1468
+
1469
+ /**
1470
+ * 保存会话数据(到存储)
1471
+ * @returns {Promise<void>}
1472
+ */
1473
+ async saveToStorage() {
1474
+ const data = {
1475
+ sessionId: this.sessionId,
1476
+ messages: this.messageStore.messages,
1477
+ variables: Object.fromEntries(this.variables),
1478
+ metadata: this.metadata,
1479
+ savedAt: Date.now(),
1480
+ };
1481
+ await this.storage.save(this.sessionId, data);
1482
+ }
1483
+
1484
+ /**
1485
+ * 删除会话数据
1486
+ * @returns {Promise<void>}
1487
+ */
1488
+ async deleteFromStorage() {
1489
+ await this.storage.delete(this.sessionId);
1490
+ }
1491
+ }
1492
+ ```
1493
+
1494
+ ### 11.6 Framework 集成
1495
+
1496
+ ```javascript
1497
+ class Framework {
1498
+ constructor(config = {}) {
1499
+ // ...
1500
+
1501
+ // 配置存储适配器
1502
+ const storageConfig = config.storage || {};
1503
+ this._configureStorage(storageConfig);
1504
+ }
1505
+
1506
+ _configureStorage(config) {
1507
+ if (typeof config === 'string') {
1508
+ // 简单字符串配置:'memory', 'file', 'json', 'sqlite'
1509
+ switch (config) {
1510
+ case 'memory':
1511
+ this.sessionStorage = new MemoryStorage();
1512
+ break;
1513
+ case 'file':
1514
+ case 'json':
1515
+ this.sessionStorage = new JsonStorage({ dir: '.sessions' });
1516
+ break;
1517
+ case 'sqlite':
1518
+ this.sessionStorage = new SQLiteStorage();
1519
+ break;
1520
+ default:
1521
+ this.sessionStorage = new MemoryStorage();
1522
+ }
1523
+ } else if (config instanceof SessionStorageAdapter) {
1524
+ // 直接传入适配器实例
1525
+ this.sessionStorage = config;
1526
+ } else if (config && typeof config.type === 'string') {
1527
+ // 对象配置 { type: 'file', dir: '.sessions' }
1528
+ switch (config.type) {
1529
+ case 'file':
1530
+ case 'json':
1531
+ this.sessionStorage = new JsonStorage(config);
1532
+ break;
1533
+ case 'sqlite':
1534
+ this.sessionStorage = new SQLiteStorage(config);
1535
+ break;
1536
+ default:
1537
+ this.sessionStorage = new MemoryStorage();
1538
+ }
1539
+ } else {
1540
+ this.sessionStorage = new MemoryStorage();
1541
+ }
1542
+ }
1543
+
1544
+ /**
1545
+ * 获取当前使用的存储适配器
1546
+ * @returns {SessionStorageAdapter}
1547
+ */
1548
+ getSessionStorage() {
1549
+ return this.sessionStorage;
1550
+ }
1551
+ }
1552
+ ```
1553
+
1554
+ ### 11.7 使用示例
1555
+
1556
+ ```javascript
1557
+ // 创建 Framework 时指定存储
1558
+ const framework = await Framework.bootstrap({
1559
+ storage: 'file', // 使用文件存储
1560
+ // 或
1561
+ storage: {
1562
+ type: 'sqlite',
1563
+ dbPath: '.sessions/sessions.db',
1564
+ },
1565
+ // 或
1566
+ storage: new CustomStorageAdapter(), // 自定义存储
1567
+ });
1568
+
1569
+ // 运行时切换存储
1570
+ framework.sessionStorage = new SQLiteStorage({ dbPath: '/tmp/sessions.db' });
1571
+ ```
1572
+
1573
+ ### 11.8 设计优势
1574
+
1575
+ | 特性 | 说明 |
1576
+ | ------------ | ---------------------------------- |
1577
+ | **接口隔离** | SessionContext 不关心具体存储实现 |
1578
+ | **可测试性** | 使用 MemoryStorage 进行单元测试 |
1579
+ | **渐进迁移** | 可从 JSON 文件迁移到 SQLite |
1580
+ | **插件化** | 用户可实现自己的存储适配器 |
1581
+ | **配置灵活** | 支持字符串、对象、实例多种配置方式 |
1582
+
1583
+ ---
1584
+
1585
+ ## 附录:关键文件变更清单
1586
+
1587
+ | 文件 | 变更类型 | 描述 |
1588
+ | ----------------------------- | -------- | ------------------------------------------------- |
1589
+ | `src/core/framework.js` | 修改 | 添加三层 AsyncLocalStorage,新增 Context 获取方法 |
1590
+ | `src/core/agent-chat.js` | 修改 | Per-Session 消息存储,移除共享 `_messages` |
1591
+ | `src/core/session-context.js` | 新增 | SessionContext 类 |
1592
+ | `src/core/request-context.js` | 新增 | RequestContext 类 |
1593
+ | `src/core/agent-context.js` | 新增 | AgentContext 类 |
1594
+ | `src/core/context-manager.js` | 新增 | ContextManager 类 |
1595
+ | `plugins/session-plugin.js` | 修改 | 优先使用 SessionContext |
1596
+ | `docs/CONTEXT_DESIGN.md` | 新增 | 本文档 |