noteconnection 1.6.8 → 1.8.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.
- package/LICENSE +674 -21
- package/README.md +258 -64
- package/dist/src/agent_workspace.contract.parity.test.js +475 -0
- package/dist/src/agent_workspace.frontend.test.js +9989 -0
- package/dist/src/agent_workspace.locale.contract.test.js +95 -0
- package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
- package/dist/src/copy.assets.contract.test.js +130 -0
- package/dist/src/core/PathBridge.js +28 -3
- package/dist/src/export/WorkspaceExportBundle.js +1130 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
- package/dist/src/export/index.js +18 -0
- package/dist/src/export/types.js +2 -0
- package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
- package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
- package/dist/src/foundation.release.evidence.contract.test.js +406 -0
- package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
- package/dist/src/frontend/README.md +258 -64
- package/dist/src/frontend/agent_workspace.js +5353 -0
- package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
- package/dist/src/frontend/analysis.js +31 -4
- package/dist/src/frontend/app.js +3597 -141
- package/dist/src/frontend/focus_mode_interactions.js +33 -0
- package/dist/src/frontend/godot_future_path_renderer.js +540 -0
- package/dist/src/frontend/godot_tree_interactions.js +178 -0
- package/dist/src/frontend/graph_state.mjs +105 -0
- package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
- package/dist/src/frontend/i18n.mjs +186 -0
- package/dist/src/frontend/index.html +641 -266
- package/dist/src/frontend/layout_gpu.js +12 -4
- package/dist/src/frontend/locales/en.json +721 -5
- package/dist/src/frontend/locales/zh.json +721 -5
- package/dist/src/frontend/main.mjs +60 -0
- package/dist/src/frontend/markdown_runtime.js +827 -0
- package/dist/src/frontend/notemd.css +49 -0
- package/dist/src/frontend/notemd.html +7 -1
- package/dist/src/frontend/notemd.js +64 -0
- package/dist/src/frontend/path.html +107 -0
- package/dist/src/frontend/path_app.js +2189 -150
- package/dist/src/frontend/path_layout.mjs +143 -0
- package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
- package/dist/src/frontend/path_modules_bridge.js +486 -0
- package/dist/src/frontend/path_state.mjs +118 -0
- package/dist/src/frontend/path_styles.css +146 -0
- package/dist/src/frontend/path_worker_bridge.mjs +85 -0
- package/dist/src/frontend/reader.js +522 -27
- package/dist/src/frontend/runtime_bridge.js +67 -54
- package/dist/src/frontend/runtime_bridge.mjs +279 -0
- package/dist/src/frontend/settings.js +130 -12
- package/dist/src/frontend/simulationWorker.js +241 -6
- package/dist/src/frontend/source_manager.js +190 -21
- package/dist/src/frontend/styles.css +2853 -72
- package/dist/src/frontend/workbench_state.mjs +101 -0
- package/dist/src/frontend/workspace_panes.js +10168 -0
- package/dist/src/frontend.locale.contract.test.js +62 -0
- package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
- package/dist/src/indexing/IndexLifecycle.js +195 -0
- package/dist/src/indexing/IndexLifecycle.test.js +49 -0
- package/dist/src/indexing/SegmentBuilder.js +64 -0
- package/dist/src/indexing/UnitBuilder.js +48 -0
- package/dist/src/indexing/types.js +2 -0
- package/dist/src/knowledge.api.contract.test.js +170 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
- package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
- package/dist/src/learning/answerReleaseReview.js +4319 -0
- package/dist/src/learning/answerReleaseReview.test.js +2888 -0
- package/dist/src/learning/api.js +2 -0
- package/dist/src/learning/conversationComposer.js +1480 -0
- package/dist/src/learning/conversationComposer.test.js +1817 -0
- package/dist/src/learning/domains/ConversationManager.js +53 -0
- package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
- package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
- package/dist/src/learning/domains/MasteryEngine.js +387 -0
- package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
- package/dist/src/learning/domains/QualityEvaluator.js +307 -0
- package/dist/src/learning/domains/TutorRouter.js +313 -0
- package/dist/src/learning/domains/index.js +33 -0
- package/dist/src/learning/domains/types.js +7 -0
- package/dist/src/learning/errors.js +29 -0
- package/dist/src/learning/evidenceContextAssembler.js +1176 -0
- package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
- package/dist/src/learning/graphContextAssembler.js +870 -0
- package/dist/src/learning/graphContextAssembler.test.js +1033 -0
- package/dist/src/learning/index.js +28 -0
- package/dist/src/learning/queryBackend.js +1898 -0
- package/dist/src/learning/queryBackend.test.js +955 -0
- package/dist/src/learning/ragContextPack.js +257 -0
- package/dist/src/learning/ragContextPack.test.js +160 -0
- package/dist/src/learning/ragPublicText.js +38 -0
- package/dist/src/learning/ragSufficiencyJudge.js +161 -0
- package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
- package/dist/src/learning/requestNormalization.js +198 -0
- package/dist/src/learning/runtimeCapability.js +4677 -0
- package/dist/src/learning/runtimeCapability.test.js +3635 -0
- package/dist/src/learning/store.js +1240 -0
- package/dist/src/learning/store.test.js +1126 -0
- package/dist/src/learning/tutorAdapter.js +2 -0
- package/dist/src/learning/types.js +2 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
- package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
- package/dist/src/lfs.asset.policy.contract.test.js +153 -0
- package/dist/src/license.policy.contract.test.js +66 -0
- package/dist/src/memory/MemoryGovernance.js +74 -0
- package/dist/src/memory/MemoryGovernance.test.js +46 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
- package/dist/src/middleware/auth.js +17 -0
- package/dist/src/middleware/body-parser.js +45 -0
- package/dist/src/middleware/cors.js +44 -0
- package/dist/src/middleware/index.js +21 -0
- package/dist/src/middleware/request-trace.js +96 -0
- package/dist/src/notemd/AppConfigToml.js +6 -4
- package/dist/src/notemd/MermaidProcessor.js +400 -50
- package/dist/src/notemd/NotemdService.js +498 -13
- package/dist/src/notemd/PromptManager.js +15 -0
- package/dist/src/notemd/cli/commands.js +357 -0
- package/dist/src/notemd/cli/dispatcher.js +225 -0
- package/dist/src/notemd/cli/index.js +169 -0
- package/dist/src/notemd/cli/parser.js +68 -0
- package/dist/src/notemd/cli/types.js +2 -0
- package/dist/src/notemd/constants.js +43 -0
- package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
- package/dist/src/notemd/diagram/diagramSpec.js +79 -0
- package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
- package/dist/src/notemd/diagram/intent.js +95 -0
- package/dist/src/notemd/diagram/planner.js +71 -0
- package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
- package/dist/src/notemd/diagram/types.js +18 -0
- package/dist/src/notemd/index.js +26 -0
- package/dist/src/notemd/operations/capabilityManifest.js +23 -0
- package/dist/src/notemd/operations/cliContracts.js +17 -0
- package/dist/src/notemd/operations/configProfileCommands.js +85 -0
- package/dist/src/notemd/operations/registry.contract.test.js +95 -0
- package/dist/src/notemd/operations/registry.js +991 -0
- package/dist/src/notemd/operations/types.js +2 -0
- package/dist/src/notemd/providerDiagnostics.js +220 -0
- package/dist/src/notemd/providerProfiles.js +42 -0
- package/dist/src/notemd/providerTemplates.js +231 -0
- package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
- package/dist/src/notemd/search/SearchManager.js +13 -0
- package/dist/src/notemd/search/SearchProvider.js +2 -0
- package/dist/src/notemd/search/TavilyProvider.js +44 -0
- package/dist/src/notemd.agent.manifest.test.js +85 -0
- package/dist/src/notemd.api.contract.test.js +14 -1
- package/dist/src/notemd.app_config_toml.test.js +2 -1
- package/dist/src/notemd.batch.workflow.test.js +117 -0
- package/dist/src/notemd.cli.e2e.test.js +136 -0
- package/dist/src/notemd.core.test.js +51 -0
- package/dist/src/notemd.diagram.pipeline.test.js +233 -0
- package/dist/src/notemd.providerTemplates.test.js +34 -0
- package/dist/src/notemd.server.integration.test.js +143 -35
- package/dist/src/notemd.workflow.pipeline.test.js +162 -0
- package/dist/src/pathbridge.handshake.contract.test.js +16 -2
- package/dist/src/pathmode.background.contract.test.js +69 -0
- package/dist/src/pathmode.settings.api.contract.test.js +9 -0
- package/dist/src/pkg.sidecar.contract.test.js +9 -3
- package/dist/src/platform/ExportProfile.js +58 -0
- package/dist/src/platform/PlatformCapabilities.js +45 -0
- package/dist/src/platform/PlatformCapabilities.test.js +30 -0
- package/dist/src/platform/RenderMaterializer.js +33 -0
- package/dist/src/platform/RenderMaterializer.test.js +32 -0
- package/dist/src/query_backend.external_http.integration.test.js +410 -0
- package/dist/src/reader_renderer.js +404 -3
- package/dist/src/reader_renderer.test.js +87 -0
- package/dist/src/release.godot.mirror.contract.test.js +73 -0
- package/dist/src/resources/ResourceRegistry.js +223 -0
- package/dist/src/resources/ResourceRegistry.test.js +61 -0
- package/dist/src/resources/types.js +2 -0
- package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
- package/dist/src/routes/data.js +267 -0
- package/dist/src/routes/diagnostics.js +51 -0
- package/dist/src/routes/index.js +23 -0
- package/dist/src/routes/knowledge.js +968 -0
- package/dist/src/routes/markdown.js +287 -0
- package/dist/src/routes/notemd.js +565 -0
- package/dist/src/routes/registry.contract.test.js +130 -0
- package/dist/src/routes/render.js +285 -0
- package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
- package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
- package/dist/src/routes/settings.js +6 -0
- package/dist/src/routes/staticFiles.js +94 -0
- package/dist/src/routes/types.js +2 -0
- package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
- package/dist/src/server.js +11090 -1549
- package/dist/src/server.migration.test.js +193 -21
- package/dist/src/server.port.fallback.contract.test.js +63 -0
- package/dist/src/session/SessionStateStore.js +81 -0
- package/dist/src/session/SessionStateStore.test.js +58 -0
- package/dist/src/session/types.js +2 -0
- package/dist/src/settings.runtime.contract.test.js +50 -0
- package/dist/src/shared/types.contract.test.js +107 -0
- package/dist/src/shared/types.js +22 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
- package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
- package/dist/src/source_manager.loadflow.test.js +46 -0
- package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
- package/dist/src/tauri.frontend.build.contract.test.js +60 -0
- package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
- package/dist/src/utils/RuntimePaths.js +4 -13
- package/dist/src/utils/platform.js +153 -0
- package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
- package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
- package/dist/src/workflows/types.js +2 -0
- package/dist/src/workspace/WorkspaceRegistry.js +122 -0
- package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
- package/dist/src/workspace/types.js +2 -0
- package/package.json +61 -10
|
@@ -0,0 +1,1176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assembleRagEvidenceContext = assembleRagEvidenceContext;
|
|
4
|
+
const ragContextPack_1 = require("./ragContextPack");
|
|
5
|
+
const DEFAULT_PARAGRAPH_WINDOW = 5;
|
|
6
|
+
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
|
+
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
|
+
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
|
+
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;
|
|
11
|
+
const COMPARABLE_LOCATION_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:location|site|region|zone|room|rack|slot|bay))\s+(?:is|=|:)\s*([a-z0-9][a-z0-9 /_.-]{1,80}?)(?=\.|,|;|\n|$)/gi;
|
|
12
|
+
const COMPARABLE_IDENTITY_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:owner|assignee|contact|maintainer|team|group))\s+(?:is|=|:)\s*([a-z][a-z0-9 &/_.-]{1,80}?)(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
13
|
+
const COMPARABLE_ENDPOINT_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:endpoint|url|uri|route))\s+(?:is|=|:)\s*((?:https?:\/\/|\/|[a-z0-9][a-z0-9._-]*\/)[a-z0-9/?#&=._~:%+\-/]*?[a-z0-9/#&=_~:%+\-/])(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
14
|
+
const COMPARABLE_DEPENDENCY_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:dependency|package|provider|driver|runtime|library|module|plugin|adapter))\s+(?:is|are|=|:)\s*((?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9+.#/_@ -]{0,79}?)(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
15
|
+
const COMPARABLE_FORMAT_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:format|schema|encoding|serialization|content type|mime type))\s+(?:is|are|=|:)\s*([a-z0-9][a-z0-9+.#/_ -]{0,79}?)(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
16
|
+
const COMPARABLE_PROTOCOL_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:protocol|transport protocol|wire protocol))\s+(?:is|are|=|:)\s*([a-z0-9][a-z0-9+.#/_ -]{0,79}?)(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
17
|
+
const COMPARABLE_VERSION_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:version|revision))\s+(?:is|are|=|:)\s*((?:v\s*)?\d+(?:\.\d+){1,4}(?:[-+._][a-z0-9]+)?|v\s*\d+(?:[-+._][a-z0-9]+)?)(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
18
|
+
const COMPARABLE_PORT_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:port|listener port|service port))\s+(?:is|are|=|:)\s*(\d{1,5})(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
19
|
+
const COMPARABLE_STATUS_CODE_FACT_PATTERN = /\b(?:the\s+)?([a-z][a-z0-9 -]{2,80}?(?:http status code|response status code|status code))\s+(?:is|are|=|:)\s*(\d{3})(?=\s+(?:in|for|on|under|within)\s+|\.|,|;|\n|$)/gi;
|
|
20
|
+
const COMPARABLE_STATE_VALUE_GROUPS = {
|
|
21
|
+
enabled: 'enabled_disabled',
|
|
22
|
+
disabled: 'enabled_disabled',
|
|
23
|
+
active: 'active_inactive',
|
|
24
|
+
inactive: 'active_inactive',
|
|
25
|
+
available: 'available_unavailable',
|
|
26
|
+
unavailable: 'available_unavailable',
|
|
27
|
+
supported: 'supported_unsupported',
|
|
28
|
+
unsupported: 'supported_unsupported',
|
|
29
|
+
allowed: 'allowed_blocked',
|
|
30
|
+
blocked: 'allowed_blocked',
|
|
31
|
+
required: 'required_optional',
|
|
32
|
+
optional: 'required_optional',
|
|
33
|
+
open: 'open_closed',
|
|
34
|
+
closed: 'open_closed',
|
|
35
|
+
on: 'on_off',
|
|
36
|
+
off: 'on_off',
|
|
37
|
+
};
|
|
38
|
+
const COMPARABLE_TEMPORAL_SCOPE_GROUPS = {
|
|
39
|
+
current: 'current',
|
|
40
|
+
active: 'current',
|
|
41
|
+
present: 'current',
|
|
42
|
+
latest: 'current',
|
|
43
|
+
historical: 'historical',
|
|
44
|
+
historic: 'historical',
|
|
45
|
+
legacy: 'historical',
|
|
46
|
+
previous: 'historical',
|
|
47
|
+
archived: 'historical',
|
|
48
|
+
deprecated: 'historical',
|
|
49
|
+
superseded: 'historical',
|
|
50
|
+
planned: 'planned',
|
|
51
|
+
future: 'planned',
|
|
52
|
+
upcoming: 'planned',
|
|
53
|
+
scheduled: 'planned',
|
|
54
|
+
};
|
|
55
|
+
const COMPARABLE_TEMPORAL_SCOPE_PATTERN = /\b(current|active|present|latest|historical|historic|legacy|previous|archived|deprecated|superseded|planned|future|upcoming|scheduled)\b/i;
|
|
56
|
+
const COMPARABLE_ENVIRONMENT_SCOPE_ALIASES = {
|
|
57
|
+
production: 'production',
|
|
58
|
+
prod: 'production',
|
|
59
|
+
staging: 'staging',
|
|
60
|
+
stage: 'staging',
|
|
61
|
+
development: 'development',
|
|
62
|
+
dev: 'development',
|
|
63
|
+
test: 'test',
|
|
64
|
+
testing: 'test',
|
|
65
|
+
qa: 'qa',
|
|
66
|
+
uat: 'uat',
|
|
67
|
+
sandbox: 'sandbox',
|
|
68
|
+
local: 'local',
|
|
69
|
+
preview: 'preview',
|
|
70
|
+
canary: 'canary',
|
|
71
|
+
};
|
|
72
|
+
const COMPARABLE_ENVIRONMENT_SCOPE_PATTERN = /\b(?:in|for|on|under|within)\s+(?:the\s+)?(production|prod|staging|stage|development|dev|test|testing|qa|uat|sandbox|local|preview|canary)(?:\s+(?:environment|env|deployment|cluster|workspace|tenant|runtime))?\b|\b(production|staging|development|test|testing|qa|uat|sandbox|local|preview|canary)\s+(?:environment|env|deployment|cluster|workspace|tenant|runtime)\b/i;
|
|
73
|
+
const COMPARABLE_VERSION_SCOPE_PATTERN = /\b(?:in|for|on|under|within)\s+(?:the\s+)?(?:version|ver\.?|v)\s*([0-9]+(?:\.[0-9]+){0,3}(?:[-+._][a-z0-9]+)?)\b|\b(?:version|ver\.?|v)\s*([0-9]+(?:\.[0-9]+){0,3}(?:[-+._][a-z0-9]+)?)\b/i;
|
|
74
|
+
const COMPARABLE_PLATFORM_SCOPE_ALIASES = {
|
|
75
|
+
windows: 'windows',
|
|
76
|
+
win32: 'windows',
|
|
77
|
+
macos: 'macos',
|
|
78
|
+
mac: 'macos',
|
|
79
|
+
linux: 'linux',
|
|
80
|
+
android: 'android',
|
|
81
|
+
ios: 'ios',
|
|
82
|
+
web: 'web',
|
|
83
|
+
desktop: 'desktop',
|
|
84
|
+
mobile: 'mobile',
|
|
85
|
+
};
|
|
86
|
+
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;
|
|
87
|
+
function normalizeWhitespace(value) {
|
|
88
|
+
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
89
|
+
}
|
|
90
|
+
function sanitizeFragmentPart(value) {
|
|
91
|
+
return normalizeWhitespace(value)
|
|
92
|
+
.replace(/[^a-zA-Z0-9_-]+/g, '_')
|
|
93
|
+
.replace(/^_+|_+$/g, '')
|
|
94
|
+
.slice(0, 80) || 'fragment';
|
|
95
|
+
}
|
|
96
|
+
function splitSourceLines(content) {
|
|
97
|
+
const normalizedContent = String(content || '');
|
|
98
|
+
if (!normalizedContent) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const lines = normalizedContent.split(/\r\n|\n|\r/);
|
|
102
|
+
const records = [];
|
|
103
|
+
let cursor = 0;
|
|
104
|
+
lines.forEach((line, index) => {
|
|
105
|
+
const startOffset = cursor;
|
|
106
|
+
const endOffset = startOffset + line.length;
|
|
107
|
+
records.push({
|
|
108
|
+
lineNumber: index + 1,
|
|
109
|
+
text: line,
|
|
110
|
+
startOffset,
|
|
111
|
+
endOffset,
|
|
112
|
+
});
|
|
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
|
+
});
|
|
123
|
+
return records;
|
|
124
|
+
}
|
|
125
|
+
function isTableLine(text) {
|
|
126
|
+
const trimmed = String(text || '').trim();
|
|
127
|
+
return trimmed.startsWith('|') && trimmed.endsWith('|');
|
|
128
|
+
}
|
|
129
|
+
function parseMarkdownBlocks(content) {
|
|
130
|
+
const sourceLines = splitSourceLines(content);
|
|
131
|
+
const blocks = [];
|
|
132
|
+
const headingStack = [];
|
|
133
|
+
let pendingLines = [];
|
|
134
|
+
let pendingKind = 'paragraph';
|
|
135
|
+
let pendingHeadingPath = [];
|
|
136
|
+
let inCodeFence = false;
|
|
137
|
+
const currentHeadingPath = () => headingStack.map((heading) => heading.title);
|
|
138
|
+
const flushPending = () => {
|
|
139
|
+
if (pendingLines.length <= 0) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const text = pendingLines.map((line) => line.text).join('\n').trim();
|
|
143
|
+
if (text) {
|
|
144
|
+
const first = pendingLines[0];
|
|
145
|
+
const last = pendingLines[pendingLines.length - 1];
|
|
146
|
+
blocks.push({
|
|
147
|
+
kind: pendingKind,
|
|
148
|
+
text,
|
|
149
|
+
startOffset: first.startOffset,
|
|
150
|
+
endOffset: last.endOffset,
|
|
151
|
+
startLine: first.lineNumber,
|
|
152
|
+
endLine: last.lineNumber,
|
|
153
|
+
headingPath: [...pendingHeadingPath],
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
pendingLines = [];
|
|
157
|
+
pendingKind = 'paragraph';
|
|
158
|
+
pendingHeadingPath = currentHeadingPath();
|
|
159
|
+
};
|
|
160
|
+
sourceLines.forEach((line) => {
|
|
161
|
+
const rawText = String(line.text || '');
|
|
162
|
+
const trimmed = rawText.trim();
|
|
163
|
+
const fenceLine = /^```/.test(trimmed);
|
|
164
|
+
if (fenceLine) {
|
|
165
|
+
if (!inCodeFence) {
|
|
166
|
+
flushPending();
|
|
167
|
+
pendingKind = 'code';
|
|
168
|
+
pendingHeadingPath = currentHeadingPath();
|
|
169
|
+
pendingLines = [line];
|
|
170
|
+
inCodeFence = true;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
pendingLines.push(line);
|
|
174
|
+
inCodeFence = false;
|
|
175
|
+
flushPending();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (inCodeFence) {
|
|
179
|
+
pendingLines.push(line);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!trimmed) {
|
|
183
|
+
flushPending();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const headingMatch = /^(#{1,6})\s+(.+?)\s*$/.exec(trimmed);
|
|
187
|
+
if (headingMatch) {
|
|
188
|
+
flushPending();
|
|
189
|
+
const level = headingMatch[1].length;
|
|
190
|
+
const title = normalizeWhitespace(headingMatch[2].replace(/#+\s*$/, ''));
|
|
191
|
+
while (headingStack.length > 0 && headingStack[headingStack.length - 1].level >= level) {
|
|
192
|
+
headingStack.pop();
|
|
193
|
+
}
|
|
194
|
+
headingStack.push({ level, title });
|
|
195
|
+
blocks.push({
|
|
196
|
+
kind: 'heading',
|
|
197
|
+
text: trimmed,
|
|
198
|
+
startOffset: line.startOffset,
|
|
199
|
+
endOffset: line.endOffset,
|
|
200
|
+
startLine: line.lineNumber,
|
|
201
|
+
endLine: line.lineNumber,
|
|
202
|
+
headingPath: currentHeadingPath(),
|
|
203
|
+
});
|
|
204
|
+
pendingHeadingPath = currentHeadingPath();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const nextKind = isTableLine(trimmed) ? 'table' : 'paragraph';
|
|
208
|
+
if (pendingLines.length > 0 && pendingKind !== nextKind) {
|
|
209
|
+
flushPending();
|
|
210
|
+
}
|
|
211
|
+
if (pendingLines.length <= 0) {
|
|
212
|
+
pendingKind = nextKind;
|
|
213
|
+
pendingHeadingPath = currentHeadingPath();
|
|
214
|
+
}
|
|
215
|
+
pendingLines.push(line);
|
|
216
|
+
});
|
|
217
|
+
flushPending();
|
|
218
|
+
return blocks;
|
|
219
|
+
}
|
|
220
|
+
function spanHasUsableOffsets(span, sourceLength) {
|
|
221
|
+
return Number.isFinite(span.startOffset)
|
|
222
|
+
&& Number.isFinite(span.endOffset)
|
|
223
|
+
&& span.startOffset >= 0
|
|
224
|
+
&& span.endOffset >= span.startOffset
|
|
225
|
+
&& span.startOffset <= sourceLength;
|
|
226
|
+
}
|
|
227
|
+
function blocksContainSnippet(blocks, snippet) {
|
|
228
|
+
if (!snippet) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
return normalizeWhitespace(blocks.map((block) => block.text).join(' ')).includes(snippet);
|
|
232
|
+
}
|
|
233
|
+
function blocksForEvidence(blocks, span, content) {
|
|
234
|
+
const snippet = normalizeWhitespace(span.snippet);
|
|
235
|
+
const lineMatches = Number.isFinite(span.startLine) && Number.isFinite(span.endLine) && span.startLine > 0
|
|
236
|
+
? blocks.filter((block) => block.endLine >= span.startLine && block.startLine <= span.endLine)
|
|
237
|
+
: [];
|
|
238
|
+
if (spanHasUsableOffsets(span, content.length)) {
|
|
239
|
+
const endOffset = Math.min(span.endOffset, content.length);
|
|
240
|
+
const matches = blocks.filter((block) => block.endOffset >= span.startOffset && block.startOffset <= endOffset);
|
|
241
|
+
if (matches.length > 0) {
|
|
242
|
+
if (!blocksContainSnippet(matches, snippet) && blocksContainSnippet(lineMatches, snippet)) {
|
|
243
|
+
return lineMatches;
|
|
244
|
+
}
|
|
245
|
+
return matches;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (lineMatches.length > 0) {
|
|
249
|
+
return lineMatches;
|
|
250
|
+
}
|
|
251
|
+
if (snippet) {
|
|
252
|
+
const normalizedBlocks = blocks.filter((block) => normalizeWhitespace(block.text).includes(snippet));
|
|
253
|
+
if (normalizedBlocks.length > 0) {
|
|
254
|
+
return normalizedBlocks;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
function sameHeadingPath(a, b) {
|
|
260
|
+
return a.length === b.length && a.every((part, index) => part === b[index]);
|
|
261
|
+
}
|
|
262
|
+
function buildSectionBlocks(blocks, evidenceBlocks, paragraphWindow) {
|
|
263
|
+
if (evidenceBlocks.length <= 0) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
const headingPath = evidenceBlocks[0].headingPath;
|
|
267
|
+
if (headingPath.length > 0) {
|
|
268
|
+
const sectionBlocks = blocks.filter((block) => sameHeadingPath(block.headingPath, headingPath));
|
|
269
|
+
if (sectionBlocks.length > 0) {
|
|
270
|
+
return sectionBlocks;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const firstIndex = blocks.indexOf(evidenceBlocks[0]);
|
|
274
|
+
const lastIndex = blocks.indexOf(evidenceBlocks[evidenceBlocks.length - 1]);
|
|
275
|
+
if (firstIndex < 0 || lastIndex < 0) {
|
|
276
|
+
return evidenceBlocks;
|
|
277
|
+
}
|
|
278
|
+
const start = Math.max(0, firstIndex - paragraphWindow);
|
|
279
|
+
const end = Math.min(blocks.length - 1, lastIndex + paragraphWindow);
|
|
280
|
+
return blocks.slice(start, end + 1);
|
|
281
|
+
}
|
|
282
|
+
function sourceBlockKey(block) {
|
|
283
|
+
return `${block.startOffset}:${block.endOffset}:${block.text}`;
|
|
284
|
+
}
|
|
285
|
+
function mergeBlocks(blocks) {
|
|
286
|
+
const seen = new Set();
|
|
287
|
+
const merged = [];
|
|
288
|
+
blocks.forEach((block) => {
|
|
289
|
+
const key = sourceBlockKey(block);
|
|
290
|
+
if (seen.has(key)) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
seen.add(key);
|
|
294
|
+
merged.push(block);
|
|
295
|
+
});
|
|
296
|
+
return merged.sort((a, b) => a.startOffset - b.startOffset);
|
|
297
|
+
}
|
|
298
|
+
function fragmentTextFromBlocks(blocks) {
|
|
299
|
+
return mergeBlocks(blocks)
|
|
300
|
+
.map((block) => block.text)
|
|
301
|
+
.filter(Boolean)
|
|
302
|
+
.join('\n\n')
|
|
303
|
+
.trim();
|
|
304
|
+
}
|
|
305
|
+
function normalizeComparableFactSubject(value) {
|
|
306
|
+
return normalizeWhitespace(value)
|
|
307
|
+
.toLowerCase()
|
|
308
|
+
.replace(/^(the|a|an)\s+/i, '')
|
|
309
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
310
|
+
.trim();
|
|
311
|
+
}
|
|
312
|
+
function comparableFactSentenceTail(blockText, match) {
|
|
313
|
+
const matchIndex = typeof match.index === 'number' ? match.index : -1;
|
|
314
|
+
if (matchIndex < 0) {
|
|
315
|
+
return '';
|
|
316
|
+
}
|
|
317
|
+
const tail = String(blockText || '').slice(matchIndex + String(match[0] || '').length);
|
|
318
|
+
return normalizeWhitespace(tail.split(/[.!?]/)[0] || '');
|
|
319
|
+
}
|
|
320
|
+
function comparableFactTemporalScopeKey(subjectLabel, sentenceTail) {
|
|
321
|
+
const scopedText = `${normalizeWhitespace(subjectLabel)} ${normalizeWhitespace(sentenceTail)}`;
|
|
322
|
+
const match = COMPARABLE_TEMPORAL_SCOPE_PATTERN.exec(scopedText);
|
|
323
|
+
if (!match) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
return COMPARABLE_TEMPORAL_SCOPE_GROUPS[String(match[1] || '').toLowerCase()] || null;
|
|
327
|
+
}
|
|
328
|
+
function comparableFactEnvironmentScopeKey(subjectLabel, sentenceTail) {
|
|
329
|
+
const scopedText = `${normalizeWhitespace(subjectLabel)} ${normalizeWhitespace(sentenceTail)}`;
|
|
330
|
+
const match = COMPARABLE_ENVIRONMENT_SCOPE_PATTERN.exec(scopedText);
|
|
331
|
+
if (!match) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const environmentLabel = String(match[1] || match[2] || '').toLowerCase();
|
|
335
|
+
return COMPARABLE_ENVIRONMENT_SCOPE_ALIASES[environmentLabel] || null;
|
|
336
|
+
}
|
|
337
|
+
function comparableFactVersionScopeKey(subjectLabel, sentenceTail) {
|
|
338
|
+
const scopedText = `${normalizeWhitespace(subjectLabel)} ${normalizeWhitespace(sentenceTail)}`;
|
|
339
|
+
const match = COMPARABLE_VERSION_SCOPE_PATTERN.exec(scopedText);
|
|
340
|
+
if (!match) {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
const versionLabel = normalizeWhitespace(match[1] || match[2] || '').toLowerCase();
|
|
344
|
+
if (!/^[0-9]+(?:\.[0-9]+){0,3}(?:[-+._][a-z0-9]+)?$/.test(versionLabel)) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
return versionLabel;
|
|
348
|
+
}
|
|
349
|
+
function comparableFactPlatformScopeKey(subjectLabel, sentenceTail) {
|
|
350
|
+
const scopedText = `${normalizeWhitespace(subjectLabel)} ${normalizeWhitespace(sentenceTail)}`;
|
|
351
|
+
const match = COMPARABLE_PLATFORM_SCOPE_PATTERN.exec(scopedText);
|
|
352
|
+
if (!match) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
const platformLabel = String(match[1] || match[2] || '').toLowerCase();
|
|
356
|
+
return COMPARABLE_PLATFORM_SCOPE_ALIASES[platformLabel] || null;
|
|
357
|
+
}
|
|
358
|
+
function comparableFactScopeKeys(subjectLabel, sentenceTail) {
|
|
359
|
+
const scopeKeys = [];
|
|
360
|
+
const temporalScopeKey = comparableFactTemporalScopeKey(subjectLabel, sentenceTail);
|
|
361
|
+
if (temporalScopeKey) {
|
|
362
|
+
scopeKeys.push(`temporal:${temporalScopeKey}`);
|
|
363
|
+
}
|
|
364
|
+
const environmentScopeKey = comparableFactEnvironmentScopeKey(subjectLabel, sentenceTail);
|
|
365
|
+
if (environmentScopeKey) {
|
|
366
|
+
scopeKeys.push(`environment:${environmentScopeKey}`);
|
|
367
|
+
}
|
|
368
|
+
const versionScopeKey = comparableFactVersionScopeKey(subjectLabel, sentenceTail);
|
|
369
|
+
if (versionScopeKey) {
|
|
370
|
+
scopeKeys.push(`version:${versionScopeKey}`);
|
|
371
|
+
}
|
|
372
|
+
const platformScopeKey = comparableFactPlatformScopeKey(subjectLabel, sentenceTail);
|
|
373
|
+
if (platformScopeKey) {
|
|
374
|
+
scopeKeys.push(`platform:${platformScopeKey}`);
|
|
375
|
+
}
|
|
376
|
+
return scopeKeys.sort();
|
|
377
|
+
}
|
|
378
|
+
function comparableFactSubjectKey(subjectLabel, scopeKeys) {
|
|
379
|
+
const scopedSubjectLabel = scopeKeys.some((scopeKey) => scopeKey.startsWith('temporal:'))
|
|
380
|
+
? normalizeWhitespace(subjectLabel).replace(COMPARABLE_TEMPORAL_SCOPE_PATTERN, '')
|
|
381
|
+
: subjectLabel;
|
|
382
|
+
const subjectKey = normalizeComparableFactSubject(scopedSubjectLabel);
|
|
383
|
+
return scopeKeys.length > 0 && subjectKey
|
|
384
|
+
? `${subjectKey}@scope:${scopeKeys.join('+')}`
|
|
385
|
+
: subjectKey;
|
|
386
|
+
}
|
|
387
|
+
function normalizeComparableFactUnit(value) {
|
|
388
|
+
const normalized = normalizeWhitespace(value).toLowerCase();
|
|
389
|
+
if (normalized === 'um') {
|
|
390
|
+
return 'µm';
|
|
391
|
+
}
|
|
392
|
+
if (normalized === 'degree' || normalized === 'degrees') {
|
|
393
|
+
return 'deg';
|
|
394
|
+
}
|
|
395
|
+
return normalized;
|
|
396
|
+
}
|
|
397
|
+
function normalizeComparableDateValue(value) {
|
|
398
|
+
const normalized = normalizeWhitespace(value);
|
|
399
|
+
const dateMatch = /^(\d{4})-(\d{2})-(\d{2})$/.exec(normalized);
|
|
400
|
+
if (dateMatch) {
|
|
401
|
+
const year = Number(dateMatch[1]);
|
|
402
|
+
const month = Number(dateMatch[2]);
|
|
403
|
+
const day = Number(dateMatch[3]);
|
|
404
|
+
if (year >= 1000 && year <= 2999 && month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
405
|
+
return `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const yearMatch = /^(\d{4})$/.exec(normalized);
|
|
410
|
+
if (yearMatch) {
|
|
411
|
+
const year = Number(yearMatch[1]);
|
|
412
|
+
if (year >= 1000 && year <= 2999) {
|
|
413
|
+
return yearMatch[1];
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
function normalizeComparableStateValue(value) {
|
|
419
|
+
const valueKey = normalizeWhitespace(value).toLowerCase();
|
|
420
|
+
const groupKey = COMPARABLE_STATE_VALUE_GROUPS[valueKey];
|
|
421
|
+
if (!valueKey || !groupKey) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
return { valueKey, groupKey };
|
|
425
|
+
}
|
|
426
|
+
function normalizeComparableLocationValue(value) {
|
|
427
|
+
return normalizeWhitespace(value)
|
|
428
|
+
.toLowerCase()
|
|
429
|
+
.replace(/^(the|a|an)\s+/i, '')
|
|
430
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
431
|
+
.trim();
|
|
432
|
+
}
|
|
433
|
+
function normalizeComparableIdentityValue(value) {
|
|
434
|
+
return normalizeWhitespace(value)
|
|
435
|
+
.toLowerCase()
|
|
436
|
+
.replace(/^(the|a|an)\s+/i, '')
|
|
437
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
438
|
+
.trim();
|
|
439
|
+
}
|
|
440
|
+
function normalizeComparableEndpointValue(value) {
|
|
441
|
+
const normalized = normalizeWhitespace(value)
|
|
442
|
+
.toLowerCase()
|
|
443
|
+
.replace(/^['"`]+|['"`]+$/g, '');
|
|
444
|
+
return normalized.length > 1
|
|
445
|
+
? normalized.replace(/\/+$/g, '')
|
|
446
|
+
: normalized;
|
|
447
|
+
}
|
|
448
|
+
function normalizeComparableDependencyValue(value) {
|
|
449
|
+
return normalizeWhitespace(value)
|
|
450
|
+
.toLowerCase()
|
|
451
|
+
.replace(/^['"`]+|['"`]+$/g, '')
|
|
452
|
+
.replace(/^(the|a|an)\s+/i, '')
|
|
453
|
+
.replace(/[^a-z0-9+.#/@_-]+/g, ' ')
|
|
454
|
+
.replace(/\s+/g, ' ')
|
|
455
|
+
.trim();
|
|
456
|
+
}
|
|
457
|
+
function normalizeComparableFormatValue(value) {
|
|
458
|
+
return normalizeWhitespace(value)
|
|
459
|
+
.toLowerCase()
|
|
460
|
+
.replace(/^['"`]+|['"`]+$/g, '')
|
|
461
|
+
.replace(/^(the|a|an)\s+/i, '')
|
|
462
|
+
.replace(/[^a-z0-9+.#/_-]+/g, ' ')
|
|
463
|
+
.replace(/\s+/g, ' ')
|
|
464
|
+
.trim();
|
|
465
|
+
}
|
|
466
|
+
function normalizeComparableProtocolValue(value) {
|
|
467
|
+
return normalizeWhitespace(value)
|
|
468
|
+
.toLowerCase()
|
|
469
|
+
.replace(/^['"`]+|['"`]+$/g, '')
|
|
470
|
+
.replace(/^(the|a|an)\s+/i, '')
|
|
471
|
+
.replace(/[^a-z0-9+.#/_-]+/g, ' ')
|
|
472
|
+
.replace(/\s+/g, ' ')
|
|
473
|
+
.trim();
|
|
474
|
+
}
|
|
475
|
+
function normalizeComparableVersionValue(value) {
|
|
476
|
+
return normalizeWhitespace(value)
|
|
477
|
+
.toLowerCase()
|
|
478
|
+
.replace(/^['"`]+|['"`]+$/g, '')
|
|
479
|
+
.replace(/^v\s*/i, '')
|
|
480
|
+
.replace(/\s+/g, '')
|
|
481
|
+
.trim();
|
|
482
|
+
}
|
|
483
|
+
function normalizeComparablePortValue(value) {
|
|
484
|
+
const port = Number(normalizeWhitespace(value));
|
|
485
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
return String(port);
|
|
489
|
+
}
|
|
490
|
+
function normalizeComparableStatusCodeValue(value) {
|
|
491
|
+
const statusCode = Number(normalizeWhitespace(value));
|
|
492
|
+
if (!Number.isInteger(statusCode) || statusCode < 100 || statusCode > 599) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
return String(statusCode);
|
|
496
|
+
}
|
|
497
|
+
function extractComparableEvidenceFacts(params) {
|
|
498
|
+
if (params.block.kind === 'heading' || params.block.kind === 'code') {
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
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) {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
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
|
+
}
|
|
522
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_QUANTITY_FACT_PATTERN)) {
|
|
523
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
524
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
525
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
526
|
+
const value = Number(match[2]);
|
|
527
|
+
if (!subjectKey || !Number.isFinite(value)) {
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
facts.push({
|
|
531
|
+
subjectKey,
|
|
532
|
+
subjectLabel,
|
|
533
|
+
valueKey: `${Number(value.toFixed(12))}`,
|
|
534
|
+
valueLabel: match[2],
|
|
535
|
+
factKind: 'quantity',
|
|
536
|
+
block: params.block,
|
|
537
|
+
citationIds: params.citationIds,
|
|
538
|
+
item: params.item,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_DATE_FACT_PATTERN)) {
|
|
542
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
543
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
544
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
545
|
+
const valueKey = normalizeComparableDateValue(match[2]);
|
|
546
|
+
if (!subjectKey || !valueKey) {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
facts.push({
|
|
550
|
+
subjectKey,
|
|
551
|
+
subjectLabel,
|
|
552
|
+
valueKey,
|
|
553
|
+
valueLabel: match[2],
|
|
554
|
+
factKind: 'date',
|
|
555
|
+
block: params.block,
|
|
556
|
+
citationIds: params.citationIds,
|
|
557
|
+
item: params.item,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_STATE_FACT_PATTERN)) {
|
|
561
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
562
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
563
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
564
|
+
const stateValue = normalizeComparableStateValue(match[2]);
|
|
565
|
+
if (!subjectKey || !stateValue) {
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
facts.push({
|
|
569
|
+
subjectKey: `${subjectKey}:${stateValue.groupKey}`,
|
|
570
|
+
subjectLabel,
|
|
571
|
+
valueKey: stateValue.valueKey,
|
|
572
|
+
valueLabel: match[2],
|
|
573
|
+
factKind: 'state',
|
|
574
|
+
block: params.block,
|
|
575
|
+
citationIds: params.citationIds,
|
|
576
|
+
item: params.item,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_LOCATION_FACT_PATTERN)) {
|
|
580
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
581
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, `${normalizeWhitespace(match[2])} ${comparableFactSentenceTail(params.block.text, match)}`);
|
|
582
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
583
|
+
const valueKey = normalizeComparableLocationValue(match[2]);
|
|
584
|
+
if (!subjectKey || !valueKey) {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
facts.push({
|
|
588
|
+
subjectKey,
|
|
589
|
+
subjectLabel,
|
|
590
|
+
valueKey,
|
|
591
|
+
valueLabel: normalizeWhitespace(match[2]),
|
|
592
|
+
factKind: 'location',
|
|
593
|
+
block: params.block,
|
|
594
|
+
citationIds: params.citationIds,
|
|
595
|
+
item: params.item,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_IDENTITY_FACT_PATTERN)) {
|
|
599
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
600
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, `${normalizeWhitespace(match[2])} ${comparableFactSentenceTail(params.block.text, match)}`);
|
|
601
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
602
|
+
const valueKey = normalizeComparableIdentityValue(match[2]);
|
|
603
|
+
if (!subjectKey || !valueKey) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
facts.push({
|
|
607
|
+
subjectKey,
|
|
608
|
+
subjectLabel,
|
|
609
|
+
valueKey,
|
|
610
|
+
valueLabel: normalizeWhitespace(match[2]),
|
|
611
|
+
factKind: 'identity',
|
|
612
|
+
block: params.block,
|
|
613
|
+
citationIds: params.citationIds,
|
|
614
|
+
item: params.item,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_ENDPOINT_FACT_PATTERN)) {
|
|
618
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
619
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
620
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
621
|
+
const valueKey = normalizeComparableEndpointValue(match[2]);
|
|
622
|
+
if (!subjectKey || !valueKey) {
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
facts.push({
|
|
626
|
+
subjectKey,
|
|
627
|
+
subjectLabel,
|
|
628
|
+
valueKey,
|
|
629
|
+
valueLabel: normalizeWhitespace(match[2]),
|
|
630
|
+
factKind: 'endpoint',
|
|
631
|
+
block: params.block,
|
|
632
|
+
citationIds: params.citationIds,
|
|
633
|
+
item: params.item,
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_DEPENDENCY_FACT_PATTERN)) {
|
|
637
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
638
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
639
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
640
|
+
const valueKey = normalizeComparableDependencyValue(match[2]);
|
|
641
|
+
if (!subjectKey || !valueKey) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
facts.push({
|
|
645
|
+
subjectKey,
|
|
646
|
+
subjectLabel,
|
|
647
|
+
valueKey,
|
|
648
|
+
valueLabel: normalizeWhitespace(match[2]),
|
|
649
|
+
factKind: 'dependency',
|
|
650
|
+
block: params.block,
|
|
651
|
+
citationIds: params.citationIds,
|
|
652
|
+
item: params.item,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_FORMAT_FACT_PATTERN)) {
|
|
656
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
657
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
658
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
659
|
+
const valueKey = normalizeComparableFormatValue(match[2]);
|
|
660
|
+
if (!subjectKey || !valueKey) {
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
facts.push({
|
|
664
|
+
subjectKey,
|
|
665
|
+
subjectLabel,
|
|
666
|
+
valueKey,
|
|
667
|
+
valueLabel: normalizeWhitespace(match[2]),
|
|
668
|
+
factKind: 'format',
|
|
669
|
+
block: params.block,
|
|
670
|
+
citationIds: params.citationIds,
|
|
671
|
+
item: params.item,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_PROTOCOL_FACT_PATTERN)) {
|
|
675
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
676
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
677
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
678
|
+
const valueKey = normalizeComparableProtocolValue(match[2]);
|
|
679
|
+
if (!subjectKey || !valueKey) {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
facts.push({
|
|
683
|
+
subjectKey,
|
|
684
|
+
subjectLabel,
|
|
685
|
+
valueKey,
|
|
686
|
+
valueLabel: normalizeWhitespace(match[2]),
|
|
687
|
+
factKind: 'protocol',
|
|
688
|
+
block: params.block,
|
|
689
|
+
citationIds: params.citationIds,
|
|
690
|
+
item: params.item,
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_VERSION_FACT_PATTERN)) {
|
|
694
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
695
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
696
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
697
|
+
const valueKey = normalizeComparableVersionValue(match[2]);
|
|
698
|
+
if (!subjectKey || !valueKey) {
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
facts.push({
|
|
702
|
+
subjectKey,
|
|
703
|
+
subjectLabel,
|
|
704
|
+
valueKey,
|
|
705
|
+
valueLabel: normalizeWhitespace(match[2]),
|
|
706
|
+
factKind: 'version',
|
|
707
|
+
block: params.block,
|
|
708
|
+
citationIds: params.citationIds,
|
|
709
|
+
item: params.item,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_PORT_FACT_PATTERN)) {
|
|
713
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
714
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
715
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
716
|
+
const valueKey = normalizeComparablePortValue(match[2]);
|
|
717
|
+
if (!subjectKey || !valueKey) {
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
facts.push({
|
|
721
|
+
subjectKey,
|
|
722
|
+
subjectLabel,
|
|
723
|
+
valueKey,
|
|
724
|
+
valueLabel: match[2],
|
|
725
|
+
factKind: 'port',
|
|
726
|
+
block: params.block,
|
|
727
|
+
citationIds: params.citationIds,
|
|
728
|
+
item: params.item,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
for (const match of String(params.block.text || '').matchAll(COMPARABLE_STATUS_CODE_FACT_PATTERN)) {
|
|
732
|
+
const subjectLabel = normalizeWhitespace(match[1]);
|
|
733
|
+
const scopeKeys = comparableFactScopeKeys(subjectLabel, comparableFactSentenceTail(params.block.text, match));
|
|
734
|
+
const subjectKey = comparableFactSubjectKey(subjectLabel, scopeKeys);
|
|
735
|
+
const valueKey = normalizeComparableStatusCodeValue(match[2]);
|
|
736
|
+
if (!subjectKey || !valueKey) {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
facts.push({
|
|
740
|
+
subjectKey,
|
|
741
|
+
subjectLabel,
|
|
742
|
+
valueKey,
|
|
743
|
+
valueLabel: match[2],
|
|
744
|
+
factKind: 'status_code',
|
|
745
|
+
block: params.block,
|
|
746
|
+
citationIds: params.citationIds,
|
|
747
|
+
item: params.item,
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
return facts;
|
|
751
|
+
}
|
|
752
|
+
function buildBlockCitationMap(group, blocks, source) {
|
|
753
|
+
const citationIdsByBlock = new Map();
|
|
754
|
+
group.entries.forEach(({ item }) => {
|
|
755
|
+
itemEvidenceSpans(item).forEach((span) => {
|
|
756
|
+
blocksForEvidence(blocks, span, source.content).forEach((block) => {
|
|
757
|
+
const key = sourceBlockKey(block);
|
|
758
|
+
const citationIds = citationIdsByBlock.get(key) || new Set();
|
|
759
|
+
citationIds.add(span.id);
|
|
760
|
+
citationIdsByBlock.set(key, citationIds);
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
return citationIdsByBlock;
|
|
765
|
+
}
|
|
766
|
+
function extractComparableEvidenceFactsFromBlockEntries(entries, citationIdsByBlock) {
|
|
767
|
+
return entries
|
|
768
|
+
.sort((left, right) => left.block.startOffset - right.block.startOffset)
|
|
769
|
+
.flatMap(({ block, item }) => extractComparableEvidenceFacts({
|
|
770
|
+
block,
|
|
771
|
+
item,
|
|
772
|
+
citationIds: Array.from(citationIdsByBlock.get(sourceBlockKey(block)) || []),
|
|
773
|
+
}));
|
|
774
|
+
}
|
|
775
|
+
function selectRepresentativeGroupItem(group) {
|
|
776
|
+
const rankedEntries = group.entries.slice().sort((left, right) => {
|
|
777
|
+
if (left.directRole !== right.directRole) {
|
|
778
|
+
return left.directRole === 'direct_support' ? -1 : 1;
|
|
779
|
+
}
|
|
780
|
+
return Number(right.item.score || 0) - Number(left.item.score || 0);
|
|
781
|
+
});
|
|
782
|
+
return rankedEntries[0]?.item || null;
|
|
783
|
+
}
|
|
784
|
+
function buildEvidenceItemMap(group, blocks, source) {
|
|
785
|
+
const itemByBlock = new Map();
|
|
786
|
+
group.entries.forEach(({ item }) => {
|
|
787
|
+
itemEvidenceSpans(item).forEach((span) => {
|
|
788
|
+
blocksForEvidence(blocks, span, source.content).forEach((block) => {
|
|
789
|
+
const key = sourceBlockKey(block);
|
|
790
|
+
const previous = itemByBlock.get(key);
|
|
791
|
+
if (!previous || Number(item.score || 0) > Number(previous.score || 0)) {
|
|
792
|
+
itemByBlock.set(key, item);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
return itemByBlock;
|
|
798
|
+
}
|
|
799
|
+
function collectSelectedContextComparableFacts(group, source, paragraphWindow) {
|
|
800
|
+
const blocks = parseMarkdownBlocks(source.content);
|
|
801
|
+
const citationIdsByBlock = buildBlockCitationMap(group, blocks, source);
|
|
802
|
+
const selectedBlocks = new Map();
|
|
803
|
+
group.entries
|
|
804
|
+
.filter((entry) => entry.expandDocumentContext)
|
|
805
|
+
.forEach(({ item }) => {
|
|
806
|
+
itemEvidenceSpans(item).forEach((span) => {
|
|
807
|
+
const evidenceBlocks = blocksForEvidence(blocks, span, source.content);
|
|
808
|
+
buildSectionBlocks(blocks, evidenceBlocks, paragraphWindow).forEach((block) => {
|
|
809
|
+
selectedBlocks.set(sourceBlockKey(block), { block, item });
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
return extractComparableEvidenceFactsFromBlockEntries(Array.from(selectedBlocks.values()), citationIdsByBlock);
|
|
814
|
+
}
|
|
815
|
+
function collectFullDocumentComparableFacts(group, source) {
|
|
816
|
+
const fallbackItem = selectRepresentativeGroupItem(group);
|
|
817
|
+
if (!fallbackItem) {
|
|
818
|
+
return [];
|
|
819
|
+
}
|
|
820
|
+
const blocks = parseMarkdownBlocks(source.content);
|
|
821
|
+
const citationIdsByBlock = buildBlockCitationMap(group, blocks, source);
|
|
822
|
+
const evidenceItemByBlock = buildEvidenceItemMap(group, blocks, source);
|
|
823
|
+
return extractComparableEvidenceFactsFromBlockEntries(blocks.map((block) => ({
|
|
824
|
+
block,
|
|
825
|
+
item: evidenceItemByBlock.get(sourceBlockKey(block)) || fallbackItem,
|
|
826
|
+
})), citationIdsByBlock);
|
|
827
|
+
}
|
|
828
|
+
function comparableFactDocumentKey(fact) {
|
|
829
|
+
return `${fact.item.atom.documentId}\n${fact.item.atom.sourcePath}`;
|
|
830
|
+
}
|
|
831
|
+
function buildConflictFragments(group, facts, paragraphWindow) {
|
|
832
|
+
const fragments = [];
|
|
833
|
+
const seenConflicts = new Set();
|
|
834
|
+
facts.forEach((left, leftIndex) => {
|
|
835
|
+
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) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const blockDistance = Math.abs(left.block.startLine - right.block.startLine);
|
|
843
|
+
const sameScopedSection = left.block.headingPath.length > 0
|
|
844
|
+
&& sameHeadingPath(left.block.headingPath, right.block.headingPath);
|
|
845
|
+
if (!sameScopedSection && blockDistance > Math.max(2, paragraphWindow)) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const orderedValues = [left.valueKey, right.valueKey].sort();
|
|
849
|
+
const conflictKey = `${left.subjectKey}:${left.factKind}:${orderedValues[0]}:${orderedValues[1]}`;
|
|
850
|
+
if (seenConflicts.has(conflictKey)) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
seenConflicts.add(conflictKey);
|
|
854
|
+
const firstBlock = left.block.startOffset <= right.block.startOffset ? left.block : right.block;
|
|
855
|
+
const lastBlock = left.block.endOffset >= right.block.endOffset ? left.block : right.block;
|
|
856
|
+
const citationIds = Array.from(new Set([
|
|
857
|
+
...left.citationIds,
|
|
858
|
+
...right.citationIds,
|
|
859
|
+
...itemEvidenceSpans(left.item).map((span) => span.id),
|
|
860
|
+
...itemEvidenceSpans(right.item).map((span) => span.id),
|
|
861
|
+
].filter(Boolean)));
|
|
862
|
+
const conflictBlockTexts = Array.from(new Map([left.block, right.block].map((block) => [sourceBlockKey(block), block.text])).values());
|
|
863
|
+
const text = [
|
|
864
|
+
`Conflicting evidence for ${left.subjectLabel}:`,
|
|
865
|
+
...conflictBlockTexts,
|
|
866
|
+
].join('\n');
|
|
867
|
+
fragments.push({
|
|
868
|
+
fragmentId: `rag_conflict_${sanitizeFragmentPart(group.documentId)}_${fragments.length + 1}`,
|
|
869
|
+
role: 'conflict',
|
|
870
|
+
text,
|
|
871
|
+
atomId: left.item.atom.id,
|
|
872
|
+
documentId: group.documentId,
|
|
873
|
+
sourcePath: group.sourcePath,
|
|
874
|
+
title: left.item.atom.title,
|
|
875
|
+
headingPath: [...left.block.headingPath],
|
|
876
|
+
startOffset: firstBlock.startOffset,
|
|
877
|
+
endOffset: lastBlock.endOffset,
|
|
878
|
+
startLine: firstBlock.startLine,
|
|
879
|
+
endLine: lastBlock.endLine,
|
|
880
|
+
charCount: text.length,
|
|
881
|
+
tokenEstimate: (0, ragContextPack_1.estimateRagTokenCount)(text),
|
|
882
|
+
truncated: false,
|
|
883
|
+
citationIds,
|
|
884
|
+
relationEdgeIds: Array.from(new Set([
|
|
885
|
+
...left.item.relationPath.map((edge) => edge.id),
|
|
886
|
+
...right.item.relationPath.map((edge) => edge.id),
|
|
887
|
+
])),
|
|
888
|
+
score: Number(Math.max(Number(left.item.score || 0), Number(right.item.score || 0)).toFixed(4)),
|
|
889
|
+
sourceBoundary: 'full_document',
|
|
890
|
+
});
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
return fragments;
|
|
894
|
+
}
|
|
895
|
+
function buildCrossDocumentConflictFragments(facts) {
|
|
896
|
+
const orderedFacts = facts.slice().sort((left, right) => {
|
|
897
|
+
const sourceDelta = String(left.item.atom.sourcePath || '').localeCompare(String(right.item.atom.sourcePath || ''));
|
|
898
|
+
if (sourceDelta !== 0) {
|
|
899
|
+
return sourceDelta;
|
|
900
|
+
}
|
|
901
|
+
return left.block.startOffset - right.block.startOffset;
|
|
902
|
+
});
|
|
903
|
+
const fragments = [];
|
|
904
|
+
const seenConflicts = new Set();
|
|
905
|
+
orderedFacts.forEach((left, leftIndex) => {
|
|
906
|
+
orderedFacts.slice(leftIndex + 1).forEach((right) => {
|
|
907
|
+
if (comparableFactDocumentKey(left) === comparableFactDocumentKey(right)) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (left.subjectKey !== right.subjectKey || left.factKind !== right.factKind) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
if (left.valueKey === right.valueKey) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const orderedSourceKeys = [comparableFactDocumentKey(left), comparableFactDocumentKey(right)].sort();
|
|
917
|
+
const orderedValues = [left.valueKey, right.valueKey].sort();
|
|
918
|
+
const conflictKey = `${left.subjectKey}:${left.factKind}:${orderedSourceKeys[0]}:${orderedSourceKeys[1]}:${orderedValues[0]}:${orderedValues[1]}`;
|
|
919
|
+
if (seenConflicts.has(conflictKey)) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
seenConflicts.add(conflictKey);
|
|
923
|
+
const citationIds = Array.from(new Set([
|
|
924
|
+
...left.citationIds,
|
|
925
|
+
...right.citationIds,
|
|
926
|
+
...itemEvidenceSpans(left.item).map((span) => span.id),
|
|
927
|
+
...itemEvidenceSpans(right.item).map((span) => span.id),
|
|
928
|
+
].filter(Boolean)));
|
|
929
|
+
const text = [
|
|
930
|
+
`Conflicting evidence for ${left.subjectLabel} across documents:`,
|
|
931
|
+
`${left.item.atom.title}: ${left.block.text}`,
|
|
932
|
+
`${right.item.atom.title}: ${right.block.text}`,
|
|
933
|
+
].join('\n');
|
|
934
|
+
fragments.push({
|
|
935
|
+
fragmentId: `rag_conflict_cross_document_${sanitizeFragmentPart(left.item.atom.documentId)}_${sanitizeFragmentPart(right.item.atom.documentId)}_${fragments.length + 1}`,
|
|
936
|
+
role: 'conflict',
|
|
937
|
+
text,
|
|
938
|
+
atomId: left.item.atom.id,
|
|
939
|
+
documentId: `cross_document_conflict_${sanitizeFragmentPart(left.item.atom.documentId)}_${sanitizeFragmentPart(right.item.atom.documentId)}`,
|
|
940
|
+
sourcePath: `${left.item.atom.sourcePath} | ${right.item.atom.sourcePath}`,
|
|
941
|
+
title: `${left.item.atom.title} / ${right.item.atom.title}`,
|
|
942
|
+
headingPath: [],
|
|
943
|
+
charCount: text.length,
|
|
944
|
+
tokenEstimate: (0, ragContextPack_1.estimateRagTokenCount)(text),
|
|
945
|
+
truncated: false,
|
|
946
|
+
citationIds,
|
|
947
|
+
relationEdgeIds: Array.from(new Set([
|
|
948
|
+
...left.item.relationPath.map((edge) => edge.id),
|
|
949
|
+
...right.item.relationPath.map((edge) => edge.id),
|
|
950
|
+
])),
|
|
951
|
+
score: Number(Math.max(Number(left.item.score || 0), Number(right.item.score || 0)).toFixed(4)),
|
|
952
|
+
sourceBoundary: 'full_document',
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
});
|
|
956
|
+
return fragments;
|
|
957
|
+
}
|
|
958
|
+
function limitGraphNeighborDocumentContextFragments(fragments) {
|
|
959
|
+
const graphNeighborContextFragments = fragments
|
|
960
|
+
.map((fragment, index) => ({ fragment, index }))
|
|
961
|
+
.filter((entry) => (entry.fragment.role === 'graph_neighbor_support'
|
|
962
|
+
&& entry.fragment.sourceBoundary === 'full_document'))
|
|
963
|
+
.sort((left, right) => {
|
|
964
|
+
const scoreDelta = Number(right.fragment.score || 0) - Number(left.fragment.score || 0);
|
|
965
|
+
if (Math.abs(scoreDelta) > 0.0001) {
|
|
966
|
+
return scoreDelta;
|
|
967
|
+
}
|
|
968
|
+
return left.index - right.index;
|
|
969
|
+
});
|
|
970
|
+
const selectedContextFragmentIds = new Set(graphNeighborContextFragments
|
|
971
|
+
.slice(0, MAX_GRAPH_NEIGHBOR_DOCUMENT_CONTEXT_FRAGMENTS)
|
|
972
|
+
.map((entry) => entry.fragment.fragmentId));
|
|
973
|
+
return fragments.filter((fragment) => (fragment.role !== 'graph_neighbor_support'
|
|
974
|
+
|| fragment.sourceBoundary !== 'full_document'
|
|
975
|
+
|| selectedContextFragmentIds.has(fragment.fragmentId)));
|
|
976
|
+
}
|
|
977
|
+
function buildDirectFragment(item, span, index, role = 'direct_support') {
|
|
978
|
+
const text = normalizeWhitespace(String(span.snippet || item.atom.content || item.atom.title || ''));
|
|
979
|
+
return {
|
|
980
|
+
fragmentId: `${role === 'graph_neighbor_support' ? 'rag_graph_neighbor' : 'rag_direct'}_${sanitizeFragmentPart(item.atom.documentId)}_${sanitizeFragmentPart(span.id || String(index + 1))}`,
|
|
981
|
+
role,
|
|
982
|
+
text,
|
|
983
|
+
atomId: item.atom.id,
|
|
984
|
+
documentId: item.atom.documentId,
|
|
985
|
+
sourcePath: item.atom.sourcePath,
|
|
986
|
+
title: item.atom.title,
|
|
987
|
+
headingPath: Array.isArray(item.atom.metadata?.sectionPath) ? [...item.atom.metadata.sectionPath] : [],
|
|
988
|
+
startOffset: Number.isFinite(span.startOffset) ? span.startOffset : undefined,
|
|
989
|
+
endOffset: Number.isFinite(span.endOffset) ? span.endOffset : undefined,
|
|
990
|
+
startLine: Number.isFinite(span.startLine) ? span.startLine : undefined,
|
|
991
|
+
endLine: Number.isFinite(span.endLine) ? span.endLine : undefined,
|
|
992
|
+
charCount: text.length,
|
|
993
|
+
tokenEstimate: (0, ragContextPack_1.estimateRagTokenCount)(text),
|
|
994
|
+
truncated: false,
|
|
995
|
+
citationIds: [span.id],
|
|
996
|
+
relationEdgeIds: item.relationPath.map((edge) => edge.id),
|
|
997
|
+
score: Number(Number(item.score || 0).toFixed(4)),
|
|
998
|
+
sourceBoundary: 'direct_span_only',
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
function groupItemsByDocument(items, graphNeighborItems = []) {
|
|
1002
|
+
const groups = new Map();
|
|
1003
|
+
const appendItem = (item, index, directRole, expandDocumentContext) => {
|
|
1004
|
+
const documentId = String(item.atom.documentId || `document_${index + 1}`).trim();
|
|
1005
|
+
const sourcePath = String(item.atom.sourcePath || '').trim();
|
|
1006
|
+
const key = `${documentId}\n${sourcePath}`;
|
|
1007
|
+
let group = groups.get(key);
|
|
1008
|
+
if (!group) {
|
|
1009
|
+
group = {
|
|
1010
|
+
documentId,
|
|
1011
|
+
sourcePath,
|
|
1012
|
+
entries: [],
|
|
1013
|
+
};
|
|
1014
|
+
groups.set(key, group);
|
|
1015
|
+
}
|
|
1016
|
+
group.entries.push({
|
|
1017
|
+
item,
|
|
1018
|
+
directRole,
|
|
1019
|
+
expandDocumentContext,
|
|
1020
|
+
});
|
|
1021
|
+
};
|
|
1022
|
+
items.forEach((item, index) => appendItem(item, index, 'direct_support', true));
|
|
1023
|
+
graphNeighborItems.forEach((item, index) => appendItem(item, index, 'graph_neighbor_support', true));
|
|
1024
|
+
return Array.from(groups.values());
|
|
1025
|
+
}
|
|
1026
|
+
function itemEvidenceSpans(item) {
|
|
1027
|
+
return Array.isArray(item.evidenceSpans) ? item.evidenceSpans.filter(Boolean) : [];
|
|
1028
|
+
}
|
|
1029
|
+
async function resolveDocumentSource(group, sourceResolver) {
|
|
1030
|
+
if (!sourceResolver) {
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
const atomIds = Array.from(new Set(group.entries.map((entry) => entry.item.atom.id).filter(Boolean)));
|
|
1034
|
+
const citationIds = Array.from(new Set(group.entries.flatMap((entry) => itemEvidenceSpans(entry.item).map((span) => span.id)).filter(Boolean)));
|
|
1035
|
+
const source = await sourceResolver({
|
|
1036
|
+
documentId: group.documentId,
|
|
1037
|
+
sourcePath: group.sourcePath,
|
|
1038
|
+
atomIds,
|
|
1039
|
+
citationIds,
|
|
1040
|
+
});
|
|
1041
|
+
if (!source || !String(source.content || '').trim()) {
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
return {
|
|
1045
|
+
...source,
|
|
1046
|
+
documentId: String(source.documentId || group.documentId),
|
|
1047
|
+
sourcePath: String(source.sourcePath || group.sourcePath),
|
|
1048
|
+
content: String(source.content || ''),
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
function buildParentFragments(group, source, paragraphWindow) {
|
|
1052
|
+
const blocks = parseMarkdownBlocks(source.content);
|
|
1053
|
+
const parentDrafts = new Map();
|
|
1054
|
+
group.entries
|
|
1055
|
+
.filter((entry) => entry.expandDocumentContext)
|
|
1056
|
+
.forEach(({ item, directRole }) => {
|
|
1057
|
+
const fragmentRole = directRole === 'graph_neighbor_support' ? 'graph_neighbor_support' : 'parent_context';
|
|
1058
|
+
itemEvidenceSpans(item).forEach((span) => {
|
|
1059
|
+
const evidenceBlocks = blocksForEvidence(blocks, span, source.content);
|
|
1060
|
+
if (evidenceBlocks.length <= 0) {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const sectionBlocks = buildSectionBlocks(blocks, evidenceBlocks, paragraphWindow);
|
|
1064
|
+
if (sectionBlocks.length <= 0) {
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
const headingPath = evidenceBlocks[0].headingPath.length > 0
|
|
1068
|
+
? evidenceBlocks[0].headingPath
|
|
1069
|
+
: (Array.isArray(item.atom.metadata?.sectionPath) ? item.atom.metadata.sectionPath : []);
|
|
1070
|
+
const parentKey = `${fragmentRole}\n${group.documentId}\n${group.sourcePath}\n${headingPath.join('/') || 'local_window'}`;
|
|
1071
|
+
let draft = parentDrafts.get(parentKey);
|
|
1072
|
+
if (!draft) {
|
|
1073
|
+
draft = {
|
|
1074
|
+
key: parentKey,
|
|
1075
|
+
role: fragmentRole,
|
|
1076
|
+
item,
|
|
1077
|
+
documentId: group.documentId,
|
|
1078
|
+
sourcePath: group.sourcePath,
|
|
1079
|
+
headingPath: [...headingPath],
|
|
1080
|
+
blocks: [],
|
|
1081
|
+
citationIds: new Set(),
|
|
1082
|
+
relationEdgeIds: new Set(),
|
|
1083
|
+
score: 0,
|
|
1084
|
+
};
|
|
1085
|
+
parentDrafts.set(parentKey, draft);
|
|
1086
|
+
}
|
|
1087
|
+
draft.blocks.push(...sectionBlocks);
|
|
1088
|
+
draft.citationIds.add(span.id);
|
|
1089
|
+
item.relationPath.forEach((edge) => draft?.relationEdgeIds.add(edge.id));
|
|
1090
|
+
draft.score = Math.max(draft.score, Number(item.score || 0));
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
return Array.from(parentDrafts.values()).map((draft, index) => {
|
|
1094
|
+
const blocksForFragment = mergeBlocks(draft.blocks);
|
|
1095
|
+
const text = fragmentTextFromBlocks(blocksForFragment);
|
|
1096
|
+
const first = blocksForFragment[0];
|
|
1097
|
+
const last = blocksForFragment[blocksForFragment.length - 1];
|
|
1098
|
+
const fragmentPrefix = draft.role === 'graph_neighbor_support'
|
|
1099
|
+
? 'rag_graph_neighbor_context'
|
|
1100
|
+
: 'rag_parent';
|
|
1101
|
+
return {
|
|
1102
|
+
fragmentId: `${fragmentPrefix}_${sanitizeFragmentPart(draft.documentId)}_${index + 1}`,
|
|
1103
|
+
role: draft.role,
|
|
1104
|
+
text,
|
|
1105
|
+
atomId: draft.item.atom.id,
|
|
1106
|
+
documentId: draft.documentId,
|
|
1107
|
+
sourcePath: draft.sourcePath,
|
|
1108
|
+
title: draft.item.atom.title,
|
|
1109
|
+
headingPath: [...draft.headingPath],
|
|
1110
|
+
startOffset: first?.startOffset,
|
|
1111
|
+
endOffset: last?.endOffset,
|
|
1112
|
+
startLine: first?.startLine,
|
|
1113
|
+
endLine: last?.endLine,
|
|
1114
|
+
charCount: text.length,
|
|
1115
|
+
tokenEstimate: (0, ragContextPack_1.estimateRagTokenCount)(text),
|
|
1116
|
+
truncated: false,
|
|
1117
|
+
citationIds: Array.from(draft.citationIds),
|
|
1118
|
+
relationEdgeIds: Array.from(draft.relationEdgeIds),
|
|
1119
|
+
score: Number(Number(draft.score || 0).toFixed(4)),
|
|
1120
|
+
sourceBoundary: 'full_document',
|
|
1121
|
+
};
|
|
1122
|
+
}).filter((fragment) => fragment.text.length > 0);
|
|
1123
|
+
}
|
|
1124
|
+
async function assembleRagEvidenceContext(params) {
|
|
1125
|
+
const paragraphWindow = Math.floor(Math.max(0, Math.min(20, Number(params.paragraphWindow ?? DEFAULT_PARAGRAPH_WINDOW))));
|
|
1126
|
+
const decisions = [];
|
|
1127
|
+
const rawFragments = [];
|
|
1128
|
+
const fullDocumentComparableFacts = [];
|
|
1129
|
+
let readFullDocument = false;
|
|
1130
|
+
const groups = groupItemsByDocument(Array.isArray(params.items) ? params.items : [], Array.isArray(params.graphNeighborItems) ? params.graphNeighborItems : []);
|
|
1131
|
+
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));
|
|
1136
|
+
});
|
|
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(',');
|
|
1143
|
+
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',
|
|
1151
|
+
});
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
readFullDocument = true;
|
|
1155
|
+
decisions.push({
|
|
1156
|
+
documentId: group.documentId,
|
|
1157
|
+
sourcePath: group.sourcePath,
|
|
1158
|
+
sourceBoundary: 'full_document',
|
|
1159
|
+
status: 'read',
|
|
1160
|
+
charsRead: source.content.length,
|
|
1161
|
+
});
|
|
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
|
+
}
|
|
1167
|
+
rawFragments.push(...buildCrossDocumentConflictFragments(fullDocumentComparableFacts));
|
|
1168
|
+
return (0, ragContextPack_1.buildRagContextPack)({
|
|
1169
|
+
query: params.query,
|
|
1170
|
+
generatedAt: params.generatedAt,
|
|
1171
|
+
sourceBoundary: readFullDocument ? 'full_document' : 'direct_span_only',
|
|
1172
|
+
fragments: limitGraphNeighborDocumentContextFragments(rawFragments),
|
|
1173
|
+
sourceDecisions: decisions,
|
|
1174
|
+
budget: params.budget,
|
|
1175
|
+
});
|
|
1176
|
+
}
|