dominds 1.13.2 → 1.15.2

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 (200) hide show
  1. package/dist/bootstrap/global-dialog-event-broadcaster.d.ts +18 -0
  2. package/dist/bootstrap/global-dialog-event-broadcaster.js +81 -0
  3. package/dist/dialog-fork.js +13 -12
  4. package/dist/dialog.d.ts +61 -50
  5. package/dist/dialog.js +284 -78
  6. package/dist/docs/dialog-system.md +12 -0
  7. package/dist/docs/dialog-system.zh.md +12 -0
  8. package/dist/docs/dominds-terminology.md +17 -0
  9. package/dist/docs/issues/global-dialog-event-broadcaster-missing.md +128 -0
  10. package/dist/docs/llm-provider-isolation.md +35 -0
  11. package/dist/docs/llm-provider-isolation.zh.md +35 -0
  12. package/dist/llm/client.d.ts +2 -1
  13. package/dist/llm/defaults.yaml +118 -4
  14. package/dist/llm/gen/anthropic.js +2 -4
  15. package/dist/llm/gen/codex.d.ts +11 -0
  16. package/dist/llm/gen/codex.js +41 -31
  17. package/dist/llm/gen/failure-classifier.js +17 -0
  18. package/dist/llm/gen/mock.js +45 -21
  19. package/dist/llm/gen/openai-compatible.d.ts +2 -0
  20. package/dist/llm/gen/openai-compatible.js +43 -38
  21. package/dist/llm/gen/openai.d.ts +3 -1
  22. package/dist/llm/gen/openai.js +888 -71
  23. package/dist/llm/gen/tool-call-context.d.ts +7 -2
  24. package/dist/llm/gen/tool-call-context.js +55 -13
  25. package/dist/llm/gen.d.ts +60 -3
  26. package/dist/llm/kernel-driver/context.js +1 -1
  27. package/dist/llm/kernel-driver/drive.js +374 -348
  28. package/dist/llm/kernel-driver/flow.js +3 -3
  29. package/dist/llm/kernel-driver/guardrails.d.ts +1 -1
  30. package/dist/llm/kernel-driver/guardrails.js +4 -4
  31. package/dist/llm/kernel-driver/runtime.js +11 -29
  32. package/dist/llm/kernel-driver/subdialog.js +56 -5
  33. package/dist/llm/kernel-driver/tellask-special.d.ts +38 -12
  34. package/dist/llm/kernel-driver/tellask-special.js +489 -180
  35. package/dist/llm/kernel-driver/types.d.ts +1 -1
  36. package/dist/persistence.d.ts +30 -62
  37. package/dist/persistence.js +978 -986
  38. package/dist/priming.js +398 -365
  39. package/dist/recovery/reply-special.js +3 -3
  40. package/dist/runtime/inter-dialog-format.d.ts +1 -1
  41. package/dist/runtime/inter-dialog-format.js +1 -1
  42. package/dist/runtime/reply-prompt-copy.js +4 -4
  43. package/dist/server/setup-routes.js +26 -5
  44. package/dist/server/snippets-routes.d.ts +1 -0
  45. package/dist/server/snippets-routes.js +20 -9
  46. package/dist/server/websocket-handler.js +58 -25
  47. package/dist/shared/utils/fbr.js +12 -8
  48. package/dist/shared/utils/inter-dialog-format.js +6 -4
  49. package/dist/team.d.ts +24 -13
  50. package/dist/team.js +123 -32
  51. package/dist/tool.d.ts +26 -0
  52. package/dist/tool.js +97 -0
  53. package/dist/tools/team_mgmt.js +18 -0
  54. package/package.json +2 -2
  55. package/webapp/dist/assets/{_basePickBy-CBOtd63g.js → _basePickBy-DsirmCgI.js} +3 -3
  56. package/webapp/dist/assets/_basePickBy-DsirmCgI.js.map +1 -0
  57. package/webapp/dist/assets/{_baseUniq-mfoKz4Wm.js → _baseUniq-tR6G8loB.js} +2 -2
  58. package/webapp/dist/assets/_baseUniq-tR6G8loB.js.map +1 -0
  59. package/webapp/dist/assets/{arc-Dq0WZLyu.js → arc-CzxpASkZ.js} +2 -2
  60. package/webapp/dist/assets/arc-CzxpASkZ.js.map +1 -0
  61. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-CNmygmp3.js → architectureDiagram-2XIMDMQ5-BSH7H5oI.js} +26 -8
  62. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-BSH7H5oI.js.map +1 -0
  63. package/webapp/dist/assets/{blockDiagram-VD42YOAC-DvE0lybt.js → blockDiagram-WCTKOSBZ-DpLIr7yO.js} +187 -170
  64. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-DpLIr7yO.js.map +1 -0
  65. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-CR7zJ2_u.js → c4Diagram-IC4MRINW-WuYKgWfY.js} +4 -4
  66. package/webapp/dist/assets/c4Diagram-IC4MRINW-WuYKgWfY.js.map +1 -0
  67. package/webapp/dist/assets/{channel-DrTrnYx4.js → channel-B-v9dqLN.js} +2 -2
  68. package/webapp/dist/assets/channel-B-v9dqLN.js.map +1 -0
  69. package/webapp/dist/assets/{chunk-4BX2VUAB-CVuJEIeN.js → chunk-4BX2VUAB-MtFUfKZy.js} +2 -2
  70. package/webapp/dist/assets/chunk-4BX2VUAB-MtFUfKZy.js.map +1 -0
  71. package/webapp/dist/assets/{chunk-55IACEB6-BxUoXApB.js → chunk-55IACEB6-rY9AJdzj.js} +2 -2
  72. package/webapp/dist/assets/chunk-55IACEB6-rY9AJdzj.js.map +1 -0
  73. package/webapp/dist/assets/{chunk-FMBD7UC4-TX-LVAaV.js → chunk-FMBD7UC4-B-RtOs7e.js} +2 -2
  74. package/webapp/dist/assets/chunk-FMBD7UC4-B-RtOs7e.js.map +1 -0
  75. package/webapp/dist/assets/{chunk-TZMSLE5B-Cw689yRl.js → chunk-JSJVCQXG-Da1d3uS4.js} +14 -6
  76. package/webapp/dist/assets/chunk-JSJVCQXG-Da1d3uS4.js.map +1 -0
  77. package/webapp/dist/assets/{chunk-QN33PNHL-D1uiKlOO.js → chunk-KX2RTZJC-DH9UrpuG.js} +2 -2
  78. package/webapp/dist/assets/chunk-KX2RTZJC-DH9UrpuG.js.map +1 -0
  79. package/webapp/dist/assets/{chunk-DI55MBZ5-SAhxUTqQ.js → chunk-NQ4KR5QH-CK365lrr.js} +9 -7
  80. package/webapp/dist/assets/chunk-NQ4KR5QH-CK365lrr.js.map +1 -0
  81. package/webapp/dist/assets/{chunk-QZHKN3VN-BxuV0Oba.js → chunk-QZHKN3VN-BCaWPGDm.js} +2 -2
  82. package/webapp/dist/assets/chunk-QZHKN3VN-BCaWPGDm.js.map +1 -0
  83. package/webapp/dist/assets/{chunk-B4BG7PRW-DpMa3-9L.js → chunk-WL4C6EOR-DDCnEwft.js} +171 -121
  84. package/webapp/dist/assets/chunk-WL4C6EOR-DDCnEwft.js.map +1 -0
  85. package/webapp/dist/assets/{classDiagram-2ON5EDUG-BTTGianr.js → classDiagram-VBA2DB6C-CvMBU4WA.js} +7 -6
  86. package/webapp/dist/assets/classDiagram-VBA2DB6C-CvMBU4WA.js.map +1 -0
  87. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-BTTGianr.js → classDiagram-v2-RAHNMMFH-CvMBU4WA.js} +7 -6
  88. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-CvMBU4WA.js.map +1 -0
  89. package/webapp/dist/assets/{clone-Dk8cAI3I.js → clone-r98jR0MC.js} +2 -2
  90. package/webapp/dist/assets/clone-r98jR0MC.js.map +1 -0
  91. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-BjJnzB2N.js → cose-bilkent-S5V4N54A-t6J60Ogk.js} +2 -2
  92. package/webapp/dist/assets/cose-bilkent-S5V4N54A-t6J60Ogk.js.map +1 -0
  93. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  94. package/webapp/dist/assets/{dagre-6UL2VRFP-VF-xGhAf.js → dagre-KLK3FWXG-BlqmY2DV.js} +7 -7
  95. package/webapp/dist/assets/dagre-KLK3FWXG-BlqmY2DV.js.map +1 -0
  96. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  97. package/webapp/dist/assets/{diagram-PSM6KHXK-Ba5U0oRY.js → diagram-E7M64L7V-FwCHeIUD.js} +10 -10
  98. package/webapp/dist/assets/diagram-E7M64L7V-FwCHeIUD.js.map +1 -0
  99. package/webapp/dist/assets/{diagram-QEK2KX5R-DoYCnEw_.js → diagram-IFDJBPK2-NhtmkuZG.js} +9 -8
  100. package/webapp/dist/assets/diagram-IFDJBPK2-NhtmkuZG.js.map +1 -0
  101. package/webapp/dist/assets/{diagram-S2PKOQOG-CkK4SRyE.js → diagram-P4PSJMXO-B9FcmokX.js} +8 -8
  102. package/webapp/dist/assets/diagram-P4PSJMXO-B9FcmokX.js.map +1 -0
  103. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-DkI5eYww.js → erDiagram-INFDFZHY-DHKmWvtB.js} +96 -75
  104. package/webapp/dist/assets/erDiagram-INFDFZHY-DHKmWvtB.js.map +1 -0
  105. package/webapp/dist/assets/{flowDiagram-NV44I4VS-wOdPUQ7Y.js → flowDiagram-PKNHOUZH-C7Zi8I7T.js} +98 -81
  106. package/webapp/dist/assets/flowDiagram-PKNHOUZH-C7Zi8I7T.js.map +1 -0
  107. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-BtRWgkUH.js → ganttDiagram-A5KZAMGK-Cv2T8tz_.js} +28 -3
  108. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-Cv2T8tz_.js.map +1 -0
  109. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js → gitGraphDiagram-K3NZZRJ6-DztaipJU.js} +38 -46
  110. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-DztaipJU.js.map +1 -0
  111. package/webapp/dist/assets/graph-C5yf62Vs.js +782 -0
  112. package/webapp/dist/assets/graph-C5yf62Vs.js.map +1 -0
  113. package/webapp/dist/assets/{index-xvYYeHuy.css → index-YaxF76or.css} +1 -1
  114. package/webapp/dist/assets/{index-rYmIohM_.js → index-hve5MWPs.js} +1603 -1415
  115. package/webapp/dist/assets/index-hve5MWPs.js.map +1 -0
  116. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-BMaxCvH5.js → infoDiagram-LFFYTUFH-VgsbBPZP.js} +7 -7
  117. package/webapp/dist/assets/infoDiagram-LFFYTUFH-VgsbBPZP.js.map +1 -0
  118. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  119. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js +966 -0
  120. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js.map +1 -0
  121. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-ejyerzmG.js → journeyDiagram-4ABVD52K-OO8sev-Y.js} +5 -5
  122. package/webapp/dist/assets/journeyDiagram-4ABVD52K-OO8sev-Y.js.map +1 -0
  123. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-CYj35TEs.js → kanban-definition-K7BYSVSG-DiYCC1Ig.js} +5 -3
  124. package/webapp/dist/assets/kanban-definition-K7BYSVSG-DiYCC1Ig.js.map +1 -0
  125. package/webapp/dist/assets/{layout-7Ql4zmuL.js → layout-DdZSgGdu.js} +5 -5
  126. package/webapp/dist/assets/layout-DdZSgGdu.js.map +1 -0
  127. package/webapp/dist/assets/{linear-CVmgVPuZ.js → linear-7-aHtaFi.js} +2 -2
  128. package/webapp/dist/assets/linear-7-aHtaFi.js.map +1 -0
  129. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-DOpxjGVo.js → mindmap-definition-YRQLILUH-IG3I-RdD.js} +7 -5
  130. package/webapp/dist/assets/mindmap-definition-YRQLILUH-IG3I-RdD.js.map +1 -0
  131. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  132. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-CLQjpmAG.js → pieDiagram-SKSYHLDU-z68KJT5r.js} +8 -8
  133. package/webapp/dist/assets/pieDiagram-SKSYHLDU-z68KJT5r.js.map +1 -0
  134. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-ClD_bz7z.js → quadrantDiagram-337W2JSQ-DaENWdO6.js} +3 -3
  135. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-DaENWdO6.js.map +1 -0
  136. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-DOpb-TWH.js → requirementDiagram-Z7DCOOCP-ROTFv4sa.js} +16 -6
  137. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-ROTFv4sa.js.map +1 -0
  138. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-D8Hsj3yx.js → sankeyDiagram-WA2Y5GQK-CK7qtpzw.js} +2 -2
  139. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CK7qtpzw.js.map +1 -0
  140. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-CFMNjBER.js → sequenceDiagram-2WXFIKYE-R5lDySeI.js} +601 -201
  141. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-R5lDySeI.js.map +1 -0
  142. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-BQeDlw0P.js → stateDiagram-RAJIS63D-sr7msF5U.js} +9 -9
  143. package/webapp/dist/assets/stateDiagram-RAJIS63D-sr7msF5U.js.map +1 -0
  144. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-DscX61Rs.js → stateDiagram-v2-FVOUBMTO-X663liwS.js} +5 -5
  145. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-X663liwS.js.map +1 -0
  146. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-BcXPSTiw.js → timeline-definition-YZTLITO2-Bw0TdG26.js} +3 -3
  147. package/webapp/dist/assets/timeline-definition-YZTLITO2-Bw0TdG26.js.map +1 -0
  148. package/webapp/dist/assets/{treemap-GDKQZRPO-BBr4UV0Z.js → treemap-KZPCXAKY-D_sjKwI7.js} +37 -24
  149. package/webapp/dist/assets/treemap-KZPCXAKY-D_sjKwI7.js.map +1 -0
  150. package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js +2487 -0
  151. package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js.map +1 -0
  152. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-CS5RAtQE.js → xychartDiagram-JWTSCODW-C65ESjTc.js} +4 -4
  153. package/webapp/dist/assets/xychartDiagram-JWTSCODW-C65ESjTc.js.map +1 -0
  154. package/webapp/dist/index.html +2 -2
  155. package/webapp/dist/assets/_basePickBy-CBOtd63g.js.map +0 -1
  156. package/webapp/dist/assets/_baseUniq-mfoKz4Wm.js.map +0 -1
  157. package/webapp/dist/assets/arc-Dq0WZLyu.js.map +0 -1
  158. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-CNmygmp3.js.map +0 -1
  159. package/webapp/dist/assets/blockDiagram-VD42YOAC-DvE0lybt.js.map +0 -1
  160. package/webapp/dist/assets/c4Diagram-YG6GDRKO-CR7zJ2_u.js.map +0 -1
  161. package/webapp/dist/assets/channel-DrTrnYx4.js.map +0 -1
  162. package/webapp/dist/assets/chunk-4BX2VUAB-CVuJEIeN.js.map +0 -1
  163. package/webapp/dist/assets/chunk-55IACEB6-BxUoXApB.js.map +0 -1
  164. package/webapp/dist/assets/chunk-B4BG7PRW-DpMa3-9L.js.map +0 -1
  165. package/webapp/dist/assets/chunk-DI55MBZ5-SAhxUTqQ.js.map +0 -1
  166. package/webapp/dist/assets/chunk-FMBD7UC4-TX-LVAaV.js.map +0 -1
  167. package/webapp/dist/assets/chunk-QN33PNHL-D1uiKlOO.js.map +0 -1
  168. package/webapp/dist/assets/chunk-QZHKN3VN-BxuV0Oba.js.map +0 -1
  169. package/webapp/dist/assets/chunk-TZMSLE5B-Cw689yRl.js.map +0 -1
  170. package/webapp/dist/assets/classDiagram-2ON5EDUG-BTTGianr.js.map +0 -1
  171. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-BTTGianr.js.map +0 -1
  172. package/webapp/dist/assets/clone-Dk8cAI3I.js.map +0 -1
  173. package/webapp/dist/assets/cose-bilkent-S5V4N54A-BjJnzB2N.js.map +0 -1
  174. package/webapp/dist/assets/dagre-6UL2VRFP-VF-xGhAf.js.map +0 -1
  175. package/webapp/dist/assets/diagram-PSM6KHXK-Ba5U0oRY.js.map +0 -1
  176. package/webapp/dist/assets/diagram-QEK2KX5R-DoYCnEw_.js.map +0 -1
  177. package/webapp/dist/assets/diagram-S2PKOQOG-CkK4SRyE.js.map +0 -1
  178. package/webapp/dist/assets/erDiagram-Q2GNP2WA-DkI5eYww.js.map +0 -1
  179. package/webapp/dist/assets/flowDiagram-NV44I4VS-wOdPUQ7Y.js.map +0 -1
  180. package/webapp/dist/assets/ganttDiagram-JELNMOA3-BtRWgkUH.js.map +0 -1
  181. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js.map +0 -1
  182. package/webapp/dist/assets/graph-DAMkuTbn.js +0 -425
  183. package/webapp/dist/assets/graph-DAMkuTbn.js.map +0 -1
  184. package/webapp/dist/assets/index-rYmIohM_.js.map +0 -1
  185. package/webapp/dist/assets/infoDiagram-HS3SLOUP-BMaxCvH5.js.map +0 -1
  186. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-ejyerzmG.js.map +0 -1
  187. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-CYj35TEs.js.map +0 -1
  188. package/webapp/dist/assets/layout-7Ql4zmuL.js.map +0 -1
  189. package/webapp/dist/assets/linear-CVmgVPuZ.js.map +0 -1
  190. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-DOpxjGVo.js.map +0 -1
  191. package/webapp/dist/assets/pieDiagram-ADFJNKIX-CLQjpmAG.js.map +0 -1
  192. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-ClD_bz7z.js.map +0 -1
  193. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-DOpb-TWH.js.map +0 -1
  194. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-D8Hsj3yx.js.map +0 -1
  195. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-CFMNjBER.js.map +0 -1
  196. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-BQeDlw0P.js.map +0 -1
  197. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-DscX61Rs.js.map +0 -1
  198. package/webapp/dist/assets/timeline-definition-IT6M3QCI-BcXPSTiw.js.map +0 -1
  199. package/webapp/dist/assets/treemap-GDKQZRPO-BBr4UV0Z.js.map +0 -1
  200. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-CS5RAtQE.js.map +0 -1
package/dist/dialog.js CHANGED
@@ -597,7 +597,7 @@ class Dialog {
597
597
  grammar: prompt.grammar,
598
598
  userLanguageCode: prompt.userLanguageCode,
599
599
  origin: prompt.origin,
600
- q4hAnswerCallIds: prompt.q4hAnswerCallIds,
600
+ q4hAnswerCallId: prompt.q4hAnswerCallId,
601
601
  tellaskReplyDirective: prompt.tellaskReplyDirective,
602
602
  skipTaskdoc: prompt.skipTaskdoc,
603
603
  subdialogReplyTarget: prompt.subdialogReplyTarget,
@@ -622,7 +622,7 @@ class Dialog {
622
622
  msgId: nextPrompt.msgId,
623
623
  grammar: nextPrompt.grammar ?? 'markdown',
624
624
  userLanguageCode: nextPrompt.userLanguageCode,
625
- q4hAnswerCallIds: nextPrompt.q4hAnswerCallIds,
625
+ q4hAnswerCallId: nextPrompt.q4hAnswerCallId,
626
626
  tellaskReplyDirective: nextPrompt.tellaskReplyDirective,
627
627
  skipTaskdoc: nextPrompt.skipTaskdoc,
628
628
  subdialogReplyTarget: nextPrompt.subdialogReplyTarget,
@@ -657,23 +657,20 @@ class Dialog {
657
657
  this._upNextQueue = [this.buildUpNextPromptState(normalized, 'new_course_start')];
658
658
  return normalized;
659
659
  }
660
- mergePromptQ4HAnswerCallIds(existing, incoming) {
661
- if (!existing || existing.length === 0) {
662
- return incoming && incoming.length > 0 ? [...incoming] : undefined;
660
+ mergePromptQ4HAnswerCallId(existing, incoming) {
661
+ const normalizedExisting = typeof existing === 'string' ? existing.trim() : '';
662
+ const normalizedIncoming = typeof incoming === 'string' ? incoming.trim() : '';
663
+ if (normalizedExisting === '') {
664
+ return normalizedIncoming !== '' ? normalizedIncoming : undefined;
663
665
  }
664
- if (!incoming || incoming.length === 0) {
665
- return [...existing];
666
+ if (normalizedIncoming === '') {
667
+ return normalizedExisting;
666
668
  }
667
- const seen = new Set();
668
- const merged = [];
669
- for (const raw of [...existing, ...incoming]) {
670
- const callId = raw.trim();
671
- if (callId === '' || seen.has(callId))
672
- continue;
673
- seen.add(callId);
674
- merged.push(callId);
669
+ if (normalizedExisting !== normalizedIncoming) {
670
+ throw new Error(`Q4H answer continuation invariant violation: conflicting callIds cannot be merged ` +
671
+ `(dialog=${this.id.valueOf()} existing=${normalizedExisting} incoming=${normalizedIncoming})`);
675
672
  }
676
- return merged.length > 0 ? merged : undefined;
673
+ return normalizedExisting;
677
674
  }
678
675
  enqueueQueuedPromptState(state) {
679
676
  this._upNextQueue.push(state);
@@ -685,7 +682,7 @@ class Dialog {
685
682
  grammar: state.grammar ?? 'markdown',
686
683
  userLanguageCode: state.userLanguageCode ?? this._lastUserLanguageCode,
687
684
  origin: state.origin,
688
- q4hAnswerCallIds: state.q4hAnswerCallIds,
685
+ q4hAnswerCallId: state.q4hAnswerCallId,
689
686
  tellaskReplyDirective: state.tellaskReplyDirective,
690
687
  skipTaskdoc: state.skipTaskdoc,
691
688
  subdialogReplyTarget: state.subdialogReplyTarget,
@@ -714,7 +711,7 @@ class Dialog {
714
711
  grammar: options.grammar,
715
712
  userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
716
713
  origin: 'user',
717
- q4hAnswerCallIds: options.q4hAnswerCallIds,
714
+ q4hAnswerCallId: options.q4hAnswerCallId,
718
715
  };
719
716
  this.enqueueQueuedPromptState(created);
720
717
  return created;
@@ -724,7 +721,7 @@ class Dialog {
724
721
  prompt: `${existing.prompt}\n\n---\n\n${trimmed}`,
725
722
  grammar: options.grammar,
726
723
  userLanguageCode: options.userLanguageCode ?? existing.userLanguageCode ?? this._lastUserLanguageCode,
727
- q4hAnswerCallIds: this.mergePromptQ4HAnswerCallIds(existing.q4hAnswerCallIds, options.q4hAnswerCallIds),
724
+ q4hAnswerCallId: this.mergePromptQ4HAnswerCallId(existing.q4hAnswerCallId, options.q4hAnswerCallId),
728
725
  };
729
726
  this.replaceQueuedPromptState(existing.msgId, merged);
730
727
  this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
@@ -733,8 +730,10 @@ class Dialog {
733
730
  queueDeferredQ4HAnswerPrompt(options) {
734
731
  const trimmed = options.prompt.trim();
735
732
  if (!trimmed) {
736
- throw new Error('Prompt is required to queue deferred Q4H answer');
733
+ throw new Error('Continuation input is required to queue deferred Q4H answer');
737
734
  }
735
+ // This queue item only carries the already-answered askHuman call correlation into the next
736
+ // resumed drive. The answer itself has already been persisted as tellask result/carryover.
738
737
  const created = {
739
738
  kind: 'deferred_q4h_answer',
740
739
  prompt: trimmed,
@@ -742,7 +741,7 @@ class Dialog {
742
741
  grammar: options.grammar,
743
742
  userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
744
743
  origin: 'user',
745
- q4hAnswerCallIds: options.q4hAnswerCallIds,
744
+ q4hAnswerCallId: options.q4hAnswerCallId,
746
745
  runControl: undefined,
747
746
  };
748
747
  this.enqueueQueuedPromptState(created);
@@ -762,7 +761,7 @@ class Dialog {
762
761
  grammar: options.grammar,
763
762
  userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
764
763
  origin: 'runtime',
765
- q4hAnswerCallIds: options.q4hAnswerCallIds,
764
+ q4hAnswerCallId: options.q4hAnswerCallId,
766
765
  tellaskReplyDirective: options.tellaskReplyDirective,
767
766
  skipTaskdoc: options.skipTaskdoc,
768
767
  subdialogReplyTarget: options.subdialogReplyTarget,
@@ -777,7 +776,7 @@ class Dialog {
777
776
  msgId: options.msgId,
778
777
  grammar: options.grammar,
779
778
  userLanguageCode: options.userLanguageCode ?? existing.userLanguageCode ?? this._lastUserLanguageCode,
780
- q4hAnswerCallIds: this.mergePromptQ4HAnswerCallIds(existing.q4hAnswerCallIds, options.q4hAnswerCallIds),
779
+ q4hAnswerCallId: this.mergePromptQ4HAnswerCallId(existing.q4hAnswerCallId, options.q4hAnswerCallId),
781
780
  tellaskReplyDirective: options.tellaskReplyDirective ?? existing.tellaskReplyDirective,
782
781
  skipTaskdoc: options.skipTaskdoc ?? existing.skipTaskdoc,
783
782
  subdialogReplyTarget: options.subdialogReplyTarget ?? existing.subdialogReplyTarget,
@@ -887,6 +886,184 @@ class Dialog {
887
886
  async receiveFuncResult(result) {
888
887
  return await this.dlgStore.receiveFuncResult(this, result);
889
888
  }
889
+ async receiveTellaskResult(result) {
890
+ return await this.dlgStore.receiveTellaskResult(this, result);
891
+ }
892
+ async receiveTellaskCarryover(result) {
893
+ return await this.dlgStore.receiveTellaskCarryover(this, result);
894
+ }
895
+ async receiveTellaskCallResult(responderId, callName, mentionList, tellaskContent, result, status, callId) {
896
+ const tellaskResult = callName === 'tellask'
897
+ ? {
898
+ type: 'tellask_result_msg',
899
+ role: 'tool',
900
+ genseq: this.activeGenSeqOrUndefined ?? 1,
901
+ callId,
902
+ callName,
903
+ status,
904
+ content: result,
905
+ call: {
906
+ tellaskContent,
907
+ mentionList: mentionList ?? [],
908
+ },
909
+ responder: {
910
+ responderId,
911
+ },
912
+ }
913
+ : callName === 'tellaskSessionless'
914
+ ? {
915
+ type: 'tellask_result_msg',
916
+ role: 'tool',
917
+ genseq: this.activeGenSeqOrUndefined ?? 1,
918
+ callId,
919
+ callName,
920
+ status,
921
+ content: result,
922
+ call: {
923
+ tellaskContent,
924
+ mentionList: mentionList ?? [],
925
+ },
926
+ responder: {
927
+ responderId,
928
+ },
929
+ }
930
+ : {
931
+ type: 'tellask_result_msg',
932
+ role: 'tool',
933
+ genseq: this.activeGenSeqOrUndefined ?? 1,
934
+ callId,
935
+ callName,
936
+ status,
937
+ content: result,
938
+ call: {
939
+ tellaskContent,
940
+ },
941
+ responder: {
942
+ responderId,
943
+ },
944
+ };
945
+ await this.receiveTellaskResult(tellaskResult);
946
+ }
947
+ async receiveTellaskResponse(responderId, callName, mentionList, tellaskContent, status, subdialogId, options) {
948
+ const currentCourse = this.activeGenCourseOrUndefined ?? this.currentCourse;
949
+ const resultRoute = {
950
+ ...(subdialogId ? { calleeDialogId: subdialogId.selfId } : {}),
951
+ ...(typeof options.calleeCourse === 'number' ? { calleeCourse: options.calleeCourse } : {}),
952
+ ...(typeof options.calleeGenseq === 'number' ? { calleeGenseq: options.calleeGenseq } : {}),
953
+ };
954
+ const carryoverOriginCourse = options.originCourse;
955
+ const isCrossCourseResponse = carryoverOriginCourse !== undefined && carryoverOriginCourse !== currentCourse;
956
+ if (isCrossCourseResponse) {
957
+ // Cross-course completion is not represented as a tool-result pair in the latest course.
958
+ // Instead we write a canonical current-course carryover message that restates the older
959
+ // tellask plus its resolution so the latest-course LLM context can read it directly.
960
+ if (typeof options.carryoverContent !== 'string') {
961
+ throw new Error(`receiveTellaskResponse invariant violation: missing carryover content for cross-course response ` +
962
+ `(callId=${options.callId}, callName=${callName}, originCourse=${options.originCourse}, currentCourse=${currentCourse})`);
963
+ }
964
+ if (options.carryoverContent.trim() === '') {
965
+ throw new Error(`receiveTellaskResponse invariant violation: empty carryover content ` +
966
+ `(callId=${options.callId}, callName=${callName})`);
967
+ }
968
+ if (options.response.trim() === '') {
969
+ throw new Error(`receiveTellaskResponse invariant violation: empty carryover response body ` +
970
+ `(callId=${options.callId}, callName=${callName})`);
971
+ }
972
+ if (callName === 'tellaskBack') {
973
+ throw new Error(`receiveTellaskResponse invariant violation: tellaskBack does not support carryover (callId=${options.callId})`);
974
+ }
975
+ const carryoverResult = {
976
+ type: 'tellask_carryover_msg',
977
+ role: 'user',
978
+ genseq: this.activeGenSeqOrUndefined ?? 1,
979
+ content: options.carryoverContent,
980
+ originCourse: carryoverOriginCourse,
981
+ carryoverCourse: currentCourse,
982
+ responderId,
983
+ callName,
984
+ tellaskContent,
985
+ status,
986
+ response: options.response,
987
+ agentId: options.agentId,
988
+ callId: options.callId,
989
+ originMemberId: options.originMemberId,
990
+ ...(callName === 'tellask'
991
+ ? {
992
+ mentionList: mentionList ?? [],
993
+ sessionSlug: options.sessionSlug,
994
+ }
995
+ : callName === 'tellaskSessionless'
996
+ ? {
997
+ mentionList: mentionList ?? [],
998
+ }
999
+ : {}),
1000
+ ...resultRoute,
1001
+ };
1002
+ await this.receiveTellaskCarryover(carryoverResult);
1003
+ return carryoverResult;
1004
+ }
1005
+ const tellaskResult = callName === 'tellask'
1006
+ ? {
1007
+ type: 'tellask_result_msg',
1008
+ role: 'tool',
1009
+ genseq: this.activeGenSeqOrUndefined ?? 1,
1010
+ callId: options.callId,
1011
+ callName,
1012
+ status,
1013
+ content: options.response,
1014
+ call: {
1015
+ tellaskContent,
1016
+ mentionList: mentionList ?? [],
1017
+ ...(options.sessionSlug ? { sessionSlug: options.sessionSlug } : {}),
1018
+ },
1019
+ responder: {
1020
+ responderId,
1021
+ agentId: options.agentId,
1022
+ originMemberId: options.originMemberId,
1023
+ },
1024
+ route: resultRoute,
1025
+ }
1026
+ : callName === 'tellaskSessionless'
1027
+ ? {
1028
+ type: 'tellask_result_msg',
1029
+ role: 'tool',
1030
+ genseq: this.activeGenSeqOrUndefined ?? 1,
1031
+ callId: options.callId,
1032
+ callName,
1033
+ status,
1034
+ content: options.response,
1035
+ call: {
1036
+ tellaskContent,
1037
+ mentionList: mentionList ?? [],
1038
+ },
1039
+ responder: {
1040
+ responderId,
1041
+ agentId: options.agentId,
1042
+ originMemberId: options.originMemberId,
1043
+ },
1044
+ route: resultRoute,
1045
+ }
1046
+ : {
1047
+ type: 'tellask_result_msg',
1048
+ role: 'tool',
1049
+ genseq: this.activeGenSeqOrUndefined ?? 1,
1050
+ callId: options.callId,
1051
+ callName,
1052
+ status,
1053
+ content: options.response,
1054
+ call: {
1055
+ tellaskContent,
1056
+ },
1057
+ responder: {
1058
+ responderId,
1059
+ agentId: options.agentId,
1060
+ originMemberId: options.originMemberId,
1061
+ },
1062
+ route: resultRoute,
1063
+ };
1064
+ await this.receiveTellaskResult(tellaskResult);
1065
+ return tellaskResult;
1066
+ }
890
1067
  async notifyGeneratingStart(msgId) {
891
1068
  // Capture the generation's starting course so any events emitted during this generation
892
1069
  // remain attributed to the correct course even if a tool mutates dialog.currentCourse
@@ -964,29 +1141,34 @@ class Dialog {
964
1141
  async webSearchCall(payload) {
965
1142
  await this.dlgStore.webSearchCall(this, payload);
966
1143
  }
1144
+ async nativeToolCall(payload) {
1145
+ await this.dlgStore.nativeToolCall(this, payload);
1146
+ }
967
1147
  // Tellask-special call lifecycle events
968
1148
  async callingStart(payload) {
969
1149
  // Store callId for inline call-result correlation
970
1150
  this.setCurrentCallId(payload.callId);
971
1151
  await this.dlgStore.callingStart(this, payload);
972
1152
  }
973
- /**
974
- * Receive call result with callId for inline correlation
975
- */
976
- async receiveTellaskCallResult(responderId, callName, mentionList, tellaskContent, result, status, callId) {
977
- return await this.dlgStore.receiveTellaskCallResult(this, responderId, callName, mentionList, tellaskContent, result, status, callId);
978
- }
979
- /**
980
- * Receive tellask response (separate bubble for tellask sideline replies)
981
- */
982
- async receiveTellaskResponse(responderId, callName, mentionList, tellaskContent, status, subdialogId, options) {
983
- return await this.dlgStore.receiveTellaskResponse(this, responderId, callName, mentionList, tellaskContent, status, subdialogId, options);
984
- }
985
1153
  async updateQuestions4Human(questions) {
986
1154
  return await this.dlgStore.updateQuestions4Human(this, questions);
987
1155
  }
988
- async persistUserMessage(content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallIds, tellaskReplyDirective) {
989
- return await this.dlgStore.persistUserMessage(this, content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallIds, tellaskReplyDirective);
1156
+ async persistUserMessage(content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallId, tellaskReplyDirective) {
1157
+ return await this.dlgStore.persistUserMessage(this, content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallId, tellaskReplyDirective);
1158
+ }
1159
+ async receiveHumanReply(args) {
1160
+ if (args.content.trim() === '') {
1161
+ throw new Error(`receiveHumanReply invariant violation: empty human reply content ` +
1162
+ `(rootId=${this.id.rootId} selfId=${this.id.selfId})`);
1163
+ }
1164
+ const callId = args.q4hAnswerCallId.trim();
1165
+ if (callId === '') {
1166
+ throw new Error(`receiveHumanReply invariant violation: empty q4hAnswerCallId ` +
1167
+ `(rootId=${this.id.rootId} selfId=${this.id.selfId})`);
1168
+ }
1169
+ if (args.userLanguageCode === 'zh' || args.userLanguageCode === 'en') {
1170
+ this.setLastUserLanguageCode(args.userLanguageCode);
1171
+ }
990
1172
  }
991
1173
  async appendTellaskReplyResolution(payload) {
992
1174
  await this.dlgStore.appendTellaskReplyResolution(this, payload);
@@ -997,11 +1179,17 @@ class Dialog {
997
1179
  async persistUiOnlyMarkdown(content, genseq) {
998
1180
  return await this.dlgStore.persistUiOnlyMarkdown(this, content, genseq);
999
1181
  }
1000
- async persistFunctionCall(id, name, arguments_, genseq) {
1001
- return await this.dlgStore.persistFunctionCall(this, id, name, arguments_, genseq);
1182
+ async persistFunctionCall(id, name, rawArgumentsText, genseq) {
1183
+ return await this.dlgStore.persistFunctionCall(this, id, name, rawArgumentsText, genseq);
1184
+ }
1185
+ async persistTellaskCall(id, name, rawArgumentsText, genseq, options) {
1186
+ return await this.dlgStore.persistTellaskCall(this, id, name, rawArgumentsText, genseq, options);
1187
+ }
1188
+ async persistFunctionCallResultPair(id, name, rawArgumentsText, genseq, result) {
1189
+ return await this.dlgStore.persistFunctionCallResultPair(this, id, name, rawArgumentsText, genseq, result);
1002
1190
  }
1003
- async persistTellaskSpecialCall(id, name, arguments_, genseq) {
1004
- return await this.dlgStore.persistTellaskSpecialCall(this, id, name, arguments_, genseq);
1191
+ async persistTellaskCallResultPair(args) {
1192
+ return await this.dlgStore.persistTellaskCallResultPair(this, args);
1005
1193
  }
1006
1194
  /**
1007
1195
  * Post subdialog completion response to this dialog.
@@ -1058,7 +1246,7 @@ class Dialog {
1058
1246
  // Emit virtual generating_start_evt for subdialog response bubble
1059
1247
  await this.notifyGeneratingStart();
1060
1248
  const rawResponse = response;
1061
- // Emit TellaskResponseEvent
1249
+ // Emit TellaskResultEvent
1062
1250
  const evt = (() => {
1063
1251
  switch (callName) {
1064
1252
  case 'tellask':
@@ -1067,48 +1255,69 @@ class Dialog {
1067
1255
  `(dialogId=${this.id.selfId}, subdialogId=${subdialogId.selfId}, callId=${callId})`);
1068
1256
  }
1069
1257
  return {
1070
- type: 'tellask_response_evt',
1071
- responderId,
1072
- calleeDialogId: subdialogId.selfId,
1258
+ type: 'tellask_result_evt',
1073
1259
  callName,
1074
- sessionSlug,
1075
- mentionList: mentionList ?? [],
1076
- tellaskContent,
1260
+ call: {
1261
+ sessionSlug,
1262
+ mentionList: mentionList ?? [],
1263
+ tellaskContent,
1264
+ },
1077
1265
  status: 'completed',
1078
1266
  course: this.currentCourse,
1079
- response: rawResponse,
1080
- agentId: responderAgentId ?? responderId,
1081
1267
  callId,
1082
- originMemberId,
1268
+ genseq: this.activeGenSeqOrUndefined ?? 1,
1269
+ content: rawResponse,
1270
+ responder: {
1271
+ responderId,
1272
+ agentId: responderAgentId ?? responderId,
1273
+ originMemberId,
1274
+ },
1275
+ route: {
1276
+ calleeDialogId: subdialogId.selfId,
1277
+ },
1083
1278
  };
1084
1279
  case 'tellaskSessionless':
1085
1280
  return {
1086
- type: 'tellask_response_evt',
1087
- responderId,
1088
- calleeDialogId: subdialogId.selfId,
1281
+ type: 'tellask_result_evt',
1089
1282
  callName,
1090
- mentionList: mentionList ?? [],
1091
- tellaskContent,
1283
+ call: {
1284
+ mentionList: mentionList ?? [],
1285
+ tellaskContent,
1286
+ },
1092
1287
  status: 'completed',
1093
1288
  course: this.currentCourse,
1094
- response: rawResponse,
1095
- agentId: responderAgentId ?? responderId,
1096
1289
  callId,
1097
- originMemberId,
1290
+ genseq: this.activeGenSeqOrUndefined ?? 1,
1291
+ content: rawResponse,
1292
+ responder: {
1293
+ responderId,
1294
+ agentId: responderAgentId ?? responderId,
1295
+ originMemberId,
1296
+ },
1297
+ route: {
1298
+ calleeDialogId: subdialogId.selfId,
1299
+ },
1098
1300
  };
1099
1301
  case 'freshBootsReasoning':
1100
1302
  return {
1101
- type: 'tellask_response_evt',
1102
- responderId,
1103
- calleeDialogId: subdialogId.selfId,
1303
+ type: 'tellask_result_evt',
1104
1304
  callName,
1105
- tellaskContent,
1305
+ call: {
1306
+ tellaskContent,
1307
+ },
1106
1308
  status: 'completed',
1107
1309
  course: this.currentCourse,
1108
- response: rawResponse,
1109
- agentId: responderAgentId ?? responderId,
1110
1310
  callId,
1111
- originMemberId,
1311
+ genseq: this.activeGenSeqOrUndefined ?? 1,
1312
+ content: rawResponse,
1313
+ responder: {
1314
+ responderId,
1315
+ agentId: responderAgentId ?? responderId,
1316
+ originMemberId,
1317
+ },
1318
+ route: {
1319
+ calleeDialogId: subdialogId.selfId,
1320
+ },
1112
1321
  };
1113
1322
  }
1114
1323
  })();
@@ -1117,7 +1326,7 @@ class Dialog {
1117
1326
  await this.notifyGeneratingFinish();
1118
1327
  }
1119
1328
  catch (err) {
1120
- log_1.log.warn('Failed to post tellask_response_evt event', undefined, {
1329
+ log_1.log.warn('Failed to post tellask_result_evt event', undefined, {
1121
1330
  error: err,
1122
1331
  message: err instanceof Error ? err.message : String(err),
1123
1332
  });
@@ -1342,14 +1551,8 @@ class DialogStore {
1342
1551
  sayingChunk(_dialog, _chunk) { }
1343
1552
  sayingFinish(_dialog) { }
1344
1553
  async receiveFuncResult(_dialog, _funcResult) { }
1345
- /**
1346
- * Receive call result with callId for inline correlation
1347
- */
1348
- async receiveTellaskCallResult(_dialog, _responderId, _callName, _mentionList, _tellaskContent, _result, _status, _callId) { }
1349
- /**
1350
- * Receive tellask response (separate bubble for tellask sideline replies)
1351
- */
1352
- async receiveTellaskResponse(_dialog, _responderId, _callName, _mentionList, _tellaskContent, _status, _subdialogId, _options) { }
1554
+ async receiveTellaskResult(_dialog, _funcResult) { }
1555
+ async receiveTellaskCarryover(_dialog, _result) { }
1353
1556
  async updateQuestions4Human(_dialog, _questions) { }
1354
1557
  /**
1355
1558
  * Load Questions for Human state from storage
@@ -1374,6 +1577,7 @@ class DialogStore {
1374
1577
  // Function call event (non-streaming mode - single event)
1375
1578
  async funcCallRequested(_dialog, _funcId, _funcName, _argumentsStr) { }
1376
1579
  async webSearchCall(_dialog, _payload) { }
1580
+ async nativeToolCall(_dialog, _payload) { }
1377
1581
  /**
1378
1582
  * Load current course number from persisted metadata
1379
1583
  * This method should be implemented by subclasses to read from storage
@@ -1397,7 +1601,7 @@ class DialogStore {
1397
1601
  /**
1398
1602
  * Persist a user message to storage
1399
1603
  */
1400
- async persistUserMessage(_dialog, _content, _msgId, _grammar, _origin, _userLanguageCode, _q4hAnswerCallIds, _tellaskReplyDirective) { }
1604
+ async persistUserMessage(_dialog, _content, _msgId, _grammar, _origin, _userLanguageCode, _q4hAnswerCallId, _tellaskReplyDirective) { }
1401
1605
  async appendTellaskReplyResolution(_dialog, _payload) { }
1402
1606
  /**
1403
1607
  * Persist an assistant message to storage
@@ -1411,8 +1615,10 @@ class DialogStore {
1411
1615
  /**
1412
1616
  * Persist a function call to storage
1413
1617
  */
1414
- async persistFunctionCall(_dialog, _id, _name, _arguments, _genseq) { }
1415
- async persistTellaskSpecialCall(_dialog, _id, _name, _arguments, _genseq) { }
1618
+ async persistFunctionCall(_dialog, _id, _name, _rawArgumentsText, _genseq) { }
1619
+ async persistTellaskCall(_dialog, _id, _name, _rawArgumentsText, _genseq, _options) { }
1620
+ async persistFunctionCallResultPair(_dialog, _id, _name, _rawArgumentsText, _genseq, _result) { }
1621
+ async persistTellaskCallResultPair(_dialog, _args) { }
1416
1622
  /**
1417
1623
  * Start a new course in storage
1418
1624
  */
@@ -132,6 +132,18 @@ Frontend clients never drive dialogs. Instead, they:
132
132
 
133
133
  All driving logic, resumption decisions, and state management remain purely backend concerns.
134
134
 
135
+ ### Global Dialog Event Broadcaster
136
+
137
+ Some dialog events are rtws-global rather than dialog-scoped, including `new_q4h_asked`, `q4h_answered`, `subdialog_created_evt`, and `dlg_touched_evt`.
138
+
139
+ These events require a **global dialog event broadcaster** to be installed during runtime bootstrap before any dialog-driving logic runs. This broadcaster is mandatory infrastructure, not an optional optimization:
140
+
141
+ - WebUI server runtime installs a WebSocket fanout broadcaster
142
+ - Script / test / future runtimes must also install a broadcaster, typically a recording broadcaster
143
+ - Tests should bootstrap the broadcaster at runtime entry and then either assert on captured events or ignore them
144
+
145
+ Missing broadcaster is therefore a runtime bootstrap invariant violation, not a Q4H/business-layer condition.
146
+
135
147
  ### State Persistence
136
148
 
137
149
  Dialog state is persisted to storage at key points:
@@ -131,6 +131,18 @@
131
131
 
132
132
  所有驱动逻辑、恢复决策和状态管理仍然是纯粹的后端关注点。
133
133
 
134
+ ### 全局对话事件广播器
135
+
136
+ 有一类对话事件属于 rtws 全局状态,而不是某个对话自己的局部流,包括 `new_q4h_asked`、`q4h_answered`、`subdialog_created_evt`、`dlg_touched_evt`。
137
+
138
+ 这些事件要求 runtime 在任何对话驱动逻辑开始前,就先完成**全局对话事件广播器**的 bootstrap。它是必要基础设施,不是可选优化:
139
+
140
+ - WebUI server runtime 安装 WebSocket fanout broadcaster
141
+ - script / test / future runtime 也必须安装 broadcaster,通常使用 recording broadcaster
142
+ - tests 应在 runtime 入口完成 broadcaster bootstrap;需要验证广播时断言捕获内容,不需要时可直接忽略
143
+
144
+ 因此,“缺少 broadcaster”应被视为 runtime bootstrap 不变式被破坏,而不是 Q4H/业务层条件。
145
+
134
146
  ### 状态持久化
135
147
 
136
148
  对话状态在关键点持久化到存储:
@@ -91,6 +91,23 @@
91
91
  - EN: A Tellask is not casual chat; it is a collaboration action that Dominds can drive, route, and coordinate (including suspend/resume).
92
92
  - ZH: Tellask 不是随意聊天,而是一种可被 Dominds 驱动、路由、并由系统协调(包括挂起/恢复)的协作动作。
93
93
 
94
+ ### Tellask Carryover(诉请补全)
95
+
96
+ - EN: **Tellask carryover** is the canonical way to represent, in the **latest/current course**, the completion of a Tellask that was originally asked in an older course.
97
+ - ZH: **Tellask carryover(诉请补全)** 是把“旧 course 中发起、但对当前推进仍然重要的诉请完成结果”规范化写入 **最新/当前 course** 的方式。
98
+
99
+ - EN: The LLM reads only the latest/current course during normal restore/drive. Carryover exists specifically so the latest course can still see the old request context plus its resolved result without reloading the older course transcript.
100
+ - ZH: 正常的 restore/drive 中,LLM 只读取最新/当前 course。carryover 的存在,就是为了让最新 course 在**不重新加载旧 course transcript** 的前提下,仍能看到旧提问的必要上下文及其完成结果。
101
+
102
+ - EN: `originCourse` is provenance only: it tells you where the original Tellask was issued.
103
+ - ZH: `originCourse` 只表示来源(provenance):原始诉请是在哪个 course 发起的。
104
+
105
+ - EN: `carryoverCourse` and event `course` are owner-course fields: they tell you which current/latest course now contains the usable context.
106
+ - ZH: `carryoverCourse` 和事件里的 `course` 表示归属 course:当前哪一个最新 course 持有这段可直接使用的上下文。
107
+
108
+ - EN: Carryover is **not** a technical tool-result pair for the old call. It is ordinary current-course context that happens to preserve the semantics of an older Tellask completion.
109
+ - ZH: carryover **不是** 旧 call 的技术性 tool-result 配对;它是当前 course 中的普通规范化上下文,只是语义上保留了那次旧诉请的完成结果。
110
+
94
111
  #### Tellask headline(诉请头)
95
112
 
96
113
  - EN: The first line of a Tellask block, starting with `tellaskSessionless({ targetAgentId: "<name>", tellaskContent: "..." })`.