dominds 1.16.8 → 1.16.10

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 (140) hide show
  1. package/README.md +1 -1
  2. package/README.zh.md +1 -1
  3. package/dist/docs/app-constitution.md +2 -2
  4. package/dist/docs/app-constitution.zh.md +2 -2
  5. package/dist/docs/dialog-system.md +4 -0
  6. package/dist/docs/dialog-system.zh.md +4 -0
  7. package/dist/docs/team_mgmt-toolset.md +1 -1
  8. package/dist/docs/team_mgmt-toolset.zh.md +1 -1
  9. package/dist/llm/gen/codex.d.ts +1 -9
  10. package/dist/llm/gen/codex.js +3 -29
  11. package/dist/llm/kernel-driver/drive.js +22 -81
  12. package/dist/llm/kernel-driver/flow.js +61 -0
  13. package/dist/llm/kernel-driver/reply-guidance.d.ts +1 -0
  14. package/dist/llm/kernel-driver/reply-guidance.js +15 -5
  15. package/dist/llm/stop-reason-i18n.d.ts +1 -1
  16. package/dist/llm/stop-reason-i18n.js +0 -4
  17. package/dist/persistence.js +13 -3
  18. package/dist/runtime/interjection-pause-stop.js +4 -0
  19. package/dist/runtime/reply-prompt-copy.js +2 -2
  20. package/dist/tools/apply-patch.js +5 -5
  21. package/dist/tools/builtins.js +11 -12
  22. package/dist/tools/prompts/codex_inspect_and_patch_tools/en/errors.md +45 -0
  23. package/dist/tools/prompts/codex_inspect_and_patch_tools/en/index.md +37 -0
  24. package/dist/tools/prompts/codex_inspect_and_patch_tools/en/principles.md +41 -0
  25. package/dist/tools/prompts/codex_inspect_and_patch_tools/en/scenarios.md +40 -0
  26. package/dist/tools/prompts/codex_inspect_and_patch_tools/en/tools.md +47 -0
  27. package/dist/tools/prompts/codex_inspect_and_patch_tools/zh/errors.md +45 -0
  28. package/dist/tools/prompts/codex_inspect_and_patch_tools/zh/index.md +37 -0
  29. package/dist/tools/prompts/codex_inspect_and_patch_tools/zh/principles.md +41 -0
  30. package/dist/tools/prompts/codex_inspect_and_patch_tools/zh/scenarios.md +40 -0
  31. package/dist/tools/prompts/codex_inspect_and_patch_tools/zh/tools.md +47 -0
  32. package/dist/tools/team_mgmt.js +13 -21
  33. package/package.json +4 -4
  34. package/webapp/dist/assets/{_basePickBy-B7FV6Gnn.js → _basePickBy-BKgfg72Q.js} +3 -3
  35. package/webapp/dist/assets/{_basePickBy-B7FV6Gnn.js.map → _basePickBy-BKgfg72Q.js.map} +1 -1
  36. package/webapp/dist/assets/{_baseUniq-CmVnLJpw.js → _baseUniq-DKhs7H5s.js} +2 -2
  37. package/webapp/dist/assets/{_baseUniq-CmVnLJpw.js.map → _baseUniq-DKhs7H5s.js.map} +1 -1
  38. package/webapp/dist/assets/{arc-DSJlh9AU.js → arc-Tykg3Fx4.js} +2 -2
  39. package/webapp/dist/assets/{arc-DSJlh9AU.js.map → arc-Tykg3Fx4.js.map} +1 -1
  40. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-Cd2cTHzo.js → architectureDiagram-2XIMDMQ5-BAyn6_in.js} +7 -7
  41. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-Cd2cTHzo.js.map → architectureDiagram-2XIMDMQ5-BAyn6_in.js.map} +1 -1
  42. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-C0uEp_Tz.js → blockDiagram-WCTKOSBZ-CGbo6x6_.js} +7 -7
  43. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-C0uEp_Tz.js.map → blockDiagram-WCTKOSBZ-CGbo6x6_.js.map} +1 -1
  44. package/webapp/dist/assets/{c4Diagram-IC4MRINW-B37U92JK.js → c4Diagram-IC4MRINW-CUizxwo0.js} +3 -3
  45. package/webapp/dist/assets/{c4Diagram-IC4MRINW-B37U92JK.js.map → c4Diagram-IC4MRINW-CUizxwo0.js.map} +1 -1
  46. package/webapp/dist/assets/{channel-DLHnjnQf.js → channel-DvP7WrjO.js} +2 -2
  47. package/webapp/dist/assets/{channel-DLHnjnQf.js.map → channel-DvP7WrjO.js.map} +1 -1
  48. package/webapp/dist/assets/{chunk-4BX2VUAB-DnZvfQyp.js → chunk-4BX2VUAB-cgnNaqgV.js} +2 -2
  49. package/webapp/dist/assets/{chunk-4BX2VUAB-DnZvfQyp.js.map → chunk-4BX2VUAB-cgnNaqgV.js.map} +1 -1
  50. package/webapp/dist/assets/{chunk-55IACEB6-BG-cjz3M.js → chunk-55IACEB6-BEUsguTg.js} +2 -2
  51. package/webapp/dist/assets/{chunk-55IACEB6-BG-cjz3M.js.map → chunk-55IACEB6-BEUsguTg.js.map} +1 -1
  52. package/webapp/dist/assets/{chunk-FMBD7UC4-B1L8cPfl.js → chunk-FMBD7UC4-B2p0efxm.js} +2 -2
  53. package/webapp/dist/assets/{chunk-FMBD7UC4-B1L8cPfl.js.map → chunk-FMBD7UC4-B2p0efxm.js.map} +1 -1
  54. package/webapp/dist/assets/{chunk-JSJVCQXG-C65w23ZF.js → chunk-JSJVCQXG-Bd09WM0B.js} +2 -2
  55. package/webapp/dist/assets/{chunk-JSJVCQXG-C65w23ZF.js.map → chunk-JSJVCQXG-Bd09WM0B.js.map} +1 -1
  56. package/webapp/dist/assets/{chunk-KX2RTZJC-_4YSMrEL.js → chunk-KX2RTZJC-DaLNbvSI.js} +2 -2
  57. package/webapp/dist/assets/{chunk-KX2RTZJC-_4YSMrEL.js.map → chunk-KX2RTZJC-DaLNbvSI.js.map} +1 -1
  58. package/webapp/dist/assets/{chunk-NQ4KR5QH-ComURSQb.js → chunk-NQ4KR5QH-6dsWbn61.js} +4 -4
  59. package/webapp/dist/assets/{chunk-NQ4KR5QH-ComURSQb.js.map → chunk-NQ4KR5QH-6dsWbn61.js.map} +1 -1
  60. package/webapp/dist/assets/{chunk-QZHKN3VN-BrxvuRI6.js → chunk-QZHKN3VN-C0e1qWr4.js} +2 -2
  61. package/webapp/dist/assets/{chunk-QZHKN3VN-BrxvuRI6.js.map → chunk-QZHKN3VN-C0e1qWr4.js.map} +1 -1
  62. package/webapp/dist/assets/{chunk-WL4C6EOR-CW7tAF_z.js → chunk-WL4C6EOR-ChheG0OU.js} +6 -6
  63. package/webapp/dist/assets/{chunk-WL4C6EOR-CW7tAF_z.js.map → chunk-WL4C6EOR-ChheG0OU.js.map} +1 -1
  64. package/webapp/dist/assets/{classDiagram-VBA2DB6C-BWrPbwfd.js → classDiagram-VBA2DB6C-uUAnWHsS.js} +7 -7
  65. package/webapp/dist/assets/{classDiagram-VBA2DB6C-BWrPbwfd.js.map → classDiagram-VBA2DB6C-uUAnWHsS.js.map} +1 -1
  66. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BWrPbwfd.js → classDiagram-v2-RAHNMMFH-uUAnWHsS.js} +7 -7
  67. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BWrPbwfd.js.map → classDiagram-v2-RAHNMMFH-uUAnWHsS.js.map} +1 -1
  68. package/webapp/dist/assets/{clone-B-TYPsxN.js → clone-BiXgC0aE.js} +2 -2
  69. package/webapp/dist/assets/{clone-B-TYPsxN.js.map → clone-BiXgC0aE.js.map} +1 -1
  70. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-C1bfZcVY.js → cose-bilkent-S5V4N54A-C-h7F7gA.js} +2 -2
  71. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-C1bfZcVY.js.map → cose-bilkent-S5V4N54A-C-h7F7gA.js.map} +1 -1
  72. package/webapp/dist/assets/{dagre-KLK3FWXG-BfD08d2e.js → dagre-KLK3FWXG-5B3k91Lo.js} +7 -7
  73. package/webapp/dist/assets/{dagre-KLK3FWXG-BfD08d2e.js.map → dagre-KLK3FWXG-5B3k91Lo.js.map} +1 -1
  74. package/webapp/dist/assets/{diagram-E7M64L7V-Dyd9OucT.js → diagram-E7M64L7V-CFtPXaNS.js} +8 -8
  75. package/webapp/dist/assets/{diagram-E7M64L7V-Dyd9OucT.js.map → diagram-E7M64L7V-CFtPXaNS.js.map} +1 -1
  76. package/webapp/dist/assets/{diagram-IFDJBPK2-BN-JCceb.js → diagram-IFDJBPK2-CUuPPDwe.js} +7 -7
  77. package/webapp/dist/assets/{diagram-IFDJBPK2-BN-JCceb.js.map → diagram-IFDJBPK2-CUuPPDwe.js.map} +1 -1
  78. package/webapp/dist/assets/{diagram-P4PSJMXO-C2jh_Kry.js → diagram-P4PSJMXO-D7oYhsVm.js} +7 -7
  79. package/webapp/dist/assets/{diagram-P4PSJMXO-C2jh_Kry.js.map → diagram-P4PSJMXO-D7oYhsVm.js.map} +1 -1
  80. package/webapp/dist/assets/{erDiagram-INFDFZHY-C3Pj10OJ.js → erDiagram-INFDFZHY-DUDHdT_e.js} +5 -5
  81. package/webapp/dist/assets/{erDiagram-INFDFZHY-C3Pj10OJ.js.map → erDiagram-INFDFZHY-DUDHdT_e.js.map} +1 -1
  82. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-a5vzSSCo.js → flowDiagram-PKNHOUZH-CT7lC5J1.js} +7 -7
  83. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-a5vzSSCo.js.map → flowDiagram-PKNHOUZH-CT7lC5J1.js.map} +1 -1
  84. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-DIua0Qjr.js → ganttDiagram-A5KZAMGK-BhlcqqDR.js} +3 -3
  85. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-DIua0Qjr.js.map → ganttDiagram-A5KZAMGK-BhlcqqDR.js.map} +1 -1
  86. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-CJt16FXK.js → gitGraphDiagram-K3NZZRJ6-CYLYfqiB.js} +8 -8
  87. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-CJt16FXK.js.map → gitGraphDiagram-K3NZZRJ6-CYLYfqiB.js.map} +1 -1
  88. package/webapp/dist/assets/{graph-lQSuHjYm.js → graph-twAxd7MO.js} +3 -3
  89. package/webapp/dist/assets/{graph-lQSuHjYm.js.map → graph-twAxd7MO.js.map} +1 -1
  90. package/webapp/dist/assets/{index-B7llu28V.js → index-Bc1jJvgU.js} +36 -175
  91. package/webapp/dist/assets/{index-B7llu28V.js.map → index-Bc1jJvgU.js.map} +1 -1
  92. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-DzJuXz5H.js → infoDiagram-LFFYTUFH-BAA1VoC0.js} +6 -6
  93. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-DzJuXz5H.js.map → infoDiagram-LFFYTUFH-BAA1VoC0.js.map} +1 -1
  94. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-BP_s17vw.js → ishikawaDiagram-PHBUUO56-CyPvSx5W.js} +2 -2
  95. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-BP_s17vw.js.map → ishikawaDiagram-PHBUUO56-CyPvSx5W.js.map} +1 -1
  96. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-Lic1mhBM.js → journeyDiagram-4ABVD52K-CbOLT63I.js} +5 -5
  97. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-Lic1mhBM.js.map → journeyDiagram-4ABVD52K-CbOLT63I.js.map} +1 -1
  98. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-Baf2kCwQ.js → kanban-definition-K7BYSVSG-CUS-wMLR.js} +3 -3
  99. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-Baf2kCwQ.js.map → kanban-definition-K7BYSVSG-CUS-wMLR.js.map} +1 -1
  100. package/webapp/dist/assets/{layout-DUMDc8rv.js → layout-C0HW4ZHi.js} +5 -5
  101. package/webapp/dist/assets/{layout-DUMDc8rv.js.map → layout-C0HW4ZHi.js.map} +1 -1
  102. package/webapp/dist/assets/{linear-CZMoHeVH.js → linear-DNEgfkh6.js} +2 -2
  103. package/webapp/dist/assets/{linear-CZMoHeVH.js.map → linear-DNEgfkh6.js.map} +1 -1
  104. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-DB2sQ--_.js → mindmap-definition-YRQLILUH-D5URwhNx.js} +4 -4
  105. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-DB2sQ--_.js.map → mindmap-definition-YRQLILUH-D5URwhNx.js.map} +1 -1
  106. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-Dgdrlric.js → pieDiagram-SKSYHLDU-BQbBOUPk.js} +8 -8
  107. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-Dgdrlric.js.map → pieDiagram-SKSYHLDU-BQbBOUPk.js.map} +1 -1
  108. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-CJcFYfqf.js → quadrantDiagram-337W2JSQ-B_dFbPid.js} +3 -3
  109. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-CJcFYfqf.js.map → quadrantDiagram-337W2JSQ-B_dFbPid.js.map} +1 -1
  110. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-CDsT-ac7.js → requirementDiagram-Z7DCOOCP-CtWDUSB3.js} +4 -4
  111. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-CDsT-ac7.js.map → requirementDiagram-Z7DCOOCP-CtWDUSB3.js.map} +1 -1
  112. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-DiO55skm.js → sankeyDiagram-WA2Y5GQK-CqRH7yj4.js} +2 -2
  113. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-DiO55skm.js.map → sankeyDiagram-WA2Y5GQK-CqRH7yj4.js.map} +1 -1
  114. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-CNHjdBNC.js → sequenceDiagram-2WXFIKYE-B1-5-5Qx.js} +4 -4
  115. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-CNHjdBNC.js.map → sequenceDiagram-2WXFIKYE-B1-5-5Qx.js.map} +1 -1
  116. package/webapp/dist/assets/{stateDiagram-RAJIS63D-CtS3TXEd.js → stateDiagram-RAJIS63D-9krZSpaD.js} +9 -9
  117. package/webapp/dist/assets/{stateDiagram-RAJIS63D-CtS3TXEd.js.map → stateDiagram-RAJIS63D-9krZSpaD.js.map} +1 -1
  118. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-BdjJA1de.js → stateDiagram-v2-FVOUBMTO-DDLC-q0s.js} +5 -5
  119. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-BdjJA1de.js.map → stateDiagram-v2-FVOUBMTO-DDLC-q0s.js.map} +1 -1
  120. package/webapp/dist/assets/{timeline-definition-YZTLITO2-D3AiTIhK.js → timeline-definition-YZTLITO2-C64HbHNO.js} +3 -3
  121. package/webapp/dist/assets/{timeline-definition-YZTLITO2-D3AiTIhK.js.map → timeline-definition-YZTLITO2-C64HbHNO.js.map} +1 -1
  122. package/webapp/dist/assets/{treemap-KZPCXAKY-F6nRvLGK.js → treemap-KZPCXAKY-TEywEA7B.js} +5 -5
  123. package/webapp/dist/assets/{treemap-KZPCXAKY-F6nRvLGK.js.map → treemap-KZPCXAKY-TEywEA7B.js.map} +1 -1
  124. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BoukZEuo.js → vennDiagram-LZ73GAT5-CT9sZ6ft.js} +2 -2
  125. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-BoukZEuo.js.map → vennDiagram-LZ73GAT5-CT9sZ6ft.js.map} +1 -1
  126. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-ByfGkhZz.js → xychartDiagram-JWTSCODW-CEAg_tiv.js} +3 -3
  127. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-ByfGkhZz.js.map → xychartDiagram-JWTSCODW-CEAg_tiv.js.map} +1 -1
  128. package/webapp/dist/index.html +1 -1
  129. package/dist/tools/plan.d.ts +0 -2
  130. package/dist/tools/plan.js +0 -171
  131. package/dist/tools/prompts/codex_style_tools/en/errors.md +0 -102
  132. package/dist/tools/prompts/codex_style_tools/en/index.md +0 -59
  133. package/dist/tools/prompts/codex_style_tools/en/principles.md +0 -87
  134. package/dist/tools/prompts/codex_style_tools/en/scenarios.md +0 -88
  135. package/dist/tools/prompts/codex_style_tools/en/tools.md +0 -119
  136. package/dist/tools/prompts/codex_style_tools/zh/errors.md +0 -102
  137. package/dist/tools/prompts/codex_style_tools/zh/index.md +0 -59
  138. package/dist/tools/prompts/codex_style_tools/zh/principles.md +0 -87
  139. package/dist/tools/prompts/codex_style_tools/zh/scenarios.md +0 -88
  140. package/dist/tools/prompts/codex_style_tools/zh/tools.md +0 -119
package/README.md CHANGED
@@ -203,7 +203,7 @@ Then:
203
203
 
204
204
  Platform note:
205
205
 
206
- - On Windows, the runtime does not register the `codex_style_tools` toolset. Do not grant `codex_style_tools` in `.minds/team.yaml` for Windows hosts.
206
+ - On Windows, the runtime does not register the `codex_inspect_and_patch_tools` toolset. Do not grant `codex_inspect_and_patch_tools` in `.minds/team.yaml` for Windows hosts.
207
207
 
208
208
  ## Start from scratch
209
209
 
package/README.zh.md CHANGED
@@ -142,7 +142,7 @@ dominds
142
142
 
143
143
  平台说明:
144
144
 
145
- - Windows 运行时不会注册 `codex_style_tools` 工具集。在 Windows 主机上的 `.minds/team.yaml` 中不要授予 `codex_style_tools`。
145
+ - Windows 运行时不会注册 `codex_inspect_and_patch_tools` 工具集。在 Windows 主机上的 `.minds/team.yaml` 中不要授予 `codex_inspect_and_patch_tools`。
146
146
 
147
147
  ## 从零开始(空文件夹启动)
148
148
 
@@ -444,7 +444,7 @@ Suggested team shape:
444
444
  - non-goal: does not directly edit product code or take over build/process management.
445
445
  - `web_developer`
446
446
  - primary responsibility: implement UI/interaction fixes, consume `web_tester` findings, and close the loop;
447
- - default toolsets: code-edit/search tools such as `codex_style_tools` (or equivalent) plus optional access to read tester evidence;
447
+ - default toolsets: code-edit/search tools such as `codex_inspect_and_patch_tools` (or equivalent) plus optional access to read tester evidence;
448
448
  - non-goal: should not blur browser acceptance into an implicit “I also tested it” posture; when acceptance is needed, it should explicitly tell/ask `web_tester`.
449
449
 
450
450
  Suggested `team.yaml` fragment:
@@ -463,7 +463,7 @@ members:
463
463
  icon: '🛠️'
464
464
  toolsets:
465
465
  - ws_read
466
- - codex_style_tools
466
+ - codex_inspect_and_patch_tools
467
467
  ```
468
468
 
469
469
  Requirements for the `playwright_interactive` toolset design:
@@ -443,7 +443,7 @@ Web Dev App 需要明确区分三套命名,避免再次漂移:
443
443
  - 非目标:不直接修改业务代码;不接管构建/进程管理。
444
444
  - `web_developer`
445
445
  - 主要职责:实现页面/UI/交互修复,消费 `web_tester` 的缺陷报告并完成闭环。
446
- - 默认工具集:代码修改/检索工具(如 `codex_style_tools` 或等价工具)+ 可选读取 `web_tester` 产出的证据。
446
+ - 默认工具集:代码修改/检索工具(如 `codex_inspect_and_patch_tools` 或等价工具)+ 可选读取 `web_tester` 产出的证据。
447
447
  - 非目标:不把浏览器验收职责模糊地“顺手做掉”;需要时应显式 tellask `web_tester` 做验收。
448
448
 
449
449
  建议的 `team.yaml` 片段:
@@ -462,7 +462,7 @@ members:
462
462
  icon: '🛠️'
463
463
  toolsets:
464
464
  - ws_read
465
- - codex_style_tools
465
+ - codex_inspect_and_patch_tools
466
466
  ```
467
467
 
468
468
  关于 `playwright_interactive` 工具集的设计要求:
@@ -168,6 +168,8 @@ When a dialog still carries an inter-dialog reply obligation, but the user tempo
168
168
  5. As long as the user keeps sending new messages, the dialog stays in temporary interjection-chat handling, and that paused projection remains in place only if it was established in the first place.
169
169
  6. Only an explicit UI `Continue` attempts to restore the original task.
170
170
 
171
+ **Strict boundary**: a formal `askHuman` answer is not part of this "user interjection" category. As soon as a prompt carries a real `q4hAnswerCallId`, it belongs to the askHuman reply channel and semantically continues an already-materialized question/answer chain; it must never be downgraded into temporary local side-chat.
172
+
171
173
  **Key point**: this `stopped` state is only a temporary run-control / UI projection. It is not the same as an ordinary system-stop failure, and it is not the final business source of truth. It also does not apply to every interjection; it exists only when there really is a parked original task to resume.
172
174
 
173
175
  After the user clicks `Continue`, the backend MUST re-evaluate fresh persistence facts and decide which true-source case now applies. It must not infer the result purely from the visible `displayState`:
@@ -183,6 +185,7 @@ After the user clicks `Continue`, the backend MUST re-evaluate fresh persistence
183
185
 
184
186
  - `refreshRunControlProjectionFromPersistenceFacts()` MUST preserve the special "interjection handled; original task paused" `stopped` projection until the user explicitly clicks `Continue`; otherwise the UI collapses back to ordinary `blocked` too early and breaks multi-turn interjection UX. Conversely, when there is no parked original task, this paused projection should not be created at all.
185
187
  - The actual outcome of `Continue` MUST be decided in the resume drive path by re-reading fresh persistence facts. "Continue is clickable" does not mean "the dialog will definitely enter proceeding immediately".
188
+ - If `Continue` reveals that the true state is still `blocked`, the reply-obligation reassertion copy should be materialized immediately as a runtime guide in both `dlg.msgs` and persisted course history, while also surfacing as a frontend bubble. That lets the later real resume path rely on ordinary context replay instead of synthesizing a second duplicate runtime prompt.
186
189
  - The run-control toolbar's `resumable` count should align with "manual Continue attempt is meaningful". Therefore an interjection-paused `stopped` dialog still counts as resumable even when underlying blocker facts remain, because the business meaning of `Continue` there is "exit the temporary paused projection and re-evaluate from source-of-truth facts".
187
190
 
188
191
  **Mental-model warning**:
@@ -190,6 +193,7 @@ After the user clicks `Continue`, the backend MUST re-evaluate fresh persistence
190
193
  - Do not reason about this flow from `displayState.kind === 'stopped'` alone.
191
194
  - Do not reason about it from blocker facts alone and then wonder why the UI still shows `stopped`.
192
195
  - Do not reason about it from `resume_dialog` eligibility alone and assume resumption always means immediate running.
196
+ - Do not flatten every `origin === 'user'` prompt into "interjection"; a non-empty `q4hAnswerCallId` means askHuman answer continuation and follows a different semantic path.
193
197
 
194
198
  You need all of the following together to understand the behavior correctly:
195
199
 
@@ -167,6 +167,8 @@
167
167
  5. 只要用户继续发送新消息,就继续作为插话临时对话处理;这个 paused projection 仅在它已被建立时持续保持。
168
168
  6. 只有用户显式点击 UI `Continue`,系统才尝试恢复原任务。
169
169
 
170
+ **严格边界**:`askHuman` 的正式回答不属于这里的“用户插话”。只要一条 prompt 带着真实的 `q4hAnswerCallId`,它就属于 askHuman 回复通道,语义上是在继续已 materialize 的提问/应答链路,绝不能被压入“本地临时插话聊天”。
171
+
170
172
  **关键点**:这里的 `stopped` 只是一个临时 run-control / UI 投影,不等于普通 system-stop 失败,也不是最终的业务真源;并且它不是所有插话都会出现,只在“确有一个待恢复的原任务被临时停靠”时出现。
171
173
 
172
174
  点击 `Continue` 后,后端必须重新从 persistence 真源判定当前对话属于哪一种情况,而不能只根据表面的 `displayState` 做静态推断:
@@ -182,6 +184,7 @@
182
184
 
183
185
  - `refreshRunControlProjectionFromPersistenceFacts()` 在用户尚未点击 `Continue` 前,必须保留这层“插话已处理;原任务已暂停”的 `stopped` 投影;否则 UI 会过早塌回普通 `blocked`,破坏多轮插话体验。反过来,如果当前其实没有待恢复原任务,则根本不应建立这层 paused projection。
184
186
  - 真正决定 `Continue` 结果的逻辑,必须在恢复驱动路径中重新读取 fresh persistence facts;不能把“可点 Continue”误解为“必然立即 proceeding”。
187
+ - 若 `Continue` 后真源仍是 `blocked`,回复责任重申文案应当立即作为 runtime guide 同时进入 `dlg.msgs` 与持久化课程历史,并同步发前端气泡;这样后面真正恢复 drive 时只需正常读取上下文,不应再额外补发一条重复的 runtime prompt。
185
188
  - run-control 工具栏中的 `resumable` 计数,应与“是否允许手动 Continue 尝试”保持一致。因此,处于 interjection-paused `stopped` 的对话即便底层仍有 blocker,也应计入 `resumable`;因为 `Continue` 的业务语义正是“退出这层临时 paused projection,并从真源重判下一步”。
186
189
 
187
190
  **心智模型提醒**:
@@ -189,6 +192,7 @@
189
192
  - 不能只看 `displayState.kind === 'stopped'` 就理解这条链路。
190
193
  - 不能只看 blocker facts 就理解为什么 UI 仍显示 `stopped`。
191
194
  - 也不能只看 `resume_dialog` eligibility 就推断恢复后一定马上运行。
195
+ - 更不能把所有 `origin === 'user'` 的输入都笼统视作“用户插话”;`q4hAnswerCallId` 非空的 prompt 是 askHuman answer continuation,必须按另一条语义链处理。
192
196
 
193
197
  必须把以下几块一起看,才能形成完整且精确的理解:
194
198
 
@@ -533,7 +533,7 @@ Best practices:
533
533
 
534
534
  - Make `member_defaults` conservative. Grant additional tools/dirs on a per-member basis.
535
535
  - Prefer toolsets over individually enumerating tools unless you need a one-off tool.
536
- - Platform note: Windows runtime intentionally does not register `codex_style_tools`; do not grant that toolset in `.minds/team.yaml` on Windows hosts.
536
+ - Platform note: Windows runtime intentionally does not register `codex_inspect_and_patch_tools`; do not grant that toolset in `.minds/team.yaml` on Windows hosts.
537
537
  - Keep `.minds/team.yaml` ownership tight; only the team manager should be able to edit it.
538
538
  - Avoid repeating built-in constraints in `team.yaml`:
539
539
  - `*.tsk/**` (encapsulated Taskdocs) are hard-denied for all general file tools.
@@ -452,7 +452,7 @@ members:
452
452
 
453
453
  - 使 `member_defaults` 保守。按成员授予额外的工具/目录
454
454
  - 优先使用工具集而不是单独枚举工具,除非你需要一次性工具
455
- - 平台说明:Windows 运行时不会注册 `codex_style_tools`;在 Windows 主机上的 `.minds/team.yaml` 中不要授予该工具集
455
+ - 平台说明:Windows 运行时不会注册 `codex_inspect_and_patch_tools`;在 Windows 主机上的 `.minds/team.yaml` 中不要授予该工具集
456
456
  - 保持 `.minds/team.yaml` 的所有权严格;只有团队管理者应该能够编辑它
457
457
  - 避免在 `team.yaml` 中重复内置约束:
458
458
  - `*.tsk/**`(封装的差遣牒任务包)对所有通用文件工具被硬性拒绝
@@ -11,15 +11,7 @@ import type { FuncTool } from '../../tool';
11
11
  import type { ChatMessage, ProviderConfig } from '../client';
12
12
  import type { LlmBatchResult, LlmFailureDisposition, LlmGenerator, LlmRequestContext, LlmStreamReceiver, LlmStreamResult } from '../gen';
13
13
  export declare function resolveCodexServiceTier(serviceTier: ChatGptResponsesRequest['service_tier'] | undefined): Exclude<NonNullable<ChatGptResponsesRequest['service_tier']>, 'default'> | undefined;
14
- export declare function spliceCodexBuiltinPrompt(params: {
15
- template: string;
16
- defaultModel: string;
17
- loadPrompt: (model: string) => string | null;
18
- }): string;
19
- export declare function resolveCodexInstructions(systemPrompt: string, options?: {
20
- defaultModel?: string;
21
- loadPrompt?: (model: string) => string | null;
22
- }): string;
14
+ export declare function resolveCodexInstructions(systemPrompt: string): string;
23
15
  export declare class CodexGen implements LlmGenerator {
24
16
  get apiType(): string;
25
17
  classifyFailure(error: unknown): LlmFailureDisposition | undefined;
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CodexGen = void 0;
37
37
  exports.resolveCodexServiceTier = resolveCodexServiceTier;
38
- exports.spliceCodexBuiltinPrompt = spliceCodexBuiltinPrompt;
39
38
  exports.resolveCodexInstructions = resolveCodexInstructions;
40
39
  const log_1 = require("../../log");
41
40
  const i18n_text_1 = require("../../runtime/i18n-text");
@@ -47,7 +46,6 @@ const tool_output_limit_1 = require("./tool-output-limit");
47
46
  const tool_result_image_ingest_1 = require("./tool-result-image-ingest");
48
47
  const log = (0, log_1.createLogger)('llm/codex');
49
48
  const codexFallbackInstructions = 'You are Codex CLI.';
50
- const CODEX_SYSTEM_PROMPT_DIRECTIVE_PATTERN = /^([ \t]*)@codex-system-prompt(?::([A-Za-z0-9._-]+))?([ \t]*)$/gm;
51
49
  function resolveCodexServiceTier(serviceTier) {
52
50
  // The ChatGPT codex backend rejects the literal `default` tier even though some SDK typings
53
51
  // still list it. Omitting the field preserves the standard tier semantics without a 400.
@@ -116,29 +114,8 @@ function tryExtractApiReturnedModel(value) {
116
114
  const trimmed = model.trim();
117
115
  return trimmed.length > 0 ? trimmed : undefined;
118
116
  }
119
- function spliceCodexBuiltinPrompt(params) {
120
- let replaced = false;
121
- const resolved = params.template.replace(CODEX_SYSTEM_PROMPT_DIRECTIVE_PATTERN, (_match, leading, overrideModel, trailing) => {
122
- const selectedModel = overrideModel ?? params.defaultModel;
123
- const prompt = params.loadPrompt(selectedModel);
124
- if (prompt === null) {
125
- throw new Error(`Bundled Codex prompt template not found for model: ${selectedModel}`);
126
- }
127
- replaced = true;
128
- return `${leading}${prompt}${trailing}`;
129
- });
130
- return replaced ? resolved : params.template;
131
- }
132
- function resolveCodexInstructions(systemPrompt, options) {
133
- const baseInstructions = systemPrompt.trim().length > 0 ? systemPrompt : codexFallbackInstructions;
134
- if (options?.defaultModel === undefined || options.loadPrompt === undefined) {
135
- return baseInstructions;
136
- }
137
- return spliceCodexBuiltinPrompt({
138
- template: baseInstructions,
139
- defaultModel: options.defaultModel,
140
- loadPrompt: options.loadPrompt,
141
- });
117
+ function resolveCodexInstructions(systemPrompt) {
118
+ return systemPrompt.trim().length > 0 ? systemPrompt : codexFallbackInstructions;
142
119
  }
143
120
  function funcToolToCodex(funcTool) {
144
121
  // MCP schemas are passed through to providers. Codex tool schema types are narrower; runtime
@@ -534,10 +511,7 @@ class CodexGen {
534
511
  if (!agent.model) {
535
512
  throw new Error(`Internal error: Model is undefined for agent '${agent.id}'`);
536
513
  }
537
- const instructions = resolveCodexInstructions(systemPrompt, {
538
- defaultModel: agent.model,
539
- loadPrompt: codexAuth.loadCodexPromptSync,
540
- });
514
+ const instructions = resolveCodexInstructions(systemPrompt);
541
515
  const payload = await buildCodexRequest(providerConfig, agent, instructions, funcTools, requestContext, context, receiver.toolResultImageIngest);
542
516
  let sayingStarted = false;
543
517
  let thinkingStarted = false;
@@ -807,7 +807,6 @@ async function emitAssistantSaying(dlg, content) {
807
807
  await dlg.sayingChunk(content);
808
808
  await dlg.sayingFinish();
809
809
  }
810
- const KERNEL_DRIVER_IDENTICAL_UPDATE_PLAN_ONLY_STOP_THRESHOLD = 3;
811
810
  function resolveFuncToolFollowupMode(tool) {
812
811
  return tool?.followupMode ?? 'immediate';
813
812
  }
@@ -820,20 +819,6 @@ function shouldImmediatelyFollowUpToolOutcome(tool, outcome) {
820
819
  }
821
820
  return shouldImmediatelyFollowUpSuccessfulToolResult(tool);
822
821
  }
823
- function formatIdenticalUpdatePlanLoopStopDetail(language) {
824
- return language === 'zh'
825
- ? `检测到连续 ${String(KERNEL_DRIVER_IDENTICAL_UPDATE_PLAN_ONLY_STOP_THRESHOLD)} 次完全相同的 update_plan 自动续跑,已停止本次自动执行以避免自激发循环。你可以点击“继续”人工观察,或先修改计划内容再继续。`
826
- : `Detected ${String(KERNEL_DRIVER_IDENTICAL_UPDATE_PLAN_ONLY_STOP_THRESHOLD)} consecutive identical update_plan auto-follow-up rounds. Stopped this automatic run to avoid a self-trigger loop. You can click Continue to inspect manually, or change the plan content before resuming.`;
827
- }
828
- function extractAssistantSayingRoundSignature(newMsgs) {
829
- const contents = newMsgs
830
- .filter((msg) => msg.type === 'saying_msg' && msg.role === 'assistant')
831
- .map((msg) => msg.content);
832
- if (contents.length === 0) {
833
- return undefined;
834
- }
835
- return JSON.stringify(contents);
836
- }
837
822
  async function executeFunctionCalls(args) {
838
823
  const functionPromises = args.funcCalls.map(async (func) => {
839
824
  throwIfAborted(args.abortSignal, args.dlg);
@@ -933,7 +918,6 @@ async function executeFunctionRound(args) {
933
918
  shouldStopAfterReplyTool: false,
934
919
  pairedMessages: [],
935
920
  tellaskToolOutputs: [],
936
- updatePlanOnlyRound: false,
937
921
  };
938
922
  }
939
923
  throwIfAborted(args.abortSignal, args.dlg);
@@ -977,24 +961,6 @@ async function executeFunctionRound(args) {
977
961
  }
978
962
  return shouldImmediatelyFollowUpToolOutcome(tool, outcome);
979
963
  });
980
- const updatePlanOnlyRound = (() => {
981
- if (tellaskRound.normalCalls.length === 0) {
982
- return false;
983
- }
984
- if (tellaskRound.handledCallIds.length > 0 || tellaskRound.toolOutputs.length > 0) {
985
- return false;
986
- }
987
- for (const call of tellaskRound.normalCalls) {
988
- if (call.name !== 'update_plan') {
989
- return false;
990
- }
991
- const outcome = genericOutcomeByCallId.get(call.id);
992
- if (outcome !== 'success') {
993
- return false;
994
- }
995
- }
996
- return true;
997
- })();
998
964
  const resultByCallId = new Map();
999
965
  const register = (result) => {
1000
966
  const existing = resultByCallId.get(result.id);
@@ -1043,7 +1009,6 @@ async function executeFunctionRound(args) {
1043
1009
  shouldStopAfterReplyTool: tellaskRound.shouldStopAfterReplyTool,
1044
1010
  pairedMessages,
1045
1011
  tellaskToolOutputs: [...tellaskRound.toolOutputs],
1046
- updatePlanOnlyRound,
1047
1012
  };
1048
1013
  }
1049
1014
  async function resetDiligenceBudgetAfterQ4H(dlg, team) {
@@ -1202,8 +1167,6 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1202
1167
  let retryStoppedRecoveryPrompt;
1203
1168
  let skipTaskdocForThisDrive = humanPrompt?.skipTaskdoc === true;
1204
1169
  let genIterNo = 0;
1205
- let consecutiveIdenticalUpdatePlanOnlyRounds = 0;
1206
- let lastIdenticalUpdatePlanRoundSignature;
1207
1170
  // Quirk retry state intentionally spans multiple request invocations in the same driver run,
1208
1171
  // including course changes. Provider/API retry heuristics are tracked independently from
1209
1172
  // user-facing course boundaries.
@@ -1417,16 +1380,28 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1417
1380
  if (currentPrompt.origin === 'user' &&
1418
1381
  replyGuidance.suppressInterDialogReplyGuidance &&
1419
1382
  replyGuidance.deferredReplyReassertionDirective !== undefined) {
1383
+ // WARNING:
1384
+ // User interjection suppression is a reversible state transition, not a one-shot
1385
+ // latch. The normal cycle is:
1386
+ // - user interjects -> suppress reply obligation
1387
+ // - operator clicks Continue -> restore reply obligation
1388
+ // - user interjects again -> suppress it again
1389
+ //
1390
+ // Therefore a repeated interjection after a blocked Continue MUST re-arm the deferred
1391
+ // state and re-materialize the suppression guide, even when the underlying reply
1392
+ // directive itself did not change.
1420
1393
  const existingDeferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dlg.id, dlg.status);
1421
1394
  const nextDeferredReplyReassertion = {
1422
- reason: 'user_interjection_while_pending_subdialog',
1395
+ reason: 'user_interjection_with_parked_original_task',
1423
1396
  directive: replyGuidance.deferredReplyReassertionDirective,
1424
1397
  };
1425
- if (existingDeferredReplyReassertion === undefined ||
1426
- !hasSameReplyDirective(existingDeferredReplyReassertion.directive, nextDeferredReplyReassertion.directive)) {
1398
+ const mustRearmDeferredReplyReassertion = existingDeferredReplyReassertion === undefined ||
1399
+ existingDeferredReplyReassertion.resumeGuideSurfaced === true ||
1400
+ !hasSameReplyDirective(existingDeferredReplyReassertion.directive, nextDeferredReplyReassertion.directive);
1401
+ if (mustRearmDeferredReplyReassertion) {
1427
1402
  await persistence_1.DialogPersistence.setDeferredReplyReassertion(dlg.id, nextDeferredReplyReassertion, dlg.status);
1428
1403
  }
1429
- if (existingDeferredReplyReassertion === undefined) {
1404
+ if (mustRearmDeferredReplyReassertion) {
1430
1405
  currentRuntimeGuideMsg = replyGuidance.transientGuideContent
1431
1406
  ? {
1432
1407
  type: 'transient_guide_msg',
@@ -1466,6 +1441,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1466
1441
  currentRuntimeGuideMsg = undefined;
1467
1442
  }
1468
1443
  if (isQ4HAnswerPrompt) {
1444
+ if (!replyGuidance.isQ4HAnswerPrompt) {
1445
+ throw new Error(`kernel-driver q4h answer classification invariant violation: msgId=${currentPrompt.msgId} was parsed as q4h answer before reply-guidance but not after`);
1446
+ }
1469
1447
  // Record only the answered call correlation / user language for the resumed round.
1470
1448
  // The actual human answer fact was already persisted via askHuman tellask result flow.
1471
1449
  await dlg.receiveHumanReply({
@@ -1474,6 +1452,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1474
1452
  q4hAnswerCallId,
1475
1453
  });
1476
1454
  }
1455
+ else if (replyGuidance.isQ4HAnswerPrompt) {
1456
+ throw new Error(`kernel-driver q4h answer classification invariant violation: msgId=${currentPrompt.msgId} was classified as q4h answer by reply-guidance without a normalized q4hAnswerCallId`);
1457
+ }
1477
1458
  else {
1478
1459
  await dlg.addChatMessages({
1479
1460
  type: 'prompting_msg',
@@ -1992,25 +1973,6 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1992
1973
  newMsgs.push(...routed.pairedMessages);
1993
1974
  }
1994
1975
  await dlg.addChatMessages(...newMsgs);
1995
- const assistantSayingRoundSignature = extractAssistantSayingRoundSignature(newMsgs);
1996
- const identicalUpdatePlanRoundSignature = routed.updatePlanOnlyRound
1997
- ? JSON.stringify({
1998
- assistantSayingRoundSignature: assistantSayingRoundSignature ?? '',
1999
- })
2000
- : undefined;
2001
- if (identicalUpdatePlanRoundSignature !== undefined) {
2002
- if (identicalUpdatePlanRoundSignature === lastIdenticalUpdatePlanRoundSignature) {
2003
- consecutiveIdenticalUpdatePlanOnlyRounds += 1;
2004
- }
2005
- else {
2006
- consecutiveIdenticalUpdatePlanOnlyRounds = 1;
2007
- lastIdenticalUpdatePlanRoundSignature = identicalUpdatePlanRoundSignature;
2008
- }
2009
- }
2010
- else {
2011
- consecutiveIdenticalUpdatePlanOnlyRounds = 0;
2012
- lastIdenticalUpdatePlanRoundSignature = undefined;
2013
- }
2014
1976
  const persistedFbrState = await loadDialogFbrState(dlg);
2015
1977
  if (persistedFbrState) {
2016
1978
  if (persistedFbrState.phase === 'finalization') {
@@ -2096,27 +2058,6 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2096
2058
  await resetDiligenceBudgetAfterQ4H(dlg, team);
2097
2059
  break;
2098
2060
  }
2099
- if (consecutiveIdenticalUpdatePlanOnlyRounds >=
2100
- KERNEL_DRIVER_IDENTICAL_UPDATE_PLAN_ONLY_STOP_THRESHOLD) {
2101
- const detail = formatIdenticalUpdatePlanLoopStopDetail((0, work_language_1.getWorkLanguage)());
2102
- log_1.log.error(detail, new Error('kernel_driver_identical_update_plan_loop_stopped'), {
2103
- dialogId: dlg.id.valueOf(),
2104
- rootId: dlg.id.rootId,
2105
- selfId: dlg.id.selfId,
2106
- course: dlg.currentCourse,
2107
- genseq: dlg.activeGenSeqOrUndefined ?? null,
2108
- consecutiveIdenticalUpdatePlanOnlyRounds,
2109
- identicalUpdatePlanRoundSignature,
2110
- });
2111
- throw new KernelDriverInterruptedError({
2112
- kind: 'system_stop',
2113
- detail,
2114
- i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
2115
- detail,
2116
- kind: 'identical_update_plan_loop',
2117
- }),
2118
- });
2119
- }
2120
2061
  // Start an immediate post-tool generation only when this round produced tool outputs that
2121
2062
  // warrant same-drive LLM reaction right away. Provider-native side-channel UI events are
2122
2063
  // meaningful output, but they are not transcript/context inputs and therefore must not
@@ -6,6 +6,7 @@ const run_control_1 = require("../../apps/run-control");
6
6
  const dialog_1 = require("../../dialog");
7
7
  const dialog_display_state_1 = require("../../dialog-display-state");
8
8
  const dialog_global_registry_1 = require("../../dialog-global-registry");
9
+ const evt_registry_1 = require("../../evt-registry");
9
10
  const log_1 = require("../../log");
10
11
  const load_1 = require("../../minds/load");
11
12
  const persistence_1 = require("../../persistence");
@@ -271,6 +272,15 @@ async function maybeResolveDeferredReplyReassertionPrompt(dialog) {
271
272
  await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
272
273
  return undefined;
273
274
  }
275
+ // WARNING:
276
+ // `resumeGuideSurfaced` means the reply-obligation reassertion has already been materialized as a
277
+ // runtime guide and injected into both dialog.msgs and persisted course history at blocked
278
+ // Continue time. Once that has happened, later real resume must not emit a second visible prompt:
279
+ // normal context replay is now the source of truth for the model-facing reminder.
280
+ if (deferredReplyReassertion.resumeGuideSurfaced === true) {
281
+ await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
282
+ return undefined;
283
+ }
274
284
  await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
275
285
  const language = (0, work_language_1.getWorkLanguage)();
276
286
  return {
@@ -286,6 +296,51 @@ async function maybeResolveDeferredReplyReassertionPrompt(dialog) {
286
296
  tellaskReplyDirective: deferredReplyReassertion.directive,
287
297
  };
288
298
  }
299
+ async function maybeSurfaceDeferredReplyReassertionGuideForBlockedContinue(dialog) {
300
+ const deferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dialog.id, dialog.status);
301
+ if (!deferredReplyReassertion || deferredReplyReassertion.resumeGuideSurfaced === true) {
302
+ return;
303
+ }
304
+ const activeDirective = await (0, tellask_special_1.loadLatestActiveTellaskReplyDirective)(dialog);
305
+ if (!activeDirective ||
306
+ activeDirective.targetCallId !== deferredReplyReassertion.directive.targetCallId) {
307
+ await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
308
+ return;
309
+ }
310
+ const language = (0, work_language_1.getWorkLanguage)();
311
+ const content = await (0, reply_guidance_1.buildReplyObligationReassertionPrompt)({
312
+ dlg: dialog,
313
+ directive: deferredReplyReassertion.directive,
314
+ language,
315
+ });
316
+ const genseq = dialog.activeGenSeqOrUndefined ?? 1;
317
+ // WARNING:
318
+ // This helper intentionally does three things at once:
319
+ // 1. append the guide into dialog.msgs so an in-memory later resume sees it naturally;
320
+ // 2. persist a runtime_guide_record so reload/replay reconstructs the same context;
321
+ // 3. emit runtime_guide_evt so the user immediately sees the reassertion bubble after Continue.
322
+ //
323
+ // Do not "optimize" this into only an event or only a deferred prompt. The whole point is that
324
+ // once blocked Continue is clicked, the guide becomes a first-class historical context fact and
325
+ // later real driving should need no special duplicate reassertion path.
326
+ await dialog.addChatMessages({
327
+ type: 'transient_guide_msg',
328
+ role: 'assistant',
329
+ content,
330
+ });
331
+ await persistence_1.DialogPersistence.persistRuntimeGuide(dialog, content, genseq);
332
+ (0, evt_registry_1.postDialogEvent)(dialog, {
333
+ type: 'runtime_guide_evt',
334
+ course: dialog.currentCourse,
335
+ genseq,
336
+ content,
337
+ });
338
+ await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, {
339
+ reason: 'user_interjection_with_parked_original_task',
340
+ directive: deferredReplyReassertion.directive,
341
+ resumeGuideSurfaced: true,
342
+ }, dialog.status);
343
+ }
289
344
  async function resolveEffectivePrompt(dialog, humanPrompt) {
290
345
  if (humanPrompt) {
291
346
  return { prompt: humanPrompt, fromUpNext: false };
@@ -439,6 +494,7 @@ async function executeDriveRound(args) {
439
494
  subdialogs: suspension.subdialogs,
440
495
  });
441
496
  await (0, dialog_display_state_1.setDialogDisplayState)(dialog.id, restoredState);
497
+ await maybeSurfaceDeferredReplyReassertionGuideForBlockedContinue(dialog);
442
498
  log_1.log.debug('kernel-driver continue after interjection pause restored true suspended state from fresh persistence facts', undefined, {
443
499
  dialogId: dialog.id.valueOf(),
444
500
  restoredState,
@@ -582,8 +638,13 @@ async function executeDriveRound(args) {
582
638
  // suppressed a still-pending inter-dialog reply obligation that must be reasserted later.
583
639
  // User interjections without a parked original task should simply finish and fall back to the
584
640
  // dialog's true underlying state, without showing the special stopped panel.
641
+ //
642
+ // Q4H answers are explicitly outside this branch even though they also come from the human.
643
+ // They belong to the askHuman reply channel and must continue the suspended askHuman round,
644
+ // never be mistaken for ad hoc interjection chat.
585
645
  shouldPauseAfterLocalUserInterjection =
586
646
  effectivePrompt?.origin === 'user' &&
647
+ !replyGuidance.isQ4HAnswerPrompt &&
587
648
  replyGuidance.suppressInterDialogReplyGuidance &&
588
649
  replyGuidance.deferredReplyReassertionDirective !== undefined;
589
650
  activeTellaskReplyDirective = replyGuidance.activeReplyDirective;
@@ -12,6 +12,7 @@ export declare function resolvePromptReplyGuidance(args: {
12
12
  }): Promise<{
13
13
  activeReplyDirective: KernelDriverHumanPrompt['tellaskReplyDirective'];
14
14
  deferredReplyReassertionDirective: KernelDriverHumanPrompt['tellaskReplyDirective'];
15
+ isQ4HAnswerPrompt: boolean;
15
16
  promptContent: string | undefined;
16
17
  persistedTellaskReplyDirective: KernelDriverHumanPrompt['tellaskReplyDirective'];
17
18
  suppressInterDialogReplyGuidance: boolean;
@@ -92,6 +92,8 @@ async function shouldSuppressInterDialogReplyGuidanceForUserInterjection(args) {
92
92
  // blocked or resume real driving.
93
93
  //
94
94
  // Do not "simplify" this into a pure display-state check or a pure pending-subdialog check.
95
+ // Proceeding dialogs with a still-active reply obligation are part of the same rule: a fresh
96
+ // user interjection should still suppress the live reply obligation and answer locally first.
95
97
  // The business anchor is the deferred reply reassertion, while the paused execution marker keeps
96
98
  // repeated interjection turns behaving as local side conversation until explicit Continue.
97
99
  const prompt = args.prompt;
@@ -105,13 +107,17 @@ async function shouldSuppressInterDialogReplyGuidanceForUserInterjection(args) {
105
107
  return false;
106
108
  }
107
109
  const latest = await persistence_1.DialogPersistence.loadDialogLatest(args.dlg.id, args.dlg.status);
108
- if (latest?.deferredReplyReassertion?.reason === 'user_interjection_while_pending_subdialog') {
110
+ if (latest?.deferredReplyReassertion?.reason === 'user_interjection_with_parked_original_task') {
109
111
  return true;
110
112
  }
111
113
  if (latest?.executionMarker?.kind === 'interrupted' &&
112
114
  (0, interjection_pause_stop_1.isUserInterjectionPauseStopReason)(latest.executionMarker.reason)) {
113
115
  return true;
114
116
  }
117
+ const activeReplyDirective = await (0, tellask_special_1.loadLatestActiveTellaskReplyDirective)(args.dlg);
118
+ if (activeReplyDirective) {
119
+ return true;
120
+ }
115
121
  // Use strict persistence reads here. This branch changes business behavior, so a read failure
116
122
  // must loud-fail the round instead of being silently treated as "pending subdialogs exist".
117
123
  const pendingSubdialogs = await persistence_1.DialogPersistence.loadPendingSubdialogs(args.dlg.id, args.dlg.status);
@@ -119,10 +125,13 @@ async function shouldSuppressInterDialogReplyGuidanceForUserInterjection(args) {
119
125
  }
120
126
  async function resolvePromptReplyGuidance(args) {
121
127
  const prompt = args.prompt;
122
- const suppressInterDialogReplyGuidance = await shouldSuppressInterDialogReplyGuidanceForUserInterjection({
123
- dlg: args.dlg,
124
- prompt,
125
- });
128
+ const isQ4HAnswerPrompt = typeof prompt?.q4hAnswerCallId === 'string' && prompt.q4hAnswerCallId.trim() !== '';
129
+ const suppressInterDialogReplyGuidance = isQ4HAnswerPrompt
130
+ ? false
131
+ : await shouldSuppressInterDialogReplyGuidanceForUserInterjection({
132
+ dlg: args.dlg,
133
+ prompt,
134
+ });
126
135
  const availableReplyDirective = prompt?.tellaskReplyDirective ?? (await (0, tellask_special_1.loadLatestActiveTellaskReplyDirective)(args.dlg));
127
136
  const activeReplyDirective = suppressInterDialogReplyGuidance
128
137
  ? undefined
@@ -142,6 +151,7 @@ async function resolvePromptReplyGuidance(args) {
142
151
  deferredReplyReassertionDirective: suppressInterDialogReplyGuidance
143
152
  ? availableReplyDirective
144
153
  : undefined,
154
+ isQ4HAnswerPrompt,
145
155
  promptContent,
146
156
  persistedTellaskReplyDirective: prompt?.tellaskReplyDirective ?? activeReplyDirective,
147
157
  suppressInterDialogReplyGuidance,
@@ -1,5 +1,5 @@
1
1
  import type { DialogDisplayTextI18n } from '@longrun-ai/kernel/types/display-state';
2
- type HumanStopReasonKind = 'aborted' | 'transport_interrupted' | 'malformed_stream' | 'conflicting_stream' | 'invalid_tool_call' | 'incomplete_tool_call_stream' | 'invalid_tool_context' | 'provider_rejected' | 'request_failed' | 'identical_update_plan_loop' | 'generic';
2
+ type HumanStopReasonKind = 'aborted' | 'transport_interrupted' | 'malformed_stream' | 'conflicting_stream' | 'invalid_tool_call' | 'incomplete_tool_call_stream' | 'invalid_tool_context' | 'provider_rejected' | 'request_failed' | 'generic';
3
3
  export declare function buildHumanSystemStopReasonTextI18n(args: {
4
4
  detail: string;
5
5
  providerName?: string;
@@ -82,10 +82,6 @@ function renderHumanStopReason(args) {
82
82
  return args.language === 'zh'
83
83
  ? `本次生成因上游报错而停止。${formatUpstreamRawMessage(args.detail, 'zh')}`
84
84
  : `This generation was stopped because the upstream service returned an error. ${formatUpstreamRawMessage(args.detail, 'en')}`;
85
- case 'identical_update_plan_loop':
86
- return args.language === 'zh'
87
- ? '模型连续重复相同的 update_plan 结果,本次生成已停止。'
88
- : 'The model repeated the same update_plan result multiple times. This generation was stopped.';
89
85
  case 'generic':
90
86
  return args.language === 'zh'
91
87
  ? `本次生成已因系统错误停止。${formatUpstreamRawMessage(args.detail, 'zh')}`
@@ -1003,12 +1003,16 @@ function parseDialogLatestFile(value) {
1003
1003
  return undefined;
1004
1004
  if (!isRecord(deferredReplyReassertionRaw))
1005
1005
  return null;
1006
- if (deferredReplyReassertionRaw.reason !== 'user_interjection_while_pending_subdialog') {
1006
+ if (deferredReplyReassertionRaw.reason !== 'user_interjection_with_parked_original_task') {
1007
1007
  return null;
1008
1008
  }
1009
1009
  const directiveRaw = deferredReplyReassertionRaw.directive;
1010
1010
  if (!isRecord(directiveRaw))
1011
1011
  return null;
1012
+ const resumeGuideSurfacedRaw = deferredReplyReassertionRaw.resumeGuideSurfaced;
1013
+ if (resumeGuideSurfacedRaw !== undefined && typeof resumeGuideSurfacedRaw !== 'boolean') {
1014
+ return null;
1015
+ }
1012
1016
  const expectedReplyCallName = directiveRaw.expectedReplyCallName;
1013
1017
  const targetCallId = directiveRaw.targetCallId;
1014
1018
  const tellaskContent = directiveRaw.tellaskContent;
@@ -1025,22 +1029,28 @@ function parseDialogLatestFile(value) {
1025
1029
  if (typeof targetDialogId !== 'string')
1026
1030
  return null;
1027
1031
  return {
1028
- reason: 'user_interjection_while_pending_subdialog',
1032
+ reason: 'user_interjection_with_parked_original_task',
1029
1033
  directive: {
1030
1034
  expectedReplyCallName,
1031
1035
  targetCallId,
1032
1036
  targetDialogId,
1033
1037
  tellaskContent,
1034
1038
  },
1039
+ ...(resumeGuideSurfacedRaw === undefined
1040
+ ? {}
1041
+ : { resumeGuideSurfaced: resumeGuideSurfacedRaw }),
1035
1042
  };
1036
1043
  }
1037
1044
  return {
1038
- reason: 'user_interjection_while_pending_subdialog',
1045
+ reason: 'user_interjection_with_parked_original_task',
1039
1046
  directive: {
1040
1047
  expectedReplyCallName,
1041
1048
  targetCallId,
1042
1049
  tellaskContent,
1043
1050
  },
1051
+ ...(resumeGuideSurfacedRaw === undefined
1052
+ ? {}
1053
+ : { resumeGuideSurfaced: resumeGuideSurfacedRaw }),
1044
1054
  };
1045
1055
  })();
1046
1056
  if (deferredReplyReassertion === null)
@@ -12,6 +12,10 @@ const USER_INTERJECTION_PAUSE_STOP_DETAIL = 'user_interjection_pause_resume_orig
12
12
  // resume afterwards, the interjection should simply complete and the dialog should fall back to
13
13
  // its true underlying state without showing this stopped panel.
14
14
  //
15
+ // In particular, askHuman answers are NOT "user interjections" for this purpose. A prompt carrying
16
+ // a real `q4hAnswerCallId` belongs to the askHuman reply channel and must never be routed through
17
+ // this paused-original-task stop semantics.
18
+ //
15
19
  // Do not change this file in isolation. The complete behavior depends on coordinated logic across:
16
20
  // - `reply-guidance.ts` suppressing upstream reply obligation during interjection chat
17
21
  // - `flow.ts` parking after the local reply, then re-running fresh-fact resume