dominds 1.16.1 → 1.16.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 (175) hide show
  1. package/dist/dialog-display-state.d.ts +4 -0
  2. package/dist/dialog-display-state.js +138 -19
  3. package/dist/dialog-fork.js +13 -5
  4. package/dist/dialog-interruption.d.ts +18 -0
  5. package/dist/dialog-interruption.js +34 -0
  6. package/dist/docs/dominds-agent-collaboration.zh.md +401 -0
  7. package/dist/llm/api-quirks.d.ts +11 -0
  8. package/dist/llm/api-quirks.js +322 -24
  9. package/dist/llm/defaults.yaml +2 -1
  10. package/dist/llm/gen/mock.d.ts +1 -1
  11. package/dist/llm/gen/mock.js +12 -0
  12. package/dist/llm/gen/openai-compatible.d.ts +1 -1
  13. package/dist/llm/gen/openai-compatible.js +9 -1
  14. package/dist/llm/gen/openai.d.ts +1 -1
  15. package/dist/llm/gen/openai.js +16 -2
  16. package/dist/llm/gen.d.ts +10 -0
  17. package/dist/llm/gen.js +10 -0
  18. package/dist/llm/kernel-driver/drive.js +994 -866
  19. package/dist/llm/kernel-driver/runtime.d.ts +16 -2
  20. package/dist/llm/kernel-driver/runtime.js +75 -10
  21. package/dist/llm/stop-reason-i18n.d.ts +9 -0
  22. package/dist/llm/stop-reason-i18n.js +114 -0
  23. package/dist/persistence-errors.d.ts +26 -0
  24. package/dist/persistence-errors.js +68 -0
  25. package/dist/persistence.d.ts +119 -82
  26. package/dist/persistence.js +1055 -426
  27. package/dist/server/api-routes.js +174 -58
  28. package/dist/server/websocket-handler.js +125 -21
  29. package/dist/tools/team_mgmt.js +2 -2
  30. package/package.json +3 -3
  31. package/webapp/dist/assets/{_basePickBy-C0MXVhMo.js → _basePickBy-BJWCmtUm.js} +3 -3
  32. package/webapp/dist/assets/_basePickBy-BJWCmtUm.js.map +1 -0
  33. package/webapp/dist/assets/{_baseUniq-CRygl41M.js → _baseUniq-D6k_lGfs.js} +2 -2
  34. package/webapp/dist/assets/_baseUniq-D6k_lGfs.js.map +1 -0
  35. package/webapp/dist/assets/{arc-BeHJtnt2.js → arc-DYDaESgj.js} +2 -2
  36. package/webapp/dist/assets/arc-DYDaESgj.js.map +1 -0
  37. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CGdo5TVi.js → architectureDiagram-VXUJARFQ-DZMT7dqc.js} +8 -26
  38. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-DZMT7dqc.js.map +1 -0
  39. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-C59VaSGw.js → blockDiagram-VD42YOAC-CABpgVAa.js} +170 -187
  40. package/webapp/dist/assets/blockDiagram-VD42YOAC-CABpgVAa.js.map +1 -0
  41. package/webapp/dist/assets/{c4Diagram-IC4MRINW-B3YCBmTx.js → c4Diagram-YG6GDRKO-COLZS8Ul.js} +4 -4
  42. package/webapp/dist/assets/c4Diagram-YG6GDRKO-COLZS8Ul.js.map +1 -0
  43. package/webapp/dist/assets/{channel-Dn4i8EAY.js → channel-CYFm9Cri.js} +2 -2
  44. package/webapp/dist/assets/channel-CYFm9Cri.js.map +1 -0
  45. package/webapp/dist/assets/{chunk-4BX2VUAB-CQbB3HlW.js → chunk-4BX2VUAB-CX_-XbaN.js} +2 -2
  46. package/webapp/dist/assets/chunk-4BX2VUAB-CX_-XbaN.js.map +1 -0
  47. package/webapp/dist/assets/{chunk-55IACEB6-CkQR_Qu9.js → chunk-55IACEB6-ByD-NdBC.js} +2 -2
  48. package/webapp/dist/assets/chunk-55IACEB6-ByD-NdBC.js.map +1 -0
  49. package/webapp/dist/assets/{chunk-WL4C6EOR-Bjl-j3bf.js → chunk-B4BG7PRW-BANemsDD.js} +121 -171
  50. package/webapp/dist/assets/chunk-B4BG7PRW-BANemsDD.js.map +1 -0
  51. package/webapp/dist/assets/{chunk-NQ4KR5QH-DBxY8LL2.js → chunk-DI55MBZ5-DV7sdJmQ.js} +7 -9
  52. package/webapp/dist/assets/chunk-DI55MBZ5-DV7sdJmQ.js.map +1 -0
  53. package/webapp/dist/assets/{chunk-FMBD7UC4-YSIOuaST.js → chunk-FMBD7UC4-DYGviJnf.js} +2 -2
  54. package/webapp/dist/assets/chunk-FMBD7UC4-DYGviJnf.js.map +1 -0
  55. package/webapp/dist/assets/{chunk-KX2RTZJC-DFiwgnFf.js → chunk-QN33PNHL-B8DWRL9f.js} +2 -2
  56. package/webapp/dist/assets/chunk-QN33PNHL-B8DWRL9f.js.map +1 -0
  57. package/webapp/dist/assets/{chunk-QZHKN3VN-BAdR6lLc.js → chunk-QZHKN3VN-B1aYIzMR.js} +2 -2
  58. package/webapp/dist/assets/chunk-QZHKN3VN-B1aYIzMR.js.map +1 -0
  59. package/webapp/dist/assets/{chunk-JSJVCQXG-CWDLRawz.js → chunk-TZMSLE5B-_HISzxl3.js} +6 -14
  60. package/webapp/dist/assets/chunk-TZMSLE5B-_HISzxl3.js.map +1 -0
  61. package/webapp/dist/assets/{classDiagram-VBA2DB6C-KBeS6WXD.js → classDiagram-2ON5EDUG-CpyYj1Rc.js} +6 -7
  62. package/webapp/dist/assets/classDiagram-2ON5EDUG-CpyYj1Rc.js.map +1 -0
  63. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-KBeS6WXD.js → classDiagram-v2-WZHVMYZB-CpyYj1Rc.js} +6 -7
  64. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-CpyYj1Rc.js.map +1 -0
  65. package/webapp/dist/assets/{clone-C7-aS2vm.js → clone-B_9AxWIU.js} +2 -2
  66. package/webapp/dist/assets/clone-B_9AxWIU.js.map +1 -0
  67. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-Bo5IsL-5.js → cose-bilkent-S5V4N54A-17ECLfPR.js} +2 -2
  68. package/webapp/dist/assets/cose-bilkent-S5V4N54A-17ECLfPR.js.map +1 -0
  69. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  70. package/webapp/dist/assets/{dagre-KLK3FWXG-C3FeHj7D.js → dagre-6UL2VRFP-DhRqcF1o.js} +7 -7
  71. package/webapp/dist/assets/dagre-6UL2VRFP-DhRqcF1o.js.map +1 -0
  72. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  73. package/webapp/dist/assets/{diagram-E7M64L7V-0HfiRW0u.js → diagram-PSM6KHXK-lpDT6Wwb.js} +10 -10
  74. package/webapp/dist/assets/diagram-PSM6KHXK-lpDT6Wwb.js.map +1 -0
  75. package/webapp/dist/assets/{diagram-IFDJBPK2-DJwh7SMO.js → diagram-QEK2KX5R-ohxbPpDH.js} +8 -9
  76. package/webapp/dist/assets/diagram-QEK2KX5R-ohxbPpDH.js.map +1 -0
  77. package/webapp/dist/assets/{diagram-P4PSJMXO-Y0RbvUZX.js → diagram-S2PKOQOG-DAFFGfch.js} +8 -8
  78. package/webapp/dist/assets/diagram-S2PKOQOG-DAFFGfch.js.map +1 -0
  79. package/webapp/dist/assets/{erDiagram-INFDFZHY-B183xyLa.js → erDiagram-Q2GNP2WA-BH-7rI43.js} +75 -96
  80. package/webapp/dist/assets/erDiagram-Q2GNP2WA-BH-7rI43.js.map +1 -0
  81. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-C_BR3Y3J.js → flowDiagram-NV44I4VS-CdEjFbz4.js} +81 -98
  82. package/webapp/dist/assets/flowDiagram-NV44I4VS-CdEjFbz4.js.map +1 -0
  83. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-CHLgkuJT.js → ganttDiagram-JELNMOA3-Cju2t-lK.js} +3 -28
  84. package/webapp/dist/assets/ganttDiagram-JELNMOA3-Cju2t-lK.js.map +1 -0
  85. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-D6C_HU1q.js → gitGraphDiagram-V2S2FVAM-CUJ5oxCg.js} +46 -38
  86. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-CUJ5oxCg.js.map +1 -0
  87. package/webapp/dist/assets/graph-mhcc7ldf.js +425 -0
  88. package/webapp/dist/assets/graph-mhcc7ldf.js.map +1 -0
  89. package/webapp/dist/assets/{index-CLoNHmSQ.js → index-BLnM-uON.js} +2098 -1841
  90. package/webapp/dist/assets/{index-CLoNHmSQ.js.map → index-BLnM-uON.js.map} +1 -1
  91. package/webapp/dist/assets/{index-YaxF76or.css → index-xvYYeHuy.css} +1 -1
  92. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-mnMS61NO.js → infoDiagram-HS3SLOUP-Df8p0okQ.js} +7 -7
  93. package/webapp/dist/assets/infoDiagram-HS3SLOUP-Df8p0okQ.js.map +1 -0
  94. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  95. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-BdeERn0r.js → journeyDiagram-XKPGCS4Q-BXMl8H-d.js} +5 -5
  96. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-BXMl8H-d.js.map +1 -0
  97. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-MLeCGcYg.js → kanban-definition-3W4ZIXB7-Cc5RwCEh.js} +3 -5
  98. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-Cc5RwCEh.js.map +1 -0
  99. package/webapp/dist/assets/{layout-DY9VEQ9M.js → layout-BB2SvQcE.js} +5 -5
  100. package/webapp/dist/assets/layout-BB2SvQcE.js.map +1 -0
  101. package/webapp/dist/assets/{linear-D-T1xRsr.js → linear-Cj50lA0a.js} +2 -2
  102. package/webapp/dist/assets/linear-Cj50lA0a.js.map +1 -0
  103. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-CrVm8vj2.js → mindmap-definition-VGOIOE7T-BLBklJVX.js} +5 -7
  104. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-BLBklJVX.js.map +1 -0
  105. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  106. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-GtLLV0r1.js → pieDiagram-ADFJNKIX-BQrOgSc-.js} +8 -8
  107. package/webapp/dist/assets/pieDiagram-ADFJNKIX-BQrOgSc-.js.map +1 -0
  108. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BCgk_2LP.js → quadrantDiagram-AYHSOK5B-C62TxtsO.js} +3 -3
  109. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-C62TxtsO.js.map +1 -0
  110. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-DYUKHWKR.js → requirementDiagram-UZGBJVZJ-Is6Q3osM.js} +6 -16
  111. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-Is6Q3osM.js.map +1 -0
  112. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-CFAH8OaY.js → sankeyDiagram-TZEHDZUN-CCAW8Dr2.js} +2 -2
  113. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-CCAW8Dr2.js.map +1 -0
  114. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-BTnhbv6w.js → sequenceDiagram-WL72ISMW-DpODpbl6.js} +201 -601
  115. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-DpODpbl6.js.map +1 -0
  116. package/webapp/dist/assets/{stateDiagram-RAJIS63D-DWUnJTwO.js → stateDiagram-FKZM4ZOC-B8LHaf5T.js} +9 -9
  117. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-B8LHaf5T.js.map +1 -0
  118. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-B-fc52bp.js → stateDiagram-v2-4FDKWEC3-4FzTkpgz.js} +5 -5
  119. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-4FzTkpgz.js.map +1 -0
  120. package/webapp/dist/assets/{timeline-definition-YZTLITO2-BMZ52QaG.js → timeline-definition-IT6M3QCI-Ckxc4qZe.js} +3 -3
  121. package/webapp/dist/assets/timeline-definition-IT6M3QCI-Ckxc4qZe.js.map +1 -0
  122. package/webapp/dist/assets/{treemap-KZPCXAKY-fgSrze0S.js → treemap-GDKQZRPO-QQa4vKMv.js} +24 -37
  123. package/webapp/dist/assets/treemap-GDKQZRPO-QQa4vKMv.js.map +1 -0
  124. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-D6aviLtC.js → xychartDiagram-PRI3JC2R-Ba2uJcs6.js} +4 -4
  125. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-Ba2uJcs6.js.map +1 -0
  126. package/webapp/dist/index.html +2 -2
  127. package/webapp/dist/assets/_basePickBy-C0MXVhMo.js.map +0 -1
  128. package/webapp/dist/assets/_baseUniq-CRygl41M.js.map +0 -1
  129. package/webapp/dist/assets/arc-BeHJtnt2.js.map +0 -1
  130. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-CGdo5TVi.js.map +0 -1
  131. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-C59VaSGw.js.map +0 -1
  132. package/webapp/dist/assets/c4Diagram-IC4MRINW-B3YCBmTx.js.map +0 -1
  133. package/webapp/dist/assets/channel-Dn4i8EAY.js.map +0 -1
  134. package/webapp/dist/assets/chunk-4BX2VUAB-CQbB3HlW.js.map +0 -1
  135. package/webapp/dist/assets/chunk-55IACEB6-CkQR_Qu9.js.map +0 -1
  136. package/webapp/dist/assets/chunk-FMBD7UC4-YSIOuaST.js.map +0 -1
  137. package/webapp/dist/assets/chunk-JSJVCQXG-CWDLRawz.js.map +0 -1
  138. package/webapp/dist/assets/chunk-KX2RTZJC-DFiwgnFf.js.map +0 -1
  139. package/webapp/dist/assets/chunk-NQ4KR5QH-DBxY8LL2.js.map +0 -1
  140. package/webapp/dist/assets/chunk-QZHKN3VN-BAdR6lLc.js.map +0 -1
  141. package/webapp/dist/assets/chunk-WL4C6EOR-Bjl-j3bf.js.map +0 -1
  142. package/webapp/dist/assets/classDiagram-VBA2DB6C-KBeS6WXD.js.map +0 -1
  143. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-KBeS6WXD.js.map +0 -1
  144. package/webapp/dist/assets/clone-C7-aS2vm.js.map +0 -1
  145. package/webapp/dist/assets/cose-bilkent-S5V4N54A-Bo5IsL-5.js.map +0 -1
  146. package/webapp/dist/assets/dagre-KLK3FWXG-C3FeHj7D.js.map +0 -1
  147. package/webapp/dist/assets/diagram-E7M64L7V-0HfiRW0u.js.map +0 -1
  148. package/webapp/dist/assets/diagram-IFDJBPK2-DJwh7SMO.js.map +0 -1
  149. package/webapp/dist/assets/diagram-P4PSJMXO-Y0RbvUZX.js.map +0 -1
  150. package/webapp/dist/assets/erDiagram-INFDFZHY-B183xyLa.js.map +0 -1
  151. package/webapp/dist/assets/flowDiagram-PKNHOUZH-C_BR3Y3J.js.map +0 -1
  152. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-CHLgkuJT.js.map +0 -1
  153. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-D6C_HU1q.js.map +0 -1
  154. package/webapp/dist/assets/graph-D0hgB-X8.js +0 -782
  155. package/webapp/dist/assets/graph-D0hgB-X8.js.map +0 -1
  156. package/webapp/dist/assets/infoDiagram-LFFYTUFH-mnMS61NO.js.map +0 -1
  157. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-XFIJsnJj.js +0 -966
  158. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-XFIJsnJj.js.map +0 -1
  159. package/webapp/dist/assets/journeyDiagram-4ABVD52K-BdeERn0r.js.map +0 -1
  160. package/webapp/dist/assets/kanban-definition-K7BYSVSG-MLeCGcYg.js.map +0 -1
  161. package/webapp/dist/assets/layout-DY9VEQ9M.js.map +0 -1
  162. package/webapp/dist/assets/linear-D-T1xRsr.js.map +0 -1
  163. package/webapp/dist/assets/mindmap-definition-YRQLILUH-CrVm8vj2.js.map +0 -1
  164. package/webapp/dist/assets/pieDiagram-SKSYHLDU-GtLLV0r1.js.map +0 -1
  165. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-BCgk_2LP.js.map +0 -1
  166. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-DYUKHWKR.js.map +0 -1
  167. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CFAH8OaY.js.map +0 -1
  168. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-BTnhbv6w.js.map +0 -1
  169. package/webapp/dist/assets/stateDiagram-RAJIS63D-DWUnJTwO.js.map +0 -1
  170. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-B-fc52bp.js.map +0 -1
  171. package/webapp/dist/assets/timeline-definition-YZTLITO2-BMZ52QaG.js.map +0 -1
  172. package/webapp/dist/assets/treemap-KZPCXAKY-fgSrze0S.js.map +0 -1
  173. package/webapp/dist/assets/vennDiagram-LZ73GAT5-D5DmDY9l.js +0 -2487
  174. package/webapp/dist/assets/vennDiagram-LZ73GAT5-D5DmDY9l.js.map +0 -1
  175. package/webapp/dist/assets/xychartDiagram-JWTSCODW-D6aviLtC.js.map +0 -1
@@ -7,6 +7,7 @@ const id_1 = require("@longrun-ai/kernel/utils/id");
7
7
  const time_1 = require("@longrun-ai/kernel/utils/time");
8
8
  const dialog_1 = require("../../dialog");
9
9
  const dialog_display_state_1 = require("../../dialog-display-state");
10
+ const dialog_interruption_1 = require("../../dialog-interruption");
10
11
  const evt_registry_1 = require("../../evt-registry");
11
12
  const log_1 = require("../../log");
12
13
  const load_1 = require("../../minds/load");
@@ -16,9 +17,12 @@ const reply_prompt_copy_1 = require("../../runtime/reply-prompt-copy");
16
17
  const work_language_1 = require("../../runtime/work-language");
17
18
  const tool_1 = require("../../tool");
18
19
  const taskdoc_1 = require("../../utils/taskdoc");
20
+ const api_quirks_1 = require("../api-quirks");
19
21
  const client_1 = require("../client");
22
+ const gen_1 = require("../gen");
20
23
  const registry_1 = require("../gen/registry");
21
24
  const tool_call_context_1 = require("../gen/tool-call-context");
25
+ const stop_reason_i18n_1 = require("../stop-reason-i18n");
22
26
  const tools_projection_1 = require("../tools-projection");
23
27
  const context_1 = require("./context");
24
28
  const context_health_1 = require("./context-health");
@@ -94,7 +98,17 @@ class KernelDriverInterruptedError extends Error {
94
98
  }
95
99
  }
96
100
  function resolveStoppedContinueEnabled(reason) {
97
- return reason.kind !== 'llm_retry_stopped';
101
+ return (0, dialog_interruption_1.isInterruptionReasonManualResumeEligible)(reason);
102
+ }
103
+ function buildAbortedSystemStopReason() {
104
+ return {
105
+ kind: 'system_stop',
106
+ detail: 'Aborted.',
107
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
108
+ detail: 'Aborted.',
109
+ kind: 'aborted',
110
+ }),
111
+ };
98
112
  }
99
113
  function throwIfAborted(abortSignal, dlg) {
100
114
  if (!abortSignal?.aborted)
@@ -106,7 +120,7 @@ function throwIfAborted(abortSignal, dlg) {
106
120
  if (stopRequested === 'user_stop') {
107
121
  throw new KernelDriverInterruptedError({ kind: 'user_stop' });
108
122
  }
109
- throw new KernelDriverInterruptedError({ kind: 'system_stop', detail: 'Aborted.' });
123
+ throw new KernelDriverInterruptedError(buildAbortedSystemStopReason());
110
124
  }
111
125
  function isFbrSubdialogDialog(dlg) {
112
126
  return dlg instanceof dialog_1.SubDialog && dlg.assignmentFromSup.callName === 'freshBootsReasoning';
@@ -916,6 +930,7 @@ async function executeFunctionRound(args) {
916
930
  shouldStopAfterReplyTool: false,
917
931
  pairedMessages: [],
918
932
  tellaskToolOutputs: [],
933
+ updatePlanOnlyRound: false,
919
934
  };
920
935
  }
921
936
  throwIfAborted(args.abortSignal, args.dlg);
@@ -959,26 +974,23 @@ async function executeFunctionRound(args) {
959
974
  }
960
975
  return shouldImmediatelyFollowUpToolOutcome(tool, outcome);
961
976
  });
962
- const updatePlanOnlyRawArgumentsSignature = (() => {
977
+ const updatePlanOnlyRound = (() => {
963
978
  if (tellaskRound.normalCalls.length === 0) {
964
- return undefined;
979
+ return false;
965
980
  }
966
981
  if (tellaskRound.handledCallIds.length > 0 || tellaskRound.toolOutputs.length > 0) {
967
- return undefined;
982
+ return false;
968
983
  }
969
- const signatures = [];
970
984
  for (const call of tellaskRound.normalCalls) {
971
985
  if (call.name !== 'update_plan') {
972
- return undefined;
986
+ return false;
973
987
  }
974
988
  const outcome = genericOutcomeByCallId.get(call.id);
975
989
  if (outcome !== 'success') {
976
- return undefined;
990
+ return false;
977
991
  }
978
- const argsStr = typeof call.arguments === 'string' ? call.arguments : JSON.stringify(call.arguments ?? {});
979
- signatures.push(argsStr);
980
992
  }
981
- return JSON.stringify(signatures);
993
+ return true;
982
994
  })();
983
995
  const resultByCallId = new Map();
984
996
  const register = (result) => {
@@ -1028,7 +1040,7 @@ async function executeFunctionRound(args) {
1028
1040
  shouldStopAfterReplyTool: tellaskRound.shouldStopAfterReplyTool,
1029
1041
  pairedMessages,
1030
1042
  tellaskToolOutputs: [...tellaskRound.toolOutputs],
1031
- updatePlanOnlyRawArgumentsSignature,
1043
+ updatePlanOnlyRound,
1032
1044
  };
1033
1045
  }
1034
1046
  async function resetDiligenceBudgetAfterQ4H(dlg, team) {
@@ -1100,6 +1112,16 @@ async function maybeContinueWithDiligencePrompt(args) {
1100
1112
  }
1101
1113
  return { kind: 'break' };
1102
1114
  }
1115
+ async function maybePrepareRetryStoppedRecoveryPrompt(args) {
1116
+ if (args.reason.recoveryAction.kind !== 'diligence_push_once') {
1117
+ return { kind: 'break' };
1118
+ }
1119
+ return await maybeContinueWithDiligencePrompt({
1120
+ dlg: args.dlg,
1121
+ team: args.team,
1122
+ suppressDiligencePushForDrive: args.suppressDiligencePushForDrive,
1123
+ });
1124
+ }
1103
1125
  async function maybeContinueWithHealthPromptBeforeDiligence(args) {
1104
1126
  const { dlg, providerCfg, model } = args;
1105
1127
  // This path is only used as a higher-priority alternative to Diligence Push.
@@ -1174,10 +1196,15 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1174
1196
  let fbrConclusion;
1175
1197
  let pubRemindersVer = dlg.remindersVer;
1176
1198
  let pendingPrompt = humanPrompt;
1199
+ let retryStoppedRecoveryPrompt;
1177
1200
  let skipTaskdocForThisDrive = humanPrompt?.skipTaskdoc === true;
1178
1201
  let genIterNo = 0;
1179
1202
  let consecutiveIdenticalUpdatePlanOnlyRounds = 0;
1180
1203
  let lastIdenticalUpdatePlanRoundSignature;
1204
+ // Quirk retry state intentionally spans multiple request invocations in the same driver run,
1205
+ // including course changes. Provider/API retry heuristics are tracked independently from
1206
+ // user-facing course boundaries.
1207
+ const retryQuirkSessionByProviderModel = new Map();
1181
1208
  if (!humanPrompt) {
1182
1209
  try {
1183
1210
  const executionMarker = await (0, dialog_display_state_1.loadDialogExecutionMarker)(dlg.id, 'running');
@@ -1193,332 +1220,564 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1193
1220
  }
1194
1221
  await (0, dialog_display_state_1.clearDialogInterruptedExecutionMarker)(dlg.id);
1195
1222
  await (0, dialog_display_state_1.setDialogDisplayState)(dlg.id, { kind: 'proceeding' });
1196
- try {
1197
- for (;;) {
1198
- genIterNo += 1;
1199
- throwIfAborted(abortSignal, dlg);
1200
- const activeFbrState = await loadDialogFbrState(dlg);
1201
- if (isFbrSubdialogDialog(dlg)) {
1202
- dlg.setFbrConclusionToolsEnabled(activeFbrState !== undefined && (0, fbr_1.isFbrFinalizationState)(activeFbrState));
1203
- if (pendingPrompt === undefined &&
1204
- activeFbrState &&
1205
- activeFbrState.promptDelivered !== true) {
1206
- pendingPrompt = buildKernelDriverFbrPrompt(dlg, activeFbrState);
1223
+ driveCoreLoop: for (;;) {
1224
+ try {
1225
+ for (;;) {
1226
+ genIterNo += 1;
1227
+ throwIfAborted(abortSignal, dlg);
1228
+ const activeFbrState = await loadDialogFbrState(dlg);
1229
+ if (isFbrSubdialogDialog(dlg)) {
1230
+ dlg.setFbrConclusionToolsEnabled(activeFbrState !== undefined && (0, fbr_1.isFbrFinalizationState)(activeFbrState));
1231
+ if (pendingPrompt === undefined &&
1232
+ activeFbrState &&
1233
+ activeFbrState.promptDelivered !== true) {
1234
+ pendingPrompt = buildKernelDriverFbrPrompt(dlg, activeFbrState);
1235
+ }
1207
1236
  }
1208
- }
1209
- const minds = await (0, load_1.loadAgentMinds)(dlg.agentId, dlg);
1210
- const team = minds.team;
1211
- const policy = (0, guardrails_1.buildKernelDriverPolicy)({
1212
- dlg,
1213
- agent: minds.agent,
1214
- systemPrompt: minds.systemPrompt,
1215
- agentTools: minds.agentTools,
1216
- language: (0, work_language_1.getWorkLanguage)(),
1217
- });
1218
- const policyValidation = (0, guardrails_1.validateKernelDriverPolicyInvariants)(policy, (0, work_language_1.getWorkLanguage)());
1219
- if (!policyValidation.ok) {
1220
- throw new Error(`kernel-driver policy invariant violation: ${policyValidation.detail}`);
1221
- }
1222
- const agent = policy.effectiveAgent;
1223
- const systemPrompt = policy.effectiveSystemPrompt;
1224
- const agentTools = policy.effectiveAgentTools;
1225
- const provider = agent.provider ?? team.memberDefaults.provider;
1226
- const model = agent.model ?? team.memberDefaults.model;
1227
- if (!provider) {
1228
- throw new Error(`Configuration Error: No provider configured for agent '${dlg.agentId}'. Please specify a provider in the agent's configuration or in member_defaults section of .minds/team.yaml.`);
1229
- }
1230
- if (!model) {
1231
- throw new Error(`Configuration Error: No model configured for agent '${dlg.agentId}'. Please specify a model in the agent's configuration or in member_defaults section of .minds/team.yaml.`);
1232
- }
1233
- const llmCfg = await client_1.LlmConfig.load();
1234
- const providerCfg = llmCfg.getProvider(provider);
1235
- if (!providerCfg) {
1236
- throw new Error(`Provider configuration error: Provider '${provider}' not found for agent '${dlg.agentId}'. Please check .minds/llm.yaml and .minds/team.yaml configuration.`);
1237
- }
1238
- if (!providerCfg.models || !providerCfg.models[model]) {
1239
- throw new Error(`Configuration error: invalid model '${model}' for provider '${provider}' (agent='${dlg.agentId}').`);
1240
- }
1241
- const llmGen = (0, registry_1.getLlmGenerator)(providerCfg.apiType);
1242
- if (!llmGen) {
1243
- throw new Error(`LLM generator not found: API type '${providerCfg.apiType}' for provider '${provider}' in agent '${dlg.agentId}'. Please check .minds/llm.yaml configuration.`);
1244
- }
1245
- const retryPolicy = resolveKernelDriverRetryPolicy(providerCfg);
1246
- const canonicalFuncTools = agentTools.filter((t) => t.type === 'func');
1247
- const isSubdialog = dlg.id.rootId !== dlg.id.selfId;
1248
- const fbrEffortDefault = resolveFbrEffortDefaultForTool(agent);
1249
- const effectiveFuncTools = policy.mode === 'default'
1250
- ? mergeTellaskVirtualTools(canonicalFuncTools, {
1251
- includeTellaskBack: isSubdialog,
1252
- fbrEffortDefault,
1253
- })
1254
- : canonicalFuncTools;
1255
- const projected = (0, tools_projection_1.projectFuncToolsForProvider)(providerCfg.apiType, effectiveFuncTools);
1256
- const funcTools = projected.tools;
1257
- if (genIterNo > 1) {
1258
- const snapshot = dlg.getLastContextHealth();
1259
- const hasQueuedUpNext = dlg.hasUpNext() || pendingPrompt !== undefined;
1260
- const modelInfoForRemediation = resolveModelInfo(providerCfg, model);
1261
- const cautionRemediationCadenceGenerations = (0, context_health_1.resolveCautionRemediationCadenceGenerations)(modelInfoForRemediation?.caution_remediation_cadence_generations);
1262
- const criticalCountdownRemaining = (0, context_health_1.resolveCriticalCountdownRemaining)(dlg.id.key(), snapshot);
1263
- const healthDecision = (0, context_health_1.decideKernelDriverContextHealth)({
1264
- dialogKey: dlg.id.key(),
1265
- snapshot,
1266
- hadUserPromptThisGen: isUserOriginPrompt(pendingPrompt),
1267
- canInjectPromptThisGen: !hasQueuedUpNext,
1268
- cautionRemediationCadenceGenerations,
1269
- criticalCountdownRemaining,
1237
+ const minds = await (0, load_1.loadAgentMinds)(dlg.agentId, dlg);
1238
+ const team = minds.team;
1239
+ const policy = (0, guardrails_1.buildKernelDriverPolicy)({
1240
+ dlg,
1241
+ agent: minds.agent,
1242
+ systemPrompt: minds.systemPrompt,
1243
+ agentTools: minds.agentTools,
1244
+ language: (0, work_language_1.getWorkLanguage)(),
1270
1245
  });
1271
- if (healthDecision.kind === 'suspend') {
1272
- log_1.log.debug('kernel-driver suspend iterative generation due to critical context while waiting for human prompt', undefined, {
1273
- dialogId: dlg.id.valueOf(),
1274
- rootId: dlg.id.rootId,
1275
- selfId: dlg.id.selfId,
1276
- genIterNo,
1277
- pendingPromptOrigin: pendingPrompt?.origin ?? null,
1246
+ const policyValidation = (0, guardrails_1.validateKernelDriverPolicyInvariants)(policy, (0, work_language_1.getWorkLanguage)());
1247
+ if (!policyValidation.ok) {
1248
+ throw new Error(`kernel-driver policy invariant violation: ${policyValidation.detail}`);
1249
+ }
1250
+ const agent = policy.effectiveAgent;
1251
+ const systemPrompt = policy.effectiveSystemPrompt;
1252
+ const agentTools = policy.effectiveAgentTools;
1253
+ const prepareRetryStoppedRecovery = async (reason) => {
1254
+ retryStoppedRecoveryPrompt = undefined;
1255
+ const recovery = await maybePrepareRetryStoppedRecoveryPrompt({
1256
+ dlg,
1257
+ team,
1258
+ suppressDiligencePushForDrive,
1259
+ reason,
1278
1260
  });
1279
- break;
1261
+ if (recovery.kind !== 'continue') {
1262
+ return 'stop';
1263
+ }
1264
+ retryStoppedRecoveryPrompt = recovery.prompt;
1265
+ return 'continue';
1266
+ };
1267
+ const provider = agent.provider ?? team.memberDefaults.provider;
1268
+ const model = agent.model ?? team.memberDefaults.model;
1269
+ if (!provider) {
1270
+ throw new Error(`Configuration Error: No provider configured for agent '${dlg.agentId}'. Please specify a provider in the agent's configuration or in member_defaults section of .minds/team.yaml.`);
1280
1271
  }
1281
- if (healthDecision.kind === 'continue') {
1282
- if (healthDecision.reason === 'critical_force_new_course') {
1283
- const language = (0, work_language_1.getWorkLanguage)();
1284
- const newCoursePrompt = (0, driver_messages_1.formatNewCourseStartPrompt)(language, {
1285
- nextCourse: dlg.currentCourse + 1,
1286
- source: 'critical_auto_clear',
1287
- });
1288
- await dlg.startNewCourse(newCoursePrompt);
1289
- dlg.setLastContextHealth({ kind: 'unavailable', reason: 'usage_unavailable' });
1290
- (0, context_health_1.resetContextHealthRoundState)(dlg.id.key());
1291
- const nextPrompt = resolveUpNextPrompt(dlg);
1292
- if (!nextPrompt) {
1293
- throw new Error(`kernel-driver critical force-new-course invariant violation: missing upNext prompt after startNewCourse for dialog=${dlg.id.valueOf()}`);
1294
- }
1295
- pendingPrompt = nextPrompt;
1296
- skipTaskdocForThisDrive = false;
1272
+ if (!model) {
1273
+ throw new Error(`Configuration Error: No model configured for agent '${dlg.agentId}'. Please specify a model in the agent's configuration or in member_defaults section of .minds/team.yaml.`);
1274
+ }
1275
+ const llmCfg = await client_1.LlmConfig.load();
1276
+ const providerCfg = llmCfg.getProvider(provider);
1277
+ if (!providerCfg) {
1278
+ throw new Error(`Provider configuration error: Provider '${provider}' not found for agent '${dlg.agentId}'. Please check .minds/llm.yaml and .minds/team.yaml configuration.`);
1279
+ }
1280
+ if (!providerCfg.models || !providerCfg.models[model]) {
1281
+ throw new Error(`Configuration error: invalid model '${model}' for provider '${provider}' (agent='${dlg.agentId}').`);
1282
+ }
1283
+ const llmGen = (0, registry_1.getLlmGenerator)(providerCfg.apiType);
1284
+ if (!llmGen) {
1285
+ throw new Error(`LLM generator not found: API type '${providerCfg.apiType}' for provider '${provider}' in agent '${dlg.agentId}'. Please check .minds/llm.yaml configuration.`);
1286
+ }
1287
+ const resolveRetryQuirkSession = () => {
1288
+ const key = `${provider}::${model}`;
1289
+ const existing = retryQuirkSessionByProviderModel.get(key);
1290
+ if (existing) {
1291
+ return existing;
1297
1292
  }
1298
- else if (!hasQueuedUpNext) {
1299
- const language = (0, work_language_1.getWorkLanguage)();
1300
- const guideText = healthDecision.reason === 'caution_soft_remediation'
1301
- ? (0, driver_messages_1.formatAgentFacingContextHealthV3RemediationGuide)(language, {
1302
- kind: 'caution',
1303
- mode: 'soft',
1304
- })
1305
- : (0, driver_messages_1.formatAgentFacingContextHealthV3RemediationGuide)(language, {
1306
- kind: 'critical',
1307
- mode: 'countdown',
1308
- promptsRemainingAfterThis: (0, context_health_1.consumeCriticalCountdown)(dlg.id.key()),
1309
- promptsTotal: context_health_1.KERNEL_DRIVER_DEFAULT_CRITICAL_COUNTDOWN_GENERATIONS,
1310
- });
1311
- pendingPrompt = {
1312
- content: guideText,
1313
- msgId: (0, id_1.generateShortId)(),
1314
- grammar: 'markdown',
1315
- origin: 'runtime',
1316
- userLanguageCode: language,
1317
- };
1293
+ const created = (0, api_quirks_1.createLlmFailureQuirkHandlerSession)(providerCfg);
1294
+ if (!created) {
1295
+ return undefined;
1318
1296
  }
1319
- }
1320
- }
1321
- let contextHealthForGen;
1322
- let llmGenModelForGen = model;
1323
- const currentPrompt = pendingPrompt;
1324
- const currentReplyTarget = currentPrompt?.subdialogReplyTarget;
1325
- const currentFbrState = await loadDialogFbrState(dlg);
1326
- let currentRuntimeGuideMsg;
1327
- const currentPromptFromFbrState = currentPrompt !== undefined &&
1328
- currentFbrState !== undefined &&
1329
- currentFbrState.promptDelivered !== true;
1330
- pendingPrompt = undefined;
1331
- await dlg.notifyGeneratingStart(currentPrompt?.msgId);
1332
- try {
1333
- if (currentPrompt) {
1334
- const origin = currentPrompt.origin;
1335
- if (origin === 'diligence_push' &&
1336
- (dlg.disableDiligencePush || suppressDiligencePushForDrive)) {
1337
- log_1.log.debug('kernel-driver skip diligence prompt after disable toggle', undefined, {
1297
+ retryQuirkSessionByProviderModel.set(key, created);
1298
+ return created;
1299
+ };
1300
+ const retryPolicy = resolveKernelDriverRetryPolicy(providerCfg);
1301
+ const canonicalFuncTools = agentTools.filter((t) => t.type === 'func');
1302
+ const isSubdialog = dlg.id.rootId !== dlg.id.selfId;
1303
+ const fbrEffortDefault = resolveFbrEffortDefaultForTool(agent);
1304
+ const effectiveFuncTools = policy.mode === 'default'
1305
+ ? mergeTellaskVirtualTools(canonicalFuncTools, {
1306
+ includeTellaskBack: isSubdialog,
1307
+ fbrEffortDefault,
1308
+ })
1309
+ : canonicalFuncTools;
1310
+ const projected = (0, tools_projection_1.projectFuncToolsForProvider)(providerCfg.apiType, effectiveFuncTools);
1311
+ const funcTools = projected.tools;
1312
+ if (genIterNo > 1) {
1313
+ const snapshot = dlg.getLastContextHealth();
1314
+ const hasQueuedUpNext = dlg.hasUpNext() || pendingPrompt !== undefined;
1315
+ const modelInfoForRemediation = resolveModelInfo(providerCfg, model);
1316
+ const cautionRemediationCadenceGenerations = (0, context_health_1.resolveCautionRemediationCadenceGenerations)(modelInfoForRemediation?.caution_remediation_cadence_generations);
1317
+ const criticalCountdownRemaining = (0, context_health_1.resolveCriticalCountdownRemaining)(dlg.id.key(), snapshot);
1318
+ const healthDecision = (0, context_health_1.decideKernelDriverContextHealth)({
1319
+ dialogKey: dlg.id.key(),
1320
+ snapshot,
1321
+ hadUserPromptThisGen: isUserOriginPrompt(pendingPrompt),
1322
+ canInjectPromptThisGen: !hasQueuedUpNext,
1323
+ cautionRemediationCadenceGenerations,
1324
+ criticalCountdownRemaining,
1325
+ });
1326
+ if (healthDecision.kind === 'suspend') {
1327
+ log_1.log.debug('kernel-driver suspend iterative generation due to critical context while waiting for human prompt', undefined, {
1338
1328
  dialogId: dlg.id.valueOf(),
1339
- msgId: currentPrompt.msgId,
1329
+ rootId: dlg.id.rootId,
1330
+ selfId: dlg.id.selfId,
1331
+ genIterNo,
1332
+ pendingPromptOrigin: pendingPrompt?.origin ?? null,
1340
1333
  });
1341
1334
  break;
1342
1335
  }
1343
- if (currentPrompt.skipTaskdoc === true) {
1344
- skipTaskdocForThisDrive = true;
1345
- }
1346
- const persistedUserLanguageCode = currentPrompt.userLanguageCode ?? dlg.getLastUserLanguageCode();
1347
- const q4hAnswerCallId = normalizeQ4HAnswerCallId(currentPrompt.q4hAnswerCallId);
1348
- // `q4hAnswerCallId` marks a continuation input for an already-materialized askHuman
1349
- // answer. It is not a second business-level user prompt that should re-enter transcript.
1350
- const isQ4HAnswerPrompt = q4hAnswerCallId !== undefined;
1351
- const promptLanguage = persistedUserLanguageCode === 'zh' || persistedUserLanguageCode === 'en'
1352
- ? persistedUserLanguageCode
1353
- : (0, work_language_1.getWorkLanguage)();
1354
- const replyGuidance = await (0, reply_guidance_1.resolvePromptReplyGuidance)({
1355
- dlg,
1356
- prompt: currentPrompt,
1357
- language: promptLanguage,
1358
- });
1359
- if (currentPrompt.origin === 'user' &&
1360
- replyGuidance.suppressInterDialogReplyGuidance &&
1361
- replyGuidance.deferredReplyReassertionDirective !== undefined) {
1362
- const existingDeferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dlg.id, dlg.status);
1363
- const nextDeferredReplyReassertion = {
1364
- reason: 'user_interjection_while_pending_subdialog',
1365
- directive: replyGuidance.deferredReplyReassertionDirective,
1366
- };
1367
- if (existingDeferredReplyReassertion === undefined ||
1368
- !hasSameReplyDirective(existingDeferredReplyReassertion.directive, nextDeferredReplyReassertion.directive)) {
1369
- await persistence_1.DialogPersistence.setDeferredReplyReassertion(dlg.id, nextDeferredReplyReassertion, dlg.status);
1336
+ if (healthDecision.kind === 'continue') {
1337
+ if (healthDecision.reason === 'critical_force_new_course') {
1338
+ const language = (0, work_language_1.getWorkLanguage)();
1339
+ const newCoursePrompt = (0, driver_messages_1.formatNewCourseStartPrompt)(language, {
1340
+ nextCourse: dlg.currentCourse + 1,
1341
+ source: 'critical_auto_clear',
1342
+ });
1343
+ await dlg.startNewCourse(newCoursePrompt);
1344
+ dlg.setLastContextHealth({ kind: 'unavailable', reason: 'usage_unavailable' });
1345
+ (0, context_health_1.resetContextHealthRoundState)(dlg.id.key());
1346
+ const nextPrompt = resolveUpNextPrompt(dlg);
1347
+ if (!nextPrompt) {
1348
+ throw new Error(`kernel-driver critical force-new-course invariant violation: missing upNext prompt after startNewCourse for dialog=${dlg.id.valueOf()}`);
1349
+ }
1350
+ pendingPrompt = nextPrompt;
1351
+ skipTaskdocForThisDrive = false;
1370
1352
  }
1371
- if (existingDeferredReplyReassertion === undefined) {
1372
- currentRuntimeGuideMsg = replyGuidance.transientGuideContent
1373
- ? {
1374
- type: 'transient_guide_msg',
1375
- role: 'assistant',
1376
- content: replyGuidance.transientGuideContent,
1377
- }
1378
- : undefined;
1353
+ else if (!hasQueuedUpNext) {
1354
+ const language = (0, work_language_1.getWorkLanguage)();
1355
+ const guideText = healthDecision.reason === 'caution_soft_remediation'
1356
+ ? (0, driver_messages_1.formatAgentFacingContextHealthV3RemediationGuide)(language, {
1357
+ kind: 'caution',
1358
+ mode: 'soft',
1359
+ })
1360
+ : (0, driver_messages_1.formatAgentFacingContextHealthV3RemediationGuide)(language, {
1361
+ kind: 'critical',
1362
+ mode: 'countdown',
1363
+ promptsRemainingAfterThis: (0, context_health_1.consumeCriticalCountdown)(dlg.id.key()),
1364
+ promptsTotal: context_health_1.KERNEL_DRIVER_DEFAULT_CRITICAL_COUNTDOWN_GENERATIONS,
1365
+ });
1366
+ pendingPrompt = {
1367
+ content: guideText,
1368
+ msgId: (0, id_1.generateShortId)(),
1369
+ grammar: 'markdown',
1370
+ origin: 'runtime',
1371
+ userLanguageCode: language,
1372
+ };
1379
1373
  }
1380
1374
  }
1381
- else if (currentPrompt.origin === 'user' &&
1382
- !replyGuidance.suppressInterDialogReplyGuidance) {
1383
- await persistence_1.DialogPersistence.setDeferredReplyReassertion(dlg.id, undefined, dlg.status);
1384
- }
1385
- if (!replyGuidance.suppressInterDialogReplyGuidance &&
1386
- !currentRuntimeGuideMsg &&
1387
- replyGuidance.transientGuideContent) {
1388
- currentRuntimeGuideMsg = {
1389
- type: 'transient_guide_msg',
1390
- role: 'assistant',
1391
- content: replyGuidance.transientGuideContent,
1392
- };
1393
- }
1394
- if (replyGuidance.promptContent === undefined) {
1395
- throw new Error(`kernel-driver reply guidance invariant violation: missing prompt content for dialog=${dlg.id.valueOf()} msgId=${currentPrompt.msgId}`);
1396
- }
1397
- const renderPromptAsRuntimeGuideBubble = origin === 'runtime' &&
1398
- (0, reply_prompt_copy_1.isStandaloneRuntimeGuidePromptContent)(replyGuidance.promptContent);
1399
- if (currentRuntimeGuideMsg) {
1400
- await dlg.addChatMessages(currentRuntimeGuideMsg);
1401
- await persistence_1.DialogPersistence.persistRuntimeGuide(dlg, currentRuntimeGuideMsg.content, dlg.activeGenSeq);
1402
- (0, evt_registry_1.postDialogEvent)(dlg, {
1403
- type: 'runtime_guide_evt',
1404
- course: dlg.currentCourse,
1405
- genseq: dlg.activeGenSeq,
1406
- content: currentRuntimeGuideMsg.content,
1407
- });
1408
- currentRuntimeGuideMsg = undefined;
1409
- }
1410
- if (isQ4HAnswerPrompt) {
1411
- // Record only the answered call correlation / user language for the resumed round.
1412
- // The actual human answer fact was already persisted via askHuman tellask result flow.
1413
- await dlg.receiveHumanReply({
1414
- content: replyGuidance.promptContent,
1415
- userLanguageCode: persistedUserLanguageCode,
1416
- q4hAnswerCallId,
1417
- });
1418
- }
1419
- else {
1420
- await dlg.addChatMessages({
1421
- type: 'prompting_msg',
1422
- role: 'user',
1423
- genseq: dlg.activeGenSeq,
1424
- msgId: currentPrompt.msgId,
1425
- grammar: 'markdown',
1426
- content: replyGuidance.promptContent,
1427
- });
1428
- await dlg.persistUserMessage(replyGuidance.promptContent, currentPrompt.msgId, 'markdown', origin, persistedUserLanguageCode, q4hAnswerCallId, replyGuidance.persistedTellaskReplyDirective);
1429
- }
1430
- if (renderPromptAsRuntimeGuideBubble) {
1431
- (0, evt_registry_1.postDialogEvent)(dlg, {
1432
- type: 'runtime_guide_evt',
1433
- course: dlg.currentCourse,
1434
- genseq: dlg.activeGenSeq,
1435
- content: replyGuidance.promptContent,
1436
- });
1437
- }
1438
- else if (!isQ4HAnswerPrompt) {
1439
- // Emit the live user-side boundary event for UI generation bubbles.
1440
- // Without this, realtime turns can miss user content + divider (<hr/>).
1441
- (0, evt_registry_1.postDialogEvent)(dlg, {
1442
- type: 'end_of_user_saying_evt',
1443
- course: dlg.currentCourse,
1444
- genseq: dlg.activeGenSeq,
1445
- msgId: currentPrompt.msgId,
1446
- content: replyGuidance.promptContent,
1447
- grammar: 'markdown',
1448
- origin,
1449
- userLanguageCode: persistedUserLanguageCode,
1450
- q4hAnswerCallId,
1375
+ }
1376
+ let contextHealthForGen;
1377
+ let llmGenModelForGen = model;
1378
+ const currentPrompt = pendingPrompt;
1379
+ const currentReplyTarget = currentPrompt?.subdialogReplyTarget;
1380
+ const currentFbrState = await loadDialogFbrState(dlg);
1381
+ let currentRuntimeGuideMsg;
1382
+ const currentPromptFromFbrState = currentPrompt !== undefined &&
1383
+ currentFbrState !== undefined &&
1384
+ currentFbrState.promptDelivered !== true;
1385
+ pendingPrompt = undefined;
1386
+ await dlg.notifyGeneratingStart(currentPrompt?.msgId);
1387
+ try {
1388
+ if (currentPrompt) {
1389
+ const origin = currentPrompt.origin;
1390
+ if (origin === 'diligence_push' &&
1391
+ (dlg.disableDiligencePush || suppressDiligencePushForDrive)) {
1392
+ log_1.log.debug('kernel-driver skip diligence prompt after disable toggle', undefined, {
1393
+ dialogId: dlg.id.valueOf(),
1394
+ msgId: currentPrompt.msgId,
1395
+ });
1396
+ break;
1397
+ }
1398
+ if (currentPrompt.skipTaskdoc === true) {
1399
+ skipTaskdocForThisDrive = true;
1400
+ }
1401
+ const persistedUserLanguageCode = currentPrompt.userLanguageCode ?? dlg.getLastUserLanguageCode();
1402
+ const q4hAnswerCallId = normalizeQ4HAnswerCallId(currentPrompt.q4hAnswerCallId);
1403
+ // `q4hAnswerCallId` marks a continuation input for an already-materialized askHuman
1404
+ // answer. It is not a second business-level user prompt that should re-enter transcript.
1405
+ const isQ4HAnswerPrompt = q4hAnswerCallId !== undefined;
1406
+ const promptLanguage = persistedUserLanguageCode === 'zh' || persistedUserLanguageCode === 'en'
1407
+ ? persistedUserLanguageCode
1408
+ : (0, work_language_1.getWorkLanguage)();
1409
+ const replyGuidance = await (0, reply_guidance_1.resolvePromptReplyGuidance)({
1410
+ dlg,
1411
+ prompt: currentPrompt,
1412
+ language: promptLanguage,
1451
1413
  });
1452
- }
1453
- if (currentPromptFromFbrState && currentFbrState) {
1454
- await persistDialogFbrState(dlg, (0, fbr_1.markFbrPromptDelivered)(currentFbrState));
1455
- }
1456
- // Ideal: provider SDKs should support a dedicated role='environment' for runtime
1457
- // metadata. Today, most providers only accept user/assistant (and tool as a special
1458
- // case), so Dominds must project environment/system-like content as role='user'.
1459
- const replyTarget = currentPrompt.subdialogReplyTarget;
1460
- if (replyTarget) {
1461
- const normalizedCallId = replyTarget.callId.trim();
1462
- if (normalizedCallId === '') {
1463
- throw new Error(`kernel-driver assignment anchor invariant violation: empty callId (dialog=${dlg.id.valueOf()})`);
1414
+ if (currentPrompt.origin === 'user' &&
1415
+ replyGuidance.suppressInterDialogReplyGuidance &&
1416
+ replyGuidance.deferredReplyReassertionDirective !== undefined) {
1417
+ const existingDeferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dlg.id, dlg.status);
1418
+ const nextDeferredReplyReassertion = {
1419
+ reason: 'user_interjection_while_pending_subdialog',
1420
+ directive: replyGuidance.deferredReplyReassertionDirective,
1421
+ };
1422
+ if (existingDeferredReplyReassertion === undefined ||
1423
+ !hasSameReplyDirective(existingDeferredReplyReassertion.directive, nextDeferredReplyReassertion.directive)) {
1424
+ await persistence_1.DialogPersistence.setDeferredReplyReassertion(dlg.id, nextDeferredReplyReassertion, dlg.status);
1425
+ }
1426
+ if (existingDeferredReplyReassertion === undefined) {
1427
+ currentRuntimeGuideMsg = replyGuidance.transientGuideContent
1428
+ ? {
1429
+ type: 'transient_guide_msg',
1430
+ role: 'assistant',
1431
+ content: replyGuidance.transientGuideContent,
1432
+ }
1433
+ : undefined;
1434
+ }
1435
+ }
1436
+ else if (currentPrompt.origin === 'user' &&
1437
+ !replyGuidance.suppressInterDialogReplyGuidance) {
1438
+ await persistence_1.DialogPersistence.setDeferredReplyReassertion(dlg.id, undefined, dlg.status);
1439
+ }
1440
+ if (!replyGuidance.suppressInterDialogReplyGuidance &&
1441
+ !currentRuntimeGuideMsg &&
1442
+ replyGuidance.transientGuideContent) {
1443
+ currentRuntimeGuideMsg = {
1444
+ type: 'transient_guide_msg',
1445
+ role: 'assistant',
1446
+ content: replyGuidance.transientGuideContent,
1447
+ };
1448
+ }
1449
+ if (replyGuidance.promptContent === undefined) {
1450
+ throw new Error(`kernel-driver reply guidance invariant violation: missing prompt content for dialog=${dlg.id.valueOf()} msgId=${currentPrompt.msgId}`);
1451
+ }
1452
+ const renderPromptAsRuntimeGuideBubble = origin === 'runtime' &&
1453
+ (0, reply_prompt_copy_1.isStandaloneRuntimeGuidePromptContent)(replyGuidance.promptContent);
1454
+ if (currentRuntimeGuideMsg) {
1455
+ await dlg.addChatMessages(currentRuntimeGuideMsg);
1456
+ await persistence_1.DialogPersistence.persistRuntimeGuide(dlg, currentRuntimeGuideMsg.content, dlg.activeGenSeq);
1457
+ (0, evt_registry_1.postDialogEvent)(dlg, {
1458
+ type: 'runtime_guide_evt',
1459
+ course: dlg.currentCourse,
1460
+ genseq: dlg.activeGenSeq,
1461
+ content: currentRuntimeGuideMsg.content,
1462
+ });
1463
+ currentRuntimeGuideMsg = undefined;
1464
+ }
1465
+ if (isQ4HAnswerPrompt) {
1466
+ // Record only the answered call correlation / user language for the resumed round.
1467
+ // The actual human answer fact was already persisted via askHuman tellask result flow.
1468
+ await dlg.receiveHumanReply({
1469
+ content: replyGuidance.promptContent,
1470
+ userLanguageCode: persistedUserLanguageCode,
1471
+ q4hAnswerCallId,
1472
+ });
1473
+ }
1474
+ else {
1475
+ await dlg.addChatMessages({
1476
+ type: 'prompting_msg',
1477
+ role: 'user',
1478
+ genseq: dlg.activeGenSeq,
1479
+ msgId: currentPrompt.msgId,
1480
+ grammar: 'markdown',
1481
+ content: replyGuidance.promptContent,
1482
+ });
1483
+ await dlg.persistUserMessage(replyGuidance.promptContent, currentPrompt.msgId, 'markdown', origin, persistedUserLanguageCode, q4hAnswerCallId, replyGuidance.persistedTellaskReplyDirective);
1484
+ }
1485
+ if (renderPromptAsRuntimeGuideBubble) {
1486
+ (0, evt_registry_1.postDialogEvent)(dlg, {
1487
+ type: 'runtime_guide_evt',
1488
+ course: dlg.currentCourse,
1489
+ genseq: dlg.activeGenSeq,
1490
+ content: replyGuidance.promptContent,
1491
+ });
1492
+ }
1493
+ else if (!isQ4HAnswerPrompt) {
1494
+ // Emit the live user-side boundary event for UI generation bubbles.
1495
+ // Without this, realtime turns can miss user content + divider (<hr/>).
1496
+ (0, evt_registry_1.postDialogEvent)(dlg, {
1497
+ type: 'end_of_user_saying_evt',
1498
+ course: dlg.currentCourse,
1499
+ genseq: dlg.activeGenSeq,
1500
+ msgId: currentPrompt.msgId,
1501
+ content: replyGuidance.promptContent,
1502
+ grammar: 'markdown',
1503
+ origin,
1504
+ userLanguageCode: persistedUserLanguageCode,
1505
+ q4hAnswerCallId,
1506
+ });
1507
+ }
1508
+ if (currentPromptFromFbrState && currentFbrState) {
1509
+ await persistDialogFbrState(dlg, (0, fbr_1.markFbrPromptDelivered)(currentFbrState));
1510
+ }
1511
+ // Ideal: provider SDKs should support a dedicated role='environment' for runtime
1512
+ // metadata. Today, most providers only accept user/assistant (and tool as a special
1513
+ // case), so Dominds must project environment/system-like content as role='user'.
1514
+ const replyTarget = currentPrompt.subdialogReplyTarget;
1515
+ if (replyTarget) {
1516
+ const normalizedCallId = replyTarget.callId.trim();
1517
+ if (normalizedCallId === '') {
1518
+ throw new Error(`kernel-driver assignment anchor invariant violation: empty callId (dialog=${dlg.id.valueOf()})`);
1519
+ }
1520
+ const record = {
1521
+ ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1522
+ type: 'tellask_call_anchor_record',
1523
+ anchorRole: 'assignment',
1524
+ callId: normalizedCallId,
1525
+ genseq: dlg.activeGenSeq,
1526
+ ...(0, storage_1.toRootGenerationAnchor)({
1527
+ rootCourse: (dlg instanceof dialog_1.SubDialog ? dlg.rootDialog : dlg).currentCourse,
1528
+ rootGenseq: (dlg instanceof dialog_1.SubDialog ? dlg.rootDialog : dlg).activeGenSeqOrUndefined ?? 0,
1529
+ }),
1530
+ };
1531
+ const course = dlg.activeGenCourseOrUndefined ?? dlg.currentCourse;
1532
+ await persistence_1.DialogPersistence.appendEvent(dlg.id, course, record, dlg.status);
1464
1533
  }
1465
- const record = {
1466
- ts: (0, time_1.formatUnifiedTimestamp)(new Date()),
1467
- type: 'tellask_call_anchor_record',
1468
- anchorRole: 'assignment',
1469
- callId: normalizedCallId,
1470
- genseq: dlg.activeGenSeq,
1471
- ...(0, storage_1.toRootGenerationAnchor)({
1472
- rootCourse: (dlg instanceof dialog_1.SubDialog ? dlg.rootDialog : dlg).currentCourse,
1473
- rootGenseq: (dlg instanceof dialog_1.SubDialog ? dlg.rootDialog : dlg).activeGenSeqOrUndefined ?? 0,
1474
- }),
1475
- };
1476
- const course = dlg.activeGenCourseOrUndefined ?? dlg.currentCourse;
1477
- await persistence_1.DialogPersistence.appendEvent(dlg.id, course, record, dlg.status);
1478
1534
  }
1479
- }
1480
- await dlg.processReminderUpdates();
1481
- pubRemindersVer = dlg.remindersVer;
1482
- const taskDocMsg = dlg.taskDocPath && !skipTaskdocForThisDrive ? await (0, taskdoc_1.formatTaskDocContent)(dlg) : undefined;
1483
- const renderedReminders = await renderRemindersForContext(dlg);
1484
- const ctxMsgs = (0, context_1.assembleDriveContextMessages)({
1485
- base: {
1486
- prependedContextMessages: policy.prependedContextMessages,
1487
- memories: minds.memories,
1488
- taskDocMsg,
1489
- coursePrefixMsgs: dlg.getCoursePrefixMsgs(),
1490
- dialogMsgsForContext: await buildDialogMsgsForContext(dlg),
1491
- },
1492
- ephemeral: {
1493
- runtimeGuideMsgs: currentRuntimeGuideMsg ? [currentRuntimeGuideMsg] : undefined,
1494
- },
1495
- tail: { renderedReminders },
1496
- });
1497
- const newMsgs = [];
1498
- const streamedFuncCalls = [];
1499
- let sawWebSearchSideChannelOutput = false;
1500
- let sawNativeToolSideChannelOutput = false;
1501
- const streamOrBatch = async () => {
1502
- let batchAttemptCourse;
1503
- let batchAttemptCheckpointOffset;
1504
- const rollbackBatchAttempt = async () => {
1505
- if (batchAttemptCourse === undefined || batchAttemptCheckpointOffset === undefined) {
1506
- throw new Error(`kernel-driver batch retry invariant violation: missing checkpoint (dialog=${dlg.id.valueOf()})`);
1535
+ await dlg.processReminderUpdates();
1536
+ pubRemindersVer = dlg.remindersVer;
1537
+ const taskDocMsg = dlg.taskDocPath && !skipTaskdocForThisDrive
1538
+ ? await (0, taskdoc_1.formatTaskDocContent)(dlg)
1539
+ : undefined;
1540
+ const renderedReminders = await renderRemindersForContext(dlg);
1541
+ const ctxMsgs = (0, context_1.assembleDriveContextMessages)({
1542
+ base: {
1543
+ prependedContextMessages: policy.prependedContextMessages,
1544
+ memories: minds.memories,
1545
+ taskDocMsg,
1546
+ coursePrefixMsgs: dlg.getCoursePrefixMsgs(),
1547
+ dialogMsgsForContext: await buildDialogMsgsForContext(dlg),
1548
+ },
1549
+ ephemeral: {
1550
+ runtimeGuideMsgs: currentRuntimeGuideMsg ? [currentRuntimeGuideMsg] : undefined,
1551
+ },
1552
+ tail: { renderedReminders },
1553
+ });
1554
+ const newMsgs = [];
1555
+ const streamedFuncCalls = [];
1556
+ let sawWebSearchSideChannelOutput = false;
1557
+ let sawNativeToolSideChannelOutput = false;
1558
+ const streamOrBatch = async () => {
1559
+ let batchAttemptCourse;
1560
+ let batchAttemptCheckpointOffset;
1561
+ const rollbackBatchAttempt = async () => {
1562
+ if (batchAttemptCourse === undefined || batchAttemptCheckpointOffset === undefined) {
1563
+ throw new Error(`kernel-driver batch retry invariant violation: missing checkpoint (dialog=${dlg.id.valueOf()})`);
1564
+ }
1565
+ await persistence_1.DialogPersistence.rollbackCourseFileToOffset(dlg.id, batchAttemptCourse, batchAttemptCheckpointOffset, dlg.status);
1566
+ (0, evt_registry_1.postDialogEvent)(dlg, {
1567
+ type: 'genseq_discard_evt',
1568
+ course: batchAttemptCourse,
1569
+ genseq: dlg.activeGenSeq,
1570
+ reason: 'retry',
1571
+ });
1572
+ sawWebSearchSideChannelOutput = false;
1573
+ sawNativeToolSideChannelOutput = false;
1574
+ streamedFuncCalls.length = 0;
1575
+ newMsgs.length = 0;
1576
+ };
1577
+ if (agent.streaming === false) {
1578
+ const batch = await (0, runtime_1.runLlmRequestWithRetry)({
1579
+ dlg,
1580
+ provider,
1581
+ modelId: model,
1582
+ providerConfig: providerCfg,
1583
+ abortSignal,
1584
+ maxRetries: retryPolicy.maxRetries,
1585
+ retryInitialDelayMs: retryPolicy.initialDelayMs,
1586
+ retryConservativeDelayMs: retryPolicy.conservativeDelayMs,
1587
+ retryBackoffMultiplier: retryPolicy.backoffMultiplier,
1588
+ retryMaxDelayMs: retryPolicy.maxDelayMs,
1589
+ classifyFailure: llmGen.classifyFailure?.bind(llmGen),
1590
+ quirkFailureHandlerSession: resolveRetryQuirkSession(),
1591
+ canRetry: () => true,
1592
+ onRetry: rollbackBatchAttempt,
1593
+ onGiveUp: rollbackBatchAttempt,
1594
+ onRetryStopped: prepareRetryStoppedRecovery,
1595
+ doRequest: async () => {
1596
+ batchAttemptCourse = dlg.activeGenCourseOrUndefined ?? dlg.currentCourse;
1597
+ batchAttemptCheckpointOffset = await persistence_1.DialogPersistence.captureCourseFileOffset(dlg.id, batchAttemptCourse, dlg.status);
1598
+ sawWebSearchSideChannelOutput = false;
1599
+ sawNativeToolSideChannelOutput = false;
1600
+ streamedFuncCalls.length = 0;
1601
+ newMsgs.length = 0;
1602
+ const batchResult = await llmGen.genMoreMessages(providerCfg, agent, systemPrompt, funcTools, {
1603
+ dialogSelfId: dlg.id.selfId,
1604
+ dialogRootId: dlg.id.rootId,
1605
+ promptCacheKey: `${dlg.id.selfId}:c${String(dlg.currentCourse)}`,
1606
+ }, ctxMsgs, dlg.activeGenSeq, abortSignal);
1607
+ if (!hasMeaningfulBatchOutput(batchResult)) {
1608
+ throw {
1609
+ status: 503,
1610
+ code: KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE,
1611
+ message: `LLM returned empty response (provider=${provider}, model=${model}, streaming=false).`,
1612
+ };
1613
+ }
1614
+ return batchResult;
1615
+ },
1616
+ });
1617
+ return {
1618
+ usage: batch.usage,
1619
+ llmGenModel: batch.llmGenModel,
1620
+ batchMessages: batch.messages,
1621
+ batchOutputs: batch.outputs,
1622
+ };
1507
1623
  }
1508
- await persistence_1.DialogPersistence.rollbackCourseFileToOffset(dlg.id, batchAttemptCourse, batchAttemptCheckpointOffset, dlg.status);
1509
- (0, evt_registry_1.postDialogEvent)(dlg, {
1510
- type: 'genseq_discard_evt',
1511
- course: batchAttemptCourse,
1512
- genseq: dlg.activeGenSeq,
1513
- reason: 'retry',
1514
- });
1515
- sawWebSearchSideChannelOutput = false;
1516
- sawNativeToolSideChannelOutput = false;
1517
- streamedFuncCalls.length = 0;
1518
- newMsgs.length = 0;
1519
- };
1520
- if (agent.streaming === false) {
1521
- const batch = await (0, runtime_1.runLlmRequestWithRetry)({
1624
+ let currentSayingContent = '';
1625
+ let currentThinkingContent = '';
1626
+ let currentThinkingReasoning = undefined;
1627
+ let streamAttemptCourse;
1628
+ let streamAttemptCheckpointOffset;
1629
+ let streamAttemptSayingContent;
1630
+ let streamAttemptSayingGenseq;
1631
+ let streamActive = { kind: 'idle' };
1632
+ const rollbackStreamAttempt = async () => {
1633
+ if (streamAttemptCourse === undefined ||
1634
+ streamAttemptCheckpointOffset === undefined) {
1635
+ throw new Error(`kernel-driver stream retry invariant violation: missing checkpoint (dialog=${dlg.id.valueOf()})`);
1636
+ }
1637
+ await persistence_1.DialogPersistence.rollbackCourseFileToOffset(dlg.id, streamAttemptCourse, streamAttemptCheckpointOffset, dlg.status);
1638
+ (0, evt_registry_1.postDialogEvent)(dlg, {
1639
+ type: 'genseq_discard_evt',
1640
+ course: streamAttemptCourse,
1641
+ genseq: dlg.activeGenSeq,
1642
+ reason: 'retry',
1643
+ });
1644
+ streamActive = { kind: 'idle' };
1645
+ currentThinkingContent = '';
1646
+ currentThinkingReasoning = undefined;
1647
+ currentSayingContent = '';
1648
+ streamAttemptSayingContent = undefined;
1649
+ streamAttemptSayingGenseq = undefined;
1650
+ sawWebSearchSideChannelOutput = false;
1651
+ sawNativeToolSideChannelOutput = false;
1652
+ streamedFuncCalls.length = 0;
1653
+ newMsgs.length = 0;
1654
+ };
1655
+ const receiver = {
1656
+ streamError: async (detail) => {
1657
+ await dlg.streamError(detail);
1658
+ },
1659
+ thinkingStart: async () => {
1660
+ throwIfAborted(abortSignal, dlg);
1661
+ if (streamActive.kind !== 'idle') {
1662
+ const detail = `Protocol violation: thinkingStart while ${streamActive.kind} is active`;
1663
+ await dlg.streamError(detail);
1664
+ throw new gen_1.LlmStreamErrorEmittedError({
1665
+ detail,
1666
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
1667
+ detail,
1668
+ kind: 'conflicting_stream',
1669
+ }),
1670
+ });
1671
+ }
1672
+ streamActive = { kind: 'thinking' };
1673
+ currentThinkingContent = '';
1674
+ currentThinkingReasoning = undefined;
1675
+ await dlg.thinkingStart();
1676
+ },
1677
+ thinkingChunk: async (chunk) => {
1678
+ throwIfAborted(abortSignal, dlg);
1679
+ currentThinkingContent += chunk;
1680
+ await dlg.thinkingChunk(chunk);
1681
+ },
1682
+ thinkingFinish: async (reasoning) => {
1683
+ throwIfAborted(abortSignal, dlg);
1684
+ if (streamActive.kind !== 'thinking') {
1685
+ const detail = `Protocol violation: thinkingFinish while ${streamActive.kind} is active`;
1686
+ await dlg.streamError(detail);
1687
+ throw new gen_1.LlmStreamErrorEmittedError({
1688
+ detail,
1689
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
1690
+ detail,
1691
+ kind: 'conflicting_stream',
1692
+ }),
1693
+ });
1694
+ }
1695
+ streamActive = { kind: 'idle' };
1696
+ if (reasoning)
1697
+ currentThinkingReasoning = reasoning;
1698
+ await dlg.thinkingFinish(reasoning);
1699
+ if (currentThinkingContent.length > 0 || currentThinkingReasoning !== undefined) {
1700
+ newMsgs.push({
1701
+ type: 'thinking_msg',
1702
+ role: 'assistant',
1703
+ genseq: dlg.activeGenSeq,
1704
+ content: currentThinkingContent,
1705
+ reasoning: currentThinkingReasoning,
1706
+ });
1707
+ }
1708
+ currentThinkingContent = '';
1709
+ currentThinkingReasoning = undefined;
1710
+ },
1711
+ sayingStart: async () => {
1712
+ throwIfAborted(abortSignal, dlg);
1713
+ if (streamActive.kind !== 'idle') {
1714
+ const detail = `Protocol violation: sayingStart while ${streamActive.kind} is active`;
1715
+ await dlg.streamError(detail);
1716
+ throw new gen_1.LlmStreamErrorEmittedError({
1717
+ detail,
1718
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
1719
+ detail,
1720
+ kind: 'conflicting_stream',
1721
+ }),
1722
+ });
1723
+ }
1724
+ streamActive = { kind: 'saying' };
1725
+ currentSayingContent = '';
1726
+ await dlg.sayingStart();
1727
+ },
1728
+ sayingChunk: async (chunk) => {
1729
+ throwIfAborted(abortSignal, dlg);
1730
+ currentSayingContent += chunk;
1731
+ await dlg.sayingChunk(chunk);
1732
+ },
1733
+ sayingFinish: async () => {
1734
+ throwIfAborted(abortSignal, dlg);
1735
+ if (streamActive.kind !== 'saying') {
1736
+ const detail = `Protocol violation: sayingFinish while ${streamActive.kind} is active`;
1737
+ await dlg.streamError(detail);
1738
+ throw new gen_1.LlmStreamErrorEmittedError({
1739
+ detail,
1740
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
1741
+ detail,
1742
+ kind: 'conflicting_stream',
1743
+ }),
1744
+ });
1745
+ }
1746
+ streamActive = { kind: 'idle' };
1747
+ await dlg.sayingFinish();
1748
+ const sayingMessage = {
1749
+ type: 'saying_msg',
1750
+ role: 'assistant',
1751
+ genseq: dlg.activeGenSeq,
1752
+ content: currentSayingContent,
1753
+ };
1754
+ newMsgs.push(sayingMessage);
1755
+ streamAttemptSayingContent = currentSayingContent;
1756
+ streamAttemptSayingGenseq = sayingMessage.genseq;
1757
+ },
1758
+ funcCall: async (callId, name, argsStr) => {
1759
+ throwIfAborted(abortSignal, dlg);
1760
+ streamedFuncCalls.push({
1761
+ type: 'func_call_msg',
1762
+ role: 'assistant',
1763
+ genseq: dlg.activeGenSeq,
1764
+ id: callId,
1765
+ name,
1766
+ arguments: argsStr,
1767
+ });
1768
+ },
1769
+ webSearchCall: async (call) => {
1770
+ throwIfAborted(abortSignal, dlg);
1771
+ sawWebSearchSideChannelOutput = true;
1772
+ await dlg.webSearchCall(projectLlmWebSearchCall(call));
1773
+ },
1774
+ nativeToolCall: async (call) => {
1775
+ throwIfAborted(abortSignal, dlg);
1776
+ sawNativeToolSideChannelOutput = true;
1777
+ await dlg.nativeToolCall(call);
1778
+ },
1779
+ };
1780
+ const res = await (0, runtime_1.runLlmRequestWithRetry)({
1522
1781
  dlg,
1523
1782
  provider,
1524
1783
  modelId: model,
@@ -1530,620 +1789,489 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1530
1789
  retryBackoffMultiplier: retryPolicy.backoffMultiplier,
1531
1790
  retryMaxDelayMs: retryPolicy.maxDelayMs,
1532
1791
  classifyFailure: llmGen.classifyFailure?.bind(llmGen),
1792
+ quirkFailureHandlerSession: resolveRetryQuirkSession(),
1533
1793
  canRetry: () => true,
1534
- onRetry: rollbackBatchAttempt,
1535
- onGiveUp: rollbackBatchAttempt,
1794
+ onRetry: rollbackStreamAttempt,
1795
+ onGiveUp: rollbackStreamAttempt,
1796
+ onRetryStopped: prepareRetryStoppedRecovery,
1536
1797
  doRequest: async () => {
1537
- batchAttemptCourse = dlg.activeGenCourseOrUndefined ?? dlg.currentCourse;
1538
- batchAttemptCheckpointOffset = await persistence_1.DialogPersistence.captureCourseFileOffset(dlg.id, batchAttemptCourse, dlg.status);
1798
+ streamAttemptCourse = dlg.activeGenCourseOrUndefined ?? dlg.currentCourse;
1799
+ streamAttemptCheckpointOffset = await persistence_1.DialogPersistence.captureCourseFileOffset(dlg.id, streamAttemptCourse, dlg.status);
1800
+ streamActive = { kind: 'idle' };
1801
+ currentThinkingContent = '';
1802
+ currentThinkingReasoning = undefined;
1803
+ currentSayingContent = '';
1804
+ streamAttemptSayingContent = undefined;
1805
+ streamAttemptSayingGenseq = undefined;
1539
1806
  sawWebSearchSideChannelOutput = false;
1540
1807
  sawNativeToolSideChannelOutput = false;
1541
1808
  streamedFuncCalls.length = 0;
1542
1809
  newMsgs.length = 0;
1543
- const batchResult = await llmGen.genMoreMessages(providerCfg, agent, systemPrompt, funcTools, {
1810
+ const streamResult = await llmGen.genToReceiver(providerCfg, agent, systemPrompt, funcTools, {
1544
1811
  dialogSelfId: dlg.id.selfId,
1545
1812
  dialogRootId: dlg.id.rootId,
1546
1813
  promptCacheKey: `${dlg.id.selfId}:c${String(dlg.currentCourse)}`,
1547
- }, ctxMsgs, dlg.activeGenSeq, abortSignal);
1548
- if (!hasMeaningfulBatchOutput(batchResult)) {
1814
+ }, ctxMsgs, receiver, dlg.activeGenSeq, abortSignal);
1815
+ const hasThinkingContent = currentThinkingContent.trim() !== '';
1816
+ const hasSayingContent = (streamAttemptSayingContent ?? '').trim() !== '';
1817
+ const hasFunctionCall = streamedFuncCalls.length > 0;
1818
+ if (!hasThinkingContent &&
1819
+ !hasSayingContent &&
1820
+ !hasFunctionCall &&
1821
+ !sawWebSearchSideChannelOutput &&
1822
+ !sawNativeToolSideChannelOutput) {
1549
1823
  throw {
1550
1824
  status: 503,
1551
1825
  code: KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE,
1552
- message: `LLM returned empty response (provider=${provider}, model=${model}, streaming=false).`,
1826
+ message: `LLM returned empty response (provider=${provider}, model=${model}, streaming=true).`,
1553
1827
  };
1554
1828
  }
1555
- return batchResult;
1829
+ return streamResult;
1556
1830
  },
1557
1831
  });
1558
- return {
1559
- usage: batch.usage,
1560
- llmGenModel: batch.llmGenModel,
1561
- batchMessages: batch.messages,
1562
- batchOutputs: batch.outputs,
1563
- };
1564
- }
1565
- let currentSayingContent = '';
1566
- let currentThinkingContent = '';
1567
- let currentThinkingReasoning = undefined;
1568
- let streamAttemptCourse;
1569
- let streamAttemptCheckpointOffset;
1570
- let streamAttemptSayingContent;
1571
- let streamAttemptSayingGenseq;
1572
- let streamActive = { kind: 'idle' };
1573
- const rollbackStreamAttempt = async () => {
1574
- if (streamAttemptCourse === undefined || streamAttemptCheckpointOffset === undefined) {
1575
- throw new Error(`kernel-driver stream retry invariant violation: missing checkpoint (dialog=${dlg.id.valueOf()})`);
1832
+ if (streamAttemptSayingContent !== undefined) {
1833
+ lastAssistantSayingContent = streamAttemptSayingContent;
1834
+ lastAssistantSayingGenseq =
1835
+ streamAttemptSayingGenseq === undefined ? null : streamAttemptSayingGenseq;
1836
+ lastAssistantReplyTarget = currentReplyTarget;
1576
1837
  }
1577
- await persistence_1.DialogPersistence.rollbackCourseFileToOffset(dlg.id, streamAttemptCourse, streamAttemptCheckpointOffset, dlg.status);
1578
- (0, evt_registry_1.postDialogEvent)(dlg, {
1579
- type: 'genseq_discard_evt',
1580
- course: streamAttemptCourse,
1581
- genseq: dlg.activeGenSeq,
1582
- reason: 'retry',
1583
- });
1584
- streamActive = { kind: 'idle' };
1585
- currentThinkingContent = '';
1586
- currentThinkingReasoning = undefined;
1587
- currentSayingContent = '';
1588
- streamAttemptSayingContent = undefined;
1589
- streamAttemptSayingGenseq = undefined;
1590
- sawWebSearchSideChannelOutput = false;
1591
- sawNativeToolSideChannelOutput = false;
1592
- streamedFuncCalls.length = 0;
1593
- newMsgs.length = 0;
1838
+ return { usage: res.usage, llmGenModel: res.llmGenModel };
1594
1839
  };
1595
- const receiver = {
1596
- streamError: async (detail) => {
1597
- await dlg.streamError(detail);
1598
- },
1599
- thinkingStart: async () => {
1600
- throwIfAborted(abortSignal, dlg);
1601
- if (streamActive.kind !== 'idle') {
1602
- const detail = `Protocol violation: thinkingStart while ${streamActive.kind} is active`;
1603
- await dlg.streamError(detail);
1604
- throw new Error(detail);
1605
- }
1606
- streamActive = { kind: 'thinking' };
1607
- currentThinkingContent = '';
1608
- currentThinkingReasoning = undefined;
1609
- await dlg.thinkingStart();
1610
- },
1611
- thinkingChunk: async (chunk) => {
1612
- throwIfAborted(abortSignal, dlg);
1613
- currentThinkingContent += chunk;
1614
- await dlg.thinkingChunk(chunk);
1615
- },
1616
- thinkingFinish: async (reasoning) => {
1617
- throwIfAborted(abortSignal, dlg);
1618
- if (streamActive.kind !== 'thinking') {
1619
- const detail = `Protocol violation: thinkingFinish while ${streamActive.kind} is active`;
1620
- await dlg.streamError(detail);
1621
- throw new Error(detail);
1622
- }
1623
- streamActive = { kind: 'idle' };
1624
- if (reasoning)
1625
- currentThinkingReasoning = reasoning;
1626
- await dlg.thinkingFinish(reasoning);
1627
- if (currentThinkingContent.length > 0 || currentThinkingReasoning !== undefined) {
1628
- newMsgs.push({
1629
- type: 'thinking_msg',
1630
- role: 'assistant',
1631
- genseq: dlg.activeGenSeq,
1632
- content: currentThinkingContent,
1633
- reasoning: currentThinkingReasoning,
1634
- });
1635
- }
1636
- currentThinkingContent = '';
1637
- currentThinkingReasoning = undefined;
1638
- },
1639
- sayingStart: async () => {
1640
- throwIfAborted(abortSignal, dlg);
1641
- if (streamActive.kind !== 'idle') {
1642
- const detail = `Protocol violation: sayingStart while ${streamActive.kind} is active`;
1643
- await dlg.streamError(detail);
1644
- throw new Error(detail);
1645
- }
1646
- streamActive = { kind: 'saying' };
1647
- currentSayingContent = '';
1648
- await dlg.sayingStart();
1649
- },
1650
- sayingChunk: async (chunk) => {
1651
- throwIfAborted(abortSignal, dlg);
1652
- currentSayingContent += chunk;
1653
- await dlg.sayingChunk(chunk);
1654
- },
1655
- sayingFinish: async () => {
1656
- throwIfAborted(abortSignal, dlg);
1657
- if (streamActive.kind !== 'saying') {
1658
- const detail = `Protocol violation: sayingFinish while ${streamActive.kind} is active`;
1659
- await dlg.streamError(detail);
1660
- throw new Error(detail);
1661
- }
1662
- streamActive = { kind: 'idle' };
1663
- await dlg.sayingFinish();
1664
- const sayingMessage = {
1665
- type: 'saying_msg',
1666
- role: 'assistant',
1667
- genseq: dlg.activeGenSeq,
1668
- content: currentSayingContent,
1669
- };
1670
- newMsgs.push(sayingMessage);
1671
- streamAttemptSayingContent = currentSayingContent;
1672
- streamAttemptSayingGenseq = sayingMessage.genseq;
1673
- },
1674
- funcCall: async (callId, name, argsStr) => {
1675
- throwIfAborted(abortSignal, dlg);
1676
- streamedFuncCalls.push({
1677
- type: 'func_call_msg',
1678
- role: 'assistant',
1679
- genseq: dlg.activeGenSeq,
1680
- id: callId,
1681
- name,
1682
- arguments: argsStr,
1683
- });
1684
- },
1685
- webSearchCall: async (call) => {
1686
- throwIfAborted(abortSignal, dlg);
1687
- sawWebSearchSideChannelOutput = true;
1688
- await dlg.webSearchCall(projectLlmWebSearchCall(call));
1689
- },
1690
- nativeToolCall: async (call) => {
1691
- throwIfAborted(abortSignal, dlg);
1692
- sawNativeToolSideChannelOutput = true;
1693
- await dlg.nativeToolCall(call);
1694
- },
1695
- };
1696
- const res = await (0, runtime_1.runLlmRequestWithRetry)({
1697
- dlg,
1698
- provider,
1699
- modelId: model,
1700
- providerConfig: providerCfg,
1701
- abortSignal,
1702
- maxRetries: retryPolicy.maxRetries,
1703
- retryInitialDelayMs: retryPolicy.initialDelayMs,
1704
- retryConservativeDelayMs: retryPolicy.conservativeDelayMs,
1705
- retryBackoffMultiplier: retryPolicy.backoffMultiplier,
1706
- retryMaxDelayMs: retryPolicy.maxDelayMs,
1707
- classifyFailure: llmGen.classifyFailure?.bind(llmGen),
1708
- canRetry: () => true,
1709
- onRetry: rollbackStreamAttempt,
1710
- onGiveUp: rollbackStreamAttempt,
1711
- doRequest: async () => {
1712
- streamAttemptCourse = dlg.activeGenCourseOrUndefined ?? dlg.currentCourse;
1713
- streamAttemptCheckpointOffset = await persistence_1.DialogPersistence.captureCourseFileOffset(dlg.id, streamAttemptCourse, dlg.status);
1714
- streamActive = { kind: 'idle' };
1715
- currentThinkingContent = '';
1716
- currentThinkingReasoning = undefined;
1717
- currentSayingContent = '';
1718
- streamAttemptSayingContent = undefined;
1719
- streamAttemptSayingGenseq = undefined;
1720
- sawWebSearchSideChannelOutput = false;
1721
- sawNativeToolSideChannelOutput = false;
1722
- streamedFuncCalls.length = 0;
1723
- newMsgs.length = 0;
1724
- const streamResult = await llmGen.genToReceiver(providerCfg, agent, systemPrompt, funcTools, {
1725
- dialogSelfId: dlg.id.selfId,
1726
- dialogRootId: dlg.id.rootId,
1727
- promptCacheKey: `${dlg.id.selfId}:c${String(dlg.currentCourse)}`,
1728
- }, ctxMsgs, receiver, dlg.activeGenSeq, abortSignal);
1729
- const hasThinkingContent = currentThinkingContent.trim() !== '';
1730
- const hasSayingContent = (streamAttemptSayingContent ?? '').trim() !== '';
1731
- const hasFunctionCall = streamedFuncCalls.length > 0;
1732
- if (!hasThinkingContent &&
1733
- !hasSayingContent &&
1734
- !hasFunctionCall &&
1735
- !sawWebSearchSideChannelOutput &&
1736
- !sawNativeToolSideChannelOutput) {
1737
- throw {
1738
- status: 503,
1739
- code: KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE,
1740
- message: `LLM returned empty response (provider=${provider}, model=${model}, streaming=true).`,
1741
- };
1742
- }
1743
- return streamResult;
1744
- },
1745
- });
1746
- if (streamAttemptSayingContent !== undefined) {
1747
- lastAssistantSayingContent = streamAttemptSayingContent;
1748
- lastAssistantSayingGenseq =
1749
- streamAttemptSayingGenseq === undefined ? null : streamAttemptSayingGenseq;
1750
- lastAssistantReplyTarget = currentReplyTarget;
1840
+ const llmOutput = await streamOrBatch();
1841
+ if (typeof llmOutput.llmGenModel === 'string' && llmOutput.llmGenModel.trim() !== '') {
1842
+ llmGenModelForGen = llmOutput.llmGenModel.trim();
1751
1843
  }
1752
- return { usage: res.usage, llmGenModel: res.llmGenModel };
1753
- };
1754
- const llmOutput = await streamOrBatch();
1755
- if (typeof llmOutput.llmGenModel === 'string' && llmOutput.llmGenModel.trim() !== '') {
1756
- llmGenModelForGen = llmOutput.llmGenModel.trim();
1757
- }
1758
- contextHealthForGen = computeContextHealthSnapshot({
1759
- providerCfg,
1760
- model,
1761
- usage: llmOutput.usage,
1762
- });
1763
- dlg.setLastContextHealth(contextHealthForGen);
1764
- const batchOutputs = Array.isArray(llmOutput.batchOutputs) && llmOutput.batchOutputs.length > 0
1765
- ? llmOutput.batchOutputs
1766
- : Array.isArray(llmOutput.batchMessages)
1767
- ? llmOutput.batchMessages.map((message) => ({ kind: 'message', message }))
1768
- : [];
1769
- for (const output of batchOutputs) {
1770
- switch (output.kind) {
1771
- case 'message': {
1772
- const msg = output.message;
1773
- if (msg.type === 'thinking_msg' || msg.type === 'saying_msg') {
1774
- newMsgs.push(msg);
1775
- if (msg.type === 'thinking_msg') {
1776
- await (0, events_1.emitThinkingEvents)(dlg, msg.content, msg.reasoning);
1844
+ contextHealthForGen = computeContextHealthSnapshot({
1845
+ providerCfg,
1846
+ model,
1847
+ usage: llmOutput.usage,
1848
+ });
1849
+ dlg.setLastContextHealth(contextHealthForGen);
1850
+ const batchOutputs = Array.isArray(llmOutput.batchOutputs) && llmOutput.batchOutputs.length > 0
1851
+ ? llmOutput.batchOutputs
1852
+ : Array.isArray(llmOutput.batchMessages)
1853
+ ? llmOutput.batchMessages.map((message) => ({ kind: 'message', message }))
1854
+ : [];
1855
+ for (const output of batchOutputs) {
1856
+ switch (output.kind) {
1857
+ case 'message': {
1858
+ const msg = output.message;
1859
+ if (msg.type === 'thinking_msg' || msg.type === 'saying_msg') {
1860
+ newMsgs.push(msg);
1861
+ if (msg.type === 'thinking_msg') {
1862
+ await (0, events_1.emitThinkingEvents)(dlg, msg.content, msg.reasoning);
1863
+ }
1864
+ else {
1865
+ lastAssistantSayingContent = msg.content;
1866
+ lastAssistantSayingGenseq = msg.genseq;
1867
+ lastAssistantReplyTarget = currentReplyTarget;
1868
+ await emitAssistantSaying(dlg, msg.content);
1869
+ }
1870
+ break;
1777
1871
  }
1778
- else {
1779
- lastAssistantSayingContent = msg.content;
1780
- lastAssistantSayingGenseq = msg.genseq;
1781
- lastAssistantReplyTarget = currentReplyTarget;
1782
- await emitAssistantSaying(dlg, msg.content);
1872
+ if (msg.type === 'func_call_msg') {
1873
+ streamedFuncCalls.push(msg);
1783
1874
  }
1784
1875
  break;
1785
1876
  }
1786
- if (msg.type === 'func_call_msg') {
1787
- streamedFuncCalls.push(msg);
1877
+ case 'web_search_call': {
1878
+ sawWebSearchSideChannelOutput = true;
1879
+ await dlg.webSearchCall(projectLlmWebSearchCall(output.call));
1880
+ break;
1881
+ }
1882
+ case 'native_tool_call': {
1883
+ sawNativeToolSideChannelOutput = true;
1884
+ await dlg.nativeToolCall(output.call);
1885
+ break;
1886
+ }
1887
+ default: {
1888
+ const _exhaustive = output;
1889
+ throw new Error(`Unhandled batch output kind: ${String(_exhaustive)}`);
1788
1890
  }
1789
- break;
1790
- }
1791
- case 'web_search_call': {
1792
- sawWebSearchSideChannelOutput = true;
1793
- await dlg.webSearchCall(projectLlmWebSearchCall(output.call));
1794
- break;
1795
- }
1796
- case 'native_tool_call': {
1797
- sawNativeToolSideChannelOutput = true;
1798
- await dlg.nativeToolCall(output.call);
1799
- break;
1800
- }
1801
- default: {
1802
- const _exhaustive = output;
1803
- throw new Error(`Unhandled batch output kind: ${String(_exhaustive)}`);
1804
1891
  }
1805
1892
  }
1806
- }
1807
- const tellaskCallCount = policy.allowTellaskFunctions
1808
- ? streamedFuncCalls.filter((c) => c.name === 'tellask' ||
1809
- c.name === 'tellaskSessionless' ||
1810
- c.name === 'tellaskBack' ||
1811
- c.name === 'askHuman' ||
1812
- c.name === 'freshBootsReasoning').length
1813
- : 0;
1814
- const policyViolationKind = (0, guardrails_1.resolveKernelDriverPolicyViolationKind)({
1815
- policy,
1816
- tellaskCallCount,
1817
- functionCallCount: streamedFuncCalls.length,
1818
- });
1819
- if (policyViolationKind) {
1820
- const violationText = (0, driver_messages_1.formatDomindsNoteFbrToollessViolation)((0, work_language_1.getWorkLanguage)(), {
1821
- kind: policyViolationKind,
1893
+ const tellaskCallCount = policy.allowTellaskFunctions
1894
+ ? streamedFuncCalls.filter((c) => c.name === 'tellask' ||
1895
+ c.name === 'tellaskSessionless' ||
1896
+ c.name === 'tellaskBack' ||
1897
+ c.name === 'askHuman' ||
1898
+ c.name === 'freshBootsReasoning').length
1899
+ : 0;
1900
+ const policyViolationKind = (0, guardrails_1.resolveKernelDriverPolicyViolationKind)({
1901
+ policy,
1902
+ tellaskCallCount,
1903
+ functionCallCount: streamedFuncCalls.length,
1822
1904
  });
1823
- const genseq = dlg.activeGenSeq;
1824
- const violationMsg = {
1825
- type: 'saying_msg',
1826
- role: 'assistant',
1827
- genseq,
1828
- content: violationText,
1829
- };
1830
- await emitAssistantSaying(dlg, violationText);
1831
- newMsgs.push(violationMsg);
1832
- await dlg.addChatMessages(...newMsgs);
1833
- lastAssistantSayingContent = violationText;
1834
- lastAssistantSayingGenseq = genseq;
1835
- lastAssistantReplyTarget = currentReplyTarget;
1836
- const persistedFbrState = await loadDialogFbrState(dlg);
1837
- if (!persistedFbrState) {
1838
- return {
1839
- lastAssistantSayingContent,
1840
- lastAssistantSayingGenseq,
1841
- lastFunctionCallGenseq,
1842
- lastAssistantReplyTarget,
1905
+ if (policyViolationKind) {
1906
+ const violationText = (0, driver_messages_1.formatDomindsNoteFbrToollessViolation)((0, work_language_1.getWorkLanguage)(), {
1907
+ kind: policyViolationKind,
1908
+ });
1909
+ const genseq = dlg.activeGenSeq;
1910
+ const violationMsg = {
1911
+ type: 'saying_msg',
1912
+ role: 'assistant',
1913
+ genseq,
1914
+ content: violationText,
1915
+ };
1916
+ await emitAssistantSaying(dlg, violationText);
1917
+ newMsgs.push(violationMsg);
1918
+ await dlg.addChatMessages(...newMsgs);
1919
+ lastAssistantSayingContent = violationText;
1920
+ lastAssistantSayingGenseq = genseq;
1921
+ lastAssistantReplyTarget = currentReplyTarget;
1922
+ const persistedFbrState = await loadDialogFbrState(dlg);
1923
+ if (!persistedFbrState) {
1924
+ return {
1925
+ lastAssistantSayingContent,
1926
+ lastAssistantSayingGenseq,
1927
+ lastFunctionCallGenseq,
1928
+ lastAssistantReplyTarget,
1929
+ };
1930
+ }
1931
+ const nextFbrState = (0, fbr_1.advanceFbrState)(persistedFbrState);
1932
+ if (nextFbrState) {
1933
+ if (!isFbrSubdialogDialog(dlg)) {
1934
+ throw new Error(`kernel-driver FBR invariant violation: persisted FBR state on non-FBR dialog (${dlg.id.valueOf()})`);
1935
+ }
1936
+ await persistDialogFbrState(dlg, nextFbrState);
1937
+ dlg.setFbrConclusionToolsEnabled((0, fbr_1.isFbrFinalizationState)(nextFbrState));
1938
+ pendingPrompt = buildKernelDriverFbrPrompt(dlg, nextFbrState);
1939
+ continue;
1940
+ }
1941
+ fbrConclusion = {
1942
+ responseText: (0, fbr_1.buildProgrammaticFbrUnreasonableSituationContent)({
1943
+ language: (0, work_language_1.getWorkLanguage)(),
1944
+ finalizationAttempts: persistedFbrState.effort,
1945
+ }),
1946
+ responseGenseq: genseq,
1843
1947
  };
1844
- }
1845
- const nextFbrState = (0, fbr_1.advanceFbrState)(persistedFbrState);
1846
- if (nextFbrState) {
1847
1948
  if (!isFbrSubdialogDialog(dlg)) {
1848
1949
  throw new Error(`kernel-driver FBR invariant violation: persisted FBR state on non-FBR dialog (${dlg.id.valueOf()})`);
1849
1950
  }
1850
- await persistDialogFbrState(dlg, nextFbrState);
1851
- dlg.setFbrConclusionToolsEnabled((0, fbr_1.isFbrFinalizationState)(nextFbrState));
1852
- pendingPrompt = buildKernelDriverFbrPrompt(dlg, nextFbrState);
1853
- continue;
1951
+ await persistDialogFbrState(dlg, undefined);
1952
+ dlg.setFbrConclusionToolsEnabled(false);
1953
+ break;
1854
1954
  }
1855
- fbrConclusion = {
1856
- responseText: (0, fbr_1.buildProgrammaticFbrUnreasonableSituationContent)({
1857
- language: (0, work_language_1.getWorkLanguage)(),
1858
- finalizationAttempts: persistedFbrState.effort,
1859
- }),
1860
- responseGenseq: genseq,
1861
- };
1862
- if (!isFbrSubdialogDialog(dlg)) {
1863
- throw new Error(`kernel-driver FBR invariant violation: persisted FBR state on non-FBR dialog (${dlg.id.valueOf()})`);
1955
+ for (const call of streamedFuncCalls) {
1956
+ const rawCallGenseq = call.genseq;
1957
+ if (!Number.isFinite(rawCallGenseq) || rawCallGenseq <= 0)
1958
+ continue;
1959
+ const callGenseq = Math.floor(rawCallGenseq);
1960
+ if (lastFunctionCallGenseq === null || callGenseq > lastFunctionCallGenseq) {
1961
+ lastFunctionCallGenseq = callGenseq;
1962
+ }
1864
1963
  }
1865
- await persistDialogFbrState(dlg, undefined);
1866
- dlg.setFbrConclusionToolsEnabled(false);
1867
- break;
1868
- }
1869
- for (const call of streamedFuncCalls) {
1870
- const rawCallGenseq = call.genseq;
1871
- if (!Number.isFinite(rawCallGenseq) || rawCallGenseq <= 0)
1872
- continue;
1873
- const callGenseq = Math.floor(rawCallGenseq);
1874
- if (lastFunctionCallGenseq === null || callGenseq > lastFunctionCallGenseq) {
1875
- lastFunctionCallGenseq = callGenseq;
1964
+ const routed = await executeFunctionRound({
1965
+ dlg,
1966
+ agent,
1967
+ agentTools,
1968
+ funcCalls: streamedFuncCalls,
1969
+ callbacks,
1970
+ abortSignal,
1971
+ allowTellaskFunctions: policy.allowTellaskFunctions,
1972
+ });
1973
+ if (routed.tellaskToolOutputs.length > 0) {
1974
+ newMsgs.push(...routed.tellaskToolOutputs);
1876
1975
  }
1877
- }
1878
- const routed = await executeFunctionRound({
1879
- dlg,
1880
- agent,
1881
- agentTools,
1882
- funcCalls: streamedFuncCalls,
1883
- callbacks,
1884
- abortSignal,
1885
- allowTellaskFunctions: policy.allowTellaskFunctions,
1886
- });
1887
- if (routed.tellaskToolOutputs.length > 0) {
1888
- newMsgs.push(...routed.tellaskToolOutputs);
1889
- }
1890
- if (routed.pairedMessages.length > 0) {
1891
- newMsgs.push(...routed.pairedMessages);
1892
- }
1893
- await dlg.addChatMessages(...newMsgs);
1894
- const assistantSayingRoundSignature = extractAssistantSayingRoundSignature(newMsgs);
1895
- const identicalUpdatePlanRoundSignature = routed.updatePlanOnlyRawArgumentsSignature !== undefined &&
1896
- assistantSayingRoundSignature !== undefined
1897
- ? JSON.stringify({
1898
- assistantSayingRoundSignature,
1899
- updatePlanRawArgumentsSignature: routed.updatePlanOnlyRawArgumentsSignature,
1900
- })
1901
- : undefined;
1902
- if (identicalUpdatePlanRoundSignature !== undefined) {
1903
- if (identicalUpdatePlanRoundSignature === lastIdenticalUpdatePlanRoundSignature) {
1904
- consecutiveIdenticalUpdatePlanOnlyRounds += 1;
1976
+ if (routed.pairedMessages.length > 0) {
1977
+ newMsgs.push(...routed.pairedMessages);
1978
+ }
1979
+ await dlg.addChatMessages(...newMsgs);
1980
+ const assistantSayingRoundSignature = extractAssistantSayingRoundSignature(newMsgs);
1981
+ const identicalUpdatePlanRoundSignature = routed.updatePlanOnlyRound
1982
+ ? JSON.stringify({
1983
+ assistantSayingRoundSignature: assistantSayingRoundSignature ?? '',
1984
+ })
1985
+ : undefined;
1986
+ if (identicalUpdatePlanRoundSignature !== undefined) {
1987
+ if (identicalUpdatePlanRoundSignature === lastIdenticalUpdatePlanRoundSignature) {
1988
+ consecutiveIdenticalUpdatePlanOnlyRounds += 1;
1989
+ }
1990
+ else {
1991
+ consecutiveIdenticalUpdatePlanOnlyRounds = 1;
1992
+ lastIdenticalUpdatePlanRoundSignature = identicalUpdatePlanRoundSignature;
1993
+ }
1905
1994
  }
1906
1995
  else {
1907
- consecutiveIdenticalUpdatePlanOnlyRounds = 1;
1908
- lastIdenticalUpdatePlanRoundSignature = identicalUpdatePlanRoundSignature;
1996
+ consecutiveIdenticalUpdatePlanOnlyRounds = 0;
1997
+ lastIdenticalUpdatePlanRoundSignature = undefined;
1909
1998
  }
1910
- }
1911
- else {
1912
- consecutiveIdenticalUpdatePlanOnlyRounds = 0;
1913
- lastIdenticalUpdatePlanRoundSignature = undefined;
1914
- }
1915
- const persistedFbrState = await loadDialogFbrState(dlg);
1916
- if (persistedFbrState) {
1917
- if (persistedFbrState.phase === 'finalization') {
1918
- const inspection = (0, fbr_1.inspectFbrConclusionAttempt)(newMsgs);
1919
- if (inspection.kind === 'accepted') {
1920
- log_1.log.debug('kernel-driver accepted FBR conclusion attempt', undefined, {
1921
- dialogId: dlg.id.valueOf(),
1922
- toolName: inspection.toolName,
1923
- callId: inspection.callId,
1924
- });
1925
- fbrConclusion = {
1926
- responseText: inspection.content,
1927
- responseGenseq: inspection.genseq,
1928
- };
1999
+ const persistedFbrState = await loadDialogFbrState(dlg);
2000
+ if (persistedFbrState) {
2001
+ if (persistedFbrState.phase === 'finalization') {
2002
+ const inspection = (0, fbr_1.inspectFbrConclusionAttempt)(newMsgs);
2003
+ if (inspection.kind === 'accepted') {
2004
+ log_1.log.debug('kernel-driver accepted FBR conclusion attempt', undefined, {
2005
+ dialogId: dlg.id.valueOf(),
2006
+ toolName: inspection.toolName,
2007
+ callId: inspection.callId,
2008
+ });
2009
+ fbrConclusion = {
2010
+ responseText: inspection.content,
2011
+ responseGenseq: inspection.genseq,
2012
+ };
2013
+ if (!isFbrSubdialogDialog(dlg)) {
2014
+ throw new Error(`kernel-driver FBR invariant violation: persisted FBR state on non-FBR dialog (${dlg.id.valueOf()})`);
2015
+ }
2016
+ await persistDialogFbrState(dlg, undefined);
2017
+ dlg.setFbrConclusionToolsEnabled(false);
2018
+ break;
2019
+ }
2020
+ if (inspection.kind === 'rejected') {
2021
+ const detail = `FBR conclusion attempt rejected: ${inspection.reason}`;
2022
+ await dlg.streamError(detail);
2023
+ log_1.log.warn(detail, undefined, {
2024
+ rootId: dlg.id.rootId,
2025
+ selfId: dlg.id.selfId,
2026
+ });
2027
+ }
2028
+ }
2029
+ const nextFbrState = (0, fbr_1.advanceFbrState)(persistedFbrState);
2030
+ if (nextFbrState) {
1929
2031
  if (!isFbrSubdialogDialog(dlg)) {
1930
2032
  throw new Error(`kernel-driver FBR invariant violation: persisted FBR state on non-FBR dialog (${dlg.id.valueOf()})`);
1931
2033
  }
1932
- await persistDialogFbrState(dlg, undefined);
1933
- dlg.setFbrConclusionToolsEnabled(false);
1934
- break;
1935
- }
1936
- if (inspection.kind === 'rejected') {
1937
- const detail = `FBR conclusion attempt rejected: ${inspection.reason}`;
1938
- await dlg.streamError(detail);
1939
- log_1.log.warn(detail, undefined, {
1940
- rootId: dlg.id.rootId,
1941
- selfId: dlg.id.selfId,
1942
- });
2034
+ await persistDialogFbrState(dlg, nextFbrState);
2035
+ dlg.setFbrConclusionToolsEnabled((0, fbr_1.isFbrFinalizationState)(nextFbrState));
2036
+ pendingPrompt = buildKernelDriverFbrPrompt(dlg, nextFbrState);
2037
+ continue;
1943
2038
  }
1944
- }
1945
- const nextFbrState = (0, fbr_1.advanceFbrState)(persistedFbrState);
1946
- if (nextFbrState) {
2039
+ fbrConclusion = {
2040
+ responseText: (0, fbr_1.buildProgrammaticFbrUnreasonableSituationContent)({
2041
+ language: (0, work_language_1.getWorkLanguage)(),
2042
+ finalizationAttempts: persistedFbrState.effort,
2043
+ }),
2044
+ responseGenseq: lastAssistantSayingGenseq ??
2045
+ lastFunctionCallGenseq ??
2046
+ dlg.activeGenSeqOrUndefined ??
2047
+ 1,
2048
+ };
1947
2049
  if (!isFbrSubdialogDialog(dlg)) {
1948
2050
  throw new Error(`kernel-driver FBR invariant violation: persisted FBR state on non-FBR dialog (${dlg.id.valueOf()})`);
1949
2051
  }
1950
- await persistDialogFbrState(dlg, nextFbrState);
1951
- dlg.setFbrConclusionToolsEnabled((0, fbr_1.isFbrFinalizationState)(nextFbrState));
1952
- pendingPrompt = buildKernelDriverFbrPrompt(dlg, nextFbrState);
2052
+ await persistDialogFbrState(dlg, undefined);
2053
+ dlg.setFbrConclusionToolsEnabled(false);
2054
+ break;
2055
+ }
2056
+ if (routed.shouldStopAfterReplyTool) {
2057
+ log_1.log.debug('kernel-driver stop round after explicit replyTellask* tool', undefined, {
2058
+ dialogId: dlg.id.valueOf(),
2059
+ toolNames: streamedFuncCalls
2060
+ .filter((call) => call.name === 'replyTellask' ||
2061
+ call.name === 'replyTellaskSessionless' ||
2062
+ call.name === 'replyTellaskBack')
2063
+ .map((call) => call.name),
2064
+ });
2065
+ break;
2066
+ }
2067
+ if (dlg.hasUpNext()) {
2068
+ pendingPrompt = resolveUpNextPrompt(dlg);
1953
2069
  continue;
1954
2070
  }
1955
- fbrConclusion = {
1956
- responseText: (0, fbr_1.buildProgrammaticFbrUnreasonableSituationContent)({
1957
- language: (0, work_language_1.getWorkLanguage)(),
1958
- finalizationAttempts: persistedFbrState.effort,
1959
- }),
1960
- responseGenseq: lastAssistantSayingGenseq ??
1961
- lastFunctionCallGenseq ??
1962
- dlg.activeGenSeqOrUndefined ??
1963
- 1,
1964
- };
1965
- if (!isFbrSubdialogDialog(dlg)) {
1966
- throw new Error(`kernel-driver FBR invariant violation: persisted FBR state on non-FBR dialog (${dlg.id.valueOf()})`);
2071
+ if (dlg.remindersVer > pubRemindersVer) {
2072
+ await dlg.processReminderUpdates();
2073
+ pubRemindersVer = dlg.remindersVer;
1967
2074
  }
1968
- await persistDialogFbrState(dlg, undefined);
1969
- dlg.setFbrConclusionToolsEnabled(false);
1970
- break;
1971
- }
1972
- if (routed.shouldStopAfterReplyTool) {
1973
- log_1.log.debug('kernel-driver stop round after explicit replyTellask* tool', undefined, {
1974
- dialogId: dlg.id.valueOf(),
1975
- toolNames: streamedFuncCalls
1976
- .filter((call) => call.name === 'replyTellask' ||
1977
- call.name === 'replyTellaskSessionless' ||
1978
- call.name === 'replyTellaskBack')
1979
- .map((call) => call.name),
1980
- });
1981
- break;
1982
- }
1983
- if (dlg.hasUpNext()) {
1984
- pendingPrompt = resolveUpNextPrompt(dlg);
1985
- continue;
1986
- }
1987
- if (dlg.remindersVer > pubRemindersVer) {
1988
- await dlg.processReminderUpdates();
1989
- pubRemindersVer = dlg.remindersVer;
1990
- }
1991
- // Tool execution may have created pending Q4H/subdialogs mid-round. Respect the
1992
- // dialog's actual suspension state here so auto-continue is decided in one place.
1993
- const suspensionAfterToolRound = await dlg.getSuspensionStatus({
1994
- allowPendingSubdialogs: routed.hasImmediateFollowupToolCalls,
1995
- });
1996
- if (!suspensionAfterToolRound.canDrive) {
1997
- await resetDiligenceBudgetAfterQ4H(dlg, team);
1998
- break;
1999
- }
2000
- if (consecutiveIdenticalUpdatePlanOnlyRounds >=
2001
- KERNEL_DRIVER_IDENTICAL_UPDATE_PLAN_ONLY_STOP_THRESHOLD) {
2002
- const detail = formatIdenticalUpdatePlanLoopStopDetail((0, work_language_1.getWorkLanguage)());
2003
- log_1.log.error(detail, new Error('kernel_driver_identical_update_plan_loop_stopped'), {
2004
- dialogId: dlg.id.valueOf(),
2005
- rootId: dlg.id.rootId,
2006
- selfId: dlg.id.selfId,
2007
- course: dlg.currentCourse,
2008
- genseq: dlg.activeGenSeqOrUndefined ?? null,
2009
- consecutiveIdenticalUpdatePlanOnlyRounds,
2010
- identicalUpdatePlanRoundSignature,
2011
- });
2012
- throw new KernelDriverInterruptedError({ kind: 'system_stop', detail });
2013
- }
2014
- // Start an immediate post-tool generation only when this round produced tool outputs that
2015
- // warrant same-drive LLM reaction right away. Provider-native side-channel UI events are
2016
- // meaningful output, but they are not transcript/context inputs and therefore must not
2017
- // trigger another immediate generation round by themselves.
2018
- const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls || routed.tellaskToolOutputs.length > 0;
2019
- if (!shouldStartImmediatePostToolGeneration) {
2020
- const healthFirst = await maybeContinueWithHealthPromptBeforeDiligence({
2021
- dlg,
2022
- providerCfg,
2023
- model,
2075
+ // Tool execution may have created pending Q4H/subdialogs mid-round. Respect the
2076
+ // dialog's actual suspension state here so auto-continue is decided in one place.
2077
+ const suspensionAfterToolRound = await dlg.getSuspensionStatus({
2078
+ allowPendingSubdialogs: routed.hasImmediateFollowupToolCalls,
2024
2079
  });
2025
- if (healthFirst.kind === 'health_continue') {
2026
- pendingPrompt = healthFirst.prompt;
2027
- if (healthFirst.resetTaskdoc) {
2028
- skipTaskdocForThisDrive = false;
2029
- }
2030
- continue;
2080
+ if (!suspensionAfterToolRound.canDrive) {
2081
+ await resetDiligenceBudgetAfterQ4H(dlg, team);
2082
+ break;
2083
+ }
2084
+ if (consecutiveIdenticalUpdatePlanOnlyRounds >=
2085
+ KERNEL_DRIVER_IDENTICAL_UPDATE_PLAN_ONLY_STOP_THRESHOLD) {
2086
+ const detail = formatIdenticalUpdatePlanLoopStopDetail((0, work_language_1.getWorkLanguage)());
2087
+ log_1.log.error(detail, new Error('kernel_driver_identical_update_plan_loop_stopped'), {
2088
+ dialogId: dlg.id.valueOf(),
2089
+ rootId: dlg.id.rootId,
2090
+ selfId: dlg.id.selfId,
2091
+ course: dlg.currentCourse,
2092
+ genseq: dlg.activeGenSeqOrUndefined ?? null,
2093
+ consecutiveIdenticalUpdatePlanOnlyRounds,
2094
+ identicalUpdatePlanRoundSignature,
2095
+ });
2096
+ throw new KernelDriverInterruptedError({
2097
+ kind: 'system_stop',
2098
+ detail,
2099
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({
2100
+ detail,
2101
+ kind: 'identical_update_plan_loop',
2102
+ }),
2103
+ });
2031
2104
  }
2032
- if (healthFirst.kind === 'health_suspend') {
2105
+ // Start an immediate post-tool generation only when this round produced tool outputs that
2106
+ // warrant same-drive LLM reaction right away. Provider-native side-channel UI events are
2107
+ // meaningful output, but they are not transcript/context inputs and therefore must not
2108
+ // trigger another immediate generation round by themselves.
2109
+ const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls || routed.tellaskToolOutputs.length > 0;
2110
+ if (!shouldStartImmediatePostToolGeneration) {
2111
+ const healthFirst = await maybeContinueWithHealthPromptBeforeDiligence({
2112
+ dlg,
2113
+ providerCfg,
2114
+ model,
2115
+ });
2116
+ if (healthFirst.kind === 'health_continue') {
2117
+ pendingPrompt = healthFirst.prompt;
2118
+ if (healthFirst.resetTaskdoc) {
2119
+ skipTaskdocForThisDrive = false;
2120
+ }
2121
+ continue;
2122
+ }
2123
+ if (healthFirst.kind === 'health_suspend') {
2124
+ break;
2125
+ }
2126
+ const next = await maybeContinueWithDiligencePrompt({
2127
+ dlg,
2128
+ team,
2129
+ suppressDiligencePushForDrive: suppressDiligencePushForDrive,
2130
+ });
2131
+ if (next.kind === 'continue') {
2132
+ pendingPrompt = next.prompt;
2133
+ continue;
2134
+ }
2033
2135
  break;
2034
2136
  }
2035
- const next = await maybeContinueWithDiligencePrompt({
2036
- dlg,
2037
- team,
2038
- suppressDiligencePushForDrive: suppressDiligencePushForDrive,
2039
- });
2040
- if (next.kind === 'continue') {
2041
- pendingPrompt = next.prompt;
2137
+ if (shouldStartImmediatePostToolGeneration) {
2042
2138
  continue;
2043
2139
  }
2044
- break;
2045
2140
  }
2046
- if (shouldStartImmediatePostToolGeneration) {
2047
- continue;
2141
+ finally {
2142
+ await dlg.notifyGeneratingFinish(contextHealthForGen, llmGenModelForGen);
2048
2143
  }
2049
2144
  }
2050
- finally {
2051
- await dlg.notifyGeneratingFinish(contextHealthForGen, llmGenModelForGen);
2052
- }
2053
- }
2054
- throwIfAborted(abortSignal, dlg);
2055
- finalDisplayState = await (0, dialog_display_state_1.computeIdleDisplayState)(dlg);
2056
- }
2057
- catch (err) {
2058
- const stopRequested = (0, dialog_display_state_1.getStopRequestedReason)(dlg.id);
2059
- const interruptedReason = err instanceof runtime_1.LlmRetryStoppedError
2060
- ? err.reason
2061
- : err instanceof KernelDriverInterruptedError
2062
- ? err.reason
2063
- : abortSignal.aborted
2064
- ? stopRequested === 'emergency_stop'
2065
- ? { kind: 'emergency_stop' }
2066
- : stopRequested === 'user_stop'
2067
- ? { kind: 'user_stop' }
2068
- : { kind: 'system_stop', detail: 'Aborted.' }
2069
- : undefined;
2070
- if (interruptedReason) {
2071
- finalDisplayState = {
2072
- kind: 'stopped',
2073
- reason: interruptedReason,
2074
- continueEnabled: resolveStoppedContinueEnabled(interruptedReason),
2075
- };
2076
- (0, dialog_display_state_1.broadcastDisplayStateMarker)(dlg.id, { kind: 'interrupted', reason: interruptedReason });
2145
+ throwIfAborted(abortSignal, dlg);
2146
+ finalDisplayState = await (0, dialog_display_state_1.computeIdleDisplayState)(dlg);
2147
+ break driveCoreLoop;
2077
2148
  }
2078
- else {
2079
- const errText = (0, log_1.extractErrorDetails)(err).message;
2080
- try {
2081
- await dlg.streamError(errText);
2082
- }
2083
- catch {
2084
- // best-effort
2149
+ catch (err) {
2150
+ if (err instanceof runtime_1.LlmRetryStoppedError && retryStoppedRecoveryPrompt !== undefined) {
2151
+ pendingPrompt = retryStoppedRecoveryPrompt;
2152
+ retryStoppedRecoveryPrompt = undefined;
2153
+ continue driveCoreLoop;
2085
2154
  }
2086
- finalDisplayState = {
2087
- kind: 'stopped',
2088
- reason: { kind: 'system_stop', detail: errText },
2089
- continueEnabled: true,
2090
- };
2091
- (0, dialog_display_state_1.broadcastDisplayStateMarker)(dlg.id, {
2092
- kind: 'interrupted',
2093
- reason: { kind: 'system_stop', detail: errText },
2094
- });
2095
- }
2096
- }
2097
- finally {
2098
- if (!finalDisplayState) {
2099
- try {
2100
- finalDisplayState = await (0, dialog_display_state_1.computeIdleDisplayState)(dlg);
2155
+ retryStoppedRecoveryPrompt = undefined;
2156
+ const stopRequested = (0, dialog_display_state_1.getStopRequestedReason)(dlg.id);
2157
+ const interruptedReason = err instanceof runtime_1.LlmRetryStoppedError
2158
+ ? err.reason
2159
+ : err instanceof KernelDriverInterruptedError
2160
+ ? err.reason
2161
+ : abortSignal.aborted
2162
+ ? stopRequested === 'emergency_stop'
2163
+ ? { kind: 'emergency_stop' }
2164
+ : stopRequested === 'user_stop'
2165
+ ? { kind: 'user_stop' }
2166
+ : buildAbortedSystemStopReason()
2167
+ : undefined;
2168
+ if (interruptedReason) {
2169
+ finalDisplayState = {
2170
+ kind: 'stopped',
2171
+ reason: interruptedReason,
2172
+ continueEnabled: resolveStoppedContinueEnabled(interruptedReason),
2173
+ };
2174
+ (0, dialog_display_state_1.broadcastDisplayStateMarker)(dlg.id, { kind: 'interrupted', reason: interruptedReason });
2101
2175
  }
2102
- catch (stateErr) {
2103
- log_1.log.warn('kernel-driver failed to compute final display-state projection; falling back to idle', stateErr, {
2104
- dialogId: dlg.id.valueOf(),
2176
+ else {
2177
+ const llmRequestFailure = err instanceof runtime_1.LlmRequestFailedError ? err : undefined;
2178
+ const emittedStreamError = err instanceof gen_1.LlmStreamErrorEmittedError ? err : undefined;
2179
+ const errText = llmRequestFailure?.detail ??
2180
+ emittedStreamError?.detail ??
2181
+ (0, log_1.extractErrorDetails)(err).message;
2182
+ if (!llmRequestFailure?.streamErrorEmitted && !emittedStreamError) {
2183
+ try {
2184
+ await dlg.streamError(errText);
2185
+ }
2186
+ catch {
2187
+ // best-effort
2188
+ }
2189
+ }
2190
+ finalDisplayState = {
2191
+ kind: 'stopped',
2192
+ reason: (llmRequestFailure?.i18nStopReason ?? emittedStreamError?.i18nStopReason) !== undefined
2193
+ ? {
2194
+ kind: 'system_stop',
2195
+ detail: errText,
2196
+ i18nStopReason: llmRequestFailure?.i18nStopReason ??
2197
+ emittedStreamError?.i18nStopReason ??
2198
+ (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({ detail: errText }),
2199
+ }
2200
+ : {
2201
+ kind: 'system_stop',
2202
+ detail: errText,
2203
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({ detail: errText }),
2204
+ },
2205
+ continueEnabled: true,
2206
+ };
2207
+ (0, dialog_display_state_1.broadcastDisplayStateMarker)(dlg.id, {
2208
+ kind: 'interrupted',
2209
+ reason: (llmRequestFailure?.i18nStopReason ?? emittedStreamError?.i18nStopReason) !== undefined
2210
+ ? {
2211
+ kind: 'system_stop',
2212
+ detail: errText,
2213
+ i18nStopReason: llmRequestFailure?.i18nStopReason ??
2214
+ emittedStreamError?.i18nStopReason ??
2215
+ (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({ detail: errText }),
2216
+ }
2217
+ : {
2218
+ kind: 'system_stop',
2219
+ detail: errText,
2220
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({ detail: errText }),
2221
+ },
2105
2222
  });
2106
- finalDisplayState = { kind: 'idle_waiting_user' };
2107
2223
  }
2224
+ break driveCoreLoop;
2108
2225
  }
2109
- if (abortSignal.aborted &&
2110
- finalDisplayState.kind !== 'stopped' &&
2111
- finalDisplayState.kind !== 'dead') {
2112
- const stopRequested = (0, dialog_display_state_1.getStopRequestedReason)(dlg.id);
2113
- const lateInterruptedReason = stopRequested === 'emergency_stop'
2114
- ? { kind: 'emergency_stop' }
2115
- : stopRequested === 'user_stop'
2116
- ? { kind: 'user_stop' }
2117
- : { kind: 'system_stop', detail: 'Aborted.' };
2118
- finalDisplayState = {
2119
- kind: 'stopped',
2120
- reason: lateInterruptedReason,
2121
- continueEnabled: resolveStoppedContinueEnabled(lateInterruptedReason),
2122
- };
2123
- (0, dialog_display_state_1.broadcastDisplayStateMarker)(dlg.id, { kind: 'interrupted', reason: lateInterruptedReason });
2124
- }
2226
+ }
2227
+ if (!finalDisplayState) {
2125
2228
  try {
2126
- const latest = await persistence_1.DialogPersistence.loadDialogLatest(dlg.id, 'running');
2127
- if (dlg.id.selfId !== dlg.id.rootId && latest?.executionMarker?.kind === 'dead') {
2128
- finalDisplayState = { kind: 'dead', reason: latest.executionMarker.reason };
2129
- }
2229
+ finalDisplayState = await (0, dialog_display_state_1.computeIdleDisplayState)(dlg);
2130
2230
  }
2131
- catch (err) {
2132
- log_1.log.warn('kernel-driver failed to re-check displayState before finalizing', err, {
2231
+ catch (stateErr) {
2232
+ log_1.log.warn('kernel-driver failed to compute final display-state projection; falling back to idle', stateErr, {
2133
2233
  dialogId: dlg.id.valueOf(),
2134
2234
  });
2235
+ finalDisplayState = { kind: 'idle_waiting_user' };
2135
2236
  }
2136
- if (finalDisplayState.kind === 'stopped') {
2137
- await (0, dialog_display_state_1.setDialogExecutionMarker)(dlg.id, {
2138
- kind: 'interrupted',
2139
- reason: finalDisplayState.reason,
2140
- });
2141
- }
2142
- else if (finalDisplayState.kind !== 'dead') {
2143
- await (0, dialog_display_state_1.clearDialogInterruptedExecutionMarker)(dlg.id);
2237
+ }
2238
+ if (abortSignal.aborted &&
2239
+ finalDisplayState.kind !== 'stopped' &&
2240
+ finalDisplayState.kind !== 'dead') {
2241
+ const stopRequested = (0, dialog_display_state_1.getStopRequestedReason)(dlg.id);
2242
+ const lateInterruptedReason = stopRequested === 'emergency_stop'
2243
+ ? { kind: 'emergency_stop' }
2244
+ : stopRequested === 'user_stop'
2245
+ ? { kind: 'user_stop' }
2246
+ : buildAbortedSystemStopReason();
2247
+ finalDisplayState = {
2248
+ kind: 'stopped',
2249
+ reason: lateInterruptedReason,
2250
+ continueEnabled: resolveStoppedContinueEnabled(lateInterruptedReason),
2251
+ };
2252
+ (0, dialog_display_state_1.broadcastDisplayStateMarker)(dlg.id, { kind: 'interrupted', reason: lateInterruptedReason });
2253
+ }
2254
+ try {
2255
+ const latest = await persistence_1.DialogPersistence.loadDialogLatest(dlg.id, 'running');
2256
+ if (dlg.id.selfId !== dlg.id.rootId && latest?.executionMarker?.kind === 'dead') {
2257
+ finalDisplayState = { kind: 'dead', reason: latest.executionMarker.reason };
2144
2258
  }
2145
- await (0, dialog_display_state_1.setDialogDisplayState)(dlg.id, finalDisplayState);
2146
2259
  }
2260
+ catch (err) {
2261
+ log_1.log.warn('kernel-driver failed to re-check displayState before finalizing', err, {
2262
+ dialogId: dlg.id.valueOf(),
2263
+ });
2264
+ }
2265
+ if (finalDisplayState.kind === 'stopped') {
2266
+ await (0, dialog_display_state_1.setDialogExecutionMarker)(dlg.id, {
2267
+ kind: 'interrupted',
2268
+ reason: finalDisplayState.reason,
2269
+ });
2270
+ }
2271
+ else if (finalDisplayState.kind !== 'dead') {
2272
+ await (0, dialog_display_state_1.clearDialogInterruptedExecutionMarker)(dlg.id);
2273
+ }
2274
+ await (0, dialog_display_state_1.setDialogDisplayState)(dlg.id, finalDisplayState);
2147
2275
  return {
2148
2276
  lastAssistantSayingContent,
2149
2277
  lastAssistantSayingGenseq,