evidex
v0.1.1
Published
Resolver and validator for the SourceCheck spec: machine-checkable source refs for LLM output.
Readme
evidex
Resolver and validator for the SourceCheck spec: machine-checkable source refs for LLM output.
An LLM produces claims; evidex makes the evidence traceable. The model's output is a list of annotations — each points at a claim span and at its supporting source spans. evidex resolves those refs against an immutable document session and reports exactly which annotation is valid, what text each ref covers, and why invalid ones failed.
DRI (Do Not Repeat Input): the model only points at text. The resolver produces it. Ambiguity fails instead of guessing.
Install
pnpm add evidexNode.js >= 20.19. ESM only.
Use as a library
import { buildSession, validateAnnotations } from "evidex";
const session = buildSession([
{ path: "report.txt", content: "The outcome was positive." },
{ path: "output.txt", content: "The result is positive." },
]);
const report = validateAnnotations(session, [
{
claim: {
path: "output.txt",
start: { line: 1, str: "result" },
end: { line: 1, str: "positive" },
},
sources: [
{
path: "report.txt",
start: { line: 1, str: "outcome" },
end: { line: 1, str: "positive" },
polarity: "supports",
confidence: 0.9,
},
],
},
]);
// report.entries[0].claim.text === "result is positive"
// report.entries[0].sources[0].text === "outcome was positive"
// report.validCount === 1Use as a CLI
evidex session report.txt output.txt -o session.json # build an LF-normalized session
evidex validate session.json annotations.json # resolve + validate, prints the report
evidex check input.json --json # single-file batch check; exit 0/1/2
evidex index doc.txt # numbered lines for authoring refscheck reads a single file shaped { session, annotations } (or stdin when the argument is -).
Exit codes follow the skill contract: 0 = all refs resolved, 1 = resolution failure,
2 = invalid JSON or schema. index prints each document with 1-based line numbers so an
agent can author refs against them.
The format in 60 seconds
- Session — an immutable set of documents:
{ path, content }, LF-normalized, unique paths. - Ref — an exact text span:
{ path, start: { line, str }, end: { line, str } }(1-based lines, boundary strings included in the span). - Annotation — a claim ref + a non-empty list of source refs, each with
polarity(supports|refutes) andconfidence(0..1).
Validity rules: path must exist, lines in range, start.line <= end.line, boundary strings must occur exactly once on their lines (extend str to disambiguate — there is no occurrence index), and the start boundary must not come after the end boundary. See skills/sourcecheck/SPEC.md for the full spec.
Typical pipeline
source docs + claim text
│ buildSession()
▼
session.json ──► given to the LLM (same content it must cite)
│ model returns annotations.json
▼
validateAnnotations(session, annotations) ──► report
│
▼
render: highlight claims, link sources (polarity/confidence/errors)The session is the single source of truth for all three consumers: the agent authors refs against it, the resolver validates against it, and the frontend renders from it.
Architecture
src/
core/ pure computation (zero I/O): session.ts · ref.ts · annotate.ts · types.ts
cli/ main.ts — session / validate subcommands
index.ts public API barrel
tests/ *.test.ts, imports via the @/ aliasLayer rule: core depends on nothing; the CLI only orchestrates. A stdio/HTTP MCP server can later be added as a CLI subcommand without touching core.
Development
pnpm install
pnpm run check # typecheck + lint + tests
pnpm test # vitest
pnpm run build # CLI bundle + library bundle + type declarations (dist/)
pnpm run format # biome check --writeToolchain: TypeScript 7 (native), Vite 7, Vitest 3, Biome 2 (4-space indent, rustfmt-style), pnpm.
License
MIT
