dominds 1.27.1 → 1.27.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/apps/runtime.js +3 -1
  2. package/dist/dialog-global-registry.d.ts +11 -1
  3. package/dist/dialog-global-registry.js +45 -0
  4. package/dist/dialog.d.ts +13 -3
  5. package/dist/dialog.js +108 -17
  6. package/dist/docs/daemon-cmd-runner.md +5 -0
  7. package/dist/docs/daemon-cmd-runner.zh.md +5 -0
  8. package/dist/llm/kernel-driver/drive.js +382 -12
  9. package/dist/llm/kernel-driver/fbr.d.ts +9 -0
  10. package/dist/llm/kernel-driver/fbr.js +186 -59
  11. package/dist/llm/kernel-driver/flow.js +58 -56
  12. package/dist/llm/kernel-driver/reply-guidance.d.ts +3 -0
  13. package/dist/llm/kernel-driver/reply-guidance.js +15 -31
  14. package/dist/minds/load.js +1 -0
  15. package/dist/persistence.js +22 -1
  16. package/dist/runtime/driver-messages.d.ts +9 -0
  17. package/dist/runtime/driver-messages.js +61 -5
  18. package/dist/runtime/shared-reminder-update-impact.d.ts +20 -0
  19. package/dist/runtime/shared-reminder-update-impact.js +110 -0
  20. package/dist/server/dominds-runtime-status.js +23 -0
  21. package/dist/server/static-server.js +20 -5
  22. package/dist/tool-availability.js +1 -0
  23. package/dist/tools/builtins.js +2 -0
  24. package/dist/tools/cmd-runner-protocol.d.ts +6 -0
  25. package/dist/tools/cmd-runner-protocol.js +57 -2
  26. package/dist/tools/cmd-runner.js +83 -2
  27. package/dist/tools/ctrl.d.ts +2 -0
  28. package/dist/tools/ctrl.js +179 -5
  29. package/dist/tools/os.js +115 -14
  30. package/dist/tools/process-kill.js +49 -0
  31. package/dist/tools/prompts/control/en/errors.md +1 -1
  32. package/dist/tools/prompts/control/en/index.md +1 -1
  33. package/dist/tools/prompts/control/en/principles.md +18 -17
  34. package/dist/tools/prompts/control/en/tools.md +24 -1
  35. package/dist/tools/prompts/control/zh/errors.md +1 -1
  36. package/dist/tools/prompts/control/zh/index.md +1 -1
  37. package/dist/tools/prompts/control/zh/principles.md +17 -16
  38. package/dist/tools/prompts/control/zh/tools.md +24 -1
  39. package/dist/tools/prompts/os/en/tools.md +2 -0
  40. package/dist/tools/prompts/os/zh/tools.md +2 -0
  41. package/package.json +3 -3
  42. package/webapp/dist/assets/{main-APO6XREZ.js → main-NXVX2KTO.js} +24 -5
  43. package/webapp/dist/assets/{main-APO6XREZ.js.map → main-NXVX2KTO.js.map} +3 -3
  44. package/webapp/dist/index.html +1 -1
@@ -46,6 +46,7 @@ const KERNEL_DRIVER_DEFAULT_RETRY_POLICY = {
46
46
  backoffMultiplier: 1.5,
47
47
  maxDelayMs: 30 * 60 * 1000, // 30 minutes
48
48
  };
49
+ const CLEAR_MIND_TOOL_NAME = 'clear_mind';
49
50
  const KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE = 'DOMINDS_LLM_EMPTY_RESPONSE';
50
51
  // Wrapper isolation boundary:
51
52
  // - Wrappers emit provider-native web-search events.
@@ -246,6 +247,35 @@ function buildKernelDriverFbrPrompt(dlg, state) {
246
247
  origin: 'runtime',
247
248
  };
248
249
  }
250
+ function resolveLatestModelOutputGenseq(dlg) {
251
+ for (let index = dlg.msgs.length - 1; index >= 0; index -= 1) {
252
+ const msg = dlg.msgs[index];
253
+ if (msg === undefined) {
254
+ continue;
255
+ }
256
+ switch (msg.type) {
257
+ case 'saying_msg':
258
+ case 'thinking_msg':
259
+ case 'func_call_msg': {
260
+ const genseq = Math.floor(msg.genseq);
261
+ if (Number.isFinite(genseq) && genseq > 0) {
262
+ return genseq;
263
+ }
264
+ break;
265
+ }
266
+ default:
267
+ break;
268
+ }
269
+ }
270
+ return undefined;
271
+ }
272
+ function resolveProgrammaticFbrConclusionGenseq(args) {
273
+ return (args.lastAssistantSayingGenseq ??
274
+ args.lastFunctionCallGenseq ??
275
+ resolveLatestModelOutputGenseq(args.dlg) ??
276
+ args.dlg.activeGenSeqOrUndefined ??
277
+ 1);
278
+ }
249
279
  function normalizeQ4HAnswerCallId(raw) {
250
280
  if (typeof raw !== 'string')
251
281
  return undefined;
@@ -750,7 +780,7 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
750
780
  '1. 把需要回传给主线对话的结论、证据定位和风险整理清楚。',
751
781
  '2. 对于下一程恢复工作需要的信息,写入提醒项。',
752
782
  '',
753
- '然后尽快完成当前支线回复;如果你有 clear_mind({}),再调用它开启新一程。',
783
+ '然后调用 clear_mind({}) 开启新一程,并尽快完成当前支线回复。',
754
784
  '',
755
785
  `详情:本次返回约 ${approxBytes} 字节。`,
756
786
  ].join('\n');
@@ -775,7 +805,7 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
775
805
  '1. Organize the conclusions, evidence pointers, and risks that need to go back to the Mainline dialog.',
776
806
  '2. Write any details needed to resume the next course into reminders.',
777
807
  '',
778
- 'Then finish the current Sideline dialog reply as soon as possible; if you have clear_mind({}), call it to start a new course.',
808
+ 'Then call clear_mind({}) to start a new course, and finish the current Sideline dialog reply as soon as possible.',
779
809
  '',
780
810
  `Detail: this return was about ${approxBytes} bytes.`,
781
811
  ].join('\n');
@@ -814,6 +844,9 @@ function countToolResultVisibleBytes(output) {
814
844
  return bytes;
815
845
  }
816
846
  function applyContextHealthToolResultVisibilityLimit(args) {
847
+ if (isFbrSideDialog(args.dlg)) {
848
+ return { output: args.output, largeReturnUnavailable: false };
849
+ }
817
850
  if ((0, context_health_1.getContextHealthRemediationLevel)(args.contextHealth) === undefined) {
818
851
  return { output: args.output, largeReturnUnavailable: false };
819
852
  }
@@ -1381,6 +1414,161 @@ function formatInvalidFuncCallRuntimeGuide(language, call) {
1381
1414
  .filter((line) => line.length > 0)
1382
1415
  .join('\n');
1383
1416
  }
1417
+ function stableJsonStringify(value) {
1418
+ if (value === null)
1419
+ return 'null';
1420
+ if (typeof value === 'string')
1421
+ return JSON.stringify(value);
1422
+ if (typeof value === 'number' || typeof value === 'boolean')
1423
+ return JSON.stringify(value);
1424
+ if (Array.isArray(value)) {
1425
+ return `[${value.map((entry) => stableJsonStringify(entry)).join(',')}]`;
1426
+ }
1427
+ if (typeof value === 'object') {
1428
+ const record = value;
1429
+ return `{${Object.keys(record)
1430
+ .sort()
1431
+ .map((key) => `${JSON.stringify(key)}:${stableJsonStringify(record[key])}`)
1432
+ .join(',')}}`;
1433
+ }
1434
+ return JSON.stringify(String(value));
1435
+ }
1436
+ function canonicalizeToolCallArguments(rawArguments) {
1437
+ try {
1438
+ return stableJsonStringify(JSON.parse(rawArguments));
1439
+ }
1440
+ catch {
1441
+ return rawArguments;
1442
+ }
1443
+ }
1444
+ function canonicalizeToolResult(result) {
1445
+ return stableJsonStringify({
1446
+ content: result.content,
1447
+ contentItems: result.contentItems ?? null,
1448
+ });
1449
+ }
1450
+ function buildToolResultFingerprint(call, result) {
1451
+ return {
1452
+ toolName: call.name,
1453
+ argumentsFingerprint: canonicalizeToolCallArguments(call.arguments),
1454
+ resultFingerprint: canonicalizeToolResult(result),
1455
+ };
1456
+ }
1457
+ function sameToolResultFingerprint(left, right) {
1458
+ return (left.toolName === right.toolName &&
1459
+ left.argumentsFingerprint === right.argumentsFingerprint &&
1460
+ left.resultFingerprint === right.resultFingerprint);
1461
+ }
1462
+ function formatRepeatedToolCallRuntimeGuide(args) {
1463
+ const resultPreview = args.resultContent.length > 800
1464
+ ? `${args.resultContent.slice(0, 800)}\n...`
1465
+ : args.resultContent;
1466
+ if (args.language === 'en') {
1467
+ return [
1468
+ '[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.',
1469
+ '',
1470
+ `- tool: ${args.toolName}`,
1471
+ `- callIds: ${args.callIds.join(', ')}`,
1472
+ '- arguments:',
1473
+ '```json',
1474
+ args.argumentsFingerprint,
1475
+ '```',
1476
+ '- repeated result:',
1477
+ '```text',
1478
+ resultPreview,
1479
+ '```',
1480
+ '',
1481
+ '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.',
1482
+ ].join('\n');
1483
+ }
1484
+ return [
1485
+ '[Dominds 提示] 你刚才连续三次调用了同一个工具,参数完全相同,Dominds 观察到三次工具返回值也完全相同。',
1486
+ '',
1487
+ `- 工具: ${args.toolName}`,
1488
+ `- callIds: ${args.callIds.join(', ')}`,
1489
+ '- 参数:',
1490
+ '```json',
1491
+ args.argumentsFingerprint,
1492
+ '```',
1493
+ '- 重复返回值:',
1494
+ '```text',
1495
+ resultPreview,
1496
+ '```',
1497
+ '',
1498
+ '请先质疑这个行为,不要机械地再次调用同一个工具。同样的调用大概率不会产生新信息。请重新阅读用户诉求和工具返回值,然后自行纠正:基于已有信息作答、换一种行动,或明确说明当前阻塞点。',
1499
+ ].join('\n');
1500
+ }
1501
+ function formatRepeatedToolCallStoppedDetail(args) {
1502
+ if (args.language === 'en') {
1503
+ return (`Stopped because the LLM ignored Dominds' repeated-tool-call correction notice and again made ` +
1504
+ `the same tool call three times with identical results. This indicates an LLM behavior/personality problem: ` +
1505
+ `it is looping on ${args.toolName} instead of reconsidering the task. callIds=${args.callIds.join(', ')}`);
1506
+ }
1507
+ return (`已停止:LLM 已收到 Dominds 的重复工具调用纠正提醒,但仍然再次连续三次调用同一个工具并得到完全相同的结果。` +
1508
+ `这说明当前 LLM 存在行为/个性问题:它在 ${args.toolName} 上机械循环,而不是重新判断任务。` +
1509
+ `callIds=${args.callIds.join(', ')}`);
1510
+ }
1511
+ const REPEATED_TOOL_CALL_STOP_WINDOW_GENSEQS = 5;
1512
+ function inspectRepeatedToolCallRound(args) {
1513
+ ensureRepeatedToolCallMonitorCourse(args.state, args.currentCourse);
1514
+ const recentPairs = [];
1515
+ for (let index = 0; index < args.pairedMessages.length - 1; index += 1) {
1516
+ const call = args.pairedMessages[index];
1517
+ const result = args.pairedMessages[index + 1];
1518
+ if (call?.type !== 'func_call_msg' || result?.type !== 'func_result_msg') {
1519
+ continue;
1520
+ }
1521
+ if (result.id !== call.id) {
1522
+ continue;
1523
+ }
1524
+ recentPairs.push({
1525
+ call,
1526
+ result,
1527
+ fingerprint: buildToolResultFingerprint(call, result),
1528
+ });
1529
+ }
1530
+ for (const pair of recentPairs) {
1531
+ const currentSequence = args.state.sequence;
1532
+ if (currentSequence !== undefined &&
1533
+ sameToolResultFingerprint(currentSequence.fingerprint, pair.fingerprint)) {
1534
+ currentSequence.callIds.push(pair.call.id);
1535
+ if (currentSequence.callIds.length > 3) {
1536
+ currentSequence.callIds.splice(0, currentSequence.callIds.length - 3);
1537
+ }
1538
+ currentSequence.resultContent = pair.result.content;
1539
+ }
1540
+ else {
1541
+ args.state.sequence = {
1542
+ fingerprint: pair.fingerprint,
1543
+ callIds: [pair.call.id],
1544
+ resultContent: pair.result.content,
1545
+ };
1546
+ }
1547
+ const sequence = args.state.sequence;
1548
+ if (sequence === undefined || sequence.callIds.length < 3) {
1549
+ continue;
1550
+ }
1551
+ const recentReminder = args.state.lastReminderGenseq !== undefined &&
1552
+ pair.call.genseq > args.state.lastReminderGenseq &&
1553
+ pair.call.genseq - args.state.lastReminderGenseq <= REPEATED_TOOL_CALL_STOP_WINDOW_GENSEQS;
1554
+ const callIds = [...sequence.callIds];
1555
+ return {
1556
+ toolName: pair.call.name,
1557
+ callIds,
1558
+ argumentsFingerprint: pair.fingerprint.argumentsFingerprint,
1559
+ resultContent: sequence.resultContent,
1560
+ reminderContent: formatRepeatedToolCallRuntimeGuide({
1561
+ language: args.language,
1562
+ toolName: pair.call.name,
1563
+ callIds,
1564
+ argumentsFingerprint: pair.fingerprint.argumentsFingerprint,
1565
+ resultContent: sequence.resultContent,
1566
+ }),
1567
+ repeatedAfterReminder: recentReminder,
1568
+ };
1569
+ }
1570
+ return undefined;
1571
+ }
1384
1572
  async function persistInvalidFuncCallRuntimeGuide(args) {
1385
1573
  const { dlg, call } = args;
1386
1574
  const sourceText = args.source === 'streamed' ? 'streamed' : 'batch';
@@ -1405,6 +1593,17 @@ async function persistInvalidFuncCallRuntimeGuide(args) {
1405
1593
  });
1406
1594
  await persistAndPostRuntimeGuide(dlg, content);
1407
1595
  }
1596
+ function resetRepeatedToolCallMonitor(state) {
1597
+ state.sequence = undefined;
1598
+ state.lastReminderGenseq = undefined;
1599
+ }
1600
+ function ensureRepeatedToolCallMonitorCourse(state, course) {
1601
+ if (state.course === course) {
1602
+ return;
1603
+ }
1604
+ resetRepeatedToolCallMonitor(state);
1605
+ state.course = course;
1606
+ }
1408
1607
  function isQueuedNewCourseRuntimePrompt(prompt) {
1409
1608
  return (prompt?.kind === 'new_course_runtime_guide' ||
1410
1609
  prompt?.kind === 'new_course_runtime_reply' ||
@@ -1458,6 +1657,7 @@ function summarizeRoutedFunctionResult(routed) {
1458
1657
  immediateFollowupCallIds: routed.immediateFollowupCallIds,
1459
1658
  immediateTellaskOutputCallIds: routed.immediateTellaskOutputCallIds,
1460
1659
  invalidTellaskCallIds: routed.invalidTellaskCallIds,
1660
+ repeatedToolCallReminderCallIds: routed.repeatedToolCallReminderCallIds,
1461
1661
  shouldStopAfterReplyTool: routed.shouldStopAfterReplyTool,
1462
1662
  shouldStopAfterPendingTellaskWait: routed.shouldStopAfterPendingTellaskWait,
1463
1663
  pairedMessageTypes: routed.pairedMessages.map((msg) => msg.type),
@@ -1511,6 +1711,8 @@ function shouldCaptureUnexpectedIdleAfterToolRound(args) {
1511
1711
  return true;
1512
1712
  if (diagnostics.routed.immediateTellaskOutputCallIds.length > 0)
1513
1713
  return true;
1714
+ if (diagnostics.routed.repeatedToolCallReminderCallIds.length > 0)
1715
+ return true;
1514
1716
  return false;
1515
1717
  }
1516
1718
  function sanitizeDebugFileSegment(value) {
@@ -1694,6 +1896,12 @@ function buildImmediateFollowupTriggerExpectation(args) {
1694
1896
  callIds: [...invalidRecoveryCallIds],
1695
1897
  });
1696
1898
  }
1899
+ if (args.routed.repeatedToolCallReminderCallIds.length > 0) {
1900
+ reasons.push({
1901
+ kind: 'repeated_tool_call_reminder',
1902
+ callIds: args.routed.repeatedToolCallReminderCallIds,
1903
+ });
1904
+ }
1697
1905
  if (reasons.length === 0) {
1698
1906
  return undefined;
1699
1907
  }
@@ -1786,6 +1994,30 @@ function shouldImmediatelyFollowUpToolOutcome(tool, outcome) {
1786
1994
  }
1787
1995
  return shouldImmediatelyFollowUpSuccessfulToolResult(tool);
1788
1996
  }
1997
+ function isFailedToolOutcome(outcome) {
1998
+ return outcome === 'failure' || outcome === 'partial_failure';
1999
+ }
2000
+ function formatClearMindBlockedByFailedSiblingTools(failedTools) {
2001
+ const language = (0, work_language_1.getWorkLanguage)();
2002
+ const details = failedTools
2003
+ .map((tool) => `- ${tool.toolName} (callId=${tool.callId}, outcome=${String(tool.outcome)})`)
2004
+ .join('\n');
2005
+ return language === 'zh'
2006
+ ? [
2007
+ '错误:本轮 clear_mind 与其它工具一起调用,但其它工具返回了失败结果。',
2008
+ '',
2009
+ details,
2010
+ '',
2011
+ 'clear_mind 已拒绝开启新一程。请先确保其它工具调用正常完成(必要时修正参数、重试或处理失败),然后再次调用 clear_mind。',
2012
+ ].join('\n')
2013
+ : [
2014
+ 'Error: clear_mind was called in the same round as other tools, and at least one other tool returned a failure result.',
2015
+ '',
2016
+ details,
2017
+ '',
2018
+ 'clear_mind refused to start a new course. Ensure the other tool calls complete normally first (fix arguments, retry, or handle the failure), then call clear_mind again.',
2019
+ ].join('\n');
2020
+ }
1789
2021
  function trimOptionalCallId(value) {
1790
2022
  if (typeof value !== 'string')
1791
2023
  return undefined;
@@ -1958,7 +2190,7 @@ async function executeFunctionCalls(args) {
1958
2190
  throwIfAborted(args.abortSignal, args.dlg);
1959
2191
  await args.dlg.persistFunctionCall(prepared.func.id, prepared.func.name, prepared.argsStr, prepared.callGenseq, prepared.func.rawId);
1960
2192
  }
1961
- const functionPromises = preparedCalls.map(async ({ func, originalFunc, callGenseq, argsStr, tool, preparedInvocationArgs, }) => {
2193
+ const executePreparedCall = async ({ func, originalFunc, callGenseq, argsStr, tool, preparedInvocationArgs, }) => {
1962
2194
  let result;
1963
2195
  let outcome = 'success';
1964
2196
  let forceImmediateFollowup = false;
@@ -2051,8 +2283,54 @@ async function executeFunctionCalls(args) {
2051
2283
  throw rethrowError;
2052
2284
  }
2053
2285
  return { func, originalFunc, outcome, forceImmediateFollowup, result };
2054
- });
2055
- return await Promise.all(functionPromises);
2286
+ };
2287
+ const blockClearMindCall = async (prepared, failedTools) => {
2288
+ const output = (0, tool_1.toolFailure)(formatClearMindBlockedByFailedSiblingTools(failedTools));
2289
+ const result = {
2290
+ type: 'func_result_msg',
2291
+ id: prepared.func.id,
2292
+ rawId: prepared.func.rawId,
2293
+ effectiveId: prepared.func.effectiveId,
2294
+ name: prepared.func.name,
2295
+ content: output.content,
2296
+ role: 'tool',
2297
+ genseq: prepared.callGenseq,
2298
+ };
2299
+ await args.dlg.receiveFuncResult(result);
2300
+ return {
2301
+ func: prepared.func,
2302
+ originalFunc: prepared.originalFunc,
2303
+ outcome: output.outcome,
2304
+ forceImmediateFollowup: false,
2305
+ result,
2306
+ };
2307
+ };
2308
+ const clearMindCalls = preparedCalls.filter((call) => call.func.name === CLEAR_MIND_TOOL_NAME);
2309
+ const siblingCalls = preparedCalls.filter((call) => call.func.name !== CLEAR_MIND_TOOL_NAME);
2310
+ const failedPriorToolResults = args.failedPriorToolResults ?? [];
2311
+ if (clearMindCalls.length === 0 || siblingCalls.length === 0) {
2312
+ if (clearMindCalls.length > 0 && failedPriorToolResults.length > 0) {
2313
+ return await Promise.all(clearMindCalls.map((call) => blockClearMindCall(call, failedPriorToolResults)));
2314
+ }
2315
+ return await Promise.all(preparedCalls.map((call) => executePreparedCall(call)));
2316
+ }
2317
+ const siblingExecutions = await Promise.all(siblingCalls.map((call) => executePreparedCall(call)));
2318
+ const failedSiblingToolResults = [
2319
+ ...failedPriorToolResults,
2320
+ ...siblingExecutions
2321
+ .filter((execution) => isFailedToolOutcome(execution.outcome))
2322
+ .map((execution) => ({
2323
+ callId: execution.result.id,
2324
+ toolName: execution.result.name,
2325
+ outcome: execution.outcome,
2326
+ })),
2327
+ ];
2328
+ if (failedSiblingToolResults.length > 0) {
2329
+ const clearMindExecutions = await Promise.all(clearMindCalls.map((call) => blockClearMindCall(call, failedSiblingToolResults)));
2330
+ return [...siblingExecutions, ...clearMindExecutions];
2331
+ }
2332
+ const clearMindExecutions = await Promise.all(clearMindCalls.map((call) => executePreparedCall(call)));
2333
+ return [...siblingExecutions, ...clearMindExecutions];
2056
2334
  }
2057
2335
  async function executeFunctionRound(args) {
2058
2336
  if (args.funcCalls.length === 0) {
@@ -2062,6 +2340,7 @@ async function executeFunctionRound(args) {
2062
2340
  immediateFollowupCallIds: [],
2063
2341
  immediateTellaskOutputCallIds: [],
2064
2342
  invalidTellaskCallIds: [],
2343
+ repeatedToolCallReminderCallIds: [],
2065
2344
  shouldStopAfterReplyTool: false,
2066
2345
  shouldStopAfterPendingTellaskWait: false,
2067
2346
  pairedMessages: [],
@@ -2094,6 +2373,17 @@ async function executeFunctionRound(args) {
2094
2373
  activePromptReplyDirective: args.activePromptReplyDirective,
2095
2374
  });
2096
2375
  throwIfAborted(args.abortSignal, args.dlg);
2376
+ const failedTellaskToolResults = tellaskRound.invalidTellaskCallIds.map((callId) => {
2377
+ const call = args.funcCalls.find((candidate) => candidate.id === callId);
2378
+ if (call === undefined) {
2379
+ throw new Error(`kernel-driver tellask result invariant violation: missing invalid tellask call '${callId}'`);
2380
+ }
2381
+ return {
2382
+ callId,
2383
+ toolName: call.name,
2384
+ outcome: 'failure',
2385
+ };
2386
+ });
2097
2387
  const genericExecutions = await executeFunctionCalls({
2098
2388
  dlg: args.dlg,
2099
2389
  agent: args.agent,
@@ -2101,6 +2391,7 @@ async function executeFunctionRound(args) {
2101
2391
  funcCalls: tellaskRound.normalCalls,
2102
2392
  abortSignal: args.abortSignal,
2103
2393
  contextHealthForToolResultVisibility: args.contextHealthForToolResultVisibility,
2394
+ failedPriorToolResults: failedTellaskToolResults,
2104
2395
  });
2105
2396
  const genericExecutionByOriginalCall = new Map(genericExecutions.map((execution) => [execution.originalFunc, execution]));
2106
2397
  const funcToolByName = new Map(args.agentTools
@@ -2180,6 +2471,7 @@ async function executeFunctionRound(args) {
2180
2471
  immediateFollowupCallIds,
2181
2472
  immediateTellaskOutputCallIds: tellaskRound.immediateTellaskOutputCallIds,
2182
2473
  invalidTellaskCallIds: tellaskRound.invalidTellaskCallIds,
2474
+ repeatedToolCallReminderCallIds: [],
2183
2475
  shouldStopAfterReplyTool: tellaskRound.shouldStopAfterReplyTool,
2184
2476
  shouldStopAfterPendingTellaskWait: tellaskRound.shouldStopAfterPendingTellaskWait,
2185
2477
  pairedMessages,
@@ -2390,6 +2682,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2390
2682
  let fbrConclusion;
2391
2683
  let pubRemindersVer = dlg.remindersVer;
2392
2684
  let lastToolRoundStopDiagnostics;
2685
+ const repeatedToolCallMonitor = {};
2393
2686
  let pendingPrompt = humanPrompt;
2394
2687
  let resolvingImmediateToolResultForUserPrompt = false;
2395
2688
  let resolvingImmediateToolResultUserPromptMsgId;
@@ -2424,8 +2717,41 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2424
2717
  for (;;) {
2425
2718
  genIterNo += 1;
2426
2719
  throwIfAborted(abortSignal, dlg);
2427
- const activeFbrState = await loadDialogFbrState(dlg);
2720
+ let activeFbrState = await loadDialogFbrState(dlg);
2428
2721
  if (isFbrSideDialog(dlg)) {
2722
+ const fbrContextHealthLevel = (0, context_health_1.getContextHealthRemediationLevel)(dlg.getLastContextHealth());
2723
+ if (activeFbrState !== undefined && fbrContextHealthLevel === 'critical') {
2724
+ log_1.log.warn('kernel-driver ending FBR with critical context fixed conclusion', undefined, {
2725
+ rootId: dlg.id.rootId,
2726
+ selfId: dlg.id.selfId,
2727
+ course: dlg.currentCourse,
2728
+ phase: activeFbrState.phase,
2729
+ iteration: activeFbrState.iteration,
2730
+ });
2731
+ fbrConclusion = {
2732
+ responseText: (0, fbr_1.buildProgrammaticFbrContextCriticalContent)({
2733
+ language: (0, work_language_1.getWorkLanguage)(),
2734
+ }),
2735
+ responseGenseq: resolveProgrammaticFbrConclusionGenseq({
2736
+ dlg,
2737
+ lastAssistantSayingGenseq,
2738
+ lastFunctionCallGenseq,
2739
+ }),
2740
+ replyResolutionCallId: `fbr-context-critical-${(0, id_1.generateShortId)()}`,
2741
+ };
2742
+ await persistDialogFbrState(dlg, undefined);
2743
+ dlg.setFbrConclusionToolsEnabled(false);
2744
+ finalDisplayState = await (0, dialog_display_state_1.computeIdleDisplayState)(dlg);
2745
+ break driveCoreLoop;
2746
+ }
2747
+ if (activeFbrState !== undefined &&
2748
+ fbrContextHealthLevel === 'caution' &&
2749
+ !(0, fbr_1.isFbrContextCautionFinalizationState)(activeFbrState) &&
2750
+ (pendingPrompt === undefined || pendingPrompt.origin === 'runtime')) {
2751
+ activeFbrState = (0, fbr_1.forceFbrContextCautionFinalizationState)(activeFbrState);
2752
+ await persistDialogFbrState(dlg, activeFbrState);
2753
+ pendingPrompt = buildKernelDriverFbrPrompt(dlg, activeFbrState);
2754
+ }
2429
2755
  dlg.setFbrConclusionToolsEnabled(activeFbrState !== undefined && (0, fbr_1.isFbrFinalizationState)(activeFbrState));
2430
2756
  if (pendingPrompt === undefined &&
2431
2757
  activeFbrState &&
@@ -2591,6 +2917,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2591
2917
  let llmGenModelForGen = model;
2592
2918
  const currentPrompt = pendingPrompt;
2593
2919
  const currentReplyTarget = currentPrompt?.calleeDialogReplyTarget;
2920
+ if (currentGenerationBelongsToUserPrompt) {
2921
+ resetRepeatedToolCallMonitor(repeatedToolCallMonitor);
2922
+ }
2594
2923
  let currentBusinessContinuation = driveOptions?.businessContinuation ?? { kind: 'none' };
2595
2924
  if (currentPrompt?.tellaskReplyDirective !== undefined) {
2596
2925
  currentBusinessContinuation = {
@@ -2605,7 +2934,10 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2605
2934
  let currentRuntimeGuideMsg;
2606
2935
  const currentPromptFromFbrState = currentPrompt !== undefined &&
2607
2936
  currentFbrState !== undefined &&
2608
- currentFbrState.promptDelivered !== true;
2937
+ currentFbrState.promptDelivered !== true &&
2938
+ isFbrSideDialog(dlg) &&
2939
+ currentPrompt.origin === 'runtime' &&
2940
+ currentPrompt.content === buildKernelDriverFbrPrompt(dlg, currentFbrState).content;
2609
2941
  pendingPrompt = undefined;
2610
2942
  if (currentPrompt) {
2611
2943
  const diligenceSkipReason = await shouldSkipDiligencePromptBeforeGeneration({
@@ -3472,7 +3804,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3472
3804
  : currentGenerationBelongsToUserToolChain
3473
3805
  ? currentUserPromptMsgId
3474
3806
  : undefined;
3475
- const routed = await executeFunctionRound({
3807
+ let routed = await executeFunctionRound({
3476
3808
  dlg,
3477
3809
  agent,
3478
3810
  agentTools,
@@ -3518,6 +3850,41 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3518
3850
  newMsgs.push(...routed.pairedMessages);
3519
3851
  }
3520
3852
  await dlg.addChatMessages(...newMsgs);
3853
+ const repeatedToolCallInspection = inspectRepeatedToolCallRound({
3854
+ state: repeatedToolCallMonitor,
3855
+ currentCourse: dlg.activeGenCourseOrUndefined ?? dlg.currentCourse,
3856
+ pairedMessages: routed.pairedMessages,
3857
+ language: (0, work_language_1.getWorkLanguage)(),
3858
+ });
3859
+ if (repeatedToolCallInspection !== undefined) {
3860
+ if (repeatedToolCallInspection.repeatedAfterReminder) {
3861
+ const detail = formatRepeatedToolCallStoppedDetail({
3862
+ language: (0, work_language_1.getWorkLanguage)(),
3863
+ toolName: repeatedToolCallInspection.toolName,
3864
+ callIds: repeatedToolCallInspection.callIds,
3865
+ });
3866
+ log_1.log.error('kernel-driver stopped after repeated identical tool calls ignored guidance', undefined, {
3867
+ rootId: dlg.id.rootId,
3868
+ selfId: dlg.id.selfId,
3869
+ course: dlg.activeGenCourseOrUndefined ?? dlg.currentCourse,
3870
+ genseq: dlg.activeGenSeq,
3871
+ toolName: repeatedToolCallInspection.toolName,
3872
+ callIds: repeatedToolCallInspection.callIds,
3873
+ });
3874
+ await dlg.streamError(detail);
3875
+ throw new gen_1.LlmStreamErrorEmittedError({
3876
+ detail,
3877
+ i18nStopReason: (0, stop_reason_i18n_1.buildHumanSystemStopReasonTextI18n)({ detail }),
3878
+ });
3879
+ }
3880
+ await persistAndEmitRuntimeGuide(dlg, repeatedToolCallInspection.reminderContent);
3881
+ repeatedToolCallMonitor.lastReminderGenseq = dlg.activeGenSeq;
3882
+ repeatedToolCallMonitor.sequence = undefined;
3883
+ routed = {
3884
+ ...routed,
3885
+ repeatedToolCallReminderCallIds: repeatedToolCallInspection.callIds,
3886
+ };
3887
+ }
3521
3888
  const persistedFbrState = await loadDialogFbrState(dlg);
3522
3889
  if (persistedFbrState) {
3523
3890
  if (persistedFbrState.phase === 'finalization') {
@@ -3564,10 +3931,11 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3564
3931
  language: (0, work_language_1.getWorkLanguage)(),
3565
3932
  finalizationAttempts: persistedFbrState.effort,
3566
3933
  }),
3567
- responseGenseq: lastAssistantSayingGenseq ??
3568
- lastFunctionCallGenseq ??
3569
- dlg.activeGenSeqOrUndefined ??
3570
- 1,
3934
+ responseGenseq: resolveProgrammaticFbrConclusionGenseq({
3935
+ dlg,
3936
+ lastAssistantSayingGenseq,
3937
+ lastFunctionCallGenseq,
3938
+ }),
3571
3939
  replyResolutionCallId: `fbr-conclusion-${(0, id_1.generateShortId)()}`,
3572
3940
  };
3573
3941
  if (!isFbrSideDialog(dlg)) {
@@ -3608,6 +3976,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3608
3976
  // trigger another immediate generation round by themselves.
3609
3977
  const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls ||
3610
3978
  routed.hasImmediateTellaskOutputs ||
3979
+ routed.repeatedToolCallReminderCallIds.length > 0 ||
3611
3980
  invalidFuncCallCount > 0;
3612
3981
  if (shouldStartImmediatePostToolGeneration) {
3613
3982
  const expectation = buildImmediateFollowupTriggerExpectation({
@@ -3622,6 +3991,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
3622
3991
  `(dialog=${dlg.id.valueOf()}, course=${String(dlg.activeGenCourseOrUndefined ?? dlg.currentCourse)}, ` +
3623
3992
  `genseq=${String(dlg.activeGenSeq)}, immediateToolCalls=${String(routed.hasImmediateFollowupToolCalls)}, ` +
3624
3993
  `immediateTellaskOutputs=${String(routed.hasImmediateTellaskOutputs)}, ` +
3994
+ `repeatedToolCallReminderCallIds=${routed.repeatedToolCallReminderCallIds.join('+')}, ` +
3625
3995
  `invalidFuncCallCount=${String(invalidFuncCallCount)})`);
3626
3996
  }
3627
3997
  immediateFollowupTriggerExpectation = expectation;
@@ -32,6 +32,13 @@ export declare function buildFbrFinalizationPrompt(args: {
32
32
  total: number;
33
33
  language: LanguageCode;
34
34
  }): string;
35
+ export declare function buildFbrContextCautionFinalizationPrompt(args: {
36
+ attempt: number;
37
+ language: LanguageCode;
38
+ }): string;
39
+ export declare function buildProgrammaticFbrContextCriticalContent(args: {
40
+ language: LanguageCode;
41
+ }): string;
35
42
  export declare function buildProgrammaticFbrUnreasonableSituationContent(args: {
36
43
  language: LanguageCode;
37
44
  finalizationAttempts: number;
@@ -40,6 +47,8 @@ export declare function inspectFbrConclusionAttempt(messages: readonly ChatMessa
40
47
  export declare function createInitialFbrState(effort: number): DialogFbrState;
41
48
  export declare function markFbrPromptDelivered(state: DialogFbrState): DialogFbrState;
42
49
  export declare function isFbrFinalizationState(state: DialogFbrState): boolean;
50
+ export declare function isFbrContextCautionFinalizationState(state: DialogFbrState): boolean;
51
+ export declare function forceFbrContextCautionFinalizationState(state: DialogFbrState): DialogFbrState;
43
52
  export declare function advanceFbrState(state: DialogFbrState): DialogFbrState | undefined;
44
53
  export declare function buildFbrPromptForState(args: {
45
54
  state: DialogFbrState;