dominds 1.2.5 → 1.2.7

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 (149) hide show
  1. package/dist/agent-priming.js +2051 -0
  2. package/dist/apps/app-lock-file.js +228 -0
  3. package/dist/apps/assigned-port.js +124 -0
  4. package/dist/apps/enabled-apps.js +472 -7
  5. package/dist/apps/manifest.js +37 -0
  6. package/dist/apps/override-paths.js +19 -6
  7. package/dist/apps/problems.js +43 -0
  8. package/dist/apps/resolution-file.js +370 -0
  9. package/dist/apps/runtime.js +5 -17
  10. package/dist/apps/teammates.js +102 -1
  11. package/dist/cli/disable.js +10 -6
  12. package/dist/cli/enable.js +21 -19
  13. package/dist/cli/install.js +40 -18
  14. package/dist/cli/uninstall.js +6 -6
  15. package/dist/cli/update.js +38 -13
  16. package/dist/dialog.js +5 -0
  17. package/dist/docs/app-constitution.md +85 -18
  18. package/dist/docs/app-constitution.zh.md +86 -21
  19. package/dist/docs/dialog-system.md +1 -1
  20. package/dist/docs/dialog-system.zh.md +1 -1
  21. package/dist/docs/dominds-agent-priming.md +218 -0
  22. package/dist/docs/dominds-agent-priming.zh.md +196 -0
  23. package/dist/docs/drive-logic-context-refactor-plan.zh.md +338 -0
  24. package/dist/docs/keep-going.md +176 -0
  25. package/dist/docs/keep-going.zh.md +162 -0
  26. package/dist/docs/showing-by-doing.md +208 -0
  27. package/dist/docs/showing-by-doing.zh.md +177 -0
  28. package/dist/docs/team-mgmt-toolset.md +482 -0
  29. package/dist/docs/team-mgmt-toolset.zh.md +426 -0
  30. package/dist/llm/defaults.yaml +1 -1
  31. package/dist/llm/driver.js +4093 -0
  32. package/dist/llm/kernel-driver/drive.js +5 -2
  33. package/dist/llm/kernel-driver/flow.js +3 -0
  34. package/dist/minds/promptdocs.js +263 -0
  35. package/dist/problems.js +67 -16
  36. package/dist/server/api-routes.js +333 -0
  37. package/dist/server/prompts-routes.js +545 -0
  38. package/dist/server/server-core.js +4 -0
  39. package/dist/server/websocket-handler.js +17 -0
  40. package/dist/shared/team-mgmt-manual.js +120 -0
  41. package/dist/shared/types/prompts.js +2 -0
  42. package/dist/shared/types/tellask.js +8 -0
  43. package/dist/showing-by-doing.js +1091 -0
  44. package/dist/snippets/README.en.md +3 -0
  45. package/dist/snippets/README.md +4 -0
  46. package/dist/static/assets/{_basePickBy-CF9r08iy.js → _basePickBy-BMCtwrV7.js} +3 -3
  47. package/dist/static/assets/{_basePickBy-CF9r08iy.js.map → _basePickBy-BMCtwrV7.js.map} +1 -1
  48. package/dist/static/assets/{_baseUniq-CxKv0cd4.js → _baseUniq-BuyCgJiA.js} +2 -2
  49. package/dist/static/assets/{_baseUniq-CxKv0cd4.js.map → _baseUniq-BuyCgJiA.js.map} +1 -1
  50. package/dist/static/assets/{arc-C9JyvnlB.js → arc-BDuN8lwA.js} +2 -2
  51. package/dist/static/assets/{arc-C9JyvnlB.js.map → arc-BDuN8lwA.js.map} +1 -1
  52. package/dist/static/assets/{architectureDiagram-VXUJARFQ-CpcUgjHf.js → architectureDiagram-VXUJARFQ-C-ekqGAD.js} +7 -7
  53. package/dist/static/assets/{architectureDiagram-VXUJARFQ-CpcUgjHf.js.map → architectureDiagram-VXUJARFQ-C-ekqGAD.js.map} +1 -1
  54. package/dist/static/assets/{blockDiagram-VD42YOAC-BA9vtmm7.js → blockDiagram-VD42YOAC-CgQiNuuQ.js} +7 -7
  55. package/dist/static/assets/{blockDiagram-VD42YOAC-BA9vtmm7.js.map → blockDiagram-VD42YOAC-CgQiNuuQ.js.map} +1 -1
  56. package/dist/static/assets/{c4Diagram-YG6GDRKO-D49MGNdF.js → c4Diagram-YG6GDRKO-DONC39q-.js} +3 -3
  57. package/dist/static/assets/{c4Diagram-YG6GDRKO-D49MGNdF.js.map → c4Diagram-YG6GDRKO-DONC39q-.js.map} +1 -1
  58. package/dist/static/assets/{channel-B4KzL0Kg.js → channel-CJTFwXIG.js} +2 -2
  59. package/dist/static/assets/{channel-B4KzL0Kg.js.map → channel-CJTFwXIG.js.map} +1 -1
  60. package/dist/static/assets/{chunk-4BX2VUAB-0F-1ayl0.js → chunk-4BX2VUAB-NaIy4uLJ.js} +2 -2
  61. package/dist/static/assets/{chunk-4BX2VUAB-0F-1ayl0.js.map → chunk-4BX2VUAB-NaIy4uLJ.js.map} +1 -1
  62. package/dist/static/assets/{chunk-55IACEB6-Dnl2HDTZ.js → chunk-55IACEB6-JUKI_Ayx.js} +2 -2
  63. package/dist/static/assets/{chunk-55IACEB6-Dnl2HDTZ.js.map → chunk-55IACEB6-JUKI_Ayx.js.map} +1 -1
  64. package/dist/static/assets/{chunk-B4BG7PRW-Bhx5RbkQ.js → chunk-B4BG7PRW-dIswFJDn.js} +5 -5
  65. package/dist/static/assets/{chunk-B4BG7PRW-Bhx5RbkQ.js.map → chunk-B4BG7PRW-dIswFJDn.js.map} +1 -1
  66. package/dist/static/assets/{chunk-DI55MBZ5-EYd1wL3E.js → chunk-DI55MBZ5-DU2b_N30.js} +4 -4
  67. package/dist/static/assets/{chunk-DI55MBZ5-EYd1wL3E.js.map → chunk-DI55MBZ5-DU2b_N30.js.map} +1 -1
  68. package/dist/static/assets/{chunk-FMBD7UC4-DAjkhhUU.js → chunk-FMBD7UC4-BgExcScw.js} +2 -2
  69. package/dist/static/assets/{chunk-FMBD7UC4-DAjkhhUU.js.map → chunk-FMBD7UC4-BgExcScw.js.map} +1 -1
  70. package/dist/static/assets/{chunk-QN33PNHL-CK6TY7IE.js → chunk-QN33PNHL-bitxyqh7.js} +2 -2
  71. package/dist/static/assets/{chunk-QN33PNHL-CK6TY7IE.js.map → chunk-QN33PNHL-bitxyqh7.js.map} +1 -1
  72. package/dist/static/assets/{chunk-QZHKN3VN-CketngiE.js → chunk-QZHKN3VN-Cor8u7DT.js} +2 -2
  73. package/dist/static/assets/{chunk-QZHKN3VN-CketngiE.js.map → chunk-QZHKN3VN-Cor8u7DT.js.map} +1 -1
  74. package/dist/static/assets/{chunk-TZMSLE5B-Bcuvqo45.js → chunk-TZMSLE5B-Aceoxav_.js} +2 -2
  75. package/dist/static/assets/{chunk-TZMSLE5B-Bcuvqo45.js.map → chunk-TZMSLE5B-Aceoxav_.js.map} +1 -1
  76. package/dist/static/assets/{classDiagram-2ON5EDUG-CaP4T3r4.js → classDiagram-2ON5EDUG-D1Q6a8Hg.js} +6 -6
  77. package/dist/static/assets/{classDiagram-2ON5EDUG-CaP4T3r4.js.map → classDiagram-2ON5EDUG-D1Q6a8Hg.js.map} +1 -1
  78. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-CaP4T3r4.js → classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js} +6 -6
  79. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-CaP4T3r4.js.map → classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js.map} +1 -1
  80. package/dist/static/assets/{clone-C-JULvnG.js → clone-MlWbv1V0.js} +2 -2
  81. package/dist/static/assets/{clone-C-JULvnG.js.map → clone-MlWbv1V0.js.map} +1 -1
  82. package/dist/static/assets/{cose-bilkent-S5V4N54A-vXCmi_eC.js → cose-bilkent-S5V4N54A-DWPCXSrn.js} +2 -2
  83. package/dist/static/assets/{cose-bilkent-S5V4N54A-vXCmi_eC.js.map → cose-bilkent-S5V4N54A-DWPCXSrn.js.map} +1 -1
  84. package/dist/static/assets/{dagre-6UL2VRFP-bhGzX6kO.js → dagre-6UL2VRFP-C8ptQ9V3.js} +7 -7
  85. package/dist/static/assets/{dagre-6UL2VRFP-bhGzX6kO.js.map → dagre-6UL2VRFP-C8ptQ9V3.js.map} +1 -1
  86. package/dist/static/assets/{diagram-PSM6KHXK-BUKfmfGk.js → diagram-PSM6KHXK-Bgf1FqkE.js} +8 -8
  87. package/dist/static/assets/{diagram-PSM6KHXK-BUKfmfGk.js.map → diagram-PSM6KHXK-Bgf1FqkE.js.map} +1 -1
  88. package/dist/static/assets/{diagram-QEK2KX5R-DYlq3uFq.js → diagram-QEK2KX5R-BZ5xzofU.js} +7 -7
  89. package/dist/static/assets/{diagram-QEK2KX5R-DYlq3uFq.js.map → diagram-QEK2KX5R-BZ5xzofU.js.map} +1 -1
  90. package/dist/static/assets/{diagram-S2PKOQOG-CjxkLHWG.js → diagram-S2PKOQOG-Dwp47T9I.js} +7 -7
  91. package/dist/static/assets/{diagram-S2PKOQOG-CjxkLHWG.js.map → diagram-S2PKOQOG-Dwp47T9I.js.map} +1 -1
  92. package/dist/static/assets/{erDiagram-Q2GNP2WA-S3hR85On.js → erDiagram-Q2GNP2WA-Cx4weIHl.js} +5 -5
  93. package/dist/static/assets/{erDiagram-Q2GNP2WA-S3hR85On.js.map → erDiagram-Q2GNP2WA-Cx4weIHl.js.map} +1 -1
  94. package/dist/static/assets/{flowDiagram-NV44I4VS-aBmNMuQ0.js → flowDiagram-NV44I4VS-vNUuIeRk.js} +6 -6
  95. package/dist/static/assets/{flowDiagram-NV44I4VS-aBmNMuQ0.js.map → flowDiagram-NV44I4VS-vNUuIeRk.js.map} +1 -1
  96. package/dist/static/assets/{ganttDiagram-JELNMOA3-DJxXaiW1.js → ganttDiagram-JELNMOA3-BEfozJAr.js} +3 -3
  97. package/dist/static/assets/{ganttDiagram-JELNMOA3-DJxXaiW1.js.map → ganttDiagram-JELNMOA3-BEfozJAr.js.map} +1 -1
  98. package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-DEOBCM0G.js → gitGraphDiagram-V2S2FVAM-eHxwc3d9.js} +8 -8
  99. package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-DEOBCM0G.js.map → gitGraphDiagram-V2S2FVAM-eHxwc3d9.js.map} +1 -1
  100. package/dist/static/assets/{graph-DwrKSIE7.js → graph-C6a6uAok.js} +3 -3
  101. package/dist/static/assets/{graph-DwrKSIE7.js.map → graph-C6a6uAok.js.map} +1 -1
  102. package/dist/static/assets/{index-HWTRvE2k.js → index-D3TQbAKh.js} +383 -59
  103. package/dist/static/assets/index-D3TQbAKh.js.map +1 -0
  104. package/dist/static/assets/{infoDiagram-HS3SLOUP-BH9kVuYd.js → infoDiagram-HS3SLOUP-CX0NiId3.js} +6 -6
  105. package/dist/static/assets/{infoDiagram-HS3SLOUP-BH9kVuYd.js.map → infoDiagram-HS3SLOUP-CX0NiId3.js.map} +1 -1
  106. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-Dap7AcjR.js → journeyDiagram-XKPGCS4Q-C1IepPZ-.js} +5 -5
  107. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-Dap7AcjR.js.map → journeyDiagram-XKPGCS4Q-C1IepPZ-.js.map} +1 -1
  108. package/dist/static/assets/{kanban-definition-3W4ZIXB7-4NOl8MEj.js → kanban-definition-3W4ZIXB7-uMNX4Z1W.js} +3 -3
  109. package/dist/static/assets/{kanban-definition-3W4ZIXB7-4NOl8MEj.js.map → kanban-definition-3W4ZIXB7-uMNX4Z1W.js.map} +1 -1
  110. package/dist/static/assets/{layout-D6uIxu1E.js → layout-CpE3kk5z.js} +5 -5
  111. package/dist/static/assets/{layout-D6uIxu1E.js.map → layout-CpE3kk5z.js.map} +1 -1
  112. package/dist/static/assets/{linear-CvBOGQA2.js → linear-DV8laXr9.js} +2 -2
  113. package/dist/static/assets/{linear-CvBOGQA2.js.map → linear-DV8laXr9.js.map} +1 -1
  114. package/dist/static/assets/{mindmap-definition-VGOIOE7T-ugsrLNY5.js → mindmap-definition-VGOIOE7T-CKjgVM9S.js} +4 -4
  115. package/dist/static/assets/{mindmap-definition-VGOIOE7T-ugsrLNY5.js.map → mindmap-definition-VGOIOE7T-CKjgVM9S.js.map} +1 -1
  116. package/dist/static/assets/{pieDiagram-ADFJNKIX-CdVZjM8g.js → pieDiagram-ADFJNKIX-BBonlNyT.js} +8 -8
  117. package/dist/static/assets/{pieDiagram-ADFJNKIX-CdVZjM8g.js.map → pieDiagram-ADFJNKIX-BBonlNyT.js.map} +1 -1
  118. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-A6m5lZKd.js → quadrantDiagram-AYHSOK5B-BTI8HbBu.js} +3 -3
  119. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-A6m5lZKd.js.map → quadrantDiagram-AYHSOK5B-BTI8HbBu.js.map} +1 -1
  120. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-Cac3zSJH.js → requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js} +4 -4
  121. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-Cac3zSJH.js.map → requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js.map} +1 -1
  122. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DXDdUUl1.js → sankeyDiagram-TZEHDZUN-DibLVGzg.js} +2 -2
  123. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DXDdUUl1.js.map → sankeyDiagram-TZEHDZUN-DibLVGzg.js.map} +1 -1
  124. package/dist/static/assets/{sequenceDiagram-WL72ISMW-Domsjl5Y.js → sequenceDiagram-WL72ISMW-qXatfzVt.js} +4 -4
  125. package/dist/static/assets/{sequenceDiagram-WL72ISMW-Domsjl5Y.js.map → sequenceDiagram-WL72ISMW-qXatfzVt.js.map} +1 -1
  126. package/dist/static/assets/{stateDiagram-FKZM4ZOC-Bu0lRQK1.js → stateDiagram-FKZM4ZOC-7fgxCQHo.js} +9 -9
  127. package/dist/static/assets/{stateDiagram-FKZM4ZOC-Bu0lRQK1.js.map → stateDiagram-FKZM4ZOC-7fgxCQHo.js.map} +1 -1
  128. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-D0K-n3ic.js → stateDiagram-v2-4FDKWEC3-DcWlOAnF.js} +5 -5
  129. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-D0K-n3ic.js.map → stateDiagram-v2-4FDKWEC3-DcWlOAnF.js.map} +1 -1
  130. package/dist/static/assets/{timeline-definition-IT6M3QCI-BGvpddwR.js → timeline-definition-IT6M3QCI-iX2MRdpY.js} +3 -3
  131. package/dist/static/assets/{timeline-definition-IT6M3QCI-BGvpddwR.js.map → timeline-definition-IT6M3QCI-iX2MRdpY.js.map} +1 -1
  132. package/dist/static/assets/{treemap-GDKQZRPO-BoOzOm2j.js → treemap-GDKQZRPO-AVRnyXu1.js} +5 -5
  133. package/dist/static/assets/{treemap-GDKQZRPO-BoOzOm2j.js.map → treemap-GDKQZRPO-AVRnyXu1.js.map} +1 -1
  134. package/dist/static/assets/{xychartDiagram-PRI3JC2R-C_h3_ICR.js → xychartDiagram-PRI3JC2R-DVYEo5aJ.js} +3 -3
  135. package/dist/static/assets/{xychartDiagram-PRI3JC2R-C_h3_ICR.js.map → xychartDiagram-PRI3JC2R-DVYEo5aJ.js.map} +1 -1
  136. package/dist/static/index.html +1 -1
  137. package/dist/team.js +52 -48
  138. package/dist/tellask.js +439 -0
  139. package/dist/tools/context-health.js +177 -0
  140. package/dist/tools/diag.js +583 -0
  141. package/dist/tools/fs.js +194 -68
  142. package/dist/tools/prompts/memory/en/principles.md +13 -5
  143. package/dist/tools/prompts/memory/en/tools.md +11 -36
  144. package/dist/tools/prompts/memory/zh/principles.md +18 -8
  145. package/dist/tools/prompts/memory/zh/tools.md +11 -36
  146. package/dist/tools/team-mgmt.js +3487 -0
  147. package/dist/utils/task-doc.js +236 -0
  148. package/package.json +1 -1
  149. package/dist/static/assets/index-HWTRvE2k.js.map +0 -1
@@ -0,0 +1,338 @@
1
+ # Drive 逻辑上下文拼装现状与重构计划(2026-02)
2
+
3
+ 状态:Draft
4
+ 语义基线:以 `dominds/main/llm/driver.ts` 与 `dominds/main/persistence.ts` 当前实现为准。
5
+ 测试基线(2026-02-09 更新):drive 相关脚本统一通过 `main/llm/driver-entry.ts` 调用,可用 `DOMINDS_DRIVER_ENGINE=v1|v2` 进行引擎切换验证。
6
+
7
+ ## 1. 背景与目标
8
+
9
+ 近期在对话 `@ux (39/12/417f4a49)` 中暴露出一个上下文一致性问题:同一主线对话内已经收到了 `@cmdr/@browser_tester` 的回贴并据此更新 Taskdoc,但后续回复仍声称“没看到原始回贴文本”。
10
+
11
+ 这类问题的根因不是单条提示词,而是 **drive 内多轮生成的上下文来源语义不统一**:有的上下文是持久消息(`dlg.msgs`),有的是一次性注入(仅首轮),还有的是消费队列(take/commit/rollback)。
12
+
13
+ 本文目标:
14
+
15
+ 1. 固化“当前代码结构现状”的可审计视图。
16
+ 2. 记录本轮已做的修补。
17
+ 3. 制定一次更大范围的 drive logic 重构计划,使后续维护更清晰、不易回归。
18
+
19
+ ## 2. 当前结构(代码现状)
20
+
21
+ ### 2.1 核心文件与职责
22
+
23
+ - `main/llm/driver.ts`
24
+ - `_driveDialogStream()`:单次 drive 主循环,负责 prompt 处理、上下文拼装、LLM 调用、工具调用、suspend/continue。
25
+ - `supplyResponseToSupdialog()`:子对话回贴写入父对话响应队列、更新 pending、触发 auto-revive。
26
+ - `main/persistence.ts`
27
+ - `takeSubdialogResponses()/commitTakenSubdialogResponses()/rollbackTakenSubdialogResponses()`:子对话回贴队列的“取走-提交/回滚”机制。
28
+ - `rebuildFromEvents()`:重建 `dlg.msgs`(包含 `teammate_response_record -> tellask_result_msg` 映射)。
29
+ - `main/dialog.ts`
30
+ - `addChatMessages()`:运行时消息容器(in-memory)。
31
+
32
+ ### 2.2 当前上下文来源(进入 LLM 的入口)
33
+
34
+ 在 `_driveDialogStream()` 中,每轮 gen 的 `ctxMsgs` 由以下部分拼装:
35
+
36
+ 1. `prependedContextMessages`(策略注入)
37
+ 2. `memories`
38
+ 3. `taskDocMsg`
39
+ 4. `coursePrefixMsgs`
40
+ 5. `dialogMsgsForContext`(来自 `dlg.msgs`)
41
+ 6. `subdialogResponseContextMsgs`(来自 `takeSubdialogResponses` 的注入)
42
+ 7. `internalDrivePromptMsg`(internal prompt 注入)
43
+ 8. reminders + language guide(末尾插入)
44
+
45
+ 要点:`dlg.msgs` 是“稳定上下文”;队列/内部 prompt 属于“drive 级上下文”。
46
+
47
+ ## 3. 本轮修补(已落地)
48
+
49
+ ### 3.1 已修问题
50
+
51
+ 1. **同一 drive 多轮丢失 teammate response 上下文**
52
+ - 修复:把 `takeSubdialogResponses` 生成的 `subdialogResponseContextMsgs` 在同一 drive 内跨迭代保留,而非仅 `genIterNo===1` 注入。
53
+
54
+ 2. **中断时误 commit 已 take 队列**
55
+ - 修复:在 interrupted 分支标记 `generationHadError = true`,确保 finally 走 rollback,不会把未稳定消费的队列当作已消费。
56
+
57
+ 3. **internal prompt 语义收敛为 drive 级 priming**
58
+ - 修复:移除生命周期分支;`persistMode='internal'` 统一为 drive-scoped priming 注入。
59
+ - 语义:仅在当前 drive 生效、不入 `dlg.msgs`、不持久化、不渲染 UI。
60
+ - 依据:当前唯一使用场景是 Agent Priming,且明确要求 loop 迭代中持续可见。
61
+
62
+ 4. **teammate response 稳定化到 `dlg.msgs`(与工具结果语义对齐)**
63
+ - 修复:当 take/commit 成功后,把该批回贴镜像为 `tellask_result_msg` 写入 `dlg.msgs`,让后续 drive 不依赖一次性队列注入。
64
+
65
+ 5. **队列记录补齐状态字段**
66
+ - 修复:`subdialog-responses` 记录新增可选 `status`(`completed|failed`),并在镜像消息时使用该状态(默认 `completed`)。
67
+
68
+ ## 4. 仍存在的结构债务
69
+
70
+ ### 4.1 状态语义分散
71
+
72
+ 同一类“队友回贴事实”同时存在于:
73
+
74
+ - `teammate_response_record`(事件持久层)
75
+ - `subdialog-responses.json`(消费队列)
76
+ - `dlg.msgs`(运行时上下文)
77
+
78
+ 缺少单一“源事实 -> 视图派生”的规范,维护成本高。
79
+
80
+ ### 4.2 拼装流程耦合过深
81
+
82
+ `_driveDialogStream()` 同时承担:
83
+
84
+ - prompt 生命周期管理
85
+ - 上下文装配
86
+ - policy 校验
87
+ - 流式/非流式分支
88
+ - 工具调用循环
89
+ - suspend/revive 与队列提交事务
90
+
91
+ 单函数职责过重,不利于做语义回归验证。
92
+
93
+ ### 4.3 缺少针对性回归测试矩阵
94
+
95
+ 现有 tellask 测试覆盖“auto-revive 能跑通”,但对以下关键边界覆盖不足:
96
+
97
+ - 同一 drive 的多轮迭代上下文一致性
98
+ - interrupted + take queue 的 rollback 语义
99
+ - committed queue 镜像到 `dlg.msgs` 后的去重/恢复语义
100
+
101
+ ## 5. 语义决策记录(本轮确认)
102
+
103
+ 1. `persistMode='internal'` 不是“下一轮临时补丁”,而是 **drive 级 priming 通道**。
104
+ 2. 当前无 `next_gen` 真实业务需求,先不保留该分支,避免语义包袱。
105
+ 3. 如未来出现单轮内部提示需求,新增能力应基于明确场景和回归测试,不提前抽象。
106
+
107
+ ## 6. Priming 重点支持目标
108
+
109
+ 本次重构把 Agent Priming 作为第一优先级目标,不再作为“顺带兼容”。
110
+
111
+ ### 6.1 Priming 对 drive 的硬需求
112
+
113
+ 1. Priming 使用的临时引导 prompt 只作用于当前 drive。
114
+ 2. 在同一 drive 的多轮迭代中(工具调用、context remediation、continue)必须持续可见。
115
+ 3. 该 prompt 不进入 `dlg.msgs`,不写事件,不落盘,不渲染 UI。
116
+ 4. drive 中断/失败后不得残留到下一次 drive。
117
+
118
+ ### 6.2 Priming 支持实现(简化方案)
119
+
120
+ 核心原则:**不做通用“多生命周期临时 prompt 框架”,只保留 drive 级 priming 通道**。
121
+
122
+ 1. 在新 driver 中把 priming 输入建模为单一字段:`internalDrivePrimingMsg?: ChatMessage`。
123
+ 2. 上下文装配时固定规则:每轮迭代都在末尾注入 `internalDrivePrimingMsg`。
124
+ 3. 通过 driver 生命周期自然回收:drive 结束即销毁,不做额外状态机。
125
+ 4. 仅保留 `persistMode='internal'` 这一种 priming 入口,不再引入 `next_gen`/scope 分叉。
126
+
127
+ 这样可以把 priming 需求落实为“一个字段 + 一条注入规则”,实现面最小化。
128
+
129
+ ## 7. 两阶段重构计划(按新模块重写)
130
+
131
+ ### 阶段 1:新建并上线 `driver-v2`,旧模块原样保留
132
+
133
+ 目标:在新模块里重写 drive 逻辑,旧 `driver.ts` 保持原样,便于并排对比与问题定位。
134
+
135
+ 交付物:
136
+
137
+ 1. 新模块(建议):
138
+ - `main/llm/driver-v2/index.ts`(对外入口)
139
+ - `main/llm/driver-v2/orchestrator.ts`(drive 级编排入口)
140
+ - `main/llm/driver-v2/types.ts`(v2 内部类型与运行态)
141
+ - `main/llm/driver-v2/context.ts`(上下文装配,含 priming 注入)
142
+ - `main/llm/driver-v2/policy.ts`(策略模型骨架)
143
+ - `main/llm/driver-v2/context-health.ts`(context-health 决策骨架)
144
+ - `main/llm/driver-v2/subdialog-txn.ts`(take/commit/rollback 事务)
145
+ - `main/llm/driver-v2/supdialog-response.ts`(subdialog 回贴与 auto-revive 编排)
146
+ - `main/llm/driver-v2/round.ts`(单轮生成与 side effects)
147
+ - 当前状态(2026-02-09 最新):`orchestrator` 已去除 `emitSayingEvents / supplyResponseToSupdialog / runBackendDriver` 的 v1 passthrough;`round` 已接管 lock/dead-check/upNext/subdialog-reply 收尾流程;核心循环已切换到 `driver-v2/core.ts:driveDialogStreamCoreV2`,不再调用 `_driveDialogStream/driveDialogStreamCoreV1`。
148
+ - 仍保留的过渡 bridge(2026-02-09):`coreV2` 已把重试、diligence、参数校验、saying-event 处理迁入 `driver-v2/`(`runtime-utils.ts`、`saying-events.ts`);当前仅 `tellask` 执行链仍通过 `driver-v2/tellask-bridge.ts -> driver.ts` 过渡调用,属于阶段 1 末尾需继续收敛的技术债。
149
+ 2. 保持旧模块文件不动:
150
+ - `main/llm/driver.ts` 继续存在,作为对照基线。
151
+ 3. 切换方式:
152
+ - 通过单点入口切换到 v2(建议配置开关或固定切换点),避免多处散改 import。
153
+ - 当前状态(2026-02-09):`main/llm/driver-entry.ts` 已支持 `DOMINDS_DRIVER_ENGINE=v1|v2`(默认 `v2`)。
154
+ 4. Priming 专项支持:
155
+ - v2 内置 `internalDrivePrimingMsg` 注入规则(每轮注入、drive 内有效、绝不持久化)。
156
+ 5. 关键回归与复放:
157
+ - `multi-iter subdialog response`
158
+ - `interrupt rollback`
159
+ - `commit mirror to dlg.msgs`
160
+ - `no duplicate after restore`
161
+ - `internal drive priming persists across iterations`
162
+ - 复放 `39/12/417f4a49` 风格样本
163
+
164
+ 阶段 1 验收门槛:
165
+
166
+ 1. `pnpm -C dominds run lint:types` 通过。
167
+ 2. tellask 相关回归通过。
168
+ 3. priming 专项回归通过。
169
+ 4. 对比旧 driver,关键语义一致且已知 bug 被修复。
170
+
171
+ ### 阶段 2:v2 稳定后删除旧模块并清理
172
+
173
+ 目标:确认 v2 可稳定替代后,删除旧逻辑,收敛维护面。
174
+
175
+ 交付物:
176
+
177
+ 1. 删除旧 `driver.ts` 中被 v2 覆盖的实现(或将其瘦身为转发壳并最终移除)。
178
+ 2. 清理迁移期开关、对照代码、过渡注释和 dead code。
179
+ 3. 固化最终文档:
180
+ - 新 driver 模块边界
181
+ - priming 通道语义
182
+ - 事务边界与错误处理约束
183
+
184
+ 阶段 2 验收门槛:
185
+
186
+ 1. 全量类型检查与回归通过。
187
+ 2. 无旧模块引用残留(import/调用链清零)。
188
+ 3. 行为与阶段 1 上线结果一致。
189
+
190
+ ## 8. 目标接口草案(v2,简化版)
191
+
192
+ ```ts
193
+ type DriveV2Input = {
194
+ persistedPrompt?: HumanPrompt; // 常规用户输入(会持久化)
195
+ internalDrivePrimingMsg?: ChatMessage; // 仅 drive 内可见
196
+ skipTaskdoc?: boolean;
197
+ };
198
+
199
+ type DriveV2Runtime = {
200
+ takenSubdialogResponses: TakenSubdialogResponse[];
201
+ generationAttempted: boolean;
202
+ };
203
+ ```
204
+
205
+ 说明:
206
+
207
+ 1. priming 只保留 `internalDrivePrimingMsg` 一条路径。
208
+ 2. 不提供“下一轮临时注入”接口,避免再次引入语义分叉。
209
+ 3. 所有持久化副作用统一在编排层执行,不放进上下文纯函数。
210
+
211
+ ## 9. 重构约束(必须保持)
212
+
213
+ 1. 不改变现有 wire 协议事件名与基础语义(除非显式版本升级)。
214
+ 2. 不引入 silent fallback;上下文事务异常需 loud 日志与可见错误信号。
215
+ 3. 保持 TypeScript strict 与可静态验证属性;禁止 `any`。
216
+ 4. 旧模块在阶段 1 必须原样保留,便于对照和回滚。
217
+
218
+ ## 10. 现有测试合理性评估(针对 dlg drive)
219
+
220
+ 结论(2026-02-09 更新):**覆盖面已明显提升,足以支撑阶段 1 的持续迁移;但仍有未完成的对照/复放用例,尚未达到“可删除旧模块”的阶段 2 门槛**。
221
+
222
+ ### 10.1 现有测试的合理性(优点)
223
+
224
+ 1. 以脚本方式在临时 rtws 跑通关键链路,具备真实文件系统与持久化路径,能抓到不少“运行态”问题。
225
+ 2. 已覆盖部分核心行为:
226
+ - tellask 解析/流式解析稳定性
227
+ - root auto-revive 基础路径
228
+ - type B 注册去重基础路径
229
+ - diligence push/Q4H 的部分事件行为
230
+ 3. 执行成本低,适合快速 smoke。
231
+
232
+ ### 10.2 仍存关键缺口(阶段 2 前需补齐)
233
+
234
+ 1. `v1-v2-parity-diligence-q4h.ts` 仍未实现(当前只有 switch-smoke 级别验证,不是逐断言 parity)。
235
+ 2. `replay-39-12-417f4a49-style.ts` 仍未实现(尚未形成稳定复放门禁)。
236
+ 3. 旧模块仍保留;v2 已激活入口与外围编排,且核心生成循环已独立到 `coreV2`,但仍存在 bridge 依赖(tellask/重试/diligence 等辅助函数),尚未进入“删除旧模块”收敛阶段。
237
+
238
+ ## 11. v2 测试设计与上线门禁
239
+
240
+ ### 11.1 分层策略
241
+
242
+ 1. L0 单元层(纯逻辑,零 I/O)
243
+ - 目标:验证 v2 新模块内部规则和数据变换。
244
+ - 关注:context 装配顺序、priming 注入规则、txn 状态机。
245
+ 2. L1 集成层(临时 rtws + mock provider)
246
+ - 目标:验证 drive 主链路和持久化副作用。
247
+ - 关注:take/commit/rollback、auto-revive、runState、event 语义。
248
+ 3. L2 对照/复放层(v1 vs v2)
249
+ - 目标:验证重写后与既有正确语义一致,且已知 bug 被修复。
250
+ - 关注:同输入下关键输出等价、上下文可见性不回退。
251
+
252
+ ### 11.2 v2 必做用例(最小集)
253
+
254
+ 1. `driver-v2/context-assembly-order.ts`
255
+ - 断言:`base -> ephemeral -> tail` 装配顺序稳定,且 reminders/language guide 插入点与现有语义一致。
256
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
257
+ 2. `driver-v2/internal-drive-priming-multi-iter.ts`
258
+ - 断言:priming 提示在同一 drive 第 1/2/3 轮均可见。
259
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
260
+ 3. `driver-v2/internal-drive-priming-not-persisted.ts`
261
+ - 断言:priming 不进入 `dlg.msgs`、不写 events、不落盘。
262
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
263
+ 4. `driver-v2/internal-drive-priming-no-leak-next-drive.ts`
264
+ - 断言:drive 结束后 priming 不泄漏到下一次 drive。
265
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
266
+ 5. `driver-v2/subdialog-queue-interrupt-rollback.ts`
267
+ - 断言:take 后 interrupted/error 必 rollback,下一次 drive 可重见。
268
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
269
+ 6. `driver-v2/subdialog-queue-commit-mirror.ts`
270
+ - 断言:成功 commit 后镜像到 `dlg.msgs`,后续不依赖队列注入。
271
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
272
+ 7. `driver-v2/subdialog-restore-live-equivalence.ts`
273
+ - 断言:restore 路径与 live 路径对 teammate response 的上下文等价。
274
+ - v1 状态:可跑;已实现并通过(2026-02-09)。`restoreDialogHierarchy(rootId, status)` 需由调用方显式传入状态。
275
+ 8. `driver-v2/multi-iter-tool-round-context-continuity.ts`
276
+ - 断言:工具回合 continue 后,前序关键上下文不丢失。
277
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
278
+ 9. `driver-v2/v1-v2-parity-basic-tellask.ts`
279
+ - 断言:同 mock 输入下,v1/v2 在可观察语义上等价(除已知 bug 修复差异)。
280
+ - v1 状态:可跑;已实现并通过(2026-02-09)。
281
+ 10. `driver-v2/v1-v2-parity-diligence-q4h.ts`
282
+ - 断言:diligence/Q4H 关键事件与 runState 语义一致。
283
+ - v1 状态:尚未实现专门 parity 用例;但已通过 `driver-v2:switch-smoke:engine-v1|v2` 覆盖该链路的切换可用性验证(2026-02-09)。
284
+
285
+ 11. `driver-v2/replay-39-12-417f4a49-style.ts`
286
+ - 断言:复放样本不再出现“已收到回贴却声称没看到”的语义回归。
287
+ - v1 状态:可先做“现状复放”脚本;v2 需要转为门禁回归。当前未实现。
288
+
289
+ ### 11.3 阶段 1/2 的测试门禁
290
+
291
+ 阶段 1(v2 上线前)必须通过:
292
+
293
+ 1. `lint:types`
294
+ 2. 全部 L0 用例
295
+ 3. 全部 L1 最小集(上面 1~7)
296
+ 4. 至少 2 个 L2 对照用例(上面 8~9)
297
+ 5. priming 专项 3 条(上面 1~3)全部通过
298
+
299
+ 阶段 2(删除旧模块前)必须通过:
300
+
301
+ 1. 阶段 1 全部门禁
302
+ 2. 全部 L2 用例(含复放样本)
303
+ 3. old-driver 引用清零后再跑一次全套,确保无隐性依赖
304
+
305
+ ### 11.4 执行组织建议
306
+
307
+ 在 `tests/package.json` 里新增独立脚本分组(阶段 1 即可开始):
308
+
309
+ 1. `driver-v2:unit`
310
+ 2. `driver-v2:integration`
311
+ 3. `driver-v2:parity`
312
+ 4. `driver-v2:replay`
313
+ 5. `driver-v2:gate`(汇总门禁脚本)
314
+
315
+ 目的:把“能跑”与“可上线”分开,避免只凭单条 smoke 测试判断重写完成。
316
+
317
+ 当前已落地脚本(2026-02-09):
318
+
319
+ 1. `driver-v2:integration:engine-v1`
320
+ 2. `driver-v2:integration:engine-v2`
321
+ 3. `driver-v2:switch-smoke:engine-v1`
322
+ 4. `driver-v2:switch-smoke:engine-v2`
323
+ 5. `driver-v2:parity`
324
+
325
+ ### 11.5 测试基座约定(script-rtws + mock provider)
326
+
327
+ 1. 阶段 1 的集成测试默认基于 `tests/script-rtws` 运行,避免污染真实 rtws。
328
+ - 执行约束:统一通过 `tests/cli.ts` 入口传 `-C script-rtws`;不允许其它 rtws。
329
+ - 并发约束:同一时刻只允许一个 `tests/cli.ts` 进程使用 `script-rtws`,禁止并发执行多个脚本(会互相污染快照与恢复过程)。
330
+ - 运行收尾:`tests/cli.ts` 默认会在脚本结束后将 `tests/script-rtws` 恢复到“测试前快照”(可通过 `DOMINDS_TEST_RTWS_RESTORE_MODE=head` 改为恢复到 git HEAD;`DOMINDS_TEST_RTWS_RESTORE=0` 可禁用自动恢复用于调试)。
331
+ 2. 默认使用 `apiType: mock`,通过 `mock-db/<model>.yaml` 驱动可重复测试。
332
+ - driver 入口切换统一走 `main/llm/driver-entry.ts`,通过环境变量 `DOMINDS_DRIVER_ENGINE=v1|v2` 选择引擎;测试代码避免直接调用 `driveDialogStream` 的 v1 直连入口。
333
+ 3. mock 响应可使用:
334
+ - `delayMs`:整次响应前延迟(用于中断/rollback窗口)
335
+ - `chunkDelayMs`:流式分块延迟(用于流顺序与 stop 时序)
336
+ - `funcCalls`:响应后追加函数调用序列(用于工具回合/continue 测试)
337
+ - `contextContains`:要求上下文中必须包含指定片段(用于上下文连续性断言)
338
+ 4. 需要“慢速请求”场景时,优先在 mock 数据层配置,不依赖真实外部 provider。
@@ -0,0 +1,176 @@
1
+ # Keep-Going (Diligence Auto-Continue) — Design Doc
2
+
3
+ ## Summary
4
+
5
+ Dominds root dialogs are intended for long-run operation. A root dialog “stopping” (becoming idle)
6
+ is often not what operators want: they want the agent to keep pushing forward until it either:
7
+
8
+ - legitimately suspends for a human decision (Q4H), or
9
+ - legitimately suspends waiting for subdialogs (tellask/backfill).
10
+
11
+ This document specifies a runtime mechanism (“keep-going”) that, for **root/main dialogs only**,
12
+ prevents the dialog from stopping: whenever the driver would otherwise stop, it auto-sends a short
13
+ diligence prompt (rendered as a normal user bubble) and continues generation, except when the dialog
14
+ is legitimately suspended (Q4H or pending subdialogs).
15
+
16
+ ## Goals
17
+
18
+ - Prevent root dialogs from stopping except for legitimate suspension states (Q4H / subdialogs).
19
+ - Keep behavior predictable and bounded (no infinite loops).
20
+ - Make the nudge text configurable per workspace (rtws) and language.
21
+ - Provide a clear, user-controlled “disable” mechanism.
22
+
23
+ ## Non-goals
24
+
25
+ - Auto-completing / auto-marking a dialog as done.
26
+ - Applying this behavior to subdialogs (subdialogs remain scoped and should report back to their caller).
27
+
28
+ ## Definitions
29
+
30
+ - **Root/main dialog**: a `RootDialog` (`dlg.id.rootId === dlg.id.selfId`), the primary conversation thread.
31
+ - **Subdialog**: a `SubDialog`, created for tellask / scoped work.
32
+ - **Q4H**: “Questions for Human”, initiated via `!?@human`, which suspends dialog progression until the human responds.
33
+
34
+ ## Expected “normal” completion path (recommended)
35
+
36
+ When the agent needs a human decision to conclude (e.g., confirm a choice or decide whether to mark the dialog done), the correct path is:
37
+
38
+ 1. The agent issues a Q4H (`!?@human`) with the necessary context and explicit decision request.
39
+ 2. The WebUI surfaces the Q4H clearly.
40
+ 3. The human decides and either:
41
+ - marks the root dialog “done” manually, or
42
+ - provides the requested info so the dialog can proceed.
43
+
44
+ This is the “controlled convergence” path. The keep-going mechanism should **not** override legitimate suspension states.
45
+
46
+ ## Keep-going behavior (“auto-continue” fallback)
47
+
48
+ ### Trigger conditions (must all hold)
49
+
50
+ - Dialog is the **root/main dialog** (never for subdialogs).
51
+ - Dialog is **not suspended**:
52
+ - no pending Q4H, and
53
+ - no pending subdialogs (waiting for backfill).
54
+ - The driver would otherwise stop the generation loop (i.e., no tool/function outputs require another iteration).
55
+
56
+ ### Action
57
+
58
+ The runtime auto-sends a diligence prompt (rendered as a normal user bubble) and runs another
59
+ generation iteration.
60
+
61
+ ### Boundedness
62
+
63
+ To avoid infinite loops, keep-going has a per-dialog budget (per-member `diligence-push-max`) that controls
64
+ how many auto-continued diligence prompts can be injected for a given dialog before the runtime forces a
65
+ Q4H suspension.
66
+
67
+ - Default: **3**
68
+ - If `< 1`, keep-going is effectively disabled for that member
69
+ - Configurable per-member via `diligence-push-max` in `.minds/team.yaml`
70
+
71
+ ### Reset on Q4H
72
+
73
+ When a dialog becomes suspended due to a pending Q4H (Questions for Human), the keep-going injection
74
+ counter is reset. This ensures that after the human answers the Q4H and the dialog is resumed, the
75
+ dialog gets a fresh keep-going budget again.
76
+
77
+ ### Budget exhausted → force Q4H
78
+
79
+ When the keep-going budget is exhausted, the runtime creates a Q4H entry that asks the human whether
80
+ to continue or stop. This converts “boundedness” into a legitimate suspension point and avoids
81
+ infinite auto-continue loops.
82
+
83
+ ### Disable switch
84
+
85
+ Keep-going can be disabled per-rtws in either of these ways:
86
+
87
+ - If the selected diligence file exists but its content is empty/whitespace, keep-going is disabled (no injection).
88
+
89
+ Keep-going can be disabled per-member in either of these ways:
90
+
91
+ - If `diligence-push-max < 1`, keep-going is disabled for that member (no injection).
92
+
93
+ ## Diligence prompt resolution
94
+
95
+ Let `<rtws>` be the current runtime workspace (i.e., `process.cwd()`).
96
+
97
+ Resolution order:
98
+
99
+ 1. `<rtws>/.minds/diligence.<work-lang-id>.md` (e.g., `diligence.zh.md`)
100
+ 2. `<rtws>/.minds/diligence.md` (language-agnostic fallback)
101
+ 3. Built-in fallback text (hardcoded i18n; `zh` is canonical and embedded in source)
102
+
103
+ If the first existing file in the above order has empty/whitespace content, **disable** keep-going.
104
+
105
+ Note: YAML frontmatter in diligence files is **ignored** by the runtime. If present, it is treated as
106
+ non-content metadata and stripped from the prompt body.
107
+
108
+ ### Team member cap: `diligence-push-max`
109
+
110
+ Each team member can optionally cap keep-going via `.minds/team.yaml`:
111
+
112
+ ```yaml
113
+ members:
114
+ alice:
115
+ diligence-push-max: 10
116
+ ```
117
+
118
+ Rules:
119
+
120
+ - If missing, `diligence-push-max` defaults to **3** for that member.
121
+ - If `diligence-push-max < 1`, keep-going is disabled for that member (no injection), even if the diligence file exists.
122
+ - Built-in shadow members `fuxi` and `pangu` default to `diligence-push-max: 0` unless explicitly overridden in team.yaml.
123
+
124
+ ## UX notes
125
+
126
+ - Keep-going is a runtime-only nudge, but it should be **visible**: the diligence prompt is rendered
127
+ as a normal user message bubble (auto-sent by the runtime) so operators can understand why an
128
+ extra iteration occurred.
129
+ - Users should observe that the agent continues with a brief follow-up after tool-only operations.
130
+ - When the agent truly needs user intervention, it should use Q4H. Keep-going should not try to “fake” completion.
131
+
132
+ ## Implementation (backend)
133
+
134
+ ### Where
135
+
136
+ Implemented in the LLM driver loop (`dominds/main/llm/driver.ts`) as a small post-iteration check:
137
+
138
+ 1. If `suspendForHuman` is true, stop (Q4H / subdialog pending).
139
+ 2. If there is any tool feedback, continue normally.
140
+ 3. Otherwise (root only), attempt keep-going auto-continue:
141
+ - If disabled → stop normally.
142
+ - If budget exhausted → create Q4H and stop.
143
+ - Else → auto-send diligence prompt and continue.
144
+
145
+ ### Message type
146
+
147
+ We inject the diligence prompt as an auto-sent user message:
148
+
149
+ - `ChatMessage` of type `prompting_msg` with `role: 'user'`
150
+
151
+ This ensures:
152
+
153
+ - It is present in the model context
154
+ - It is persisted as a human message record
155
+ - It renders in the chat timeline as a normal user bubble (like any other user message)
156
+
157
+ ## Observability
158
+
159
+ Recommended follow-ups (not required for initial implementation):
160
+
161
+ - Add a structured log line when keep-going is triggered, including:
162
+ - dialog id
163
+ - language
164
+ - which diligence source was used (lang-specific / generic / built-in / disabled)
165
+ - Optional metrics counter for “keep-going triggered” and “keep-going disabled by empty file”.
166
+
167
+ ## Testing
168
+
169
+ Regression tests should cover:
170
+
171
+ - Root dialog: tool-only output → diligence injection → continued response
172
+ - Root dialog: empty assistant output → diligence injection → continued response
173
+ - Subdialog: no diligence injection
174
+ - Workspace config:
175
+ - `.minds/diligence.md` is honored when lang-specific file is absent
176
+ - empty diligence file disables keep-going
@@ -0,0 +1,162 @@
1
+ # Keep-Going(鞭策)— 设计文档
2
+
3
+ ## 概述
4
+
5
+ Dominds 根对话旨在长期运行。根对话"停止"(变为空闲)通常不是操作员想要的:他们希望智能体持续推进,直到:
6
+
7
+ - 合法地暂停等待人类决策(Q4H),或
8
+ - 合法地暂停等待子对话(tellask/backfill)。
9
+
10
+ 本文档指定了一个运行时机制("keep-going"),仅针对**根/主对话**,防止对话停止:每当驱动程序即将停止时,它会自动发送一个简短的鞭策语(渲染为正常的用户气泡)并继续生成,除非对话处于合法暂停状态(Q4H 或待处理的子对话)。
11
+
12
+ ## 目标
13
+
14
+ - 防止根对话停止,除非处于合法暂停状态(Q4H / 子对话)。
15
+ - 保持行为可预测和有界(无无限循环)。
16
+ - 使鞭策语文本可按工作区(rtws)和语言配置。
17
+ - 提供清晰的用户控制的"禁用"机制。
18
+
19
+ ## 非目标
20
+
21
+ - 自动完成/自动将对话标记为完成。
22
+ - 将此行为应用于子对话(子对话保持范围,应向其调用者报告)。
23
+
24
+ ## 定义
25
+
26
+ - **根/主对话**:`RootDialog`(`dlg.id.rootId === dlg.id.selfId`),主要对话线程。
27
+ - **子对话**:`SubDialog`,为 tellask / 作用域工作创建。
28
+ - **Q4H**:"Questions for Human",通过 `!?@human` 发起,暂停对话进度直到人类响应。
29
+
30
+ ## 预期的"正常"完成路径(推荐)
31
+
32
+ 当智能体需要人类决策来结束时(例如,确认选择或决定是否将对话标记为完成),正确的路径是:
33
+
34
+ 1. 智能体发出 Q4H(`!?@human`)并提供必要的上下文和明确的决策请求。
35
+ 2. WebUI 清楚地呈现 Q4H。
36
+ 3. 人类决定并:
37
+ - 手动将根对话标记为"完成",或
38
+ - 提供请求的信息以便对话继续。
39
+
40
+ 这是"受控收敛"路径。keep-going 机制不应覆盖合法的暂停状态。
41
+
42
+ ## Keep-Going 行为("自动继续"回退)
43
+
44
+ ### 触发条件(必须全部满足)
45
+
46
+ - 对话是**根/主对话**(绝不会是子对话)。
47
+ - 对话**未暂停**:
48
+ - 没有待处理的 Q4H,并且
49
+ - 没有待处理的子对话(等待回填)。
50
+ - 驱动程序即将停止生成循环(即没有工具/函数输出需要另一次迭代)。
51
+
52
+ ### 操作
53
+
54
+ 运行时自动发送一个鞭策语(渲染为正常的用户气泡)并运行另一次生成迭代。
55
+
56
+ ### 有界性
57
+
58
+ 为避免无限循环,keep-going 有一个按对话的预算(每个成员的 `diligence-push-max`),控制对于给定对话在运行时强制 Q4H 暂停之前可以注入多少个自动继续的鞭策语。
59
+
60
+ - 默认值:**3**
61
+ - 如果 `< 1`,则该成员的 keep-going 有效禁用
62
+ - 可通过 `.minds/team.yaml` 中的 `diligence-push-max` 按成员配置
63
+
64
+ ### Q4H 时重置
65
+
66
+ 当对话因待处理的 Q4H(Questions for Human)而暂停时,keep-going 注入计数器会被重置。这确保在人类回答 Q4H 并恢复对话后,对话会获得新的 keep-going 预算。
67
+
68
+ ### 预算耗尽 → 强制 Q4H
69
+
70
+ 当 keep-going 预算耗尽时,运行时会创建一个 Q4H 条目,询问人类是否继续或停止。这将"有界性"转换为合法的暂停点,并避免无限自动继续循环。
71
+
72
+ ### 禁用开关
73
+
74
+ 可以通过以下任一方式按 rtws 禁用 keep-going:
75
+
76
+ - 如果选中的鞭策语文件存在但其内容为空/仅空白,则禁用 keep-going(不注入)。
77
+
78
+ 可以通过以下任一方式按成员禁用 keep-going:
79
+
80
+ - 如果 `diligence-push-max < 1`,则该成员的 keep-going 被禁用(不注入)。
81
+
82
+ ## 鞭策语解析
83
+
84
+ 让 `<rtws>` 为当前运行时工作区(即 `process.cwd()`)。
85
+
86
+ 解析顺序:
87
+
88
+ 1. `<rtws>/.minds/diligence.<work-lang-id>.md`(例如,`diligence.zh.md`)
89
+ 2. `<rtws>/.minds/diligence.md`(语言无关的回退)
90
+ 3. 内置回退文本(硬编码的 i18n;`zh` 是规范的并嵌入在源代码中)
91
+
92
+ 如果上述顺序中第一个存在的文件具有空/空白内容,则**禁用** keep-going。
93
+
94
+ 注意:鞭策语文件中的 YAML frontmatter 会被运行时忽略。如果存在,它被视为非内容元数据并从提示正文中剥离。
95
+
96
+ ### 团队成员上限:`diligence-push-max`
97
+
98
+ 每个团队成员可以选择通过 `.minds/team.yaml` 限制 keep-going:
99
+
100
+ ```yaml
101
+ members:
102
+ alice:
103
+ diligence-push-max: 10
104
+ ```
105
+
106
+ 规则:
107
+
108
+ - 如果缺失,`diligence-push-max` 对于该成员默认为 **3**。
109
+ - 如果 `diligence-push-max < 1`,则该成员的 keep-going 被禁用(不注入),即使鞭策语文件存在。
110
+ - 内置影子成员 `fuxi` 和 `pangu` 默认为 `diligence-push-max: 0`,除非在 team.yaml 中显式覆盖。
111
+
112
+ ## UX 备注
113
+
114
+ - Keep-going 是一个仅运行时的提示,但它应该是**可见的**:鞭策语渲染为正常的用户消息气泡(由运行时自动发送),以便操作员理解为什么会出现额外的迭代。
115
+ - 用户应该观察到智能体在仅工具操作后继续简短的跟进。
116
+ - 当智能体真正需要用户干预时,它应该使用 Q4H。Keep-going 不应试图"假装"完成。
117
+
118
+ ## 实现(后端)
119
+
120
+ ### 位置
121
+
122
+ 在 LLM 驱动程序循环中实现(`dominds/main/llm/driver.ts`),作为迭代后的小检查:
123
+
124
+ 1. 如果 `suspendForHuman` 为 true,则停止(Q4H / 子对话待处理)。
125
+ 2. 如果有任何工具反馈,则正常继续。
126
+ 3. 否则(仅限根),尝试 keep-going 自动继续:
127
+ - 如果禁用 → 正常停止。
128
+ - 如果预算耗尽 → 创建 Q4H 并停止。
129
+ - 否则 → 自动发送鞭策语并继续。
130
+
131
+ ### 消息类型
132
+
133
+ 我们将鞭策语作为自动发送的用户消息注入:
134
+
135
+ - 类型为 `prompting_msg`、角色为 `'user'` 的 `ChatMessage`
136
+
137
+ 这确保了:
138
+
139
+ - 它存在于模型上下文中
140
+ - 它作为人类消息记录持久化
141
+ - 它在聊天时间线中渲染为正常的用户气泡(像任何其他用户消息一样)
142
+
143
+ ## 可观察性
144
+
145
+ 建议的后续步骤(初始实现不需要):
146
+
147
+ - 当触发 keep-going 时添加结构化日志行,包括:
148
+ - 对话 id
149
+ - 语言
150
+ - 使用的鞭策语来源(语言特定/通用/内置/禁用)
151
+ - 为"keep-going 触发"和"keep-going 因空文件禁用"添加可选的指标计数器。
152
+
153
+ ## 测试
154
+
155
+ 回归测试应覆盖:
156
+
157
+ - 根对话:仅工具输出 → 鞭策语注入 → 继续响应
158
+ - 根对话:空助手输出 → 鞭策语注入 → 继续响应
159
+ - 子对话:无鞭策语注入
160
+ - 工作区配置:
161
+ - 当语言特定文件缺失时,`.minds/diligence.md` 被遵守
162
+ - 空的鞭策语文件禁用 keep-going