dominds 1.27.1 → 1.27.2

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/dialog.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import type { ContextHealthSnapshot } from '@longrun-ai/kernel/types/context-health';
16
16
  import type { DialogEvent, NativeToolCallPayload, ReminderContent, WebSearchCallAction, WebSearchCallSource } from '@longrun-ai/kernel/types/dialog';
17
- import type { DialogCalleeReplyTarget, DialogQueuedDeferredQ4HAnswerState, DialogQueuedPromptState, DialogQueuedUserGenerationBoundaryState, DialogRunControlSpec, DialogRuntimePrompt, DialogUserPrompt, DriveIntent } from '@longrun-ai/kernel/types/drive-intent';
17
+ import type { DialogCalleeReplyTarget, DialogQueuedDeferredQ4HAnswerState, DialogQueuedPromptState, DialogQueuedUserGenerationBoundaryState, DialogRunControlSpec, DialogRuntimePrompt, DialogRuntimeSideDialogPrompt, DialogUserPrompt, DriveIntent } from '@longrun-ai/kernel/types/drive-intent';
18
18
  import type { LanguageCode } from '@longrun-ai/kernel/types/language';
19
19
  import type { ActiveCalleesFile, CalleeCourseNumber, CalleeGenerationSeqNumber, CallSiteCourseNo, CallSiteGenseqNo, DialogAskerStackState, DialogLatestFile, DialogMetadataFile, DialogNextStepTrigger, HumanQuestion, ProviderData, ReasoningPayload, TellaskCallRecordName, TellaskReplyDirective } from '@longrun-ai/kernel/types/storage';
20
20
  import { ChatMessage, FuncResultMsg, TellaskCarryoverMsg, TellaskResultMsg } from './llm/client';
@@ -130,6 +130,7 @@ export declare function buildSideDialogAskerStack(args: {
130
130
  askerDialogId: string;
131
131
  assignment: AssignmentFromAsker;
132
132
  }): DialogAskerStackState;
133
+ export declare function buildSideDialogAssignmentPromptMeta(sideDialog: SideDialog): Pick<DialogRuntimeSideDialogPrompt, 'tellaskReplyDirective' | 'calleeDialogReplyTarget'>;
133
134
  /**
134
135
  * Abstract base class for all dialog types.
135
136
  * Contains common properties and methods shared between MainDialog and SideDialog.
package/dist/dialog.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DialogStore = exports.MainDialog = exports.SideDialog = exports.Dialog = exports.DialogID = exports.InvalidReminderIndexError = void 0;
4
4
  exports.scheduleGlobalDialogMutexCleanupForRoot = scheduleGlobalDialogMutexCleanupForRoot;
5
5
  exports.buildSideDialogAskerStack = buildSideDialogAskerStack;
6
+ exports.buildSideDialogAssignmentPromptMeta = buildSideDialogAssignmentPromptMeta;
6
7
  const id_1 = require("@longrun-ai/kernel/utils/id");
7
8
  const time_1 = require("@longrun-ai/kernel/utils/time");
8
9
  const util_1 = require("util");
@@ -1381,6 +1381,161 @@ function formatInvalidFuncCallRuntimeGuide(language, call) {
1381
1381
  .filter((line) => line.length > 0)
1382
1382
  .join('\n');
1383
1383
  }
1384
+ function stableJsonStringify(value) {
1385
+ if (value === null)
1386
+ return 'null';
1387
+ if (typeof value === 'string')
1388
+ return JSON.stringify(value);
1389
+ if (typeof value === 'number' || typeof value === 'boolean')
1390
+ return JSON.stringify(value);
1391
+ if (Array.isArray(value)) {
1392
+ return `[${value.map((entry) => stableJsonStringify(entry)).join(',')}]`;
1393
+ }
1394
+ if (typeof value === 'object') {
1395
+ const record = value;
1396
+ return `{${Object.keys(record)
1397
+ .sort()
1398
+ .map((key) => `${JSON.stringify(key)}:${stableJsonStringify(record[key])}`)
1399
+ .join(',')}}`;
1400
+ }
1401
+ return JSON.stringify(String(value));
1402
+ }
1403
+ function canonicalizeToolCallArguments(rawArguments) {
1404
+ try {
1405
+ return stableJsonStringify(JSON.parse(rawArguments));
1406
+ }
1407
+ catch {
1408
+ return rawArguments;
1409
+ }
1410
+ }
1411
+ function canonicalizeToolResult(result) {
1412
+ return stableJsonStringify({
1413
+ content: result.content,
1414
+ contentItems: result.contentItems ?? null,
1415
+ });
1416
+ }
1417
+ function buildToolResultFingerprint(call, result) {
1418
+ return {
1419
+ toolName: call.name,
1420
+ argumentsFingerprint: canonicalizeToolCallArguments(call.arguments),
1421
+ resultFingerprint: canonicalizeToolResult(result),
1422
+ };
1423
+ }
1424
+ function sameToolResultFingerprint(left, right) {
1425
+ return (left.toolName === right.toolName &&
1426
+ left.argumentsFingerprint === right.argumentsFingerprint &&
1427
+ left.resultFingerprint === right.resultFingerprint);
1428
+ }
1429
+ function formatRepeatedToolCallRuntimeGuide(args) {
1430
+ const resultPreview = args.resultContent.length > 800
1431
+ ? `${args.resultContent.slice(0, 800)}\n...`
1432
+ : args.resultContent;
1433
+ if (args.language === 'en') {
1434
+ return [
1435
+ '[Runtime notice] You have just made the same tool call three times, with identical arguments, and Dominds observed the exact same tool result each time.',
1436
+ '',
1437
+ `- tool: ${args.toolName}`,
1438
+ `- callIds: ${args.callIds.join(', ')}`,
1439
+ '- arguments:',
1440
+ '```json',
1441
+ args.argumentsFingerprint,
1442
+ '```',
1443
+ '- repeated result:',
1444
+ '```text',
1445
+ resultPreview,
1446
+ '```',
1447
+ '',
1448
+ 'Question this behavior before calling tools again. The repeated result strongly suggests the same call will not produce new information. Re-read the user request and the tool result, then correct course: answer from the available information, choose a different action, or explain what is blocked.',
1449
+ ].join('\n');
1450
+ }
1451
+ return [
1452
+ '[Dominds 提示] 你刚才连续三次调用了同一个工具,参数完全相同,Dominds 观察到三次工具返回值也完全相同。',
1453
+ '',
1454
+ `- 工具: ${args.toolName}`,
1455
+ `- callIds: ${args.callIds.join(', ')}`,
1456
+ '- 参数:',
1457
+ '```json',
1458
+ args.argumentsFingerprint,
1459
+ '```',
1460
+ '- 重复返回值:',
1461
+ '```text',
1462
+ resultPreview,
1463
+ '```',
1464
+ '',
1465
+ '请先质疑这个行为,不要机械地再次调用同一个工具。同样的调用大概率不会产生新信息。请重新阅读用户诉求和工具返回值,然后自行纠正:基于已有信息作答、换一种行动,或明确说明当前阻塞点。',
1466
+ ].join('\n');
1467
+ }
1468
+ function formatRepeatedToolCallStoppedDetail(args) {
1469
+ if (args.language === 'en') {
1470
+ return (`Stopped because the LLM ignored Dominds' repeated-tool-call correction notice and again made ` +
1471
+ `the same tool call three times with identical results. This indicates an LLM behavior/personality problem: ` +
1472
+ `it is looping on ${args.toolName} instead of reconsidering the task. callIds=${args.callIds.join(', ')}`);
1473
+ }
1474
+ return (`已停止:LLM 已收到 Dominds 的重复工具调用纠正提醒,但仍然再次连续三次调用同一个工具并得到完全相同的结果。` +
1475
+ `这说明当前 LLM 存在行为/个性问题:它在 ${args.toolName} 上机械循环,而不是重新判断任务。` +
1476
+ `callIds=${args.callIds.join(', ')}`);
1477
+ }
1478
+ const REPEATED_TOOL_CALL_STOP_WINDOW_GENSEQS = 5;
1479
+ function inspectRepeatedToolCallRound(args) {
1480
+ ensureRepeatedToolCallMonitorCourse(args.state, args.currentCourse);
1481
+ const recentPairs = [];
1482
+ for (let index = 0; index < args.pairedMessages.length - 1; index += 1) {
1483
+ const call = args.pairedMessages[index];
1484
+ const result = args.pairedMessages[index + 1];
1485
+ if (call?.type !== 'func_call_msg' || result?.type !== 'func_result_msg') {
1486
+ continue;
1487
+ }
1488
+ if (result.id !== call.id) {
1489
+ continue;
1490
+ }
1491
+ recentPairs.push({
1492
+ call,
1493
+ result,
1494
+ fingerprint: buildToolResultFingerprint(call, result),
1495
+ });
1496
+ }
1497
+ for (const pair of recentPairs) {
1498
+ const currentSequence = args.state.sequence;
1499
+ if (currentSequence !== undefined &&
1500
+ sameToolResultFingerprint(currentSequence.fingerprint, pair.fingerprint)) {
1501
+ currentSequence.callIds.push(pair.call.id);
1502
+ if (currentSequence.callIds.length > 3) {
1503
+ currentSequence.callIds.splice(0, currentSequence.callIds.length - 3);
1504
+ }
1505
+ currentSequence.resultContent = pair.result.content;
1506
+ }
1507
+ else {
1508
+ args.state.sequence = {
1509
+ fingerprint: pair.fingerprint,
1510
+ callIds: [pair.call.id],
1511
+ resultContent: pair.result.content,
1512
+ };
1513
+ }
1514
+ const sequence = args.state.sequence;
1515
+ if (sequence === undefined || sequence.callIds.length < 3) {
1516
+ continue;
1517
+ }
1518
+ const recentReminder = args.state.lastReminderGenseq !== undefined &&
1519
+ pair.call.genseq > args.state.lastReminderGenseq &&
1520
+ pair.call.genseq - args.state.lastReminderGenseq <= REPEATED_TOOL_CALL_STOP_WINDOW_GENSEQS;
1521
+ const callIds = [...sequence.callIds];
1522
+ return {
1523
+ toolName: pair.call.name,
1524
+ callIds,
1525
+ argumentsFingerprint: pair.fingerprint.argumentsFingerprint,
1526
+ resultContent: sequence.resultContent,
1527
+ reminderContent: formatRepeatedToolCallRuntimeGuide({
1528
+ language: args.language,
1529
+ toolName: pair.call.name,
1530
+ callIds,
1531
+ argumentsFingerprint: pair.fingerprint.argumentsFingerprint,
1532
+ resultContent: sequence.resultContent,
1533
+ }),
1534
+ repeatedAfterReminder: recentReminder,
1535
+ };
1536
+ }
1537
+ return undefined;
1538
+ }
1384
1539
  async function persistInvalidFuncCallRuntimeGuide(args) {
1385
1540
  const { dlg, call } = args;
1386
1541
  const sourceText = args.source === 'streamed' ? 'streamed' : 'batch';
@@ -1405,6 +1560,17 @@ async function persistInvalidFuncCallRuntimeGuide(args) {
1405
1560
  });
1406
1561
  await persistAndPostRuntimeGuide(dlg, content);
1407
1562
  }
1563
+ function resetRepeatedToolCallMonitor(state) {
1564
+ state.sequence = undefined;
1565
+ state.lastReminderGenseq = undefined;
1566
+ }
1567
+ function ensureRepeatedToolCallMonitorCourse(state, course) {
1568
+ if (state.course === course) {
1569
+ return;
1570
+ }
1571
+ resetRepeatedToolCallMonitor(state);
1572
+ state.course = course;
1573
+ }
1408
1574
  function isQueuedNewCourseRuntimePrompt(prompt) {
1409
1575
  return (prompt?.kind === 'new_course_runtime_guide' ||
1410
1576
  prompt?.kind === 'new_course_runtime_reply' ||
@@ -1458,6 +1624,7 @@ function summarizeRoutedFunctionResult(routed) {
1458
1624
  immediateFollowupCallIds: routed.immediateFollowupCallIds,
1459
1625
  immediateTellaskOutputCallIds: routed.immediateTellaskOutputCallIds,
1460
1626
  invalidTellaskCallIds: routed.invalidTellaskCallIds,
1627
+ repeatedToolCallReminderCallIds: routed.repeatedToolCallReminderCallIds,
1461
1628
  shouldStopAfterReplyTool: routed.shouldStopAfterReplyTool,
1462
1629
  shouldStopAfterPendingTellaskWait: routed.shouldStopAfterPendingTellaskWait,
1463
1630
  pairedMessageTypes: routed.pairedMessages.map((msg) => msg.type),
@@ -1511,6 +1678,8 @@ function shouldCaptureUnexpectedIdleAfterToolRound(args) {
1511
1678
  return true;
1512
1679
  if (diagnostics.routed.immediateTellaskOutputCallIds.length > 0)
1513
1680
  return true;
1681
+ if (diagnostics.routed.repeatedToolCallReminderCallIds.length > 0)
1682
+ return true;
1514
1683
  return false;
1515
1684
  }
1516
1685
  function sanitizeDebugFileSegment(value) {
@@ -1694,6 +1863,12 @@ function buildImmediateFollowupTriggerExpectation(args) {
1694
1863
  callIds: [...invalidRecoveryCallIds],
1695
1864
  });
1696
1865
  }
1866
+ if (args.routed.repeatedToolCallReminderCallIds.length > 0) {
1867
+ reasons.push({
1868
+ kind: 'repeated_tool_call_reminder',
1869
+ callIds: args.routed.repeatedToolCallReminderCallIds,
1870
+ });
1871
+ }
1697
1872
  if (reasons.length === 0) {
1698
1873
  return undefined;
1699
1874
  }
@@ -2062,6 +2237,7 @@ async function executeFunctionRound(args) {
2062
2237
  immediateFollowupCallIds: [],
2063
2238
  immediateTellaskOutputCallIds: [],
2064
2239
  invalidTellaskCallIds: [],
2240
+ repeatedToolCallReminderCallIds: [],
2065
2241
  shouldStopAfterReplyTool: false,
2066
2242
  shouldStopAfterPendingTellaskWait: false,
2067
2243
  pairedMessages: [],
@@ -2180,6 +2356,7 @@ async function executeFunctionRound(args) {
2180
2356
  immediateFollowupCallIds,
2181
2357
  immediateTellaskOutputCallIds: tellaskRound.immediateTellaskOutputCallIds,
2182
2358
  invalidTellaskCallIds: tellaskRound.invalidTellaskCallIds,
2359
+ repeatedToolCallReminderCallIds: [],
2183
2360
  shouldStopAfterReplyTool: tellaskRound.shouldStopAfterReplyTool,
2184
2361
  shouldStopAfterPendingTellaskWait: tellaskRound.shouldStopAfterPendingTellaskWait,
2185
2362
  pairedMessages,
@@ -2390,6 +2567,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2390
2567
  let fbrConclusion;
2391
2568
  let pubRemindersVer = dlg.remindersVer;
2392
2569
  let lastToolRoundStopDiagnostics;
2570
+ const repeatedToolCallMonitor = {};
2393
2571
  let pendingPrompt = humanPrompt;
2394
2572
  let resolvingImmediateToolResultForUserPrompt = false;
2395
2573
  let resolvingImmediateToolResultUserPromptMsgId;
@@ -2591,6 +2769,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2591
2769
  let llmGenModelForGen = model;
2592
2770
  const currentPrompt = pendingPrompt;
2593
2771
  const currentReplyTarget = currentPrompt?.calleeDialogReplyTarget;
2772
+ if (currentGenerationBelongsToUserPrompt) {
2773
+ resetRepeatedToolCallMonitor(repeatedToolCallMonitor);
2774
+ }
2594
2775
  let currentBusinessContinuation = driveOptions?.businessContinuation ?? { kind: 'none' };
2595
2776
  if (currentPrompt?.tellaskReplyDirective !== undefined) {
2596
2777
  currentBusinessContinuation = {
@@ -3472,7 +3653,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3472
3653
  : currentGenerationBelongsToUserToolChain
3473
3654
  ? currentUserPromptMsgId
3474
3655
  : undefined;
3475
- const routed = await executeFunctionRound({
3656
+ let routed = await executeFunctionRound({
3476
3657
  dlg,
3477
3658
  agent,
3478
3659
  agentTools,
@@ -3518,6 +3699,41 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3518
3699
  newMsgs.push(...routed.pairedMessages);
3519
3700
  }
3520
3701
  await dlg.addChatMessages(...newMsgs);
3702
+ const repeatedToolCallInspection = inspectRepeatedToolCallRound({
3703
+ state: repeatedToolCallMonitor,
3704
+ currentCourse: dlg.activeGenCourseOrUndefined ?? dlg.currentCourse,
3705
+ pairedMessages: routed.pairedMessages,
3706
+ language: (0, work_language_1.getWorkLanguage)(),
3707
+ });
3708
+ if (repeatedToolCallInspection !== undefined) {
3709
+ if (repeatedToolCallInspection.repeatedAfterReminder) {
3710
+ const detail = formatRepeatedToolCallStoppedDetail({
3711
+ language: (0, work_language_1.getWorkLanguage)(),
3712
+ toolName: repeatedToolCallInspection.toolName,
3713
+ callIds: repeatedToolCallInspection.callIds,
3714
+ });
3715
+ log_1.log.error('kernel-driver stopped after repeated identical tool calls ignored guidance', undefined, {
3716
+ rootId: dlg.id.rootId,
3717
+ selfId: dlg.id.selfId,
3718
+ course: dlg.activeGenCourseOrUndefined ?? dlg.currentCourse,
3719
+ genseq: dlg.activeGenSeq,
3720
+ toolName: repeatedToolCallInspection.toolName,
3721
+ callIds: repeatedToolCallInspection.callIds,
3722
+ });
3723
+ await dlg.streamError(detail);
3724
+ throw new gen_1.LlmStreamErrorEmittedError({
3725
+ detail,
3726
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({ detail }),
3727
+ });
3728
+ }
3729
+ await persistAndEmitRuntimeGuide(dlg, repeatedToolCallInspection.reminderContent);
3730
+ repeatedToolCallMonitor.lastReminderGenseq = dlg.activeGenSeq;
3731
+ repeatedToolCallMonitor.sequence = undefined;
3732
+ routed = {
3733
+ ...routed,
3734
+ repeatedToolCallReminderCallIds: repeatedToolCallInspection.callIds,
3735
+ };
3736
+ }
3521
3737
  const persistedFbrState = await loadDialogFbrState(dlg);
3522
3738
  if (persistedFbrState) {
3523
3739
  if (persistedFbrState.phase === 'finalization') {
@@ -3608,6 +3824,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3608
3824
  // trigger another immediate generation round by themselves.
3609
3825
  const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls ||
3610
3826
  routed.hasImmediateTellaskOutputs ||
3827
+ routed.repeatedToolCallReminderCallIds.length > 0 ||
3611
3828
  invalidFuncCallCount > 0;
3612
3829
  if (shouldStartImmediatePostToolGeneration) {
3613
3830
  const expectation = buildImmediateFollowupTriggerExpectation({
@@ -3622,6 +3839,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3622
3839
  `(dialog=${dlg.id.valueOf()}, course=${String(dlg.activeGenCourseOrUndefined ?? dlg.currentCourse)}, ` +
3623
3840
  `genseq=${String(dlg.activeGenSeq)}, immediateToolCalls=${String(routed.hasImmediateFollowupToolCalls)}, ` +
3624
3841
  `immediateTellaskOutputs=${String(routed.hasImmediateTellaskOutputs)}, ` +
3842
+ `repeatedToolCallReminderCallIds=${routed.repeatedToolCallReminderCallIds.join('+')}, ` +
3625
3843
  `invalidFuncCallCount=${String(invalidFuncCallCount)})`);
3626
3844
  }
3627
3845
  immediateFollowupTriggerExpectation = expectation;
@@ -69,12 +69,6 @@ async function queueReplyReminderFollowUp(args) {
69
69
  function isReplyToolReminderPrompt(prompt) {
70
70
  return typeof prompt?.content === 'string' && (0, reply_prompt_copy_1.isReplyToolReminderPromptContent)(prompt.content);
71
71
  }
72
- function hasSameReplyDirective(left, right) {
73
- return (left.expectedReplyCallName === right.expectedReplyCallName &&
74
- left.targetDialogId === right.targetDialogId &&
75
- left.targetCallId === right.targetCallId &&
76
- left.tellaskContent === right.tellaskContent);
77
- }
78
72
  function hasSameCalleeReplyTarget(left, right) {
79
73
  return (left.callerDialogId === right.callerDialogId &&
80
74
  left.callId === right.callId &&
@@ -82,29 +76,6 @@ function hasSameCalleeReplyTarget(left, right) {
82
76
  left.callSiteCourse === right.callSiteCourse &&
83
77
  left.callSiteGenseq === right.callSiteGenseq);
84
78
  }
85
- function buildCurrentSideDialogAssignmentReplyDirective(dialog) {
86
- switch (dialog.assignmentFromAsker.callName) {
87
- case 'tellask':
88
- return {
89
- expectedReplyCallName: 'replyTellask',
90
- targetDialogId: dialog.assignmentFromAsker.askerDialogId,
91
- targetCallId: dialog.assignmentFromAsker.callId,
92
- tellaskContent: dialog.assignmentFromAsker.tellaskContent,
93
- };
94
- case 'tellaskSessionless':
95
- case 'freshBootsReasoning':
96
- return {
97
- expectedReplyCallName: 'replyTellaskSessionless',
98
- targetDialogId: dialog.assignmentFromAsker.askerDialogId,
99
- targetCallId: dialog.assignmentFromAsker.callId,
100
- tellaskContent: dialog.assignmentFromAsker.tellaskContent,
101
- };
102
- default: {
103
- const _exhaustive = dialog.assignmentFromAsker.callName;
104
- throw new Error(`Unsupported sideDialog assignment callName: ${_exhaustive}`);
105
- }
106
- }
107
- }
108
79
  function isQueuedReplyObligationContinuation(prompt) {
109
80
  return ((prompt.kind === 'new_course_runtime_reply' ||
110
81
  prompt.kind === 'new_course_runtime_sideDialog') &&
@@ -151,8 +122,8 @@ async function claimQueuedReplyObligationContinuation(args) {
151
122
  return 'stale';
152
123
  }
153
124
  if (args.dialog instanceof dialog_1.SideDialog) {
154
- const assignmentDirective = buildCurrentSideDialogAssignmentReplyDirective(args.dialog);
155
- if (hasSameReplyDirective(assignmentDirective, directive)) {
125
+ const assignmentDirective = (0, dialog_1.buildSideDialogAssignmentPromptMeta)(args.dialog).tellaskReplyDirective;
126
+ if ((0, reply_guidance_1.hasSameReplyDirective)(assignmentDirective, directive)) {
156
127
  return 'claimed';
157
128
  }
158
129
  }
@@ -163,7 +134,7 @@ async function claimQueuedReplyObligationContinuation(args) {
163
134
  if (activeDirective.targetCallId !== directive.targetCallId) {
164
135
  return 'stale';
165
136
  }
166
- if (!hasSameReplyDirective(activeDirective, directive)) {
137
+ if (!(0, reply_guidance_1.hasSameReplyDirective)(activeDirective, directive)) {
167
138
  throw new Error(`reply obligation continuation invariant violation: active obligation changed for callId=${directive.targetCallId} ` +
168
139
  `(dialog=${args.dialog.id.valueOf()}, expectedReplyCallName=${directive.expectedReplyCallName}, ` +
169
140
  `activeReplyCallName=${activeDirective.expectedReplyCallName}, targetDialogId=${directive.targetDialogId}, ` +
@@ -190,7 +161,7 @@ async function resolveSideDialogReplyDirectiveForAssistantOutput(args) {
190
161
  if (latestHasTellaskResultForCallId(latest, targetCallId)) {
191
162
  return args.currentDirective;
192
163
  }
193
- const assignmentDirective = buildCurrentSideDialogAssignmentReplyDirective(args.dialog);
164
+ const assignmentDirective = (0, dialog_1.buildSideDialogAssignmentPromptMeta)(args.dialog).tellaskReplyDirective;
194
165
  if (assignmentDirective.targetCallId !== targetCallId) {
195
166
  return args.currentDirective;
196
167
  }
@@ -204,7 +175,7 @@ async function resolveSideDialogReplyDirectiveForAssistantOutput(args) {
204
175
  return args.currentDirective;
205
176
  }
206
177
  const activeDirective = await (0, tellask_special_1.loadActiveTellaskReplyDirective)(args.dialog);
207
- if (activeDirective && !hasSameReplyDirective(activeDirective, assignmentDirective)) {
178
+ if (activeDirective && !(0, reply_guidance_1.hasSameReplyDirective)(activeDirective, assignmentDirective)) {
208
179
  throw new Error(`sideDialog assistant output reply directive invariant violation: active obligation does not match latest assignment ` +
209
180
  `(dialog=${args.dialog.id.valueOf()}, targetCallId=${targetCallId}, ` +
210
181
  `activeTargetCallId=${activeDirective.targetCallId}, assignmentTargetCallId=${assignmentDirective.targetCallId})`);
@@ -796,7 +767,7 @@ function pendingRuntimePromptMatchesQueuePrompt(args) {
796
767
  return args.prompt.kind === 'new_course_runtime_guide';
797
768
  }
798
769
  if (args.prompt.kind === 'new_course_runtime_guide' ||
799
- !hasSameReplyDirective(args.pendingRuntimePrompt.tellaskReplyDirective, args.prompt.tellaskReplyDirective)) {
770
+ !(0, reply_guidance_1.hasSameReplyDirective)(args.pendingRuntimePrompt.tellaskReplyDirective, args.prompt.tellaskReplyDirective)) {
800
771
  return false;
801
772
  }
802
773
  if (args.pendingRuntimePrompt.calleeDialogReplyTarget === undefined) {
@@ -1135,6 +1106,8 @@ async function executeDriveRound(args) {
1135
1106
  };
1136
1107
  let calleeDialogReplyTarget = replyContinuationScope.target();
1137
1108
  let activeTellaskReplyDirective = replyContinuationScope.directive();
1109
+ let suppressedCalleeDialogReplyTargetAfterUserInterjection;
1110
+ let suppressedTellaskReplyDirectiveAfterUserInterjection;
1138
1111
  let activePromptWasReplyToolReminder = false;
1139
1112
  let shouldPauseAfterLocalUserInterjection = false;
1140
1113
  let resumeFromInterjectionPause = false;
@@ -1559,6 +1532,14 @@ async function executeDriveRound(args) {
1559
1532
  replyGuidance.suppressInterDialogReplyGuidance;
1560
1533
  activeTellaskReplyDirective =
1561
1534
  replyGuidance.activeReplyDirective ?? replyContinuationScope.directive();
1535
+ suppressedTellaskReplyDirectiveAfterUserInterjection =
1536
+ replyGuidance.suppressInterDialogReplyGuidance
1537
+ ? replyGuidance.suppressedTellaskReplyDirective
1538
+ : undefined;
1539
+ suppressedCalleeDialogReplyTargetAfterUserInterjection =
1540
+ replyGuidance.suppressInterDialogReplyGuidance
1541
+ ? replyGuidance.suppressedCalleeDialogReplyTarget
1542
+ : undefined;
1562
1543
  activePromptWasReplyToolReminder = isReplyToolReminderPrompt(effectivePrompt);
1563
1544
  let activePromptCarriesReplyDirective = effectivePrompt?.tellaskReplyDirective !== undefined &&
1564
1545
  activeTellaskReplyDirective !== undefined &&
@@ -1666,29 +1647,50 @@ async function executeDriveRound(args) {
1666
1647
  backgroundCalleeBlocksImplicitReply,
1667
1648
  });
1668
1649
  }
1669
- else if (activeTellaskReplyDirective === undefined) {
1670
- log_1.log.debug('kernel-driver skip sideDialog answering reminder because no active tellask reply directive is bound to this drive', undefined, {
1671
- rootId: dialog.id.rootId,
1672
- selfId: dialog.id.selfId,
1673
- });
1674
- }
1675
1650
  else {
1676
- const language = (0, work_language_1.getWorkLanguage)();
1677
- followUp = buildRuntimeReplyReminderFollowUp({
1678
- directive: activeTellaskReplyDirective,
1679
- prompt: await buildAnsweringReplyReminderPrompt({
1680
- dlg: dialog,
1681
- directive: activeTellaskReplyDirective,
1651
+ const replyDirectiveForAnswering = activeTellaskReplyDirective ?? suppressedTellaskReplyDirectiveAfterUserInterjection;
1652
+ if (replyDirectiveForAnswering === undefined) {
1653
+ log_1.log.debug('kernel-driver skip sideDialog answering reminder because no active tellask reply directive is bound to this drive', undefined, {
1654
+ rootId: dialog.id.rootId,
1655
+ selfId: dialog.id.selfId,
1656
+ });
1657
+ }
1658
+ else {
1659
+ if (calleeDialogReplyTarget !== undefined &&
1660
+ calleeDialogReplyTarget.callId !== replyDirectiveForAnswering.targetCallId) {
1661
+ throw new Error(`sideDialog answering reminder invariant violation: reply target callId does not match directive ` +
1662
+ `(dialog=${dialog.id.valueOf()}, targetCallId=${calleeDialogReplyTarget.callId}, ` +
1663
+ `directiveTargetCallId=${replyDirectiveForAnswering.targetCallId})`);
1664
+ }
1665
+ const currentAssignmentPromptMeta = (0, dialog_1.buildSideDialogAssignmentPromptMeta)(dialog);
1666
+ const currentAssignmentReplyTarget = (0, reply_guidance_1.hasSameReplyDirective)(replyDirectiveForAnswering, currentAssignmentPromptMeta.tellaskReplyDirective)
1667
+ ? currentAssignmentPromptMeta.calleeDialogReplyTarget
1668
+ : undefined;
1669
+ const replyTargetForAnswering = calleeDialogReplyTarget ??
1670
+ (suppressedTellaskReplyDirectiveAfterUserInterjection !== undefined &&
1671
+ (0, reply_guidance_1.hasSameReplyDirective)(replyDirectiveForAnswering, suppressedTellaskReplyDirectiveAfterUserInterjection)
1672
+ ? suppressedCalleeDialogReplyTargetAfterUserInterjection
1673
+ : undefined) ??
1674
+ currentAssignmentReplyTarget;
1675
+ const language = (0, work_language_1.getWorkLanguage)();
1676
+ followUp = buildRuntimeReplyReminderFollowUp({
1677
+ directive: replyDirectiveForAnswering,
1678
+ prompt: await buildAnsweringReplyReminderPrompt({
1679
+ dlg: dialog,
1680
+ directive: replyDirectiveForAnswering,
1681
+ language,
1682
+ }),
1682
1683
  language,
1683
- }),
1684
- language,
1685
- calleeDialogReplyTarget,
1686
- });
1687
- log_1.log.debug('kernel-driver queued sideDialog replyTellask reminder after answering-only output', undefined, {
1688
- dialogId: dialog.id.valueOf(),
1689
- targetCallId: activeTellaskReplyDirective.targetCallId,
1690
- targetCallerDialogId: calleeDialogReplyTarget?.callerDialogId,
1691
- });
1684
+ calleeDialogReplyTarget: replyTargetForAnswering,
1685
+ });
1686
+ log_1.log.debug('kernel-driver queued sideDialog replyTellask reminder after answering-only output', undefined, {
1687
+ dialogId: dialog.id.valueOf(),
1688
+ targetCallId: replyDirectiveForAnswering.targetCallId,
1689
+ targetCallerDialogId: replyTargetForAnswering?.callerDialogId,
1690
+ resumedAfterUserInterjection: activeTellaskReplyDirective === undefined &&
1691
+ suppressedTellaskReplyDirectiveAfterUserInterjection !== undefined,
1692
+ });
1693
+ }
1692
1694
  }
1693
1695
  }
1694
1696
  if (dialog instanceof dialog_1.SideDialog &&
@@ -5,6 +5,7 @@ export declare function resolveReplyTargetAgentId(args: {
5
5
  dlg: Dialog;
6
6
  directive: TellaskReplyDirective;
7
7
  }): Promise<string | undefined>;
8
+ export declare function hasSameReplyDirective(left: TellaskReplyDirective | undefined, right: TellaskReplyDirective | undefined): boolean;
8
9
  export declare function resolvePromptReplyGuidance(args: {
9
10
  dlg: Dialog;
10
11
  prompt: KernelDriverPrompt | undefined;
@@ -14,6 +15,8 @@ export declare function resolvePromptReplyGuidance(args: {
14
15
  isQ4HAnswerPrompt: boolean;
15
16
  promptContent: string | undefined;
16
17
  persistedTellaskReplyDirective: KernelDriverPrompt['tellaskReplyDirective'];
18
+ suppressedCalleeDialogReplyTarget: KernelDriverPrompt['calleeDialogReplyTarget'];
19
+ suppressedTellaskReplyDirective: KernelDriverPrompt['tellaskReplyDirective'];
17
20
  suppressInterDialogReplyGuidance: boolean;
18
21
  transientGuideContent: string | undefined;
19
22
  }>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveReplyTargetAgentId = resolveReplyTargetAgentId;
4
+ exports.hasSameReplyDirective = hasSameReplyDirective;
4
5
  exports.resolvePromptReplyGuidance = resolvePromptReplyGuidance;
5
6
  exports.buildReplyObligationSuppressionGuide = buildReplyObligationSuppressionGuide;
6
7
  const dialog_1 = require("../../dialog");
@@ -91,40 +92,17 @@ function hasSameReplyDirective(left, right) {
91
92
  }
92
93
  return true;
93
94
  }
94
- function buildCurrentSideDialogAssignmentDirective(dlg) {
95
- switch (dlg.assignmentFromAsker.callName) {
96
- case 'tellask':
97
- return {
98
- expectedReplyCallName: 'replyTellask',
99
- targetDialogId: dlg.assignmentFromAsker.askerDialogId,
100
- targetCallId: dlg.assignmentFromAsker.callId,
101
- tellaskContent: dlg.assignmentFromAsker.tellaskContent,
102
- };
103
- case 'tellaskSessionless':
104
- case 'freshBootsReasoning':
105
- return {
106
- expectedReplyCallName: 'replyTellaskSessionless',
107
- targetDialogId: dlg.assignmentFromAsker.askerDialogId,
108
- targetCallId: dlg.assignmentFromAsker.callId,
109
- tellaskContent: dlg.assignmentFromAsker.tellaskContent,
110
- };
111
- default: {
112
- const _exhaustive = dlg.assignmentFromAsker.callName;
113
- throw new Error(`Unsupported sideDialog assignment callName: ${_exhaustive}`);
114
- }
115
- }
116
- }
117
- async function resolveFreshCurrentSideDialogAssignmentDirective(args) {
95
+ async function resolveFreshCurrentSideDialogAssignmentPromptMeta(args) {
118
96
  if (!(args.dlg instanceof dialog_1.SideDialog)) {
119
97
  return undefined;
120
98
  }
121
- const currentAssignmentDirective = buildCurrentSideDialogAssignmentDirective(args.dlg);
99
+ const currentAssignmentPromptMeta = (0, dialog_1.buildSideDialogAssignmentPromptMeta)(args.dlg);
122
100
  if (args.prompt?.origin === 'runtime') {
123
101
  const promptDirective = args.prompt.tellaskReplyDirective;
124
102
  if (!promptDirective) {
125
103
  return undefined;
126
104
  }
127
- if (!hasSameReplyDirective(promptDirective, currentAssignmentDirective)) {
105
+ if (!hasSameReplyDirective(promptDirective, currentAssignmentPromptMeta.tellaskReplyDirective)) {
128
106
  return undefined;
129
107
  }
130
108
  }
@@ -135,14 +113,14 @@ async function resolveFreshCurrentSideDialogAssignmentDirective(args) {
135
113
  if (!latest) {
136
114
  return undefined;
137
115
  }
138
- if (latest.sideDialogFinalResponse?.callId === currentAssignmentDirective.targetCallId.trim()) {
116
+ const targetCallId = currentAssignmentPromptMeta.tellaskReplyDirective.targetCallId.trim();
117
+ if (latest.sideDialogFinalResponse?.callId === targetCallId) {
139
118
  return undefined;
140
119
  }
141
- const targetCallId = currentAssignmentDirective.targetCallId.trim();
142
120
  if (latest.tellaskResults.results.some((entry) => entry.callId.trim() === targetCallId)) {
143
121
  return undefined;
144
122
  }
145
- return currentAssignmentDirective;
123
+ return currentAssignmentPromptMeta;
146
124
  }
147
125
  async function resolveFreshPendingAskBackReplyDirective(args) {
148
126
  const prompt = args.prompt;
@@ -241,7 +219,7 @@ async function resolvePromptReplyGuidance(args) {
241
219
  const prompt = args.prompt;
242
220
  const isQ4HAnswerPrompt = typeof prompt?.q4hAnswerCallId === 'string' && prompt.q4hAnswerCallId.trim() !== '';
243
221
  const latest = await persistence_1.DialogPersistence.loadDialogLatest(args.dlg.id, args.dlg.status);
244
- const persistedCurrentSideDialogAssignmentDirective = await resolveFreshCurrentSideDialogAssignmentDirective({
222
+ const persistedCurrentSideDialogAssignmentPromptMeta = await resolveFreshCurrentSideDialogAssignmentPromptMeta({
245
223
  dlg: args.dlg,
246
224
  prompt,
247
225
  });
@@ -255,7 +233,7 @@ async function resolvePromptReplyGuidance(args) {
255
233
  ? latest.pendingRuntimePrompt.tellaskReplyDirective
256
234
  : undefined;
257
235
  const persistedActiveReplyObligation = await (0, tellask_special_1.loadActiveTellaskReplyDirective)(args.dlg);
258
- const persistedActiveReplyDirective = persistedCurrentSideDialogAssignmentDirective ??
236
+ const persistedActiveReplyDirective = persistedCurrentSideDialogAssignmentPromptMeta?.tellaskReplyDirective ??
259
237
  persistedPendingAskBackReplyDirective ??
260
238
  persistedPendingRuntimePromptDirective ??
261
239
  persistedActiveReplyObligation;
@@ -290,6 +268,12 @@ async function resolvePromptReplyGuidance(args) {
290
268
  promptDirective: prompt?.tellaskReplyDirective,
291
269
  persistedDirective: persistedActiveReplyDirective,
292
270
  }),
271
+ suppressedCalleeDialogReplyTarget: suppressInterDialogReplyGuidance
272
+ ? persistedCurrentSideDialogAssignmentPromptMeta?.calleeDialogReplyTarget
273
+ : undefined,
274
+ suppressedTellaskReplyDirective: suppressInterDialogReplyGuidance
275
+ ? persistedActiveReplyDirective
276
+ : undefined,
293
277
  suppressInterDialogReplyGuidance,
294
278
  transientGuideContent: suppressInterDialogReplyGuidance && prompt !== undefined
295
279
  ? buildReplyObligationSuppressionGuide({
@@ -1564,6 +1564,10 @@ function parseDialogFollowupReason(value) {
1564
1564
  const callIds = parseStringArrayField(value.callIds);
1565
1565
  return callIds === null ? null : { kind: 'invalid_tool_recovery', callIds };
1566
1566
  }
1567
+ case 'repeated_tool_call_reminder': {
1568
+ const callIds = parseStringArrayField(value.callIds);
1569
+ return callIds === null ? null : { kind: 'repeated_tool_call_reminder', callIds };
1570
+ }
1567
1571
  case 'reply_delivery_result':
1568
1572
  if (typeof value.replyDeliveryId !== 'string' ||
1569
1573
  value.replyDeliveryId.trim() === '' ||