dominds 1.24.3 → 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/llm/gen/mock.js +2 -0
- package/dist/llm/kernel-driver/drive.js +74 -16
- package/dist/llm/kernel-driver/flow.js +88 -26
- package/dist/llm/kernel-driver/loop.js +85 -46
- package/dist/llm/kernel-driver/types.d.ts +10 -2
- package/dist/persistence.d.ts +29 -1
- package/dist/persistence.js +153 -0
- 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
|
@@ -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;
|
|
@@ -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';
|
|
@@ -112,6 +119,7 @@ export type KernelDriverCoreResult = {
|
|
|
112
119
|
lastAssistantThinkingGenseq: number | null;
|
|
113
120
|
lastFunctionCallGenseq: number | null;
|
|
114
121
|
lastAssistantReplyTarget?: KernelDriverCalleeReplyTarget;
|
|
122
|
+
lastBusinessContinuation: DialogBusinessContinuation;
|
|
115
123
|
fbrConclusion?: {
|
|
116
124
|
responseText: string;
|
|
117
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,6 +502,9 @@ 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;
|
|
481
509
|
private static removeWithRetry;
|
|
482
510
|
static loadDriveWatchedDialogIds(rootDialogId: DialogID, status?: DialogStatusKind): Promise<readonly DialogID[]>;
|
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,
|
|
@@ -1427,6 +1429,32 @@ function parseDialogGenerationRunState(value) {
|
|
|
1427
1429
|
}
|
|
1428
1430
|
return null;
|
|
1429
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
|
+
}
|
|
1430
1458
|
function parseStringArrayField(value) {
|
|
1431
1459
|
if (!Array.isArray(value))
|
|
1432
1460
|
return null;
|
|
@@ -1471,6 +1499,35 @@ function parseDialogFollowupReason(value) {
|
|
|
1471
1499
|
return null;
|
|
1472
1500
|
}
|
|
1473
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
|
+
}
|
|
1474
1531
|
function parseDialogNextStepTrigger(value) {
|
|
1475
1532
|
if (!isRecord(value))
|
|
1476
1533
|
return null;
|
|
@@ -1537,6 +1594,13 @@ function parseDialogNextStepTrigger(value) {
|
|
|
1537
1594
|
return null;
|
|
1538
1595
|
reasons.push(reason);
|
|
1539
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;
|
|
1540
1604
|
return {
|
|
1541
1605
|
...base,
|
|
1542
1606
|
kind: 'followup',
|
|
@@ -1545,6 +1609,7 @@ function parseDialogNextStepTrigger(value) {
|
|
|
1545
1609
|
genseq: (0, storage_1.toCallSiteGenseqNo)(genseq),
|
|
1546
1610
|
},
|
|
1547
1611
|
reasons,
|
|
1612
|
+
...(continuation === undefined ? {} : { continuation }),
|
|
1548
1613
|
};
|
|
1549
1614
|
}
|
|
1550
1615
|
case 'mainline_diligence': {
|
|
@@ -1916,6 +1981,12 @@ function parseDialogLatestFile(value) {
|
|
|
1916
1981
|
: parseDialogGenerationRunState(generationRunStateRaw);
|
|
1917
1982
|
if (generationRunState === null)
|
|
1918
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;
|
|
1919
1990
|
const nextStepRaw = value.nextStep;
|
|
1920
1991
|
if (nextStepRaw === undefined)
|
|
1921
1992
|
return null;
|
|
@@ -2072,6 +2143,7 @@ function parseDialogLatestFile(value) {
|
|
|
2072
2143
|
displayState,
|
|
2073
2144
|
executionMarker,
|
|
2074
2145
|
generationRunState,
|
|
2146
|
+
backendDriveStall,
|
|
2075
2147
|
nextStep,
|
|
2076
2148
|
userWait,
|
|
2077
2149
|
replyDelivery,
|
|
@@ -2685,6 +2757,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
|
|
|
2685
2757
|
generating: true,
|
|
2686
2758
|
displayState: { kind: 'proceeding' },
|
|
2687
2759
|
executionMarker: undefined,
|
|
2760
|
+
backendDriveStall: undefined,
|
|
2688
2761
|
nextStep,
|
|
2689
2762
|
generationRunState: {
|
|
2690
2763
|
kind: 'open',
|
|
@@ -2707,6 +2780,7 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
|
|
|
2707
2780
|
catch (err) {
|
|
2708
2781
|
log_1.log.warn('Failed to persist gen_start event', err);
|
|
2709
2782
|
}
|
|
2783
|
+
return acceptedTriggers;
|
|
2710
2784
|
}
|
|
2711
2785
|
getResultArrivalBatchIdsFromAcceptedTriggers(triggerIds) {
|
|
2712
2786
|
const batchIds = [];
|
|
@@ -4741,6 +4815,14 @@ class DialogPersistence {
|
|
|
4741
4815
|
this.driveWatchMutexes.set(key, created);
|
|
4742
4816
|
return created;
|
|
4743
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
|
+
}
|
|
4744
4826
|
static getLatestWriteBackKey(dialogId, status) {
|
|
4745
4827
|
// Include dialogs root dir to avoid cross-test/process.cwd collisions.
|
|
4746
4828
|
return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}`;
|
|
@@ -4748,6 +4830,9 @@ class DialogPersistence {
|
|
|
4748
4830
|
static getDriveWatchKey(rootDialogId, status) {
|
|
4749
4831
|
return `${this.getDialogsRootDir()}|${status}|${rootDialogId.rootId}|drive-watch`;
|
|
4750
4832
|
}
|
|
4833
|
+
static getBackendDriveStallKey(dialogId, status) {
|
|
4834
|
+
return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}|backend-drive-stall`;
|
|
4835
|
+
}
|
|
4751
4836
|
static getQ4HWriteBackKey(dialogId, status) {
|
|
4752
4837
|
// Include dialogs root dir to avoid cross-test/process.cwd collisions.
|
|
4753
4838
|
return `${this.getDialogsRootDir()}|${status}|${dialogId.valueOf()}|q4h`;
|
|
@@ -6716,6 +6801,73 @@ class DialogPersistence {
|
|
|
6716
6801
|
await fs.promises.writeFile(tempFile, jsonContent, 'utf-8');
|
|
6717
6802
|
await this.renameWithRetry(tempFile, filePath, 5);
|
|
6718
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
|
+
}
|
|
6719
6871
|
static async mutateDriveWatch(rootDialogId, mutator, status = 'running') {
|
|
6720
6872
|
const key = this.getDriveWatchKey(rootDialogId, status);
|
|
6721
6873
|
const mutex = this.getDriveWatchMutex(key);
|
|
@@ -8632,5 +8784,6 @@ DialogPersistence.q4hWriteBackMutexes = new Map();
|
|
|
8632
8784
|
DialogPersistence.q4hWriteBack = new Map();
|
|
8633
8785
|
DialogPersistence.activeCalleesMutexes = new Map();
|
|
8634
8786
|
DialogPersistence.driveWatchMutexes = new Map();
|
|
8787
|
+
DialogPersistence.backendDriveStallMutexes = new Map();
|
|
8635
8788
|
DialogPersistence.courseAppendMutexes = new Map();
|
|
8636
8789
|
DialogPersistence.mainDialogWriteBackCancelGenerations = new Map();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type LanguageCode } from '@longrun-ai/kernel/types/language';
|
|
2
2
|
export declare function formatSystemNoticePrefix(language: LanguageCode): string;
|
|
3
|
+
export declare function isAgentFacingCriticalUserInterjectionRemediationGuideContent(content: string): boolean;
|
|
3
4
|
export declare function formatAutoMaintainedReminderManualMirrorBan(language: LanguageCode): string;
|
|
4
5
|
export declare function formatCurrentUserLanguagePreference(workingLanguage: LanguageCode, uiLanguage: LanguageCode): string;
|
|
5
6
|
export declare function formatUserLanguagePreferenceChangedNotice(workingLanguage: LanguageCode, previousUiLanguage: LanguageCode, nextUiLanguage: LanguageCode): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.formatSystemNoticePrefix = formatSystemNoticePrefix;
|
|
4
|
+
exports.isAgentFacingCriticalUserInterjectionRemediationGuideContent = isAgentFacingCriticalUserInterjectionRemediationGuideContent;
|
|
4
5
|
exports.formatAutoMaintainedReminderManualMirrorBan = formatAutoMaintainedReminderManualMirrorBan;
|
|
5
6
|
exports.formatCurrentUserLanguagePreference = formatCurrentUserLanguagePreference;
|
|
6
7
|
exports.formatUserLanguagePreferenceChangedNotice = formatUserLanguagePreferenceChangedNotice;
|
|
@@ -23,6 +24,10 @@ const language_1 = require("@longrun-ai/kernel/types/language");
|
|
|
23
24
|
function formatSystemNoticePrefix(language) {
|
|
24
25
|
return language === 'zh' ? '【系统提示】' : '[System notice]';
|
|
25
26
|
}
|
|
27
|
+
function isAgentFacingCriticalUserInterjectionRemediationGuideContent(content) {
|
|
28
|
+
return (content.startsWith(`${formatSystemNoticePrefix('zh')} 上下文状态:🔴 告急;收到用户插话`) ||
|
|
29
|
+
content.startsWith(`${formatSystemNoticePrefix('en')} Context state: 🔴 critical; user interjection received`));
|
|
30
|
+
}
|
|
26
31
|
function formatAutoMaintainedReminderManualMirrorBan(language) {
|
|
27
32
|
return language === 'zh'
|
|
28
33
|
? '这条状态由系统维护;禁止把它抄进、改写进、或同步维护到你手工创建的提醒项里。'
|
|
@@ -570,7 +575,7 @@ function formatAgentFacingCriticalUserInterjectionRemediationGuide(language, arg
|
|
|
570
575
|
return [
|
|
571
576
|
`${formatSystemNoticePrefix(language)} 上下文状态:🔴 告急;收到用户插话`,
|
|
572
577
|
'',
|
|
573
|
-
'
|
|
578
|
+
'本轮刚收到的用户消息是真实用户插话,不是普通运行时处置提示;必须把它当作有效用户轮次处理,让用户看到你已经接住了这次插话。',
|
|
574
579
|
'',
|
|
575
580
|
`这次用户轮次已计入告急处置倒计数。系统最多再提醒你 ${args.promptsRemainingAfterThis} 次,之后将自动清理头脑开启新一程对话。`,
|
|
576
581
|
'',
|
|
@@ -584,7 +589,7 @@ function formatAgentFacingCriticalUserInterjectionRemediationGuide(language, arg
|
|
|
584
589
|
return [
|
|
585
590
|
`${formatSystemNoticePrefix(language)} Context state: 🔴 critical; user interjection received`,
|
|
586
591
|
'',
|
|
587
|
-
'The
|
|
592
|
+
'The user message just received in this turn is a real user interjection, not an ordinary runtime remediation notice. Treat it as an effective user turn and make the system reaction visible to the user.',
|
|
588
593
|
'',
|
|
589
594
|
`This user turn has been counted toward critical remediation. System will remind you ${args.promptsRemainingAfterThis} more time(s), then automatically clear mind.`,
|
|
590
595
|
'',
|
|
@@ -2,12 +2,25 @@ import type { DomindsRuntimeMode, WebSocketMessage } from '@longrun-ai/kernel/ty
|
|
|
2
2
|
import { type LanguageCode } from '@longrun-ai/kernel/types/language';
|
|
3
3
|
import type { Server } from 'http';
|
|
4
4
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
5
|
+
import { Dialog } from '../dialog';
|
|
5
6
|
import type { AuthConfig } from './auth';
|
|
7
|
+
export declare function resolveUserMessageLanguageCodeForTest(args: {
|
|
8
|
+
ws: WebSocket;
|
|
9
|
+
raw: unknown;
|
|
10
|
+
fallbackDialog?: Dialog;
|
|
11
|
+
}): LanguageCode;
|
|
6
12
|
export declare function shouldQueueUserSupplementAtGenerationBoundary(args: {
|
|
7
13
|
latestGenerating: boolean;
|
|
8
14
|
inMemoryGenerating: boolean;
|
|
9
15
|
isLocked: boolean;
|
|
10
16
|
}): boolean;
|
|
17
|
+
type UserMessageIngressPrompt = {
|
|
18
|
+
content: string;
|
|
19
|
+
msgId: string;
|
|
20
|
+
grammar: 'markdown';
|
|
21
|
+
userLanguageCode?: LanguageCode;
|
|
22
|
+
};
|
|
23
|
+
export declare function wrapCriticalUserInterjectionPromptAtIngress(dialog: Dialog, prompt: UserMessageIngressPrompt): UserMessageIngressPrompt;
|
|
11
24
|
/**
|
|
12
25
|
* Handle incoming WebSocket messages
|
|
13
26
|
*/
|
|
@@ -20,3 +33,4 @@ export declare function setupWebSocketServer(httpServer: Server, clients: Set<We
|
|
|
20
33
|
* Clean up all event channels and subscriptions
|
|
21
34
|
*/
|
|
22
35
|
export declare function cleanupEventSystems(): void;
|
|
36
|
+
export {};
|