dominds 1.25.15 → 1.25.16

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.
@@ -302,28 +302,28 @@ function getUserOriginPromptMsgId(prompt) {
302
302
  ? prompt.msgId
303
303
  : undefined;
304
304
  }
305
- function samePendingUserInterjectionMsg(pending, msgId) {
306
- return pending.msgId === msgId;
305
+ function samePendingUserInterjectionCoordinate(left, right) {
306
+ return left.msgId === right.msgId && left.course === right.course && left.genseq === right.genseq;
307
307
  }
308
308
  async function maybeResolveAnsweredUserInterjection(args) {
309
309
  if (args.userPromptMsgId === undefined ||
310
310
  args.assistantSayingContent === null ||
311
311
  args.assistantSayingContent.trim() === '' ||
312
312
  args.assistantSayingGenseq === null) {
313
- return;
313
+ return undefined;
314
314
  }
315
315
  for (const rawGenseq of args.functionCallGenseqs) {
316
316
  if (!Number.isFinite(rawGenseq) || rawGenseq <= 0) {
317
317
  continue;
318
318
  }
319
319
  if (args.assistantSayingGenseq <= Math.floor(rawGenseq)) {
320
- return;
320
+ return undefined;
321
321
  }
322
322
  }
323
323
  const latest = await persistence_1.DialogPersistence.loadDialogLatest(args.dlg.id, args.dlg.status);
324
324
  const pending = latest?.pendingUserInterjectionReply;
325
- if (pending === undefined || !samePendingUserInterjectionMsg(pending, args.userPromptMsgId)) {
326
- return;
325
+ if (pending === undefined || pending.msgId !== args.userPromptMsgId) {
326
+ return undefined;
327
327
  }
328
328
  const course = args.dlg.activeGenCourseOrUndefined ?? args.dlg.currentCourse;
329
329
  const answerIdSource = [
@@ -369,14 +369,18 @@ async function maybeResolveAnsweredUserInterjection(args) {
369
369
  const userPromptMsgId = args.userPromptMsgId;
370
370
  if (previousPending === undefined ||
371
371
  userPromptMsgId === undefined ||
372
- !samePendingUserInterjectionMsg(previousPending, userPromptMsgId)) {
372
+ userPromptMsgId !== pending.msgId ||
373
+ !samePendingUserInterjectionCoordinate(previousPending, pending)) {
373
374
  return { kind: 'noop' };
374
375
  }
376
+ const next = { ...previous };
377
+ delete next.pendingUserInterjectionReply;
375
378
  return {
376
- kind: 'patch',
377
- patch: { pendingUserInterjectionReply: undefined },
379
+ kind: 'replace',
380
+ next,
378
381
  };
379
382
  }, args.dlg.status);
383
+ return answer;
380
384
  }
381
385
  async function persistAndEmitRuntimeGuide(dlg, content) {
382
386
  await dlg.addChatMessages({
@@ -2207,6 +2211,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2207
2211
  let lastFunctionCallGenseq = null;
2208
2212
  let lastAssistantReplyTarget;
2209
2213
  let lastBusinessContinuation = { kind: 'none' };
2214
+ let answeredUserInterjection;
2215
+ let currentPromptIsUserInterjection = false;
2216
+ let currentUserInterjectionReply;
2210
2217
  let fbrConclusion;
2211
2218
  let pubRemindersVer = dlg.remindersVer;
2212
2219
  let lastToolRoundStopDiagnostics;
@@ -2455,6 +2462,8 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2455
2462
  }
2456
2463
  }
2457
2464
  lastBusinessContinuation = currentBusinessContinuation;
2465
+ currentPromptIsUserInterjection = false;
2466
+ currentUserInterjectionReply = undefined;
2458
2467
  let generationBodyError;
2459
2468
  let immediateFollowupTriggerExpectation;
2460
2469
  const q4hAnswerCallId = normalizeQ4HAnswerCallId(currentPrompt?.q4hAnswerCallId);
@@ -2476,9 +2485,10 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2476
2485
  language: promptLanguage,
2477
2486
  });
2478
2487
  const deferredReplyReassertionDirective = replyGuidance.deferredReplyReassertionDirective;
2479
- const currentPromptIsUserInterjection = currentPrompt.origin === 'user' &&
2480
- replyGuidance.suppressInterDialogReplyGuidance &&
2481
- deferredReplyReassertionDirective !== undefined;
2488
+ currentPromptIsUserInterjection =
2489
+ currentPrompt.origin === 'user' &&
2490
+ replyGuidance.suppressInterDialogReplyGuidance &&
2491
+ deferredReplyReassertionDirective !== undefined;
2482
2492
  if (currentPromptIsUserInterjection) {
2483
2493
  // WARNING:
2484
2494
  // User interjection suppression is a reversible state transition, not a one-shot
@@ -2490,13 +2500,29 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2490
2500
  // Legacy blocked-Continue paths may also re-enter here. A repeated interjection MUST
2491
2501
  // re-arm the deferred state and re-materialize the suppression guide, even when the
2492
2502
  // underlying reply directive itself did not change.
2503
+ const deferredDirective = deferredReplyReassertionDirective;
2504
+ if (deferredDirective === undefined) {
2505
+ throw new Error(`kernel-driver user interjection invariant violation: missing deferred reply directive for dialog=${dlg.id.valueOf()} msgId=${currentPrompt.msgId}`);
2506
+ }
2493
2507
  const existingDeferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dlg.id, dlg.status);
2508
+ currentUserInterjectionReply = {
2509
+ msgId: currentPrompt.msgId,
2510
+ course: (0, storage_1.toDialogCourseNumber)(dlg.activeGenCourseOrUndefined ?? dlg.currentCourse),
2511
+ genseq: (0, storage_1.toCallSiteGenseqNo)(dlg.activeGenSeq),
2512
+ };
2494
2513
  const nextDeferredReplyReassertion = {
2495
2514
  reason: 'user_interjection_with_parked_original_task',
2496
- directive: deferredReplyReassertionDirective,
2515
+ directive: deferredDirective,
2516
+ userInterjection: currentUserInterjectionReply,
2497
2517
  };
2498
2518
  const mustRearmDeferredReplyReassertion = existingDeferredReplyReassertion === undefined ||
2499
2519
  existingDeferredReplyReassertion.resumeGuideSurfaced === true ||
2520
+ existingDeferredReplyReassertion.userInterjection.msgId !==
2521
+ nextDeferredReplyReassertion.userInterjection.msgId ||
2522
+ existingDeferredReplyReassertion.userInterjection.course !==
2523
+ nextDeferredReplyReassertion.userInterjection.course ||
2524
+ existingDeferredReplyReassertion.userInterjection.genseq !==
2525
+ nextDeferredReplyReassertion.userInterjection.genseq ||
2500
2526
  !hasSameReplyDirective(existingDeferredReplyReassertion.directive, nextDeferredReplyReassertion.directive);
2501
2527
  if (mustRearmDeferredReplyReassertion) {
2502
2528
  await persistence_1.DialogPersistence.setDeferredReplyReassertion(dlg.id, nextDeferredReplyReassertion, dlg.status);
@@ -2528,13 +2554,15 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2528
2554
  throw new Error(`kernel-driver reply guidance invariant violation: missing prompt content for dialog=${dlg.id.valueOf()} msgId=${currentPrompt.msgId}`);
2529
2555
  }
2530
2556
  if (currentPromptIsUserInterjection) {
2557
+ if (currentUserInterjectionReply === undefined) {
2558
+ throw new Error(`kernel-driver user interjection invariant violation: missing pending reply coordinate for dialog=${dlg.id.valueOf()} msgId=${currentPrompt.msgId}`);
2559
+ }
2560
+ const pendingUserInterjectionReply = currentUserInterjectionReply;
2531
2561
  await persistence_1.DialogPersistence.mutateDialogLatest(dlg.id, () => ({
2532
2562
  kind: 'patch',
2533
2563
  patch: {
2534
2564
  pendingUserInterjectionReply: {
2535
- msgId: currentPrompt.msgId,
2536
- course: (0, storage_1.toDialogCourseNumber)(dlg.activeGenCourseOrUndefined ?? dlg.currentCourse),
2537
- genseq: (0, storage_1.toCallSiteGenseqNo)(dlg.activeGenSeq),
2565
+ ...pendingUserInterjectionReply,
2538
2566
  },
2539
2567
  },
2540
2568
  }), dlg.status);
@@ -3217,6 +3245,31 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3217
3245
  lastFunctionCallGenseq = callGenseq;
3218
3246
  }
3219
3247
  }
3248
+ const userInterjectionMsgIdForVisibleAnswer = currentPrompt?.origin === 'user' && !isQ4HAnswerPrompt
3249
+ ? currentPrompt.msgId
3250
+ : currentGenerationBelongsToUserToolChain
3251
+ ? currentUserPromptMsgId
3252
+ : undefined;
3253
+ if (userInterjectionMsgIdForVisibleAnswer !== undefined) {
3254
+ const streamedCurrentRoundSayingContent = batchOutputs.length === 0 &&
3255
+ lastAssistantSayingGenseq !== previousAssistantSayingGenseq
3256
+ ? lastAssistantSayingContent
3257
+ : null;
3258
+ const streamedCurrentRoundSayingGenseq = batchOutputs.length === 0 &&
3259
+ lastAssistantSayingGenseq !== previousAssistantSayingGenseq
3260
+ ? lastAssistantSayingGenseq
3261
+ : null;
3262
+ const answer = await maybeResolveAnsweredUserInterjection({
3263
+ dlg,
3264
+ userPromptMsgId: userInterjectionMsgIdForVisibleAnswer,
3265
+ assistantSayingContent: currentRoundAssistantSayingContent ?? streamedCurrentRoundSayingContent,
3266
+ assistantSayingGenseq: currentRoundAssistantSayingGenseq ?? streamedCurrentRoundSayingGenseq,
3267
+ functionCallGenseqs: currentRoundFunctionCallGenseqs,
3268
+ });
3269
+ if (answer !== undefined) {
3270
+ answeredUserInterjection = answer;
3271
+ }
3272
+ }
3220
3273
  const routed = await executeFunctionRound({
3221
3274
  dlg,
3222
3275
  agent,
@@ -3234,21 +3287,6 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3234
3287
  newMsgs.push(...routed.pairedMessages);
3235
3288
  }
3236
3289
  await dlg.addChatMessages(...newMsgs);
3237
- const streamedCurrentRoundSayingContent = batchOutputs.length === 0 && lastAssistantSayingGenseq !== previousAssistantSayingGenseq
3238
- ? lastAssistantSayingContent
3239
- : null;
3240
- const streamedCurrentRoundSayingGenseq = batchOutputs.length === 0 && lastAssistantSayingGenseq !== previousAssistantSayingGenseq
3241
- ? lastAssistantSayingGenseq
3242
- : null;
3243
- if (currentPrompt?.origin === 'user' && !isQ4HAnswerPrompt) {
3244
- await maybeResolveAnsweredUserInterjection({
3245
- dlg,
3246
- userPromptMsgId: currentPrompt.msgId,
3247
- assistantSayingContent: currentRoundAssistantSayingContent ?? streamedCurrentRoundSayingContent,
3248
- assistantSayingGenseq: currentRoundAssistantSayingGenseq ?? streamedCurrentRoundSayingGenseq,
3249
- functionCallGenseqs: currentRoundFunctionCallGenseqs,
3250
- });
3251
- }
3252
3290
  const persistedFbrState = await loadDialogFbrState(dlg);
3253
3291
  if (persistedFbrState) {
3254
3292
  if (persistedFbrState.phase === 'finalization') {
@@ -3674,6 +3712,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3674
3712
  lastFunctionCallGenseq,
3675
3713
  lastAssistantReplyTarget,
3676
3714
  lastBusinessContinuation,
3715
+ answeredUserInterjection,
3677
3716
  fbrConclusion,
3678
3717
  };
3679
3718
  }
@@ -25,6 +25,79 @@ const idle_reminder_wake_1 = require("./idle-reminder-wake");
25
25
  const reply_guidance_1 = require("./reply-guidance");
26
26
  const sideDialog_1 = require("./sideDialog");
27
27
  const tellask_special_1 = require("./tellask-special");
28
+ async function confirmAnsweredUserInterjectionCredential(args) {
29
+ const credential = args.credential;
30
+ if (credential === undefined) {
31
+ return undefined;
32
+ }
33
+ const dialog = args.dialog;
34
+ const latest = await persistence_1.DialogPersistence.loadDialogLatest(dialog.id, dialog.status);
35
+ const answers = await persistence_1.DialogPersistence.loadAnswersToHumanState(dialog.id, dialog.status);
36
+ const persistedCredential = answers.find((answer) => answer.id === credential.id);
37
+ if (persistedCredential === undefined) {
38
+ return undefined;
39
+ }
40
+ if (persistedCredential.userInterjection.msgId !== credential.userInterjection.msgId ||
41
+ persistedCredential.userInterjection.course !== credential.userInterjection.course ||
42
+ persistedCredential.userInterjection.genseq !== credential.userInterjection.genseq ||
43
+ persistedCredential.answerRef.course !== credential.answerRef.course ||
44
+ persistedCredential.answerRef.genseq !== credential.answerRef.genseq) {
45
+ return undefined;
46
+ }
47
+ if (persistedCredential.userInterjection.msgId !== args.userInterjection.msgId ||
48
+ persistedCredential.userInterjection.course !== args.userInterjection.course ||
49
+ persistedCredential.userInterjection.genseq !== args.userInterjection.genseq) {
50
+ return undefined;
51
+ }
52
+ const pending = latest?.pendingUserInterjectionReply;
53
+ if (pending === undefined) {
54
+ return persistedCredential;
55
+ }
56
+ if (pending.msgId !== persistedCredential.userInterjection.msgId ||
57
+ pending.course !== persistedCredential.userInterjection.course ||
58
+ pending.genseq !== persistedCredential.userInterjection.genseq) {
59
+ return undefined;
60
+ }
61
+ await persistence_1.DialogPersistence.mutateDialogLatest(dialog.id, (previous) => {
62
+ const previousPending = previous.pendingUserInterjectionReply;
63
+ if (previousPending === undefined ||
64
+ previousPending.msgId !== persistedCredential.userInterjection.msgId ||
65
+ previousPending.course !== persistedCredential.userInterjection.course ||
66
+ previousPending.genseq !== persistedCredential.userInterjection.genseq) {
67
+ return { kind: 'noop' };
68
+ }
69
+ const next = { ...previous };
70
+ delete next.pendingUserInterjectionReply;
71
+ return { kind: 'replace', next };
72
+ }, dialog.status);
73
+ return persistedCredential;
74
+ }
75
+ async function resolveLatestAnsweredUserInterjectionCredential(dialog, userInterjection) {
76
+ const answers = await persistence_1.DialogPersistence.loadAnswersToHumanState(dialog.id, dialog.status);
77
+ for (let index = answers.length - 1; index >= 0; index -= 1) {
78
+ const answer = answers[index];
79
+ if (answer.userInterjection.msgId === userInterjection.msgId &&
80
+ answer.userInterjection.course === userInterjection.course &&
81
+ answer.userInterjection.genseq === userInterjection.genseq) {
82
+ return answer;
83
+ }
84
+ }
85
+ return undefined;
86
+ }
87
+ async function resolveAnsweredUserInterjectionCredentialForPrompt(args) {
88
+ const userPromptMsgId = args.userPromptMsgId;
89
+ if (userPromptMsgId === undefined) {
90
+ return undefined;
91
+ }
92
+ const answers = await persistence_1.DialogPersistence.loadAnswersToHumanState(args.dialog.id, args.dialog.status);
93
+ for (let index = answers.length - 1; index >= 0; index -= 1) {
94
+ const answer = answers[index];
95
+ if (answer.userInterjection.msgId === userPromptMsgId) {
96
+ return answer;
97
+ }
98
+ }
99
+ return undefined;
100
+ }
28
101
  function buildRuntimeReplyReminderFollowUp(args) {
29
102
  const common = {
30
103
  prompt: args.prompt,
@@ -961,6 +1034,10 @@ async function inspectSideDialogBusinessContinuationDrive(args) {
961
1034
  };
962
1035
  }
963
1036
  async function maybeResolveDeferredReplyReassertionPrompt(dialog) {
1037
+ const latest = await persistence_1.DialogPersistence.loadDialogLatest(dialog.id, dialog.status);
1038
+ if (latest?.pendingUserInterjectionReply !== undefined) {
1039
+ return undefined;
1040
+ }
964
1041
  const deferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dialog.id, dialog.status);
965
1042
  if (!deferredReplyReassertion) {
966
1043
  return undefined;
@@ -980,6 +1057,14 @@ async function maybeResolveDeferredReplyReassertionPrompt(dialog) {
980
1057
  await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
981
1058
  return undefined;
982
1059
  }
1060
+ const answeredUserInterjection = await confirmAnsweredUserInterjectionCredential({
1061
+ dialog,
1062
+ credential: await resolveLatestAnsweredUserInterjectionCredential(dialog, deferredReplyReassertion.userInterjection),
1063
+ userInterjection: deferredReplyReassertion.userInterjection,
1064
+ });
1065
+ if (answeredUserInterjection === undefined) {
1066
+ return undefined;
1067
+ }
983
1068
  await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
984
1069
  const language = (0, work_language_1.getWorkLanguage)();
985
1070
  return {
@@ -987,6 +1072,7 @@ async function maybeResolveDeferredReplyReassertionPrompt(dialog) {
987
1072
  dlg: dialog,
988
1073
  directive: deferredReplyReassertion.directive,
989
1074
  language,
1075
+ answeredUserInterjection,
990
1076
  }),
991
1077
  msgId: (0, id_1.generateShortId)(),
992
1078
  grammar: 'markdown',
@@ -1007,12 +1093,20 @@ async function maybeSurfaceDeferredReplyReassertionGuideForBlockedContinue(dialo
1007
1093
  return;
1008
1094
  }
1009
1095
  const language = (0, work_language_1.getWorkLanguage)();
1096
+ const answeredUserInterjection = await confirmAnsweredUserInterjectionCredential({
1097
+ dialog,
1098
+ credential: await resolveLatestAnsweredUserInterjectionCredential(dialog, deferredReplyReassertion.userInterjection),
1099
+ userInterjection: deferredReplyReassertion.userInterjection,
1100
+ });
1101
+ if (answeredUserInterjection === undefined) {
1102
+ return;
1103
+ }
1010
1104
  const content = await (0, reply_guidance_1.buildReplyObligationReassertionPrompt)({
1011
1105
  dlg: dialog,
1012
1106
  directive: deferredReplyReassertion.directive,
1013
1107
  language,
1108
+ answeredUserInterjection,
1014
1109
  });
1015
- const genseq = dialog.activeGenSeqOrUndefined ?? 1;
1016
1110
  // WARNING:
1017
1111
  // This helper intentionally does three things at once:
1018
1112
  // 1. append the guide into dialog.msgs so an in-memory later resume sees it naturally;
@@ -1026,6 +1120,7 @@ async function maybeSurfaceDeferredReplyReassertionGuideForBlockedContinue(dialo
1026
1120
  await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, {
1027
1121
  reason: 'user_interjection_with_parked_original_task',
1028
1122
  directive: deferredReplyReassertion.directive,
1123
+ userInterjection: deferredReplyReassertion.userInterjection,
1029
1124
  resumeGuideSurfaced: true,
1030
1125
  }, dialog.status);
1031
1126
  }
@@ -1639,15 +1734,6 @@ async function executeDriveRound(args) {
1639
1734
  !replyGuidance.isQ4HAnswerPrompt &&
1640
1735
  replyGuidance.suppressInterDialogReplyGuidance &&
1641
1736
  replyGuidance.deferredReplyReassertionDirective !== undefined;
1642
- if (effectivePrompt?.origin === 'user' &&
1643
- !replyGuidance.isQ4HAnswerPrompt &&
1644
- !replyGuidance.suppressInterDialogReplyGuidance &&
1645
- replyGuidance.deferredReplyReassertionDirective !== undefined) {
1646
- await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, {
1647
- reason: 'user_interjection_with_parked_original_task',
1648
- directive: replyGuidance.deferredReplyReassertionDirective,
1649
- }, dialog.status);
1650
- }
1651
1737
  activeTellaskReplyDirective =
1652
1738
  replyGuidance.activeReplyDirective ?? replyContinuationScope.directive();
1653
1739
  activePromptWasReplyToolReminder = isReplyToolReminderPrompt(effectivePrompt);
@@ -1736,6 +1822,52 @@ async function executeDriveRound(args) {
1736
1822
  }
1737
1823
  let tailError;
1738
1824
  try {
1825
+ if (shouldPauseAfterLocalUserInterjection &&
1826
+ effectivePrompt?.origin === 'user' &&
1827
+ !interruptedBySignal &&
1828
+ followUp === undefined &&
1829
+ driveResult?.lastAssistantSayingContent !== undefined &&
1830
+ driveResult.lastAssistantSayingContent !== null &&
1831
+ driveResult.lastAssistantSayingContent.trim() !== '') {
1832
+ const deferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dialog.id, dialog.status);
1833
+ if (deferredReplyReassertion !== undefined &&
1834
+ deferredReplyReassertion.reason === 'user_interjection_with_parked_original_task') {
1835
+ const answeredUserInterjection = await confirmAnsweredUserInterjectionCredential({
1836
+ dialog,
1837
+ credential: driveResult.answeredUserInterjection ??
1838
+ (await resolveAnsweredUserInterjectionCredentialForPrompt({
1839
+ dialog,
1840
+ userPromptMsgId: effectivePrompt?.msgId,
1841
+ })),
1842
+ userInterjection: deferredReplyReassertion.userInterjection,
1843
+ });
1844
+ if (answeredUserInterjection !== undefined) {
1845
+ const language = (0, work_language_1.getWorkLanguage)();
1846
+ const prompt = await (0, reply_guidance_1.buildReplyObligationReassertionPrompt)({
1847
+ dlg: dialog,
1848
+ directive: deferredReplyReassertion.directive,
1849
+ language,
1850
+ answeredUserInterjection,
1851
+ });
1852
+ followUp = {
1853
+ kind: 'runtime_reply_reminder',
1854
+ prompt,
1855
+ msgId: (0, id_1.generateShortId)(),
1856
+ grammar: 'markdown',
1857
+ origin: 'runtime',
1858
+ userLanguageCode: language,
1859
+ tellaskReplyDirective: deferredReplyReassertion.directive,
1860
+ };
1861
+ await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
1862
+ log_1.log.debug('kernel-driver queued automatic reply-obligation reassertion after user interjection answer', undefined, {
1863
+ dialogId: dialog.id.valueOf(),
1864
+ rootId: dialog.id.rootId,
1865
+ selfId: dialog.id.selfId,
1866
+ targetCallId: deferredReplyReassertion.directive.targetCallId,
1867
+ });
1868
+ }
1869
+ }
1870
+ }
1739
1871
  if (dialog instanceof dialog_1.SideDialog &&
1740
1872
  driveResult &&
1741
1873
  !interruptedBySignal &&
@@ -1978,36 +2110,6 @@ async function executeDriveRound(args) {
1978
2110
  }
1979
2111
  }
1980
2112
  }
1981
- if (shouldPauseAfterLocalUserInterjection &&
1982
- !interruptedBySignal &&
1983
- followUp === undefined &&
1984
- driveResult?.lastAssistantSayingContent !== null) {
1985
- const deferredReplyReassertion = await persistence_1.DialogPersistence.getDeferredReplyReassertion(dialog.id, dialog.status);
1986
- if (deferredReplyReassertion?.reason === 'user_interjection_with_parked_original_task') {
1987
- const language = (0, work_language_1.getWorkLanguage)();
1988
- const prompt = await (0, reply_guidance_1.buildReplyObligationReassertionPrompt)({
1989
- dlg: dialog,
1990
- directive: deferredReplyReassertion.directive,
1991
- language,
1992
- });
1993
- followUp = {
1994
- kind: 'runtime_reply_reminder',
1995
- prompt,
1996
- msgId: (0, id_1.generateShortId)(),
1997
- grammar: 'markdown',
1998
- origin: 'runtime',
1999
- userLanguageCode: language,
2000
- tellaskReplyDirective: deferredReplyReassertion.directive,
2001
- };
2002
- await persistence_1.DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
2003
- log_1.log.debug('kernel-driver queued automatic reply-obligation reassertion after user interjection answer', undefined, {
2004
- dialogId: dialog.id.valueOf(),
2005
- rootId: dialog.id.rootId,
2006
- selfId: dialog.id.selfId,
2007
- targetCallId: deferredReplyReassertion.directive.targetCallId,
2008
- });
2009
- }
2010
- }
2011
2113
  if (followUp) {
2012
2114
  if (followUp.kind === 'runtime_reply_reminder' ||
2013
2115
  followUp.kind === 'runtime_sideDialog_reply_reminder') {
@@ -1,4 +1,4 @@
1
- import type { TellaskReplyDirective } from '@longrun-ai/kernel/types/storage';
1
+ import type { AnswerToHumanItem, TellaskReplyDirective } from '@longrun-ai/kernel/types/storage';
2
2
  import { Dialog } from '../../dialog';
3
3
  import type { KernelDriverPrompt } from './types';
4
4
  export declare function resolveReplyTargetAgentId(args: {
@@ -25,4 +25,5 @@ export declare function buildReplyObligationReassertionPrompt(args: {
25
25
  dlg: Dialog;
26
26
  directive: NonNullable<KernelDriverPrompt['tellaskReplyDirective']>;
27
27
  language: 'zh' | 'en';
28
+ answeredUserInterjection: AnswerToHumanItem;
28
29
  }): Promise<string>;
@@ -110,15 +110,20 @@ function buildCurrentSideDialogAssignmentDirective(dlg) {
110
110
  }
111
111
  }
112
112
  async function resolveFreshCurrentSideDialogAssignmentDirective(args) {
113
- if (!(args.dlg instanceof dialog_1.SideDialog) || args.prompt?.origin !== 'runtime') {
114
- return undefined;
115
- }
116
- const promptDirective = args.prompt.tellaskReplyDirective;
117
- if (!promptDirective) {
113
+ if (!(args.dlg instanceof dialog_1.SideDialog)) {
118
114
  return undefined;
119
115
  }
120
116
  const currentAssignmentDirective = buildCurrentSideDialogAssignmentDirective(args.dlg);
121
- if (!hasSameReplyDirective(promptDirective, currentAssignmentDirective)) {
117
+ if (args.prompt?.origin === 'runtime') {
118
+ const promptDirective = args.prompt.tellaskReplyDirective;
119
+ if (!promptDirective) {
120
+ return undefined;
121
+ }
122
+ if (!hasSameReplyDirective(promptDirective, currentAssignmentDirective)) {
123
+ return undefined;
124
+ }
125
+ }
126
+ else if (args.prompt?.origin !== 'user') {
122
127
  return undefined;
123
128
  }
124
129
  const latest = await persistence_1.DialogPersistence.loadDialogLatest(args.dlg.id, args.dlg.status);
@@ -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, DialogBusinessContinuation } from '@longrun-ai/kernel/types/storage';
3
+ import type { AnswerToHumanItem, 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_business_continuation' | 'kernel_driver_idle_reminder_wake';
@@ -99,6 +99,7 @@ export type KernelDriverCoreResult = {
99
99
  lastFunctionCallGenseq: number | null;
100
100
  lastAssistantReplyTarget?: KernelDriverCalleeReplyTarget;
101
101
  lastBusinessContinuation: DialogBusinessContinuation;
102
+ answeredUserInterjection?: AnswerToHumanItem;
102
103
  fbrConclusion?: {
103
104
  responseText: string;
104
105
  responseGenseq: number;
@@ -555,6 +555,26 @@ function parsePersistenceJson(args) {
555
555
  function isRecord(value) {
556
556
  return typeof value === 'object' && value !== null;
557
557
  }
558
+ function parseDialogPendingUserInterjectionReply(value) {
559
+ if (!isRecord(value))
560
+ return null;
561
+ const msgId = value.msgId;
562
+ const course = value.course;
563
+ const genseq = value.genseq;
564
+ if (typeof msgId !== 'string' || msgId.trim() === '')
565
+ return null;
566
+ if (typeof course !== 'number' || !Number.isInteger(course) || course <= 0) {
567
+ return null;
568
+ }
569
+ if (typeof genseq !== 'number' || !Number.isInteger(genseq) || genseq <= 0) {
570
+ return null;
571
+ }
572
+ return {
573
+ msgId,
574
+ course: (0, storage_1.toDialogCourseNumber)(course),
575
+ genseq: (0, storage_1.toCallSiteGenseqNo)(genseq),
576
+ };
577
+ }
558
578
  function parseDisplayTextI18n(value) {
559
579
  if (value === undefined)
560
580
  return undefined;
@@ -2148,24 +2168,7 @@ function parseDialogLatestFile(value) {
2148
2168
  const pendingUserInterjectionReply = (() => {
2149
2169
  if (pendingUserInterjectionReplyRaw === undefined)
2150
2170
  return undefined;
2151
- if (!isRecord(pendingUserInterjectionReplyRaw))
2152
- return null;
2153
- const msgId = pendingUserInterjectionReplyRaw.msgId;
2154
- const course = pendingUserInterjectionReplyRaw.course;
2155
- const genseq = pendingUserInterjectionReplyRaw.genseq;
2156
- if (typeof msgId !== 'string' || msgId.trim() === '')
2157
- return null;
2158
- if (typeof course !== 'number' || !Number.isFinite(course) || Math.floor(course) <= 0) {
2159
- return null;
2160
- }
2161
- if (typeof genseq !== 'number' || !Number.isFinite(genseq) || Math.floor(genseq) <= 0) {
2162
- return null;
2163
- }
2164
- return {
2165
- msgId,
2166
- course: (0, storage_1.toDialogCourseNumber)(course),
2167
- genseq: (0, storage_1.toCallSiteGenseqNo)(genseq),
2168
- };
2171
+ return parseDialogPendingUserInterjectionReply(pendingUserInterjectionReplyRaw);
2169
2172
  })();
2170
2173
  if (pendingUserInterjectionReply === null)
2171
2174
  return null;
@@ -2238,6 +2241,9 @@ function parseDialogLatestFile(value) {
2238
2241
  const directive = parseTellaskReplyDirective(deferredReplyReassertionRaw.directive);
2239
2242
  if (directive === null)
2240
2243
  return null;
2244
+ const userInterjection = parseDialogPendingUserInterjectionReply(deferredReplyReassertionRaw.userInterjection);
2245
+ if (userInterjection === null)
2246
+ return null;
2241
2247
  const resumeGuideSurfacedRaw = deferredReplyReassertionRaw.resumeGuideSurfaced;
2242
2248
  if (resumeGuideSurfacedRaw !== undefined && typeof resumeGuideSurfacedRaw !== 'boolean') {
2243
2249
  return null;
@@ -2245,6 +2251,7 @@ function parseDialogLatestFile(value) {
2245
2251
  return {
2246
2252
  reason: 'user_interjection_with_parked_original_task',
2247
2253
  directive,
2254
+ userInterjection,
2248
2255
  ...(resumeGuideSurfacedRaw === undefined
2249
2256
  ? {}
2250
2257
  : { resumeGuideSurfaced: resumeGuideSurfacedRaw }),
@@ -2441,21 +2448,23 @@ function isAnswerToHumanItem(value) {
2441
2448
  return false;
2442
2449
  if (typeof value.userInterjection.course !== 'number')
2443
2450
  return false;
2444
- if (!Number.isFinite(value.userInterjection.course))
2451
+ if (!Number.isInteger(value.userInterjection.course) || value.userInterjection.course <= 0) {
2445
2452
  return false;
2453
+ }
2446
2454
  if (typeof value.userInterjection.genseq !== 'number')
2447
2455
  return false;
2448
- if (!Number.isFinite(value.userInterjection.genseq))
2456
+ if (!Number.isInteger(value.userInterjection.genseq) || value.userInterjection.genseq <= 0) {
2449
2457
  return false;
2458
+ }
2450
2459
  if (!isRecord(value.answerRef))
2451
2460
  return false;
2452
2461
  if (typeof value.answerRef.course !== 'number')
2453
2462
  return false;
2454
- if (!Number.isFinite(value.answerRef.course))
2463
+ if (!Number.isInteger(value.answerRef.course) || value.answerRef.course <= 0)
2455
2464
  return false;
2456
2465
  if (typeof value.answerRef.genseq !== 'number')
2457
2466
  return false;
2458
- if (!Number.isFinite(value.answerRef.genseq))
2467
+ if (!Number.isInteger(value.answerRef.genseq) || value.answerRef.genseq <= 0)
2459
2468
  return false;
2460
2469
  return true;
2461
2470
  }
@@ -9103,7 +9112,17 @@ class DialogPersistence {
9103
9112
  }, status);
9104
9113
  }
9105
9114
  static async setDeferredReplyReassertion(dialogId, deferredReplyReassertion, status = 'running') {
9106
- await this.mutateDialogLatest(dialogId, () => ({ kind: 'patch', patch: { deferredReplyReassertion } }), status);
9115
+ await this.mutateDialogLatest(dialogId, (previous) => {
9116
+ if (deferredReplyReassertion !== undefined) {
9117
+ return { kind: 'patch', patch: { deferredReplyReassertion } };
9118
+ }
9119
+ if (previous.deferredReplyReassertion === undefined) {
9120
+ return { kind: 'noop' };
9121
+ }
9122
+ const next = { ...previous };
9123
+ delete next.deferredReplyReassertion;
9124
+ return { kind: 'replace', next };
9125
+ }, status);
9107
9126
  }
9108
9127
  static async getDeferredReplyReassertion(dialogId, status = 'running') {
9109
9128
  const latest = await this.loadDialogLatest(dialogId, status);
@@ -9330,10 +9349,9 @@ class DialogPersistence {
9330
9349
  if (!existingAnswers.some((item) => item.id === recoveredAnswer.id)) {
9331
9350
  await this.appendAnswerToHumanState(dialogId, recoveredAnswer, status);
9332
9351
  }
9333
- return {
9334
- ...latest,
9335
- pendingUserInterjectionReply: undefined,
9336
- };
9352
+ const next = { ...latest };
9353
+ delete next.pendingUserInterjectionReply;
9354
+ return next;
9337
9355
  }
9338
9356
  }
9339
9357
  if (normalizedPending?.msgId === latest.pendingUserInterjectionReply.msgId &&
@@ -9341,6 +9359,11 @@ class DialogPersistence {
9341
9359
  normalizedPending?.genseq === latest.pendingUserInterjectionReply.genseq) {
9342
9360
  return latest;
9343
9361
  }
9362
+ if (normalizedPending === undefined) {
9363
+ const next = { ...latest };
9364
+ delete next.pendingUserInterjectionReply;
9365
+ return next;
9366
+ }
9344
9367
  return {
9345
9368
  ...latest,
9346
9369
  pendingUserInterjectionReply: normalizedPending,
@@ -272,7 +272,9 @@ function getReminderMaintenanceInstructions(language, reminderId, meta) {
272
272
  const deleteAltInstruction = isReminderGuideMetaRecord(deleteValue) && typeof deleteValue['altInstruction'] === 'string'
273
273
  ? deleteValue['altInstruction'].trim()
274
274
  : undefined;
275
- const daemonCompleted = metaKind === 'daemon' && isReminderGuideMetaRecord(metaValue) && metaValue['completed'] === true;
275
+ const daemonCompleted = metaKind === 'daemon' &&
276
+ isReminderGuideMetaRecord(metaValue) &&
277
+ metaValue['completed'] === true;
276
278
  const deleteInstruction = language === 'zh'
277
279
  ? isPendingTellaskReminder && pendingTellaskCount !== undefined && pendingTellaskCount > 0
278
280
  ? '删除通道:当前仍有进行中诉请;我不能用 delete_reminder 删除。要改变某一路长线诉请时,我复用同一 sessionSlug 再发 tellask;tellaskSessionless 不能修改或停止旧任务。'
@@ -114,16 +114,18 @@ function buildReplyObligationSuppressionGuideText(language) {
114
114
  if (language === 'zh') {
115
115
  return [
116
116
  exports.REPLY_SUPPRESSION_PREFIX_ZH,
117
- '先把用户刚插进来的这轮正常接住,按眼前的话题继续回答。',
118
- '原来那条长线先放一放,别急着顺着它往下收口。',
119
- '等原任务允许恢复时,运行时会再提醒你把那条线接回去。',
117
+ '本轮最新用户消息是真实用户插话;先按这条最新用户消息回答,让用户看到你已经接住了当前话题。',
118
+ '原来的长线诉请、回复工具义务、技能/SOP 触发条件都已暂存;不要在回答当前用户消息前切回旧任务、旧工具流程或旧收口。',
119
+ '只有当前用户消息本身需要,才使用工具;不要因为旧长线任务、旧技能提示或旧提醒项去调用工具。',
120
+ '等当前用户插话已经得到可见回复后,运行时会再提醒你接回原来的长线。',
120
121
  ].join('\n');
121
122
  }
122
123
  return [
123
124
  exports.REPLY_SUPPRESSION_PREFIX_EN,
124
- "First, handle the user's interjection normally and keep answering the topic in front of you.",
125
- 'Set the earlier long-line thread aside for now; do not rush to close it yet.',
126
- 'When the original task becomes resumable again, runtime will remind you to pick that thread up again.',
125
+ 'The latest user message in this turn is a real user interjection; answer that latest user message first so the user can see you handled the current topic.',
126
+ 'The earlier long-line request, reply-tool obligation, and skill/SOP triggers are parked; do not switch back to the old task, old tool flow, or old closure before answering the current user message.',
127
+ 'Use tools only if the current user message itself requires them; do not call tools because of the earlier long-line task, old skill hints, or old reminders.',
128
+ 'After the current user interjection has a visible reply, runtime will remind you to resume the earlier long-line thread.',
127
129
  ].join('\n');
128
130
  }
129
131
  function buildReplyObligationReassertionText(args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dominds",
3
- "version": "1.25.15",
3
+ "version": "1.25.16",
4
4
  "description": "Dominds CLI and aggregation shell for the LongRun AI kernel/runtime packages.",
5
5
  "type": "commonjs",
6
6
  "publishConfig": {
@@ -53,8 +53,8 @@
53
53
  "yaml": "^2.8.2",
54
54
  "zod": "^4.3.6",
55
55
  "@longrun-ai/codex-auth": "0.13.0",
56
- "@longrun-ai/kernel": "1.15.11",
57
- "@longrun-ai/shell": "1.15.11"
56
+ "@longrun-ai/kernel": "1.15.12",
57
+ "@longrun-ai/shell": "1.15.12"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^25.3.5",