dominds 1.13.2 → 1.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/dist/bootstrap/global-dialog-event-broadcaster.d.ts +18 -0
  2. package/dist/bootstrap/global-dialog-event-broadcaster.js +81 -0
  3. package/dist/dialog-fork.js +13 -12
  4. package/dist/dialog.d.ts +61 -50
  5. package/dist/dialog.js +284 -78
  6. package/dist/docs/dialog-system.md +12 -0
  7. package/dist/docs/dialog-system.zh.md +12 -0
  8. package/dist/docs/dominds-terminology.md +17 -0
  9. package/dist/docs/issues/global-dialog-event-broadcaster-missing.md +128 -0
  10. package/dist/docs/llm-provider-isolation.md +35 -0
  11. package/dist/docs/llm-provider-isolation.zh.md +35 -0
  12. package/dist/llm/client.d.ts +2 -1
  13. package/dist/llm/defaults.yaml +118 -4
  14. package/dist/llm/gen/anthropic.js +2 -4
  15. package/dist/llm/gen/codex.d.ts +11 -0
  16. package/dist/llm/gen/codex.js +41 -31
  17. package/dist/llm/gen/failure-classifier.js +17 -0
  18. package/dist/llm/gen/mock.js +45 -21
  19. package/dist/llm/gen/openai-compatible.d.ts +2 -0
  20. package/dist/llm/gen/openai-compatible.js +43 -38
  21. package/dist/llm/gen/openai.d.ts +3 -1
  22. package/dist/llm/gen/openai.js +888 -71
  23. package/dist/llm/gen/tool-call-context.d.ts +7 -2
  24. package/dist/llm/gen/tool-call-context.js +55 -13
  25. package/dist/llm/gen.d.ts +60 -3
  26. package/dist/llm/kernel-driver/context.js +1 -1
  27. package/dist/llm/kernel-driver/drive.js +374 -348
  28. package/dist/llm/kernel-driver/flow.js +3 -3
  29. package/dist/llm/kernel-driver/guardrails.d.ts +1 -1
  30. package/dist/llm/kernel-driver/guardrails.js +4 -4
  31. package/dist/llm/kernel-driver/runtime.js +11 -29
  32. package/dist/llm/kernel-driver/subdialog.js +56 -5
  33. package/dist/llm/kernel-driver/tellask-special.d.ts +38 -12
  34. package/dist/llm/kernel-driver/tellask-special.js +489 -180
  35. package/dist/llm/kernel-driver/types.d.ts +1 -1
  36. package/dist/persistence.d.ts +30 -62
  37. package/dist/persistence.js +978 -986
  38. package/dist/priming.js +398 -365
  39. package/dist/recovery/reply-special.js +3 -3
  40. package/dist/runtime/inter-dialog-format.d.ts +1 -1
  41. package/dist/runtime/inter-dialog-format.js +1 -1
  42. package/dist/runtime/reply-prompt-copy.js +4 -4
  43. package/dist/server/setup-routes.js +26 -5
  44. package/dist/server/snippets-routes.d.ts +1 -0
  45. package/dist/server/snippets-routes.js +20 -9
  46. package/dist/server/websocket-handler.js +58 -25
  47. package/dist/shared/utils/fbr.js +12 -8
  48. package/dist/shared/utils/inter-dialog-format.js +6 -4
  49. package/dist/team.d.ts +24 -13
  50. package/dist/team.js +123 -32
  51. package/dist/tool.d.ts +26 -0
  52. package/dist/tool.js +97 -0
  53. package/dist/tools/team_mgmt.js +18 -0
  54. package/package.json +2 -2
  55. package/webapp/dist/assets/{_basePickBy-CBOtd63g.js → _basePickBy-DsirmCgI.js} +3 -3
  56. package/webapp/dist/assets/_basePickBy-DsirmCgI.js.map +1 -0
  57. package/webapp/dist/assets/{_baseUniq-mfoKz4Wm.js → _baseUniq-tR6G8loB.js} +2 -2
  58. package/webapp/dist/assets/_baseUniq-tR6G8loB.js.map +1 -0
  59. package/webapp/dist/assets/{arc-Dq0WZLyu.js → arc-CzxpASkZ.js} +2 -2
  60. package/webapp/dist/assets/arc-CzxpASkZ.js.map +1 -0
  61. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-CNmygmp3.js → architectureDiagram-2XIMDMQ5-BSH7H5oI.js} +26 -8
  62. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-BSH7H5oI.js.map +1 -0
  63. package/webapp/dist/assets/{blockDiagram-VD42YOAC-DvE0lybt.js → blockDiagram-WCTKOSBZ-DpLIr7yO.js} +187 -170
  64. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-DpLIr7yO.js.map +1 -0
  65. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-CR7zJ2_u.js → c4Diagram-IC4MRINW-WuYKgWfY.js} +4 -4
  66. package/webapp/dist/assets/c4Diagram-IC4MRINW-WuYKgWfY.js.map +1 -0
  67. package/webapp/dist/assets/{channel-DrTrnYx4.js → channel-B-v9dqLN.js} +2 -2
  68. package/webapp/dist/assets/channel-B-v9dqLN.js.map +1 -0
  69. package/webapp/dist/assets/{chunk-4BX2VUAB-CVuJEIeN.js → chunk-4BX2VUAB-MtFUfKZy.js} +2 -2
  70. package/webapp/dist/assets/chunk-4BX2VUAB-MtFUfKZy.js.map +1 -0
  71. package/webapp/dist/assets/{chunk-55IACEB6-BxUoXApB.js → chunk-55IACEB6-rY9AJdzj.js} +2 -2
  72. package/webapp/dist/assets/chunk-55IACEB6-rY9AJdzj.js.map +1 -0
  73. package/webapp/dist/assets/{chunk-FMBD7UC4-TX-LVAaV.js → chunk-FMBD7UC4-B-RtOs7e.js} +2 -2
  74. package/webapp/dist/assets/chunk-FMBD7UC4-B-RtOs7e.js.map +1 -0
  75. package/webapp/dist/assets/{chunk-TZMSLE5B-Cw689yRl.js → chunk-JSJVCQXG-Da1d3uS4.js} +14 -6
  76. package/webapp/dist/assets/chunk-JSJVCQXG-Da1d3uS4.js.map +1 -0
  77. package/webapp/dist/assets/{chunk-QN33PNHL-D1uiKlOO.js → chunk-KX2RTZJC-DH9UrpuG.js} +2 -2
  78. package/webapp/dist/assets/chunk-KX2RTZJC-DH9UrpuG.js.map +1 -0
  79. package/webapp/dist/assets/{chunk-DI55MBZ5-SAhxUTqQ.js → chunk-NQ4KR5QH-CK365lrr.js} +9 -7
  80. package/webapp/dist/assets/chunk-NQ4KR5QH-CK365lrr.js.map +1 -0
  81. package/webapp/dist/assets/{chunk-QZHKN3VN-BxuV0Oba.js → chunk-QZHKN3VN-BCaWPGDm.js} +2 -2
  82. package/webapp/dist/assets/chunk-QZHKN3VN-BCaWPGDm.js.map +1 -0
  83. package/webapp/dist/assets/{chunk-B4BG7PRW-DpMa3-9L.js → chunk-WL4C6EOR-DDCnEwft.js} +171 -121
  84. package/webapp/dist/assets/chunk-WL4C6EOR-DDCnEwft.js.map +1 -0
  85. package/webapp/dist/assets/{classDiagram-2ON5EDUG-BTTGianr.js → classDiagram-VBA2DB6C-CvMBU4WA.js} +7 -6
  86. package/webapp/dist/assets/classDiagram-VBA2DB6C-CvMBU4WA.js.map +1 -0
  87. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-BTTGianr.js → classDiagram-v2-RAHNMMFH-CvMBU4WA.js} +7 -6
  88. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-CvMBU4WA.js.map +1 -0
  89. package/webapp/dist/assets/{clone-Dk8cAI3I.js → clone-r98jR0MC.js} +2 -2
  90. package/webapp/dist/assets/clone-r98jR0MC.js.map +1 -0
  91. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-BjJnzB2N.js → cose-bilkent-S5V4N54A-t6J60Ogk.js} +2 -2
  92. package/webapp/dist/assets/cose-bilkent-S5V4N54A-t6J60Ogk.js.map +1 -0
  93. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  94. package/webapp/dist/assets/{dagre-6UL2VRFP-VF-xGhAf.js → dagre-KLK3FWXG-BlqmY2DV.js} +7 -7
  95. package/webapp/dist/assets/dagre-KLK3FWXG-BlqmY2DV.js.map +1 -0
  96. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  97. package/webapp/dist/assets/{diagram-PSM6KHXK-Ba5U0oRY.js → diagram-E7M64L7V-FwCHeIUD.js} +10 -10
  98. package/webapp/dist/assets/diagram-E7M64L7V-FwCHeIUD.js.map +1 -0
  99. package/webapp/dist/assets/{diagram-QEK2KX5R-DoYCnEw_.js → diagram-IFDJBPK2-NhtmkuZG.js} +9 -8
  100. package/webapp/dist/assets/diagram-IFDJBPK2-NhtmkuZG.js.map +1 -0
  101. package/webapp/dist/assets/{diagram-S2PKOQOG-CkK4SRyE.js → diagram-P4PSJMXO-B9FcmokX.js} +8 -8
  102. package/webapp/dist/assets/diagram-P4PSJMXO-B9FcmokX.js.map +1 -0
  103. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-DkI5eYww.js → erDiagram-INFDFZHY-DHKmWvtB.js} +96 -75
  104. package/webapp/dist/assets/erDiagram-INFDFZHY-DHKmWvtB.js.map +1 -0
  105. package/webapp/dist/assets/{flowDiagram-NV44I4VS-wOdPUQ7Y.js → flowDiagram-PKNHOUZH-C7Zi8I7T.js} +98 -81
  106. package/webapp/dist/assets/flowDiagram-PKNHOUZH-C7Zi8I7T.js.map +1 -0
  107. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-BtRWgkUH.js → ganttDiagram-A5KZAMGK-Cv2T8tz_.js} +28 -3
  108. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-Cv2T8tz_.js.map +1 -0
  109. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js → gitGraphDiagram-K3NZZRJ6-DztaipJU.js} +38 -46
  110. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-DztaipJU.js.map +1 -0
  111. package/webapp/dist/assets/graph-C5yf62Vs.js +782 -0
  112. package/webapp/dist/assets/graph-C5yf62Vs.js.map +1 -0
  113. package/webapp/dist/assets/{index-xvYYeHuy.css → index-YaxF76or.css} +1 -1
  114. package/webapp/dist/assets/{index-rYmIohM_.js → index-hve5MWPs.js} +1603 -1415
  115. package/webapp/dist/assets/index-hve5MWPs.js.map +1 -0
  116. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-BMaxCvH5.js → infoDiagram-LFFYTUFH-VgsbBPZP.js} +7 -7
  117. package/webapp/dist/assets/infoDiagram-LFFYTUFH-VgsbBPZP.js.map +1 -0
  118. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  119. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js +966 -0
  120. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js.map +1 -0
  121. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-ejyerzmG.js → journeyDiagram-4ABVD52K-OO8sev-Y.js} +5 -5
  122. package/webapp/dist/assets/journeyDiagram-4ABVD52K-OO8sev-Y.js.map +1 -0
  123. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-CYj35TEs.js → kanban-definition-K7BYSVSG-DiYCC1Ig.js} +5 -3
  124. package/webapp/dist/assets/kanban-definition-K7BYSVSG-DiYCC1Ig.js.map +1 -0
  125. package/webapp/dist/assets/{layout-7Ql4zmuL.js → layout-DdZSgGdu.js} +5 -5
  126. package/webapp/dist/assets/layout-DdZSgGdu.js.map +1 -0
  127. package/webapp/dist/assets/{linear-CVmgVPuZ.js → linear-7-aHtaFi.js} +2 -2
  128. package/webapp/dist/assets/linear-7-aHtaFi.js.map +1 -0
  129. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-DOpxjGVo.js → mindmap-definition-YRQLILUH-IG3I-RdD.js} +7 -5
  130. package/webapp/dist/assets/mindmap-definition-YRQLILUH-IG3I-RdD.js.map +1 -0
  131. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  132. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-CLQjpmAG.js → pieDiagram-SKSYHLDU-z68KJT5r.js} +8 -8
  133. package/webapp/dist/assets/pieDiagram-SKSYHLDU-z68KJT5r.js.map +1 -0
  134. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-ClD_bz7z.js → quadrantDiagram-337W2JSQ-DaENWdO6.js} +3 -3
  135. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-DaENWdO6.js.map +1 -0
  136. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-DOpb-TWH.js → requirementDiagram-Z7DCOOCP-ROTFv4sa.js} +16 -6
  137. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-ROTFv4sa.js.map +1 -0
  138. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-D8Hsj3yx.js → sankeyDiagram-WA2Y5GQK-CK7qtpzw.js} +2 -2
  139. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CK7qtpzw.js.map +1 -0
  140. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-CFMNjBER.js → sequenceDiagram-2WXFIKYE-R5lDySeI.js} +601 -201
  141. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-R5lDySeI.js.map +1 -0
  142. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-BQeDlw0P.js → stateDiagram-RAJIS63D-sr7msF5U.js} +9 -9
  143. package/webapp/dist/assets/stateDiagram-RAJIS63D-sr7msF5U.js.map +1 -0
  144. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-DscX61Rs.js → stateDiagram-v2-FVOUBMTO-X663liwS.js} +5 -5
  145. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-X663liwS.js.map +1 -0
  146. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-BcXPSTiw.js → timeline-definition-YZTLITO2-Bw0TdG26.js} +3 -3
  147. package/webapp/dist/assets/timeline-definition-YZTLITO2-Bw0TdG26.js.map +1 -0
  148. package/webapp/dist/assets/{treemap-GDKQZRPO-BBr4UV0Z.js → treemap-KZPCXAKY-D_sjKwI7.js} +37 -24
  149. package/webapp/dist/assets/treemap-KZPCXAKY-D_sjKwI7.js.map +1 -0
  150. package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js +2487 -0
  151. package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js.map +1 -0
  152. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-CS5RAtQE.js → xychartDiagram-JWTSCODW-C65ESjTc.js} +4 -4
  153. package/webapp/dist/assets/xychartDiagram-JWTSCODW-C65ESjTc.js.map +1 -0
  154. package/webapp/dist/index.html +2 -2
  155. package/webapp/dist/assets/_basePickBy-CBOtd63g.js.map +0 -1
  156. package/webapp/dist/assets/_baseUniq-mfoKz4Wm.js.map +0 -1
  157. package/webapp/dist/assets/arc-Dq0WZLyu.js.map +0 -1
  158. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-CNmygmp3.js.map +0 -1
  159. package/webapp/dist/assets/blockDiagram-VD42YOAC-DvE0lybt.js.map +0 -1
  160. package/webapp/dist/assets/c4Diagram-YG6GDRKO-CR7zJ2_u.js.map +0 -1
  161. package/webapp/dist/assets/channel-DrTrnYx4.js.map +0 -1
  162. package/webapp/dist/assets/chunk-4BX2VUAB-CVuJEIeN.js.map +0 -1
  163. package/webapp/dist/assets/chunk-55IACEB6-BxUoXApB.js.map +0 -1
  164. package/webapp/dist/assets/chunk-B4BG7PRW-DpMa3-9L.js.map +0 -1
  165. package/webapp/dist/assets/chunk-DI55MBZ5-SAhxUTqQ.js.map +0 -1
  166. package/webapp/dist/assets/chunk-FMBD7UC4-TX-LVAaV.js.map +0 -1
  167. package/webapp/dist/assets/chunk-QN33PNHL-D1uiKlOO.js.map +0 -1
  168. package/webapp/dist/assets/chunk-QZHKN3VN-BxuV0Oba.js.map +0 -1
  169. package/webapp/dist/assets/chunk-TZMSLE5B-Cw689yRl.js.map +0 -1
  170. package/webapp/dist/assets/classDiagram-2ON5EDUG-BTTGianr.js.map +0 -1
  171. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-BTTGianr.js.map +0 -1
  172. package/webapp/dist/assets/clone-Dk8cAI3I.js.map +0 -1
  173. package/webapp/dist/assets/cose-bilkent-S5V4N54A-BjJnzB2N.js.map +0 -1
  174. package/webapp/dist/assets/dagre-6UL2VRFP-VF-xGhAf.js.map +0 -1
  175. package/webapp/dist/assets/diagram-PSM6KHXK-Ba5U0oRY.js.map +0 -1
  176. package/webapp/dist/assets/diagram-QEK2KX5R-DoYCnEw_.js.map +0 -1
  177. package/webapp/dist/assets/diagram-S2PKOQOG-CkK4SRyE.js.map +0 -1
  178. package/webapp/dist/assets/erDiagram-Q2GNP2WA-DkI5eYww.js.map +0 -1
  179. package/webapp/dist/assets/flowDiagram-NV44I4VS-wOdPUQ7Y.js.map +0 -1
  180. package/webapp/dist/assets/ganttDiagram-JELNMOA3-BtRWgkUH.js.map +0 -1
  181. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js.map +0 -1
  182. package/webapp/dist/assets/graph-DAMkuTbn.js +0 -425
  183. package/webapp/dist/assets/graph-DAMkuTbn.js.map +0 -1
  184. package/webapp/dist/assets/index-rYmIohM_.js.map +0 -1
  185. package/webapp/dist/assets/infoDiagram-HS3SLOUP-BMaxCvH5.js.map +0 -1
  186. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-ejyerzmG.js.map +0 -1
  187. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-CYj35TEs.js.map +0 -1
  188. package/webapp/dist/assets/layout-7Ql4zmuL.js.map +0 -1
  189. package/webapp/dist/assets/linear-CVmgVPuZ.js.map +0 -1
  190. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-DOpxjGVo.js.map +0 -1
  191. package/webapp/dist/assets/pieDiagram-ADFJNKIX-CLQjpmAG.js.map +0 -1
  192. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-ClD_bz7z.js.map +0 -1
  193. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-DOpb-TWH.js.map +0 -1
  194. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-D8Hsj3yx.js.map +0 -1
  195. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-CFMNjBER.js.map +0 -1
  196. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-BQeDlw0P.js.map +0 -1
  197. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-DscX61Rs.js.map +0 -1
  198. package/webapp/dist/assets/timeline-definition-IT6M3QCI-BcXPSTiw.js.map +0 -1
  199. package/webapp/dist/assets/treemap-GDKQZRPO-BBr4UV0Z.js.map +0 -1
  200. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-CS5RAtQE.js.map +0 -1
@@ -18,6 +18,7 @@ const tool_1 = require("../../tool");
18
18
  const taskdoc_1 = require("../../utils/taskdoc");
19
19
  const client_1 = require("../client");
20
20
  const registry_1 = require("../gen/registry");
21
+ const tool_call_context_1 = require("../gen/tool-call-context");
21
22
  const tools_projection_1 = require("../tools-projection");
22
23
  const context_1 = require("./context");
23
24
  const context_health_1 = require("./context-health");
@@ -35,6 +36,57 @@ const KERNEL_DRIVER_DEFAULT_RETRY_POLICY = {
35
36
  maxDelayMs: 30 * 60 * 1000, // 30 minutes
36
37
  };
37
38
  const KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE = 'DOMINDS_LLM_EMPTY_RESPONSE';
39
+ // Wrapper isolation boundary:
40
+ // - Wrappers emit provider-native web-search events.
41
+ // - The driver is the first place allowed to project them into a narrower shared dialog shape.
42
+ function projectLlmWebSearchCall(call) {
43
+ if (call.source === 'codex') {
44
+ return call;
45
+ }
46
+ const action = call.action;
47
+ if (!action) {
48
+ return {
49
+ source: call.source,
50
+ phase: call.phase,
51
+ itemId: call.itemId,
52
+ status: call.status,
53
+ };
54
+ }
55
+ if (action.type === 'search') {
56
+ const query = typeof action.query === 'string' && action.query.trim().length > 0
57
+ ? action.query
58
+ : Array.isArray(action.queries)
59
+ ? action.queries.find((entry) => entry.trim().length > 0)
60
+ : undefined;
61
+ return {
62
+ source: call.source,
63
+ phase: call.phase,
64
+ itemId: call.itemId,
65
+ status: call.status,
66
+ action: query !== undefined ? { type: 'search', query } : { type: 'search' },
67
+ };
68
+ }
69
+ if (action.type === 'open_page') {
70
+ return {
71
+ source: call.source,
72
+ phase: call.phase,
73
+ itemId: call.itemId,
74
+ status: call.status,
75
+ action: typeof action.url === 'string' ? { type: 'open_page', url: action.url } : action,
76
+ };
77
+ }
78
+ return {
79
+ source: call.source,
80
+ phase: call.phase,
81
+ itemId: call.itemId,
82
+ status: call.status,
83
+ action: {
84
+ type: 'find_in_page',
85
+ ...(typeof action.url === 'string' ? { url: action.url } : {}),
86
+ ...(typeof action.pattern === 'string' ? { pattern: action.pattern } : {}),
87
+ },
88
+ };
89
+ }
38
90
  class KernelDriverInterruptedError extends Error {
39
91
  constructor(reason) {
40
92
  super('Dialog interrupted');
@@ -86,19 +138,11 @@ function buildKernelDriverFbrPrompt(dlg, state) {
86
138
  origin: 'runtime',
87
139
  };
88
140
  }
89
- function normalizeQ4HAnswerCallIds(raw) {
90
- if (!raw || raw.length === 0)
141
+ function normalizeQ4HAnswerCallId(raw) {
142
+ if (typeof raw !== 'string')
91
143
  return undefined;
92
- const seen = new Set();
93
- const normalized = [];
94
- for (const value of raw) {
95
- const callId = value.trim();
96
- if (callId === '' || seen.has(callId))
97
- continue;
98
- seen.add(callId);
99
- normalized.push(callId);
100
- }
101
- return normalized.length > 0 ? normalized : undefined;
144
+ const callId = raw.trim();
145
+ return callId !== '' ? callId : undefined;
102
146
  }
103
147
  function isUserOriginPrompt(prompt) {
104
148
  if (!prompt)
@@ -171,8 +215,23 @@ function resolveKernelDriverRetryPolicy(providerCfg) {
171
215
  maxDelayMs: Math.max(initialDelayMs, conservativeDelayMs, maxDelayMs),
172
216
  };
173
217
  }
174
- function hasMeaningfulBatchOutput(messages) {
175
- for (const msg of messages) {
218
+ function hasMeaningfulBatchOutput(batch) {
219
+ if (Array.isArray(batch.outputs) && batch.outputs.length > 0) {
220
+ for (const output of batch.outputs) {
221
+ if (output.kind !== 'message') {
222
+ return true;
223
+ }
224
+ const msg = output.message;
225
+ if (msg.type === 'func_call_msg') {
226
+ return true;
227
+ }
228
+ if ((msg.type === 'saying_msg' || msg.type === 'thinking_msg') && msg.content.trim() !== '') {
229
+ return true;
230
+ }
231
+ }
232
+ return false;
233
+ }
234
+ for (const msg of batch.messages) {
176
235
  if (msg.type === 'func_call_msg') {
177
236
  return true;
178
237
  }
@@ -380,7 +439,7 @@ const TELLASK_SPECIAL_VIRTUAL_TOOLS = [
380
439
  },
381
440
  },
382
441
  ];
383
- function mergeTellaskSpecialVirtualTools(baseTools, options) {
442
+ function mergeTellaskVirtualTools(baseTools, options) {
384
443
  const merged = [...baseTools];
385
444
  const seen = new Set(merged.map((tool) => tool.name));
386
445
  const freshBootsReasoning = createFreshBootsReasoningTool({
@@ -492,7 +551,7 @@ function resolveUpNextPrompt(dlg) {
492
551
  grammar: upNext.grammar ?? 'markdown',
493
552
  origin: upNext.origin,
494
553
  userLanguageCode: upNext.userLanguageCode,
495
- q4hAnswerCallIds: upNext.q4hAnswerCallIds,
554
+ q4hAnswerCallId: upNext.q4hAnswerCallId,
496
555
  tellaskReplyDirective: upNext.tellaskReplyDirective,
497
556
  skipTaskdoc: upNext.skipTaskdoc,
498
557
  subdialogReplyTarget: upNext.subdialogReplyTarget,
@@ -551,48 +610,30 @@ function hasSameReplyDirective(left, right) {
551
610
  }
552
611
  return true;
553
612
  }
554
- function formatElapsedSecondsText(startedAtMs) {
555
- const language = (0, work_language_1.getWorkLanguage)();
556
- if (startedAtMs === null) {
557
- return language === 'zh' ? '未知时长' : 'unknown elapsed time';
558
- }
559
- const elapsedMs = Math.max(0, Date.now() - startedAtMs);
560
- const elapsedSec = Math.floor(elapsedMs / 1000);
561
- return language === 'zh' ? `${elapsedSec} 秒` : `${elapsedSec}s`;
562
- }
563
- function formatPendingSpecialFuncResult(name, startedAtMs) {
564
- const language = (0, work_language_1.getWorkLanguage)();
565
- const elapsed = formatElapsedSecondsText(startedAtMs);
566
- if (name === 'askHuman') {
567
- return language === 'zh'
568
- ? `Q4H 仍在等待人类回复,已持续 ${elapsed}。`
569
- : `Q4H is still waiting for human reply (elapsed ${elapsed}).`;
570
- }
571
- return language === 'zh'
572
- ? `支线对话仍在进行中,已持续 ${elapsed}。`
573
- : `Sideline dialog is still running (elapsed ${elapsed}).`;
574
- }
575
- function formatResolvedAskHumanResult() {
576
- return (0, work_language_1.getWorkLanguage)() === 'zh'
577
- ? 'Q4H 已结束等待状态,请参考后续用户消息。'
578
- : 'Q4H wait is resolved; refer to subsequent user messages.';
613
+ function buildPendingTellaskFuncResult(args) {
614
+ return {
615
+ type: 'func_result_msg',
616
+ role: 'tool',
617
+ genseq: args.genseq,
618
+ id: args.callId,
619
+ name: args.callName,
620
+ content: (0, tellask_special_1.formatPendingTellaskFuncResultContent)(args.callName, null),
621
+ };
579
622
  }
580
- async function projectTellaskSpecialFuncResultsForContext(args) {
581
- const hasSpecialFuncCall = args.dialogMsgsForContext.some((msg) => msg.type === 'func_call_msg' && (0, tellask_special_1.isTellaskSpecialFunctionName)(msg.name));
582
- if (!hasSpecialFuncCall) {
583
- return [...args.dialogMsgsForContext];
584
- }
585
- const pendingSubdialogs = await persistence_1.DialogPersistence.loadPendingSubdialogs(args.dialog.id, args.dialog.status);
586
- const pendingSubByCallId = new Map();
623
+ async function loadPendingTellaskSpecialStates(dialog) {
624
+ const pendingByCallId = new Map();
625
+ const pendingSubdialogs = await persistence_1.DialogPersistence.loadPendingSubdialogs(dialog.id, dialog.status);
587
626
  for (const pending of pendingSubdialogs) {
588
627
  const callId = pending.callId.trim();
589
628
  if (callId === '') {
590
629
  continue;
591
630
  }
592
- pendingSubByCallId.set(callId, { createdAt: pending.createdAt });
631
+ pendingByCallId.set(callId, {
632
+ callName: pending.callName,
633
+ startedAtMs: parseUnifiedTimestampMs(pending.createdAt),
634
+ });
593
635
  }
594
- const pendingQ4H = await persistence_1.DialogPersistence.loadQuestions4HumanState(args.dialog.id, args.dialog.status);
595
- const pendingQ4HByCallId = new Map();
636
+ const pendingQ4H = await persistence_1.DialogPersistence.loadQuestions4HumanState(dialog.id, dialog.status);
596
637
  for (const question of pendingQ4H) {
597
638
  if (typeof question.callId !== 'string') {
598
639
  continue;
@@ -601,23 +642,42 @@ async function projectTellaskSpecialFuncResultsForContext(args) {
601
642
  if (callId === '') {
602
643
  continue;
603
644
  }
604
- pendingQ4HByCallId.set(callId, { askedAt: question.askedAt });
645
+ pendingByCallId.set(callId, {
646
+ callName: 'askHuman',
647
+ startedAtMs: parseUnifiedTimestampMs(question.askedAt),
648
+ });
605
649
  }
606
- const settledByCallId = new Map();
650
+ return pendingByCallId;
651
+ }
652
+ async function projectTellaskFuncResultsForContext(args) {
653
+ const hasSpecialFuncCall = args.dialogMsgsForContext.some((msg) => msg.type === 'func_call_msg' && (0, tellask_special_1.isTellaskCallFunctionName)(msg.name));
654
+ if (!hasSpecialFuncCall) {
655
+ return {
656
+ messages: [...args.dialogMsgsForContext],
657
+ projectedResultCallIds: new Set(),
658
+ };
659
+ }
660
+ const pendingSpecialByCallId = await loadPendingTellaskSpecialStates(args.dialog);
661
+ // Only technical tool-result-shaped messages can satisfy provider tool-call adjacency. A
662
+ // carryover message is different: it is already the canonical latest-course business context and
663
+ // intentionally does not act as a tool-result surrogate for an older-course call that is no
664
+ // longer present in current context.
665
+ const pairedToolResultContentByCallId = new Map();
607
666
  const existingSpecialFuncResults = new Map();
608
667
  for (const msg of args.dialogMsgsForContext) {
609
668
  if (msg.type === 'tellask_result_msg') {
610
669
  const callId = typeof msg.callId === 'string' ? msg.callId.trim() : '';
611
670
  if (callId !== '') {
612
- settledByCallId.set(callId, msg.content);
671
+ pairedToolResultContentByCallId.set(callId, msg.content);
613
672
  }
614
673
  continue;
615
674
  }
616
- if (msg.type === 'func_result_msg' && (0, tellask_special_1.isTellaskSpecialFunctionName)(msg.name)) {
675
+ if (msg.type === 'func_result_msg' && (0, tellask_special_1.isTellaskCallFunctionName)(msg.name)) {
617
676
  existingSpecialFuncResults.set(msg.id, msg);
618
677
  }
619
678
  }
620
679
  const projected = [];
680
+ const projectedResultCallIds = new Set();
621
681
  const specialCallIds = new Set();
622
682
  for (const msg of args.dialogMsgsForContext) {
623
683
  if (msg.type === 'func_result_msg' && specialCallIds.has(msg.id)) {
@@ -627,67 +687,98 @@ async function projectTellaskSpecialFuncResultsForContext(args) {
627
687
  if (msg.type !== 'func_call_msg') {
628
688
  continue;
629
689
  }
630
- if (!(0, tellask_special_1.isTellaskSpecialFunctionName)(msg.name)) {
690
+ if (!(0, tellask_special_1.isTellaskCallFunctionName)(msg.name)) {
631
691
  continue;
632
692
  }
633
693
  specialCallIds.add(msg.id);
634
- const settled = settledByCallId.get(msg.id);
635
- if (settled !== undefined) {
694
+ const pairedToolResultContent = pairedToolResultContentByCallId.get(msg.id);
695
+ if (pairedToolResultContent !== undefined) {
696
+ projectedResultCallIds.add(msg.id);
636
697
  projected.push({
637
698
  type: 'func_result_msg',
638
699
  role: 'tool',
639
700
  genseq: msg.genseq,
640
701
  id: msg.id,
641
702
  name: msg.name,
642
- content: settled,
703
+ content: pairedToolResultContent,
643
704
  });
644
705
  continue;
645
706
  }
646
707
  const existingResult = existingSpecialFuncResults.get(msg.id);
647
708
  if (existingResult) {
709
+ projectedResultCallIds.add(msg.id);
648
710
  projected.push(existingResult);
649
711
  continue;
650
712
  }
651
- if (msg.name === 'askHuman') {
652
- const pendingQ4HState = pendingQ4HByCallId.get(msg.id);
653
- const content = pendingQ4HState
654
- ? formatPendingSpecialFuncResult(msg.name, parseUnifiedTimestampMs(pendingQ4HState.askedAt))
655
- : formatResolvedAskHumanResult();
713
+ const pendingSpecialState = pendingSpecialByCallId.get(msg.id);
714
+ if (pendingSpecialState?.callName === msg.name) {
715
+ projectedResultCallIds.add(msg.id);
656
716
  projected.push({
657
717
  type: 'func_result_msg',
658
718
  role: 'tool',
659
719
  genseq: msg.genseq,
660
720
  id: msg.id,
661
721
  name: msg.name,
662
- content,
722
+ content: (0, tellask_special_1.formatPendingTellaskFuncResultContent)(msg.name, pendingSpecialState.startedAtMs),
663
723
  });
664
724
  continue;
665
725
  }
666
- const pendingSubState = pendingSubByCallId.get(msg.id);
667
- projected.push({
668
- type: 'func_result_msg',
669
- role: 'tool',
726
+ projectedResultCallIds.add(msg.id);
727
+ projected.push(buildPendingTellaskFuncResult({
728
+ callId: msg.id,
729
+ callName: msg.name,
670
730
  genseq: msg.genseq,
671
- id: msg.id,
672
- name: msg.name,
673
- content: formatPendingSpecialFuncResult(msg.name, pendingSubState ? parseUnifiedTimestampMs(pendingSubState.createdAt) : null),
674
- });
731
+ }));
675
732
  }
676
- return projected;
733
+ return {
734
+ messages: projected,
735
+ projectedResultCallIds,
736
+ };
677
737
  }
678
738
  async function buildDialogMsgsForContext(dlg) {
679
- const rawDialogMsgsForContext = dlg.msgs.filter((m) => {
680
- if (!m)
681
- return false;
682
- if (m.type === 'ui_only_markdown_msg')
683
- return false;
684
- return true;
685
- });
686
- const projected = await projectTellaskSpecialFuncResultsForContext({
739
+ const rawDialogMsgsForContext = dlg.msgs.filter((m) => !!m);
740
+ const projected = await projectTellaskFuncResultsForContext({
687
741
  dialog: dlg,
688
742
  dialogMsgsForContext: rawDialogMsgsForContext,
689
743
  });
690
- return projected.filter((msg) => msg.type !== 'tellask_result_msg');
744
+ const businessFiltered = projected.messages.filter((msg) => {
745
+ if (msg.type !== 'tellask_result_msg') {
746
+ return true;
747
+ }
748
+ // Business tellask result bubbles stay in storage/UI, but when the same latest-course call is
749
+ // also projected into an adjacent technical tool result for provider context we omit the
750
+ // duplicate bubble form here. Carryover messages are intentionally not filtered by this branch:
751
+ // they are already the canonical latest-course context, not a tool-pair surrogate.
752
+ return !projected.projectedResultCallIds.has(msg.callId);
753
+ });
754
+ const sanitized = (0, tool_call_context_1.sanitizeToolContextForProvider)(businessFiltered);
755
+ if (sanitized.droppedViolations.length > 0) {
756
+ const details = sanitized.droppedViolations.map((violation) => (0, tool_call_context_1.formatToolCallAdjacencyViolation)(violation, 'kernel-driver provider context sanitization'));
757
+ const summary = `kernel-driver dropped ${sanitized.droppedViolations.length} unpaired persisted tool ` +
758
+ `message(s) before provider projection for dialog=${dlg.id.valueOf()}; see logs for details.`;
759
+ log_1.log.error(summary, new Error('kernel_driver_provider_context_sanitized_unpaired_tool_msgs'), {
760
+ rootId: dlg.id.rootId,
761
+ selfId: dlg.id.selfId,
762
+ droppedViolationCount: sanitized.droppedViolations.length,
763
+ droppedViolations: sanitized.droppedViolations.map((violation) => ({
764
+ kind: violation.kind,
765
+ callId: violation.callId,
766
+ toolName: violation.toolName,
767
+ index: violation.index,
768
+ })),
769
+ detailPreview: details.slice(0, 3),
770
+ });
771
+ try {
772
+ await dlg.streamError(`${summary} ${details.slice(0, 3).join(' ')}`);
773
+ }
774
+ catch (error) {
775
+ log_1.log.warn('kernel-driver failed to emit stream_error_evt for sanitized tool context', error, {
776
+ rootId: dlg.id.rootId,
777
+ selfId: dlg.id.selfId,
778
+ });
779
+ }
780
+ }
781
+ return sanitized.messages;
691
782
  }
692
783
  async function emitAssistantSaying(dlg, content) {
693
784
  if (content.trim() === '')
@@ -696,146 +787,94 @@ async function emitAssistantSaying(dlg, content) {
696
787
  await dlg.sayingChunk(content);
697
788
  await dlg.sayingFinish();
698
789
  }
699
- function isReplyTellaskCallName(name) {
700
- return (name === 'replyTellask' || name === 'replyTellaskSessionless' || name === 'replyTellaskBack');
701
- }
702
- function isToolArgumentsObject(value) {
703
- return typeof value === 'object' && value !== null && !Array.isArray(value);
704
- }
705
- function prepareFuncCallArguments(argumentsStr) {
706
- if (argumentsStr.trim() === '') {
707
- return {
708
- ok: true,
709
- raw: {},
710
- contextArguments: '{}',
711
- };
712
- }
713
- let parsed;
714
- try {
715
- parsed = JSON.parse(argumentsStr);
716
- }
717
- catch (err) {
718
- return {
719
- ok: false,
720
- error: `Arguments must be valid JSON: ${err instanceof Error ? err.message : String(err)}`,
721
- contextArguments: '{}',
722
- };
723
- }
724
- if (!isToolArgumentsObject(parsed)) {
725
- return {
726
- ok: false,
727
- error: 'Arguments must be an object',
728
- contextArguments: '{}',
729
- };
730
- }
731
- return {
732
- ok: true,
733
- raw: parsed,
734
- contextArguments: argumentsStr,
735
- };
736
- }
737
790
  async function executeFunctionCalls(args) {
738
791
  const functionPromises = args.funcCalls.map(async (func) => {
739
792
  throwIfAborted(args.abortSignal, args.dlg);
740
793
  const callGenseq = func.genseq;
741
794
  const argsStr = typeof func.arguments === 'string' ? func.arguments : JSON.stringify(func.arguments ?? {});
742
- const preparedArgs = prepareFuncCallArguments(argsStr);
743
- const persistedArguments = preparedArgs.ok ? preparedArgs.raw : {};
744
- await args.dlg.funcCallRequested(func.id, func.name, preparedArgs.contextArguments);
745
- await args.dlg.persistFunctionCall(func.id, func.name, persistedArguments, callGenseq);
746
795
  const tool = args.agentTools.find((t) => t.type === 'func' && t.name === func.name);
747
- if (!tool) {
748
- const errorResult = {
749
- type: 'func_result_msg',
750
- id: func.id,
751
- name: func.name,
752
- content: `Tool '${func.name}' not found`,
753
- role: 'tool',
754
- genseq: callGenseq,
755
- };
756
- await args.dlg.receiveFuncResult(errorResult);
757
- return errorResult;
758
- }
796
+ const preparedInvocationArgs = tool !== undefined ? (0, tool_1.resolveFuncToolInvocationArguments)(tool, argsStr) : null;
797
+ await args.dlg.funcCallRequested(func.id, func.name, argsStr);
759
798
  let result;
760
- let resultPersisted = false;
761
- if (!preparedArgs.ok) {
762
- log_1.log.warn('kernel-driver rejected function call arguments before execution', undefined, {
763
- funcName: func.name,
764
- arguments: argsStr,
765
- error: preparedArgs.error,
766
- });
799
+ let rethrowError;
800
+ if (!tool) {
767
801
  result = {
768
802
  type: 'func_result_msg',
769
803
  id: func.id,
770
804
  name: func.name,
771
- content: `Invalid arguments: ${preparedArgs.error}`,
805
+ content: `Tool '${func.name}' not found`,
772
806
  role: 'tool',
773
807
  genseq: callGenseq,
774
808
  };
775
809
  }
776
810
  else {
777
- const argsValidation = (0, runtime_1.validateFuncToolArguments)(tool, preparedArgs.raw);
778
- if (!argsValidation.ok) {
811
+ if (!preparedInvocationArgs || !preparedInvocationArgs.ok) {
812
+ const errorText = preparedInvocationArgs?.error ?? 'Arguments could not be prepared for tool invocation';
813
+ log_1.log.warn('kernel-driver rejected function call arguments before execution', undefined, {
814
+ funcName: func.name,
815
+ arguments: argsStr,
816
+ error: errorText,
817
+ });
779
818
  result = {
780
819
  type: 'func_result_msg',
781
820
  id: func.id,
782
821
  name: func.name,
783
- content: `Invalid arguments: ${argsValidation.error}`,
822
+ content: `Invalid arguments: ${errorText}`,
784
823
  role: 'tool',
785
824
  genseq: callGenseq,
786
825
  };
787
- await args.dlg.receiveFuncResult(result);
788
- return result;
789
826
  }
790
- const argsObj = argsValidation.args;
791
- try {
792
- throwIfAborted(args.abortSignal, args.dlg);
793
- const output = await tool.call(args.dlg, args.agent, argsObj);
794
- throwIfAborted(args.abortSignal, args.dlg);
795
- const normalized = typeof output === 'string'
796
- ? { content: output, contentItems: undefined }
797
- : {
798
- content: typeof output.content === 'string' ? output.content : String(output),
799
- contentItems: Array.isArray(output.contentItems) ? output.contentItems : undefined,
827
+ else {
828
+ try {
829
+ throwIfAborted(args.abortSignal, args.dlg);
830
+ const output = await tool.call(args.dlg, args.agent, preparedInvocationArgs.args);
831
+ throwIfAborted(args.abortSignal, args.dlg);
832
+ const normalized = typeof output === 'string'
833
+ ? { content: output, contentItems: undefined }
834
+ : {
835
+ content: typeof output.content === 'string' ? output.content : String(output),
836
+ contentItems: Array.isArray(output.contentItems)
837
+ ? output.contentItems
838
+ : undefined,
839
+ };
840
+ result = {
841
+ type: 'func_result_msg',
842
+ id: func.id,
843
+ name: func.name,
844
+ content: String(normalized.content),
845
+ contentItems: normalized.contentItems,
846
+ role: 'tool',
847
+ genseq: callGenseq,
800
848
  };
801
- result = {
802
- type: 'func_result_msg',
803
- id: func.id,
804
- name: func.name,
805
- content: String(normalized.content),
806
- contentItems: normalized.contentItems,
807
- role: 'tool',
808
- genseq: callGenseq,
809
- };
810
- }
811
- catch (err) {
812
- const errText = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
813
- result = {
814
- type: 'func_result_msg',
815
- id: func.id,
816
- name: func.name,
817
- content: `Function '${func.name}' execution failed: ${errText}`,
818
- role: 'tool',
819
- genseq: callGenseq,
820
- };
821
- if (args.abortSignal?.aborted || err instanceof KernelDriverInterruptedError) {
822
- if (!resultPersisted) {
823
- await args.dlg.receiveFuncResult({
849
+ }
850
+ catch (err) {
851
+ const errText = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
852
+ result = {
853
+ type: 'func_result_msg',
854
+ id: func.id,
855
+ name: func.name,
856
+ content: `Function '${func.name}' execution failed: ${errText}`,
857
+ role: 'tool',
858
+ genseq: callGenseq,
859
+ };
860
+ if (args.abortSignal?.aborted || err instanceof KernelDriverInterruptedError) {
861
+ result = {
824
862
  type: 'func_result_msg',
825
863
  id: func.id,
826
864
  name: func.name,
827
- content: `Function '${func.name}' interrupted after call was persisted: ${errText}`,
865
+ content: `Function '${func.name}' interrupted before completion: ${errText}`,
828
866
  role: 'tool',
829
867
  genseq: callGenseq,
830
- });
831
- resultPersisted = true;
868
+ };
869
+ rethrowError = err;
832
870
  }
833
- throw err;
834
871
  }
835
872
  }
836
873
  }
837
- await args.dlg.receiveFuncResult(result);
838
- resultPersisted = true;
874
+ await args.dlg.persistFunctionCallResultPair(func.id, func.name, argsStr, callGenseq, result);
875
+ if (rethrowError !== undefined) {
876
+ throw rethrowError;
877
+ }
839
878
  return result;
840
879
  });
841
880
  return await Promise.all(functionPromises);
@@ -850,8 +889,8 @@ async function executeFunctionRound(args) {
850
889
  };
851
890
  }
852
891
  throwIfAborted(args.abortSignal, args.dlg);
853
- const allowTellaskBack = args.allowTellaskSpecialFunctions && args.dlg.id.rootId !== args.dlg.id.selfId;
854
- const allowedSpecials = args.allowTellaskSpecialFunctions
892
+ const allowTellaskBack = args.allowTellaskFunctions && args.dlg.id.rootId !== args.dlg.id.selfId;
893
+ const allowedSpecials = args.allowTellaskFunctions
855
894
  ? new Set([
856
895
  'tellask',
857
896
  'tellaskSessionless',
@@ -863,114 +902,19 @@ async function executeFunctionRound(args) {
863
902
  ...(allowTellaskBack ? ['tellaskBack'] : []),
864
903
  ])
865
904
  : new Set();
866
- const classified = (0, tellask_special_1.classifyTellaskSpecialFunctionCalls)(args.funcCalls, { allowedSpecials });
867
- const specialCallById = new Map(classified.specialCalls.map((call) => [call.callId, call]));
868
- const originalCallById = new Map(args.funcCalls.map((call) => [call.id, call]));
869
- const toPersistedSpecialCallArgs = (call) => {
870
- switch (call.callName) {
871
- case 'tellaskBack':
872
- return { tellaskContent: call.tellaskContent };
873
- case 'askHuman':
874
- return { tellaskContent: call.tellaskContent };
875
- case 'freshBootsReasoning':
876
- return {
877
- tellaskContent: call.tellaskContent,
878
- ...(call.effort !== undefined ? { effort: call.effort } : {}),
879
- };
880
- case 'tellask':
881
- return {
882
- targetAgentId: call.targetAgentId,
883
- sessionSlug: call.sessionSlug,
884
- tellaskContent: call.tellaskContent,
885
- };
886
- case 'tellaskSessionless':
887
- return {
888
- targetAgentId: call.targetAgentId,
889
- tellaskContent: call.tellaskContent,
890
- };
891
- case 'replyTellask':
892
- case 'replyTellaskSessionless':
893
- case 'replyTellaskBack':
894
- return { replyContent: call.replyContent };
895
- }
896
- };
897
- for (const callMsg of args.funcCalls) {
898
- throwIfAborted(args.abortSignal, args.dlg);
899
- const special = specialCallById.get(callMsg.id);
900
- if (!special) {
901
- continue;
902
- }
903
- const argumentsStr = typeof callMsg.arguments === 'string'
904
- ? callMsg.arguments
905
- : JSON.stringify(callMsg.arguments ?? {});
906
- if (isReplyTellaskCallName(callMsg.name)) {
907
- await args.dlg.funcCallRequested(callMsg.id, callMsg.name, argumentsStr);
908
- }
909
- await args.dlg.persistTellaskSpecialCall(callMsg.id, special.callName, toPersistedSpecialCallArgs(special), callMsg.genseq);
910
- }
911
- const issueResults = [];
912
- for (const issue of classified.parseIssues) {
913
- const result = {
914
- type: 'func_result_msg',
915
- id: issue.call.id,
916
- name: issue.call.name,
917
- content: `Invalid arguments for tellask special function '${issue.call.name}': ${issue.error}`,
918
- role: 'tool',
919
- genseq: issue.call.genseq,
920
- };
921
- await args.dlg.receiveFuncResult(result);
922
- issueResults.push(result);
923
- }
924
905
  throwIfAborted(args.abortSignal, args.dlg);
925
- let tellaskExecution;
926
- try {
927
- tellaskExecution = await (0, tellask_special_1.executeTellaskSpecialCalls)({
928
- dlg: args.dlg,
929
- calls: classified.specialCalls,
930
- callbacks: args.callbacks,
931
- });
932
- }
933
- catch (err) {
934
- const errText = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
935
- for (const call of classified.specialCalls) {
936
- if (issueResults.some((result) => result.id === call.callId)) {
937
- continue;
938
- }
939
- const originalCall = originalCallById.get(call.callId);
940
- if (!originalCall) {
941
- throw new Error(`kernel-driver tellask special call invariant violation: missing original call for '${call.callId}'`);
942
- }
943
- await args.dlg.receiveFuncResult({
944
- type: 'func_result_msg',
945
- id: call.callId,
946
- name: call.callName,
947
- content: args.abortSignal?.aborted || err instanceof KernelDriverInterruptedError
948
- ? `Special function '${call.callName}' interrupted after call was persisted: ${errText}`
949
- : `Special function '${call.callName}' execution failed after call was persisted: ${errText}`,
950
- role: 'tool',
951
- genseq: originalCall.genseq,
952
- });
953
- }
954
- throw err;
955
- }
956
- const tellaskFuncResults = [];
957
- const tellaskToolOutputs = [];
958
- for (const output of tellaskExecution.toolOutputs) {
959
- if (output.type === 'func_result_msg') {
960
- await args.dlg.receiveFuncResult(output);
961
- tellaskFuncResults.push(output);
962
- continue;
963
- }
964
- tellaskToolOutputs.push(output);
965
- }
966
- const shouldStopAfterReplyTool = tellaskExecution.successfulReplyCallIds.length > 0;
906
+ const tellaskRound = await (0, tellask_special_1.processTellaskFunctionRound)({
907
+ dlg: args.dlg,
908
+ funcCalls: args.funcCalls,
909
+ allowedSpecials,
910
+ callbacks: args.callbacks,
911
+ });
967
912
  throwIfAborted(args.abortSignal, args.dlg);
968
- const specialCallIds = new Set(classified.specialCalls.map((call) => call.callId));
969
913
  const genericResults = await executeFunctionCalls({
970
914
  dlg: args.dlg,
971
915
  agent: args.agent,
972
916
  agentTools: args.agentTools,
973
- funcCalls: classified.normalCalls,
917
+ funcCalls: tellaskRound.normalCalls,
974
918
  abortSignal: args.abortSignal,
975
919
  });
976
920
  const resultByCallId = new Map();
@@ -981,42 +925,46 @@ async function executeFunctionRound(args) {
981
925
  }
982
926
  resultByCallId.set(result.id, result);
983
927
  };
984
- for (const result of issueResults) {
985
- register(result);
986
- }
987
- for (const result of tellaskFuncResults) {
928
+ for (const result of tellaskRound.tellaskResults) {
988
929
  register(result);
989
930
  }
990
931
  for (const result of genericResults) {
991
932
  register(result);
992
933
  }
993
934
  const pairedMessages = [];
935
+ const tellaskCallMsgById = new Map(tellaskRound.tellaskCallMessages.map((msg) => [msg.id, msg]));
936
+ const specialCallIds = new Set(tellaskRound.handledCallIds);
994
937
  for (const call of args.funcCalls) {
995
- const originalArgsStr = typeof call.arguments === 'string' ? call.arguments : JSON.stringify(call.arguments ?? {});
996
- const preparedArgs = prepareFuncCallArguments(originalArgsStr);
997
- pairedMessages.push({
998
- type: 'func_call_msg',
999
- role: 'assistant',
1000
- genseq: call.genseq,
1001
- id: call.id,
1002
- name: call.name,
1003
- arguments: preparedArgs.contextArguments,
1004
- });
938
+ const tellaskCallMsg = tellaskCallMsgById.get(call.id);
939
+ if (tellaskCallMsg) {
940
+ pairedMessages.push(tellaskCallMsg);
941
+ }
942
+ else {
943
+ const originalArgsStr = typeof call.arguments === 'string' ? call.arguments : JSON.stringify(call.arguments ?? {});
944
+ pairedMessages.push({
945
+ type: 'func_call_msg',
946
+ role: 'assistant',
947
+ genseq: call.genseq,
948
+ id: call.id,
949
+ name: call.name,
950
+ arguments: originalArgsStr,
951
+ });
952
+ }
1005
953
  const result = resultByCallId.get(call.id);
1006
954
  if (result) {
1007
955
  pairedMessages.push(result);
1008
956
  continue;
1009
957
  }
1010
958
  if (specialCallIds.has(call.id)) {
1011
- continue;
959
+ throw new Error(`kernel-driver tellask result invariant violation: missing tellask result for call id '${call.id}' (${call.name})`);
1012
960
  }
1013
961
  throw new Error(`kernel-driver function result invariant violation: missing result for call id '${call.id}' (${call.name})`);
1014
962
  }
1015
963
  return {
1016
- hadNormalToolCalls: classified.normalCalls.length > 0,
1017
- shouldStopAfterReplyTool,
964
+ hadNormalToolCalls: tellaskRound.normalCalls.length > 0,
965
+ shouldStopAfterReplyTool: tellaskRound.shouldStopAfterReplyTool,
1018
966
  pairedMessages,
1019
- tellaskToolOutputs,
967
+ tellaskToolOutputs: [...tellaskRound.toolOutputs],
1020
968
  };
1021
969
  }
1022
970
  async function resetDiligenceBudgetAfterQ4H(dlg, team) {
@@ -1233,7 +1181,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1233
1181
  const isSubdialog = dlg.id.rootId !== dlg.id.selfId;
1234
1182
  const fbrEffortDefault = resolveFbrEffortDefaultForTool(agent);
1235
1183
  const effectiveFuncTools = policy.mode === 'default'
1236
- ? mergeTellaskSpecialVirtualTools(canonicalFuncTools, {
1184
+ ? mergeTellaskVirtualTools(canonicalFuncTools, {
1237
1185
  includeTellaskBack: isSubdialog,
1238
1186
  fbrEffortDefault,
1239
1187
  })
@@ -1330,7 +1278,10 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1330
1278
  skipTaskdocForThisDrive = true;
1331
1279
  }
1332
1280
  const persistedUserLanguageCode = currentPrompt.userLanguageCode ?? dlg.getLastUserLanguageCode();
1333
- const q4hAnswerCallIds = normalizeQ4HAnswerCallIds(currentPrompt.q4hAnswerCallIds);
1281
+ const q4hAnswerCallId = normalizeQ4HAnswerCallId(currentPrompt.q4hAnswerCallId);
1282
+ // `q4hAnswerCallId` marks a continuation input for an already-materialized askHuman
1283
+ // answer. It is not a second business-level user prompt that should re-enter transcript.
1284
+ const isQ4HAnswerPrompt = q4hAnswerCallId !== undefined;
1334
1285
  const promptLanguage = persistedUserLanguageCode === 'zh' || persistedUserLanguageCode === 'en'
1335
1286
  ? persistedUserLanguageCode
1336
1287
  : (0, work_language_1.getWorkLanguage)();
@@ -1390,15 +1341,26 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1390
1341
  });
1391
1342
  currentRuntimeGuideMsg = undefined;
1392
1343
  }
1393
- await dlg.addChatMessages({
1394
- type: 'prompting_msg',
1395
- role: 'user',
1396
- genseq: dlg.activeGenSeq,
1397
- msgId: currentPrompt.msgId,
1398
- grammar: 'markdown',
1399
- content: replyGuidance.promptContent,
1400
- });
1401
- await dlg.persistUserMessage(replyGuidance.promptContent, currentPrompt.msgId, 'markdown', origin, persistedUserLanguageCode, q4hAnswerCallIds, replyGuidance.persistedTellaskReplyDirective);
1344
+ if (isQ4HAnswerPrompt) {
1345
+ // Record only the answered call correlation / user language for the resumed round.
1346
+ // The actual human answer fact was already persisted via askHuman tellask result flow.
1347
+ await dlg.receiveHumanReply({
1348
+ content: replyGuidance.promptContent,
1349
+ userLanguageCode: persistedUserLanguageCode,
1350
+ q4hAnswerCallId,
1351
+ });
1352
+ }
1353
+ else {
1354
+ await dlg.addChatMessages({
1355
+ type: 'prompting_msg',
1356
+ role: 'user',
1357
+ genseq: dlg.activeGenSeq,
1358
+ msgId: currentPrompt.msgId,
1359
+ grammar: 'markdown',
1360
+ content: replyGuidance.promptContent,
1361
+ });
1362
+ await dlg.persistUserMessage(replyGuidance.promptContent, currentPrompt.msgId, 'markdown', origin, persistedUserLanguageCode, q4hAnswerCallId, replyGuidance.persistedTellaskReplyDirective);
1363
+ }
1402
1364
  if (renderPromptAsRuntimeGuideBubble) {
1403
1365
  (0, evt_registry_1.postDialogEvent)(dlg, {
1404
1366
  type: 'runtime_guide_evt',
@@ -1407,10 +1369,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1407
1369
  content: replyGuidance.promptContent,
1408
1370
  });
1409
1371
  }
1410
- else {
1372
+ else if (!isQ4HAnswerPrompt) {
1411
1373
  // Emit the live user-side boundary event for UI generation bubbles.
1412
- // Without this, realtime turns can miss user content + divider (<hr/>),
1413
- // while replay (from persisted human_text_record) still looks correct.
1374
+ // Without this, realtime turns can miss user content + divider (<hr/>).
1414
1375
  (0, evt_registry_1.postDialogEvent)(dlg, {
1415
1376
  type: 'end_of_user_saying_evt',
1416
1377
  course: dlg.currentCourse,
@@ -1420,7 +1381,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1420
1381
  grammar: 'markdown',
1421
1382
  origin,
1422
1383
  userLanguageCode: persistedUserLanguageCode,
1423
- q4hAnswerCallIds,
1384
+ q4hAnswerCallId,
1424
1385
  });
1425
1386
  }
1426
1387
  if (currentPromptFromFbrState && currentFbrState) {
@@ -1469,7 +1430,27 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1469
1430
  });
1470
1431
  const newMsgs = [];
1471
1432
  const streamedFuncCalls = [];
1433
+ let sawWebSearchSideChannelOutput = false;
1434
+ let sawNativeToolSideChannelOutput = false;
1472
1435
  const streamOrBatch = async () => {
1436
+ let batchAttemptCourse;
1437
+ let batchAttemptCheckpointOffset;
1438
+ const rollbackBatchAttempt = async () => {
1439
+ if (batchAttemptCourse === undefined || batchAttemptCheckpointOffset === undefined) {
1440
+ throw new Error(`kernel-driver batch retry invariant violation: missing checkpoint (dialog=${dlg.id.valueOf()})`);
1441
+ }
1442
+ await persistence_1.DialogPersistence.rollbackCourseFileToOffset(dlg.id, batchAttemptCourse, batchAttemptCheckpointOffset, dlg.status);
1443
+ (0, evt_registry_1.postDialogEvent)(dlg, {
1444
+ type: 'genseq_discard_evt',
1445
+ course: batchAttemptCourse,
1446
+ genseq: dlg.activeGenSeq,
1447
+ reason: 'retry',
1448
+ });
1449
+ sawWebSearchSideChannelOutput = false;
1450
+ sawNativeToolSideChannelOutput = false;
1451
+ streamedFuncCalls.length = 0;
1452
+ newMsgs.length = 0;
1453
+ };
1473
1454
  if (agent.streaming === false) {
1474
1455
  const batch = await (0, runtime_1.runLlmRequestWithRetry)({
1475
1456
  dlg,
@@ -1482,13 +1463,21 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1482
1463
  retryMaxDelayMs: retryPolicy.maxDelayMs,
1483
1464
  classifyFailure: llmGen.classifyFailure?.bind(llmGen),
1484
1465
  canRetry: () => true,
1466
+ onRetry: rollbackBatchAttempt,
1467
+ onGiveUp: rollbackBatchAttempt,
1485
1468
  doRequest: async () => {
1469
+ batchAttemptCourse = dlg.activeGenCourseOrUndefined ?? dlg.currentCourse;
1470
+ batchAttemptCheckpointOffset = await persistence_1.DialogPersistence.captureCourseFileOffset(dlg.id, batchAttemptCourse, dlg.status);
1471
+ sawWebSearchSideChannelOutput = false;
1472
+ sawNativeToolSideChannelOutput = false;
1473
+ streamedFuncCalls.length = 0;
1474
+ newMsgs.length = 0;
1486
1475
  const batchResult = await llmGen.genMoreMessages(providerCfg, agent, systemPrompt, funcTools, {
1487
1476
  dialogSelfId: dlg.id.selfId,
1488
1477
  dialogRootId: dlg.id.rootId,
1489
1478
  promptCacheKey: `${dlg.id.selfId}:c${String(dlg.currentCourse)}`,
1490
1479
  }, ctxMsgs, dlg.activeGenSeq, abortSignal);
1491
- if (!hasMeaningfulBatchOutput(batchResult.messages)) {
1480
+ if (!hasMeaningfulBatchOutput(batchResult)) {
1492
1481
  throw {
1493
1482
  status: 503,
1494
1483
  code: KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE,
@@ -1502,6 +1491,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1502
1491
  usage: batch.usage,
1503
1492
  llmGenModel: batch.llmGenModel,
1504
1493
  batchMessages: batch.messages,
1494
+ batchOutputs: batch.outputs,
1505
1495
  };
1506
1496
  }
1507
1497
  let currentSayingContent = '';
@@ -1511,7 +1501,6 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1511
1501
  let streamAttemptCheckpointOffset;
1512
1502
  let streamAttemptSayingContent;
1513
1503
  let streamAttemptSayingGenseq;
1514
- let streamSawWebSearchCall = false;
1515
1504
  let streamActive = { kind: 'idle' };
1516
1505
  const rollbackStreamAttempt = async () => {
1517
1506
  if (streamAttemptCourse === undefined || streamAttemptCheckpointOffset === undefined) {
@@ -1530,7 +1519,8 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1530
1519
  currentSayingContent = '';
1531
1520
  streamAttemptSayingContent = undefined;
1532
1521
  streamAttemptSayingGenseq = undefined;
1533
- streamSawWebSearchCall = false;
1522
+ sawWebSearchSideChannelOutput = false;
1523
+ sawNativeToolSideChannelOutput = false;
1534
1524
  streamedFuncCalls.length = 0;
1535
1525
  newMsgs.length = 0;
1536
1526
  };
@@ -1626,8 +1616,13 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1626
1616
  },
1627
1617
  webSearchCall: async (call) => {
1628
1618
  throwIfAborted(abortSignal, dlg);
1629
- streamSawWebSearchCall = true;
1630
- await dlg.webSearchCall(call);
1619
+ sawWebSearchSideChannelOutput = true;
1620
+ await dlg.webSearchCall(projectLlmWebSearchCall(call));
1621
+ },
1622
+ nativeToolCall: async (call) => {
1623
+ throwIfAborted(abortSignal, dlg);
1624
+ sawNativeToolSideChannelOutput = true;
1625
+ await dlg.nativeToolCall(call);
1631
1626
  },
1632
1627
  };
1633
1628
  const res = await (0, runtime_1.runLlmRequestWithRetry)({
@@ -1652,7 +1647,8 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1652
1647
  currentSayingContent = '';
1653
1648
  streamAttemptSayingContent = undefined;
1654
1649
  streamAttemptSayingGenseq = undefined;
1655
- streamSawWebSearchCall = false;
1650
+ sawWebSearchSideChannelOutput = false;
1651
+ sawNativeToolSideChannelOutput = false;
1656
1652
  streamedFuncCalls.length = 0;
1657
1653
  newMsgs.length = 0;
1658
1654
  const streamResult = await llmGen.genToReceiver(providerCfg, agent, systemPrompt, funcTools, {
@@ -1666,7 +1662,8 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1666
1662
  if (!hasThinkingContent &&
1667
1663
  !hasSayingContent &&
1668
1664
  !hasFunctionCall &&
1669
- !streamSawWebSearchCall) {
1665
+ !sawWebSearchSideChannelOutput &&
1666
+ !sawNativeToolSideChannelOutput) {
1670
1667
  throw {
1671
1668
  status: 503,
1672
1669
  code: KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE,
@@ -1694,24 +1691,50 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1694
1691
  usage: llmOutput.usage,
1695
1692
  });
1696
1693
  dlg.setLastContextHealth(contextHealthForGen);
1697
- if (Array.isArray(llmOutput.batchMessages)) {
1698
- const assistantMsgs = llmOutput.batchMessages.filter((m) => m.type === 'saying_msg' || m.type === 'thinking_msg');
1699
- for (const msg of assistantMsgs) {
1700
- newMsgs.push(msg);
1701
- if (msg.type === 'thinking_msg') {
1702
- await (0, events_1.emitThinkingEvents)(dlg, msg.content, msg.reasoning);
1694
+ const batchOutputs = Array.isArray(llmOutput.batchOutputs) && llmOutput.batchOutputs.length > 0
1695
+ ? llmOutput.batchOutputs
1696
+ : Array.isArray(llmOutput.batchMessages)
1697
+ ? llmOutput.batchMessages.map((message) => ({ kind: 'message', message }))
1698
+ : [];
1699
+ for (const output of batchOutputs) {
1700
+ switch (output.kind) {
1701
+ case 'message': {
1702
+ const msg = output.message;
1703
+ if (msg.type === 'thinking_msg' || msg.type === 'saying_msg') {
1704
+ newMsgs.push(msg);
1705
+ if (msg.type === 'thinking_msg') {
1706
+ await (0, events_1.emitThinkingEvents)(dlg, msg.content, msg.reasoning);
1707
+ }
1708
+ else {
1709
+ lastAssistantSayingContent = msg.content;
1710
+ lastAssistantSayingGenseq = msg.genseq;
1711
+ lastAssistantReplyTarget = currentReplyTarget;
1712
+ await emitAssistantSaying(dlg, msg.content);
1713
+ }
1714
+ break;
1715
+ }
1716
+ if (msg.type === 'func_call_msg') {
1717
+ streamedFuncCalls.push(msg);
1718
+ }
1719
+ break;
1720
+ }
1721
+ case 'web_search_call': {
1722
+ sawWebSearchSideChannelOutput = true;
1723
+ await dlg.webSearchCall(projectLlmWebSearchCall(output.call));
1724
+ break;
1725
+ }
1726
+ case 'native_tool_call': {
1727
+ sawNativeToolSideChannelOutput = true;
1728
+ await dlg.nativeToolCall(output.call);
1729
+ break;
1703
1730
  }
1704
- else if (msg.type === 'saying_msg') {
1705
- lastAssistantSayingContent = msg.content;
1706
- lastAssistantSayingGenseq = msg.genseq;
1707
- lastAssistantReplyTarget = currentReplyTarget;
1708
- await emitAssistantSaying(dlg, msg.content);
1731
+ default: {
1732
+ const _exhaustive = output;
1733
+ throw new Error(`Unhandled batch output kind: ${String(_exhaustive)}`);
1709
1734
  }
1710
1735
  }
1711
- const funcCalls = llmOutput.batchMessages.filter((m) => m.type === 'func_call_msg');
1712
- streamedFuncCalls.push(...funcCalls);
1713
1736
  }
1714
- const tellaskCallCount = policy.allowTellaskSpecialFunctions
1737
+ const tellaskCallCount = policy.allowTellaskFunctions
1715
1738
  ? streamedFuncCalls.filter((c) => c.name === 'tellask' ||
1716
1739
  c.name === 'tellaskSessionless' ||
1717
1740
  c.name === 'tellaskBack' ||
@@ -1789,7 +1812,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1789
1812
  funcCalls: streamedFuncCalls,
1790
1813
  callbacks,
1791
1814
  abortSignal,
1792
- allowTellaskSpecialFunctions: policy.allowTellaskSpecialFunctions,
1815
+ allowTellaskFunctions: policy.allowTellaskFunctions,
1793
1816
  });
1794
1817
  if (routed.tellaskToolOutputs.length > 0) {
1795
1818
  newMsgs.push(...routed.tellaskToolOutputs);
@@ -1883,6 +1906,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1883
1906
  await resetDiligenceBudgetAfterQ4H(dlg, team);
1884
1907
  break;
1885
1908
  }
1909
+ // Continue only when this round produced new context that must be fed back into the next
1910
+ // LLM turn. Provider-native side-channel UI events are meaningful output, but they are not
1911
+ // transcript/context inputs and therefore must not trigger another generation round.
1886
1912
  const shouldContinue = streamedFuncCalls.length > 0 ||
1887
1913
  routed.pairedMessages.length > 0 ||
1888
1914
  routed.tellaskToolOutputs.length > 0;