sxng-cli 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/README.md +118 -11
  2. package/dist/claims/deterministic-checks.d.ts +47 -0
  3. package/dist/claims/deterministic-checks.d.ts.map +1 -0
  4. package/dist/claims/deterministic-checks.js +143 -0
  5. package/dist/claims/deterministic-checks.js.map +1 -0
  6. package/dist/claims/policy.d.ts +25 -0
  7. package/dist/claims/policy.d.ts.map +1 -0
  8. package/dist/claims/policy.js +168 -0
  9. package/dist/claims/policy.js.map +1 -0
  10. package/dist/claims/store.d.ts +31 -0
  11. package/dist/claims/store.d.ts.map +1 -0
  12. package/dist/claims/store.js +127 -0
  13. package/dist/claims/store.js.map +1 -0
  14. package/dist/claims/types.d.ts +93 -0
  15. package/dist/claims/types.d.ts.map +1 -0
  16. package/dist/claims/types.js +9 -0
  17. package/dist/claims/types.js.map +1 -0
  18. package/dist/commands/build-graph.d.ts +0 -0
  19. package/dist/commands/build-graph.d.ts.map +0 -0
  20. package/dist/commands/build-graph.js +0 -0
  21. package/dist/commands/build-graph.js.map +0 -0
  22. package/dist/commands/claim.d.ts +13 -0
  23. package/dist/commands/claim.d.ts.map +1 -0
  24. package/dist/commands/claim.js +121 -0
  25. package/dist/commands/claim.js.map +1 -0
  26. package/dist/commands/doc-index.d.ts +0 -0
  27. package/dist/commands/doc-index.d.ts.map +1 -1
  28. package/dist/commands/doc-index.js +28 -29
  29. package/dist/commands/doc-index.js.map +1 -1
  30. package/dist/commands/doc-search.d.ts +0 -0
  31. package/dist/commands/doc-search.d.ts.map +1 -1
  32. package/dist/commands/doc-search.js +1 -0
  33. package/dist/commands/doc-search.js.map +1 -1
  34. package/dist/commands/evidence.d.ts +21 -0
  35. package/dist/commands/evidence.d.ts.map +1 -0
  36. package/dist/commands/evidence.js +191 -0
  37. package/dist/commands/evidence.js.map +1 -0
  38. package/dist/commands/extract.d.ts +0 -0
  39. package/dist/commands/extract.d.ts.map +1 -1
  40. package/dist/commands/extract.js +9 -21
  41. package/dist/commands/extract.js.map +1 -1
  42. package/dist/commands/graph-add.d.ts +0 -0
  43. package/dist/commands/graph-add.d.ts.map +1 -1
  44. package/dist/commands/graph-add.d.ts.sync-conflict-20260629-232900-2SGIW5T.map +0 -0
  45. package/dist/commands/graph-add.js +13 -3
  46. package/dist/commands/graph-add.js.map +1 -1
  47. package/dist/commands/graph-add.js.sync-conflict-20260629-232916-2SGIW5T.map +0 -0
  48. package/dist/commands/graph-add.sync-conflict-20260629-232853-2SGIW5T.js +0 -0
  49. package/dist/commands/graph-drill.d.ts +0 -0
  50. package/dist/commands/graph-drill.d.ts.map +0 -0
  51. package/dist/commands/graph-drill.js +0 -0
  52. package/dist/commands/graph-drill.js.map +0 -0
  53. package/dist/commands/graph-explore.d.ts +0 -0
  54. package/dist/commands/graph-explore.d.ts.map +0 -0
  55. package/dist/commands/graph-explore.js +0 -0
  56. package/dist/commands/graph-explore.js.map +0 -0
  57. package/dist/commands/graph-obfuscate.d.ts +0 -0
  58. package/dist/commands/graph-obfuscate.d.ts.map +0 -0
  59. package/dist/commands/graph-obfuscate.js +0 -0
  60. package/dist/commands/graph-obfuscate.js.map +0 -0
  61. package/dist/commands/graph-search.d.ts +0 -0
  62. package/dist/commands/graph-search.d.ts.map +0 -0
  63. package/dist/commands/graph-search.js +0 -0
  64. package/dist/commands/graph-search.js.map +0 -0
  65. package/dist/commands/graph-traverse.d.ts +0 -0
  66. package/dist/commands/graph-traverse.d.ts.map +0 -0
  67. package/dist/commands/graph-traverse.js +0 -0
  68. package/dist/commands/graph-traverse.js.map +0 -0
  69. package/dist/commands/query-graph.d.ts +0 -0
  70. package/dist/commands/query-graph.d.ts.map +0 -0
  71. package/dist/commands/query-graph.js +0 -0
  72. package/dist/commands/query-graph.js.map +0 -0
  73. package/dist/commands/raw.d.ts +0 -0
  74. package/dist/commands/raw.d.ts.map +0 -0
  75. package/dist/commands/raw.js +0 -0
  76. package/dist/commands/raw.js.map +0 -0
  77. package/dist/commands/results-add.d.ts +1 -0
  78. package/dist/commands/results-add.d.ts.map +1 -1
  79. package/dist/commands/results-add.js +6 -2
  80. package/dist/commands/results-add.js.map +1 -1
  81. package/dist/commands/review.d.ts +12 -0
  82. package/dist/commands/review.d.ts.map +1 -0
  83. package/dist/commands/review.js +111 -0
  84. package/dist/commands/review.js.map +1 -0
  85. package/dist/commands/session.d.ts +0 -0
  86. package/dist/commands/session.d.ts.map +1 -1
  87. package/dist/commands/session.js +5 -0
  88. package/dist/commands/session.js.map +1 -1
  89. package/dist/commands/verdict.d.ts +9 -0
  90. package/dist/commands/verdict.d.ts.map +1 -0
  91. package/dist/commands/verdict.js +29 -0
  92. package/dist/commands/verdict.js.map +1 -0
  93. package/dist/config.d.ts +0 -0
  94. package/dist/config.d.ts.map +0 -0
  95. package/dist/config.js +0 -0
  96. package/dist/config.js.map +0 -0
  97. package/dist/deep/co-occurrence.d.ts +0 -0
  98. package/dist/deep/co-occurrence.d.ts.map +0 -0
  99. package/dist/deep/co-occurrence.js +0 -0
  100. package/dist/deep/co-occurrence.js.map +0 -0
  101. package/dist/deep/dedupe.d.ts +9 -4
  102. package/dist/deep/dedupe.d.ts.map +1 -1
  103. package/dist/deep/dedupe.js +54 -11
  104. package/dist/deep/dedupe.js.map +1 -1
  105. package/dist/deep/degree-utils.d.ts +0 -0
  106. package/dist/deep/degree-utils.d.ts.map +0 -0
  107. package/dist/deep/degree-utils.js +0 -0
  108. package/dist/deep/degree-utils.js.map +0 -0
  109. package/dist/deep/extractor.d.sync-conflict-20260629-232902-2SGIW5T.ts +0 -0
  110. package/dist/deep/extractor.d.ts +0 -0
  111. package/dist/deep/extractor.d.ts.map +0 -0
  112. package/dist/deep/extractor.d.ts.sync-conflict-20260629-232921-2SGIW5T.map +0 -0
  113. package/dist/deep/extractor.js +0 -0
  114. package/dist/deep/extractor.js.map +0 -0
  115. package/dist/deep/extractor.js.sync-conflict-20260629-232921-2SGIW5T.map +0 -0
  116. package/dist/deep/extractor.sync-conflict-20260629-232903-2SGIW5T.js +0 -0
  117. package/dist/deep/graph-explore.d.ts +0 -0
  118. package/dist/deep/graph-explore.d.ts.map +0 -0
  119. package/dist/deep/graph-explore.js +0 -0
  120. package/dist/deep/graph-explore.js.map +0 -0
  121. package/dist/deep/graph-obfuscate.d.ts +0 -0
  122. package/dist/deep/graph-obfuscate.d.ts.map +0 -0
  123. package/dist/deep/graph-obfuscate.js +0 -0
  124. package/dist/deep/graph-obfuscate.js.map +0 -0
  125. package/dist/deep/graph-preprocess.d.ts +5 -0
  126. package/dist/deep/graph-preprocess.d.ts.map +1 -1
  127. package/dist/deep/graph-preprocess.js +6 -0
  128. package/dist/deep/graph-preprocess.js.map +1 -1
  129. package/dist/deep/graph-sampling.d.ts +0 -0
  130. package/dist/deep/graph-sampling.d.ts.map +0 -0
  131. package/dist/deep/graph-sampling.js +0 -0
  132. package/dist/deep/graph-sampling.js.map +0 -0
  133. package/dist/deep/graph.d.sync-conflict-20260629-232917-2SGIW5T.ts +0 -0
  134. package/dist/deep/graph.d.ts +1 -1
  135. package/dist/deep/graph.d.ts.map +1 -1
  136. package/dist/deep/graph.d.ts.sync-conflict-20260629-232911-2SGIW5T.map +0 -0
  137. package/dist/deep/graph.js +8 -4
  138. package/dist/deep/graph.js.map +1 -1
  139. package/dist/deep/graph.js.sync-conflict-20260629-232855-2SGIW5T.map +0 -0
  140. package/dist/deep/graph.sync-conflict-20260629-232907-2SGIW5T.js +0 -0
  141. package/dist/deep/index.d.ts +1 -1
  142. package/dist/deep/index.d.ts.map +1 -1
  143. package/dist/deep/index.js +1 -1
  144. package/dist/deep/index.js.map +1 -1
  145. package/dist/deep/iteration-data.d.ts +0 -0
  146. package/dist/deep/iteration-data.d.ts.map +1 -1
  147. package/dist/deep/iteration-data.js +2 -2
  148. package/dist/deep/iteration-data.js.map +1 -1
  149. package/dist/deep/keywords.d.ts +0 -0
  150. package/dist/deep/keywords.d.ts.map +0 -0
  151. package/dist/deep/keywords.js +0 -0
  152. package/dist/deep/keywords.js.map +0 -0
  153. package/dist/deep/local-doc/indexer.d.ts +16 -1
  154. package/dist/deep/local-doc/indexer.d.ts.map +1 -1
  155. package/dist/deep/local-doc/indexer.js +305 -22
  156. package/dist/deep/local-doc/indexer.js.map +1 -1
  157. package/dist/deep/local-doc/scanner.d.ts +6 -6
  158. package/dist/deep/local-doc/scanner.d.ts.map +1 -1
  159. package/dist/deep/local-doc/scanner.js +123 -128
  160. package/dist/deep/local-doc/scanner.js.map +1 -1
  161. package/dist/deep/local-doc/searcher.d.ts +1 -0
  162. package/dist/deep/local-doc/searcher.d.ts.map +1 -1
  163. package/dist/deep/local-doc/searcher.js +25 -7
  164. package/dist/deep/local-doc/searcher.js.map +1 -1
  165. package/dist/deep/local-doc/types.d.ts +21 -0
  166. package/dist/deep/local-doc/types.d.ts.map +1 -1
  167. package/dist/deep/local-doc/types.js +0 -0
  168. package/dist/deep/local-doc/types.js.map +1 -1
  169. package/dist/deep/path-extraction.d.ts +0 -0
  170. package/dist/deep/path-extraction.d.ts.map +0 -0
  171. package/dist/deep/path-extraction.js +0 -0
  172. package/dist/deep/path-extraction.js.map +0 -0
  173. package/dist/deep/quality-assess.d.ts +6 -4
  174. package/dist/deep/quality-assess.d.ts.map +1 -1
  175. package/dist/deep/quality-assess.js +37 -29
  176. package/dist/deep/quality-assess.js.map +1 -1
  177. package/dist/deep/query-redundancy.d.ts +0 -0
  178. package/dist/deep/query-redundancy.d.ts.map +0 -0
  179. package/dist/deep/query-redundancy.js +0 -0
  180. package/dist/deep/query-redundancy.js.map +0 -0
  181. package/dist/deep/query-suggest.d.ts +0 -0
  182. package/dist/deep/query-suggest.d.ts.map +0 -0
  183. package/dist/deep/query-suggest.js +0 -0
  184. package/dist/deep/query-suggest.js.map +0 -0
  185. package/dist/deep/recovery-analysis.d.ts +0 -0
  186. package/dist/deep/recovery-analysis.d.ts.map +0 -0
  187. package/dist/deep/recovery-analysis.js +0 -0
  188. package/dist/deep/recovery-analysis.js.map +0 -0
  189. package/dist/deep/rrf.d.ts +0 -0
  190. package/dist/deep/rrf.d.ts.map +0 -0
  191. package/dist/deep/rrf.js +0 -0
  192. package/dist/deep/rrf.js.map +0 -0
  193. package/dist/deep/search-strategy.d.ts +0 -0
  194. package/dist/deep/search-strategy.d.ts.map +0 -0
  195. package/dist/deep/search-strategy.js +0 -0
  196. package/dist/deep/search-strategy.js.map +0 -0
  197. package/dist/deep/session.d.sync-conflict-20260629-232917-2SGIW5T.ts +0 -0
  198. package/dist/deep/session.d.ts +10 -2
  199. package/dist/deep/session.d.ts.map +1 -1
  200. package/dist/deep/session.d.ts.sync-conflict-20260629-232921-2SGIW5T.map +0 -0
  201. package/dist/deep/session.js +57 -33
  202. package/dist/deep/session.js.map +1 -1
  203. package/dist/deep/session.js.sync-conflict-20260629-232854-2SGIW5T.map +0 -0
  204. package/dist/deep/simhash.d.ts +0 -0
  205. package/dist/deep/simhash.d.ts.map +0 -0
  206. package/dist/deep/simhash.js +0 -0
  207. package/dist/deep/simhash.js.map +0 -0
  208. package/dist/deep/tfidf.d.ts +0 -0
  209. package/dist/deep/tfidf.d.ts.map +0 -0
  210. package/dist/deep/tfidf.js +0 -0
  211. package/dist/deep/tfidf.js.map +0 -0
  212. package/dist/index.d.ts +0 -0
  213. package/dist/index.d.ts.map +0 -0
  214. package/dist/index.js +0 -0
  215. package/dist/index.js.map +0 -0
  216. package/dist/init.d.ts +0 -0
  217. package/dist/init.d.ts.map +0 -0
  218. package/dist/init.js +0 -0
  219. package/dist/init.js.map +0 -0
  220. package/dist/protocol.d.ts +0 -0
  221. package/dist/protocol.d.ts.map +0 -0
  222. package/dist/protocol.js +0 -0
  223. package/dist/protocol.js.map +0 -0
  224. package/dist/runCli.d.ts +0 -0
  225. package/dist/runCli.d.ts.map +1 -1
  226. package/dist/runCli.d.ts.sync-conflict-20260629-232854-2SGIW5T.map +0 -0
  227. package/dist/runCli.js +172 -17
  228. package/dist/runCli.js.map +1 -1
  229. package/dist/runCli.js.sync-conflict-20260629-232857-2SGIW5T.map +0 -0
  230. package/dist/runCli.sync-conflict-20260629-232915-2SGIW5T.js +0 -0
  231. package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.d.ts +0 -0
  232. package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.d.ts.map +0 -0
  233. package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.js +0 -0
  234. package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.js.map +0 -0
  235. package/dist/service.d.ts +0 -0
  236. package/dist/service.d.ts.map +1 -1
  237. package/dist/service.js +3 -35
  238. package/dist/service.js.map +1 -1
  239. package/package.json +2 -2
  240. package/sxng.config.example.json +0 -0
package/README.md CHANGED
@@ -29,11 +29,14 @@
29
29
  - 📄 **Multiple Formats** — Markdown (LLM-optimized) or JSON output
30
30
  - 🧠 **Deep Search** — Multi-round iterative research with session accumulation, quality assessment, and recovery strategies
31
31
  - 🔍 **Content Extraction** — Extract full article content from URLs or session results, with Obscura (JS rendering) and Jina Reader fallbacks
32
- - 🗂️ **Session Management** — Accumulate search results across rounds with deduplication; pending → approve → graph injection workflow
33
- - **Quality Assessment** — 4 independent indicators: content depth, entity richness, source diversity, novelty
34
- - 🕸️ **Knowledge Graph** — Structural (query→result→domain) + semantic (entity relations) graph layers
35
- - 🔄 **Query Redundancy Check** — Jaccard similarity + SimHash to avoid repeated queries
32
+ - 🗂️ **Session Management** — Accumulate search results across rounds; normalized URLs and full-text character 5-gram Jaccard remove duplicates before the pending → approve → graph injection workflow
33
+ - 🔗 **External Result Fusion** — Inject results from Tavily, Exa, or any search tool into the same session pipeline via `results-add`; shared pending pool, unified quality assessment
34
+ - **Quality Assessment** — 3 independent indicators: content depth, source diversity, and novelty
35
+ - 🕸️ **Knowledge Graph** — Structural (query→result→domain) + semantic (entity relations with source-round provenance) graph layers
36
+ - 🔄 **Query Redundancy Check** — Word-level or character-bigram Jaccard detects repeated queries
36
37
  - 💡 **Agent-First Design** — Outputs structured analysis data (quality, suggestions, recovery) for LLM Agent decision-making
38
+ - 📁 **Local Document Search** — Index and BM25-search local Markdown/text files; results auto-injected into the session pipeline as `source: "local"`
39
+ - ✅ **Claim—Evidence—Review Pipeline** — L2/L3 only: submit atomic claims, auto-search evidence, verify with stance, then policy-aggregate by publisher-domain diversity for auto-approval or Agent review
37
40
 
38
41
  ## 📦 Installation
39
42
 
@@ -117,13 +120,24 @@ obscura --version
117
120
  | `sxng session-report <session>` | Full session analysis report |
118
121
  | `sxng session-list` | List all sessions |
119
122
  | `sxng session-delete <name>` | Delete a session |
120
- | `sxng graph-preprocess <session>` | TF-IDF + co-occurrence analysis |
121
- | `sxng graph-add <session>` | Add entities/edges to knowledge graph |
123
+ | `sxng graph-preprocess <session>` | TF-IDF + co-occurrence + result provenance analysis |
124
+ | `sxng graph-add <session>` | Add entities/edges to the knowledge graph; new entities require source rounds |
122
125
  | `sxng graph-search <session>` | Discover entities by keyword |
123
126
  | `sxng graph-explore <session>` | View entity relations |
124
127
  | `sxng graph-drill <session>` | Follow specific relations |
125
128
  | `sxng graph-traverse <session>` | Traverse reasoning paths |
126
129
  | `sxng graph-obfuscate <session>` | List obfuscation candidates |
130
+ | `sxng results-add <session> --query <query> --data <json>` | Inject external search results into session as pending |
131
+ | `sxng doc-index <path>` | Index local documents for BM25 search |
132
+ | `sxng doc-search <session> <query> --path <path>` | Search indexed docs and inject results into session |
133
+ | `sxng claim-add <session> --claims <json>` | Submit atomic claims (single or batch, auto evidence-search) |
134
+ | `sxng claim-list <session>` | List claims |
135
+ | `sxng evidence-search <session> --claim-id <id>` | Search candidate evidence (read-only) |
136
+ | `sxng evidence-verify <session> --claim-id <id>` | Confirm evidence + submit stance (+ optional auto-policy) |
137
+ | `sxng evidence-list <session> --claim-id <id>` | List evidence for a claim |
138
+ | `sxng verdict-list <session> --claim-id <id>` | List verdicts for a claim |
139
+ | `sxng policy-aggregate <session>` | Run policy aggregation manually |
140
+ | `sxng review-list <session>` | List reviews |
127
141
  | `sxng --health` | Check SearXNG server health |
128
142
  | `sxng --engines-list` | List available search engines |
129
143
  | `sxng --categories-list` | List available categories |
@@ -220,6 +234,8 @@ Deep search enables multi-round iterative research with session accumulation, qu
220
234
 
221
235
  ```
222
236
  Search → Extract → Preprocess → Build Graph → Quality Assess → Approve → (Loop or Explore)
237
+
238
+ (L2/L3) Claim—Evidence—Review → Final Output
223
239
  ```
224
240
 
225
241
  ### Quick Example
@@ -232,14 +248,14 @@ sxng --session new --owner "agent-1" --desc "Rust async study" "rust async ecosy
232
248
  # 2. Extract content from results
233
249
  sxng extract --session ds_1712345678_abcdef
234
250
 
235
- # 3. Preprocess for entity discovery (TF-IDF + co-occurrence)
251
+ # 3. Preprocess for entity discovery and result provenance
236
252
  sxng graph-preprocess ds_1712345678_abcdef
237
253
 
238
- # 4. Add entities to knowledge graph
254
+ # 4. Add entities with source rounds from graph-preprocess resultProvenance
239
255
  sxng graph-add ds_1712345678_abcdef --data '{
240
256
  "entities": [
241
- {"label": "tokio", "entityType": "runtime", "score": 0.95},
242
- {"label": "async-std", "entityType": "runtime", "score": 0.85}
257
+ {"label": "tokio", "entityType": "runtime", "score": 0.95, "sourceRounds": [1]},
258
+ {"label": "async-std", "entityType": "runtime", "score": 0.85, "sourceRounds": [1]}
243
259
  ],
244
260
  "edges": [
245
261
  {"source": "e:tokio", "target": "e:async_std", "relation": "alternative_to", "weight": 0.9}
@@ -256,6 +272,12 @@ sxng --session ds_1712345678_abcdef --quality --approve "0,1,2,3"
256
272
  sxng --session ds_1712345678_abcdef --queries "tokio vs async-std,benchmark 2026" --redundancy warn
257
273
  ```
258
274
 
275
+ ### Quality and Evidence Semantics
276
+
277
+ - Extracted web content is deduplicated by normalized URL, then by full-text character 5-gram Jaccard similarity. Query redundancy uses a separate word-level or character-bigram Jaccard check.
278
+ - `--quality` assesses the newest recorded round against earlier approved results. A URL already seen in an earlier round is non-novel, even when it reappears in the newest round.
279
+ - Claim policy treats two normalized publisher domains as two sources. It does not infer corporate ownership, editorial relationships, or syndication across domains.
280
+
259
281
  ### Session Management
260
282
 
261
283
  | Command | Description |
@@ -290,6 +312,91 @@ Two layers:
290
312
 
291
313
  Graph navigation commands: `graph-search` (discover entities), `graph-explore` (view relations), `graph-drill` (follow specific relations), `graph-traverse` (traverse reasoning paths).
292
314
 
315
+ `graph-preprocess` returns `resultProvenance` (`url`, `title`, `rounds`) for each result. For every new entity, select the supporting results, union their `rounds`, and send that array as `sourceRounds`. This lets `strategy-info` calculate entity growth from verified search rounds.
316
+
317
+ ### External Search Results
318
+
319
+ Results from other search tools (Tavily, Exa, etc.) can be injected into any active session via `results-add`, following the same pipeline as native sxng results:
320
+
321
+ ```bash
322
+ sxng results-add <session-name> --query "async runtime" --data '[
323
+ {"url": "https://...", "title": "...", "source": "tavily"}
324
+ ]'
325
+ ```
326
+
327
+ After injection, results are `pending` and follow `--quality` → `--approve` → `graph-add`. The required `--query` records the source query so `graph-preprocess` can provide correct `resultProvenance` rounds.
328
+
329
+ ### Claim—Evidence—Review Pipeline (L2/L3 Only)
330
+
331
+ After search, extraction, and graph building, the Agent can run the claim audit pipeline to verify individual statements before final output. CLI does the deterministic work (evidence anchoring, hash verification, Jaccard matching, policy rules); the Agent does all semantic reasoning (claim extraction, stance judgement, final output decisions).
332
+
333
+ **Workflow:**
334
+
335
+ ```
336
+ Agent writes draft → claim-add (auto evidence-search) →
337
+ evidence-verify (with --complete auto-policy) → Review → Agent adjusts output
338
+ ```
339
+
340
+ **Quick Example:**
341
+
342
+ ```bash
343
+ # 1. Submit claims from your draft
344
+ sxng claim-add my-session --claims '[
345
+ {"text":"Tokio is the most widely used async runtime","riskLevel":"medium"},
346
+ {"text":"async-std is no longer actively maintained","riskLevel":"medium"}
347
+ ]'
348
+ # → returns claims with IDs + auto-discovered evidence candidates
349
+
350
+ # 2. Verify evidence + submit stance (+ auto policy aggregation)
351
+ sxng evidence-verify my-session --claim-id "cl_001" \
352
+ --evidence '{"resultUrl":"https://tokio.rs/","quote":"Tokio is the most widely used async runtime...","charStart":45,"charEnd":93}' \
353
+ --stance support --reason "Official docs confirm" --complete
354
+ # → returns evidence + verdict + review (decision: approved / needsReview)
355
+
356
+ # 3. Check final reviews
357
+ sxng review-list my-session
358
+ ```
359
+
360
+ **Key Principles:**
361
+ - **Deterministic checks**: CLI validates URL in approved results, UTF-16 offsets within range, SHA256 hash of quote matches source
362
+ - **No embedded LLM**: All semantic work (claim extraction, stance) is done by the Agent outside the CLI
363
+ - **Policy rules**: 7 rules (singleRefute → highRiskInsufficient → dualSourceSupport → dualSourceMixed → singleSource → allInsufficient → fallback) — pure rule engine, no model calls
364
+ - **`--complete` flag**: Evidence + verdict + auto-policy in one call
365
+
366
+ | Commands | Purpose |
367
+ |----------|---------|
368
+ | `claim-add` | Submit claims (auto-triggers evidence search) |
369
+ | `evidence-verify` | Confirm evidence + submit stance + optional auto-policy |
370
+ | `policy-aggregate` | Manual re-aggregation |
371
+ | `claim-list` / `evidence-list` / `verdict-list` / `review-list` | Query pipeline state |
372
+
373
+ The pipeline is **only available for L2/L3 deep search sessions** — L1 simple searches have no session or approved results pool.
374
+
375
+ ### Local Document Search
376
+
377
+ `doc-index` and `doc-search` enable BM25 full-text search over local documents, with results flowing directly into the session pipeline:
378
+
379
+ ```bash
380
+ # Index (auto-triggered by doc-search)
381
+ sxng doc-index ./docs
382
+
383
+ # Search and inject into session
384
+ sxng doc-search <session-name> "query" --path ./docs
385
+ ```
386
+
387
+ **How it works:** auto-indexing on first use (Orama BM25, title×3 / headings×2 / content×1), results injected as `source: "local"` pending, same `--quality` → `--approve` → `graph-add` flow. Use `graph-preprocess` before adding new entities so their `sourceRounds` come from result provenance. Local searches do **not** increment the round counter.
388
+
389
+ **Quality note:** Pure local search yields `sourceDiversity: 1`. Combine with web results for adequate diversity.
390
+
391
+ ### Known Limitations
392
+
393
+ - Session and claim state is stored across JSON files without transactional concurrent-write protection.
394
+ - URL extraction is not an SSRF security boundary; use only trusted public URLs.
395
+ - Local document scanning has no defined symbolic-link boundary or cycle policy.
396
+ - Index rebuilds overwrite the current persistence files; an interruption or disk failure can require re-indexing.
397
+
398
+ See [PRD-005 design improvements](docs/prds/PRD-005-design-improvements.md) for the deferred remediation plan.
399
+
293
400
  ## 🔗 Links
294
401
 
295
402
  - **GitHub:** https://github.com/hkwuks/sxng-cli
@@ -298,4 +405,4 @@ Graph navigation commands: `graph-search` (discover entities), `graph-explore` (
298
405
 
299
406
  ## 📄 License
300
407
 
301
- MIT
408
+ MIT
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Deterministic checks — no LLM dependency.
3
+ *
4
+ * - checkAnchor: URL in approved results, offset in range, hash matches
5
+ * - computeSourceClusterId: normalized publisher domain → SHA256 fingerprint
6
+ * - searchCandidates: keyword + Jaccard sentence matching
7
+ */
8
+ import { EvidenceSpan } from './types.js';
9
+ export interface AnchorCheck {
10
+ url: boolean;
11
+ offsetInRange: boolean;
12
+ hashMatches: boolean;
13
+ }
14
+ /**
15
+ * Verify that an EvidenceSpan is properly anchored to approved content.
16
+ */
17
+ export declare function checkAnchor(evidence: EvidenceSpan, content: string): AnchorCheck;
18
+ /**
19
+ * Verify that evidence.resultUrl exists in the session's approved results.
20
+ * Returns the matching Result's content, or null if not found.
21
+ */
22
+ export declare function resolveApprovedContent(sessionDir: string, resultUrl: string): {
23
+ content: string;
24
+ } | null;
25
+ /**
26
+ * Compute a source cluster ID for an EvidenceSpan.
27
+ * Groups evidence by normalized publisher domain for domain-level checks.
28
+ * It does not determine cross-domain corporate or editorial affiliation.
29
+ */
30
+ export declare function computeSourceClusterId(evidence: EvidenceSpan): string;
31
+ /** A candidate evidence match, not persisted until verified by Agent. */
32
+ export interface EvidenceCandidate {
33
+ resultUrl: string;
34
+ quote: string;
35
+ charStart: number;
36
+ charEnd: number;
37
+ contentHash: string;
38
+ domain: string;
39
+ author?: string;
40
+ siteName?: string;
41
+ }
42
+ /**
43
+ * Search approved session results for candidate evidence supporting the claim.
44
+ * Uses keyword + Jaccard sentence matching. Read-only — results not persisted.
45
+ */
46
+ export declare function searchCandidates(sessionDir: string, claimText: string): EvidenceCandidate[];
47
+ //# sourceMappingURL=deterministic-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deterministic-checks.d.ts","sourceRoot":"","sources":["../../src/claims/deterministic-checks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM1C,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,OAAO,CAAC;IACb,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,GACd,WAAW,CAQb;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ5B;AAID;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,YAAY,GACrB,MAAM,CAQR;AAID,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAsCD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,iBAAiB,EAAE,CAwDrB"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Deterministic checks — no LLM dependency.
3
+ *
4
+ * - checkAnchor: URL in approved results, offset in range, hash matches
5
+ * - computeSourceClusterId: normalized publisher domain → SHA256 fingerprint
6
+ * - searchCandidates: keyword + Jaccard sentence matching
7
+ */
8
+ import { createHash } from 'crypto';
9
+ import { getApprovedResults } from '../deep/session.js';
10
+ import { loadSessionResults } from '../deep/session.js';
11
+ /**
12
+ * Verify that an EvidenceSpan is properly anchored to approved content.
13
+ */
14
+ export function checkAnchor(evidence, content) {
15
+ const slice = content.slice(evidence.charStart, evidence.charEnd);
16
+ const computedHash = createHash('sha256').update(slice).digest('hex');
17
+ return {
18
+ url: true, // caller must have resolved url → content already
19
+ offsetInRange: evidence.charEnd <= content.length && evidence.charStart >= 0,
20
+ hashMatches: computedHash === evidence.contentHash,
21
+ };
22
+ }
23
+ /**
24
+ * Verify that evidence.resultUrl exists in the session's approved results.
25
+ * Returns the matching Result's content, or null if not found.
26
+ */
27
+ export function resolveApprovedContent(sessionDir, resultUrl) {
28
+ const approved = getApprovedResults(sessionDir);
29
+ for (const r of approved) {
30
+ if (r.url === resultUrl && r.content) {
31
+ return { content: r.content };
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+ // ── Source clustering ───────────────────────────────────────────────
37
+ /**
38
+ * Compute a source cluster ID for an EvidenceSpan.
39
+ * Groups evidence by normalized publisher domain for domain-level checks.
40
+ * It does not determine cross-domain corporate or editorial affiliation.
41
+ */
42
+ export function computeSourceClusterId(evidence) {
43
+ let publisher = '';
44
+ try {
45
+ publisher = new URL(evidence.resultUrl).hostname.toLowerCase().replace(/^www\./, '');
46
+ }
47
+ catch { /* empty */ }
48
+ // Unknown publishers share one cluster so they cannot inflate domain diversity.
49
+ return createHash('sha256').update(publisher).digest('hex').slice(0, 16);
50
+ }
51
+ /** Simple stopwords for tokenization */
52
+ const STOPWORDS = new Set([
53
+ 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
54
+ 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
55
+ 'should', 'may', 'might', 'shall', 'can', 'need', 'dare', 'ought',
56
+ 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as',
57
+ 'into', 'through', 'during', 'before', 'after', 'above', 'below',
58
+ 'between', 'out', 'off', 'over', 'under', 'again', 'further',
59
+ 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how',
60
+ 'all', 'each', 'every', 'both', 'few', 'more', 'most', 'other',
61
+ 'some', 'such', 'no', 'not', 'only', 'own', 'same', 'so', 'than',
62
+ 'too', 'very', 'just', 'because', 'but', 'and', 'or', 'if', 'while',
63
+ 'although', 'this', 'that', 'these', 'those', 'it', 'its',
64
+ 'the', 'which', 'who', 'whom', 'what',
65
+ ]);
66
+ function tokenize(text) {
67
+ return text
68
+ .toLowerCase()
69
+ .split(/[^a-z0-9]+/)
70
+ .filter(t => t.length > 1 && !STOPWORDS.has(t));
71
+ }
72
+ function jaccardSimilarity(a, b) {
73
+ if (a.size === 0 && b.size === 0)
74
+ return 0;
75
+ let intersection = 0;
76
+ for (const word of a) {
77
+ if (b.has(word))
78
+ intersection++;
79
+ }
80
+ const union = a.size + b.size - intersection;
81
+ return union === 0 ? 0 : intersection / union;
82
+ }
83
+ const JACCARD_THRESHOLD = 0.3;
84
+ const MAX_CANDIDATES = 5;
85
+ /**
86
+ * Search approved session results for candidate evidence supporting the claim.
87
+ * Uses keyword + Jaccard sentence matching. Read-only — results not persisted.
88
+ */
89
+ export function searchCandidates(sessionDir, claimText) {
90
+ const keywords = tokenize(claimText);
91
+ if (keywords.length === 0)
92
+ return [];
93
+ const keywordSet = new Set(keywords);
94
+ const allResults = loadSessionResults(sessionDir);
95
+ const approved = allResults.filter(r => r.status === 'approved' && r.content);
96
+ const candidates = [];
97
+ for (const result of approved) {
98
+ const content = result.content;
99
+ // Split into sentences
100
+ const sentences = content.split(/(?<=[.!?])\s+|(?<=\n)\s*/);
101
+ for (const sentence of sentences) {
102
+ const trimmed = sentence.trim();
103
+ if (trimmed.length < 10)
104
+ continue;
105
+ const words = tokenize(trimmed);
106
+ if (words.length === 0)
107
+ continue;
108
+ const wordSet = new Set(words);
109
+ const score = jaccardSimilarity(keywordSet, wordSet);
110
+ if (score < JACCARD_THRESHOLD)
111
+ continue;
112
+ const charStart = content.indexOf(trimmed);
113
+ if (charStart === -1)
114
+ continue;
115
+ const charEnd = charStart + trimmed.length;
116
+ const contentHash = createHash('sha256')
117
+ .update(trimmed)
118
+ .digest('hex');
119
+ let domain = '';
120
+ try {
121
+ domain = new URL(result.url).hostname;
122
+ }
123
+ catch { /* empty */ }
124
+ candidates.push({
125
+ score,
126
+ candidate: {
127
+ resultUrl: result.url,
128
+ quote: trimmed,
129
+ charStart,
130
+ charEnd,
131
+ contentHash,
132
+ domain,
133
+ author: result.byline || undefined,
134
+ siteName: result.siteName || undefined,
135
+ },
136
+ });
137
+ }
138
+ }
139
+ // Sort by Jaccard score descending, take top N
140
+ candidates.sort((a, b) => b.score - a.score);
141
+ return candidates.slice(0, MAX_CANDIDATES).map(c => c.candidate);
142
+ }
143
+ //# sourceMappingURL=deterministic-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deterministic-checks.js","sourceRoot":"","sources":["../../src/claims/deterministic-checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Policy aggregation engine — pure rule engine, no LLM.
3
+ *
4
+ * Input: all Verdicts + associated EvidenceSpans for one Claim
5
+ * Output: a Review with decision and matched rule
6
+ *
7
+ * Rules are evaluated in order, first match wins.
8
+ */
9
+ import { Verdict, EvidenceSpan, Review } from './types.js';
10
+ export interface PolicyInput {
11
+ claimId: string;
12
+ riskLevel: 'low' | 'medium' | 'high';
13
+ verdicts: Verdict[];
14
+ evidences: EvidenceSpan[];
15
+ }
16
+ /**
17
+ * Run all rules against a single claim's data and produce a Review.
18
+ */
19
+ export declare function aggregateClaim(input: PolicyInput): Review;
20
+ /**
21
+ * Aggregate all claims that have verdicts but no review yet.
22
+ * Returns the new reviews.
23
+ */
24
+ export declare function aggregateAll(claimVerdicts: Array<PolicyInput>): Review[];
25
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/claims/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,OAAO,EACP,YAAY,EACZ,MAAM,EAIP,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAqJD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA+BzD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,KAAK,CAAC,WAAW,CAAC,GAChC,MAAM,EAAE,CAEV"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Policy aggregation engine — pure rule engine, no LLM.
3
+ *
4
+ * Input: all Verdicts + associated EvidenceSpans for one Claim
5
+ * Output: a Review with decision and matched rule
6
+ *
7
+ * Rules are evaluated in order, first match wins.
8
+ */
9
+ /**
10
+ * Evaluate deterministic checks for a set of verdicts + evidences.
11
+ */
12
+ function computeChecks(verdicts, evidences) {
13
+ const stanceCounts = { support: 0, refute: 0, insufficient: 0 };
14
+ for (const v of verdicts) {
15
+ stanceCounts[v.stance]++;
16
+ }
17
+ // Domain-level source independence: count publisher-domain cluster IDs.
18
+ const uniqueSources = new Set(evidences
19
+ .filter(e => e.sourceClusterId)
20
+ .map(e => e.sourceClusterId));
21
+ const sourceIndependent = uniqueSources.size >= 2;
22
+ return {
23
+ sourceIndependent,
24
+ hasRefute: stanceCounts.refute > 0,
25
+ allSupport: stanceCounts.support > 0 && stanceCounts.refute === 0 && stanceCounts.insufficient === 0,
26
+ };
27
+ }
28
+ /**
29
+ * Build conflict info from verdicts+evidences for refute scenarios.
30
+ */
31
+ function buildConflict(verdicts, evidences) {
32
+ const refutingVerdicts = verdicts.filter(v => v.stance === 'refute');
33
+ const supportingVerdicts = verdicts.filter(v => v.stance === 'support');
34
+ if (refutingVerdicts.length === 0)
35
+ return undefined;
36
+ const supportingQuotes = supportingVerdicts
37
+ .slice(0, 5)
38
+ .map(v => {
39
+ const ev = evidences.find(e => v.evidenceIds.includes(e.id));
40
+ return `${v.id}: ${ev?.quote.slice(0, 100) || 'no quote'}`;
41
+ });
42
+ const refutingQuotes = refutingVerdicts
43
+ .slice(0, 5)
44
+ .map(v => {
45
+ const ev = evidences.find(e => v.evidenceIds.includes(e.id));
46
+ return `${v.id}: ${ev?.quote.slice(0, 100) || 'no quote'}`;
47
+ });
48
+ return {
49
+ summary: `Claim is supported by ${supportingVerdicts.length} evidence(s) and refuted by ${refutingVerdicts.length} evidence(s)`,
50
+ supporting: supportingQuotes,
51
+ refuting: refutingQuotes,
52
+ };
53
+ }
54
+ // ── Rules (evaluated in order) ──────────────────────────────────────
55
+ const RULES = [
56
+ // #1 singleRefute: any refute → needsReview
57
+ {
58
+ name: 'singleRefute',
59
+ evaluate(input) {
60
+ const hasRefute = input.verdicts.some(v => v.stance === 'refute');
61
+ return hasRefute ? 'needsReview' : null;
62
+ },
63
+ },
64
+ // #2 highRiskInsufficient: high risk + <2 publisher domains → needsReview
65
+ {
66
+ name: 'highRiskInsufficient',
67
+ evaluate(input) {
68
+ if (input.riskLevel !== 'high')
69
+ return null;
70
+ const uniqueSources = new Set(input.evidences.filter(e => e.sourceClusterId).map(e => e.sourceClusterId));
71
+ if (uniqueSources.size < 2)
72
+ return 'needsReview';
73
+ return null;
74
+ },
75
+ },
76
+ // #3 dualSourceSupport: >= 2 publisher domains + all support → approved
77
+ {
78
+ name: 'dualSourceSupport',
79
+ evaluate(input) {
80
+ const uniqueSources = new Set(input.evidences.filter(e => e.sourceClusterId).map(e => e.sourceClusterId));
81
+ if (uniqueSources.size < 2)
82
+ return null;
83
+ const allSupport = input.verdicts.every(v => v.stance === 'support');
84
+ if (allSupport)
85
+ return 'approved';
86
+ return null;
87
+ },
88
+ },
89
+ // #4 dualSourceMixed: >= 2 publisher domains but some insufficient → needsReview
90
+ {
91
+ name: 'dualSourceMixed',
92
+ evaluate(input) {
93
+ const uniqueSources = new Set(input.evidences.filter(e => e.sourceClusterId).map(e => e.sourceClusterId));
94
+ if (uniqueSources.size < 2)
95
+ return null;
96
+ const hasInsufficient = input.verdicts.some(v => v.stance === 'insufficient');
97
+ if (hasInsufficient)
98
+ return 'needsReview';
99
+ return null;
100
+ },
101
+ },
102
+ // #5 singleSource: only 1 publisher domain → needsReview
103
+ {
104
+ name: 'singleSource',
105
+ evaluate(input) {
106
+ const uniqueSources = new Set(input.evidences.filter(e => e.sourceClusterId).map(e => e.sourceClusterId));
107
+ if (uniqueSources.size === 1)
108
+ return 'needsReview';
109
+ return null;
110
+ },
111
+ },
112
+ // #6 allInsufficient: all verdicts are insufficient → needsReview
113
+ {
114
+ name: 'allInsufficient',
115
+ evaluate(input) {
116
+ if (input.verdicts.length === 0)
117
+ return null;
118
+ const allInsufficient = input.verdicts.every(v => v.stance === 'insufficient');
119
+ return allInsufficient ? 'needsReview' : null;
120
+ },
121
+ },
122
+ // #7 fallback: catch-all
123
+ {
124
+ name: 'fallback',
125
+ evaluate() {
126
+ return 'needsReview';
127
+ },
128
+ },
129
+ ];
130
+ // ── Aggregation ─────────────────────────────────────────────────────
131
+ /**
132
+ * Run all rules against a single claim's data and produce a Review.
133
+ */
134
+ export function aggregateClaim(input) {
135
+ const checks = computeChecks(input.verdicts, input.evidences);
136
+ let decision = 'needsReview';
137
+ let matchedRule = 'fallback';
138
+ for (const rule of RULES) {
139
+ const result = rule.evaluate(input);
140
+ if (result !== null) {
141
+ decision = result;
142
+ matchedRule = rule.name;
143
+ break;
144
+ }
145
+ }
146
+ const review = {
147
+ claimId: input.claimId,
148
+ decision,
149
+ autoPass: decision === 'approved',
150
+ checks,
151
+ matchedRule,
152
+ reviewer: 'agent',
153
+ reviewedAt: Date.now(),
154
+ };
155
+ // Populate conflict only when refute exists
156
+ if (checks.hasRefute) {
157
+ review.conflict = buildConflict(input.verdicts, input.evidences);
158
+ }
159
+ return review;
160
+ }
161
+ /**
162
+ * Aggregate all claims that have verdicts but no review yet.
163
+ * Returns the new reviews.
164
+ */
165
+ export function aggregateAll(claimVerdicts) {
166
+ return claimVerdicts.map(aggregateClaim);
167
+ }
168
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/claims/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Claims store — CRUD operations for claims/*.json files.
3
+ *
4
+ * Each session directory has a claims/ subdirectory with four files
5
+ * using the same envelope format as results.json/graph.json:
6
+ * { "status": "ok", "data": { "claims": [...] } }
7
+ */
8
+ import { Claim, EvidenceSpan, Verdict, Review, ReviewStatus } from './types.js';
9
+ export declare function loadClaims(sessionDir: string): Claim[];
10
+ export declare function saveClaims(sessionDir: string, claims: Claim[]): void;
11
+ /** Allocate next claim ID: cl_<seq>. Reads current count from file. */
12
+ export declare function nextClaimId(sessionDir: string): string;
13
+ export declare function loadEvidences(sessionDir: string): EvidenceSpan[];
14
+ export declare function saveEvidences(sessionDir: string, evidences: EvidenceSpan[]): void;
15
+ /** Allocate next evidence ID: ev_<seq>. */
16
+ export declare function nextEvidenceId(sessionDir: string): string;
17
+ export declare function loadVerdicts(sessionDir: string): Verdict[];
18
+ export declare function saveVerdicts(sessionDir: string, verdicts: Verdict[]): void;
19
+ /** Allocate next verdict ID: vd_<seq>. */
20
+ export declare function nextVerdictId(sessionDir: string): string;
21
+ export declare function loadReviews(sessionDir: string): Review[];
22
+ export declare function saveReviews(sessionDir: string, reviews: Review[]): void;
23
+ /** Get claims filtered by status */
24
+ export declare function getClaimsByStatus(sessionDir: string, status?: Claim['status']): Claim[];
25
+ /** Get reviews filtered by decision */
26
+ export declare function getReviewsByDecision(sessionDir: string, decision?: ReviewStatus): Review[];
27
+ /** Get evidence for a specific claim */
28
+ export declare function getEvidenceForClaim(sessionDir: string, claimId: string): EvidenceSpan[];
29
+ /** Get verdicts for a specific claim */
30
+ export declare function getVerdictsForClaim(sessionDir: string, claimId: string): Verdict[];
31
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/claims/store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EACL,KAAK,EACL,YAAY,EACZ,OAAO,EACP,MAAM,EACN,YAAY,EACb,MAAM,YAAY,CAAC;AAkEpB,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,CAEtD;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAGpE;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAItD;AAID,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,EAAE,CAEhE;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI,CAGjF;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAIzD;AAID,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,EAAE,CAE1D;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,CAG1E;AAED,0CAA0C;AAC1C,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAIxD;AAID,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAExD;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAGvE;AAID,oCAAoC;AACpC,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,GACvB,KAAK,EAAE,CAIT;AAED,uCAAuC;AACvC,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,YAAY,GACtB,MAAM,EAAE,CAIV;AAED,wCAAwC;AACxC,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,YAAY,EAAE,CAEhB;AAED,wCAAwC;AACxC,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,EAAE,CAEX"}