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
@@ -51,9 +51,81 @@ const evt_registry_1 = require("./evt-registry");
51
51
  const log_1 = require("./log");
52
52
  const async_fifo_mutex_1 = require("./runtime/async-fifo-mutex");
53
53
  const reply_prompt_copy_1 = require("./runtime/reply-prompt-copy");
54
- const work_language_1 = require("./runtime/work-language");
55
54
  const tool_1 = require("./tool");
56
55
  const registry_1 = require("./tools/registry");
56
+ function isTellaskBusinessCallName(value) {
57
+ return (value === 'tellask' ||
58
+ value === 'tellaskSessionless' ||
59
+ value === 'tellaskBack' ||
60
+ value === 'askHuman' ||
61
+ value === 'freshBootsReasoning');
62
+ }
63
+ function isSuppressedTellaskPlaceholderFuncResult(args) {
64
+ if (!isTellaskBusinessCallName(args.name)) {
65
+ return false;
66
+ }
67
+ const raw = args.content.trim();
68
+ if (raw === '') {
69
+ return false;
70
+ }
71
+ if (raw === 'Q4H 已结束等待状态,请参考 askHuman 结果气泡。' ||
72
+ raw === 'Q4H wait is resolved; refer to the askHuman result bubble.') {
73
+ return true;
74
+ }
75
+ if (raw.startsWith('Q4H 仍在等待人类回复,已持续 ') ||
76
+ raw.startsWith('Q4H is still waiting for human reply (elapsed ')) {
77
+ return true;
78
+ }
79
+ if (raw.startsWith('支线对话仍在进行中,已持续 ') ||
80
+ raw.startsWith('Sideline dialog is still running (elapsed ')) {
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ function buildTellaskResultRoute(route, fallback) {
86
+ const effective = route ?? fallback;
87
+ if (!effective)
88
+ return undefined;
89
+ return {
90
+ ...(effective.calleeDialogId ? { calleeDialogId: effective.calleeDialogId } : {}),
91
+ ...(typeof effective.calleeCourse === 'number'
92
+ ? { calleeCourse: (0, storage_1.toCalleeCourseNumber)(effective.calleeCourse) }
93
+ : {}),
94
+ ...(typeof effective.calleeGenseq === 'number'
95
+ ? { calleeGenseq: (0, storage_1.toCalleeGenerationSeqNumber)(effective.calleeGenseq) }
96
+ : {}),
97
+ };
98
+ }
99
+ function requireNonEmptyTrimmedString(value, field, context) {
100
+ if (typeof value !== 'string') {
101
+ throw new Error(`${context} invariant violation: missing ${field}`);
102
+ }
103
+ const trimmed = value.trim();
104
+ if (trimmed === '') {
105
+ throw new Error(`${context} invariant violation: empty ${field}`);
106
+ }
107
+ return trimmed;
108
+ }
109
+ function requireTellaskResultResponderId(result, context) {
110
+ return requireNonEmptyTrimmedString(result.responder?.responderId ?? result.responderId, 'responderId', `${context} (callId=${result.callId}, callName=${result.callName})`);
111
+ }
112
+ function requireTellaskResultContent(result, context) {
113
+ return requireNonEmptyTrimmedString(result.call?.tellaskContent ?? result.tellaskContent, 'tellaskContent', `${context} (callId=${result.callId}, callName=${result.callName})`);
114
+ }
115
+ function resolveTellaskResultMentionList(result, context) {
116
+ const mentionList = result.call?.mentionList ?? result.mentionList;
117
+ if (mentionList === undefined) {
118
+ return [];
119
+ }
120
+ if (!Array.isArray(mentionList) || mentionList.some((item) => typeof item !== 'string')) {
121
+ throw new Error(`${context} invariant violation: invalid mentionList ` +
122
+ `(callId=${result.callId}, callName=${result.callName})`);
123
+ }
124
+ return [...mentionList];
125
+ }
126
+ function requireTellaskResultSessionSlug(result, context) {
127
+ return requireNonEmptyTrimmedString(result.call?.sessionSlug ?? result.sessionSlug, 'sessionSlug', `${context} (callId=${result.callId}, callName=${result.callName})`);
128
+ }
57
129
  function getErrorCode(error) {
58
130
  if (typeof error !== 'object' || error === null)
59
131
  return undefined;
@@ -93,114 +165,315 @@ function cloneRootGenerationAnchor(anchor) {
93
165
  rootGenseq: anchor.rootGenseq,
94
166
  };
95
167
  }
96
- function formatCrashRecoveredFuncResultContent(toolName) {
97
- if ((0, work_language_1.getWorkLanguage)() === 'zh') {
98
- return (`Function '${toolName}' execution failed: ` +
99
- 'Dominds 在该工具调用发出后发生了进程意外退出,原调用未能产出结果。' +
100
- '这是启动恢复阶段自动补记的失败结果,请基于当前上下文判断是否需要重试。');
101
- }
102
- return (`Function '${toolName}' execution failed: ` +
103
- 'Dominds exited unexpectedly after this tool call was persisted, so no tool result was produced. ' +
104
- 'This failure record was added automatically during startup recovery; decide whether to retry.');
105
- }
106
- function readPersistedTellaskStringArg(arguments_, field, callName, callId) {
107
- const value = arguments_[field];
108
- if (typeof value !== 'string' || value.trim() === '') {
109
- throw new Error(`tellask special persistence invariant violation: missing ${field} for ${callName} (callId=${callId})`);
110
- }
111
- return value;
168
+ function buildFuncCallRecord(args) {
169
+ return {
170
+ ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
171
+ type: 'func_call_record',
172
+ genseq: args.genseq,
173
+ id: args.id,
174
+ name: args.name,
175
+ rawArgumentsText: args.rawArgumentsText,
176
+ };
112
177
  }
113
- function readPersistedTellaskOptionalNumberArg(arguments_, field, callName, callId) {
114
- const value = arguments_[field];
115
- if (value === undefined) {
116
- return undefined;
117
- }
118
- if (typeof value !== 'number' || !Number.isFinite(value)) {
119
- throw new Error(`tellask special persistence invariant violation: invalid ${field} for ${callName} (callId=${callId})`);
120
- }
121
- return value;
178
+ function buildFuncResultRecord(funcResult, genseq) {
179
+ return {
180
+ ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
181
+ type: 'func_result_record',
182
+ id: funcResult.id,
183
+ name: funcResult.name,
184
+ content: funcResult.content,
185
+ contentItems: funcResult.contentItems,
186
+ genseq,
187
+ };
122
188
  }
123
- function buildTellaskSpecialCallRecord(args) {
124
- const base = {
189
+ function buildTellaskCallRecord(args) {
190
+ return {
125
191
  ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
126
- type: 'tellask_special_call_record',
192
+ type: 'tellask_call_record',
127
193
  genseq: args.genseq,
128
194
  id: args.id,
195
+ name: args.name,
196
+ rawArgumentsText: args.rawArgumentsText,
197
+ deliveryMode: args.deliveryMode,
129
198
  };
130
- switch (args.name) {
131
- case 'tellaskBack':
132
- return {
133
- ...base,
134
- name: args.name,
135
- tellaskContent: readPersistedTellaskStringArg(args.arguments_, 'tellaskContent', args.name, args.id),
136
- };
199
+ }
200
+ function buildTellaskResultRecord(result, genseq) {
201
+ if (!isTellaskBusinessCallName(result.callName)) {
202
+ throw new Error(`buildTellaskResultRecord invariant violation: ${result.callName} is not a tellask business result`);
203
+ }
204
+ const responderId = requireTellaskResultResponderId(result, 'buildTellaskResultRecord');
205
+ const tellaskContent = requireTellaskResultContent(result, 'buildTellaskResultRecord');
206
+ const route = buildTellaskResultRoute(result.route, {
207
+ calleeDialogId: result.calleeDialogId,
208
+ calleeCourse: result.calleeCourse,
209
+ calleeGenseq: result.calleeGenseq,
210
+ });
211
+ const base = {
212
+ ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
213
+ type: 'tellask_result_record',
214
+ genseq,
215
+ callId: result.callId,
216
+ status: result.status,
217
+ content: result.content,
218
+ ...(typeof result.calling_genseq === 'number'
219
+ ? { calling_genseq: (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq) }
220
+ : {}),
221
+ responder: {
222
+ responderId,
223
+ ...((result.responder?.agentId ?? result.agentId)
224
+ ? { agentId: result.responder?.agentId ?? result.agentId }
225
+ : {}),
226
+ ...((result.responder?.originMemberId ?? result.originMemberId)
227
+ ? { originMemberId: result.responder?.originMemberId ?? result.originMemberId }
228
+ : {}),
229
+ },
230
+ ...(route ? { route } : {}),
231
+ };
232
+ switch (result.callName) {
137
233
  case 'tellask':
138
234
  return {
139
235
  ...base,
140
- name: args.name,
141
- targetAgentId: readPersistedTellaskStringArg(args.arguments_, 'targetAgentId', args.name, args.id),
142
- sessionSlug: readPersistedTellaskStringArg(args.arguments_, 'sessionSlug', args.name, args.id),
143
- tellaskContent: readPersistedTellaskStringArg(args.arguments_, 'tellaskContent', args.name, args.id),
236
+ callName: result.callName,
237
+ call: {
238
+ tellaskContent,
239
+ mentionList: resolveTellaskResultMentionList(result, 'buildTellaskResultRecord'),
240
+ sessionSlug: requireTellaskResultSessionSlug(result, 'buildTellaskResultRecord'),
241
+ },
144
242
  };
145
243
  case 'tellaskSessionless':
146
244
  return {
147
245
  ...base,
148
- name: args.name,
149
- targetAgentId: readPersistedTellaskStringArg(args.arguments_, 'targetAgentId', args.name, args.id),
150
- tellaskContent: readPersistedTellaskStringArg(args.arguments_, 'tellaskContent', args.name, args.id),
151
- };
152
- case 'replyTellask':
153
- case 'replyTellaskSessionless':
154
- case 'replyTellaskBack':
155
- return {
156
- ...base,
157
- name: args.name,
158
- replyContent: readPersistedTellaskStringArg(args.arguments_, 'replyContent', args.name, args.id),
246
+ callName: result.callName,
247
+ call: {
248
+ tellaskContent,
249
+ mentionList: resolveTellaskResultMentionList(result, 'buildTellaskResultRecord'),
250
+ },
159
251
  };
252
+ case 'tellaskBack':
160
253
  case 'askHuman':
254
+ case 'freshBootsReasoning':
161
255
  return {
162
256
  ...base,
163
- name: args.name,
164
- tellaskContent: readPersistedTellaskStringArg(args.arguments_, 'tellaskContent', args.name, args.id),
165
- };
166
- case 'freshBootsReasoning': {
167
- const effort = readPersistedTellaskOptionalNumberArg(args.arguments_, 'effort', args.name, args.id);
168
- return {
169
- ...base,
170
- name: args.name,
171
- tellaskContent: readPersistedTellaskStringArg(args.arguments_, 'tellaskContent', args.name, args.id),
172
- ...(effort !== undefined ? { effort } : {}),
257
+ callName: result.callName,
258
+ call: {
259
+ tellaskContent,
260
+ },
173
261
  };
174
- }
175
262
  }
176
263
  }
177
- function formatTellaskSpecialCallArguments(record) {
178
- switch (record.name) {
179
- case 'tellaskBack':
180
- return JSON.stringify({ tellaskContent: record.tellaskContent });
181
- case 'tellask':
182
- return JSON.stringify({
183
- targetAgentId: record.targetAgentId,
184
- sessionSlug: record.sessionSlug,
185
- tellaskContent: record.tellaskContent,
186
- });
187
- case 'tellaskSessionless':
188
- return JSON.stringify({
189
- targetAgentId: record.targetAgentId,
190
- tellaskContent: record.tellaskContent,
191
- });
192
- case 'replyTellask':
193
- case 'replyTellaskSessionless':
194
- case 'replyTellaskBack':
195
- return JSON.stringify({ replyContent: record.replyContent });
196
- case 'askHuman':
197
- return JSON.stringify({ tellaskContent: record.tellaskContent });
198
- case 'freshBootsReasoning':
199
- return JSON.stringify({
200
- tellaskContent: record.tellaskContent,
201
- ...(record.effort !== undefined ? { effort: record.effort } : {}),
202
- });
264
+ function buildTellaskCarryoverRecord(result, genseq) {
265
+ return {
266
+ ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
267
+ type: 'tellask_carryover_record',
268
+ genseq,
269
+ originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
270
+ carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
271
+ responderId: result.responderId,
272
+ callName: result.callName,
273
+ tellaskContent: result.tellaskContent,
274
+ status: result.status,
275
+ response: result.response,
276
+ content: result.content,
277
+ agentId: result.agentId,
278
+ callId: result.callId,
279
+ originMemberId: result.originMemberId,
280
+ ...(result.callName === 'tellask'
281
+ ? {
282
+ sessionSlug: result.sessionSlug,
283
+ mentionList: result.mentionList,
284
+ }
285
+ : result.callName === 'tellaskSessionless'
286
+ ? {
287
+ mentionList: result.mentionList,
288
+ }
289
+ : {}),
290
+ ...(result.calleeDialogId ? { calleeDialogId: result.calleeDialogId } : {}),
291
+ ...(typeof result.calleeCourse === 'number' ? { calleeCourse: result.calleeCourse } : {}),
292
+ ...(typeof result.calleeGenseq === 'number' ? { calleeGenseq: result.calleeGenseq } : {}),
293
+ };
294
+ }
295
+ function buildTellaskResultEvent(result, course) {
296
+ if (!isTellaskBusinessCallName(result.callName)) {
297
+ throw new Error(`buildTellaskResultEvent invariant violation: ${result.callName} is not a tellask business result`);
298
+ }
299
+ const responderId = requireTellaskResultResponderId(result, 'buildTellaskResultEvent');
300
+ const tellaskContent = requireTellaskResultContent(result, 'buildTellaskResultEvent');
301
+ const route = buildTellaskResultRoute(result.route, {
302
+ calleeDialogId: result.calleeDialogId,
303
+ calleeCourse: result.calleeCourse,
304
+ calleeGenseq: result.calleeGenseq,
305
+ });
306
+ if (result.callName === 'tellask') {
307
+ const effectiveSessionSlug = requireTellaskResultSessionSlug(result, 'buildTellaskResultEvent');
308
+ return {
309
+ type: 'tellask_result_evt',
310
+ course,
311
+ genseq: result.genseq,
312
+ calling_genseq: typeof result.calling_genseq === 'number'
313
+ ? (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq)
314
+ : undefined,
315
+ callId: result.callId,
316
+ callName: result.callName,
317
+ status: result.status,
318
+ content: result.content,
319
+ call: {
320
+ tellaskContent,
321
+ mentionList: resolveTellaskResultMentionList(result, 'buildTellaskResultEvent'),
322
+ sessionSlug: effectiveSessionSlug,
323
+ },
324
+ responder: {
325
+ responderId,
326
+ ...((result.responder?.agentId ?? result.agentId)
327
+ ? { agentId: result.responder?.agentId ?? result.agentId }
328
+ : {}),
329
+ ...((result.responder?.originMemberId ?? result.originMemberId)
330
+ ? { originMemberId: result.responder?.originMemberId ?? result.originMemberId }
331
+ : {}),
332
+ },
333
+ ...(route ? { route } : {}),
334
+ };
335
+ }
336
+ if (result.callName === 'tellaskSessionless') {
337
+ return {
338
+ type: 'tellask_result_evt',
339
+ course,
340
+ genseq: result.genseq,
341
+ calling_genseq: typeof result.calling_genseq === 'number'
342
+ ? (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq)
343
+ : undefined,
344
+ callId: result.callId,
345
+ callName: result.callName,
346
+ status: result.status,
347
+ content: result.content,
348
+ call: {
349
+ tellaskContent,
350
+ mentionList: resolveTellaskResultMentionList(result, 'buildTellaskResultEvent'),
351
+ },
352
+ responder: {
353
+ responderId,
354
+ ...((result.responder?.agentId ?? result.agentId)
355
+ ? { agentId: result.responder?.agentId ?? result.agentId }
356
+ : {}),
357
+ ...((result.responder?.originMemberId ?? result.originMemberId)
358
+ ? { originMemberId: result.responder?.originMemberId ?? result.originMemberId }
359
+ : {}),
360
+ },
361
+ ...(route ? { route } : {}),
362
+ };
363
+ }
364
+ return {
365
+ type: 'tellask_result_evt',
366
+ course,
367
+ genseq: result.genseq,
368
+ calling_genseq: typeof result.calling_genseq === 'number'
369
+ ? (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq)
370
+ : undefined,
371
+ callId: result.callId,
372
+ callName: result.callName,
373
+ status: result.status,
374
+ content: result.content,
375
+ call: {
376
+ tellaskContent,
377
+ },
378
+ responder: {
379
+ responderId,
380
+ ...((result.responder?.agentId ?? result.agentId)
381
+ ? { agentId: result.responder?.agentId ?? result.agentId }
382
+ : {}),
383
+ ...((result.responder?.originMemberId ?? result.originMemberId)
384
+ ? { originMemberId: result.responder?.originMemberId ?? result.originMemberId }
385
+ : {}),
386
+ },
387
+ ...(route ? { route } : {}),
388
+ };
389
+ }
390
+ function buildTellaskCarryoverEvent(result, course) {
391
+ if (result.callName === 'tellask') {
392
+ const sessionSlug = result.sessionSlug?.trim();
393
+ if (!sessionSlug) {
394
+ throw new Error(`buildTellaskCarryoverEvent invariant violation: missing sessionSlug for tellask call ${result.callId}`);
395
+ }
396
+ return {
397
+ type: 'tellask_carryover_evt',
398
+ course,
399
+ genseq: result.genseq,
400
+ originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
401
+ carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
402
+ responderId: result.responderId,
403
+ callName: result.callName,
404
+ sessionSlug,
405
+ mentionList: result.mentionList ?? [],
406
+ tellaskContent: result.tellaskContent,
407
+ status: result.status,
408
+ response: result.response,
409
+ content: result.content,
410
+ agentId: result.agentId,
411
+ callId: result.callId,
412
+ originMemberId: result.originMemberId,
413
+ ...(result.calleeDialogId ? { calleeDialogId: result.calleeDialogId } : {}),
414
+ ...(typeof result.calleeCourse === 'number'
415
+ ? { calleeCourse: (0, storage_1.toCalleeCourseNumber)(result.calleeCourse) }
416
+ : {}),
417
+ ...(typeof result.calleeGenseq === 'number'
418
+ ? { calleeGenseq: (0, storage_1.toCalleeGenerationSeqNumber)(result.calleeGenseq) }
419
+ : {}),
420
+ };
421
+ }
422
+ if (result.callName === 'tellaskSessionless') {
423
+ return {
424
+ type: 'tellask_carryover_evt',
425
+ course,
426
+ genseq: result.genseq,
427
+ originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
428
+ carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
429
+ responderId: result.responderId,
430
+ callName: result.callName,
431
+ mentionList: result.mentionList ?? [],
432
+ tellaskContent: result.tellaskContent,
433
+ status: result.status,
434
+ response: result.response,
435
+ content: result.content,
436
+ agentId: result.agentId,
437
+ callId: result.callId,
438
+ originMemberId: result.originMemberId,
439
+ ...(result.calleeDialogId ? { calleeDialogId: result.calleeDialogId } : {}),
440
+ ...(typeof result.calleeCourse === 'number'
441
+ ? { calleeCourse: (0, storage_1.toCalleeCourseNumber)(result.calleeCourse) }
442
+ : {}),
443
+ ...(typeof result.calleeGenseq === 'number'
444
+ ? { calleeGenseq: (0, storage_1.toCalleeGenerationSeqNumber)(result.calleeGenseq) }
445
+ : {}),
446
+ };
203
447
  }
448
+ return {
449
+ type: 'tellask_carryover_evt',
450
+ course,
451
+ genseq: result.genseq,
452
+ originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
453
+ carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
454
+ responderId: result.responderId,
455
+ callName: result.callName,
456
+ tellaskContent: result.tellaskContent,
457
+ status: result.status,
458
+ response: result.response,
459
+ content: result.content,
460
+ agentId: result.agentId,
461
+ callId: result.callId,
462
+ originMemberId: result.originMemberId,
463
+ ...(result.calleeDialogId ? { calleeDialogId: result.calleeDialogId } : {}),
464
+ ...(typeof result.calleeCourse === 'number'
465
+ ? { calleeCourse: (0, storage_1.toCalleeCourseNumber)(result.calleeCourse) }
466
+ : {}),
467
+ ...(typeof result.calleeGenseq === 'number'
468
+ ? { calleeGenseq: (0, storage_1.toCalleeGenerationSeqNumber)(result.calleeGenseq) }
469
+ : {}),
470
+ };
471
+ }
472
+ function formatTellaskCallArguments(record) {
473
+ return record.rawArgumentsText;
474
+ }
475
+ function isReplyTellaskCallRecordName(name) {
476
+ return (name === 'replyTellask' || name === 'replyTellaskSessionless' || name === 'replyTellaskBack');
204
477
  }
205
478
  function resolveRootGenerationAnchor(dialog) {
206
479
  const rootDialog = dialog instanceof dialog_1.SubDialog ? dialog.rootDialog : dialog;
@@ -643,41 +916,92 @@ function isSubdialogResponseRecord(value) {
643
916
  }
644
917
  // Remove old type definitions - now using kernel/types/storage.ts
645
918
  const id_1 = require("./utils/id");
646
- function parseReplayTellaskSpecialCall(record) {
919
+ function parseReplayTellaskCall(record) {
920
+ if (record.deliveryMode !== 'tellask_call_start') {
921
+ return null;
922
+ }
923
+ let parsed;
924
+ try {
925
+ parsed = JSON.parse(record.rawArgumentsText);
926
+ }
927
+ catch (error) {
928
+ throw new Error(`persisted tellask rawArgumentsText is not valid JSON for replay (callId=${record.id}, name=${record.name}): ${error instanceof Error ? error.message : String(error)}`);
929
+ }
930
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
931
+ throw new Error(`persisted tellask rawArgumentsText must decode to an object for replay (callId=${record.id}, name=${record.name})`);
932
+ }
933
+ const args = parsed;
647
934
  switch (record.name) {
648
- case 'tellaskBack':
935
+ case 'tellaskBack': {
936
+ const tellaskContent = args['tellaskContent'];
937
+ if (typeof tellaskContent !== 'string' || tellaskContent.trim() === '') {
938
+ throw new Error(`persisted tellaskBack missing tellaskContent (callId=${record.id})`);
939
+ }
649
940
  return {
650
941
  callName: 'tellaskBack',
651
- tellaskContent: record.tellaskContent,
942
+ tellaskContent,
652
943
  callId: record.id,
653
944
  };
654
- case 'askHuman':
945
+ }
946
+ case 'askHuman': {
947
+ const tellaskContent = args['tellaskContent'];
948
+ if (typeof tellaskContent !== 'string' || tellaskContent.trim() === '') {
949
+ throw new Error(`persisted askHuman missing tellaskContent (callId=${record.id})`);
950
+ }
655
951
  return {
656
952
  callName: 'askHuman',
657
- tellaskContent: record.tellaskContent,
953
+ tellaskContent,
658
954
  callId: record.id,
659
955
  };
660
- case 'freshBootsReasoning':
956
+ }
957
+ case 'freshBootsReasoning': {
958
+ const tellaskContent = args['tellaskContent'];
959
+ if (typeof tellaskContent !== 'string' || tellaskContent.trim() === '') {
960
+ throw new Error(`persisted freshBootsReasoning missing tellaskContent (callId=${record.id})`);
961
+ }
661
962
  return {
662
963
  callName: 'freshBootsReasoning',
663
- tellaskContent: record.tellaskContent,
964
+ tellaskContent,
664
965
  callId: record.id,
665
966
  };
666
- case 'tellask':
967
+ }
968
+ case 'tellask': {
969
+ const targetAgentId = args['targetAgentId'];
970
+ const sessionSlug = args['sessionSlug'];
971
+ const tellaskContent = args['tellaskContent'];
972
+ if (typeof targetAgentId !== 'string' || targetAgentId.trim() === '') {
973
+ throw new Error(`persisted tellask missing targetAgentId (callId=${record.id})`);
974
+ }
975
+ if (typeof sessionSlug !== 'string' || sessionSlug.trim() === '') {
976
+ throw new Error(`persisted tellask missing sessionSlug (callId=${record.id})`);
977
+ }
978
+ if (typeof tellaskContent !== 'string' || tellaskContent.trim() === '') {
979
+ throw new Error(`persisted tellask missing tellaskContent (callId=${record.id})`);
980
+ }
667
981
  return {
668
982
  callName: 'tellask',
669
- mentionList: [`@${record.targetAgentId}`],
670
- sessionSlug: record.sessionSlug,
671
- tellaskContent: record.tellaskContent,
983
+ mentionList: [`@${targetAgentId}`],
984
+ sessionSlug,
985
+ tellaskContent,
672
986
  callId: record.id,
673
987
  };
674
- case 'tellaskSessionless':
988
+ }
989
+ case 'tellaskSessionless': {
990
+ const targetAgentId = args['targetAgentId'];
991
+ const tellaskContent = args['tellaskContent'];
992
+ if (typeof targetAgentId !== 'string' || targetAgentId.trim() === '') {
993
+ throw new Error(`persisted tellaskSessionless missing targetAgentId (callId=${record.id})`);
994
+ }
995
+ if (typeof tellaskContent !== 'string' || tellaskContent.trim() === '') {
996
+ throw new Error(`persisted tellaskSessionless missing tellaskContent (callId=${record.id})`);
997
+ }
675
998
  return {
676
999
  callName: 'tellaskSessionless',
677
- mentionList: [`@${record.targetAgentId}`],
678
- tellaskContent: record.tellaskContent,
1000
+ mentionList: [`@${targetAgentId}`],
1001
+ tellaskContent,
679
1002
  callId: record.id,
680
1003
  };
1004
+ }
681
1005
  case 'replyTellask':
682
1006
  case 'replyTellaskSessionless':
683
1007
  case 'replyTellaskBack':
@@ -686,6 +1010,16 @@ function parseReplayTellaskSpecialCall(record) {
686
1010
  return null;
687
1011
  }
688
1012
  }
1013
+ function isTellaskCallFunctionName(name) {
1014
+ return (name === 'tellaskBack' ||
1015
+ name === 'tellask' ||
1016
+ name === 'tellaskSessionless' ||
1017
+ name === 'replyTellask' ||
1018
+ name === 'replyTellaskSessionless' ||
1019
+ name === 'replyTellaskBack' ||
1020
+ name === 'askHuman' ||
1021
+ name === 'freshBootsReasoning');
1022
+ }
689
1023
  /**
690
1024
  * Uses append-only pattern for events, exceptional overwrite for reminders
691
1025
  */
@@ -844,455 +1178,54 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
844
1178
  if (!Number.isFinite(genseq) || genseq <= 0) {
845
1179
  throw new Error(`receiveFuncResult invariant violation: missing valid genseq for func result ${funcResult.id}`);
846
1180
  }
847
- // Persist function result record
848
- const funcResultRecord = {
849
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
850
- type: 'func_result_record',
851
- id: funcResult.id,
852
- name: funcResult.name,
853
- content: funcResult.content,
854
- contentItems: funcResult.contentItems,
855
- genseq,
856
- };
1181
+ const funcResultRecord = buildFuncResultRecord(funcResult, genseq);
857
1182
  await this.appendEvent(dialog, course, funcResultRecord);
858
1183
  // Send event to frontend
859
- const funcResultEvt = {
860
- type: 'func_result_evt',
861
- id: funcResult.id,
1184
+ if (!isSuppressedTellaskPlaceholderFuncResult({
862
1185
  name: funcResult.name,
863
1186
  content: funcResult.content,
864
- contentItems: funcResult.contentItems,
865
- course,
866
- };
867
- (0, evt_registry_1.postDialogEvent)(dialog, funcResultEvt);
1187
+ })) {
1188
+ const funcResultEvt = {
1189
+ type: 'func_result_evt',
1190
+ id: funcResult.id,
1191
+ name: funcResult.name,
1192
+ content: funcResult.content,
1193
+ contentItems: funcResult.contentItems,
1194
+ course,
1195
+ };
1196
+ (0, evt_registry_1.postDialogEvent)(dialog, funcResultEvt);
1197
+ }
868
1198
  }
869
- /**
870
- * Receive and handle tellask call results with callId for inline result display
871
- *
872
- * Call Types:
873
- * - tellask-special function call (inline bubble)
874
- * - Result displays INLINE in the same bubble
875
- * - Uses callId for correlation between call_start and response
876
- * - Uses receiveTellaskCallResult() + callId parameter
877
- *
878
- * - Tellask sideline response (subdialog response bubble)
879
- * - Result displays in SEPARATE bubble (subdialog response)
880
- * - Uses calleeDialogId for correlation
881
- * - Uses receiveTellaskResponse() instead
882
- *
883
- * @param dialog - The dialog receiving the response
884
- * @param responderId - ID of the tool/agent that responded (e.g., "add_reminder")
885
- * @param mentionList - Mention list of the original call
886
- * @param tellaskContent - Tellask content of the original call
887
- * @param result - The result content to display
888
- * @param status - Response status ('completed' | 'failed')
889
- * @param callId - Correlation ID from call_start_evt (REQUIRED for inline display)
890
- */
891
- async receiveTellaskCallResult(dialog, responderId, callName, mentionList, tellaskContent, result, status, callId) {
1199
+ async receiveTellaskResult(dialog, result) {
892
1200
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
893
- const calling_genseq = dialog.activeGenSeqOrUndefined !== undefined
894
- ? (0, storage_1.toCallingGenerationSeqNumber)(dialog.activeGenSeqOrUndefined)
895
- : undefined;
896
- // Persist record WITH callId for replay correlation
897
- const ev = (() => {
898
- switch (callName) {
899
- case 'tellask':
900
- case 'tellaskSessionless':
901
- return {
902
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
903
- type: 'tellask_call_result_record',
904
- responderId,
905
- callName,
906
- mentionList: mentionList ?? [],
907
- tellaskContent,
908
- status,
909
- result,
910
- calling_genseq,
911
- callId,
912
- };
913
- case 'tellaskBack':
914
- case 'askHuman':
915
- case 'freshBootsReasoning':
916
- return {
917
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
918
- type: 'tellask_call_result_record',
919
- responderId,
920
- callName,
921
- tellaskContent,
922
- status,
923
- result,
924
- calling_genseq,
925
- callId,
926
- };
927
- }
928
- })();
929
- await this.appendEvent(dialog, course, ev);
930
- // Emit TellaskCallResultEvent WITH callId for UI correlation
931
- const toolResponseEvt = (() => {
932
- switch (callName) {
933
- case 'tellask':
934
- case 'tellaskSessionless':
935
- return {
936
- type: 'tellask_call_result_evt',
937
- responderId,
938
- callName,
939
- mentionList: mentionList ?? [],
940
- tellaskContent,
941
- status,
942
- result,
943
- course,
944
- calling_genseq,
945
- callId,
946
- };
947
- case 'tellaskBack':
948
- case 'askHuman':
949
- case 'freshBootsReasoning':
950
- return {
951
- type: 'tellask_call_result_evt',
952
- responderId,
953
- callName,
954
- tellaskContent,
955
- status,
956
- result,
957
- course,
958
- calling_genseq,
959
- callId,
960
- };
961
- }
962
- })();
963
- (0, evt_registry_1.postDialogEvent)(dialog, toolResponseEvt);
964
- }
965
- /**
966
- * Receive and handle tellask responses (separate bubble for subdialog/supdialog replies)
967
- *
968
- * Call Types:
969
- * - Tellask sideline response
970
- * - Result displays in SEPARATE bubble (subdialog or supdialog response)
971
- * - Uses calleeDialogId for correlation (not callId)
972
- * - Uses this method (receiveTellaskResponse)
973
- *
974
- * @param dialog - The dialog receiving the response
975
- * @param responderId - ID of the responder agent (e.g., "coder")
976
- * @param mentionList - Mention list of the original tellask
977
- * @param tellaskContent - Tellask content of the original tellask
978
- * @param status - Response status ('completed' | 'failed')
979
- * @param calleeDialogId - ID of the callee dialog (subdialog OR supdialog) for navigation links
980
- */
981
- async receiveTellaskResponse(dialog, responderId, callName, mentionList, tellaskContent, status, calleeDialogId, options) {
982
- const currentCourse = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
983
- const calling_genseq = dialog.activeGenSeqOrUndefined !== undefined
984
- ? (0, storage_1.toCallingGenerationSeqNumber)(dialog.activeGenSeqOrUndefined)
985
- : undefined;
986
- const calleeDialogSelfId = calleeDialogId ? calleeDialogId.selfId : undefined;
987
- const response = options.response;
988
- const agentId = options.agentId;
989
- const callId = options.callId;
990
- const originMemberId = options.originMemberId;
991
- const originCourse = options.originCourse;
992
- const carryoverContent = options.carryoverContent;
993
- const carryoverText = typeof carryoverContent === 'string' && carryoverContent.trim() !== ''
994
- ? carryoverContent
995
- : undefined;
996
- const sessionSlug = typeof options.sessionSlug === 'string' && options.sessionSlug.trim() !== ''
997
- ? options.sessionSlug.trim()
998
- : undefined;
999
- const calleeCourse = options.calleeCourse;
1000
- const calleeGenseq = options.calleeGenseq;
1001
- const normalizedMentionList = mentionList ?? [];
1002
- const isCrossCourseCarryover = originCourse !== undefined && originCourse !== currentCourse;
1003
- if (!isCrossCourseCarryover) {
1004
- const responseCourse = originCourse ?? currentCourse;
1005
- const ev = (() => {
1006
- switch (callName) {
1007
- case 'tellask':
1008
- if (!sessionSlug) {
1009
- throw new Error(`receiveTellaskResponse invariant violation: missing sessionSlug for tellask ` +
1010
- `(dialogId=${dialog.id.selfId}, callId=${callId})`);
1011
- }
1012
- return {
1013
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1014
- type: 'tellask_response_record',
1015
- responderId,
1016
- callName,
1017
- sessionSlug,
1018
- calleeDialogId: calleeDialogSelfId,
1019
- calleeCourse,
1020
- calleeGenseq,
1021
- mentionList: normalizedMentionList,
1022
- tellaskContent,
1023
- status,
1024
- calling_genseq,
1025
- response,
1026
- agentId,
1027
- callId,
1028
- originMemberId,
1029
- };
1030
- case 'tellaskSessionless':
1031
- return {
1032
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1033
- type: 'tellask_response_record',
1034
- responderId,
1035
- callName,
1036
- calleeDialogId: calleeDialogSelfId,
1037
- calleeCourse,
1038
- calleeGenseq,
1039
- mentionList: normalizedMentionList,
1040
- tellaskContent,
1041
- status,
1042
- calling_genseq,
1043
- response,
1044
- agentId,
1045
- callId,
1046
- originMemberId,
1047
- };
1048
- case 'tellaskBack':
1049
- case 'freshBootsReasoning':
1050
- return {
1051
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1052
- type: 'tellask_response_record',
1053
- responderId,
1054
- callName,
1055
- calleeDialogId: calleeDialogSelfId,
1056
- calleeCourse,
1057
- calleeGenseq,
1058
- tellaskContent,
1059
- status,
1060
- calling_genseq,
1061
- response,
1062
- agentId,
1063
- callId,
1064
- originMemberId,
1065
- };
1066
- }
1067
- })();
1068
- await this.appendEvent(dialog, responseCourse, ev);
1069
- const tellaskResponseEvt = (() => {
1070
- switch (callName) {
1071
- case 'tellask':
1072
- if (!sessionSlug) {
1073
- throw new Error(`receiveTellaskResponse invariant violation: missing sessionSlug for tellask ` +
1074
- `(dialogId=${dialog.id.selfId}, callId=${callId})`);
1075
- }
1076
- return {
1077
- type: 'tellask_response_evt',
1078
- responderId,
1079
- callName,
1080
- sessionSlug,
1081
- calleeDialogId: calleeDialogSelfId,
1082
- calleeCourse,
1083
- calleeGenseq,
1084
- mentionList: normalizedMentionList,
1085
- tellaskContent,
1086
- status,
1087
- course: responseCourse,
1088
- calling_genseq,
1089
- response,
1090
- agentId,
1091
- callId,
1092
- originMemberId,
1093
- };
1094
- case 'tellaskSessionless':
1095
- return {
1096
- type: 'tellask_response_evt',
1097
- responderId,
1098
- callName,
1099
- calleeDialogId: calleeDialogSelfId,
1100
- calleeCourse,
1101
- calleeGenseq,
1102
- mentionList: normalizedMentionList,
1103
- tellaskContent,
1104
- status,
1105
- course: responseCourse,
1106
- calling_genseq,
1107
- response,
1108
- agentId,
1109
- callId,
1110
- originMemberId,
1111
- };
1112
- case 'tellaskBack':
1113
- case 'freshBootsReasoning':
1114
- return {
1115
- type: 'tellask_response_evt',
1116
- responderId,
1117
- callName,
1118
- calleeDialogId: calleeDialogSelfId,
1119
- calleeCourse,
1120
- calleeGenseq,
1121
- tellaskContent,
1122
- status,
1123
- course: responseCourse,
1124
- calling_genseq,
1125
- response,
1126
- agentId,
1127
- callId,
1128
- originMemberId,
1129
- };
1130
- }
1131
- })();
1132
- (0, evt_registry_1.postDialogEvent)(dialog, tellaskResponseEvt);
1133
- return;
1134
- }
1135
- if (!originCourse) {
1136
- throw new Error(`receiveTellaskResponse invariant violation: missing originCourse for cross-course carryover ` +
1137
- `(dialogId=${dialog.id.selfId}, callId=${callId}, currentCourse=${currentCourse})`);
1138
- }
1139
- if (callName === 'tellaskBack') {
1140
- throw new Error(`tellask carryover does not support tellaskBack (dialogId=${dialog.id.selfId}, callId=${callId})`);
1201
+ const genseq = dialog.activeGenSeqOrUndefined ?? result.genseq;
1202
+ if (!Number.isFinite(genseq) || genseq <= 0) {
1203
+ throw new Error(`receiveTellaskResult invariant violation: missing valid genseq for tellask result ${result.callId}`);
1141
1204
  }
1142
- if (carryoverText === undefined) {
1143
- throw new Error(`receiveTellaskResponse invariant violation: missing carryoverContent for cross-course tellask ` +
1144
- `(dialogId=${dialog.id.selfId}, callId=${callId}, originCourse=${originCourse}, currentCourse=${currentCourse})`);
1205
+ const normalizedResult = genseq === result.genseq
1206
+ ? result
1207
+ : {
1208
+ ...result,
1209
+ genseq,
1210
+ };
1211
+ const record = buildTellaskResultRecord(normalizedResult, genseq);
1212
+ await this.appendEvent(dialog, course, record);
1213
+ (0, evt_registry_1.postDialogEvent)(dialog, buildTellaskResultEvent(normalizedResult, course));
1214
+ }
1215
+ async receiveTellaskCarryover(dialog, result) {
1216
+ const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
1217
+ const genseq = dialog.activeGenSeqOrUndefined ?? result.genseq;
1218
+ if (!Number.isFinite(genseq) || genseq <= 0) {
1219
+ throw new Error(`receiveTellaskCarryover invariant violation: missing valid genseq for tellask carryover ${result.callId}`);
1145
1220
  }
1146
- const carryoverCallRecord = {
1147
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1148
- type: 'tellask_call_carryover_record',
1149
- responderId,
1150
- status,
1151
- callId,
1152
- carryoverCourse: (0, storage_1.toDialogCourseNumber)(currentCourse),
1153
- };
1154
- await this.appendEvent(dialog, originCourse, carryoverCallRecord);
1155
- const carryoverCallEvent = {
1156
- type: 'tellask_call_carryover_evt',
1157
- course: originCourse,
1158
- responderId,
1159
- status,
1160
- callId,
1161
- carryoverCourse: (0, storage_1.toDialogCourseNumber)(currentCourse),
1162
- };
1163
- (0, evt_registry_1.postDialogEvent)(dialog, carryoverCallEvent);
1164
- const carryoverRecord = (() => {
1165
- switch (callName) {
1166
- case 'tellask':
1167
- if (!sessionSlug) {
1168
- throw new Error(`receiveTellaskResponse invariant violation: missing sessionSlug for tellask carryover ` +
1169
- `(dialogId=${dialog.id.selfId}, callId=${callId})`);
1170
- }
1171
- return {
1172
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1173
- type: 'tellask_carryover_result_record',
1174
- originCourse,
1175
- responderId,
1176
- callName,
1177
- sessionSlug,
1178
- mentionList: normalizedMentionList,
1179
- tellaskContent,
1180
- status,
1181
- response,
1182
- content: carryoverText,
1183
- agentId,
1184
- callId,
1185
- originMemberId,
1186
- calleeDialogId: calleeDialogSelfId,
1187
- calleeCourse,
1188
- calleeGenseq,
1189
- };
1190
- case 'tellaskSessionless':
1191
- return {
1192
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1193
- type: 'tellask_carryover_result_record',
1194
- originCourse,
1195
- responderId,
1196
- callName,
1197
- mentionList: normalizedMentionList,
1198
- tellaskContent,
1199
- status,
1200
- response,
1201
- content: carryoverText,
1202
- agentId,
1203
- callId,
1204
- originMemberId,
1205
- calleeDialogId: calleeDialogSelfId,
1206
- calleeCourse,
1207
- calleeGenseq,
1208
- };
1209
- case 'freshBootsReasoning':
1210
- return {
1211
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1212
- type: 'tellask_carryover_result_record',
1213
- originCourse,
1214
- responderId,
1215
- callName,
1216
- tellaskContent,
1217
- status,
1218
- response,
1219
- content: carryoverText,
1220
- agentId,
1221
- callId,
1222
- originMemberId,
1223
- calleeDialogId: calleeDialogSelfId,
1224
- calleeCourse,
1225
- calleeGenseq,
1226
- };
1227
- }
1228
- })();
1229
- await this.appendEvent(dialog, currentCourse, carryoverRecord);
1230
- const carryoverEvent = (() => {
1231
- switch (callName) {
1232
- case 'tellask':
1233
- if (!sessionSlug) {
1234
- throw new Error(`receiveTellaskResponse invariant violation: missing sessionSlug for tellask carryover evt ` +
1235
- `(dialogId=${dialog.id.selfId}, callId=${callId})`);
1236
- }
1237
- return {
1238
- type: 'tellask_carryover_result_evt',
1239
- course: currentCourse,
1240
- originCourse,
1241
- responderId,
1242
- callName,
1243
- sessionSlug,
1244
- mentionList: normalizedMentionList,
1245
- tellaskContent,
1246
- status,
1247
- response,
1248
- content: carryoverText,
1249
- agentId,
1250
- callId,
1251
- originMemberId,
1252
- calleeDialogId: calleeDialogSelfId,
1253
- calleeCourse,
1254
- calleeGenseq,
1255
- };
1256
- case 'tellaskSessionless':
1257
- return {
1258
- type: 'tellask_carryover_result_evt',
1259
- course: currentCourse,
1260
- originCourse,
1261
- responderId,
1262
- callName,
1263
- mentionList: normalizedMentionList,
1264
- tellaskContent,
1265
- status,
1266
- response,
1267
- content: carryoverText,
1268
- agentId,
1269
- callId,
1270
- originMemberId,
1271
- calleeDialogId: calleeDialogSelfId,
1272
- calleeCourse,
1273
- calleeGenseq,
1274
- };
1275
- case 'freshBootsReasoning':
1276
- return {
1277
- type: 'tellask_carryover_result_evt',
1278
- course: currentCourse,
1279
- originCourse,
1280
- responderId,
1281
- callName,
1282
- tellaskContent,
1283
- status,
1284
- response,
1285
- content: carryoverText,
1286
- agentId,
1287
- callId,
1288
- originMemberId,
1289
- calleeDialogId: calleeDialogSelfId,
1290
- calleeCourse,
1291
- calleeGenseq,
1292
- };
1293
- }
1294
- })();
1295
- (0, evt_registry_1.postDialogEvent)(dialog, carryoverEvent);
1221
+ const normalizedResult = genseq === result.genseq
1222
+ ? result
1223
+ : {
1224
+ ...result,
1225
+ genseq,
1226
+ };
1227
+ await this.appendEvent(dialog, course, buildTellaskCarryoverRecord(normalizedResult, genseq));
1228
+ (0, evt_registry_1.postDialogEvent)(dialog, buildTellaskCarryoverEvent(normalizedResult, course));
1296
1229
  }
1297
1230
  /**
1298
1231
  * Ensure subdialog directory exists (delegate to DialogPersistence)
@@ -1306,6 +1239,9 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1306
1239
  async appendEvent(dialog, course, event) {
1307
1240
  await DialogPersistence.appendEvent(this.dialogId, course, attachRootGenerationRef(dialog, event));
1308
1241
  }
1242
+ async appendEvents(dialog, course, events) {
1243
+ await DialogPersistence.appendEvents(this.dialogId, course, events.map((event) => attachRootGenerationRef(dialog, event)));
1244
+ }
1309
1245
  /**
1310
1246
  * Notify start of LLM generation for frontend bubble management
1311
1247
  * CRITICAL: This must be called BEFORE any substream events (thinking_start, markdown_start, etc.)
@@ -1573,6 +1509,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1573
1509
  ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1574
1510
  type: 'web_search_call_record',
1575
1511
  genseq: dialog.activeGenSeq,
1512
+ source: payload.source,
1576
1513
  phase: payload.phase,
1577
1514
  itemId,
1578
1515
  status: payload.status,
@@ -1583,6 +1520,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1583
1520
  type: 'web_search_call_evt',
1584
1521
  course,
1585
1522
  genseq: dialog.activeGenSeq,
1523
+ source: payload.source,
1586
1524
  phase: payload.phase,
1587
1525
  itemId,
1588
1526
  status: payload.status,
@@ -1590,6 +1528,111 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1590
1528
  };
1591
1529
  (0, evt_registry_1.postDialogEvent)(dialog, evt);
1592
1530
  }
1531
+ async nativeToolCall(dialog, payload) {
1532
+ const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
1533
+ const itemId = 'itemId' in payload && typeof payload.itemId === 'string' ? payload.itemId.trim() : undefined;
1534
+ let record;
1535
+ let evt;
1536
+ if (payload.itemType === 'custom_tool_call') {
1537
+ const callId = payload.callId.trim();
1538
+ if (callId === '') {
1539
+ log_1.log.error('Protocol violation: custom nativeToolCall called without callId; dropping event', new Error('native_tool_call_empty_call_id'), {
1540
+ dialog,
1541
+ itemType: payload.itemType,
1542
+ phase: payload.phase,
1543
+ itemId: payload.itemId,
1544
+ status: payload.status,
1545
+ });
1546
+ return;
1547
+ }
1548
+ if (itemId !== undefined && itemId === '') {
1549
+ log_1.log.error('Protocol violation: custom nativeToolCall called with empty optional itemId; dropping event', new Error('native_tool_call_empty_optional_item_id'), {
1550
+ dialog,
1551
+ itemType: payload.itemType,
1552
+ phase: payload.phase,
1553
+ callId,
1554
+ status: payload.status,
1555
+ });
1556
+ return;
1557
+ }
1558
+ record = {
1559
+ ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1560
+ type: 'native_tool_call_record',
1561
+ genseq: dialog.activeGenSeq,
1562
+ source: payload.source,
1563
+ itemType: payload.itemType,
1564
+ phase: payload.phase,
1565
+ callId,
1566
+ ...(itemId !== undefined && itemId !== '' ? { itemId } : {}),
1567
+ status: payload.status,
1568
+ title: payload.title,
1569
+ summary: payload.summary,
1570
+ detail: payload.detail,
1571
+ };
1572
+ evt = {
1573
+ type: 'native_tool_call_evt',
1574
+ course,
1575
+ genseq: dialog.activeGenSeq,
1576
+ source: payload.source,
1577
+ itemType: payload.itemType,
1578
+ phase: payload.phase,
1579
+ callId,
1580
+ ...(itemId !== undefined && itemId !== '' ? { itemId } : {}),
1581
+ status: payload.status,
1582
+ title: payload.title,
1583
+ summary: payload.summary,
1584
+ detail: payload.detail,
1585
+ };
1586
+ }
1587
+ else {
1588
+ if ('callId' in payload) {
1589
+ log_1.log.error('Protocol violation: non-custom nativeToolCall called with unexpected callId; dropping event', new Error('native_tool_call_unexpected_call_id'), {
1590
+ dialog,
1591
+ itemType: payload.itemType,
1592
+ phase: payload.phase,
1593
+ status: payload.status,
1594
+ });
1595
+ return;
1596
+ }
1597
+ if (itemId === undefined || itemId === '') {
1598
+ log_1.log.error('Protocol violation: non-custom nativeToolCall called without itemId; dropping event', new Error('native_tool_call_empty_item_id'), {
1599
+ dialog,
1600
+ itemType: payload.itemType,
1601
+ phase: payload.phase,
1602
+ status: payload.status,
1603
+ });
1604
+ return;
1605
+ }
1606
+ record = {
1607
+ ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1608
+ type: 'native_tool_call_record',
1609
+ genseq: dialog.activeGenSeq,
1610
+ source: payload.source,
1611
+ itemType: payload.itemType,
1612
+ phase: payload.phase,
1613
+ itemId,
1614
+ status: payload.status,
1615
+ title: payload.title,
1616
+ summary: payload.summary,
1617
+ detail: payload.detail,
1618
+ };
1619
+ evt = {
1620
+ type: 'native_tool_call_evt',
1621
+ course,
1622
+ genseq: dialog.activeGenSeq,
1623
+ source: payload.source,
1624
+ itemType: payload.itemType,
1625
+ phase: payload.phase,
1626
+ itemId,
1627
+ status: payload.status,
1628
+ title: payload.title,
1629
+ summary: payload.summary,
1630
+ detail: payload.detail,
1631
+ };
1632
+ }
1633
+ await this.appendEvent(dialog, course, record);
1634
+ (0, evt_registry_1.postDialogEvent)(dialog, evt);
1635
+ }
1593
1636
  /**
1594
1637
  * Emit stream error for current generation lifecycle (uses active genseq when present)
1595
1638
  */
@@ -1641,25 +1684,15 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1641
1684
  * Note: The end_of_user_saying_evt is emitted by the driver after user content
1642
1685
  * is rendered and any tellask calls are parsed/executed.
1643
1686
  */
1644
- async persistUserMessage(dialog, content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallIds, tellaskReplyDirective) {
1687
+ async persistUserMessage(dialog, content, msgId, grammar, origin, userLanguageCode, q4hAnswerCallId, tellaskReplyDirective) {
1645
1688
  const course = dialog.currentCourse;
1646
1689
  // Use activeGenSeqOrUndefined to handle case when genseq hasn't been initialized yet
1647
1690
  const genseq = dialog.activeGenSeqOrUndefined ?? 1;
1648
- const normalizedQ4HAnswerCallIds = (() => {
1649
- if (!q4hAnswerCallIds || q4hAnswerCallIds.length === 0) {
1650
- return undefined;
1651
- }
1652
- const seen = new Set();
1653
- const normalized = [];
1654
- for (const raw of q4hAnswerCallIds) {
1655
- const callId = raw.trim();
1656
- if (callId === '' || seen.has(callId))
1657
- continue;
1658
- seen.add(callId);
1659
- normalized.push(callId);
1660
- }
1661
- return normalized.length > 0 ? normalized : undefined;
1662
- })();
1691
+ const normalizedQ4HAnswerCallId = typeof q4hAnswerCallId === 'string' && q4hAnswerCallId.trim() !== ''
1692
+ ? q4hAnswerCallId.trim()
1693
+ : undefined;
1694
+ // `q4hAnswerCallId` marks continuation glue for a resumed round after askHuman is answered.
1695
+ // The canonical answer fact is persisted separately in tellask result/carryover records.
1663
1696
  const humanEv = {
1664
1697
  ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1665
1698
  type: 'human_text_record',
@@ -1669,7 +1702,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1669
1702
  grammar,
1670
1703
  origin,
1671
1704
  userLanguageCode,
1672
- q4hAnswerCallIds: normalizedQ4HAnswerCallIds,
1705
+ q4hAnswerCallId: normalizedQ4HAnswerCallId,
1673
1706
  tellaskReplyDirective,
1674
1707
  };
1675
1708
  await this.appendEvent(dialog, course, humanEv);
@@ -1737,29 +1770,100 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1737
1770
  /**
1738
1771
  * Persist a function call to storage
1739
1772
  */
1740
- async persistFunctionCall(dialog, id, name, arguments_, genseq) {
1773
+ async persistFunctionCall(dialog, id, name, rawArgumentsText, genseq) {
1741
1774
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
1742
- const funcCallEvent = {
1743
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1744
- type: 'func_call_record',
1745
- genseq,
1746
- id,
1747
- name,
1748
- arguments: arguments_,
1749
- };
1775
+ const funcCallEvent = buildFuncCallRecord({ id, name, rawArgumentsText, genseq });
1750
1776
  await this.appendEvent(dialog, course, funcCallEvent);
1751
1777
  // NOTE: func_call_evt REMOVED - persistence uses FuncCallRecord directly
1752
1778
  // UI display uses func_call_requested_evt instead
1753
1779
  }
1754
- async persistTellaskSpecialCall(dialog, id, name, arguments_, genseq) {
1780
+ async persistTellaskCall(dialog, id, name, rawArgumentsText, genseq, options) {
1755
1781
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
1756
- const tellaskCallEvent = buildTellaskSpecialCallRecord({
1782
+ const tellaskCallEvent = buildTellaskCallRecord({
1757
1783
  id,
1758
1784
  name,
1759
- arguments_,
1785
+ rawArgumentsText,
1760
1786
  genseq,
1787
+ deliveryMode: options?.deliveryMode ??
1788
+ (isReplyTellaskCallRecordName(name) ? 'func_call_requested' : 'tellask_call_start'),
1789
+ });
1790
+ await this.appendEvent(dialog, course, tellaskCallEvent);
1791
+ if (isReplyTellaskCallRecordName(name)) {
1792
+ const funcCallEvt = {
1793
+ type: 'func_call_requested_evt',
1794
+ funcId: id,
1795
+ funcName: name,
1796
+ arguments: formatTellaskCallArguments(tellaskCallEvent),
1797
+ course,
1798
+ genseq: dialog.activeGenSeqOrUndefined ?? genseq,
1799
+ };
1800
+ (0, evt_registry_1.postDialogEvent)(dialog, funcCallEvt);
1801
+ }
1802
+ }
1803
+ async persistFunctionCallResultPair(dialog, id, name, rawArgumentsText, genseq, result) {
1804
+ const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
1805
+ const resultGenseq = dialog.activeGenSeqOrUndefined ?? result.genseq;
1806
+ if (!Number.isFinite(resultGenseq) || resultGenseq <= 0) {
1807
+ throw new Error(`persistFunctionCallResultPair invariant violation: missing valid genseq for func result ${result.id}`);
1808
+ }
1809
+ await this.appendEvents(dialog, course, [
1810
+ buildFuncCallRecord({ id, name, rawArgumentsText, genseq }),
1811
+ buildFuncResultRecord(result, resultGenseq),
1812
+ ]);
1813
+ if (!isSuppressedTellaskPlaceholderFuncResult({
1814
+ name: result.name,
1815
+ content: result.content,
1816
+ })) {
1817
+ const funcResultEvt = {
1818
+ type: 'func_result_evt',
1819
+ id: result.id,
1820
+ name: result.name,
1821
+ content: result.content,
1822
+ contentItems: result.contentItems,
1823
+ course,
1824
+ };
1825
+ (0, evt_registry_1.postDialogEvent)(dialog, funcResultEvt);
1826
+ }
1827
+ }
1828
+ async persistTellaskCallResultPair(dialog, args) {
1829
+ const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
1830
+ const resultGenseq = dialog.activeGenSeqOrUndefined ?? args.result.genseq;
1831
+ if (!Number.isFinite(resultGenseq) || resultGenseq <= 0) {
1832
+ throw new Error(`persistTellaskCallResultPair invariant violation: missing valid genseq for tellask result ${args.result.type === 'func_result_msg' ? args.result.id : args.result.callId}`);
1833
+ }
1834
+ const callRecord = buildTellaskCallRecord({
1835
+ id: args.id,
1836
+ name: args.name,
1837
+ rawArgumentsText: args.rawArgumentsText,
1838
+ genseq: args.genseq,
1839
+ deliveryMode: args.deliveryMode,
1761
1840
  });
1762
- await this.appendEvent(dialog, course, tellaskCallEvent);
1841
+ if (args.result.type === 'func_result_msg') {
1842
+ await this.appendEvents(dialog, course, [
1843
+ callRecord,
1844
+ buildFuncResultRecord(args.result, resultGenseq),
1845
+ ]);
1846
+ if (!isSuppressedTellaskPlaceholderFuncResult({
1847
+ name: args.result.name,
1848
+ content: args.result.content,
1849
+ })) {
1850
+ const funcResultEvt = {
1851
+ type: 'func_result_evt',
1852
+ id: args.result.id,
1853
+ name: args.result.name,
1854
+ content: args.result.content,
1855
+ contentItems: args.result.contentItems,
1856
+ course,
1857
+ };
1858
+ (0, evt_registry_1.postDialogEvent)(dialog, funcResultEvt);
1859
+ }
1860
+ return;
1861
+ }
1862
+ await this.appendEvents(dialog, course, [
1863
+ callRecord,
1864
+ buildTellaskResultRecord(args.result, resultGenseq),
1865
+ ]);
1866
+ (0, evt_registry_1.postDialogEvent)(dialog, buildTellaskResultEvent(args.result, course));
1763
1867
  }
1764
1868
  /**
1765
1869
  * Update questions for human state (exceptional overwrite pattern)
@@ -2034,6 +2138,12 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2034
2138
  }
2035
2139
  switch (event.type) {
2036
2140
  case 'human_text_record': {
2141
+ if (typeof event.q4hAnswerCallId === 'string' && event.q4hAnswerCallId.trim() !== '') {
2142
+ // Q4H-annotated human_text_record is a technical continuation marker for a resumed drive.
2143
+ // The canonical answer fact already exists in tellask result/carryover records, so UI
2144
+ // replay must not emit it as another user prompt bubble.
2145
+ break;
2146
+ }
2037
2147
  const genseq = event.genseq;
2038
2148
  const content = event.content || '';
2039
2149
  const grammar = 'markdown';
@@ -2093,7 +2203,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2093
2203
  grammar,
2094
2204
  origin,
2095
2205
  userLanguageCode,
2096
- q4hAnswerCallIds: event.q4hAnswerCallIds,
2206
+ q4hAnswerCallId: event.q4hAnswerCallId,
2097
2207
  dialog: { selfId: dialog.id.selfId, rootId: dialog.id.rootId },
2098
2208
  timestamp: event.ts,
2099
2209
  }));
@@ -2264,28 +2374,16 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2264
2374
  const content = event.content || '';
2265
2375
  if (!content.trim())
2266
2376
  break;
2267
- const dialogIdent = { selfId: dialog.id.selfId, rootId: dialog.id.rootId };
2268
2377
  if (ws.readyState === 1) {
2269
- ws.send(JSON.stringify({
2270
- type: 'markdown_start_evt',
2271
- course,
2272
- genseq: event.genseq,
2273
- dialog: dialogIdent,
2274
- timestamp: event.ts,
2275
- }));
2276
- ws.send(JSON.stringify({
2277
- type: 'markdown_chunk_evt',
2278
- chunk: content,
2378
+ const uiOnlyMarkdownEvt = {
2379
+ type: 'ui_only_markdown_evt',
2279
2380
  course,
2280
2381
  genseq: event.genseq,
2281
- dialog: dialogIdent,
2282
- timestamp: event.ts,
2283
- }));
2382
+ content,
2383
+ };
2284
2384
  ws.send(JSON.stringify({
2285
- type: 'markdown_finish_evt',
2286
- course,
2287
- genseq: event.genseq,
2288
- dialog: dialogIdent,
2385
+ ...uiOnlyMarkdownEvt,
2386
+ dialog: { selfId: dialog.id.selfId, rootId: dialog.id.rootId },
2289
2387
  timestamp: event.ts,
2290
2388
  }));
2291
2389
  }
@@ -2297,7 +2395,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2297
2395
  type: 'func_call_requested_evt',
2298
2396
  funcId: event.id,
2299
2397
  funcName: event.name,
2300
- arguments: JSON.stringify(event.arguments),
2398
+ arguments: event.rawArgumentsText,
2301
2399
  course,
2302
2400
  genseq: event.genseq,
2303
2401
  dialog: {
@@ -2311,8 +2409,27 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2311
2409
  }
2312
2410
  break;
2313
2411
  }
2314
- case 'tellask_special_call_record': {
2315
- const specialCall = parseReplayTellaskSpecialCall(event);
2412
+ case 'tellask_call_record': {
2413
+ if (event.deliveryMode === 'func_call_requested') {
2414
+ const replyCall = {
2415
+ type: 'func_call_requested_evt',
2416
+ funcId: event.id,
2417
+ funcName: event.name,
2418
+ arguments: formatTellaskCallArguments(event),
2419
+ course,
2420
+ genseq: event.genseq,
2421
+ dialog: {
2422
+ selfId: dialog.id.selfId,
2423
+ rootId: dialog.id.rootId,
2424
+ },
2425
+ timestamp: event.ts,
2426
+ };
2427
+ if (ws.readyState === 1) {
2428
+ ws.send(JSON.stringify(replyCall));
2429
+ }
2430
+ break;
2431
+ }
2432
+ const specialCall = parseReplayTellaskCall(event);
2316
2433
  if (!specialCall) {
2317
2434
  break;
2318
2435
  }
@@ -2378,6 +2495,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2378
2495
  }
2379
2496
  const webSearchCall = {
2380
2497
  type: 'web_search_call_evt',
2498
+ source: event.source,
2381
2499
  phase: event.phase,
2382
2500
  itemId,
2383
2501
  status: event.status,
@@ -2395,7 +2513,108 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2395
2513
  }
2396
2514
  break;
2397
2515
  }
2516
+ case 'native_tool_call_record': {
2517
+ let nativeToolCall;
2518
+ if (event.itemType === 'custom_tool_call') {
2519
+ const callId = typeof event.callId === 'string' ? event.callId.trim() : '';
2520
+ if (callId === '') {
2521
+ log_1.log.error('Protocol violation: persisted custom native_tool_call_record missing callId; skipping WS event', new Error('persisted_native_tool_call_record_missing_call_id'), {
2522
+ dialog,
2523
+ course,
2524
+ genseq: event.genseq,
2525
+ itemType: event.itemType,
2526
+ phase: event.phase,
2527
+ itemId: event.itemId,
2528
+ });
2529
+ break;
2530
+ }
2531
+ if (typeof event.itemId === 'string' && event.itemId.trim() === '') {
2532
+ log_1.log.error('Protocol violation: persisted custom native_tool_call_record carried empty optional itemId; skipping WS event', new Error('persisted_native_tool_call_record_empty_optional_item_id'), {
2533
+ dialog,
2534
+ course,
2535
+ genseq: event.genseq,
2536
+ itemType: event.itemType,
2537
+ phase: event.phase,
2538
+ callId,
2539
+ });
2540
+ break;
2541
+ }
2542
+ nativeToolCall = {
2543
+ type: 'native_tool_call_evt',
2544
+ source: event.source,
2545
+ itemType: event.itemType,
2546
+ phase: event.phase,
2547
+ callId,
2548
+ ...(typeof event.itemId === 'string' && event.itemId.trim() !== ''
2549
+ ? { itemId: event.itemId.trim() }
2550
+ : {}),
2551
+ status: event.status,
2552
+ title: event.title,
2553
+ summary: event.summary,
2554
+ detail: event.detail,
2555
+ course,
2556
+ genseq: event.genseq,
2557
+ dialog: {
2558
+ selfId: dialog.id.selfId,
2559
+ rootId: dialog.id.rootId,
2560
+ },
2561
+ timestamp: event.ts,
2562
+ };
2563
+ }
2564
+ else {
2565
+ if ('callId' in event) {
2566
+ log_1.log.error('Protocol violation: persisted non-custom native_tool_call_record carried unexpected callId; skipping WS event', new Error('persisted_native_tool_call_record_unexpected_call_id'), {
2567
+ dialog,
2568
+ course,
2569
+ genseq: event.genseq,
2570
+ itemType: event.itemType,
2571
+ phase: event.phase,
2572
+ callId: event.callId,
2573
+ });
2574
+ break;
2575
+ }
2576
+ const itemId = typeof event.itemId === 'string' ? event.itemId.trim() : '';
2577
+ if (itemId === '') {
2578
+ log_1.log.error('Protocol violation: persisted native_tool_call_record missing itemId; skipping WS event', new Error('persisted_native_tool_call_record_missing_item_id'), {
2579
+ dialog,
2580
+ course,
2581
+ genseq: event.genseq,
2582
+ itemType: event.itemType,
2583
+ phase: event.phase,
2584
+ });
2585
+ break;
2586
+ }
2587
+ nativeToolCall = {
2588
+ type: 'native_tool_call_evt',
2589
+ source: event.source,
2590
+ itemType: event.itemType,
2591
+ phase: event.phase,
2592
+ itemId,
2593
+ status: event.status,
2594
+ title: event.title,
2595
+ summary: event.summary,
2596
+ detail: event.detail,
2597
+ course,
2598
+ genseq: event.genseq,
2599
+ dialog: {
2600
+ selfId: dialog.id.selfId,
2601
+ rootId: dialog.id.rootId,
2602
+ },
2603
+ timestamp: event.ts,
2604
+ };
2605
+ }
2606
+ if (ws.readyState === 1) {
2607
+ ws.send(JSON.stringify(nativeToolCall));
2608
+ }
2609
+ break;
2610
+ }
2398
2611
  case 'func_result_record': {
2612
+ if (isSuppressedTellaskPlaceholderFuncResult({
2613
+ name: event.name,
2614
+ content: event.content,
2615
+ })) {
2616
+ break;
2617
+ }
2399
2618
  // Handle function result events from persistence
2400
2619
  const funcResult = {
2401
2620
  type: 'func_result_evt',
@@ -2415,6 +2634,45 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2415
2634
  }
2416
2635
  break;
2417
2636
  }
2637
+ case 'tellask_result_record': {
2638
+ const base = {
2639
+ type: 'tellask_result_evt',
2640
+ course,
2641
+ genseq: event.genseq,
2642
+ callId: event.callId,
2643
+ status: event.status,
2644
+ content: event.content,
2645
+ ...(event.calling_genseq !== undefined ? { calling_genseq: event.calling_genseq } : {}),
2646
+ responder: event.responder,
2647
+ ...(event.route ? { route: event.route } : {}),
2648
+ dialog: {
2649
+ selfId: dialog.id.selfId,
2650
+ rootId: dialog.id.rootId,
2651
+ },
2652
+ timestamp: event.ts,
2653
+ };
2654
+ const tellaskResultEvent = event.callName === 'tellask'
2655
+ ? {
2656
+ ...base,
2657
+ callName: event.callName,
2658
+ call: event.call,
2659
+ }
2660
+ : event.callName === 'tellaskSessionless'
2661
+ ? {
2662
+ ...base,
2663
+ callName: event.callName,
2664
+ call: event.call,
2665
+ }
2666
+ : {
2667
+ ...base,
2668
+ callName: event.callName,
2669
+ call: event.call,
2670
+ };
2671
+ if (ws.readyState === 1) {
2672
+ ws.send(JSON.stringify(tellaskResultEvent));
2673
+ }
2674
+ break;
2675
+ }
2418
2676
  case 'quest_for_sup_record': {
2419
2677
  // Handle subdialog creation requests
2420
2678
  const subdialogId = new dialog_1.DialogID(event.subDialogId, dialog.id.rootId);
@@ -2470,55 +2728,6 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2470
2728
  }
2471
2729
  break;
2472
2730
  }
2473
- case 'tellask_call_result_record': {
2474
- // Handle tellask-call inline results
2475
- const responseEvent = (() => {
2476
- switch (event.callName) {
2477
- case 'tellask':
2478
- case 'tellaskSessionless':
2479
- return {
2480
- type: 'tellask_call_result_evt',
2481
- responderId: event.responderId,
2482
- callName: event.callName,
2483
- mentionList: event.mentionList,
2484
- tellaskContent: event.tellaskContent,
2485
- status: event.status,
2486
- result: event.result,
2487
- callId: event.callId || '',
2488
- course,
2489
- calling_genseq: event.calling_genseq,
2490
- dialog: {
2491
- selfId: dialog.id.selfId,
2492
- rootId: dialog.id.rootId,
2493
- },
2494
- timestamp: event.ts,
2495
- };
2496
- case 'tellaskBack':
2497
- case 'askHuman':
2498
- case 'freshBootsReasoning':
2499
- return {
2500
- type: 'tellask_call_result_evt',
2501
- responderId: event.responderId,
2502
- callName: event.callName,
2503
- tellaskContent: event.tellaskContent,
2504
- status: event.status,
2505
- result: event.result,
2506
- callId: event.callId || '',
2507
- course,
2508
- calling_genseq: event.calling_genseq,
2509
- dialog: {
2510
- selfId: dialog.id.selfId,
2511
- rootId: dialog.id.rootId,
2512
- },
2513
- timestamp: event.ts,
2514
- };
2515
- }
2516
- })();
2517
- if (ws.readyState === 1) {
2518
- ws.send(JSON.stringify(responseEvent));
2519
- }
2520
- break;
2521
- }
2522
2731
  case 'tellask_call_anchor_record': {
2523
2732
  const anchorEvent = event.anchorRole === 'assignment'
2524
2733
  ? {
@@ -2556,210 +2765,56 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2556
2765
  }
2557
2766
  break;
2558
2767
  }
2559
- case 'tellask_call_carryover_record': {
2560
- const carryoverEvent = {
2561
- type: 'tellask_call_carryover_evt',
2768
+ case 'subdialog_created_record':
2769
+ case 'reminders_reconciled_record':
2770
+ case 'questions4human_reconciled_record':
2771
+ case 'pending_subdialogs_reconciled_record':
2772
+ case 'subdialog_registry_reconciled_record':
2773
+ case 'subdialog_responses_reconciled_record':
2774
+ break;
2775
+ case 'tellask_carryover_record': {
2776
+ const base = {
2777
+ type: 'tellask_carryover_evt',
2562
2778
  course,
2779
+ genseq: event.genseq,
2780
+ originCourse: event.originCourse,
2781
+ carryoverCourse: event.carryoverCourse,
2563
2782
  responderId: event.responderId,
2783
+ tellaskContent: event.tellaskContent,
2564
2784
  status: event.status,
2785
+ response: event.response,
2786
+ content: event.content,
2787
+ agentId: event.agentId,
2565
2788
  callId: event.callId,
2566
- carryoverCourse: event.carryoverCourse,
2789
+ originMemberId: event.originMemberId,
2790
+ ...(event.calleeDialogId ? { calleeDialogId: event.calleeDialogId } : {}),
2791
+ ...(event.calleeCourse !== undefined ? { calleeCourse: event.calleeCourse } : {}),
2792
+ ...(event.calleeGenseq !== undefined ? { calleeGenseq: event.calleeGenseq } : {}),
2567
2793
  dialog: {
2568
2794
  selfId: dialog.id.selfId,
2569
2795
  rootId: dialog.id.rootId,
2570
2796
  },
2571
2797
  timestamp: event.ts,
2572
2798
  };
2573
- if (ws.readyState === 1) {
2574
- ws.send(JSON.stringify(carryoverEvent));
2575
- }
2576
- break;
2577
- }
2578
- case 'tellask_carryover_result_record': {
2579
- const carryoverEvent = (() => {
2580
- switch (event.callName) {
2581
- case 'tellask':
2582
- return {
2583
- type: 'tellask_carryover_result_evt',
2584
- course,
2585
- originCourse: event.originCourse,
2586
- responderId: event.responderId,
2587
- callName: event.callName,
2588
- sessionSlug: event.sessionSlug,
2589
- mentionList: event.mentionList,
2590
- tellaskContent: event.tellaskContent,
2591
- status: event.status,
2592
- response: event.response,
2593
- content: event.content,
2594
- agentId: event.agentId,
2595
- callId: event.callId,
2596
- originMemberId: event.originMemberId,
2597
- calleeDialogId: event.calleeDialogId,
2598
- calleeCourse: event.calleeCourse,
2599
- calleeGenseq: event.calleeGenseq,
2600
- dialog: {
2601
- selfId: dialog.id.selfId,
2602
- rootId: dialog.id.rootId,
2603
- },
2604
- timestamp: event.ts,
2605
- };
2606
- case 'tellaskSessionless':
2607
- return {
2608
- type: 'tellask_carryover_result_evt',
2609
- course,
2610
- originCourse: event.originCourse,
2611
- responderId: event.responderId,
2612
- callName: event.callName,
2613
- mentionList: event.mentionList,
2614
- tellaskContent: event.tellaskContent,
2615
- status: event.status,
2616
- response: event.response,
2617
- content: event.content,
2618
- agentId: event.agentId,
2619
- callId: event.callId,
2620
- originMemberId: event.originMemberId,
2621
- calleeDialogId: event.calleeDialogId,
2622
- calleeCourse: event.calleeCourse,
2623
- calleeGenseq: event.calleeGenseq,
2624
- dialog: {
2625
- selfId: dialog.id.selfId,
2626
- rootId: dialog.id.rootId,
2627
- },
2628
- timestamp: event.ts,
2629
- };
2630
- case 'freshBootsReasoning':
2631
- return {
2632
- type: 'tellask_carryover_result_evt',
2633
- course,
2634
- originCourse: event.originCourse,
2635
- responderId: event.responderId,
2636
- callName: event.callName,
2637
- tellaskContent: event.tellaskContent,
2638
- status: event.status,
2639
- response: event.response,
2640
- content: event.content,
2641
- agentId: event.agentId,
2642
- callId: event.callId,
2643
- originMemberId: event.originMemberId,
2644
- calleeDialogId: event.calleeDialogId,
2645
- calleeCourse: event.calleeCourse,
2646
- calleeGenseq: event.calleeGenseq,
2647
- dialog: {
2648
- selfId: dialog.id.selfId,
2649
- rootId: dialog.id.rootId,
2650
- },
2651
- timestamp: event.ts,
2652
- };
2653
- }
2654
- })();
2655
- if (ws.readyState === 1) {
2656
- ws.send(JSON.stringify(carryoverEvent));
2657
- }
2658
- break;
2659
- }
2660
- case 'subdialog_created_record':
2661
- case 'reminders_reconciled_record':
2662
- case 'questions4human_reconciled_record':
2663
- case 'pending_subdialogs_reconciled_record':
2664
- case 'subdialog_registry_reconciled_record':
2665
- case 'subdialog_responses_reconciled_record':
2666
- break;
2667
- case 'tellask_response_record': {
2668
- // Handle tellask response events (separate bubble for tellask sideline replies)
2669
- const mentionList = (() => {
2670
- switch (event.callName) {
2671
- case 'tellask':
2672
- case 'tellaskSessionless':
2673
- return event.mentionList;
2674
- case 'tellaskBack':
2675
- case 'freshBootsReasoning':
2676
- return undefined;
2799
+ const tellaskCarryoverEvent = event.callName === 'tellask'
2800
+ ? {
2801
+ ...base,
2802
+ callName: event.callName,
2803
+ sessionSlug: event.sessionSlug,
2804
+ mentionList: event.mentionList,
2677
2805
  }
2678
- })();
2679
- const tellaskResponseEvent = (() => {
2680
- switch (event.callName) {
2681
- case 'tellask': {
2682
- const sessionSlug = typeof event.sessionSlug === 'string' && event.sessionSlug.trim() !== ''
2683
- ? event.sessionSlug.trim()
2684
- : undefined;
2685
- if (!sessionSlug) {
2686
- throw new Error(`Replay tellask_response_record invariant violation: missing sessionSlug for tellask ` +
2687
- `(rootId=${dialog.id.rootId}, selfId=${dialog.id.selfId}, course=${course}, callId=${event.callId})`);
2688
- }
2689
- return {
2690
- type: 'tellask_response_evt',
2691
- responderId: event.responderId,
2692
- callName: event.callName,
2693
- sessionSlug,
2694
- calleeDialogId: event.calleeDialogId,
2695
- calleeCourse: event.calleeCourse,
2696
- calleeGenseq: event.calleeGenseq,
2697
- mentionList,
2698
- tellaskContent: event.tellaskContent,
2699
- status: event.status,
2700
- response: event.response,
2701
- agentId: event.agentId,
2702
- callId: event.callId,
2703
- originMemberId: event.originMemberId,
2704
- course,
2705
- calling_genseq: event.calling_genseq,
2706
- dialog: {
2707
- selfId: dialog.id.selfId,
2708
- rootId: dialog.id.rootId,
2709
- },
2710
- timestamp: event.ts,
2711
- };
2806
+ : event.callName === 'tellaskSessionless'
2807
+ ? {
2808
+ ...base,
2809
+ callName: event.callName,
2810
+ mentionList: event.mentionList,
2712
2811
  }
2713
- case 'tellaskSessionless':
2714
- return {
2715
- type: 'tellask_response_evt',
2716
- responderId: event.responderId,
2717
- callName: event.callName,
2718
- calleeDialogId: event.calleeDialogId,
2719
- calleeCourse: event.calleeCourse,
2720
- calleeGenseq: event.calleeGenseq,
2721
- mentionList,
2722
- tellaskContent: event.tellaskContent,
2723
- status: event.status,
2724
- response: event.response,
2725
- agentId: event.agentId,
2726
- callId: event.callId,
2727
- originMemberId: event.originMemberId,
2728
- course,
2729
- calling_genseq: event.calling_genseq,
2730
- dialog: {
2731
- selfId: dialog.id.selfId,
2732
- rootId: dialog.id.rootId,
2733
- },
2734
- timestamp: event.ts,
2735
- };
2736
- case 'tellaskBack':
2737
- case 'freshBootsReasoning':
2738
- return {
2739
- type: 'tellask_response_evt',
2740
- responderId: event.responderId,
2741
- callName: event.callName,
2742
- calleeDialogId: event.calleeDialogId,
2743
- calleeCourse: event.calleeCourse,
2744
- calleeGenseq: event.calleeGenseq,
2745
- tellaskContent: event.tellaskContent,
2746
- status: event.status,
2747
- response: event.response,
2748
- agentId: event.agentId,
2749
- callId: event.callId,
2750
- originMemberId: event.originMemberId,
2751
- course,
2752
- calling_genseq: event.calling_genseq,
2753
- dialog: {
2754
- selfId: dialog.id.selfId,
2755
- rootId: dialog.id.rootId,
2756
- },
2757
- timestamp: event.ts,
2758
- };
2759
- }
2760
- })();
2812
+ : {
2813
+ ...base,
2814
+ callName: event.callName,
2815
+ };
2761
2816
  if (ws.readyState === 1) {
2762
- ws.send(JSON.stringify(tellaskResponseEvent));
2817
+ ws.send(JSON.stringify(tellaskCarryoverEvent));
2763
2818
  }
2764
2819
  break;
2765
2820
  }
@@ -2791,71 +2846,6 @@ exports.DiskFileDialogStore = DiskFileDialogStore;
2791
2846
  * Utility class for managing dialog persistence
2792
2847
  */
2793
2848
  class DialogPersistence {
2794
- static async repairInterruptedFunctionCallsOnRestore(args) {
2795
- const repaired = [];
2796
- const unresolvedById = new Map();
2797
- for (const event of args.events) {
2798
- if (event.type === 'func_call_record') {
2799
- unresolvedById.set(event.id, event);
2800
- repaired.push(event);
2801
- continue;
2802
- }
2803
- if (event.type === 'func_result_record') {
2804
- if (!unresolvedById.has(event.id)) {
2805
- const repairCall = {
2806
- ts: event.ts,
2807
- type: 'func_call_record',
2808
- genseq: event.genseq,
2809
- id: event.id,
2810
- name: event.name,
2811
- arguments: {},
2812
- };
2813
- log_1.log.error('Recovered orphaned persisted func_result_record by synthesizing missing func_call_record during dialog restore', new Error('dialog_restore_recovered_orphaned_func_result'), {
2814
- rootId: args.dialogId.rootId,
2815
- selfId: args.dialogId.selfId,
2816
- course: args.course,
2817
- genseq: event.genseq,
2818
- callId: event.id,
2819
- toolName: event.name,
2820
- });
2821
- repaired.push(repairCall);
2822
- }
2823
- else {
2824
- unresolvedById.delete(event.id);
2825
- }
2826
- repaired.push(event);
2827
- continue;
2828
- }
2829
- repaired.push(event);
2830
- }
2831
- if (args.status !== 'running') {
2832
- return repaired;
2833
- }
2834
- if (unresolvedById.size === 0) {
2835
- return repaired;
2836
- }
2837
- for (const call of unresolvedById.values()) {
2838
- const repairRecord = {
2839
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
2840
- type: 'func_result_record',
2841
- id: call.id,
2842
- name: call.name,
2843
- content: formatCrashRecoveredFuncResultContent(call.name),
2844
- genseq: call.genseq,
2845
- };
2846
- log_1.log.error('Recovered unresolved persisted func_call_record after unexpected process exit', new Error('dialog_restore_recovered_func_call'), {
2847
- rootId: args.dialogId.rootId,
2848
- selfId: args.dialogId.selfId,
2849
- course: args.course,
2850
- genseq: call.genseq,
2851
- callId: call.id,
2852
- toolName: call.name,
2853
- });
2854
- await this.appendEvent(args.dialogId, args.course, repairRecord, args.status);
2855
- repaired.push(repairRecord);
2856
- }
2857
- return repaired;
2858
- }
2859
2849
  static getLatestWriteBackMutex(key) {
2860
2850
  const existing = this.latestWriteBackMutexes.get(key);
2861
2851
  if (existing)
@@ -2911,7 +2901,6 @@ class DialogPersistence {
2911
2901
  static cloneQuestions4Human(questions) {
2912
2902
  return questions.map((question) => ({
2913
2903
  ...question,
2914
- remainingCallIds: question.remainingCallIds ? [...question.remainingCallIds] : undefined,
2915
2904
  callSiteRef: { ...question.callSiteRef },
2916
2905
  }));
2917
2906
  }
@@ -3294,10 +3283,13 @@ class DialogPersistence {
3294
3283
  /**
3295
3284
  * Append event to course JSONL file (append-only pattern)
3296
3285
  */
3297
- static async appendEvent(dialogId, course, event, status = 'running') {
3286
+ static async appendEvents(dialogId, course, events, status = 'running') {
3298
3287
  const appendMutexKey = this.getCourseAppendMutexKey(dialogId, course, status);
3299
3288
  const release = await this.getCourseAppendMutex(appendMutexKey).acquire();
3300
3289
  try {
3290
+ if (events.length === 0) {
3291
+ return;
3292
+ }
3301
3293
  const dialogPath = this.getDialogEventsPath(dialogId, status);
3302
3294
  const courseFilename = this.getCourseFilename(course);
3303
3295
  const courseFilePath = path.join(dialogPath, courseFilename);
@@ -3306,8 +3298,8 @@ class DialogPersistence {
3306
3298
  // Serialize appends per dialog+course file. Concurrent `appendFile` calls can interleave and
3307
3299
  // corrupt JSONL lines (e.g. tool results appended in parallel), which later manifests as
3308
3300
  // `Unterminated string in JSON ...` during resume.
3309
- const eventLine = JSON.stringify(event) + '\n';
3310
- await fs.promises.appendFile(courseFilePath, eventLine, 'utf-8');
3301
+ const serialized = events.map((event) => JSON.stringify(event)).join('\n') + '\n';
3302
+ await fs.promises.appendFile(courseFilePath, serialized, 'utf-8');
3311
3303
  // Update latest.yaml with new lastModified timestamp
3312
3304
  await this.mutateDialogLatest(dialogId, () => ({
3313
3305
  kind: 'patch',
@@ -3318,13 +3310,16 @@ class DialogPersistence {
3318
3310
  }), status);
3319
3311
  }
3320
3312
  catch (error) {
3321
- log_1.log.error(`Failed to append event to dialog ${dialogId} course ${course}:`, error);
3313
+ log_1.log.error(`Failed to append events to dialog ${dialogId} course ${course}:`, error);
3322
3314
  throw error;
3323
3315
  }
3324
3316
  finally {
3325
3317
  release();
3326
3318
  }
3327
3319
  }
3320
+ static async appendEvent(dialogId, course, event, status = 'running') {
3321
+ await this.appendEvents(dialogId, course, [event], status);
3322
+ }
3328
3323
  static async persistRuntimeGuide(dialog, content, genseq) {
3329
3324
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
3330
3325
  const ev = {
@@ -3741,29 +3736,26 @@ class DialogPersistence {
3741
3736
  }
3742
3737
  static async appendQuestion4HumanState(dialogId, question, status = 'running') {
3743
3738
  const questionId = question.id;
3744
- const normalizedCallId = typeof question.callId === 'string' && question.callId.trim() !== ''
3745
- ? question.callId.trim()
3746
- : null;
3739
+ const normalizedCallId = question.callId.trim();
3740
+ if (normalizedCallId === '') {
3741
+ throw new Error(`Q4H append invariant violation: empty callId (dialog=${dialogId.valueOf()} questionId=${questionId})`);
3742
+ }
3747
3743
  await this.mutateQuestions4HumanState(dialogId, (previousQuestions) => {
3748
3744
  const byId = previousQuestions.find((q) => q.id === questionId);
3749
3745
  if (byId) {
3750
3746
  throw new Error(`Q4H duplicate question id violation: dialog=${dialogId.valueOf()} status=${status} questionId=${questionId} existingAskedAt=${byId.askedAt} incomingAskedAt=${question.askedAt}`);
3751
3747
  }
3752
- if (normalizedCallId) {
3753
- const byCallId = previousQuestions.find((q) => {
3754
- return typeof q.callId === 'string' && q.callId.trim() === normalizedCallId;
3755
- });
3756
- if (byCallId) {
3757
- throw new Error(`Q4H duplicate call id violation: dialog=${dialogId.valueOf()} status=${status} callId=${normalizedCallId} existingQuestionId=${byCallId.id} incomingQuestionId=${questionId} existingAskedAt=${byCallId.askedAt} incomingAskedAt=${question.askedAt}`);
3758
- }
3748
+ const byCallId = previousQuestions.find((q) => q.callId.trim() === normalizedCallId);
3749
+ if (byCallId) {
3750
+ throw new Error(`Q4H duplicate call id violation: dialog=${dialogId.valueOf()} status=${status} callId=${normalizedCallId} existingQuestionId=${byCallId.id} incomingQuestionId=${questionId} existingAskedAt=${byCallId.askedAt} incomingAskedAt=${question.askedAt}`);
3759
3751
  }
3760
3752
  if (previousQuestions.length > 0) {
3761
3753
  const existingIds = previousQuestions.map((q) => q.id).join(',');
3762
3754
  const existingCallIds = previousQuestions
3763
- .map((q) => (typeof q.callId === 'string' ? q.callId.trim() : ''))
3755
+ .map((q) => q.callId.trim())
3764
3756
  .filter((value) => value !== '')
3765
3757
  .join(',');
3766
- throw new Error(`Q4H multi-pending violation: dialog=${dialogId.valueOf()} status=${status} existingCount=${previousQuestions.length} existingQuestionIds=${existingIds} existingCallIds=${existingCallIds} incomingQuestionId=${questionId} incomingCallId=${normalizedCallId ?? ''}`);
3758
+ throw new Error(`Q4H multi-pending violation: dialog=${dialogId.valueOf()} status=${status} existingCount=${previousQuestions.length} existingQuestionIds=${existingIds} existingCallIds=${existingCallIds} incomingQuestionId=${questionId} incomingCallId=${normalizedCallId}`);
3767
3759
  }
3768
3760
  return { kind: 'append', question };
3769
3761
  }, status);
@@ -4929,8 +4921,11 @@ class DialogPersistence {
4929
4921
  }
4930
4922
  }
4931
4923
  /**
4932
- * Restore dialog from disk using JSONL events (optimized: only latest course loaded)
4933
- * For historical courses, use loadCourseEvents() on-demand for UI navigation
4924
+ * Restore dialog from disk using JSONL events (optimized: only latest course loaded).
4925
+ * Historical-course ask/tellask context that still matters to the current round must already be
4926
+ * represented via latest-course carryover records; older courses themselves are not reloaded into
4927
+ * LLM context during restore. For historical courses, use loadCourseEvents() on-demand for UI
4928
+ * navigation.
4934
4929
  */
4935
4930
  static async restoreDialog(dialogId, status = 'running') {
4936
4931
  try {
@@ -4940,16 +4935,11 @@ class DialogPersistence {
4940
4935
  return null;
4941
4936
  }
4942
4937
  const reminders = await this.loadReminderState(dialogId, status);
4943
- // Only load latest course for dialog state restoration
4938
+ // Only load latest course for dialog state restoration. Cross-course business context should
4939
+ // already have been materialized into latest-course carryover records when needed.
4944
4940
  const currentCourse = await this.getCurrentCourseNumber(dialogId, status);
4945
4941
  const latestEvents = await this.readCourseEvents(dialogId, currentCourse, status);
4946
- const repairedEvents = await this.repairInterruptedFunctionCallsOnRestore({
4947
- dialogId,
4948
- course: currentCourse,
4949
- status,
4950
- events: latestEvents,
4951
- });
4952
- const reconstructedState = await this.rebuildFromEvents(repairedEvents, metadata, reminders, currentCourse);
4942
+ const reconstructedState = await this.rebuildFromEvents(latestEvents, metadata, reminders, currentCourse);
4953
4943
  return reconstructedState;
4954
4944
  }
4955
4945
  catch (error) {
@@ -4964,7 +4954,7 @@ class DialogPersistence {
4964
4954
  return await this.readCourseEvents(dialogId, course, status);
4965
4955
  }
4966
4956
  /**
4967
- * Reconstruct dialog state from JSONL events (optimized: only latest course needed)
4957
+ * Reconstruct dialog state from JSONL events (optimized: only latest course needed).
4968
4958
  */
4969
4959
  static async rebuildFromEvents(events, metadata, reminders, currentCourse) {
4970
4960
  // Events are already in chronological order from JSONL file (append-only pattern)
@@ -4995,15 +4985,6 @@ class DialogPersistence {
4995
4985
  });
4996
4986
  break;
4997
4987
  }
4998
- case 'ui_only_markdown_record': {
4999
- messages.push({
5000
- type: 'ui_only_markdown_msg',
5001
- role: 'assistant',
5002
- genseq: event.genseq,
5003
- content: event.content,
5004
- });
5005
- break;
5006
- }
5007
4988
  case 'runtime_guide_record': {
5008
4989
  messages.push({
5009
4990
  type: 'transient_guide_msg',
@@ -5013,6 +4994,11 @@ class DialogPersistence {
5013
4994
  break;
5014
4995
  }
5015
4996
  case 'human_text_record': {
4997
+ if (typeof event.q4hAnswerCallId === 'string' && event.q4hAnswerCallId.trim() !== '') {
4998
+ // Keep this out of transcript reconstruction: it is only the continuation glue for an
4999
+ // answered askHuman call, not a persisted business-level user prompt fact.
5000
+ break;
5001
+ }
5016
5002
  // Convert human text to prompting message
5017
5003
  messages.push({
5018
5004
  type: 'prompting_msg',
@@ -5034,18 +5020,18 @@ class DialogPersistence {
5034
5020
  genseq: event.genseq,
5035
5021
  id: event.id,
5036
5022
  name: event.name,
5037
- arguments: event.arguments ? JSON.stringify(event.arguments) : '{}',
5023
+ arguments: event.rawArgumentsText,
5038
5024
  });
5039
5025
  break;
5040
5026
  }
5041
- case 'tellask_special_call_record': {
5027
+ case 'tellask_call_record': {
5042
5028
  messages.push({
5043
5029
  type: 'func_call_msg',
5044
5030
  role: 'assistant',
5045
5031
  genseq: event.genseq,
5046
5032
  id: event.id,
5047
5033
  name: event.name,
5048
- arguments: formatTellaskSpecialCallArguments(event),
5034
+ arguments: formatTellaskCallArguments(event),
5049
5035
  });
5050
5036
  break;
5051
5037
  }
@@ -5053,6 +5039,10 @@ class DialogPersistence {
5053
5039
  // UI-only timeline event for native web_search tool call visualization.
5054
5040
  // Must not be injected into LLM context reconstruction.
5055
5041
  break;
5042
+ case 'native_tool_call_record':
5043
+ // UI-only timeline event for OpenAI Responses native tool visualization.
5044
+ // Must not be injected into LLM context reconstruction.
5045
+ break;
5056
5046
  case 'func_result_record': {
5057
5047
  // Convert function result to ChatMessage
5058
5048
  messages.push({
@@ -5066,69 +5056,68 @@ class DialogPersistence {
5066
5056
  });
5067
5057
  break;
5068
5058
  }
5069
- case 'tellask_call_result_record': {
5070
- // Convert tellask-call inline result to ChatMessage
5071
- const mentionList = (() => {
5072
- switch (event.callName) {
5073
- case 'tellask':
5074
- case 'tellaskSessionless':
5075
- return event.mentionList;
5076
- case 'tellaskBack':
5077
- case 'askHuman':
5078
- case 'freshBootsReasoning':
5079
- return undefined;
5080
- }
5081
- })();
5059
+ case 'tellask_result_record': {
5082
5060
  messages.push({
5083
5061
  type: 'tellask_result_msg',
5084
5062
  role: 'tool',
5085
- responderId: event.responderId,
5086
- mentionList,
5087
- tellaskContent: event.tellaskContent,
5088
- status: event.status,
5063
+ genseq: event.genseq,
5089
5064
  callId: event.callId,
5090
- content: event.result,
5091
- });
5092
- break;
5093
- }
5094
- case 'tellask_call_carryover_record':
5095
- break;
5096
- case 'tellask_response_record': {
5097
- // Convert tellask response to ChatMessage (separate bubble)
5098
- // Note: Tellask responses are stored as separate records but use same message type
5099
- const mentionList = (() => {
5100
- switch (event.callName) {
5101
- case 'tellask':
5102
- case 'tellaskSessionless':
5103
- return event.mentionList;
5104
- case 'tellaskBack':
5105
- case 'freshBootsReasoning':
5106
- return undefined;
5107
- }
5108
- })();
5109
- messages.push({
5110
- type: 'tellask_result_msg',
5111
- role: 'tool',
5112
- responderId: event.responderId,
5113
- mentionList,
5114
- tellaskContent: event.tellaskContent,
5065
+ callName: event.callName,
5115
5066
  status: event.status,
5116
- callId: event.callId,
5117
- content: event.response,
5067
+ content: event.content,
5068
+ ...(event.calling_genseq !== undefined ? { calling_genseq: event.calling_genseq } : {}),
5069
+ call: event.call,
5070
+ responder: event.responder,
5071
+ ...(event.route ? { route: event.route } : {}),
5072
+ responderId: event.responder.responderId,
5073
+ ...(event.callName === 'tellask' || event.callName === 'tellaskSessionless'
5074
+ ? { mentionList: event.call.mentionList }
5075
+ : {}),
5076
+ tellaskContent: event.call.tellaskContent,
5077
+ ...(event.callName === 'tellask' ? { sessionSlug: event.call.sessionSlug } : {}),
5078
+ ...(event.responder.agentId ? { agentId: event.responder.agentId } : {}),
5079
+ ...(event.responder.originMemberId
5080
+ ? { originMemberId: event.responder.originMemberId }
5081
+ : {}),
5082
+ ...(event.route?.calleeDialogId ? { calleeDialogId: event.route.calleeDialogId } : {}),
5083
+ ...(event.route?.calleeCourse !== undefined
5084
+ ? { calleeCourse: event.route.calleeCourse }
5085
+ : {}),
5086
+ ...(event.route?.calleeGenseq !== undefined
5087
+ ? { calleeGenseq: event.route.calleeGenseq }
5088
+ : {}),
5118
5089
  });
5119
5090
  break;
5120
5091
  }
5121
- case 'tellask_carryover_result_record': {
5092
+ case 'tellask_carryover_record': {
5122
5093
  messages.push({
5123
- type: 'tellask_carryover_result_msg',
5094
+ type: 'tellask_carryover_msg',
5124
5095
  role: 'user',
5096
+ genseq: event.genseq,
5125
5097
  content: event.content,
5126
5098
  originCourse: event.originCourse,
5099
+ carryoverCourse: event.carryoverCourse,
5127
5100
  responderId: event.responderId,
5128
5101
  callName: event.callName,
5129
5102
  tellaskContent: event.tellaskContent,
5130
5103
  status: event.status,
5104
+ response: event.response,
5105
+ agentId: event.agentId,
5131
5106
  callId: event.callId,
5107
+ originMemberId: event.originMemberId,
5108
+ ...(event.callName === 'tellask'
5109
+ ? {
5110
+ mentionList: event.mentionList,
5111
+ sessionSlug: event.sessionSlug,
5112
+ }
5113
+ : event.callName === 'tellaskSessionless'
5114
+ ? {
5115
+ mentionList: event.mentionList,
5116
+ }
5117
+ : {}),
5118
+ ...(event.calleeDialogId ? { calleeDialogId: event.calleeDialogId } : {}),
5119
+ ...(event.calleeCourse !== undefined ? { calleeCourse: event.calleeCourse } : {}),
5120
+ ...(event.calleeGenseq !== undefined ? { calleeGenseq: event.calleeGenseq } : {}),
5132
5121
  });
5133
5122
  break;
5134
5123
  }
@@ -5149,6 +5138,9 @@ class DialogPersistence {
5149
5138
  // This record is UI navigation metadata for deep links in callee dialogs.
5150
5139
  // It does not contribute to model context or chat transcript reconstruction.
5151
5140
  break;
5141
+ case 'ui_only_markdown_record':
5142
+ // UI-only records are replay-only rendering facts. They do not enter dialog messages or ctx.
5143
+ break;
5152
5144
  case 'subdialog_created_record':
5153
5145
  case 'reminders_reconciled_record':
5154
5146
  case 'questions4human_reconciled_record':