dominds 1.24.1 → 1.24.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 (107) hide show
  1. package/dist/dialog-drive-work.d.ts +1 -0
  2. package/dist/dialog-drive-work.js +20 -1
  3. package/dist/docs/tellask-background-continuation-refactor.zh.md +144 -15
  4. package/dist/llm/kernel-driver/drive.js +35 -2
  5. package/dist/llm/kernel-driver/flow.js +296 -78
  6. package/dist/llm/kernel-driver/reply-guidance.js +0 -3
  7. package/dist/llm/kernel-driver/tellask-special.d.ts +1 -0
  8. package/dist/llm/kernel-driver/tellask-special.js +3 -0
  9. package/dist/llm/kernel-driver/types.d.ts +0 -3
  10. package/dist/persistence.d.ts +2 -0
  11. package/dist/persistence.js +57 -31
  12. package/package.json +3 -3
  13. package/webapp/dist/assets/{_basePickBy-LdPzzrA2.js → _basePickBy-ZLV93S3E.js} +3 -3
  14. package/webapp/dist/assets/{_basePickBy-LdPzzrA2.js.map → _basePickBy-ZLV93S3E.js.map} +1 -1
  15. package/webapp/dist/assets/{_baseUniq-Dd4qkGWy.js → _baseUniq-D0wSOJ06.js} +2 -2
  16. package/webapp/dist/assets/{_baseUniq-Dd4qkGWy.js.map → _baseUniq-D0wSOJ06.js.map} +1 -1
  17. package/webapp/dist/assets/{arc-Bx35xRdo.js → arc-BHclbMTS.js} +2 -2
  18. package/webapp/dist/assets/{arc-Bx35xRdo.js.map → arc-BHclbMTS.js.map} +1 -1
  19. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CqJear9j.js → architectureDiagram-2XIMDMQ5-CK99gE_D.js} +7 -7
  20. package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-CqJear9j.js.map → architectureDiagram-2XIMDMQ5-CK99gE_D.js.map} +1 -1
  21. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-9tLmXg6W.js → blockDiagram-WCTKOSBZ-fE5MBTEU.js} +7 -7
  22. package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-9tLmXg6W.js.map → blockDiagram-WCTKOSBZ-fE5MBTEU.js.map} +1 -1
  23. package/webapp/dist/assets/{c4Diagram-IC4MRINW-Dzv3iLb1.js → c4Diagram-IC4MRINW-BSLyPyoU.js} +3 -3
  24. package/webapp/dist/assets/{c4Diagram-IC4MRINW-Dzv3iLb1.js.map → c4Diagram-IC4MRINW-BSLyPyoU.js.map} +1 -1
  25. package/webapp/dist/assets/{channel-DlYmbJjD.js → channel-DSvMpp-a.js} +2 -2
  26. package/webapp/dist/assets/{channel-DlYmbJjD.js.map → channel-DSvMpp-a.js.map} +1 -1
  27. package/webapp/dist/assets/{chunk-4BX2VUAB-H-sbLEAZ.js → chunk-4BX2VUAB-OXEX170k.js} +2 -2
  28. package/webapp/dist/assets/{chunk-4BX2VUAB-H-sbLEAZ.js.map → chunk-4BX2VUAB-OXEX170k.js.map} +1 -1
  29. package/webapp/dist/assets/{chunk-55IACEB6-DM99Uhn7.js → chunk-55IACEB6-BFQ_spQD.js} +2 -2
  30. package/webapp/dist/assets/{chunk-55IACEB6-DM99Uhn7.js.map → chunk-55IACEB6-BFQ_spQD.js.map} +1 -1
  31. package/webapp/dist/assets/{chunk-FMBD7UC4-BrYi5v3v.js → chunk-FMBD7UC4-CbQ2BBPs.js} +2 -2
  32. package/webapp/dist/assets/{chunk-FMBD7UC4-BrYi5v3v.js.map → chunk-FMBD7UC4-CbQ2BBPs.js.map} +1 -1
  33. package/webapp/dist/assets/{chunk-JSJVCQXG-xsaAqb_D.js → chunk-JSJVCQXG-C4P1mjCL.js} +2 -2
  34. package/webapp/dist/assets/{chunk-JSJVCQXG-xsaAqb_D.js.map → chunk-JSJVCQXG-C4P1mjCL.js.map} +1 -1
  35. package/webapp/dist/assets/{chunk-KX2RTZJC-DP9JzJzb.js → chunk-KX2RTZJC-BMd-daMY.js} +2 -2
  36. package/webapp/dist/assets/{chunk-KX2RTZJC-DP9JzJzb.js.map → chunk-KX2RTZJC-BMd-daMY.js.map} +1 -1
  37. package/webapp/dist/assets/{chunk-NQ4KR5QH-B1Dmz0tx.js → chunk-NQ4KR5QH-B_ZhWMXR.js} +4 -4
  38. package/webapp/dist/assets/{chunk-NQ4KR5QH-B1Dmz0tx.js.map → chunk-NQ4KR5QH-B_ZhWMXR.js.map} +1 -1
  39. package/webapp/dist/assets/{chunk-QZHKN3VN-1BrJQ9T8.js → chunk-QZHKN3VN-Cbf92xIw.js} +2 -2
  40. package/webapp/dist/assets/{chunk-QZHKN3VN-1BrJQ9T8.js.map → chunk-QZHKN3VN-Cbf92xIw.js.map} +1 -1
  41. package/webapp/dist/assets/{chunk-WL4C6EOR-m9798qm6.js → chunk-WL4C6EOR-PtH-blkK.js} +6 -6
  42. package/webapp/dist/assets/{chunk-WL4C6EOR-m9798qm6.js.map → chunk-WL4C6EOR-PtH-blkK.js.map} +1 -1
  43. package/webapp/dist/assets/{classDiagram-VBA2DB6C-DunVoVHy.js → classDiagram-VBA2DB6C-Dc3ncaD0.js} +7 -7
  44. package/webapp/dist/assets/{classDiagram-VBA2DB6C-DunVoVHy.js.map → classDiagram-VBA2DB6C-Dc3ncaD0.js.map} +1 -1
  45. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-DunVoVHy.js → classDiagram-v2-RAHNMMFH-Dc3ncaD0.js} +7 -7
  46. package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-DunVoVHy.js.map → classDiagram-v2-RAHNMMFH-Dc3ncaD0.js.map} +1 -1
  47. package/webapp/dist/assets/{clone-CxYNYmli.js → clone-E9Ad85BC.js} +2 -2
  48. package/webapp/dist/assets/{clone-CxYNYmli.js.map → clone-E9Ad85BC.js.map} +1 -1
  49. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-CnD5vxv9.js → cose-bilkent-S5V4N54A-B-nj0o74.js} +2 -2
  50. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-CnD5vxv9.js.map → cose-bilkent-S5V4N54A-B-nj0o74.js.map} +1 -1
  51. package/webapp/dist/assets/{dagre-KLK3FWXG-D8iLo6g8.js → dagre-KLK3FWXG-CyJYNIbm.js} +7 -7
  52. package/webapp/dist/assets/{dagre-KLK3FWXG-D8iLo6g8.js.map → dagre-KLK3FWXG-CyJYNIbm.js.map} +1 -1
  53. package/webapp/dist/assets/{diagram-E7M64L7V-CCGf4yS4.js → diagram-E7M64L7V-C8eweQ7b.js} +8 -8
  54. package/webapp/dist/assets/{diagram-E7M64L7V-CCGf4yS4.js.map → diagram-E7M64L7V-C8eweQ7b.js.map} +1 -1
  55. package/webapp/dist/assets/{diagram-IFDJBPK2-nZVTTM2s.js → diagram-IFDJBPK2-DMdygRl0.js} +7 -7
  56. package/webapp/dist/assets/{diagram-IFDJBPK2-nZVTTM2s.js.map → diagram-IFDJBPK2-DMdygRl0.js.map} +1 -1
  57. package/webapp/dist/assets/{diagram-P4PSJMXO-DUG05oR4.js → diagram-P4PSJMXO-BQDZHb0a.js} +7 -7
  58. package/webapp/dist/assets/{diagram-P4PSJMXO-DUG05oR4.js.map → diagram-P4PSJMXO-BQDZHb0a.js.map} +1 -1
  59. package/webapp/dist/assets/{erDiagram-INFDFZHY-DTtcqarO.js → erDiagram-INFDFZHY-C1HaXN6E.js} +5 -5
  60. package/webapp/dist/assets/{erDiagram-INFDFZHY-DTtcqarO.js.map → erDiagram-INFDFZHY-C1HaXN6E.js.map} +1 -1
  61. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-Ol68h2lV.js → flowDiagram-PKNHOUZH-24nNqQyo.js} +7 -7
  62. package/webapp/dist/assets/{flowDiagram-PKNHOUZH-Ol68h2lV.js.map → flowDiagram-PKNHOUZH-24nNqQyo.js.map} +1 -1
  63. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-BD9KkhMb.js → ganttDiagram-A5KZAMGK-BWPOFaLV.js} +3 -3
  64. package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-BD9KkhMb.js.map → ganttDiagram-A5KZAMGK-BWPOFaLV.js.map} +1 -1
  65. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-CZ4C5yhB.js → gitGraphDiagram-K3NZZRJ6-D7_L-p_Y.js} +8 -8
  66. package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-CZ4C5yhB.js.map → gitGraphDiagram-K3NZZRJ6-D7_L-p_Y.js.map} +1 -1
  67. package/webapp/dist/assets/{graph-BICDsOFt.js → graph-OHu4dL2n.js} +3 -3
  68. package/webapp/dist/assets/{graph-BICDsOFt.js.map → graph-OHu4dL2n.js.map} +1 -1
  69. package/webapp/dist/assets/{index-BTMpGPk_.js → index-CDCDAfqP.js} +238 -83
  70. package/webapp/dist/assets/{index-BTMpGPk_.js.map → index-CDCDAfqP.js.map} +1 -1
  71. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-D0xrjViS.js → infoDiagram-LFFYTUFH-CvaBM5j6.js} +6 -6
  72. package/webapp/dist/assets/{infoDiagram-LFFYTUFH-D0xrjViS.js.map → infoDiagram-LFFYTUFH-CvaBM5j6.js.map} +1 -1
  73. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-Beaj-xkw.js → ishikawaDiagram-PHBUUO56-DB1l2Uue.js} +2 -2
  74. package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-Beaj-xkw.js.map → ishikawaDiagram-PHBUUO56-DB1l2Uue.js.map} +1 -1
  75. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-k7BmCpXO.js → journeyDiagram-4ABVD52K-TQR6_teO.js} +5 -5
  76. package/webapp/dist/assets/{journeyDiagram-4ABVD52K-k7BmCpXO.js.map → journeyDiagram-4ABVD52K-TQR6_teO.js.map} +1 -1
  77. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-BBQOWu--.js → kanban-definition-K7BYSVSG-B-BOuC-U.js} +3 -3
  78. package/webapp/dist/assets/{kanban-definition-K7BYSVSG-BBQOWu--.js.map → kanban-definition-K7BYSVSG-B-BOuC-U.js.map} +1 -1
  79. package/webapp/dist/assets/{layout-B_h66HyR.js → layout-B8yqIqbx.js} +5 -5
  80. package/webapp/dist/assets/{layout-B_h66HyR.js.map → layout-B8yqIqbx.js.map} +1 -1
  81. package/webapp/dist/assets/{linear-QdCnoDG9.js → linear-CoLfiZKK.js} +2 -2
  82. package/webapp/dist/assets/{linear-QdCnoDG9.js.map → linear-CoLfiZKK.js.map} +1 -1
  83. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-BTk4BxEf.js → mindmap-definition-YRQLILUH-P70BMIHI.js} +4 -4
  84. package/webapp/dist/assets/{mindmap-definition-YRQLILUH-BTk4BxEf.js.map → mindmap-definition-YRQLILUH-P70BMIHI.js.map} +1 -1
  85. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-DQonY6Ln.js → pieDiagram-SKSYHLDU-DsS_4dTB.js} +8 -8
  86. package/webapp/dist/assets/{pieDiagram-SKSYHLDU-DQonY6Ln.js.map → pieDiagram-SKSYHLDU-DsS_4dTB.js.map} +1 -1
  87. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-6tqc3Gus.js → quadrantDiagram-337W2JSQ-DoM9PEq-.js} +3 -3
  88. package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-6tqc3Gus.js.map → quadrantDiagram-337W2JSQ-DoM9PEq-.js.map} +1 -1
  89. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-C6r4anqd.js → requirementDiagram-Z7DCOOCP-Bn3lYMMI.js} +4 -4
  90. package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-C6r4anqd.js.map → requirementDiagram-Z7DCOOCP-Bn3lYMMI.js.map} +1 -1
  91. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-DE2CTSeA.js → sankeyDiagram-WA2Y5GQK-97kCegRT.js} +2 -2
  92. package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-DE2CTSeA.js.map → sankeyDiagram-WA2Y5GQK-97kCegRT.js.map} +1 -1
  93. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-DSnyMSe6.js → sequenceDiagram-2WXFIKYE-DXqjQjf6.js} +4 -4
  94. package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-DSnyMSe6.js.map → sequenceDiagram-2WXFIKYE-DXqjQjf6.js.map} +1 -1
  95. package/webapp/dist/assets/{stateDiagram-RAJIS63D-BsQOXkdZ.js → stateDiagram-RAJIS63D-DQcTPKWP.js} +9 -9
  96. package/webapp/dist/assets/{stateDiagram-RAJIS63D-BsQOXkdZ.js.map → stateDiagram-RAJIS63D-DQcTPKWP.js.map} +1 -1
  97. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-DW0AWykH.js → stateDiagram-v2-FVOUBMTO-DHmxRVJn.js} +5 -5
  98. package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-DW0AWykH.js.map → stateDiagram-v2-FVOUBMTO-DHmxRVJn.js.map} +1 -1
  99. package/webapp/dist/assets/{timeline-definition-YZTLITO2-Dyxr7fE4.js → timeline-definition-YZTLITO2-BlovQQ4B.js} +3 -3
  100. package/webapp/dist/assets/{timeline-definition-YZTLITO2-Dyxr7fE4.js.map → timeline-definition-YZTLITO2-BlovQQ4B.js.map} +1 -1
  101. package/webapp/dist/assets/{treemap-KZPCXAKY-CPaPLIL2.js → treemap-KZPCXAKY-CGu93c9S.js} +5 -5
  102. package/webapp/dist/assets/{treemap-KZPCXAKY-CPaPLIL2.js.map → treemap-KZPCXAKY-CGu93c9S.js.map} +1 -1
  103. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-CTduB1wl.js → vennDiagram-LZ73GAT5-Do1jprrz.js} +2 -2
  104. package/webapp/dist/assets/{vennDiagram-LZ73GAT5-CTduB1wl.js.map → vennDiagram-LZ73GAT5-Do1jprrz.js.map} +1 -1
  105. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-Sb80BTd7.js → xychartDiagram-JWTSCODW-BKa1DxVq.js} +3 -3
  106. package/webapp/dist/assets/{xychartDiagram-JWTSCODW-Sb80BTd7.js.map → xychartDiagram-JWTSCODW-BKa1DxVq.js.map} +1 -1
  107. package/webapp/dist/index.html +1 -1
@@ -1,3 +1,4 @@
1
1
  import type { DialogLatestFile } from '@longrun-ai/kernel/types/storage';
2
2
  export type DialogLatestSnapshot = DialogLatestFile | null;
3
+ export declare function hasRecoverableGenerationBeyondFinalResponse(latest: DialogLatestFile): boolean;
3
4
  export declare function hasDurableDriveWork(latest: DialogLatestSnapshot): boolean;
@@ -1,10 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasRecoverableGenerationBeyondFinalResponse = hasRecoverableGenerationBeyondFinalResponse;
3
4
  exports.hasDurableDriveWork = hasDurableDriveWork;
4
5
  const dialog_generation_run_1 = require("./dialog-generation-run");
5
6
  function hasResultArrivalTrigger(latest) {
6
7
  return latest.nextStep.triggers.some((trigger) => trigger.kind === 'result_arrival');
7
8
  }
9
+ function hasRecoverableGenerationBeyondFinalResponse(latest) {
10
+ if ((0, dialog_generation_run_1.getRecoverableGenerationRunState)(latest) === undefined) {
11
+ return false;
12
+ }
13
+ const finalResponse = latest.sideDialogFinalResponse;
14
+ if (finalResponse === undefined) {
15
+ return true;
16
+ }
17
+ return (latest.pendingRuntimePrompt !== undefined ||
18
+ (latest.latestAssignmentAnchor !== undefined &&
19
+ latest.latestAssignmentAnchor.callId !== finalResponse.callId));
20
+ }
8
21
  function hasDurableDriveWork(latest) {
9
22
  if (!latest) {
10
23
  return false;
@@ -14,8 +27,14 @@ function hasDurableDriveWork(latest) {
14
27
  (replyDelivery.status === 'pending' || replyDelivery.toolResultStatus === 'pending')) {
15
28
  return true;
16
29
  }
30
+ if (latest.pendingRuntimePrompt !== undefined) {
31
+ return true;
32
+ }
33
+ if (hasRecoverableGenerationBeyondFinalResponse(latest)) {
34
+ return true;
35
+ }
17
36
  if (latest.sideDialogFinalResponse !== undefined) {
18
37
  return hasResultArrivalTrigger(latest);
19
38
  }
20
- return (latest.nextStep.triggers.length > 0 || (0, dialog_generation_run_1.getRecoverableGenerationRunState)(latest) !== undefined);
39
+ return latest.nextStep.triggers.length > 0;
21
40
  }
@@ -97,12 +97,62 @@ Diligence Push 是例外:它只作用于主线对话,因为它是编排保
97
97
 
98
98
  因此 `blocker` 只应作为旧代码命名或旧 displayState 的迁移对象被提及,不应成为新设计里的业务概念。
99
99
 
100
+ ### 业务 continuation:只记录意图,轮到时重新 claim
101
+
102
+ 这次重构的核心抽象不是“无 prompt 驱动是否有资格继续”,而是“当前有一个什么业务 continuation 需要尝试处理”。
103
+
104
+ 原则:
105
+
106
+ - continuation 只记录“想继续什么业务”,不记录“创建当时判断过,所以未来一定可以继续”。
107
+ - continuation 轮到执行时,必须基于最新持久事实重新 claim / decide。
108
+ - 事实仍成立:消费 continuation 并继续。
109
+ - 事实已变化:作为 stale continuation 静默丢弃,最多写 `debug` 日志。
110
+ - 事实冲突:说明状态不自洽,按 loud error policy 失败。
111
+ - 事实暂时被先决等待阻塞:保留 continuation 或投影成对应阻塞态,不在调度循环里拼接跨场景规则。
112
+
113
+ 这条原则的价值在于把跨时间线判断局部化封装起来。调度循环只负责按顺序取出 continuation,并把它交给对应业务 handler;handler 自己读取最新状态并决定是否仍然成立。这样可以避免现在的 `source + reason + noPromptSideDialogResumeEntitlement + executionMarker + sideDialogFinalResponse + nextStep` 在一个大 gate 里互相缠绕。
114
+
115
+ 目标形态:
116
+
117
+ ```ts
118
+ type DialogContinuation =
119
+ | { kind: 'user_message'; msgId: string }
120
+ | { kind: 'user_answered_human_request'; questionId: string; callId: string }
121
+ | { kind: 'assignment_arrived'; promptId: string; callId: string }
122
+ | { kind: 'assignment_updated'; promptId: string; callId: string; sessionSlug: string }
123
+ | { kind: 'requested_work_replied'; batchId: string }
124
+ | {
125
+ kind: 'deliver_tellask_reply';
126
+ targetDialogId: string;
127
+ targetCallId: string;
128
+ expectedReplyCallName: 'replyTellask' | 'replyTellaskSessionless' | 'replyTellaskBack';
129
+ }
130
+ | { kind: 'operator_continue_original_task'; dialogId: string }
131
+ | { kind: 'recover_open_generation'; course: number; genseq: number }
132
+ | { kind: 'mainline_diligence'; diligenceId: string };
133
+ ```
134
+
135
+ 这个 union 是业务语义草图,不要求一次性落地成对外协议;目标是约束内部命名和控制流:任何新 continuation 都必须能回答“哪个业务事实到了、轮到时如何按最新事实重新判断”。
136
+
137
+ 典型 claim 规则:
138
+
139
+ - `requested_work_replied(batchId)`:读取 `active-callees.json` / batch 状态。batch 仍 complete 且未消费,则把 batch 结果注入上下文并消费;batch 已删除或已消费,则 stale `debug` 丢弃;batch 状态冲突则 loud fail。
140
+ - `deliver_tellask_reply(targetCallId)`:读取 active reply obligation 和 `sideDialogFinalResponse`。同一 `targetCallId` 的回复义务仍存在且未 final,则注入 reply reminder / reassertion;义务消失、callId 已变化或 final anchor 已覆盖该 callId,则 stale `debug` 丢弃;callId 相同但 expected reply tool / target dialog 不一致,则 loud fail。
141
+ - `operator_continue_original_task`:读取 deferred reply reassertion、active callee、user wait、execution marker 等最新事实。原任务仍被下游阻塞,则恢复真实阻塞态;原任务已不阻塞,则把 deferred reassertion 转成 runtime prompt 并继续;没有 parked original task,则 stale `debug` 丢弃。
142
+ - `recover_open_generation(course,genseq)`:只读取 `generationRunState.open`。同一 open generation 仍存在则恢复;已 closed 则 stale `debug` 丢弃;状态缺失/冲突则 malformed / loud fail。
143
+
144
+ 日志原则:
145
+
146
+ - stale continuation 是正常竞速结果,使用 `debug`。
147
+ - 因状态冲突导致无法判断的 continuation 是一致性问题,使用 loud error / stream error / structured diagnostic。
148
+ - 不把 stale continuation 打成 ERROR;ERROR 应只表示协议/状态机不自洽或 unsafe path。
149
+
100
150
  ### 同类概念也要降级
101
151
 
102
152
  类似 `blocker`,下面几个词也容易把技术症状误升级成业务机制:
103
153
 
104
154
  - `needsDrive`:业务上不是“这个 dialog 需要被驱动”的抽象状态,而是有一个具体下一步动作来源:用户输入、queued prompt、主线 Diligence Push、回贴结果到达、open generation 续跑等。目标实现不再保留 boolean 投影;只保留具体 `nextStep.triggers` 和 registry wake signal。
105
- - `entitlement` / `revive`:业务上不是某个 dialog 获得“恢复权”,而是“有新事实到达,caller 应处理”。目标实现以 `result_arrival` trigger 表达这件事;若底层仍临时保留 entitlement token,只能作为防重入/防误续推的内部令牌,并必须携带同一个 `batchId`。
155
+ - `entitlement` / `revive`:业务上不是某个 dialog 获得“恢复权”,而是“有新事实到达,caller 应处理”。目标实现以业务 continuation 表达这件事,例如 `requested_work_replied(batchId)` `deliver_tellask_reply(targetCallId)`;若底层仍临时保留 entitlement token,只能作为迁移期防重入/防误续推的内部令牌,并必须携带同一个业务 identity。
106
156
  - `wait-group`:业务上不是 caller 正在等待一组 callee,而是“同一次派发批次的回贴结果要成组收口”。因此设计用 dispatch batch / 派发批次描述,不再新增 `waitGroupId` 这类等待语义字段名。
107
157
  - `proceeding recovery`:业务上不是恢复 proceeding 状态,而是“上一次 generation 确认未闭合,所以继续同一轮”。已闭合 generation 只能从状态快照重新投影状态。
108
158
  - `latest` / `displayState`:业务事实的来源应是状态机转移时维护的结构化元信息;`displayState`、run-control counts 都只是投影,不能反向决定业务事实。
@@ -532,6 +582,60 @@ pending tellask observability 使用 `PendingBackgroundWork`。四者不可混
532
582
  - completion obligations 决定“这个 dialog 是否还欠上游交付”。
533
583
  - background work 决定“有哪些后台诉请仍在进行中”。
534
584
 
585
+ ### 1.5. 把 reply obligation follow-up 改成业务 continuation
586
+
587
+ `reply_obligation_follow_up` 是第一批适合下手的迁移点。它现在通过 `noPromptSideDialogResumeEntitlement.reason='reply_obligation_follow_up'` 表示“这次无 prompt sideDialog drive 可以继续”,这会把调度资格和业务事实绑在一起。目标态应改为一个局部自洽的 continuation:
588
+
589
+ ```ts
590
+ type DeliverTellaskReplyContinuation = {
591
+ kind: 'deliver_tellask_reply';
592
+ targetDialogId: string;
593
+ targetCallId: string;
594
+ expectedReplyCallName: 'replyTellask' | 'replyTellaskSessionless' | 'replyTellaskBack';
595
+ };
596
+ ```
597
+
598
+ 创建点:
599
+
600
+ - sideDialog 或 ask-back dialog 生成了可疑似最终回复的普通自然语言内容;
601
+ - 当前 prompt 不是 reply tool reminder;
602
+ - active reply obligation 仍存在;
603
+ - 系统需要排一条 runtime reminder,让模型用正确的 `replyTellask*` 完成交付。
604
+
605
+ 创建时只记录目标业务 identity,不把“未来一定可继续”的判断固化进去。轮到执行时重新读取最新事实:
606
+
607
+ ```ts
608
+ async function claimDeliverTellaskReplyContinuation(
609
+ dialogId: DialogID,
610
+ continuation: DeliverTellaskReplyContinuation,
611
+ ): Promise<'claimed' | 'stale'> {
612
+ const latest = await DialogPersistence.loadDialogLatest(dialogId, 'running');
613
+ const active = await DialogPersistence.loadActiveTellaskReplyObligation(dialogId, 'running');
614
+ const finalResponse = latest?.sideDialogFinalResponse;
615
+
616
+ if (finalResponse?.callId === continuation.targetCallId) return 'stale';
617
+ if (active === undefined) return 'stale';
618
+ if (active.targetCallId !== continuation.targetCallId) return 'stale';
619
+ if (active.targetDialogId !== continuation.targetDialogId) {
620
+ throw new Error('reply obligation continuation invariant violation: target dialog changed');
621
+ }
622
+ if (active.expectedReplyCallName !== continuation.expectedReplyCallName) {
623
+ throw new Error('reply obligation continuation invariant violation: reply tool changed');
624
+ }
625
+ return 'claimed';
626
+ }
627
+ ```
628
+
629
+ 业务含义:
630
+
631
+ - 当前仍有同一个 `targetCallId` 的回复义务:继续注入/消费 reply reminder。
632
+ - 回复义务已经消失:说明已交付或状态变化,stale `debug` 丢弃。
633
+ - 当前回复义务换成别的 callId:说明这条 continuation 是旧任务的迟到唤醒,stale `debug` 丢弃。
634
+ - final response anchor 已覆盖该 callId:说明该 callId 已完成,stale `debug` 丢弃。
635
+ - callId 相同但 target dialog / expected reply tool 改变:这是状态不自洽,loud fail。
636
+
637
+ 这能替代当前 `reply_obligation_follow_up` 和 `final response anchor` 的大 gate 组合:不再问“这个 no-prompt drive 有没有 entitlement”,而是问“这条 deliver-tellask-reply continuation 对应的回复义务此刻是否仍成立”。
638
+
535
639
  ### 2. 调整 dialog display projection
536
640
 
537
641
  `computeIdleDisplayState()` 对任意 dialog:
@@ -717,7 +821,7 @@ closed-generation projection 清掉 stale `generating` 后,要同步处理 `ne
717
821
 
718
822
  - 更新 `active-callees.json` 中对应 batch member 的 resolved/final 状态。
719
823
  - 若同 batch 已全部完成,则把 batch 标记为 `resolved`,写入 `result_arrival` trigger,并由 backend/直接调度路径启动 caller 处理新事实。
720
- - schedule 等待方 drive,带当前实现所需的 `noPromptSideDialogResumeEntitlement`。
824
+ - schedule 等待方 drive 只负责叫醒;真正是否继续,由 `requested_work_replied(batchId)` continuation 在轮到时重新读取 batch / trigger / user wait 等最新事实后 claim。
721
825
 
722
826
  保留原则:
723
827
 
@@ -727,7 +831,7 @@ closed-generation projection 清掉 stale `generating` 后,要同步处理 `ne
727
831
  - 多路同组 reply 中间态不得触发 caller LLM generation,但要更新 observability、pending reminder 和对应 call bubble。
728
832
  - 自动继续 prompt/drive 应明确携带“有新回贴事实可处理”的原因。
729
833
  - 若等待方正在 Q4H / askHuman user wait 中,batch complete 仍写入 `result_arrival` trigger,但 driver 停在 user wait;用户回答清除 user wait 后再消费该 trigger。
730
- - 当前实现里的 revive entitlement 应携带 `batchId` / batch completion facts,并对应生成 `result_arrival` trigger;目标态移除“全局 pending sideDialogs 是否为空”这类猜测。
834
+ - 当前实现里的 revive entitlement 应先收敛为携带 `batchId` / batch completion facts 的迁移期令牌,并对应生成 `result_arrival` trigger;目标态移除 entitlement,保留 `requested_work_replied(batchId)` 业务 continuation。
731
835
  - 若等待方 dialog 已经 idle,收到回贴可以自动 drive 处理结果;这不违反 background_continue。
732
836
 
733
837
  ## 模块级改造清单
@@ -890,6 +994,22 @@ UI 业务状态投影结论:
890
994
  - 输入:模拟 event 写入成功但状态快照元信息未更新的异常路径。
891
995
  - 期望:runtime 发 warning / structured diagnostic,把该 dialog 转移到 `malformed/`,停止 unsafe drive;不回扫历史补猜。
892
996
 
997
+ 22. reply obligation follow-up stale 丢弃。
998
+ - 输入:排入 `deliver_tellask_reply(targetCallId=A)` continuation 后,A 对应 active reply obligation 已被 reply tool / direct-fallback 完成,或 `sideDialogFinalResponse.callId=A` 已存在。
999
+ - 期望:continuation 执行时重新 claim 最新事实,发现已完成后 `debug` 丢弃;不新增 gen_start,不写 ERROR/WARN。
1000
+
1001
+ 23. reply obligation follow-up callId 换绑。
1002
+ - 输入:排入 `deliver_tellask_reply(targetCallId=A)` 后,当前 active reply obligation 已变为 `targetCallId=B`。
1003
+ - 期望:A 的 continuation 作为旧任务迟到唤醒 `debug` 丢弃;B 的回复义务不被 A 的 continuation 消费。
1004
+
1005
+ 24. reply obligation follow-up 同 callId 但 target/tool 冲突。
1006
+ - 输入:排入 `deliver_tellask_reply(targetCallId=A, expectedReplyCallName=replyTellaskSessionless)`,执行时 active reply obligation 仍是 A,但 target dialog 或 expected reply tool 与 continuation 不一致。
1007
+ - 期望:按 loud error policy 失败,记录稳定关联字段;不能静默按任一方继续。
1008
+
1009
+ 25. 用户插话期间下游回贴到达。
1010
+ - 输入:sideDialog 因用户插话进入 `user_interjection_pause_resume_original_task`,随后下游 active callee batch complete,产生 `requested_work_replied(batchId)` continuation。
1011
+ - 期望:continuation 基于最新事实 claim;若原任务已不再被其它事实阻塞,则自动结束插话暂停并交付 deferred reply reassertion;若仍被其它先决事实阻塞,则恢复真实阻塞态,不在调度循环里用特殊组合规则硬拼。
1012
+
893
1013
  ### 重启恢复测试
894
1014
 
895
1015
  1. closed single pending tellask generation + stale `generating=true`。
@@ -956,6 +1076,7 @@ UI 业务状态投影结论:
956
1076
  - `nextStep` 已成为必要字段,`latest` 缺失它或 trigger 结构不合法就直接进入 `malformed/`,不再自动补猜。
957
1077
  - `latest.tellaskCalls` 和 `latest.tellaskResults` 都已落地,tellask call/result duplicate 判定读取 latest 索引,不再扫描当前 course JSONL。
958
1078
  - `replyDelivery`、`latest.userWait`、`latestAssignmentAnchor`、`sideDialogFinalResponse` 等状态字段都已补齐,恢复/尾部判定优先读状态快照。
1079
+ - reply obligation follow-up 已经不再依赖创建时绑定的 `noPromptSideDialogResumeEntitlement`;尾部会按最新事实重新认领,并在 `driveDialogStreamCore` 已经先消费更新 bubble 的场景下,按最后一个 assistant output 对应的 reply target 重新回绑到当前业务义务,避免更新诉请竞速把后续回贴继续机会锁死。
959
1080
 
960
1081
  调度和唤醒也已收敛:
961
1082
 
@@ -963,6 +1084,8 @@ UI 业务状态投影结论:
963
1084
  - `globalDialogRegistry` 只承载 live registry 与 wake signal,`wakeDrive` / `clearDriveWake` / `isDriveWakeQueued` 是唯一公开语义。
964
1085
  - backend loop 只扫描 live root dialog 本身 + root-local `drive-watch.json` 中的支线子集;支线只在 `latest.nextStep.triggers`、open `generationRunState`、pending/delivered-but-tool-result-pending `replyDelivery` 等 durable work 存在时进入 watch。
965
1086
  - `root_drive_wake` 若在 generation start 被 accepted 后遇到 core/tail failure,会重新写回 durable wake,避免失败收尾误清队列。
1087
+ - `reply_obligation_follow_up` 第一刀已落地:runtime reply reminder 不再通过 `noPromptSideDialogResumeEntitlement.reason='reply_obligation_follow_up'` 获得无 prompt drive 资格,而是在轮到消费 queued prompt 时读取最新 `sideDialogFinalResponse`、target dialog `tellaskResults`、当前 sideDialog assignment / active reply obligation 后重新 claim。若这次 assistant output 已经在 core 内部消费了更新 bubble,则 tail 会按最后一个 assistant output 的 reply target 重新认领当前义务,再决定是否继续。旧 callId 已完成、回复义务已消失、或当前回复义务换成其它 callId 时,queued reminder 以 `debug` 丢弃;callId 相同但 target/tool/content 不一致时 loud fail。
1088
+ - stale reply reminder 被丢弃后,如果最新状态没有其它 durable drive work,driver 直接停止,不进入 core 开空 generation;caller result-arrival 在 final response anchor 后也不再绕一圈旧的 invalid reply-tool detour。
966
1089
 
967
1090
  UI 和投影语义也已对齐:
968
1091
 
@@ -980,18 +1103,21 @@ UI 和投影语义也已对齐:
980
1103
 
981
1104
  ## 迁移步骤
982
1105
 
983
- 1. 新增 generationRunState、DialogUserWaitState、NextStepTriggerState、dispatch batch、reply delivery status 等状态机元信息,并在每次状态转移时同步维护。
984
- 2. 移除 runtime 历史回扫补猜路径;旧 dialog 缺必要元信息时进入 `malformed/`。如确有业务价值,由人类手工分析 `malformed/` 记录并修正后续跑。
985
- 3. 新增 fact helpers:user wait、next-step facts、completion obligations、background pending work,全部读取状态快照。
986
- 4. 调整 dialog `getSuspensionStatus()` 或新增 next-step facts API,先让 drive 使用新 API;API 必须按角色区分 caller background work 与 callee completion obligation。
987
- 5. 调整 display projection,移除 pending tellask -> `waiting_for_sideDialogs`,覆盖 caller/callee 角色组合。
988
- 6. 调整 post-tool continuation,把 pending tellask ack immediate followup 中排除并加测试。
989
- 7. 新增 `active-callees.json`,以 `batchId` 保证多路同组回复齐后才触发 result-arrival handling;消费后删除 batch,不累积历史。
990
- 8. 调整 Diligence Push,显式读取状态快照里的 pending tellask context 并改文案。
991
- 9. 调整 restart recovery,仅读取状态快照判断 open/closed generation;缺元信息或冲突时转移到 `malformed/`。
992
- 10. 调整 proceeding recovery,只恢复真正 open generation。
993
- 11. 调整 backend loop,避免 `resumeInProgressGeneration` 绕过已 closed background boundary。
994
- 12. 更新 tests / docs / UI run-control copy。
1106
+ 1. 已完成:建立业务 continuation 抽象和 claim 原则。调度层只负责叫醒/取下一条 continuation;业务 handler 轮到时重新读取最新持久事实决定 claimed / stale / conflict。
1107
+ 2. 已完成第一刀:迁移 `reply_obligation_follow_up`。queued runtime reply reminder 携带 `targetCallId`、`targetDialogId`、`expectedReplyCallName` 和诉请内容,执行时重新读取 active reply obligation、sideDialog assignment、target tellask result 和 final response anchor;若 core 已先消费更新 bubble,则 tail 会按最后一个 assistant output 的 reply target 重新认领当前义务。stale 用 `debug` 丢弃,状态冲突 loud fail。
1108
+ 3. 下一刀:迁移 `result_arrival` / caller revive,用 `requested_work_replied(batchId)` continuation 替换 revive entitlement;direct schedule 和 backend watch 都只叫醒,claim 时读取 batch/trigger 最新事实。
1109
+ 4. 新增 generationRunState、DialogUserWaitState、NextStepTriggerState、dispatch batch、reply delivery status 等状态机元信息,并在每次状态转移时同步维护。
1110
+ 5. 移除 runtime 历史回扫补猜路径;旧 dialog 缺必要元信息时进入 `malformed/`。如确有业务价值,由人类手工分析 `malformed/` 记录并修正后续跑。
1111
+ 6. 新增 fact helpers:user wait、next-step facts、completion obligations、background pending work,全部读取状态快照。
1112
+ 7. 调整 dialog `getSuspensionStatus()` 或新增 next-step facts API,先让 drive 使用新 API;API 必须按角色区分 caller background work 与 callee completion obligation。
1113
+ 8. 调整 display projection,移除 pending tellask -> `waiting_for_sideDialogs`,覆盖 caller/callee 角色组合。
1114
+ 9. 调整 post-tool continuation,把 pending tellask ack 从 immediate followup 中排除并加测试。
1115
+ 10. 新增 `active-callees.json`,以 `batchId` 保证多路同组回复齐后才触发 result-arrival handling;消费后删除 batch,不累积历史。
1116
+ 11. 调整 Diligence Push,显式读取状态快照里的 pending tellask context 并改文案。
1117
+ 12. 调整 restart recovery,仅读取状态快照判断 open/closed generation;缺元信息或冲突时转移到 `malformed/`。
1118
+ 13. 调整 proceeding recovery,只恢复真正 open generation。
1119
+ 14. 调整 backend loop,避免 `resumeInProgressGeneration` 绕过已 closed background boundary。
1120
+ 15. 更新 tests / docs / UI run-control copy。
995
1121
 
996
1122
  ## 风险与判定
997
1123
 
@@ -1007,9 +1133,12 @@ UI 和投影语义也已对齐:
1007
1133
 
1008
1134
  ### 判定原则
1009
1135
 
1136
+ - 业务 continuation 创建时只记录业务 identity 和意图;轮到执行时必须重新读取最新持久事实 claim,不能复用创建时的判断结论。
1137
+ - stale continuation 是正常竞速结果,使用 `debug` 丢弃;只有事实冲突、元信息缺失或 unsafe path 才 loud fail。
1010
1138
  - pending tellask 存在时,caller dialog 可被驱动,但不是因为 pending tellask 必须驱动。
1011
1139
  - pending tellask 存在时,caller dialog 可 idle,但 idle 不代表后台完成。
1012
1140
  - active reply obligation 存在时,当前 dialog 是 callee;它需要最终通过对应 reply path 收口,但它自己的下游 pending tellask 不决定它是否继续推进。
1141
+ - reply obligation follow-up 必须绑定具体 `targetCallId`;当前 active reply obligation 已消失、已换 callId、或 final response anchor 已覆盖该 callId 时,该 follow-up stale 丢弃。
1013
1142
  - caller 处理 callee 回复时,触发粒度是 dispatch batch complete;单个 `tellask` 是 size = 1 的特例。
1014
1143
  - 只有不存在 `awaiting_user_answer` 等先决等待事实,且存在未消费 `NextStepTriggerState` trigger 或当前工具轮产生 immediate tool result,才能启动新一轮。
1015
1144
  - 已有 `gen_finish_record` 的 generation 不应被 proceeding recovery 重放。
@@ -1197,7 +1197,7 @@ function reserveKnownFunctionCallId(reservation, callId) {
1197
1197
  reservation.knownCallIds.add(normalized);
1198
1198
  }
1199
1199
  }
1200
- function collectKnownFunctionCallIdsForCurrentCourse(dialog) {
1200
+ async function collectKnownFunctionCallIdsForCurrentCourse(dialog) {
1201
1201
  const known = new Set();
1202
1202
  const addKnown = (callId) => {
1203
1203
  const normalized = trimOptionalCallId(callId);
@@ -1218,6 +1218,29 @@ function collectKnownFunctionCallIdsForCurrentCourse(dialog) {
1218
1218
  addKnown(msg.rawId);
1219
1219
  addKnown(msg.effectiveId);
1220
1220
  }
1221
+ const persistedEvents = await persistence_1.DialogPersistence.loadCourseEvents(dialog.id, course, dialog.status);
1222
+ for (const event of persistedEvents) {
1223
+ switch (event.type) {
1224
+ case 'func_call_record': {
1225
+ addKnown(event.id);
1226
+ addKnown(event.rawId);
1227
+ addKnown(event.effectiveId);
1228
+ break;
1229
+ }
1230
+ case 'func_result_record': {
1231
+ addKnown(event.id);
1232
+ addKnown(event.rawId);
1233
+ addKnown(event.effectiveId);
1234
+ break;
1235
+ }
1236
+ case 'tellask_call_record': {
1237
+ addKnown(event.id);
1238
+ break;
1239
+ }
1240
+ default:
1241
+ break;
1242
+ }
1243
+ }
1221
1244
  return known;
1222
1245
  }
1223
1246
  function allocateDuplicateEffectiveCallId(args) {
@@ -1265,7 +1288,7 @@ function allocateEffectiveFunctionCallId(args) {
1265
1288
  }
1266
1289
  async function normalizeGeneratedFunctionCallIds(args) {
1267
1290
  const reservation = {
1268
- knownCallIds: new Set(collectKnownFunctionCallIdsForCurrentCourse(args.dialog)),
1291
+ knownCallIds: new Set(await collectKnownFunctionCallIdsForCurrentCourse(args.dialog)),
1269
1292
  seenRawIdsThisRound: new Set(),
1270
1293
  nextDuplicateSuffixByRawId: new Map(),
1271
1294
  };
@@ -1426,6 +1449,7 @@ async function executeFunctionRound(args) {
1426
1449
  immediateFollowupCallIds: [],
1427
1450
  immediateTellaskOutputCallIds: [],
1428
1451
  shouldStopAfterReplyTool: false,
1452
+ shouldStopAfterPendingTellaskWait: false,
1429
1453
  pairedMessages: [],
1430
1454
  tellaskToolOutputs: [],
1431
1455
  };
@@ -1535,6 +1559,7 @@ async function executeFunctionRound(args) {
1535
1559
  immediateFollowupCallIds,
1536
1560
  immediateTellaskOutputCallIds: tellaskRound.immediateTellaskOutputCallIds,
1537
1561
  shouldStopAfterReplyTool: tellaskRound.shouldStopAfterReplyTool,
1562
+ shouldStopAfterPendingTellaskWait: tellaskRound.shouldStopAfterPendingTellaskWait,
1538
1563
  pairedMessages,
1539
1564
  tellaskToolOutputs: [...tellaskRound.toolOutputs],
1540
1565
  };
@@ -2809,6 +2834,14 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2809
2834
  routed.hasImmediateTellaskOutputs ||
2810
2835
  invalidFuncCallCount > 0;
2811
2836
  if (!shouldStartImmediatePostToolGeneration) {
2837
+ if (routed.shouldStopAfterPendingTellaskWait) {
2838
+ log_1.log.debug('kernel-driver stop round after pending tellask wait boundary', undefined, {
2839
+ dialogId: dlg.id.valueOf(),
2840
+ rootId: dlg.id.rootId,
2841
+ selfId: dlg.id.selfId,
2842
+ });
2843
+ break;
2844
+ }
2812
2845
  const healthFirst = await maybeContinueWithHealthPromptBeforeDiligence({
2813
2846
  dlg,
2814
2847
  providerCfg,