@tea-agent/loop-agent 0.40.0-next.2 → 0.40.0-next.4

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 (140) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +9 -2
  3. package/README.md +2 -2
  4. package/dist/application/task-lifecycle/advance.js +12 -0
  5. package/dist/build-stamp.json +3 -3
  6. package/dist/cli/update/runtime-activity.js +1 -29
  7. package/dist/commands/dag-rerun.js +1 -1
  8. package/dist/executors/dag-pi-executor.js +46 -5
  9. package/dist/executors/shell-executor.js +28 -0
  10. package/dist/executors/workspace-write-snapshot.js +68 -0
  11. package/dist/shared/dag-prompt-override.js +4 -5
  12. package/dist/shared/update/npm-client.js +33 -11
  13. package/dist/task/config-types.js +10 -0
  14. package/dist/task/contract/apply.js +11 -1
  15. package/dist/task/contract/constants.js +2 -0
  16. package/dist/task/contract/hash.js +3 -0
  17. package/dist/task/contract/observe.js +25 -4
  18. package/dist/task/contract/paths.js +2 -1
  19. package/dist/task/contract/project.js +15 -1
  20. package/dist/task/contract/schema.js +2 -0
  21. package/dist/task/contract/transaction.js +12 -0
  22. package/dist/task/source-prepare/completeness.js +188 -0
  23. package/dist/task/source-prepare/fragment-inventory.js +474 -0
  24. package/dist/task/source-prepare/index.js +3 -0
  25. package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
  26. package/dist/task/source-prepare/ledger-review.js +214 -0
  27. package/dist/task/source-prepare/ledger.js +545 -0
  28. package/dist/task/source-prepare/prepare.js +262 -1
  29. package/dist/task/source-prepare/semantic-intake.js +19 -3
  30. package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
  31. package/dist/task/source-prepare/types.js +22 -0
  32. package/dist/worker/cli.js +12 -26
  33. package/dist/worker/console/console-handoff.js +82 -0
  34. package/dist/worker/console/console-update-and-init.js +115 -0
  35. package/dist/worker/console/console-update-runtime.js +84 -0
  36. package/dist/worker/console/draft-store.js +49 -0
  37. package/dist/worker/console/operation-store.js +1 -1
  38. package/dist/worker/console/operator-actions.js +129 -4
  39. package/dist/worker/console/operator-surface-health.js +1 -0
  40. package/dist/worker/console/prd-intake-bridge.js +13 -0
  41. package/dist/worker/console/routes.js +3 -0
  42. package/dist/worker/console/server.js +73 -5
  43. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DLMldIE8.js → abnfDiagram-N423BO3Z-CTx1TrNa.js} +1 -1
  44. package/dist/worker/console/static/assets/{arc-DVOg1dSW.js → arc-B4i9cVxX.js} +1 -1
  45. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-715v7joo.js → architectureDiagram-T3A2C74G-BAXE8_Rl.js} +1 -1
  46. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BtMEGtAQ.js → blockDiagram-VBNYF7ZC-C5Xh59at.js} +1 -1
  47. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-qUxXsdxu.js → c4Diagram-5PPSVZJV-B_asOzAu.js} +1 -1
  48. package/dist/worker/console/static/assets/channel-Dj_4Moei.js +1 -0
  49. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BYMag72f.js → chunk-2GRJ4B5K-wRO_iRNc.js} +1 -1
  50. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-C5jt5rtx.js → chunk-2Q5K7J3B-ByOaSh_G.js} +1 -1
  51. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DvZghfS2.js → chunk-5RXB4S5H-DjLzudan.js} +1 -1
  52. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CXutxAmY.js → chunk-5VM5RSS4-JCNGkXYT.js} +1 -1
  53. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BkANIDWk.js → chunk-6Q2QTUOP-BhrPXXMZ.js} +1 -1
  54. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bwy1wwlr.js → chunk-GF5L2VYU-TJ5JuEBT.js} +1 -1
  55. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-D3pWYVCG.js → chunk-JWPE2WC7-VVthn_mD.js} +1 -1
  56. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DRzjzmFD.js → chunk-KBJHAD2P-D60-Mny4.js} +1 -1
  57. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C4xXDxXL.js → chunk-RYQCIY6F-zXv7aVye.js} +1 -1
  58. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BngIZ90b.js → chunk-XXDRQBXY-By9VQoi1.js} +1 -1
  59. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C0YYAyCa.js +1 -0
  60. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C0YYAyCa.js +1 -0
  61. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-B40ACoFn.js → cose-bilkent-JH36ORCC-C4zQLe-M.js} +1 -1
  62. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BE-1ZRAI.js → cynefin-VYW2F7L2-wh5N2ReN.js} +1 -1
  63. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BoYbwVTh.js → cynefinDiagram-MW4NZA55-Dlu5wIqs.js} +1 -1
  64. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-B5-KA0fK.js → dagre-VZM6K2ZE-CrDgthc6.js} +1 -1
  65. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DJpCToT5.js → diagram-7IWD3JNH-zN1clg27.js} +1 -1
  66. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-De8R04Ng.js → diagram-B4RE2ZJO-BN-o7J7-.js} +1 -1
  67. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BU1N8DiA.js → diagram-LBJQPF4R-C8SuPt0d.js} +1 -1
  68. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BUUUzSYI.js → diagram-Q27KOJAE-BoizuHbZ.js} +1 -1
  69. package/dist/worker/console/static/assets/{diagram-UB23O5K3-zLoT1RC4.js → diagram-UB23O5K3-BzUzXixG.js} +1 -1
  70. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-_q4-4N8c.js → ebnfDiagram-BXEA7PRR-Ev3lA4Cf.js} +1 -1
  71. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CTqzoNSL.js → erDiagram-JOGREHBK-GALUzRbs.js} +1 -1
  72. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CMKSYKJM.js → flowDiagram-UKHOOZJN-CZJN65Sb.js} +1 -1
  73. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Dj6U8Sb7.js → ganttDiagram-PKOTCBZU-JLB9OESl.js} +1 -1
  74. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BfoxVYky.js → gitGraphDiagram-DS77QQ5N-CcvCr4GD.js} +1 -1
  75. package/dist/worker/console/static/assets/{index-CA0nQEbb.js → index-CW88Fjbw.js} +81 -81
  76. package/dist/worker/console/static/assets/index-DP0ZJl39.css +1 -0
  77. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-T5NfvHGV.js → infoDiagram-6WML65LV-HG5z7aVg.js} +1 -1
  78. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BEhUMkut.js → ishikawaDiagram-WSZJBQD7-BHEu9chR.js} +1 -1
  79. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-B2Ld6nNt.js → journeyDiagram-NVQOT4AX-Cp_Up8-a.js} +1 -1
  80. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-nV9T85yS.js → kanban-definition-27J2QSJJ-DvVeY-em.js} +1 -1
  81. package/dist/worker/console/static/assets/{linear-CIO1zDYh.js → linear-aGmcjgTn.js} +1 -1
  82. package/dist/worker/console/static/assets/{mermaid.core-B_ucAqLq.js → mermaid.core-Biyo-tqA.js} +5 -5
  83. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BIA3VNcZ.js → mindmap-definition-FAOFIHXS-DyxY2pIb.js} +1 -1
  84. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CNzLjNV3.js → pegDiagram-VL7TDLO6-CkdGuc50.js} +1 -1
  85. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-PWytzfc_.js → pieDiagram-7S7Q4E2Y-NNQI9xVR.js} +1 -1
  86. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CS0wGoIq.js → quadrantDiagram-CIZ2JOQS-PAJkuz_G.js} +1 -1
  87. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-C3Wsqph8.js → railroadDiagram-AXF67PYL-ZwrQod0O.js} +1 -1
  88. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DF6FDTYg.js → requirementDiagram-LRYGKXZP-D3ZfrzM2.js} +1 -1
  89. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BdQUJm6y.js → sankeyDiagram-W5VNT64P-DDGFmWk5.js} +1 -1
  90. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-Bv9rppGg.js → sequenceDiagram-SI44F4Z6-DO6mmXTA.js} +1 -1
  91. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-C3Hpvb_B.js → sizeCapture-X5ZJPWSS-DMbCQiJt.js} +1 -1
  92. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-CZOWLpno.js → stateDiagram-OKZ733FA-Di_YUEfC.js} +1 -1
  93. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrdDvela.js +1 -0
  94. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CPQRIZx5.js → swimlanes-SLNWSIFB-Bn2PphNJ.js} +2 -2
  95. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DPkEAULl.js +8 -0
  96. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CkwM9BgA.js → timeline-definition-Z64GVDOM-DPh8pRsp.js} +1 -1
  97. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-wGSoDzYc.js → vennDiagram-T6HMQDX7-GMfl26pW.js} +1 -1
  98. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-GNVas2gx.js → wardleyDiagram-T6FBY63Y-DTlmVrmF.js} +1 -1
  99. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-C5USnHw7.js → xychartDiagram-ELKLHX3M-YGBjKAS5.js} +1 -1
  100. package/dist/worker/console/static/index.html +2 -2
  101. package/dist/worker/console/static-src/app/console-types.js +40 -1
  102. package/dist/worker/console/static-src/app/useOperatorActions.js +5 -0
  103. package/dist/worker/console/static-src/app/useTaskWizard.js +73 -1
  104. package/dist/worker/observability/init-runtime-activity.js +26 -0
  105. package/dist/worker/observe/node-input.js +88 -2
  106. package/dist/worker/observe/routes.js +188 -0
  107. package/dist/worker/observe/static/api.js +3 -3
  108. package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
  109. package/dist/worker/observe/static/dag-node-purpose.js +203 -0
  110. package/dist/worker/observe/static/format.js +37 -0
  111. package/dist/worker/observe/static/index.html +5 -0
  112. package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
  113. package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
  114. package/dist/worker/observe/static/state.js +2 -2
  115. package/dist/worker/observe/static/styles.css +357 -35
  116. package/dist/worker/observe/static/views/dag-graph.js +4 -2
  117. package/dist/worker/observe/static/views/dag-inspector.js +531 -78
  118. package/dist/worker/observe/static/views/dag.d.ts +13 -0
  119. package/dist/worker/observe/static/views/dag.js +4 -3
  120. package/dist/worker/observe/static/views/task.js +55 -0
  121. package/dist/workflows/dag/frontend-implementation-contract.js +88 -2
  122. package/dist/workflows/dag/frontend-plan-render.js +24 -0
  123. package/dist/workflows/dag/frontend-prewrite-gate.js +197 -0
  124. package/dist/workflows/dag/frontend-review-context.js +92 -10
  125. package/dist/workflows/dag/init-hybrid.js +144 -4
  126. package/dist/workflows/dag/node-execution.js +45 -0
  127. package/dist/workflows/dag/types.js +45 -12
  128. package/docs/README.md +1 -0
  129. package/docs/architecture/evolution.md +45 -0
  130. package/docs/init-surface.manifest.json +1 -0
  131. package/docs/templates/README.md +1 -1
  132. package/docs/templates/frontend-implementation-contract.schema.json +681 -24
  133. package/package.json +1 -1
  134. package/skills/loop-agent/references/hybrid-dag.md +7 -0
  135. package/dist/worker/console/static/assets/channel-BG6TLen_.js +0 -1
  136. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-GuyUqs0h.js +0 -1
  137. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-GuyUqs0h.js +0 -1
  138. package/dist/worker/console/static/assets/index-C3GgHg-g.css +0 -1
  139. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dgsvn63q.js +0 -1
  140. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DaNfG1BJ.js +0 -8
@@ -0,0 +1,214 @@
1
+ import { mergeInventoryItems } from "./ledger-reconciliation.js";
2
+ // ---------------------------------------------------------------------------
3
+ // 独立 reviewer(AC-007 / AC-FIDELITY-REPAIR-007)
4
+ // context-only / duplicate / conflicts 由独立可注入 reviewer 审核,与 reconciler
5
+ // 是不同调用阶段(fixture 断言调用顺序,杜绝共享一次输出冒充独立审核)。
6
+ // 最多一次有界修订;review/revision 失败或第二次不通过 fail closed。低风险
7
+ // context-only 仅 reviewer pass 放行,高风险 context-only 始终阻断;duplicate
8
+ // 保留全部来源但审核失败(含修订后仍拒绝)以稳定码 fail closed。
9
+ // ---------------------------------------------------------------------------
10
+ /** review 片段原文按 budget 有界截断(AC-004 / AC-HARD-004),禁止整文档入
11
+ * prompt;沿用 MAX_SOURCE_EXCERPT_CHARS 边界语义。 */
12
+ export const LEDGER_REVIEW_DEFAULT_BUDGET_CHARS = 2000;
13
+ export const LEDGER_REVIEW_MAX_REVISIONS = 1;
14
+ /** review 失败(reviewer 异常/第二次不通过)统一稳定错误码。 */
15
+ export class LedgerReviewError extends Error {
16
+ code = "SOURCE_FIDELITY_RECONCILIATION_FAILED";
17
+ constructor(message, options) {
18
+ super(message, options);
19
+ this.name = "LedgerReviewError";
20
+ }
21
+ }
22
+ /**
23
+ * 独立 reviewer 编排:仅 context-only(低风险)、duplicate 与 conflict 进入审核。
24
+ * - context-only high risk:始终阻断(不调 reviewer,fail closed)。
25
+ * - context-only low:仅 reviewer pass 放行;否则(一次修订后仍失败)阻断。
26
+ * - duplicate:进入审核并记录;duplicate 保留全部来源,失败不升级为阻断。
27
+ * - conflict:需 reviewer pass 或存在有效 human resolution,否则阻断。
28
+ * reviewer 异常即 fail closed,不回退。
29
+ */
30
+ export async function reviewLedgerFragments(input) {
31
+ const { canonicalRequirements, reviewer, maxRevisions = LEDGER_REVIEW_MAX_REVISIONS, conflictIds = new Set(), resolvedConflictIds = new Set(), } = input;
32
+ const calls = input.calls ?? [];
33
+ const reasons = [];
34
+ const reviewed = [];
35
+ for (const fragment of input.fragments) {
36
+ let kind = null;
37
+ if (fragment.disposition === "context-only")
38
+ kind = "context-only";
39
+ else if (fragment.disposition === "duplicate")
40
+ kind = "duplicate";
41
+ else if (conflictIds.has(fragment.id))
42
+ kind = "conflict";
43
+ if (!kind)
44
+ continue;
45
+ // 高风险 context-only:始终阻断,不进入 reviewer。
46
+ if (kind === "context-only" && fragment.risk === "high") {
47
+ reviewed.push({
48
+ fragmentId: fragment.id,
49
+ kind,
50
+ approved: false,
51
+ revisionUsed: false,
52
+ });
53
+ reasons.push(`high-risk context-only fragment ${fragment.id} always blocks; reviewer cannot waive it`);
54
+ return {
55
+ approved: false,
56
+ code: "SOURCE_FIDELITY_HIGH_RISK_UNCOVERED",
57
+ reasons,
58
+ reviewed,
59
+ calls,
60
+ };
61
+ }
62
+ const resolutionAvailable = kind === "conflict" && resolvedConflictIds.has(fragment.id);
63
+ // AC-002:已被绑定当前 inputDigest 的有效 human resolution 覆盖的 conflict
64
+ // (resolutionAvailable=true)直接 approved(revisionUsed=false),注入的
65
+ // reviewer(含模型 reviewer)零调用:calls 不出现该 fragment 条目,reviewed
66
+ // audit 保留该条目。其余 fragment(unresolved / context-only / duplicate /
67
+ // 未解决的 conflict)的 reviewer 调用、修订与审核行为完全不变。
68
+ if (resolutionAvailable) {
69
+ reviewed.push({
70
+ fragmentId: fragment.id,
71
+ kind,
72
+ approved: true,
73
+ revisionUsed: false,
74
+ });
75
+ continue;
76
+ }
77
+ let current = fragment;
78
+ let approved = false;
79
+ let revisionUsed = false;
80
+ let revisionsRemaining = maxRevisions;
81
+ for (;;) {
82
+ const request = {
83
+ kind,
84
+ fragment: current,
85
+ canonicalRequirements,
86
+ resolutionAvailable,
87
+ };
88
+ let response;
89
+ try {
90
+ response = await reviewer(request);
91
+ }
92
+ catch (error) {
93
+ throw new LedgerReviewError(`independent reviewer failed for ${kind} fragment ${fragment.id}: ${error instanceof Error ? error.message : String(error)}`);
94
+ }
95
+ calls.push(fragment.id);
96
+ if (response?.approved === true) {
97
+ approved = true;
98
+ break;
99
+ }
100
+ // 最多一次有界修订:仅当 reviewer 给出 revision 建议且还有配额时采纳。
101
+ if (revisionsRemaining > 0 && response?.revision) {
102
+ current = {
103
+ ...current,
104
+ ...(response.revision.disposition
105
+ ? { disposition: response.revision.disposition }
106
+ : {}),
107
+ ...(response.revision.reason
108
+ ? { reason: response.revision.reason }
109
+ : {}),
110
+ };
111
+ revisionsRemaining -= 1;
112
+ revisionUsed = true;
113
+ continue;
114
+ }
115
+ approved = false;
116
+ break;
117
+ }
118
+ reviewed.push({
119
+ fragmentId: fragment.id,
120
+ kind,
121
+ approved,
122
+ revisionUsed,
123
+ });
124
+ if (approved)
125
+ continue;
126
+ if (kind === "duplicate") {
127
+ // duplicate 保留全部来源,但审核失败(含 LEDGER_REVIEW_MAX_REVISIONS=1
128
+ // 单次有界修订后仍拒绝)即 fail closed(稳定码阻断,绝不静默放行未确认
129
+ // 的重复排除;ledger 保留全部 duplicate 来源记录不丢失)。
130
+ reasons.push(`duplicate fragment ${fragment.id} not approved by independent reviewer${revisionUsed ? " after one bounded revision" : ""}`);
131
+ return {
132
+ approved: false,
133
+ code: "SOURCE_FIDELITY_RECONCILIATION_FAILED",
134
+ reasons,
135
+ reviewed,
136
+ calls,
137
+ };
138
+ }
139
+ reasons.push(`${kind} fragment ${fragment.id} not approved by independent reviewer${revisionUsed ? " after one bounded revision" : ""}`);
140
+ return {
141
+ approved: false,
142
+ code: kind === "context-only"
143
+ ? "SOURCE_FIDELITY_CONTEXT_ONLY_UNREVIEWED"
144
+ : "SOURCE_FIDELITY_CONFLICT",
145
+ reasons,
146
+ reviewed,
147
+ calls,
148
+ };
149
+ }
150
+ return { approved: true, code: null, reasons, reviewed, calls };
151
+ }
152
+ /**
153
+ * 仓库内确定性默认 reviewer(无模型依赖):context-only 需完整排除理由且低风险;
154
+ * duplicate 需完整理由;conflict 需有效 human resolution。prepare 在未注入外部
155
+ * reviewer 时使用该实现,保证 frontend task 的 ledger 评审语义 fail closed。
156
+ */
157
+ export const defaultLedgerReviewer = async (request) => {
158
+ if (request.kind === "context-only") {
159
+ const complete = Boolean(request.fragment.reason?.trim());
160
+ return {
161
+ approved: complete && request.fragment.risk === "low",
162
+ reason: complete
163
+ ? "context-only 排除理由完整且低风险"
164
+ : "context-only fragment 缺少完整排除理由",
165
+ };
166
+ }
167
+ if (request.kind === "duplicate") {
168
+ const complete = Boolean(request.fragment.reason?.trim());
169
+ return {
170
+ approved: complete,
171
+ reason: complete
172
+ ? "duplicate 排除理由完整(保留全部来源)"
173
+ : "duplicate fragment 缺少完整排除理由",
174
+ };
175
+ }
176
+ return {
177
+ approved: request.resolutionAvailable,
178
+ reason: request.resolutionAvailable
179
+ ? "conflict 已被绑定当前 inputDigest 的有效 human resolution 覆盖"
180
+ : "conflict 无有效 human resolution 覆盖,保持阻断",
181
+ };
182
+ };
183
+ /** 供 prepare.ts 接入:从 ledger fragments 中挑选需审核的条目,复用
184
+ * mergeInventoryItems 的 id→原文映射,按 budgetChars 有界截断后传入 reviewer
185
+ * (AC-004 / AC-HARD-004):请求携带非空有界原文与精确 fragment id,完整文档
186
+ * 绝不进入下游 prompt;原文不可得时退化为 reason / id 保证非空。 */
187
+ export function ledgerReviewItemsFromRecords(records, input) {
188
+ const budgetChars = input?.budgetChars ?? LEDGER_REVIEW_DEFAULT_BUDGET_CHARS;
189
+ const merged = input?.fragments
190
+ ? mergeInventoryItems({ records, fragments: input.fragments })
191
+ : records.map((record) => ({
192
+ id: record.id,
193
+ path: "",
194
+ headingPath: record.headingPath,
195
+ lineRange: record.lineRange,
196
+ text: "",
197
+ disposition: record.disposition,
198
+ ...(record.reason ? { reason: record.reason } : {}),
199
+ risk: record.risk,
200
+ canonicalRequirementIds: record.canonicalRequirementIds,
201
+ }));
202
+ return merged.map((item) => {
203
+ const bounded = item.text.slice(0, budgetChars);
204
+ const text = bounded.trim() || item.reason?.trim() || item.id;
205
+ return {
206
+ id: item.id,
207
+ text,
208
+ disposition: item.disposition,
209
+ ...(item.reason ? { reason: item.reason } : {}),
210
+ risk: item.risk,
211
+ canonicalRequirementIds: item.canonicalRequirementIds,
212
+ };
213
+ });
214
+ }