dominds 1.24.2 → 1.24.4
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.
- package/dist/apps/runtime.js +0 -3
- package/dist/dialog-display-state.js +4 -2
- package/dist/dialog-global-registry.d.ts +15 -1
- package/dist/dialog-global-registry.js +73 -5
- package/dist/dialog.d.ts +3 -3
- package/dist/dialog.js +4 -2
- package/dist/docs/tellask-background-continuation-refactor.zh.md +144 -15
- package/dist/llm/gen/mock.js +2 -0
- package/dist/llm/kernel-driver/drive.js +84 -16
- package/dist/llm/kernel-driver/flow.js +375 -100
- package/dist/llm/kernel-driver/loop.js +85 -46
- package/dist/llm/kernel-driver/reply-guidance.js +0 -3
- package/dist/llm/kernel-driver/tellask-special.d.ts +1 -0
- package/dist/llm/kernel-driver/tellask-special.js +3 -0
- package/dist/llm/kernel-driver/types.d.ts +10 -5
- package/dist/persistence.d.ts +30 -1
- package/dist/persistence.js +186 -4
- package/dist/runtime/driver-messages.d.ts +1 -0
- package/dist/runtime/driver-messages.js +7 -2
- package/dist/server/websocket-handler.d.ts +14 -0
- package/dist/server/websocket-handler.js +42 -3
- package/package.json +4 -4
- package/webapp/dist/assets/{_basePickBy-DBvh4H3k.js → _basePickBy-ZLV93S3E.js} +3 -3
- package/webapp/dist/assets/{_basePickBy-DBvh4H3k.js.map → _basePickBy-ZLV93S3E.js.map} +1 -1
- package/webapp/dist/assets/{_baseUniq-CQmc9B-o.js → _baseUniq-D0wSOJ06.js} +2 -2
- package/webapp/dist/assets/{_baseUniq-CQmc9B-o.js.map → _baseUniq-D0wSOJ06.js.map} +1 -1
- package/webapp/dist/assets/{arc-DfLiOX_4.js → arc-BHclbMTS.js} +2 -2
- package/webapp/dist/assets/{arc-DfLiOX_4.js.map → arc-BHclbMTS.js.map} +1 -1
- package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CaTVJ5ev.js → architectureDiagram-2XIMDMQ5-CK99gE_D.js} +7 -7
- package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CaTVJ5ev.js.map → architectureDiagram-2XIMDMQ5-CK99gE_D.js.map} +1 -1
- package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-DlmAtUca.js → blockDiagram-WCTKOSBZ-fE5MBTEU.js} +7 -7
- package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-DlmAtUca.js.map → blockDiagram-WCTKOSBZ-fE5MBTEU.js.map} +1 -1
- package/webapp/dist/assets/{c4Diagram-IC4MRINW-CNHKD5Sl.js → c4Diagram-IC4MRINW-BSLyPyoU.js} +3 -3
- package/webapp/dist/assets/{c4Diagram-IC4MRINW-CNHKD5Sl.js.map → c4Diagram-IC4MRINW-BSLyPyoU.js.map} +1 -1
- package/webapp/dist/assets/{channel-BTWhZYd5.js → channel-DSvMpp-a.js} +2 -2
- package/webapp/dist/assets/{channel-BTWhZYd5.js.map → channel-DSvMpp-a.js.map} +1 -1
- package/webapp/dist/assets/{chunk-4BX2VUAB-CV1n0Uhy.js → chunk-4BX2VUAB-OXEX170k.js} +2 -2
- package/webapp/dist/assets/{chunk-4BX2VUAB-CV1n0Uhy.js.map → chunk-4BX2VUAB-OXEX170k.js.map} +1 -1
- package/webapp/dist/assets/{chunk-55IACEB6-C5r77i_p.js → chunk-55IACEB6-BFQ_spQD.js} +2 -2
- package/webapp/dist/assets/{chunk-55IACEB6-C5r77i_p.js.map → chunk-55IACEB6-BFQ_spQD.js.map} +1 -1
- package/webapp/dist/assets/{chunk-FMBD7UC4-DJ1yHxrH.js → chunk-FMBD7UC4-CbQ2BBPs.js} +2 -2
- package/webapp/dist/assets/{chunk-FMBD7UC4-DJ1yHxrH.js.map → chunk-FMBD7UC4-CbQ2BBPs.js.map} +1 -1
- package/webapp/dist/assets/{chunk-JSJVCQXG-Ahg9hCCm.js → chunk-JSJVCQXG-C4P1mjCL.js} +2 -2
- package/webapp/dist/assets/{chunk-JSJVCQXG-Ahg9hCCm.js.map → chunk-JSJVCQXG-C4P1mjCL.js.map} +1 -1
- package/webapp/dist/assets/{chunk-KX2RTZJC-qBrewKt0.js → chunk-KX2RTZJC-BMd-daMY.js} +2 -2
- package/webapp/dist/assets/{chunk-KX2RTZJC-qBrewKt0.js.map → chunk-KX2RTZJC-BMd-daMY.js.map} +1 -1
- package/webapp/dist/assets/{chunk-NQ4KR5QH-1lEdM6Wi.js → chunk-NQ4KR5QH-B_ZhWMXR.js} +4 -4
- package/webapp/dist/assets/{chunk-NQ4KR5QH-1lEdM6Wi.js.map → chunk-NQ4KR5QH-B_ZhWMXR.js.map} +1 -1
- package/webapp/dist/assets/{chunk-QZHKN3VN-ChVR749G.js → chunk-QZHKN3VN-Cbf92xIw.js} +2 -2
- package/webapp/dist/assets/{chunk-QZHKN3VN-ChVR749G.js.map → chunk-QZHKN3VN-Cbf92xIw.js.map} +1 -1
- package/webapp/dist/assets/{chunk-WL4C6EOR-BAUXgk0K.js → chunk-WL4C6EOR-PtH-blkK.js} +6 -6
- package/webapp/dist/assets/{chunk-WL4C6EOR-BAUXgk0K.js.map → chunk-WL4C6EOR-PtH-blkK.js.map} +1 -1
- package/webapp/dist/assets/{classDiagram-VBA2DB6C-DlqyhKve.js → classDiagram-VBA2DB6C-Dc3ncaD0.js} +7 -7
- package/webapp/dist/assets/{classDiagram-VBA2DB6C-DlqyhKve.js.map → classDiagram-VBA2DB6C-Dc3ncaD0.js.map} +1 -1
- package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-DlqyhKve.js → classDiagram-v2-RAHNMMFH-Dc3ncaD0.js} +7 -7
- package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-DlqyhKve.js.map → classDiagram-v2-RAHNMMFH-Dc3ncaD0.js.map} +1 -1
- package/webapp/dist/assets/{clone-BFiIqUsc.js → clone-E9Ad85BC.js} +2 -2
- package/webapp/dist/assets/{clone-BFiIqUsc.js.map → clone-E9Ad85BC.js.map} +1 -1
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-JYvhtd6J.js → cose-bilkent-S5V4N54A-B-nj0o74.js} +2 -2
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-JYvhtd6J.js.map → cose-bilkent-S5V4N54A-B-nj0o74.js.map} +1 -1
- package/webapp/dist/assets/{dagre-KLK3FWXG-CCGcQh6w.js → dagre-KLK3FWXG-CyJYNIbm.js} +7 -7
- package/webapp/dist/assets/{dagre-KLK3FWXG-CCGcQh6w.js.map → dagre-KLK3FWXG-CyJYNIbm.js.map} +1 -1
- package/webapp/dist/assets/{diagram-E7M64L7V-BXC4AxAd.js → diagram-E7M64L7V-C8eweQ7b.js} +8 -8
- package/webapp/dist/assets/{diagram-E7M64L7V-BXC4AxAd.js.map → diagram-E7M64L7V-C8eweQ7b.js.map} +1 -1
- package/webapp/dist/assets/{diagram-IFDJBPK2-B--Sb3XT.js → diagram-IFDJBPK2-DMdygRl0.js} +7 -7
- package/webapp/dist/assets/{diagram-IFDJBPK2-B--Sb3XT.js.map → diagram-IFDJBPK2-DMdygRl0.js.map} +1 -1
- package/webapp/dist/assets/{diagram-P4PSJMXO-CVqgtrh3.js → diagram-P4PSJMXO-BQDZHb0a.js} +7 -7
- package/webapp/dist/assets/{diagram-P4PSJMXO-CVqgtrh3.js.map → diagram-P4PSJMXO-BQDZHb0a.js.map} +1 -1
- package/webapp/dist/assets/{erDiagram-INFDFZHY-BniHaRTt.js → erDiagram-INFDFZHY-C1HaXN6E.js} +5 -5
- package/webapp/dist/assets/{erDiagram-INFDFZHY-BniHaRTt.js.map → erDiagram-INFDFZHY-C1HaXN6E.js.map} +1 -1
- package/webapp/dist/assets/{flowDiagram-PKNHOUZH-wLKFBWTR.js → flowDiagram-PKNHOUZH-24nNqQyo.js} +7 -7
- package/webapp/dist/assets/{flowDiagram-PKNHOUZH-wLKFBWTR.js.map → flowDiagram-PKNHOUZH-24nNqQyo.js.map} +1 -1
- package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-DrptcitZ.js → ganttDiagram-A5KZAMGK-BWPOFaLV.js} +3 -3
- package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-DrptcitZ.js.map → ganttDiagram-A5KZAMGK-BWPOFaLV.js.map} +1 -1
- package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-C6l5aP44.js → gitGraphDiagram-K3NZZRJ6-D7_L-p_Y.js} +8 -8
- package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-C6l5aP44.js.map → gitGraphDiagram-K3NZZRJ6-D7_L-p_Y.js.map} +1 -1
- package/webapp/dist/assets/{graph-DXuQGYQN.js → graph-OHu4dL2n.js} +3 -3
- package/webapp/dist/assets/{graph-DXuQGYQN.js.map → graph-OHu4dL2n.js.map} +1 -1
- package/webapp/dist/assets/{index-DuQ1OCMG.js → index-CDCDAfqP.js} +165 -67
- package/webapp/dist/assets/{index-DuQ1OCMG.js.map → index-CDCDAfqP.js.map} +1 -1
- package/webapp/dist/assets/{infoDiagram-LFFYTUFH-BbleCSjW.js → infoDiagram-LFFYTUFH-CvaBM5j6.js} +6 -6
- package/webapp/dist/assets/{infoDiagram-LFFYTUFH-BbleCSjW.js.map → infoDiagram-LFFYTUFH-CvaBM5j6.js.map} +1 -1
- package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-DmV-LZuk.js → ishikawaDiagram-PHBUUO56-DB1l2Uue.js} +2 -2
- package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-DmV-LZuk.js.map → ishikawaDiagram-PHBUUO56-DB1l2Uue.js.map} +1 -1
- package/webapp/dist/assets/{journeyDiagram-4ABVD52K-D3sQFfac.js → journeyDiagram-4ABVD52K-TQR6_teO.js} +5 -5
- package/webapp/dist/assets/{journeyDiagram-4ABVD52K-D3sQFfac.js.map → journeyDiagram-4ABVD52K-TQR6_teO.js.map} +1 -1
- package/webapp/dist/assets/{kanban-definition-K7BYSVSG-BEeBlBtM.js → kanban-definition-K7BYSVSG-B-BOuC-U.js} +3 -3
- package/webapp/dist/assets/{kanban-definition-K7BYSVSG-BEeBlBtM.js.map → kanban-definition-K7BYSVSG-B-BOuC-U.js.map} +1 -1
- package/webapp/dist/assets/{layout-g7jjgV-W.js → layout-B8yqIqbx.js} +5 -5
- package/webapp/dist/assets/{layout-g7jjgV-W.js.map → layout-B8yqIqbx.js.map} +1 -1
- package/webapp/dist/assets/{linear-D_X91Yek.js → linear-CoLfiZKK.js} +2 -2
- package/webapp/dist/assets/{linear-D_X91Yek.js.map → linear-CoLfiZKK.js.map} +1 -1
- package/webapp/dist/assets/{mindmap-definition-YRQLILUH-NkMAIgRY.js → mindmap-definition-YRQLILUH-P70BMIHI.js} +4 -4
- package/webapp/dist/assets/{mindmap-definition-YRQLILUH-NkMAIgRY.js.map → mindmap-definition-YRQLILUH-P70BMIHI.js.map} +1 -1
- package/webapp/dist/assets/{pieDiagram-SKSYHLDU-Z6E4GEPC.js → pieDiagram-SKSYHLDU-DsS_4dTB.js} +8 -8
- package/webapp/dist/assets/{pieDiagram-SKSYHLDU-Z6E4GEPC.js.map → pieDiagram-SKSYHLDU-DsS_4dTB.js.map} +1 -1
- package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BH8hfOuU.js → quadrantDiagram-337W2JSQ-DoM9PEq-.js} +3 -3
- package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-BH8hfOuU.js.map → quadrantDiagram-337W2JSQ-DoM9PEq-.js.map} +1 -1
- package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-DRJkvoQI.js → requirementDiagram-Z7DCOOCP-Bn3lYMMI.js} +4 -4
- package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-DRJkvoQI.js.map → requirementDiagram-Z7DCOOCP-Bn3lYMMI.js.map} +1 -1
- package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-D2VwjtJo.js → sankeyDiagram-WA2Y5GQK-97kCegRT.js} +2 -2
- package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-D2VwjtJo.js.map → sankeyDiagram-WA2Y5GQK-97kCegRT.js.map} +1 -1
- package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-Cq-gEPOw.js → sequenceDiagram-2WXFIKYE-DXqjQjf6.js} +4 -4
- package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-Cq-gEPOw.js.map → sequenceDiagram-2WXFIKYE-DXqjQjf6.js.map} +1 -1
- package/webapp/dist/assets/{stateDiagram-RAJIS63D-CFM8Jqke.js → stateDiagram-RAJIS63D-DQcTPKWP.js} +9 -9
- package/webapp/dist/assets/{stateDiagram-RAJIS63D-CFM8Jqke.js.map → stateDiagram-RAJIS63D-DQcTPKWP.js.map} +1 -1
- package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-Dip5iGX_.js → stateDiagram-v2-FVOUBMTO-DHmxRVJn.js} +5 -5
- package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-Dip5iGX_.js.map → stateDiagram-v2-FVOUBMTO-DHmxRVJn.js.map} +1 -1
- package/webapp/dist/assets/{timeline-definition-YZTLITO2-OI9JzMjX.js → timeline-definition-YZTLITO2-BlovQQ4B.js} +3 -3
- package/webapp/dist/assets/{timeline-definition-YZTLITO2-OI9JzMjX.js.map → timeline-definition-YZTLITO2-BlovQQ4B.js.map} +1 -1
- package/webapp/dist/assets/{treemap-KZPCXAKY-CtNF416A.js → treemap-KZPCXAKY-CGu93c9S.js} +5 -5
- package/webapp/dist/assets/{treemap-KZPCXAKY-CtNF416A.js.map → treemap-KZPCXAKY-CGu93c9S.js.map} +1 -1
- package/webapp/dist/assets/{vennDiagram-LZ73GAT5-CjaPj4FZ.js → vennDiagram-LZ73GAT5-Do1jprrz.js} +2 -2
- package/webapp/dist/assets/{vennDiagram-LZ73GAT5-CjaPj4FZ.js.map → vennDiagram-LZ73GAT5-Do1jprrz.js.map} +1 -1
- package/webapp/dist/assets/{xychartDiagram-JWTSCODW-21mBt9iu.js → xychartDiagram-JWTSCODW-BKa1DxVq.js} +3 -3
- package/webapp/dist/assets/{xychartDiagram-JWTSCODW-21mBt9iu.js.map → xychartDiagram-JWTSCODW-BKa1DxVq.js.map} +1 -1
- package/webapp/dist/index.html +1 -1
|
@@ -13,6 +13,18 @@ const log_1 = require("../../log");
|
|
|
13
13
|
const persistence_1 = require("../../persistence");
|
|
14
14
|
const persistence_errors_1 = require("../../persistence-errors");
|
|
15
15
|
const engine_1 = require("./engine");
|
|
16
|
+
function formatDriveError(error) {
|
|
17
|
+
if (error instanceof Error) {
|
|
18
|
+
return {
|
|
19
|
+
name: error.name,
|
|
20
|
+
message: error.message,
|
|
21
|
+
...(typeof error.stack === 'string' && error.stack.trim() !== ''
|
|
22
|
+
? { stack: error.stack }
|
|
23
|
+
: {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return { message: String(error) };
|
|
27
|
+
}
|
|
16
28
|
function formatDriveTriggerForLog(trigger) {
|
|
17
29
|
return {
|
|
18
30
|
action: trigger.action,
|
|
@@ -26,7 +38,7 @@ function formatDriveTriggerForLog(trigger) {
|
|
|
26
38
|
};
|
|
27
39
|
}
|
|
28
40
|
async function listLiveDialogsWithDurableDriveWork() {
|
|
29
|
-
const liveDialogs = dialog_global_registry_1.globalDialogRegistry.
|
|
41
|
+
const liveDialogs = dialog_global_registry_1.globalDialogRegistry.consumeQueuedMainDialogs();
|
|
30
42
|
const queued = [];
|
|
31
43
|
for (const mainDialog of liveDialogs) {
|
|
32
44
|
let watchedDialogIds;
|
|
@@ -42,6 +54,7 @@ async function listLiveDialogsWithDurableDriveWork() {
|
|
|
42
54
|
}
|
|
43
55
|
const candidateDialogs = [mainDialog];
|
|
44
56
|
let hadCandidateInspectionError = false;
|
|
57
|
+
let hadStalledCandidateForRoot = false;
|
|
45
58
|
for (const watchedDialogId of watchedDialogIds) {
|
|
46
59
|
let watchedDialog;
|
|
47
60
|
try {
|
|
@@ -95,13 +108,24 @@ async function listLiveDialogsWithDurableDriveWork() {
|
|
|
95
108
|
if (!(0, dialog_drive_work_1.hasDurableDriveWork)(latest)) {
|
|
96
109
|
continue;
|
|
97
110
|
}
|
|
111
|
+
const durableWorkFingerprint = persistence_1.DialogPersistence.buildBackendDriveDurableWorkFingerprint(latest, dialog.id.selfId === dialog.id.rootId ? watchedDialogIds : []);
|
|
112
|
+
if (latest.backendDriveStall?.durableWorkFingerprint === durableWorkFingerprint) {
|
|
113
|
+
hadStalledCandidateForRoot = true;
|
|
114
|
+
log_1.log.warn('Backend driver skipped stalled durable work pending new facts', undefined, {
|
|
115
|
+
dialogId: dialog.id.valueOf(),
|
|
116
|
+
rootId: dialog.id.rootId,
|
|
117
|
+
selfId: dialog.id.selfId,
|
|
118
|
+
stallRecordId: latest.backendDriveStall.recordId,
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
98
122
|
hasQueuedCandidateForRoot = true;
|
|
99
123
|
queued.push({
|
|
100
124
|
rootDialog: mainDialog,
|
|
101
125
|
dialog,
|
|
102
126
|
});
|
|
103
127
|
}
|
|
104
|
-
if (!hasQueuedCandidateForRoot && !hadCandidateInspectionError) {
|
|
128
|
+
if (!hasQueuedCandidateForRoot && !hadCandidateInspectionError && !hadStalledCandidateForRoot) {
|
|
105
129
|
dialog_global_registry_1.globalDialogRegistry.clearDriveWake(mainDialog.id.rootId, {
|
|
106
130
|
source: 'kernel_driver_backend_loop',
|
|
107
131
|
reason: 'no_durable_drive_work',
|
|
@@ -111,29 +135,6 @@ async function listLiveDialogsWithDurableDriveWork() {
|
|
|
111
135
|
}
|
|
112
136
|
return queued;
|
|
113
137
|
}
|
|
114
|
-
async function reassertLiveRootWakeForDurableWork() {
|
|
115
|
-
for (const mainDialog of dialog_global_registry_1.globalDialogRegistry.getAll()) {
|
|
116
|
-
try {
|
|
117
|
-
const rootHasPendingNextStepTriggers = await persistence_1.DialogPersistence.hasPendingNextStepTriggers(mainDialog.id, mainDialog.status);
|
|
118
|
-
const watchedDialogIds = await persistence_1.DialogPersistence.loadDriveWatchedDialogIds(mainDialog.id, mainDialog.status);
|
|
119
|
-
if (!rootHasPendingNextStepTriggers && watchedDialogIds.length === 0) {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
dialog_global_registry_1.globalDialogRegistry.wakeDrive(mainDialog.id.rootId, {
|
|
123
|
-
source: 'kernel_driver_backend_loop',
|
|
124
|
-
reason: rootHasPendingNextStepTriggers
|
|
125
|
-
? 'root_next_step_still_pending'
|
|
126
|
-
: 'drive_watch_still_pending',
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
log_1.log.error('Backend driver failed to reassert root wake for durable work', error, {
|
|
131
|
-
rootId: mainDialog.id.rootId,
|
|
132
|
-
selfId: mainDialog.id.selfId,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
138
|
async function driveQueuedDialogsOnce() {
|
|
138
139
|
const dialogsToDrive = await listLiveDialogsWithDurableDriveWork();
|
|
139
140
|
for (const { rootDialog, dialog } of dialogsToDrive) {
|
|
@@ -143,7 +144,6 @@ async function driveQueuedDialogsOnce() {
|
|
|
143
144
|
await persistence_1.DialogPersistence.removeDriveWatchForDialog(dialog.id, dialog.status);
|
|
144
145
|
continue;
|
|
145
146
|
}
|
|
146
|
-
const currentHasPendingNextStepTriggers = (latestForDrive?.nextStep.triggers.length ?? 0) > 0;
|
|
147
147
|
const currentResumeInProgressGeneration = (0, dialog_generation_run_1.getRecoverableGenerationRunState)(latestForDrive) !== undefined;
|
|
148
148
|
const currentHasBackendDurableWork = (0, dialog_drive_work_1.hasDurableDriveWork)(latestForDrive);
|
|
149
149
|
const executionMarker = latestForDrive?.executionMarker;
|
|
@@ -203,20 +203,27 @@ async function driveQueuedDialogsOnce() {
|
|
|
203
203
|
const stillHasDurableWork = (0, dialog_drive_work_1.hasDurableDriveWork)(latestAfterDrive);
|
|
204
204
|
const shouldStayQueued = dialog.hasUpNext() || !status.canDrive || stillHasDurableWork;
|
|
205
205
|
if (shouldStayQueued) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
:
|
|
211
|
-
? '
|
|
212
|
-
: '
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
206
|
+
const canRetryImmediately = dialog.hasUpNext() || (status.canDrive && stillHasDurableWork);
|
|
207
|
+
if (canRetryImmediately) {
|
|
208
|
+
dialog_global_registry_1.globalDialogRegistry.wakeDrive(rootDialog.id.rootId, {
|
|
209
|
+
source: 'kernel_driver_backend_loop',
|
|
210
|
+
reason: dialog.hasUpNext()
|
|
211
|
+
? 'post_drive_upnext_pending'
|
|
212
|
+
: 'post_drive_durable_work_pending',
|
|
213
|
+
});
|
|
214
|
+
if (dialog.id.selfId === dialog.id.rootId) {
|
|
215
|
+
await persistence_1.DialogPersistence.upsertRootDriveWakeTrigger(dialog.id, dialog.hasUpNext() ? 'post_drive_upnext_pending' : 'post_drive_durable_work_pending', dialog.status);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
log_1.log.debug('Backend driver left durable work parked until the blocking state changes', undefined, {
|
|
220
|
+
dialogId: dialog.id.valueOf(),
|
|
221
|
+
rootId: dialog.id.rootId,
|
|
222
|
+
selfId: dialog.id.selfId,
|
|
223
|
+
waitingQ4H: status.q4h,
|
|
224
|
+
backgroundCalleeDialogs: status.backgroundCalleeDialogs,
|
|
225
|
+
stillHasDurableWork,
|
|
226
|
+
});
|
|
220
227
|
}
|
|
221
228
|
}
|
|
222
229
|
else {
|
|
@@ -289,14 +296,47 @@ async function driveQueuedDialogsOnce() {
|
|
|
289
296
|
const rootHasPendingNextStepTriggers = await persistence_1.DialogPersistence.hasPendingNextStepTriggers(rootId, dialog.status);
|
|
290
297
|
const watchedDialogIds = await persistence_1.DialogPersistence.loadDriveWatchedDialogIds(rootId, dialog.status);
|
|
291
298
|
if (rootHasPendingNextStepTriggers || watchedDialogIds.length > 0) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
299
|
+
const durableWorkFingerprint = persistence_1.DialogPersistence.buildBackendDriveDurableWorkFingerprint(latestAfterError, dialog.id.selfId === dialog.id.rootId ? watchedDialogIds : []);
|
|
300
|
+
const record = await persistence_1.DialogPersistence.appendBackendDriveStallRecord(dialog.id, {
|
|
301
|
+
dialogId: dialog.id.valueOf(),
|
|
302
|
+
rootId: dialog.id.rootId,
|
|
303
|
+
selfId: dialog.id.selfId,
|
|
304
|
+
status: dialog.status,
|
|
305
|
+
reason: 'backend_drive_error',
|
|
306
|
+
durableWorkFingerprint,
|
|
307
|
+
latestSummary: latestAfterError === null
|
|
308
|
+
? null
|
|
309
|
+
: {
|
|
310
|
+
currentCourse: latestAfterError.currentCourse,
|
|
311
|
+
status: latestAfterError.status,
|
|
312
|
+
generating: latestAfterError.generating ?? false,
|
|
313
|
+
displayState: latestAfterError.displayState ?? null,
|
|
314
|
+
executionMarker: latestAfterError.executionMarker ?? null,
|
|
315
|
+
generationRunState: latestAfterError.generationRunState ?? null,
|
|
316
|
+
nextStepTriggerCount: latestAfterError.nextStep.triggers.length,
|
|
317
|
+
pendingRuntimePromptMsgId: latestAfterError.pendingRuntimePrompt?.msgId ?? null,
|
|
318
|
+
replyDelivery: latestAfterError.replyDelivery ?? null,
|
|
319
|
+
userWait: latestAfterError.userWait ?? null,
|
|
320
|
+
sideDialogFinalResponse: latestAfterError.sideDialogFinalResponse ?? null,
|
|
321
|
+
},
|
|
322
|
+
error: formatDriveError(err),
|
|
323
|
+
context: {
|
|
324
|
+
rootHasPendingNextStepTriggers,
|
|
325
|
+
watchedDialogCount: watchedDialogIds.length,
|
|
326
|
+
},
|
|
327
|
+
}, dialog.status);
|
|
328
|
+
log_1.log.warn('Backend driver persisted stalled durable work after drive error', undefined, {
|
|
329
|
+
dialogId: dialog.id.valueOf(),
|
|
330
|
+
rootId: dialog.id.rootId,
|
|
331
|
+
selfId: dialog.id.selfId,
|
|
332
|
+
stallRecordId: record.recordId,
|
|
333
|
+
rootHasPendingNextStepTriggers,
|
|
334
|
+
watchedDialogCount: watchedDialogIds.length,
|
|
295
335
|
});
|
|
296
336
|
}
|
|
297
337
|
}
|
|
298
|
-
catch (
|
|
299
|
-
log_1.log.error('Failed to
|
|
338
|
+
catch (stallErr) {
|
|
339
|
+
log_1.log.error('Failed to persist backend drive stall after drive error', stallErr, {
|
|
300
340
|
dialogId: dialog.id.valueOf(),
|
|
301
341
|
rootId: dialog.id.rootId,
|
|
302
342
|
selfId: dialog.id.selfId,
|
|
@@ -304,7 +344,6 @@ async function driveQueuedDialogsOnce() {
|
|
|
304
344
|
}
|
|
305
345
|
}
|
|
306
346
|
}
|
|
307
|
-
await reassertLiveRootWakeForDurableWork();
|
|
308
347
|
}
|
|
309
348
|
function isBackendDriverAborted(options) {
|
|
310
349
|
return options?.abortSignal?.aborted === true;
|
|
@@ -125,9 +125,6 @@ async function resolveFreshCurrentSideDialogAssignmentDirective(args) {
|
|
|
125
125
|
if (!latest) {
|
|
126
126
|
return undefined;
|
|
127
127
|
}
|
|
128
|
-
if (latest.pendingRuntimePrompt?.msgId !== args.prompt.msgId) {
|
|
129
|
-
return undefined;
|
|
130
|
-
}
|
|
131
128
|
if (latest.sideDialogFinalResponse?.callId === currentAssignmentDirective.targetCallId.trim()) {
|
|
132
129
|
return undefined;
|
|
133
130
|
}
|
|
@@ -122,6 +122,7 @@ export type TellaskFunctionRoundResult = Readonly<{
|
|
|
122
122
|
hasImmediateTellaskOutputs: boolean;
|
|
123
123
|
immediateTellaskOutputCallIds: readonly string[];
|
|
124
124
|
shouldStopAfterReplyTool: boolean;
|
|
125
|
+
shouldStopAfterPendingTellaskWait: boolean;
|
|
125
126
|
}>;
|
|
126
127
|
export declare function processTellaskFunctionRound(args: {
|
|
127
128
|
dlg: Dialog;
|
|
@@ -2390,6 +2390,7 @@ async function processTellaskFunctionRound(args) {
|
|
|
2390
2390
|
const tellaskToolOutputs = [];
|
|
2391
2391
|
const immediateTellaskOutputCallIds = [];
|
|
2392
2392
|
let hasImmediateTellaskOutputs = false;
|
|
2393
|
+
let shouldStopAfterPendingTellaskWait = false;
|
|
2393
2394
|
for (const output of tellaskExecution.toolOutputs) {
|
|
2394
2395
|
if (output.type === 'func_result_msg') {
|
|
2395
2396
|
const result = output;
|
|
@@ -2445,6 +2446,7 @@ async function processTellaskFunctionRound(args) {
|
|
|
2445
2446
|
});
|
|
2446
2447
|
tellaskFuncResultByCallId.set(call.callId, pendingResult);
|
|
2447
2448
|
tellaskFuncResults.push(pendingResult);
|
|
2449
|
+
shouldStopAfterPendingTellaskWait = true;
|
|
2448
2450
|
}
|
|
2449
2451
|
for (const result of tellaskFuncResults) {
|
|
2450
2452
|
await persistTellaskFuncResult(args.dlg, result);
|
|
@@ -2459,5 +2461,6 @@ async function processTellaskFunctionRound(args) {
|
|
|
2459
2461
|
hasImmediateTellaskOutputs,
|
|
2460
2462
|
immediateTellaskOutputCallIds,
|
|
2461
2463
|
shouldStopAfterReplyTool: orderedInvalidCalls.length === 0 && tellaskExecution.successfulReplyCallIds.length > 0,
|
|
2464
|
+
shouldStopAfterPendingTellaskWait,
|
|
2462
2465
|
};
|
|
2463
2466
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DialogDisplayState, DialogInterruptionReason } from '@longrun-ai/kernel/types/display-state';
|
|
2
2
|
import type { DialogDiligencePrompt, DialogPrompt, DialogRunControlSpec, DialogRuntimeGuidePrompt, DialogRuntimePrompt, DialogRuntimeReplyPrompt, DialogRuntimeSideDialogPrompt, DialogUserPrompt } from '@longrun-ai/kernel/types/drive-intent';
|
|
3
|
-
import type { CallSiteCourseNo, CallSiteGenseqNo } from '@longrun-ai/kernel/types/storage';
|
|
3
|
+
import type { CallSiteCourseNo, CallSiteGenseqNo, DialogBusinessContinuation } from '@longrun-ai/kernel/types/storage';
|
|
4
4
|
import type { Dialog, DialogID } from '../../dialog';
|
|
5
5
|
export type KernelDriverRunControl = DialogRunControlSpec;
|
|
6
6
|
export type KernelDriverDriveSource = 'unspecified' | 'ws_user_message' | 'ws_user_answer' | 'ws_diligence_push' | 'ws_resume_dialog' | 'ws_resume_all' | 'kernel_driver_backend_loop' | 'kernel_driver_follow_up' | 'kernel_driver_sideDialog_init' | 'kernel_driver_sideDialog_resume' | 'kernel_driver_fbr_sideDialog_round' | 'kernel_driver_type_a_askerDialog_call' | 'kernel_driver_supply_response_caller_revive' | 'kernel_driver_idle_reminder_wake';
|
|
@@ -8,7 +8,14 @@ export type KernelDriverDriveOptions = Readonly<{
|
|
|
8
8
|
suppressDiligencePush?: boolean;
|
|
9
9
|
allowResumeFromInterrupted?: boolean;
|
|
10
10
|
resumeInProgressGeneration?: boolean;
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Business continuation identity for no-prompt driver iterations.
|
|
13
|
+
*
|
|
14
|
+
* This is deliberately part of the drive contract instead of being rediscovered from old
|
|
15
|
+
* transcript/assignment records. A continuation must tell the next iteration what business
|
|
16
|
+
* obligation it is continuing, or the driver treats it as no business continuation.
|
|
17
|
+
*/
|
|
18
|
+
businessContinuation?: DialogBusinessContinuation;
|
|
12
19
|
noPromptSideDialogResumeEntitlement?: Readonly<{
|
|
13
20
|
callerDialogId: string;
|
|
14
21
|
reason: 'reply_tellask_back_delivered';
|
|
@@ -21,9 +28,6 @@ export type KernelDriverDriveOptions = Readonly<{
|
|
|
21
28
|
sideDialogId?: string;
|
|
22
29
|
callType?: 'A' | 'B' | 'C';
|
|
23
30
|
callId?: string;
|
|
24
|
-
}> | Readonly<{
|
|
25
|
-
callerDialogId: string;
|
|
26
|
-
reason: 'reply_obligation_follow_up';
|
|
27
31
|
}> | Readonly<{
|
|
28
32
|
callerDialogId: string;
|
|
29
33
|
reason: 'resolved_pending_sideDialog_reply';
|
|
@@ -115,6 +119,7 @@ export type KernelDriverCoreResult = {
|
|
|
115
119
|
lastAssistantThinkingGenseq: number | null;
|
|
116
120
|
lastFunctionCallGenseq: number | null;
|
|
117
121
|
lastAssistantReplyTarget?: KernelDriverCalleeReplyTarget;
|
|
122
|
+
lastBusinessContinuation: DialogBusinessContinuation;
|
|
118
123
|
fbrConclusion?: {
|
|
119
124
|
responseText: string;
|
|
120
125
|
responseGenseq: number;
|
package/dist/persistence.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export declare class DiskFileDialogStore extends DialogStore {
|
|
|
87
87
|
* CRITICAL: This must be called BEFORE any substream events (thinking_start, markdown_start, etc.)
|
|
88
88
|
* to ensure proper event ordering on the frontend.
|
|
89
89
|
*/
|
|
90
|
-
notifyGeneratingStart(dialog: Dialog, msgId?: string): Promise<
|
|
90
|
+
notifyGeneratingStart(dialog: Dialog, msgId?: string): Promise<readonly DialogNextStepTrigger[]>;
|
|
91
91
|
private getResultArrivalBatchIdsFromAcceptedTriggers;
|
|
92
92
|
/**
|
|
93
93
|
* Notify end of LLM generation for frontend bubble management
|
|
@@ -259,6 +259,28 @@ type DialogLatestMutation = {
|
|
|
259
259
|
kind: 'replace';
|
|
260
260
|
next: DialogLatestFile;
|
|
261
261
|
};
|
|
262
|
+
export type BackendDriveStallWrite = Readonly<{
|
|
263
|
+
dialogId: string;
|
|
264
|
+
rootId: string;
|
|
265
|
+
selfId: string;
|
|
266
|
+
status: DialogStatusKind;
|
|
267
|
+
reason: 'backend_drive_error';
|
|
268
|
+
durableWorkFingerprint: string;
|
|
269
|
+
latestSummary: Record<string, unknown> | null;
|
|
270
|
+
error: {
|
|
271
|
+
name?: string;
|
|
272
|
+
message: string;
|
|
273
|
+
stack?: string;
|
|
274
|
+
};
|
|
275
|
+
context: {
|
|
276
|
+
rootHasPendingNextStepTriggers: boolean;
|
|
277
|
+
watchedDialogCount: number;
|
|
278
|
+
};
|
|
279
|
+
}>;
|
|
280
|
+
export type BackendDriveStallRecord = BackendDriveStallWrite & Readonly<{
|
|
281
|
+
recordId: string;
|
|
282
|
+
recordedAt: string;
|
|
283
|
+
}>;
|
|
262
284
|
type MainDialogWriteBackCancellationToken = Readonly<{
|
|
263
285
|
scopeKey: string;
|
|
264
286
|
generation: number;
|
|
@@ -285,6 +307,7 @@ export declare class DialogPersistence {
|
|
|
285
307
|
private static readonly q4hWriteBack;
|
|
286
308
|
private static readonly activeCalleesMutexes;
|
|
287
309
|
private static readonly driveWatchMutexes;
|
|
310
|
+
private static readonly backendDriveStallMutexes;
|
|
288
311
|
private static readonly courseAppendMutexes;
|
|
289
312
|
private static readonly mainDialogWriteBackCancelGenerations;
|
|
290
313
|
private static getLatestWriteBackMutex;
|
|
@@ -293,8 +316,10 @@ export declare class DialogPersistence {
|
|
|
293
316
|
private static getCourseAppendMutexKey;
|
|
294
317
|
private static getActiveCalleesMutex;
|
|
295
318
|
private static getDriveWatchMutex;
|
|
319
|
+
private static getBackendDriveStallMutex;
|
|
296
320
|
private static getLatestWriteBackKey;
|
|
297
321
|
private static getDriveWatchKey;
|
|
322
|
+
private static getBackendDriveStallKey;
|
|
298
323
|
private static getQ4HWriteBackKey;
|
|
299
324
|
private static getActiveCalleesKey;
|
|
300
325
|
private static getMainDialogWriteBackCancelScopeKey;
|
|
@@ -477,7 +502,11 @@ export declare class DialogPersistence {
|
|
|
477
502
|
private static normalizeDriveWatchFile;
|
|
478
503
|
private static loadDriveWatchFromDisk;
|
|
479
504
|
private static writeDriveWatchToDisk;
|
|
505
|
+
private static getBackendDriveStallJsonlPath;
|
|
506
|
+
static buildBackendDriveDurableWorkFingerprint(latest: DialogLatestFile | null, watchedDialogIds?: readonly DialogID[]): string;
|
|
507
|
+
static appendBackendDriveStallRecord(dialogId: DialogID, write: BackendDriveStallWrite, status?: DialogStatusKind): Promise<BackendDriveStallRecord>;
|
|
480
508
|
private static mutateDriveWatch;
|
|
509
|
+
private static removeWithRetry;
|
|
481
510
|
static loadDriveWatchedDialogIds(rootDialogId: DialogID, status?: DialogStatusKind): Promise<readonly DialogID[]>;
|
|
482
511
|
private static setDialogDriveWatched;
|
|
483
512
|
static syncDriveWatchForDialogLatest(dialogId: DialogID, latest: DialogLatestFile, status?: DialogStatusKind): Promise<void>;
|
package/dist/persistence.js
CHANGED
|
@@ -98,6 +98,7 @@ function summarizeLatestProjectionState(latest) {
|
|
|
98
98
|
displayState: latest.displayState ?? null,
|
|
99
99
|
executionMarker: latest.executionMarker ?? null,
|
|
100
100
|
generationRunState: latest.generationRunState ?? null,
|
|
101
|
+
backendDriveStall: latest.backendDriveStall ?? null,
|
|
101
102
|
nextStepTriggerCount: latest.nextStep.triggers.length,
|
|
102
103
|
userWait: latest.userWait ?? null,
|
|
103
104
|
replyDelivery: latest.replyDelivery ?? null,
|
|
@@ -135,6 +136,7 @@ function summarizeLatestMutationPatch(patch) {
|
|
|
135
136
|
displayState: patch.displayState ?? null,
|
|
136
137
|
executionMarker: patch.executionMarker ?? null,
|
|
137
138
|
generationRunState: patch.generationRunState ?? null,
|
|
139
|
+
backendDriveStall: patch.backendDriveStall ?? null,
|
|
138
140
|
nextStepTriggerCount: patch.nextStep?.triggers.length ?? null,
|
|
139
141
|
userWait: patch.userWait ?? null,
|
|
140
142
|
replyDelivery: patch.replyDelivery ?? null,
|
|
@@ -412,6 +414,16 @@ function getErrorCode(error) {
|
|
|
412
414
|
const maybeCode = error.code;
|
|
413
415
|
return typeof maybeCode === 'string' ? maybeCode : undefined;
|
|
414
416
|
}
|
|
417
|
+
const RETRYABLE_FILESYSTEM_ERROR_CODES = new Set(['ENOENT', 'EPERM', 'EACCES', 'EBUSY']);
|
|
418
|
+
const FILESYSTEM_RETRY_BASE_DELAY_MS = 20;
|
|
419
|
+
const FILESYSTEM_RETRY_MAX_DELAY_MS = 250;
|
|
420
|
+
function getFilesystemRetryDelayMs(attempt) {
|
|
421
|
+
const delayMs = FILESYSTEM_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);
|
|
422
|
+
return Math.min(FILESYSTEM_RETRY_MAX_DELAY_MS, delayMs);
|
|
423
|
+
}
|
|
424
|
+
async function sleepForFilesystemRetry(attempt) {
|
|
425
|
+
await new Promise((resolve) => setTimeout(resolve, getFilesystemRetryDelayMs(attempt)));
|
|
426
|
+
}
|
|
415
427
|
function isGenericUnexpectedEofLikeError(error) {
|
|
416
428
|
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
417
429
|
return message.includes('unexpected eof') || message.includes('unexpected end');
|
|
@@ -1417,6 +1429,32 @@ function parseDialogGenerationRunState(value) {
|
|
|
1417
1429
|
}
|
|
1418
1430
|
return null;
|
|
1419
1431
|
}
|
|
1432
|
+
function parseDialogBackendDriveStallState(value) {
|
|
1433
|
+
if (!isRecord(value))
|
|
1434
|
+
return null;
|
|
1435
|
+
if (value.kind !== 'backend_drive_error')
|
|
1436
|
+
return null;
|
|
1437
|
+
if (typeof value.recordId !== 'string' || value.recordId.trim() === '')
|
|
1438
|
+
return null;
|
|
1439
|
+
if (typeof value.durableWorkFingerprint !== 'string' ||
|
|
1440
|
+
value.durableWorkFingerprint.trim() === '') {
|
|
1441
|
+
return null;
|
|
1442
|
+
}
|
|
1443
|
+
if (typeof value.failedAt !== 'string' || value.failedAt.trim() === '')
|
|
1444
|
+
return null;
|
|
1445
|
+
if (value.errorName !== undefined && typeof value.errorName !== 'string')
|
|
1446
|
+
return null;
|
|
1447
|
+
if (typeof value.errorMessage !== 'string' || value.errorMessage.trim() === '')
|
|
1448
|
+
return null;
|
|
1449
|
+
return {
|
|
1450
|
+
kind: 'backend_drive_error',
|
|
1451
|
+
recordId: value.recordId,
|
|
1452
|
+
durableWorkFingerprint: value.durableWorkFingerprint,
|
|
1453
|
+
failedAt: value.failedAt,
|
|
1454
|
+
...(value.errorName === undefined ? {} : { errorName: value.errorName }),
|
|
1455
|
+
errorMessage: value.errorMessage,
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1420
1458
|
function parseStringArrayField(value) {
|
|
1421
1459
|
if (!Array.isArray(value))
|
|
1422
1460
|
return null;
|
|
@@ -1461,6 +1499,35 @@ function parseDialogFollowupReason(value) {
|
|
|
1461
1499
|
return null;
|
|
1462
1500
|
}
|
|
1463
1501
|
}
|
|
1502
|
+
function parseDialogBusinessContinuation(value) {
|
|
1503
|
+
if (!isRecord(value))
|
|
1504
|
+
return null;
|
|
1505
|
+
switch (value.kind) {
|
|
1506
|
+
case 'none':
|
|
1507
|
+
return { kind: 'none' };
|
|
1508
|
+
case 'inter_dialog_reply': {
|
|
1509
|
+
const tellaskReplyDirective = parseTellaskReplyDirective(value.tellaskReplyDirective);
|
|
1510
|
+
if (tellaskReplyDirective === null)
|
|
1511
|
+
return null;
|
|
1512
|
+
if (value.calleeDialogReplyTarget === undefined) {
|
|
1513
|
+
return {
|
|
1514
|
+
kind: 'inter_dialog_reply',
|
|
1515
|
+
tellaskReplyDirective,
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
const calleeDialogReplyTarget = parseDialogCalleeReplyTarget(value.calleeDialogReplyTarget);
|
|
1519
|
+
if (calleeDialogReplyTarget === null)
|
|
1520
|
+
return null;
|
|
1521
|
+
return {
|
|
1522
|
+
kind: 'inter_dialog_reply',
|
|
1523
|
+
tellaskReplyDirective,
|
|
1524
|
+
calleeDialogReplyTarget,
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
default:
|
|
1528
|
+
return null;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1464
1531
|
function parseDialogNextStepTrigger(value) {
|
|
1465
1532
|
if (!isRecord(value))
|
|
1466
1533
|
return null;
|
|
@@ -1527,6 +1594,13 @@ function parseDialogNextStepTrigger(value) {
|
|
|
1527
1594
|
return null;
|
|
1528
1595
|
reasons.push(reason);
|
|
1529
1596
|
}
|
|
1597
|
+
const continuation = (() => {
|
|
1598
|
+
if (value.continuation === undefined)
|
|
1599
|
+
return undefined;
|
|
1600
|
+
return parseDialogBusinessContinuation(value.continuation);
|
|
1601
|
+
})();
|
|
1602
|
+
if (continuation === null)
|
|
1603
|
+
return null;
|
|
1530
1604
|
return {
|
|
1531
1605
|
...base,
|
|
1532
1606
|
kind: 'followup',
|
|
@@ -1535,6 +1609,7 @@ function parseDialogNextStepTrigger(value) {
|
|
|
1535
1609
|
genseq: (0, storage_1.toCallSiteGenseqNo)(genseq),
|
|
1536
1610
|
},
|
|
1537
1611
|
reasons,
|
|
1612
|
+
...(continuation === undefined ? {} : { continuation }),
|
|
1538
1613
|
};
|
|
1539
1614
|
}
|
|
1540
1615
|
case 'mainline_diligence': {
|
|
@@ -1906,6 +1981,12 @@ function parseDialogLatestFile(value) {
|
|
|
1906
1981
|
: parseDialogGenerationRunState(generationRunStateRaw);
|
|
1907
1982
|
if (generationRunState === null)
|
|
1908
1983
|
return null;
|
|
1984
|
+
const backendDriveStallRaw = value.backendDriveStall;
|
|
1985
|
+
const backendDriveStall = backendDriveStallRaw === undefined
|
|
1986
|
+
? undefined
|
|
1987
|
+
: parseDialogBackendDriveStallState(backendDriveStallRaw);
|
|
1988
|
+
if (backendDriveStall === null)
|
|
1989
|
+
return null;
|
|
1909
1990
|
const nextStepRaw = value.nextStep;
|
|
1910
1991
|
if (nextStepRaw === undefined)
|
|
1911
1992
|
return null;
|
|
@@ -2062,6 +2143,7 @@ function parseDialogLatestFile(value) {
|
|
|
2062
2143
|
displayState,
|
|
2063
2144
|
executionMarker,
|
|
2064
2145
|
generationRunState,
|
|
2146
|
+
backendDriveStall,
|
|
2065
2147
|
nextStep,
|
|
2066
2148
|
userWait,
|
|
2067
2149
|
replyDelivery,
|
|
@@ -2675,6 +2757,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
|
|
|
2675
2757
|
generating: true,
|
|
2676
2758
|
displayState: { kind: 'proceeding' },
|
|
2677
2759
|
executionMarker: undefined,
|
|
2760
|
+
backendDriveStall: undefined,
|
|
2678
2761
|
nextStep,
|
|
2679
2762
|
generationRunState: {
|
|
2680
2763
|
kind: 'open',
|
|
@@ -2697,6 +2780,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
|
|
|
2697
2780
|
catch (err) {
|
|
2698
2781
|
log_1.log.warn('Failed to persist gen_start event', err);
|
|
2699
2782
|
}
|
|
2783
|
+
return acceptedTriggers;
|
|
2700
2784
|
}
|
|
2701
2785
|
getResultArrivalBatchIdsFromAcceptedTriggers(triggerIds) {
|
|
2702
2786
|
const batchIds = [];
|
|
@@ -4731,6 +4815,14 @@ class DialogPersistence {
|
|
|
4731
4815
|
this.driveWatchMutexes.set(key, created);
|
|
4732
4816
|
return created;
|
|
4733
4817
|
}
|
|
4818
|
+
static getBackendDriveStallMutex(key) {
|
|
4819
|
+
const existing = this.backendDriveStallMutexes.get(key);
|
|
4820
|
+
if (existing)
|
|
4821
|
+
return existing;
|
|
4822
|
+
const created = new async_fifo_mutex_1.AsyncFifoMutex();
|
|
4823
|
+
this.backendDriveStallMutexes.set(key, created);
|
|
4824
|
+
return created;
|
|
4825
|
+
}
|
|
4734
4826
|
static getLatestWriteBackKey(dialogId, status) {
|
|
4735
4827
|
// Include dialogs root dir to avoid cross-test/process.cwd collisions.
|
|
4736
4828
|
return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}`;
|
|
@@ -4738,6 +4830,9 @@ class DialogPersistence {
|
|
|
4738
4830
|
static getDriveWatchKey(rootDialogId, status) {
|
|
4739
4831
|
return `${this.getDialogsRootDir()}|${status}|${rootDialogId.rootId}|drive-watch`;
|
|
4740
4832
|
}
|
|
4833
|
+
static getBackendDriveStallKey(dialogId, status) {
|
|
4834
|
+
return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}|backend-drive-stall`;
|
|
4835
|
+
}
|
|
4741
4836
|
static getQ4HWriteBackKey(dialogId, status) {
|
|
4742
4837
|
// Include dialogs root dir to avoid cross-test/process.cwd collisions.
|
|
4743
4838
|
return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}|q4h`;
|
|
@@ -6696,7 +6791,7 @@ class DialogPersistence {
|
|
|
6696
6791
|
const normalized = this.normalizeDriveWatchFile(file);
|
|
6697
6792
|
const filePath = this.getDriveWatchFilePath(rootDialogId, status);
|
|
6698
6793
|
if (normalized.dialogs.length === 0) {
|
|
6699
|
-
await
|
|
6794
|
+
await this.removeWithRetry(filePath);
|
|
6700
6795
|
return;
|
|
6701
6796
|
}
|
|
6702
6797
|
const dialogPath = this.getMainDialogPath(rootDialogId, status);
|
|
@@ -6706,6 +6801,73 @@ class DialogPersistence {
|
|
|
6706
6801
|
await fs.promises.writeFile(tempFile, jsonContent, 'utf-8');
|
|
6707
6802
|
await this.renameWithRetry(tempFile, filePath, 5);
|
|
6708
6803
|
}
|
|
6804
|
+
static getBackendDriveStallJsonlPath(dialogId, status) {
|
|
6805
|
+
return path.join(this.getDialogEventsPath(dialogId, status), 'backend-drive-stalls.jsonl');
|
|
6806
|
+
}
|
|
6807
|
+
static buildBackendDriveDurableWorkFingerprint(latest, watchedDialogIds = []) {
|
|
6808
|
+
if (latest === null) {
|
|
6809
|
+
return JSON.stringify({ latest: null });
|
|
6810
|
+
}
|
|
6811
|
+
const nextStepTriggers = sortNextStepTriggersForConsumption(latest.nextStep.triggers).map((trigger) => ({
|
|
6812
|
+
triggerId: trigger.triggerId,
|
|
6813
|
+
kind: trigger.kind,
|
|
6814
|
+
seq: trigger.seq,
|
|
6815
|
+
createdAt: trigger.createdAt,
|
|
6816
|
+
payload: trigger,
|
|
6817
|
+
}));
|
|
6818
|
+
return JSON.stringify({
|
|
6819
|
+
currentCourse: latest.currentCourse,
|
|
6820
|
+
status: latest.status,
|
|
6821
|
+
generating: latest.generating ?? false,
|
|
6822
|
+
displayState: latest.displayState ?? null,
|
|
6823
|
+
executionMarker: latest.executionMarker ?? null,
|
|
6824
|
+
nextStepTriggers,
|
|
6825
|
+
pendingRuntimePromptMsgId: latest.pendingRuntimePrompt?.msgId ?? null,
|
|
6826
|
+
generationRunState: latest.generationRunState ?? null,
|
|
6827
|
+
replyDelivery: latest.replyDelivery ?? null,
|
|
6828
|
+
sideDialogFinalResponse: latest.sideDialogFinalResponse ?? null,
|
|
6829
|
+
latestAssignmentAnchor: latest.latestAssignmentAnchor ?? null,
|
|
6830
|
+
userWait: latest.userWait ?? null,
|
|
6831
|
+
watchedDialogIds: watchedDialogIds.map((dialogId) => dialogId.valueOf()).sort(),
|
|
6832
|
+
});
|
|
6833
|
+
}
|
|
6834
|
+
static async appendBackendDriveStallRecord(dialogId, write, status = 'running') {
|
|
6835
|
+
const recordedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
|
|
6836
|
+
const record = {
|
|
6837
|
+
...write,
|
|
6838
|
+
recordId: (0, node_crypto_1.randomUUID)(),
|
|
6839
|
+
recordedAt,
|
|
6840
|
+
};
|
|
6841
|
+
const key = this.getBackendDriveStallKey(dialogId, status);
|
|
6842
|
+
const mutex = this.getBackendDriveStallMutex(key);
|
|
6843
|
+
const release = await mutex.acquire();
|
|
6844
|
+
try {
|
|
6845
|
+
const filePath = this.getBackendDriveStallJsonlPath(dialogId, status);
|
|
6846
|
+
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
6847
|
+
await fs.promises.appendFile(filePath, `${JSON.stringify(record)}\n`, 'utf-8');
|
|
6848
|
+
await this.mutateDialogLatest(dialogId, () => ({
|
|
6849
|
+
kind: 'patch',
|
|
6850
|
+
patch: {
|
|
6851
|
+
backendDriveStall: {
|
|
6852
|
+
kind: 'backend_drive_error',
|
|
6853
|
+
recordId: record.recordId,
|
|
6854
|
+
durableWorkFingerprint: write.durableWorkFingerprint,
|
|
6855
|
+
failedAt: recordedAt,
|
|
6856
|
+
...(write.error.name === undefined ? {} : { errorName: write.error.name }),
|
|
6857
|
+
errorMessage: write.error.message,
|
|
6858
|
+
},
|
|
6859
|
+
},
|
|
6860
|
+
}), status);
|
|
6861
|
+
return record;
|
|
6862
|
+
}
|
|
6863
|
+
finally {
|
|
6864
|
+
release();
|
|
6865
|
+
const current = this.backendDriveStallMutexes.get(key);
|
|
6866
|
+
if (current === mutex && !mutex.isLocked()) {
|
|
6867
|
+
this.backendDriveStallMutexes.delete(key);
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6709
6871
|
static async mutateDriveWatch(rootDialogId, mutator, status = 'running') {
|
|
6710
6872
|
const key = this.getDriveWatchKey(rootDialogId, status);
|
|
6711
6873
|
const mutex = this.getDriveWatchMutex(key);
|
|
@@ -6720,6 +6882,24 @@ class DialogPersistence {
|
|
|
6720
6882
|
release();
|
|
6721
6883
|
}
|
|
6722
6884
|
}
|
|
6885
|
+
static async removeWithRetry(filePath, maxRetries = 5) {
|
|
6886
|
+
let lastError;
|
|
6887
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
6888
|
+
try {
|
|
6889
|
+
await fs.promises.rm(filePath, { force: true });
|
|
6890
|
+
return;
|
|
6891
|
+
}
|
|
6892
|
+
catch (error) {
|
|
6893
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
6894
|
+
const errorCode = getErrorCode(error);
|
|
6895
|
+
if (!RETRYABLE_FILESYSTEM_ERROR_CODES.has(errorCode ?? '') || attempt === maxRetries) {
|
|
6896
|
+
throw error;
|
|
6897
|
+
}
|
|
6898
|
+
await sleepForFilesystemRetry(attempt);
|
|
6899
|
+
}
|
|
6900
|
+
}
|
|
6901
|
+
throw lastError;
|
|
6902
|
+
}
|
|
6723
6903
|
static async loadDriveWatchedDialogIds(rootDialogId, status = 'running') {
|
|
6724
6904
|
try {
|
|
6725
6905
|
const file = await this.loadDriveWatchFromDisk(rootDialogId, status);
|
|
@@ -7753,11 +7933,12 @@ class DialogPersistence {
|
|
|
7753
7933
|
throw error;
|
|
7754
7934
|
}
|
|
7755
7935
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
7756
|
-
|
|
7936
|
+
const errorCode = getErrorCode(error);
|
|
7937
|
+
if (!RETRYABLE_FILESYSTEM_ERROR_CODES.has(errorCode ?? '') || attempt === maxRetries) {
|
|
7757
7938
|
throw error;
|
|
7758
7939
|
}
|
|
7759
|
-
// Exponential backoff for
|
|
7760
|
-
await
|
|
7940
|
+
// Exponential backoff for transient filesystem contention.
|
|
7941
|
+
await sleepForFilesystemRetry(attempt);
|
|
7761
7942
|
}
|
|
7762
7943
|
}
|
|
7763
7944
|
throw lastError;
|
|
@@ -8603,5 +8784,6 @@ DialogPersistence.q4hWriteBackMutexes = new Map();
|
|
|
8603
8784
|
DialogPersistence.q4hWriteBack = new Map();
|
|
8604
8785
|
DialogPersistence.activeCalleesMutexes = new Map();
|
|
8605
8786
|
DialogPersistence.driveWatchMutexes = new Map();
|
|
8787
|
+
DialogPersistence.backendDriveStallMutexes = new Map();
|
|
8606
8788
|
DialogPersistence.courseAppendMutexes = new Map();
|
|
8607
8789
|
DialogPersistence.mainDialogWriteBackCancelGenerations = new Map();
|