dominds 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dist/dialog-global-registry.js +58 -3
  2. package/dist/dialog.js +17 -0
  3. package/dist/docs/dialog-system.md +18 -16
  4. package/dist/docs/dialog-system.zh.md +20 -19
  5. package/dist/evt-registry.js +16 -7
  6. package/dist/llm/driver-v2/context.js +2 -1
  7. package/dist/llm/driver-v2/core.js +102 -119
  8. package/dist/llm/driver-v2/orchestrator.js +65 -38
  9. package/dist/llm/driver-v2/round.js +213 -35
  10. package/dist/llm/driver-v2/runtime-utils.js +1 -0
  11. package/dist/llm/driver-v2/supdialog-response.js +5 -18
  12. package/dist/llm/driver-v2/tellask-bridge.js +95 -6
  13. package/dist/llm/driver.js +29 -2
  14. package/dist/minds/builtin/cmdr/persona.md +3 -0
  15. package/dist/minds/builtin/dijiang/knowledge.md +287 -0
  16. package/dist/minds/builtin/dijiang/persona.md +7 -0
  17. package/dist/persistence.js +28 -0
  18. package/dist/server/websocket-handler.js +134 -25
  19. package/dist/shared/diligence.js +8 -2
  20. package/dist/showing-by-doing.js +31 -34
  21. package/dist/static/assets/{_baseUniq-BxKNVs_E.js → _baseUniq-zV3pyJ-s.js} +2 -2
  22. package/dist/static/assets/{_baseUniq-BxKNVs_E.js.map → _baseUniq-zV3pyJ-s.js.map} +1 -1
  23. package/dist/static/assets/{arc-SobL0GO-.js → arc-Ctgmrh71.js} +2 -2
  24. package/dist/static/assets/{arc-SobL0GO-.js.map → arc-Ctgmrh71.js.map} +1 -1
  25. package/dist/static/assets/{architectureDiagram-VXUJARFQ-MMDS1QOC.js → architectureDiagram-VXUJARFQ-CAH1Zinn.js} +6 -6
  26. package/dist/static/assets/{architectureDiagram-VXUJARFQ-MMDS1QOC.js.map → architectureDiagram-VXUJARFQ-CAH1Zinn.js.map} +1 -1
  27. package/dist/static/assets/{blockDiagram-VD42YOAC-BOsSirNK.js → blockDiagram-VD42YOAC-Djz6JdD2.js} +7 -7
  28. package/dist/static/assets/{blockDiagram-VD42YOAC-BOsSirNK.js.map → blockDiagram-VD42YOAC-Djz6JdD2.js.map} +1 -1
  29. package/dist/static/assets/{c4Diagram-YG6GDRKO-D4jQkzwx.js → c4Diagram-YG6GDRKO-CSuskOyq.js} +3 -3
  30. package/dist/static/assets/{c4Diagram-YG6GDRKO-D4jQkzwx.js.map → c4Diagram-YG6GDRKO-CSuskOyq.js.map} +1 -1
  31. package/dist/static/assets/{channel-CNJ5IKbI.js → channel-Ym6U59Kh.js} +2 -2
  32. package/dist/static/assets/{channel-CNJ5IKbI.js.map → channel-Ym6U59Kh.js.map} +1 -1
  33. package/dist/static/assets/{chunk-4BX2VUAB-Dzp3xxyo.js → chunk-4BX2VUAB-CqbVylSz.js} +2 -2
  34. package/dist/static/assets/{chunk-4BX2VUAB-Dzp3xxyo.js.map → chunk-4BX2VUAB-CqbVylSz.js.map} +1 -1
  35. package/dist/static/assets/{chunk-55IACEB6-CbmixuSI.js → chunk-55IACEB6-CeWj8Jm_.js} +2 -2
  36. package/dist/static/assets/{chunk-55IACEB6-CbmixuSI.js.map → chunk-55IACEB6-CeWj8Jm_.js.map} +1 -1
  37. package/dist/static/assets/{chunk-B4BG7PRW-CdpmYjdb.js → chunk-B4BG7PRW-BjxJkuhq.js} +5 -5
  38. package/dist/static/assets/{chunk-B4BG7PRW-CdpmYjdb.js.map → chunk-B4BG7PRW-BjxJkuhq.js.map} +1 -1
  39. package/dist/static/assets/{chunk-DI55MBZ5-wBUIgR3h.js → chunk-DI55MBZ5-9kxfXB4B.js} +4 -4
  40. package/dist/static/assets/{chunk-DI55MBZ5-wBUIgR3h.js.map → chunk-DI55MBZ5-9kxfXB4B.js.map} +1 -1
  41. package/dist/static/assets/{chunk-FMBD7UC4-l62jcBz-.js → chunk-FMBD7UC4-DyM3IVWB.js} +2 -2
  42. package/dist/static/assets/{chunk-FMBD7UC4-l62jcBz-.js.map → chunk-FMBD7UC4-DyM3IVWB.js.map} +1 -1
  43. package/dist/static/assets/{chunk-QN33PNHL-BXfKZUpE.js → chunk-QN33PNHL-DGU21aEB.js} +2 -2
  44. package/dist/static/assets/{chunk-QN33PNHL-BXfKZUpE.js.map → chunk-QN33PNHL-DGU21aEB.js.map} +1 -1
  45. package/dist/static/assets/{chunk-QZHKN3VN-BYB4VxWj.js → chunk-QZHKN3VN-Cw9Hpt-y.js} +2 -2
  46. package/dist/static/assets/{chunk-QZHKN3VN-BYB4VxWj.js.map → chunk-QZHKN3VN-Cw9Hpt-y.js.map} +1 -1
  47. package/dist/static/assets/{chunk-TZMSLE5B-CvlWEnXl.js → chunk-TZMSLE5B-J-eTCnFj.js} +2 -2
  48. package/dist/static/assets/{chunk-TZMSLE5B-CvlWEnXl.js.map → chunk-TZMSLE5B-J-eTCnFj.js.map} +1 -1
  49. package/dist/static/assets/{classDiagram-2ON5EDUG-x2kw_AQY.js → classDiagram-2ON5EDUG-BvoDAd8E.js} +6 -6
  50. package/dist/static/assets/{classDiagram-2ON5EDUG-x2kw_AQY.js.map → classDiagram-2ON5EDUG-BvoDAd8E.js.map} +1 -1
  51. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-x2kw_AQY.js → classDiagram-v2-WZHVMYZB-BvoDAd8E.js} +6 -6
  52. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-x2kw_AQY.js.map → classDiagram-v2-WZHVMYZB-BvoDAd8E.js.map} +1 -1
  53. package/dist/static/assets/{clone-CfBpi3MU.js → clone-CojsCkky.js} +2 -2
  54. package/dist/static/assets/{clone-CfBpi3MU.js.map → clone-CojsCkky.js.map} +1 -1
  55. package/dist/static/assets/{cose-bilkent-S5V4N54A-DQSzPb7y.js → cose-bilkent-S5V4N54A-BEtK3OcG.js} +2 -2
  56. package/dist/static/assets/{cose-bilkent-S5V4N54A-DQSzPb7y.js.map → cose-bilkent-S5V4N54A-BEtK3OcG.js.map} +1 -1
  57. package/dist/static/assets/{dagre-6UL2VRFP-BuBo_U3J.js → dagre-6UL2VRFP-C5DMaVj0.js} +7 -7
  58. package/dist/static/assets/{dagre-6UL2VRFP-BuBo_U3J.js.map → dagre-6UL2VRFP-C5DMaVj0.js.map} +1 -1
  59. package/dist/static/assets/{diagram-PSM6KHXK-J2z13Z5A.js → diagram-PSM6KHXK-kVP0dZ6K.js} +7 -7
  60. package/dist/static/assets/{diagram-PSM6KHXK-J2z13Z5A.js.map → diagram-PSM6KHXK-kVP0dZ6K.js.map} +1 -1
  61. package/dist/static/assets/{diagram-QEK2KX5R-x8XUGykE.js → diagram-QEK2KX5R-C4p2yibe.js} +6 -6
  62. package/dist/static/assets/{diagram-QEK2KX5R-x8XUGykE.js.map → diagram-QEK2KX5R-C4p2yibe.js.map} +1 -1
  63. package/dist/static/assets/{diagram-S2PKOQOG-BE6Nr1BM.js → diagram-S2PKOQOG-B3vObsYY.js} +6 -6
  64. package/dist/static/assets/{diagram-S2PKOQOG-BE6Nr1BM.js.map → diagram-S2PKOQOG-B3vObsYY.js.map} +1 -1
  65. package/dist/static/assets/{erDiagram-Q2GNP2WA-D3qVnFAh.js → erDiagram-Q2GNP2WA-AKQItpVu.js} +5 -5
  66. package/dist/static/assets/{erDiagram-Q2GNP2WA-D3qVnFAh.js.map → erDiagram-Q2GNP2WA-AKQItpVu.js.map} +1 -1
  67. package/dist/static/assets/{flowDiagram-NV44I4VS-Cjp9Rt9t.js → flowDiagram-NV44I4VS-2dalJoL3.js} +6 -6
  68. package/dist/static/assets/{flowDiagram-NV44I4VS-Cjp9Rt9t.js.map → flowDiagram-NV44I4VS-2dalJoL3.js.map} +1 -1
  69. package/dist/static/assets/{ganttDiagram-JELNMOA3-D_hc66FC.js → ganttDiagram-JELNMOA3-DUw98Lbz.js} +3 -3
  70. package/dist/static/assets/{ganttDiagram-JELNMOA3-D_hc66FC.js.map → ganttDiagram-JELNMOA3-DUw98Lbz.js.map} +1 -1
  71. package/dist/static/assets/{gitGraphDiagram-NY62KEGX-1I_zaTtz.js → gitGraphDiagram-NY62KEGX-DdwIFk8M.js} +7 -7
  72. package/dist/static/assets/{gitGraphDiagram-NY62KEGX-1I_zaTtz.js.map → gitGraphDiagram-NY62KEGX-DdwIFk8M.js.map} +1 -1
  73. package/dist/static/assets/{graph-K6mmQESm.js → graph-BBEAP9Z9.js} +3 -3
  74. package/dist/static/assets/{graph-K6mmQESm.js.map → graph-BBEAP9Z9.js.map} +1 -1
  75. package/dist/static/assets/{index-RV1MEt3b.js → index-CqMOe9zt.js} +195 -114
  76. package/dist/static/assets/index-CqMOe9zt.js.map +1 -0
  77. package/dist/static/assets/{infoDiagram-WHAUD3N6-CqazW4ov.js → infoDiagram-WHAUD3N6-Cep11ST9.js} +5 -5
  78. package/dist/static/assets/{infoDiagram-WHAUD3N6-CqazW4ov.js.map → infoDiagram-WHAUD3N6-Cep11ST9.js.map} +1 -1
  79. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-CCwUViz2.js → journeyDiagram-XKPGCS4Q-Ba8wkBNB.js} +5 -5
  80. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-CCwUViz2.js.map → journeyDiagram-XKPGCS4Q-Ba8wkBNB.js.map} +1 -1
  81. package/dist/static/assets/{kanban-definition-3W4ZIXB7-BKj9P06Z.js → kanban-definition-3W4ZIXB7-DhxNBORX.js} +3 -3
  82. package/dist/static/assets/{kanban-definition-3W4ZIXB7-BKj9P06Z.js.map → kanban-definition-3W4ZIXB7-DhxNBORX.js.map} +1 -1
  83. package/dist/static/assets/{layout-BcrPlYah.js → layout-Ckbr6Zyz.js} +5 -5
  84. package/dist/static/assets/{layout-BcrPlYah.js.map → layout-Ckbr6Zyz.js.map} +1 -1
  85. package/dist/static/assets/{linear-DYXbzldi.js → linear-BrSsI51I.js} +2 -2
  86. package/dist/static/assets/{linear-DYXbzldi.js.map → linear-BrSsI51I.js.map} +1 -1
  87. package/dist/static/assets/{min-CAfJrdkg.js → min-CeEvLc8D.js} +3 -3
  88. package/dist/static/assets/{min-CAfJrdkg.js.map → min-CeEvLc8D.js.map} +1 -1
  89. package/dist/static/assets/{mindmap-definition-VGOIOE7T-CXWJYrd0.js → mindmap-definition-VGOIOE7T-7ctLd3o3.js} +4 -4
  90. package/dist/static/assets/{mindmap-definition-VGOIOE7T-CXWJYrd0.js.map → mindmap-definition-VGOIOE7T-7ctLd3o3.js.map} +1 -1
  91. package/dist/static/assets/{pieDiagram-ADFJNKIX-DjKlsPYr.js → pieDiagram-ADFJNKIX-CqyILziW.js} +7 -7
  92. package/dist/static/assets/{pieDiagram-ADFJNKIX-DjKlsPYr.js.map → pieDiagram-ADFJNKIX-CqyILziW.js.map} +1 -1
  93. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-fcJtlyUO.js → quadrantDiagram-AYHSOK5B-Byi_xYum.js} +3 -3
  94. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-fcJtlyUO.js.map → quadrantDiagram-AYHSOK5B-Byi_xYum.js.map} +1 -1
  95. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DmB9Ezaa.js → requirementDiagram-UZGBJVZJ-Boj5vxiZ.js} +4 -4
  96. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DmB9Ezaa.js.map → requirementDiagram-UZGBJVZJ-Boj5vxiZ.js.map} +1 -1
  97. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-CdGDwaMo.js → sankeyDiagram-TZEHDZUN-DzoUAdfa.js} +2 -2
  98. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-CdGDwaMo.js.map → sankeyDiagram-TZEHDZUN-DzoUAdfa.js.map} +1 -1
  99. package/dist/static/assets/{sequenceDiagram-WL72ISMW-C-jiKkhi.js → sequenceDiagram-WL72ISMW-BKKxpG1m.js} +4 -4
  100. package/dist/static/assets/{sequenceDiagram-WL72ISMW-C-jiKkhi.js.map → sequenceDiagram-WL72ISMW-BKKxpG1m.js.map} +1 -1
  101. package/dist/static/assets/{stateDiagram-FKZM4ZOC-M9808W5W.js → stateDiagram-FKZM4ZOC-B-J7uRZT.js} +9 -9
  102. package/dist/static/assets/{stateDiagram-FKZM4ZOC-M9808W5W.js.map → stateDiagram-FKZM4ZOC-B-J7uRZT.js.map} +1 -1
  103. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-JE0YQyVh.js → stateDiagram-v2-4FDKWEC3-BOzmoUba.js} +5 -5
  104. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-JE0YQyVh.js.map → stateDiagram-v2-4FDKWEC3-BOzmoUba.js.map} +1 -1
  105. package/dist/static/assets/{timeline-definition-IT6M3QCI-a2Rh8B62.js → timeline-definition-IT6M3QCI-zu1Rojx9.js} +3 -3
  106. package/dist/static/assets/{timeline-definition-IT6M3QCI-a2Rh8B62.js.map → timeline-definition-IT6M3QCI-zu1Rojx9.js.map} +1 -1
  107. package/dist/static/assets/{treemap-KMMF4GRG-D3tvybuo.js → treemap-KMMF4GRG-C4D91awC.js} +4 -4
  108. package/dist/static/assets/{treemap-KMMF4GRG-D3tvybuo.js.map → treemap-KMMF4GRG-C4D91awC.js.map} +1 -1
  109. package/dist/static/assets/{xychartDiagram-PRI3JC2R-kmwKLP3Y.js → xychartDiagram-PRI3JC2R-YvLKaPZ-.js} +3 -3
  110. package/dist/static/assets/{xychartDiagram-PRI3JC2R-kmwKLP3Y.js.map → xychartDiagram-PRI3JC2R-YvLKaPZ-.js.map} +1 -1
  111. package/dist/static/index.html +1 -1
  112. package/dist/tools/context-health.js +7 -7
  113. package/dist/utils/task-doc.js +16 -16
  114. package/package.json +1 -1
  115. package/dist/docs/keep-going.zh.md +0 -162
  116. package/dist/docs/showing-by-doing.md +0 -208
  117. package/dist/docs/showing-by-doing.zh.md +0 -177
  118. package/dist/minds/promptdocs.js +0 -263
  119. package/dist/snippets/README.en.md +0 -3
  120. package/dist/static/assets/index-RV1MEt3b.js.map +0 -1
@@ -42,48 +42,75 @@ async function supplyResponseToSupdialog(...args) {
42
42
  },
43
43
  });
44
44
  }
45
+ function formatDriveTriggerForLog(trigger) {
46
+ return {
47
+ action: trigger.action,
48
+ rootId: trigger.rootId,
49
+ entryFound: trigger.entryFound,
50
+ previousNeedsDrive: trigger.previousNeedsDrive,
51
+ nextNeedsDrive: trigger.nextNeedsDrive,
52
+ source: trigger.source,
53
+ reason: trigger.reason,
54
+ emittedAtMs: trigger.emittedAtMs,
55
+ };
56
+ }
57
+ async function driveQueuedDialogsOnce() {
58
+ const dialogsToDrive = dialog_global_registry_1.globalDialogRegistry.getDialogsNeedingDrive();
59
+ for (const rootDialog of dialogsToDrive) {
60
+ try {
61
+ const latest = await persistence_1.DialogPersistence.loadDialogLatest(rootDialog.id, 'running');
62
+ const runStateKind = latest?.runState?.kind;
63
+ if (runStateKind === 'interrupted' || runStateKind === 'proceeding_stop_requested') {
64
+ dialog_global_registry_1.globalDialogRegistry.markNotNeedingDrive(rootDialog.id.rootId, {
65
+ source: 'driver_v2_backend_loop',
66
+ reason: `run_state_blocked:${runStateKind}`,
67
+ });
68
+ await persistence_1.DialogPersistence.setNeedsDrive(rootDialog.id, false, rootDialog.status);
69
+ continue;
70
+ }
71
+ if (!(await rootDialog.canDrive())) {
72
+ continue;
73
+ }
74
+ await driveDialogStream(rootDialog, undefined, true);
75
+ const status = await rootDialog.getSuspensionStatus();
76
+ const shouldStayQueued = rootDialog.hasUpNext() || !status.canDrive;
77
+ if (shouldStayQueued) {
78
+ dialog_global_registry_1.globalDialogRegistry.markNeedsDrive(rootDialog.id.rootId, {
79
+ source: 'driver_v2_backend_loop',
80
+ reason: rootDialog.hasUpNext() ? 'post_drive_upnext_pending' : 'post_drive_suspended',
81
+ });
82
+ await persistence_1.DialogPersistence.setNeedsDrive(rootDialog.id, true, rootDialog.status);
83
+ }
84
+ else {
85
+ dialog_global_registry_1.globalDialogRegistry.markNotNeedingDrive(rootDialog.id.rootId, {
86
+ source: 'driver_v2_backend_loop',
87
+ reason: 'post_drive_idle',
88
+ });
89
+ await persistence_1.DialogPersistence.setNeedsDrive(rootDialog.id, false, rootDialog.status);
90
+ }
91
+ if (status.subdialogs) {
92
+ log_1.log.info(`Dialog ${rootDialog.id.rootId} suspended, waiting for subdialogs`);
93
+ }
94
+ if (status.q4h) {
95
+ log_1.log.info(`Dialog ${rootDialog.id.rootId} awaiting Q4H answer`);
96
+ }
97
+ }
98
+ catch (err) {
99
+ log_1.log.error(`Error driving dialog ${rootDialog.id.rootId}:`, err, undefined, {
100
+ dialogId: rootDialog.id.rootId,
101
+ });
102
+ }
103
+ }
104
+ }
45
105
  function runBackendDriver() {
46
106
  return (async () => {
47
107
  while (true) {
48
108
  try {
49
- const dialogsToDrive = dialog_global_registry_1.globalDialogRegistry.getDialogsNeedingDrive();
50
- for (const rootDialog of dialogsToDrive) {
51
- try {
52
- const latest = await persistence_1.DialogPersistence.loadDialogLatest(rootDialog.id, 'running');
53
- const runStateKind = latest?.runState?.kind;
54
- if (runStateKind === 'interrupted' || runStateKind === 'proceeding_stop_requested') {
55
- dialog_global_registry_1.globalDialogRegistry.markNotNeedingDrive(rootDialog.id.rootId);
56
- await persistence_1.DialogPersistence.setNeedsDrive(rootDialog.id, false, rootDialog.status);
57
- continue;
58
- }
59
- if (!(await rootDialog.canDrive())) {
60
- continue;
61
- }
62
- await driveDialogStream(rootDialog, undefined, true);
63
- const status = await rootDialog.getSuspensionStatus();
64
- const shouldStayQueued = rootDialog.hasUpNext() || !status.canDrive;
65
- if (shouldStayQueued) {
66
- dialog_global_registry_1.globalDialogRegistry.markNeedsDrive(rootDialog.id.rootId);
67
- await persistence_1.DialogPersistence.setNeedsDrive(rootDialog.id, true, rootDialog.status);
68
- }
69
- else {
70
- dialog_global_registry_1.globalDialogRegistry.markNotNeedingDrive(rootDialog.id.rootId);
71
- await persistence_1.DialogPersistence.setNeedsDrive(rootDialog.id, false, rootDialog.status);
72
- }
73
- if (status.subdialogs) {
74
- log_1.log.info(`Dialog ${rootDialog.id.rootId} suspended, waiting for subdialogs`);
75
- }
76
- if (status.q4h) {
77
- log_1.log.info(`Dialog ${rootDialog.id.rootId} awaiting Q4H answer`);
78
- }
79
- }
80
- catch (err) {
81
- log_1.log.error(`Error driving dialog ${rootDialog.id.rootId}:`, err, undefined, {
82
- dialogId: rootDialog.id.rootId,
83
- });
84
- }
85
- }
86
- await new Promise((resolve) => setTimeout(resolve, 100));
109
+ await driveQueuedDialogsOnce();
110
+ const trigger = await dialog_global_registry_1.globalDialogRegistry.waitForDriveTrigger();
111
+ log_1.log.debug('Backend driver woke from drive trigger event', undefined, {
112
+ trigger: formatDriveTriggerForLog(trigger),
113
+ });
87
114
  }
88
115
  catch (loopErr) {
89
116
  log_1.log.error('Error in backend driver loop:', loopErr);
@@ -6,11 +6,95 @@ const dialog_run_state_1 = require("../../dialog-run-state");
6
6
  const log_1 = require("../../log");
7
7
  const load_1 = require("../../minds/load");
8
8
  const persistence_1 = require("../../persistence");
9
+ const driver_messages_1 = require("../../shared/i18n/driver-messages");
9
10
  const runtime_language_1 = require("../../shared/runtime-language");
11
+ const id_1 = require("../../shared/utils/id");
10
12
  const context_health_1 = require("./context-health");
11
13
  const core_1 = require("./core");
12
14
  const policy_1 = require("./policy");
13
15
  const supdialog_response_1 = require("./supdialog-response");
16
+ const defaultCriticalCountdownGenerations = 5;
17
+ async function loadPendingDiagnosticsSnapshot(args) {
18
+ const ownerDialogIdObj = new dialog_1.DialogID(args.ownerDialogId, args.rootId);
19
+ try {
20
+ const pending = await persistence_1.DialogPersistence.loadPendingSubdialogs(ownerDialogIdObj);
21
+ const matchedSubdialogIds = pending
22
+ .filter((record) => record.subdialogId === args.expectedSubdialogId)
23
+ .map((record) => record.subdialogId);
24
+ return {
25
+ kind: 'loaded',
26
+ ownerDialogId: args.ownerDialogId,
27
+ totalCount: pending.length,
28
+ matchedSubdialogIds,
29
+ records: pending.map((record) => ({
30
+ subdialogId: record.subdialogId,
31
+ callType: record.callType,
32
+ targetAgentId: record.targetAgentId,
33
+ tellaskSession: record.tellaskSession,
34
+ createdAt: record.createdAt,
35
+ tellaskHeadSummary: record.tellaskHead.trim().slice(0, 160),
36
+ })),
37
+ };
38
+ }
39
+ catch (err) {
40
+ return {
41
+ kind: 'error',
42
+ ownerDialogId: args.ownerDialogId,
43
+ error: err instanceof Error ? err.message : String(err),
44
+ };
45
+ }
46
+ }
47
+ const contextHealthRoundStateByDialogKey = new Map();
48
+ function getContextHealthRoundState(dialog) {
49
+ const key = dialog.id.key();
50
+ const existing = contextHealthRoundStateByDialogKey.get(key);
51
+ if (existing) {
52
+ return existing;
53
+ }
54
+ const created = {};
55
+ contextHealthRoundStateByDialogKey.set(key, created);
56
+ return created;
57
+ }
58
+ function resetContextHealthRoundState(dialog) {
59
+ contextHealthRoundStateByDialogKey.delete(dialog.id.key());
60
+ }
61
+ function resolveCriticalCountdownRemaining(dialog, snapshot) {
62
+ if (!snapshot || snapshot.kind !== 'available') {
63
+ resetContextHealthRoundState(dialog);
64
+ return defaultCriticalCountdownGenerations;
65
+ }
66
+ if (snapshot.level !== 'critical') {
67
+ if (snapshot.level === 'healthy') {
68
+ resetContextHealthRoundState(dialog);
69
+ return defaultCriticalCountdownGenerations;
70
+ }
71
+ const state = getContextHealthRoundState(dialog);
72
+ state.lastSeenLevel = snapshot.level;
73
+ state.criticalCountdownRemaining = undefined;
74
+ return defaultCriticalCountdownGenerations;
75
+ }
76
+ const state = getContextHealthRoundState(dialog);
77
+ if (state.lastSeenLevel !== 'critical' ||
78
+ typeof state.criticalCountdownRemaining !== 'number' ||
79
+ !Number.isFinite(state.criticalCountdownRemaining)) {
80
+ state.lastSeenLevel = 'critical';
81
+ state.criticalCountdownRemaining = defaultCriticalCountdownGenerations;
82
+ }
83
+ const remaining = Math.floor(state.criticalCountdownRemaining);
84
+ return remaining > 0 ? remaining : 0;
85
+ }
86
+ function consumeCriticalCountdown(dialog) {
87
+ const state = getContextHealthRoundState(dialog);
88
+ const currentRaw = typeof state.criticalCountdownRemaining === 'number' &&
89
+ Number.isFinite(state.criticalCountdownRemaining)
90
+ ? Math.floor(state.criticalCountdownRemaining)
91
+ : defaultCriticalCountdownGenerations;
92
+ const current = currentRaw > 0 ? currentRaw : 0;
93
+ const next = Math.max(0, current - 1);
94
+ state.lastSeenLevel = 'critical';
95
+ state.criticalCountdownRemaining = next;
96
+ return next;
97
+ }
14
98
  function resolveEffectivePrompt(dialog, humanPrompt) {
15
99
  if (humanPrompt) {
16
100
  return humanPrompt;
@@ -22,7 +106,7 @@ function resolveEffectivePrompt(dialog, humanPrompt) {
22
106
  return {
23
107
  content: upNext.prompt,
24
108
  msgId: upNext.msgId,
25
- grammar: 'markdown',
109
+ grammar: upNext.grammar ?? 'markdown',
26
110
  userLanguageCode: upNext.userLanguageCode === 'zh' || upNext.userLanguageCode === 'en'
27
111
  ? upNext.userLanguageCode
28
112
  : undefined,
@@ -77,6 +161,24 @@ async function executeDriveRound(args) {
77
161
  dialogId: dialog.id.valueOf(),
78
162
  });
79
163
  }
164
+ // Queued/auto drive (without fresh human input) must not proceed while dialog is
165
+ // suspended by pending Q4H or subdialogs. This prevents duplicate generations when
166
+ // multiple wake-ups race around the same subdialog completion boundary.
167
+ if (!humanPrompt) {
168
+ const suspension = await dialog.getSuspensionStatus();
169
+ if (!suspension.canDrive) {
170
+ log_1.log.info('driver-v2 skip queued auto-drive while dialog is suspended', {
171
+ dialogId: dialog.id.valueOf(),
172
+ rootId: dialog.id.rootId,
173
+ selfId: dialog.id.selfId,
174
+ waitInQue,
175
+ hasQueuedUpNext: dialog.hasUpNext(),
176
+ waitingQ4H: suspension.q4h,
177
+ waitingSubdialogs: suspension.subdialogs,
178
+ });
179
+ return;
180
+ }
181
+ }
80
182
  const minds = await (0, load_1.loadAgentMinds)(dialog.agentId, dialog);
81
183
  const policy = (0, policy_1.buildDriverV2Policy)({
82
184
  dlg: dialog,
@@ -89,18 +191,56 @@ async function executeDriveRound(args) {
89
191
  if (!validation.ok) {
90
192
  throw new Error(`driver-v2 policy invariant violation: ${validation.detail}`);
91
193
  }
194
+ const snapshot = dialog.getLastContextHealth();
195
+ const hasQueuedUpNext = dialog.hasUpNext();
196
+ const criticalCountdownRemaining = resolveCriticalCountdownRemaining(dialog, snapshot);
92
197
  const healthDecision = (0, context_health_1.decideDriverV2ContextHealth)({
93
- snapshot: dialog.getLastContextHealth(),
94
- hadUserPromptThisGen: args.driveArgs[1] !== undefined,
95
- criticalCountdownRemaining: 1,
198
+ snapshot,
199
+ hadUserPromptThisGen: humanPrompt !== undefined,
200
+ criticalCountdownRemaining,
96
201
  });
97
202
  if (healthDecision.kind === 'suspend') {
98
203
  return;
99
204
  }
205
+ let healthPrompt;
206
+ if (healthDecision.kind === 'continue') {
207
+ if (healthDecision.reason === 'critical_force_new_course') {
208
+ const language = (0, runtime_language_1.getWorkLanguage)();
209
+ const newCoursePrompt = language === 'zh'
210
+ ? '系统因上下文已告急(critical)而自动开启新一程对话,请继续推进任务。'
211
+ : 'System auto-started a new dialog course because context health is critical. Please continue the task.';
212
+ await dialog.startNewCourse(newCoursePrompt);
213
+ dialog.setLastContextHealth({ kind: 'unavailable', reason: 'usage_unavailable' });
214
+ resetContextHealthRoundState(dialog);
215
+ }
216
+ else if (!hasQueuedUpNext) {
217
+ const language = (0, runtime_language_1.getWorkLanguage)();
218
+ const guideText = healthDecision.reason === 'caution_soft_remediation'
219
+ ? (0, driver_messages_1.formatUserFacingContextHealthV3RemediationGuide)(language, {
220
+ kind: 'caution',
221
+ mode: 'soft',
222
+ })
223
+ : (0, driver_messages_1.formatUserFacingContextHealthV3RemediationGuide)(language, {
224
+ kind: 'critical',
225
+ mode: 'countdown',
226
+ promptsRemainingAfterThis: consumeCriticalCountdown(dialog),
227
+ promptsTotal: defaultCriticalCountdownGenerations,
228
+ });
229
+ healthPrompt = {
230
+ content: guideText,
231
+ msgId: (0, id_1.generateShortId)(),
232
+ grammar: 'markdown',
233
+ userLanguageCode: language,
234
+ };
235
+ }
236
+ }
100
237
  args.runtime.driveCount += 1;
101
238
  args.runtime.totalGenIterations += 1;
102
239
  args.runtime.usedLegacyDriveCore = false;
103
- const effectivePrompt = resolveEffectivePrompt(dialog, humanPrompt);
240
+ const promptForCore = healthDecision.kind === 'continue' && healthDecision.reason === 'critical_force_new_course'
241
+ ? undefined
242
+ : (healthPrompt ?? humanPrompt);
243
+ const effectivePrompt = resolveEffectivePrompt(dialog, promptForCore);
104
244
  subdialogReplyTarget = effectivePrompt?.subdialogReplyTarget;
105
245
  if (effectivePrompt && effectivePrompt.userLanguageCode) {
106
246
  dialog.setLastUserLanguageCode(effectivePrompt.userLanguageCode);
@@ -120,47 +260,85 @@ async function executeDriveRound(args) {
120
260
  }
121
261
  release();
122
262
  }
123
- if (followUp) {
124
- args.scheduleDrive(dialog, {
125
- waitInQue: true,
126
- humanPrompt: {
127
- content: followUp.prompt,
128
- msgId: followUp.msgId,
129
- grammar: 'markdown',
130
- userLanguageCode: followUp.userLanguageCode === 'zh' || followUp.userLanguageCode === 'en'
131
- ? followUp.userLanguageCode
132
- : undefined,
133
- },
134
- });
135
- return;
136
- }
137
263
  if (dialog instanceof dialog_1.SubDialog &&
138
264
  driveResult &&
139
265
  !driveResult.interrupted &&
140
266
  driveResult.lastAssistantSayingContent !== null) {
141
- const suspension = await dialog.getSuspensionStatus();
142
- if (!suspension.canDrive) {
143
- log_1.log.info('driver-v2 skip supplying subdialog response because dialog is still suspended', {
144
- rootId: dialog.id.rootId,
145
- selfId: dialog.id.selfId,
146
- waitingQ4H: suspension.q4h,
147
- waitingSubdialogs: suspension.subdialogs,
148
- });
149
- return;
150
- }
267
+ let supplied = false;
151
268
  if (subdialogReplyTarget) {
152
- await (0, supdialog_response_1.supplySubdialogResponseToSpecificCallerIfPendingV2)({
269
+ supplied = await (0, supdialog_response_1.supplySubdialogResponseToSpecificCallerIfPendingV2)({
153
270
  subdialog: dialog,
154
271
  responseText: driveResult.lastAssistantSayingContent,
155
272
  target: subdialogReplyTarget,
156
273
  scheduleDrive: args.scheduleDrive,
157
274
  });
158
- return;
275
+ if (!supplied) {
276
+ supplied = await (0, supdialog_response_1.supplySubdialogResponseToAssignedCallerIfPendingV2)({
277
+ subdialog: dialog,
278
+ responseText: driveResult.lastAssistantSayingContent,
279
+ scheduleDrive: args.scheduleDrive,
280
+ });
281
+ }
159
282
  }
160
- await (0, supdialog_response_1.supplySubdialogResponseToAssignedCallerIfPendingV2)({
161
- subdialog: dialog,
162
- responseText: driveResult.lastAssistantSayingContent,
163
- scheduleDrive: args.scheduleDrive,
283
+ else {
284
+ supplied = await (0, supdialog_response_1.supplySubdialogResponseToAssignedCallerIfPendingV2)({
285
+ subdialog: dialog,
286
+ responseText: driveResult.lastAssistantSayingContent,
287
+ scheduleDrive: args.scheduleDrive,
288
+ });
289
+ }
290
+ if (!supplied && subdialogReplyTarget) {
291
+ const assignment = dialog.assignmentFromSup;
292
+ const ownerDialogIds = Array.from(new Set([subdialogReplyTarget.ownerDialogId, assignment.callerDialogId]));
293
+ const pendingSnapshots = await Promise.all(ownerDialogIds.map(async (ownerDialogId) => loadPendingDiagnosticsSnapshot({
294
+ rootId: dialog.id.rootId,
295
+ ownerDialogId,
296
+ expectedSubdialogId: dialog.id.selfId,
297
+ })));
298
+ const streamErr = `Subdialog response supply invariant violation: ` +
299
+ `subdialog=${dialog.id.selfId} root=${dialog.id.rootId} ` +
300
+ `targetOwner=${subdialogReplyTarget.ownerDialogId} targetCallType=${subdialogReplyTarget.callType} targetCallId=${subdialogReplyTarget.callId} ` +
301
+ `assignmentCaller=${assignment.callerDialogId} assignmentCallId=${assignment.callId} ` +
302
+ `pendingSnapshots=${pendingSnapshots.length}`;
303
+ try {
304
+ await dialog.streamError(streamErr);
305
+ }
306
+ catch (streamErrPost) {
307
+ log_1.log.warn('driver-v2 failed to emit stream_error_evt for response supply violation', {
308
+ rootId: dialog.id.rootId,
309
+ selfId: dialog.id.selfId,
310
+ targetOwnerDialogId: subdialogReplyTarget.ownerDialogId,
311
+ targetCallType: subdialogReplyTarget.callType,
312
+ targetCallId: subdialogReplyTarget.callId,
313
+ assignmentCallerDialogId: assignment.callerDialogId,
314
+ assignmentCallId: assignment.callId,
315
+ pendingSnapshots,
316
+ error: streamErrPost instanceof Error ? streamErrPost.message : String(streamErrPost),
317
+ });
318
+ }
319
+ log_1.log.error('driver-v2 subdialog produced response but found no pending caller to supply', {
320
+ rootId: dialog.id.rootId,
321
+ selfId: dialog.id.selfId,
322
+ targetOwnerDialogId: subdialogReplyTarget.ownerDialogId,
323
+ targetCallType: subdialogReplyTarget.callType,
324
+ targetCallId: subdialogReplyTarget.callId,
325
+ assignmentCallerDialogId: assignment.callerDialogId,
326
+ assignmentCallId: assignment.callId,
327
+ pendingSnapshots,
328
+ });
329
+ }
330
+ }
331
+ if (followUp) {
332
+ args.scheduleDrive(dialog, {
333
+ waitInQue: true,
334
+ humanPrompt: {
335
+ content: followUp.prompt,
336
+ msgId: followUp.msgId,
337
+ grammar: followUp.grammar ?? 'markdown',
338
+ userLanguageCode: followUp.userLanguageCode === 'zh' || followUp.userLanguageCode === 'en'
339
+ ? followUp.userLanguageCode
340
+ : undefined,
341
+ },
164
342
  });
165
343
  }
166
344
  }
@@ -181,6 +181,7 @@ async function suspendForKeepGoingBudgetExhausted(options) {
181
181
  bodyContent: question.bodyContent,
182
182
  askedAt: question.askedAt,
183
183
  callId: question.callId,
184
+ remainingCallIds: question.remainingCallIds,
184
185
  callSiteRef: question.callSiteRef,
185
186
  rootId: dlg.id.rootId,
186
187
  agentId: dlg.agentId,
@@ -9,9 +9,7 @@ const dialog_instance_registry_1 = require("../../dialog-instance-registry");
9
9
  const log_1 = require("../../log");
10
10
  const persistence_1 = require("../../persistence");
11
11
  const runtime_language_1 = require("../../shared/runtime-language");
12
- const id_1 = require("../../shared/utils/id");
13
12
  const inter_dialog_format_1 = require("../../shared/utils/inter-dialog-format");
14
- const time_1 = require("../../shared/utils/time");
15
13
  const pending_tellask_reminder_1 = require("../../tools/pending-tellask-reminder");
16
14
  const subdialog_txn_1 = require("./subdialog-txn");
17
15
  async function syncPendingTellaskReminderBestEffort(dlg, where) {
@@ -98,20 +96,6 @@ async function supplyResponseToSupdialogV2(args) {
98
96
  if (tellaskHead.trim() === '') {
99
97
  tellaskHead = responseText.slice(0, 100) + (responseText.length > 100 ? '...' : '');
100
98
  }
101
- const completedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
102
- const responseId = (0, id_1.generateShortId)();
103
- await persistence_1.DialogPersistence.appendSubdialogResponse(parentDialog.id, {
104
- responseId,
105
- subdialogId: subdialogId.selfId,
106
- response: responseText,
107
- completedAt,
108
- status,
109
- callType,
110
- tellaskHead,
111
- responderId,
112
- originMemberId,
113
- callId: callId ?? '',
114
- });
115
99
  await persistence_1.DialogPersistence.savePendingSubdialogs(parentDialog.id, filteredPending);
116
100
  const hasQ4H = await parentDialog.hasPendingQ4H();
117
101
  const shouldRevive = !hasQ4H && filteredPending.length === 0;
@@ -134,7 +118,7 @@ async function supplyResponseToSupdialogV2(args) {
134
118
  originMemberId: result.originMemberId ?? parentDialog.agentId,
135
119
  });
136
120
  // Keep in-memory dialog context in sync with live teammate-response events immediately.
137
- // Without this, tellask_result_msg can appear batched at drive-finalization mirror time.
121
+ // v2 context assembly now relies on dialog msgs + persisted teammate_response_record only.
138
122
  const immediateMirror = {
139
123
  type: 'tellask_result_msg',
140
124
  role: 'tool',
@@ -153,7 +137,10 @@ async function supplyResponseToSupdialogV2(args) {
153
137
  if (result.shouldRevive) {
154
138
  log_1.log.info(`All Type ${callType} subdialogs complete, parent ${parentDialog.id.selfId} auto-reviving`);
155
139
  if (parentDialog instanceof dialog_1.RootDialog) {
156
- dialog_global_registry_1.globalDialogRegistry.markNeedsDrive(parentDialog.id.rootId);
140
+ dialog_global_registry_1.globalDialogRegistry.markNeedsDrive(parentDialog.id.rootId, {
141
+ source: 'driver_v2_supply_response',
142
+ reason: `all_pending_subdialogs_resolved:type_${callType}`,
143
+ });
157
144
  }
158
145
  scheduleDrive(parentDialog, {
159
146
  waitInQue: true,
@@ -46,7 +46,6 @@ const inter_dialog_format_1 = require("../../shared/utils/inter-dialog-format");
46
46
  const time_1 = require("../../shared/utils/time");
47
47
  const team_1 = require("../../team");
48
48
  const pending_tellask_reminder_1 = require("../../tools/pending-tellask-reminder");
49
- const id_2 = require("../../utils/id");
50
49
  const subdialog_txn_1 = require("./subdialog-txn");
51
50
  function showErrorToAi(err) {
52
51
  try {
@@ -193,6 +192,33 @@ async function updateSubdialogAssignment(subdialog, assignment) {
193
192
  subdialog.assignmentFromSup = assignment;
194
193
  await persistence_1.DialogPersistence.updateSubdialogAssignment(subdialog.id, assignment);
195
194
  }
195
+ async function lookupLiveRegisteredSubdialog(rootDialog, agentId, tellaskSession) {
196
+ const existing = rootDialog.lookupSubdialog(agentId, tellaskSession);
197
+ if (!existing) {
198
+ return undefined;
199
+ }
200
+ const existingSession = existing.tellaskSession;
201
+ if (!existingSession) {
202
+ throw new Error(`Type B registry invariant violation: lookupSubdialog returned entry without tellaskSession (root=${rootDialog.id.valueOf()} sub=${existing.id.valueOf()})`);
203
+ }
204
+ const latest = await persistence_1.DialogPersistence.loadDialogLatest(existing.id, rootDialog.status);
205
+ const runState = latest?.runState;
206
+ if (!runState || runState.kind !== 'dead') {
207
+ return existing;
208
+ }
209
+ const removed = rootDialog.unregisterSubdialog(existing.agentId, existingSession);
210
+ if (!removed) {
211
+ throw new Error(`Failed to unregister dead registered subdialog: root=${rootDialog.id.valueOf()} sub=${existing.id.valueOf()} session=${existingSession}`);
212
+ }
213
+ await rootDialog.saveSubdialogRegistry();
214
+ log_1.log.info('Pruned dead registered subdialog from Type B registry', undefined, {
215
+ rootId: rootDialog.id.rootId,
216
+ subdialogId: existing.id.selfId,
217
+ agentId: existing.agentId,
218
+ tellaskSession: existingSession,
219
+ });
220
+ return undefined;
221
+ }
196
222
  function extractLastAssistantResponse(messages, defaultMessage) {
197
223
  let responseText = '';
198
224
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -392,13 +418,21 @@ async function executeTellaskCall(dlg, agent, firstMention, tellaskHead, body, c
392
418
  const isQ4H = firstMention === 'human';
393
419
  if (isQ4H) {
394
420
  try {
395
- const questionId = `q4h-${(0, id_2.generateDialogID)()}`;
421
+ const normalizedCallId = callId.trim();
422
+ if (normalizedCallId === '') {
423
+ throw new Error(`Q4H call invariant violation: empty callId (rootId=${dlg.id.rootId} selfId=${dlg.id.selfId})`);
424
+ }
425
+ const questionId = `q4h-${dlg.id.rootId}-${dlg.id.selfId}-c${dlg.currentCourse}-${normalizedCallId}`;
426
+ const normalizedRemainingCallIds = Array.from(new Set((options?.q4hRemainingCallIds ?? [])
427
+ .map((value) => value.trim())
428
+ .filter((value) => value !== '' && value !== normalizedCallId)));
396
429
  const question = {
397
430
  id: questionId,
398
431
  tellaskHead: tellaskHead.trim(),
399
432
  bodyContent: body.trim(),
400
433
  askedAt: (0, time_1.formatUnifiedTimestamp)(new Date()),
401
- callId: callId.trim() === '' ? undefined : callId,
434
+ callId: normalizedCallId,
435
+ remainingCallIds: normalizedRemainingCallIds.length > 0 ? normalizedRemainingCallIds : undefined,
402
436
  callSiteRef: {
403
437
  course: dlg.currentCourse,
404
438
  messageIndex: dlg.msgs.length,
@@ -414,6 +448,7 @@ async function executeTellaskCall(dlg, agent, firstMention, tellaskHead, body, c
414
448
  bodyContent: question.bodyContent,
415
449
  askedAt: question.askedAt,
416
450
  callId: question.callId,
451
+ remainingCallIds: question.remainingCallIds,
417
452
  callSiteRef: question.callSiteRef,
418
453
  rootId: dlg.id.rootId,
419
454
  agentId: dlg.agentId,
@@ -662,7 +697,7 @@ async function executeTellaskCall(dlg, agent, firstMention, tellaskHead, body, c
662
697
  callId,
663
698
  collectiveTargets: options?.collectiveTargets ?? [parseResult.agentId],
664
699
  };
665
- const existing = rootDialog.lookupSubdialog(parseResult.agentId, derivedSession);
700
+ const existing = await lookupLiveRegisteredSubdialog(rootDialog, parseResult.agentId, derivedSession);
666
701
  if (existing) {
667
702
  try {
668
703
  await updateSubdialogAssignment(existing, assignment);
@@ -902,7 +937,7 @@ async function executeTellaskCall(dlg, agent, firstMention, tellaskHead, body, c
902
937
  };
903
938
  const pendingOwner = callerDialog;
904
939
  const result = await (0, subdialog_txn_1.withSubdialogTxnLock)(rootDialog.id, async () => {
905
- const existing = rootDialog.lookupSubdialog(parseResult.agentId, parseResult.tellaskSession);
940
+ const existing = await lookupLiveRegisteredSubdialog(rootDialog, parseResult.agentId, parseResult.tellaskSession);
906
941
  if (existing) {
907
942
  try {
908
943
  await updateSubdialogAssignment(existing, assignment);
@@ -1049,7 +1084,61 @@ async function executeTellaskCalls(args) {
1049
1084
  const callbacks = ensureCallbacks(args.callbacks);
1050
1085
  const malformedToolOutputs = await emitMalformedTellaskResponses(dlg, collectedCalls);
1051
1086
  const validCalls = collectedCalls.filter((call) => call.validation.kind === 'valid');
1052
- const results = await Promise.all(validCalls.map((call) => executeTellaskCall(dlg, agent, call.validation.firstMention, call.tellaskHead, call.body, call.callId, callbacks)));
1087
+ const q4hCalls = validCalls.filter((call) => call.validation.firstMention === 'human');
1088
+ const nonQ4HCalls = validCalls
1089
+ .filter((call) => call.validation.firstMention !== 'human')
1090
+ .map((call) => ({ ...call }));
1091
+ let mergedQ4HCall = null;
1092
+ if (q4hCalls.length === 1) {
1093
+ mergedQ4HCall = { ...q4hCalls[0] };
1094
+ }
1095
+ else if (q4hCalls.length > 1) {
1096
+ const primary = q4hCalls[0];
1097
+ const remainingCallIds = q4hCalls
1098
+ .slice(1)
1099
+ .map((call) => call.callId.trim())
1100
+ .filter((callId) => callId !== '');
1101
+ const language = (0, runtime_language_1.getWorkLanguage)();
1102
+ const intro = language === 'zh'
1103
+ ? `我这次有 ${q4hCalls.length} 个问题,想请你一次性回复:`
1104
+ : `I have ${q4hCalls.length} questions this round. Please answer them in one response:`;
1105
+ const mergedBody = [
1106
+ intro,
1107
+ ...q4hCalls.map((call, index) => {
1108
+ const body = call.body.trim();
1109
+ const normalizedBody = body !== ''
1110
+ ? body
1111
+ : language === 'zh'
1112
+ ? '请结合当前上下文补充这一项。'
1113
+ : 'Please provide this item based on the current context.';
1114
+ return language === 'zh'
1115
+ ? `问题 ${index + 1}:\n${normalizedBody}`
1116
+ : `Question ${index + 1}:\n${normalizedBody}`;
1117
+ }),
1118
+ ].join('\n\n');
1119
+ mergedQ4HCall = {
1120
+ ...primary,
1121
+ body: mergedBody,
1122
+ q4hRemainingCallIds: remainingCallIds.length > 0 ? remainingCallIds : undefined,
1123
+ };
1124
+ log_1.log.info('Q4H multi-question normalized into a single prompt', {
1125
+ rootId: dlg.id.rootId,
1126
+ selfId: dlg.id.selfId,
1127
+ mergedCount: q4hCalls.length,
1128
+ primaryCallId: primary.callId,
1129
+ remainingCallIds,
1130
+ });
1131
+ }
1132
+ const executionCalls = mergedQ4HCall
1133
+ ? [...nonQ4HCalls, mergedQ4HCall]
1134
+ : nonQ4HCalls;
1135
+ const results = [];
1136
+ for (const call of executionCalls) {
1137
+ const result = await executeTellaskCall(dlg, agent, call.validation.firstMention, call.tellaskHead, call.body, call.callId, callbacks, {
1138
+ q4hRemainingCallIds: call.q4hRemainingCallIds,
1139
+ });
1140
+ results.push(result);
1141
+ }
1053
1142
  const suspend = results.some((result) => result.suspend);
1054
1143
  const toolOutputs = [...malformedToolOutputs, ...results.flatMap((result) => result.toolOutputs)];
1055
1144
  const subdialogsCreated = results.flatMap((result) => result.subdialogsCreated);