dominds 1.18.2 → 1.19.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 (277) hide show
  1. package/dist/access-control.js +6 -6
  2. package/dist/apps/runtime.d.ts +2 -2
  3. package/dist/apps/runtime.js +28 -28
  4. package/dist/apps-host/client.d.ts +1 -1
  5. package/dist/apps-host/host.js +7 -7
  6. package/dist/apps-host/ipc-types.d.ts +2 -2
  7. package/dist/apps-host/ipc-types.js +10 -10
  8. package/dist/cli/read.d.ts +0 -1
  9. package/dist/cli/read.js +1 -6
  10. package/dist/dialog-display-state.d.ts +6 -6
  11. package/dist/dialog-display-state.js +46 -46
  12. package/dist/dialog-factory.d.ts +12 -12
  13. package/dist/dialog-factory.js +33 -30
  14. package/dist/dialog-fork.d.ts +2 -2
  15. package/dist/dialog-fork.js +140 -115
  16. package/dist/dialog-global-registry.d.ts +5 -5
  17. package/dist/dialog-global-registry.js +11 -11
  18. package/dist/dialog-instance-registry.d.ts +3 -3
  19. package/dist/dialog-instance-registry.js +52 -41
  20. package/dist/dialog.d.ts +100 -113
  21. package/dist/dialog.js +274 -229
  22. package/dist/docs/agent-priming.md +5 -5
  23. package/dist/docs/agent-priming.zh.md +5 -5
  24. package/dist/docs/app-constitution.md +1 -1
  25. package/dist/docs/app-constitution.zh.md +1 -1
  26. package/dist/docs/cli-usage.md +1 -1
  27. package/dist/docs/cli-usage.zh.md +1 -1
  28. package/dist/docs/design.md +14 -14
  29. package/dist/docs/design.zh.md +14 -14
  30. package/dist/docs/dialog-persistence.md +58 -58
  31. package/dist/docs/dialog-persistence.zh.md +61 -61
  32. package/dist/docs/dialog-system.md +363 -367
  33. package/dist/docs/dialog-system.zh.md +355 -357
  34. package/dist/docs/diligence-push.md +18 -18
  35. package/dist/docs/diligence-push.zh.md +17 -17
  36. package/dist/docs/dominds-agent-collaboration.zh.md +3 -3
  37. package/dist/docs/dominds-terminology.md +46 -47
  38. package/dist/docs/encapsulated-taskdoc.md +4 -4
  39. package/dist/docs/encapsulated-taskdoc.zh.md +3 -3
  40. package/dist/docs/fbr.md +30 -30
  41. package/dist/docs/fbr.zh.md +15 -15
  42. package/dist/docs/i18n.md +2 -2
  43. package/dist/docs/i18n.zh.md +2 -2
  44. package/dist/docs/mcp-support.md +5 -4
  45. package/dist/docs/mcp-support.zh.md +3 -2
  46. package/dist/docs/memory-system.md +4 -4
  47. package/dist/docs/memory-system.zh.md +1 -1
  48. package/dist/docs/mottos.md +1 -1
  49. package/dist/docs/mottos.zh.md +1 -1
  50. package/dist/docs/q4h.md +3 -3
  51. package/dist/docs/q4h.zh.md +1 -1
  52. package/dist/docs/roadmap.md +2 -2
  53. package/dist/docs/team_mgmt-toolset.md +11 -3
  54. package/dist/docs/team_mgmt-toolset.zh.md +9 -2
  55. package/dist/docs/tellask-collab.md +18 -18
  56. package/dist/docs/tellask-collab.zh.md +8 -8
  57. package/dist/docs/tellask-revive-context-refactor.zh.md +591 -0
  58. package/dist/evt-registry.d.ts +1 -2
  59. package/dist/evt-registry.js +2 -7
  60. package/dist/llm/gen/mock.js +9 -0
  61. package/dist/llm/kernel-driver/context.d.ts +1 -2
  62. package/dist/llm/kernel-driver/context.js +12 -26
  63. package/dist/llm/kernel-driver/drive.js +99 -75
  64. package/dist/llm/kernel-driver/engine.d.ts +2 -2
  65. package/dist/llm/kernel-driver/engine.js +10 -10
  66. package/dist/llm/kernel-driver/fbr.js +6 -6
  67. package/dist/llm/kernel-driver/flow.d.ts +1 -1
  68. package/dist/llm/kernel-driver/flow.js +129 -93
  69. package/dist/llm/kernel-driver/guardrails.js +4 -4
  70. package/dist/llm/kernel-driver/index.d.ts +1 -1
  71. package/dist/llm/kernel-driver/index.js +2 -2
  72. package/dist/llm/kernel-driver/loop.js +30 -30
  73. package/dist/llm/kernel-driver/reply-guidance.js +47 -52
  74. package/dist/llm/kernel-driver/restore.d.ts +3 -3
  75. package/dist/llm/kernel-driver/restore.js +23 -28
  76. package/dist/llm/kernel-driver/runtime.d.ts +1 -1
  77. package/dist/llm/kernel-driver/runtime.js +1 -1
  78. package/dist/llm/kernel-driver/sideDialog-txn.d.ts +8 -0
  79. package/dist/llm/kernel-driver/{subdialog-txn.js → sideDialog-txn.js} +13 -13
  80. package/dist/llm/kernel-driver/{subdialog.d.ts → sideDialog.d.ts} +13 -13
  81. package/dist/llm/kernel-driver/{subdialog.js → sideDialog.js} +203 -170
  82. package/dist/llm/kernel-driver/tellask-special.d.ts +7 -3
  83. package/dist/llm/kernel-driver/tellask-special.js +474 -497
  84. package/dist/llm/kernel-driver/types.d.ts +25 -9
  85. package/dist/mcp/config.d.ts +1 -0
  86. package/dist/mcp/config.js +7 -2
  87. package/dist/mcp/supervisor.d.ts +2 -0
  88. package/dist/mcp/supervisor.js +2 -1
  89. package/dist/minds/builtin/pangu/persona.en.md +4 -4
  90. package/dist/minds/load.js +6 -6
  91. package/dist/minds/system-prompt-parts.d.ts +1 -1
  92. package/dist/minds/system-prompt-parts.js +12 -12
  93. package/dist/minds/system-prompt.d.ts +1 -1
  94. package/dist/minds/system-prompt.js +56 -56
  95. package/dist/persistence-errors.d.ts +1 -1
  96. package/dist/persistence.d.ts +126 -121
  97. package/dist/persistence.js +1190 -786
  98. package/dist/priming.d.ts +3 -3
  99. package/dist/priming.js +62 -61
  100. package/dist/recovery/reply-special.js +5 -5
  101. package/dist/runtime/driver-messages.d.ts +3 -2
  102. package/dist/runtime/driver-messages.js +68 -57
  103. package/dist/runtime/inter-dialog-format.d.ts +12 -10
  104. package/dist/runtime/inter-dialog-format.js +80 -35
  105. package/dist/runtime/interjection-pause-stop.js +1 -1
  106. package/dist/runtime/reply-prompt-copy.d.ts +7 -3
  107. package/dist/runtime/reply-prompt-copy.js +39 -14
  108. package/dist/server/api-routes.js +87 -83
  109. package/dist/server/static-server.js +1 -1
  110. package/dist/server/websocket-handler.js +163 -153
  111. package/dist/tool-availability.js +1 -1
  112. package/dist/tools/app-reminders.js +17 -4
  113. package/dist/tools/ctrl.js +5 -5
  114. package/dist/tools/os.js +16 -16
  115. package/dist/tools/pending-tellask-reminder.js +20 -14
  116. package/dist/tools/prompts/control/en/index.md +1 -1
  117. package/dist/tools/prompts/control/en/principles.md +8 -8
  118. package/dist/tools/prompts/control/en/scenarios.md +7 -7
  119. package/dist/tools/prompts/control/en/tools.md +7 -7
  120. package/dist/tools/prompts/control/zh/principles.md +4 -4
  121. package/dist/tools/prompts/control/zh/scenarios.md +5 -5
  122. package/dist/tools/prompts/control/zh/tools.md +3 -3
  123. package/dist/tools/prompts/team_memory/en/scenarios.md +1 -1
  124. package/dist/tools/prompts/team_memory/zh/scenarios.md +1 -1
  125. package/dist/tools/team_mgmt-manual.js +2 -2
  126. package/dist/tools/team_mgmt-mcp-manual.js +10 -0
  127. package/dist/tools/team_mgmt.js +4 -4
  128. package/dist/utils/taskdoc.js +12 -12
  129. package/package.json +3 -3
  130. package/webapp/dist/assets/{_basePickBy-BPJaiZdW.js → _basePickBy-B7M9Q0Fa.js} +3 -3
  131. package/webapp/dist/assets/_basePickBy-B7M9Q0Fa.js.map +1 -0
  132. package/webapp/dist/assets/{_baseUniq-BEetT15i.js → _baseUniq-DAeYoL6j.js} +2 -2
  133. package/webapp/dist/assets/_baseUniq-DAeYoL6j.js.map +1 -0
  134. package/webapp/dist/assets/{arc-Dm7Zf36f.js → arc-Bh4nDbNR.js} +2 -2
  135. package/webapp/dist/assets/arc-Bh4nDbNR.js.map +1 -0
  136. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-BpTPtkuo.js → architectureDiagram-2XIMDMQ5-CxqmdsIm.js} +26 -8
  137. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-CxqmdsIm.js.map +1 -0
  138. package/webapp/dist/assets/{blockDiagram-VD42YOAC-C8fLN0iu.js → blockDiagram-WCTKOSBZ-CxIWLtpt.js} +187 -170
  139. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-CxIWLtpt.js.map +1 -0
  140. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-BpPr62CH.js → c4Diagram-IC4MRINW-1qErOIgG.js} +4 -4
  141. package/webapp/dist/assets/c4Diagram-IC4MRINW-1qErOIgG.js.map +1 -0
  142. package/webapp/dist/assets/{channel-EMYoPjW3.js → channel-DkgZHNUe.js} +2 -2
  143. package/webapp/dist/assets/channel-DkgZHNUe.js.map +1 -0
  144. package/webapp/dist/assets/{chunk-4BX2VUAB-CefNtjWG.js → chunk-4BX2VUAB-BmdMbU9v.js} +2 -2
  145. package/webapp/dist/assets/chunk-4BX2VUAB-BmdMbU9v.js.map +1 -0
  146. package/webapp/dist/assets/{chunk-55IACEB6-C_X7T43V.js → chunk-55IACEB6-D6LDTDBy.js} +2 -2
  147. package/webapp/dist/assets/chunk-55IACEB6-D6LDTDBy.js.map +1 -0
  148. package/webapp/dist/assets/{chunk-FMBD7UC4-ORmtkrtS.js → chunk-FMBD7UC4-C-BdCe4C.js} +2 -2
  149. package/webapp/dist/assets/chunk-FMBD7UC4-C-BdCe4C.js.map +1 -0
  150. package/webapp/dist/assets/{chunk-TZMSLE5B-Gao4qrq7.js → chunk-JSJVCQXG-WA_BLIm9.js} +14 -6
  151. package/webapp/dist/assets/chunk-JSJVCQXG-WA_BLIm9.js.map +1 -0
  152. package/webapp/dist/assets/{chunk-QN33PNHL-LTAOVhWu.js → chunk-KX2RTZJC-CA7sDJO5.js} +2 -2
  153. package/webapp/dist/assets/chunk-KX2RTZJC-CA7sDJO5.js.map +1 -0
  154. package/webapp/dist/assets/{chunk-DI55MBZ5-CbvrsI_w.js → chunk-NQ4KR5QH-wlvxalE3.js} +9 -7
  155. package/webapp/dist/assets/chunk-NQ4KR5QH-wlvxalE3.js.map +1 -0
  156. package/webapp/dist/assets/{chunk-QZHKN3VN-ZoUM_4u5.js → chunk-QZHKN3VN-Bo1VMcph.js} +2 -2
  157. package/webapp/dist/assets/chunk-QZHKN3VN-Bo1VMcph.js.map +1 -0
  158. package/webapp/dist/assets/{chunk-B4BG7PRW-BRe3_2oA.js → chunk-WL4C6EOR-B-Pk44be.js} +171 -121
  159. package/webapp/dist/assets/chunk-WL4C6EOR-B-Pk44be.js.map +1 -0
  160. package/webapp/dist/assets/{classDiagram-2ON5EDUG-uha1vIGN.js → classDiagram-VBA2DB6C-BqKuyb49.js} +7 -6
  161. package/webapp/dist/assets/classDiagram-VBA2DB6C-BqKuyb49.js.map +1 -0
  162. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-uha1vIGN.js → classDiagram-v2-RAHNMMFH-BqKuyb49.js} +7 -6
  163. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-BqKuyb49.js.map +1 -0
  164. package/webapp/dist/assets/{clone-_9Ayb1Gp.js → clone-BX5z8WVZ.js} +2 -2
  165. package/webapp/dist/assets/clone-BX5z8WVZ.js.map +1 -0
  166. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-C8wDw3NY.js → cose-bilkent-S5V4N54A-B-s11SgN.js} +2 -2
  167. package/webapp/dist/assets/cose-bilkent-S5V4N54A-B-s11SgN.js.map +1 -0
  168. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  169. package/webapp/dist/assets/{dagre-6UL2VRFP-BUSeNot0.js → dagre-KLK3FWXG-DmQFV2qK.js} +7 -7
  170. package/webapp/dist/assets/dagre-KLK3FWXG-DmQFV2qK.js.map +1 -0
  171. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  172. package/webapp/dist/assets/{diagram-PSM6KHXK-CMZAksVC.js → diagram-E7M64L7V-QRaBfST8.js} +10 -10
  173. package/webapp/dist/assets/diagram-E7M64L7V-QRaBfST8.js.map +1 -0
  174. package/webapp/dist/assets/{diagram-QEK2KX5R-BQKoRtwy.js → diagram-IFDJBPK2-lrWn1Obo.js} +9 -8
  175. package/webapp/dist/assets/diagram-IFDJBPK2-lrWn1Obo.js.map +1 -0
  176. package/webapp/dist/assets/{diagram-S2PKOQOG-DjMG97kd.js → diagram-P4PSJMXO-sTU7Hh-Y.js} +8 -8
  177. package/webapp/dist/assets/diagram-P4PSJMXO-sTU7Hh-Y.js.map +1 -0
  178. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-BujwA137.js → erDiagram-INFDFZHY-Cx6jc9Wq.js} +96 -75
  179. package/webapp/dist/assets/erDiagram-INFDFZHY-Cx6jc9Wq.js.map +1 -0
  180. package/webapp/dist/assets/{flowDiagram-NV44I4VS-DgwPjg4y.js → flowDiagram-PKNHOUZH-DfGI49Dz.js} +98 -81
  181. package/webapp/dist/assets/flowDiagram-PKNHOUZH-DfGI49Dz.js.map +1 -0
  182. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-Db2ykf3E.js → ganttDiagram-A5KZAMGK-nrcHWWaM.js} +28 -3
  183. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-nrcHWWaM.js.map +1 -0
  184. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-D_gSifkv.js → gitGraphDiagram-K3NZZRJ6-D8ivAqd6.js} +38 -46
  185. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-D8ivAqd6.js.map +1 -0
  186. package/webapp/dist/assets/graph-R5G-y8tB.js +782 -0
  187. package/webapp/dist/assets/graph-R5G-y8tB.js.map +1 -0
  188. package/webapp/dist/assets/{index-DLajsIDJ.js → index--fy89xGh.js} +2214 -2023
  189. package/webapp/dist/assets/index--fy89xGh.js.map +1 -0
  190. package/webapp/dist/assets/{index-xvYYeHuy.css → index-DZFkLLVz.css} +18 -10
  191. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-BDba5pKs.js → infoDiagram-LFFYTUFH-PIoZHr7s.js} +7 -7
  192. package/webapp/dist/assets/infoDiagram-LFFYTUFH-PIoZHr7s.js.map +1 -0
  193. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  194. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-oCM-LYk1.js +966 -0
  195. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-oCM-LYk1.js.map +1 -0
  196. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-CmJAbmlm.js → journeyDiagram-4ABVD52K-C2qidjQ5.js} +5 -5
  197. package/webapp/dist/assets/journeyDiagram-4ABVD52K-C2qidjQ5.js.map +1 -0
  198. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-DxQeBTDk.js → kanban-definition-K7BYSVSG-Du0TC8WS.js} +5 -3
  199. package/webapp/dist/assets/kanban-definition-K7BYSVSG-Du0TC8WS.js.map +1 -0
  200. package/webapp/dist/assets/{layout-DteV_yE8.js → layout-VmEo1OEB.js} +5 -5
  201. package/webapp/dist/assets/layout-VmEo1OEB.js.map +1 -0
  202. package/webapp/dist/assets/{linear-zItbPrND.js → linear-B662YHAc.js} +2 -2
  203. package/webapp/dist/assets/linear-B662YHAc.js.map +1 -0
  204. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-BJXI7UqO.js → mindmap-definition-YRQLILUH-D7arZj95.js} +7 -5
  205. package/webapp/dist/assets/mindmap-definition-YRQLILUH-D7arZj95.js.map +1 -0
  206. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  207. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-BpM-aH2p.js → pieDiagram-SKSYHLDU-DvjPP4PA.js} +8 -8
  208. package/webapp/dist/assets/pieDiagram-SKSYHLDU-DvjPP4PA.js.map +1 -0
  209. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-NXdIpA15.js → quadrantDiagram-337W2JSQ-B_JUGMj_.js} +3 -3
  210. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-B_JUGMj_.js.map +1 -0
  211. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-D1AICAA0.js → requirementDiagram-Z7DCOOCP-DF0mpvE3.js} +16 -6
  212. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-DF0mpvE3.js.map +1 -0
  213. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-WiReDPfo.js → sankeyDiagram-WA2Y5GQK-CoXlxv00.js} +2 -2
  214. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CoXlxv00.js.map +1 -0
  215. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-Cw76oP8t.js → sequenceDiagram-2WXFIKYE-DYqT5Pg7.js} +601 -201
  216. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-DYqT5Pg7.js.map +1 -0
  217. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-QjCeRczs.js → stateDiagram-RAJIS63D-D9b1mN8-.js} +9 -9
  218. package/webapp/dist/assets/stateDiagram-RAJIS63D-D9b1mN8-.js.map +1 -0
  219. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-IClqxQ4s.js → stateDiagram-v2-FVOUBMTO-DNzgudL_.js} +5 -5
  220. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-DNzgudL_.js.map +1 -0
  221. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-BfyfTY7m.js → timeline-definition-YZTLITO2-CkyKUY7A.js} +3 -3
  222. package/webapp/dist/assets/timeline-definition-YZTLITO2-CkyKUY7A.js.map +1 -0
  223. package/webapp/dist/assets/{treemap-GDKQZRPO-C5MiL6--.js → treemap-KZPCXAKY-CZd09kF-.js} +37 -24
  224. package/webapp/dist/assets/treemap-KZPCXAKY-CZd09kF-.js.map +1 -0
  225. package/webapp/dist/assets/vennDiagram-LZ73GAT5-BxVF5Olo.js +2487 -0
  226. package/webapp/dist/assets/vennDiagram-LZ73GAT5-BxVF5Olo.js.map +1 -0
  227. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-ybaJrSry.js → xychartDiagram-JWTSCODW-BRwRloPc.js} +4 -4
  228. package/webapp/dist/assets/xychartDiagram-JWTSCODW-BRwRloPc.js.map +1 -0
  229. package/webapp/dist/index.html +2 -2
  230. package/dist/docs/issues/global-dialog-event-broadcaster-missing.md +0 -128
  231. package/dist/llm/kernel-driver/subdialog-txn.d.ts +0 -8
  232. package/webapp/dist/assets/_basePickBy-BPJaiZdW.js.map +0 -1
  233. package/webapp/dist/assets/_baseUniq-BEetT15i.js.map +0 -1
  234. package/webapp/dist/assets/arc-Dm7Zf36f.js.map +0 -1
  235. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-BpTPtkuo.js.map +0 -1
  236. package/webapp/dist/assets/blockDiagram-VD42YOAC-C8fLN0iu.js.map +0 -1
  237. package/webapp/dist/assets/c4Diagram-YG6GDRKO-BpPr62CH.js.map +0 -1
  238. package/webapp/dist/assets/channel-EMYoPjW3.js.map +0 -1
  239. package/webapp/dist/assets/chunk-4BX2VUAB-CefNtjWG.js.map +0 -1
  240. package/webapp/dist/assets/chunk-55IACEB6-C_X7T43V.js.map +0 -1
  241. package/webapp/dist/assets/chunk-B4BG7PRW-BRe3_2oA.js.map +0 -1
  242. package/webapp/dist/assets/chunk-DI55MBZ5-CbvrsI_w.js.map +0 -1
  243. package/webapp/dist/assets/chunk-FMBD7UC4-ORmtkrtS.js.map +0 -1
  244. package/webapp/dist/assets/chunk-QN33PNHL-LTAOVhWu.js.map +0 -1
  245. package/webapp/dist/assets/chunk-QZHKN3VN-ZoUM_4u5.js.map +0 -1
  246. package/webapp/dist/assets/chunk-TZMSLE5B-Gao4qrq7.js.map +0 -1
  247. package/webapp/dist/assets/classDiagram-2ON5EDUG-uha1vIGN.js.map +0 -1
  248. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-uha1vIGN.js.map +0 -1
  249. package/webapp/dist/assets/clone-_9Ayb1Gp.js.map +0 -1
  250. package/webapp/dist/assets/cose-bilkent-S5V4N54A-C8wDw3NY.js.map +0 -1
  251. package/webapp/dist/assets/dagre-6UL2VRFP-BUSeNot0.js.map +0 -1
  252. package/webapp/dist/assets/diagram-PSM6KHXK-CMZAksVC.js.map +0 -1
  253. package/webapp/dist/assets/diagram-QEK2KX5R-BQKoRtwy.js.map +0 -1
  254. package/webapp/dist/assets/diagram-S2PKOQOG-DjMG97kd.js.map +0 -1
  255. package/webapp/dist/assets/erDiagram-Q2GNP2WA-BujwA137.js.map +0 -1
  256. package/webapp/dist/assets/flowDiagram-NV44I4VS-DgwPjg4y.js.map +0 -1
  257. package/webapp/dist/assets/ganttDiagram-JELNMOA3-Db2ykf3E.js.map +0 -1
  258. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-D_gSifkv.js.map +0 -1
  259. package/webapp/dist/assets/graph-BHjCU5xP.js +0 -425
  260. package/webapp/dist/assets/graph-BHjCU5xP.js.map +0 -1
  261. package/webapp/dist/assets/index-DLajsIDJ.js.map +0 -1
  262. package/webapp/dist/assets/infoDiagram-HS3SLOUP-BDba5pKs.js.map +0 -1
  263. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-CmJAbmlm.js.map +0 -1
  264. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-DxQeBTDk.js.map +0 -1
  265. package/webapp/dist/assets/layout-DteV_yE8.js.map +0 -1
  266. package/webapp/dist/assets/linear-zItbPrND.js.map +0 -1
  267. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-BJXI7UqO.js.map +0 -1
  268. package/webapp/dist/assets/pieDiagram-ADFJNKIX-BpM-aH2p.js.map +0 -1
  269. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-NXdIpA15.js.map +0 -1
  270. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-D1AICAA0.js.map +0 -1
  271. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-WiReDPfo.js.map +0 -1
  272. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-Cw76oP8t.js.map +0 -1
  273. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-QjCeRczs.js.map +0 -1
  274. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-IClqxQ4s.js.map +0 -1
  275. package/webapp/dist/assets/timeline-definition-IT6M3QCI-BfyfTY7m.js.map +0 -1
  276. package/webapp/dist/assets/treemap-GDKQZRPO-C5MiL6--.js.map +0 -1
  277. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-ybaJrSry.js.map +0 -1
@@ -89,7 +89,7 @@ function summarizeLatestProjectionState(latest) {
89
89
  status: latest.status,
90
90
  messageCount: latest.messageCount ?? null,
91
91
  functionCallCount: latest.functionCallCount ?? null,
92
- subdialogCount: latest.subdialogCount ?? null,
92
+ sideDialogCount: latest.sideDialogCount ?? null,
93
93
  generating: latest.generating ?? false,
94
94
  needsDrive: latest.needsDrive ?? false,
95
95
  disableDiligencePush: latest.disableDiligencePush ?? false,
@@ -101,8 +101,8 @@ function summarizeLatestProjectionState(latest) {
101
101
  pendingCourseStartPromptGrammar: latest.pendingCourseStartPrompt?.grammar ?? null,
102
102
  pendingCourseStartPromptUserLanguageCode: latest.pendingCourseStartPrompt?.userLanguageCode ?? null,
103
103
  pendingCourseStartPromptContentLength: latest.pendingCourseStartPrompt?.content.length ?? null,
104
- pendingCourseStartPromptReplyTargetCallId: latest.pendingCourseStartPrompt?.subdialogReplyTarget?.callId ?? null,
105
- pendingCourseStartPromptReplyTargetOwnerDialogId: latest.pendingCourseStartPrompt?.subdialogReplyTarget?.ownerDialogId ?? null,
104
+ pendingCourseStartPromptReplyTargetCallId: latest.pendingCourseStartPrompt?.sideDialogReplyTarget?.callId ?? null,
105
+ pendingCourseStartPromptReplyTargetOwnerDialogId: latest.pendingCourseStartPrompt?.sideDialogReplyTarget?.ownerDialogId ?? null,
106
106
  pendingCourseStartPromptExpectedReplyCallName: latest.pendingCourseStartPrompt?.tellaskReplyDirective?.expectedReplyCallName ?? null,
107
107
  pendingCourseStartPromptTargetCallId: latest.pendingCourseStartPrompt?.tellaskReplyDirective?.targetCallId ?? null,
108
108
  };
@@ -119,7 +119,7 @@ function summarizeLatestMutationPatch(patch) {
119
119
  status: patch.status ?? null,
120
120
  messageCount: patch.messageCount ?? null,
121
121
  functionCallCount: patch.functionCallCount ?? null,
122
- subdialogCount: patch.subdialogCount ?? null,
122
+ sideDialogCount: patch.sideDialogCount ?? null,
123
123
  generating: patch.generating ?? null,
124
124
  needsDrive: patch.needsDrive ?? null,
125
125
  disableDiligencePush: patch.disableDiligencePush ?? null,
@@ -131,8 +131,8 @@ function summarizeLatestMutationPatch(patch) {
131
131
  pendingCourseStartPromptGrammar: patch.pendingCourseStartPrompt?.grammar ?? null,
132
132
  pendingCourseStartPromptUserLanguageCode: patch.pendingCourseStartPrompt?.userLanguageCode ?? null,
133
133
  pendingCourseStartPromptContentLength: patch.pendingCourseStartPrompt?.content.length ?? null,
134
- pendingCourseStartPromptReplyTargetOwnerDialogId: patch.pendingCourseStartPrompt?.subdialogReplyTarget?.ownerDialogId ?? null,
135
- pendingCourseStartPromptReplyTargetCallId: patch.pendingCourseStartPrompt?.subdialogReplyTarget?.callId ?? null,
134
+ pendingCourseStartPromptReplyTargetOwnerDialogId: patch.pendingCourseStartPrompt?.sideDialogReplyTarget?.ownerDialogId ?? null,
135
+ pendingCourseStartPromptReplyTargetCallId: patch.pendingCourseStartPrompt?.sideDialogReplyTarget?.callId ?? null,
136
136
  pendingCourseStartPromptExpectedReplyCallName: patch.pendingCourseStartPrompt?.tellaskReplyDirective?.expectedReplyCallName ?? null,
137
137
  pendingCourseStartPromptTargetCallId: patch.pendingCourseStartPrompt?.tellaskReplyDirective?.targetCallId ?? null,
138
138
  };
@@ -220,7 +220,7 @@ function normalizeGeneratingDisplayStateMismatch(dialogId, status, previous, lat
220
220
  executionMarker: hasInterruptedExecutionMarker ? undefined : latest.executionMarker,
221
221
  };
222
222
  }
223
- const quarantiningRootDialogs = new Set();
223
+ const quarantiningMainDialogs = new Set();
224
224
  const PERSISTABLE_DIALOG_STATUSES = ['running', 'completed', 'archived'];
225
225
  const RUN_STATUS_DIR = 'run';
226
226
  const DONE_STATUS_DIR = 'done';
@@ -272,7 +272,11 @@ function isSuppressedTellaskPlaceholderFuncResult(args) {
272
272
  return true;
273
273
  }
274
274
  if (raw.startsWith('支线对话仍在进行中,已持续 ') ||
275
- raw.startsWith('Sideline dialog is still running (elapsed ')) {
275
+ raw.startsWith('Side Dialog is still running (elapsed ')) {
276
+ return true;
277
+ }
278
+ if ((raw.startsWith('[Dominds 诉请状态]') || raw.startsWith('[Dominds tellask status]')) &&
279
+ (raw.includes('当前仍在等待') || raw.includes('is still waiting'))) {
276
280
  return true;
277
281
  }
278
282
  return false;
@@ -619,11 +623,11 @@ function buildTellaskResultRecord(result) {
619
623
  status: result.status,
620
624
  content: result.content,
621
625
  contentItems: result.contentItems,
622
- ...(typeof result.originCourse === 'number'
623
- ? { originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse) }
626
+ ...(typeof result.callSiteCourse === 'number'
627
+ ? { callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse) }
624
628
  : {}),
625
- ...(typeof result.calling_genseq === 'number'
626
- ? { calling_genseq: (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq) }
629
+ ...(typeof result.callSiteGenseq === 'number'
630
+ ? { callSiteGenseq: (0, storage_1.toCallSiteGenseqNo)(result.callSiteGenseq) }
627
631
  : {}),
628
632
  responder: {
629
633
  responderId,
@@ -673,7 +677,7 @@ function buildTellaskCarryoverRecord(result, genseq) {
673
677
  ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
674
678
  type: 'tellask_carryover_record',
675
679
  genseq,
676
- originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
680
+ callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse),
677
681
  carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
678
682
  responderId: result.responderId,
679
683
  callName: result.callName,
@@ -716,11 +720,11 @@ function buildTellaskResultEvent(result, course) {
716
720
  return {
717
721
  type: 'tellask_result_evt',
718
722
  course,
719
- ...(typeof result.originCourse === 'number'
720
- ? { originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse) }
723
+ ...(typeof result.callSiteCourse === 'number'
724
+ ? { callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse) }
721
725
  : {}),
722
- calling_genseq: typeof result.calling_genseq === 'number'
723
- ? (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq)
726
+ callSiteGenseq: typeof result.callSiteGenseq === 'number'
727
+ ? (0, storage_1.toCallSiteGenseqNo)(result.callSiteGenseq)
724
728
  : undefined,
725
729
  callId: result.callId,
726
730
  callName: result.callName,
@@ -747,11 +751,11 @@ function buildTellaskResultEvent(result, course) {
747
751
  return {
748
752
  type: 'tellask_result_evt',
749
753
  course,
750
- ...(typeof result.originCourse === 'number'
751
- ? { originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse) }
754
+ ...(typeof result.callSiteCourse === 'number'
755
+ ? { callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse) }
752
756
  : {}),
753
- calling_genseq: typeof result.calling_genseq === 'number'
754
- ? (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq)
757
+ callSiteGenseq: typeof result.callSiteGenseq === 'number'
758
+ ? (0, storage_1.toCallSiteGenseqNo)(result.callSiteGenseq)
755
759
  : undefined,
756
760
  callId: result.callId,
757
761
  callName: result.callName,
@@ -776,11 +780,11 @@ function buildTellaskResultEvent(result, course) {
776
780
  return {
777
781
  type: 'tellask_result_evt',
778
782
  course,
779
- ...(typeof result.originCourse === 'number'
780
- ? { originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse) }
783
+ ...(typeof result.callSiteCourse === 'number'
784
+ ? { callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse) }
781
785
  : {}),
782
- calling_genseq: typeof result.calling_genseq === 'number'
783
- ? (0, storage_1.toCallingGenerationSeqNumber)(result.calling_genseq)
786
+ callSiteGenseq: typeof result.callSiteGenseq === 'number'
787
+ ? (0, storage_1.toCallSiteGenseqNo)(result.callSiteGenseq)
784
788
  : undefined,
785
789
  callId: result.callId,
786
790
  callName: result.callName,
@@ -811,7 +815,7 @@ function buildTellaskCarryoverEvent(result, course) {
811
815
  type: 'tellask_carryover_evt',
812
816
  course,
813
817
  genseq: result.genseq,
814
- originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
818
+ callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse),
815
819
  carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
816
820
  responderId: result.responderId,
817
821
  callName: result.callName,
@@ -838,7 +842,7 @@ function buildTellaskCarryoverEvent(result, course) {
838
842
  type: 'tellask_carryover_evt',
839
843
  course,
840
844
  genseq: result.genseq,
841
- originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
845
+ callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse),
842
846
  carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
843
847
  responderId: result.responderId,
844
848
  callName: result.callName,
@@ -863,7 +867,7 @@ function buildTellaskCarryoverEvent(result, course) {
863
867
  type: 'tellask_carryover_evt',
864
868
  course,
865
869
  genseq: result.genseq,
866
- originCourse: (0, storage_1.toCallingCourseNumber)(result.originCourse),
870
+ callSiteCourse: (0, storage_1.toCallSiteCourseNo)(result.callSiteCourse),
867
871
  carryoverCourse: (0, storage_1.toDialogCourseNumber)(result.carryoverCourse),
868
872
  responderId: result.responderId,
869
873
  callName: result.callName,
@@ -890,10 +894,10 @@ function isReplyTellaskCallRecordName(name) {
890
894
  return (name === 'replyTellask' || name === 'replyTellaskSessionless' || name === 'replyTellaskBack');
891
895
  }
892
896
  function resolveRootGenerationAnchor(dialog) {
893
- const rootDialog = dialog instanceof dialog_1.SubDialog ? dialog.rootDialog : dialog;
897
+ const mainDialog = dialog instanceof dialog_1.SideDialog ? dialog.mainDialog : dialog;
894
898
  return (0, storage_1.toRootGenerationAnchor)({
895
- rootCourse: rootDialog.currentCourse,
896
- rootGenseq: rootDialog.activeGenSeqOrUndefined ?? 0,
899
+ rootCourse: mainDialog.currentCourse,
900
+ rootGenseq: mainDialog.activeGenSeqOrUndefined ?? 0,
897
901
  });
898
902
  }
899
903
  function resolveReconciledRecordWriteTarget(dialog) {
@@ -923,7 +927,7 @@ function attachRootGenerationRef(dialog, record) {
923
927
  rootGenseq: anchor.rootGenseq,
924
928
  };
925
929
  }
926
- function isRootDialogMetadataFile(value) {
930
+ function isMainDialogMetadataFile(value) {
927
931
  if (!isRecord(value))
928
932
  return false;
929
933
  if (typeof value.id !== 'string')
@@ -934,11 +938,11 @@ function isRootDialogMetadataFile(value) {
934
938
  return false;
935
939
  if (typeof value.createdAt !== 'string')
936
940
  return false;
937
- if (value.supdialogId !== undefined)
941
+ if (value.askerDialogId !== undefined)
938
942
  return false;
939
943
  if (value.sessionSlug !== undefined)
940
944
  return false;
941
- if (value.assignmentFromSup !== undefined)
945
+ if (value.assignmentFromAsker !== undefined)
942
946
  return false;
943
947
  if (value.priming !== undefined) {
944
948
  if (!isRecord(value.priming))
@@ -952,66 +956,49 @@ function isRootDialogMetadataFile(value) {
952
956
  }
953
957
  return true;
954
958
  }
955
- function isSubdialogMetadataFile(value) {
959
+ function isSideDialogAssignmentFromAsker(value) {
956
960
  if (!isRecord(value))
957
961
  return false;
958
- if (typeof value.id !== 'string')
959
- return false;
960
- if (typeof value.agentId !== 'string')
961
- return false;
962
- if (typeof value.taskDocPath !== 'string')
963
- return false;
964
- if (typeof value.createdAt !== 'string')
965
- return false;
966
- if (typeof value.supdialogId !== 'string')
967
- return false;
968
- if (value.priming !== undefined)
969
- return false;
970
- if (value.sessionSlug !== undefined && typeof value.sessionSlug !== 'string')
971
- return false;
972
- const assignment = value.assignmentFromSup;
973
- if (!isRecord(assignment))
974
- return false;
975
- if (typeof assignment.tellaskContent !== 'string')
962
+ if (typeof value.tellaskContent !== 'string')
976
963
  return false;
977
- if (typeof assignment.originMemberId !== 'string')
964
+ if (typeof value.originMemberId !== 'string')
978
965
  return false;
979
- if (typeof assignment.callerDialogId !== 'string')
966
+ if (typeof value.askerDialogId !== 'string')
980
967
  return false;
981
- if (typeof assignment.callId !== 'string')
968
+ if (typeof value.callId !== 'string')
982
969
  return false;
983
- if (assignment.collectiveTargets !== undefined) {
984
- if (!Array.isArray(assignment.collectiveTargets))
970
+ if (value.collectiveTargets !== undefined) {
971
+ if (!Array.isArray(value.collectiveTargets))
985
972
  return false;
986
- if (!assignment.collectiveTargets.every((item) => typeof item === 'string'))
973
+ if (!value.collectiveTargets.every((item) => typeof item === 'string'))
987
974
  return false;
988
975
  }
989
- if (assignment.effectiveFbrEffort !== undefined) {
990
- if (typeof assignment.effectiveFbrEffort !== 'number' ||
991
- !Number.isInteger(assignment.effectiveFbrEffort)) {
976
+ if (value.effectiveFbrEffort !== undefined) {
977
+ if (typeof value.effectiveFbrEffort !== 'number' ||
978
+ !Number.isInteger(value.effectiveFbrEffort)) {
992
979
  return false;
993
980
  }
994
- if (assignment.effectiveFbrEffort < 1 || assignment.effectiveFbrEffort > 100) {
981
+ if (value.effectiveFbrEffort < 1 || value.effectiveFbrEffort > 100) {
995
982
  return false;
996
983
  }
997
984
  }
998
- switch (assignment.callName) {
985
+ switch (value.callName) {
999
986
  case 'tellask':
1000
987
  case 'tellaskSessionless': {
1001
- if (!Array.isArray(assignment.mentionList))
988
+ if (!Array.isArray(value.mentionList))
1002
989
  return false;
1003
- if (assignment.mentionList.length < 1)
990
+ if (value.mentionList.length < 1)
1004
991
  return false;
1005
- if (!assignment.mentionList.every((item) => typeof item === 'string'))
992
+ if (!value.mentionList.every((item) => typeof item === 'string'))
1006
993
  return false;
1007
- if (assignment.effectiveFbrEffort !== undefined)
994
+ if (value.effectiveFbrEffort !== undefined)
1008
995
  return false;
1009
996
  break;
1010
997
  }
1011
998
  case 'freshBootsReasoning': {
1012
- if (assignment.mentionList !== undefined)
999
+ if (value.mentionList !== undefined)
1013
1000
  return false;
1014
- if (assignment.effectiveFbrEffort === undefined)
1001
+ if (value.effectiveFbrEffort === undefined)
1015
1002
  return false;
1016
1003
  break;
1017
1004
  }
@@ -1020,8 +1007,26 @@ function isSubdialogMetadataFile(value) {
1020
1007
  }
1021
1008
  return true;
1022
1009
  }
1023
- function isDialogMetadataFile(value) {
1024
- return isRootDialogMetadataFile(value) || isSubdialogMetadataFile(value);
1010
+ function isSideDialogMetadataFile(value) {
1011
+ if (!isRecord(value))
1012
+ return false;
1013
+ if (typeof value.id !== 'string')
1014
+ return false;
1015
+ if (typeof value.agentId !== 'string')
1016
+ return false;
1017
+ if (typeof value.taskDocPath !== 'string')
1018
+ return false;
1019
+ if (typeof value.createdAt !== 'string')
1020
+ return false;
1021
+ if (value.askerDialogId !== undefined)
1022
+ return false;
1023
+ if (value.assignmentFromAsker !== undefined)
1024
+ return false;
1025
+ if (value.priming !== undefined)
1026
+ return false;
1027
+ if (value.sessionSlug !== undefined && typeof value.sessionSlug !== 'string')
1028
+ return false;
1029
+ return true;
1025
1030
  }
1026
1031
  function parseTellaskReplyDirective(value) {
1027
1032
  if (!isRecord(value))
@@ -1037,24 +1042,116 @@ function parseTellaskReplyDirective(value) {
1037
1042
  if (typeof targetCallId !== 'string' || typeof tellaskContent !== 'string') {
1038
1043
  return null;
1039
1044
  }
1045
+ const targetDialogId = value.targetDialogId;
1046
+ if (typeof targetDialogId !== 'string')
1047
+ return null;
1040
1048
  if (expectedReplyCallName === 'replyTellaskBack') {
1041
- const targetDialogId = value.targetDialogId;
1042
- if (typeof targetDialogId !== 'string')
1043
- return null;
1044
1049
  return {
1045
1050
  expectedReplyCallName,
1046
- targetCallId,
1047
1051
  targetDialogId,
1052
+ targetCallId,
1048
1053
  tellaskContent,
1049
1054
  };
1050
1055
  }
1051
1056
  return {
1052
1057
  expectedReplyCallName,
1058
+ targetDialogId,
1053
1059
  targetCallId,
1054
1060
  tellaskContent,
1055
1061
  };
1056
1062
  }
1057
- function parseDialogSubdialogReplyTarget(value) {
1063
+ function isAskerDialogStackFrame(value) {
1064
+ if (!isRecord(value))
1065
+ return false;
1066
+ if (value.kind !== 'asker_dialog_stack_frame')
1067
+ return false;
1068
+ if (typeof value.askerDialogId !== 'string')
1069
+ return false;
1070
+ if (value.assignmentFromAsker !== undefined &&
1071
+ !isSideDialogAssignmentFromAsker(value.assignmentFromAsker)) {
1072
+ return false;
1073
+ }
1074
+ if (value.assignmentFromAsker !== undefined &&
1075
+ value.assignmentFromAsker.askerDialogId !== value.askerDialogId) {
1076
+ return false;
1077
+ }
1078
+ if (value.tellaskReplyObligation !== undefined) {
1079
+ const directive = parseTellaskReplyDirective(value.tellaskReplyObligation);
1080
+ if (directive === null)
1081
+ return false;
1082
+ if (value.assignmentFromAsker !== undefined) {
1083
+ const expectedReplyCallName = value.assignmentFromAsker.callName === 'tellask'
1084
+ ? 'replyTellask'
1085
+ : 'replyTellaskSessionless';
1086
+ if (directive.expectedReplyCallName !== expectedReplyCallName)
1087
+ return false;
1088
+ if (directive.targetDialogId !== value.askerDialogId)
1089
+ return false;
1090
+ if (directive.targetCallId !== value.assignmentFromAsker.callId)
1091
+ return false;
1092
+ if (directive.tellaskContent !== value.assignmentFromAsker.tellaskContent)
1093
+ return false;
1094
+ }
1095
+ else if (directive.targetDialogId !== value.askerDialogId) {
1096
+ return false;
1097
+ }
1098
+ }
1099
+ return true;
1100
+ }
1101
+ function isDialogAskerStackState(value) {
1102
+ if (!isRecord(value))
1103
+ return false;
1104
+ if (!Array.isArray(value.askerStack))
1105
+ return false;
1106
+ return value.askerStack.every(isAskerDialogStackFrame);
1107
+ }
1108
+ function getDialogAskerStackTop(state) {
1109
+ const top = state.askerStack[state.askerStack.length - 1];
1110
+ if (!top) {
1111
+ throw new Error('asker stack invariant violation: empty stack');
1112
+ }
1113
+ return top;
1114
+ }
1115
+ function getDialogAskerStackCurrentAssignment(state) {
1116
+ for (let index = state.askerStack.length - 1; index >= 0; index -= 1) {
1117
+ const frame = state.askerStack[index];
1118
+ if (frame?.assignmentFromAsker !== undefined) {
1119
+ return frame.assignmentFromAsker;
1120
+ }
1121
+ }
1122
+ throw new Error('asker stack invariant violation: missing assignment frame');
1123
+ }
1124
+ function buildAssignmentTellaskReplyObligation(args) {
1125
+ switch (args.assignment.callName) {
1126
+ case 'tellask':
1127
+ return {
1128
+ expectedReplyCallName: 'replyTellask',
1129
+ targetDialogId: args.targetDialogId,
1130
+ targetCallId: args.assignment.callId,
1131
+ tellaskContent: args.assignment.tellaskContent,
1132
+ };
1133
+ case 'tellaskSessionless':
1134
+ case 'freshBootsReasoning':
1135
+ return {
1136
+ expectedReplyCallName: 'replyTellaskSessionless',
1137
+ targetDialogId: args.targetDialogId,
1138
+ targetCallId: args.assignment.callId,
1139
+ tellaskContent: args.assignment.tellaskContent,
1140
+ };
1141
+ }
1142
+ }
1143
+ function buildAssignmentAskerStackFrame(args) {
1144
+ return {
1145
+ kind: 'asker_dialog_stack_frame',
1146
+ askerDialogId: args.askerDialogId,
1147
+ assignmentFromAsker: args.assignment,
1148
+ tellaskReplyObligation: buildAssignmentTellaskReplyObligation({
1149
+ targetDialogId: args.assignment.askerDialogId,
1150
+ assignment: args.assignment,
1151
+ }),
1152
+ };
1153
+ }
1154
+ function parseDialogSideDialogReplyTarget(value) {
1058
1155
  if (!isRecord(value))
1059
1156
  return null;
1060
1157
  const ownerDialogId = value.ownerDialogId;
@@ -1096,22 +1193,22 @@ function parseDialogPendingCourseStartPrompt(value) {
1096
1193
  if (value.tellaskReplyDirective !== undefined && tellaskReplyDirective === null) {
1097
1194
  return null;
1098
1195
  }
1099
- const subdialogReplyTarget = value.subdialogReplyTarget === undefined
1196
+ const sideDialogReplyTarget = value.sideDialogReplyTarget === undefined
1100
1197
  ? undefined
1101
- : parseDialogSubdialogReplyTarget(value.subdialogReplyTarget);
1102
- if (value.subdialogReplyTarget !== undefined && subdialogReplyTarget === null) {
1198
+ : parseDialogSideDialogReplyTarget(value.sideDialogReplyTarget);
1199
+ if (value.sideDialogReplyTarget !== undefined && sideDialogReplyTarget === null) {
1103
1200
  return null;
1104
1201
  }
1105
1202
  const userLanguageCode = userLanguageCodeRaw;
1106
1203
  const skipTaskdoc = skipTaskdocRaw;
1107
1204
  const normalizedTellaskReplyDirective = tellaskReplyDirective === null ? undefined : tellaskReplyDirective;
1108
- const normalizedSubdialogReplyTarget = subdialogReplyTarget === null ? undefined : subdialogReplyTarget;
1109
- if (normalizedSubdialogReplyTarget !== undefined &&
1205
+ const normalizedSideDialogReplyTarget = sideDialogReplyTarget === null ? undefined : sideDialogReplyTarget;
1206
+ if (normalizedSideDialogReplyTarget !== undefined &&
1110
1207
  normalizedTellaskReplyDirective === undefined) {
1111
1208
  return null;
1112
1209
  }
1113
1210
  if (normalizedTellaskReplyDirective !== undefined &&
1114
- normalizedSubdialogReplyTarget !== undefined) {
1211
+ normalizedSideDialogReplyTarget !== undefined) {
1115
1212
  return {
1116
1213
  content: value.content,
1117
1214
  msgId: value.msgId,
@@ -1120,7 +1217,7 @@ function parseDialogPendingCourseStartPrompt(value) {
1120
1217
  ...(userLanguageCode === undefined ? {} : { userLanguageCode }),
1121
1218
  ...(skipTaskdoc === undefined ? {} : { skipTaskdoc }),
1122
1219
  tellaskReplyDirective: normalizedTellaskReplyDirective,
1123
- subdialogReplyTarget: normalizedSubdialogReplyTarget,
1220
+ sideDialogReplyTarget: normalizedSideDialogReplyTarget,
1124
1221
  };
1125
1222
  }
1126
1223
  if (normalizedTellaskReplyDirective !== undefined) {
@@ -1162,7 +1259,7 @@ function parseDialogLatestFile(value) {
1162
1259
  return null;
1163
1260
  if (value.functionCallCount !== undefined && typeof value.functionCallCount !== 'number')
1164
1261
  return null;
1165
- if (value.subdialogCount !== undefined && typeof value.subdialogCount !== 'number')
1262
+ if (value.sideDialogCount !== undefined && typeof value.sideDialogCount !== 'number')
1166
1263
  return null;
1167
1264
  if (value.generating !== undefined && typeof value.generating !== 'boolean')
1168
1265
  return null;
@@ -1203,10 +1300,13 @@ function parseDialogLatestFile(value) {
1203
1300
  switch (reason.kind) {
1204
1301
  case 'needs_human_input':
1205
1302
  return { kind: 'blocked', reason: { kind: 'needs_human_input' } };
1206
- case 'waiting_for_subdialogs':
1207
- return { kind: 'blocked', reason: { kind: 'waiting_for_subdialogs' } };
1208
- case 'needs_human_input_and_subdialogs':
1209
- return { kind: 'blocked', reason: { kind: 'needs_human_input_and_subdialogs' } };
1303
+ case 'waiting_for_sideDialogs':
1304
+ return { kind: 'blocked', reason: { kind: 'waiting_for_sideDialogs' } };
1305
+ case 'needs_human_input_and_sideDialogs':
1306
+ return {
1307
+ kind: 'blocked',
1308
+ reason: { kind: 'needs_human_input_and_sideDialogs' },
1309
+ };
1210
1310
  default:
1211
1311
  return null;
1212
1312
  }
@@ -1342,7 +1442,7 @@ function parseDialogLatestFile(value) {
1342
1442
  lastModified: value.lastModified,
1343
1443
  messageCount: value.messageCount,
1344
1444
  functionCallCount: value.functionCallCount,
1345
- subdialogCount: value.subdialogCount,
1445
+ sideDialogCount: value.sideDialogCount,
1346
1446
  status: value.status,
1347
1447
  generating: value.generating,
1348
1448
  needsDrive: value.needsDrive,
@@ -1355,12 +1455,14 @@ function parseDialogLatestFile(value) {
1355
1455
  diligencePushRemainingBudget: value.diligencePushRemainingBudget,
1356
1456
  };
1357
1457
  }
1358
- function isSubdialogResponseRecord(value) {
1458
+ function isSideDialogResponseRecord(value) {
1359
1459
  if (!isRecord(value))
1360
1460
  return false;
1361
1461
  if (typeof value.responseId !== 'string')
1362
1462
  return false;
1363
- if (typeof value.subdialogId !== 'string')
1463
+ if (value.responseId.trim() === '')
1464
+ return false;
1465
+ if (typeof value.sideDialogId !== 'string')
1364
1466
  return false;
1365
1467
  if (typeof value.response !== 'string')
1366
1468
  return false;
@@ -1402,6 +1504,19 @@ function isSubdialogResponseRecord(value) {
1402
1504
  return false;
1403
1505
  return true;
1404
1506
  }
1507
+ function assertUniqueSideDialogResponseIds(records, context) {
1508
+ const seen = new Set();
1509
+ for (const record of records) {
1510
+ const responseId = record.responseId.trim();
1511
+ if (responseId === '') {
1512
+ throw new Error(`sideDialog responses empty responseId invariant violation: ${context}`);
1513
+ }
1514
+ if (seen.has(responseId)) {
1515
+ throw new Error(`sideDialog responses duplicate responseId invariant violation: ${context} responseId=${responseId}`);
1516
+ }
1517
+ seen.add(responseId);
1518
+ }
1519
+ }
1405
1520
  function isReminderPriority(value) {
1406
1521
  return value === 'high' || value === 'medium' || value === 'low';
1407
1522
  }
@@ -1457,14 +1572,14 @@ function isHumanQuestion(value) {
1457
1572
  return false;
1458
1573
  if (typeof value.callSiteRef.messageIndex !== 'number')
1459
1574
  return false;
1460
- if ('callingGenseq' in value.callSiteRef) {
1461
- const callingGenseq = value.callSiteRef.callingGenseq;
1462
- if (callingGenseq !== undefined) {
1463
- if (typeof callingGenseq !== 'number')
1575
+ if ('callSiteGenseq' in value.callSiteRef) {
1576
+ const callSiteGenseq = value.callSiteRef.callSiteGenseq;
1577
+ if (callSiteGenseq !== undefined) {
1578
+ if (typeof callSiteGenseq !== 'number')
1464
1579
  return false;
1465
- if (!Number.isFinite(callingGenseq))
1580
+ if (!Number.isFinite(callSiteGenseq))
1466
1581
  return false;
1467
- if (Math.floor(callingGenseq) <= 0)
1582
+ if (Math.floor(callSiteGenseq) <= 0)
1468
1583
  return false;
1469
1584
  }
1470
1585
  }
@@ -1597,85 +1712,78 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1597
1712
  this.thinkingReasoning = undefined;
1598
1713
  this.dialogId = dialogId;
1599
1714
  }
1600
- // === DialogStore interface methods (for compatibility) ===
1715
+ // === DialogStore interface methods ===
1601
1716
  /**
1602
- * Create subdialog with automatic persistence
1717
+ * Create sideDialog with automatic persistence
1603
1718
  */
1604
- async createSubDialog(callerDialog, targetAgentId, mentionList, tellaskContent, options) {
1719
+ async createSideDialog(askerDialog, targetAgentId, mentionList, tellaskContent, options) {
1605
1720
  const generatedId = (0, id_1.generateDialogID)();
1606
1721
  const nowTs = (0, time_1.formatUnifiedTimestamp)(new Date());
1607
- const rootDialog = callerDialog instanceof dialog_1.RootDialog
1608
- ? callerDialog
1609
- : callerDialog instanceof dialog_1.SubDialog
1610
- ? callerDialog.rootDialog
1722
+ const mainDialog = askerDialog instanceof dialog_1.MainDialog
1723
+ ? askerDialog
1724
+ : askerDialog instanceof dialog_1.SideDialog
1725
+ ? askerDialog.mainDialog
1611
1726
  : (() => {
1612
- throw new Error(`createSubDialog invariant violation: unsupported caller dialog type (${callerDialog.constructor.name})`);
1727
+ throw new Error(`createSideDialog invariant violation: unsupported asker type (${askerDialog.constructor.name})`);
1613
1728
  })();
1614
- const rootStatus = rootDialog.status;
1729
+ const rootStatus = mainDialog.status;
1615
1730
  if (rootStatus !== 'running') {
1616
- throw new Error(`createSubDialog invariant violation: root dialog must be running (rootId=${rootDialog.id.rootId}, status=${rootStatus})`);
1617
- }
1618
- const subdialogId = new dialog_1.DialogID(generatedId, rootDialog.id.rootId);
1619
- // Prepare subdialog store
1620
- const subdialogStore = new DiskFileDialogStore(subdialogId);
1621
- const subdialog = new dialog_1.SubDialog(subdialogStore, rootDialog, callerDialog.taskDocPath, subdialogId, targetAgentId, {
1622
- callName: options.callName,
1623
- mentionList,
1624
- tellaskContent,
1625
- originMemberId: options.originMemberId,
1626
- callerDialogId: options.callerDialogId,
1627
- callId: options.callId,
1628
- collectiveTargets: options.collectiveTargets,
1629
- effectiveFbrEffort: options.effectiveFbrEffort,
1630
- }, options.sessionSlug);
1631
- // Initial subdialog user prompt is now persisted at first drive (driver.ts)
1632
- // Ensure subdialog directory and persist metadata under supdialog/.subdialogs/
1633
- await this.ensureSubdialogDirectory(subdialogId);
1634
- const metadata = {
1635
- id: subdialogId.selfId,
1636
- agentId: targetAgentId,
1637
- taskDocPath: callerDialog.taskDocPath,
1638
- createdAt: nowTs,
1639
- supdialogId: callerDialog.id.selfId,
1640
- sessionSlug: options.sessionSlug,
1641
- assignmentFromSup: {
1731
+ throw new Error(`createSideDialog invariant violation: main dialog must be running (rootId=${mainDialog.id.rootId}, status=${rootStatus})`);
1732
+ }
1733
+ const sideDialogId = new dialog_1.DialogID(generatedId, mainDialog.id.rootId);
1734
+ // Prepare sideDialog store
1735
+ const sideDialogStore = new DiskFileDialogStore(sideDialogId);
1736
+ const sideDialog = new dialog_1.SideDialog(sideDialogStore, mainDialog, askerDialog.taskDocPath, sideDialogId, targetAgentId, (0, dialog_1.buildSideDialogAskerStack)({
1737
+ askerDialogId: options.askerDialogId,
1738
+ assignment: {
1642
1739
  callName: options.callName,
1643
1740
  mentionList,
1644
1741
  tellaskContent,
1645
1742
  originMemberId: options.originMemberId,
1646
- callerDialogId: options.callerDialogId,
1743
+ askerDialogId: options.askerDialogId,
1647
1744
  callId: options.callId,
1648
1745
  collectiveTargets: options.collectiveTargets,
1649
1746
  effectiveFbrEffort: options.effectiveFbrEffort,
1650
1747
  },
1748
+ }), options.sessionSlug);
1749
+ // Initial sideDialog user prompt is now persisted at first drive (driver.ts)
1750
+ // Ensure sideDialog directory and persist metadata under askerDialog/.sideDialogs/
1751
+ await this.ensureSideDialogDirectory(sideDialogId);
1752
+ const metadata = {
1753
+ id: sideDialogId.selfId,
1754
+ agentId: targetAgentId,
1755
+ taskDocPath: askerDialog.taskDocPath,
1756
+ createdAt: nowTs,
1757
+ sessionSlug: options.sessionSlug,
1651
1758
  };
1652
- await DialogPersistence.saveSubdialogMetadata(subdialogId, metadata);
1653
- const rootAnchor = resolveRootGenerationAnchor(callerDialog);
1654
- const parentCourse = callerDialog.activeGenCourseOrUndefined ?? callerDialog.currentCourse;
1655
- const subdialogCreatedRecord = {
1759
+ await DialogPersistence.saveSideDialogAskerStackState(sideDialogId, sideDialog.askerStack);
1760
+ await DialogPersistence.saveSideDialogMetadata(sideDialogId, metadata);
1761
+ const rootAnchor = resolveRootGenerationAnchor(askerDialog);
1762
+ const parentCourse = askerDialog.activeGenCourseOrUndefined ?? askerDialog.currentCourse;
1763
+ const sideDialogCreatedRecord = {
1656
1764
  ts: nowTs,
1657
- type: 'subdialog_created_record',
1765
+ type: 'sideDialog_created_record',
1658
1766
  ...cloneRootGenerationAnchor(rootAnchor),
1659
- subdialogId: subdialogId.selfId,
1660
- supdialogId: callerDialog.id.selfId,
1767
+ sideDialogId: sideDialogId.selfId,
1768
+ askerDialogId: askerDialog.id.selfId,
1661
1769
  agentId: targetAgentId,
1662
- taskDocPath: callerDialog.taskDocPath,
1770
+ taskDocPath: askerDialog.taskDocPath,
1663
1771
  createdAt: nowTs,
1664
1772
  sessionSlug: options.sessionSlug,
1665
- assignmentFromSup: {
1773
+ assignmentFromAsker: {
1666
1774
  callName: options.callName,
1667
1775
  mentionList,
1668
1776
  tellaskContent,
1669
1777
  originMemberId: options.originMemberId,
1670
- callerDialogId: options.callerDialogId,
1778
+ askerDialogId: options.askerDialogId,
1671
1779
  callId: options.callId,
1672
1780
  collectiveTargets: options.collectiveTargets,
1673
1781
  effectiveFbrEffort: options.effectiveFbrEffort,
1674
1782
  },
1675
1783
  };
1676
- await this.appendEvent(callerDialog, parentCourse, subdialogCreatedRecord);
1784
+ await this.appendEvent(askerDialog, parentCourse, sideDialogCreatedRecord);
1677
1785
  // Initialize latest.yaml via the mutation API (write-back will flush).
1678
- await DialogPersistence.mutateDialogLatest(subdialogId, () => ({
1786
+ await DialogPersistence.mutateDialogLatest(sideDialogId, () => ({
1679
1787
  kind: 'replace',
1680
1788
  next: {
1681
1789
  currentCourse: 1,
@@ -1683,61 +1791,61 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1683
1791
  status: 'active',
1684
1792
  messageCount: 0,
1685
1793
  functionCallCount: 0,
1686
- subdialogCount: 0,
1794
+ sideDialogCount: 0,
1687
1795
  displayState: { kind: 'idle_waiting_user' },
1688
1796
  disableDiligencePush: false,
1689
1797
  },
1690
1798
  }));
1691
- // Supdialog clarification context is persisted in subdialog metadata (supdialogCall)
1692
- const rootSubdialogCount = await DialogPersistence.countAllSubdialogsUnderRoot(rootDialog.id, rootStatus);
1693
- const subdialogCreatedEvt = {
1694
- type: 'subdialog_created_evt',
1799
+ // AskerDialog clarification context is persisted in sideDialog metadata (askerDialogCall)
1800
+ const rootSideDialogCount = await DialogPersistence.countAllSideDialogsUnderRoot(mainDialog.id, rootStatus);
1801
+ const sideDialogCreatedEvt = {
1802
+ type: 'sideDialog_created_evt',
1695
1803
  dialog: {
1696
- selfId: subdialogId.selfId,
1697
- rootId: subdialogId.rootId,
1804
+ selfId: sideDialogId.selfId,
1805
+ rootId: sideDialogId.rootId,
1698
1806
  },
1699
1807
  timestamp: new Date().toISOString(),
1700
1808
  course: parentCourse,
1701
1809
  parentDialog: {
1702
- selfId: callerDialog.id.selfId,
1703
- rootId: callerDialog.id.rootId,
1810
+ selfId: askerDialog.id.selfId,
1811
+ rootId: askerDialog.id.rootId,
1704
1812
  },
1705
- subDialog: {
1706
- selfId: subdialogId.selfId,
1707
- rootId: subdialogId.rootId,
1813
+ sideDialog: {
1814
+ selfId: sideDialogId.selfId,
1815
+ rootId: sideDialogId.rootId,
1708
1816
  },
1709
1817
  targetAgentId,
1710
1818
  callName: options.callName,
1711
1819
  mentionList,
1712
1820
  tellaskContent,
1713
- rootSubdialogCount,
1714
- subDialogNode: {
1715
- selfId: subdialogId.selfId,
1716
- rootId: subdialogId.rootId,
1717
- supdialogId: callerDialog.id.selfId,
1821
+ rootSideDialogCount,
1822
+ sideDialogNode: {
1823
+ selfId: sideDialogId.selfId,
1824
+ rootId: sideDialogId.rootId,
1825
+ askerDialogId: askerDialog.id.selfId,
1718
1826
  agentId: targetAgentId,
1719
- taskDocPath: callerDialog.taskDocPath,
1827
+ taskDocPath: askerDialog.taskDocPath,
1720
1828
  status: rootStatus,
1721
1829
  currentCourse: 1,
1722
1830
  createdAt: nowTs,
1723
1831
  lastModified: nowTs,
1724
1832
  displayState: { kind: 'idle_waiting_user' },
1725
1833
  sessionSlug: options.sessionSlug,
1726
- assignmentFromSup: {
1834
+ assignmentFromAsker: {
1727
1835
  callName: options.callName,
1728
1836
  mentionList,
1729
1837
  tellaskContent,
1730
1838
  originMemberId: options.originMemberId,
1731
- callerDialogId: options.callerDialogId,
1839
+ askerDialogId: options.askerDialogId,
1732
1840
  callId: options.callId,
1733
1841
  effectiveFbrEffort: options.effectiveFbrEffort,
1734
1842
  },
1735
1843
  },
1736
1844
  };
1737
- // Post subdialog_created_evt to PARENT's PubChan so frontend can receive it
1738
- // The frontend subscribes to the parent's events, not the subdialog's
1739
- (0, evt_registry_1.postDialogEvent)(callerDialog, subdialogCreatedEvt);
1740
- return subdialog;
1845
+ // Post sideDialog_created_evt to PARENT's PubChan so frontend can receive it
1846
+ // The frontend subscribes to the parent's events, not the sideDialog's
1847
+ (0, evt_registry_1.postDialogEvent)(askerDialog, sideDialogCreatedEvt);
1848
+ return sideDialog;
1741
1849
  }
1742
1850
  /**
1743
1851
  * Receive and handle function call results (includes logging)
@@ -1815,10 +1923,10 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1815
1923
  (0, evt_registry_1.postDialogEvent)(dialog, buildTellaskCarryoverEvent(normalizedResult, course));
1816
1924
  }
1817
1925
  /**
1818
- * Ensure subdialog directory exists (delegate to DialogPersistence)
1926
+ * Ensure sideDialog directory exists (delegate to DialogPersistence)
1819
1927
  */
1820
- async ensureSubdialogDirectory(dialogId) {
1821
- return await DialogPersistence.ensureSubdialogDirectory(dialogId);
1928
+ async ensureSideDialogDirectory(dialogId) {
1929
+ return await DialogPersistence.ensureSideDialogDirectory(dialogId);
1822
1930
  }
1823
1931
  async findExistingFuncResultRecord(dialog, callId) {
1824
1932
  const latest = await DialogPersistence.loadDialogLatest(dialog.id, dialog.status);
@@ -1837,6 +1945,22 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1837
1945
  }
1838
1946
  return undefined;
1839
1947
  }
1948
+ async findExistingCallRecord(dialog, callId) {
1949
+ const latest = await DialogPersistence.loadDialogLatest(dialog.id, dialog.status);
1950
+ const maxCourse = latest?.currentCourse ?? dialog.currentCourse;
1951
+ for (let course = 1; course <= maxCourse; course += 1) {
1952
+ const events = await DialogPersistence.loadCourseEvents(dialog.id, course, dialog.status);
1953
+ for (const event of events) {
1954
+ if (event.type === 'func_call_record' && event.id === callId) {
1955
+ return { course, record: event };
1956
+ }
1957
+ if (event.type === 'tellask_call_record' && event.id === callId) {
1958
+ return { course, record: event };
1959
+ }
1960
+ }
1961
+ }
1962
+ return undefined;
1963
+ }
1840
1964
  async findExistingTellaskResultRecord(dialog, callId) {
1841
1965
  const latest = await DialogPersistence.loadDialogLatest(dialog.id, dialog.status);
1842
1966
  const maxCourse = latest?.currentCourse ?? dialog.currentCourse;
@@ -1854,10 +1978,41 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
1854
1978
  }
1855
1979
  return undefined;
1856
1980
  }
1981
+ async raiseDuplicateCallInvariantViolation(args) {
1982
+ const err = new Error(`${args.kind} duplicate callId invariant violation: rootId=${args.dialog.id.rootId} selfId=${args.dialog.id.selfId} ` +
1983
+ `callId=${args.callId} callName=${args.callName} existingName=${args.existingName} ` +
1984
+ `existingCourse=${args.existingCourse} existingGenseq=${args.existingGenseq} ` +
1985
+ `incomingCourse=${args.incomingCourse} incomingGenseq=${args.incomingGenseq}`);
1986
+ log_1.log.error('Duplicate call detected; rejecting second write', err, {
1987
+ rootId: args.dialog.id.rootId,
1988
+ selfId: args.dialog.id.selfId,
1989
+ callId: args.callId,
1990
+ callName: args.callName,
1991
+ kind: args.kind,
1992
+ existingName: args.existingName,
1993
+ existingCourse: args.existingCourse,
1994
+ existingGenseq: args.existingGenseq,
1995
+ incomingCourse: args.incomingCourse,
1996
+ incomingGenseq: args.incomingGenseq,
1997
+ });
1998
+ try {
1999
+ await this.streamError(args.dialog, err.message);
2000
+ }
2001
+ catch (streamErr) {
2002
+ log_1.log.warn('Failed to emit stream_error_evt for duplicate call', streamErr, {
2003
+ rootId: args.dialog.id.rootId,
2004
+ selfId: args.dialog.id.selfId,
2005
+ callId: args.callId,
2006
+ callName: args.callName,
2007
+ kind: args.kind,
2008
+ });
2009
+ }
2010
+ throw err;
2011
+ }
1857
2012
  async raiseDuplicateCallResultInvariantViolation(args) {
1858
2013
  // Duplicate final results are not harmless transcript noise. They mean two different program
1859
2014
  // paths both believed they owned the same business-level completion fact for one callId.
1860
- // In ask-back flows this usually points to identity confusion between requester/responder or
2015
+ // In ask-back flows this usually points to identity confusion between asker/tellaskee or
1861
2016
  // canonical reply-tool delivery versus another mistaken write path. We fail fast here so the
1862
2017
  // second writer keeps its own stack trace instead of silently corrupting the dialog transcript.
1863
2018
  const err = new Error(`${args.kind} duplicate callId invariant violation: rootId=${args.dialog.id.rootId} selfId=${args.dialog.id.selfId} ` +
@@ -2141,19 +2296,6 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2141
2296
  })();
2142
2297
  (0, evt_registry_1.postDialogEvent)(dialog, evt);
2143
2298
  }
2144
- // Function call events (non-streaming mode - single event captures entire call)
2145
- async funcCallRequested(dialog, funcId, funcName, argumentsStr) {
2146
- const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
2147
- const funcCallEvt = {
2148
- type: 'func_call_requested_evt',
2149
- funcId,
2150
- funcName,
2151
- arguments: argumentsStr,
2152
- course,
2153
- genseq: dialog.activeGenSeq,
2154
- };
2155
- (0, evt_registry_1.postDialogEvent)(dialog, funcCallEvt);
2156
- }
2157
2299
  async webSearchCall(dialog, payload) {
2158
2300
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
2159
2301
  const itemId = payload.itemId.trim();
@@ -2545,6 +2687,10 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2545
2687
  if (deferredReplyReassertion?.directive.targetCallId === payload.targetCallId) {
2546
2688
  await DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
2547
2689
  }
2690
+ const activeObligation = await DialogPersistence.loadActiveTellaskReplyObligation(dialog.id, dialog.status);
2691
+ if (activeObligation?.targetCallId === payload.targetCallId) {
2692
+ await DialogPersistence.setActiveTellaskReplyObligation(dialog.id, undefined, dialog.status);
2693
+ }
2548
2694
  }
2549
2695
  /**
2550
2696
  * Persist an assistant message to storage
@@ -2593,13 +2739,54 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2593
2739
  */
2594
2740
  async persistFunctionCall(dialog, id, name, rawArgumentsText, genseq) {
2595
2741
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
2742
+ if (!Number.isFinite(genseq) || genseq <= 0) {
2743
+ throw new Error(`persistFunctionCall invariant violation: missing valid genseq for func call ${id}`);
2744
+ }
2745
+ const existingCall = await this.findExistingCallRecord(dialog, id);
2746
+ if (existingCall) {
2747
+ await this.raiseDuplicateCallInvariantViolation({
2748
+ dialog,
2749
+ kind: 'func_call',
2750
+ callId: id,
2751
+ callName: name,
2752
+ incomingCourse: course,
2753
+ incomingGenseq: genseq,
2754
+ existingCourse: existingCall.course,
2755
+ existingGenseq: existingCall.record.genseq,
2756
+ existingName: existingCall.record.name,
2757
+ });
2758
+ }
2596
2759
  const funcCallEvent = buildFuncCallRecord({ id, name, rawArgumentsText, genseq });
2597
2760
  await this.appendEvent(dialog, course, funcCallEvent);
2598
- // NOTE: func_call_evt REMOVED - persistence uses FuncCallRecord directly
2599
- // UI display uses func_call_requested_evt instead
2761
+ const funcCallEvt = {
2762
+ type: 'func_call_requested_evt',
2763
+ funcId: id,
2764
+ funcName: name,
2765
+ arguments: rawArgumentsText,
2766
+ course,
2767
+ genseq,
2768
+ };
2769
+ (0, evt_registry_1.postDialogEvent)(dialog, funcCallEvt);
2600
2770
  }
2601
2771
  async persistTellaskCall(dialog, id, name, rawArgumentsText, genseq, options) {
2602
2772
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
2773
+ if (!Number.isFinite(genseq) || genseq <= 0) {
2774
+ throw new Error(`persistTellaskCall invariant violation: missing valid genseq for tellask call ${id}`);
2775
+ }
2776
+ const existingCall = await this.findExistingCallRecord(dialog, id);
2777
+ if (existingCall) {
2778
+ await this.raiseDuplicateCallInvariantViolation({
2779
+ dialog,
2780
+ kind: 'tellask_call',
2781
+ callId: id,
2782
+ callName: name,
2783
+ incomingCourse: course,
2784
+ incomingGenseq: genseq,
2785
+ existingCourse: existingCall.course,
2786
+ existingGenseq: existingCall.record.genseq,
2787
+ existingName: existingCall.record.name,
2788
+ });
2789
+ }
2603
2790
  const tellaskCallEvent = buildTellaskCallRecord({
2604
2791
  id,
2605
2792
  name,
@@ -2609,81 +2796,17 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2609
2796
  (isReplyTellaskCallRecordName(name) ? 'func_call_requested' : 'tellask_call_start'),
2610
2797
  });
2611
2798
  await this.appendEvent(dialog, course, tellaskCallEvent);
2612
- if (isReplyTellaskCallRecordName(name)) {
2799
+ if (tellaskCallEvent.deliveryMode === 'func_call_requested') {
2613
2800
  const funcCallEvt = {
2614
2801
  type: 'func_call_requested_evt',
2615
2802
  funcId: id,
2616
2803
  funcName: name,
2617
2804
  arguments: formatTellaskCallArguments(tellaskCallEvent),
2618
2805
  course,
2619
- genseq: dialog.activeGenSeqOrUndefined ?? genseq,
2620
- };
2621
- (0, evt_registry_1.postDialogEvent)(dialog, funcCallEvt);
2622
- }
2623
- }
2624
- async persistFunctionCallResultPair(dialog, id, name, rawArgumentsText, genseq, result) {
2625
- const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
2626
- const resultGenseq = dialog.activeGenSeqOrUndefined ?? result.genseq;
2627
- if (!Number.isFinite(resultGenseq) || resultGenseq <= 0) {
2628
- throw new Error(`persistFunctionCallResultPair invariant violation: missing valid genseq for func result ${result.id}`);
2629
- }
2630
- await this.appendEvents(dialog, course, [
2631
- buildFuncCallRecord({ id, name, rawArgumentsText, genseq }),
2632
- buildFuncResultRecord(result, resultGenseq),
2633
- ]);
2634
- if (!isSuppressedTellaskPlaceholderFuncResult({
2635
- name: result.name,
2636
- content: result.content,
2637
- })) {
2638
- const funcResultEvt = {
2639
- type: 'func_result_evt',
2640
- id: result.id,
2641
- name: result.name,
2642
- content: result.content,
2643
- contentItems: result.contentItems,
2644
- course,
2645
2806
  genseq,
2646
2807
  };
2647
- (0, evt_registry_1.postDialogEvent)(dialog, funcResultEvt);
2648
- }
2649
- }
2650
- async persistTellaskCallResultPair(dialog, args) {
2651
- const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
2652
- const callRecord = buildTellaskCallRecord({
2653
- id: args.id,
2654
- name: args.name,
2655
- rawArgumentsText: args.rawArgumentsText,
2656
- genseq: args.genseq,
2657
- deliveryMode: args.deliveryMode,
2658
- });
2659
- if (args.result.type === 'func_result_msg') {
2660
- const resultGenseq = dialog.activeGenSeqOrUndefined ?? args.result.genseq;
2661
- if (!Number.isFinite(resultGenseq) || resultGenseq <= 0) {
2662
- throw new Error(`persistTellaskCallResultPair invariant violation: missing valid genseq for func result ${args.result.id}`);
2663
- }
2664
- await this.appendEvents(dialog, course, [
2665
- callRecord,
2666
- buildFuncResultRecord(args.result, resultGenseq),
2667
- ]);
2668
- if (!isSuppressedTellaskPlaceholderFuncResult({
2669
- name: args.result.name,
2670
- content: args.result.content,
2671
- })) {
2672
- const funcResultEvt = {
2673
- type: 'func_result_evt',
2674
- id: args.result.id,
2675
- name: args.result.name,
2676
- content: args.result.content,
2677
- contentItems: args.result.contentItems,
2678
- course,
2679
- genseq: resultGenseq,
2680
- };
2681
- (0, evt_registry_1.postDialogEvent)(dialog, funcResultEvt);
2682
- }
2683
- return;
2808
+ (0, evt_registry_1.postDialogEvent)(dialog, funcCallEvt);
2684
2809
  }
2685
- await this.appendEvents(dialog, course, [callRecord, buildTellaskResultRecord(args.result)]);
2686
- (0, evt_registry_1.postDialogEvent)(dialog, buildTellaskResultEvent(args.result, course));
2687
2810
  }
2688
2811
  /**
2689
2812
  * Update questions for human state (exceptional overwrite pattern)
@@ -2701,162 +2824,168 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2701
2824
  async loadDialogMetadata(dialogId, status) {
2702
2825
  return await DialogPersistence.loadDialogMetadata(dialogId, status);
2703
2826
  }
2704
- async loadPendingSubdialogs(rootDialogId, status) {
2705
- const records = await DialogPersistence.loadPendingSubdialogs(rootDialogId, status);
2827
+ async loadSideDialogAssignmentFromAsker(dialogId, status) {
2828
+ if (dialogId.rootId === dialogId.selfId)
2829
+ return null;
2830
+ return await DialogPersistence.loadSideDialogAssignmentFromAsker(dialogId, status);
2831
+ }
2832
+ async loadPendingSideDialogs(mainDialogId, status) {
2833
+ const records = await DialogPersistence.loadPendingSideDialogs(mainDialogId, status);
2706
2834
  return records.map((record) => ({
2707
- subdialogId: new dialog_1.DialogID(record.subdialogId, rootDialogId.rootId),
2835
+ sideDialogId: new dialog_1.DialogID(record.sideDialogId, mainDialogId.rootId),
2708
2836
  createdAt: record.createdAt,
2709
2837
  mentionList: record.mentionList,
2710
2838
  tellaskContent: record.tellaskContent,
2711
2839
  targetAgentId: record.targetAgentId,
2712
2840
  callId: record.callId,
2713
- callingCourse: record.callingCourse,
2841
+ callSiteCourse: record.callSiteCourse,
2842
+ callSiteGenseq: record.callSiteGenseq,
2714
2843
  callType: record.callType,
2715
2844
  sessionSlug: record.sessionSlug,
2716
2845
  }));
2717
2846
  }
2718
- async saveSubdialogRegistry(dialog, rootDialogId, entries, status) {
2719
- await DialogPersistence.saveSubdialogRegistry(rootDialogId, entries, status);
2720
- await DialogPersistence.appendSubdialogRegistryReconciledRecord(rootDialogId, entries.map((entry) => ({
2847
+ async saveSideDialogRegistry(dialog, mainDialogId, entries, status) {
2848
+ await DialogPersistence.saveSideDialogRegistry(mainDialogId, entries, status);
2849
+ await DialogPersistence.appendSideDialogRegistryReconciledRecord(mainDialogId, entries.map((entry) => ({
2721
2850
  key: entry.key,
2722
- subdialogId: entry.subdialogId.selfId,
2851
+ sideDialogId: entry.sideDialogId.selfId,
2723
2852
  agentId: entry.agentId,
2724
2853
  sessionSlug: entry.sessionSlug,
2725
2854
  })), resolveReconciledRecordWriteTarget(dialog), status);
2726
2855
  }
2727
- async loadSubdialogRegistry(rootDialog, status) {
2728
- const entries = await DialogPersistence.loadSubdialogRegistry(rootDialog.id, status);
2856
+ async loadSideDialogRegistry(mainDialog, status) {
2857
+ const entries = await DialogPersistence.loadSideDialogRegistry(mainDialog.id, status);
2729
2858
  const shouldPruneDead = status === 'running';
2730
2859
  let prunedDeadRegistryEntries = false;
2731
- const restoringSubdialogs = new Map();
2732
- const ensureSubdialogLoaded = async (subdialogId, ancestry = new Set()) => {
2733
- if (ancestry.has(subdialogId.selfId)) {
2734
- throw new Error(`Subdialog registry restore invariant violation: cyclic parent chain ` +
2735
- `(rootId=${rootDialog.id.rootId}, selfId=${subdialogId.selfId})`);
2860
+ const restoringSideDialogs = new Map();
2861
+ const ensureSideDialogLoaded = async (sideDialogId, ancestry = new Set()) => {
2862
+ if (ancestry.has(sideDialogId.selfId)) {
2863
+ throw new Error(`SideDialog registry restore invariant violation: cyclic parent chain ` +
2864
+ `(rootId=${mainDialog.id.rootId}, selfId=${sideDialogId.selfId})`);
2736
2865
  }
2737
- const existing = rootDialog.lookupDialog(subdialogId.selfId);
2866
+ const existing = mainDialog.lookupDialog(sideDialogId.selfId);
2738
2867
  if (existing) {
2739
- if (!(existing instanceof dialog_1.SubDialog)) {
2740
- throw new Error(`Dialog registry type invariant violation: expected SubDialog ` +
2741
- `(rootId=${rootDialog.id.rootId}, selfId=${subdialogId.selfId})`);
2868
+ if (!(existing instanceof dialog_1.SideDialog)) {
2869
+ throw new Error(`Dialog registry type invariant violation: expected SideDialog ` +
2870
+ `(rootId=${mainDialog.id.rootId}, selfId=${sideDialogId.selfId})`);
2742
2871
  }
2743
2872
  return existing;
2744
2873
  }
2745
- const inFlight = restoringSubdialogs.get(subdialogId.selfId);
2874
+ const inFlight = restoringSideDialogs.get(sideDialogId.selfId);
2746
2875
  if (inFlight) {
2747
2876
  return await inFlight;
2748
2877
  }
2749
2878
  const task = (async () => {
2750
2879
  const nextAncestry = new Set(ancestry);
2751
- nextAncestry.add(subdialogId.selfId);
2752
- const subdialogState = await DialogPersistence.restoreDialog(subdialogId, status);
2753
- if (!subdialogState) {
2754
- throw new Error(`Subdialog registry restore invariant violation: missing dialog state ` +
2755
- `(rootId=${rootDialog.id.rootId}, selfId=${subdialogId.selfId})`);
2880
+ nextAncestry.add(sideDialogId.selfId);
2881
+ const sideDialogState = await DialogPersistence.restoreDialog(sideDialogId, status);
2882
+ if (!sideDialogState) {
2883
+ throw new Error(`SideDialog registry restore invariant violation: missing dialog state ` +
2884
+ `(rootId=${mainDialog.id.rootId}, selfId=${sideDialogId.selfId})`);
2756
2885
  }
2757
- const metadata = subdialogState.metadata;
2758
- if (!isSubdialogMetadataFile(metadata)) {
2759
- throw new Error(`Subdialog registry restore invariant violation: expected subdialog metadata ` +
2760
- `(rootId=${rootDialog.id.rootId}, selfId=${subdialogId.selfId})`);
2886
+ const metadata = sideDialogState.metadata;
2887
+ if (!isSideDialogMetadataFile(metadata)) {
2888
+ throw new Error(`SideDialog registry restore invariant violation: expected sideDialog metadata ` +
2889
+ `(rootId=${mainDialog.id.rootId}, selfId=${sideDialogId.selfId})`);
2761
2890
  }
2762
- const assignmentFromSup = metadata.assignmentFromSup;
2763
- if (!assignmentFromSup) {
2764
- throw new Error(`Subdialog registry restore invariant violation: missing assignmentFromSup ` +
2765
- `(rootId=${rootDialog.id.rootId}, selfId=${subdialogId.selfId})`);
2891
+ const askerStack = await DialogPersistence.loadSideDialogAskerStackState(sideDialogId, status);
2892
+ if (!askerStack) {
2893
+ throw new Error(`SideDialog registry restore invariant violation: missing asker stack ` +
2894
+ `(rootId=${mainDialog.id.rootId}, selfId=${sideDialogId.selfId})`);
2766
2895
  }
2896
+ const assignmentFromAsker = getDialogAskerStackCurrentAssignment(askerStack);
2767
2897
  const parentIds = [];
2768
2898
  const maybePushParentId = (candidate) => {
2769
2899
  if (!candidate)
2770
2900
  return;
2771
- if (candidate === rootDialog.id.rootId)
2901
+ if (candidate === mainDialog.id.rootId)
2772
2902
  return;
2773
- if (candidate === subdialogId.selfId)
2903
+ if (candidate === sideDialogId.selfId)
2774
2904
  return;
2775
2905
  if (parentIds.includes(candidate))
2776
2906
  return;
2777
2907
  parentIds.push(candidate);
2778
2908
  };
2779
- maybePushParentId(metadata.supdialogId);
2780
- maybePushParentId(assignmentFromSup.callerDialogId);
2909
+ maybePushParentId(assignmentFromAsker.askerDialogId);
2781
2910
  for (const parentId of parentIds) {
2782
- if (rootDialog.lookupDialog(parentId)) {
2911
+ if (mainDialog.lookupDialog(parentId)) {
2783
2912
  continue;
2784
2913
  }
2785
- const parentDialogId = new dialog_1.DialogID(parentId, rootDialog.id.rootId);
2914
+ const parentDialogId = new dialog_1.DialogID(parentId, mainDialog.id.rootId);
2786
2915
  const parentMeta = await DialogPersistence.loadDialogMetadata(parentDialogId, status);
2787
2916
  if (!parentMeta) {
2788
- throw new Error(`Subdialog registry restore invariant violation: missing parent metadata ` +
2789
- `(rootId=${rootDialog.id.rootId}, childId=${subdialogId.selfId}, parentId=${parentId})`);
2917
+ throw new Error(`SideDialog registry restore invariant violation: missing parent metadata ` +
2918
+ `(rootId=${mainDialog.id.rootId}, childId=${sideDialogId.selfId}, parentId=${parentId})`);
2790
2919
  }
2791
- if (!isSubdialogMetadataFile(parentMeta)) {
2792
- throw new Error(`Subdialog registry restore invariant violation: parent is not a subdialog ` +
2793
- `(rootId=${rootDialog.id.rootId}, childId=${subdialogId.selfId}, parentId=${parentId})`);
2920
+ if (!isSideDialogMetadataFile(parentMeta)) {
2921
+ throw new Error(`SideDialog registry restore invariant violation: parent is not a sideDialog ` +
2922
+ `(rootId=${mainDialog.id.rootId}, childId=${sideDialogId.selfId}, parentId=${parentId})`);
2794
2923
  }
2795
- await ensureSubdialogLoaded(parentDialogId, nextAncestry);
2796
- if (!rootDialog.lookupDialog(parentId)) {
2797
- throw new Error(`Subdialog registry restore invariant violation: parent restore failed ` +
2798
- `(rootId=${rootDialog.id.rootId}, childId=${subdialogId.selfId}, parentId=${parentId})`);
2924
+ await ensureSideDialogLoaded(parentDialogId, nextAncestry);
2925
+ if (!mainDialog.lookupDialog(parentId)) {
2926
+ throw new Error(`SideDialog registry restore invariant violation: parent restore failed ` +
2927
+ `(rootId=${mainDialog.id.rootId}, childId=${sideDialogId.selfId}, parentId=${parentId})`);
2799
2928
  }
2800
2929
  }
2801
- const subdialogStore = new DiskFileDialogStore(subdialogId);
2802
- const subdialog = new dialog_1.SubDialog(subdialogStore, rootDialog, metadata.taskDocPath, new dialog_1.DialogID(subdialogId.selfId, rootDialog.id.rootId), metadata.agentId, assignmentFromSup, metadata.sessionSlug, {
2803
- messages: subdialogState.messages,
2804
- reminders: subdialogState.reminders,
2805
- currentCourse: subdialogState.currentCourse,
2806
- contextHealth: subdialogState.contextHealth,
2930
+ const sideDialogStore = new DiskFileDialogStore(sideDialogId);
2931
+ const sideDialog = new dialog_1.SideDialog(sideDialogStore, mainDialog, metadata.taskDocPath, new dialog_1.DialogID(sideDialogId.selfId, mainDialog.id.rootId), metadata.agentId, askerStack, metadata.sessionSlug, {
2932
+ messages: sideDialogState.messages,
2933
+ reminders: sideDialogState.reminders,
2934
+ currentCourse: sideDialogState.currentCourse,
2935
+ contextHealth: sideDialogState.contextHealth,
2807
2936
  });
2808
- const latest = await DialogPersistence.loadDialogLatest(subdialogId, status);
2809
- subdialog.disableDiligencePush = latest?.disableDiligencePush ?? false;
2810
- if (subdialog.sessionSlug) {
2811
- rootDialog.registerSubdialog(subdialog);
2937
+ const latest = await DialogPersistence.loadDialogLatest(sideDialogId, status);
2938
+ sideDialog.disableDiligencePush = latest?.disableDiligencePush ?? false;
2939
+ if (sideDialog.sessionSlug) {
2940
+ mainDialog.registerSideDialog(sideDialog);
2812
2941
  }
2813
- return subdialog;
2942
+ return sideDialog;
2814
2943
  })();
2815
- restoringSubdialogs.set(subdialogId.selfId, task);
2944
+ restoringSideDialogs.set(sideDialogId.selfId, task);
2816
2945
  try {
2817
2946
  return await task;
2818
2947
  }
2819
2948
  finally {
2820
- restoringSubdialogs.delete(subdialogId.selfId);
2949
+ restoringSideDialogs.delete(sideDialogId.selfId);
2821
2950
  }
2822
2951
  };
2823
2952
  for (const entry of entries) {
2824
2953
  if (!entry.sessionSlug)
2825
2954
  continue;
2826
2955
  if (shouldPruneDead) {
2827
- const latest = await DialogPersistence.loadDialogLatest(entry.subdialogId, status);
2956
+ const latest = await DialogPersistence.loadDialogLatest(entry.sideDialogId, status);
2828
2957
  const executionMarker = latest?.executionMarker;
2829
2958
  if (executionMarker && executionMarker.kind === 'dead') {
2830
2959
  prunedDeadRegistryEntries = true;
2831
- rootDialog.unregisterSubdialog(entry.agentId, entry.sessionSlug);
2832
- log_1.log.debug('Skip dead subdialog while loading Type B registry', undefined, {
2833
- rootId: rootDialog.id.rootId,
2834
- subdialogId: entry.subdialogId.selfId,
2960
+ mainDialog.unregisterSideDialog(entry.agentId, entry.sessionSlug);
2961
+ log_1.log.debug('Skip dead sideDialog while loading Type B registry', undefined, {
2962
+ rootId: mainDialog.id.rootId,
2963
+ sideDialogId: entry.sideDialogId.selfId,
2835
2964
  agentId: entry.agentId,
2836
2965
  sessionSlug: entry.sessionSlug,
2837
2966
  });
2838
2967
  continue;
2839
2968
  }
2840
2969
  }
2841
- const subdialog = await ensureSubdialogLoaded(entry.subdialogId);
2842
- if (!subdialog.sessionSlug) {
2843
- throw new Error(`Subdialog registry invariant violation: missing sessionSlug on loaded subdialog ` +
2844
- `(rootId=${rootDialog.id.rootId}, selfId=${entry.subdialogId.selfId}, expectedSessionSlug=${entry.sessionSlug})`);
2970
+ const sideDialog = await ensureSideDialogLoaded(entry.sideDialogId);
2971
+ if (!sideDialog.sessionSlug) {
2972
+ throw new Error(`SideDialog registry invariant violation: missing sessionSlug on loaded sideDialog ` +
2973
+ `(rootId=${mainDialog.id.rootId}, selfId=${entry.sideDialogId.selfId}, expectedSessionSlug=${entry.sessionSlug})`);
2845
2974
  }
2846
- if (subdialog.sessionSlug !== entry.sessionSlug) {
2847
- throw new Error(`Subdialog registry invariant violation: sessionSlug mismatch ` +
2848
- `(rootId=${rootDialog.id.rootId}, selfId=${entry.subdialogId.selfId}, ` +
2849
- `expected=${entry.sessionSlug}, actual=${subdialog.sessionSlug})`);
2975
+ if (sideDialog.sessionSlug !== entry.sessionSlug) {
2976
+ throw new Error(`SideDialog registry invariant violation: sessionSlug mismatch ` +
2977
+ `(rootId=${mainDialog.id.rootId}, selfId=${entry.sideDialogId.selfId}, ` +
2978
+ `expected=${entry.sessionSlug}, actual=${sideDialog.sessionSlug})`);
2850
2979
  }
2851
- if (subdialog.agentId !== entry.agentId) {
2852
- throw new Error(`Subdialog registry invariant violation: agentId mismatch ` +
2853
- `(rootId=${rootDialog.id.rootId}, selfId=${entry.subdialogId.selfId}, ` +
2854
- `expected=${entry.agentId}, actual=${subdialog.agentId})`);
2980
+ if (sideDialog.agentId !== entry.agentId) {
2981
+ throw new Error(`SideDialog registry invariant violation: agentId mismatch ` +
2982
+ `(rootId=${mainDialog.id.rootId}, selfId=${entry.sideDialogId.selfId}, ` +
2983
+ `expected=${entry.agentId}, actual=${sideDialog.agentId})`);
2855
2984
  }
2856
- rootDialog.registerSubdialog(subdialog);
2985
+ mainDialog.registerSideDialog(sideDialog);
2857
2986
  }
2858
2987
  if (prunedDeadRegistryEntries) {
2859
- await rootDialog.saveSubdialogRegistry();
2988
+ await mainDialog.saveSideDialogRegistry();
2860
2989
  }
2861
2990
  }
2862
2991
  /**
@@ -3514,8 +3643,8 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
3514
3643
  callId: event.callId,
3515
3644
  status: event.status,
3516
3645
  content: event.content,
3517
- ...(event.originCourse !== undefined ? { originCourse: event.originCourse } : {}),
3518
- ...(event.calling_genseq !== undefined ? { calling_genseq: event.calling_genseq } : {}),
3646
+ ...(event.callSiteCourse !== undefined ? { callSiteCourse: event.callSiteCourse } : {}),
3647
+ ...(event.callSiteGenseq !== undefined ? { callSiteGenseq: event.callSiteGenseq } : {}),
3519
3648
  responder: event.responder,
3520
3649
  ...(event.route ? { route: event.route } : {}),
3521
3650
  dialog: {
@@ -3546,68 +3675,64 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
3546
3675
  }
3547
3676
  break;
3548
3677
  }
3549
- case 'quest_for_sup_record': {
3550
- // Handle subdialog creation requests
3551
- const persistedStatus = assertPersistableDialogStatus(status, 'sendEventDirectlyToWebSocket:quest_for_sup_record');
3552
- const subdialogId = new dialog_1.DialogID(event.subDialogId, dialog.id.rootId);
3553
- const metadata = await DialogPersistence.loadDialogMetadata(subdialogId, status);
3554
- if (!metadata || !isSubdialogMetadataFile(metadata)) {
3555
- throw new Error(`subdialog_created_evt replay invariant violation: metadata missing for ${subdialogId.valueOf()} in ${status}`);
3678
+ case 'sideDialog_request_record': {
3679
+ // Handle sideDialog creation requests
3680
+ const persistedStatus = assertPersistableDialogStatus(status, 'sendEventDirectlyToWebSocket:sideDialog_request_record');
3681
+ const sideDialogId = new dialog_1.DialogID(event.sideDialogId, dialog.id.rootId);
3682
+ const metadata = await DialogPersistence.loadDialogMetadata(sideDialogId, status);
3683
+ if (!metadata || !isSideDialogMetadataFile(metadata)) {
3684
+ throw new Error(`sideDialog_created_evt replay invariant violation: metadata missing for ${sideDialogId.valueOf()} in ${status}`);
3556
3685
  }
3557
- const subMeta = metadata;
3558
- const subLatest = await DialogPersistence.loadDialogLatest(subdialogId, status);
3559
- const derivedSupdialogId = subMeta.assignmentFromSup?.callerDialogId &&
3560
- subMeta.assignmentFromSup.callerDialogId.trim() !== ''
3561
- ? subMeta.assignmentFromSup.callerDialogId
3562
- : typeof subMeta.supdialogId === 'string' && subMeta.supdialogId.trim() !== ''
3563
- ? subMeta.supdialogId
3564
- : dialog.id.selfId;
3565
- const callName = subMeta.assignmentFromSup?.callName;
3686
+ const sideMeta = metadata;
3687
+ const sideLatest = await DialogPersistence.loadDialogLatest(sideDialogId, status);
3688
+ const assignmentFromAsker = await DialogPersistence.loadSideDialogAssignmentFromAsker(sideDialogId, status);
3689
+ const derivedAskerDialogId = assignmentFromAsker.askerDialogId.trim();
3690
+ const callName = assignmentFromAsker.callName;
3566
3691
  if (callName !== 'tellask' &&
3567
3692
  callName !== 'tellaskSessionless' &&
3568
3693
  callName !== 'freshBootsReasoning') {
3569
- throw new Error(`subdialog_created_evt replay invariant violation: missing assignment callName for ${subdialogId.valueOf()} in ${status}`);
3694
+ throw new Error(`sideDialog_created_evt replay invariant violation: missing assignment callName for ${sideDialogId.valueOf()} in ${status}`);
3570
3695
  }
3571
- const rootSubdialogCount = await DialogPersistence.countAllSubdialogsUnderRoot(new dialog_1.DialogID(subdialogId.rootId), persistedStatus);
3572
- const subdialogCreatedEvent = {
3573
- type: 'subdialog_created_evt',
3696
+ const rootSideDialogCount = await DialogPersistence.countAllSideDialogsUnderRoot(new dialog_1.DialogID(sideDialogId.rootId), persistedStatus);
3697
+ const sideDialogCreatedEvent = {
3698
+ type: 'sideDialog_created_evt',
3574
3699
  course,
3575
3700
  dialog: {
3576
3701
  // Add dialog field for proper event routing
3577
- selfId: subdialogId.selfId,
3578
- rootId: subdialogId.rootId,
3702
+ selfId: sideDialogId.selfId,
3703
+ rootId: sideDialogId.rootId,
3579
3704
  },
3580
3705
  parentDialog: {
3581
3706
  selfId: dialog.id.selfId,
3582
3707
  rootId: dialog.id.rootId,
3583
3708
  },
3584
- subDialog: {
3585
- selfId: subdialogId.selfId,
3586
- rootId: subdialogId.rootId,
3709
+ sideDialog: {
3710
+ selfId: sideDialogId.selfId,
3711
+ rootId: sideDialogId.rootId,
3587
3712
  },
3588
- targetAgentId: subMeta.agentId,
3713
+ targetAgentId: sideMeta.agentId,
3589
3714
  callName,
3590
3715
  mentionList: event.mentionList,
3591
3716
  tellaskContent: event.tellaskContent,
3592
- rootSubdialogCount,
3593
- subDialogNode: {
3594
- selfId: subMeta.id,
3595
- rootId: subdialogId.rootId,
3596
- supdialogId: derivedSupdialogId,
3597
- agentId: subMeta.agentId,
3598
- taskDocPath: subMeta.taskDocPath,
3717
+ rootSideDialogCount,
3718
+ sideDialogNode: {
3719
+ selfId: sideMeta.id,
3720
+ rootId: sideDialogId.rootId,
3721
+ askerDialogId: derivedAskerDialogId,
3722
+ agentId: sideMeta.agentId,
3723
+ taskDocPath: sideMeta.taskDocPath,
3599
3724
  status: persistedStatus,
3600
- currentCourse: subLatest?.currentCourse || 1,
3601
- createdAt: subMeta.createdAt,
3602
- lastModified: subLatest?.lastModified || subMeta.createdAt,
3603
- displayState: subLatest?.displayState,
3604
- sessionSlug: subMeta.sessionSlug,
3605
- assignmentFromSup: subMeta.assignmentFromSup,
3725
+ currentCourse: sideLatest?.currentCourse || 1,
3726
+ createdAt: sideMeta.createdAt,
3727
+ lastModified: sideLatest?.lastModified || sideMeta.createdAt,
3728
+ displayState: sideLatest?.displayState,
3729
+ sessionSlug: sideMeta.sessionSlug,
3730
+ assignmentFromAsker,
3606
3731
  },
3607
3732
  timestamp: event.ts,
3608
3733
  };
3609
3734
  if (ws.readyState === 1) {
3610
- ws.send(JSON.stringify(subdialogCreatedEvent));
3735
+ ws.send(JSON.stringify(sideDialogCreatedEvent));
3611
3736
  }
3612
3737
  break;
3613
3738
  }
@@ -3635,8 +3760,8 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
3635
3760
  callId: event.callId,
3636
3761
  assignmentCourse: event.assignmentCourse,
3637
3762
  assignmentGenseq: event.assignmentGenseq,
3638
- callerDialogId: event.callerDialogId,
3639
- callerCourse: event.callerCourse,
3763
+ askerDialogId: event.askerDialogId,
3764
+ askerCourse: event.askerCourse,
3640
3765
  dialog: {
3641
3766
  selfId: dialog.id.selfId,
3642
3767
  rootId: dialog.id.rootId,
@@ -3648,19 +3773,19 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
3648
3773
  }
3649
3774
  break;
3650
3775
  }
3651
- case 'subdialog_created_record':
3776
+ case 'sideDialog_created_record':
3652
3777
  case 'reminders_reconciled_record':
3653
3778
  case 'questions4human_reconciled_record':
3654
- case 'pending_subdialogs_reconciled_record':
3655
- case 'subdialog_registry_reconciled_record':
3656
- case 'subdialog_responses_reconciled_record':
3779
+ case 'pending_sideDialogs_reconciled_record':
3780
+ case 'sideDialog_registry_reconciled_record':
3781
+ case 'sideDialog_responses_reconciled_record':
3657
3782
  break;
3658
3783
  case 'tellask_carryover_record': {
3659
3784
  const base = {
3660
3785
  type: 'tellask_carryover_evt',
3661
3786
  course,
3662
3787
  genseq: event.genseq,
3663
- originCourse: event.originCourse,
3788
+ callSiteCourse: event.callSiteCourse,
3664
3789
  carryoverCourse: event.carryoverCourse,
3665
3790
  responderId: event.responderId,
3666
3791
  tellaskContent: event.tellaskContent,
@@ -3727,7 +3852,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
3727
3852
  exports.DiskFileDialogStore = DiskFileDialogStore;
3728
3853
  class DialogWriteBackCanceledError extends Error {
3729
3854
  constructor(token, phase) {
3730
- super(`Dialog writeback canceled for ${token.rootDialogId} (${token.status}) during ${phase}`);
3855
+ super(`Dialog writeback canceled for ${token.mainDialogId} (${token.status}) during ${phase}`);
3731
3856
  this.name = 'DialogWriteBackCanceledError';
3732
3857
  }
3733
3858
  }
@@ -3765,12 +3890,12 @@ class DialogPersistence {
3765
3890
  static getCourseAppendMutexKey(dialogId, course, status) {
3766
3891
  return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}|course:${course}`;
3767
3892
  }
3768
- static getPendingSubdialogsWriteBackMutex(key) {
3769
- const existing = this.pendingSubdialogsWriteBackMutexes.get(key);
3893
+ static getPendingSideDialogsWriteBackMutex(key) {
3894
+ const existing = this.pendingSideDialogsWriteBackMutexes.get(key);
3770
3895
  if (existing)
3771
3896
  return existing;
3772
3897
  const created = new async_fifo_mutex_1.AsyncFifoMutex();
3773
- this.pendingSubdialogsWriteBackMutexes.set(key, created);
3898
+ this.pendingSideDialogsWriteBackMutexes.set(key, created);
3774
3899
  return created;
3775
3900
  }
3776
3901
  static getLatestWriteBackKey(dialogId, status) {
@@ -3781,27 +3906,27 @@ class DialogPersistence {
3781
3906
  // Include dialogs root dir to avoid cross-test/process.cwd collisions.
3782
3907
  return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}|q4h`;
3783
3908
  }
3784
- static getPendingSubdialogsWriteBackKey(rootDialogId, status) {
3785
- return `${this.getDialogsRootDir()}|${status}|${rootDialogId.valueOf()}|pending-subdialogs`;
3909
+ static getPendingSideDialogsWriteBackKey(mainDialogId, status) {
3910
+ return `${this.getDialogsRootDir()}|${status}|${mainDialogId.valueOf()}|pending-sideDialogs`;
3786
3911
  }
3787
- static getRootDialogWriteBackCancelScopeKey(rootDialogId, status) {
3788
- return `${this.getDialogsRootDir()}|${status}|${rootDialogId.selfId}|writeback-cancel`;
3912
+ static getMainDialogWriteBackCancelScopeKey(mainDialogId, status) {
3913
+ return `${this.getDialogsRootDir()}|${status}|${mainDialogId.selfId}|writeback-cancel`;
3789
3914
  }
3790
- static createRootDialogWriteBackCancellationToken(dialogId, status) {
3791
- const rootDialogId = dialogId.rootId === dialogId.selfId ? dialogId : new dialog_1.DialogID(dialogId.rootId);
3792
- const scopeKey = this.getRootDialogWriteBackCancelScopeKey(rootDialogId, status);
3915
+ static createMainDialogWriteBackCancellationToken(dialogId, status) {
3916
+ const mainDialogId = dialogId.rootId === dialogId.selfId ? dialogId : new dialog_1.DialogID(dialogId.rootId);
3917
+ const scopeKey = this.getMainDialogWriteBackCancelScopeKey(mainDialogId, status);
3793
3918
  return {
3794
3919
  scopeKey,
3795
- generation: this.rootDialogWriteBackCancelGenerations.get(scopeKey) ?? 0,
3796
- rootDialogId: rootDialogId.selfId,
3920
+ generation: this.mainDialogWriteBackCancelGenerations.get(scopeKey) ?? 0,
3921
+ mainDialogId: mainDialogId.selfId,
3797
3922
  status,
3798
3923
  };
3799
3924
  }
3800
- static assertRootDialogWriteBackNotCanceled(token, phase) {
3801
- if (this.quarantinedRootDialogScopes.has(token.scopeKey)) {
3925
+ static assertMainDialogWriteBackNotCanceled(token, phase) {
3926
+ if (this.quarantinedMainDialogScopes.has(token.scopeKey)) {
3802
3927
  throw new DialogWriteBackCanceledError(token, phase);
3803
3928
  }
3804
- const currentGeneration = this.rootDialogWriteBackCancelGenerations.get(token.scopeKey) ?? 0;
3929
+ const currentGeneration = this.mainDialogWriteBackCancelGenerations.get(token.scopeKey) ?? 0;
3805
3930
  if (currentGeneration !== token.generation) {
3806
3931
  throw new DialogWriteBackCanceledError(token, phase);
3807
3932
  }
@@ -3811,45 +3936,51 @@ class DialogPersistence {
3811
3936
  throw error;
3812
3937
  }
3813
3938
  if (cancellationToken) {
3814
- this.assertRootDialogWriteBackNotCanceled(cancellationToken, phase);
3939
+ this.assertMainDialogWriteBackNotCanceled(cancellationToken, phase);
3815
3940
  if (!(await this.pathExists(dialogPath))) {
3816
3941
  throw new DialogWriteBackCanceledError(cancellationToken, `${phase}:dialog-path-missing`);
3817
3942
  }
3818
3943
  }
3819
3944
  throw error;
3820
3945
  }
3821
- static cancelRootDialogWriteBacks(rootDialogId, status) {
3822
- const scopeKey = this.getRootDialogWriteBackCancelScopeKey(rootDialogId, status);
3823
- const nextGeneration = (this.rootDialogWriteBackCancelGenerations.get(scopeKey) ?? 0) + 1;
3824
- this.rootDialogWriteBackCancelGenerations.set(scopeKey, nextGeneration);
3825
- this.clearWriteBackEntriesForRootDialog(rootDialogId, status);
3946
+ static cancelMainDialogWriteBacks(mainDialogId, status) {
3947
+ const scopeKey = this.getMainDialogWriteBackCancelScopeKey(mainDialogId, status);
3948
+ const nextGeneration = (this.mainDialogWriteBackCancelGenerations.get(scopeKey) ?? 0) + 1;
3949
+ this.mainDialogWriteBackCancelGenerations.set(scopeKey, nextGeneration);
3950
+ this.clearWriteBackEntriesForMainDialog(mainDialogId, status);
3826
3951
  }
3827
3952
  static getDialogMetadataPath(dialogId, status) {
3828
3953
  const dialogPath = dialogId.rootId === dialogId.selfId
3829
- ? this.getRootDialogPath(dialogId, status)
3830
- : this.getSubdialogPath(dialogId, status);
3954
+ ? this.getMainDialogPath(dialogId, status)
3955
+ : this.getSideDialogPath(dialogId, status);
3831
3956
  return path.join(dialogPath, 'dialog.yaml');
3832
3957
  }
3958
+ static getDialogAskerStackPath(dialogId, status) {
3959
+ const dialogPath = dialogId.rootId === dialogId.selfId
3960
+ ? this.getMainDialogPath(dialogId, status)
3961
+ : this.getSideDialogPath(dialogId, status);
3962
+ return path.join(dialogPath, 'asker-stack.jsonl');
3963
+ }
3833
3964
  static async assertDialogMetadataExistsForAppend(dialogId, status, cancellationToken, phase) {
3834
- this.assertRootDialogWriteBackNotCanceled(cancellationToken, phase);
3965
+ this.assertMainDialogWriteBackNotCanceled(cancellationToken, phase);
3835
3966
  const metadataPath = this.getDialogMetadataPath(dialogId, status);
3836
3967
  try {
3837
3968
  await fs.promises.access(metadataPath);
3838
3969
  }
3839
3970
  catch (error) {
3840
3971
  if (getErrorCode(error) === 'ENOENT') {
3841
- this.assertRootDialogWriteBackNotCanceled(cancellationToken, `${phase}:metadata-missing`);
3972
+ this.assertMainDialogWriteBackNotCanceled(cancellationToken, `${phase}:metadata-missing`);
3842
3973
  throw new Error(`Refusing to append events for dialog ${dialogId.valueOf()}: missing dialog metadata at ${metadataPath}`);
3843
3974
  }
3844
3975
  throw error;
3845
3976
  }
3846
3977
  }
3847
3978
  static async cleanupCanceledAppendPlaceholder(dialogId, status) {
3848
- const rootDialogId = dialogId.rootId === dialogId.selfId ? dialogId : new dialog_1.DialogID(dialogId.rootId);
3849
- const rootPath = this.getRootDialogPath(rootDialogId, status);
3979
+ const mainDialogId = dialogId.rootId === dialogId.selfId ? dialogId : new dialog_1.DialogID(dialogId.rootId);
3980
+ const rootPath = this.getMainDialogPath(mainDialogId, status);
3850
3981
  await fs.promises.rm(rootPath, { recursive: true, force: true });
3851
3982
  }
3852
- static clonePendingSubdialogRecords(records) {
3983
+ static clonePendingSideDialogRecords(records) {
3853
3984
  return records.map((record) => ({
3854
3985
  ...record,
3855
3986
  mentionList: record.mentionList ? [...record.mentionList] : undefined,
@@ -3866,7 +3997,7 @@ class DialogPersistence {
3866
3997
  ...entry,
3867
3998
  }));
3868
3999
  }
3869
- static cloneSubdialogResponses(responses) {
4000
+ static cloneSideDialogResponses(responses) {
3870
4001
  return responses.map((response) => ({
3871
4002
  ...response,
3872
4003
  mentionList: response.mentionList ? [...response.mentionList] : undefined,
@@ -3890,30 +4021,30 @@ class DialogPersistence {
3890
4021
  };
3891
4022
  await this.appendEvent(dialogId, resolveTargetCourseFromWriteTarget(writeTarget), record, status);
3892
4023
  }
3893
- static async appendPendingSubdialogsReconciledRecord(dialogId, pendingSubdialogs, writeTarget, status) {
4024
+ static async appendPendingSideDialogsReconciledRecord(dialogId, pendingSideDialogs, writeTarget, status) {
3894
4025
  const record = {
3895
4026
  ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
3896
- type: 'pending_subdialogs_reconciled_record',
4027
+ type: 'pending_sideDialogs_reconciled_record',
3897
4028
  ...cloneRootGenerationAnchor(writeTarget.rootAnchor),
3898
- pendingSubdialogs: this.clonePendingSubdialogRecords(pendingSubdialogs),
4029
+ pendingSideDialogs: this.clonePendingSideDialogRecords(pendingSideDialogs),
3899
4030
  };
3900
4031
  await this.appendEvent(dialogId, resolveTargetCourseFromWriteTarget(writeTarget), record, status);
3901
4032
  }
3902
- static async appendSubdialogRegistryReconciledRecord(dialogId, entries, writeTarget, status) {
4033
+ static async appendSideDialogRegistryReconciledRecord(dialogId, entries, writeTarget, status) {
3903
4034
  const record = {
3904
4035
  ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
3905
- type: 'subdialog_registry_reconciled_record',
4036
+ type: 'sideDialog_registry_reconciled_record',
3906
4037
  ...cloneRootGenerationAnchor(writeTarget.rootAnchor),
3907
4038
  entries: this.cloneRegistryEntries(entries),
3908
4039
  };
3909
4040
  await this.appendEvent(dialogId, resolveTargetCourseFromWriteTarget(writeTarget), record, status);
3910
4041
  }
3911
- static async appendSubdialogResponsesReconciledRecord(dialogId, responses, writeTarget, status) {
4042
+ static async appendSideDialogResponsesReconciledRecord(dialogId, responses, writeTarget, status) {
3912
4043
  const record = {
3913
4044
  ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
3914
- type: 'subdialog_responses_reconciled_record',
4045
+ type: 'sideDialog_responses_reconciled_record',
3915
4046
  ...cloneRootGenerationAnchor(writeTarget.rootAnchor),
3916
- responses: this.cloneSubdialogResponses(responses),
4047
+ responses: this.cloneSideDialogResponses(responses),
3917
4048
  };
3918
4049
  await this.appendEvent(dialogId, resolveTargetCourseFromWriteTarget(writeTarget), record, status);
3919
4050
  }
@@ -3926,42 +4057,42 @@ class DialogPersistence {
3926
4057
  /**
3927
4058
  * Get the full path for a dialog directory
3928
4059
  */
3929
- static getRootDialogPath(dialogId, status = 'running') {
4060
+ static getMainDialogPath(dialogId, status = 'running') {
3930
4061
  if (dialogId.rootId !== dialogId.selfId) {
3931
- throw new Error('Expected root dialog id');
4062
+ throw new Error('Expected main dialog id');
3932
4063
  }
3933
- const statusDir = getPersistableStatusDirName(status, 'DialogPersistence.getRootDialogPath');
4064
+ const statusDir = getPersistableStatusDirName(status, 'DialogPersistence.getMainDialogPath');
3934
4065
  return path.join(this.getDialogsRootDir(), statusDir, dialogId.selfId);
3935
4066
  }
3936
4067
  /**
3937
- * Get the events/state directory for a dialog (composite ID for subdialogs)
4068
+ * Get the events/state directory for a dialog (composite ID for sideDialogs)
3938
4069
  */
3939
4070
  static getDialogEventsPath(dialogId, status = 'running') {
3940
- // Root dialogs store events under their own directory.
3941
- // Subdialogs store events under the root's subdialogs/<self> directory.
4071
+ // Main dialogs store events under their own directory.
4072
+ // SideDialogs store events under the root's sideDialogs/<self> directory.
3942
4073
  if (dialogId.rootId === dialogId.selfId) {
3943
- return this.getRootDialogPath(dialogId, status);
4074
+ return this.getMainDialogPath(dialogId, status);
3944
4075
  }
3945
- return this.getSubdialogPath(dialogId, status);
4076
+ return this.getSideDialogPath(dialogId, status);
3946
4077
  }
3947
4078
  /**
3948
- * Get the path for a subdialog within a supdialog
4079
+ * Get the path for a sideDialog within an askerDialog
3949
4080
  */
3950
- static getSubdialogPath(dialogId, status = 'running') {
4081
+ static getSideDialogPath(dialogId, status = 'running') {
3951
4082
  if (dialogId.rootId === dialogId.selfId) {
3952
- throw new Error('Expected subdialog id (self differs from root)');
4083
+ throw new Error('Expected sideDialog id (self differs from root)');
3953
4084
  }
3954
- const rootPath = this.getRootDialogPath(new dialog_1.DialogID(dialogId.rootId), status);
3955
- return path.join(rootPath, this.SUBDIALOGS_DIR, dialogId.selfId);
4085
+ const rootPath = this.getMainDialogPath(new dialog_1.DialogID(dialogId.rootId), status);
4086
+ return path.join(rootPath, this.SIDE_DIALOGS_DIR, dialogId.selfId);
3956
4087
  }
3957
- static getMalformedRootDialogPath(dialogId, status) {
4088
+ static getMalformedMainDialogPath(dialogId, status) {
3958
4089
  if (dialogId.rootId !== dialogId.selfId) {
3959
- throw new Error('Expected root dialog id');
4090
+ throw new Error('Expected main dialog id');
3960
4091
  }
3961
4092
  void status;
3962
4093
  return path.join(this.getDialogsRootDir(), this.MALFORMED_DIR, dialogId.selfId);
3963
4094
  }
3964
- static inferRootDialogIdFromMetadataRelativeDir(relativeDir) {
4095
+ static inferMainDialogIdFromMetadataRelativeDir(relativeDir) {
3965
4096
  const dir = relativeDir.trim();
3966
4097
  if (dir === '' || dir === '.' || dir === path.sep) {
3967
4098
  return null;
@@ -3970,8 +4101,8 @@ class DialogPersistence {
3970
4101
  if (segments.length === 0) {
3971
4102
  return null;
3972
4103
  }
3973
- const subdialogsIndex = segments.indexOf(this.SUBDIALOGS_DIR);
3974
- const rootSegments = subdialogsIndex === -1 ? segments : segments.slice(0, subdialogsIndex);
4104
+ const sideDialogsIndex = segments.indexOf(this.SIDE_DIALOGS_DIR);
4105
+ const rootSegments = sideDialogsIndex === -1 ? segments : segments.slice(0, sideDialogsIndex);
3975
4106
  if (rootSegments.length === 0) {
3976
4107
  return null;
3977
4108
  }
@@ -3986,19 +4117,19 @@ class DialogPersistence {
3986
4117
  if (segments.length === 0) {
3987
4118
  return null;
3988
4119
  }
3989
- const subdialogsIndex = segments.indexOf(this.SUBDIALOGS_DIR);
3990
- if (subdialogsIndex === -1) {
4120
+ const sideDialogsIndex = segments.indexOf(this.SIDE_DIALOGS_DIR);
4121
+ if (sideDialogsIndex === -1) {
3991
4122
  return segments.join('/');
3992
4123
  }
3993
- const subdialogSegments = segments.slice(subdialogsIndex + 1);
3994
- if (subdialogSegments.length === 0) {
4124
+ const sideDialogSegments = segments.slice(sideDialogsIndex + 1);
4125
+ if (sideDialogSegments.length === 0) {
3995
4126
  return null;
3996
4127
  }
3997
- return subdialogSegments.join('/');
4128
+ return sideDialogSegments.join('/');
3998
4129
  }
3999
- static async listSubdialogIdsUnderRoot(rootDialogId, status) {
4000
- const subdialogsPath = path.join(this.getRootDialogPath(rootDialogId, status), this.SUBDIALOGS_DIR);
4001
- const subdialogIds = new Set();
4130
+ static async listSideDialogIdsUnderRoot(mainDialogId, status) {
4131
+ const sideDialogsPath = path.join(this.getMainDialogPath(mainDialogId, status), this.SIDE_DIALOGS_DIR);
4132
+ const sideDialogIds = new Set();
4002
4133
  const visit = async (dirPath, relativePath = '') => {
4003
4134
  let entries;
4004
4135
  try {
@@ -4019,11 +4150,11 @@ class DialogPersistence {
4019
4150
  const dialogYamlPath = path.join(fullPath, 'dialog.yaml');
4020
4151
  try {
4021
4152
  await fs.promises.access(dialogYamlPath);
4022
- const inferredId = this.inferExpectedDialogIdFromMetadataRelativeDir(path.join(this.SUBDIALOGS_DIR, entryRelativePath));
4153
+ const inferredId = this.inferExpectedDialogIdFromMetadataRelativeDir(path.join(this.SIDE_DIALOGS_DIR, entryRelativePath));
4023
4154
  if (!inferredId) {
4024
- throw new Error(`Failed to infer subdialog id from relative path ${entryRelativePath} under root ${rootDialogId.selfId}`);
4155
+ throw new Error(`Failed to infer sideDialog id from relative path ${entryRelativePath} under root ${mainDialogId.selfId}`);
4025
4156
  }
4026
- subdialogIds.add(inferredId);
4157
+ sideDialogIds.add(inferredId);
4027
4158
  continue;
4028
4159
  }
4029
4160
  catch (error) {
@@ -4034,8 +4165,8 @@ class DialogPersistence {
4034
4165
  await visit(fullPath, entryRelativePath);
4035
4166
  }
4036
4167
  };
4037
- await visit(subdialogsPath);
4038
- return [...subdialogIds];
4168
+ await visit(sideDialogsPath);
4169
+ return [...sideDialogIds];
4039
4170
  }
4040
4171
  static async pathExists(targetPath) {
4041
4172
  try {
@@ -4058,11 +4189,11 @@ class DialogPersistence {
4058
4189
  this.latestWriteBack.delete(key);
4059
4190
  this.latestWriteBackMutexes.delete(key);
4060
4191
  }
4061
- static clearWriteBackEntriesForRootDialog(rootDialogId, status) {
4062
- const basePrefix = `${this.getDialogsRootDir()}|${status}|${rootDialogId.selfId}`;
4063
- const matchesRootDialogKey = (key) => key === basePrefix || key.startsWith(`${basePrefix}#`) || key.startsWith(`${basePrefix}|`);
4192
+ static clearWriteBackEntriesForMainDialog(mainDialogId, status) {
4193
+ const basePrefix = `${this.getDialogsRootDir()}|${status}|${mainDialogId.selfId}`;
4194
+ const matchesMainDialogKey = (key) => key === basePrefix || key.startsWith(`${basePrefix}#`) || key.startsWith(`${basePrefix}|`);
4064
4195
  for (const [key, entry] of this.latestWriteBack.entries()) {
4065
- if (!matchesRootDialogKey(key))
4196
+ if (!matchesMainDialogKey(key))
4066
4197
  continue;
4067
4198
  if (entry.kind === 'scheduled') {
4068
4199
  clearTimeout(entry.timer);
@@ -4070,12 +4201,12 @@ class DialogPersistence {
4070
4201
  this.latestWriteBack.delete(key);
4071
4202
  }
4072
4203
  for (const key of this.latestWriteBackMutexes.keys()) {
4073
- if (matchesRootDialogKey(key)) {
4204
+ if (matchesMainDialogKey(key)) {
4074
4205
  this.latestWriteBackMutexes.delete(key);
4075
4206
  }
4076
4207
  }
4077
4208
  for (const [key, entry] of this.q4hWriteBack.entries()) {
4078
- if (!matchesRootDialogKey(key))
4209
+ if (!matchesMainDialogKey(key))
4079
4210
  continue;
4080
4211
  if (entry.kind === 'scheduled') {
4081
4212
  clearTimeout(entry.timer);
@@ -4083,72 +4214,72 @@ class DialogPersistence {
4083
4214
  this.q4hWriteBack.delete(key);
4084
4215
  }
4085
4216
  for (const key of this.q4hWriteBackMutexes.keys()) {
4086
- if (matchesRootDialogKey(key)) {
4217
+ if (matchesMainDialogKey(key)) {
4087
4218
  this.q4hWriteBackMutexes.delete(key);
4088
4219
  }
4089
4220
  }
4090
- for (const [key, entry] of this.pendingSubdialogsWriteBack.entries()) {
4091
- if (!matchesRootDialogKey(key))
4221
+ for (const [key, entry] of this.pendingSideDialogsWriteBack.entries()) {
4222
+ if (!matchesMainDialogKey(key))
4092
4223
  continue;
4093
4224
  if (entry.kind === 'scheduled') {
4094
4225
  clearTimeout(entry.timer);
4095
4226
  }
4096
- this.pendingSubdialogsWriteBack.delete(key);
4227
+ this.pendingSideDialogsWriteBack.delete(key);
4097
4228
  }
4098
- for (const key of this.pendingSubdialogsWriteBackMutexes.keys()) {
4099
- if (matchesRootDialogKey(key)) {
4100
- this.pendingSubdialogsWriteBackMutexes.delete(key);
4229
+ for (const key of this.pendingSideDialogsWriteBackMutexes.keys()) {
4230
+ if (matchesMainDialogKey(key)) {
4231
+ this.pendingSideDialogsWriteBackMutexes.delete(key);
4101
4232
  }
4102
4233
  }
4103
4234
  for (const key of this.courseAppendMutexes.keys()) {
4104
- if (matchesRootDialogKey(key)) {
4235
+ if (matchesMainDialogKey(key)) {
4105
4236
  this.courseAppendMutexes.delete(key);
4106
4237
  }
4107
4238
  }
4108
4239
  }
4109
4240
  static async quarantineMalformedDialog(dialogId, status, reason, error) {
4110
- const rootDialogId = dialogId.rootId === dialogId.selfId ? dialogId : new dialog_1.DialogID(dialogId.rootId);
4111
- const quarantineKey = `${status}|${rootDialogId.selfId}`;
4112
- if (quarantiningRootDialogs.has(quarantineKey)) {
4241
+ const mainDialogId = dialogId.rootId === dialogId.selfId ? dialogId : new dialog_1.DialogID(dialogId.rootId);
4242
+ const quarantineKey = `${status}|${mainDialogId.selfId}`;
4243
+ if (quarantiningMainDialogs.has(quarantineKey)) {
4113
4244
  return;
4114
4245
  }
4115
- quarantiningRootDialogs.add(quarantineKey);
4246
+ quarantiningMainDialogs.add(quarantineKey);
4116
4247
  let quarantined = false;
4117
4248
  try {
4118
4249
  await prepareDialogQuarantineHook?.({
4119
4250
  dialogId,
4120
- rootDialogId,
4251
+ mainDialogId,
4121
4252
  status,
4122
4253
  reason,
4123
4254
  error,
4124
4255
  });
4125
- this.quarantinedRootDialogScopes.add(this.getRootDialogWriteBackCancelScopeKey(rootDialogId, status));
4126
- this.cancelRootDialogWriteBacks(rootDialogId, status);
4127
- const sourcePath = this.getRootDialogPath(rootDialogId, status);
4256
+ this.quarantinedMainDialogScopes.add(this.getMainDialogWriteBackCancelScopeKey(mainDialogId, status));
4257
+ this.cancelMainDialogWriteBacks(mainDialogId, status);
4258
+ const sourcePath = this.getMainDialogPath(mainDialogId, status);
4128
4259
  if (!(await this.pathExists(sourcePath))) {
4129
4260
  return;
4130
4261
  }
4131
- let destinationPath = this.getMalformedRootDialogPath(rootDialogId, status);
4262
+ let destinationPath = this.getMalformedMainDialogPath(mainDialogId, status);
4132
4263
  if (await this.pathExists(destinationPath)) {
4133
- destinationPath = path.join(this.getDialogsRootDir(), this.MALFORMED_DIR, `${rootDialogId.selfId}__${(0, node_crypto_1.randomUUID)()}`);
4264
+ destinationPath = path.join(this.getDialogsRootDir(), this.MALFORMED_DIR, `${mainDialogId.selfId}__${(0, node_crypto_1.randomUUID)()}`);
4134
4265
  }
4135
4266
  await fs.promises.mkdir(path.dirname(destinationPath), { recursive: true });
4136
4267
  await fs.promises.rename(sourcePath, destinationPath);
4137
4268
  quarantined = true;
4138
- log_1.log.warn(`Quarantined malformed dialog ${rootDialogId.selfId}`, undefined, {
4269
+ log_1.log.warn(`Quarantined malformed dialog ${mainDialogId.selfId}`, undefined, {
4139
4270
  status,
4140
4271
  reason,
4141
4272
  sourcePath,
4142
4273
  destinationPath,
4143
4274
  errorMessage: error.message,
4144
4275
  dialogId: dialogId.valueOf(),
4145
- rootDialogId: rootDialogId.valueOf(),
4276
+ mainDialogId: mainDialogId.valueOf(),
4146
4277
  });
4147
4278
  dialogsQuarantinedBroadcaster?.({
4148
4279
  type: 'dialogs_quarantined',
4149
4280
  status: 'quarantining',
4150
4281
  fromStatus: assertPersistableDialogStatus(status, 'DialogPersistence.quarantineMalformedDialog(fromStatus)'),
4151
- rootId: rootDialogId.selfId,
4282
+ rootId: mainDialogId.selfId,
4152
4283
  dialogId: dialogId.selfId,
4153
4284
  reason,
4154
4285
  timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
@@ -4158,7 +4289,7 @@ class DialogPersistence {
4158
4289
  try {
4159
4290
  await finalizeDialogQuarantineHook?.({
4160
4291
  dialogId,
4161
- rootDialogId,
4292
+ mainDialogId,
4162
4293
  status,
4163
4294
  reason,
4164
4295
  error,
@@ -4166,7 +4297,7 @@ class DialogPersistence {
4166
4297
  });
4167
4298
  }
4168
4299
  finally {
4169
- quarantiningRootDialogs.delete(quarantineKey);
4300
+ quarantiningMainDialogs.delete(quarantineKey);
4170
4301
  }
4171
4302
  }
4172
4303
  }
@@ -4197,8 +4328,8 @@ class DialogPersistence {
4197
4328
  /**
4198
4329
  * Ensure dialog directory structure exists
4199
4330
  */
4200
- static async ensureRootDialogDirectory(dialogId, status = 'running') {
4201
- const dialogPath = this.getRootDialogPath(dialogId, status);
4331
+ static async ensureMainDialogDirectory(dialogId, status = 'running') {
4332
+ const dialogPath = this.getMainDialogPath(dialogId, status);
4202
4333
  try {
4203
4334
  await fs.promises.mkdir(dialogPath, { recursive: true });
4204
4335
  return dialogPath;
@@ -4209,16 +4340,16 @@ class DialogPersistence {
4209
4340
  }
4210
4341
  }
4211
4342
  /**
4212
- * Ensure subdialog directory structure exists
4343
+ * Ensure sideDialog directory structure exists
4213
4344
  */
4214
- static async ensureSubdialogDirectory(dialogId, status = 'running') {
4215
- const subdialogPath = this.getSubdialogPath(dialogId, status);
4345
+ static async ensureSideDialogDirectory(dialogId, status = 'running') {
4346
+ const sideDialogPath = this.getSideDialogPath(dialogId, status);
4216
4347
  try {
4217
- await fs.promises.mkdir(subdialogPath, { recursive: true });
4218
- return subdialogPath;
4348
+ await fs.promises.mkdir(sideDialogPath, { recursive: true });
4349
+ return sideDialogPath;
4219
4350
  }
4220
4351
  catch (error) {
4221
- log_1.log.error(`Failed to create subdialog directory ${subdialogPath}:`, error);
4352
+ log_1.log.error(`Failed to create sideDialog directory ${sideDialogPath}:`, error);
4222
4353
  throw error;
4223
4354
  }
4224
4355
  }
@@ -4227,8 +4358,8 @@ class DialogPersistence {
4227
4358
  */
4228
4359
  static async markDialogCompleted(dialogId) {
4229
4360
  try {
4230
- const dialogPath = this.getRootDialogPath(dialogId, 'running');
4231
- const completedPath = this.getRootDialogPath(dialogId, 'completed');
4361
+ const dialogPath = this.getMainDialogPath(dialogId, 'running');
4362
+ const completedPath = this.getMainDialogPath(dialogId, 'completed');
4232
4363
  await fs.promises.mkdir(completedPath, { recursive: true });
4233
4364
  // Move files from current to completed
4234
4365
  const files = await fs.promises.readdir(dialogPath);
@@ -4244,7 +4375,7 @@ class DialogPersistence {
4244
4375
  }
4245
4376
  }
4246
4377
  /**
4247
- * List candidate root dialog IDs by scanning `dialog.yaml`.
4378
+ * List candidate main dialog IDs by scanning `dialog.yaml`.
4248
4379
  *
4249
4380
  * This scanner intentionally stays lightweight: it only validates the path<->id identity needed
4250
4381
  * for safe enumeration, and leaves full metadata shape validation to the subsequent lazy-load
@@ -4301,9 +4432,9 @@ class DialogPersistence {
4301
4432
  catch (yamlError) {
4302
4433
  const persistenceError = (0, persistence_errors_1.findDomindsPersistenceFileError)(yamlError);
4303
4434
  if (persistenceError) {
4304
- const rootDialogId = this.inferRootDialogIdFromMetadataRelativeDir(path.dirname(entryRelativePath));
4305
- if (rootDialogId) {
4306
- await this.quarantineMalformedDialog(rootDialogId, status, 'listDialogs', persistenceError);
4435
+ const mainDialogId = this.inferMainDialogIdFromMetadataRelativeDir(path.dirname(entryRelativePath));
4436
+ if (mainDialogId) {
4437
+ await this.quarantineMalformedDialog(mainDialogId, status, 'listDialogs', persistenceError);
4307
4438
  }
4308
4439
  }
4309
4440
  log_1.log.warn(`🔍 listDialogs: Failed to parse dialog.yaml at ${fullPath}:`, yamlError);
@@ -4347,8 +4478,8 @@ class DialogPersistence {
4347
4478
  }
4348
4479
  }
4349
4480
  /**
4350
- * List all dialog IDs (root + subdialogs) together with their root IDs.
4351
- * This is the only safe way to enumerate subdialogs because their directory names
4481
+ * List all dialog IDs (main dialogs + sideDialogs) together with their root IDs.
4482
+ * This is the only safe way to enumerate sideDialogs because their directory names
4352
4483
  * are not guaranteed to be their selfId.
4353
4484
  *
4354
4485
  * Like `listDialogs()`, this is a candidate scanner rather than a full metadata validator.
@@ -4359,9 +4490,9 @@ class DialogPersistence {
4359
4490
  const statusDir = this.getDialogsRootDir();
4360
4491
  const specificDir = path.join(statusDir, getPersistableStatusDirName(status, 'DialogPersistence.listAllDialogIds'));
4361
4492
  const result = [];
4362
- const rootDialogIdByDialogYamlPath = new Map();
4493
+ const mainDialogIdByDialogYamlPath = new Map();
4363
4494
  const readDialogYamlId = async (dialogYamlPath) => {
4364
- const cached = rootDialogIdByDialogYamlPath.get(dialogYamlPath);
4495
+ const cached = mainDialogIdByDialogYamlPath.get(dialogYamlPath);
4365
4496
  if (cached !== undefined)
4366
4497
  return cached;
4367
4498
  try {
@@ -4391,19 +4522,19 @@ class DialogPersistence {
4391
4522
  });
4392
4523
  }
4393
4524
  const normalized = idValue.trim();
4394
- rootDialogIdByDialogYamlPath.set(dialogYamlPath, normalized);
4525
+ mainDialogIdByDialogYamlPath.set(dialogYamlPath, normalized);
4395
4526
  return normalized;
4396
4527
  }
4397
4528
  catch (error) {
4398
4529
  const persistenceError = (0, persistence_errors_1.findDomindsPersistenceFileError)(error);
4399
4530
  if (persistenceError) {
4400
4531
  const relativeDir = path.relative(specificDir, path.dirname(dialogYamlPath));
4401
- const rootDialogId = this.inferRootDialogIdFromMetadataRelativeDir(relativeDir);
4402
- if (rootDialogId) {
4403
- await this.quarantineMalformedDialog(rootDialogId, status, 'listAllDialogIds:readDialogYamlId', persistenceError);
4532
+ const mainDialogId = this.inferMainDialogIdFromMetadataRelativeDir(relativeDir);
4533
+ if (mainDialogId) {
4534
+ await this.quarantineMalformedDialog(mainDialogId, status, 'listAllDialogIds:readDialogYamlId', persistenceError);
4404
4535
  }
4405
4536
  }
4406
- rootDialogIdByDialogYamlPath.set(dialogYamlPath, null);
4537
+ mainDialogIdByDialogYamlPath.set(dialogYamlPath, null);
4407
4538
  return null;
4408
4539
  }
4409
4540
  };
@@ -4414,11 +4545,11 @@ class DialogPersistence {
4414
4545
  const segments = dir.split(path.sep).filter((seg) => seg.length > 0 && seg !== '.');
4415
4546
  if (segments.length === 0)
4416
4547
  return null;
4417
- // Root dialog IDs in this repo can contain path separators (e.g. "f4/44/cd85c4e2").
4418
- // The root dialog directory is therefore nested (RUN_DIR/<rootId>/dialog.yaml).
4548
+ // Main dialog IDs in this repo can contain path separators (e.g. "f4/44/cd85c4e2").
4549
+ // The main dialog directory is therefore nested (RUN_DIR/<rootId>/dialog.yaml).
4419
4550
  //
4420
- // To infer the rootId for any dialog.yaml we find (root or subdialog), scan prefixes of the
4421
- // directory path and pick the first prefix that is itself a valid root dialog directory:
4551
+ // To infer the rootId for any dialog.yaml we find (main dialog or sideDialog), scan prefixes of the
4552
+ // directory path and pick the first prefix that is itself a valid main dialog directory:
4422
4553
  // - it has a dialog.yaml
4423
4554
  // - its dialog.yaml id matches the prefix joined with '/'
4424
4555
  for (let i = 1; i <= segments.length; i++) {
@@ -4495,9 +4626,9 @@ class DialogPersistence {
4495
4626
  catch (yamlError) {
4496
4627
  const persistenceError = (0, persistence_errors_1.findDomindsPersistenceFileError)(yamlError);
4497
4628
  if (persistenceError) {
4498
- const rootDialogId = this.inferRootDialogIdFromMetadataRelativeDir(relDir);
4499
- if (rootDialogId) {
4500
- await this.quarantineMalformedDialog(rootDialogId, status, 'listAllDialogIds', persistenceError);
4629
+ const mainDialogId = this.inferMainDialogIdFromMetadataRelativeDir(relDir);
4630
+ if (mainDialogId) {
4631
+ await this.quarantineMalformedDialog(mainDialogId, status, 'listAllDialogIds', persistenceError);
4501
4632
  }
4502
4633
  }
4503
4634
  log_1.log.warn(`🔍 listAllDialogIds: Failed to parse dialog.yaml at ${fullPath}:`, yamlError);
@@ -4525,7 +4656,7 @@ class DialogPersistence {
4525
4656
  static async appendEvents(dialogId, course, events, status = 'running') {
4526
4657
  const appendMutexKey = this.getCourseAppendMutexKey(dialogId, course, status);
4527
4658
  const release = await this.getCourseAppendMutex(appendMutexKey).acquire();
4528
- const cancellationToken = this.createRootDialogWriteBackCancellationToken(dialogId, status);
4659
+ const cancellationToken = this.createMainDialogWriteBackCancellationToken(dialogId, status);
4529
4660
  try {
4530
4661
  if (events.length === 0) {
4531
4662
  return;
@@ -5021,7 +5152,7 @@ class DialogPersistence {
5021
5152
  if (entry.kind !== 'scheduled')
5022
5153
  return;
5023
5154
  clearTimeout(entry.timer);
5024
- const cancellationToken = this.createRootDialogWriteBackCancellationToken(entry.dialogId, entry.status);
5155
+ const cancellationToken = this.createMainDialogWriteBackCancellationToken(entry.dialogId, entry.status);
5025
5156
  const inFlight = this.writeQ4HStateToDisk(entry.dialogId, entry.state, entry.status, cancellationToken);
5026
5157
  captured = {
5027
5158
  dialogId: entry.dialogId,
@@ -5107,7 +5238,7 @@ class DialogPersistence {
5107
5238
  }
5108
5239
  static async writeQ4HStateToDisk(dialogId, state, status, cancellationToken) {
5109
5240
  if (cancellationToken) {
5110
- this.assertRootDialogWriteBackNotCanceled(cancellationToken, 'writeQ4HStateToDisk:start');
5241
+ this.assertMainDialogWriteBackNotCanceled(cancellationToken, 'writeQ4HStateToDisk:start');
5111
5242
  }
5112
5243
  const dialogPath = this.getDialogEventsPath(dialogId, status);
5113
5244
  const questionsFilePath = path.join(dialogPath, 'q4h.yaml');
@@ -5132,7 +5263,7 @@ class DialogPersistence {
5132
5263
  */
5133
5264
  static async loadAllQ4HState() {
5134
5265
  try {
5135
- // Get all running dialogs (root + subdialogs) with correct rootId association.
5266
+ // Get all running dialogs (main dialogs + sideDialogs) with correct rootId association.
5136
5267
  const dialogIds = await this.listAllDialogIds('running');
5137
5268
  const allQuestions = [];
5138
5269
  for (const dialogIdObj of dialogIds) {
@@ -5183,36 +5314,35 @@ class DialogPersistence {
5183
5314
  log_1.log.error(`Failed to clear q4h.yaml for dialog ${dialogId}:`, error);
5184
5315
  }
5185
5316
  }
5186
- // === PHASE 6: SUBDIALOG SUPPLY PERSISTENCE ===
5317
+ // === PHASE 6: SIDE DIALOG PENDING PERSISTENCE ===
5187
5318
  /**
5188
- * Save pending subdialogs for Type A supply mechanism.
5189
- * Tracks subdialogs that were created but not yet completed.
5319
+ * Save pending sideDialogs that have an outstanding tellask/reply delivery.
5190
5320
  */
5191
- static async savePendingSubdialogs(rootDialogId, pendingSubdialogs, rootAnchor, status = 'running') {
5192
- const next = pendingSubdialogs.map((r) => ({ ...r }));
5193
- await this.mutatePendingSubdialogs(rootDialogId, () => ({ kind: 'replace', records: next }), rootAnchor, status);
5321
+ static async savePendingSideDialogs(mainDialogId, pendingSideDialogs, rootAnchor, status = 'running') {
5322
+ const next = pendingSideDialogs.map((r) => ({ ...r }));
5323
+ await this.mutatePendingSideDialogs(mainDialogId, () => ({ kind: 'replace', records: next }), rootAnchor, status);
5194
5324
  }
5195
5325
  /**
5196
- * Load pending subdialogs for Type A supply mechanism.
5326
+ * Load pending sideDialogs that have an outstanding tellask/reply delivery.
5197
5327
  */
5198
- static async loadPendingSubdialogs(rootDialogId, status = 'running') {
5199
- const key = this.getPendingSubdialogsWriteBackKey(rootDialogId, status);
5200
- const staged = this.pendingSubdialogsWriteBack.get(key);
5328
+ static async loadPendingSideDialogs(mainDialogId, status = 'running') {
5329
+ const key = this.getPendingSideDialogsWriteBackKey(mainDialogId, status);
5330
+ const staged = this.pendingSideDialogsWriteBack.get(key);
5201
5331
  if (staged) {
5202
5332
  return staged.state.kind === 'deleted' ? [] : staged.state.records;
5203
5333
  }
5204
5334
  try {
5205
- return await this.loadPendingSubdialogsFromDisk(rootDialogId, status);
5335
+ return await this.loadPendingSideDialogsFromDisk(mainDialogId, status);
5206
5336
  }
5207
5337
  catch (error) {
5208
- await this.rethrowAfterQuarantiningDialogPersistenceProblem(rootDialogId, status, 'loadPendingSubdialogs', error);
5209
- throw new Error('unreachable after loadPendingSubdialogs persistence rethrow');
5338
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(mainDialogId, status, 'loadPendingSideDialogs', error);
5339
+ throw new Error('unreachable after loadPendingSideDialogs persistence rethrow');
5210
5340
  }
5211
5341
  }
5212
- static isPendingSubdialogRecord(value) {
5342
+ static isPendingSideDialogRecord(value) {
5213
5343
  if (!isRecord(value))
5214
5344
  return false;
5215
- if (typeof value.subdialogId !== 'string')
5345
+ if (typeof value.sideDialogId !== 'string')
5216
5346
  return false;
5217
5347
  if (typeof value.createdAt !== 'string')
5218
5348
  return false;
@@ -5242,28 +5372,18 @@ class DialogPersistence {
5242
5372
  return false;
5243
5373
  if (typeof value.callId !== 'string')
5244
5374
  return false;
5245
- if ('callingCourse' in value) {
5246
- const callingCourse = value.callingCourse;
5247
- if (callingCourse !== undefined) {
5248
- if (typeof callingCourse !== 'number')
5249
- return false;
5250
- if (!Number.isFinite(callingCourse))
5251
- return false;
5252
- if (Math.floor(callingCourse) <= 0)
5253
- return false;
5254
- }
5255
- }
5256
- if ('callingGenseq' in value) {
5257
- const callingGenseq = value.callingGenseq;
5258
- if (callingGenseq !== undefined) {
5259
- if (typeof callingGenseq !== 'number')
5260
- return false;
5261
- if (!Number.isFinite(callingGenseq))
5262
- return false;
5263
- if (Math.floor(callingGenseq) <= 0)
5264
- return false;
5265
- }
5266
- }
5375
+ if (typeof value.callSiteCourse !== 'number')
5376
+ return false;
5377
+ if (!Number.isInteger(value.callSiteCourse))
5378
+ return false;
5379
+ if (value.callSiteCourse <= 0)
5380
+ return false;
5381
+ if (typeof value.callSiteGenseq !== 'number')
5382
+ return false;
5383
+ if (!Number.isInteger(value.callSiteGenseq))
5384
+ return false;
5385
+ if (value.callSiteGenseq <= 0)
5386
+ return false;
5267
5387
  if (value.callType !== 'A' && value.callType !== 'B' && value.callType !== 'C')
5268
5388
  return false;
5269
5389
  if ('sessionSlug' in value) {
@@ -5273,27 +5393,56 @@ class DialogPersistence {
5273
5393
  }
5274
5394
  return true;
5275
5395
  }
5276
- static async loadPendingSubdialogsFromDisk(rootDialogId, status) {
5277
- const dialogPath = this.getDialogResponsesPath(rootDialogId, status);
5278
- const filePath = path.join(dialogPath, 'pending-subdialogs.json');
5396
+ static assertNoDuplicateSessionedTellaskPendingRecords(records, context) {
5397
+ const seen = new Map();
5398
+ for (const record of records) {
5399
+ if (record.callType !== 'B')
5400
+ continue;
5401
+ if (record.callName !== 'tellask')
5402
+ continue;
5403
+ if (record.sessionSlug === undefined)
5404
+ continue;
5405
+ const sessionSlug = record.sessionSlug.trim();
5406
+ if (sessionSlug === '')
5407
+ continue;
5408
+ const key = `${record.targetAgentId}\0${sessionSlug}`;
5409
+ const previous = seen.get(key);
5410
+ if (previous) {
5411
+ throw new Error(`pending-sideDialogs invariant violation: duplicate sessioned tellask pending record ` +
5412
+ `(rootId=${context.rootId}, selfId=${context.selfId}, status=${context.status}, ` +
5413
+ `targetAgentId=${record.targetAgentId}, sessionSlug=${sessionSlug}, ` +
5414
+ `previousSideDialogId=${previous.sideDialogId}, previousCallId=${previous.callId}, ` +
5415
+ `duplicateSideDialogId=${record.sideDialogId}, duplicateCallId=${record.callId})`);
5416
+ }
5417
+ seen.set(key, record);
5418
+ }
5419
+ }
5420
+ static async loadPendingSideDialogsFromDisk(mainDialogId, status) {
5421
+ const dialogPath = this.getDialogResponsesPath(mainDialogId, status);
5422
+ const filePath = path.join(dialogPath, 'pending-sideDialogs.json');
5279
5423
  try {
5280
5424
  const content = await readPersistenceTextFile({
5281
5425
  filePath,
5282
- source: 'pending_subdialogs',
5426
+ source: 'pending_sideDialogs',
5283
5427
  format: 'json',
5284
5428
  });
5285
5429
  const parsed = parsePersistenceJson({
5286
5430
  content,
5287
5431
  filePath,
5288
- source: 'pending_subdialogs',
5432
+ source: 'pending_sideDialogs',
5289
5433
  });
5290
- if (!Array.isArray(parsed) || !parsed.every((item) => this.isPendingSubdialogRecord(item))) {
5434
+ if (!Array.isArray(parsed) || !parsed.every((item) => this.isPendingSideDialogRecord(item))) {
5291
5435
  throw buildInvalidPersistenceFileError({
5292
- source: 'pending_subdialogs',
5436
+ source: 'pending_sideDialogs',
5293
5437
  format: 'json',
5294
5438
  filePath,
5295
5439
  });
5296
5440
  }
5441
+ this.assertNoDuplicateSessionedTellaskPendingRecords(parsed, {
5442
+ rootId: mainDialogId.rootId,
5443
+ selfId: mainDialogId.selfId,
5444
+ status,
5445
+ });
5297
5446
  return parsed;
5298
5447
  }
5299
5448
  catch (error) {
@@ -5302,17 +5451,17 @@ class DialogPersistence {
5302
5451
  throw error;
5303
5452
  }
5304
5453
  }
5305
- static async mutatePendingSubdialogs(rootDialogId, mutator, rootAnchor, status = 'running') {
5306
- const key = this.getPendingSubdialogsWriteBackKey(rootDialogId, status);
5307
- const mutex = this.getPendingSubdialogsWriteBackMutex(key);
5454
+ static async mutatePendingSideDialogs(mainDialogId, mutator, rootAnchor, status = 'running') {
5455
+ const key = this.getPendingSideDialogsWriteBackKey(mainDialogId, status);
5456
+ const mutex = this.getPendingSideDialogsWriteBackMutex(key);
5308
5457
  const release = await mutex.acquire();
5309
5458
  try {
5310
- const staged = this.pendingSubdialogsWriteBack.get(key);
5459
+ const staged = this.pendingSideDialogsWriteBack.get(key);
5311
5460
  const previousRecords = staged && staged.state.kind === 'file'
5312
5461
  ? staged.state.records
5313
5462
  : staged && staged.state.kind === 'deleted'
5314
5463
  ? []
5315
- : await this.loadPendingSubdialogsFromDisk(rootDialogId, status);
5464
+ : await this.loadPendingSideDialogsFromDisk(mainDialogId, status);
5316
5465
  const mutation = mutator(previousRecords);
5317
5466
  let nextRecords = previousRecords;
5318
5467
  const removedRecords = [];
@@ -5322,20 +5471,20 @@ class DialogPersistence {
5322
5471
  else if (mutation.kind === 'append') {
5323
5472
  nextRecords = [...previousRecords, mutation.record];
5324
5473
  }
5325
- else if (mutation.kind === 'removeBySubdialogId') {
5474
+ else if (mutation.kind === 'removeBySideDialogId') {
5326
5475
  for (const r of previousRecords) {
5327
- if (r.subdialogId === mutation.subdialogId)
5476
+ if (r.sideDialogId === mutation.sideDialogId)
5328
5477
  removedRecords.push(r);
5329
5478
  }
5330
- nextRecords = previousRecords.filter((r) => r.subdialogId !== mutation.subdialogId);
5479
+ nextRecords = previousRecords.filter((r) => r.sideDialogId !== mutation.sideDialogId);
5331
5480
  }
5332
- else if (mutation.kind === 'removeBySubdialogIds') {
5333
- const remove = new Set(mutation.subdialogIds);
5481
+ else if (mutation.kind === 'removeBySideDialogIds') {
5482
+ const remove = new Set(mutation.sideDialogIds);
5334
5483
  for (const r of previousRecords) {
5335
- if (remove.has(r.subdialogId))
5484
+ if (remove.has(r.sideDialogId))
5336
5485
  removedRecords.push(r);
5337
5486
  }
5338
- nextRecords = previousRecords.filter((r) => !remove.has(r.subdialogId));
5487
+ nextRecords = previousRecords.filter((r) => !remove.has(r.sideDialogId));
5339
5488
  }
5340
5489
  else if (mutation.kind === 'replace') {
5341
5490
  nextRecords = [...mutation.records];
@@ -5346,17 +5495,28 @@ class DialogPersistence {
5346
5495
  }
5347
5496
  else {
5348
5497
  const _exhaustive = mutation;
5349
- throw new Error(`Unhandled pending-subdialogs mutation: ${String(_exhaustive)}`);
5498
+ throw new Error(`Unhandled pending-sideDialogs mutation: ${String(_exhaustive)}`);
5499
+ }
5500
+ for (let index = 0; index < nextRecords.length; index += 1) {
5501
+ if (!this.isPendingSideDialogRecord(nextRecords[index])) {
5502
+ throw new Error(`pending-sideDialogs write invariant violation: malformed record at index ${index} ` +
5503
+ `(rootId=${mainDialogId.rootId}, selfId=${mainDialogId.selfId}, status=${status})`);
5504
+ }
5350
5505
  }
5506
+ this.assertNoDuplicateSessionedTellaskPendingRecords(nextRecords, {
5507
+ rootId: mainDialogId.rootId,
5508
+ selfId: mainDialogId.selfId,
5509
+ status,
5510
+ });
5351
5511
  const nextState = nextRecords.length === 0 ? { kind: 'deleted' } : { kind: 'file', records: nextRecords };
5352
- const pending = this.pendingSubdialogsWriteBack.get(key);
5512
+ const pending = this.pendingSideDialogsWriteBack.get(key);
5353
5513
  if (!pending) {
5354
5514
  const timer = setTimeout(() => {
5355
- void this.flushPendingSubdialogsWriteBack(key);
5356
- }, this.PENDING_SUBDIALOGS_WRITEBACK_WINDOW_MS);
5357
- this.pendingSubdialogsWriteBack.set(key, {
5515
+ void this.flushPendingSideDialogsWriteBack(key);
5516
+ }, this.PENDING_SIDE_DIALOGS_WRITEBACK_WINDOW_MS);
5517
+ this.pendingSideDialogsWriteBack.set(key, {
5358
5518
  kind: 'scheduled',
5359
- dialogId: rootDialogId,
5519
+ dialogId: mainDialogId,
5360
5520
  status,
5361
5521
  state: nextState,
5362
5522
  timer,
@@ -5368,7 +5528,7 @@ class DialogPersistence {
5368
5528
  pending.dirty = true;
5369
5529
  }
5370
5530
  if (rootAnchor) {
5371
- await this.appendPendingSubdialogsReconciledRecord(rootDialogId, nextRecords, rootAnchorWriteTarget(rootAnchor), status);
5531
+ await this.appendPendingSideDialogsReconciledRecord(mainDialogId, nextRecords, rootAnchorWriteTarget(rootAnchor), status);
5372
5532
  }
5373
5533
  return { previousRecords, records: nextRecords, removedRecords };
5374
5534
  }
@@ -5376,22 +5536,22 @@ class DialogPersistence {
5376
5536
  release();
5377
5537
  }
5378
5538
  }
5379
- static async appendPendingSubdialog(rootDialogId, record, rootAnchor, status = 'running') {
5380
- await this.mutatePendingSubdialogs(rootDialogId, () => ({ kind: 'append', record }), rootAnchor, status);
5539
+ static async appendPendingSideDialog(mainDialogId, record, rootAnchor, status = 'running') {
5540
+ await this.mutatePendingSideDialogs(mainDialogId, () => ({ kind: 'append', record }), rootAnchor, status);
5381
5541
  }
5382
- static async removePendingSubdialog(rootDialogId, subdialogId, rootAnchor, status = 'running') {
5383
- await this.mutatePendingSubdialogs(rootDialogId, () => ({ kind: 'removeBySubdialogId', subdialogId }), rootAnchor, status);
5542
+ static async removePendingSideDialog(mainDialogId, sideDialogId, rootAnchor, status = 'running') {
5543
+ await this.mutatePendingSideDialogs(mainDialogId, () => ({ kind: 'removeBySideDialogId', sideDialogId }), rootAnchor, status);
5384
5544
  }
5385
- static async clearPendingSubdialogs(rootDialogId, rootAnchor, status = 'running') {
5386
- await this.mutatePendingSubdialogs(rootDialogId, () => ({ kind: 'clear' }), rootAnchor, status);
5545
+ static async clearPendingSideDialogs(mainDialogId, rootAnchor, status = 'running') {
5546
+ await this.mutatePendingSideDialogs(mainDialogId, () => ({ kind: 'clear' }), rootAnchor, status);
5387
5547
  }
5388
- static async flushPendingSubdialogsWriteBack(key) {
5389
- const mutex = this.getPendingSubdialogsWriteBackMutex(key);
5548
+ static async flushPendingSideDialogsWriteBack(key) {
5549
+ const mutex = this.getPendingSideDialogsWriteBackMutex(key);
5390
5550
  let captured;
5391
5551
  {
5392
5552
  const release = await mutex.acquire();
5393
5553
  try {
5394
- const entry = this.pendingSubdialogsWriteBack.get(key);
5554
+ const entry = this.pendingSideDialogsWriteBack.get(key);
5395
5555
  if (!entry)
5396
5556
  return;
5397
5557
  if (entry.kind === 'flushing')
@@ -5399,15 +5559,15 @@ class DialogPersistence {
5399
5559
  if (entry.kind !== 'scheduled')
5400
5560
  return;
5401
5561
  clearTimeout(entry.timer);
5402
- const cancellationToken = this.createRootDialogWriteBackCancellationToken(entry.dialogId, entry.status);
5403
- const inFlight = this.writePendingSubdialogsToDisk(entry.dialogId, entry.state, entry.status, cancellationToken);
5562
+ const cancellationToken = this.createMainDialogWriteBackCancellationToken(entry.dialogId, entry.status);
5563
+ const inFlight = this.writePendingSideDialogsToDisk(entry.dialogId, entry.state, entry.status, cancellationToken);
5404
5564
  captured = {
5405
5565
  dialogId: entry.dialogId,
5406
5566
  status: entry.status,
5407
5567
  stateToWrite: entry.state,
5408
5568
  inFlight,
5409
5569
  };
5410
- this.pendingSubdialogsWriteBack.set(key, {
5570
+ this.pendingSideDialogsWriteBack.set(key, {
5411
5571
  kind: 'flushing',
5412
5572
  dialogId: entry.dialogId,
5413
5573
  status: entry.status,
@@ -5428,7 +5588,7 @@ class DialogPersistence {
5428
5588
  catch (error) {
5429
5589
  const release = await mutex.acquire();
5430
5590
  try {
5431
- const entry = this.pendingSubdialogsWriteBack.get(key);
5591
+ const entry = this.pendingSideDialogsWriteBack.get(key);
5432
5592
  if (!entry)
5433
5593
  return;
5434
5594
  if (entry.kind !== 'flushing')
@@ -5436,13 +5596,13 @@ class DialogPersistence {
5436
5596
  if (entry.inFlight !== captured.inFlight)
5437
5597
  return;
5438
5598
  if (isDialogWriteBackCanceledError(error)) {
5439
- this.pendingSubdialogsWriteBack.delete(key);
5599
+ this.pendingSideDialogsWriteBack.delete(key);
5440
5600
  return;
5441
5601
  }
5442
5602
  const timer = setTimeout(() => {
5443
- void this.flushPendingSubdialogsWriteBack(key);
5444
- }, this.PENDING_SUBDIALOGS_WRITEBACK_WINDOW_MS);
5445
- this.pendingSubdialogsWriteBack.set(key, {
5603
+ void this.flushPendingSideDialogsWriteBack(key);
5604
+ }, this.PENDING_SIDE_DIALOGS_WRITEBACK_WINDOW_MS);
5605
+ this.pendingSideDialogsWriteBack.set(key, {
5446
5606
  kind: 'scheduled',
5447
5607
  dialogId: entry.dialogId,
5448
5608
  status: entry.status,
@@ -5457,7 +5617,7 @@ class DialogPersistence {
5457
5617
  }
5458
5618
  const release = await mutex.acquire();
5459
5619
  try {
5460
- const entry = this.pendingSubdialogsWriteBack.get(key);
5620
+ const entry = this.pendingSideDialogsWriteBack.get(key);
5461
5621
  if (!entry)
5462
5622
  return;
5463
5623
  if (entry.kind !== 'flushing')
@@ -5465,13 +5625,13 @@ class DialogPersistence {
5465
5625
  if (entry.inFlight !== captured.inFlight)
5466
5626
  return;
5467
5627
  if (!entry.dirty) {
5468
- this.pendingSubdialogsWriteBack.delete(key);
5628
+ this.pendingSideDialogsWriteBack.delete(key);
5469
5629
  return;
5470
5630
  }
5471
5631
  const timer = setTimeout(() => {
5472
- void this.flushPendingSubdialogsWriteBack(key);
5473
- }, this.PENDING_SUBDIALOGS_WRITEBACK_WINDOW_MS);
5474
- this.pendingSubdialogsWriteBack.set(key, {
5632
+ void this.flushPendingSideDialogsWriteBack(key);
5633
+ }, this.PENDING_SIDE_DIALOGS_WRITEBACK_WINDOW_MS);
5634
+ this.pendingSideDialogsWriteBack.set(key, {
5475
5635
  kind: 'scheduled',
5476
5636
  dialogId: entry.dialogId,
5477
5637
  status: entry.status,
@@ -5483,12 +5643,12 @@ class DialogPersistence {
5483
5643
  release();
5484
5644
  }
5485
5645
  }
5486
- static async writePendingSubdialogsToDisk(rootDialogId, state, status, cancellationToken) {
5646
+ static async writePendingSideDialogsToDisk(mainDialogId, state, status, cancellationToken) {
5487
5647
  if (cancellationToken) {
5488
- this.assertRootDialogWriteBackNotCanceled(cancellationToken, 'writePendingSubdialogsToDisk:start');
5648
+ this.assertMainDialogWriteBackNotCanceled(cancellationToken, 'writePendingSideDialogsToDisk:start');
5489
5649
  }
5490
- const dialogPath = this.getDialogResponsesPath(rootDialogId, status);
5491
- const filePath = path.join(dialogPath, 'pending-subdialogs.json');
5650
+ const dialogPath = this.getDialogResponsesPath(mainDialogId, status);
5651
+ const filePath = path.join(dialogPath, 'pending-sideDialogs.json');
5492
5652
  if (state.kind === 'deleted') {
5493
5653
  await fs.promises.rm(filePath, { force: true });
5494
5654
  return;
@@ -5499,75 +5659,75 @@ class DialogPersistence {
5499
5659
  await fs.promises.writeFile(tempFile, jsonContent, 'utf-8');
5500
5660
  }
5501
5661
  catch (error) {
5502
- await this.rethrowWriteBackPathMissingAsCanceled(error, dialogPath, cancellationToken, 'writePendingSubdialogsToDisk:write-temp');
5662
+ await this.rethrowWriteBackPathMissingAsCanceled(error, dialogPath, cancellationToken, 'writePendingSideDialogsToDisk:write-temp');
5503
5663
  throw error;
5504
5664
  }
5505
5665
  await this.renameWithRetry(tempFile, filePath, 5, cancellationToken);
5506
5666
  }
5507
5667
  /**
5508
- * Get the path for storing subdialog responses (supports both root and subdialog parents).
5509
- * For Type C subdialogs created inside another subdialog, responses are stored at the parent's level.
5668
+ * Get the path for storing sideDialog responses (supports both main dialog and sideDialog tellaskers).
5669
+ * For Type C sideDialogs created inside another sideDialog, responses are stored at the parent's level.
5510
5670
  */
5511
5671
  static getDialogResponsesPath(dialogId, status = 'running') {
5512
- // Root dialogs store responses in their own directory.
5513
- // Subdialogs store responses in the parent's location (root or subdialog).
5672
+ // Main dialogs store responses in their own directory.
5673
+ // SideDialogs store responses in the tellasker's location (main dialog or sideDialog).
5514
5674
  if (dialogId.rootId === dialogId.selfId) {
5515
- // Root dialog: use root's directory
5516
- return this.getRootDialogPath(dialogId, status);
5675
+ // Main dialog: use root's directory
5676
+ return this.getMainDialogPath(dialogId, status);
5517
5677
  }
5518
- // Subdialog: store in parent's subdialogs directory
5519
- // The parent is always identified by rootId (could be root or parent subdialog)
5678
+ // SideDialog: store in parent's sideDialogs directory
5679
+ // The parent is always identified by rootId (could be root or parent sideDialog)
5520
5680
  const parentSelfId = dialogId.rootId;
5521
- const rootPath = this.getRootDialogPath(new dialog_1.DialogID(parentSelfId), status);
5522
- return path.join(rootPath, this.SUBDIALOGS_DIR, dialogId.selfId);
5681
+ const rootPath = this.getMainDialogPath(new dialog_1.DialogID(parentSelfId), status);
5682
+ return path.join(rootPath, this.SIDE_DIALOGS_DIR, dialogId.selfId);
5523
5683
  }
5524
5684
  /**
5525
- * Save subdialog responses for Type A supply mechanism.
5526
- * Tracks responses from completed subdialogs.
5685
+ * Save responses delivered back from completed sideDialogs.
5527
5686
  */
5528
- static async saveSubdialogResponses(rootDialogId, responses, rootAnchor, status = 'running') {
5687
+ static async saveSideDialogResponses(mainDialogId, responses, rootAnchor, status = 'running') {
5529
5688
  try {
5530
- const dialogPath = this.getDialogResponsesPath(rootDialogId, status);
5531
- const filePath = path.join(dialogPath, 'subdialog-responses.json');
5689
+ assertUniqueSideDialogResponseIds(responses, `save rootId=${mainDialogId.rootId}`);
5690
+ const dialogPath = this.getDialogResponsesPath(mainDialogId, status);
5691
+ const filePath = path.join(dialogPath, 'sideDialog-responses.json');
5532
5692
  // Atomic write operation
5533
5693
  const jsonContent = JSON.stringify(responses, null, 2);
5534
5694
  const tempFile = path.join(dialogPath, `.${path.basename(filePath)}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`);
5535
5695
  await fs.promises.writeFile(tempFile, jsonContent, 'utf-8');
5536
5696
  await this.renameWithRetry(tempFile, filePath);
5537
5697
  if (rootAnchor) {
5538
- await this.appendSubdialogResponsesReconciledRecord(rootDialogId, responses, rootAnchorWriteTarget(rootAnchor), status);
5698
+ await this.appendSideDialogResponsesReconciledRecord(mainDialogId, responses, rootAnchorWriteTarget(rootAnchor), status);
5539
5699
  }
5540
5700
  }
5541
5701
  catch (error) {
5542
- log_1.log.error(`Failed to save subdialog responses for dialog ${rootDialogId}:`, error);
5702
+ log_1.log.error(`Failed to save sideDialog responses for dialog ${mainDialogId}:`, error);
5543
5703
  throw error;
5544
5704
  }
5545
5705
  }
5546
5706
  /**
5547
- * Load subdialog responses for Type A supply mechanism.
5707
+ * Load responses delivered back from completed sideDialogs.
5548
5708
  */
5549
- static async loadSubdialogResponses(rootDialogId, status = 'running') {
5709
+ static async loadSideDialogResponses(mainDialogId, status = 'running') {
5550
5710
  try {
5551
- const dialogPath = this.getDialogResponsesPath(rootDialogId, status);
5552
- const filePath = path.join(dialogPath, 'subdialog-responses.json');
5553
- const inflightPath = path.join(dialogPath, 'subdialog-responses.processing.json');
5711
+ const dialogPath = this.getDialogResponsesPath(mainDialogId, status);
5712
+ const filePath = path.join(dialogPath, 'sideDialog-responses.json');
5713
+ const inflightPath = path.join(dialogPath, 'sideDialog-responses.processing.json');
5554
5714
  try {
5555
5715
  const results = [];
5556
5716
  const tryReadArray = async (p) => {
5557
5717
  try {
5558
5718
  const content = await readPersistenceTextFile({
5559
5719
  filePath: p,
5560
- source: 'subdialog_responses',
5720
+ source: 'sideDialog_responses',
5561
5721
  format: 'json',
5562
5722
  });
5563
5723
  const parsed = parsePersistenceJson({
5564
5724
  content,
5565
5725
  filePath: p,
5566
- source: 'subdialog_responses',
5726
+ source: 'sideDialog_responses',
5567
5727
  });
5568
5728
  if (!Array.isArray(parsed)) {
5569
5729
  throw buildInvalidPersistenceFileError({
5570
- source: 'subdialog_responses',
5730
+ source: 'sideDialog_responses',
5571
5731
  format: 'json',
5572
5732
  filePath: p,
5573
5733
  });
@@ -5584,21 +5744,17 @@ class DialogPersistence {
5584
5744
  const primary = await tryReadArray(filePath);
5585
5745
  const inflight = await tryReadArray(inflightPath);
5586
5746
  for (const item of [...primary, ...inflight]) {
5587
- if (!isSubdialogResponseRecord(item)) {
5747
+ if (!isSideDialogResponseRecord(item)) {
5588
5748
  throw buildInvalidPersistenceFileError({
5589
- source: 'subdialog_responses',
5749
+ source: 'sideDialog_responses',
5590
5750
  format: 'json',
5591
5751
  filePath,
5592
5752
  });
5593
5753
  }
5594
5754
  results.push(item);
5595
5755
  }
5596
- // Deduplicate by responseId (primary wins over inflight order is irrelevant)
5597
- const byId = new Map();
5598
- for (const r of results) {
5599
- byId.set(r.responseId, r);
5600
- }
5601
- return Array.from(byId.values());
5756
+ assertUniqueSideDialogResponseIds(results, `load rootId=${mainDialogId.rootId} status=${status}`);
5757
+ return results;
5602
5758
  }
5603
5759
  catch (error) {
5604
5760
  if (getErrorCode(error) === 'ENOENT') {
@@ -5608,51 +5764,52 @@ class DialogPersistence {
5608
5764
  }
5609
5765
  }
5610
5766
  catch (error) {
5611
- await this.rethrowAfterQuarantiningDialogPersistenceProblem(rootDialogId, status, 'loadSubdialogResponses', error);
5612
- throw new Error('unreachable after loadSubdialogResponses persistence rethrow');
5767
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(mainDialogId, status, 'loadSideDialogResponses', error);
5768
+ throw new Error('unreachable after loadSideDialogResponses persistence rethrow');
5613
5769
  }
5614
5770
  }
5615
- static async loadSubdialogResponsesQueue(dialogId, status = 'running') {
5771
+ static async loadSideDialogResponsesQueue(dialogId, status = 'running') {
5616
5772
  try {
5617
5773
  const dialogPath = this.getDialogResponsesPath(dialogId, status);
5618
- const filePath = path.join(dialogPath, 'subdialog-responses.json');
5774
+ const filePath = path.join(dialogPath, 'sideDialog-responses.json');
5619
5775
  const content = await readPersistenceTextFile({
5620
5776
  filePath,
5621
- source: 'subdialog_responses',
5777
+ source: 'sideDialog_responses',
5622
5778
  format: 'json',
5623
5779
  });
5624
5780
  const parsed = parsePersistenceJson({
5625
5781
  content,
5626
5782
  filePath,
5627
- source: 'subdialog_responses',
5783
+ source: 'sideDialog_responses',
5628
5784
  });
5629
- if (!Array.isArray(parsed) || !parsed.every((item) => isSubdialogResponseRecord(item))) {
5785
+ if (!Array.isArray(parsed) || !parsed.every((item) => isSideDialogResponseRecord(item))) {
5630
5786
  throw buildInvalidPersistenceFileError({
5631
- source: 'subdialog_responses',
5787
+ source: 'sideDialog_responses',
5632
5788
  format: 'json',
5633
5789
  filePath,
5634
5790
  });
5635
5791
  }
5792
+ assertUniqueSideDialogResponseIds(parsed, `load queue rootId=${dialogId.rootId} selfId=${dialogId.selfId} status=${status}`);
5636
5793
  return parsed;
5637
5794
  }
5638
5795
  catch (error) {
5639
5796
  if (getErrorCode(error) === 'ENOENT') {
5640
5797
  return [];
5641
5798
  }
5642
- await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'loadSubdialogResponsesQueue', error);
5643
- throw new Error('unreachable after loadSubdialogResponsesQueue persistence rethrow');
5799
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'loadSideDialogResponsesQueue', error);
5800
+ throw new Error('unreachable after loadSideDialogResponsesQueue persistence rethrow');
5644
5801
  }
5645
5802
  }
5646
- static async appendSubdialogResponse(dialogId, response, rootAnchor, status = 'running') {
5647
- const existing = await this.loadSubdialogResponsesQueue(dialogId, status);
5803
+ static async appendSideDialogResponse(dialogId, response, rootAnchor, status = 'running') {
5804
+ const existing = await this.loadSideDialogResponsesQueue(dialogId, status);
5648
5805
  existing.push(response);
5649
- await this.saveSubdialogResponses(dialogId, existing, rootAnchor, status);
5806
+ await this.saveSideDialogResponses(dialogId, existing, rootAnchor, status);
5650
5807
  }
5651
- static async takeSubdialogResponses(dialogId, status = 'running') {
5808
+ static async takeSideDialogResponses(dialogId, status = 'running') {
5652
5809
  try {
5653
5810
  const dialogPath = this.getDialogResponsesPath(dialogId, status);
5654
- const filePath = path.join(dialogPath, 'subdialog-responses.json');
5655
- const inflightPath = path.join(dialogPath, 'subdialog-responses.processing.json');
5811
+ const filePath = path.join(dialogPath, 'sideDialog-responses.json');
5812
+ const inflightPath = path.join(dialogPath, 'sideDialog-responses.processing.json');
5656
5813
  // If a previous processing file exists, merge it back so it will be re-processed.
5657
5814
  try {
5658
5815
  await fs.promises.access(inflightPath);
@@ -5663,7 +5820,7 @@ class DialogPersistence {
5663
5820
  }
5664
5821
  }
5665
5822
  if (await this.pathExists(inflightPath)) {
5666
- await this.rollbackTakenSubdialogResponses(dialogId, status);
5823
+ await this.rollbackTakenSideDialogResponses(dialogId, status);
5667
5824
  }
5668
5825
  try {
5669
5826
  await fs.promises.rename(filePath, inflightPath);
@@ -5676,56 +5833,57 @@ class DialogPersistence {
5676
5833
  }
5677
5834
  const raw = await readPersistenceTextFile({
5678
5835
  filePath: inflightPath,
5679
- source: 'subdialog_responses',
5836
+ source: 'sideDialog_responses',
5680
5837
  format: 'json',
5681
5838
  });
5682
5839
  const parsed = parsePersistenceJson({
5683
5840
  content: raw,
5684
5841
  filePath: inflightPath,
5685
- source: 'subdialog_responses',
5842
+ source: 'sideDialog_responses',
5686
5843
  });
5687
- if (!Array.isArray(parsed) || !parsed.every((item) => isSubdialogResponseRecord(item))) {
5844
+ if (!Array.isArray(parsed) || !parsed.every((item) => isSideDialogResponseRecord(item))) {
5688
5845
  throw buildInvalidPersistenceFileError({
5689
- source: 'subdialog_responses',
5846
+ source: 'sideDialog_responses',
5690
5847
  format: 'json',
5691
5848
  filePath: inflightPath,
5692
5849
  });
5693
5850
  }
5851
+ assertUniqueSideDialogResponseIds(parsed, `take rootId=${dialogId.rootId} selfId=${dialogId.selfId} status=${status}`);
5694
5852
  return parsed;
5695
5853
  }
5696
5854
  catch (error) {
5697
5855
  if (getErrorCode(error) === 'ENOENT') {
5698
5856
  return [];
5699
5857
  }
5700
- await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'takeSubdialogResponses', error);
5701
- throw new Error('unreachable after takeSubdialogResponses persistence rethrow');
5858
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'takeSideDialogResponses', error);
5859
+ throw new Error('unreachable after takeSideDialogResponses persistence rethrow');
5702
5860
  }
5703
5861
  }
5704
- static async commitTakenSubdialogResponses(dialogId, status = 'running') {
5862
+ static async commitTakenSideDialogResponses(dialogId, status = 'running') {
5705
5863
  const dialogPath = this.getDialogResponsesPath(dialogId, status);
5706
- const inflightPath = path.join(dialogPath, 'subdialog-responses.processing.json');
5864
+ const inflightPath = path.join(dialogPath, 'sideDialog-responses.processing.json');
5707
5865
  await fs.promises.rm(inflightPath, { force: true });
5708
5866
  }
5709
- static async rollbackTakenSubdialogResponses(dialogId, status = 'running') {
5867
+ static async rollbackTakenSideDialogResponses(dialogId, status = 'running') {
5710
5868
  try {
5711
5869
  const dialogPath = this.getDialogResponsesPath(dialogId, status);
5712
- const filePath = path.join(dialogPath, 'subdialog-responses.json');
5713
- const inflightPath = path.join(dialogPath, 'subdialog-responses.processing.json');
5870
+ const filePath = path.join(dialogPath, 'sideDialog-responses.json');
5871
+ const inflightPath = path.join(dialogPath, 'sideDialog-responses.processing.json');
5714
5872
  let inflight = [];
5715
5873
  try {
5716
5874
  const raw = await readPersistenceTextFile({
5717
5875
  filePath: inflightPath,
5718
- source: 'subdialog_responses',
5876
+ source: 'sideDialog_responses',
5719
5877
  format: 'json',
5720
5878
  });
5721
5879
  const parsed = parsePersistenceJson({
5722
5880
  content: raw,
5723
5881
  filePath: inflightPath,
5724
- source: 'subdialog_responses',
5882
+ source: 'sideDialog_responses',
5725
5883
  });
5726
- if (!Array.isArray(parsed) || !parsed.every((item) => isSubdialogResponseRecord(item))) {
5884
+ if (!Array.isArray(parsed) || !parsed.every((item) => isSideDialogResponseRecord(item))) {
5727
5885
  throw buildInvalidPersistenceFileError({
5728
- source: 'subdialog_responses',
5886
+ source: 'sideDialog_responses',
5729
5887
  format: 'json',
5730
5888
  filePath: inflightPath,
5731
5889
  });
@@ -5742,17 +5900,17 @@ class DialogPersistence {
5742
5900
  try {
5743
5901
  const raw = await readPersistenceTextFile({
5744
5902
  filePath,
5745
- source: 'subdialog_responses',
5903
+ source: 'sideDialog_responses',
5746
5904
  format: 'json',
5747
5905
  });
5748
5906
  const parsed = parsePersistenceJson({
5749
5907
  content: raw,
5750
5908
  filePath,
5751
- source: 'subdialog_responses',
5909
+ source: 'sideDialog_responses',
5752
5910
  });
5753
- if (!Array.isArray(parsed) || !parsed.every((item) => isSubdialogResponseRecord(item))) {
5911
+ if (!Array.isArray(parsed) || !parsed.every((item) => isSideDialogResponseRecord(item))) {
5754
5912
  throw buildInvalidPersistenceFileError({
5755
- source: 'subdialog_responses',
5913
+ source: 'sideDialog_responses',
5756
5914
  format: 'json',
5757
5915
  filePath,
5758
5916
  });
@@ -5764,12 +5922,8 @@ class DialogPersistence {
5764
5922
  throw error;
5765
5923
  }
5766
5924
  }
5767
- const merged = [...inflight, ...primary];
5768
- const byId = new Map();
5769
- for (const r of merged) {
5770
- byId.set(r.responseId, r);
5771
- }
5772
- const result = Array.from(byId.values());
5925
+ const result = [...inflight, ...primary];
5926
+ assertUniqueSideDialogResponseIds(result, `rollback rootId=${dialogId.rootId} selfId=${dialogId.selfId} status=${status}`);
5773
5927
  const jsonContent = JSON.stringify(result, null, 2);
5774
5928
  const tempFile = path.join(dialogPath, `.${path.basename(filePath)}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`);
5775
5929
  await fs.promises.writeFile(tempFile, jsonContent, 'utf-8');
@@ -5777,16 +5931,25 @@ class DialogPersistence {
5777
5931
  await fs.promises.rm(inflightPath, { force: true });
5778
5932
  }
5779
5933
  catch (error) {
5780
- await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'rollbackTakenSubdialogResponses', error);
5781
- throw new Error('unreachable after rollbackTakenSubdialogResponses persistence rethrow');
5934
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'rollbackTakenSideDialogResponses', error);
5935
+ throw new Error('unreachable after rollbackTakenSideDialogResponses persistence rethrow');
5782
5936
  }
5783
5937
  }
5784
5938
  /**
5785
- * Save root dialog metadata (write-once pattern)
5939
+ * Save main dialog metadata (write-once pattern)
5786
5940
  */
5787
- static async saveRootDialogMetadata(dialogId, metadata, status = 'running') {
5941
+ static async saveMainDialogMetadata(dialogId, metadata, status = 'running') {
5788
5942
  try {
5789
- const dialogPath = this.getRootDialogPath(dialogId, status);
5943
+ if (dialogId.rootId !== dialogId.selfId) {
5944
+ throw new Error(`saveMainDialogMetadata expects a main dialog id: ${dialogId.valueOf()}`);
5945
+ }
5946
+ if (!isMainDialogMetadataFile(metadata)) {
5947
+ throw new Error(`Invalid main dialog metadata for ${dialogId.selfId}`);
5948
+ }
5949
+ if (metadata.id !== dialogId.selfId) {
5950
+ throw new Error(`Main dialog metadata id mismatch: dialogId=${dialogId.selfId} metadataId=${metadata.id}`);
5951
+ }
5952
+ const dialogPath = this.getMainDialogPath(dialogId, status);
5790
5953
  // Ensure dialog directory exists first
5791
5954
  await fs.promises.mkdir(dialogPath, { recursive: true });
5792
5955
  // Atomic write operation
@@ -5806,30 +5969,32 @@ class DialogPersistence {
5806
5969
  */
5807
5970
  static async saveDialogMetadata(dialogId, metadata, status = 'running') {
5808
5971
  if (dialogId.rootId === dialogId.selfId) {
5809
- if (!isRootDialogMetadataFile(metadata)) {
5810
- throw new Error(`Expected root dialog metadata for ${dialogId.selfId}`);
5972
+ if (!isMainDialogMetadataFile(metadata)) {
5973
+ throw new Error(`Expected main dialog metadata for ${dialogId.selfId}`);
5811
5974
  }
5812
- return this.saveRootDialogMetadata(dialogId, metadata, status);
5975
+ return this.saveMainDialogMetadata(dialogId, metadata, status);
5813
5976
  }
5814
- // For subdialogs, delegate to saveSubdialogMetadata
5815
- if (!isSubdialogMetadataFile(metadata)) {
5816
- throw new Error(`Expected subdialog metadata for ${dialogId.selfId}`);
5977
+ // For sideDialogs, delegate to saveSideDialogMetadata
5978
+ if (!isSideDialogMetadataFile(metadata)) {
5979
+ throw new Error(`Expected sideDialog metadata for ${dialogId.selfId}`);
5817
5980
  }
5818
- return this.saveSubdialogMetadata(dialogId, metadata, status);
5819
- }
5820
- /**
5821
- * Save dialog metadata (legacy - use saveRootDialogMetadata instead)
5822
- * @deprecated
5823
- */
5824
- static async _saveDialogMetadata(dialogId, metadata, status = 'running') {
5825
- return this.saveRootDialogMetadata(dialogId, metadata, status);
5981
+ return this.saveSideDialogMetadata(dialogId, metadata, status);
5826
5982
  }
5827
5983
  /**
5828
- * Save subdialog metadata under the supdialog's .subdialogs directory
5984
+ * Save sideDialog metadata under the root dialog's sideDialogs directory.
5829
5985
  */
5830
- static async saveSubdialogMetadata(dialogId, metadata, status = 'running') {
5986
+ static async saveSideDialogMetadata(dialogId, metadata, status = 'running') {
5831
5987
  try {
5832
- const subPath = this.getSubdialogPath(dialogId, status);
5988
+ if (dialogId.rootId === dialogId.selfId) {
5989
+ throw new Error(`saveSideDialogMetadata expects a sideDialog id: ${dialogId.valueOf()}`);
5990
+ }
5991
+ if (!isSideDialogMetadataFile(metadata)) {
5992
+ throw new Error(`Invalid sideDialog metadata for ${dialogId.selfId}`);
5993
+ }
5994
+ if (metadata.id !== dialogId.selfId) {
5995
+ throw new Error(`sideDialog metadata id mismatch: dialogId=${dialogId.selfId} metadataId=${metadata.id}`);
5996
+ }
5997
+ const subPath = this.getSideDialogPath(dialogId, status);
5833
5998
  const metadataFilePath = path.join(subPath, 'dialog.yaml');
5834
5999
  // Creation sites must ensure the directory exists first. Update paths intentionally do not
5835
6000
  // recreate missing directories so quarantine can use "directory disappeared" as cancellation.
@@ -5839,33 +6004,259 @@ class DialogPersistence {
5839
6004
  await this.renameWithRetry(tempFile, metadataFilePath);
5840
6005
  }
5841
6006
  catch (error) {
5842
- log_1.log.error(`Failed to save subdialog YAML for ${dialogId.selfId} under root dialog ${dialogId.rootId}:`, error);
6007
+ log_1.log.error(`Failed to save sideDialog YAML for ${dialogId.selfId} under main dialog ${dialogId.rootId}:`, error);
5843
6008
  throw error;
5844
6009
  }
5845
6010
  }
6011
+ static async saveSideDialogAskerStackState(dialogId, state, status = 'running') {
6012
+ if (dialogId.rootId === dialogId.selfId) {
6013
+ throw new Error('saveSideDialogAskerStackState expects a sideDialog id');
6014
+ }
6015
+ if (!isDialogAskerStackState(state)) {
6016
+ throw new Error(`Invalid asker stack for dialog ${dialogId.selfId}`);
6017
+ }
6018
+ await this.saveDialogAskerStack(dialogId, state, status);
6019
+ }
6020
+ static async loadSideDialogAskerStackState(dialogId, status = 'running') {
6021
+ if (dialogId.rootId === dialogId.selfId) {
6022
+ return null;
6023
+ }
6024
+ const stack = await this.loadDialogAskerStack(dialogId, status);
6025
+ return stack.askerStack.length === 0 ? null : stack;
6026
+ }
6027
+ static async loadSideDialogAssignmentFromAsker(dialogId, status = 'running') {
6028
+ if (dialogId.rootId === dialogId.selfId) {
6029
+ throw new Error('loadSideDialogAssignmentFromAsker expects a sideDialog id');
6030
+ }
6031
+ const stack = await this.loadSideDialogAskerStackState(dialogId, status);
6032
+ if (!stack) {
6033
+ throw new Error(`Missing asker stack for sideDialog ${dialogId.selfId}`);
6034
+ }
6035
+ return getDialogAskerStackCurrentAssignment(stack);
6036
+ }
6037
+ static parseDialogAskerStackJsonlRows(args) {
6038
+ const askerStack = [];
6039
+ const rows = [];
6040
+ const rawLines = args.content.split(/(?<=\n)/u);
6041
+ let byteOffset = 0;
6042
+ for (let index = 0; index < rawLines.length; index += 1) {
6043
+ const rawLine = rawLines[index];
6044
+ const line = rawLine.endsWith('\n') ? rawLine.slice(0, -1) : rawLine;
6045
+ const startOffset = byteOffset;
6046
+ const endOffset = startOffset + Buffer.byteLength(rawLine, 'utf-8');
6047
+ byteOffset = endOffset;
6048
+ if (line.trim() === '')
6049
+ continue;
6050
+ const parsed = parsePersistenceJson({
6051
+ content: line,
6052
+ filePath: args.filePath,
6053
+ source: 'dialog_asker_stack',
6054
+ lineNumber: index + 1,
6055
+ });
6056
+ if (!isAskerDialogStackFrame(parsed)) {
6057
+ throw buildInvalidPersistenceFileError({
6058
+ source: 'dialog_asker_stack',
6059
+ format: 'jsonl',
6060
+ filePath: args.filePath,
6061
+ lineNumber: index + 1,
6062
+ });
6063
+ }
6064
+ askerStack.push(parsed);
6065
+ rows.push({
6066
+ frame: parsed,
6067
+ startOffset,
6068
+ endOffset,
6069
+ });
6070
+ }
6071
+ return { state: { askerStack }, rows };
6072
+ }
6073
+ static async appendDialogAskerStackFrames(dialogId, frames, status) {
6074
+ if (frames.length === 0)
6075
+ return;
6076
+ const filePath = this.getDialogAskerStackPath(dialogId, status);
6077
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
6078
+ const content = frames.map((frame) => `${JSON.stringify(frame)}\n`).join('');
6079
+ await fs.promises.appendFile(filePath, content, 'utf-8');
6080
+ }
6081
+ static async saveDialogAskerStack(dialogId, state, status = 'running') {
6082
+ if (!isDialogAskerStackState(state)) {
6083
+ throw new Error(`Invalid asker stack for dialog ${dialogId.selfId}`);
6084
+ }
6085
+ const filePath = this.getDialogAskerStackPath(dialogId, status);
6086
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
6087
+ await fs.promises.truncate(filePath, 0).catch(async (error) => {
6088
+ if (getErrorCode(error) === 'ENOENT') {
6089
+ await fs.promises.writeFile(filePath, '', 'utf-8');
6090
+ return;
6091
+ }
6092
+ throw error;
6093
+ });
6094
+ await this.appendDialogAskerStackFrames(dialogId, state.askerStack, status);
6095
+ }
6096
+ static async loadDialogAskerStack(dialogId, status = 'running') {
6097
+ return (await this.loadDialogAskerStackRows(dialogId, status)).state;
6098
+ }
6099
+ static async loadDialogAskerStackRows(dialogId, status) {
6100
+ const filePath = this.getDialogAskerStackPath(dialogId, status);
6101
+ try {
6102
+ const content = await readPersistenceTextFile({
6103
+ filePath,
6104
+ source: 'dialog_asker_stack',
6105
+ format: 'jsonl',
6106
+ });
6107
+ return { filePath, ...this.parseDialogAskerStackJsonlRows({ content, filePath }) };
6108
+ }
6109
+ catch (error) {
6110
+ if (getErrorCode(error) === 'ENOENT') {
6111
+ return { filePath, state: { askerStack: [] }, rows: [] };
6112
+ }
6113
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'loadDialogAskerStack', error);
6114
+ throw new Error('unreachable after loadDialogAskerStack persistence rethrow');
6115
+ }
6116
+ }
6117
+ static async truncateDialogAskerStackToDepth(dialogId, depth, status) {
6118
+ const loaded = await this.loadDialogAskerStackRows(dialogId, status);
6119
+ if (!Number.isInteger(depth) || depth < 0 || depth > loaded.rows.length) {
6120
+ throw new Error(`asker stack truncate invariant violation: invalid depth ` +
6121
+ `(rootId=${dialogId.rootId}, selfId=${dialogId.selfId}, depth=${String(depth)}, size=${String(loaded.rows.length)})`);
6122
+ }
6123
+ const truncateOffset = depth === loaded.rows.length
6124
+ ? (loaded.rows[loaded.rows.length - 1]?.endOffset ?? 0)
6125
+ : (loaded.rows[depth]?.startOffset ?? 0);
6126
+ await fs.promises.mkdir(path.dirname(loaded.filePath), { recursive: true });
6127
+ await fs.promises.truncate(loaded.filePath, truncateOffset).catch(async (error) => {
6128
+ if (getErrorCode(error) === 'ENOENT' && truncateOffset === 0) {
6129
+ await fs.promises.writeFile(loaded.filePath, '', 'utf-8');
6130
+ return;
6131
+ }
6132
+ throw error;
6133
+ });
6134
+ return { askerStack: loaded.rows.slice(0, depth).map((row) => row.frame) };
6135
+ }
6136
+ static async replaceDialogAskerStackFrameAndAppend(args) {
6137
+ const loaded = await this.loadDialogAskerStackRows(args.dialogId, args.status);
6138
+ const matchingIndexes = loaded.rows
6139
+ .map((row, index) => (args.findFrame(row.frame) ? index : -1))
6140
+ .filter((index) => index >= 0);
6141
+ if (matchingIndexes.length === 0) {
6142
+ throw new Error(args.missingFrameMessage);
6143
+ }
6144
+ if (matchingIndexes.length > 1) {
6145
+ throw new Error(`replace pending asker stack invariant violation: duplicate old frames ` +
6146
+ `(rootId=${args.dialogId.rootId}, selfId=${args.dialogId.selfId}, matches=${String(matchingIndexes.length)})`);
6147
+ }
6148
+ const replaceIndex = matchingIndexes[0];
6149
+ const replacedRow = loaded.rows[replaceIndex];
6150
+ await fs.promises.truncate(loaded.filePath, replacedRow.startOffset);
6151
+ const retainedBefore = loaded.rows.slice(0, replaceIndex).map((row) => row.frame);
6152
+ const retainedAfter = loaded.rows.slice(replaceIndex + 1).map((row) => row.frame);
6153
+ await this.appendDialogAskerStackFrames(args.dialogId, [args.appendFrame, ...retainedAfter], args.status);
6154
+ return {
6155
+ askerStack: [...retainedBefore, args.appendFrame, ...retainedAfter],
6156
+ };
6157
+ }
6158
+ static async pushTellaskReplyObligation(dialogId, obligation, status = 'running') {
6159
+ const frame = {
6160
+ kind: 'asker_dialog_stack_frame',
6161
+ askerDialogId: obligation.targetDialogId,
6162
+ tellaskReplyObligation: obligation,
6163
+ };
6164
+ if (dialogId.rootId === dialogId.selfId) {
6165
+ await this.appendDialogAskerStackFrames(dialogId, [frame], status);
6166
+ return;
6167
+ }
6168
+ const state = await this.loadSideDialogAskerStackState(dialogId, status);
6169
+ if (!state) {
6170
+ throw new Error(`Missing asker stack for sideDialog ${dialogId.selfId}`);
6171
+ }
6172
+ await this.appendDialogAskerStackFrames(dialogId, [frame], status);
6173
+ }
6174
+ static async setActiveTellaskReplyObligation(dialogId, obligation, status = 'running') {
6175
+ if (obligation !== undefined) {
6176
+ await this.pushTellaskReplyObligation(dialogId, obligation, status);
6177
+ return;
6178
+ }
6179
+ if (dialogId.rootId === dialogId.selfId) {
6180
+ const stackFile = await this.loadDialogAskerStack(dialogId, status);
6181
+ if (stackFile.askerStack.length === 0)
6182
+ return;
6183
+ await this.truncateDialogAskerStackToDepth(dialogId, stackFile.askerStack.length - 1, status);
6184
+ return;
6185
+ }
6186
+ const state = await this.loadSideDialogAskerStackState(dialogId, status);
6187
+ if (!state) {
6188
+ throw new Error(`Missing asker stack for sideDialog ${dialogId.selfId}`);
6189
+ }
6190
+ if (state.askerStack.length > 1) {
6191
+ await this.truncateDialogAskerStackToDepth(dialogId, state.askerStack.length - 1, status);
6192
+ return;
6193
+ }
6194
+ const top = getDialogAskerStackTop(state);
6195
+ await this.truncateDialogAskerStackToDepth(dialogId, 0, status);
6196
+ if (top.assignmentFromAsker === undefined) {
6197
+ return;
6198
+ }
6199
+ await this.appendDialogAskerStackFrames(dialogId, [
6200
+ {
6201
+ kind: 'asker_dialog_stack_frame',
6202
+ askerDialogId: top.askerDialogId,
6203
+ assignmentFromAsker: top.assignmentFromAsker,
6204
+ },
6205
+ ], status);
6206
+ }
6207
+ static async loadActiveTellaskReplyObligation(dialogId, status = 'running') {
6208
+ if (dialogId.rootId === dialogId.selfId) {
6209
+ const stack = (await this.loadDialogAskerStack(dialogId, status)).askerStack;
6210
+ return stack[stack.length - 1]?.tellaskReplyObligation;
6211
+ }
6212
+ return getDialogAskerStackTop(await this.requireSideDialogAskerStackState(dialogId, status))
6213
+ .tellaskReplyObligation;
6214
+ }
6215
+ static async requireSideDialogAskerStackState(dialogId, status) {
6216
+ const state = await this.loadSideDialogAskerStackState(dialogId, status);
6217
+ if (!state) {
6218
+ throw new Error(`Missing asker stack for sideDialog ${dialogId.selfId}`);
6219
+ }
6220
+ return state;
6221
+ }
5846
6222
  /**
5847
- * Update assignmentFromSup for an existing subdialog.
6223
+ * Update assignmentFromAsker for an existing sideDialog.
5848
6224
  */
5849
- static async updateSubdialogAssignment(dialogId, assignment, status = 'running') {
6225
+ static async updateSideDialogAssignment(dialogId, assignment, status = 'running', options) {
5850
6226
  if (dialogId.rootId === dialogId.selfId) {
5851
- throw new Error('updateSubdialogAssignment expects a subdialog id');
6227
+ throw new Error('updateSideDialogAssignment expects a sideDialog id');
5852
6228
  }
5853
6229
  const metadata = await this.loadDialogMetadata(dialogId, status);
5854
- if (!metadata || !isSubdialogMetadataFile(metadata)) {
5855
- throw new Error(`Missing dialog metadata for subdialog ${dialogId.selfId}`);
6230
+ if (!metadata || !isSideDialogMetadataFile(metadata)) {
6231
+ throw new Error(`Missing dialog metadata for sideDialog ${dialogId.selfId}`);
6232
+ }
6233
+ const nextAssignmentFrame = buildAssignmentAskerStackFrame({
6234
+ askerDialogId: assignment.askerDialogId,
6235
+ assignment,
6236
+ });
6237
+ if (options?.replacePendingCallId === undefined) {
6238
+ await this.requireSideDialogAskerStackState(dialogId, status);
6239
+ await this.appendDialogAskerStackFrames(dialogId, [nextAssignmentFrame], status);
6240
+ }
6241
+ else {
6242
+ const replacePendingAskerDialogId = options.replacePendingAskerDialogId ?? assignment.askerDialogId;
6243
+ await this.replaceDialogAskerStackFrameAndAppend({
6244
+ dialogId,
6245
+ status,
6246
+ findFrame: (frame) => frame.assignmentFromAsker?.askerDialogId === replacePendingAskerDialogId &&
6247
+ frame.assignmentFromAsker.callId === options.replacePendingCallId,
6248
+ missingFrameMessage: `replace pending asker stack invariant violation: missing old frame ` +
6249
+ `(rootId=${dialogId.rootId}, selfId=${dialogId.selfId}, askerDialogId=${replacePendingAskerDialogId}, callId=${options.replacePendingCallId})`,
6250
+ appendFrame: nextAssignmentFrame,
6251
+ });
5856
6252
  }
5857
- const next = {
5858
- ...metadata,
5859
- assignmentFromSup: assignment,
5860
- };
5861
- await this.saveSubdialogMetadata(dialogId, next, status);
5862
6253
  }
5863
6254
  /**
5864
- * Load root dialog metadata
6255
+ * Load main dialog metadata
5865
6256
  */
5866
- static async loadRootDialogMetadata(dialogId, status = 'running') {
6257
+ static async loadMainDialogMetadata(dialogId, status = 'running') {
5867
6258
  try {
5868
- const dialogPath = this.getRootDialogPath(dialogId, status);
6259
+ const dialogPath = this.getMainDialogPath(dialogId, status);
5869
6260
  const metadataFilePath = path.join(dialogPath, 'dialog.yaml');
5870
6261
  try {
5871
6262
  const content = await readPersistenceTextFile({
@@ -5878,7 +6269,7 @@ class DialogPersistence {
5878
6269
  filePath: metadataFilePath,
5879
6270
  source: 'dialog_metadata',
5880
6271
  });
5881
- if (!isDialogMetadataFile(parsed)) {
6272
+ if (!isMainDialogMetadataFile(parsed)) {
5882
6273
  throw buildInvalidPersistenceFileError({
5883
6274
  source: 'dialog_metadata',
5884
6275
  format: 'yaml',
@@ -5899,8 +6290,8 @@ class DialogPersistence {
5899
6290
  if (getErrorCode(error) === 'ENOENT') {
5900
6291
  return null;
5901
6292
  }
5902
- await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'loadRootDialogMetadata', error);
5903
- throw new Error('unreachable after loadRootDialogMetadata persistence rethrow');
6293
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(dialogId, status, 'loadMainDialogMetadata', error);
6294
+ throw new Error('unreachable after loadMainDialogMetadata persistence rethrow');
5904
6295
  }
5905
6296
  }
5906
6297
  catch (error) {
@@ -5909,17 +6300,14 @@ class DialogPersistence {
5909
6300
  }
5910
6301
  }
5911
6302
  /**
5912
- * Load dialog metadata (universal - works with any DialogID)
6303
+ * Load dialog metadata from the path implied by the DialogID.
5913
6304
  */
5914
6305
  static async loadDialogMetadata(dialogId, status = 'running') {
5915
- // For root dialogs, use the selfId
5916
- // For subdialogs, this is more complex - we need to find the root metadata
5917
6306
  if (dialogId.rootId === dialogId.selfId) {
5918
- return this.loadRootDialogMetadata(dialogId, status);
6307
+ return this.loadMainDialogMetadata(dialogId, status);
5919
6308
  }
5920
- // For subdialogs, we need to load from the subdialog location
5921
- const subdialogPath = this.getSubdialogPath(dialogId, status);
5922
- const metadataFilePath = path.join(subdialogPath, 'dialog.yaml');
6309
+ const sideDialogPath = this.getSideDialogPath(dialogId, status);
6310
+ const metadataFilePath = path.join(sideDialogPath, 'dialog.yaml');
5923
6311
  try {
5924
6312
  const content = await readPersistenceTextFile({
5925
6313
  filePath: metadataFilePath,
@@ -5931,13 +6319,29 @@ class DialogPersistence {
5931
6319
  filePath: metadataFilePath,
5932
6320
  source: 'dialog_metadata',
5933
6321
  });
5934
- if (!isDialogMetadataFile(parsed)) {
6322
+ if (!isSideDialogMetadataFile(parsed)) {
5935
6323
  throw buildInvalidPersistenceFileError({
5936
6324
  source: 'dialog_metadata',
5937
6325
  format: 'yaml',
5938
6326
  filePath: metadataFilePath,
5939
6327
  });
5940
6328
  }
6329
+ if (parsed.id !== dialogId.selfId) {
6330
+ throw buildInvalidPersistenceFileError({
6331
+ source: 'dialog_metadata',
6332
+ format: 'yaml',
6333
+ filePath: metadataFilePath,
6334
+ });
6335
+ }
6336
+ const askerStack = await this.loadSideDialogAskerStackState(dialogId, status);
6337
+ if (!askerStack) {
6338
+ throw buildInvalidPersistenceFileError({
6339
+ source: 'dialog_asker_stack',
6340
+ format: 'jsonl',
6341
+ filePath: this.getDialogAskerStackPath(dialogId, status),
6342
+ });
6343
+ }
6344
+ getDialogAskerStackCurrentAssignment(askerStack);
5941
6345
  return parsed;
5942
6346
  }
5943
6347
  catch (error) {
@@ -5954,7 +6358,7 @@ class DialogPersistence {
5954
6358
  static async writeDialogLatestToDisk(dialogId, latest, status = 'running', cancellationToken) {
5955
6359
  try {
5956
6360
  if (cancellationToken) {
5957
- this.assertRootDialogWriteBackNotCanceled(cancellationToken, 'writeDialogLatestToDisk:start');
6361
+ this.assertMainDialogWriteBackNotCanceled(cancellationToken, 'writeDialogLatestToDisk:start');
5958
6362
  }
5959
6363
  const dialogPath = this.getDialogEventsPath(dialogId, status);
5960
6364
  const latestFilePath = path.join(dialogPath, 'latest.yaml');
@@ -5989,7 +6393,7 @@ class DialogPersistence {
5989
6393
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
5990
6394
  try {
5991
6395
  if (cancellationToken) {
5992
- this.assertRootDialogWriteBackNotCanceled(cancellationToken, `renameWithRetry:${path.basename(destination)}:before-rename`);
6396
+ this.assertMainDialogWriteBackNotCanceled(cancellationToken, `renameWithRetry:${path.basename(destination)}:before-rename`);
5993
6397
  }
5994
6398
  await fs.promises.rename(source, destination);
5995
6399
  return;
@@ -6045,10 +6449,10 @@ class DialogPersistence {
6045
6449
  static async mutateDialogLatest(dialogId, mutator, status = 'running', cancellationToken) {
6046
6450
  const key = this.getLatestWriteBackKey(dialogId, status);
6047
6451
  const mutex = this.getLatestWriteBackMutex(key);
6048
- const effectiveCancellationToken = cancellationToken ?? this.createRootDialogWriteBackCancellationToken(dialogId, status);
6452
+ const effectiveCancellationToken = cancellationToken ?? this.createMainDialogWriteBackCancellationToken(dialogId, status);
6049
6453
  const release = await mutex.acquire();
6050
6454
  try {
6051
- this.assertRootDialogWriteBackNotCanceled(effectiveCancellationToken, 'mutateDialogLatest');
6455
+ this.assertMainDialogWriteBackNotCanceled(effectiveCancellationToken, 'mutateDialogLatest');
6052
6456
  const staged = this.latestWriteBack.get(key);
6053
6457
  const latestFromDisk = staged ? null : await this.loadDialogLatestFromDisk(dialogId, status);
6054
6458
  const existing = (staged ? staged.latest : latestFromDisk) || {
@@ -6089,7 +6493,7 @@ class DialogPersistence {
6089
6493
  latestSource: staged ? 'staged' : latestFromDisk ? 'disk' : 'default_bootstrap',
6090
6494
  latestWriteBackKey: key,
6091
6495
  });
6092
- this.assertRootDialogWriteBackNotCanceled(effectiveCancellationToken, 'mutateDialogLatest:before-stage');
6496
+ this.assertMainDialogWriteBackNotCanceled(effectiveCancellationToken, 'mutateDialogLatest:before-stage');
6093
6497
  const pending = this.latestWriteBack.get(key);
6094
6498
  if (!pending) {
6095
6499
  const timer = setTimeout(() => {
@@ -6149,7 +6553,7 @@ class DialogPersistence {
6149
6553
  return;
6150
6554
  clearTimeout(entry.timer);
6151
6555
  const latestToWrite = entry.latest;
6152
- const cancellationToken = this.createRootDialogWriteBackCancellationToken(entry.dialogId, entry.status);
6556
+ const cancellationToken = this.createMainDialogWriteBackCancellationToken(entry.dialogId, entry.status);
6153
6557
  const inFlight = this.writeDialogLatestToDisk(entry.dialogId, latestToWrite, entry.status, cancellationToken);
6154
6558
  captured = {
6155
6559
  dialogId: entry.dialogId,
@@ -6340,18 +6744,18 @@ class DialogPersistence {
6340
6744
  };
6341
6745
  }
6342
6746
  /**
6343
- * Count subdialogs under a root dialog (no single-layer listing exposed)
6747
+ * Count sideDialogs under a main dialog (no single-layer listing exposed)
6344
6748
  */
6345
- static async countAllSubdialogsUnderRoot(rootDialogId, status = 'running') {
6346
- if (rootDialogId.rootId !== rootDialogId.selfId) {
6347
- throw new Error(`countAllSubdialogsUnderRoot invariant violation: expected root dialog id, got ${rootDialogId.valueOf()}`);
6749
+ static async countAllSideDialogsUnderRoot(mainDialogId, status = 'running') {
6750
+ if (mainDialogId.rootId !== mainDialogId.selfId) {
6751
+ throw new Error(`countAllSideDialogsUnderRoot invariant violation: expected main dialog id, got ${mainDialogId.valueOf()}`);
6348
6752
  }
6349
6753
  try {
6350
6754
  const dialogIds = await this.listAllDialogIds(status);
6351
- return dialogIds.filter((dialogId) => dialogId.rootId === rootDialogId.rootId && dialogId.selfId !== dialogId.rootId).length;
6755
+ return dialogIds.filter((dialogId) => dialogId.rootId === mainDialogId.rootId && dialogId.selfId !== dialogId.rootId).length;
6352
6756
  }
6353
6757
  catch (error) {
6354
- log_1.log.error(`Failed to count all subdialogs under root ${rootDialogId.selfId}:`, error);
6758
+ log_1.log.error(`Failed to count all sideDialogs under root ${mainDialogId.selfId}:`, error);
6355
6759
  throw error;
6356
6760
  }
6357
6761
  }
@@ -6359,22 +6763,22 @@ class DialogPersistence {
6359
6763
  /**
6360
6764
  * Restore complete dialog tree from disk
6361
6765
  */
6362
- static async restoreDialogTree(rootDialogId, status = 'running') {
6766
+ static async restoreDialogTree(mainDialogId, status = 'running') {
6363
6767
  try {
6364
- // First restore the root dialog
6365
- const rootState = await this.restoreDialog(rootDialogId, status);
6768
+ // First restore the main dialog
6769
+ const rootState = await this.restoreDialog(mainDialogId, status);
6366
6770
  if (!rootState) {
6367
6771
  return null;
6368
6772
  }
6369
- // Recursively restore subdialogs
6370
- const subdialogIds = await this.listSubdialogIdsUnderRoot(rootDialogId, status);
6371
- for (const subdialogId of subdialogIds) {
6372
- await this.restoreDialogTree(new dialog_1.DialogID(subdialogId, rootDialogId.rootId), status);
6773
+ // Recursively restore sideDialogs
6774
+ const sideDialogIds = await this.listSideDialogIdsUnderRoot(mainDialogId, status);
6775
+ for (const sideDialogId of sideDialogIds) {
6776
+ await this.restoreDialogTree(new dialog_1.DialogID(sideDialogId, mainDialogId.rootId), status);
6373
6777
  }
6374
6778
  return rootState;
6375
6779
  }
6376
6780
  catch (error) {
6377
- log_1.log.error(`Failed to restore dialog tree for ${rootDialogId.valueOf()}:`, error);
6781
+ log_1.log.error(`Failed to restore dialog tree for ${mainDialogId.valueOf()}:`, error);
6378
6782
  return null;
6379
6783
  }
6380
6784
  }
@@ -6532,8 +6936,8 @@ class DialogPersistence {
6532
6936
  status: event.status,
6533
6937
  content: event.content,
6534
6938
  contentItems: event.contentItems,
6535
- ...(event.originCourse !== undefined ? { originCourse: event.originCourse } : {}),
6536
- ...(event.calling_genseq !== undefined ? { calling_genseq: event.calling_genseq } : {}),
6939
+ ...(event.callSiteCourse !== undefined ? { callSiteCourse: event.callSiteCourse } : {}),
6940
+ ...(event.callSiteGenseq !== undefined ? { callSiteGenseq: event.callSiteGenseq } : {}),
6537
6941
  call: event.call,
6538
6942
  responder: event.responder,
6539
6943
  ...(event.route ? { route: event.route } : {}),
@@ -6564,7 +6968,7 @@ class DialogPersistence {
6564
6968
  genseq: event.genseq,
6565
6969
  content: event.content,
6566
6970
  contentItems: event.contentItems,
6567
- originCourse: event.originCourse,
6971
+ callSiteCourse: event.callSiteCourse,
6568
6972
  carryoverCourse: event.carryoverCourse,
6569
6973
  responderId: event.responderId,
6570
6974
  callName: event.callName,
@@ -6599,23 +7003,23 @@ class DialogPersistence {
6599
7003
  contextHealth = event.contextHealth;
6600
7004
  }
6601
7005
  break;
6602
- case 'quest_for_sup_record':
7006
+ case 'sideDialog_request_record':
6603
7007
  // These events are handled separately in dialog restoration
6604
7008
  // Skip them for message reconstruction
6605
7009
  break;
6606
7010
  case 'tellask_call_anchor_record':
6607
- // This record is UI navigation metadata for deep links in callee dialogs.
7011
+ // This record is UI navigation metadata for deep links in tellaskee dialogs.
6608
7012
  // It does not contribute to model context or chat transcript reconstruction.
6609
7013
  break;
6610
7014
  case 'ui_only_markdown_record':
6611
7015
  // UI-only records are replay-only rendering facts. They do not enter dialog messages or ctx.
6612
7016
  break;
6613
- case 'subdialog_created_record':
7017
+ case 'sideDialog_created_record':
6614
7018
  case 'reminders_reconciled_record':
6615
7019
  case 'questions4human_reconciled_record':
6616
- case 'pending_subdialogs_reconciled_record':
6617
- case 'subdialog_registry_reconciled_record':
6618
- case 'subdialog_responses_reconciled_record':
7020
+ case 'pending_sideDialogs_reconciled_record':
7021
+ case 'sideDialog_registry_reconciled_record':
7022
+ case 'sideDialog_responses_reconciled_record':
6619
7023
  break;
6620
7024
  default:
6621
7025
  log_1.log.warn(`Unknown event type in rebuildFromEvents`, undefined, { event });
@@ -6655,36 +7059,36 @@ class DialogPersistence {
6655
7059
  }
6656
7060
  }
6657
7061
  /**
6658
- * Delete a root dialog directory (including subdialogs) from disk.
7062
+ * Delete a main dialog directory (including sideDialogs) from disk.
6659
7063
  * Caller must provide the source status explicitly.
6660
7064
  */
6661
- static async deleteRootDialog(rootDialogId, fromStatus) {
6662
- if (rootDialogId.selfId !== rootDialogId.rootId) {
6663
- throw new Error('deleteRootDialog expects a root dialog id');
7065
+ static async deleteMainDialog(mainDialogId, fromStatus) {
7066
+ if (mainDialogId.selfId !== mainDialogId.rootId) {
7067
+ throw new Error('deleteMainDialog expects a main dialog id');
6664
7068
  }
6665
- const exists = await this.loadRootDialogMetadata(rootDialogId, fromStatus);
7069
+ const exists = await this.loadMainDialogMetadata(mainDialogId, fromStatus);
6666
7070
  if (!exists)
6667
7071
  return false;
6668
7072
  // Best-effort cleanup: remove the dialog from all status directories to avoid leaving behind
6669
7073
  // orphaned placeholder paths (e.g. `run/<id>/latest.yaml`) after a delete.
6670
7074
  for (const candidate of PERSISTABLE_DIALOG_STATUSES) {
6671
- this.cancelRootDialogWriteBacks(rootDialogId, candidate);
6672
- const candidatePath = this.getRootDialogPath(rootDialogId, candidate);
7075
+ this.cancelMainDialogWriteBacks(mainDialogId, candidate);
7076
+ const candidatePath = this.getMainDialogPath(mainDialogId, candidate);
6673
7077
  await fs.promises.rm(candidatePath, { recursive: true, force: true });
6674
7078
  }
6675
7079
  return true;
6676
7080
  }
6677
7081
  // === REGISTRY PERSISTENCE ===
6678
7082
  /**
6679
- * Save subdialog registry (TYPE B entries).
7083
+ * Save sideDialog registry (TYPE B entries).
6680
7084
  */
6681
- static async saveSubdialogRegistry(rootDialogId, entries, status = 'running') {
7085
+ static async saveSideDialogRegistry(mainDialogId, entries, status = 'running') {
6682
7086
  try {
6683
- const dialogPath = this.getDialogResponsesPath(rootDialogId, status);
7087
+ const dialogPath = this.getDialogResponsesPath(mainDialogId, status);
6684
7088
  const registryFilePath = path.join(dialogPath, 'registry.yaml');
6685
7089
  const serializableEntries = entries.map((entry) => ({
6686
7090
  key: entry.key,
6687
- subdialogId: entry.subdialogId.selfId,
7091
+ sideDialogId: entry.sideDialogId.selfId,
6688
7092
  agentId: entry.agentId,
6689
7093
  sessionSlug: entry.sessionSlug,
6690
7094
  }));
@@ -6694,30 +7098,30 @@ class DialogPersistence {
6694
7098
  await this.renameWithRetry(tempFile, registryFilePath);
6695
7099
  }
6696
7100
  catch (error) {
6697
- log_1.log.error(`Failed to save subdialog registry for dialog ${rootDialogId}:`, error);
7101
+ log_1.log.error(`Failed to save sideDialog registry for dialog ${mainDialogId}:`, error);
6698
7102
  throw error;
6699
7103
  }
6700
7104
  }
6701
7105
  /**
6702
- * Load subdialog registry.
7106
+ * Load sideDialog registry.
6703
7107
  */
6704
- static async loadSubdialogRegistry(rootDialogId, status = 'running') {
7108
+ static async loadSideDialogRegistry(mainDialogId, status = 'running') {
6705
7109
  try {
6706
- const dialogPath = this.getDialogResponsesPath(rootDialogId, status);
7110
+ const dialogPath = this.getDialogResponsesPath(mainDialogId, status);
6707
7111
  const registryFilePath = path.join(dialogPath, 'registry.yaml');
6708
7112
  const content = await readPersistenceTextFile({
6709
7113
  filePath: registryFilePath,
6710
- source: 'subdialog_registry',
7114
+ source: 'sideDialog_registry',
6711
7115
  format: 'yaml',
6712
7116
  });
6713
7117
  const parsed = parsePersistenceYaml({
6714
7118
  content,
6715
7119
  filePath: registryFilePath,
6716
- source: 'subdialog_registry',
7120
+ source: 'sideDialog_registry',
6717
7121
  });
6718
7122
  if (!isRecord(parsed) || !Array.isArray(parsed.entries)) {
6719
7123
  throw buildInvalidPersistenceFileError({
6720
- source: 'subdialog_registry',
7124
+ source: 'sideDialog_registry',
6721
7125
  format: 'yaml',
6722
7126
  filePath: registryFilePath,
6723
7127
  });
@@ -6725,18 +7129,18 @@ class DialogPersistence {
6725
7129
  const entries = parsed.entries.map((entry) => {
6726
7130
  if (!isRecord(entry) ||
6727
7131
  typeof entry.key !== 'string' ||
6728
- typeof entry.subdialogId !== 'string' ||
7132
+ typeof entry.sideDialogId !== 'string' ||
6729
7133
  typeof entry.agentId !== 'string' ||
6730
7134
  (entry.sessionSlug !== undefined && typeof entry.sessionSlug !== 'string')) {
6731
7135
  throw buildInvalidPersistenceFileError({
6732
- source: 'subdialog_registry',
7136
+ source: 'sideDialog_registry',
6733
7137
  format: 'yaml',
6734
7138
  filePath: registryFilePath,
6735
7139
  });
6736
7140
  }
6737
7141
  return {
6738
7142
  key: entry.key,
6739
- subdialogId: new dialog_1.DialogID(entry.subdialogId, rootDialogId.rootId),
7143
+ sideDialogId: new dialog_1.DialogID(entry.sideDialogId, mainDialogId.rootId),
6740
7144
  agentId: entry.agentId,
6741
7145
  sessionSlug: entry.sessionSlug,
6742
7146
  };
@@ -6747,8 +7151,8 @@ class DialogPersistence {
6747
7151
  if (getErrorCode(error) === 'ENOENT') {
6748
7152
  return [];
6749
7153
  }
6750
- await this.rethrowAfterQuarantiningDialogPersistenceProblem(rootDialogId, status, 'loadSubdialogRegistry', error);
6751
- throw new Error('unreachable after loadSubdialogRegistry persistence rethrow');
7154
+ await this.rethrowAfterQuarantiningDialogPersistenceProblem(mainDialogId, status, 'loadSideDialogRegistry', error);
7155
+ throw new Error('unreachable after loadSideDialogRegistry persistence rethrow');
6752
7156
  }
6753
7157
  }
6754
7158
  }
@@ -6758,16 +7162,16 @@ DialogPersistence.MALFORMED_DIR = 'malformed';
6758
7162
  DialogPersistence.RUN_DIR = 'run';
6759
7163
  DialogPersistence.DONE_DIR = 'done';
6760
7164
  DialogPersistence.ARCHIVE_DIR = 'archive';
6761
- DialogPersistence.SUBDIALOGS_DIR = 'subdialogs';
6762
- DialogPersistence.quarantinedRootDialogScopes = new Set();
7165
+ DialogPersistence.SIDE_DIALOGS_DIR = 'sideDialogs';
7166
+ DialogPersistence.quarantinedMainDialogScopes = new Set();
6763
7167
  DialogPersistence.LATEST_WRITEBACK_WINDOW_MS = 300;
6764
7168
  DialogPersistence.Q4H_WRITEBACK_WINDOW_MS = 300;
6765
- DialogPersistence.PENDING_SUBDIALOGS_WRITEBACK_WINDOW_MS = 300;
7169
+ DialogPersistence.PENDING_SIDE_DIALOGS_WRITEBACK_WINDOW_MS = 300;
6766
7170
  DialogPersistence.latestWriteBackMutexes = new Map();
6767
7171
  DialogPersistence.latestWriteBack = new Map();
6768
7172
  DialogPersistence.q4hWriteBackMutexes = new Map();
6769
7173
  DialogPersistence.q4hWriteBack = new Map();
6770
- DialogPersistence.pendingSubdialogsWriteBackMutexes = new Map();
6771
- DialogPersistence.pendingSubdialogsWriteBack = new Map();
7174
+ DialogPersistence.pendingSideDialogsWriteBackMutexes = new Map();
7175
+ DialogPersistence.pendingSideDialogsWriteBack = new Map();
6772
7176
  DialogPersistence.courseAppendMutexes = new Map();
6773
- DialogPersistence.rootDialogWriteBackCancelGenerations = new Map();
7177
+ DialogPersistence.mainDialogWriteBackCancelGenerations = new Map();