dominds 1.26.5 → 1.27.0

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 (120) hide show
  1. package/dist/docs/dialog-system.md +9 -6
  2. package/dist/docs/dialog-system.zh.md +10 -7
  3. package/dist/llm/gen/mock.js +10 -3
  4. package/dist/llm/gen.d.ts +4 -0
  5. package/dist/llm/kernel-driver/drive.js +164 -97
  6. package/dist/llm/kernel-driver/flow.js +53 -230
  7. package/dist/llm/kernel-driver/reminder-context.d.ts +0 -2
  8. package/dist/llm/kernel-driver/reminder-context.js +0 -4
  9. package/dist/llm/kernel-driver/reply-guidance.d.ts +1 -8
  10. package/dist/llm/kernel-driver/reply-guidance.js +8 -27
  11. package/dist/llm/kernel-driver/sideDialog.js +0 -4
  12. package/dist/llm/kernel-driver/tellask-special.d.ts +22 -2
  13. package/dist/llm/kernel-driver/tellask-special.js +122 -4
  14. package/dist/llm/kernel-driver/types.d.ts +3 -2
  15. package/dist/minds/system-prompt.js +10 -6
  16. package/dist/persistence.d.ts +2 -3
  17. package/dist/persistence.js +26 -76
  18. package/dist/runtime/driver-messages.d.ts +0 -2
  19. package/dist/runtime/driver-messages.js +2 -10
  20. package/dist/runtime/interjection-pause-stop.js +7 -8
  21. package/dist/runtime/reply-prompt-copy.d.ts +7 -9
  22. package/dist/runtime/reply-prompt-copy.js +48 -50
  23. package/dist/server/dominds-self-update.js +46 -4
  24. package/dist/server/websocket-handler.js +0 -2
  25. package/package.json +3 -3
  26. package/webapp/dist/assets/{_basePickBy-zyTFuJGa.js → _basePickBy-B5vClVxA.js} +3 -3
  27. package/webapp/dist/assets/{_basePickBy-zyTFuJGa.js.map → _basePickBy-B5vClVxA.js.map} +1 -1
  28. package/webapp/dist/assets/{_baseUniq-nq34n_7W.js → _baseUniq-CFvt8kJS.js} +2 -2
  29. package/webapp/dist/assets/{_baseUniq-nq34n_7W.js.map → _baseUniq-CFvt8kJS.js.map} +1 -1
  30. package/webapp/dist/assets/{arc-Bv7_22Od.js → arc-tJQBTeDz.js} +2 -2
  31. package/webapp/dist/assets/{arc-Bv7_22Od.js.map → arc-tJQBTeDz.js.map} +1 -1
  32. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CQe-mbPP.js → architectureDiagram-2XIMDMQ5-DHh9S187.js} +7 -7
  33. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CQe-mbPP.js.map → architectureDiagram-2XIMDMQ5-DHh9S187.js.map} +1 -1
  34. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-CX4Uu1s4.js → blockDiagram-WCTKOSBZ-BbiZ5Eja.js} +7 -7
  35. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-CX4Uu1s4.js.map → blockDiagram-WCTKOSBZ-BbiZ5Eja.js.map} +1 -1
  36. package/webapp/dist/assets/{c4Diagram-IC4MRINW-75vPtXd7.js → c4Diagram-IC4MRINW-DzSSDB-G.js} +3 -3
  37. package/webapp/dist/assets/{c4Diagram-IC4MRINW-75vPtXd7.js.map → c4Diagram-IC4MRINW-DzSSDB-G.js.map} +1 -1
  38. package/webapp/dist/assets/{channel-lMsMuju8.js → channel-XrOKEkK6.js} +2 -2
  39. package/webapp/dist/assets/{channel-lMsMuju8.js.map → channel-XrOKEkK6.js.map} +1 -1
  40. package/webapp/dist/assets/{chunk-4BX2VUAB-I371XlIM.js → chunk-4BX2VUAB-DEe7fLDb.js} +2 -2
  41. package/webapp/dist/assets/{chunk-4BX2VUAB-I371XlIM.js.map → chunk-4BX2VUAB-DEe7fLDb.js.map} +1 -1
  42. package/webapp/dist/assets/{chunk-55IACEB6-GMAfwcRQ.js → chunk-55IACEB6-DS9LKRkK.js} +2 -2
  43. package/webapp/dist/assets/{chunk-55IACEB6-GMAfwcRQ.js.map → chunk-55IACEB6-DS9LKRkK.js.map} +1 -1
  44. package/webapp/dist/assets/{chunk-FMBD7UC4-X9ms5_sJ.js → chunk-FMBD7UC4-zmHWjnBv.js} +2 -2
  45. package/webapp/dist/assets/{chunk-FMBD7UC4-X9ms5_sJ.js.map → chunk-FMBD7UC4-zmHWjnBv.js.map} +1 -1
  46. package/webapp/dist/assets/{chunk-JSJVCQXG-s2a971Od.js → chunk-JSJVCQXG-CHhshmdw.js} +2 -2
  47. package/webapp/dist/assets/{chunk-JSJVCQXG-s2a971Od.js.map → chunk-JSJVCQXG-CHhshmdw.js.map} +1 -1
  48. package/webapp/dist/assets/{chunk-KX2RTZJC-CSSBs5H9.js → chunk-KX2RTZJC-BrS-M9-j.js} +2 -2
  49. package/webapp/dist/assets/{chunk-KX2RTZJC-CSSBs5H9.js.map → chunk-KX2RTZJC-BrS-M9-j.js.map} +1 -1
  50. package/webapp/dist/assets/{chunk-NQ4KR5QH-B8-jtfW1.js → chunk-NQ4KR5QH-aQEIAgyi.js} +4 -4
  51. package/webapp/dist/assets/{chunk-NQ4KR5QH-B8-jtfW1.js.map → chunk-NQ4KR5QH-aQEIAgyi.js.map} +1 -1
  52. package/webapp/dist/assets/{chunk-QZHKN3VN-3ZZehtFv.js → chunk-QZHKN3VN-NZNwDEz0.js} +2 -2
  53. package/webapp/dist/assets/{chunk-QZHKN3VN-3ZZehtFv.js.map → chunk-QZHKN3VN-NZNwDEz0.js.map} +1 -1
  54. package/webapp/dist/assets/{chunk-WL4C6EOR-D_AtipPw.js → chunk-WL4C6EOR-BA4Gts3V.js} +6 -6
  55. package/webapp/dist/assets/{chunk-WL4C6EOR-D_AtipPw.js.map → chunk-WL4C6EOR-BA4Gts3V.js.map} +1 -1
  56. package/webapp/dist/assets/{classDiagram-VBA2DB6C-CTxhuDjN.js → classDiagram-VBA2DB6C-BnW_okvO.js} +7 -7
  57. package/webapp/dist/assets/{classDiagram-VBA2DB6C-CTxhuDjN.js.map → classDiagram-VBA2DB6C-BnW_okvO.js.map} +1 -1
  58. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-CTxhuDjN.js → classDiagram-v2-RAHNMMFH-BnW_okvO.js} +7 -7
  59. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-CTxhuDjN.js.map → classDiagram-v2-RAHNMMFH-BnW_okvO.js.map} +1 -1
  60. package/webapp/dist/assets/{clone-C9MfpI1S.js → clone-yPnqRTsi.js} +2 -2
  61. package/webapp/dist/assets/{clone-C9MfpI1S.js.map → clone-yPnqRTsi.js.map} +1 -1
  62. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-CCYyTSie.js → cose-bilkent-S5V4N54A-CIVtbB4S.js} +2 -2
  63. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-CCYyTSie.js.map → cose-bilkent-S5V4N54A-CIVtbB4S.js.map} +1 -1
  64. package/webapp/dist/assets/{dagre-KLK3FWXG-DxM19yfA.js → dagre-KLK3FWXG-ChT-W9Qx.js} +7 -7
  65. package/webapp/dist/assets/{dagre-KLK3FWXG-DxM19yfA.js.map → dagre-KLK3FWXG-ChT-W9Qx.js.map} +1 -1
  66. package/webapp/dist/assets/{diagram-E7M64L7V-BSc4Txf5.js → diagram-E7M64L7V-BN8EY3l2.js} +8 -8
  67. package/webapp/dist/assets/{diagram-E7M64L7V-BSc4Txf5.js.map → diagram-E7M64L7V-BN8EY3l2.js.map} +1 -1
  68. package/webapp/dist/assets/{diagram-IFDJBPK2-Cdj6m54B.js → diagram-IFDJBPK2-Vf6QwbQQ.js} +7 -7
  69. package/webapp/dist/assets/{diagram-IFDJBPK2-Cdj6m54B.js.map → diagram-IFDJBPK2-Vf6QwbQQ.js.map} +1 -1
  70. package/webapp/dist/assets/{diagram-P4PSJMXO-BKd75i65.js → diagram-P4PSJMXO-CjMHY4Wx.js} +7 -7
  71. package/webapp/dist/assets/{diagram-P4PSJMXO-BKd75i65.js.map → diagram-P4PSJMXO-CjMHY4Wx.js.map} +1 -1
  72. package/webapp/dist/assets/{erDiagram-INFDFZHY-eI9rTfbL.js → erDiagram-INFDFZHY-CdziCsxU.js} +5 -5
  73. package/webapp/dist/assets/{erDiagram-INFDFZHY-eI9rTfbL.js.map → erDiagram-INFDFZHY-CdziCsxU.js.map} +1 -1
  74. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-BFDDBCeb.js → flowDiagram-PKNHOUZH-BAe40rMw.js} +7 -7
  75. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-BFDDBCeb.js.map → flowDiagram-PKNHOUZH-BAe40rMw.js.map} +1 -1
  76. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-ClhbFgCt.js → ganttDiagram-A5KZAMGK-CXdjtDXe.js} +3 -3
  77. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-ClhbFgCt.js.map → ganttDiagram-A5KZAMGK-CXdjtDXe.js.map} +1 -1
  78. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-DOiQN8_s.js → gitGraphDiagram-K3NZZRJ6-U_Rn0HLE.js} +8 -8
  79. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-DOiQN8_s.js.map → gitGraphDiagram-K3NZZRJ6-U_Rn0HLE.js.map} +1 -1
  80. package/webapp/dist/assets/{graph-_a0YpojF.js → graph-HwBjKopa.js} +3 -3
  81. package/webapp/dist/assets/{graph-_a0YpojF.js.map → graph-HwBjKopa.js.map} +1 -1
  82. package/webapp/dist/assets/{index-Dou-k_rC.js → index-BWHqGclz.js} +34 -34
  83. package/webapp/dist/assets/{index-Dou-k_rC.js.map → index-BWHqGclz.js.map} +1 -1
  84. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-txkiQJKc.js → infoDiagram-LFFYTUFH-Do9srBC6.js} +6 -6
  85. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-txkiQJKc.js.map → infoDiagram-LFFYTUFH-Do9srBC6.js.map} +1 -1
  86. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-D91OZx5x.js → ishikawaDiagram-PHBUUO56-xCtj-FXJ.js} +2 -2
  87. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-D91OZx5x.js.map → ishikawaDiagram-PHBUUO56-xCtj-FXJ.js.map} +1 -1
  88. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-BdmmINf5.js → journeyDiagram-4ABVD52K-B0JWfLyo.js} +5 -5
  89. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-BdmmINf5.js.map → journeyDiagram-4ABVD52K-B0JWfLyo.js.map} +1 -1
  90. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-C-S70Yrl.js → kanban-definition-K7BYSVSG-CVcU1Etk.js} +3 -3
  91. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-C-S70Yrl.js.map → kanban-definition-K7BYSVSG-CVcU1Etk.js.map} +1 -1
  92. package/webapp/dist/assets/{layout-BRJaSqhR.js → layout-aH6Cn20A.js} +5 -5
  93. package/webapp/dist/assets/{layout-BRJaSqhR.js.map → layout-aH6Cn20A.js.map} +1 -1
  94. package/webapp/dist/assets/{linear-DyNGMbe9.js → linear-Ck43kzmo.js} +2 -2
  95. package/webapp/dist/assets/{linear-DyNGMbe9.js.map → linear-Ck43kzmo.js.map} +1 -1
  96. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-sDQWtZwL.js → mindmap-definition-YRQLILUH-6OIwzMyc.js} +4 -4
  97. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-sDQWtZwL.js.map → mindmap-definition-YRQLILUH-6OIwzMyc.js.map} +1 -1
  98. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-CxO-PR0P.js → pieDiagram-SKSYHLDU-B61-NHoC.js} +8 -8
  99. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-CxO-PR0P.js.map → pieDiagram-SKSYHLDU-B61-NHoC.js.map} +1 -1
  100. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-2eCl6G-9.js → quadrantDiagram-337W2JSQ-0eM2SOom.js} +3 -3
  101. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-2eCl6G-9.js.map → quadrantDiagram-337W2JSQ-0eM2SOom.js.map} +1 -1
  102. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-BZP0MViS.js → requirementDiagram-Z7DCOOCP-ChcYQDgM.js} +4 -4
  103. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-BZP0MViS.js.map → requirementDiagram-Z7DCOOCP-ChcYQDgM.js.map} +1 -1
  104. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-BFByQmrg.js → sankeyDiagram-WA2Y5GQK-B9cEGu9g.js} +2 -2
  105. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-BFByQmrg.js.map → sankeyDiagram-WA2Y5GQK-B9cEGu9g.js.map} +1 -1
  106. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-vs-caW9J.js → sequenceDiagram-2WXFIKYE-D1vR0q93.js} +4 -4
  107. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-vs-caW9J.js.map → sequenceDiagram-2WXFIKYE-D1vR0q93.js.map} +1 -1
  108. package/webapp/dist/assets/{stateDiagram-RAJIS63D-B_yOKRoN.js → stateDiagram-RAJIS63D-ylazdvD-.js} +9 -9
  109. package/webapp/dist/assets/{stateDiagram-RAJIS63D-B_yOKRoN.js.map → stateDiagram-RAJIS63D-ylazdvD-.js.map} +1 -1
  110. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-B285fcqL.js → stateDiagram-v2-FVOUBMTO-CxtecSrX.js} +5 -5
  111. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-B285fcqL.js.map → stateDiagram-v2-FVOUBMTO-CxtecSrX.js.map} +1 -1
  112. package/webapp/dist/assets/{timeline-definition-YZTLITO2-B_yCavMb.js → timeline-definition-YZTLITO2-4rofIQXx.js} +3 -3
  113. package/webapp/dist/assets/{timeline-definition-YZTLITO2-B_yCavMb.js.map → timeline-definition-YZTLITO2-4rofIQXx.js.map} +1 -1
  114. package/webapp/dist/assets/{treemap-KZPCXAKY-cErPVx9g.js → treemap-KZPCXAKY-0SBcN6kq.js} +5 -5
  115. package/webapp/dist/assets/{treemap-KZPCXAKY-cErPVx9g.js.map → treemap-KZPCXAKY-0SBcN6kq.js.map} +1 -1
  116. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-C9qgK3p0.js → vennDiagram-LZ73GAT5-CCqbQjX8.js} +2 -2
  117. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-C9qgK3p0.js.map → vennDiagram-LZ73GAT5-CCqbQjX8.js.map} +1 -1
  118. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-B-gCl1Yj.js → xychartDiagram-JWTSCODW-Yl52k-Si.js} +3 -3
  119. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-B-gCl1Yj.js.map → xychartDiagram-JWTSCODW-Yl52k-Si.js.map} +1 -1
  120. package/webapp/dist/index.html +1 -1
@@ -9,6 +9,7 @@ exports.formatTellaskInvalidCallResult = formatTellaskInvalidCallResult;
9
9
  exports.formatPendingTellaskFuncResultContent = formatPendingTellaskFuncResultContent;
10
10
  exports.formatResolvedTellaskFuncResultContent = formatResolvedTellaskFuncResultContent;
11
11
  exports.formatResolvedAskHumanResultContent = formatResolvedAskHumanResultContent;
12
+ exports.recordAnswerToHuman = recordAnswerToHuman;
12
13
  exports.executeTellaskCalls = executeTellaskCalls;
13
14
  exports.processTellaskFunctionRound = processTellaskFunctionRound;
14
15
  const util_1 = require("util");
@@ -37,6 +38,7 @@ const TELLASK_SPECIAL_FUNCTION_NAMES = [
37
38
  'replyTellaskSessionless',
38
39
  'replyTellaskBack',
39
40
  'askHuman',
41
+ 'answerHuman',
40
42
  'freshBootsReasoning',
41
43
  ];
42
44
  const MULTIPLE_ASKHUMAN_CALLS_ERROR = '不允许一轮多次调用 askHuman,必须单次调用问所有问题。 Do not call askHuman multiple times in one round; ask all questions in a single askHuman call.';
@@ -47,6 +49,9 @@ function isTellaskCallFunctionName(name) {
47
49
  function isReplyTellaskCallName(name) {
48
50
  return (name === 'replyTellask' || name === 'replyTellaskSessionless' || name === 'replyTellaskBack');
49
51
  }
52
+ function usesFuncRequestedSpecialLifecycle(name) {
53
+ return isReplyTellaskCallName(name) || name === 'answerHuman';
54
+ }
50
55
  function isRecord(value) {
51
56
  return typeof value === 'object' && value !== null && !Array.isArray(value);
52
57
  }
@@ -420,6 +425,20 @@ function parseTellaskCall(call) {
420
425
  },
421
426
  };
422
427
  }
428
+ case 'answerHuman': {
429
+ const answerContent = readRequiredStringField(args, 'answerContent');
430
+ if (!answerContent.ok) {
431
+ return answerContent;
432
+ }
433
+ return {
434
+ ok: true,
435
+ value: {
436
+ callId: call.id,
437
+ callName: 'answerHuman',
438
+ answerContent: answerContent.value,
439
+ },
440
+ };
441
+ }
423
442
  case 'freshBootsReasoning': {
424
443
  const tellaskContent = readRequiredStringField(args, 'tellaskContent');
425
444
  if (!tellaskContent.ok) {
@@ -2079,6 +2098,85 @@ async function executeReplyTellaskCall(args) {
2079
2098
  }
2080
2099
  }
2081
2100
  }
2101
+ function formatAnswerHumanResultContent(args) {
2102
+ const language = (0, work_language_1.getWorkLanguage)();
2103
+ return language === 'zh'
2104
+ ? `已记录给人类的答复。\n\n${args.answerContent}`
2105
+ : `Recorded the answer for the human.\n\n${args.answerContent}`;
2106
+ }
2107
+ async function recordAnswerToHuman(args) {
2108
+ const answer = {
2109
+ id: `a2h-${Buffer.from(args.answerIdSource).toString('base64url')}`,
2110
+ content: args.answerContent,
2111
+ answeredAt: (0, time_1.formatUnifiedTimestamp)(new Date()),
2112
+ answerRef: {
2113
+ course: (0, storage_1.toDialogCourseNumber)(args.course),
2114
+ genseq: (0, storage_1.toCallSiteGenseqNo)(args.genseq),
2115
+ },
2116
+ };
2117
+ await persistence_1.DialogPersistence.appendAnswerToHumanState(args.dlg.id, answer, args.dlg.status);
2118
+ const metadata = await persistence_1.DialogPersistence.loadDialogMetadata(args.dlg.id, args.dlg.status);
2119
+ const taskDocPath = metadata?.taskDocPath ?? args.dlg.taskDocPath ?? '';
2120
+ const event = {
2121
+ type: 'new_a2h_answered',
2122
+ answer: {
2123
+ ...answer,
2124
+ selfId: args.dlg.id.selfId,
2125
+ rootId: args.dlg.id.rootId,
2126
+ agentId: metadata?.agentId ?? args.dlg.agentId,
2127
+ taskDocPath,
2128
+ },
2129
+ };
2130
+ (0, evt_registry_1.postDialogEvent)(args.dlg, event);
2131
+ await persistence_1.DialogPersistence.mutateDialogLatest(args.dlg.id, (previous) => {
2132
+ const previousPending = previous.pendingUserInterjectionReply;
2133
+ if (previousPending === undefined) {
2134
+ return { kind: 'noop' };
2135
+ }
2136
+ const next = { ...previous };
2137
+ delete next.pendingUserInterjectionReply;
2138
+ return { kind: 'replace', next };
2139
+ }, args.dlg.status);
2140
+ return answer;
2141
+ }
2142
+ async function executeAnswerHumanCall(args) {
2143
+ const genseq = args.dlg.activeGenSeqOrUndefined ?? 1;
2144
+ const course = args.dlg.activeGenCourseOrUndefined ?? args.dlg.currentCourse;
2145
+ const answerContent = args.call.answerContent;
2146
+ const answer = await recordAnswerToHuman({
2147
+ dlg: args.dlg,
2148
+ answerContent,
2149
+ course,
2150
+ genseq,
2151
+ answerIdSource: [
2152
+ args.dlg.id.rootId,
2153
+ args.dlg.id.selfId,
2154
+ `c${String(course)}`,
2155
+ `g${String(genseq)}`,
2156
+ `answerHuman:${args.call.callId}`,
2157
+ args.call.callId,
2158
+ ].join('|'),
2159
+ });
2160
+ return {
2161
+ messages: [
2162
+ {
2163
+ type: 'func_result_msg',
2164
+ role: 'tool',
2165
+ genseq,
2166
+ id: args.call.callId,
2167
+ name: args.call.callName,
2168
+ content: formatAnswerHumanResultContent({ answerContent }),
2169
+ },
2170
+ ],
2171
+ answering: {
2172
+ callId: args.call.callId,
2173
+ answerContent,
2174
+ course,
2175
+ genseq,
2176
+ answer,
2177
+ },
2178
+ };
2179
+ }
2082
2180
  function toExecutableValidTellaskCall(call) {
2083
2181
  switch (call.callName) {
2084
2182
  case 'tellaskBack':
@@ -2118,6 +2216,12 @@ function toExecutableValidTellaskCall(call) {
2118
2216
  tellaskContent: call.tellaskContent,
2119
2217
  callId: call.callId,
2120
2218
  };
2219
+ case 'answerHuman':
2220
+ return {
2221
+ callName: call.callName,
2222
+ answerContent: call.answerContent,
2223
+ callId: call.callId,
2224
+ };
2121
2225
  case 'freshBootsReasoning':
2122
2226
  return {
2123
2227
  callName: call.callName,
@@ -2130,6 +2234,7 @@ function toExecutableValidTellaskCall(call) {
2130
2234
  async function executeValidTellaskCalls(args) {
2131
2235
  const results = [];
2132
2236
  const successfulReplyCallIds = [];
2237
+ const answerHumanOutputs = [];
2133
2238
  const deferredScheduleCalls = [];
2134
2239
  const registrationPhaseCallbacks = {
2135
2240
  driveDialog: args.callbacks.driveDialog,
@@ -2148,11 +2253,12 @@ async function executeValidTellaskCalls(args) {
2148
2253
  case 'replyTellaskSessionless':
2149
2254
  case 'replyTellaskBack':
2150
2255
  case 'askHuman':
2256
+ case 'answerHuman':
2151
2257
  case 'freshBootsReasoning':
2152
2258
  return undefined;
2153
2259
  }
2154
2260
  })();
2155
- if (!isReplyTellaskCallName(call.callName)) {
2261
+ if (!usesFuncRequestedSpecialLifecycle(call.callName)) {
2156
2262
  const nonReplyCall = call;
2157
2263
  const sessionSlug = nonReplyCall.callName === 'tellask' ? nonReplyCall.sessionSlug : undefined;
2158
2264
  await emitTellaskCallEvents({
@@ -2190,6 +2296,15 @@ async function executeValidTellaskCalls(args) {
2190
2296
  results.push(replyResult.messages);
2191
2297
  continue;
2192
2298
  }
2299
+ case 'answerHuman': {
2300
+ const answerResult = await executeAnswerHumanCall({
2301
+ dlg: args.dlg,
2302
+ call,
2303
+ });
2304
+ results.push(answerResult.messages);
2305
+ answerHumanOutputs.push(answerResult.answering);
2306
+ continue;
2307
+ }
2193
2308
  case 'tellask': {
2194
2309
  const targetAgentId = call.targetAgentId;
2195
2310
  if (targetAgentId.trim() === '') {
@@ -2239,11 +2354,12 @@ async function executeValidTellaskCalls(args) {
2239
2354
  return {
2240
2355
  toolOutputs: results.flatMap((result) => result),
2241
2356
  successfulReplyCallIds,
2357
+ answerHumanOutputs,
2242
2358
  };
2243
2359
  }
2244
2360
  async function executeTellaskCalls(args) {
2245
2361
  if (args.calls.length === 0) {
2246
- return { toolOutputs: [], successfulReplyCallIds: [] };
2362
+ return { toolOutputs: [], successfulReplyCallIds: [], answerHumanOutputs: [] };
2247
2363
  }
2248
2364
  return await executeValidTellaskCalls({
2249
2365
  dlg: args.dlg,
@@ -2316,7 +2432,7 @@ async function processTellaskFunctionRound(args) {
2316
2432
  if (disposition.kind === 'valid') {
2317
2433
  const handled = disposition.handled;
2318
2434
  await args.dlg.persistTellaskCall(handled.originalCall.id, handled.call.callName, getRawArgumentsText(handled.originalCall), handled.originalCall.genseq, {
2319
- deliveryMode: isReplyTellaskCallName(handled.call.callName)
2435
+ deliveryMode: usesFuncRequestedSpecialLifecycle(handled.call.callName)
2320
2436
  ? 'func_call_requested'
2321
2437
  : 'tellask_call_start',
2322
2438
  ...(isReplyTellaskCallName(handled.call.callName) &&
@@ -2415,7 +2531,8 @@ async function processTellaskFunctionRound(args) {
2415
2531
  tellaskFuncResultByCallId.set(result.id, result);
2416
2532
  tellaskFuncResults.push(result);
2417
2533
  const originatingCall = specialCallById.get(result.id);
2418
- if (originatingCall !== undefined && isReplyTellaskCallName(originatingCall.callName)) {
2534
+ if (originatingCall !== undefined &&
2535
+ usesFuncRequestedSpecialLifecycle(originatingCall.callName)) {
2419
2536
  continue;
2420
2537
  }
2421
2538
  hasImmediateTellaskOutputs = true;
@@ -2483,6 +2600,7 @@ async function processTellaskFunctionRound(args) {
2483
2600
  hasImmediateTellaskOutputs,
2484
2601
  immediateTellaskOutputCallIds,
2485
2602
  invalidTellaskCallIds: orderedInvalidCalls.map((issue) => issue.originalCall.id),
2603
+ answerHumanOutputs: tellaskExecution.answerHumanOutputs,
2486
2604
  shouldStopAfterReplyTool: tellaskExecution.successfulReplyCallIds.length > 0,
2487
2605
  shouldStopAfterPendingTellaskWait,
2488
2606
  };
@@ -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 { AnswerToHumanItem, CallSiteCourseNo, CallSiteGenseqNo, DialogBusinessContinuation } from '@longrun-ai/kernel/types/storage';
3
+ import type { CallSiteCourseNo, CallSiteGenseqNo, DialogBusinessContinuation } from '@longrun-ai/kernel/types/storage';
4
4
  import type { Dialog, DialogID } from '../../dialog';
5
5
  export type KernelDriverRunControl = DialogRunControlSpec;
6
6
  export type KernelDriverDriveSource = 'unspecified' | 'ws_user_message' | 'ws_user_answer' | 'ws_diligence_push' | 'ws_resume_dialog' | 'ws_resume_all' | 'kernel_driver_backend_loop' | 'kernel_driver_follow_up' | 'kernel_driver_sideDialog_init' | 'kernel_driver_sideDialog_resume' | 'kernel_driver_fbr_sideDialog_round' | 'kernel_driver_type_a_askerDialog_call' | 'kernel_driver_business_continuation' | 'kernel_driver_idle_reminder_wake';
@@ -96,10 +96,11 @@ export type KernelDriverCoreResult = {
96
96
  lastAssistantSayingGenseq: number | null;
97
97
  lastAssistantThinkingContent: string | null;
98
98
  lastAssistantThinkingGenseq: number | null;
99
+ lastAssistantAnsweringContent: string | null;
100
+ lastAssistantAnsweringGenseq: number | null;
99
101
  lastFunctionCallGenseq: number | null;
100
102
  lastAssistantReplyTarget?: KernelDriverCalleeReplyTarget;
101
103
  lastBusinessContinuation: DialogBusinessContinuation;
102
- answeredUserInterjection?: AnswerToHumanItem;
103
104
  fbrConclusion?: {
104
105
  responseText: string;
105
106
  responseGenseq: number;
@@ -212,13 +212,13 @@ function buildTellaskCollaborationProtocol(language, dialogScope) {
212
212
  const lines = [
213
213
  ...pickLocalized(language, {
214
214
  zh: [
215
- '- Tellask 统一走函数工具通道:`tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `freshBootsReasoning`。',
215
+ '- Tellask special 统一走函数工具通道:`tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `answerHuman` / `freshBootsReasoning`。',
216
216
  '- 对队友诉请默认使用 `tellask` 并复用 `sessionSlug`;仅在确认一次性诉请足够、且后续不需要更新任务安排/提前收口时才使用 `tellaskSessionless`,并需说明理由。',
217
217
  '- 并行协调默认允许:不要把智能体队友当成真人同事;祂不会因为你又发一条诉请而被打扰。同一个队友 + 同一个 `sessionSlug` = 接着同一件事说;`tellaskSessionless` 或不同 `sessionSlug` = 另一件独立任务。',
218
218
  '- 例外优先级(强制):`tellaskBack` 仅用于回问诉请者,不适用队友长线默认规则,也不携带 `sessionSlug`。',
219
219
  ],
220
220
  en: [
221
- '- Tellask must use the function-tool channel: `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `freshBootsReasoning`.',
221
+ '- Tellask special calls must use the function-tool channel: `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `answerHuman` / `freshBootsReasoning`.',
222
222
  '- For teammate tellasks, default to `tellask` and continue with the same `sessionSlug`; use `tellaskSessionless` only when a one-shot is truly sufficient and later assignment updates / early wrap-up are not needed.',
223
223
  '- Parallel coordination is allowed by default: do not treat an agent teammate like a human coworker who can only handle one conversation at a time. Same teammate + same `sessionSlug` = continue the same task; `tellaskSessionless` or a different `sessionSlug` = another independent task.',
224
224
  '- Mandatory exception precedence: `tellaskBack` is ask-back-only and outside the teammate-session default; it does not carry `sessionSlug`.',
@@ -246,7 +246,7 @@ function buildFbrGuidelines(language, dialogScope, contextHealthPromptMode) {
246
246
  zh: [
247
247
  '- FBR 由 `freshBootsReasoning` 触发,不属于普通队友诉请分类;请按本节规则执行。',
248
248
  '- FBR 不可调用 `tellaskBack`;其回贴标记由 Dominds 在跨对话传递正文中自动注入。',
249
- '- FBR 禁止一切 tellask(包括 `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman`)。',
249
+ '- FBR 禁止一切 tellask-special(包括 `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `answerHuman`)。',
250
250
  '- 当用户明确要求“做一次 FBR/扪心自问”,对话主理人必须发起 `freshBootsReasoning`。',
251
251
  fbrContextHealthRule,
252
252
  '- FBR 的标准入口是 `freshBootsReasoning({ tellaskContent, effort? })`;禁止用 `tellask` / `tellaskSessionless` 对自己发起 self-target 诉请来替代。',
@@ -263,7 +263,7 @@ function buildFbrGuidelines(language, dialogScope, contextHealthPromptMode) {
263
263
  en: [
264
264
  '- FBR is triggered by `freshBootsReasoning`, not by normal teammate tellasks; follow this section’s rules.',
265
265
  '- FBR cannot call `tellaskBack`; its reply marker is injected by Dominds into the inter-dialog transfer payload.',
266
- '- FBR forbids all tellask calls (including `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman`).',
266
+ '- FBR forbids all tellask-special calls (including `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `answerHuman`).',
267
267
  '- When the user explicitly requests “do an FBR / fresh boots reasoning”, the Dialog Responder must call `freshBootsReasoning`.',
268
268
  fbrContextHealthRule,
269
269
  '- The standard FBR entry is `freshBootsReasoning({ tellaskContent, effort? })`; do not emulate FBR via self-targeted `tellask` / `tellaskSessionless`.',
@@ -287,6 +287,7 @@ function buildTellaskInteractionRules(language) {
287
287
  '- `tellask`:用于可恢复的长线诉请(必须提供 `targetAgentId` / `sessionSlug` / `tellaskContent`)。',
288
288
  '- `tellaskSessionless`:用于一次性诉请(必须提供 `targetAgentId` / `tellaskContent`);它不能接着旧任务改要求,后续再次调用只是另一件独立任务,不会影响旧任务继续执行,也不会打扰同一队友正在执行的其它独立诉请。不要把智能体队友当成需要排队说话的真人同事。',
289
289
  '- `askHuman`:用于 Q4H(向人类请求必要澄清/决策/授权/缺失输入)。',
290
+ '- `answerHuman`:用于把当前要给人类看的答复记录为 A2H(answer to human);不要用它向队友回贴。',
290
291
  '- `freshBootsReasoning`:用于发起扪心自问(FBR)支线(`tellaskContent` 必填,`effort` 可选)。',
291
292
  ],
292
293
  en: [
@@ -294,6 +295,7 @@ function buildTellaskInteractionRules(language) {
294
295
  '- `tellask`: resumable tellask (requires `targetAgentId` / `sessionSlug` / `tellaskContent`).',
295
296
  '- `tellaskSessionless`: one-shot tellask (requires `targetAgentId` / `tellaskContent`); it cannot continue an earlier task or change its requirements. Later calls are separate tasks and do not affect earlier work for the same teammate. Do not treat agent teammates like human coworkers who need you to wait in line to talk.',
296
297
  '- `askHuman`: Q4H for necessary clarification/decision/authorization/missing input.',
298
+ '- `answerHuman`: record the current human-facing answer as A2H (answer to human); do not use it to reply to teammates.',
297
299
  '- `freshBootsReasoning`: starts an FBR Side Dialog (requires `tellaskContent`, optional `effort`).',
298
300
  ],
299
301
  });
@@ -406,6 +408,7 @@ function buildSystemPrompt(input) {
406
408
  - 回问诉请:支线对话用 \`tellaskBack\` 回问诉请者以澄清。
407
409
  - 扪心自问(FBR):由 \`freshBootsReasoning\` 触发的“无工具”支线推理机制。
408
410
  - 向人请示(Q4H):通过 \`askHuman\` 向人类请求必要的澄清/决策/授权/缺失输入。
411
+ - 答复人类(A2H):通过 \`answerHuman\` 记录当前要给人类看的答复。
409
412
  - 长线诉请:使用 \`tellask\` + \`sessionSlug\` 的可恢复多轮协作。
410
413
  - 一次性诉请:一次性、不可恢复的诉请。
411
414
  - 主线对话:承载共享差遣牒并负责整体推进的对话。
@@ -466,7 +469,7 @@ ${input.toolsetManualIntro}
466
469
 
467
470
  ## 交互协议
468
471
 
469
- ### Tellask Special Functions(队友/FBR/Q4H)
472
+ ### Tellask Special Functions(队友/FBR/Q4H/A2H
470
473
  ${tellaskInteractionRules}
471
474
 
472
475
  ### 函数工具(仅原生 function-calling)
@@ -505,6 +508,7 @@ System notices convey important state changes (e.g., context caution/critical, D
505
508
  - TellaskBack: a Side Dialog uses \`tellaskBack\` to ask the tellasker for clarification.
506
509
  - Fresh Boots Reasoning (FBR): a tool-less Side Dialog reasoning mechanism triggered by \`freshBootsReasoning\`.
507
510
  - Q4H (Question for Human): use \`askHuman\` to request necessary clarification/decision/authorization/missing input from a human.
511
+ - A2H (Answer to Human): use \`answerHuman\` to record the current human-facing answer.
508
512
  - Tellask Session: resumable multi-turn work using \`tellask\` with \`sessionSlug\`.
509
513
  - Fresh Tellask: a one-shot, non-resumable Tellask.
510
514
  - Main Dialog: the dialog that owns the shared Taskdoc and overall progress.
@@ -565,7 +569,7 @@ ${input.toolsetManualIntro}
565
569
 
566
570
  ## Interaction Protocols
567
571
 
568
- ### Tellask Special Functions (teammates/FBR/Q4H)
572
+ ### Tellask Special Functions (teammates/FBR/Q4H/A2H)
569
573
  ${tellaskInteractionRules}
570
574
 
571
575
  ### Function Tools (native function-calling only)
@@ -157,7 +157,7 @@ export declare class DiskFileDialogStore extends DialogStore {
157
157
  * Persist a function call to storage
158
158
  */
159
159
  persistFunctionCall(dialog: Dialog, id: string, name: string, rawArgumentsText: string, genseq: number, rawId?: string): Promise<void>;
160
- persistTellaskCall(dialog: Dialog, id: string, name: 'tellaskBack' | 'tellask' | 'tellaskSessionless' | 'replyTellask' | 'replyTellaskSessionless' | 'replyTellaskBack' | 'askHuman' | 'freshBootsReasoning', rawArgumentsText: string, genseq: number, options?: {
160
+ persistTellaskCall(dialog: Dialog, id: string, name: 'tellaskBack' | 'tellask' | 'tellaskSessionless' | 'replyTellask' | 'replyTellaskSessionless' | 'replyTellaskBack' | 'askHuman' | 'answerHuman' | 'freshBootsReasoning', rawArgumentsText: string, genseq: number, options?: {
161
161
  deliveryMode?: 'tellask_call_start' | 'func_call_requested';
162
162
  replyDirective?: TellaskReplyDirective;
163
163
  }): Promise<void>;
@@ -710,8 +710,6 @@ export declare class DialogPersistence {
710
710
  static removeNextStepTriggers(dialogId: DialogID, predicate: (trigger: DialogNextStepTrigger) => boolean, status?: DialogStatusKind): Promise<void>;
711
711
  static hasPendingNextStepTriggers(dialogId: DialogID, status?: DialogStatusKind): Promise<boolean>;
712
712
  static clearPendingRuntimePrompt(dialogId: DialogID, msgId: string, status?: DialogStatusKind): Promise<void>;
713
- static setDeferredReplyReassertion(dialogId: DialogID, deferredReplyReassertion: DialogLatestFile['deferredReplyReassertion'], status?: DialogStatusKind): Promise<void>;
714
- static getDeferredReplyReassertion(dialogId: DialogID, status?: DialogStatusKind): Promise<DialogLatestFile['deferredReplyReassertion']>;
715
713
  /**
716
714
  * Get course filename from course number
717
715
  */
@@ -752,6 +750,7 @@ export declare class DialogPersistence {
752
750
  course: number;
753
751
  pending: DialogLatestFile['pendingUserInterjectionReply'];
754
752
  }): AnswerToHumanItem | undefined;
753
+ private static answerRefSettlesPendingUserInterjection;
755
754
  private static normalizeDialogLatestPendingUserInterjectionReply;
756
755
  /**
757
756
  * Reconstruct dialog state from JSONL events (optimized: only latest course needed).
@@ -1909,6 +1909,7 @@ function isTellaskCallIndexCallName(value) {
1909
1909
  value === 'replyTellaskSessionless' ||
1910
1910
  value === 'replyTellaskBack' ||
1911
1911
  value === 'askHuman' ||
1912
+ value === 'answerHuman' ||
1912
1913
  value === 'freshBootsReasoning');
1913
1914
  }
1914
1915
  function parseDialogTellaskCallState(value) {
@@ -2243,36 +2244,6 @@ function parseDialogLatestFile(value) {
2243
2244
  })();
2244
2245
  if (fbrState === null)
2245
2246
  return null;
2246
- const deferredReplyReassertionRaw = value.deferredReplyReassertion;
2247
- const deferredReplyReassertion = (() => {
2248
- if (deferredReplyReassertionRaw === undefined)
2249
- return undefined;
2250
- if (!isRecord(deferredReplyReassertionRaw))
2251
- return null;
2252
- if (deferredReplyReassertionRaw.reason !== 'user_interjection_with_parked_original_task') {
2253
- return null;
2254
- }
2255
- const directive = parseTellaskReplyDirective(deferredReplyReassertionRaw.directive);
2256
- if (directive === null)
2257
- return null;
2258
- const userInterjection = parseDialogPendingUserInterjectionReply(deferredReplyReassertionRaw.userInterjection);
2259
- if (userInterjection === null)
2260
- return null;
2261
- const resumeGuideSurfacedRaw = deferredReplyReassertionRaw.resumeGuideSurfaced;
2262
- if (resumeGuideSurfacedRaw !== undefined && typeof resumeGuideSurfacedRaw !== 'boolean') {
2263
- return null;
2264
- }
2265
- return {
2266
- reason: 'user_interjection_with_parked_original_task',
2267
- directive,
2268
- userInterjection,
2269
- ...(resumeGuideSurfacedRaw === undefined
2270
- ? {}
2271
- : { resumeGuideSurfaced: resumeGuideSurfacedRaw }),
2272
- };
2273
- })();
2274
- if (deferredReplyReassertion === null)
2275
- return null;
2276
2247
  const pendingRuntimePromptRaw = value.pendingRuntimePrompt;
2277
2248
  const pendingRuntimePrompt = (() => {
2278
2249
  if (pendingRuntimePromptRaw === undefined)
@@ -2301,7 +2272,6 @@ function parseDialogLatestFile(value) {
2301
2272
  latestAssignmentAnchor,
2302
2273
  sideDialogFinalResponse,
2303
2274
  fbrState,
2304
- deferredReplyReassertion,
2305
2275
  pendingUserInterjectionReply,
2306
2276
  pendingRuntimePrompt,
2307
2277
  disableDiligencePush: value.disableDiligencePush,
@@ -2456,20 +2426,6 @@ function isAnswerToHumanItem(value) {
2456
2426
  return false;
2457
2427
  if (typeof value.answeredAt !== 'string')
2458
2428
  return false;
2459
- if (!isRecord(value.userInterjection))
2460
- return false;
2461
- if (typeof value.userInterjection.msgId !== 'string')
2462
- return false;
2463
- if (typeof value.userInterjection.course !== 'number')
2464
- return false;
2465
- if (!Number.isInteger(value.userInterjection.course) || value.userInterjection.course <= 0) {
2466
- return false;
2467
- }
2468
- if (typeof value.userInterjection.genseq !== 'number')
2469
- return false;
2470
- if (!Number.isInteger(value.userInterjection.genseq) || value.userInterjection.genseq <= 0) {
2471
- return false;
2472
- }
2473
2429
  if (!isRecord(value.answerRef))
2474
2430
  return false;
2475
2431
  if (typeof value.answerRef.course !== 'number')
@@ -2595,6 +2551,7 @@ function isTellaskCallFunctionName(name) {
2595
2551
  name === 'replyTellaskSessionless' ||
2596
2552
  name === 'replyTellaskBack' ||
2597
2553
  name === 'askHuman' ||
2554
+ name === 'answerHuman' ||
2598
2555
  name === 'freshBootsReasoning');
2599
2556
  }
2600
2557
  /**
@@ -3635,10 +3592,6 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
3635
3592
  targetCallId: payload.targetCallId,
3636
3593
  };
3637
3594
  await this.appendEvent(dialog, course, record);
3638
- const deferredReplyReassertion = await DialogPersistence.getDeferredReplyReassertion(dialog.id, dialog.status);
3639
- if (deferredReplyReassertion?.directive.targetCallId === payload.targetCallId) {
3640
- await DialogPersistence.setDeferredReplyReassertion(dialog.id, undefined, dialog.status);
3641
- }
3642
3595
  const activeObligation = await DialogPersistence.loadActiveTellaskReplyObligation(dialog.id, dialog.status);
3643
3596
  if (activeObligation?.targetCallId === payload.targetCallId) {
3644
3597
  await DialogPersistence.setActiveTellaskReplyObligation(dialog.id, undefined, dialog.status);
@@ -6583,7 +6536,15 @@ class DialogPersistence {
6583
6536
  filePath: answersFilePath,
6584
6537
  });
6585
6538
  }
6586
- return parsed.answers;
6539
+ return parsed.answers.map((answer) => ({
6540
+ id: answer.id,
6541
+ content: answer.content,
6542
+ answeredAt: answer.answeredAt,
6543
+ answerRef: {
6544
+ course: answer.answerRef.course,
6545
+ genseq: answer.answerRef.genseq,
6546
+ },
6547
+ }));
6587
6548
  }
6588
6549
  catch (error) {
6589
6550
  if (getErrorCode(error) === 'ENOENT')
@@ -6668,9 +6629,6 @@ class DialogPersistence {
6668
6629
  if (answerId === '') {
6669
6630
  throw new Error(`A2H append invariant violation: empty answer id (dialog=${dialogId.valueOf()})`);
6670
6631
  }
6671
- if (answer.content.trim() === '') {
6672
- throw new Error(`A2H append invariant violation: empty answer content (dialog=${dialogId.valueOf()} answerId=${answer.id})`);
6673
- }
6674
6632
  await this.mutateAnswersToHumanState(dialogId, (previousAnswers) => {
6675
6633
  const existing = previousAnswers.find((item) => item.id === answer.id);
6676
6634
  if (existing) {
@@ -9191,23 +9149,6 @@ class DialogPersistence {
9191
9149
  };
9192
9150
  }, status);
9193
9151
  }
9194
- static async setDeferredReplyReassertion(dialogId, deferredReplyReassertion, status = 'running') {
9195
- await this.mutateDialogLatest(dialogId, (previous) => {
9196
- if (deferredReplyReassertion !== undefined) {
9197
- return { kind: 'patch', patch: { deferredReplyReassertion } };
9198
- }
9199
- if (previous.deferredReplyReassertion === undefined) {
9200
- return { kind: 'noop' };
9201
- }
9202
- const next = { ...previous };
9203
- delete next.deferredReplyReassertion;
9204
- return { kind: 'replace', next };
9205
- }, status);
9206
- }
9207
- static async getDeferredReplyReassertion(dialogId, status = 'running') {
9208
- const latest = await this.loadDialogLatest(dialogId, status);
9209
- return latest?.deferredReplyReassertion;
9210
- }
9211
9152
  // === FILE SYSTEM UTILITIES ===
9212
9153
  /**
9213
9154
  * Get course filename from course number
@@ -9400,21 +9341,31 @@ class DialogPersistence {
9400
9341
  id: `a2h-${Buffer.from(answerIdSource).toString('base64url')}`,
9401
9342
  content: lastVisibleAnswer.content,
9402
9343
  answeredAt: (0, time_1.formatUnifiedTimestamp)(new Date()),
9403
- userInterjection: {
9404
- msgId: pendingUserInterjectionReply.msgId,
9405
- course: pendingUserInterjectionReply.course,
9406
- genseq: pendingUserInterjectionReply.genseq,
9407
- },
9408
9344
  answerRef: {
9409
9345
  course: (0, storage_1.toDialogCourseNumber)(args.course),
9410
9346
  genseq: (0, storage_1.toCallSiteGenseqNo)(lastVisibleAnswer.genseq),
9411
9347
  },
9412
9348
  };
9413
9349
  }
9350
+ static answerRefSettlesPendingUserInterjection(answerRef, pending) {
9351
+ if (pending === undefined) {
9352
+ return false;
9353
+ }
9354
+ if (answerRef.course > pending.course) {
9355
+ return true;
9356
+ }
9357
+ return answerRef.course === pending.course && answerRef.genseq >= pending.genseq;
9358
+ }
9414
9359
  static async normalizeDialogLatestPendingUserInterjectionReply(dialogId, status, latest) {
9415
9360
  if (latest.pendingUserInterjectionReply === undefined) {
9416
9361
  return latest;
9417
9362
  }
9363
+ const existingAnswers = await this.loadAnswersToHumanState(dialogId, status);
9364
+ if (existingAnswers.some((answer) => this.answerRefSettlesPendingUserInterjection(answer.answerRef, latest.pendingUserInterjectionReply))) {
9365
+ const next = { ...latest };
9366
+ delete next.pendingUserInterjectionReply;
9367
+ return next;
9368
+ }
9418
9369
  const events = await this.readCourseEvents(dialogId, latest.currentCourse, status);
9419
9370
  const normalizedPending = this.resolvePendingUserInterjectionReplyFromCourseEvents(events, latest.currentCourse);
9420
9371
  if (normalizedPending === undefined) {
@@ -9425,7 +9376,6 @@ class DialogPersistence {
9425
9376
  pending: latest.pendingUserInterjectionReply,
9426
9377
  });
9427
9378
  if (recoveredAnswer !== undefined) {
9428
- const existingAnswers = await this.loadAnswersToHumanState(dialogId, status);
9429
9379
  if (!existingAnswers.some((item) => item.id === recoveredAnswer.id)) {
9430
9380
  await this.appendAnswerToHumanState(dialogId, recoveredAnswer, status);
9431
9381
  }
@@ -41,8 +41,6 @@ export type ReminderContextBusiness = Readonly<{
41
41
  kind: 'pending_user_interjection';
42
42
  }> | Readonly<{
43
43
  kind: 'pending_user_interjection_with_active_reply';
44
- }> | Readonly<{
45
- kind: 'pending_user_interjection_with_parked_reply';
46
44
  }> | Readonly<{
47
45
  kind: 'user_followup_after_completed_handoff';
48
46
  }>;
@@ -218,10 +218,6 @@ function formatZhReminderBusinessTail(business) {
218
218
  // would block a valid user request and recreate the same kind of over-specific guidance
219
219
  // bug in the opposite direction.
220
220
  return '现在是用户在追问你。前面那件转交任务已经回报完成了,不需要再推进;请按用户这条消息正常交流和处理。';
221
- case 'pending_user_interjection_with_parked_reply':
222
- // A real user message interrupted an unfinished handoff. The old handoff is parked,
223
- // so the model must not rush back to reply closure before the user sees an answer.
224
- return '当前仍有真实用户插话尚未得到可见回复,且原有回贴任务已暂存;先完成对用户插话的回应,不要抢先切回原来的回贴收口。';
225
221
  case 'pending_user_interjection':
226
222
  // No special handoff state is competing with the user; the shortest useful instruction
227
223
  // is simply to answer the still-unanswered user interjection.
@@ -263,10 +259,6 @@ function formatEnReminderBusinessTail(business) {
263
259
  // would block a valid user request and recreate the same kind of over-specific guidance
264
260
  // bug in the opposite direction.
265
261
  return 'The user is asking you a follow-up now. The earlier handed-off task has already been reported back as complete, so there is nothing more to advance there. Talk with the user normally and handle this current message.';
266
- case 'pending_user_interjection_with_parked_reply':
267
- // A real user message interrupted an unfinished handoff. The old handoff is parked,
268
- // so the model must not rush back to reply closure before the user sees an answer.
269
- return "There is still a real user interjection without a visible reply, and the earlier handoff is parked; finish answering the user's interjection first, and do not switch back to closing the earlier reply yet.";
270
262
  case 'pending_user_interjection':
271
263
  // No special handoff state is competing with the user; the shortest useful instruction
272
264
  // is simply to answer the still-unanswered user interjection.
@@ -958,7 +950,7 @@ function formatDomindsNoteFbrToollessViolation(language, args) {
958
950
  `Dominds 提示:当前是扪心自问(FBR)支线对话(无工具模式)。${detail}`,
959
951
  '',
960
952
  '- 本对话无任何工具:禁止函数工具调用。',
961
- '- 本对话禁止任何 tellask-special 函数(包括 `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman`)。',
953
+ '- 本对话禁止任何 tellask-special 函数(包括 `tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `answerHuman`)。',
962
954
  '- 请只基于诉请正文(以及本支线对话自身的会话历史,如有)进行推理与总结。',
963
955
  ].join('\n');
964
956
  }
@@ -974,7 +966,7 @@ function formatDomindsNoteFbrToollessViolation(language, args) {
974
966
  `Dominds note: this is a tool-less FBR Side Dialog (triggered by \`freshBootsReasoning\`). ${detail}`,
975
967
  '',
976
968
  '- No tools are available: do not emit function tool calls.',
977
- '- No tellask-special functions are allowed (`tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman`).',
969
+ '- No tellask-special functions are allowed (`tellaskBack` / `tellask` / `tellaskSessionless` / `askHuman` / `answerHuman`).',
978
970
  '- Provide pure reasoning and a summary grounded in the tellask body (and this Side Dialog’s own tellaskSession history, if any).',
979
971
  ].join('\n');
980
972
  }
@@ -5,13 +5,12 @@ exports.isUserInterjectionPauseStopReason = isUserInterjectionPauseStopReason;
5
5
  const USER_INTERJECTION_PAUSE_STOP_DETAIL = 'user_interjection_pause_resume_original_task';
6
6
  // WARNING:
7
7
  // This special stop reason is only a UI/run-control projection for legacy paused-interjection
8
- // state. New answered-interjection flow reasserts the reply obligation automatically instead of
9
- // depending on this stop reason. It is intentionally encoded as `system_stop`, but it does NOT mean
10
- // the same thing as ordinary system-stop failure semantics.
8
+ // state. New answered-interjection flow depends on pending interjection settlement plus ordinary
9
+ // drive rules instead of this stop reason. It is intentionally encoded as `system_stop`, but it
10
+ // does NOT mean the same thing as ordinary system-stop failure semantics.
11
11
  //
12
- // Not every user interjection should use this reason. If there is no parked original task to
13
- // resume afterwards, the interjection should simply complete and the dialog should fall back to
14
- // its true underlying state without showing this resumption panel.
12
+ // New user interjections should simply complete and the dialog should fall back to its true
13
+ // underlying state without showing this legacy resumption panel.
15
14
  //
16
15
  // In particular, askHuman answers are NOT "user interjections" for this purpose. A prompt carrying
17
16
  // a real `q4hAnswerCallId` belongs to the askHuman reply channel and must never be routed through
@@ -19,8 +18,8 @@ const USER_INTERJECTION_PAUSE_STOP_DETAIL = 'user_interjection_pause_resume_orig
19
18
  //
20
19
  // Do not change this file in isolation. The complete behavior depends on coordinated logic across:
21
20
  // - `reply-guidance.ts` suppressing tellasker reply obligation during interjection chat
22
- // - `flow.ts` answering locally, then automatically reasserting reply obligation
23
- // - `dialog-display-state.ts` preserving legacy paused projection until Continue
21
+ // - `flow.ts` answering locally, then using ordinary continuation rules
22
+ // - `dialog-display-state.ts` preserving legacy paused projection until Continue during recovery
24
23
  // - `websocket-handler.ts` treating Continue as "resume attempt" rather than immediate success
25
24
  //
26
25
  // Reading only this stop reason or only `displayState.kind === 'stopped'` gives an incomplete and