@zooique/memora 4.0.0 → 5.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 (239) hide show
  1. package/CHANGELOG.md +165 -1743
  2. package/README.en.md +43 -36
  3. package/README.md +308 -303
  4. package/dist/agent/agent.d.ts +46 -82
  5. package/dist/agent/agent.d.ts.map +1 -1
  6. package/dist/agent/agent.js +153 -220
  7. package/dist/agent/agent.js.map +1 -1
  8. package/dist/agent/assembler.d.ts +3 -16
  9. package/dist/agent/assembler.d.ts.map +1 -1
  10. package/dist/agent/assembler.js +46 -35
  11. package/dist/agent/assembler.js.map +1 -1
  12. package/dist/agent/backgroundTasks.d.ts +8 -1
  13. package/dist/agent/backgroundTasks.d.ts.map +1 -1
  14. package/dist/agent/backgroundTasks.js +8 -1
  15. package/dist/agent/backgroundTasks.js.map +1 -1
  16. package/dist/agent/budget.d.ts +23 -5
  17. package/dist/agent/budget.d.ts.map +1 -1
  18. package/dist/agent/budget.js +8 -2
  19. package/dist/agent/budget.js.map +1 -1
  20. package/dist/agent/builtinToolHandlers.d.ts +27 -8
  21. package/dist/agent/builtinToolHandlers.d.ts.map +1 -1
  22. package/dist/agent/builtinToolHandlers.js +45 -9
  23. package/dist/agent/builtinToolHandlers.js.map +1 -1
  24. package/dist/agent/builtinTools.d.ts.map +1 -1
  25. package/dist/agent/builtinTools.js +4 -3
  26. package/dist/agent/builtinTools.js.map +1 -1
  27. package/dist/agent/constants.d.ts +0 -6
  28. package/dist/agent/constants.d.ts.map +1 -1
  29. package/dist/agent/constants.js +7 -8
  30. package/dist/agent/constants.js.map +1 -1
  31. package/dist/agent/contextManager.d.ts +8 -0
  32. package/dist/agent/contextManager.d.ts.map +1 -1
  33. package/dist/agent/contextManager.js +45 -3
  34. package/dist/agent/contextManager.js.map +1 -1
  35. package/dist/agent/contextPreparer.d.ts +1 -1
  36. package/dist/agent/contextPreparer.d.ts.map +1 -1
  37. package/dist/agent/contextPreparer.js +15 -10
  38. package/dist/agent/contextPreparer.js.map +1 -1
  39. package/dist/agent/guardRail.d.ts +1 -1
  40. package/dist/agent/guardRail.d.ts.map +1 -1
  41. package/dist/agent/guardRail.js +1 -1
  42. package/dist/agent/guardRail.js.map +1 -1
  43. package/dist/agent/loop.d.ts +46 -13
  44. package/dist/agent/loop.d.ts.map +1 -1
  45. package/dist/agent/loop.js +128 -25
  46. package/dist/agent/loop.js.map +1 -1
  47. package/dist/agent/managers/archiveCoordinator.d.ts +15 -3
  48. package/dist/agent/managers/archiveCoordinator.d.ts.map +1 -1
  49. package/dist/agent/managers/archiveCoordinator.js +13 -7
  50. package/dist/agent/managers/archiveCoordinator.js.map +1 -1
  51. package/dist/agent/managers/chatLockManager.d.ts +3 -9
  52. package/dist/agent/managers/chatLockManager.d.ts.map +1 -1
  53. package/dist/agent/managers/chatLockManager.js +7 -25
  54. package/dist/agent/managers/chatLockManager.js.map +1 -1
  55. package/dist/agent/managers/llmCaller.d.ts.map +1 -1
  56. package/dist/agent/managers/llmCaller.js +27 -1
  57. package/dist/agent/managers/llmCaller.js.map +1 -1
  58. package/dist/agent/managers/memoryInspector.d.ts +12 -7
  59. package/dist/agent/managers/memoryInspector.d.ts.map +1 -1
  60. package/dist/agent/managers/memoryInspector.js +32 -19
  61. package/dist/agent/managers/memoryInspector.js.map +1 -1
  62. package/dist/agent/managers/roundSummaryGenerator.d.ts +3 -1
  63. package/dist/agent/managers/roundSummaryGenerator.d.ts.map +1 -1
  64. package/dist/agent/managers/roundSummaryGenerator.js +14 -4
  65. package/dist/agent/managers/roundSummaryGenerator.js.map +1 -1
  66. package/dist/agent/managers/sessionArchiver.d.ts +20 -6
  67. package/dist/agent/managers/sessionArchiver.d.ts.map +1 -1
  68. package/dist/agent/managers/sessionArchiver.js +19 -5
  69. package/dist/agent/managers/sessionArchiver.js.map +1 -1
  70. package/dist/agent/managers/sessionManager.d.ts +21 -88
  71. package/dist/agent/managers/sessionManager.d.ts.map +1 -1
  72. package/dist/agent/managers/sessionManager.js +77 -278
  73. package/dist/agent/managers/sessionManager.js.map +1 -1
  74. package/dist/agent/seed/orchestrator.d.ts.map +1 -1
  75. package/dist/agent/seed/orchestrator.js +3 -3
  76. package/dist/agent/seed/orchestrator.js.map +1 -1
  77. package/dist/agent/seed/prepare.d.ts.map +1 -1
  78. package/dist/agent/seed/prepare.js +9 -4
  79. package/dist/agent/seed/prepare.js.map +1 -1
  80. package/dist/agent/seed/types.d.ts +11 -4
  81. package/dist/agent/seed/types.d.ts.map +1 -1
  82. package/dist/agent/seed/types.js +2 -2
  83. package/dist/agent/toolExecutor.d.ts +51 -6
  84. package/dist/agent/toolExecutor.d.ts.map +1 -1
  85. package/dist/agent/toolExecutor.js +74 -9
  86. package/dist/agent/toolExecutor.js.map +1 -1
  87. package/dist/agent/toolResultCache.js +1 -1
  88. package/dist/agent/toolRunner.d.ts +4 -2
  89. package/dist/agent/toolRunner.d.ts.map +1 -1
  90. package/dist/agent/toolRunner.js +7 -4
  91. package/dist/agent/toolRunner.js.map +1 -1
  92. package/dist/agent/types.d.ts +8 -25
  93. package/dist/agent/types.d.ts.map +1 -1
  94. package/dist/index.d.ts +9 -20
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +35 -32
  97. package/dist/index.js.map +1 -1
  98. package/dist/logging/logger.d.ts +4 -1
  99. package/dist/logging/logger.d.ts.map +1 -1
  100. package/dist/logging/logger.js +5 -2
  101. package/dist/logging/logger.js.map +1 -1
  102. package/dist/memory/gcService.d.ts.map +1 -1
  103. package/dist/memory/gcService.js +3 -5
  104. package/dist/memory/gcService.js.map +1 -1
  105. package/dist/memory/inMemorySessionStore.d.ts +2 -2
  106. package/dist/memory/inMemorySessionStore.js +2 -2
  107. package/dist/memory/inMemoryStorage.d.ts +4 -3
  108. package/dist/memory/inMemoryStorage.d.ts.map +1 -1
  109. package/dist/memory/inMemoryStorage.js +2 -2
  110. package/dist/memory/inMemoryStorage.js.map +1 -1
  111. package/dist/memory/nodeSqliteStorage.d.ts +17 -0
  112. package/dist/memory/nodeSqliteStorage.d.ts.map +1 -0
  113. package/dist/memory/nodeSqliteStorage.js +393 -0
  114. package/dist/memory/nodeSqliteStorage.js.map +1 -0
  115. package/dist/memory/projectManager.d.ts +10 -20
  116. package/dist/memory/projectManager.d.ts.map +1 -1
  117. package/dist/memory/projectManager.js +27 -41
  118. package/dist/memory/projectManager.js.map +1 -1
  119. package/dist/memory/roundStore.d.ts +76 -1
  120. package/dist/memory/roundStore.d.ts.map +1 -1
  121. package/dist/memory/roundStore.js.map +1 -1
  122. package/dist/memory/roundSummaryLookup.d.ts +26 -0
  123. package/dist/memory/roundSummaryLookup.d.ts.map +1 -0
  124. package/dist/memory/roundSummaryLookup.js +19 -0
  125. package/dist/memory/roundSummaryLookup.js.map +1 -0
  126. package/dist/memory/sessionStore.d.ts +19 -2
  127. package/dist/memory/sessionStore.d.ts.map +1 -1
  128. package/dist/memory/sessionStore.js.map +1 -1
  129. package/dist/memory/sourceValidation.d.ts +2 -13
  130. package/dist/memory/sourceValidation.d.ts.map +1 -1
  131. package/dist/memory/sourceValidation.js +5 -16
  132. package/dist/memory/sourceValidation.js.map +1 -1
  133. package/dist/memory/storageInterface.d.ts +26 -16
  134. package/dist/memory/storageInterface.d.ts.map +1 -1
  135. package/dist/memory/supersedeResolve.d.ts +39 -0
  136. package/dist/memory/supersedeResolve.d.ts.map +1 -0
  137. package/dist/memory/supersedeResolve.js +16 -0
  138. package/dist/memory/supersedeResolve.js.map +1 -0
  139. package/dist/memory/types.d.ts +3 -2
  140. package/dist/memory/types.d.ts.map +1 -1
  141. package/dist/memory/types.js +2 -1
  142. package/dist/memory/types.js.map +1 -1
  143. package/dist/role-pack/rolePackManager.d.ts +33 -24
  144. package/dist/role-pack/rolePackManager.d.ts.map +1 -1
  145. package/dist/role-pack/rolePackManager.js +48 -42
  146. package/dist/role-pack/rolePackManager.js.map +1 -1
  147. package/dist/role-pack/strategyKeys.d.ts +8 -0
  148. package/dist/role-pack/strategyKeys.d.ts.map +1 -1
  149. package/dist/role-pack/strategyKeys.js +10 -0
  150. package/dist/role-pack/strategyKeys.js.map +1 -1
  151. package/dist/role-pack/strategyResolver.d.ts +6 -1
  152. package/dist/role-pack/strategyResolver.d.ts.map +1 -1
  153. package/dist/role-pack/strategyResolver.js +20 -2
  154. package/dist/role-pack/strategyResolver.js.map +1 -1
  155. package/dist/role-pack/types.d.ts +5 -1
  156. package/dist/role-pack/types.d.ts.map +1 -1
  157. package/dist/security/pathGuard.d.ts +1 -1
  158. package/dist/security/pathGuard.d.ts.map +1 -1
  159. package/dist/security/pathGuard.js +1 -1
  160. package/dist/security/pathGuard.js.map +1 -1
  161. package/dist/utils/backgroundTask.d.ts +56 -34
  162. package/dist/utils/backgroundTask.d.ts.map +1 -1
  163. package/dist/utils/backgroundTask.js +130 -99
  164. package/dist/utils/backgroundTask.js.map +1 -1
  165. package/dist/utils/errors.d.ts +3 -2
  166. package/dist/utils/errors.d.ts.map +1 -1
  167. package/dist/utils/errors.js +3 -2
  168. package/dist/utils/errors.js.map +1 -1
  169. package/dist/utils/eventEmitter.d.ts +20 -22
  170. package/dist/utils/eventEmitter.d.ts.map +1 -1
  171. package/dist/utils/eventEmitter.js +0 -3
  172. package/dist/utils/eventEmitter.js.map +1 -1
  173. package/dist/utils/time.d.ts +3 -3
  174. package/dist/utils/time.d.ts.map +1 -1
  175. package/dist/utils/time.js +3 -3
  176. package/dist/utils/time.js.map +1 -1
  177. package/dist/utils/toError.d.ts +1 -1
  178. package/dist/utils/toError.js +1 -1
  179. package/dist/web-fetch/loginWall.d.ts +39 -0
  180. package/dist/web-fetch/loginWall.d.ts.map +1 -0
  181. package/dist/web-fetch/loginWall.js +78 -0
  182. package/dist/web-fetch/loginWall.js.map +1 -0
  183. package/dist/web-fetch/types.d.ts +8 -0
  184. package/dist/web-fetch/types.d.ts.map +1 -1
  185. package/dist/web-fetch/webFetchProvider.d.ts.map +1 -1
  186. package/dist/web-fetch/webFetchProvider.js +3 -1
  187. package/dist/web-fetch/webFetchProvider.js.map +1 -1
  188. package/dist/web-search/fetchWebSearchProvider.js +28 -23
  189. package/dist/web-search/fetchWebSearchProvider.js.map +1 -1
  190. package/docs/architecture/role-pack-authoring-guide.md +3 -1
  191. package/docs/architecture/role-pack-spec.md +2 -1
  192. package/docs/memora-api-reference.md +1349 -1276
  193. package/docs/memora-/345/215/207/347/272/247/346/214/207/345/215/227-4.x-/345/210/260-5.0.0.md +385 -0
  194. package/docs/memora-/346/216/245/345/205/245/346/214/207/345/215/227.md +87 -49
  195. package/docs/role-pack-/345/274/200/346/224/276/351/224/256/346/214/207/345/215/227.md +148 -148
  196. package/package.json +9 -4
  197. package/role-packs/README.md +41 -40
  198. package/role-packs/memora/345/212/251/346/211/213/rules.md +4 -4
  199. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/manifest.json +1 -0
  200. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/persona.md +46 -46
  201. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/rules.md +16 -16
  202. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/emotion-wave.md +46 -46
  203. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/foreshadow.md +29 -29
  204. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/outline-expand.md +73 -48
  205. 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 +445 -441
  206. package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-bible.md +62 -62
  207. 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 +1 -0
  208. package/dist/agent/managers/dedupManager.d.ts +0 -85
  209. package/dist/agent/managers/dedupManager.d.ts.map +0 -1
  210. package/dist/agent/managers/dedupManager.js +0 -248
  211. package/dist/agent/managers/dedupManager.js.map +0 -1
  212. package/dist/agent/managers/goalConsistencyChecker.d.ts +0 -42
  213. package/dist/agent/managers/goalConsistencyChecker.d.ts.map +0 -1
  214. package/dist/agent/managers/goalConsistencyChecker.js +0 -178
  215. package/dist/agent/managers/goalConsistencyChecker.js.map +0 -1
  216. package/dist/agent/managers/llmJudgeHelper.d.ts +0 -21
  217. package/dist/agent/managers/llmJudgeHelper.d.ts.map +0 -1
  218. package/dist/agent/managers/llmJudgeHelper.js +0 -32
  219. package/dist/agent/managers/llmJudgeHelper.js.map +0 -1
  220. package/dist/agent/managers/memoryAdvisor.d.ts +0 -105
  221. package/dist/agent/managers/memoryAdvisor.d.ts.map +0 -1
  222. package/dist/agent/managers/memoryAdvisor.js +0 -322
  223. package/dist/agent/managers/memoryAdvisor.js.map +0 -1
  224. package/dist/agent/managers/memoryGovernance.d.ts +0 -24
  225. package/dist/agent/managers/memoryGovernance.d.ts.map +0 -1
  226. package/dist/agent/managers/memoryGovernance.js +0 -46
  227. package/dist/agent/managers/memoryGovernance.js.map +0 -1
  228. package/dist/memory/governance.d.ts +0 -17
  229. package/dist/memory/governance.d.ts.map +0 -1
  230. package/dist/memory/governance.js +0 -17
  231. package/dist/memory/governance.js.map +0 -1
  232. package/dist/memory/projectRegistry.d.ts +0 -47
  233. package/dist/memory/projectRegistry.d.ts.map +0 -1
  234. package/dist/memory/projectRegistry.js +0 -151
  235. package/dist/memory/projectRegistry.js.map +0 -1
  236. package/dist/utils/recallDefaults.d.ts +0 -16
  237. package/dist/utils/recallDefaults.d.ts.map +0 -1
  238. package/dist/utils/recallDefaults.js +0 -16
  239. package/dist/utils/recallDefaults.js.map +0 -1
@@ -62,9 +62,9 @@ function parseCompressTarget(raw) {
62
62
  * - 中断/暂停(interruptQueue + _handleInterrupt + interject 追加用户消息、step 边界 yield paused)
63
63
  * 与 sessionStateMachine 的「纯三态 + pendingPause」语义不同源(后者不承载 interject 追加与
64
64
  * step 边界产出)——不并入,维持 loop 自持。
65
- * - 消息写入口(append* / clean* + 观测埋点 recordBudget/recordOccupancy/refreshOccupancyDialogue)同判
65
+ * - 消息写入口(append* / clean* + 观测埋点 recordBudget/recordOccupancy/refreshOccupancyFromMessages)同判
66
66
  * 不拆:写入口持有 messages / currentRoundId / executionTempSystem / offloadDir 四项 loop 私有状态,
67
- * 并回调 refreshOccupancyDialogue(**写消息→触发观测,跨域**),不满足 progressive-refactor-rules
67
+ * 并回调 refreshOccupancyFromMessages(**写消息→触发观测,跨域**),不满足 progressive-refactor-rules
68
68
  * §2.2 模式 B「职责正交(不共享状态 / 不互相调用)」前提;模式 A(领域容器提取)只搬字段不搬逻辑,
69
69
  * 收益不足以覆盖改点面。保留监控,待自然生长触发。
70
70
  * - 搜索生命周期:软上限/硬上限混沌形同构但为**同一意图的三级级联**——
@@ -81,6 +81,11 @@ function parseCompressTarget(raw) {
81
81
  const INTEL_INTRO = '[情报区 · 仅供你私有查看并作为后续作答的参考]。此区内容不要向用户复述或写进项目文档。' +
82
82
  '当你从大文本/工具结果获取到新的关键信息时,可用一句简短状态反馈用户进展(如:原来是这样… / 掌握了关键信息 / ' +
83
83
  '有一个问题… / 基本收集完毕),但不要在反馈中复述笔记/原文细节。';
84
+ /** 「用户未回答该提问」占位 tool 结果(单一载体):
85
+ * 消费点二——`cancelAsk`(整批跳过/超时未答)与 `answerQuestion`(**部分**题缺答案)。
86
+ * 两处共一常量而非各写字面量:改一处漏一处会让「未回答」这一事实在一条路径上失真,
87
+ * 而该事实直接进 LLM 决策上下文(静默失效、无报错)。 */
88
+ const ASK_ABORTED_RESULT = '[ASK_ABORTED] 用户未回答该提问';
84
89
  export class AgentLoop {
85
90
  opts;
86
91
  messages = [];
@@ -229,7 +234,7 @@ export class AgentLoop {
229
234
  toolExecutedThisTurn = false;
230
235
  /** 当前轮次 ID(processUserInput 入口分配一次,各 iteration 共享),用于溯源式摘要 */
231
236
  currentRoundId = '';
232
- /** 是否正处于自主工具步执行中(内核自用:resumeExecution 预判短路 / canContinueWithoutInput;宿主零消费) */
237
+ /** 是否正处于自主工具步执行中(内核自用:resumeExecution 预判短路;宿主零消费) */
233
238
  inAutonomousStep = false;
234
239
  /* 策略类字段(toolCallsBlocked/toolStepLimit/errorHandling/providerRouting 等)定义在
235
240
  * 单一 L2RuntimeStrategy 对象(见上方 strategy),读取统一走 this.strategy.<field> */
@@ -433,8 +438,9 @@ export class AgentLoop {
433
438
  }
434
439
  // 单工具执行器:注入 loop 稳定能力窄面,strategy 经闭包读最新(setStrategy 动态生效)
435
440
  this.toolRunner = new ToolRunner({
436
- // 原生 outcome 回调逐跳透传:ToolRunner → opts.toolExecutor → toolExecutor.execute
437
- execute: (name, args, emitOutcome) => this.opts.toolExecutor(name, args, emitOutcome),
441
+ // 原生 outcome 回调逐跳透传:ToolRunner → opts.toolExecutor → toolExecutor.execute;
442
+ // toolCallId 同步透传(web_search 证据配对通道,见 ToolRunnerDeps.execute 第 4 参)
443
+ execute: (name, args, emitOutcome, toolCallId) => this.opts.toolExecutor(name, args, emitOutcome, toolCallId),
438
444
  builtinTools: opts.builtinTools,
439
445
  preExecutionCheck: opts.preExecutionCheck,
440
446
  onToolExecuted: opts.onToolExecuted,
@@ -571,7 +577,7 @@ export class AgentLoop {
571
577
  }
572
578
  }
573
579
  }
574
- /** 是否正处于自主工具步执行中(消费者 = agent.ts 预判短路/canContinueWithoutInput,内核内部) */
580
+ /** 是否正处于自主工具步执行中(消费者 = agent.ts 预判短路,内核内部) */
575
581
  get isInAutonomousStep() {
576
582
  return this.inAutonomousStep;
577
583
  }
@@ -1487,21 +1493,33 @@ export class AgentLoop {
1487
1493
  for (const q of questions) {
1488
1494
  yield { type: 'question_pending', questions: [q] };
1489
1495
  }
1496
+ // 挂起本身不携带来源:提问的原因/来源已由 onPendingQuestion → requestPause(reason,'agent')
1497
+ // 注册进状态机 pendingPause(与用户暂停同一真源、同一入口),consumeExecutionStream 从 pending 取。
1490
1498
  yield { type: 'paused' };
1491
1499
  return 'paused';
1492
1500
  }
1493
1501
  /**
1494
1502
  * 回答在途提问(宿主在用户作答后调用,随后 continueAfterPause() 续跑):
1495
1503
  * 答案以 ask_user 工具的 tool result 回填(<tool_result> 包裹防注入,与 assistant.tool_calls 配对)。
1496
- * answers 与提问按序一对一;不足时复用最后一条/空串兜底。返回 false 表示无在途提问。
1504
+ * answers 与提问按序一对一;**不足的部分落 `[ASK_ABORTED]`「未回答」占位,不复用别题答案**。
1505
+ * 返回 false 表示无在途提问。
1497
1506
  */
1498
1507
  answerQuestion(answers) {
1499
1508
  if (!this.pendingAsk)
1500
1509
  return false;
1501
1510
  const { toolCallIds } = this.pendingAsk;
1502
1511
  toolCallIds.forEach((id, i) => {
1503
- const answer = answers[i] ?? answers[answers.length - 1] ?? '';
1504
- this.appendToolMessage(wrapToolResult(ASK_USER_TOOL.name, `[ASK_ANSWER] 用户回答:${answer}`), id);
1512
+ const answer = answers[i];
1513
+ // 逐条按序一对一;**缺答案不得拿别题的答案顶替**。
1514
+ // 旧兜底 `answers[answers.length - 1]`(不足时复用最后一条)会把同一条文本复制到剩余每题
1515
+ // ⇒ 用户只答了一道,LLM 却收到「每题都有答案」——制造假事实、无报错、直接污染决策上下文。
1516
+ // 多题场景下「底部只有一条文本的输入框」必然触发这条兜底(宿主已把回答统一走本入口)。
1517
+ // 现改:缺答案落 [ASK_ABORTED] 占位(与 cancelAsk 同文案同源),把「未答」这一事实
1518
+ // 显式告知模型;配对不变量不变——每条 toolCallId 恒有 tool 消息,不撞兼容端 400。
1519
+ const result = answer === undefined || answer.trim() === ''
1520
+ ? ASK_ABORTED_RESULT
1521
+ : `[ASK_ANSWER] 用户回答:${answer}`;
1522
+ this.appendToolMessage(wrapToolResult(ASK_USER_TOOL.name, result), id);
1505
1523
  });
1506
1524
  // 已作答提问转存快照(runResume 落盘交互输入时随回答持久化),pendingAsk 即清空
1507
1525
  this.lastAnsweredAsk = this.pendingAsk.questions;
@@ -1534,7 +1552,7 @@ export class AgentLoop {
1534
1552
  return;
1535
1553
  const { toolCallIds } = this.pendingAsk;
1536
1554
  for (const id of toolCallIds) {
1537
- this.appendToolMessage(wrapToolResult(ASK_USER_TOOL.name, '[ASK_ABORTED] 用户未回答该提问'), id);
1555
+ this.appendToolMessage(wrapToolResult(ASK_USER_TOOL.name, ASK_ABORTED_RESULT), id);
1538
1556
  }
1539
1557
  this.lastAnsweredAsk = this.pendingAsk.questions;
1540
1558
  this.pendingAsk = undefined;
@@ -1649,12 +1667,16 @@ export class AgentLoop {
1649
1667
  // 运行时护栏阈值组装(真源分配见 guardRail.GuardThresholds):
1650
1668
  // 写环=GUARD_THRESHOLDS.writeLoop,读闸=GUARD_THRESHOLDS.readFailed(独立静态真源,
1651
1669
  // 不再与 duplicateCallInterceptor 阈值复用——语义同宽,解开隐藏耦合);
1652
- // 提问=strategy.askLimit、搜索=LOOP_CONSTANTS.MAX_WEB_SEARCH_CALLS(动态真源,不入静态默认)。
1670
+ // 提问=strategy.askLimit、搜索=strategy.webSearchLimit(动态真源=角色包策略,缺省值
1671
+ // 定义在 DEFAULT_BEHAVIOR_STRATEGY.global.webSearchLimit)。
1653
1672
  const guardThresholds = {
1654
1673
  writeLoop: GUARD_THRESHOLDS.writeLoop,
1655
1674
  readFailed: GUARD_THRESHOLDS.readFailed,
1656
1675
  askLimit: this.strategy.askLimit,
1657
- maxWebSearch: LOOP_CONSTANTS.MAX_WEB_SEARCH_CALLS,
1676
+ // webSearchLimit 缺省 6 由 resolveWebSearchLimit 恒给(GUARD-SEARCH-1:
1677
+ // 硬上限 = 角色包策略键 global.webSearchLimit),与下方 SEARCH_LIMIT 文案同源取
1678
+ // strategy,二者禁分头取值(否则文案数字说谎)。
1679
+ maxWebSearch: this.strategy.webSearchLimit,
1658
1680
  };
1659
1681
  for (const tc of toolCalls) {
1660
1682
  this.metrics.toolCallCount++;
@@ -1700,7 +1722,7 @@ export class AgentLoop {
1700
1722
  }
1701
1723
  if (!this.searchDisabledHintInjected) {
1702
1724
  this.searchDisabledHintInjected = true;
1703
- this.appendSystemMessage(`[SEARCH_LIMIT] 联网搜索已达 ${LOOP_CONSTANTS.MAX_WEB_SEARCH_CALLS} 次上限,视为未检索到更多相关内容。` +
1725
+ this.appendSystemMessage(`[SEARCH_LIMIT] 联网搜索已达 ${guardThresholds.maxWebSearch} 次上限,视为未检索到更多相关内容。` +
1704
1726
  `请停止调用 web_search,直接基于已有信息继续下一步或作答。`, { executionTemp: true });
1705
1727
  }
1706
1728
  toolExecs.push({
@@ -2524,24 +2546,41 @@ export class AgentLoop {
2524
2546
  this.lastOccupancy = occupancy;
2525
2547
  }
2526
2548
  /**
2527
- * 对话占用实时刷新(预算可视化 · 输入区常驻指示器)。
2549
+ * 动态注入占用(`messages[0]` 之外的 system 消息)token 估算。
2550
+ *
2551
+ * 单一真理源 = `this.messages`:最近对话块 / 情报区 / 任务表 / 软上限与压力提示 /
2552
+ * 搜索收敛提示 / 反思与自审查提示等运行时注入,皆以 system 消息落在 index > 0。
2553
+ * 占用快照把它计入「动态注入」段(P1-1 修复)——此前该段未计入任何分段、被静默算进 free,
2554
+ * 导致指示器偏乐观。与对话段共用同一估算器(SSOT)。
2555
+ */
2556
+ getDynamicSystemTokens() {
2557
+ const dynamicSystem = this.messages.filter((m, i) => i > 0 && m.role === 'system');
2558
+ return dynamicSystem.length > 0 ? this.estimateTokens(dynamicSystem) : 0;
2559
+ }
2560
+ /**
2561
+ * 随工作记忆实时刷新占用快照中「由 messages 派生」的两段(预算可视化 · 输入区常驻指示器)。
2528
2562
  *
2529
2563
  * occupancy 主体在 prepare 期 record(见 contextPreparer),但 prepare 发生在 assistant
2530
- * 生成前,故快照的 dialogue 段天然不含本轮 assistant、且单轮对话结束后不再刷新。
2531
- * 本方法在 user 消息落库 / assistant 落盘后以最新 this.messages 重算 dialogue 段:
2532
- * dialogueCount = user 消息数(计数标准:一个问答闭环=1,残缺回答如实记录)
2533
- * dialogueTokens = 全量 user+assistant 估算(容量诚实统计问答闭环总和)
2534
- * 其余段(rolePackBase/memory/inputAnchor/outputReserve)沿用 prepare 快照,不重算
2564
+ * 生成前,且动态 system 注入在 prepare 之后陆续发生,故两段须在写入口以最新 this.messages 重算:
2565
+ * dialogue = 全量 user+assistant 估算(容量诚实统计问答闭环总和)
2566
+ * dynamicSystem = `messages[0]` 之外的 system 消息估算(见 getDynamicSystemTokens)
2567
+ * 其余段(rolePackBase/inputAnchor/outputReserve)沿用 prepare 快照,不重算
2535
2568
  * (守 SSOT:宿主只透传,内核单一真相源)。
2536
2569
  * 幂等:无快照时 no-op(如首轮 prepare 尚未发生)。
2537
2570
  */
2538
- refreshOccupancyDialogue() {
2571
+ refreshOccupancyFromMessages() {
2539
2572
  if (!this.lastOccupancy)
2540
2573
  return;
2541
2574
  const conv = this.getConversationMessages();
2542
2575
  const dialogueCount = conv.filter((m) => m.role === 'user').length;
2543
2576
  const dialogueTokens = this.estimateTokens(conv);
2544
- this.lastOccupancy = { ...this.lastOccupancy, dialogueCount, dialogueTokens };
2577
+ const dynamicSystemTokens = this.getDynamicSystemTokens();
2578
+ this.lastOccupancy = {
2579
+ ...this.lastOccupancy,
2580
+ dialogueCount,
2581
+ dialogueTokens,
2582
+ dynamicSystemTokens,
2583
+ };
2545
2584
  }
2546
2585
  /**
2547
2586
  * 写入当前激活角色包底盘占用(system prompt 总体 token)。
@@ -2693,6 +2732,65 @@ export class AgentLoop {
2693
2732
  getCurrentRoundId() {
2694
2733
  return this.currentRoundId;
2695
2734
  }
2735
+ /**
2736
+ * 记录 web_search 执行证据(悬案取证轨写点,SEARCH-QUALITY-1 P1-1 观测留痕)
2737
+ *
2738
+ * 驱动方 = 装配层接线 `toolExec.setSearchResultObserver`(toolExecutor 观察到的执行事实
2739
+ * 镜像过来)。ts/meetingRound 构造归 loop(isMeetingRound 私有,与其余三个写点同构);
2740
+ * 载荷透传不加工(toolCallId/query/endpoint/count/titles 就是 toolExecutor 看到的原样)。
2741
+ * 回答「搜不准」争议的第一现场:结果页之外,query 本身 + 端点 + 结果面落盘可裁。
2742
+ * 护栏硬拦的搜索不走此路(拦截即未执行,无结果面;拦截事实已在 tool 消息可见)。
2743
+ */
2744
+ recordWebSearchEvidence(info) {
2745
+ this.appendRoundEvidence?.({
2746
+ type: 'web_search_result',
2747
+ ts: nowIso(),
2748
+ meetingRound: this.isMeetingRound(),
2749
+ payload: {
2750
+ // toolCallId 透传(宿主渲染据此唯一配对证据回工具行);缺省 = 未透传,宿主回退 query 启发式
2751
+ toolCallId: info.toolCallId,
2752
+ query: info.query,
2753
+ endpoint: info.endpoint,
2754
+ count: info.count,
2755
+ titles: info.titles,
2756
+ },
2757
+ });
2758
+ }
2759
+ /**
2760
+ * 记录 search_memories 命中明细证据(MEM-EVENT-3:实时事件的落盘副本)
2761
+ *
2762
+ * 驱动方 = 装配层同一处 `setOnMemoryRecalled` 接线(事件广播 + 证据落盘同源一次发射)——
2763
+ * 不新造第二个观测通道,也不在宿主侧二次拼装。
2764
+ * 动机:`memoryRecalled` 是实时专属事件(不进 processEvents),宿主重放历史轮无数据源
2765
+ * ⇒ 命中明细整块消失,「这轮参考了哪几条记忆」回看时不可考。落盘副本让重放可重建。
2766
+ *
2767
+ * 载荷透传不加工(id/name/source 就是搜索结果的现成三字段);无命中不落盘——内核
2768
+ * `memoryRecalled` 本就只在有命中时发射,证据轨不为「查了但没查到」另造路径。
2769
+ */
2770
+ recordMemoryHitsEvidence(info) {
2771
+ // 防御:无明细 = 4.0.x 形态的载荷(老调用方/直调),没有可重建的内容,不写空证据
2772
+ if (!info.hits || info.hits.length === 0)
2773
+ return;
2774
+ this.appendRoundEvidence?.({
2775
+ type: 'memory_hits',
2776
+ ts: nowIso(),
2777
+ meetingRound: this.isMeetingRound(),
2778
+ payload: {
2779
+ // toolCallId 可选透传:宿主重放据此唯一配对工具行;缺省走轮次级兜底落位
2780
+ ...(info.toolCallId ? { toolCallId: info.toolCallId } : {}),
2781
+ query: info.query,
2782
+ count: info.count,
2783
+ // contentPreview 直接赋值:5.0.0 起实时载荷恒携带;undefined 经 JSON 序列化自然消失
2784
+ //(与可选透传语义一致),重放轨与实时轨同构,宿主缺失时回退 name。
2785
+ hits: info.hits.map((h) => ({
2786
+ id: h.id,
2787
+ name: h.name,
2788
+ source: h.source,
2789
+ contentPreview: h.contentPreview,
2790
+ })),
2791
+ },
2792
+ });
2793
+ }
2696
2794
  /** 设置当前轮次 ID(appendUser 在 processUserInput 之前调用,故需提前生成供 RoundSummaryGenerator 使用) */
2697
2795
  setCurrentRoundId(roundId) {
2698
2796
  this.currentRoundId = roundId;
@@ -2792,10 +2890,10 @@ export class AgentLoop {
2792
2890
  content: `<user_input>${content}</user_input>`,
2793
2891
  roundId: this.currentRoundId,
2794
2892
  });
2795
- // 用户输入即刷新占用条数(问答闭环 +1,实时反映到输入区圆环)
2796
- this.refreshOccupancyDialogue();
2893
+ // 用户输入即刷新占用(问答闭环 +1,实时反映到输入区圆环)
2894
+ this.refreshOccupancyFromMessages();
2797
2895
  }
2798
- /** 追加一条 system 消息(技能注入、召回、任务表、自审查提示等通用注入通道) */
2896
+ /** 追加一条 system 消息(技能注入、最近对话块、任务表、自审查提示等通用注入通道) */
2799
2897
  appendSystemMessage(content, opts = {}) {
2800
2898
  const msg = { role: 'system', content };
2801
2899
  this.messages.push(msg);
@@ -2803,6 +2901,9 @@ export class AgentLoop {
2803
2901
  if (opts.executionTemp) {
2804
2902
  this.executionTempSystem.add(msg);
2805
2903
  }
2904
+ // system 注入即时刷新「动态注入」占用段(P1-1):否则指示器只在 user/assistant 写入时更新,
2905
+ // 动态注入占比被静默漏计、free 偏大。无快照时 no-op(首轮 prepare 前)。
2906
+ this.refreshOccupancyFromMessages();
2806
2907
  }
2807
2908
  /** 会议轮判据(裁决证据用,操作化单点):任务表 active 任务项声明 rolePack = 会议逐项切换生效中 */
2808
2909
  isMeetingRound() {
@@ -2812,7 +2913,7 @@ export class AgentLoop {
2812
2913
  appendAssistantText(content) {
2813
2914
  this.messages.push({ role: 'assistant', content, roundId: this.currentRoundId });
2814
2915
  // assistant 落盘(含残缺/中止回复)后补算对话容量,使圆环即时含本轮回答
2815
- this.refreshOccupancyDialogue();
2916
+ this.refreshOccupancyFromMessages();
2816
2917
  }
2817
2918
  /** 追加一条带 toolCalls 的 assistant 消息(executeToolCalls 前导);附当前轮次 roundId */
2818
2919
  appendAssistantToolCall(fullContent, toolCalls) {
@@ -2903,6 +3004,8 @@ export class AgentLoop {
2903
3004
  else {
2904
3005
  this.messages.push({ role: 'system', content });
2905
3006
  }
3007
+ // 情报区属「动态注入」段,刷新占用使指示器即时含其占用(P1-1)
3008
+ this.refreshOccupancyFromMessages();
2906
3009
  }
2907
3010
  /**
2908
3011
  * 指定 toolCallId 的 tool 消息是否仍在上下文中且内容未被改动 —— 「结果仍在上下文」的共用判据。