noteconnection 1.8.0 → 1.9.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 (195) hide show
  1. package/README.md +257 -1803
  2. package/dist/src/agent_workspace.contract.parity.test.js +105 -421
  3. package/dist/src/agent_workspace.frontend.test.js +410 -11
  4. package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
  5. package/dist/src/appimage.patchelf.test.js +113 -0
  6. package/dist/src/appimage.portability.test.js +156 -0
  7. package/dist/src/backend/FileLoader.js +18 -4
  8. package/dist/src/backend/FileLoader.test.js +125 -0
  9. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  10. package/dist/src/backend/GraphBuilder.js +66 -18
  11. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  12. package/dist/src/backend/GraphMetrics.js +0 -3
  13. package/dist/src/backend/ResourceIdentity.js +155 -0
  14. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  15. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  16. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  17. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  18. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  19. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  20. package/dist/src/backend/build_dag.js +0 -1
  21. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  22. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  23. package/dist/src/core/Graph.js +138 -42
  24. package/dist/src/core/Graph.test.js +74 -0
  25. package/dist/src/core/NoteConnection.js +2 -2
  26. package/dist/src/core/PathBridge.js +265 -2
  27. package/dist/src/core/ResourceReference.js +17 -0
  28. package/dist/src/desktop.godot.pack.test.js +142 -0
  29. package/dist/src/desktop.runtime.graph.test.js +66 -0
  30. package/dist/src/export/WorkspaceExportBundle.js +44 -0
  31. package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
  32. package/dist/src/foundation.release.evidence.contract.test.js +127 -9
  33. package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
  34. package/dist/src/frontend/README.md +257 -1803
  35. package/dist/src/frontend/agent_workspace.js +665 -7
  36. package/dist/src/frontend/index.html +31 -2
  37. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  38. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  39. package/dist/src/frontend/libs/path_core.js +158 -42
  40. package/dist/src/frontend/locales/en.json +32 -0
  41. package/dist/src/frontend/locales/zh.json +32 -0
  42. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  43. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  44. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  45. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  46. package/dist/src/frontend/path.html +6 -2
  47. package/dist/src/frontend/simulationWorker.js +1 -1
  48. package/dist/src/frontend/source_manager.js +110 -4
  49. package/dist/src/frontend/storage_provider.js +661 -71
  50. package/dist/src/frontend/styles.css +107 -13
  51. package/dist/src/frontend/workspace_panes.js +111 -7
  52. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  53. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  54. package/dist/src/index.js +6 -2
  55. package/dist/src/index.knowledge-root.test.js +118 -0
  56. package/dist/src/indexing/IndexLifecycle.js +22 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +24 -0
  58. package/dist/src/knowledge.api.contract.test.js +4 -2
  59. package/dist/src/knowledge_projection.contract.test.js +134 -0
  60. package/dist/src/knowledge_projection_store.test.js +200 -0
  61. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  62. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
  64. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
  68. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  69. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  70. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  71. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  72. package/dist/src/learning/agentConversationExecution.js +211 -0
  73. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  74. package/dist/src/learning/agentConversationSerialization.js +266 -0
  75. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  76. package/dist/src/learning/agentResponseBudget.js +179 -0
  77. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  78. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  79. package/dist/src/learning/answerReleaseReview.js +1191 -250
  80. package/dist/src/learning/answerReleaseReview.test.js +1293 -7
  81. package/dist/src/learning/answerTaskPlan.js +473 -0
  82. package/dist/src/learning/conversationComposer.js +627 -386
  83. package/dist/src/learning/conversationComposer.test.js +1403 -0
  84. package/dist/src/learning/evidenceContextAssembler.js +312 -120
  85. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  86. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  87. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  88. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  89. package/dist/src/learning/graphAnswerFacts.js +94 -0
  90. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  91. package/dist/src/learning/graphAnswerPlan.js +908 -0
  92. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  93. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  94. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  95. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  96. package/dist/src/learning/graphClaimMatcher.js +104 -0
  97. package/dist/src/learning/graphConditionedContext.js +109 -0
  98. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  99. package/dist/src/learning/graphContextAssembler.js +3 -13
  100. package/dist/src/learning/graphContextAssembler.test.js +2 -2
  101. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  102. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  103. package/dist/src/learning/identityMetadata.test.js +26 -0
  104. package/dist/src/learning/index.js +1 -0
  105. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  106. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  107. package/dist/src/learning/queryBackend.js +34 -10
  108. package/dist/src/learning/queryBackend.test.js +21 -0
  109. package/dist/src/learning/ragContextPack.js +115 -16
  110. package/dist/src/learning/ragContextPack.test.js +151 -0
  111. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  112. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  113. package/dist/src/learning/ragPublicText.js +67 -4
  114. package/dist/src/learning/ragPublicText.test.js +57 -0
  115. package/dist/src/learning/ragSufficiencyJudge.js +11 -1
  116. package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
  117. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
  118. package/dist/src/learning/requestNormalization.js +106 -0
  119. package/dist/src/learning/requestNormalization.test.js +101 -0
  120. package/dist/src/learning/store.js +116 -12
  121. package/dist/src/learning/store.test.js +299 -0
  122. package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
  123. package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
  124. package/dist/src/learning/workspaceHydration.js +168 -0
  125. package/dist/src/learning/workspaceHydration.test.js +30 -0
  126. package/dist/src/license.policy.contract.test.js +9 -4
  127. package/dist/src/markdown/MarkdownGateway.js +4 -0
  128. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  129. package/dist/src/middleware/SseResponseWriter.js +122 -0
  130. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  131. package/dist/src/middleware/auth.js +28 -7
  132. package/dist/src/middleware/auth.test.js +33 -0
  133. package/dist/src/middleware/index.js +2 -1
  134. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  135. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  136. package/dist/src/mobile.artifact.contract.test.js +144 -0
  137. package/dist/src/mobile.budget.contract.test.js +90 -0
  138. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  139. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  140. package/dist/src/mobile.identity.contract.test.js +66 -0
  141. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  142. package/dist/src/mobile.identity.corpus.test.js +40 -0
  143. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  144. package/dist/src/mobile.pipeline.test.js +11 -1
  145. package/dist/src/mobile.profile.contract.test.js +225 -0
  146. package/dist/src/mobile.semantic.parity.test.js +92 -0
  147. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  148. package/dist/src/notemd/ContentGenerator.js +4 -9
  149. package/dist/src/notemd/FileProcessor.js +4 -9
  150. package/dist/src/notemd/NotemdService.js +12 -17
  151. package/dist/src/notemd/Translator.js +4 -9
  152. package/dist/src/notemd/progressReporter.js +9 -0
  153. package/dist/src/notemd.server.integration.test.js +119 -4
  154. package/dist/src/notemd.workflow.pipeline.test.js +1 -1
  155. package/dist/src/pathbridge.handshake.contract.test.js +29 -0
  156. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  157. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  158. package/dist/src/platform/ExportProfile.js +28 -0
  159. package/dist/src/platform/MobileBudget.js +11 -0
  160. package/dist/src/platform/PlatformCapabilities.js +14 -0
  161. package/dist/src/platform/PlatformCapabilities.test.js +6 -0
  162. package/dist/src/query_backend.external_http.integration.test.js +2 -2
  163. package/dist/src/release.godot.mirror.contract.test.js +28 -4
  164. package/dist/src/resources/ResourceRegistry.js +40 -0
  165. package/dist/src/resources/ResourceRegistry.test.js +36 -0
  166. package/dist/src/routes/index.js +0 -4
  167. package/dist/src/routes/knowledge.js +46 -33
  168. package/dist/src/routes/knowledgePayload.js +185 -0
  169. package/dist/src/routes/knowledgePayload.test.js +53 -0
  170. package/dist/src/routes/markdown.js +8 -0
  171. package/dist/src/routes/notemd.js +990 -234
  172. package/dist/src/routes/registry.contract.test.js +5 -2
  173. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  174. package/dist/src/routes/render.js +14 -51
  175. package/dist/src/runtime.capabilities.test.js +18 -2
  176. package/dist/src/server.js +566 -1090
  177. package/dist/src/server.migration.test.js +417 -15
  178. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  179. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  180. package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
  181. package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
  182. package/dist/src/simulation.worker.offline.test.js +104 -0
  183. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  184. package/dist/src/storage.provider.contract.test.js +152 -1
  185. package/dist/src/tauri.frontend.build.contract.test.js +8 -0
  186. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  187. package/dist/src/utils/RuntimePaths.js +6 -0
  188. package/dist/src/utils/RuntimePaths.test.js +27 -0
  189. package/dist/src/windows.installer.build.test.js +109 -0
  190. package/dist/src/windows.installer.payload.test.js +76 -0
  191. package/dist/src/windows.installer.runtime.test.js +69 -0
  192. package/dist/src/workspace/WorkspaceRegistry.js +13 -0
  193. package/package.json +32 -10
  194. package/dist/src/routes/data.js +0 -267
  195. package/dist/src/routes/diagnostics.js +0 -51
@@ -2,9 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assembleRagEvidenceContext = assembleRagEvidenceContext;
4
4
  const ragContextPack_1 = require("./ragContextPack");
5
+ const graphConditionedContext_1 = require("./graphConditionedContext");
6
+ const ragEvidenceQuality_1 = require("./ragEvidenceQuality");
5
7
  const DEFAULT_PARAGRAPH_WINDOW = 5;
6
8
  const MAX_GRAPH_NEIGHBOR_DOCUMENT_CONTEXT_FRAGMENTS = 2;
7
- const COMPARABLE_NUMERIC_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?)\s+(?:is|=|:)\s*(?:±|\+\/-|\+\s*\/\s*-)?\s*(-?\d+(?:\.\d+)?)\s*(mm|cm|m|um|µm|nm|kg|g|mg|s|ms|%|deg|degree|degrees|c|k)\b/gi;
8
9
  const COMPARABLE_QUANTITY_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:count|limit|threshold|budget|quota|capacity|size|window|attempts|retries))\s+(?:is|are|=|:)\s*(-?\d+(?:\.\d+)?)\b/gi;
9
10
  const COMPARABLE_DATE_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:date|year|deadline|cutoff|cut-off|version|release|revision|effective))\s+(?:is|=|:)\s*(\d{4}(?:-\d{2}-\d{2})?)\b/gi;
10
11
  const COMPARABLE_STATE_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:status|state|mode|flag|policy|availability|setting|gate|switch))\s+(?:is|=|:)\s*(enabled|disabled|active|inactive|available|unavailable|supported|unsupported|allowed|blocked|required|optional|open|closed|on|off)\b/gi;
@@ -84,50 +85,110 @@ const COMPARABLE_PLATFORM_SCOPE_ALIASES = {
84
85
  mobile: 'mobile',
85
86
  };
86
87
  const COMPARABLE_PLATFORM_SCOPE_PATTERN = /\b(?:in|for|on|under|within)\s+(?:the\s+)?(windows|win32|macos|mac|linux|android|ios|web|desktop|mobile)(?:\s+(?:platform|os|runtime|client|app|build|target))?\b|\b(windows|win32|macos|mac|linux|android|ios|web|desktop|mobile)\s+(?:platform|os|runtime|client|app|build|target)\b/i;
88
+ const COMPARABLE_TIME_SCOPE_PATTERN = /\b(?:in|during|on|as of)\s+(\d{4}(?:-\d{2}(?:-\d{2})?)?)\b/i;
89
+ const MEASUREMENT_UNIT_GROUPS = [
90
+ { aliases: ['B', 'byte', 'bytes', '字节'], dimension: 'information', scale: 1 },
91
+ { aliases: ['b', 'bit', 'bits', '比特'], dimension: 'information', scale: 1 / 8 },
92
+ { aliases: ['kB', 'kilobyte', 'kilobytes'], dimension: 'information', scale: 1000 },
93
+ { aliases: ['MB', 'megabyte', 'megabytes', '兆字节'], dimension: 'information', scale: 1e6 },
94
+ { aliases: ['Mb', 'megabit', 'megabits'], dimension: 'information', scale: 1e6 / 8 },
95
+ { aliases: ['GB', 'gigabyte', 'gigabytes'], dimension: 'information', scale: 1e9 },
96
+ { aliases: ['KiB', 'kibibyte', 'kibibytes'], dimension: 'information', scale: 1024 },
97
+ { aliases: ['MiB', 'mebibyte', 'mebibytes'], dimension: 'information', scale: 1024 ** 2 },
98
+ { aliases: ['GiB', 'gibibyte', 'gibibytes'], dimension: 'information', scale: 1024 ** 3 },
99
+ { aliases: ['m', 'meter', 'meters', 'metre', 'metres', '米'], dimension: 'length', scale: 1 },
100
+ { aliases: ['cm', '厘米'], dimension: 'length', scale: 1e-2 },
101
+ { aliases: ['mm', '毫米'], dimension: 'length', scale: 1e-3 },
102
+ { aliases: ['um', 'µm', 'μm', '微米'], dimension: 'length', scale: 1e-6 },
103
+ { aliases: ['nm', '纳米'], dimension: 'length', scale: 1e-9 },
104
+ { aliases: ['kg', '千克', '公斤'], dimension: 'mass', scale: 1 },
105
+ { aliases: ['g', 'gram', 'grams', '克'], dimension: 'mass', scale: 1e-3 },
106
+ { aliases: ['mg', '毫克'], dimension: 'mass', scale: 1e-6 },
107
+ { aliases: ['s', 'second', 'seconds', '秒'], dimension: 'duration', scale: 1 },
108
+ { aliases: ['ms', 'millisecond', 'milliseconds', '毫秒'], dimension: 'duration', scale: 1e-3 },
109
+ { aliases: ['us', 'µs', 'μs', '微秒'], dimension: 'duration', scale: 1e-6 },
110
+ { aliases: ['min', 'minute', 'minutes', '分钟'], dimension: 'duration', scale: 60 },
111
+ { aliases: ['h', 'hour', 'hours', '小时'], dimension: 'duration', scale: 3600 },
112
+ { aliases: ['d', 'day', 'days', '天', '日'], dimension: 'duration', scale: 86400 },
113
+ { aliases: ['Hz', 'hz', 'hertz', '赫兹'], dimension: 'frequency', scale: 1 },
114
+ { aliases: ['kHz', 'khz', '千赫兹'], dimension: 'frequency', scale: 1000 },
115
+ { aliases: ['K', 'kelvin', '开尔文'], dimension: 'temperature', scale: 1 },
116
+ { aliases: ['C', 'c', '°C', '°c', '℃', '摄氏度'], dimension: 'temperature', scale: 1, offset: 273.15 },
117
+ { aliases: ['deg', 'degree', 'degrees', '度'], dimension: 'angle', scale: Math.PI / 180 },
118
+ { aliases: ['rad', 'radian', 'radians', '弧度'], dimension: 'angle', scale: 1 },
119
+ { aliases: ['%'], dimension: 'ratio', scale: 0.01 },
120
+ ];
121
+ const MEASUREMENT_UNITS = new Map(MEASUREMENT_UNIT_GROUPS.flatMap(group => group.aliases.map(alias => [alias, group])));
122
+ const MEASUREMENT_UNIT_PATTERN = Array.from(MEASUREMENT_UNITS.keys()).sort((left, right) => right.length - left.length).join('|');
123
+ const MEASUREMENT_NUMBER_PATTERN = /[+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i;
124
+ const MEASUREMENT_PREFIX_PATTERNS = [
125
+ /(?<![\p{L}\p{N}])([\p{L}][\p{L}\p{N} _-]{1,100}?)(?:\s+(?:is|are)\s+|\s*[:=]\s*)(not\s+)?/u,
126
+ /(?<![\p{L}\p{N}])([\p{L}][\p{L}\p{N} _-]{1,100}?)\s*(?:(并不是|不是|不为|不等于|并非)|是|为|等于|:)\s*/u,
127
+ ];
128
+ const MEASUREMENT_FACT_PATTERNS = MEASUREMENT_PREFIX_PATTERNS.map(prefix => new RegExp(prefix.source + /(?:(?:\+\s*\/\s*-|±)\s*)?/.source + '(' + MEASUREMENT_NUMBER_PATTERN.source + ')' + /\s*/.source
129
+ + '(' + MEASUREMENT_UNIT_PATTERN + ')' + /(?![a-z0-9_])/.source, 'giu'));
130
+ const MEASUREMENT_UNIT_SUFFIX_PATTERN = new RegExp('^\\s*(?:' + MEASUREMENT_UNIT_PATTERN + ')(?![a-z0-9_])', 'iu');
131
+ const CHINESE_SCOPE_TERMS = {
132
+ 当前: ' current ', 目前: ' current ', 现行: ' current ', 历史: ' historical ', 旧版: ' historical ',
133
+ 计划: ' planned ', 未来: ' planned ', 生产环境: ' production environment ', 测试环境: ' test environment ',
134
+ 开发环境: ' development environment ', 预发环境: ' staging environment ', 预生产环境: ' staging environment ',
135
+ 版本: ' version ', 安卓平台: ' android platform ', 桌面平台: ' desktop platform ', 移动平台: ' mobile platform ',
136
+ };
137
+ const CHINESE_SCOPE_PATTERN = new RegExp(Object.keys(CHINESE_SCOPE_TERMS).sort((a, b) => b.length - a.length).join('|'), 'gu');
138
+ function normalizeScopeLanguage(value) {
139
+ return value.replace(CHINESE_SCOPE_PATTERN, term => CHINESE_SCOPE_TERMS[term])
140
+ .replace(/(\d{4})年(?:(\d{1,2})月)?(?:(\d{1,2})日)?/gu, (_match, year, month, day) => (` in ${year}${month ? `-${month.padStart(2, '0')}` : ''}${day ? `-${day.padStart(2, '0')}` : ''} `));
141
+ }
87
142
  function normalizeWhitespace(value) {
88
143
  return String(value || '').replace(/\s+/g, ' ').trim();
89
144
  }
145
+ function finishEvidenceFragment(fragment, execution) {
146
+ if (!execution)
147
+ return fragment;
148
+ execution.recordFragment();
149
+ if (fragment.text.length <= execution.maxFragmentChars)
150
+ return fragment;
151
+ const text = fragment.text.slice(0, execution.maxFragmentChars);
152
+ return {
153
+ ...fragment, text, charCount: text.length, tokenEstimate: (0, ragContextPack_1.estimateRagTokenCount)(text),
154
+ truncated: true, truncationReason: 'runtime_fragment_chars_limit',
155
+ };
156
+ }
90
157
  function sanitizeFragmentPart(value) {
91
158
  return normalizeWhitespace(value)
92
159
  .replace(/[^a-zA-Z0-9_-]+/g, '_')
93
160
  .replace(/^_+|_+$/g, '')
94
161
  .slice(0, 80) || 'fragment';
95
162
  }
96
- function splitSourceLines(content) {
163
+ function splitSourceLines(content, execution) {
97
164
  const normalizedContent = String(content || '');
98
165
  if (!normalizedContent) {
99
166
  return [];
100
167
  }
101
- const lines = normalizedContent.split(/\r\n|\n|\r/);
102
168
  const records = [];
169
+ const newline = /\r\n|\n|\r/g;
103
170
  let cursor = 0;
104
- lines.forEach((line, index) => {
105
- const startOffset = cursor;
106
- const endOffset = startOffset + line.length;
171
+ let delimiter;
172
+ while ((delimiter = newline.exec(normalizedContent)) !== null) {
173
+ execution?.checkSourceLineCount(records.length + 1);
107
174
  records.push({
108
- lineNumber: index + 1,
109
- text: line,
110
- startOffset,
111
- endOffset,
175
+ lineNumber: records.length + 1,
176
+ text: normalizedContent.slice(cursor, delimiter.index),
177
+ startOffset: cursor,
178
+ endOffset: delimiter.index,
112
179
  });
113
- if (normalizedContent.startsWith('\r\n', endOffset)) {
114
- cursor = endOffset + 2;
115
- }
116
- else if (normalizedContent[endOffset] === '\n' || normalizedContent[endOffset] === '\r') {
117
- cursor = endOffset + 1;
118
- }
119
- else {
120
- cursor = endOffset;
121
- }
122
- });
180
+ cursor = delimiter.index + delimiter[0].length;
181
+ }
182
+ execution?.checkSourceLineCount(records.length + 1);
183
+ records.push({ lineNumber: records.length + 1, text: normalizedContent.slice(cursor), startOffset: cursor, endOffset: normalizedContent.length });
123
184
  return records;
124
185
  }
125
186
  function isTableLine(text) {
126
187
  const trimmed = String(text || '').trim();
127
188
  return trimmed.startsWith('|') && trimmed.endsWith('|');
128
189
  }
129
- function parseMarkdownBlocks(content) {
130
- const sourceLines = splitSourceLines(content);
190
+ function parseMarkdownBlocks(content, execution) {
191
+ const sourceLines = splitSourceLines(content, execution);
131
192
  const blocks = [];
132
193
  const headingStack = [];
133
194
  let pendingLines = [];
@@ -158,6 +219,7 @@ function parseMarkdownBlocks(content) {
158
219
  pendingHeadingPath = currentHeadingPath();
159
220
  };
160
221
  sourceLines.forEach((line) => {
222
+ execution?.assertActive();
161
223
  const rawText = String(line.text || '');
162
224
  const trimmed = rawText.trim();
163
225
  const fenceLine = /^```/.test(trimmed);
@@ -306,7 +368,7 @@ function normalizeComparableFactSubject(value) {
306
368
  return normalizeWhitespace(value)
307
369
  .toLowerCase()
308
370
  .replace(/^(the|a|an)\s+/i, '')
309
- .replace(/[^a-z0-9]+/g, ' ')
371
+ .replace(/[^\p{L}\p{N}]+/gu, ' ')
310
372
  .trim();
311
373
  }
312
374
  function comparableFactSentenceTail(blockText, match) {
@@ -315,7 +377,8 @@ function comparableFactSentenceTail(blockText, match) {
315
377
  return '';
316
378
  }
317
379
  const tail = String(blockText || '').slice(matchIndex + String(match[0] || '').length);
318
- return normalizeWhitespace(tail.split(/[.!?]/)[0] || '');
380
+ const boundary = /[!?。!?;;\r\n]|\.(?!\d)/u.exec(tail);
381
+ return normalizeWhitespace(boundary ? tail.slice(0, boundary.index) : tail);
319
382
  }
320
383
  function comparableFactTemporalScopeKey(subjectLabel, sentenceTail) {
321
384
  const scopedText = `${normalizeWhitespace(subjectLabel)} ${normalizeWhitespace(sentenceTail)}`;
@@ -356,6 +419,8 @@ function comparableFactPlatformScopeKey(subjectLabel, sentenceTail) {
356
419
  return COMPARABLE_PLATFORM_SCOPE_ALIASES[platformLabel] || null;
357
420
  }
358
421
  function comparableFactScopeKeys(subjectLabel, sentenceTail) {
422
+ subjectLabel = normalizeScopeLanguage(subjectLabel);
423
+ sentenceTail = normalizeScopeLanguage(sentenceTail);
359
424
  const scopeKeys = [];
360
425
  const temporalScopeKey = comparableFactTemporalScopeKey(subjectLabel, sentenceTail);
361
426
  if (temporalScopeKey) {
@@ -373,26 +438,89 @@ function comparableFactScopeKeys(subjectLabel, sentenceTail) {
373
438
  if (platformScopeKey) {
374
439
  scopeKeys.push(`platform:${platformScopeKey}`);
375
440
  }
441
+ const timeScope = COMPARABLE_TIME_SCOPE_PATTERN.exec(`${subjectLabel} ${sentenceTail}`)?.[1];
442
+ if (timeScope)
443
+ scopeKeys.push(`time:${timeScope}`);
376
444
  return scopeKeys.sort();
377
445
  }
378
446
  function comparableFactSubjectKey(subjectLabel, scopeKeys) {
447
+ subjectLabel = normalizeScopeLanguage(subjectLabel);
379
448
  const scopedSubjectLabel = scopeKeys.some((scopeKey) => scopeKey.startsWith('temporal:'))
380
449
  ? normalizeWhitespace(subjectLabel).replace(COMPARABLE_TEMPORAL_SCOPE_PATTERN, '')
381
450
  : subjectLabel;
382
- const subjectKey = normalizeComparableFactSubject(scopedSubjectLabel);
451
+ const subjectKey = normalizeComparableFactSubject(scopedSubjectLabel
452
+ .replace(COMPARABLE_ENVIRONMENT_SCOPE_PATTERN, '')
453
+ .replace(COMPARABLE_PLATFORM_SCOPE_PATTERN, '')
454
+ .replace(COMPARABLE_VERSION_SCOPE_PATTERN, '')
455
+ .replace(COMPARABLE_TIME_SCOPE_PATTERN, ''));
383
456
  return scopeKeys.length > 0 && subjectKey
384
457
  ? `${subjectKey}@scope:${scopeKeys.join('+')}`
385
458
  : subjectKey;
386
459
  }
387
- function normalizeComparableFactUnit(value) {
388
- const normalized = normalizeWhitespace(value).toLowerCase();
389
- if (normalized === 'um') {
390
- return 'µm';
460
+ function measurementScopeKeys(text) {
461
+ const normalized = normalizeScopeLanguage(text);
462
+ const keys = new Set();
463
+ for (const pattern of [COMPARABLE_TEMPORAL_SCOPE_PATTERN, COMPARABLE_ENVIRONMENT_SCOPE_PATTERN,
464
+ COMPARABLE_VERSION_SCOPE_PATTERN, COMPARABLE_PLATFORM_SCOPE_PATTERN, COMPARABLE_TIME_SCOPE_PATTERN]) {
465
+ for (const match of normalized.matchAll(new RegExp(pattern.source, 'gi'))) {
466
+ comparableFactScopeKeys('', match[0]).forEach(key => keys.add(key));
467
+ }
391
468
  }
392
- if (normalized === 'degree' || normalized === 'degrees') {
393
- return 'deg';
469
+ // A coordinated qualifier ("in production or staging") is not a single scope.
470
+ if (Array.from(keys).some(key => key.startsWith('environment:'))) {
471
+ for (const word of normalized.toLowerCase().match(/\b[a-z]+\b/g) || []) {
472
+ const environment = COMPARABLE_ENVIRONMENT_SCOPE_ALIASES[word];
473
+ if (environment)
474
+ keys.add(`environment:${environment}`);
475
+ }
476
+ }
477
+ if (Array.from(keys).some(key => key.startsWith('platform:'))) {
478
+ for (const word of normalized.toLowerCase().match(/\b[a-z0-9]+\b/g) || []) {
479
+ const platform = COMPARABLE_PLATFORM_SCOPE_ALIASES[word];
480
+ if (platform)
481
+ keys.add(`platform:${platform}`);
482
+ }
394
483
  }
395
- return normalized;
484
+ const dimensions = new Set();
485
+ for (const key of keys) {
486
+ const dimension = key.slice(0, key.indexOf(':'));
487
+ if (dimensions.has(dimension))
488
+ return null;
489
+ dimensions.add(dimension);
490
+ }
491
+ return Array.from(keys).sort();
492
+ }
493
+ function measurementHeadingScopeKeys(headingPath) {
494
+ const byDimension = new Map();
495
+ for (const heading of headingPath) {
496
+ const keys = measurementScopeKeys(`in ${heading}`);
497
+ if (!keys)
498
+ return null;
499
+ for (const key of keys)
500
+ byDimension.set(key.slice(0, key.indexOf(':')), key);
501
+ }
502
+ return Array.from(byDimension.values()).sort();
503
+ }
504
+ function measurementAssertionScopeKeys(headingKeys, paragraphKeys, clauseKeys) {
505
+ const byDimension = new Map();
506
+ for (const key of [...headingKeys, ...paragraphKeys, ...clauseKeys]) {
507
+ byDimension.set(key.slice(0, key.indexOf(':')), key);
508
+ }
509
+ return Array.from(byDimension.values()).sort();
510
+ }
511
+ function standaloneMeasurementScopeKeys(clause) {
512
+ const normalized = normalizeScopeLanguage(clause).replace(/[.::。]+$/u, '').trim();
513
+ const keys = measurementScopeKeys(`in ${normalized}`);
514
+ if (!keys?.length)
515
+ return null;
516
+ const remainder = `in ${normalized}`
517
+ .replace(COMPARABLE_TEMPORAL_SCOPE_PATTERN, '')
518
+ .replace(COMPARABLE_ENVIRONMENT_SCOPE_PATTERN, '')
519
+ .replace(COMPARABLE_VERSION_SCOPE_PATTERN, '')
520
+ .replace(COMPARABLE_PLATFORM_SCOPE_PATTERN, '')
521
+ .replace(COMPARABLE_TIME_SCOPE_PATTERN, '')
522
+ .replace(/\b(?:in|for|on|under|within|during|as of|the)\b/gi, '').trim();
523
+ return remainder ? null : keys;
396
524
  }
397
525
  function normalizeComparableDateValue(value) {
398
526
  const normalized = normalizeWhitespace(value);
@@ -499,27 +627,61 @@ function extractComparableEvidenceFacts(params) {
499
627
  return [];
500
628
  }
501
629
  const facts = [];
502
- for (const match of String(params.block.text || '').matchAll(COMPARABLE_NUMERIC_FACT_PATTERN)) {
503
- const subjectLabel = normalizeWhitespace(match[1]);
504
- const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
505
- const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
506
- const value = Number(match[2]);
507
- const unit = normalizeComparableFactUnit(match[3]);
508
- if (!subjectKey || !Number.isFinite(value) || !unit) {
630
+ const appendFact = (fact) => {
631
+ params.execution?.recordSourceFact();
632
+ facts.push(fact);
633
+ };
634
+ const headingKeys = measurementHeadingScopeKeys(params.block.headingPath);
635
+ let paragraphKeys = [];
636
+ for (const clause of (0, ragEvidenceQuality_1.iterateRagEvidenceClauses)(params.block.text)) {
637
+ params.execution?.assertActive();
638
+ const inheritedKeys = paragraphKeys;
639
+ // Only an immediately preceding standalone qualifier is unambiguous.
640
+ paragraphKeys = standaloneMeasurementScopeKeys(clause) || [];
641
+ const clauseKeys = measurementScopeKeys(clause);
642
+ if (!headingKeys || !clauseKeys)
643
+ continue;
644
+ if (/\b(?:approximately|roughly|about|at least|at most|between|range)\b|大约|约为|至少|至多|范围/iu.test(clause))
509
645
  continue;
646
+ const scopeKeys = measurementAssertionScopeKeys(headingKeys, inheritedKeys, clauseKeys);
647
+ for (const pattern of MEASUREMENT_FACT_PATTERNS) {
648
+ for (const match of clause.matchAll(pattern)) {
649
+ params.execution?.assertActive();
650
+ const tail = clause.slice(match.index + match[0].length);
651
+ if (/^\s*(?:[\/^*]|(?:[-–—~~]|to|至|到)\s*[+-]?\d)/iu.test(tail))
652
+ continue;
653
+ const subjectLabel = normalizeWhitespace(match[1]);
654
+ const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
655
+ const tolerance = /(?:\btolerance|公差|容差)$/iu.test(subjectLabel);
656
+ // A named tolerance defines a half-width; an uncertain observation does not define an exact value.
657
+ if (/±|\+\s*\/\s*-/u.test(clause)
658
+ && (!tolerance || !/±|\+\s*\/\s*-/u.test(match[0])))
659
+ continue;
660
+ // SI symbol case is significant: µM is not µm, nor Ms milliseconds.
661
+ const unit = MEASUREMENT_UNITS.get(match[4])
662
+ || (match[4].length > 2 ? MEASUREMENT_UNITS.get(match[4].toLowerCase()) : undefined);
663
+ if (!subjectKey || !unit)
664
+ continue;
665
+ const converted = Number(match[3]) * unit.scale + (tolerance ? 0 : (unit.offset || 0));
666
+ if (!Number.isFinite(converted) || (tolerance && converted < 0))
667
+ continue;
668
+ // Significant digits remove conversion noise without rounding tiny measurements to zero.
669
+ const magnitude = Number(converted.toPrecision(15));
670
+ const relation = match[2] ? 'not_equal' : 'equal';
671
+ const dimension = tolerance ? `tolerance:${unit.dimension}` : unit.dimension;
672
+ appendFact({
673
+ subjectKey, subjectLabel,
674
+ valueKey: `${dimension}:${relation}:${magnitude}`,
675
+ valueLabel: `${match[2] || ''}${match[3]} ${match[4]}`,
676
+ factKind: 'measurement', measurement: { dimension, magnitude, relation },
677
+ block: params.block, citationIds: params.citationIds, item: params.item,
678
+ });
679
+ }
510
680
  }
511
- facts.push({
512
- subjectKey,
513
- subjectLabel,
514
- valueKey: `${Number(value.toFixed(12))}:${unit}`,
515
- valueLabel: `${match[2]} ${match[3]}`,
516
- factKind: 'measurement',
517
- block: params.block,
518
- citationIds: params.citationIds,
519
- item: params.item,
520
- });
521
681
  }
522
682
  for (const match of String(params.block.text || '').matchAll(COMPARABLE_QUANTITY_FACT_PATTERN)) {
683
+ if (MEASUREMENT_UNIT_SUFFIX_PATTERN.test(params.block.text.slice(match.index + match[0].length)))
684
+ continue;
523
685
  const subjectLabel = normalizeWhitespace(match[1]);
524
686
  const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
525
687
  const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
@@ -527,7 +689,7 @@ function extractComparableEvidenceFacts(params) {
527
689
  if (!subjectKey || !Number.isFinite(value)) {
528
690
  continue;
529
691
  }
530
- facts.push({
692
+ appendFact({
531
693
  subjectKey,
532
694
  subjectLabel,
533
695
  valueKey: `${Number(value.toFixed(12))}`,
@@ -546,7 +708,7 @@ function extractComparableEvidenceFacts(params) {
546
708
  if (!subjectKey || !valueKey) {
547
709
  continue;
548
710
  }
549
- facts.push({
711
+ appendFact({
550
712
  subjectKey,
551
713
  subjectLabel,
552
714
  valueKey,
@@ -565,7 +727,7 @@ function extractComparableEvidenceFacts(params) {
565
727
  if (!subjectKey || !stateValue) {
566
728
  continue;
567
729
  }
568
- facts.push({
730
+ appendFact({
569
731
  subjectKey: `${subjectKey}:${stateValue.groupKey}`,
570
732
  subjectLabel,
571
733
  valueKey: stateValue.valueKey,
@@ -584,7 +746,7 @@ function extractComparableEvidenceFacts(params) {
584
746
  if (!subjectKey || !valueKey) {
585
747
  continue;
586
748
  }
587
- facts.push({
749
+ appendFact({
588
750
  subjectKey,
589
751
  subjectLabel,
590
752
  valueKey,
@@ -603,7 +765,7 @@ function extractComparableEvidenceFacts(params) {
603
765
  if (!subjectKey || !valueKey) {
604
766
  continue;
605
767
  }
606
- facts.push({
768
+ appendFact({
607
769
  subjectKey,
608
770
  subjectLabel,
609
771
  valueKey,
@@ -622,7 +784,7 @@ function extractComparableEvidenceFacts(params) {
622
784
  if (!subjectKey || !valueKey) {
623
785
  continue;
624
786
  }
625
- facts.push({
787
+ appendFact({
626
788
  subjectKey,
627
789
  subjectLabel,
628
790
  valueKey,
@@ -641,7 +803,7 @@ function extractComparableEvidenceFacts(params) {
641
803
  if (!subjectKey || !valueKey) {
642
804
  continue;
643
805
  }
644
- facts.push({
806
+ appendFact({
645
807
  subjectKey,
646
808
  subjectLabel,
647
809
  valueKey,
@@ -660,7 +822,7 @@ function extractComparableEvidenceFacts(params) {
660
822
  if (!subjectKey || !valueKey) {
661
823
  continue;
662
824
  }
663
- facts.push({
825
+ appendFact({
664
826
  subjectKey,
665
827
  subjectLabel,
666
828
  valueKey,
@@ -679,7 +841,7 @@ function extractComparableEvidenceFacts(params) {
679
841
  if (!subjectKey || !valueKey) {
680
842
  continue;
681
843
  }
682
- facts.push({
844
+ appendFact({
683
845
  subjectKey,
684
846
  subjectLabel,
685
847
  valueKey,
@@ -698,7 +860,7 @@ function extractComparableEvidenceFacts(params) {
698
860
  if (!subjectKey || !valueKey) {
699
861
  continue;
700
862
  }
701
- facts.push({
863
+ appendFact({
702
864
  subjectKey,
703
865
  subjectLabel,
704
866
  valueKey,
@@ -717,7 +879,7 @@ function extractComparableEvidenceFacts(params) {
717
879
  if (!subjectKey || !valueKey) {
718
880
  continue;
719
881
  }
720
- facts.push({
882
+ appendFact({
721
883
  subjectKey,
722
884
  subjectLabel,
723
885
  valueKey,
@@ -736,7 +898,7 @@ function extractComparableEvidenceFacts(params) {
736
898
  if (!subjectKey || !valueKey) {
737
899
  continue;
738
900
  }
739
- facts.push({
901
+ appendFact({
740
902
  subjectKey,
741
903
  subjectLabel,
742
904
  valueKey,
@@ -763,10 +925,11 @@ function buildBlockCitationMap(group, blocks, source) {
763
925
  });
764
926
  return citationIdsByBlock;
765
927
  }
766
- function extractComparableEvidenceFactsFromBlockEntries(entries, citationIdsByBlock) {
928
+ function extractComparableEvidenceFactsFromBlockEntries(entries, citationIdsByBlock, execution) {
767
929
  return entries
768
930
  .sort((left, right) => left.block.startOffset - right.block.startOffset)
769
931
  .flatMap(({ block, item }) => extractComparableEvidenceFacts({
932
+ execution,
770
933
  block,
771
934
  item,
772
935
  citationIds: Array.from(citationIdsByBlock.get(sourceBlockKey(block)) || []),
@@ -796,8 +959,7 @@ function buildEvidenceItemMap(group, blocks, source) {
796
959
  });
797
960
  return itemByBlock;
798
961
  }
799
- function collectSelectedContextComparableFacts(group, source, paragraphWindow) {
800
- const blocks = parseMarkdownBlocks(source.content);
962
+ function collectSelectedContextComparableFacts(group, source, paragraphWindow, blocks, execution) {
801
963
  const citationIdsByBlock = buildBlockCitationMap(group, blocks, source);
802
964
  const selectedBlocks = new Map();
803
965
  group.entries
@@ -810,33 +972,42 @@ function collectSelectedContextComparableFacts(group, source, paragraphWindow) {
810
972
  });
811
973
  });
812
974
  });
813
- return extractComparableEvidenceFactsFromBlockEntries(Array.from(selectedBlocks.values()), citationIdsByBlock);
975
+ return extractComparableEvidenceFactsFromBlockEntries(Array.from(selectedBlocks.values()), citationIdsByBlock, execution);
814
976
  }
815
- function collectFullDocumentComparableFacts(group, source) {
977
+ function collectFullDocumentComparableFacts(group, source, blocks, execution) {
816
978
  const fallbackItem = selectRepresentativeGroupItem(group);
817
979
  if (!fallbackItem) {
818
980
  return [];
819
981
  }
820
- const blocks = parseMarkdownBlocks(source.content);
821
982
  const citationIdsByBlock = buildBlockCitationMap(group, blocks, source);
822
983
  const evidenceItemByBlock = buildEvidenceItemMap(group, blocks, source);
823
984
  return extractComparableEvidenceFactsFromBlockEntries(blocks.map((block) => ({
824
985
  block,
825
986
  item: evidenceItemByBlock.get(sourceBlockKey(block)) || fallbackItem,
826
- })), citationIdsByBlock);
987
+ })), citationIdsByBlock, execution);
827
988
  }
828
989
  function comparableFactDocumentKey(fact) {
829
990
  return `${fact.item.atom.documentId}\n${fact.item.atom.sourcePath}`;
830
991
  }
831
- function buildConflictFragments(group, facts, paragraphWindow) {
992
+ function comparableEvidenceFactsConflict(left, right) {
993
+ if (left.subjectKey !== right.subjectKey || left.factKind !== right.factKind)
994
+ return false;
995
+ if (left.factKind === 'measurement' && right.factKind === 'measurement') {
996
+ if (left.measurement.dimension !== right.measurement.dimension)
997
+ return false;
998
+ const sameMagnitude = left.measurement.magnitude === right.measurement.magnitude;
999
+ if (left.measurement.relation !== right.measurement.relation)
1000
+ return sameMagnitude;
1001
+ return left.measurement.relation === 'equal' && !sameMagnitude;
1002
+ }
1003
+ return left.valueKey !== right.valueKey;
1004
+ }
1005
+ function buildConflictFragments(group, facts, paragraphWindow, execution) {
832
1006
  const fragments = [];
833
1007
  const seenConflicts = new Set();
834
1008
  facts.forEach((left, leftIndex) => {
835
1009
  facts.slice(leftIndex + 1).forEach((right) => {
836
- if (left.subjectKey !== right.subjectKey || left.factKind !== right.factKind) {
837
- return;
838
- }
839
- if (left.valueKey === right.valueKey) {
1010
+ if (!comparableEvidenceFactsConflict(left, right)) {
840
1011
  return;
841
1012
  }
842
1013
  const blockDistance = Math.abs(left.block.startLine - right.block.startLine);
@@ -864,7 +1035,7 @@ function buildConflictFragments(group, facts, paragraphWindow) {
864
1035
  `Conflicting evidence for ${left.subjectLabel}:`,
865
1036
  ...conflictBlockTexts,
866
1037
  ].join('\n');
867
- fragments.push({
1038
+ fragments.push(finishEvidenceFragment({
868
1039
  fragmentId: `rag_conflict_${sanitizeFragmentPart(group.documentId)}_${fragments.length + 1}`,
869
1040
  role: 'conflict',
870
1041
  text,
@@ -887,12 +1058,12 @@ function buildConflictFragments(group, facts, paragraphWindow) {
887
1058
  ])),
888
1059
  score: Number(Math.max(Number(left.item.score || 0), Number(right.item.score || 0)).toFixed(4)),
889
1060
  sourceBoundary: 'full_document',
890
- });
1061
+ }, execution));
891
1062
  });
892
1063
  });
893
1064
  return fragments;
894
1065
  }
895
- function buildCrossDocumentConflictFragments(facts) {
1066
+ function buildCrossDocumentConflictFragments(facts, execution) {
896
1067
  const orderedFacts = facts.slice().sort((left, right) => {
897
1068
  const sourceDelta = String(left.item.atom.sourcePath || '').localeCompare(String(right.item.atom.sourcePath || ''));
898
1069
  if (sourceDelta !== 0) {
@@ -903,14 +1074,12 @@ function buildCrossDocumentConflictFragments(facts) {
903
1074
  const fragments = [];
904
1075
  const seenConflicts = new Set();
905
1076
  orderedFacts.forEach((left, leftIndex) => {
1077
+ execution?.assertActive();
906
1078
  orderedFacts.slice(leftIndex + 1).forEach((right) => {
907
1079
  if (comparableFactDocumentKey(left) === comparableFactDocumentKey(right)) {
908
1080
  return;
909
1081
  }
910
- if (left.subjectKey !== right.subjectKey || left.factKind !== right.factKind) {
911
- return;
912
- }
913
- if (left.valueKey === right.valueKey) {
1082
+ if (!comparableEvidenceFactsConflict(left, right)) {
914
1083
  return;
915
1084
  }
916
1085
  const orderedSourceKeys = [comparableFactDocumentKey(left), comparableFactDocumentKey(right)].sort();
@@ -931,7 +1100,7 @@ function buildCrossDocumentConflictFragments(facts) {
931
1100
  `${left.item.atom.title}: ${left.block.text}`,
932
1101
  `${right.item.atom.title}: ${right.block.text}`,
933
1102
  ].join('\n');
934
- fragments.push({
1103
+ fragments.push(finishEvidenceFragment({
935
1104
  fragmentId: `rag_conflict_cross_document_${sanitizeFragmentPart(left.item.atom.documentId)}_${sanitizeFragmentPart(right.item.atom.documentId)}_${fragments.length + 1}`,
936
1105
  role: 'conflict',
937
1106
  text,
@@ -950,7 +1119,7 @@ function buildCrossDocumentConflictFragments(facts) {
950
1119
  ])),
951
1120
  score: Number(Math.max(Number(left.item.score || 0), Number(right.item.score || 0)).toFixed(4)),
952
1121
  sourceBoundary: 'full_document',
953
- });
1122
+ }, execution));
954
1123
  });
955
1124
  });
956
1125
  return fragments;
@@ -974,9 +1143,9 @@ function limitGraphNeighborDocumentContextFragments(fragments) {
974
1143
  || fragment.sourceBoundary !== 'full_document'
975
1144
  || selectedContextFragmentIds.has(fragment.fragmentId)));
976
1145
  }
977
- function buildDirectFragment(item, span, index, role = 'direct_support') {
1146
+ function buildDirectFragment(item, span, index, role = 'direct_support', execution) {
978
1147
  const text = normalizeWhitespace(String(span.snippet || item.atom.content || item.atom.title || ''));
979
- return {
1148
+ return finishEvidenceFragment({
980
1149
  fragmentId: `${role === 'graph_neighbor_support' ? 'rag_graph_neighbor' : 'rag_direct'}_${sanitizeFragmentPart(item.atom.documentId)}_${sanitizeFragmentPart(span.id || String(index + 1))}`,
981
1150
  role,
982
1151
  text,
@@ -996,7 +1165,7 @@ function buildDirectFragment(item, span, index, role = 'direct_support') {
996
1165
  relationEdgeIds: item.relationPath.map((edge) => edge.id),
997
1166
  score: Number(Number(item.score || 0).toFixed(4)),
998
1167
  sourceBoundary: 'direct_span_only',
999
- };
1168
+ }, execution);
1000
1169
  }
1001
1170
  function groupItemsByDocument(items, graphNeighborItems = []) {
1002
1171
  const groups = new Map();
@@ -1038,7 +1207,7 @@ async function resolveDocumentSource(group, sourceResolver) {
1038
1207
  atomIds,
1039
1208
  citationIds,
1040
1209
  });
1041
- if (!source || !String(source.content || '').trim()) {
1210
+ if (!source || (!source.unavailableReason && !String(source.content || '').trim())) {
1042
1211
  return null;
1043
1212
  }
1044
1213
  return {
@@ -1048,8 +1217,7 @@ async function resolveDocumentSource(group, sourceResolver) {
1048
1217
  content: String(source.content || ''),
1049
1218
  };
1050
1219
  }
1051
- function buildParentFragments(group, source, paragraphWindow) {
1052
- const blocks = parseMarkdownBlocks(source.content);
1220
+ function buildParentFragments(group, source, paragraphWindow, blocks, execution) {
1053
1221
  const parentDrafts = new Map();
1054
1222
  group.entries
1055
1223
  .filter((entry) => entry.expandDocumentContext)
@@ -1098,7 +1266,7 @@ function buildParentFragments(group, source, paragraphWindow) {
1098
1266
  const fragmentPrefix = draft.role === 'graph_neighbor_support'
1099
1267
  ? 'rag_graph_neighbor_context'
1100
1268
  : 'rag_parent';
1101
- return {
1269
+ return finishEvidenceFragment({
1102
1270
  fragmentId: `${fragmentPrefix}_${sanitizeFragmentPart(draft.documentId)}_${index + 1}`,
1103
1271
  role: draft.role,
1104
1272
  text,
@@ -1118,7 +1286,7 @@ function buildParentFragments(group, source, paragraphWindow) {
1118
1286
  relationEdgeIds: Array.from(draft.relationEdgeIds),
1119
1287
  score: Number(Number(draft.score || 0).toFixed(4)),
1120
1288
  sourceBoundary: 'full_document',
1121
- };
1289
+ }, execution);
1122
1290
  }).filter((fragment) => fragment.text.length > 0);
1123
1291
  }
1124
1292
  async function assembleRagEvidenceContext(params) {
@@ -1126,51 +1294,75 @@ async function assembleRagEvidenceContext(params) {
1126
1294
  const decisions = [];
1127
1295
  const rawFragments = [];
1128
1296
  const fullDocumentComparableFacts = [];
1297
+ const execution = params.execution;
1129
1298
  let readFullDocument = false;
1130
1299
  const groups = groupItemsByDocument(Array.isArray(params.items) ? params.items : [], Array.isArray(params.graphNeighborItems) ? params.graphNeighborItems : []);
1131
1300
  for (const group of groups) {
1132
- group.entries.forEach((entry) => {
1133
- const item = entry.item;
1134
- itemEvidenceSpans(item).forEach((span, spanIndex) => {
1135
- rawFragments.push(buildDirectFragment(item, span, rawFragments.length + spanIndex, entry.directRole));
1301
+ await execution?.yieldCheckpoint();
1302
+ try {
1303
+ group.entries.forEach(entry => {
1304
+ itemEvidenceSpans(entry.item).forEach((span, spanIndex) => {
1305
+ rawFragments.push(buildDirectFragment(entry.item, span, rawFragments.length + spanIndex, entry.directRole, execution));
1306
+ });
1136
1307
  });
1137
- });
1138
- const source = await resolveDocumentSource(group, params.sourceResolver);
1139
- if (!source) {
1140
- const unavailableRoles = Array.from(new Set(group.entries
1141
- .map((entry) => entry.directRole)
1142
- .filter(Boolean))).join(',');
1308
+ const source = await resolveDocumentSource(group, params.sourceResolver);
1309
+ execution?.assertActive();
1310
+ if (!source || source.unavailableReason) {
1311
+ const unavailableRoles = Array.from(new Set(group.entries.map(entry => entry.directRole))).join(',');
1312
+ decisions.push({
1313
+ documentId: group.documentId, sourcePath: group.sourcePath,
1314
+ sourceBoundary: 'direct_span_only', status: 'source_window_unavailable',
1315
+ reason: source?.unavailableReason || `source_resolver_returned_no_content:${unavailableRoles}`,
1316
+ });
1317
+ continue;
1318
+ }
1319
+ readFullDocument = true;
1320
+ const blocks = parseMarkdownBlocks(source.content, execution);
1321
+ rawFragments.push(...buildParentFragments(group, source, paragraphWindow, blocks, execution));
1322
+ const selectedFacts = collectSelectedContextComparableFacts(group, source, paragraphWindow, blocks, execution);
1323
+ fullDocumentComparableFacts.push(...collectFullDocumentComparableFacts(group, source, blocks, execution));
1324
+ rawFragments.push(...buildConflictFragments(group, selectedFacts, paragraphWindow, execution));
1143
1325
  decisions.push({
1144
- documentId: group.documentId,
1145
- sourcePath: group.sourcePath,
1146
- sourceBoundary: 'direct_span_only',
1147
- status: 'source_window_unavailable',
1148
- reason: unavailableRoles
1149
- ? `source_resolver_returned_no_content:${unavailableRoles}`
1150
- : 'source_resolver_returned_no_content',
1326
+ documentId: group.documentId, sourcePath: group.sourcePath,
1327
+ sourceBoundary: 'full_document', status: 'read', charsRead: source.content.length,
1151
1328
  });
1152
- continue;
1153
1329
  }
1154
- readFullDocument = true;
1330
+ catch (error) {
1331
+ if (!execution || !execution.isResourceLimit(error))
1332
+ throw error;
1333
+ decisions.push({
1334
+ documentId: group.documentId, sourcePath: group.sourcePath,
1335
+ sourceBoundary: 'direct_span_only', status: 'source_window_unavailable', reason: error.code,
1336
+ });
1337
+ if (error.code === 'runtime_fragment_limit')
1338
+ break;
1339
+ }
1340
+ }
1341
+ try {
1342
+ rawFragments.push(...buildCrossDocumentConflictFragments(fullDocumentComparableFacts, execution));
1343
+ }
1344
+ catch (error) {
1345
+ if (!execution || !execution.isResourceLimit(error))
1346
+ throw error;
1155
1347
  decisions.push({
1156
- documentId: group.documentId,
1157
- sourcePath: group.sourcePath,
1158
- sourceBoundary: 'full_document',
1159
- status: 'read',
1160
- charsRead: source.content.length,
1348
+ documentId: 'cross_document_conflicts', sourcePath: '',
1349
+ sourceBoundary: 'direct_span_only', status: 'source_window_unavailable', reason: error.code,
1161
1350
  });
1162
- rawFragments.push(...buildParentFragments(group, source, paragraphWindow));
1163
- const selectedContextComparableFacts = collectSelectedContextComparableFacts(group, source, paragraphWindow);
1164
- fullDocumentComparableFacts.push(...collectFullDocumentComparableFacts(group, source));
1165
- rawFragments.push(...buildConflictFragments(group, selectedContextComparableFacts, paragraphWindow));
1166
1351
  }
1167
- rawFragments.push(...buildCrossDocumentConflictFragments(fullDocumentComparableFacts));
1352
+ execution?.assertActive();
1353
+ const candidateFragments = limitGraphNeighborDocumentContextFragments(rawFragments);
1354
+ const graphConditioning = (0, graphConditionedContext_1.conditionRagFragmentsByGraphPlan)({
1355
+ fragments: candidateFragments,
1356
+ graphAnswerPlan: params.graphAnswerPlan,
1357
+ });
1168
1358
  return (0, ragContextPack_1.buildRagContextPack)({
1169
1359
  query: params.query,
1170
1360
  generatedAt: params.generatedAt,
1171
1361
  sourceBoundary: readFullDocument ? 'full_document' : 'direct_span_only',
1172
- fragments: limitGraphNeighborDocumentContextFragments(rawFragments),
1362
+ fragments: candidateFragments,
1173
1363
  sourceDecisions: decisions,
1174
1364
  budget: params.budget,
1365
+ fragmentOrder: graphConditioning.fragmentOrder,
1366
+ graphConditioning: graphConditioning.trace,
1175
1367
  });
1176
1368
  }