dominds 0.7.2 → 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 (105) hide show
  1. package/dist/dialog-global-registry.js +58 -3
  2. package/dist/dialog.js +17 -0
  3. package/dist/evt-registry.js +16 -7
  4. package/dist/llm/driver-v2/context.js +2 -1
  5. package/dist/llm/driver-v2/core.js +1 -118
  6. package/dist/llm/driver-v2/orchestrator.js +65 -38
  7. package/dist/llm/driver-v2/round.js +117 -31
  8. package/dist/llm/driver-v2/runtime-utils.js +1 -0
  9. package/dist/llm/driver-v2/supdialog-response.js +5 -18
  10. package/dist/llm/driver-v2/tellask-bridge.js +66 -4
  11. package/dist/persistence.js +8 -0
  12. package/dist/server/websocket-handler.js +43 -26
  13. package/dist/static/assets/{_baseUniq-C04fddeD.js → _baseUniq-zV3pyJ-s.js} +2 -2
  14. package/dist/static/assets/{_baseUniq-C04fddeD.js.map → _baseUniq-zV3pyJ-s.js.map} +1 -1
  15. package/dist/static/assets/{arc-qmZerooe.js → arc-Ctgmrh71.js} +2 -2
  16. package/dist/static/assets/{arc-qmZerooe.js.map → arc-Ctgmrh71.js.map} +1 -1
  17. package/dist/static/assets/{architectureDiagram-VXUJARFQ-DPAwJ19g.js → architectureDiagram-VXUJARFQ-CAH1Zinn.js} +6 -6
  18. package/dist/static/assets/{architectureDiagram-VXUJARFQ-DPAwJ19g.js.map → architectureDiagram-VXUJARFQ-CAH1Zinn.js.map} +1 -1
  19. package/dist/static/assets/{blockDiagram-VD42YOAC-M0jbEJ-1.js → blockDiagram-VD42YOAC-Djz6JdD2.js} +7 -7
  20. package/dist/static/assets/{blockDiagram-VD42YOAC-M0jbEJ-1.js.map → blockDiagram-VD42YOAC-Djz6JdD2.js.map} +1 -1
  21. package/dist/static/assets/{c4Diagram-YG6GDRKO-DlF8gwFw.js → c4Diagram-YG6GDRKO-CSuskOyq.js} +3 -3
  22. package/dist/static/assets/{c4Diagram-YG6GDRKO-DlF8gwFw.js.map → c4Diagram-YG6GDRKO-CSuskOyq.js.map} +1 -1
  23. package/dist/static/assets/{channel-B2Ph7be3.js → channel-Ym6U59Kh.js} +2 -2
  24. package/dist/static/assets/{channel-B2Ph7be3.js.map → channel-Ym6U59Kh.js.map} +1 -1
  25. package/dist/static/assets/{chunk-4BX2VUAB-BZ2L6PK1.js → chunk-4BX2VUAB-CqbVylSz.js} +2 -2
  26. package/dist/static/assets/{chunk-4BX2VUAB-BZ2L6PK1.js.map → chunk-4BX2VUAB-CqbVylSz.js.map} +1 -1
  27. package/dist/static/assets/{chunk-55IACEB6-COXFmnJW.js → chunk-55IACEB6-CeWj8Jm_.js} +2 -2
  28. package/dist/static/assets/{chunk-55IACEB6-COXFmnJW.js.map → chunk-55IACEB6-CeWj8Jm_.js.map} +1 -1
  29. package/dist/static/assets/{chunk-B4BG7PRW-B1HiWlBq.js → chunk-B4BG7PRW-BjxJkuhq.js} +5 -5
  30. package/dist/static/assets/{chunk-B4BG7PRW-B1HiWlBq.js.map → chunk-B4BG7PRW-BjxJkuhq.js.map} +1 -1
  31. package/dist/static/assets/{chunk-DI55MBZ5-BO3wQRnL.js → chunk-DI55MBZ5-9kxfXB4B.js} +4 -4
  32. package/dist/static/assets/{chunk-DI55MBZ5-BO3wQRnL.js.map → chunk-DI55MBZ5-9kxfXB4B.js.map} +1 -1
  33. package/dist/static/assets/{chunk-FMBD7UC4-BdavRHSy.js → chunk-FMBD7UC4-DyM3IVWB.js} +2 -2
  34. package/dist/static/assets/{chunk-FMBD7UC4-BdavRHSy.js.map → chunk-FMBD7UC4-DyM3IVWB.js.map} +1 -1
  35. package/dist/static/assets/{chunk-QN33PNHL-DchoUNF7.js → chunk-QN33PNHL-DGU21aEB.js} +2 -2
  36. package/dist/static/assets/{chunk-QN33PNHL-DchoUNF7.js.map → chunk-QN33PNHL-DGU21aEB.js.map} +1 -1
  37. package/dist/static/assets/{chunk-QZHKN3VN-Dr3uVTtK.js → chunk-QZHKN3VN-Cw9Hpt-y.js} +2 -2
  38. package/dist/static/assets/{chunk-QZHKN3VN-Dr3uVTtK.js.map → chunk-QZHKN3VN-Cw9Hpt-y.js.map} +1 -1
  39. package/dist/static/assets/{chunk-TZMSLE5B-BhOcHZNs.js → chunk-TZMSLE5B-J-eTCnFj.js} +2 -2
  40. package/dist/static/assets/{chunk-TZMSLE5B-BhOcHZNs.js.map → chunk-TZMSLE5B-J-eTCnFj.js.map} +1 -1
  41. package/dist/static/assets/{classDiagram-2ON5EDUG-Dep-MR6W.js → classDiagram-2ON5EDUG-BvoDAd8E.js} +6 -6
  42. package/dist/static/assets/{classDiagram-2ON5EDUG-Dep-MR6W.js.map → classDiagram-2ON5EDUG-BvoDAd8E.js.map} +1 -1
  43. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-Dep-MR6W.js → classDiagram-v2-WZHVMYZB-BvoDAd8E.js} +6 -6
  44. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-Dep-MR6W.js.map → classDiagram-v2-WZHVMYZB-BvoDAd8E.js.map} +1 -1
  45. package/dist/static/assets/{clone-DzoL-dSX.js → clone-CojsCkky.js} +2 -2
  46. package/dist/static/assets/{clone-DzoL-dSX.js.map → clone-CojsCkky.js.map} +1 -1
  47. package/dist/static/assets/{cose-bilkent-S5V4N54A-Bqu_DqDB.js → cose-bilkent-S5V4N54A-BEtK3OcG.js} +2 -2
  48. package/dist/static/assets/{cose-bilkent-S5V4N54A-Bqu_DqDB.js.map → cose-bilkent-S5V4N54A-BEtK3OcG.js.map} +1 -1
  49. package/dist/static/assets/{dagre-6UL2VRFP-49InNTzs.js → dagre-6UL2VRFP-C5DMaVj0.js} +7 -7
  50. package/dist/static/assets/{dagre-6UL2VRFP-49InNTzs.js.map → dagre-6UL2VRFP-C5DMaVj0.js.map} +1 -1
  51. package/dist/static/assets/{diagram-PSM6KHXK-C1IsQq8m.js → diagram-PSM6KHXK-kVP0dZ6K.js} +7 -7
  52. package/dist/static/assets/{diagram-PSM6KHXK-C1IsQq8m.js.map → diagram-PSM6KHXK-kVP0dZ6K.js.map} +1 -1
  53. package/dist/static/assets/{diagram-QEK2KX5R-DyJeSg_Y.js → diagram-QEK2KX5R-C4p2yibe.js} +6 -6
  54. package/dist/static/assets/{diagram-QEK2KX5R-DyJeSg_Y.js.map → diagram-QEK2KX5R-C4p2yibe.js.map} +1 -1
  55. package/dist/static/assets/{diagram-S2PKOQOG-aeLjjldp.js → diagram-S2PKOQOG-B3vObsYY.js} +6 -6
  56. package/dist/static/assets/{diagram-S2PKOQOG-aeLjjldp.js.map → diagram-S2PKOQOG-B3vObsYY.js.map} +1 -1
  57. package/dist/static/assets/{erDiagram-Q2GNP2WA-BUcv4AUq.js → erDiagram-Q2GNP2WA-AKQItpVu.js} +5 -5
  58. package/dist/static/assets/{erDiagram-Q2GNP2WA-BUcv4AUq.js.map → erDiagram-Q2GNP2WA-AKQItpVu.js.map} +1 -1
  59. package/dist/static/assets/{flowDiagram-NV44I4VS-DfmTKlf_.js → flowDiagram-NV44I4VS-2dalJoL3.js} +6 -6
  60. package/dist/static/assets/{flowDiagram-NV44I4VS-DfmTKlf_.js.map → flowDiagram-NV44I4VS-2dalJoL3.js.map} +1 -1
  61. package/dist/static/assets/{ganttDiagram-JELNMOA3-BpJE8Lhp.js → ganttDiagram-JELNMOA3-DUw98Lbz.js} +3 -3
  62. package/dist/static/assets/{ganttDiagram-JELNMOA3-BpJE8Lhp.js.map → ganttDiagram-JELNMOA3-DUw98Lbz.js.map} +1 -1
  63. package/dist/static/assets/{gitGraphDiagram-NY62KEGX-DmznjSYb.js → gitGraphDiagram-NY62KEGX-DdwIFk8M.js} +7 -7
  64. package/dist/static/assets/{gitGraphDiagram-NY62KEGX-DmznjSYb.js.map → gitGraphDiagram-NY62KEGX-DdwIFk8M.js.map} +1 -1
  65. package/dist/static/assets/{graph-CcKyIZaL.js → graph-BBEAP9Z9.js} +3 -3
  66. package/dist/static/assets/{graph-CcKyIZaL.js.map → graph-BBEAP9Z9.js.map} +1 -1
  67. package/dist/static/assets/{index-CQr8MdPI.js → index-CqMOe9zt.js} +149 -101
  68. package/dist/static/assets/index-CqMOe9zt.js.map +1 -0
  69. package/dist/static/assets/{infoDiagram-WHAUD3N6-DwRPjndM.js → infoDiagram-WHAUD3N6-Cep11ST9.js} +5 -5
  70. package/dist/static/assets/{infoDiagram-WHAUD3N6-DwRPjndM.js.map → infoDiagram-WHAUD3N6-Cep11ST9.js.map} +1 -1
  71. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-BRAFSgVa.js → journeyDiagram-XKPGCS4Q-Ba8wkBNB.js} +5 -5
  72. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-BRAFSgVa.js.map → journeyDiagram-XKPGCS4Q-Ba8wkBNB.js.map} +1 -1
  73. package/dist/static/assets/{kanban-definition-3W4ZIXB7-DlJ7-fgN.js → kanban-definition-3W4ZIXB7-DhxNBORX.js} +3 -3
  74. package/dist/static/assets/{kanban-definition-3W4ZIXB7-DlJ7-fgN.js.map → kanban-definition-3W4ZIXB7-DhxNBORX.js.map} +1 -1
  75. package/dist/static/assets/{layout-CcW5-Iee.js → layout-Ckbr6Zyz.js} +5 -5
  76. package/dist/static/assets/{layout-CcW5-Iee.js.map → layout-Ckbr6Zyz.js.map} +1 -1
  77. package/dist/static/assets/{linear-sKdPIuOH.js → linear-BrSsI51I.js} +2 -2
  78. package/dist/static/assets/{linear-sKdPIuOH.js.map → linear-BrSsI51I.js.map} +1 -1
  79. package/dist/static/assets/{min-CQ0AYqmk.js → min-CeEvLc8D.js} +3 -3
  80. package/dist/static/assets/{min-CQ0AYqmk.js.map → min-CeEvLc8D.js.map} +1 -1
  81. package/dist/static/assets/{mindmap-definition-VGOIOE7T-CTRwSlu8.js → mindmap-definition-VGOIOE7T-7ctLd3o3.js} +4 -4
  82. package/dist/static/assets/{mindmap-definition-VGOIOE7T-CTRwSlu8.js.map → mindmap-definition-VGOIOE7T-7ctLd3o3.js.map} +1 -1
  83. package/dist/static/assets/{pieDiagram-ADFJNKIX-DmD5KRKE.js → pieDiagram-ADFJNKIX-CqyILziW.js} +7 -7
  84. package/dist/static/assets/{pieDiagram-ADFJNKIX-DmD5KRKE.js.map → pieDiagram-ADFJNKIX-CqyILziW.js.map} +1 -1
  85. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-C5sqZOBF.js → quadrantDiagram-AYHSOK5B-Byi_xYum.js} +3 -3
  86. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-C5sqZOBF.js.map → quadrantDiagram-AYHSOK5B-Byi_xYum.js.map} +1 -1
  87. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DBAUwM-7.js → requirementDiagram-UZGBJVZJ-Boj5vxiZ.js} +4 -4
  88. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DBAUwM-7.js.map → requirementDiagram-UZGBJVZJ-Boj5vxiZ.js.map} +1 -1
  89. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-CkaxkOo7.js → sankeyDiagram-TZEHDZUN-DzoUAdfa.js} +2 -2
  90. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-CkaxkOo7.js.map → sankeyDiagram-TZEHDZUN-DzoUAdfa.js.map} +1 -1
  91. package/dist/static/assets/{sequenceDiagram-WL72ISMW-CJ6wMDXh.js → sequenceDiagram-WL72ISMW-BKKxpG1m.js} +4 -4
  92. package/dist/static/assets/{sequenceDiagram-WL72ISMW-CJ6wMDXh.js.map → sequenceDiagram-WL72ISMW-BKKxpG1m.js.map} +1 -1
  93. package/dist/static/assets/{stateDiagram-FKZM4ZOC-C66ZP2NW.js → stateDiagram-FKZM4ZOC-B-J7uRZT.js} +9 -9
  94. package/dist/static/assets/{stateDiagram-FKZM4ZOC-C66ZP2NW.js.map → stateDiagram-FKZM4ZOC-B-J7uRZT.js.map} +1 -1
  95. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DdoOy271.js → stateDiagram-v2-4FDKWEC3-BOzmoUba.js} +5 -5
  96. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DdoOy271.js.map → stateDiagram-v2-4FDKWEC3-BOzmoUba.js.map} +1 -1
  97. package/dist/static/assets/{timeline-definition-IT6M3QCI-2A2Mqedz.js → timeline-definition-IT6M3QCI-zu1Rojx9.js} +3 -3
  98. package/dist/static/assets/{timeline-definition-IT6M3QCI-2A2Mqedz.js.map → timeline-definition-IT6M3QCI-zu1Rojx9.js.map} +1 -1
  99. package/dist/static/assets/{treemap-KMMF4GRG-CTbGSr5o.js → treemap-KMMF4GRG-C4D91awC.js} +4 -4
  100. package/dist/static/assets/{treemap-KMMF4GRG-CTbGSr5o.js.map → treemap-KMMF4GRG-C4D91awC.js.map} +1 -1
  101. package/dist/static/assets/{xychartDiagram-PRI3JC2R-C_S3XXZI.js → xychartDiagram-PRI3JC2R-YvLKaPZ-.js} +3 -3
  102. package/dist/static/assets/{xychartDiagram-PRI3JC2R-C_S3XXZI.js.map → xychartDiagram-PRI3JC2R-YvLKaPZ-.js.map} +1 -1
  103. package/dist/static/index.html +1 -1
  104. package/package.json +1 -1
  105. package/dist/static/assets/index-CQr8MdPI.js.map +0 -1
@@ -2,9 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.globalDialogRegistry = void 0;
4
4
  const persistence_1 = require("./persistence");
5
+ const evt_1 = require("./shared/evt");
5
6
  class GlobalDialogRegistry {
6
7
  constructor() {
7
8
  this.entries = new Map();
9
+ this.driveTriggerPubChan = (0, evt_1.createPubChan)();
10
+ this.driveTriggerSubChan = (0, evt_1.createSubChan)(this.driveTriggerPubChan);
8
11
  }
9
12
  static getInstance() {
10
13
  if (!GlobalDialogRegistry.instance) {
@@ -30,7 +33,10 @@ class GlobalDialogRegistry {
30
33
  try {
31
34
  const needsDrive = await persistence_1.DialogPersistence.getNeedsDrive(rootDialog.id);
32
35
  if (needsDrive) {
33
- this.markNeedsDrive(rootDialog.id.rootId);
36
+ this.markNeedsDrive(rootDialog.id.rootId, {
37
+ source: 'dialog_registry_hydration',
38
+ reason: 'persisted_needs_drive_true',
39
+ });
34
40
  }
35
41
  }
36
42
  catch {
@@ -41,17 +47,66 @@ class GlobalDialogRegistry {
41
47
  unregister(rootId) {
42
48
  this.entries.delete(rootId);
43
49
  }
44
- markNeedsDrive(rootId) {
50
+ publishDriveTrigger(args) {
51
+ this.driveTriggerPubChan.write({
52
+ type: 'drive_trigger_evt',
53
+ action: args.action,
54
+ rootId: args.rootId,
55
+ entryFound: args.entryFound,
56
+ previousNeedsDrive: args.previousNeedsDrive,
57
+ nextNeedsDrive: args.nextNeedsDrive,
58
+ source: args.meta.source,
59
+ reason: args.meta.reason,
60
+ emittedAtMs: Date.now(),
61
+ });
62
+ }
63
+ async waitForDriveTrigger() {
64
+ for (;;) {
65
+ const trigger = await this.driveTriggerSubChan.read();
66
+ if (trigger !== evt_1.EndOfStream) {
67
+ return trigger;
68
+ }
69
+ // Recreate subscription if EOS is ever observed (should not happen in normal runtime).
70
+ this.driveTriggerSubChan = (0, evt_1.createSubChan)(this.driveTriggerPubChan);
71
+ }
72
+ }
73
+ markNeedsDrive(rootId, meta) {
74
+ const triggerMeta = meta ?? {
75
+ source: 'unknown',
76
+ reason: 'unspecified',
77
+ };
45
78
  const entry = this.entries.get(rootId);
79
+ const previousNeedsDrive = entry ? entry.needsDrive : null;
46
80
  if (entry) {
47
81
  entry.needsDrive = true;
48
82
  }
83
+ this.publishDriveTrigger({
84
+ action: 'mark_needs_drive',
85
+ rootId,
86
+ entryFound: entry !== undefined,
87
+ previousNeedsDrive,
88
+ nextNeedsDrive: true,
89
+ meta: triggerMeta,
90
+ });
49
91
  }
50
- markNotNeedingDrive(rootId) {
92
+ markNotNeedingDrive(rootId, meta) {
93
+ const triggerMeta = meta ?? {
94
+ source: 'unknown',
95
+ reason: 'unspecified',
96
+ };
51
97
  const entry = this.entries.get(rootId);
98
+ const previousNeedsDrive = entry ? entry.needsDrive : null;
52
99
  if (entry) {
53
100
  entry.needsDrive = false;
54
101
  }
102
+ this.publishDriveTrigger({
103
+ action: 'mark_not_needing_drive',
104
+ rootId,
105
+ entryFound: entry !== undefined,
106
+ previousNeedsDrive,
107
+ nextNeedsDrive: false,
108
+ meta: triggerMeta,
109
+ });
55
110
  }
56
111
  getDialogsNeedingDrive() {
57
112
  return Array.from(this.entries.values())
package/dist/dialog.js CHANGED
@@ -502,9 +502,26 @@ class Dialog {
502
502
  this._upNext = {
503
503
  prompt: trimmed,
504
504
  msgId: (0, id_1.generateShortId)(),
505
+ grammar: 'markdown',
505
506
  userLanguageCode: this._lastUserLanguageCode,
506
507
  };
507
508
  }
509
+ queueUpNextPrompt(options) {
510
+ if (this._upNext !== undefined) {
511
+ throw new Error(`UpNext prompt overwrite violation: dialog=${this.id.valueOf()} existingMsgId=${this._upNext.msgId} incomingMsgId=${options.msgId}`);
512
+ }
513
+ const trimmed = options.prompt.trim();
514
+ if (!trimmed) {
515
+ throw new Error('Prompt is required to queue upNext');
516
+ }
517
+ this._upNext = {
518
+ prompt: trimmed,
519
+ msgId: options.msgId,
520
+ grammar: options.grammar,
521
+ userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
522
+ };
523
+ this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
524
+ }
508
525
  hasUpNext() {
509
526
  return this._upNext !== undefined;
510
527
  }
@@ -25,13 +25,18 @@ class DialogEventRegistryImpl {
25
25
  setQ4HBroadcaster(fn) {
26
26
  this.q4hBroadcaster = fn;
27
27
  }
28
- broadcastIfQ4H(evt) {
29
- // Only broadcast Q4H events; all other dialog events remain dialog-scoped streams.
28
+ dispatchQ4HGloballyIfNeeded(evt) {
29
+ // Q4H MUST be global-only: emit to all clients via broadcaster and do not
30
+ // also write into dialog-scoped stream, otherwise subscribed clients will
31
+ // receive duplicate deliveries via two independent paths.
30
32
  if (evt.type !== 'new_q4h_asked' && evt.type !== 'q4h_answered')
31
- return;
33
+ return false;
32
34
  const fn = this.q4hBroadcaster;
33
- if (fn)
34
- fn(evt);
35
+ if (!fn) {
36
+ throw new Error(`Q4H broadcaster missing: cannot publish ${evt.type} for dialog=${evt.dialog.selfId}`);
37
+ }
38
+ fn(evt);
39
+ return true;
35
40
  }
36
41
  /**
37
42
  * Get or create a PubChan for a specific dialog ID
@@ -75,13 +80,17 @@ class DialogEventRegistryImpl {
75
80
  */
76
81
  postEvent(dlg, event) {
77
82
  const typedEvent = this.createTypedEvent(dlg.id, event);
78
- this.broadcastIfQ4H(typedEvent);
83
+ if (this.dispatchQ4HGloballyIfNeeded(typedEvent)) {
84
+ return;
85
+ }
79
86
  const chan = this.getPubChan(dlg.id);
80
87
  chan.write(typedEvent);
81
88
  }
82
89
  postEventById(dialogId, event) {
83
90
  const typedEvent = this.createTypedEvent(dialogId, event);
84
- this.broadcastIfQ4H(typedEvent);
91
+ if (this.dispatchQ4HGloballyIfNeeded(typedEvent)) {
92
+ return;
93
+ }
85
94
  const chan = this.getPubChan(dialogId);
86
95
  chan.write(typedEvent);
87
96
  }
@@ -39,7 +39,8 @@ function buildDriveBaseContextMessages(parts) {
39
39
  }
40
40
  function appendDriveEphemeralContext(base, parts) {
41
41
  const next = [...base];
42
- if (parts.subdialogResponseContextMsgs.length > 0) {
42
+ if (Array.isArray(parts.subdialogResponseContextMsgs) &&
43
+ parts.subdialogResponseContextMsgs.length > 0) {
43
44
  next.push(...parts.subdialogResponseContextMsgs);
44
45
  }
45
46
  if (parts.internalDrivePromptMsg) {
@@ -10,7 +10,6 @@ const persistence_1 = require("../../persistence");
10
10
  const diligence_1 = require("../../shared/diligence");
11
11
  const driver_messages_1 = require("../../shared/i18n/driver-messages");
12
12
  const runtime_language_1 = require("../../shared/runtime-language");
13
- const inter_dialog_format_1 = require("../../shared/utils/inter-dialog-format");
14
13
  const tellask_1 = require("../../tellask");
15
14
  const taskdoc_1 = require("../../utils/taskdoc");
16
15
  const client_1 = require("../client");
@@ -20,7 +19,6 @@ const context_1 = require("./context");
20
19
  const policy_1 = require("./policy");
21
20
  const runtime_utils_1 = require("./runtime-utils");
22
21
  const saying_events_1 = require("./saying-events");
23
- const subdialog_txn_1 = require("./subdialog-txn");
24
22
  const tellask_bridge_1 = require("./tellask-bridge");
25
23
  class DialogInterruptedError extends Error {
26
24
  constructor(reason) {
@@ -56,7 +54,7 @@ function resolveUpNextPrompt(dlg) {
56
54
  return {
57
55
  content: upNext.prompt,
58
56
  msgId: upNext.msgId,
59
- grammar: 'markdown',
57
+ grammar: upNext.grammar ?? 'markdown',
60
58
  userLanguageCode: upNext.userLanguageCode,
61
59
  };
62
60
  }
@@ -210,21 +208,6 @@ async function buildProviderContext(args) {
210
208
  }
211
209
  return { provider, model, providerCfg };
212
210
  }
213
- async function hasQueuedSubdialogResponses(dialog) {
214
- try {
215
- const queued = await (0, subdialog_txn_1.withSubdialogTxnLock)(dialog.id, async () => {
216
- return await persistence_1.DialogPersistence.loadSubdialogResponsesQueue(dialog.id);
217
- });
218
- return queued.length > 0;
219
- }
220
- catch (err) {
221
- log_1.log.warn('driver-v2 failed to check queued subdialog responses; suppressing diligence as safe default', {
222
- dialogId: dialog.id.valueOf(),
223
- error: err,
224
- });
225
- return true;
226
- }
227
- }
228
211
  async function executeFunctionCalls(args) {
229
212
  const { dialog, agent, agentTools, funcCalls, abortSignal } = args;
230
213
  const functionPromises = funcCalls.map(async (func) => {
@@ -357,13 +340,6 @@ async function maybeContinueWithDiligencePrompt(args) {
357
340
  }
358
341
  return { kind: 'break' };
359
342
  }
360
- const queuedResponses = await hasQueuedSubdialogResponses(dlg);
361
- if (queuedResponses) {
362
- log_1.log.info('driver-v2 skip diligence prompt while subdialog responses remain queued', {
363
- dialogId: dlg.id.valueOf(),
364
- });
365
- return { kind: 'break' };
366
- }
367
343
  const prepared = await (0, runtime_utils_1.maybePrepareDiligenceAutoContinuePrompt)({
368
344
  dlg,
369
345
  isRootDialog: true,
@@ -415,12 +391,7 @@ async function driveDialogStreamCoreV2(dlg, humanPrompt, driveOptions, callbacks
415
391
  await (0, dialog_run_state_1.setDialogRunState)(dlg.id, { kind: 'proceeding' });
416
392
  let pubRemindersVer = dlg.remindersVer;
417
393
  let lastAssistantSayingContent = null;
418
- let generationHadError = false;
419
- let tookSubdialogResponses = false;
420
- let takenSubdialogResponses = [];
421
- let subdialogResponseContextMsgs = [];
422
394
  let internalDrivePromptMsg;
423
- let committedTakenSubdialogResponses = false;
424
395
  let genIterNo = 0;
425
396
  let pendingPrompt = humanPrompt;
426
397
  let skipTaskdocForThisDrive = humanPrompt?.skipTaskdoc === true;
@@ -548,20 +519,6 @@ async function driveDialogStreamCoreV2(dlg, humanPrompt, driveOptions, callbacks
548
519
  await resetDiligenceBudgetAfterQ4H(dlg, team);
549
520
  break;
550
521
  }
551
- if (genIterNo === 1 && !tookSubdialogResponses) {
552
- tookSubdialogResponses = true;
553
- try {
554
- takenSubdialogResponses = await (0, subdialog_txn_1.takeSubdialogResponses)(dlg.id);
555
- }
556
- catch (err) {
557
- log_1.log.warn('driver-v2 failed to take subdialog responses for injection', {
558
- dialogId: dlg.id.selfId,
559
- error: err,
560
- });
561
- generationHadError = true;
562
- takenSubdialogResponses = [];
563
- }
564
- }
565
522
  const taskDocMsg = dlg.taskDocPath && !skipTaskdocForThisDrive ? await (0, taskdoc_1.formatTaskDocContent)(dlg) : undefined;
566
523
  const coursePrefixMsgs = (() => {
567
524
  const msgs = dlg.getCoursePrefixMsgs();
@@ -574,19 +531,6 @@ async function driveDialogStreamCoreV2(dlg, humanPrompt, driveOptions, callbacks
574
531
  return false;
575
532
  return true;
576
533
  });
577
- if (genIterNo === 1 && takenSubdialogResponses.length > 0) {
578
- subdialogResponseContextMsgs = takenSubdialogResponses.map((response) => ({
579
- type: 'environment_msg',
580
- role: 'user',
581
- content: (0, inter_dialog_format_1.formatTeammateResponseContent)({
582
- responderId: response.responderId,
583
- requesterId: response.originMemberId,
584
- originalCallHeadLine: response.tellaskHead,
585
- responseBody: response.response,
586
- language: (0, runtime_language_1.getWorkLanguage)(),
587
- }),
588
- }));
589
- }
590
534
  await dlg.processReminderUpdates();
591
535
  const renderedReminders = dlg.reminders.length > 0
592
536
  ? await Promise.all(dlg.reminders.map(async (reminder, index) => {
@@ -618,7 +562,6 @@ async function driveDialogStreamCoreV2(dlg, humanPrompt, driveOptions, callbacks
618
562
  dialogMsgsForContext,
619
563
  },
620
564
  ephemeral: {
621
- subdialogResponseContextMsgs,
622
565
  internalDrivePromptMsg,
623
566
  },
624
567
  tail: {
@@ -1005,12 +948,10 @@ async function driveDialogStreamCoreV2(dlg, humanPrompt, driveOptions, callbacks
1005
948
  : { kind: 'system_stop', detail: 'Aborted.' }
1006
949
  : undefined;
1007
950
  if (interruptedReason) {
1008
- generationHadError = true;
1009
951
  finalRunState = { kind: 'interrupted', reason: interruptedReason };
1010
952
  (0, dialog_run_state_1.broadcastRunStateMarker)(dlg.id, { kind: 'interrupted', reason: interruptedReason });
1011
953
  return { lastAssistantSayingContent, interrupted: true };
1012
954
  }
1013
- generationHadError = true;
1014
955
  const errText = (0, log_1.extractErrorDetails)(err).message;
1015
956
  try {
1016
957
  await dlg.streamError(errText);
@@ -1053,63 +994,5 @@ async function driveDialogStreamCoreV2(dlg, humanPrompt, driveOptions, callbacks
1053
994
  });
1054
995
  }
1055
996
  await (0, dialog_run_state_1.setDialogRunState)(dlg.id, finalRunState);
1056
- if (tookSubdialogResponses) {
1057
- try {
1058
- if (generationHadError) {
1059
- await (0, subdialog_txn_1.rollbackTakenSubdialogResponses)(dlg.id);
1060
- }
1061
- else {
1062
- await (0, subdialog_txn_1.commitTakenSubdialogResponses)(dlg.id);
1063
- committedTakenSubdialogResponses = true;
1064
- }
1065
- }
1066
- catch (err2) {
1067
- log_1.log.warn('driver-v2 failed to finalize subdialog response queue after drive', {
1068
- dialogId: dlg.id.selfId,
1069
- error: err2,
1070
- });
1071
- }
1072
- }
1073
- if (committedTakenSubdialogResponses && takenSubdialogResponses.length > 0) {
1074
- try {
1075
- const mirroredMsgs = [];
1076
- for (const response of takenSubdialogResponses) {
1077
- const content = (0, inter_dialog_format_1.formatTeammateResponseContent)({
1078
- responderId: response.responderId,
1079
- requesterId: response.originMemberId,
1080
- originalCallHeadLine: response.tellaskHead,
1081
- responseBody: response.response,
1082
- language: (0, runtime_language_1.getWorkLanguage)(),
1083
- });
1084
- const status = response.status ?? 'completed';
1085
- const alreadyMirrored = dlg.msgs.some((msg) => msg.type === 'tellask_result_msg' &&
1086
- msg.role === 'tool' &&
1087
- msg.responderId === response.responderId &&
1088
- msg.tellaskHead === response.tellaskHead &&
1089
- msg.status === status &&
1090
- msg.content === content);
1091
- if (alreadyMirrored) {
1092
- continue;
1093
- }
1094
- mirroredMsgs.push({
1095
- type: 'tellask_result_msg',
1096
- role: 'tool',
1097
- responderId: response.responderId,
1098
- tellaskHead: response.tellaskHead,
1099
- status,
1100
- content,
1101
- });
1102
- }
1103
- if (mirroredMsgs.length > 0) {
1104
- await dlg.addChatMessages(...mirroredMsgs);
1105
- }
1106
- }
1107
- catch (err) {
1108
- log_1.log.warn('driver-v2 failed to mirror committed subdialog responses into dialog msgs', {
1109
- dialogId: dlg.id.selfId,
1110
- error: err,
1111
- });
1112
- }
1113
- }
1114
997
  }
1115
998
  }
@@ -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);
@@ -14,6 +14,36 @@ const core_1 = require("./core");
14
14
  const policy_1 = require("./policy");
15
15
  const supdialog_response_1 = require("./supdialog-response");
16
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
+ }
17
47
  const contextHealthRoundStateByDialogKey = new Map();
18
48
  function getContextHealthRoundState(dialog) {
19
49
  const key = dialog.id.key();
@@ -76,7 +106,7 @@ function resolveEffectivePrompt(dialog, humanPrompt) {
76
106
  return {
77
107
  content: upNext.prompt,
78
108
  msgId: upNext.msgId,
79
- grammar: 'markdown',
109
+ grammar: upNext.grammar ?? 'markdown',
80
110
  userLanguageCode: upNext.userLanguageCode === 'zh' || upNext.userLanguageCode === 'en'
81
111
  ? upNext.userLanguageCode
82
112
  : undefined,
@@ -131,6 +161,24 @@ async function executeDriveRound(args) {
131
161
  dialogId: dialog.id.valueOf(),
132
162
  });
133
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
+ }
134
182
  const minds = await (0, load_1.loadAgentMinds)(dialog.agentId, dialog);
135
183
  const policy = (0, policy_1.buildDriverV2Policy)({
136
184
  dlg: dialog,
@@ -212,47 +260,85 @@ async function executeDriveRound(args) {
212
260
  }
213
261
  release();
214
262
  }
215
- if (followUp) {
216
- args.scheduleDrive(dialog, {
217
- waitInQue: true,
218
- humanPrompt: {
219
- content: followUp.prompt,
220
- msgId: followUp.msgId,
221
- grammar: 'markdown',
222
- userLanguageCode: followUp.userLanguageCode === 'zh' || followUp.userLanguageCode === 'en'
223
- ? followUp.userLanguageCode
224
- : undefined,
225
- },
226
- });
227
- return;
228
- }
229
263
  if (dialog instanceof dialog_1.SubDialog &&
230
264
  driveResult &&
231
265
  !driveResult.interrupted &&
232
266
  driveResult.lastAssistantSayingContent !== null) {
233
- const suspension = await dialog.getSuspensionStatus();
234
- if (!suspension.canDrive) {
235
- log_1.log.info('driver-v2 skip supplying subdialog response because dialog is still suspended', {
236
- rootId: dialog.id.rootId,
237
- selfId: dialog.id.selfId,
238
- waitingQ4H: suspension.q4h,
239
- waitingSubdialogs: suspension.subdialogs,
240
- });
241
- return;
242
- }
267
+ let supplied = false;
243
268
  if (subdialogReplyTarget) {
244
- await (0, supdialog_response_1.supplySubdialogResponseToSpecificCallerIfPendingV2)({
269
+ supplied = await (0, supdialog_response_1.supplySubdialogResponseToSpecificCallerIfPendingV2)({
245
270
  subdialog: dialog,
246
271
  responseText: driveResult.lastAssistantSayingContent,
247
272
  target: subdialogReplyTarget,
248
273
  scheduleDrive: args.scheduleDrive,
249
274
  });
250
- 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
+ }
251
282
  }
252
- await (0, supdialog_response_1.supplySubdialogResponseToAssignedCallerIfPendingV2)({
253
- subdialog: dialog,
254
- responseText: driveResult.lastAssistantSayingContent,
255
- 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
+ },
256
342
  });
257
343
  }
258
344
  }