sxng-cli 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/claims/deterministic-checks.d.ts +46 -0
- package/dist/claims/deterministic-checks.d.ts.map +1 -0
- package/dist/claims/deterministic-checks.js +146 -0
- package/dist/claims/deterministic-checks.js.map +1 -0
- package/dist/claims/policy.d.ts +25 -0
- package/dist/claims/policy.d.ts.map +1 -0
- package/dist/claims/policy.js +168 -0
- package/dist/claims/policy.js.map +1 -0
- package/dist/claims/store.d.ts +31 -0
- package/dist/claims/store.d.ts.map +1 -0
- package/dist/claims/store.js +127 -0
- package/dist/claims/store.js.map +1 -0
- package/dist/claims/types.d.ts +93 -0
- package/dist/claims/types.d.ts.map +1 -0
- package/dist/claims/types.js +9 -0
- package/dist/claims/types.js.map +1 -0
- package/dist/commands/build-graph.d.ts +0 -0
- package/dist/commands/build-graph.d.ts.map +0 -0
- package/dist/commands/build-graph.js +0 -0
- package/dist/commands/build-graph.js.map +0 -0
- package/dist/commands/claim.d.ts +13 -0
- package/dist/commands/claim.d.ts.map +1 -0
- package/dist/commands/claim.js +121 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/doc-index.d.ts +0 -0
- package/dist/commands/doc-index.d.ts.map +0 -0
- package/dist/commands/doc-index.js +0 -0
- package/dist/commands/doc-index.js.map +0 -0
- package/dist/commands/doc-search.d.ts +0 -0
- package/dist/commands/doc-search.d.ts.map +0 -0
- package/dist/commands/doc-search.js +0 -0
- package/dist/commands/doc-search.js.map +0 -0
- package/dist/commands/evidence.d.ts +21 -0
- package/dist/commands/evidence.d.ts.map +1 -0
- package/dist/commands/evidence.js +187 -0
- package/dist/commands/evidence.js.map +1 -0
- package/dist/commands/extract.d.ts +0 -0
- package/dist/commands/extract.d.ts.map +0 -0
- package/dist/commands/extract.js +0 -0
- package/dist/commands/extract.js.map +0 -0
- package/dist/commands/graph-add.d.ts +0 -0
- package/dist/commands/graph-add.d.ts.map +0 -0
- package/dist/commands/graph-add.d.ts.sync-conflict-20260629-232900-2SGIW5T.map +0 -0
- package/dist/commands/graph-add.js +0 -0
- package/dist/commands/graph-add.js.map +0 -0
- package/dist/commands/graph-add.js.sync-conflict-20260629-232916-2SGIW5T.map +0 -0
- package/dist/commands/graph-add.sync-conflict-20260629-232853-2SGIW5T.js +0 -0
- package/dist/commands/graph-drill.d.ts +0 -0
- package/dist/commands/graph-drill.d.ts.map +0 -0
- package/dist/commands/graph-drill.js +0 -0
- package/dist/commands/graph-drill.js.map +0 -0
- package/dist/commands/graph-explore.d.ts +0 -0
- package/dist/commands/graph-explore.d.ts.map +0 -0
- package/dist/commands/graph-explore.js +0 -0
- package/dist/commands/graph-explore.js.map +0 -0
- package/dist/commands/graph-obfuscate.d.ts +0 -0
- package/dist/commands/graph-obfuscate.d.ts.map +0 -0
- package/dist/commands/graph-obfuscate.js +0 -0
- package/dist/commands/graph-obfuscate.js.map +0 -0
- package/dist/commands/graph-search.d.ts +0 -0
- package/dist/commands/graph-search.d.ts.map +0 -0
- package/dist/commands/graph-search.js +0 -0
- package/dist/commands/graph-search.js.map +0 -0
- package/dist/commands/graph-traverse.d.ts +0 -0
- package/dist/commands/graph-traverse.d.ts.map +0 -0
- package/dist/commands/graph-traverse.js +0 -0
- package/dist/commands/graph-traverse.js.map +0 -0
- package/dist/commands/query-graph.d.ts +0 -0
- package/dist/commands/query-graph.d.ts.map +0 -0
- package/dist/commands/query-graph.js +0 -0
- package/dist/commands/query-graph.js.map +0 -0
- package/dist/commands/raw.d.ts +0 -0
- package/dist/commands/raw.d.ts.map +0 -0
- package/dist/commands/raw.js +0 -0
- package/dist/commands/raw.js.map +0 -0
- package/dist/commands/results-add.d.ts +0 -0
- package/dist/commands/results-add.d.ts.map +0 -0
- package/dist/commands/results-add.js +0 -0
- package/dist/commands/results-add.js.map +0 -0
- package/dist/commands/review.d.ts +12 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +113 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/session.d.ts +0 -0
- package/dist/commands/session.d.ts.map +0 -0
- package/dist/commands/session.js +0 -0
- package/dist/commands/session.js.map +0 -0
- package/dist/commands/verdict.d.ts +9 -0
- package/dist/commands/verdict.d.ts.map +1 -0
- package/dist/commands/verdict.js +29 -0
- package/dist/commands/verdict.js.map +1 -0
- package/dist/config.d.ts +0 -0
- package/dist/config.d.ts.map +0 -0
- package/dist/config.js +0 -0
- package/dist/config.js.map +0 -0
- package/dist/deep/co-occurrence.d.ts +0 -0
- package/dist/deep/co-occurrence.d.ts.map +0 -0
- package/dist/deep/co-occurrence.js +0 -0
- package/dist/deep/co-occurrence.js.map +0 -0
- package/dist/deep/dedupe.d.ts +0 -0
- package/dist/deep/dedupe.d.ts.map +0 -0
- package/dist/deep/dedupe.js +0 -0
- package/dist/deep/dedupe.js.map +0 -0
- package/dist/deep/degree-utils.d.ts +0 -0
- package/dist/deep/degree-utils.d.ts.map +0 -0
- package/dist/deep/degree-utils.js +0 -0
- package/dist/deep/degree-utils.js.map +0 -0
- package/dist/deep/extractor.d.sync-conflict-20260629-232902-2SGIW5T.ts +0 -0
- package/dist/deep/extractor.d.ts +0 -0
- package/dist/deep/extractor.d.ts.map +0 -0
- package/dist/deep/extractor.d.ts.sync-conflict-20260629-232921-2SGIW5T.map +0 -0
- package/dist/deep/extractor.js +0 -0
- package/dist/deep/extractor.js.map +0 -0
- package/dist/deep/extractor.js.sync-conflict-20260629-232921-2SGIW5T.map +0 -0
- package/dist/deep/extractor.sync-conflict-20260629-232903-2SGIW5T.js +0 -0
- package/dist/deep/graph-explore.d.ts +0 -0
- package/dist/deep/graph-explore.d.ts.map +0 -0
- package/dist/deep/graph-explore.js +0 -0
- package/dist/deep/graph-explore.js.map +0 -0
- package/dist/deep/graph-obfuscate.d.ts +0 -0
- package/dist/deep/graph-obfuscate.d.ts.map +0 -0
- package/dist/deep/graph-obfuscate.js +0 -0
- package/dist/deep/graph-obfuscate.js.map +0 -0
- package/dist/deep/graph-preprocess.d.ts +0 -0
- package/dist/deep/graph-preprocess.d.ts.map +0 -0
- package/dist/deep/graph-preprocess.js +0 -0
- package/dist/deep/graph-preprocess.js.map +0 -0
- package/dist/deep/graph-sampling.d.ts +0 -0
- package/dist/deep/graph-sampling.d.ts.map +0 -0
- package/dist/deep/graph-sampling.js +0 -0
- package/dist/deep/graph-sampling.js.map +0 -0
- package/dist/deep/graph.d.sync-conflict-20260629-232917-2SGIW5T.ts +0 -0
- package/dist/deep/graph.d.ts +0 -0
- package/dist/deep/graph.d.ts.map +0 -0
- package/dist/deep/graph.d.ts.sync-conflict-20260629-232911-2SGIW5T.map +0 -0
- package/dist/deep/graph.js +0 -0
- package/dist/deep/graph.js.map +0 -0
- package/dist/deep/graph.js.sync-conflict-20260629-232855-2SGIW5T.map +0 -0
- package/dist/deep/graph.sync-conflict-20260629-232907-2SGIW5T.js +0 -0
- package/dist/deep/index.d.ts +0 -0
- package/dist/deep/index.d.ts.map +0 -0
- package/dist/deep/index.js +0 -0
- package/dist/deep/index.js.map +0 -0
- package/dist/deep/iteration-data.d.ts +0 -0
- package/dist/deep/iteration-data.d.ts.map +0 -0
- package/dist/deep/iteration-data.js +0 -0
- package/dist/deep/iteration-data.js.map +0 -0
- package/dist/deep/keywords.d.ts +0 -0
- package/dist/deep/keywords.d.ts.map +0 -0
- package/dist/deep/keywords.js +0 -0
- package/dist/deep/keywords.js.map +0 -0
- package/dist/deep/local-doc/indexer.d.ts +0 -0
- package/dist/deep/local-doc/indexer.d.ts.map +0 -0
- package/dist/deep/local-doc/indexer.js +0 -0
- package/dist/deep/local-doc/indexer.js.map +0 -0
- package/dist/deep/local-doc/scanner.d.ts +0 -0
- package/dist/deep/local-doc/scanner.d.ts.map +0 -0
- package/dist/deep/local-doc/scanner.js +0 -0
- package/dist/deep/local-doc/scanner.js.map +0 -0
- package/dist/deep/local-doc/searcher.d.ts +0 -0
- package/dist/deep/local-doc/searcher.d.ts.map +0 -0
- package/dist/deep/local-doc/searcher.js +0 -0
- package/dist/deep/local-doc/searcher.js.map +0 -0
- package/dist/deep/local-doc/types.d.ts +0 -0
- package/dist/deep/local-doc/types.d.ts.map +0 -0
- package/dist/deep/local-doc/types.js +0 -0
- package/dist/deep/local-doc/types.js.map +0 -0
- package/dist/deep/path-extraction.d.ts +0 -0
- package/dist/deep/path-extraction.d.ts.map +0 -0
- package/dist/deep/path-extraction.js +0 -0
- package/dist/deep/path-extraction.js.map +0 -0
- package/dist/deep/quality-assess.d.ts +0 -0
- package/dist/deep/quality-assess.d.ts.map +0 -0
- package/dist/deep/quality-assess.js +0 -0
- package/dist/deep/quality-assess.js.map +0 -0
- package/dist/deep/query-redundancy.d.ts +0 -0
- package/dist/deep/query-redundancy.d.ts.map +0 -0
- package/dist/deep/query-redundancy.js +0 -0
- package/dist/deep/query-redundancy.js.map +0 -0
- package/dist/deep/query-suggest.d.ts +0 -0
- package/dist/deep/query-suggest.d.ts.map +0 -0
- package/dist/deep/query-suggest.js +0 -0
- package/dist/deep/query-suggest.js.map +0 -0
- package/dist/deep/recovery-analysis.d.ts +0 -0
- package/dist/deep/recovery-analysis.d.ts.map +0 -0
- package/dist/deep/recovery-analysis.js +0 -0
- package/dist/deep/recovery-analysis.js.map +0 -0
- package/dist/deep/rrf.d.ts +0 -0
- package/dist/deep/rrf.d.ts.map +0 -0
- package/dist/deep/rrf.js +0 -0
- package/dist/deep/rrf.js.map +0 -0
- package/dist/deep/search-strategy.d.ts +0 -0
- package/dist/deep/search-strategy.d.ts.map +0 -0
- package/dist/deep/search-strategy.js +0 -0
- package/dist/deep/search-strategy.js.map +0 -0
- package/dist/deep/session.d.sync-conflict-20260629-232917-2SGIW5T.ts +0 -0
- package/dist/deep/session.d.ts +2 -0
- package/dist/deep/session.d.ts.map +1 -1
- package/dist/deep/session.d.ts.sync-conflict-20260629-232921-2SGIW5T.map +0 -0
- package/dist/deep/session.js +0 -0
- package/dist/deep/session.js.map +1 -1
- package/dist/deep/session.js.sync-conflict-20260629-232854-2SGIW5T.map +0 -0
- package/dist/deep/simhash.d.ts +0 -0
- package/dist/deep/simhash.d.ts.map +0 -0
- package/dist/deep/simhash.js +0 -0
- package/dist/deep/simhash.js.map +0 -0
- package/dist/deep/tfidf.d.ts +0 -0
- package/dist/deep/tfidf.d.ts.map +0 -0
- package/dist/deep/tfidf.js +0 -0
- package/dist/deep/tfidf.js.map +0 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.d.ts.map +0 -0
- package/dist/index.js +0 -0
- package/dist/index.js.map +0 -0
- package/dist/init.d.ts +0 -0
- package/dist/init.d.ts.map +0 -0
- package/dist/init.js +0 -0
- package/dist/init.js.map +0 -0
- package/dist/protocol.d.ts +0 -0
- package/dist/protocol.d.ts.map +0 -0
- package/dist/protocol.js +0 -0
- package/dist/protocol.js.map +0 -0
- package/dist/runCli.d.ts +0 -0
- package/dist/runCli.d.ts.map +1 -1
- package/dist/runCli.d.ts.sync-conflict-20260629-232854-2SGIW5T.map +0 -0
- package/dist/runCli.js +125 -0
- package/dist/runCli.js.map +1 -1
- package/dist/runCli.js.sync-conflict-20260629-232857-2SGIW5T.map +0 -0
- package/dist/runCli.sync-conflict-20260629-232915-2SGIW5T.js +0 -0
- package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.d.ts +0 -0
- package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.d.ts.map +0 -0
- package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.js +0 -0
- package/dist/runCli.sync-conflict-20260629-233530-JAULAPO.js.map +0 -0
- package/dist/service.d.ts +0 -0
- package/dist/service.d.ts.map +0 -0
- package/dist/service.js +0 -0
- package/dist/service.js.map +0 -0
- package/package.json +2 -2
- package/sxng.config.example.json +0 -0
package/README.md
CHANGED
|
@@ -30,10 +30,12 @@
|
|
|
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
32
|
- 🗂️ **Session Management** — Accumulate search results across rounds with deduplication; 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
|
|
33
34
|
- ⭐ **Quality Assessment** — 4 independent indicators: content depth, entity richness, source diversity, novelty
|
|
34
35
|
- 🕸️ **Knowledge Graph** — Structural (query→result→domain) + semantic (entity relations) graph layers
|
|
35
36
|
- 🔄 **Query Redundancy Check** — Jaccard similarity + SimHash to avoid 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"`
|
|
37
39
|
|
|
38
40
|
## 📦 Installation
|
|
39
41
|
|
|
@@ -124,6 +126,9 @@ obscura --version
|
|
|
124
126
|
| `sxng graph-drill <session>` | Follow specific relations |
|
|
125
127
|
| `sxng graph-traverse <session>` | Traverse reasoning paths |
|
|
126
128
|
| `sxng graph-obfuscate <session>` | List obfuscation candidates |
|
|
129
|
+
| `sxng results-add <session> --data <json>` | Inject external search results into session as pending |
|
|
130
|
+
| `sxng doc-index <path>` | Index local documents for BM25 search |
|
|
131
|
+
| `sxng doc-search <session> <query> --path <path>` | Search indexed docs and inject results into session |
|
|
127
132
|
| `sxng --health` | Check SearXNG server health |
|
|
128
133
|
| `sxng --engines-list` | List available search engines |
|
|
129
134
|
| `sxng --categories-list` | List available categories |
|
|
@@ -290,6 +295,34 @@ Two layers:
|
|
|
290
295
|
|
|
291
296
|
Graph navigation commands: `graph-search` (discover entities), `graph-explore` (view relations), `graph-drill` (follow specific relations), `graph-traverse` (traverse reasoning paths).
|
|
292
297
|
|
|
298
|
+
### External Search Results
|
|
299
|
+
|
|
300
|
+
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:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
sxng results-add <session-name> --data '[
|
|
304
|
+
{"url": "https://...", "title": "...", "source": "tavily"}
|
|
305
|
+
]'
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
After injection, results are `pending` and follow `--quality` → `--approve` → `graph-add`.
|
|
309
|
+
|
|
310
|
+
### Local Document Search
|
|
311
|
+
|
|
312
|
+
`doc-index` and `doc-search` enable BM25 full-text search over local documents, with results flowing directly into the session pipeline:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
# Index (auto-triggered by doc-search)
|
|
316
|
+
sxng doc-index ./docs
|
|
317
|
+
|
|
318
|
+
# Search and inject into session
|
|
319
|
+
sxng doc-search <session-name> "query" --path ./docs
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**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. Local searches do **not** increment the round counter.
|
|
323
|
+
|
|
324
|
+
**Quality note:** Pure local search yields `sourceDiversity: 1`. Combine with web results for adequate diversity.
|
|
325
|
+
|
|
293
326
|
## 🔗 Links
|
|
294
327
|
|
|
295
328
|
- **GitHub:** https://github.com/hkwuks/sxng-cli
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic checks — no LLM dependency.
|
|
3
|
+
*
|
|
4
|
+
* - checkAnchor: URL in approved results, offset in range, hash matches
|
|
5
|
+
* - computeSourceClusterId: domain + author/name + SimHash → 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
|
+
* Concatenates: domain + author|siteName + SimHash(quote) → SHA256 → 16 hex chars.
|
|
28
|
+
*/
|
|
29
|
+
export declare function computeSourceClusterId(evidence: EvidenceSpan): string;
|
|
30
|
+
/** A candidate evidence match, not persisted until verified by Agent. */
|
|
31
|
+
export interface EvidenceCandidate {
|
|
32
|
+
resultUrl: string;
|
|
33
|
+
quote: string;
|
|
34
|
+
charStart: number;
|
|
35
|
+
charEnd: number;
|
|
36
|
+
contentHash: string;
|
|
37
|
+
domain: string;
|
|
38
|
+
author?: string;
|
|
39
|
+
siteName?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Search approved session results for candidate evidence supporting the claim.
|
|
43
|
+
* Uses keyword + Jaccard sentence matching. Read-only — results not persisted.
|
|
44
|
+
*/
|
|
45
|
+
export declare function searchCandidates(sessionDir: string, claimText: string): EvidenceCandidate[];
|
|
46
|
+
//# 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;AAIH,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;AAMD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,YAAY,GACrB,MAAM,CAWR;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,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic checks — no LLM dependency.
|
|
3
|
+
*
|
|
4
|
+
* - checkAnchor: URL in approved results, offset in range, hash matches
|
|
5
|
+
* - computeSourceClusterId: domain + author/name + SimHash → SHA256 fingerprint
|
|
6
|
+
* - searchCandidates: keyword + Jaccard sentence matching
|
|
7
|
+
*/
|
|
8
|
+
import { createHash } from 'crypto';
|
|
9
|
+
import { SimHash } from '../deep/simhash.js';
|
|
10
|
+
import { getApprovedResults } from '../deep/session.js';
|
|
11
|
+
import { loadSessionResults } from '../deep/session.js';
|
|
12
|
+
/**
|
|
13
|
+
* Verify that an EvidenceSpan is properly anchored to approved content.
|
|
14
|
+
*/
|
|
15
|
+
export function checkAnchor(evidence, content) {
|
|
16
|
+
const slice = content.slice(evidence.charStart, evidence.charEnd);
|
|
17
|
+
const computedHash = createHash('sha256').update(slice).digest('hex');
|
|
18
|
+
return {
|
|
19
|
+
url: true, // caller must have resolved url → content already
|
|
20
|
+
offsetInRange: evidence.charEnd <= content.length && evidence.charStart >= 0,
|
|
21
|
+
hashMatches: computedHash === evidence.contentHash,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Verify that evidence.resultUrl exists in the session's approved results.
|
|
26
|
+
* Returns the matching Result's content, or null if not found.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveApprovedContent(sessionDir, resultUrl) {
|
|
29
|
+
const approved = getApprovedResults(sessionDir);
|
|
30
|
+
for (const r of approved) {
|
|
31
|
+
if (r.url === resultUrl && r.content) {
|
|
32
|
+
return { content: r.content };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
// ── Source clustering ───────────────────────────────────────────────
|
|
38
|
+
const _simhash = new SimHash();
|
|
39
|
+
/**
|
|
40
|
+
* Compute a source cluster ID for an EvidenceSpan.
|
|
41
|
+
* Concatenates: domain + author|siteName + SimHash(quote) → SHA256 → 16 hex chars.
|
|
42
|
+
*/
|
|
43
|
+
export function computeSourceClusterId(evidence) {
|
|
44
|
+
let domain = '';
|
|
45
|
+
try {
|
|
46
|
+
domain = new URL(evidence.resultUrl).hostname;
|
|
47
|
+
}
|
|
48
|
+
catch { /* empty */ }
|
|
49
|
+
const authorSig = (evidence.author || evidence.siteName || '').toLowerCase().trim();
|
|
50
|
+
const quoteSim = _simhash.hash(evidence.quote);
|
|
51
|
+
const input = `${domain}|${authorSig}|${quoteSim.toString(16)}`;
|
|
52
|
+
return createHash('sha256').update(input).digest('hex').slice(0, 16);
|
|
53
|
+
}
|
|
54
|
+
/** Simple stopwords for tokenization */
|
|
55
|
+
const STOPWORDS = new Set([
|
|
56
|
+
'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
57
|
+
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
|
|
58
|
+
'should', 'may', 'might', 'shall', 'can', 'need', 'dare', 'ought',
|
|
59
|
+
'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as',
|
|
60
|
+
'into', 'through', 'during', 'before', 'after', 'above', 'below',
|
|
61
|
+
'between', 'out', 'off', 'over', 'under', 'again', 'further',
|
|
62
|
+
'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how',
|
|
63
|
+
'all', 'each', 'every', 'both', 'few', 'more', 'most', 'other',
|
|
64
|
+
'some', 'such', 'no', 'not', 'only', 'own', 'same', 'so', 'than',
|
|
65
|
+
'too', 'very', 'just', 'because', 'but', 'and', 'or', 'if', 'while',
|
|
66
|
+
'although', 'this', 'that', 'these', 'those', 'it', 'its',
|
|
67
|
+
'the', 'which', 'who', 'whom', 'what',
|
|
68
|
+
]);
|
|
69
|
+
function tokenize(text) {
|
|
70
|
+
return text
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.split(/[^a-z0-9]+/)
|
|
73
|
+
.filter(t => t.length > 1 && !STOPWORDS.has(t));
|
|
74
|
+
}
|
|
75
|
+
function jaccardSimilarity(a, b) {
|
|
76
|
+
if (a.size === 0 && b.size === 0)
|
|
77
|
+
return 0;
|
|
78
|
+
let intersection = 0;
|
|
79
|
+
for (const word of a) {
|
|
80
|
+
if (b.has(word))
|
|
81
|
+
intersection++;
|
|
82
|
+
}
|
|
83
|
+
const union = a.size + b.size - intersection;
|
|
84
|
+
return union === 0 ? 0 : intersection / union;
|
|
85
|
+
}
|
|
86
|
+
const JACCARD_THRESHOLD = 0.3;
|
|
87
|
+
const MAX_CANDIDATES = 5;
|
|
88
|
+
/**
|
|
89
|
+
* Search approved session results for candidate evidence supporting the claim.
|
|
90
|
+
* Uses keyword + Jaccard sentence matching. Read-only — results not persisted.
|
|
91
|
+
*/
|
|
92
|
+
export function searchCandidates(sessionDir, claimText) {
|
|
93
|
+
const keywords = tokenize(claimText);
|
|
94
|
+
if (keywords.length === 0)
|
|
95
|
+
return [];
|
|
96
|
+
const keywordSet = new Set(keywords);
|
|
97
|
+
const allResults = loadSessionResults(sessionDir);
|
|
98
|
+
const approved = allResults.filter(r => r.status === 'approved' && r.content);
|
|
99
|
+
const candidates = [];
|
|
100
|
+
for (const result of approved) {
|
|
101
|
+
const content = result.content;
|
|
102
|
+
// Split into sentences
|
|
103
|
+
const sentences = content.split(/(?<=[.!?])\s+|(?<=\n)\s*/);
|
|
104
|
+
for (const sentence of sentences) {
|
|
105
|
+
const trimmed = sentence.trim();
|
|
106
|
+
if (trimmed.length < 10)
|
|
107
|
+
continue;
|
|
108
|
+
const words = tokenize(trimmed);
|
|
109
|
+
if (words.length === 0)
|
|
110
|
+
continue;
|
|
111
|
+
const wordSet = new Set(words);
|
|
112
|
+
const score = jaccardSimilarity(keywordSet, wordSet);
|
|
113
|
+
if (score < JACCARD_THRESHOLD)
|
|
114
|
+
continue;
|
|
115
|
+
const charStart = content.indexOf(trimmed);
|
|
116
|
+
if (charStart === -1)
|
|
117
|
+
continue;
|
|
118
|
+
const charEnd = charStart + trimmed.length;
|
|
119
|
+
const contentHash = createHash('sha256')
|
|
120
|
+
.update(trimmed)
|
|
121
|
+
.digest('hex');
|
|
122
|
+
let domain = '';
|
|
123
|
+
try {
|
|
124
|
+
domain = new URL(result.url).hostname;
|
|
125
|
+
}
|
|
126
|
+
catch { /* empty */ }
|
|
127
|
+
candidates.push({
|
|
128
|
+
score,
|
|
129
|
+
candidate: {
|
|
130
|
+
resultUrl: result.url,
|
|
131
|
+
quote: trimmed,
|
|
132
|
+
charStart,
|
|
133
|
+
charEnd,
|
|
134
|
+
contentHash,
|
|
135
|
+
domain,
|
|
136
|
+
author: result.byline || undefined,
|
|
137
|
+
siteName: result.siteName || undefined,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Sort by Jaccard score descending, take top N
|
|
143
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
144
|
+
return candidates.slice(0, MAX_CANDIDATES).map(c => c.candidate);
|
|
145
|
+
}
|
|
146
|
+
//# 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
|
+
// Source independence: count distinct source 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 independent sources → 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 independent sources + 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 sources 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 independent source → 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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
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 { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
// ── Path helpers ────────────────────────────────────────────────────
|
|
11
|
+
function claimsDir(sessionDir) {
|
|
12
|
+
return join(sessionDir, 'claims');
|
|
13
|
+
}
|
|
14
|
+
function claimsFile(sessionDir) {
|
|
15
|
+
return join(claimsDir(sessionDir), 'claims.json');
|
|
16
|
+
}
|
|
17
|
+
function evidencesFile(sessionDir) {
|
|
18
|
+
return join(claimsDir(sessionDir), 'evidences.json');
|
|
19
|
+
}
|
|
20
|
+
function verdictsFile(sessionDir) {
|
|
21
|
+
return join(claimsDir(sessionDir), 'verdicts.json');
|
|
22
|
+
}
|
|
23
|
+
function reviewsFile(sessionDir) {
|
|
24
|
+
return join(claimsDir(sessionDir), 'reviews.json');
|
|
25
|
+
}
|
|
26
|
+
function ensureClaimsDir(sessionDir) {
|
|
27
|
+
const dir = claimsDir(sessionDir);
|
|
28
|
+
if (!existsSync(dir)) {
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function loadArray(file) {
|
|
33
|
+
if (!existsSync(file))
|
|
34
|
+
return [];
|
|
35
|
+
try {
|
|
36
|
+
const raw = readFileSync(file, 'utf-8');
|
|
37
|
+
const parsed = JSON.parse(raw);
|
|
38
|
+
if (parsed.status === 'ok' && Array.isArray(parsed.data?.items)) {
|
|
39
|
+
return parsed.data.items;
|
|
40
|
+
}
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function writeArray(file, items) {
|
|
48
|
+
const dir = file.substring(0, file.lastIndexOf(/[\\/]/.test(file) ? file.match(/[\\/]/)[0] : '/'));
|
|
49
|
+
if (!existsSync(dir)) {
|
|
50
|
+
mkdirSync(dir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
writeFileSync(file, JSON.stringify({ status: 'ok', data: { items } }, null, 2), 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
// ── Claims ──────────────────────────────────────────────────────────
|
|
55
|
+
export function loadClaims(sessionDir) {
|
|
56
|
+
return loadArray(claimsFile(sessionDir));
|
|
57
|
+
}
|
|
58
|
+
export function saveClaims(sessionDir, claims) {
|
|
59
|
+
ensureClaimsDir(sessionDir);
|
|
60
|
+
writeArray(claimsFile(sessionDir), claims);
|
|
61
|
+
}
|
|
62
|
+
/** Allocate next claim ID: cl_<seq>. Reads current count from file. */
|
|
63
|
+
export function nextClaimId(sessionDir) {
|
|
64
|
+
const claims = loadClaims(sessionDir);
|
|
65
|
+
const seq = claims.length + 1;
|
|
66
|
+
return `cl_${String(seq).padStart(3, '0')}`;
|
|
67
|
+
}
|
|
68
|
+
// ── EvidenceSpans ────────────────────────────────────────────────────
|
|
69
|
+
export function loadEvidences(sessionDir) {
|
|
70
|
+
return loadArray(evidencesFile(sessionDir));
|
|
71
|
+
}
|
|
72
|
+
export function saveEvidences(sessionDir, evidences) {
|
|
73
|
+
ensureClaimsDir(sessionDir);
|
|
74
|
+
writeArray(evidencesFile(sessionDir), evidences);
|
|
75
|
+
}
|
|
76
|
+
/** Allocate next evidence ID: ev_<seq>. */
|
|
77
|
+
export function nextEvidenceId(sessionDir) {
|
|
78
|
+
const evs = loadEvidences(sessionDir);
|
|
79
|
+
const seq = evs.length + 1;
|
|
80
|
+
return `ev_${String(seq).padStart(3, '0')}`;
|
|
81
|
+
}
|
|
82
|
+
// ── Verdicts ────────────────────────────────────────────────────────
|
|
83
|
+
export function loadVerdicts(sessionDir) {
|
|
84
|
+
return loadArray(verdictsFile(sessionDir));
|
|
85
|
+
}
|
|
86
|
+
export function saveVerdicts(sessionDir, verdicts) {
|
|
87
|
+
ensureClaimsDir(sessionDir);
|
|
88
|
+
writeArray(verdictsFile(sessionDir), verdicts);
|
|
89
|
+
}
|
|
90
|
+
/** Allocate next verdict ID: vd_<seq>. */
|
|
91
|
+
export function nextVerdictId(sessionDir) {
|
|
92
|
+
const vds = loadVerdicts(sessionDir);
|
|
93
|
+
const seq = vds.length + 1;
|
|
94
|
+
return `vd_${String(seq).padStart(3, '0')}`;
|
|
95
|
+
}
|
|
96
|
+
// ── Reviews ─────────────────────────────────────────────────────────
|
|
97
|
+
export function loadReviews(sessionDir) {
|
|
98
|
+
return loadArray(reviewsFile(sessionDir));
|
|
99
|
+
}
|
|
100
|
+
export function saveReviews(sessionDir, reviews) {
|
|
101
|
+
ensureClaimsDir(sessionDir);
|
|
102
|
+
writeArray(reviewsFile(sessionDir), reviews);
|
|
103
|
+
}
|
|
104
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
105
|
+
/** Get claims filtered by status */
|
|
106
|
+
export function getClaimsByStatus(sessionDir, status) {
|
|
107
|
+
const all = loadClaims(sessionDir);
|
|
108
|
+
if (!status)
|
|
109
|
+
return all;
|
|
110
|
+
return all.filter(c => c.status === status);
|
|
111
|
+
}
|
|
112
|
+
/** Get reviews filtered by decision */
|
|
113
|
+
export function getReviewsByDecision(sessionDir, decision) {
|
|
114
|
+
const all = loadReviews(sessionDir);
|
|
115
|
+
if (!decision)
|
|
116
|
+
return all;
|
|
117
|
+
return all.filter(r => r.decision === decision);
|
|
118
|
+
}
|
|
119
|
+
/** Get evidence for a specific claim */
|
|
120
|
+
export function getEvidenceForClaim(sessionDir, claimId) {
|
|
121
|
+
return loadEvidences(sessionDir).filter(e => e.claimId === claimId);
|
|
122
|
+
}
|
|
123
|
+
/** Get verdicts for a specific claim */
|
|
124
|
+
export function getVerdictsForClaim(sessionDir, claimId) {
|
|
125
|
+
return loadVerdicts(sessionDir).filter(v => v.claimId === claimId);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/claims/store.ts"],"names":[],"mappings":"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"}
|