dominds 1.16.1 → 1.16.3

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 (179) hide show
  1. package/dist/dialog-display-state.d.ts +7 -1
  2. package/dist/dialog-display-state.js +146 -20
  3. package/dist/dialog-fork.js +13 -5
  4. package/dist/dialog-global-registry.d.ts +5 -1
  5. package/dist/dialog-global-registry.js +45 -1
  6. package/dist/dialog-interruption.d.ts +18 -0
  7. package/dist/dialog-interruption.js +34 -0
  8. package/dist/docs/dominds-agent-collaboration.zh.md +401 -0
  9. package/dist/llm/api-quirks.d.ts +11 -0
  10. package/dist/llm/api-quirks.js +322 -24
  11. package/dist/llm/defaults.yaml +2 -1
  12. package/dist/llm/gen/mock.d.ts +1 -1
  13. package/dist/llm/gen/mock.js +12 -0
  14. package/dist/llm/gen/openai-compatible.d.ts +1 -1
  15. package/dist/llm/gen/openai-compatible.js +9 -1
  16. package/dist/llm/gen/openai.d.ts +1 -1
  17. package/dist/llm/gen/openai.js +16 -2
  18. package/dist/llm/gen.d.ts +10 -0
  19. package/dist/llm/gen.js +10 -0
  20. package/dist/llm/kernel-driver/drive.js +994 -866
  21. package/dist/llm/kernel-driver/flow.js +230 -177
  22. package/dist/llm/kernel-driver/loop.js +5 -0
  23. package/dist/llm/kernel-driver/runtime.d.ts +16 -2
  24. package/dist/llm/kernel-driver/runtime.js +75 -10
  25. package/dist/llm/stop-reason-i18n.d.ts +9 -0
  26. package/dist/llm/stop-reason-i18n.js +114 -0
  27. package/dist/persistence-errors.d.ts +26 -0
  28. package/dist/persistence-errors.js +68 -0
  29. package/dist/persistence.d.ts +119 -82
  30. package/dist/persistence.js +1055 -426
  31. package/dist/server/api-routes.js +174 -58
  32. package/dist/server/websocket-handler.js +125 -21
  33. package/dist/tools/team_mgmt.js +2 -2
  34. package/package.json +4 -4
  35. package/webapp/dist/assets/{_basePickBy-C0MXVhMo.js → _basePickBy-BJWCmtUm.js} +3 -3
  36. package/webapp/dist/assets/_basePickBy-BJWCmtUm.js.map +1 -0
  37. package/webapp/dist/assets/{_baseUniq-CRygl41M.js → _baseUniq-D6k_lGfs.js} +2 -2
  38. package/webapp/dist/assets/_baseUniq-D6k_lGfs.js.map +1 -0
  39. package/webapp/dist/assets/{arc-BeHJtnt2.js → arc-DYDaESgj.js} +2 -2
  40. package/webapp/dist/assets/arc-DYDaESgj.js.map +1 -0
  41. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CGdo5TVi.js → architectureDiagram-VXUJARFQ-DZMT7dqc.js} +8 -26
  42. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-DZMT7dqc.js.map +1 -0
  43. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-C59VaSGw.js → blockDiagram-VD42YOAC-CABpgVAa.js} +170 -187
  44. package/webapp/dist/assets/blockDiagram-VD42YOAC-CABpgVAa.js.map +1 -0
  45. package/webapp/dist/assets/{c4Diagram-IC4MRINW-B3YCBmTx.js → c4Diagram-YG6GDRKO-COLZS8Ul.js} +4 -4
  46. package/webapp/dist/assets/c4Diagram-YG6GDRKO-COLZS8Ul.js.map +1 -0
  47. package/webapp/dist/assets/{channel-Dn4i8EAY.js → channel-CYFm9Cri.js} +2 -2
  48. package/webapp/dist/assets/channel-CYFm9Cri.js.map +1 -0
  49. package/webapp/dist/assets/{chunk-4BX2VUAB-CQbB3HlW.js → chunk-4BX2VUAB-CX_-XbaN.js} +2 -2
  50. package/webapp/dist/assets/chunk-4BX2VUAB-CX_-XbaN.js.map +1 -0
  51. package/webapp/dist/assets/{chunk-55IACEB6-CkQR_Qu9.js → chunk-55IACEB6-ByD-NdBC.js} +2 -2
  52. package/webapp/dist/assets/chunk-55IACEB6-ByD-NdBC.js.map +1 -0
  53. package/webapp/dist/assets/{chunk-WL4C6EOR-Bjl-j3bf.js → chunk-B4BG7PRW-BANemsDD.js} +121 -171
  54. package/webapp/dist/assets/chunk-B4BG7PRW-BANemsDD.js.map +1 -0
  55. package/webapp/dist/assets/{chunk-NQ4KR5QH-DBxY8LL2.js → chunk-DI55MBZ5-DV7sdJmQ.js} +7 -9
  56. package/webapp/dist/assets/chunk-DI55MBZ5-DV7sdJmQ.js.map +1 -0
  57. package/webapp/dist/assets/{chunk-FMBD7UC4-YSIOuaST.js → chunk-FMBD7UC4-DYGviJnf.js} +2 -2
  58. package/webapp/dist/assets/chunk-FMBD7UC4-DYGviJnf.js.map +1 -0
  59. package/webapp/dist/assets/{chunk-KX2RTZJC-DFiwgnFf.js → chunk-QN33PNHL-B8DWRL9f.js} +2 -2
  60. package/webapp/dist/assets/chunk-QN33PNHL-B8DWRL9f.js.map +1 -0
  61. package/webapp/dist/assets/{chunk-QZHKN3VN-BAdR6lLc.js → chunk-QZHKN3VN-B1aYIzMR.js} +2 -2
  62. package/webapp/dist/assets/chunk-QZHKN3VN-B1aYIzMR.js.map +1 -0
  63. package/webapp/dist/assets/{chunk-JSJVCQXG-CWDLRawz.js → chunk-TZMSLE5B-_HISzxl3.js} +6 -14
  64. package/webapp/dist/assets/chunk-TZMSLE5B-_HISzxl3.js.map +1 -0
  65. package/webapp/dist/assets/{classDiagram-VBA2DB6C-KBeS6WXD.js → classDiagram-2ON5EDUG-CpyYj1Rc.js} +6 -7
  66. package/webapp/dist/assets/classDiagram-2ON5EDUG-CpyYj1Rc.js.map +1 -0
  67. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-KBeS6WXD.js → classDiagram-v2-WZHVMYZB-CpyYj1Rc.js} +6 -7
  68. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-CpyYj1Rc.js.map +1 -0
  69. package/webapp/dist/assets/{clone-C7-aS2vm.js → clone-B_9AxWIU.js} +2 -2
  70. package/webapp/dist/assets/clone-B_9AxWIU.js.map +1 -0
  71. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-Bo5IsL-5.js → cose-bilkent-S5V4N54A-17ECLfPR.js} +2 -2
  72. package/webapp/dist/assets/cose-bilkent-S5V4N54A-17ECLfPR.js.map +1 -0
  73. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  74. package/webapp/dist/assets/{dagre-KLK3FWXG-C3FeHj7D.js → dagre-6UL2VRFP-DhRqcF1o.js} +7 -7
  75. package/webapp/dist/assets/dagre-6UL2VRFP-DhRqcF1o.js.map +1 -0
  76. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  77. package/webapp/dist/assets/{diagram-E7M64L7V-0HfiRW0u.js → diagram-PSM6KHXK-lpDT6Wwb.js} +10 -10
  78. package/webapp/dist/assets/diagram-PSM6KHXK-lpDT6Wwb.js.map +1 -0
  79. package/webapp/dist/assets/{diagram-IFDJBPK2-DJwh7SMO.js → diagram-QEK2KX5R-ohxbPpDH.js} +8 -9
  80. package/webapp/dist/assets/diagram-QEK2KX5R-ohxbPpDH.js.map +1 -0
  81. package/webapp/dist/assets/{diagram-P4PSJMXO-Y0RbvUZX.js → diagram-S2PKOQOG-DAFFGfch.js} +8 -8
  82. package/webapp/dist/assets/diagram-S2PKOQOG-DAFFGfch.js.map +1 -0
  83. package/webapp/dist/assets/{erDiagram-INFDFZHY-B183xyLa.js → erDiagram-Q2GNP2WA-BH-7rI43.js} +75 -96
  84. package/webapp/dist/assets/erDiagram-Q2GNP2WA-BH-7rI43.js.map +1 -0
  85. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-C_BR3Y3J.js → flowDiagram-NV44I4VS-CdEjFbz4.js} +81 -98
  86. package/webapp/dist/assets/flowDiagram-NV44I4VS-CdEjFbz4.js.map +1 -0
  87. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-CHLgkuJT.js → ganttDiagram-JELNMOA3-Cju2t-lK.js} +3 -28
  88. package/webapp/dist/assets/ganttDiagram-JELNMOA3-Cju2t-lK.js.map +1 -0
  89. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-D6C_HU1q.js → gitGraphDiagram-V2S2FVAM-CUJ5oxCg.js} +46 -38
  90. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-CUJ5oxCg.js.map +1 -0
  91. package/webapp/dist/assets/graph-mhcc7ldf.js +425 -0
  92. package/webapp/dist/assets/graph-mhcc7ldf.js.map +1 -0
  93. package/webapp/dist/assets/{index-CLoNHmSQ.js → index-BLnM-uON.js} +2098 -1841
  94. package/webapp/dist/assets/{index-CLoNHmSQ.js.map → index-BLnM-uON.js.map} +1 -1
  95. package/webapp/dist/assets/{index-YaxF76or.css → index-xvYYeHuy.css} +1 -1
  96. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-mnMS61NO.js → infoDiagram-HS3SLOUP-Df8p0okQ.js} +7 -7
  97. package/webapp/dist/assets/infoDiagram-HS3SLOUP-Df8p0okQ.js.map +1 -0
  98. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  99. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-BdeERn0r.js → journeyDiagram-XKPGCS4Q-BXMl8H-d.js} +5 -5
  100. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-BXMl8H-d.js.map +1 -0
  101. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-MLeCGcYg.js → kanban-definition-3W4ZIXB7-Cc5RwCEh.js} +3 -5
  102. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-Cc5RwCEh.js.map +1 -0
  103. package/webapp/dist/assets/{layout-DY9VEQ9M.js → layout-BB2SvQcE.js} +5 -5
  104. package/webapp/dist/assets/layout-BB2SvQcE.js.map +1 -0
  105. package/webapp/dist/assets/{linear-D-T1xRsr.js → linear-Cj50lA0a.js} +2 -2
  106. package/webapp/dist/assets/linear-Cj50lA0a.js.map +1 -0
  107. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-CrVm8vj2.js → mindmap-definition-VGOIOE7T-BLBklJVX.js} +5 -7
  108. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-BLBklJVX.js.map +1 -0
  109. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  110. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-GtLLV0r1.js → pieDiagram-ADFJNKIX-BQrOgSc-.js} +8 -8
  111. package/webapp/dist/assets/pieDiagram-ADFJNKIX-BQrOgSc-.js.map +1 -0
  112. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BCgk_2LP.js → quadrantDiagram-AYHSOK5B-C62TxtsO.js} +3 -3
  113. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-C62TxtsO.js.map +1 -0
  114. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-DYUKHWKR.js → requirementDiagram-UZGBJVZJ-Is6Q3osM.js} +6 -16
  115. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-Is6Q3osM.js.map +1 -0
  116. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-CFAH8OaY.js → sankeyDiagram-TZEHDZUN-CCAW8Dr2.js} +2 -2
  117. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-CCAW8Dr2.js.map +1 -0
  118. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-BTnhbv6w.js → sequenceDiagram-WL72ISMW-DpODpbl6.js} +201 -601
  119. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-DpODpbl6.js.map +1 -0
  120. package/webapp/dist/assets/{stateDiagram-RAJIS63D-DWUnJTwO.js → stateDiagram-FKZM4ZOC-B8LHaf5T.js} +9 -9
  121. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-B8LHaf5T.js.map +1 -0
  122. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-B-fc52bp.js → stateDiagram-v2-4FDKWEC3-4FzTkpgz.js} +5 -5
  123. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-4FzTkpgz.js.map +1 -0
  124. package/webapp/dist/assets/{timeline-definition-YZTLITO2-BMZ52QaG.js → timeline-definition-IT6M3QCI-Ckxc4qZe.js} +3 -3
  125. package/webapp/dist/assets/timeline-definition-IT6M3QCI-Ckxc4qZe.js.map +1 -0
  126. package/webapp/dist/assets/{treemap-KZPCXAKY-fgSrze0S.js → treemap-GDKQZRPO-QQa4vKMv.js} +24 -37
  127. package/webapp/dist/assets/treemap-GDKQZRPO-QQa4vKMv.js.map +1 -0
  128. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-D6aviLtC.js → xychartDiagram-PRI3JC2R-Ba2uJcs6.js} +4 -4
  129. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-Ba2uJcs6.js.map +1 -0
  130. package/webapp/dist/index.html +2 -2
  131. package/webapp/dist/assets/_basePickBy-C0MXVhMo.js.map +0 -1
  132. package/webapp/dist/assets/_baseUniq-CRygl41M.js.map +0 -1
  133. package/webapp/dist/assets/arc-BeHJtnt2.js.map +0 -1
  134. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-CGdo5TVi.js.map +0 -1
  135. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-C59VaSGw.js.map +0 -1
  136. package/webapp/dist/assets/c4Diagram-IC4MRINW-B3YCBmTx.js.map +0 -1
  137. package/webapp/dist/assets/channel-Dn4i8EAY.js.map +0 -1
  138. package/webapp/dist/assets/chunk-4BX2VUAB-CQbB3HlW.js.map +0 -1
  139. package/webapp/dist/assets/chunk-55IACEB6-CkQR_Qu9.js.map +0 -1
  140. package/webapp/dist/assets/chunk-FMBD7UC4-YSIOuaST.js.map +0 -1
  141. package/webapp/dist/assets/chunk-JSJVCQXG-CWDLRawz.js.map +0 -1
  142. package/webapp/dist/assets/chunk-KX2RTZJC-DFiwgnFf.js.map +0 -1
  143. package/webapp/dist/assets/chunk-NQ4KR5QH-DBxY8LL2.js.map +0 -1
  144. package/webapp/dist/assets/chunk-QZHKN3VN-BAdR6lLc.js.map +0 -1
  145. package/webapp/dist/assets/chunk-WL4C6EOR-Bjl-j3bf.js.map +0 -1
  146. package/webapp/dist/assets/classDiagram-VBA2DB6C-KBeS6WXD.js.map +0 -1
  147. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-KBeS6WXD.js.map +0 -1
  148. package/webapp/dist/assets/clone-C7-aS2vm.js.map +0 -1
  149. package/webapp/dist/assets/cose-bilkent-S5V4N54A-Bo5IsL-5.js.map +0 -1
  150. package/webapp/dist/assets/dagre-KLK3FWXG-C3FeHj7D.js.map +0 -1
  151. package/webapp/dist/assets/diagram-E7M64L7V-0HfiRW0u.js.map +0 -1
  152. package/webapp/dist/assets/diagram-IFDJBPK2-DJwh7SMO.js.map +0 -1
  153. package/webapp/dist/assets/diagram-P4PSJMXO-Y0RbvUZX.js.map +0 -1
  154. package/webapp/dist/assets/erDiagram-INFDFZHY-B183xyLa.js.map +0 -1
  155. package/webapp/dist/assets/flowDiagram-PKNHOUZH-C_BR3Y3J.js.map +0 -1
  156. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-CHLgkuJT.js.map +0 -1
  157. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-D6C_HU1q.js.map +0 -1
  158. package/webapp/dist/assets/graph-D0hgB-X8.js +0 -782
  159. package/webapp/dist/assets/graph-D0hgB-X8.js.map +0 -1
  160. package/webapp/dist/assets/infoDiagram-LFFYTUFH-mnMS61NO.js.map +0 -1
  161. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-XFIJsnJj.js +0 -966
  162. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-XFIJsnJj.js.map +0 -1
  163. package/webapp/dist/assets/journeyDiagram-4ABVD52K-BdeERn0r.js.map +0 -1
  164. package/webapp/dist/assets/kanban-definition-K7BYSVSG-MLeCGcYg.js.map +0 -1
  165. package/webapp/dist/assets/layout-DY9VEQ9M.js.map +0 -1
  166. package/webapp/dist/assets/linear-D-T1xRsr.js.map +0 -1
  167. package/webapp/dist/assets/mindmap-definition-YRQLILUH-CrVm8vj2.js.map +0 -1
  168. package/webapp/dist/assets/pieDiagram-SKSYHLDU-GtLLV0r1.js.map +0 -1
  169. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-BCgk_2LP.js.map +0 -1
  170. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-DYUKHWKR.js.map +0 -1
  171. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CFAH8OaY.js.map +0 -1
  172. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-BTnhbv6w.js.map +0 -1
  173. package/webapp/dist/assets/stateDiagram-RAJIS63D-DWUnJTwO.js.map +0 -1
  174. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-B-fc52bp.js.map +0 -1
  175. package/webapp/dist/assets/timeline-definition-YZTLITO2-BMZ52QaG.js.map +0 -1
  176. package/webapp/dist/assets/treemap-KZPCXAKY-fgSrze0S.js.map +0 -1
  177. package/webapp/dist/assets/vennDiagram-LZ73GAT5-D5DmDY9l.js +0 -2487
  178. package/webapp/dist/assets/vennDiagram-LZ73GAT5-D5DmDY9l.js.map +0 -1
  179. package/webapp/dist/assets/xychartDiagram-JWTSCODW-D6aviLtC.js.map +0 -1
@@ -34,7 +34,13 @@ export declare function broadcastRunControlCountsSnapshot(): Promise<void>;
34
34
  export declare function hasActiveRun(dialogId: DialogID): boolean;
35
35
  export declare function getActiveRunSignal(dialogId: DialogID): AbortSignal | undefined;
36
36
  export declare function createActiveRun(dialogId: DialogID): AbortSignal;
37
- export declare function clearActiveRun(dialogId: DialogID): void;
37
+ export declare function clearActiveRun(dialogId: DialogID, options?: Readonly<{
38
+ notifyBackendLoop?: boolean;
39
+ }>): void;
40
+ export declare function clearRootDialogQuarantiningIfIdle(rootDialogId: DialogID): void;
41
+ export declare function markRootDialogQuarantining(rootDialogId: DialogID): void;
42
+ export declare function clearRootDialogQuarantining(rootDialogId: DialogID): void;
43
+ export declare function forceStopActiveRunsForRootDialog(rootDialogId: DialogID): Promise<void>;
38
44
  export declare function getStopRequestedReason(dialogId: DialogID): StopRequestedReason | undefined;
39
45
  export declare function loadDialogExecutionMarker(dialogId: DialogID, status?: 'running' | 'completed' | 'archived'): Promise<DialogExecutionMarker | undefined>;
40
46
  export declare function setDialogExecutionMarker(dialogId: DialogID, executionMarker: DialogExecutionMarker | undefined): Promise<void>;
@@ -28,6 +28,10 @@ exports.hasActiveRun = hasActiveRun;
28
28
  exports.getActiveRunSignal = getActiveRunSignal;
29
29
  exports.createActiveRun = createActiveRun;
30
30
  exports.clearActiveRun = clearActiveRun;
31
+ exports.clearRootDialogQuarantiningIfIdle = clearRootDialogQuarantiningIfIdle;
32
+ exports.markRootDialogQuarantining = markRootDialogQuarantining;
33
+ exports.clearRootDialogQuarantining = clearRootDialogQuarantining;
34
+ exports.forceStopActiveRunsForRootDialog = forceStopActiveRunsForRootDialog;
31
35
  exports.getStopRequestedReason = getStopRequestedReason;
32
36
  exports.loadDialogExecutionMarker = loadDialogExecutionMarker;
33
37
  exports.setDialogExecutionMarker = setDialogExecutionMarker;
@@ -40,12 +44,16 @@ exports.requestInterruptDialog = requestInterruptDialog;
40
44
  exports.requestEmergencyStopAll = requestEmergencyStopAll;
41
45
  const time_1 = require("@longrun-ai/kernel/utils/time");
42
46
  const dialog_1 = require("./dialog");
47
+ const dialog_global_registry_1 = require("./dialog-global-registry");
48
+ const dialog_interruption_1 = require("./dialog-interruption");
43
49
  const evt_registry_1 = require("./evt-registry");
44
50
  const log_1 = require("./log");
45
51
  const persistence_1 = require("./persistence");
52
+ const persistence_errors_1 = require("./persistence-errors");
46
53
  const log = (0, log_1.createLogger)('dialog-display-state');
47
54
  let broadcastToClients;
48
55
  const activeRunsByDialogKey = new Map();
56
+ const quarantiningRootDialogIds = new Set();
49
57
  function setDisplayStateBroadcaster(fn) {
50
58
  broadcastToClients = fn;
51
59
  }
@@ -63,7 +71,7 @@ function syncRunControlCountsAfterActiveRunChange(trigger, dialogId) {
63
71
  })();
64
72
  }
65
73
  function isStoppedReasonResumable(reason) {
66
- return reason.kind !== 'llm_retry_stopped';
74
+ return (0, dialog_interruption_1.isInterruptionReasonManualResumeEligible)(reason);
67
75
  }
68
76
  function isDisplayStateResumable(state) {
69
77
  return state?.kind === 'stopped' && state.continueEnabled;
@@ -93,27 +101,49 @@ function shouldBroadcastRunControlCounts(previous, next) {
93
101
  async function getRunControlCountsSnapshot() {
94
102
  let proceeding = 0;
95
103
  let resumable = 0;
96
- const activeRunDialogKeys = new Set(activeRunsByDialogKey.keys());
104
+ const activeRunKeysByDialogKey = new Map(activeRunsByDialogKey.entries());
97
105
  const seenDialogKeys = new Set();
98
106
  const dialogIds = await persistence_1.DialogPersistence.listAllDialogIds('running');
99
107
  for (const dialogId of dialogIds) {
100
- const dialogKey = dialogId.key();
101
- seenDialogKeys.add(dialogKey);
102
- // Active in-memory drives are authoritative for "proceeding". This avoids transient
103
- // under-count windows when displayState persistence lags behind an already started drive.
104
- if (activeRunDialogKeys.has(dialogKey)) {
105
- proceeding++;
108
+ try {
109
+ const dialogKey = dialogId.key();
110
+ seenDialogKeys.add(dialogKey);
111
+ if (quarantiningRootDialogIds.has(dialogId.rootId)) {
112
+ continue;
113
+ }
114
+ // listAllDialogIds() is intentionally a candidate scan. Per-dialog latest reads below may
115
+ // still quarantine one malformed dialog without invalidating the rest of the snapshot.
116
+ const activeRun = activeRunKeysByDialogKey.get(dialogKey);
117
+ if (activeRun) {
118
+ proceeding++;
119
+ continue;
120
+ }
121
+ const latest = await persistence_1.DialogPersistence.loadDialogLatest(dialogId, 'running');
122
+ if (latest?.generating === true) {
123
+ proceeding++;
124
+ }
125
+ else if (isDialogLatestResumable(latest)) {
126
+ resumable++;
127
+ }
128
+ }
129
+ catch (error) {
130
+ if (!(0, persistence_errors_1.findDomindsPersistenceFileError)(error)) {
131
+ throw error;
132
+ }
133
+ log.warn('Skipping malformed dialog during run-control snapshot rebuild', error, {
134
+ dialogId: dialogId.valueOf(),
135
+ });
106
136
  continue;
107
137
  }
108
- const latest = await persistence_1.DialogPersistence.loadDialogLatest(dialogId, 'running');
109
- if (latest?.generating === true) {
110
- proceeding++;
138
+ }
139
+ for (const [dialogKey, activeRun] of activeRunKeysByDialogKey.entries()) {
140
+ if (!activeRun) {
141
+ continue;
111
142
  }
112
- else if (isDialogLatestResumable(latest)) {
113
- resumable++;
143
+ const [rootId] = dialogKey.includes('#') ? dialogKey.split('#') : [dialogKey];
144
+ if (!rootId || quarantiningRootDialogIds.has(rootId)) {
145
+ continue;
114
146
  }
115
- }
116
- for (const dialogKey of activeRunDialogKeys) {
117
147
  if (!seenDialogKeys.has(dialogKey)) {
118
148
  proceeding++;
119
149
  }
@@ -149,12 +179,76 @@ function createActiveRun(dialogId) {
149
179
  syncRunControlCountsAfterActiveRunChange('create_active_run', dialogId);
150
180
  return run.abortController.signal;
151
181
  }
152
- function clearActiveRun(dialogId) {
182
+ function clearActiveRun(dialogId, options) {
153
183
  const deleted = activeRunsByDialogKey.delete(dialogId.key());
154
- if (!deleted)
184
+ if (!deleted) {
185
+ clearQuarantiningRootDialogIfIdle(dialogId.rootId);
155
186
  return;
187
+ }
188
+ clearQuarantiningRootDialogIfIdle(dialogId.rootId);
189
+ if (dialogId.selfId === dialogId.rootId && options?.notifyBackendLoop !== false) {
190
+ dialog_global_registry_1.globalDialogRegistry.notifyActiveRunCleared(dialogId.rootId, {
191
+ source: 'dialog_display_state_active_run_clear',
192
+ reason: 'root_active_run_cleared',
193
+ });
194
+ }
156
195
  syncRunControlCountsAfterActiveRunChange('clear_active_run', dialogId);
157
196
  }
197
+ function clearQuarantiningRootDialogIfIdle(rootId) {
198
+ for (const key of activeRunsByDialogKey.keys()) {
199
+ const [candidateRootId] = key.includes('#') ? key.split('#') : [key];
200
+ if (candidateRootId === rootId) {
201
+ return;
202
+ }
203
+ }
204
+ quarantiningRootDialogIds.delete(rootId);
205
+ }
206
+ function clearRootDialogQuarantiningIfIdle(rootDialogId) {
207
+ clearQuarantiningRootDialogIfIdle(rootDialogId.selfId);
208
+ }
209
+ function markRootDialogQuarantining(rootDialogId) {
210
+ quarantiningRootDialogIds.add(rootDialogId.selfId);
211
+ }
212
+ function clearRootDialogQuarantining(rootDialogId) {
213
+ quarantiningRootDialogIds.delete(rootDialogId.selfId);
214
+ }
215
+ async function forceStopActiveRunsForRootDialog(rootDialogId) {
216
+ for (const key of Array.from(activeRunsByDialogKey.keys())) {
217
+ const [rootId, selfId] = key.includes('#') ? key.split('#') : [key, key];
218
+ if (!rootId || !selfId)
219
+ continue;
220
+ if (rootId !== rootDialogId.selfId)
221
+ continue;
222
+ const dialogId = new dialog_1.DialogID(selfId, rootId);
223
+ const run = activeRunsByDialogKey.get(key);
224
+ if (!run)
225
+ continue;
226
+ if (!run.stopRequested) {
227
+ run.stopRequested = 'emergency_stop';
228
+ try {
229
+ await setDialogDisplayState(dialogId, {
230
+ kind: 'proceeding_stop_requested',
231
+ reason: 'emergency_stop',
232
+ });
233
+ }
234
+ catch (error) {
235
+ log.warn('Failed to persist stop-requested state while forcing root dialog stop', error, {
236
+ dialogId: dialogId.valueOf(),
237
+ rootDialogId: rootDialogId.valueOf(),
238
+ });
239
+ }
240
+ }
241
+ try {
242
+ run.abortController.abort();
243
+ }
244
+ catch (error) {
245
+ log.warn('Failed to abort active run while forcing root dialog stop', error, {
246
+ dialogId: dialogId.valueOf(),
247
+ rootDialogId: rootDialogId.valueOf(),
248
+ });
249
+ }
250
+ }
251
+ }
158
252
  function getStopRequestedReason(dialogId) {
159
253
  return activeRunsByDialogKey.get(dialogId.key())?.stopRequested;
160
254
  }
@@ -350,10 +444,36 @@ async function computeIdleDisplayStateFromPersistence(dialogId) {
350
444
  }
351
445
  return { kind: 'idle_waiting_user' };
352
446
  }
447
+ async function computeIdleDisplayStateForReconciliation(dialogId) {
448
+ try {
449
+ return await computeIdleDisplayStateFromPersistence(dialogId);
450
+ }
451
+ catch (error) {
452
+ if (!(0, persistence_errors_1.findDomindsPersistenceFileError)(error)) {
453
+ throw error;
454
+ }
455
+ log.warn('Skipping malformed dialog during display-state idle reconstruction', error, {
456
+ dialogId: dialogId.valueOf(),
457
+ });
458
+ return null;
459
+ }
460
+ }
353
461
  async function reconcileDisplayStatesAfterRestart() {
354
462
  const dialogIds = await persistence_1.DialogPersistence.listAllDialogIds('running');
355
463
  for (const dialogId of dialogIds) {
356
- const latest = await persistence_1.DialogPersistence.loadDialogLatest(dialogId, 'running');
464
+ let latest;
465
+ try {
466
+ latest = await persistence_1.DialogPersistence.loadDialogLatest(dialogId, 'running');
467
+ }
468
+ catch (error) {
469
+ if (!(0, persistence_errors_1.findDomindsPersistenceFileError)(error)) {
470
+ throw error;
471
+ }
472
+ log.warn('Skipping malformed dialog during display-state restart reconciliation', error, {
473
+ dialogId: dialogId.valueOf(),
474
+ });
475
+ continue;
476
+ }
357
477
  const existing = latest?.displayState;
358
478
  const existingMarker = latest?.executionMarker;
359
479
  if (existingMarker && existingMarker.kind === 'dead' && dialogId.selfId !== dialogId.rootId) {
@@ -380,7 +500,10 @@ async function reconcileDisplayStatesAfterRestart() {
380
500
  (existing !== undefined &&
381
501
  (existing.kind === 'proceeding' || existing.kind === 'proceeding_stop_requested'));
382
502
  if (wasProceeding) {
383
- const nextIdle = await computeIdleDisplayStateFromPersistence(dialogId);
503
+ const nextIdle = await computeIdleDisplayStateForReconciliation(dialogId);
504
+ if (!nextIdle) {
505
+ continue;
506
+ }
384
507
  const next = nextIdle.kind === 'blocked'
385
508
  ? nextIdle
386
509
  : {
@@ -406,7 +529,10 @@ async function reconcileDisplayStatesAfterRestart() {
406
529
  continue;
407
530
  }
408
531
  if (!existing) {
409
- const inferred = await computeIdleDisplayStateFromPersistence(dialogId);
532
+ const inferred = await computeIdleDisplayStateForReconciliation(dialogId);
533
+ if (!inferred) {
534
+ continue;
535
+ }
410
536
  try {
411
537
  await persistence_1.DialogPersistence.mutateDialogLatest(dialogId, () => ({
412
538
  kind: 'patch',
@@ -473,21 +473,29 @@ async function appendForkBaselineState(plan, baselineSubdialogCreatedRecords) {
473
473
  }
474
474
  async function persistForkPlan(args) {
475
475
  const { plan } = args;
476
- let rewrittenMetadata;
477
476
  if (plan.targetId.selfId === plan.targetId.rootId) {
478
- rewrittenMetadata = {
479
- ...plan.metadata,
477
+ if (plan.metadata.supdialogId !== undefined ||
478
+ plan.metadata.sessionSlug !== undefined ||
479
+ plan.metadata.assignmentFromSup !== undefined) {
480
+ throw new Error(`fork root plan received subdialog metadata: ${plan.targetId.valueOf()}`);
481
+ }
482
+ const rewrittenMetadata = {
480
483
  id: plan.targetId.selfId,
484
+ agentId: plan.metadata.agentId,
485
+ taskDocPath: plan.metadata.taskDocPath,
481
486
  createdAt: args.now,
487
+ ...(plan.metadata.priming ? { priming: plan.metadata.priming } : {}),
482
488
  };
489
+ await persistence_1.DialogPersistence.saveRootDialogMetadata(plan.targetId, rewrittenMetadata, 'running');
483
490
  }
484
491
  else {
485
492
  if (plan.metadata.supdialogId === undefined) {
486
493
  throw new Error(`fork subdialog plan missing supdialog metadata: ${plan.targetId.valueOf()}`);
487
494
  }
488
- rewrittenMetadata = rewriteSubdialogMetadataForFork(plan.metadata, plan.sourceId.rootId, plan.targetId.rootId);
495
+ const rewrittenMetadata = rewriteSubdialogMetadataForFork(plan.metadata, plan.sourceId.rootId, plan.targetId.rootId);
496
+ await persistence_1.DialogPersistence.ensureSubdialogDirectory(plan.targetId, 'running');
497
+ await persistence_1.DialogPersistence.saveSubdialogMetadata(plan.targetId, rewrittenMetadata, 'running');
489
498
  }
490
- await persistence_1.DialogPersistence.saveDialogMetadata(plan.targetId, rewrittenMetadata, 'running');
491
499
  for (const course of plan.retainedCourses) {
492
500
  for (const event of course.events) {
493
501
  await persistence_1.DialogPersistence.appendEvent(plan.targetId, course.course, rewriteRecordForFork(event, plan.targetId.rootId), 'running');
@@ -1,7 +1,7 @@
1
1
  import type { RootDialog } from './dialog';
2
2
  export type DriveTriggerEvent = Readonly<{
3
3
  type: 'drive_trigger_evt';
4
- action: 'mark_needs_drive' | 'mark_not_needing_drive';
4
+ action: 'mark_needs_drive' | 'mark_not_needing_drive' | 'active_run_cleared';
5
5
  rootId: string;
6
6
  entryFound: boolean;
7
7
  previousNeedsDrive: boolean | null;
@@ -28,6 +28,10 @@ declare class GlobalDialogRegistry {
28
28
  waitForDriveTrigger(): Promise<DriveTriggerEvent>;
29
29
  markNeedsDrive(rootId: string, meta?: DriveTriggerMeta): void;
30
30
  markNotNeedingDrive(rootId: string, meta?: DriveTriggerMeta): void;
31
+ notifyActiveRunCleared(rootId: string, meta?: DriveTriggerMeta): void;
32
+ noteActiveRunBlockedQueuedDrive(rootId: string): void;
33
+ hasPendingActiveRunClearedWake(rootId: string): boolean;
34
+ isMarkedNeedingDrive(rootId: string): boolean;
31
35
  getDialogsNeedingDrive(): RootDialog[];
32
36
  getLastDriveTrigger(rootId: string): DriveTriggerEvent | undefined;
33
37
  getAll(): RootDialog[];
@@ -29,7 +29,11 @@ class GlobalDialogRegistry {
29
29
  if (existing) {
30
30
  return;
31
31
  }
32
- this.entries.set(rootDialog.id.rootId, { rootDialog, needsDrive: false });
32
+ this.entries.set(rootDialog.id.rootId, {
33
+ rootDialog,
34
+ needsDrive: false,
35
+ activeRunClearedWakePending: false,
36
+ });
33
37
  void (async () => {
34
38
  try {
35
39
  const needsDrive = await persistence_1.DialogPersistence.getNeedsDrive(rootDialog.id);
@@ -82,6 +86,8 @@ class GlobalDialogRegistry {
82
86
  const previousNeedsDrive = entry ? entry.needsDrive : null;
83
87
  if (entry) {
84
88
  entry.needsDrive = true;
89
+ // A fresh queueing trigger supersedes any earlier "wake me once active run clears" debt.
90
+ entry.activeRunClearedWakePending = false;
85
91
  }
86
92
  this.publishDriveTrigger({
87
93
  action: 'mark_needs_drive',
@@ -101,6 +107,7 @@ class GlobalDialogRegistry {
101
107
  const previousNeedsDrive = entry ? entry.needsDrive : null;
102
108
  if (entry) {
103
109
  entry.needsDrive = false;
110
+ entry.activeRunClearedWakePending = false;
104
111
  }
105
112
  this.publishDriveTrigger({
106
113
  action: 'mark_not_needing_drive',
@@ -111,6 +118,43 @@ class GlobalDialogRegistry {
111
118
  meta: triggerMeta,
112
119
  });
113
120
  }
121
+ notifyActiveRunCleared(rootId, meta) {
122
+ const triggerMeta = meta ?? {
123
+ source: 'unknown',
124
+ reason: 'unspecified',
125
+ };
126
+ const entry = this.entries.get(rootId);
127
+ if (!entry) {
128
+ return;
129
+ }
130
+ if (!entry.activeRunClearedWakePending || !entry.needsDrive) {
131
+ entry.activeRunClearedWakePending = false;
132
+ return;
133
+ }
134
+ const currentNeedsDrive = entry ? entry.needsDrive : null;
135
+ entry.activeRunClearedWakePending = false;
136
+ this.publishDriveTrigger({
137
+ action: 'active_run_cleared',
138
+ rootId,
139
+ entryFound: true,
140
+ previousNeedsDrive: currentNeedsDrive,
141
+ nextNeedsDrive: entry.needsDrive,
142
+ meta: triggerMeta,
143
+ });
144
+ }
145
+ noteActiveRunBlockedQueuedDrive(rootId) {
146
+ const entry = this.entries.get(rootId);
147
+ if (!entry || !entry.needsDrive) {
148
+ return;
149
+ }
150
+ entry.activeRunClearedWakePending = true;
151
+ }
152
+ hasPendingActiveRunClearedWake(rootId) {
153
+ return this.entries.get(rootId)?.activeRunClearedWakePending === true;
154
+ }
155
+ isMarkedNeedingDrive(rootId) {
156
+ return this.entries.get(rootId)?.needsDrive === true;
157
+ }
114
158
  getDialogsNeedingDrive() {
115
159
  return Array.from(this.entries.values())
116
160
  .filter((entry) => entry.needsDrive)
@@ -0,0 +1,18 @@
1
+ import type { DialogInterruptionReason } from '@longrun-ai/kernel/types/display-state';
2
+ /**
3
+ * Decides whether a finalized stopped dialog should expose manual Continue.
4
+ *
5
+ * This only applies to the persisted/broadcast final stopped state after the driver has fully
6
+ * unwound and confirmed an interrupted terminal projection. Transient interruption markers and
7
+ * retry-stop progress events must stay disabled until that final stopped state is written. Do not
8
+ * "simplify" this by enabling Continue earlier: that would let the UI offer a resume action before
9
+ * the dialog is actually resumable again, reintroducing exactly the class of race/false-positive
10
+ * bugs this split is here to prevent.
11
+ *
12
+ * `llm_retry_stopped` is intentionally resumable here:
13
+ * - manual Continue starts a fresh drive invocation, which naturally resets per-run retry state
14
+ * and provider-quirk tracking state;
15
+ * - it does NOT reset process-wide adaptive smart-rate backoff, which is shared by
16
+ * provider/model and should keep protecting the system.
17
+ */
18
+ export declare function isInterruptionReasonManualResumeEligible(reason: DialogInterruptionReason): boolean;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isInterruptionReasonManualResumeEligible = isInterruptionReasonManualResumeEligible;
4
+ /**
5
+ * Decides whether a finalized stopped dialog should expose manual Continue.
6
+ *
7
+ * This only applies to the persisted/broadcast final stopped state after the driver has fully
8
+ * unwound and confirmed an interrupted terminal projection. Transient interruption markers and
9
+ * retry-stop progress events must stay disabled until that final stopped state is written. Do not
10
+ * "simplify" this by enabling Continue earlier: that would let the UI offer a resume action before
11
+ * the dialog is actually resumable again, reintroducing exactly the class of race/false-positive
12
+ * bugs this split is here to prevent.
13
+ *
14
+ * `llm_retry_stopped` is intentionally resumable here:
15
+ * - manual Continue starts a fresh drive invocation, which naturally resets per-run retry state
16
+ * and provider-quirk tracking state;
17
+ * - it does NOT reset process-wide adaptive smart-rate backoff, which is shared by
18
+ * provider/model and should keep protecting the system.
19
+ */
20
+ function isInterruptionReasonManualResumeEligible(reason) {
21
+ switch (reason.kind) {
22
+ case 'user_stop':
23
+ case 'emergency_stop':
24
+ case 'server_restart':
25
+ case 'fork_continue_ready':
26
+ case 'system_stop':
27
+ case 'llm_retry_stopped':
28
+ return true;
29
+ default: {
30
+ const _exhaustive = reason;
31
+ return _exhaustive;
32
+ }
33
+ }
34
+ }