@zooique/memora 2.0.3 → 3.0.0

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 (588) hide show
  1. package/CHANGELOG.md +1028 -0
  2. package/README.en.md +305 -272
  3. package/README.md +303 -260
  4. package/config.example.json +43 -0
  5. package/dist/agent/agent.d.ts +376 -408
  6. package/dist/agent/agent.d.ts.map +1 -1
  7. package/dist/agent/agent.js +1166 -926
  8. package/dist/agent/agent.js.map +1 -1
  9. package/dist/agent/assembler.d.ts +105 -43
  10. package/dist/agent/assembler.d.ts.map +1 -1
  11. package/dist/agent/assembler.js +543 -75
  12. package/dist/agent/assembler.js.map +1 -1
  13. package/dist/agent/budget.d.ts +131 -0
  14. package/dist/agent/budget.d.ts.map +1 -0
  15. package/dist/agent/budget.js +128 -0
  16. package/dist/agent/budget.js.map +1 -0
  17. package/dist/agent/builtinToolHandlers.d.ts +225 -38
  18. package/dist/agent/builtinToolHandlers.d.ts.map +1 -1
  19. package/dist/agent/builtinToolHandlers.js +748 -132
  20. package/dist/agent/builtinToolHandlers.js.map +1 -1
  21. package/dist/agent/builtinTools.d.ts +198 -7
  22. package/dist/agent/builtinTools.d.ts.map +1 -1
  23. package/dist/agent/builtinTools.js +646 -14
  24. package/dist/agent/builtinTools.js.map +1 -1
  25. package/dist/agent/compaction.d.ts +93 -0
  26. package/dist/agent/compaction.d.ts.map +1 -0
  27. package/dist/agent/compaction.js +192 -0
  28. package/dist/agent/compaction.js.map +1 -0
  29. package/dist/agent/constants.d.ts +137 -20
  30. package/dist/agent/constants.d.ts.map +1 -1
  31. package/dist/agent/constants.js +152 -20
  32. package/dist/agent/constants.js.map +1 -1
  33. package/dist/agent/contextManager.d.ts +119 -77
  34. package/dist/agent/contextManager.d.ts.map +1 -1
  35. package/dist/agent/contextManager.js +192 -135
  36. package/dist/agent/contextManager.js.map +1 -1
  37. package/dist/agent/contextPreparer.d.ts +78 -0
  38. package/dist/agent/contextPreparer.d.ts.map +1 -0
  39. package/dist/agent/contextPreparer.js +128 -0
  40. package/dist/agent/contextPreparer.js.map +1 -0
  41. package/dist/agent/duplicateInterceptor.d.ts +67 -0
  42. package/dist/agent/duplicateInterceptor.d.ts.map +1 -0
  43. package/dist/agent/duplicateInterceptor.js +82 -0
  44. package/dist/agent/duplicateInterceptor.js.map +1 -0
  45. package/dist/agent/guardRail.d.ts +166 -0
  46. package/dist/agent/guardRail.d.ts.map +1 -0
  47. package/dist/agent/guardRail.js +333 -0
  48. package/dist/agent/guardRail.js.map +1 -0
  49. package/dist/agent/loop.d.ts +716 -295
  50. package/dist/agent/loop.d.ts.map +1 -1
  51. package/dist/agent/loop.js +2178 -664
  52. package/dist/agent/loop.js.map +1 -1
  53. package/dist/agent/managers/archiveCoordinator.d.ts +14 -171
  54. package/dist/agent/managers/archiveCoordinator.d.ts.map +1 -1
  55. package/dist/agent/managers/archiveCoordinator.js +19 -287
  56. package/dist/agent/managers/archiveCoordinator.js.map +1 -1
  57. package/dist/agent/managers/chatLockManager.d.ts +20 -88
  58. package/dist/agent/managers/chatLockManager.d.ts.map +1 -1
  59. package/dist/agent/managers/chatLockManager.js +31 -103
  60. package/dist/agent/managers/chatLockManager.js.map +1 -1
  61. package/dist/agent/managers/dedupManager.d.ts +31 -115
  62. package/dist/agent/managers/dedupManager.d.ts.map +1 -1
  63. package/dist/agent/managers/dedupManager.js +48 -136
  64. package/dist/agent/managers/dedupManager.js.map +1 -1
  65. package/dist/agent/managers/goalConsistencyChecker.d.ts +42 -0
  66. package/dist/agent/managers/goalConsistencyChecker.d.ts.map +1 -0
  67. package/dist/agent/managers/goalConsistencyChecker.js +178 -0
  68. package/dist/agent/managers/goalConsistencyChecker.js.map +1 -0
  69. package/dist/agent/managers/llmCaller.d.ts +124 -0
  70. package/dist/agent/managers/llmCaller.d.ts.map +1 -0
  71. package/dist/agent/managers/llmCaller.js +417 -0
  72. package/dist/agent/managers/llmCaller.js.map +1 -0
  73. package/dist/agent/managers/llmJudgeHelper.d.ts +8 -25
  74. package/dist/agent/managers/llmJudgeHelper.d.ts.map +1 -1
  75. package/dist/agent/managers/llmJudgeHelper.js +7 -24
  76. package/dist/agent/managers/llmJudgeHelper.js.map +1 -1
  77. package/dist/agent/managers/loopMetrics.d.ts +58 -0
  78. package/dist/agent/managers/loopMetrics.d.ts.map +1 -0
  79. package/dist/agent/managers/loopMetrics.js +68 -0
  80. package/dist/agent/managers/loopMetrics.js.map +1 -0
  81. package/dist/agent/managers/memoryAdvisor.d.ts +27 -120
  82. package/dist/agent/managers/memoryAdvisor.d.ts.map +1 -1
  83. package/dist/agent/managers/memoryAdvisor.js +85 -207
  84. package/dist/agent/managers/memoryAdvisor.js.map +1 -1
  85. package/dist/agent/managers/memoryGovernance.d.ts +10 -42
  86. package/dist/agent/managers/memoryGovernance.d.ts.map +1 -1
  87. package/dist/agent/managers/memoryGovernance.js +19 -36
  88. package/dist/agent/managers/memoryGovernance.js.map +1 -1
  89. package/dist/agent/managers/memoryInspector.d.ts +59 -268
  90. package/dist/agent/managers/memoryInspector.d.ts.map +1 -1
  91. package/dist/agent/managers/memoryInspector.js +91 -442
  92. package/dist/agent/managers/memoryInspector.js.map +1 -1
  93. package/dist/agent/managers/roundSummaryGenerator.d.ts +56 -0
  94. package/dist/agent/managers/roundSummaryGenerator.d.ts.map +1 -0
  95. package/dist/agent/managers/roundSummaryGenerator.js +201 -0
  96. package/dist/agent/managers/roundSummaryGenerator.js.map +1 -0
  97. package/dist/agent/managers/sessionArchiver.d.ts +19 -68
  98. package/dist/agent/managers/sessionArchiver.d.ts.map +1 -1
  99. package/dist/agent/managers/sessionArchiver.js +76 -147
  100. package/dist/agent/managers/sessionArchiver.js.map +1 -1
  101. package/dist/agent/managers/sessionManager.d.ts +286 -57
  102. package/dist/agent/managers/sessionManager.d.ts.map +1 -1
  103. package/dist/agent/managers/sessionManager.js +846 -85
  104. package/dist/agent/managers/sessionManager.js.map +1 -1
  105. package/dist/agent/managers/sessionNamer.d.ts +35 -0
  106. package/dist/agent/managers/sessionNamer.d.ts.map +1 -0
  107. package/dist/agent/managers/sessionNamer.js +104 -0
  108. package/dist/agent/managers/sessionNamer.js.map +1 -0
  109. package/dist/agent/managers/streamAccumulator.d.ts +8 -33
  110. package/dist/agent/managers/streamAccumulator.d.ts.map +1 -1
  111. package/dist/agent/managers/streamAccumulator.js +4 -29
  112. package/dist/agent/managers/streamAccumulator.js.map +1 -1
  113. package/dist/agent/managers/textPolishManager.d.ts +8 -44
  114. package/dist/agent/managers/textPolishManager.d.ts.map +1 -1
  115. package/dist/agent/managers/textPolishManager.js +9 -41
  116. package/dist/agent/managers/textPolishManager.js.map +1 -1
  117. package/dist/agent/managers/toolCallHelpers.d.ts +105 -0
  118. package/dist/agent/managers/toolCallHelpers.d.ts.map +1 -0
  119. package/dist/agent/managers/toolCallHelpers.js +161 -0
  120. package/dist/agent/managers/toolCallHelpers.js.map +1 -0
  121. package/dist/agent/managers/workProjection.d.ts +55 -100
  122. package/dist/agent/managers/workProjection.d.ts.map +1 -1
  123. package/dist/agent/managers/workProjection.js +164 -268
  124. package/dist/agent/managers/workProjection.js.map +1 -1
  125. package/dist/agent/messageHistory.d.ts +134 -84
  126. package/dist/agent/messageHistory.d.ts.map +1 -1
  127. package/dist/agent/messageHistory.js +419 -133
  128. package/dist/agent/messageHistory.js.map +1 -1
  129. package/dist/agent/needsPlanning.d.ts +28 -0
  130. package/dist/agent/needsPlanning.d.ts.map +1 -0
  131. package/dist/agent/needsPlanning.js +68 -0
  132. package/dist/agent/needsPlanning.js.map +1 -0
  133. package/dist/agent/seed/index.d.ts +11 -0
  134. package/dist/agent/seed/index.d.ts.map +1 -0
  135. package/dist/agent/seed/index.js +10 -0
  136. package/dist/agent/seed/index.js.map +1 -0
  137. package/dist/agent/seed/orchestrator.d.ts +83 -0
  138. package/dist/agent/seed/orchestrator.d.ts.map +1 -0
  139. package/dist/agent/seed/orchestrator.js +260 -0
  140. package/dist/agent/seed/orchestrator.js.map +1 -0
  141. package/dist/agent/seed/prepare.d.ts +49 -0
  142. package/dist/agent/seed/prepare.d.ts.map +1 -0
  143. package/dist/agent/seed/prepare.js +107 -0
  144. package/dist/agent/seed/prepare.js.map +1 -0
  145. package/dist/agent/seed/types.d.ts +115 -0
  146. package/dist/agent/seed/types.d.ts.map +1 -0
  147. package/dist/agent/seed/types.js +15 -0
  148. package/dist/agent/seed/types.js.map +1 -0
  149. package/dist/agent/sessionStateMachine.d.ts +78 -0
  150. package/dist/agent/sessionStateMachine.d.ts.map +1 -0
  151. package/dist/agent/sessionStateMachine.js +182 -0
  152. package/dist/agent/sessionStateMachine.js.map +1 -0
  153. package/dist/agent/taskTableRenderer.d.ts +51 -0
  154. package/dist/agent/taskTableRenderer.d.ts.map +1 -0
  155. package/dist/agent/taskTableRenderer.js +115 -0
  156. package/dist/agent/taskTableRenderer.js.map +1 -0
  157. package/dist/agent/toolExecutor.d.ts +236 -10
  158. package/dist/agent/toolExecutor.d.ts.map +1 -1
  159. package/dist/agent/toolExecutor.js +932 -21
  160. package/dist/agent/toolExecutor.js.map +1 -1
  161. package/dist/agent/toolLedger.d.ts +120 -0
  162. package/dist/agent/toolLedger.d.ts.map +1 -0
  163. package/dist/agent/toolLedger.js +149 -0
  164. package/dist/agent/toolLedger.js.map +1 -0
  165. package/dist/agent/toolResultCache.d.ts +134 -0
  166. package/dist/agent/toolResultCache.d.ts.map +1 -0
  167. package/dist/agent/toolResultCache.js +243 -0
  168. package/dist/agent/toolResultCache.js.map +1 -0
  169. package/dist/agent/toolResultOffload.d.ts +59 -0
  170. package/dist/agent/toolResultOffload.d.ts.map +1 -0
  171. package/dist/agent/toolResultOffload.js +70 -0
  172. package/dist/agent/toolResultOffload.js.map +1 -0
  173. package/dist/agent/toolRunner.d.ts +59 -0
  174. package/dist/agent/toolRunner.d.ts.map +1 -0
  175. package/dist/agent/toolRunner.js +147 -0
  176. package/dist/agent/toolRunner.js.map +1 -0
  177. package/dist/agent/tracer.d.ts +99 -107
  178. package/dist/agent/tracer.d.ts.map +1 -1
  179. package/dist/agent/tracer.js +24 -69
  180. package/dist/agent/tracer.js.map +1 -1
  181. package/dist/agent/types.d.ts +510 -169
  182. package/dist/agent/types.d.ts.map +1 -1
  183. package/dist/agent/types.js +2 -51
  184. package/dist/agent/types.js.map +1 -1
  185. package/dist/code-exec/codeExecutionProvider.d.ts +16 -0
  186. package/dist/code-exec/codeExecutionProvider.d.ts.map +1 -0
  187. package/dist/code-exec/codeExecutionProvider.js +57 -0
  188. package/dist/code-exec/codeExecutionProvider.js.map +1 -0
  189. package/dist/code-exec/types.d.ts +37 -0
  190. package/dist/code-exec/types.d.ts.map +1 -0
  191. package/dist/code-exec/types.js +12 -0
  192. package/dist/code-exec/types.js.map +1 -0
  193. package/dist/config/expandEnvVars.d.ts +22 -0
  194. package/dist/config/expandEnvVars.d.ts.map +1 -0
  195. package/dist/config/expandEnvVars.js +38 -0
  196. package/dist/config/expandEnvVars.js.map +1 -0
  197. package/dist/config/loader.d.ts +27 -98
  198. package/dist/config/loader.d.ts.map +1 -1
  199. package/dist/config/loader.js +110 -223
  200. package/dist/config/loader.js.map +1 -1
  201. package/dist/index.d.ts +67 -77
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +90 -69
  204. package/dist/index.js.map +1 -1
  205. package/dist/llm/abortSignal.d.ts +2 -3
  206. package/dist/llm/abortSignal.d.ts.map +1 -1
  207. package/dist/llm/abortSignal.js +2 -3
  208. package/dist/llm/abortSignal.js.map +1 -1
  209. package/dist/llm/factory.d.ts +22 -32
  210. package/dist/llm/factory.d.ts.map +1 -1
  211. package/dist/llm/factory.js +29 -46
  212. package/dist/llm/factory.js.map +1 -1
  213. package/dist/llm/openaiCompatible.d.ts +25 -40
  214. package/dist/llm/openaiCompatible.d.ts.map +1 -1
  215. package/dist/llm/openaiCompatible.js +179 -120
  216. package/dist/llm/openaiCompatible.js.map +1 -1
  217. package/dist/llm/provider.d.ts +29 -42
  218. package/dist/llm/provider.d.ts.map +1 -1
  219. package/dist/llm/provider.js +11 -10
  220. package/dist/llm/provider.js.map +1 -1
  221. package/dist/llm/types.d.ts +54 -11
  222. package/dist/llm/types.d.ts.map +1 -1
  223. package/dist/llm/types.js +0 -5
  224. package/dist/llm/types.js.map +1 -1
  225. package/dist/logging/logger.d.ts +8 -31
  226. package/dist/logging/logger.d.ts.map +1 -1
  227. package/dist/logging/logger.js +28 -215
  228. package/dist/logging/logger.js.map +1 -1
  229. package/dist/logging/loggerInterface.d.ts +9 -8
  230. package/dist/logging/loggerInterface.d.ts.map +1 -1
  231. package/dist/memory/gcService.d.ts +164 -0
  232. package/dist/memory/gcService.d.ts.map +1 -0
  233. package/dist/memory/gcService.js +215 -0
  234. package/dist/memory/gcService.js.map +1 -0
  235. package/dist/memory/governance.d.ts +11 -44
  236. package/dist/memory/governance.d.ts.map +1 -1
  237. package/dist/memory/governance.js +11 -64
  238. package/dist/memory/governance.js.map +1 -1
  239. package/dist/memory/inMemoryRoundStore.d.ts +124 -0
  240. package/dist/memory/inMemoryRoundStore.d.ts.map +1 -0
  241. package/dist/memory/inMemoryRoundStore.js +216 -0
  242. package/dist/memory/inMemoryRoundStore.js.map +1 -0
  243. package/dist/memory/inMemorySessionStore.d.ts +92 -0
  244. package/dist/memory/inMemorySessionStore.d.ts.map +1 -0
  245. package/dist/memory/inMemorySessionStore.js +219 -0
  246. package/dist/memory/inMemorySessionStore.js.map +1 -0
  247. package/dist/memory/inMemorySessionViewLoader.d.ts +89 -0
  248. package/dist/memory/inMemorySessionViewLoader.d.ts.map +1 -0
  249. package/dist/memory/inMemorySessionViewLoader.js +183 -0
  250. package/dist/memory/inMemorySessionViewLoader.js.map +1 -0
  251. package/dist/memory/inMemoryStorage.d.ts +25 -175
  252. package/dist/memory/inMemoryStorage.d.ts.map +1 -1
  253. package/dist/memory/inMemoryStorage.js +32 -221
  254. package/dist/memory/inMemoryStorage.js.map +1 -1
  255. package/dist/memory/keywordsTouch.d.ts +20 -0
  256. package/dist/memory/keywordsTouch.d.ts.map +1 -0
  257. package/dist/memory/keywordsTouch.js +25 -0
  258. package/dist/memory/keywordsTouch.js.map +1 -0
  259. package/dist/memory/lockManager.d.ts +2 -2
  260. package/dist/memory/lockManager.d.ts.map +1 -1
  261. package/dist/memory/lockManager.js +6 -8
  262. package/dist/memory/lockManager.js.map +1 -1
  263. package/dist/memory/projectManager.d.ts +30 -129
  264. package/dist/memory/projectManager.d.ts.map +1 -1
  265. package/dist/memory/projectManager.js +39 -189
  266. package/dist/memory/projectManager.js.map +1 -1
  267. package/dist/memory/projectRegistry.d.ts +14 -62
  268. package/dist/memory/projectRegistry.d.ts.map +1 -1
  269. package/dist/memory/projectRegistry.js +27 -111
  270. package/dist/memory/projectRegistry.js.map +1 -1
  271. package/dist/memory/roundStore.d.ts +629 -0
  272. package/dist/memory/roundStore.d.ts.map +1 -0
  273. package/dist/memory/roundStore.js +116 -0
  274. package/dist/memory/roundStore.js.map +1 -0
  275. package/dist/memory/sessionStore.d.ts +70 -61
  276. package/dist/memory/sessionStore.d.ts.map +1 -1
  277. package/dist/memory/sessionStore.js +23 -1
  278. package/dist/memory/sessionStore.js.map +1 -1
  279. package/dist/memory/sessionViewLoader.d.ts +141 -0
  280. package/dist/memory/sessionViewLoader.d.ts.map +1 -0
  281. package/dist/memory/sessionViewLoader.js +65 -0
  282. package/dist/memory/sessionViewLoader.js.map +1 -0
  283. package/dist/memory/sourceValidation.d.ts +10 -12
  284. package/dist/memory/sourceValidation.d.ts.map +1 -1
  285. package/dist/memory/sourceValidation.js +16 -32
  286. package/dist/memory/sourceValidation.js.map +1 -1
  287. package/dist/memory/storageInterface.d.ts +44 -150
  288. package/dist/memory/storageInterface.d.ts.map +1 -1
  289. package/dist/memory/types.d.ts +65 -164
  290. package/dist/memory/types.d.ts.map +1 -1
  291. package/dist/memory/types.js +81 -83
  292. package/dist/memory/types.js.map +1 -1
  293. package/dist/project-search/projectSearchProvider.d.ts +29 -0
  294. package/dist/project-search/projectSearchProvider.d.ts.map +1 -0
  295. package/dist/project-search/projectSearchProvider.js +57 -0
  296. package/dist/project-search/projectSearchProvider.js.map +1 -0
  297. package/dist/project-search/terms.d.ts +14 -0
  298. package/dist/project-search/terms.d.ts.map +1 -0
  299. package/dist/project-search/terms.js +68 -0
  300. package/dist/project-search/terms.js.map +1 -0
  301. package/dist/project-search/types.d.ts +139 -0
  302. package/dist/project-search/types.d.ts.map +1 -0
  303. package/dist/project-search/types.js +8 -0
  304. package/dist/project-search/types.js.map +1 -0
  305. package/dist/role-pack/capabilityMap.d.ts +28 -0
  306. package/dist/role-pack/capabilityMap.d.ts.map +1 -0
  307. package/dist/role-pack/capabilityMap.js +56 -0
  308. package/dist/role-pack/capabilityMap.js.map +1 -0
  309. package/dist/role-pack/constants.d.ts +21 -0
  310. package/dist/role-pack/constants.d.ts.map +1 -0
  311. package/dist/role-pack/constants.js +21 -0
  312. package/dist/role-pack/constants.js.map +1 -0
  313. package/dist/role-pack/rolePackManager.d.ts +269 -0
  314. package/dist/role-pack/rolePackManager.d.ts.map +1 -0
  315. package/dist/role-pack/rolePackManager.js +1082 -0
  316. package/dist/role-pack/rolePackManager.js.map +1 -0
  317. package/dist/role-pack/strategyKeys.d.ts +100 -0
  318. package/dist/role-pack/strategyKeys.d.ts.map +1 -0
  319. package/dist/role-pack/strategyKeys.js +184 -0
  320. package/dist/role-pack/strategyKeys.js.map +1 -0
  321. package/dist/role-pack/strategyResolver.d.ts +109 -0
  322. package/dist/role-pack/strategyResolver.d.ts.map +1 -0
  323. package/dist/role-pack/strategyResolver.js +304 -0
  324. package/dist/role-pack/strategyResolver.js.map +1 -0
  325. package/dist/role-pack/types.d.ts +237 -0
  326. package/dist/role-pack/types.d.ts.map +1 -0
  327. package/dist/role-pack/types.js +8 -0
  328. package/dist/role-pack/types.js.map +1 -0
  329. package/dist/role-pack/validator.d.ts +38 -0
  330. package/dist/role-pack/validator.d.ts.map +1 -0
  331. package/dist/role-pack/validator.js +436 -0
  332. package/dist/role-pack/validator.js.map +1 -0
  333. package/dist/security/pathGuard.d.ts +69 -72
  334. package/dist/security/pathGuard.d.ts.map +1 -1
  335. package/dist/security/pathGuard.js +151 -131
  336. package/dist/security/pathGuard.js.map +1 -1
  337. package/dist/skill/skillLayer3.d.ts +91 -0
  338. package/dist/skill/skillLayer3.d.ts.map +1 -0
  339. package/dist/skill/skillLayer3.js +104 -0
  340. package/dist/skill/skillLayer3.js.map +1 -0
  341. package/dist/skill/skillManager.d.ts +136 -19
  342. package/dist/skill/skillManager.d.ts.map +1 -1
  343. package/dist/skill/skillManager.js +312 -61
  344. package/dist/skill/skillManager.js.map +1 -1
  345. package/dist/skill/skillScriptRunner.d.ts +97 -0
  346. package/dist/skill/skillScriptRunner.d.ts.map +1 -0
  347. package/dist/skill/skillScriptRunner.js +271 -0
  348. package/dist/skill/skillScriptRunner.js.map +1 -0
  349. package/dist/skill/types.d.ts +73 -16
  350. package/dist/skill/types.d.ts.map +1 -1
  351. package/dist/skill/types.js +5 -1
  352. package/dist/skill/types.js.map +1 -1
  353. package/dist/utils/array.d.ts +5 -6
  354. package/dist/utils/array.d.ts.map +1 -1
  355. package/dist/utils/array.js +6 -6
  356. package/dist/utils/array.js.map +1 -1
  357. package/dist/utils/atomicWrite.d.ts +8 -0
  358. package/dist/utils/atomicWrite.d.ts.map +1 -0
  359. package/dist/utils/atomicWrite.js +31 -0
  360. package/dist/utils/atomicWrite.js.map +1 -0
  361. package/dist/utils/backgroundTask.d.ts +38 -0
  362. package/dist/utils/backgroundTask.d.ts.map +1 -0
  363. package/dist/utils/backgroundTask.js +117 -0
  364. package/dist/utils/backgroundTask.js.map +1 -0
  365. package/dist/utils/configResourceManager.d.ts +40 -72
  366. package/dist/utils/configResourceManager.d.ts.map +1 -1
  367. package/dist/utils/configResourceManager.js +78 -76
  368. package/dist/utils/configResourceManager.js.map +1 -1
  369. package/dist/utils/errors.d.ts +44 -69
  370. package/dist/utils/errors.d.ts.map +1 -1
  371. package/dist/utils/errors.js +54 -71
  372. package/dist/utils/errors.js.map +1 -1
  373. package/dist/utils/eventEmitter.d.ts +104 -103
  374. package/dist/utils/eventEmitter.d.ts.map +1 -1
  375. package/dist/utils/eventEmitter.js +37 -45
  376. package/dist/utils/eventEmitter.js.map +1 -1
  377. package/dist/utils/fileSafe.d.ts +20 -0
  378. package/dist/utils/fileSafe.d.ts.map +1 -0
  379. package/dist/utils/fileSafe.js +56 -0
  380. package/dist/utils/fileSafe.js.map +1 -0
  381. package/dist/utils/frontmatter.d.ts +2 -3
  382. package/dist/utils/frontmatter.d.ts.map +1 -1
  383. package/dist/utils/frontmatter.js +2 -3
  384. package/dist/utils/frontmatter.js.map +1 -1
  385. package/dist/utils/hash.d.ts +12 -0
  386. package/dist/utils/hash.d.ts.map +1 -0
  387. package/dist/utils/hash.js +27 -0
  388. package/dist/utils/hash.js.map +1 -0
  389. package/dist/utils/http.d.ts +17 -0
  390. package/dist/utils/http.d.ts.map +1 -0
  391. package/dist/utils/http.js +29 -0
  392. package/dist/utils/http.js.map +1 -0
  393. package/dist/utils/json.d.ts +1 -1
  394. package/dist/utils/json.js +1 -1
  395. package/dist/utils/math.d.ts +25 -3
  396. package/dist/utils/math.d.ts.map +1 -1
  397. package/dist/utils/math.js +32 -16
  398. package/dist/utils/math.js.map +1 -1
  399. package/dist/utils/objects.d.ts +2 -3
  400. package/dist/utils/objects.d.ts.map +1 -1
  401. package/dist/utils/objects.js +2 -3
  402. package/dist/utils/objects.js.map +1 -1
  403. package/dist/utils/recallDefaults.d.ts +16 -0
  404. package/dist/utils/recallDefaults.d.ts.map +1 -0
  405. package/dist/utils/recallDefaults.js +16 -0
  406. package/dist/utils/recallDefaults.js.map +1 -0
  407. package/dist/utils/safeTimer.d.ts +8 -36
  408. package/dist/utils/safeTimer.d.ts.map +1 -1
  409. package/dist/utils/safeTimer.js +9 -37
  410. package/dist/utils/safeTimer.js.map +1 -1
  411. package/dist/utils/scanner.d.ts +97 -17
  412. package/dist/utils/scanner.d.ts.map +1 -1
  413. package/dist/utils/scanner.js +269 -47
  414. package/dist/utils/scanner.js.map +1 -1
  415. package/dist/utils/segmenter.d.ts +20 -19
  416. package/dist/utils/segmenter.d.ts.map +1 -1
  417. package/dist/utils/segmenter.js +230 -97
  418. package/dist/utils/segmenter.js.map +1 -1
  419. package/dist/utils/strings.d.ts +15 -32
  420. package/dist/utils/strings.d.ts.map +1 -1
  421. package/dist/utils/strings.js +25 -44
  422. package/dist/utils/strings.js.map +1 -1
  423. package/dist/utils/time.d.ts +37 -0
  424. package/dist/utils/time.d.ts.map +1 -1
  425. package/dist/utils/time.js +56 -0
  426. package/dist/utils/time.js.map +1 -1
  427. package/dist/utils/toError.d.ts +13 -1
  428. package/dist/utils/toError.d.ts.map +1 -1
  429. package/dist/utils/toError.js +18 -1
  430. package/dist/utils/toError.js.map +1 -1
  431. package/dist/web-fetch/fetchWebFetchProvider.d.ts +14 -0
  432. package/dist/web-fetch/fetchWebFetchProvider.d.ts.map +1 -0
  433. package/dist/web-fetch/fetchWebFetchProvider.js +69 -0
  434. package/dist/web-fetch/fetchWebFetchProvider.js.map +1 -0
  435. package/dist/web-fetch/types.d.ts +30 -0
  436. package/dist/web-fetch/types.d.ts.map +1 -0
  437. package/dist/web-fetch/types.js +7 -0
  438. package/dist/web-fetch/types.js.map +1 -0
  439. package/dist/web-fetch/webFetchProvider.d.ts +10 -0
  440. package/dist/web-fetch/webFetchProvider.d.ts.map +1 -0
  441. package/dist/web-fetch/webFetchProvider.js +28 -0
  442. package/dist/web-fetch/webFetchProvider.js.map +1 -0
  443. package/dist/web-search/fetchWebSearchProvider.d.ts +27 -0
  444. package/dist/web-search/fetchWebSearchProvider.d.ts.map +1 -0
  445. package/dist/web-search/fetchWebSearchProvider.js +199 -0
  446. package/dist/web-search/fetchWebSearchProvider.js.map +1 -0
  447. package/dist/web-search/types.d.ts +46 -0
  448. package/dist/web-search/types.d.ts.map +1 -0
  449. package/dist/web-search/types.js +2 -0
  450. package/dist/{persona → web-search}/types.js.map +1 -1
  451. package/dist/web-search/webSearchProvider.d.ts +10 -0
  452. package/dist/web-search/webSearchProvider.d.ts.map +1 -0
  453. package/dist/web-search/webSearchProvider.js +30 -0
  454. package/dist/web-search/webSearchProvider.js.map +1 -0
  455. package/docs/architecture/role-pack-authoring-guide.md +243 -0
  456. package/docs/architecture/role-pack-spec.md +767 -0
  457. package/docs/memora-api-reference.md +416 -476
  458. package/docs/memora-/346/216/245/345/205/245/346/214/207/345/215/227.md +841 -806
  459. package/docs/role-pack-/345/274/200/346/224/276/351/224/256/346/214/207/345/215/227.md +148 -0
  460. package/package.json +105 -100
  461. package/role-packs/README.md +41 -0
  462. package/role-packs/memora/345/212/251/346/211/213/manifest.json +12 -0
  463. package/role-packs/memora/345/212/251/346/211/213/persona.md +12 -0
  464. package/role-packs/memora/345/212/251/346/211/213/rules.md +4 -0
  465. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/manifest.json +44 -0
  466. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/persona.md +46 -0
  467. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/rules.md +15 -0
  468. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/character-sheet.md +34 -0
  469. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/core-extractor.md +37 -0
  470. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/craft-review.md +69 -0
  471. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/dialogue-craft.md +45 -0
  472. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/emotion-wave.md +46 -0
  473. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/empathy-craft.md +79 -0
  474. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/foreshadow.md +29 -0
  475. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/hotspot-mapping.md +121 -0
  476. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/idea-lever.md +42 -0
  477. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/logline-craft.md +47 -0
  478. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/longform-guard.md +64 -0
  479. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/need-finder.md +40 -0
  480. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/outline-expand.md +46 -0
  481. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/pacing-hook.md +34 -0
  482. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/polish-method.md +113 -0
  483. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/pov-discipline.md +79 -0
  484. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/project-init/SKILL.md +112 -0
  485. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/project-init/scripts/init-novel-project.mjs +441 -0
  486. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/relationship-web.md +47 -0
  487. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/scene-craft.md +46 -0
  488. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-bible.md +60 -0
  489. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-structure.md +36 -0
  490. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/style-weaver.md +109 -0
  491. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/three-layer-check.md +27 -0
  492. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/world-builder.md +41 -0
  493. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/manifest.json +48 -0
  494. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/persona.md +73 -0
  495. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/rules.md +19 -0
  496. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/skills/api-doc-writer.md +40 -0
  497. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/skills/doc-planner.md +42 -0
  498. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/skills/nutrient-search.md +28 -0
  499. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/skills/seed-finder.md +28 -0
  500. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/skills/ssot-audit.md +34 -0
  501. package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/skills/style-guide.md +40 -0
  502. package/dist/agent/guardrail.d.ts +0 -49
  503. package/dist/agent/guardrail.d.ts.map +0 -1
  504. package/dist/agent/guardrail.js +0 -59
  505. package/dist/agent/guardrail.js.map +0 -1
  506. package/dist/agent/managers/autoConfigRefiner.d.ts +0 -39
  507. package/dist/agent/managers/autoConfigRefiner.d.ts.map +0 -1
  508. package/dist/agent/managers/autoConfigRefiner.js +0 -146
  509. package/dist/agent/managers/autoConfigRefiner.js.map +0 -1
  510. package/dist/agent/managers/configManager.d.ts +0 -209
  511. package/dist/agent/managers/configManager.d.ts.map +0 -1
  512. package/dist/agent/managers/configManager.js +0 -331
  513. package/dist/agent/managers/configManager.js.map +0 -1
  514. package/dist/agent/managers/insightExtractor.d.ts +0 -139
  515. package/dist/agent/managers/insightExtractor.d.ts.map +0 -1
  516. package/dist/agent/managers/insightExtractor.js +0 -401
  517. package/dist/agent/managers/insightExtractor.js.map +0 -1
  518. package/dist/agent/managers/memoryDecayScheduler.d.ts +0 -239
  519. package/dist/agent/managers/memoryDecayScheduler.d.ts.map +0 -1
  520. package/dist/agent/managers/memoryDecayScheduler.js +0 -413
  521. package/dist/agent/managers/memoryDecayScheduler.js.map +0 -1
  522. package/dist/agent/managers/relationBuilder.d.ts +0 -131
  523. package/dist/agent/managers/relationBuilder.d.ts.map +0 -1
  524. package/dist/agent/managers/relationBuilder.js +0 -192
  525. package/dist/agent/managers/relationBuilder.js.map +0 -1
  526. package/dist/agent/personaMatcher.d.ts +0 -29
  527. package/dist/agent/personaMatcher.d.ts.map +0 -1
  528. package/dist/agent/personaMatcher.js +0 -60
  529. package/dist/agent/personaMatcher.js.map +0 -1
  530. package/dist/agent/userFactExtractor.d.ts +0 -25
  531. package/dist/agent/userFactExtractor.d.ts.map +0 -1
  532. package/dist/agent/userFactExtractor.js +0 -88
  533. package/dist/agent/userFactExtractor.js.map +0 -1
  534. package/dist/eval/evalRunner.d.ts +0 -94
  535. package/dist/eval/evalRunner.d.ts.map +0 -1
  536. package/dist/eval/evalRunner.js +0 -141
  537. package/dist/eval/evalRunner.js.map +0 -1
  538. package/dist/eval/evalTypes.d.ts +0 -148
  539. package/dist/eval/evalTypes.d.ts.map +0 -1
  540. package/dist/eval/evalTypes.js +0 -118
  541. package/dist/eval/evalTypes.js.map +0 -1
  542. package/dist/eval/scenarios.d.ts +0 -33
  543. package/dist/eval/scenarios.d.ts.map +0 -1
  544. package/dist/eval/scenarios.js +0 -109
  545. package/dist/eval/scenarios.js.map +0 -1
  546. package/dist/llm/embedding.d.ts +0 -79
  547. package/dist/llm/embedding.d.ts.map +0 -1
  548. package/dist/llm/embedding.js +0 -184
  549. package/dist/llm/embedding.js.map +0 -1
  550. package/dist/memory/hybridMerge.d.ts +0 -50
  551. package/dist/memory/hybridMerge.d.ts.map +0 -1
  552. package/dist/memory/hybridMerge.js +0 -51
  553. package/dist/memory/hybridMerge.js.map +0 -1
  554. package/dist/memory/inMemoryRelationStore.d.ts +0 -58
  555. package/dist/memory/inMemoryRelationStore.d.ts.map +0 -1
  556. package/dist/memory/inMemoryRelationStore.js +0 -80
  557. package/dist/memory/inMemoryRelationStore.js.map +0 -1
  558. package/dist/memory/loader.d.ts +0 -51
  559. package/dist/memory/loader.d.ts.map +0 -1
  560. package/dist/memory/loader.js +0 -100
  561. package/dist/memory/loader.js.map +0 -1
  562. package/dist/memory/recall.d.ts +0 -82
  563. package/dist/memory/recall.d.ts.map +0 -1
  564. package/dist/memory/recall.js +0 -157
  565. package/dist/memory/recall.js.map +0 -1
  566. package/dist/memory/relationStore.d.ts +0 -95
  567. package/dist/memory/relationStore.d.ts.map +0 -1
  568. package/dist/memory/relationStore.js +0 -2
  569. package/dist/memory/relationStore.js.map +0 -1
  570. package/dist/memory/store.d.ts +0 -71
  571. package/dist/memory/store.d.ts.map +0 -1
  572. package/dist/memory/store.js +0 -199
  573. package/dist/memory/store.js.map +0 -1
  574. package/dist/memory/userProfile.d.ts +0 -180
  575. package/dist/memory/userProfile.d.ts.map +0 -1
  576. package/dist/memory/userProfile.js +0 -381
  577. package/dist/memory/userProfile.js.map +0 -1
  578. package/dist/memory/vectorStore.d.ts +0 -179
  579. package/dist/memory/vectorStore.d.ts.map +0 -1
  580. package/dist/memory/vectorStore.js +0 -284
  581. package/dist/memory/vectorStore.js.map +0 -1
  582. package/dist/persona/personaManager.d.ts +0 -109
  583. package/dist/persona/personaManager.d.ts.map +0 -1
  584. package/dist/persona/personaManager.js +0 -315
  585. package/dist/persona/personaManager.js.map +0 -1
  586. package/dist/persona/types.d.ts +0 -32
  587. package/dist/persona/types.d.ts.map +0 -1
  588. package/dist/persona/types.js +0 -5
@@ -1,808 +1,843 @@
1
- # Memora · 接入指南 v2.0.3
2
-
3
- > 帮助宿主项目开发者快速理解 Memora 的设计理念和接入方法。
4
- >
5
- > **版本**:v2.0.3(最后更新:2026-08-01)
6
- >
7
- > **1.0.0 之前:核心能力演进**(原内部里程碑 v3.0–v3.3,于 npm 0.2.0 前后完成):
8
- > - **Agent God Object 拆分(原 v3.0)**:记忆查询、规则注入、工具注册等方法迁移到专职 Manager,通过 `agent.<manager>.xxx()` 访问。详见 [API 参考手册](./memora-api-reference.md)。
9
- > - **可观测性与护栏(原 v3.1)**:新增可观测性(ITracer)、内容护栏(Guardrails)、工具错误反思(Reflection)、评估体系(Eval)支持。
10
- > - **关系图谱与画像(原 v3.2)**:新增 ADR-014 记忆关系图谱(IMemoryRelationStore 侧车接口)、UserProfile 用户画像管理、WorkProjectionManager 作品投影、AutoConfigRefiner 自进化配置建议。
11
- > - **npm 正式包与基础工具(原 v3.3)**:内核发布 v0.2.0(npm 正式包),精灵切换至 npm alias 依赖。Phase 1-4 全部核心完成。新增 EmbeddingProvider、安全定时器(safeSetTimeout/safeSetInterval)、Frontmatter 工具、事件系统(TypedEventEmitter)、记忆关系常量(RELATION_TYPES/RELATION_WEIGHTS)、审计类型(AuditEvent 等)、评估框架(EvalScenario/collectAgentChunks/evaluateResult)。
12
- >
13
- > **v1.0.0 变更**:1.0 正式发布。P0 阻塞修复全量收敛:DEFAULT_CONFIG 由 `config/loader.ts` 常量声明单一真理源(不再维护独立 schema)、IVectorStore 接口提取(JsonVectorStore 内置实现)、AbortSignal 合并工具、Logger 懒初始化(移除模块顶层副作用)、评估框架结构化信号(guardrailBlocked 替代文案匹配)、文档全量对齐(包名 @zooique/memora)。
14
- >
15
- > **v2.0.0 变更**:版本号提升(维护性质,无破坏性 API 变更)。
16
- >
17
- > **v2.0.1 变更**:文档版本号对齐(v1.0.2 → v2.0.1)。无 API 破坏性变更,仅同步文档与版本戳。
1
+ # Memora · 接入指南 v3.0.0
2
+
3
+ > 帮助宿主项目开发者快速理解 Memora 的设计理念和接入方法。
18
4
  >
19
- > **v2.0.2 变更**:万物皆记忆 v2(双轨模型——设定记忆 + 对话记忆)。Persona/Skill 从 SQLite 索引解耦,改为纯文件 + 内存缓存。Skill 匹配改为当轮实时注入。详见 [CHANGELOG](../CHANGELOG.md)。
5
+ > **当前稳定版**:v3.0.0。各版完整变更见 [CHANGELOG](../CHANGELOG.md)。
20
6
  >
21
- > **v2.0.3 变更**:npm 发布配置修复与质量加固。新增 `publishConfig.access = "public"`、`exports` 增加 `default` 回退条件、`keywords` 扩充至 18 个。无 API 破坏性变更。
22
-
23
- ---
24
-
25
- ## 目录
26
-
27
- - [一、核心理念](#一核心理念)
28
- - [二、三重数据存续路径](#二三重数据存续路径)
29
- - [三、最小接入步骤](#三最小接入步骤)
30
- - [四、会话持久化](#四会话持久化)
31
- - [五、API 速查](#五api-速查)
32
- - [六、宿主工具函数](#六宿主工具函数)
33
- - [七、可观测性接入(ITracer)](#七可观测性接入itracer)
34
- - [八、内容护栏(Guardrails)](#八内容护栏guardrails)
35
- - [九、评估体系(Eval)](#九评估体系eval)
36
- - [十、多步骤编排](#十多步骤编排)
37
- - [十一、关键约束](#十一关键约束)
38
-
39
- ---
40
-
41
- ## 一、核心理念
42
-
43
- **Memora 是一个无法独立运行的智能大脑内核。** 它只有接口,没有"形态"——宿主负责给它身体(UI)、血管(Provider)、神经网络(事件回路)。
44
-
45
- **万物皆记忆 v2。** Memora 有两类记忆:**设定记忆**(Persona/Skill/Rule —— Agent 的骨骼,.md 文件 + 内存缓存,确定性注入不经过召回)和**对话记忆**(Conversation/Insight/UserProfile —— Agent 的血肉,SQLite + VectorStore,语义召回)。二者通过 `autoConfigRefiner` 连接——对话洞察可生长为设定文件。
46
-
47
- **单 Agent 模型。** 所有对话、所有记忆存在同一个数据库中,**切换子项目不会丢失记忆**。
48
-
49
- **配置文件是真理源,对话记忆走 SQLite 索引。** Persona/Skill 为纯文件 + 内存缓存;Rule 文件写入 SQLite 供 bootstrap 读取;对话记忆(insight/profile)走 SQLite + 语义召回。
50
-
51
- **内核零越界。** 核心库不调用 `console.*`、不读 `process.stdin`、不管理 API Key、不写用户配置文件。
52
-
53
- **Manager 委托模式(1.0.0)。** Agent 面类只做编排,领域操作委托给 9 个专职 Manager:`agent.persona` / `agent.tools` / `agent.skills` / `agent.config` / `agent.insight` / `agent.memory` / `agent.userProfile` / `agent.works` / `agent.polish`(文本润色)。
54
-
55
- ```
56
- ┌────────────────────────────────────────────────────────────┐
57
- │ 宿主程序 │
58
- │ ┌─────────────────┐ ┌──────────────────┐ │
59
- │ │ LLM Provider 实例 │◄───│ API Key / baseUrl │ ← 宿主职责 │
60
- │ └────────┬────────┘ └──────────────────┘ │
61
- │ │ 注入 │
62
- │ ▼ │
63
- │ ┌──────────────────────────────────────────┐ │
64
- │ │ Memora 内核(Agent) │ │
65
- │ │ - chat(input) → 流式响应 │ │
66
- │ │ - 9 个 Manager getter(委托模式,含文本润色) │ │
67
- │ │ ⚠️ 不包含:UI / LLM 配置 / 用户配置模板 │ │
68
- │ └──────────────────────────────────────────┘ │
69
- └────────────────────────────────────────────────────────────┘
70
- ```
71
-
72
- ---
73
-
74
- ## 二、三重数据存续路径
75
-
76
- | 路径 | 用途 | 示例 |
77
- |------|------|------|
78
- | `AgentOptions.configDir` | Agent 级配置(personas/rules/skills) | 嵌入宿主程序 |
79
- | `AgentOptions.dataDir` | 记忆数据(memora.db + sessions/) | 跟作品走 |
80
- | `projectPath/.memora/` | 项目级配置(rules/skills) | 跟作品走 |
81
-
82
- **小说生成器推荐布局:**
83
- ```
84
- 小说项目/
85
- ├── .memora/ ← 项目级配置(rules + skills)
86
- └── .memora-data/ ← 项目级记忆(dataDir 指向此处)
87
- ├── memora.db
88
- └── sessions/
89
- ```
90
-
91
- ---
92
-
93
- ## 三、最小接入步骤
94
-
95
- ### 1. 安装
96
-
97
- ```bash
98
- npm install @zooique/memora
99
- ```
100
-
101
- ### 2. 创建 Provider + Agent
102
-
103
- ```typescript
104
- import { Agent, createLlmProvider, JsonVectorStore, setLogger } from '@zooique/memora';
105
- import type { IMemoryStorage, ILogger, EmbeddingService } from '@zooique/memora';
106
-
107
- // 宿主职责:创建 LLM Provider(Agent 不关心 API Key)
108
- const provider = createLlmProvider({
109
- provider: 'openaiCompatible',
110
- apiKey: process.env.LLM_API_KEY!,
111
- baseUrl: 'https://api.deepseek.com/v1',
112
- model: 'deepseek-chat',
113
- });
114
-
115
- // 可选:后台 Provider(投影等后台操作)
116
- const backgroundProvider = createLlmProvider({
117
- provider: 'openaiCompatible',
118
- apiKey: process.env.LLM_API_KEY!,
119
- baseUrl: 'https://api.deepseek.com/v1',
120
- model: 'deepseek-chat', // 可用更便宜的模型
121
- });
122
-
123
- // 可选:注入存储层(不传则使用 InMemoryStorage)
124
- const storage: IMemoryStorage = new MySqliteStorage('/path/to/memora.db');
125
-
126
- // 可选:全局替换日志实现(不调用则使用内置 console logger;pino 为可选 peer 依赖,动态 import 懒加载)
127
- setLogger(myCustomLogger);
128
-
129
- // 可选:向量存储(提供后启用语义搜索召回)
130
- const embeddingService: EmbeddingService = myEmbeddingService;
131
- const vectorStore = new JsonVectorStore('/path/to/vectors.json', embeddingService);
132
-
133
- // 创建 Agent
134
- const agent = new Agent({
135
- projectPath: '/path/to/novel-project',
136
- provider, // 前台 Provider(必须)
137
- backgroundProvider, // 后台 Provider(可选)
138
- configDir: '/path/to/agent-config',
139
- dataDir: '.memora',
140
- maxContextTokens: 120000,
141
- persona: '作家',
142
- permission: 'owner',
143
- allowedPaths: ['.'],
144
- confirmWrites: false,
145
- storage, // 存储层注入(可选)
146
- vectorStore, // 向量存储(可选,启用语义搜索)
147
- tracer: myOtelTracer, // 可观测性 Tracer(可选,不传则静默丢弃所有 span)
148
- });
149
-
150
- await agent.init();
151
- ```
152
-
153
- ### 3. 对话
154
-
155
- ```typescript
156
- // 流式对话(生产推荐)
157
- for await (const chunk of agent.chat('帮我写一段玄幻小说开头')) {
158
- switch (chunk.type) {
159
- case 'text': ui.appendText(chunk.content); break;
160
- case 'thinking': ui.showThinking(chunk.phase); break;
161
- case 'tool_start': ui.showToolStart(chunk.name); break;
162
- case 'tool_result':ui.showToolResult(chunk.ok); break;
163
- case 'aborted': ui.showCancelled(chunk.reason); break;
164
- }
165
- }
166
-
167
- // 同步对话(测试用)
168
- const reply = await agent.chatSync('你好');
169
- ```
170
-
171
- ### 3.5 事件订阅(可选)
172
-
173
- Agent 向宿主广播对话外事件(记忆变更、角色切换、衰减完成、会话分叉等)。
174
-
175
- ```typescript
176
- import type { AgentEventMap } from '@zooique/memora';
177
-
178
- agent.on('memoryAdded', (e) => console.log(`新记忆: ${e.source}:${e.name}`));
179
- agent.on('personaSwitched', (e) => console.log(`角色: ${e.from} → ${e.to}`));
180
- agent.on('decayCompleted', (e) => console.log(`衰减 ${e.decayedCount} 条记忆`));
181
- agent.on('memoryRecalled', (e) => console.log(`想起 ${e.count} 条记忆`));
182
- agent.on('sessionForked', (e) => console.log(`分叉: ${e.from} → ${e.to},${e.messageCount} 条消息`));
183
-
184
- // close() 自动移除所有监听器
185
- ```
186
-
187
- ### 4. 注册领域工具
188
-
189
- > 1.0.0:工具注册走 `agent.tools.xxx()`。
190
-
191
- ```typescript
192
- agent.tools.registerTool(
193
- {
194
- name: 'create_chapter',
195
- description: '创建一个新章节',
196
- parameters: {
197
- type: 'object',
198
- properties: { title: { type: 'string', description: '章节标题' } },
199
- required: ['title'],
200
- },
201
- },
202
- async (args) => {
203
- const chapter = await myNovel.createChapter(args.title);
204
- return `章节 "${args.title}" 已创建`;
205
- },
206
- );
207
- ```
208
-
209
- ### 5. 注入写入扩展 + 记忆关键词
210
-
211
- > 1.0.0:写入扩展和关键词走 `agent.insight.xxx()`。
212
-
213
- ```typescript
214
- // 写入前 diff 确认回调
215
- agent.insight.setWriteExtensions({
216
- onBeforeWrite: async (path, before, after) => {
217
- showDiff(path, before, after);
218
- return await userConfirm(`确认写入 ${path}?`);
219
- },
220
- });
221
-
222
- // 记忆关键词(领域 + 用户专属)
223
- agent.insight.setKeywords({
224
- domain: ['主角', '角色', '情节', '设定'],
225
- personal: ['我', '我的', '记住', '帮我'],
226
- });
227
- ```
228
-
229
- ### 6. 注入项目规则
230
-
231
- > 1.0.0:规则注入走 `agent.config.xxx()`。
232
-
233
- ```typescript
234
- await agent.config.addSimpleRule(
235
- '世界观规则',
236
- '这是一个东方玄幻世界,修真等级分为炼气、筑基、金丹……',
237
- );
238
- ```
239
-
240
- ### 7. 查询记忆
241
-
242
- > 1.0.0:记忆查询走 `agent.memory.xxx()`。
243
-
244
- ```typescript
245
- // 3 层记忆快照
246
- const snap = agent.memory.snapshot();
247
- console.log('工作记忆:', snap.working.total, '条');
248
- console.log('引导记忆:', snap.bootstrap.total, '条');
249
-
250
- // 关键词搜索
251
- const hits = agent.memory.search('世界观');
252
- hits.forEach(h => console.log(`${h.source}:${h.name} (${h.score})`));
253
-
254
- // 记忆库统计
255
- const stats = agent.memory.stats();
256
- console.log('记忆总数:', stats.total);
257
- ```
258
-
259
- ### 8. 管理角色
260
-
261
- > 1.0.0:角色管理走 `agent.persona.xxx`。
262
-
263
- ```typescript
264
- // 列出所有角色
265
- const names = agent.persona.list.map(p => p.name);
266
-
267
- // 手动切换(返回新角色的 system prompt,可用于 UI 展示)
268
- const personaPrompt = agent.persona.switchPersona('作家');
269
-
270
- // 锁定手动模式(禁止自动匹配)
271
- agent.persona.setMode('manual');
272
-
273
- // 查询当前状态
274
- console.log(agent.persona.activeName); // '作家'
275
- console.log(agent.persona.currentMode); // 'manual'
276
- ```
277
-
278
- ### 9. 切换项目
279
-
280
- ```typescript
281
- // switchProject() 自动 rebuild,无需手动调用 rebuildComponents()
282
- const ctx = await agent.switchProject('another-novel');
283
- console.log(`已切换到:${ctx.projectName}`);
284
- ```
285
-
286
- ### 10. 关闭
287
-
288
- ```typescript
289
- await agent.close(); // 释放项目锁 + 关闭数据库
290
- ```
291
-
292
- ---
293
-
294
- ## 四、会话持久化(ISessionStore)
295
-
296
- Memora 通过 `ISessionStore` 接口支持会话消息的持久化。宿主实现此接口,注入到 Agent。
297
-
298
- ```typescript
299
- import type { ISessionStore, SessionMessage } from '@zooique/memora';
300
-
301
- // 宿主实现接口
302
- class FileSessionStore implements ISessionStore {
303
- appendMessage(date: string, session: string, message: SessionMessage): void {
304
- // 写入文件或数据库
305
- }
306
- loadMessages(date: string, session: string): SessionMessage[] {
307
- // 从文件或数据库读取
308
- return [];
309
- }
310
- listSessions(): string[] {
311
- // 返回所有会话列表
312
- return [];
313
- }
314
- }
315
-
316
- // 注入到 Agent
317
- const agent = new Agent({
318
- // ...其他配置
319
- sessionStore: new FileSessionStore('./sessions'),
320
- });
321
- ```
322
-
323
- **会话持久化特性:**
324
- - 日期使用 `todayDate()` 动态获取,确保跨日后消息写入当天目录
325
- - `appendUser()` / `appendAssistant()` 自动调用 `appendMessage()`
326
- - `loadSessionMessages()` 自动调用 `loadMessages()`
327
- - `SessionMessage.role` 支持 `'user' | 'assistant' | 'system'`,宿主直接调用 `appendMessage()` 时可传入任意角色
328
-
329
- ---
330
-
331
- ## 四.5、会话分叉(Fork Session)
332
-
333
- Memora 支持会话分叉功能,允许用户从当前对话创建独立分支,继承完整消息历史后各自独立发展。
334
-
335
- **使用场景**:
336
- - 用户在对话中建立了丰富的上下文后,可以分叉到多个并行任务
337
- - 每个分支继承已建立的共识,避免从零开始
338
- - 分支完全独立,互不干扰
339
-
340
- ```typescript
341
- // 自动生成分支名(main-b1, main-b2, ...)
342
- const result = agent.forkSession();
343
- console.log(`已分叉到: ${result.newSession},复制了 ${result.messageCount} 条消息`);
344
-
345
- // 自定义分支名
346
- const result2 = agent.forkSession('experiment');
347
- ```
348
-
349
- **命名规则**:
350
- - 自动生成:`{原始会话名}-b{序号}`(如 `main-b1`、`main-b2`)
351
- - 支持分叉的分叉:`main-b1-b1`
352
- - 自定义名称:直接传入目标名称
353
-
354
- **记忆处理策略**:
355
- - 已有记忆:全局共享(记忆是全局知识库,不属于单个会话)
356
- - 分叉后的 Insight:各自独立(不同分支探索不同方向)
357
- - 用户画像:全局共享(UserProfile 是全局的)
358
-
359
- **事件监听**:
360
- ```typescript
361
- agent.on('sessionForked', (event) => {
362
- console.log(`从 ${event.from} 分叉到 ${event.to}`);
363
- console.log(`复制了 ${event.messageCount} 条消息`);
364
- });
365
- ```
366
-
367
- **边界情况**:
368
- - 当前会话无消息时抛出错误
369
- - `ISessionStore` 未注入时抛出错误
370
- - 对话正在进行中(`chatBusy`)时抛出错误
371
- - 自定义名称已存在时抛出错误
372
-
373
- ---
374
-
375
- ## 五、API 速查
376
-
377
- > 完整定义见 [memora-api-reference.md](./memora-api-reference.md)。
378
-
379
- ### 构造选项 `AgentOptions`
380
-
381
- | 选项 | 类型 | 必须 | 说明 |
382
- |------|------|------|------|
383
- | `projectPath` | `string` | ✅ | 项目路径 |
384
- | `provider` | `LlmProvider` | ✅ | 前台 LLM Provider |
385
- | `backgroundProvider` | `LlmProvider` | ❌ | 后台 Provider(投影) |
386
- | `configDir` | `string` | ❌ | 配置目录(personas/rules/skills) |
387
- | `dataDir` | `string` | ❌ | 记忆数据目录(默认 ~/.memora) |
388
- | `registryDir` | `string` | ❌ | 项目注册表目录(默认与 dataDir 相同) |
389
- | `maxContextTokens` | `number` | ❌ | 上下文窗口上限(默认 120000) |
390
- | `persona` | `string` | ❌ | 默认角色名 |
391
- | `permission` | `'owner' \| 'guest'` | ❌ | 安全权限(默认 'owner') |
392
- | `allowedPaths` | `string[]` | ❌ | 路径白名单(默认 [] = 全部允许) |
393
- | `confirmWrites` | `boolean` | ❌ | 写入确认(默认 false) |
394
- | `storage` | `IMemoryStorage` | ❌ | 存储层注入 |
395
- | `relationStore` | `IMemoryRelationStore` | ❌ | 记忆关系存储(ADR-014 侧车,不传则跳过关系构建) |
396
- | `vectorStore` | `IVectorStore` | ❌ | 向量存储接口(提供时启用语义搜索;内置实现 `JsonVectorStore`) |
397
- | `recallExcludeSources` | `string[]` | ❌ | 召回时排除的 source 标签(默认 `['persona', 'rule', 'skill']`) |
398
- | `sessionStore` | `ISessionStore` | ❌ | 会话存储注入 |
399
- | `tracer` | `ITracer` | ❌ | 可观测性 Tracer 注入(不传则使用 NoopTracer 静默丢弃所有 span) |
400
- | `messages` | `UIMessages` | ❌ | 宿主可覆盖的 UI 消息文本(默认英文,宿主覆盖为中文等) |
401
- | `enableContextSummary` | `boolean` | ❌ | 上下文超限时是否自动生成摘要(默认 true,开启后首次截断时增加 ~1-2s 延迟) |
402
-
403
- ### Agent 生命周期与状态
404
-
405
- | 成员 | 说明 |
406
- |------|------|
407
- | `agent.init()` | 初始化(创建存储、加载配置、组装组件),返回 `AgentContext` |
408
- | `agent.close()` | 安全关闭(释放锁 + 关闭数据库) |
409
- | `agent.initialized` | 只读 getter,`boolean` |
410
- | `agent.isBusy` | 只读 getter,`boolean`(是否正在对话中) |
411
- | `agent.lastInteractionAt` | 只读 getter,`Date \| null`,最近一次对话时间 |
412
- | `agent.context` | 只读 getter,`AgentContext \| null`,当前项目上下文 |
413
-
414
- ### 对话
415
-
416
- | 方法 | 说明 |
417
- |------|------|
418
- | `agent.chat(input, signal?)` | 流式对话,返回 `AsyncGenerator<AgentChunk>` |
419
- | `agent.chatSync(input, signal?)` | 同步对话(测试用) |
420
-
421
- ### 项目 / 会话
422
-
423
- | 方法 | 说明 |
424
- |------|------|
425
- | `agent.projects.listProjects()` / `agent.projects.list` | 列出已注册的子项目 |
426
- | `agent.switchProject(name)` | 切换到其他子项目 |
427
- | `agent.rebuildComponents()` | 通常不需要手动调用(`switchProject` 已自动执行),仅在强制刷新配置时使用 |
428
- | `agent.switchSession(name)` | 切换当前会话(同步,返回新会话名) |
429
- | `agent.forkSession(name?)` | 分叉当前会话(复制完整消息历史到新分支) |
430
- | `agent.loadSessionMessages(date, session)` | 加载指定日期/会话的消息 |
431
- | `agent.restoreSession(date, session)` | 恢复指定日期/会话 |
432
- | `agent.restoreMostRecentSession()` | 启动时恢复最近一次会话 |
433
- | `agent.agentHistory.listAllSessions()` | 列出所有会话文件名 |
434
-
435
- ### Manager 速查表
436
-
437
- | 路径 | Manager | 主要成员 |
438
- |------|---------|---------|
439
- | `agent.memory.xxx()` | MemoryInspector | 读:`snapshot()` / `search(q, n)` / `searchHybrid(q, n)` / `stats()` / `list()` / `getById(id)` / `listDeleted()` / 关系查询;写:`writeUpsert()` / `writeDelete()` / `writeRestore()` / `writePurge()` / `writeAddRelation()` 等(`writeXxx` 前缀)。`suggest()` / `sourceHealth()` 已上移至 `agent.suggest()` / `agent.sourceHealth()` |
440
- | `agent.config.xxx()` | ConfigManager | `addRule(m)` / `addSimpleRule(n, c)` / `addSkill(m)` / `addSimpleSkill(n, c, k?)` / `onSuggestion(h)` / `confirm(s)` |
441
- | `agent.tools.xxx()` | ToolExecutor | `registerTool(d, h)` / `getToolDefinitions()` / `execute(n, a)` / `list` |
442
- | `agent.insight.xxx()` | InsightExtractor | `setWriteExtensions(e)` / `setKeywords(k)` / `classify(i)` |
443
- | `agent.persona.xxx` | PersonaManager | `.list` / `.activeName` / `.currentMode` / `.switchPersona(n)` / `.setMode(m)` |
444
- | `agent.skills.xxx` | SkillManager | `.list` / `.match(i)` / `.register(skill)` / `.buildSystemPrompt()` |
445
- | `agent.userProfile.xxx()` | UserProfile | `load()` / `archiveFacts(f)` / `getConfirmed()` / `getPending()` / `buildSystemPrompt()` / `confirm(id)` / `reject(id)` |
446
- | `agent.works.xxx()` | WorkProjectionManager | `ensureProjection(path, content)` / `getProjection(path)` / `loadAll()` |
447
- | `agent.on()` / `agent.off()` / `agent.once()` | TypedEventEmitter | `memoryAdded` / `personaSwitched` / `decayCompleted` / `memoryRecalled` / `sessionForked` / `insightExtracted` / `conflictDetected` / `projectSwitched` / `skillMatched` / `archiveFailed` |
448
-
449
- ### Provider 管理
450
-
451
- | 方法 | 说明 |
452
- |------|------|
453
- | `agent.setProvider(provider)` | 运行时切换前台 Provider |
454
- | `agent.setBackgroundProvider(provider)` | 运行时切换后台 Provider |
455
-
456
- ---
457
-
458
- ## 六、宿主工具函数
459
-
460
- ```typescript
461
- import {
462
- createLlmProvider,
463
- createProviderFromConfig,
464
- loadConfig,
465
- InMemoryStorage,
466
- InMemoryRelationStore,
467
- JsonVectorStore,
468
- EmbeddingProvider,
469
- setLogger,
470
- logger,
471
- segmentText,
472
- parseFrontmatter,
473
- serializeFrontmatter,
474
- safeSetTimeout,
475
- safeSetInterval,
476
- clearSafeTimeout,
477
- clearSafeInterval,
478
- recall,
479
- extractKeywords,
480
- SOURCE_LABELS,
481
- RELATION_TYPES,
482
- RELATION_WEIGHTS,
483
- inferSource,
484
- escapeLike,
485
- validateSource,
486
- MemoraError,
487
- toError,
488
- NOOP_TRACER,
489
- TRACE_SPANS,
490
- ToolErrorCode,
491
- isRetryableErrorCode,
492
- TypedEventEmitter,
493
- collectAgentChunks,
494
- evaluateResult,
495
- } from '@zooique/memora';
496
- import type {
497
- ProviderConfig,
498
- Config,
499
- IMemoryStorage,
500
- IMemoryRelationStore,
501
- MemoryRelation,
502
- RelationDirection,
503
- ILogger,
504
- ISessionStore,
505
- SessionMessage,
506
- Memory,
507
- SourceValidationSeverity,
508
- LlmProvider,
509
- LlmChunk,
510
- ChatOptions,
511
- AgentChunk,
512
- ThinkingPhase,
513
- UIMessages,
514
- ArchiveMode,
515
- AgentOptions,
516
- AgentContext,
517
- AgentProjectEntry,
518
- AgentForkResult,
519
- ToolDefinition,
520
- ToolHandler,
521
- ToolContext,
522
- WriteExtensions,
523
- MemoryKeywords,
524
- MemorySnapshot,
525
- WorkingMemorySnapshot,
526
- BootstrapSnapshot,
527
- ArchiveSnapshot,
528
- AgentSearchHit,
529
- AgentStats,
530
- SuggestOptions,
531
- SuggestHit,
532
- SourceHealthStatus,
533
- SourceHealthEntry,
534
- SourceHealthReport,
535
- ConfigSuggestion,
536
- ConfigSuggestionHandler,
537
- AutoConfigRefinerOptions,
538
- SessionArchiveResult,
539
- WorkProjectionEntry,
540
- UserProfileEntry,
541
- ProfileCategory,
542
- ExtractedFact,
543
- PersonaMode,
544
- Persona,
545
- SkillEntry,
546
- SkillMatch,
547
- RecallOptions,
548
- EmbeddingService,
549
- EmbeddingConfig,
550
- EmbeddingResult,
551
- AgentEventMap,
552
- AgentEventName,
553
- AgentEventHandler,
554
- ForkResult,
555
- ITracer,
556
- ISpan,
557
- AgentMetrics,
558
- ToolErrorCodeValue,
559
- EvalScenario,
560
- EvalExpectation,
561
- EvalResult,
562
- AuditEvent,
563
- AuditListener,
564
- Permission,
565
- WriteDecision,
566
- WriteConfirmationInfo,
567
- WriteConfirmationRequest,
568
- OpenAICompatibleProvider,
569
- OpenAICompatibleConfig,
570
- } from '@zooique/memora';
571
- ```
572
-
573
- | 函数/类型 | 用途 |
574
- |-----------|------|
575
- | `createLlmProvider(config)` | 从扁平配置创建 LlmProvider 实例 |
576
- | `createProviderFromConfig(name, config)` | 从命名配置创建 LlmProvider 实例 |
577
- | `loadConfig(path?)` | 加载 memora.json 配置文件 |
578
- | `InMemoryStorage` | IMemoryStorage 的纯内存实现(测试用) |
579
- | `InMemoryRelationStore` | IMemoryRelationStore 的纯内存实现(测试用) |
580
- | `JsonVectorStore` | 向量存储内置实现(实现 IVectorStore 接口,宿主注入 EmbeddingService 后创建,启用语义搜索) |
581
- | `EmbeddingProvider` | OpenAI 兼容 Embedding 端点实现(满足 EmbeddingService 接口) |
582
- | `setLogger(logger)` | 替换全局日志实现 |
583
- | `logger` | 全局日志实例 |
584
- | `segmentText(text)` | 中文分词工具 |
585
- | `parseFrontmatter(text)` | 解析 Markdown frontmatter |
586
- | `serializeFrontmatter(fm, body)` | 序列化 frontmatter + body 为 Markdown |
587
- | `safeSetTimeout(fn, ms)` | 安全定时器(可跟踪清理) |
588
- | `safeSetInterval(fn, ms)` | 安全间隔器(可跟踪清理) |
589
- | `clearSafeTimeout(id)` | 清除安全定时器 |
590
- | `clearSafeInterval(id)` | 清除安全间隔器 |
591
- | `recall(storage, query, options?)` | 记忆召回(async,双通道:语义 + 关键词) |
592
- | `extractKeywords(text)` | 提取关键词 |
593
- | `SOURCE_LABELS` | source 标签常量(PERSONA / RULE / SKILL / INSIGHT / PROFILE / WORK_PROJECTION / GUARDRAIL) |
594
- | `RELATION_TYPES` | 记忆关系类型常量(CONTRADICTS / SUPPORTS / FOLLOWS / REFINES / CAUSED / RELATED) |
595
- | `RELATION_WEIGHTS` | 关系强度常量(NONE / WEAK / UNDEFINED / STRONG / CERTAIN) |
596
- | `inferSource(content)` | 从内容推断 source 标签 |
597
- | `escapeLike(query)` | 转义 SQLite LIKE 通配符 |
598
- | `validateSource(source)` | 校验 source 标签是否为已知标签(返回 warning,不阻止写入) |
599
- | `MemoraError` | 统一错误类型(结构化错误码 + 上下文) |
600
- | `toError(err)` | 将任意值转为 Error(浏览器端安全,不引入 pino) |
601
- | `NOOP_TRACER` | ITracer 的空实现(静默丢弃所有 span,零开销) |
602
- | `TRACE_SPANS` | AgentLoop 预定义 Span 名称常量(RECALL / LLM_CALL / TOOL_EXEC / RESPONSE) |
603
- | `ToolErrorCode` | 工具错误码枚举(10 种,含 PATH_NOT_ALLOWED / FILE_NOT_FOUND 等) |
604
- | `isRetryableErrorCode(code)` | 判断错误码是否可重试(5 种 retryable) |
605
- | `TypedEventEmitter` | 类型安全的事件发射器(Agent 继承此类) |
606
- | `collectAgentChunks(gen)` | 从 AgentChunk 流收集行为数据(Mock Eval 用) |
607
- | `evaluateResult(name, collected, expected)` | 比对期望与结果(Mock Eval 用) |
608
-
609
- **注意**:`SqliteStorage` 已移出到宿主项目,不再从 memora 导出。宿主需自行实现 `IMemoryStorage` 接口。
610
-
611
- `IMemoryStorage` 接口要求实现 `count()` 和 `countBySource(source)` 方法,用于高效统计记忆数量(避免全量加载数据)。详见 [API 参考手册](./memora-api-reference.md)。
612
-
613
- ---
614
-
615
- ## 七、可观测性接入(ITracer)
616
-
617
- Memora 内置了轻量的 Span/Trace 抽象,宿主可注入 OpenTelemetry 等实现来观测 AgentLoop 行为。
618
-
619
- ### 接口定义
620
-
621
- ```typescript
622
- interface ITracer {
623
- startSpan(name: string, attributes?: Record<string, string | number | boolean>): ISpan;
624
- }
625
-
626
- interface ISpan {
627
- setAttribute(key: string, value: string | number | boolean): void;
628
- end(): void;
629
- recordException(error: Error): void;
630
- }
631
- ```
632
-
633
- ### AgentLoop 预定义 Span
634
-
635
- | Span 名称 | 常量 | 触发时机 | 关键属性 |
636
- |-----------|------|---------|---------|
637
- | `recall.recall` | `TRACE_SPANS.RECALL` | 记忆召回阶段 | `recallCount` |
638
- | `llm.call` | `TRACE_SPANS.LLM_CALL` | LLM API 调用 | `model`, `messageCount`, `iteration` |
639
- | `tool.execute` | `TRACE_SPANS.TOOL_EXEC` | 工具执行 | `toolName` |
640
- | `response.generate` | `TRACE_SPANS.RESPONSE` | 整轮响应 | `inputLength` |
641
-
642
- ### 宿主接入示例(OpenTelemetry)
643
-
644
- ```typescript
645
- import { trace } from '@opentelemetry/api';
646
- import type { ITracer, ISpan } from '@zooique/memora';
647
-
648
- // 宿主实现 ITracer 接口(桥接 OpenTelemetry)
649
- class OtelTracer implements ITracer {
650
- private tracer = trace.getTracer('memora-agent');
651
-
652
- startSpan(name: string, attributes?: Record<string, string | number | boolean>): ISpan {
653
- const otelSpan = this.tracer.startSpan(name, { attributes });
654
- return {
655
- setAttribute: (key, value) => otelSpan.setAttribute(key, value),
656
- end: () => otelSpan.end(),
657
- recordException: (error) => otelSpan.recordException(error),
658
- };
659
- }
660
- }
661
-
662
- // 注入到 Agent
663
- const agent = new Agent({
664
- // ...其他配置
665
- tracer: new OtelTracer(),
666
- });
667
- ```
668
-
669
- > **不注入时**:默认使用 `NOOP_TRACER`(静默丢弃所有 span,零运行时开销)。
670
-
671
- ---
672
-
673
- ## 八、内容护栏(Guardrails)
674
-
675
- Memora 支持基于正则的内容护栏,在对话输入和 LLM 输出阶段分别检查,防止恶意输入和敏感信息泄露。
676
-
677
- ### 护栏规则格式
678
-
679
- 护栏规则以 `source: "guardrail"` 记忆形式存储,放在 `configDir/rules/guardrails/` 目录下:
680
-
681
- ```markdown
682
- ---
683
- name: 禁止执行代码
684
- source: guardrail
685
- ---
686
-
687
- pattern: /执行|运行|eval|exec/
688
- action: block
689
- ```
690
-
691
- - `pattern`:正则表达式(可选加 `/` 定界符)
692
- - `action`:`block`(阻断对话)或 `warn`(仅警告)
693
-
694
- ### 护栏行为
695
-
696
- | 阶段 | 命中 block | 命中 warn | 规则异常 |
697
- |------|-----------|----------|---------|
698
- | 输入 | 阻断对话,返回阻止消息 | 追加警告文本,继续对话 | 降级放行 + 记日志 |
699
- | 输出 | 阻断输出,返回阻止消息 | 追加警告文本,继续输出 | 降级放行 + 记日志 |
700
-
701
- > **降级优先**:护栏自身异常(如正则编译失败)永远不阻断用户对话。
702
-
703
- ### 工具错误反思(Reflection)
704
-
705
- 当工具执行失败时,错误结果包含 `[ERR:TOOL:code]` 前缀。如果错误码标记为 retryable(如 `FILE_NOT_FOUND`、`ARGUMENT_ERROR`),AgentLoop 会自动注入 `[REFLECTION_HINT]` 系统消息,引导 LLM 修正参数后重试。
706
-
707
- ```typescript
708
- import { ToolErrorCode, isRetryableErrorCode } from '@zooique/memora';
709
-
710
- // 判断错误码是否可重试
711
- isRetryableErrorCode(ToolErrorCode.FILE_NOT_FOUND); // true
712
- isRetryableErrorCode(ToolErrorCode.PATH_NOT_ALLOWED); // false
713
- ```
714
-
715
- Reflection 默认最多重试 2 次(`maxReflectionRetries`),防止无限循环。
716
-
717
- ---
718
-
719
- ## 九、评估体系(Eval)
720
-
721
- Memora 提供了 Mock Eval 框架,用于 Agent 行为回归测试(不发起真实 LLM 调用)。
722
-
723
- ### 类型定义
724
-
725
- ```typescript
726
- import type { EvalScenario, EvalExpectation, EvalResult } from '@zooique/memora';
727
- // Eval 类型已从 memora 主包导出,宿主项目可直接 import
728
- ```
729
-
730
- ### 评估场景示例
731
-
732
- ```typescript
733
- const scenario: EvalScenario = {
734
- name: '只读查询不应调用 write_file',
735
- description: '用户只查询信息时,Agent 不应写入文件',
736
- input: '帮我看看第一章写了什么',
737
- expect: {
738
- toolsCalled: ['read_file'], // 期望调用 read_file
739
- toolsNotCalled: ['write_file'], // 不应调用 write_file
740
- },
741
- };
742
- ```
743
-
744
- ### 评估工具函数
745
-
746
- ```typescript
747
- import { collectAgentChunks, evaluateResult } from '@zooique/memora';
748
-
749
- // 从 AgentChunk 流中收集行为数据
750
- const collected = await collectAgentChunks(agent.chat('帮我看看第一章'));
751
-
752
- // 比对期望
753
- const result = evaluateResult(scenario.name, collected, scenario.expect);
754
- console.log(result.passed ? '✅ 通过' : `❌ 失败: ${result.failures.join('; ')}`);
755
- ```
756
-
757
- ### 两种评估层次
758
-
759
- | 层次 | 工具 | LLM 调用 | 用途 |
760
- |------|------|---------|------|
761
- | Mock Eval | `collectAgentChunks` + `evaluateResult` | 不调用(MSW Mock) | 行为回归(工具调用、护栏、召回) |
762
- | 真实 LLM Eval | 宿主自建 | 真实调用 | 回复质量、指令遵循度 |
763
-
764
- > Mock Eval 在 CI 中运行,真实 LLM Eval 由宿主项目手动触发。
765
-
766
- ---
767
-
768
- ## 十、多步骤编排
769
-
770
- Memora 是单 Agent 模型(ADR-011),不内置多 Agent 编排。宿主项目可通过多次调用 `agent.chat()` 实现复杂工作流:
771
-
772
- ```typescript
773
- // 示例:分析项目 → 生成大纲 → 逐章写作
774
- const analysis = await agent.chatSync('分析这个项目的风格和主题');
775
- const outline = await agent.chatSync('基于分析结果,生成章节大纲');
776
- for (const chapter of chapters) {
777
- const content = await agent.chatSync(`根据大纲,写第${chapter.num}章:${chapter.title}`);
778
- // 写入文件...
779
- }
780
- ```
781
-
782
- **编排要点**:
783
- - 每次 `chat()` 共享同一个 AgentLoop 上下文(记忆、角色、技能)
784
- - 宿主负责流程控制(条件分支、并行、错误处理)
785
- - 如需隔离上下文,使用 `agent.switchSession()` 或 `agent.forkSession()`
786
-
787
- ---
788
-
789
- ## 十一、关键约束
790
-
791
- 1. **`provider` 是必填项** — Agent 无法独立运行
792
- 2. **configDir** 指向 Agent 级配置目录,所有子项目共享
793
- 3. **项目级 `.memora/`** 只放 `rules/`、`skills/` 和 `guardrails/`
794
- 4. **角色由关键词自动触发**
795
- 5. **对话历史跨子项目持久化**
796
- 6. **Manager 访问器在 `init()` 前返回 `null`** — 所有 `agent.config.xxx()` / `agent.memory.xxx()` 等调用必须在 `init()` 之后
797
- 7. **作品原始内容不进 SQLite**,Agent 通过工具按需读取
798
- 8. **配置文件是真理源**,`agent-config/` 下的配置由 MemoryLoader 启动时扫描加载
799
- 9. **禁止**为每个子项目创建独立的 memora.db
800
- 10. **禁止**项目切换时关闭/重建数据库
801
- 11. **禁止**将配置直接写入 SQLite 作为持久化存储
802
- 12. **项目切换** `switchProject()` 已自动 rebuild,通常无需手动调用 `rebuildComponents()`
803
- 13. **护栏规则**以 `source: "guardrail"` 记忆形式存储,不创建独立子系统
804
- 14. **Tracer 未注入时**自动降级为 NoopTracer,零运行时开销
805
-
806
- ---
807
-
808
- > 更多细节参见 [memora-api-reference.md](./memora-api-reference.md)(完整 API 参考)
7
+ > **对接入者有影响的迁移结论**(不含历史流水,细节见 [API 参考手册](./memora-api-reference.md)):
8
+ > - 1.0 为正式发布,API 已稳定;2.0 起设定记忆(persona/rules/skills)解耦为纯文件 + 内存缓存,唯一归角色包、不进记忆库(ADR-025),角色管理统一走 `agent.rolePack`。
9
+ > - 已移除:`SqliteStorage`(移出内核,由宿主实现 `IMemoryStorage`)、洞察层(`insight.*`)、AutoConfigRefiner / ConfigManager、记忆关系图谱与用户画像层(偏好收敛为 round-summary,冲突改用 `supersededBy` 布尔标记)。
10
+ >
11
+ ---
12
+
13
+ ## 目录
14
+
15
+ - [一、核心理念](#一核心理念)
16
+ - [二、三重数据存续路径](#二三重数据存续路径)
17
+ - [三、最小接入步骤](#三最小接入步骤)
18
+ - [四、会话持久化](#四会话持久化)
19
+ - [五、API 速查](#五api-速查)
20
+ - [六、宿主工具函数](#六宿主工具函数)
21
+ - [七、可观测性接入(ITracer)](#七可观测性接入itracer)
22
+ - [八、工具错误反思(Reflection)](#八工具错误反思reflection)
23
+ - [九、多步骤编排](#九多步骤编排)
24
+ - [十、关键约束](#十关键约束)
25
+ - [十一、用户档案实现指南(宿主扩展)](#十一用户档案实现指南宿主扩展)
26
+
27
+ ---
28
+
29
+ ## 一、核心理念
30
+
31
+ **Memora 是一个无法独立运行的智能大脑内核。** 它只有接口,没有"形态"——宿主负责给它身体(UI)、血管(Provider)、神经网络(事件回路)。
32
+
33
+ **摘要即记忆(memory-as-summary)。** Memora 的记忆只有单一轨道——**对话记忆**(round-summary 摘要,宿主持久化 + **纯关键词召回**,带 `summaryType` 语义标签);**设定**(角色包 persona/rules/skills)是 Agent 的骨骼,.md 文件 + 内存缓存,唯一归角色包、确定性注入不经过召回,不属记忆库(ADR-025)。二者边界一刀切:记忆系统只承载对话摘要、角色包承载设定;偏好类信息沉淀为摘要记忆,不设独立"用户画像"记忆层。
34
+
35
+ **单 Agent 模型。** 一个 Agent 实例的对话与记忆由**同一组存储实例**承载(`storage` / `sessionStore` 均在构造时注入、实例级),**`switchProject()` 切换项目时复用同一实例,不重建、不丢记忆**。要按工作区彼此隔离,就各自 `new Agent`——隔离粒度由宿主决定,内核不规定。
36
+
37
+ **配置文件是真理源,对话记忆由宿主持久化。** 角色包(persona/rules/skills)为纯文件 + 内存缓存,设定记忆唯一归角色包、不进记忆库(ADR-025);对话记忆(round-summary 摘要记忆,带 `summaryType` 标签)经宿主注入的 `IMemoryStorage` 持久化——**后端由宿主自选**(SQLite / JSON 文件 / 内存皆可),检索为 **纯关键词召回**(向量语义通道已随 2026-09-18 B0 收编移除,见 [API 参考手册](./memora-api-reference.md) §〇.5 构造参数表)。
38
+
39
+ **内核零越界。** 核心库模块不直接调用 `console.*`(唯一日志出口为 `logging/` 单例,默认实现写 stderr)、不读 `process.stdin`、不管理 API Key、不写用户配置文件。
40
+
41
+ **Manager 委托模式。** Agent 面类只做编排,领域操作委托给专职 Manager:`agent.rolePack`(角色包)/ `agent.tools`(工具)/ `agent.skills`(技能)/ `agent.memory`(记忆查询+写入)/ `agent.governance`(记忆治理)/ `agent.works`(作品投影)/ `agent.polish`(文本润色)共 7 个业务 Manager(另有 `projects` / `security` / `sessionManager` 等)。
42
+
43
+ ```
44
+ ┌────────────────────────────────────────────────────────────┐
45
+ │ 宿主程序 │
46
+ │ ┌─────────────────┐ ┌──────────────────┐ │
47
+ │ │ LLM Provider 实例 │◄───│ API Key / baseUrl │ ← 宿主职责 │
48
+ │ └────────┬────────┘ └──────────────────┘ │
49
+ │ │ 注入 │
50
+ │ ▼ │
51
+ │ ┌──────────────────────────────────────────┐ │
52
+ │ │ Memora 内核(Agent) │ │
53
+ │ │ - chat(input) → 流式响应 │ │
54
+ │ │ - 7 个业务 Manager getter(rolePack/tools/skills/memory/governance/works/polish,委托模式) │ │
55
+ │ │ ⚠️ 不包含:UI / LLM 配置 / 用户配置模板 │ │
56
+ │ └──────────────────────────────────────────┘ │
57
+ └────────────────────────────────────────────────────────────┘
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 二、三重数据存续路径
63
+
64
+ | 路径 | 用途 | 示例 |
65
+ |------|------|------|
66
+ | `AgentOptions.configDir` | 配置根目录(role-packs / skills) | 嵌入宿主程序 |
67
+ | `AgentOptions.dataDir` | 记忆数据目录(**必填**;记忆库形态由宿主的 `storage` 实现决定) | 宿主自定 |
68
+ | `projectPath/.memora/` | 项目级配置(rules/skills)+ 项目锁文件 `.lock` | 跟作品走 |
69
+
70
+ **小说生成器推荐布局:**
71
+ ```
72
+ 小说项目/
73
+ ├── .memora/ ← 项目级配置(rules + skills)+ 内核写入的 .lock
74
+ └── .memora-data/ ← dataDir 指向此处(记住:内核只往这里写 projects.json 注册表)
75
+ ├── projects.json ← 内核写入:项目注册表
76
+ └── <记忆库> ← 由宿主的 storage 决定形态与文件名
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 三、最小接入步骤
82
+
83
+ ### 1. 安装
84
+
85
+ ```bash
86
+ npm install @zooique/memora
87
+ ```
88
+
89
+ ### 2. 创建 Provider + Agent
90
+
91
+ ```typescript
92
+ import { Agent, createProviderFromConfig, setLogger } from '@zooique/memora';
93
+ import type { IMemoryStorage, ILogger } from '@zooique/memora';
94
+
95
+ // 宿主职责:创建 LLM Provider(Agent 不关心 API Key)
96
+ // createProviderFromConfig 是"单个 provider"入口;多 provider + active 随 loadConfig 路由见 api-reference §十 Provider 管理
97
+ const provider = createProviderFromConfig('primary', {
98
+ provider: 'openaiCompatible',
99
+ apiKey: process.env.LLM_API_KEY!,
100
+ baseUrl: 'https://api.xiaomimimo.com/v1',
101
+ model: 'deepseek-chat',
102
+ });
103
+
104
+ // 可选:后台 Provider(投影等后台操作)
105
+ const backgroundProvider = createProviderFromConfig('background', {
106
+ provider: 'openaiCompatible',
107
+ apiKey: process.env.LLM_API_KEY!,
108
+ baseUrl: 'https://api.xiaomimimo.com/v1',
109
+ model: 'deepseek-chat', // 可用更便宜的模型
110
+ });
111
+
112
+ // 可选:注入存储层(不传则使用 InMemoryStorage)。后端由宿主自选,二选一即可:
113
+ const storage: IMemoryStorage = new MySqliteStorage('/path/to/memora.db'); // SQLite(同步实现天然对齐)
114
+ // const storage: IMemoryStorage = new MyJsonStorage('/path/to/memories.json'); // JSON 文件(第一宿主 memora-vscode 的选法)
115
+
116
+ // 可选:全局替换日志实现(不调用则使用内核内置 console fallback,写 stderr;宿主可注入任意 ILogger 实现)
117
+ setLogger(myCustomLogger);
118
+
119
+ // 创建 Agent
120
+ const agent = new Agent({
121
+ projectPath: '/path/to/novel-project',
122
+ provider, // 前台 Provider(必须)
123
+ backgroundProvider, // 后台 Provider(可选)
124
+ configDir: '/path/to/agent-config',
125
+ dataDir: '/path/to/novel-project/.memora-data', // 必填;相对路径会以进程 cwd 为基准,建议传绝对路径
126
+ maxContextTokens: 120000,
127
+ activeRolePack: '作家',
128
+ permission: 'owner',
129
+ allowedPaths: ['.'],
130
+ confirmWrites: false,
131
+ storage, // 存储层注入(可选)
132
+ tracer: myOtelTracer, // 可观测性 Tracer(可选,不传则静默丢弃所有 span)
133
+ });
134
+
135
+ await agent.init();
136
+ ```
137
+
138
+ ### 3. 对话
139
+
140
+ ```typescript
141
+ // 流式对话(生产推荐)
142
+ for await (const chunk of agent.chat('帮我写一段玄幻小说开头')) {
143
+ switch (chunk.type) {
144
+ case 'text': ui.appendText(chunk.content); break;
145
+ case 'thinking': ui.showThinking(chunk.phase); break;
146
+ case 'tool_start': ui.showToolStart(chunk.name); break;
147
+ case 'tool_result':ui.showToolResult(chunk.ok); break;
148
+ case 'aborted': ui.showCancelled(chunk.reason); break;
149
+ }
150
+ }
151
+
152
+ // 同步对话(测试用)
153
+ const reply = await agent.chatSync('你好');
154
+ ```
155
+
156
+ ### 3.5 事件订阅(可选)
157
+
158
+ Agent 向宿主广播对话外事件(记忆变更、角色切换、会话分叉等)。
159
+
160
+ ```typescript
161
+ import type { AgentEventMap } from '@zooique/memora';
162
+
163
+ agent.on('memoryAdded', (e) => console.log(`新记忆: ${e.source}:${e.name}`));
164
+ agent.on('personaSwitched', (e) => console.log(`角色: ${e.from} → ${e.to}`));
165
+ agent.on('memoryRecalled', (e) => console.log(`想起 ${e.count} 条记忆`));
166
+ agent.on('sessionForked', (e) => console.log(`分叉: ${e.from} → ${e.to},${e.roundCount} 个问答闭环`));
167
+
168
+ // close() 自动移除所有监听器
169
+ ```
170
+
171
+ ### 4. 注册领域工具
172
+
173
+ > 1.0.0:工具注册走 `agent.tools.xxx()`。
174
+
175
+ ```typescript
176
+ agent.tools.registerTool(
177
+ {
178
+ name: 'create_chapter',
179
+ description: '创建一个新章节',
180
+ parameters: {
181
+ type: 'object',
182
+ properties: { title: { type: 'string', description: '章节标题' } },
183
+ required: ['title'],
184
+ },
185
+ },
186
+ async (args) => {
187
+ const chapter = await myNovel.createChapter(args.title);
188
+ return `章节 "${args.title}" 已创建`;
189
+ },
190
+ );
191
+ ```
192
+
193
+ ### 4.5 外部世界工具注入(可选,条件性暴露)
194
+
195
+ > 三个「连接外部世界」的条件工具——`web_search` / `web_fetch` / `run_code`——**宿主注入对应 provider 才暴露,未注入则不进入 LLM 工具面**,内核保持零运行时依赖。各工具的用途、参数与单工具注入方式见 [API 参考手册](./memora-api-reference.md) §8;此处演示三者**一起**注入的组合写法。
196
+
197
+ ```typescript
198
+ import { Agent, FetchWebFetchProvider } from '@zooique/memora';
199
+ import type { ICodeExecutionProvider } from '@zooique/memora';
200
+
201
+ // ① 网页抓取(搜索→抓取闭环:web_search 找链接,web_fetch 读正文)
202
+ const fetchProvider = new FetchWebFetchProvider(); // 内置零依赖默认实现
203
+
204
+ // ② 代码执行(沙箱完全由宿主提供——语言白名单/资源限制/网络隔离)
205
+ const codeExecutionProvider: ICodeExecutionProvider = {
206
+ async execute(code, language, options) {
207
+ // 例:经宿主沙箱执行,返回 stdout/stderr/exitCode/timedOut
208
+ return { stdout: '42', stderr: '', exitCode: 0, timedOut: false };
209
+ },
210
+ };
211
+
212
+ const agent = new Agent({
213
+ projectPath: '/path/to/novel-project',
214
+ dataDir: '/path/to/novel-project/.memora-data', // 必填
215
+ provider,
216
+ // webSearchProvider: new FetchWebSearchProvider(), // 搜索(web_search,参见参考手册 §8.3)
217
+ fetchProvider, // 可选:暴露 web_fetch
218
+ codeExecutionProvider, // 可选:暴露 run_code
219
+ });
220
+ ```
221
+
222
+ > **注意**:`run_code` 是通用执行能力(源码不进上下文,仅结果返回),执行隔离等级完全由宿主 provider 决定;接入前务必评估沙箱安全边界(参见参考手册 §8.5)。
223
+
224
+ ### 5. 记忆关键词与写入扩展(已移除)
225
+
226
+ > 写入扩展与记忆关键词(`agent.insight.xxx()`)已于 2026-08-14 随洞察层移除,不再提供。记忆统一以 round-summary 沉淀,经 `agent.memory.writeUpsert()` 等 `writeXxx` 方法写入。
227
+
228
+ ### 6. 查询记忆
229
+
230
+ > 1.0.0:记忆查询走 `agent.memory.xxx()`。
231
+
232
+ ```typescript
233
+ // 3 层记忆快照
234
+ const snap = agent.memory.snapshot();
235
+ console.log('工作记忆:', snap.working.total, '条');
236
+ console.log('引导记忆:', snap.bootstrap.total, '条');
237
+
238
+ // 关键词搜索
239
+ const hits = agent.memory.search('世界观');
240
+ hits.forEach(h => console.log(`${h.source}:${h.name}`));
241
+
242
+ // 记忆库统计
243
+ const stats = agent.memory.stats();
244
+ console.log('记忆总数:', stats.total);
245
+ ```
246
+
247
+ ### 7. 管理角色包
248
+
249
+ > 统一走 `agent.rolePack.xxx`(RolePackManager)。
250
+
251
+ ```typescript
252
+ const rp = agent.rolePack;
253
+ if (rp) {
254
+ // 列出所有角色包元数据
255
+ const metas = rp.listMeta();
256
+
257
+ // 手动切换角色包
258
+ rp.activate('作家');
259
+
260
+ // 查询当前状态
261
+ console.log(rp.activeName); // '作家'
262
+ console.log(rp.getActiveTraits());
263
+ }
264
+ ```
265
+
266
+ ### 8. 切换项目
267
+
268
+ ```typescript
269
+ // 按路径切换(最稳):传项目根目录的绝对路径,无需预先注册
270
+ const ctx = await agent.switchProject('/path/to/another-novel');
271
+ console.log(`已切换到:${ctx.projectName}`);
272
+
273
+ // 按名切换:依赖项目注册表命中,须先注册
274
+ // 注册表缺省落在 dataDir 内 —— 只有多项目共用同一 dataDir(或显式指定共同的 registryDir)时,
275
+ // 按名切换才成立;若 dataDir 是项目级目录,注册表会随之落进各项目、只含自身条目,按名切换不成立。
276
+ agent.projects.registerProject('/path/to/another-novel', 'another-novel');
277
+ const ctx2 = await agent.switchProject('another-novel');
278
+ ```
279
+
280
+ > 未命中注册表且传入的又不是绝对路径时,`switchProject` 直接抛错(相对路径会以进程 cwd 为基准,
281
+ > 可能静默创建出非预期的项目目录并占用其锁)。
282
+
283
+ ### 9. 关闭
284
+
285
+ ```typescript
286
+ await agent.close(); // 释放项目锁 + 关闭注入的存储实例
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 四、会话持久化(ISessionStore)
292
+
293
+ Memora 通过 `ISessionStore` 接口支持会话消息的持久化。宿主实现此接口,注入到 Agent。
294
+
295
+ > **⚠ 现行契约以类型定义为准(导出类型 `ISessionStore`,以随包 `.d.ts` 为准)**:下方示例仅示意最小形态。当前接口为 **round-based 单一模式**(`appendRoundId` / `getRoundIds` / `setRoundIds` / `createSession` / `deleteSession` + 标题元数据 `getSessionMeta` / `updateSessionMeta` / `listSessionMetas`)——消息内容只存 RoundStore,`ISessionStore` 只持 Round 指针。
296
+ > **检查点持久化已退役(2026-09-10 减法)**:`saveCheckpoint` / `loadCheckpoint` / `deleteCheckpoint` 已从接口删除;跨重启恢复链整体下线,中止/断电一律把未完成 turn 补全为完整 turn 身份并在下次会话按历史加载,运行时暂停是同 turn 内续跑(内存态)。
297
+
298
+ ```typescript
299
+ import type { ISessionStore, SessionMessage } from '@zooique/memora';
300
+
301
+ // 宿主实现接口
302
+ class FileSessionStore implements ISessionStore {
303
+ appendMessage(date: string, session: string, message: SessionMessage): void {
304
+ // 写入文件或数据库
305
+ }
306
+ loadMessages(date: string, session: string): SessionMessage[] {
307
+ // 从文件或数据库读取
308
+ return [];
309
+ }
310
+ listSessions(): string[] {
311
+ // 返回所有会话列表
312
+ return [];
313
+ }
314
+ }
315
+
316
+ // 注入到 Agent
317
+ const agent = new Agent({
318
+ // ...其他配置
319
+ sessionStore: new FileSessionStore('./sessions'),
320
+ });
321
+ ```
322
+
323
+ **会话持久化特性:**
324
+ - 日期使用 `todayDate()` 动态获取,确保跨日后消息写入当天目录
325
+ - `appendUser()` / `appendAssistant()` 自动调用 `appendMessage()`
326
+ - `loadSessionMessages()` 自动调用 `loadMessages()`
327
+ - `SessionMessage.role` 支持 `'user' | 'assistant' | 'system'`,宿主直接调用 `appendMessage()` 时可传入任意角色
328
+
329
+ ---
330
+
331
+ ## 四.5、会话分叉(Fork Session)
332
+
333
+ Memora 支持从任意问答闭环(Round)位置创建会话分叉。分叉后,新会话只包含分叉点及之前的问答闭环,后续的问答闭环在原会话中保持不变。
334
+
335
+ **使用场景**:
336
+ - 用户在对话中决定从某个关键点分叉探索不同方向
337
+ - 每个分支只继承到分叉点为止的上下文,避免无关历史干扰
338
+ - 分支完全独立,互不干扰
339
+
340
+ ```typescript
341
+ // 从指定 Round 位置分叉(唯一分叉方式,roundId 可选)
342
+ const result = agent.forkSession(roundId);
343
+ console.log(`已分叉到: ${result.newSession},复制了 ${result.roundCount} 个问答闭环`);
344
+
345
+ // 不传 roundId → 默认使用最后一个 Round(等效全量分叉)
346
+ const fullResult = agent.forkSession();
347
+
348
+ // 自定义分支名
349
+ const result2 = agent.forkSession(roundId, 'experiment');
350
+ ```
351
+
352
+ **命名规则**:
353
+ - 自动生成:平等普通会话名(与手动创建的会话一致,无 `main-b1` 等分叉标记)
354
+ - 自定义名称:直接传入目标名称作为第二参数
355
+
356
+ **记忆处理策略**:
357
+ - 已有记忆:全局共享(记忆是全局知识库,不属于单个会话)
358
+ - 分叉后的 round-summary:各自独立(不同分支探索不同方向)
359
+
360
+ **事件监听**:
361
+ ```typescript
362
+ agent.on('sessionForked', (event) => {
363
+ console.log(`从 ${event.from} 分叉到 ${event.to}`);
364
+ console.log(`复制了 ${event.roundCount} 个问答闭环`);
365
+ });
366
+ ```
367
+
368
+ **边界情况**:
369
+ - 当前会话无问答闭环时抛出错误
370
+ - 传入的 `roundId` 不存在于当前会话时抛出错误
371
+ - `ISessionStore` 未注入时抛出错误
372
+ - 对话正在进行中(`chatBusy`)时抛出错误
373
+ - 自定义名称已存在时抛出错误
374
+
375
+ ---
376
+
377
+ ## 四.6、会话归档(Archive Session)—— 宿主独立实现
378
+
379
+ Memora 内核聚焦于“对话与记忆”引擎,不负责管理用户的视觉焦点和会话列表的展示层级。**会话归档(将会话从活跃列表移至历史列表)是一个纯粹的 UI/UX 行为,应由宿主项目独立实现,无需修改 Memora 内核。**
380
+
381
+ ### 设计理念
382
+
383
+ - **内核职责**:确保对话内容(消息记录、记忆卡片)的持久化和可召回性。会话一旦创建,其内容在生命周期内始终可用。
384
+ - **宿主职责**:管理“活跃会话”与“已归档会话”的视图分离,为用户提供整洁的交互界面。
385
+
386
+ ### 实现方案
387
+
388
+ 宿主项目应在自身的数据模型中扩展会话管理功能:
389
+
390
+ #### 1. 数据库调整
391
+
392
+ 在宿主的会话数据表中增加一个状态字段,例如 `is_archived`(布尔值或枚举),用于区分会话的展示状态。
393
+
394
+ ```sql
395
+ -- 示例:宿主数据库表结构
396
+ ALTER TABLE sessions ADD COLUMN is_archived BOOLEAN DEFAULT 0;
397
+ ```
398
+
399
+ #### 2. 前端交互实现
400
+
401
+ - **归档按钮**:在会话列表项中提供“归档”操作按钮。点击时,**不调用** Memora Agent 的任何 API,而是调用宿主自身的后端接口(如 `PUT /api/sessions/{id}/archive`)。
402
+ - **列表分离**:宿主前端应维护两个视图:
403
+ - **活跃列表**:查询 `is_archived = false` 的会话。
404
+ - **历史列表**:查询 `is_archived = true` 的会话。
405
+ - **数据同步**:宿主的后端接口负责更新数据库中的 `is_archived` 字段,并通过 WebSocket 或轮询通知前端刷新列表。
406
+
407
+ ### 流程示例
408
+
409
+ ```typescript
410
+ // 宿主前端代码
411
+ async function archiveSession(sessionId: string) {
412
+ // 1. 调用宿主后端 API
413
+ const response = await fetch(`/api/sessions/${sessionId}/archive`, {
414
+ method: 'PUT',
415
+ headers: { 'Content-Type': 'application/json' }
416
+ });
417
+
418
+ if (response.ok) {
419
+ // 2. 从活跃列表移除该会话
420
+ removeSessionFromList(sessionId);
421
+ // 3. 可选:提示用户已归档
422
+ showToast('会话已归档至历史记录');
423
+ }
424
+ }
425
+
426
+ // 宿主后端路由(伪代码)
427
+ app.put('/api/sessions/:id/archive', (req, res) => {
428
+ const { id } = req.params;
429
+ // 4. 更新数据库状态
430
+ db.updateSessionStatus(id, { is_archived: true });
431
+ res.json({ success: true });
432
+ });
433
+ ```
434
+
435
+ ### 核心优势
436
+
437
+ - **解耦**:Memora 内核无需感知“归档”这一视图行为,保持纯粹的业务逻辑。
438
+ - **灵活**:宿主可自由扩展“归档”的变体,如“收藏夹”、“置顶”、“多级分类”等,均可在宿主侧独立完成。
439
+ - **稳定**:内核代码零风险,记忆召回逻辑完全不受归档行为影响,确保所有对话内容(无论归档与否)都能被准确回忆。
440
+
441
+ ---
442
+
443
+ ## 五、API 速查
444
+
445
+ > 构造选项、生命周期、对话、项目/会话、各 Manager、事件、Provider 管理等**完整条目与类型定义**详见 [memora-api-reference.md](./memora-api-reference.md)。此处只列接入期最常用的几个入口:
446
+
447
+ | 操作 | 入口 | 详见参考手册 |
448
+ |------|------|------|
449
+ | 构造 | `new Agent({ projectPath, provider, configDir, dataDir })` | §二 · AgentOptions |
450
+ | 对话 | `agent.chat(input)`(流式)/ `agent.chatSync(input)` | §三 |
451
+ | 记忆 | `agent.memory.snapshot()/search()/writeXxx()` | §四 |
452
+ | 角色包 | `agent.rolePack.listMeta()/activate()` | §七 |
453
+ | 工具 | `agent.tools.registerTool()/execute()` | §八 |
454
+ | 会话 | `agent.forkSession()` / `agent.sessionManager.*` | §六 |
455
+
456
+ > 记忆治理(去重/冲突)统一走 `agent.governance` 或 Agent 层方法,见参考手册 §四 与「记忆治理」章节。
457
+
458
+ ---
459
+
460
+ ## 六、宿主工具函数
461
+
462
+ > 所有可导入工具函数/类型的**完整 import 清单与导出声明**见 [memora-api-reference.md](./memora-api-reference.md) §十六。下表列出常用函数与其用途:
463
+
464
+ | 函数/类型 | 用途 |
465
+ |-----------|------|
466
+ | `createLlmProvider(config)` | 从完整 Config(`llm.providers` + `active`)创建激活 Provider(配合 `loadConfig`) |
467
+ | `createProviderFromConfig(name, config)` | 从命名配置创建单个 Provider 实例(扁平参数) |
468
+ | `loadConfig(path?)` | 加载 `.memora/config.json` 项目级配置文件(或传入显式 configPath) |
469
+ | `InMemoryStorage` | IMemoryStorage 的纯内存实现(测试用) |
470
+ | `setLogger(logger)` | 替换全局日志实现 |
471
+ | `logger` | 全局日志实例 |
472
+ | `segmentText(text)` | 中文分词工具 |
473
+ | `parseFrontmatter(text)` | 解析 Markdown frontmatter |
474
+ | `serializeFrontmatter(fm, body)` | 序列化 frontmatter + body 为 Markdown |
475
+ | `safeSetTimeout(fn, ms)` | 安全定时器(可跟踪清理) |
476
+ | `safeSetInterval(fn, ms)` | 安全间隔器(可跟踪清理) |
477
+ | `clearSafeTimeout(id)` | 清除安全定时器 |
478
+ | `clearSafeInterval(id)` | 清除安全间隔器 |
479
+ | `extractKeywords(text)` | 提取关键词 |
480
+ | `SOURCE_LABELS` | source 标签常量(PERSONA / RULE / SKILL / WORK_PROJECTION / ROUND_SUMMARY / UNKNOWN) |
481
+ | `escapeLike(query)` | 转义 SQLite LIKE 通配符(`%` / `_` → `\%` / `\_`)。**SQL 必须写成 `LIKE ? ESCAPE '\'`**——不写 ESCAPE 子句则反斜杠被视为普通字符,转义后的模式匹配不到任何字面值,查询**静默返回空** |
482
+ | `escapeLikeSnippet(text, maxLen?)` | 截断至 maxLen(默认 50)后转义 LIKE 通配符——搜索输入防超长解析 + 防通配符被当模式符(SQL 后端检索的配套动作,同样须配 `ESCAPE '\'`) |
483
+ | `validateSource(source)` | 校验 source 标签是否为已知标签(返回 warning,不阻止写入) |
484
+ | `MemoraError` | 统一错误类型(结构化错误码 + 上下文) |
485
+ | `toError(err)` | 将任意值转为 Error(浏览器端安全,不引入 `logging/` 模块) |
486
+ | `NOOP_TRACER` | ITracer 的空实现(静默丢弃所有 span,零开销) |
487
+ | `TRACE_SPANS` | AgentLoop 预定义 Span 名称常量(RECALL / LLM_CALL / TOOL_EXEC / RESPONSE) |
488
+ | `ToolErrorCode` | 工具错误码枚举(10 种,含 PATH_NOT_ALLOWED / FILE_NOT_FOUND 等) |
489
+ | `isRetryableErrorCode(code)` | 判断错误码是否可重试(5 种 retryable) |
490
+ | `TypedEventEmitter` | 类型安全的事件发射器(Agent 继承此类) |
491
+
492
+ **注意**:`SqliteStorage` 已移出到宿主项目,不再从 memora 导出。宿主需自行实现 `IMemoryStorage` 接口。
493
+
494
+ `IMemoryStorage` 接口要求实现 `count()` 和 `countBySource(source)` 方法,用于高效统计记忆数量(避免全量加载数据)。详见 [API 参考手册](./memora-api-reference.md)。
495
+
496
+ ---
497
+
498
+ ## 七、可观测性接入(ITracer)
499
+
500
+ Memora 内置了轻量的 Span/Trace 抽象,宿主可注入 OpenTelemetry 等实现来观测 AgentLoop 行为。
501
+
502
+ ### 接口定义
503
+
504
+ ```typescript
505
+ interface ITracer {
506
+ startSpan(name: string, attributes?: Record<string, string | number | boolean>): ISpan;
507
+ }
508
+
509
+ interface ISpan {
510
+ setAttribute(key: string, value: string | number | boolean): void;
511
+ end(): void;
512
+ recordException(error: Error): void;
513
+ }
514
+ ```
515
+
516
+ ### AgentLoop 预定义 Span
517
+
518
+ | Span 名称 | 常量 | 触发时机 | 关键属性 |
519
+ |-----------|------|---------|---------|
520
+ | `llm.call` | `TRACE_SPANS.LLM_CALL` | LLM API 调用(含重试循环) | `model`, `messageCount`, `iteration`,另写 `inputTokens` |
521
+ | `tool.execute` | `TRACE_SPANS.TOOL_EXEC` | 工具执行(并发时多个 span 时间重叠) | `toolName`,另写 `args`(被拒时 `denied`) |
522
+ | `response.generate` | `TRACE_SPANS.RESPONSE` | 整轮响应 | `inputLength`(任务级指标随流结束统一补写) |
523
+ | `context.summary` | `TRACE_SPANS.CONTEXT_SUMMARY` | 消息超 `maxContextTokens` 触发截断、生成"遗忘补偿"摘要 | `messageCount`, `summarizingMessages` |
524
+ | `archive.postProcess` | `TRACE_SPANS.POST_PROCESS` | 每轮 `chat()` 后归档(round-summary、角色/技能匹配) | `archiveMode` |
525
+ | `round.difficulty` | `TRACE_SPANS.DIFFICULTY` | **预留名,内核当前零 emit 点** | — |
526
+ | `round.report` | `TRACE_SPANS.REPORT` | **预留名,内核当前零 emit 点** | — |
527
+
528
+ > 共 **7** 个常量。`TRACE_SPANS.RECALL` / `TRACE_SPANS.RECALL_ACTUAL`(`recall.recall` / `recall.actual`)已于 **2026-09-11 物理删除**(自动记忆召回退役后无 emit 点)——宿主无需为其编写 span 处理逻辑;记忆检索耗时看 `tool.execute`(`search_memories` 工具)。
529
+
530
+ ### 宿主接入示例(OpenTelemetry)
531
+
532
+ ```typescript
533
+ import { trace } from '@opentelemetry/api';
534
+ import type { ITracer, ISpan } from '@zooique/memora';
535
+
536
+ // 宿主实现 ITracer 接口(桥接 OpenTelemetry)
537
+ class OtelTracer implements ITracer {
538
+ private tracer = trace.getTracer('memora-agent');
539
+
540
+ startSpan(name: string, attributes?: Record<string, string | number | boolean>): ISpan {
541
+ const otelSpan = this.tracer.startSpan(name, { attributes });
542
+ return {
543
+ setAttribute: (key, value) => otelSpan.setAttribute(key, value),
544
+ end: () => otelSpan.end(),
545
+ recordException: (error) => otelSpan.recordException(error),
546
+ };
547
+ }
548
+ }
549
+
550
+ // 注入到 Agent
551
+ const agent = new Agent({
552
+ // ...其他配置
553
+ tracer: new OtelTracer(),
554
+ });
555
+ ```
556
+
557
+ > **不注入时**:默认使用 `NOOP_TRACER`(静默丢弃所有 span,零运行时开销)。
558
+
559
+ ---
560
+
561
+ ## 八、工具错误反思(Reflection)
562
+
563
+ > 注:原「内容护栏(Guardrails)」章节已移除——guardrail 是「零规则、无扫描映射、无消费者」的空转链,已随内核摘除(2026-08-17,见 memory-role-pack-boundary.md §4.4)。
564
+
565
+ 当工具执行失败时,错误结果包含 `[ERR:TOOL:code]` 前缀。如果错误码标记为 retryable(如 `FILE_NOT_FOUND`、`ARGUMENT_ERROR`),AgentLoop 会自动注入 `[REFLECTION_HINT]` 系统消息,引导 LLM 修正参数后重试。
566
+
567
+ ```typescript
568
+ import { ToolErrorCode, isRetryableErrorCode } from '@zooique/memora';
569
+
570
+ // 判断错误码是否可重试
571
+ isRetryableErrorCode(ToolErrorCode.FILE_NOT_FOUND); // true
572
+ isRetryableErrorCode(ToolErrorCode.PATH_NOT_ALLOWED); // false
573
+ ```
574
+
575
+ Reflection 默认最多重试 2 次(`maxReflectionRetries`),防止无限循环。
576
+
577
+ ---
578
+
579
+ ## 九、多步骤编排
580
+
581
+ Memora 是单 Agent 模型(ADR-011),不内置多 Agent 编排。宿主项目可通过多次调用 `agent.chat()` 实现复杂工作流:
582
+
583
+ ```typescript
584
+ // 示例:分析项目 → 生成大纲 → 逐章写作
585
+ const analysis = await agent.chatSync('分析这个项目的风格和主题');
586
+ const outline = await agent.chatSync('基于分析结果,生成章节大纲');
587
+ for (const chapter of chapters) {
588
+ const content = await agent.chatSync(`根据大纲,写第${chapter.num}章:${chapter.title}`);
589
+ // 写入文件...
590
+ }
591
+ ```
592
+
593
+ **编排要点**:
594
+ - 每次 `chat()` 共享同一个 AgentLoop 上下文(记忆、角色、技能)
595
+ - 宿主负责流程控制(条件分支、并行、错误处理)
596
+ - 如需隔离上下文,使用 `agent.sessionManager.switchSession()` 或 `agent.forkSession()`
597
+
598
+ ---
599
+
600
+ ## 十、关键约束
601
+
602
+ **前提:存储方案由宿主自选,内核只定契约。** 内核只定义 `IMemoryStorage` / `ISessionStore` 接口,**不规定后端**——SQLite(better-sqlite3)、JSON 文件、内存实现一视同仁,任何后端都不得被写进内核逻辑。接口方法为**同步语义**(对齐 better-sqlite3),异步后端需另行扩展接口;JSON 落地实现在同步路径上做全量重写,记忆量增长时由宿主自行评估更优后端(阈值与权衡见 `storageInterface.ts` 头注释)。第一宿主 memora-vscode 当前用 JSON 文件,换成 SQLite **不需要改内核**。
603
+
604
+ 1. **`provider` 是必填项** — Agent 无法独立运行
605
+ 2. **configDir** 指向 Agent 级配置目录,所有项目共享
606
+ 3. **项目级 `.memora/`** 放项目级配置(`rules/`、`skills/`)与内核写入的项目锁 `.lock`;`dataDir` 若指向此处(第一宿主 memora-vscode 即如此),记忆库与会话也落在这里
607
+ 4. **角色包只能手动切换**(`agent.switchRolePack(name)` / `agent.rolePack.activate(name)`)——无输入自动匹配、无粘性锁存、无互斥触发;已激活的角色包在当前会话内保持固定(v0.13 已移除自动匹配全链)
608
+ 5. **对话历史随 Agent 持久化** — `sessionStore` 是实例级注入的,`switchProject()` 不换存储;未注入则仅内存保存
609
+ 6. **Manager 访问器在 `init()` 前返回 `null`** — 所有 `agent.rolePack.xxx()` / `agent.memory.xxx()` 等调用必须在 `init()` 之后
610
+ 7. **作品原始内容不进记忆库**,Agent 通过工具按需读取(大文本走 `read_file` 分段,提炼后只留情报,不全量入库 / 入上下文)
611
+ 8. **配置文件是真理源**,`<configDir>/role-packs/` 下的角色包由 RolePackManager 启动时扫描加载
612
+ 9. **一个 Agent = 一份记忆库** — `storage` 与 `dataDir` 都是 **Agent 实例级**的:`switchProject()` 复用同一实例,不按项目 / 子项目分裂。**要按工作区彼此隔离,就各自 `new Agent`**——隔离粒度归宿主决定,内核不规定
613
+ 10. **项目切换不重建、不关闭存储** — `switchProject()` 复用已注入的实例,只在 `close()` 时关闭(`ProjectManager` 在 Agent 生命周期内缓存)
614
+ 11. **禁止把配置放进记忆库** — 配置(角色包 / persona / rules / skills)的真理源是文件(`configDir` / 角色包),设定记忆归角色包内容层、不进记忆库(ADR-025 / `memory-role-pack-boundary`)
615
+ 12. **项目切换** `switchProject()` 已自动 rebuild,通常无需手动调用 `rebuildComponents()`
616
+ 13. **Tracer 未注入时**自动降级为 NoopTracer,零运行时开销
617
+
618
+ ---
619
+
620
+ ## 十.5、执行前检查决策(preExecutionCheck)⚠️ 安全基线
621
+
622
+ `preExecutionCheck` 是工具执行前的统一闸门(内核 `ToolExecutor` 单点聚合检查:只读 → 审批 → 执行三态)。宿主经 `AgentOptions.preExecutionCheck` 注入,返回 `allow` / `skip` / `confirm` 三态。
623
+
624
+ ### 单用户桌面场景(可恒放行)
625
+
626
+ 本地运行的单用户宿主(VS Code 插件 / 桌面应用)可注入恒放行:
627
+
628
+ ```ts
629
+ preExecutionCheck: () => ({ skip: false }), // 放行,等价于 allow
630
+ ```
631
+
632
+ **理由**(宿主装配注入点见仓库内参考实现 `hosts/memora-vscode/src/extension/host/assemble.ts`,**非随包产物**):
633
+ 1. 宿主运行在用户本地,天然信任模型;
634
+ 2. 工具审计已由 `tool_start` / `tool_result` chunk + `tool.execute` span 承担,不重复记录。
635
+
636
+ ### 多用户 / 服务端部署(必须替换)⚠️
637
+
638
+ **恒放行仅限单用户本地场景。** 一旦接入多用户或服务端部署,必须替换为真实审批策略,否则任意用户可经工具读写任意文件 / 执行任意命令。最小实现要点:
639
+
640
+ - **审批在宿主侧实现**:内核 3.0.0 已删内核审批键(`toolApproval`,理由见下 §十.7 补记),多用户场景须由宿主在 `preExecutionCheck` 实现真实审批(返回 `denied` 拒绝),高危操作(写文件 / 执行命令)默认拒绝而非放行;
641
+ - **路径守卫**:结合内核 `SecurityGuard` 的 `allowedPaths` 白名单 + 28 类禁止规则做硬性拦截,返回 `skip` 阻断越权调用;
642
+ - **只读策略**:角色包 `toolReadonly: 'readonly'` 时,preExecutionCheck 须拒绝一切写操作。
643
+
644
+ > 本决策同时受 `tasks/待完成任务.md` 设计纪律 **D5** 约束:恒放行属单用户场景可接受,但必须显式标注,未来多用户/服务端再接入真实策略。
645
+
646
+ ---
647
+
648
+ ## 十.6、存储单例约束(sessionStore / roundStore)⚠️ 契约基线
649
+
650
+ `sessionStore`(会话元数据)与 `roundStore`(问答闭环 / round 落盘)**必须全局唯一单例,且 UI 面板与 Agent 装配共享同一实例**。
651
+
652
+ ### 为什么是硬约束(双实例覆盖写)
653
+
654
+ 会话与 round 落盘的最终载体是同一份磁盘文件(`*.memora/sessions.json` + 各 round 文件)。若 UI 面板与 Agent 各自 `new` 一份 store:
655
+
656
+ - 两侧各自 `load()` 后内存状态分叉;
657
+ - 任一侧 `save()` 都只写自己内存视角,**后写覆盖先写** → 另一侧已追加的会话 / 消息丢失;
658
+ - 典型症状:重启后"会话记录加载不全"、跨面板操作互相吞掉对方写入。
659
+
660
+ 这是数据正确性 bug,**不是性能或风格问题**,任何宿主对接都会踩。
661
+
662
+ ### 规范模式(VS Code 宿主实证)
663
+
664
+ 在 `activate()` 中创建一次,注入两侧(见仓库内参考实现 `hosts/memora-vscode/src/extension/extension.ts`,**非随包产物**:`activate()` 内 `new WorkspaceRoundStore(...)` 与 `new WorkspaceSessionStore(workspacePath, roundStore)` 各建一次;面板侧经 `new MemoraChatViewProvider(..., sessionStore, ...)` 与 `settingsProvider.setAgentFactory(...)`,Agent 侧经 `getOrCreateAgent(..., sessionStore, roundStore, ...)` → `assembleAgent(...)`):
665
+
666
+ ```ts
667
+ // 单例:activate 中创建一次
668
+ const sessionStore = new WorkspaceSessionStore(workspacePath, roundStore);
669
+ sessionStore.load();
670
+
671
+ // 注入 UI 面板:面板经 setAgentFactory 复用同一 agentPromise → 同一份 store
672
+ chatProvider.setAgentFactory((projectPath) =>
673
+ getOrCreateAgent(projectPath, providerStore, sessionStore, roundStore, /* ... */));
674
+
675
+ // 注入 Agent 装配:assembleAgent 透传同一 sessionStore / roundStore 进内核
676
+ ```
677
+
678
+ `sessionStore` 经 `getOrCreateAgent` → `assembleAgent` 透传进内核;UI 面板经 `chatProvider.setAgentFactory` 复用同一 `agentPromise`,从而共用同一份 store。**两侧的 store 引用必须指向同一对象**,禁止各建各的。
679
+
680
+ ### 单例 / 原子写各防什么(边界辨析)
681
+
682
+ - **单例**防**同进程内**两个代码路径各 `new` 一份 store → 内存分叉 → 互相覆盖写。VS Code 单宿主下,单例已彻底封死这条路。
683
+ - **原子写**(`atomicWriteFileSync`:先写 `.tmp` 再 `renameSync`)防**写一半进程崩了**导致文件损坏——落盘要么旧内容、要么完整新内容,不会半截。memora 已实现,且**内核与宿主各有一份**:内核 `src/utils/atomicWrite.ts` 的 `atomicWriteFile`(异步,当前生产消费者为 `WorkProjectionManager`);宿主 `hosts/memora-vscode/src/extension/host/atomicWriteSync.ts` 的 `atomicWriteFileSync`(同步,供 `WorkspaceSessionStore` / `WorkspaceRoundStore` / `WorkspaceStorage` 等宿主存储消费,**非随包产物**)。
684
+ - **两者都不防"跨进程双开"**:同工作区开两个窗口 / 两个宿主进程 = 两个 `sessionStore` 单例 = 两个进程各原子写同一 `*.memora/sessions.json`。原子写保"不损坏"但不保"不丢更新"(后 rename 覆盖先 rename)。此场景须靠**进程锁 / 单实例守卫**兜底(参考 grida 对 `sessions.db` 加进程锁、重复启动直接拒绝)。
685
+
686
+ ### 新宿主接入检查清单
687
+
688
+ - [ ] `sessionStore` / `roundStore` 在宿主生命周期内只 `new` 一次;
689
+ - [ ] UI 面板(对话视图 / 会话树 / 设置)与 Agent 装配引用**同一个** store 实例;
690
+ - [ ] 落盘走**原子写**(`tmp` + `rename`),禁止裸 `writeFile` 覆盖(防崩溃损坏);
691
+ - [ ] **跨进程 / 多窗口宿主须加文件锁或单实例守卫**:单例只防同进程双实例,不防同工作区双开各自写同一 `sessions.json`;原子写保证不损坏但不保证不丢更新,须进程锁兜底;
692
+ - [ ] 测试覆盖:模拟"面板先写、Agent 后写"或反之,断言最终落盘含两侧写入(防回归双实例覆盖写)。
693
+
694
+ > 本约束原仅存于宿主实现注释(`extension.ts` 的"杜绝双实例覆盖写";仓库内参考实现,**非随包产物**),现提升为宿主接入契约基线,避免新宿主对接踩坑。原子写实锤已具备(`atomicWriteFileSync`),单例 + 原子写已对齐主流硬化做法(grida / Chatbox 同款 tmp+rename);唯一真实残留为跨进程锁,已列入检查清单第 4 项。
695
+
696
+ ## 十.7、策略键消费矩阵(16 键 SSOT 落点)📌 参考
697
+
698
+ > 逐键矩阵(16 键 × 内核消费位置 × UI 侧消费)为仓库内开发文档(SSOT:`src/role-pack/strategyKeys.ts`),**不随包发布**;宿主对接所需的键语义见 [role-pack-authoring-guide.md](./architecture/role-pack-authoring-guide.md) §三。此处只给新宿主对接必知的结论与边界。
699
+
700
+ - **16 键全部被内核真实消费、零 `[草案]`**(prepare 1 / act 7 / reflect 3 / global 5)。解析层 `rolePackManager` + `strategyResolver` 84 项测试守护,消费层跨 `contextPreparer` / `loop` / `agent` / `managers/llmCaller` / `toolRunner` / `orchestrator` / `prepare` 多文件覆盖。
701
+ - **三类流向,单一收口无镜像**:
702
+ 1. act 5 键(`toolMode`/`toolStepLimit`/`providerRouting`/`multiStepReasoning`/`toolReadonly`)+ `selfReview` + global 4 键(`errorHandling`/`contextLimit`/`stepBudget`/`askLimit`),统一经 `resolveL2Strategy()` 聚合注入 `L2RuntimeStrategy`,loop 经 `this.strategy.<field>` 读取;`act.temperature`/`outputLimit` 由 `agent.ts` 直映射 `ChatOptions`(不经聚合);
703
+ 2. prepare 键经 `resolveActiveStrategy()` 单一真理源流入 `contextPreparer` / `roundSummaryGenerator`;
704
+ 3. persona 指令类(`userFollowup`/`askOn`/`askLimit`)由 `assembleRolePack()` 单收口注入。
705
+ - **⚠️ 边界纠正:`toolReadonly` 内核已执行,非「无宿主执行方」**。`toolRunner.ts` 只剩两层闸:闸①(只读闸 `toolReadonly==='readonly'`)**由内核执行**;闸③(宿主 `preExecutionCheck` 注入点)当前 VS Code 宿主恒放行(`assemble.ts` 恒返回 `() => ({ skip: false })`)只是单用户信任模型下的显式让行(关联 十.5 / 设计纪律 D5),未来多用户/服务端须替换真实审批。原闸② `toolApproval` 审批链已随 3.0.0 删键(2026-09-11,故代码注释 ①/③ 编号保留):单用户本地无真实审批场景,原"通知宿主 + UI 需审批 chip"是展示性假承诺——整键删除,新宿主切勿再期待 `onToolApproval` 回调或 `toolApproval==='confirm'` 语义,审批须自行在 `preExecutionCheck` 实现。
706
+ - **UI 侧 6 键有呈现**:宿主角色列表视图渲染策略 chip 5 键(`toolReadonly` / `summaryFocus` / `outputLimit` / `temperature` / `multiStepReasoning`)+ `toolMode` 经对话视图 capability badge 渲染('纯 LLM' / '全部工具')。能力名映射与策略键无关,**勿混淆**。(上述 UI 实现均为仓库内宿主参考实现,**非随包产物**。)
707
+
708
+ ---
709
+
710
+ ## 十一、用户档案实现指南(宿主扩展)
711
+
712
+ > **为什么内核没有独立的用户档案模块?**
713
+
714
+ ### 设计哲学:偏好涌现 vs 配置驱动
715
+
716
+ Memora 的记忆系统通过 `round-summary` 的 `type=preference` 类型承载"偏好涌现"的核心需求。用户在对话中的偏好(如"喜欢简洁回答"、"偏好中文沟通")会自然沉淀为摘要记忆,并在后续对话中随召回注入上下文。
717
+
718
+ 这种"涌现驱动"的设计与传统"配置驱动"的用户档案方案有本质区别:
719
+
720
+ | 维度 | WorkBuddy 类方案(配置驱动) | Memora 方案(涌现驱动) |
721
+ |------|---------------------------|----------------------|
722
+ | 更新方式 | 手动编辑档案文档 | 对话自然沉淀 |
723
+ | 结构化程度 | 强(schema 约束) | 弱(LLM 理解) |
724
+ | 内核侵入性 | 高(需新模块) | 低(复用现有机制) |
725
+
726
+ **Memora 选择"涌现驱动"的核心理由**:
727
+ 1. **单一真理源**:偏好从对话中涌现,而非外部注入,避免双写冲突
728
+ 2. **自然生长**:不预埋用户画像结构,符合"种子决定长势、土壤决定养分"哲学
729
+ 3. **零新增模块**:复用现有记忆/召回机制
730
+
731
+ ### 宿主实现方案
732
+
733
+ 如果宿主确实需要"预配置用户身份"的场景(如"我叫张三、后端工程师、偏好 Go 语言"),可以通过以下两种方式扩展,**内核零改动**。
734
+
735
+ #### 方案一:工具注册(LLM 按需查询)
736
+
737
+ 适合场景:用户身份信息在对话中被 LLM 按需查询。
738
+
739
+ ```typescript
740
+ // 宿主实现用户档案读取逻辑
741
+ async function loadUserProfile(): Promise<Record<string, unknown>> {
742
+ // 从文件、数据库或环境变量读取
743
+ return {
744
+ name: '张三',
745
+ role: '后端工程师',
746
+ preferredLanguage: 'zh-CN',
747
+ codingLanguage: 'Go',
748
+ // ...其他用户信息
749
+ };
750
+ }
751
+
752
+ // 注册为工具,LLM 需要时主动调用
753
+ agent.tools.registerTool(
754
+ {
755
+ name: 'read_user_profile',
756
+ description: '读取当前用户的档案信息(姓名、角色、偏好等)',
757
+ parameters: {
758
+ type: 'object',
759
+ properties: {}, // 无参数
760
+ },
761
+ },
762
+ async () => {
763
+ const profile = await loadUserProfile();
764
+ return `用户档案:姓名=${profile.name},角色=${profile.role},偏好语言=${profile.preferredLanguage}`;
765
+ },
766
+ );
767
+ ```
768
+
769
+ #### 方案二:复用 `agent.injectAffect()` 固定注入(设计定案)
770
+
771
+ 适合场景:用户身份 / 基础设定需要**始终在上下文中**,且与角色包解耦(切换角色不丢失)。
772
+
773
+ 内核公开 API `agent.injectAffect(text)`(见 [API 参考手册](./memora-api-reference.md) §十二 完整 API 一览·角色条目)是「宿主可控的 system prompt 固定文本槽」:文本原样插在**角色包 prompt 与 bootstrap 记忆之间**,角色切换不清除、实时重建,传空串清除。名字虽沿用"情感基调",机制即通用文本注入,正适合承载用户基础设定。
774
+
775
+ ```typescript
776
+ // 宿主启动装配、agent.init() 完成后调用一次(内存态——每次启动从宿主自有存储读取后注入)
777
+ async function loadUserProfile(): Promise<{
778
+ name: string;
779
+ role?: string;
780
+ background?: string;
781
+ preferences?: string[];
782
+ } | null> {
783
+ // 宿主实现:文件 / SQLite / 环境变量均可
784
+ }
785
+
786
+ const profile = await loadUserProfile();
787
+ if (profile) {
788
+ agent.injectAffect(
789
+ [
790
+ '【用户基础设定】',
791
+ `姓名:${profile.name}`,
792
+ `角色:${profile.role ?? '未指定'}`,
793
+ `背景:${profile.background ?? '未指定'}`,
794
+ `偏好:${profile.preferences?.join('、') ?? '无'}`,
795
+ ].join('\n'),
796
+ );
797
+ }
798
+ // 需要清除时:agent.injectAffect('');
799
+ ```
800
+
801
+ ### 两种方案对比
802
+
803
+ | 维度 | 方案一:工具注册 | 方案二:injectAffect 固定注入 |
804
+ |------|---------------|-------------------|
805
+ | 触发方式 | LLM 按需调用 | 装配时注入一次,始终在上下文 |
806
+ | Token 效率 | 高(仅需要时消耗) | 低-中(每轮随 system prompt 携带) |
807
+ | 实现复杂度 | 低 | 低 |
808
+ | 适用场景 | 身份信息偶尔被查询 | 身份信息必须始终可见、且与角色解耦 |
809
+
810
+ ### 用户档案文件格式示例
811
+
812
+ 宿主可以使用任意格式存储用户档案,推荐 JSON 或 Markdown:
813
+
814
+ **`user-profile.json`**
815
+ ```json
816
+ {
817
+ "name": "张三",
818
+ "role": "后端工程师",
819
+ "background": "5 年 Go 开发经验,熟悉微服务架构",
820
+ "preferences": ["简洁回答", "中文沟通", "代码优先"]
821
+ }
822
+ ```
823
+
824
+ **`user-profile.md`**
825
+ ```markdown
826
+ # 用户档案
827
+
828
+ - **姓名**:张三
829
+ - **角色**:后端工程师
830
+ - **背景**:5 年 Go 开发经验,熟悉微服务架构
831
+ - **偏好**:简洁回答、中文沟通、代码优先
832
+ ```
833
+
834
+ ### 核心约束
835
+
836
+ 1. **用户档案完全由宿主管理**:存储、更新、版本管理
837
+ 2. **内核零新增**:不新增存储、不新增 API——方案二复用现有公开方法 `agent.injectAffect()`,仅以文本形式注入,内核不感知"档案"语义
838
+ 3. **不注入时零影响**:未实现用户档案的宿主,Agent 行为不变
839
+ 4. **偏好记忆仍走涌现路径**:对话中涌现的偏好(如"用户喜欢简洁回答")仍通过 `preference` 摘要沉淀,用户档案仅承载**静态/半静态身份信息**
840
+
841
+ ---
842
+
843
+ > 更多细节参见 [memora-api-reference.md](./memora-api-reference.md)(完整 API 参考)